A Circular Binary Clock

by JohnThinger in Circuits > Arduino

1696 Views, 4 Favorites, 0 Comments

A Circular Binary Clock

A Circular Binary Clock
001_Render_001.jpg

In a previous instructable, I have presented a concept for making a “linear” binary clock from a standard WS2812 LED strip and an ATtiny microprocessor. The design of that clock was not that great… …which is why I made a new version using an LED ring.

This clock will show the hours, minutes, and seconds of the current time in a binary style on the 16 LEDs of the ring - 4 representing the hours (blue), 6 representing the minutes (pink) and 6 representing the seconds (green). You will get the correct time, if you just add the single 2x values of each LED. As an example, it is 11:36:49 a.m. in the picture above. Hence, the clock lights the blue LEDs “0”, “1”, and “3” for the hours (= 20 + 21 + 23 = 1 + 2 + 8 = 11). It will also light the pink LEDs “2” and “5” for the minutes (= 22 + 25 = 4 + 32 = 36), and the green LEDs “0”, “4”, and “5” (= 20 + 24 + 25 = 1 + 16 + 32 = 49).

Simply put, a binary clock is not making life easier, but it trains the brains…  

Supplies

002_Supplies.jpg

For making this clock, you will need:

  • 1x LED-Ring with 16 WS2812 LEDs (inner diameter: 54 mm; outer diameter: 68 mm)
  • 1x ATtiny85 (20U - DIP-8 version with 5V)
  • 1x DS1302 (or DS1307) RTC chip (DIP-8 version)
  • 1x 32.768 kHz Crystal Oscillator with 6pF Load Capacitance
  • 1x PCB Prototyping Board (Perfboard): 14 x 20 Holes // 40 x 60 mm
  • 2x 8 Pin DIP Socket
  • 1x Battery Clip (diameter 20 mm)
  • 1x CR2032 battery
  • 1x 2-Pin Terminal block (optional)
  • 1x 47 µF Capacitor (optional)
  • 1x Barrel Jack Socket 5.5 mm/2.1 mm (w/ M7 Nut)
  • 1x 5V DC Power supply or USB to Barrel Jack Cable
  • 1x Breadboard (optional)
  • Some 0.14…0.25 mm² Cable
  • 1x USP Programmer or Arduino Uno + Some Jumper Cables
  • 1x Sheet of Tracing Paper (i.e. “Transparent Paper” for copying drawings)
  • 4x M2 Screw and 4x M2 Nut
  • 3x M3x25 Screw and 3x M3 Nut
  • 1x 3D Printer and some filament, or a Printing Service, or some Self-made Case
Please note: There are different sizes of the LED rings with 16 LEDs out there. My housing design uses the version with an outside diameter of 68 mm (and an inner diameter of 54 mm). The rings with an outer diameter of 70 mm should fit, too. There are, however, much smaller sizes like the original Adafruit or NeoPixel versions that have an outer diameter of 44.5 mm. Those versions will not fit here!
Please note: The DS1302 need a 32.768 kHz crystal oscillator that expects a load capacitance of 6 pF (which is included inside the DS1302). Most crystals with 32.768 have a load capacitance of 12.5 pF which might cause a drift of a few seconds a day. You may use these crystals if your ok with the drift, or you might add extra capacitors as explained here. In the end, the DS1302 and a (cheap) crystal will show a drift of a few seconds a day anyway. If you want a more precise RTC, maybe take a look at the DS3231.

A Test-Circuit

003_Test_Circuit.jpg

This is an optional step for testing all the parts before creating the “real” circuit on the perfboard. So, if you have a breadboard around, just set it up as shown in the attached picture. Be careful with the orientation marks of the ATtiny and the DS130x. As shown in the picture, the DS1302 is flipped upside down.

If you have put together everything and connect the 5V power supply, you will see… …nothing. Because the ATtiny is “empty” (or contains an old program). This will be changed in the next step.

Programming the ATtiny

004_Programming.jpg

The procedure for installing the ATtiny libraries in the Arduino IDE is described here and here. These tutorials also show how to program an ATtiny using an Arduino Uno. As mentioned in the second tutorial, the capacitor between the RESET pin of the Arduino Uno and GND is kind of optional. In my programming setup – as shown in the attached circuit diagram – I never used it and never had any trouble.

The code for controlling the LED and the RTC is attached to this step. It is based on a DS1302 RTC chip, so if you use another RTC, please adapt the corresponding lines since you might need another library. As in my previous clock version, I am loading the time of the host computer during the programming process to the ATtiny chip. If the microcontroller is powered-up running, it is asking the DS1302 for its stored time. If that chip has a time that is earlier than what is stored in the ATtiny from the programming process (= “compile time”), it will use that time. This is done in a few lines in the setup() routine:

