BBC micro:bit
Binary Clock

Introduction

This project uses 2 shift registers to display the time in binary form on 16 LEDs. The RTC is the DS3231 which works well with the micro:bit.

We need at least 4 place values for the hour (12hr format), 6 place values for the minutes, and 6 for the seconds. With 16 LEDs, you could do,

Binary Time

So the following pattern...

Binary Time

... makes the time, 8+2 = 10 hours, 16 + 4 + 1 = 21 minutes, 4+2+1 = 7 seconds. You add up the numbers above each of the lights that is on and you get your time values.

Circuit

Binary Clock Circuit

You also need to make some connections to the RTC.

  • Connect GND on the breakout to GND on the microbit.
  • Connect VCC to 3V.
  • Connect SDA to pin 20.
  • Connect SCL to pin 19.

Sparkfun do some breakout boards for the ickle surface mount version of the 74HC595 shift register. It has its pins set up in such a way that you can easily chain the shift registers and has the output pins tidily in a row for you. I used these, some bargraphs and some 330 Ohm resistor networks to throw together a test circuit more quickly. It looked like this,

Binary Clock Circuit

Paying close attention to the image and using the system described above, the time shown is 4:35 and 23 seconds. Of the first 4 LEDs, only the second is lit. According to the diagram above, this makes the hour 4. The minutes part is the next 4 LEDs of the left bar graph and the 3rd and 4th LEDs of the second bargraph. 2 LEDs at each end of the bargraphs are not connected. This shows LEDs on for 32 + 2 + 1, making 35. For the seconds, the LEDs show 16 + 4 + 2 + 1 = 23.

OK, so it doesn't look great and the split display makes the time hard to read. It does prove the concept though and is another challenge met.

Programming

Here's the code for all of this.

from microbit import *

def bcd2dec(bcd):
    return (((bcd & 0xf0) >> 4) * 10 + (bcd & 0x0f))

def dec2bcd(dec):
    tens, units = divmod(dec, 10)
    return (tens << 4) + units
    
def get_time():
    i2c.write(addr, b'\x00', repeat=False)
    buf = i2c.read(addr, 7, repeat=False)
    ss = bcd2dec(buf[0])
    mm = bcd2dec(buf[1])
    if buf[2] & 0x40:
        hh = bcd2dec(buf[2] & 0x1f)
        if buf[2] & 0x20:
            hh += 12
    else:
        hh = bcd2dec(buf[2])
    wday = buf[3]
    DD = bcd2dec(buf[4])
    MM = bcd2dec(buf[5] & 0x1f)
    YY = bcd2dec(buf[6])+2000
    return [hh,mm,ss,YY,MM,DD,wday]

def ShiftOut(value, clck, dta):
    bits = [value >> i & 1 for i in range(7,-1,-1)]
    for i in range(7,-1,-1):
        dta.write_digital(bits[i])
        clck.write_digital(1)
        clck.write_digital(0)

def ShowTime():
    tm = get_time()
    if tm[0]>12:
        tm[0] -= 12
    # bitshift the digits into place    
    lbyte = (tm[0]<<4) + (tm[1]>>2)
    rbyte = ((tm[1] & 3)<<6) + tm[2]
    ShiftOut(rbyte, clockpin, datapin)
    sleep(1)
    ShiftOut(lbyte, clockpin, datapin)
    latchpin.write_digital(1)
    latchpin.write_digital(0)

        
addr = 0x68
buf = bytearray(7)
datapin = pin13
clockpin = pin14
latchpin = pin15
datapin.write_digital(0)
clockpin.write_digital(0)
latchpin.write_digital(0)

while True:
    ShowTime()
    sleep(500)

Up until the ShowTime function, this is code that has been used before in other projects. The ShiftOut function is a more programmer-friendly implementation. The hard work is done in two neat lines, the ones that assign values to lbyte and rbyte. The left and right shift operators move the binary place values of the number right or left the number of places specified. To get only the rightmost 2 bits of the minutes, for rbyte, an AND operation is used to mask out all but the first 2 bits.

Challenges

  1. With a buzzer, you can make your clock chime at regular intervals and code for an alarm.
  2. With a few buttons or using the built-in buttons, you can make it possible for a user to set the time or the alarms.
  3. With the buttons, you can add some nice light shows or make more sensible use of power by requiring a button press to see the time.