Art Deco Analog Clock
People who know me well also know that I really dig clocks and calendars. Trying to understand the Mayan calendar or the calculations of the eastern date are puzzles I just can't resist. Also clocks and not the ordinary ones have my special attention.
A friend pointed out that there was a contest here at instructables so I decide to participate. I'll build a clock as never seen before. Based on old displays and a very modern Arduino I intend to build a steampunk clock.
Wait and see
Getting Started
In order to build a clock one has to make some choices and divide the project in different parts of development.
- First get some old bakelite meters
- Design a case
- Choose functionality
- Program the control
Trough the Dutch ebay i purchased some old meters. A meter with a range from 0-60A, one from 0-10A and the third with a range of 2 mA. A meter with a 2mA range is simply directly controlled by a output of an arduino. The other two meters needed a current of 60/10Amps which is much to high to be electronic simple. So I dismounted the old pointersystem and replaced it with a servo. (step 2)
Now I only need a RTC, an Arduino, two servo's and a resistor. For the design to be steampunk or art-deco I added two vacuumtubes and a color OLED display. (step 3)
The case design is made by my daughter (see image). (step 4)
The program (step 5)
The functionality is basic. Aftre some time I'll add some easter eggs
like a easter egg on the display during easter and more... (step 6)
Making It Possible
Making it possible
A panel meter is an industrial instrument. Maybe it is a simple meter which can be directly driven by an arduino output. You can always hope it will. Most meters won't be so user-friendly. Especially meters of the V, W and A types a most common used for values which makes direct control by an Arduino impossible. Except the problem being far to great current s or voltages needed, most old meters (the prettiest) don't have a linear scale. I have overcome this problem by simply using a servo (photo)
Schematics
Hardware
The clock is build with just a few parts:
- An Arduino Uno
- Two servos
- A RTC 3231
- A OLED Display "just for fun"
- A resistor
- Two addressable multicolour LED's (cosmetic)
- Two vacuum tubes with connector (cosmetic)
RTC
The rtc is used to maintain the right time and for backup if no power is available. It is simply connected through I2C and +5V and GND.
Servos
With a servo it is quite simple to point a pointer to the right angle. Some calibration is required for zero and span in case of a linear scale. My scales weren't linear so I divided the scale in linear parts en made the software calculate the correct angle for the according time. This may result in an angle for every possibility for some scales. Most scales have a scientific base which can be calculated.
Resistor
One of the panel meters has a 2mA range. An analogue out can be programmed for a voltage output. By using a resistor this voltage is linear with the current through it. 5V and 220 ohms means a current of 2.27 mA. Beside the full scale current being to high, the resistor accuracy is bad (10 or 20%). I took a resistor slightly above the calculated value and let the software do the math.
Display
The main reason the display was included was because I'd like to try to make a cat-eye vacuum tube like design. The voltages needed for a antique cat-eye vacuum tube were not suitable for my project. So I decided to use higher tech solutions. The advantage of a display is it's versatile purpose. It can be used for monitoring, setup and for design purposes.
Tube lights
(not implemented yet)
The tube lights are a design virtue like the vacuum tubes themselves. They have no purpose whatsoever, the are just for cosmetic use. With the two addressable multicolour led's it's possible to give a colour effect to the vacuum tubes.
Switches
(not implemented yet)
At least two switches will be used to set-up the time and date.
Building a Case
My daugther had designed a case only by two words: steam punk and art-deco. From while to while she would send me some existing designs with the question wich i liked best. After some time she came up with a great design. It is simple and elegant. It has three blocks with a meter and for the show two vacuum tubes and a OLED color display. First I wanted to use a antique tube called cat-eye. I didn't because of the requered voltages (250 V).
Center Clock (CC)
The middle clock (CC) you can see in the cad drawing.
- CC Front 116 x 116 x 9 mm
- CC Back 116 x 116 x 9 mm
- CC Right 98 x 98 x 9 mm
- CC Left 98 x 98 x 9 mm
- CC Top 116 x 98 x 9 mm
- CC Bottom 116 x 98 x 9 mm
Right and Left Clock (RC and LC)
The right and left clock are exact mirrors of each other.
- LC/RC Front 100 x 91 x 9 mm
- LC/RC Top 82 x 62 x 9 mm
- LC Right/ RC Left 140 x 80 x 9 mm
- LC RRight / RC LLeft 40 x 80 x 9 mm
- LC/RC Back 100 x 91 x 9 mm
No bottom !!!
Footer
The footer consist of a board with the size 320 x 116 x 9 mm with a little foot underneath. Between the left and rightclock, i.e. under the middle clock I made space for two vacuum tubes with connector and an OLED display. My foot is in total 20 mm high, but that's just some wood leftover.
The Program
The program
Libraries
First we include the needed libraries
#include <adafruit_tftlcd.h> //OLED display #include <wire.h> //Needed for I2C #include <servo.h> //Servo #include <time.h> //Time lib #include "ds3231.h" //RTC #include <adafruit_gfx.h> //Display Graphics #include <adafruit_ssd1331.h> //SSD1331 lib #include <spi.h> //???
Definitions
The 10A and 60A servos are connected to the PWM outputs 5 and 6. By trial and error I calibrated the zero and span of the meters. These are defined as an angle like MA10_0, meaning the 10A meter and the zero angle. Be careful with trying because a servo has a much greater possible angle than a meter can move without damaging the pointer.
//The PWM outputs for the servo's #define MA10 5 //Output for 0-10A #define MA10_0 110 //Servo angle for 0 #define MA10_10 36 //Servo angle for 10 A #define A60 6 //Output for 0-60A #define A60_0 156 //Servo angle for 0 #define A60_60 63 //Servo angle for 60 A
The display uses 5 pins. You could use any pin but for performance reason these are the best
//Display pins #define sclk 13 #define mosi 11 #define cs 10 #define rst 9 #define dc 8
I defined some colours for the colour OLED display to use. Brown, Gold, Maroon and Olive are 16bit colours from the art-deco colour pallet.
// Color definitions #define BLACK 0x0000 #define BLUE 0x001F #define RED 0xF800 #define GREEN 0x07E0 #define CYAN 0x07FF #define MAGENTA 0xF81F #define YELLOW 0xEDE0 #define WHITE 0xFFFF #define ORANGE 0xFD20 #define BROWN 0x61C3 #define GOLD 0xE5C0 #define MAROON 0x7800 #define OLIVE 0x7BE0
Define the output for the 0-2mA meter.
//Define the output for the 0-2mA meter #define MA2 3
Declarations
First we declare the two servos
//Declare the two servos Servo servo_MA10; //10A meter Servo servo_A60; //60A meter
Than the OLED display
Declare the display Adafruit_SSD1331 display = Adafruit_SSD1331(cs, dc, rst);
Finally an bitmap logo. It is an monochrome bitmap. I used LCDassistent.EXE to convert a black and white picture to the HEX-code. Of course you could do this by hand if you have to much time on your hands.
//Logo bitmap (monochroom)<br>static const unsigned char PROGMEM logo[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF8, 0x1F, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x0F, 0xE0, 0x0F, 0xFF, 0xF8, 0x1F, 0xFF, 0x80, 0x01, 0xFF, 0x00, 0x00, 0x3F, 0xF8, 0x0F, 0xFF, 0xF0, 0x03, 0xFF, 0xC0, 0x01, 0xFF, 0x00, 0x00, 0x7F, 0xFC, 0x0F, 0xFF, 0xF0, 0x01, 0xFF, 0xC0, 0x03, 0xFF, 0x00, 0x00, 0xFF, 0x0E, 0x00, 0x3F, 0xF0, 0x00, 0xFF, 0xE0, 0x03, 0xFF, 0x0F, 0xE1, 0xFF, 0x0F, 0x00, 0x3F, 0xE0, 0x00, 0xFF, 0xE0, 0x07, 0xFF, 0x0F, 0xE1, 0xFF, 0x0F, 0x00, 0x7F, 0xE0, 0x00, 0xFF, 0xE0, 0x07, 0xFF, 0x0F, 0xE3, 0xFF, 0x0F, 0x80, 0x7F, 0xE0, 0x00, 0xFF, 0xE0, 0x0F, 0xFF, 0x0F, 0xE3, 0xFF, 0x0F, 0x80, 0xFF, 0xC0, 0x00, 0xFF, 0xC0, 0x0F, 0xFF, 0x0F, 0xE3, 0xFF, 0x0F, 0x80, 0xFF, 0xC0, 0x01, 0xFF, 0xC0, 0x1F, 0xFF, 0x0F, 0xE3, 0xFF, 0x0F, 0x81, 0xFF, 0xC0, 0x01, 0xFF, 0xC0, 0x1D, 0xFF, 0x0F, 0xE3, 0xFF, 0x0F, 0x81, 0xFF, 0x80, 0x01, 0xFF, 0xC0, 0x39, 0xFF, 0x00, 0x03, 0xFF, 0x0F, 0x81, 0xFF, 0x80, 0x01, 0xFF, 0x80, 0x39, 0xFF, 0x00, 0x03, 0xFF, 0x0F, 0x83, 0xFF, 0x80, 0x03, 0xFF, 0x80, 0x71, 0xFF, 0x00, 0x03, 0xFF, 0x0F, 0x83, 0xFF, 0x80, 0x03, 0xFF, 0x00, 0x71, 0xFF, 0x0F, 0xE3, 0xFF, 0x0F, 0x83, 0xFF, 0x80, 0x07, 0xFF, 0x00, 0xE1, 0xFF, 0x0F, 0xE1, 0xFF, 0x0F, 0x03, 0xFF, 0x80, 0x07, 0xFE, 0x01, 0xE1, 0xFF, 0xCF, 0xE1, 0xFF, 0x0F, 0x03, 0xFF, 0x00, 0x0F, 0xFE, 0x01, 0xFF, 0xFF, 0xCF, 0xE0, 0xFF, 0x0E, 0x07, 0xFF, 0x00, 0x0F, 0xFC, 0x03, 0xFF, 0xFF, 0x0F, 0xE0, 0x7F, 0x0C, 0x07, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x01, 0xFF, 0x0F, 0xE0, 0x3F, 0xF8, 0x07, 0xFF, 0x00, 0x1F, 0xFF, 0xF0, 0x01, 0xFF, 0x0F, 0xE0, 0x0F, 0xE0, 0x07, 0xFF, 0x00, 0x3F, 0xFF, 0xF0, 0x01, 0xFF, 0x0F, 0xE0, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
Variables
The time variable is a struct type (record in java and pascal). An object with several variables of various types. It is declared in the time library and ready for use after including time.h.
uint8_t time[8]; struct ts t; //Struct is een record //t.hour = hours //t.min = minutes //t.sec = seconds
Some variables which are used to finally show the time and displays.
int minAngle = 0; // servo position in degrees int hrAngle = 0; int hr10Angle = 0; int uren; int uren10; int min; int i = 0; int dir = 1;
Set up
First set-up lines are pretty standard to establish communication through the serial connection at a rate of 9600 Baud. I use this connection mostly to debug the code.
void setup()<br>{ Serial.begin(9600); Serial.println("Analog clock by 24/07");
Then the wire library is activated. If I'm not mistaking this initiates the I2C communication.
Wire.begin();
Then the RTC is initialized. I used 3231 IC because of the accuracy.
//Initialize the RTC<br> DS3231_init(DS3231_INTCN);
Next we make the pin 3 an output and attach both servos
//make an output of mA2<br> pinMode(MA2, OUTPUT); servo_MA10.attach(MA10); servo_A60.attach(A60);
At last I fired up the OLED display and gave it an art-deco background colour.
display.begin();<br> display.fillScreen(MAROON); }
Loop
In the loop we first get the time and give the time variables their corresponding value. Than we write the time values to the serial connection to check the time to be correct.
void loop()<br>{ //Get the time in the struct DS3231_get(&t); //Get the hours and minutes (Dutch) uren = t.hour; min = t.min; //Write time to serial Serial.print(uren); Serial.print(":"); Serial.print(min); Serial.print(" - ");
Depending on the value of the used resistor there are three values of the output. Between 0 and 10 hours the meter will show 0, so the output shall be 0. Between 10 and 20 the output must be according to the 1mA value, i.e. 2.2V. Theoretically it would be 112, but the resistor has an 10 % range and every meter has an error. So I determined the two values by trial and error.
//Determine output MA2<br> //hr10Angle is an analog outputvalue (0-255) //hr10Angle = (176 / 24) * uren; if (uren < 10) { hr10Angle = 0; } if ((uren > 9) && (uren < 20)){ hr10Angle = 88; } if (uren > 19) { hr10Angle = 176; } analogWrite(MA2, hr10Angle); //Serial.print("hr10Angle: "); //Serial.print(hr10Angle); //Serial.print(" - ");
In fact I check for every value of the meter. With a linear meter you could do different but in my case with a none scientific scale one has to, to read the right time. After that there is a safeguard. If there is any error in the program causing the angle out of the range, the safeguard will bring it back in range to prevent damage to the meter.
//Determine angle of MA10<br> //Get rid of the 10 or 20 by mod function uren10 = uren % 10; if (uren10 == 0) { hrAngle = 109; } if (uren10 == 1) { hrAngle = 105; } if (uren10 == 2) { hrAngle = 100; } if (uren10 == 3) { hrAngle = 93; } if (uren10 == 4) { hrAngle = 85; } if (uren10 == 5) { hrAngle = 77; } if (uren10 == 6) { hrAngle = 65; } if (uren10 == 7) { hrAngle = 58; } if (uren10 == 8) { hrAngle = 47; } if (uren10 == 9) { hrAngle = 40; } if (uren10 == 10) { hrAngle = 36; } //Safeguard of the meter if ((minAngle > 109) || (minAngle < 36)) { minAngle = 77; } //hrAngle = (MA10_10 - MA10_0)/10.0 * uren + MA10_0; //hrAngle = 40; servo_MA10.write(hrAngle); //Serial.print("hrAngle: "); //Serial.print(hrAngle); //Serial.print(" - ");
The 60 A has several ranges. Below 10 minutes every value is calculated separately. Above the 10a there are three semi-linear ranges. After calculation there is a safeguard again to prevent damage of the meter
//Determine angle of A60<br> //Below 10 minutes if (min < 2) { minAngle = 154; } if (min == 2) { minAngle = 153; } if ((min == 3) || (min == 4)) { minAngle = 152; } if ((min == 5) || (min == 6)) { minAngle = 151; } if (min == 7) { minAngle = 150; } if (min == 8) { minAngle = 149; } if (min == 9) { minAngle = 147; } //10 tot 30 minuten (145 - 105) if ((min > 9) && (min < 30)) { minAngle = (105 - 145)/20.0 * (min - 10) + 145; } //30 tot 40 minuten (105 - 87) if ((min > 29) && (min < 40)) { minAngle = (87 - 105)/10.0 * (min - 30) + 105; } //40 tot 60 minuten (86 - 60) if ((min > 39) && (min < 60)) { minAngle = (60 - 86)/20.0 * (min - 40) + 86; } //Beveiliging van de meter if ((minAngle > 154) || (minAngle < 60)) { minAngle = 105; } //minAngle = 86; //calibratie servo_A60.write(minAngle); //Serial.print("minAngle: "); //Serial.print(minAngle);
The next line will display the logo on the OLED display in the colour GOLD, starting in 0,0 and with a width of 96 and a height of 32.
display.drawBitmap(0, 0, logo, 96 , 32, GOLD);
The next block of code is used to write the time to the display. Every loop first I draw a rectangle in the background colour and then I write the new time. The if statements are used to show leading zero's
display.setTextSize(1);<br> display.setTextColor(WHITE); display.fillRect(0, 32, 96, 16, MAROON); display.setCursor(32,32); if (uren < 10) { display.print("0"); } display.print(uren); display.print(":"); if (min < 10) { display.print("0"); } display.print(min); display.print(":"); if (t.sec < 10) { display.print("0"); } display.print(t.sec);
The last block of code displays a cat-eye vacuum tube like animation. It are simply two rectangles drawn according a counter an a direction bit.
if (dir == 1) {<br> i++; } else { i--; } if (i == 6) { dir = -1; } if (i == 0) { dir = 1; } display.fillRect(0, 44, 96, 16, MAROON); display.fillRect(48 - i * 8, 44, i * 16, 16, OLIVE);
And standard every loop ends with these statements.
Serial.println();<br> delay(1000); }
Did You Miss Me
Of course, I could be a politician, I promised more than I gave. It is all for a good reason. The main reason being my holiday next week and the end date of the clock contest 30 of July.
Easter Eggs
This project screams for Easter eggs to be included. For now I have some small ideas but the sky is the limit.
- One vacuum-tube green and the other red during Christmas
- An Easter egg in the display during Easter
- Fireworks on new years eve
- And all the ideas I'll get from you