Four Letter Clock
This is a clock project making use of a range of parts that work quite nicely by themselves, but need a bit of work to combine. Having successfully got a few RTCs to work, I wanted to find a nice easy way for a user to set the time on the chip. Since I had a few spaces left on my Pimoroni pHAT Stack, I thought I'd add a Touch pHAT into the project and write the code to allow the user to set the time.
The photograph shows the following pieces of kit,
- 4tronix Bit:2:Pi
- Pimoroni pHAT Stack
- Adafruit PiRTC PCF8523 Real Time Clock
- Pimoroni Four Letter pHAT
- Pimoroni Touch pHAT
See the separate pages on these accessories to see how to connect them. I doubt that I would be so keen to connect them without the pHAT Stack and Bit:2:Pi.
This is a pretty long program. I've flattened the code a fair bit to allow all of this to squeeze into the micro:bit. I have left the full font in place for the moment. The next feature on the clock is likely to end this luxury, along with the comments.
The main problem to face in this project is caused by the Touch pHAT. The LEDs are not connected in a way that allows us to take advantage of the LED tracking feature of the CAP1166 chip. In order to replicate that functionality, we have to turn on the LED when we read that a pad is pressed. For this to work, there can't be long delays between readings being taken from the chip.
The second Touch pHAT issue is how easy it is to trigger a press. When you are setting the time, you want to avoid accidental presses causing too much damage. I decided to use a 3 second press of one pad to enter the time setting mode and a similar 3 second press to confirm the new time.
Pads A and B are used to set the hours, C and D to set the minutes.
from microbit import * # four letter pHAT fbuf = bytearray(*16) # font - each character is 2 bytes - index is ASCII - 32 font = [0x0,0x6,0x220,0x12ce,0x12ed,0xc24,0x235d,0x400,0x2400,0x900, 0x3fc0,0x12c0,0x800,0xc0,0x0,0xc00,0xc3f,0x6,0xdb,0x8f,0xe6, 0x2069,0xfd,0x7,0xff,0xef,0x1200,0xa00,0x2400,0xc8,0x900, 0x1083,0x2bb,0xf7,0x128f,0x39,0x120f,0xf9,0x71,0xbd,0xf6, 0x1200,0x1e,0x2470,0x38,0x536,0x2136,0x3f,0xf3,0x203f,0x20f3, 0xed,0x1201,0x3e,0xc30,0x2836,0x2d00,0x1500,0xc09,0x39,0x2100, 0xf,0xc03,0x8,0x100,0x1058,0x2078,0xd8,0x88e,0x858,0x71,0x48e, 0x1070,0x1000,0xe,0x3600,0x30,0x10d4,0x1050,0xdc,0x170,0x486, 0x50,0x2088,0x78,0x1c,0x2004,0x2814,0x28c0,0x200c,0x848,0x949, 0x1200,0x2489,0x520] # update flp def flshow(): data = bytearray() + fbuf i2c.write(0x70,data) # set a digit def set_dig(v,p): vbin = font[ord(v)-32] fbuf[p*2] = vbin & 0xFF fbuf[p*2+1] = (vbin >> 8) & 0xFF # print a 4 letter string def set_str(s): for i,ch in enumerate(s): set_dig(ch,i) # display time def show_time(h,m): hrs = str(h) if h<10: hrs = "0"+hrs mins = str(m) if m<10: mins = "0"+mins set_str(hrs+mins) fbuf |= (1 << 6) flshow() # RTC functions def bcd2bin(value): return (value or 0) - 6 * ((value or 0) >> 4) def bin2bcd(value): return (value or 0) + 6 * ((value or 0) // 10) def get_time(): i2c.write(0x68, b'\x03') buf = i2c.read(0x68, 7) m = bcd2bin(buf) h = bcd2bin(buf) return m,h # touch functions def w_i2c(a,r,v): i2c.write(a, bytes([r,v])) # get the touch input and light the correct LEDs def get_tou(): w_i2c(0x2c,0,0) i2c.write(0x2c,b'\x03') d = i2c.read(0x2c,1) p = sum(1<<(5-i) for i in range(6) if d>>i&1) i2c.write(0x2c,b'\x74') l = i2c.read(0x2c,1) if p!=l: w_i2c(0x2c,0x74,p) return [p >> i & 1 for i in range(5,-1,-1)] # set time mode def set_tim(): i2c.write(0x70,b'\x83') #blink mm,hh = get_time() tset = True lastr = 0 while tset: a = get_tou() r = int("".join(str(i) for i in a), 2) if lastr!=r: if a: hh -= 1 if a: hh += 1 if a: mm -= 1 if a: mm += 1 if hh>23: hh=0 if hh<0: hh=23 if mm>59: mm=0 if mm<0: mm = 59 # set time held for 3 seconds if a: tst = running_time() while a==1: a = get_tou() sleep(5) if running_time()-tst>3000: tset = False show_time(hh,mm) lastr = r #set the time i2c.write(0x68, b'\x03') buf = i2c.read(0x68, 7) tm = [0x03] + [buf,bin2bcd(mm),bin2bcd(hh),buf,buf,buf,buf] i2c.write(0x68,bytes(tm)) i2c.write(0x70,b'\x81') #no blink # init flp i2c.write(0x70,b'\x21') i2c.write(0x70,b'\x81') #no blink i2c.write(0x70,b'\xFF') #brightness full # init touch w_i2c(0x2C,0x72,0) sleep(1) w_i2c(0x2C,0x41,0x30) sleep(1) last = 0 while True: a = get_tou() # check for user interaction if a==1: tmr = running_time() # wait for release while a==1: a = get_tou() sleep(5) # if it took more than 3s, set time if running_time()-tmr>3000: set_tim() # update if more than a second has passed if running_time()-last>1000: mm,hh = get_time() show_time(hh,mm) last = running_time() sleep(5)
Review & Next Steps
The purpose of this project was really to add another accessory to the pHAT Stack and to get the micro:bit controlling everything without running out of memory. Although there is a fair bit of trimming to cut out features that aren't being used, there's still a lot of scope for cutting things down.
The font is a luxury. It could be cut down to the digits and upper case characters without a great loss of future functionality. That would save a decent amount of space and would not be too hard to encode. That would leave space for messages as well as the time display.
The code for the time setting could trimmed down. Alternatively, a Piano or Drum HAT could be used, where the LEDs do the tracking. All of the touch devices give you the sensor data as a byte, which I've turned into a list in this program. In single touch mode, the byte will equal a binary place value or 0 if nothing is touched. Checking for those numbers instead of turning the reading to a list will save some lines of code.
After a little trimming, adding an alarm feature would be good. It's easy to run a few jumpers to a buzzer from the Bit:2:Pi or the spare headers on the pHAT Stack. A change to the time setting process and the code could be reused. As well as setting the time of an alarm, a means to set the alarm and turn it off needs to be considered.
At this point, there would be space left for one more thing. Something shiny and blinky?