if(now < compileTime){
compileTime += 120;
rtc.SetDateTime(compileTime);
}

As you can see, the set the RTC time to the compile time, but it adds 120 seconds. This is because compiling will take some seconds after the time was read from the PCs clock and you will need some seconds to grab the ATtiny from the Arduino Uno programming circuit, place it in the target circuit and start it together with the RTC. So, I assume 120 seconds are ok for this sequence, but you might want to adapt it.

The rest of the code is fairly simple. The loop() function just reads the current hours, minutes and seconds from the RTC and sets the LEDs accordingly. The basic colors of the LEDs are “blue”, “pink” and “yellowish green”. This can be changed in the following three lines:

color = (coeff << 16) + (40 << 8);                            // Seconds --> Green.
color = ((coeff*2 + 40) << 16) + (coeff/3);                   // Minutes --> Pink.
color = (((unsigned long)(coeff*1.5)) << 8) + (coeff + 40); // Hours --> Blue.

The calculations of the colors are, however, a bit tricky, as the clock implements a “glow” effect. Hence, the colors in the three lines above do not just consist of constant values, but instead they are based on a coefficient which is calculated with the lines

coeff = abs(((count + LOOP_MAX/6 * k) % LOOP_MAX) - LOOP_MAX/2); // For seconds and minutes.
coeff = abs(((count + LOOP_MAX/4 * k) % LOOP_MAX) - LOOP_MAX/2); // For hours.

The idea behind this is that each LED has a slightly different color which is cycled through the seconds, minutes and hour LEDs every 20 ms (see delay at the end of the loop() routine). Thus, if you are not satisfied with the colors, you can try different things:

  • Rewrite the entire color coding, and – as an example – use plain constant color codes.
  • Change the speed of the glow by changing the length of the delay() command at the end of the loop() routine.
  • Change the #define LOOP_MAX at the beginning of the code for making the “glow spectrum” wider or narrower.

Finally, there are two more useful #defines at the beginning of the code:

  • #define LED_BRIGHTNESS: This value is set to 127. The maximum brightness of the LEDs is 255, so all the LEDs are technically only half as bright as possible. If your device appears to be too dark, just increase this value. Personally, I found higher values not satisfying as the LEDs become really bright and therefore the colors do not look that saturated anymore.
  • #define PIXEL_SHIFT: The connector pins of your specific LED ring might be in positions that make it unsuitable to rotate the ring in a way that the first LED matches the “0” of the seconds section of the housing. Hence, you may rotate the LED ring in any position that feels ok for you and then simply “shift” the start number (i.e. first LED in terms of the numbering on the ring) to match the code. In my case, the 13th LED of the ring is the one that matches the first LED of the seconds.

So far, you have successfully programmed the ATtiny. If you place it in the test circuit and supply 5V, the LEDs should start counting the time. If you are working with the test circuit, please check whether the DS1302 is keeping the time by switching on and off the device. If the LEDs with the same state after each start, probably the wiring to the battery clip is wrong or the orientation of the battery in the clip is wrong of the battery is empty. Just check everything before soldering the components.

The Perfboard Circuit

005_Diagram_a.jpg
005_Front.JPEG
005_Back.JPEG
005_Routing.jpg

Firstly, you must cut the perfboard. It has 14 x 20 holes, but we need only 14 x 12, so either use a small (metal) saw to cut it, or - which is much easier - use a cutter knife for marking a split line and then break it as shown here. Using the edge of a table while breaking it after cutting the split line makes is even more easy than breaking it in air.

Afterwards, you can start soldering. The attached diagrams show a way how to solder the parts to a perfboard. It is kind of a suggestion, because the dimensions of the battery clip may differ, so there is a chance you need to rearrange some connections a bit. Furthermore, some parts are optional:

  1. Block Terminal. The block terminal might ease the assembly of the clock. It is useful, in particular, if you have already mounted the barrel jack connector to the housing and you want to remove the circuit again while leaving the barrel jack connector mounted. In fact, however, it is probably easier to unmount the barrel jack connector from the housing than unlocking the wires from the terminal, so you may omit the terminal and solder wires directly to the board.
  2. Capacitor: A capacitor directly before the microcontroller and the RTC shields them from any disturbances from the power supply or changing driven loads. As most supplies are already stabilized and we do not have motors or similar elements here, you can probably go without a capacitor.
  3. DIP-8 Sockets: I recommend to use DIP-8 sockets under both the ATtiny and the RTC chip. For the ATtiny this is strongly mandatory: you are expected to unmount and remount the ATtiny each time you are programming it, so you will be stuck if you solder it to the board directly. This is different for the real time clock chip: you do not need to de-/re-install it at any time, so you can solder it directly to the board. However, if you intend to exchange or re-use it – or if anything is wrong with your soldering/wiring, it can be easier (and cheaper) to use a socket here, too.

