Project showcase
Recycled Meter Artwork

Recycled Meter Artwork © CC BY-SA

Battery powered artwork made from recycled ammeter to hang on your wall

  • 532 views
  • 0 comments
  • 6 respects

Components and supplies

About this project

I bought a cheap pocket watch meter from eBay thinking that it would make an interesting novelty item. It turned out that the meter I'd bought wasn't suitable, but by then I'd committed myself to producing something which would hang on a wall and be a talking point.

The centre of the display is an analogue ammeter which is energised by a charged capacitor which discharges through the meter animating the pointer needle in doing so.

An LED display mirrors the movement of the pointer providing an eye catching display.

The whole is controlled by an Atmel 328 microprocessor, directly developed on an Arduino Uno, which measures the current light levels in the room, and randomly triggers the display, all powered by three AA batteries.

The pictures tell a short story, the original meter was designed for use on valve radios and required over 100mA and just couldn't be run by an Arduino. I thought of it being mounted with a Nano but it would be easier to develop with an Uno. These are early display layout ideas. In the end I took the meter apart with the intention of replacing the mechanism, not very successful.

Eventually I picked up an old voltmeter with a 100uA mechanism, perfect.

The original build used an Arduino to connect the bits in what is a fairly simple system. Six digital pins drive the coloured LED's via 330R resistors.

One digital pin is used to energise the LDR voltage divider, the voltage being measured on one of the ADC pins and used to estimate the current light level and the time of day.

One digital pin is used to charge the capacitor via a diode and 220R resistor.

The meter is connected across the capacitor via a 10k resistor. This value may need to be changed depending upon the full scale measurement on the ammeter used.

I also wired in a reset button, to be mounted on the side of the display case.

Lastly, a further connection is made from the anode of one of the LED's to provide a voltage reference to check the battery voltage level. This circuit has never been very successful and I'll change it to a simple voltage divider the next time the batteries run flat and the display is off the wall.

Running the display from batteries using an Arduino Uno was not practical, the current consumption would be too high as much of the board is active all the time, and I wanted the display to be up on a wall untouched for at least six months at a time.

To cut current consumption, the display circuits were developed with an Arduino and breadboard, the circuits transferred to matrix board and then the finally programmed processor removed from the Arduino and put into a socket on a small piece of matrix board, together with the xtal, and joined together with ribbon cable.

In the end, the display runs for a full 12 months on one set of batteries.

A useful trick is to replace the Atmel processor in an Arduino Uno with a ZIF socket, this one fits fine, and then reinsert the processor. Once the project is ready to go, the processor is already programmed and just needs removing and putting into a socket on the final board.

As might be imagined, the code for running the basic display isn't very complicated but the key area is reduction of power consumption. There are two approaches to this, one is to only run the display when its likely someone will see it, and secondly to cut the power consumption of circuits to a minimum.

The program has to have the Narcoleptic libraries installed before compilation.

All delays in the system are implemented using the narcoleptic library for full low power mode of the processor, with a power consumption measured in a few nanoamps.

The processor sleeps for four seconds at a time, and on waking, runs a random routine to determine if the system will wake of not. If not, the system sleeps for another four seconds.

If the random routine is true, the LDR circuit is activated and a light level measurement taken. The LDR circuit is deactivated immediately afterwards to save power.

The system works on four estimated time periods.

  • Night - its very dark and no one is likely to watch - do nothing and go back to sleep
  • Early Morning - in the first part there are unlikely to be any watchers, but maintain stats as if daytime
  • Daytime - there may be watchers, but activate only the analogue meter, not the LED's
  • Evening - it's likely there will be watchers so activate the full display

The system estimates that day length will change with the seasons, so evening is extended into what would otherwise be night as the length of days is shorter, but when watchers are still likely to be present.

If the time of day is appropriate, a digital output is used to charge the capacitor and then switched off. With an analogue only display, the system goes back to sleep with all output off and the capacitor discharges through the meter whose pointer, which had flicked over to full scale, returns to zero.

With the LED display active, the system measures the voltage on the capacitor and presents a running light display based on the measured voltage until it drops below a threshold when the system sleeps.

A second random selection takes place towards the end of the display to determine if the display will be repeated or not, providing more interest for the watcher.

A white LED is activated to illuminate the meter face when the LED show is active.