For the soldering, I started by soldering the “inactive” elements, i.e. the two sockets, the terminal block and the battery clip (with the battery being de-installed!). Then, I soldered the tracks which are visible as darker lines/areas in the perfboard sketches. The tracks all go to the backside of the board. If you want to use the capacitor, make sure to leave the required holes open. After the tracks, add the capacitor and the crystal as shown in the diagram.

Please note: There are two sketches of the plain perfboard in the circuit diagram highlighting the solder tracks. One shows the perfboard from its front, one from the back. The backside is where you will make the solder tracks. Hence, that view is mirrored to the other one, so it will be exactly what you see during soldering.

After you have finished the circuit, solder the wires to the LED ring and to the barrel jack connector.

Then, install the DS130x and the battery. (Re-)program the ATtiny again, so it grabs the current time from your PC. Place the ATtiny to its socket, check the orientation of the RTC chip and the ATtiny again and then power-up the device. The LEDs should be blinking and the time should be stored – ad buffered – by the RTC and the battery now.

All you need now is a fancy case which is provided with the next step.

Building the Case

006_Case_Printing.jpg
006_Assembly_1.jpg
006_Assembly_2.jpg
006_Assembly_3_4.jpg
006_Assembly_5a.jpg
006_Assembly_5b.jpg
006_Assembly_6.jpg
006_Assembly_8.jpg
006_Assembly_9.jpg

Building the Case

Print the parts “Front.stl”, “Middle.stl”, “Back.stl”, “Lid.stl”, and “2x.stl” (0.2 mm layer height, standard 0.4 mm nozzle will be ok; printing positions are given in one of the pictures). The “2x.stl” is optional. If you print it, use a different filament as used with the “Front.stl”.

The proposed assembly sequence is as follows:

  1. Use two M2 screws and two M2 nuts to fix the perfboard to the “Back” part. The active elements should face to front of the clock). Make sure that the LED-Ring is on the front side of the back and the barrel jack socket is on the backside.
  2. Screw the barrel jack socket to the “Lid” using the M7 nut that comes with the connector. If your design includes the terminal block, you can do this step separately from the first step. In this case, it is now time to connect the 5V and ground wires from the barrel jack socket to the terminal.
  3. Fix the LED ring to the “Back” part using two M2 screws and two M2 nuts. Most rings will have four holes, but fixing them with two screws (preferably using two diagonal holes) will be enough.
  4. Plug-in the power supply and make a final test. At this time, it is still easy to unmount/remount the ATtiny.
  5. Grab a sheet of tracing paper and place the “Middle” part on it. Mark the inner and outer loops of this part on the paper twice as shown in the pictures. Then use a knife and cut these pieces from the paper. There is no need to cut these two rings precisely – rough cut will do.
  6. Now, put the three M3x25 screws through the “Front” part. Then turn it around and place the two rings from the tracing paper on it. Why do we use that paper? Well, the single LEDs give a very bright and direct light, but we need a diffuse light here that is evenly distributed to the single digits. Hence, the paper is used as a “diffuser”. If your paper is too thing (“too transparent”) use some more layers. If it too dense (= blocking to much light), use only one layer or increase the LED_BRIGHTNESS value in the code.
  7. Place the “Middle” part on the “Front” part. Make sure to get the orientation right, so the bridges do not interfere with the digits on the front side. Also, make sure that the gaps in the bridges are facing to the back.
  8. Push the M3x25 screws from the "Front" part through the "Middle" and the "Back" parts and finally through the "Lid". Tighten the screws using the three M3 nuts.
  9. Use some glue to fix the “2” and “x”.
  10. Done!

Final Thoughts

Final.jpg

The “circular” binary clock looks much better than my previous “linear” design. A small, remaining problem is the drift of the real-time chip. According to many sources, this can be improved by using a DS3231 instead of the DS1302/1307. Another way would be to add some buttons on the housing for adjusting the time. Personally, I am fine with a drift of some seconds a day, as you might want to reprogram the clock anyway at certain dates, e.g. when changing from daylight saving time to winter time.

I hope you have enjoyed this tutorial and always remember: there are only 10 kinds of people in the world, those who understand binary and those who don't.

Addendum

Addendum_01.jpg
Addendum_02.jpg
Addendum_03.jpg
Addendum_04.jpg
FrontSide.jpg
BackSide.jpg

My DS1302 clock was constantly going too fast with a deviation of about 1 minutes per week. This is not too bad, but the way for readjusting the time is not very convenient as you have to open the clock, unmount the ATtiny, reprogram it, and re-assemble it again.