The narcoleptic library by Peter Knight, puts the processor into a full sleep mode where outputs will remain in the state they were on entering sleep but all internal clocks stop except the sleep timer which is limited to four seconds. This can be tested in an Arduino but because of the Arduino power LED and USB circuits doesn't achieve the same power savings.

The system still contains code which was meant to account for the decreasing capacity of the batteries but this hasn't proved useful. Next time its off the wall I'll change the program to provide some sort of battery status via the LEDs or ammeter.

The final version has a reset button mounted on the side of the display case. The main reason for this is to allow demonstrations to visitors so the system will run through its basic routine 10 times after reset before going back to it's normal random routine.

Code

Wall_Meter_Display.inoArduino
Runs a basic light display and is intended to run with very low power consumption for battery operation
#include <Narcoleptic.h>

/*
  WallDisplay - Traffic light
  Creates a basic traffic light display for a wallmount
*/
int voltref = 0;    // hold reference voltage level for low voltage calculations
int demoCount = 0;   // used to run the demo section when first powered up
int Direction = 1;   // sets direction either 0 or 1
int SetDelay = 1; //0=standard delay, 1=NarcoDelay, low power and no serial port usage
unsigned long randInit = 20;  // limits of random number generator, set low for testing
int LDR;          // voltage measured on LDR
int darkLimit = 1000;  // ldr reading for its dark
int lightLimit = 750;  // ldr reading for its light
int timeOfDay = 1;  // time of day set to gloaming so that power on and reset runs full display  0 = night, 1 = gloaming, 2=day
int lightChange = 0;  // count of contiguous light LDR samples
int gloamingChange = 0;   // count of contiguous gloaming LDR samples
int darkChange = 0;   // count of contiguous dark LDR samples
int randLimit = 20;  // limits of random number generator, set low for testing
int delayPeriod = 4000; //inital delay period just in case its needed
int summer = 14970;  // length of longest day in samples
int winter = 7035;  // length of shortest day in samples
int longDayCount = 0;  // approximate length of day in displays
int shortDayCount = 0;  // approximate length of day in displays
int maxDay = 0;  // previous length of light in display cycles
int delayDayCount = 21; // number of display cycles to defer in the morning based on previous days estimated length
int capacitor=2 // pin applying power to capacitor
int LDRpower=3 // pin applying power to ldr
int red1=5;   // 1st red led
int red2=6;   // 2nd red led
int amber1=7;   // 1st amber led
int amber2=8;   // 2nd amber led
int green1=9;   // 1st green led
int green2=10;   // 2nd green led
int whiteled=11; // white led to illuminate meter



// ---------------------------------------------------------------------------------------    
// the setup function runs once when reset it pressed or power the board
void setup() {
  // initialize digital output pins
  //   pin A1
  //   pin A2
  //   pin A3
  pinMode(capacitor, OUTPUT);
  pinMode(LDRpower, OUTPUT);
  pinMode(red1, OUTPUT);
  pinMode(red2, OUTPUT);
  pinMode(amber1, OUTPUT);
  pinMode(amber2, OUTPUT);
  pinMode(green1, OUTPUT);
  pinMode(green2, OUTPUT);
  pinMode(whiteled, OUTPUT);

  longDayCount = (summer - winter) / randLimit;
  shortDayCount = winter / randLimit;
 
  

  randInit = LDRvoltage();
  randomSeed(randInit);          // randomise on light level
  if (SetDelay == 0)     // only run serial output if standard not narco delay
      Serial.begin(9600);
}




// ---------------------------------------------------------------------------------------    
// the loop function runs over and over again forever
void loop() {
int newTimeOfDay;
// only run routine if random comes up otherwise sleep
   
     
    randInit = random(randLimit);
    SerialSub(SetDelay, "Random ", randInit);
// ---------------------------------------------------------------------------------------    
    
    if (delayDayCount < 20)   //  count down non running of display after dawn when no one will be watching
         {
          delayDayCount++;
          randInit=0;    // extend non running after dawn
          SerialSub(SetDelay, "Delay ", demoCount);
         }
// ---------------------------------------------------------------------------------------      
    if (demoCount < 10)   //  demo and test routine runs every time after power on or reset
         {
          demoCount++;
          randInit=1;    // fudge for test functions
          SerialSub(SetDelay, "Demo ", demoCount);
         }
// ---------------------------------------------------------------------------------------   
    if (randInit == 1) 
      {
        voltref = refVoltage();    // get the reference voltage
        LDR = LDRvoltage();   // get light level
        newTimeOfDay = dayOrNight();
        if (timeOfDay != newTimeOfDay)
            timeOfDay = newTimeOfDay;   // swap to new time of day if theres an update
        SerialSub(SetDelay, "timeOfDay", timeOfDay);    
        if (timeOfDay == 0)    // if night then just wait for 4 or 8 seconds
            {
             if (SetDelay == 0)
                 delayPeriod = 4000;   // if standard sleep then fixed to 4 seconds
             else
                 delayPeriod = 8000;  // if narco then sleep extra time
             }
        else
            {    
            Subloop();    // This breakout runs the display
            delayPeriod = 4000;
            }
      }  
    Ndelay(SetDelay, delayPeriod); //  Delay for 'delayPeriod' seconds approximately
}

// ---------------------------------------------------------------------------------------  
int LDRvoltage() {
    digitalWrite(LDRpower, HIGH);     // activate LDR to check light levels
    int LDRread = analogRead(2);
    digitalWrite(LDRpower, LOW);
    SerialSub(SetDelay, "LDRvoltage", LDRread);
    return LDRread;
}


// ---------------------------------------------------------------------------------------  
// Estimate whether it is day, gloaming or night. The unit has to be in one state for at least four iterations before a state change is registered
// the sequence is light, gloaming, dark, light. 
int dayOrNight() {
  int dayLight;
  dayLight = timeOfDay;
  if (lightChange > maxDay)
      maxDay = lightChange;
  if (LDR > darkLimit)   // calulate continuous state
      {
      lightChange = 0;
      gloamingChange = 0;
      darkChange++;
      }   
    else if (LDR < lightLimit) 
      {
      lightChange++;
      gloamingChange = 0;
      darkChange = 0;
      }  
    else 
      {
      lightChange = 0;
      gloamingChange++;
      darkChange = 0;
      }  
  if (lightChange >10)   // after state the same for four iterations return a change
       dayLight = 2;
       else if (gloamingChange > 10)
                dayLight = 1;
       else if (darkChange > 10)
                dayLight = 0;
  if (timeOfDay == 0 and dayLight==1) // do not allow change from dark to gloaming
      dayLight = 0;        
  SerialSub(SetDelay, "DayOrNight", dayLight);
  return dayLight;        
}


// ---------------------------------------------------------------------------------------  
// get reference voltage for low battery compensation
int refVoltage() {
    digitalWrite(red1, HIGH);
    int readVoltage = analogRead(3);
    digitalWrite(red2, LOW);
    SerialSub(SetDelay, "voltref", readVoltage);
    return readVoltage;
}


// ---------------------------------------------------------------------------------------  
// Return capacitor voltage
int capVoltage() {
    int readVoltage = analogRead(1);
 //   SerialSub(SetDelay, "CapVoltage", readVoltage);
    return readVoltage;
}


// ---------------------------------------------------------------------------------------  
// routine to charge capacitor 
void chargeCap() {
  if (timeOfDay == 1)
      digitalWrite(whiteled, HIGH);     // activate meter illumination
     Ndelay(SetDelay, 300); // wait for 300 milliseconds to attract attention    
  do {
   digitalWrite(capacitor, HIGH);  // start to charge capacitor
   Ndelay(SetDelay, 100); // wait for 100 milliseconds for capacitor to charge
  } while (capVoltage()<800);
  Ndelay(SetDelay, 200); // wait for 200 milliseconds for capacitor to display
   digitalWrite(capacitor, LOW);  // stop charging  
}


// ---------------------------------------------------------------------------------------  
// routine to reverse directiion setting
void reverseDirection() {
    if (Direction == 1)    // reverse previous light direction
  {
    Direction = 2;
  }
  else
  {
    Direction = 1;
  }
}
// ---------------------------------------------------------------------------------------  
// Main display loop
void Subloop() {

// Start by charging capacitor
chargeCap();

// Reverse previous direction to keep variations
  reverseDirection();

// run light display until capacitor runs down
// routine only runs LEDs if gloaming when viewers are likely to see it
  do {     
      if (Direction == 1 and timeOfDay == 1)
      {
        Traffic1();    // run forward light run
      }
      if (Direction == 2 and timeOfDay == 1)
      {
        Traffic2();    // run reverse light run
      }
      if (timeOfDay == 2)
          Ndelay(SetDelay, 1000); //add time delay omitted due to not running lights
      if (random(20) == 1) // Add restart to cap voltage to give random extended displays
          { 
          chargeCap();
          reverseDirection();
          }
  }  while (capVoltage() > 50);
      
      digitalWritewhiteled, LOW);      // turn off meter illumination when done
}