The constant drift is probably caused by the crystal. The DS1302 relies on a crystal that is made for a capacitive load of 6 pF. This capacitive load is integrated in the DS1302, so you do not need to install any capacitors in your circuit. Crystals made for a 6 pF load, however, are not very common. You will find them in good electronics stores, but if you look around ebay and AliExpress, you will mainly find crystals for other loads at this size. Well, I made the “mistake” to buy my 6 pF crystals from a no-name company, and while they said to be for 6 pF loads, they probably are not. I guess they are either not very precise or they are simply made for a different load factor. In this case you can add very small additional capacitors between each leg of the crystal and ground to increase the capacitive load. It is not possible though to calculate the proper values for these additional capacitors if you do not really know what the crystal on hand was made for. Thus, you will probably have no problem with the original design if you have a good 6 pF capacitor, but I personally ended up with four options:

  1. Just keep it as it is and live with the drift.
  2. Get a new (real 6pF) crystal.
  3. Do some trial and error for additive capacitors.
  4. Come up with an alternative design.

I was a bit unhappy anyway, because the original clock has no option for adjusting the time without re-programming it. Hence, I went for the fourth option resulting in the modified design that is shown the attached files and pictures. It uses a D1307 instead of a DS1302 which results in the following advantages:

  • While the overall precision of both RTC chips is mostly the same, the DS1307 requires a 12.5 pF crystal. These are a bit easier to get than the 6 pF crystals.
  • The DS1307 uses the I2C bus for communicating with the microcontroller. This bus needs only two wires. The DS1302 needed 3 wires.
  • As a consequence, only two of the six data pins of ATtiny are used by the RTC and one by the LED communication. One pin acts as a reset pin (as long as fuses are not set differently), leaving two pins for other applications. This is perfect for attaching two buttons for adjusting the time in forward and backward direction.

If you want to apply the changes, you need:

  • A new printed “Lid” for the backside of the clock (Lid_Buttons.stl).
  • A DS1307 DIP-8 RTC chip.
  • A 12.5 pF crystal.
  • Two 4.7 kOhm resistors (for pulling up the I2C lines).
  • Two push buttons with M7 threads to be attached to the backside.
  • The new program attached to this step. For compiling this program, please install the TinyWireM, TimeLib, and TinyDS1307 libraries first.

All other components are identical to the original design.

Please note: Some of the libraries mentioned above might not be available from the library manager of the Arduino IDE. In this case, please download them from GitHub and copy them manually to your library folder.
Please note: The data transfer between the ATtiny and the DS1307 will fail as long as there is no battery attached to the circuit additionally to the 5V power supply. In contrast, the DS1302 operates properly without a battery whiile only 5V are applied. Hence, add the battery before starting the clock even at the first time.

I hope you are enjoying either the original or this modified design!

Another Combination...

Alternate_Design_With_DS1302.jpg

After publishing this instructable, there has been a request for combining the DS1302 with its 6 pF crystal and the two buttons. This is technically possible, but there is a tiny obstacle: the DS1302 needs three lines for communicating with the Attiny while the DS1307 needs only two. As a result, there are only two free pins after connecting the LED ring and the 1302 to the ATtiny... ...and one of these two pins is not really "free" as it is the reset pin. The reset pin can be reconfigured to be a normal I/O pin, but this makes programming the ATtiny much more inconvinient.

However, there is a workaround: both buttons can be connected to one pin using different resistors. As proposed in the attached sketch, the reading pin should be connected to one side of each button. Additionally, it should connected to ground through a 100 kOhm resistor, so it does not read any values if no button is pressed. The buttons themselves should be connected to 5 V on the other side through different resistors. I propose to use a 10 kOhm resistor on one button and 100 kOhm on the other one. This way, the input pin will read different values if one of the buttons is pressed. You will find the handling of the buttons in the attached ino file in lines 87 to 99. If you want to use other resistors, please adapt the threshold values in the code accordingly. Please make sure that the resistors are not too small (i.e. > 1 kOhm) for not getting high currents at the input pin. If you have an Arduino Nano or Uno around, you can connect the buttons in the same way to an analog input pin (e.g. A2) and use the following code to read the values from the serial monitor of the Arduino IDE:

#define PIN_BTN       A2

void setup(){
 pinMode(PIN_BTN, INPUT);
 Serial.begin(9600);
}

void loop(){
 int buttonAnalogState = analogRead(A0);
 Serial.println(buttonAnalogState);
 delay(100);
}


More information on the wiring of multiple switches to one pin can be found here.

I hope you are enjoying either the original, the modified, or this "combined" design!