// ---------------------------------------------------------------------------------------  
// One of two LED driver routines which switch on LEDs according to the voltage on the capacitor
void Traffic1()
{
  int light = capVoltage();


  if (light > 60)
  {
    digitalWrite(red1, HIGH);   // Turn on red
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 80)
  {
    digitalWrite(red2, HIGH);   // turn on red
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 120)
  {
    digitalWrite(amber1, HIGH); // Turn on anber
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 200)
  {
    digitalWrite(amber2, HIGH);   // Turn on amber
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 320)
  {
    digitalWrite(green1, HIGH);   // turn on green
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 580)
  {
    digitalWrite(green2, HIGH); // Turn on green
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  digitalWrite(red1, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(red2, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(amber1, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(amber2, LOW);
  Ndelay(SetDelay, 100);;
  digitalWrite(green1, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(green2, LOW);

}


// ---------------------------------------------------------------------------------------  
// One of two LED driver routines which switch on LEDs according to the voltage on the capacitor
// This one works in the opposite direction to the first
void Traffic2()
{
  int light = capVoltage();


  if (light > 60)
  {
    digitalWrite(10, HIGH);   // Turn on green
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 80)
  {
    digitalWrite(9, HIGH);   // turn on green
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 120)
  {
    digitalWrite(8, HIGH); // Turn on amber
    Ndelay(SetDelay, 100);; // wait for 100 milliseconds
  }
  if (light > 200)
  {
    digitalWrite(7, HIGH);   // Turn on amber
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 320)
  {
    digitalWrite(6, HIGH);   // turn on red
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  if (light > 580)
  {
    digitalWrite(5, HIGH); // Turn on red
    Ndelay(SetDelay, 100); // wait for 100 milliseconds
  }
  digitalWrite(10, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(9, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(8, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(7, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(6, LOW);
  Ndelay(SetDelay, 100);
  digitalWrite(5, LOW);

}


// ---------------------------------------------------------------------------------------  
// General delay routine which can switch between barco delay and standard for testing
void Ndelay(int delaytype, int period)
{
  if (delaytype == 0)
  {
    delay(period);
  }
  else
  {
    Narcoleptic.delay(period);
  }
}


// ---------------------------------------------------------------------------------------  
// Serial output routine runs during testing
void SerialSub(int delaytype, char stringy[20], int value)
{
  if (delaytype == 0)
  {
    Serial.print(stringy);
    Serial.println(value);
  }
 
}

Schematics

Diagram of breadboarded project
Runs the basic animation of the meter and the light display
Wall meter fritzing cxwxhx87fb
Schematic
Wall meter schematic rvhaay4ibi

Comments

Author

Default
tekyinblack2
  • 1 project
  • 1 follower

Additional contributors

Published on

August 2, 2020

Members who respect this project

Adambenz idgaerlrvn iluighmnkdAoh14gjmk p a1m5qvw5ig9wuavsxxbmpgtiyyo2maqgyq=s96 c

and 2 others

See similar projects
you might like

Similar projects you might like

YL-38 Moisture Meter (YL-69 Sensor)

by chocochunks

  • 13,349 views
  • 1 comment
  • 7 respects

ECOPlant - Plant Monitoring

Project in progress by PohWL

  • 7,341 views
  • 1 comment
  • 24 respects

Fire and Smoke Alarm

Project showcase by ayanfeoluwaadekanye1

  • 8,655 views
  • 13 comments
  • 11 respects

Stereo NeoPixel Ring VU Meter

Project showcase by ericBcreator

  • 115,939 views
  • 373 comments
  • 206 respects

LED Display with Arduino ADC and PWM

Project tutorial by Ian Etheridge

  • 9,896 views
  • 1 comment
  • 13 respects

Sigfox kWh Meter

Project tutorial by jassak

  • 9,406 views
  • 8 comments
  • 15 respects
Add projectSign up / Login