Skip to content

Instantly share code, notes, and snippets.

@aallan
Last active September 19, 2024 16:59
Show Gist options
  • Save aallan/ea16d05f7967d8ab899dfff12833a70f to your computer and use it in GitHub Desktop.
Save aallan/ea16d05f7967d8ab899dfff12833a70f to your computer and use it in GitHub Desktop.
Example MicroPython watch code for the RP2040 1.28-inch TFT display watch board
# Digital Watch Face
#
# Author: Tony Goodhew (28th January 2023)
# Updates: Toby Roberts, Andrew Scheller & Alasdair Allan (March 2023)
#
# Original code taken from https://www.instructables.com/Digital-Watch-Display-MicroPython/
from machine import Pin,I2C,SPI,PWM
import framebuf
import time
import math
DC = 8
CS = 9
SCK = 10
MOSI = 11
RST = 12
BL = 13
Vbat_Pin = 29
width = 240
height = 240
class LCD_1inch28(framebuf.FrameBuffer): # Waveshare RP2040 1.28" IPS LCD Board Driver - Round Display
def __init__(self):
self.width = 240
self.height = 240
self.cs = Pin(CS,Pin.OUT)
self.rst = Pin(RST,Pin.OUT)
self.cs(1)
self.spi = SPI(1,100_000_000,polarity=0, phase=0,sck=Pin(SCK),mosi=Pin(MOSI),miso=None)
self.dc = Pin(DC,Pin.OUT)
self.dc(1)
self.buffer = bytearray(self.height * self.width * 2)
super().__init__(self.buffer, self.width, self.height, framebuf.RGB565)
self.init_display()
self.red = 0x07E0
self.green = 0x001f
self.blue = 0xf800
self.white = 0xffff
self.fill(self.white)
self.show()
self.pwm = PWM(Pin(BL))
self.pwm.freq(5000)
def write_cmd(self, cmd):
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(bytearray([buf]))
self.cs(1)
def set_bl_pwm(self,duty):
self.pwm.duty_u16(duty)#max 65535
def init_display(self):
"""Initialize display"""
self.rst(1)
time.sleep(0.01)
self.rst(0)
time.sleep(0.01)
self.rst(1)
time.sleep(0.05)
self.write_cmd(0xEF)
self.write_cmd(0xEB)
self.write_data(0x14)
self.write_cmd(0xFE)
self.write_cmd(0xEF)
self.write_cmd(0xEB)
self.write_data(0x14)
self.write_cmd(0x84)
self.write_data(0x40)
self.write_cmd(0x85)
self.write_data(0xFF)
self.write_cmd(0x86)
self.write_data(0xFF)
self.write_cmd(0x87)
self.write_data(0xFF)
self.write_cmd(0x88)
self.write_data(0x0A)
self.write_cmd(0x89)
self.write_data(0x21)
self.write_cmd(0x8A)
self.write_data(0x00)
self.write_cmd(0x8B)
self.write_data(0x80)
self.write_cmd(0x8C)
self.write_data(0x01)
self.write_cmd(0x8D)
self.write_data(0x01)
self.write_cmd(0x8E)
self.write_data(0xFF)
self.write_cmd(0x8F)
self.write_data(0xFF)
self.write_cmd(0xB6)
self.write_data(0x00)
self.write_data(0x20)
self.write_cmd(0x36)
self.write_data(0x98)
self.write_cmd(0x3A)
self.write_data(0x05)
self.write_cmd(0x90)
self.write_data(0x08)
self.write_data(0x08)
self.write_data(0x08)
self.write_data(0x08)
self.write_cmd(0xBD)
self.write_data(0x06)
self.write_cmd(0xBC)
self.write_data(0x00)
self.write_cmd(0xFF)
self.write_data(0x60)
self.write_data(0x01)
self.write_data(0x04)
self.write_cmd(0xC3)
self.write_data(0x13)
self.write_cmd(0xC4)
self.write_data(0x13)
self.write_cmd(0xC9)
self.write_data(0x22)
self.write_cmd(0xBE)
self.write_data(0x11)
self.write_cmd(0xE1)
self.write_data(0x10)
self.write_data(0x0E)
self.write_cmd(0xDF)
self.write_data(0x21)
self.write_data(0x0c)
self.write_data(0x02)
self.write_cmd(0xF0)
self.write_data(0x45)
self.write_data(0x09)
self.write_data(0x08)
self.write_data(0x08)
self.write_data(0x26)
self.write_data(0x2A)
self.write_cmd(0xF1)
self.write_data(0x43)
self.write_data(0x70)
self.write_data(0x72)
self.write_data(0x36)
self.write_data(0x37)
self.write_data(0x6F)
self.write_cmd(0xF2)
self.write_data(0x45)
self.write_data(0x09)
self.write_data(0x08)
self.write_data(0x08)
self.write_data(0x26)
self.write_data(0x2A)
self.write_cmd(0xF3)
self.write_data(0x43)
self.write_data(0x70)
self.write_data(0x72)
self.write_data(0x36)
self.write_data(0x37)
self.write_data(0x6F)
self.write_cmd(0xED)
self.write_data(0x1B)
self.write_data(0x0B)
self.write_cmd(0xAE)
self.write_data(0x77)
self.write_cmd(0xCD)
self.write_data(0x63)
self.write_cmd(0x70)
self.write_data(0x07)
self.write_data(0x07)
self.write_data(0x04)
self.write_data(0x0E)
self.write_data(0x0F)
self.write_data(0x09)
self.write_data(0x07)
self.write_data(0x08)
self.write_data(0x03)
self.write_cmd(0xE8)
self.write_data(0x34)
self.write_cmd(0x62)
self.write_data(0x18)
self.write_data(0x0D)
self.write_data(0x71)
self.write_data(0xED)
self.write_data(0x70)
self.write_data(0x70)
self.write_data(0x18)
self.write_data(0x0F)
self.write_data(0x71)
self.write_data(0xEF)
self.write_data(0x70)
self.write_data(0x70)
self.write_cmd(0x63)
self.write_data(0x18)
self.write_data(0x11)
self.write_data(0x71)
self.write_data(0xF1)
self.write_data(0x70)
self.write_data(0x70)
self.write_data(0x18)
self.write_data(0x13)
self.write_data(0x71)
self.write_data(0xF3)
self.write_data(0x70)
self.write_data(0x70)
self.write_cmd(0x64)
self.write_data(0x28)
self.write_data(0x29)
self.write_data(0xF1)
self.write_data(0x01)
self.write_data(0xF1)
self.write_data(0x00)
self.write_data(0x07)
self.write_cmd(0x66)
self.write_data(0x3C)
self.write_data(0x00)
self.write_data(0xCD)
self.write_data(0x67)
self.write_data(0x45)
self.write_data(0x45)
self.write_data(0x10)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0x00)
self.write_cmd(0x67)
self.write_data(0x00)
self.write_data(0x3C)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0x01)
self.write_data(0x54)
self.write_data(0x10)
self.write_data(0x32)
self.write_data(0x98)
self.write_cmd(0x74)
self.write_data(0x10)
self.write_data(0x85)
self.write_data(0x80)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0x4E)
self.write_data(0x00)
self.write_cmd(0x98)
self.write_data(0x3e)
self.write_data(0x07)
self.write_cmd(0x35)
self.write_cmd(0x21)
self.write_cmd(0x11)
time.sleep(0.12)
self.write_cmd(0x29)
time.sleep(0.02)
self.write_cmd(0x21)
self.write_cmd(0x11)
self.write_cmd(0x29)
self.write_cmd(0x36)
self.write_data(0x68)
def show(self):
self.write_cmd(0x2A)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0xef)
self.write_cmd(0x2B)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0x00)
self.write_data(0xEF)
self.write_cmd(0x2C)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(self.buffer)
self.cs(1)
class QMI8658(object):
def __init__(self,address=0X6B):
self._address = address
self._bus = I2C(id=1,scl=Pin(I2C_SDL),sda=Pin(I2C_SDA),freq=100_000)
bRet=self.WhoAmI()
if bRet :
self.Read_Revision()
else :
return NULL
self.Config_apply()
def _read_byte(self,cmd):
rec=self._bus.readfrom_mem(int(self._address),int(cmd),1)
return rec[0]
def _read_block(self, reg, length=1):
rec=self._bus.readfrom_mem(int(self._address),int(reg),length)
return rec
def _read_u16(self,cmd):
LSB = self._bus.readfrom_mem(int(self._address),int(cmd),1)
MSB = self._bus.readfrom_mem(int(self._address),int(cmd)+1,1)
return (MSB[0] << 8) + LSB[0]
def _write_byte(self,cmd,val):
self._bus.writeto_mem(int(self._address),int(cmd),bytes([int(val)]))
def WhoAmI(self):
bRet=False
if (0x05) == self._read_byte(0x00):
bRet = True
return bRet
def Read_Revision(self):
return self._read_byte(0x01)
def Config_apply(self):
# REG CTRL1
self._write_byte(0x02,0x60)
# REG CTRL2 : QMI8658AccRange_8g and QMI8658AccOdr_1000Hz
self._write_byte(0x03,0x23)
# REG CTRL3 : QMI8658GyrRange_512dps and QMI8658GyrOdr_1000Hz
self._write_byte(0x04,0x53)
# REG CTRL4 : No
self._write_byte(0x05,0x00)
# REG CTRL5 : Enable Gyroscope And Accelerometer Low-Pass Filter
self._write_byte(0x06,0x11)
# REG CTRL6 : Disables Motion on Demand.
self._write_byte(0x07,0x00)
# REG CTRL7 : Enable Gyroscope And Accelerometer
self._write_byte(0x08,0x03)
def Read_Raw_XYZ(self):
xyz=[0,0,0,0,0,0]
raw_timestamp = self._read_block(0x30,3)
raw_acc_xyz=self._read_block(0x35,6)
raw_gyro_xyz=self._read_block(0x3b,6)
raw_xyz=self._read_block(0x35,12)
timestamp = (raw_timestamp[2]<<16)|(raw_timestamp[1]<<8)|(raw_timestamp[0])
for i in range(6):
# xyz[i]=(raw_acc_xyz[(i*2)+1]<<8)|(raw_acc_xyz[i*2])
# xyz[i+3]=(raw_gyro_xyz[((i+3)*2)+1]<<8)|(raw_gyro_xyz[(i+3)*2])
xyz[i] = (raw_xyz[(i*2)+1]<<8)|(raw_xyz[i*2])
if xyz[i] >= 32767:
xyz[i] = xyz[i]-65535
return xyz
def Read_XYZ(self):
xyz=[0,0,0,0,0,0]
raw_xyz=self.Read_Raw_XYZ()
#QMI8658AccRange_8g
acc_lsb_div=(1<<12)
#QMI8658GyrRange_512dps
gyro_lsb_div = 64
for i in range(3):
xyz[i]=raw_xyz[i]/acc_lsb_div#(acc_lsb_div/1000.0)
xyz[i+3]=raw_xyz[i+3]*1.0/gyro_lsb_div
return xyz
def colour(R,G,B): # Convert RGB888 to RGB565
return (((G&0b00011100)<<3) +((B&0b11111000)>>3)<<8) + (R&0b11111000)+((G&0b11100000)>>5)
LCD = LCD_1inch28() #=============== Initialise the display ===================
LCD.set_bl_pwm(65535) # Brightness
#qmi8658=QMI8658() # Initialise gyro accl
#Vbat= ADC(Pin(Vbat_Pin)) # Lipo voltage pin
# ========== Start of Triangles code =============
# Modified from https://github.com/SpiderMaf/PiPicoDsply/blob/main/filled-triangles.py
# To work on WaveShare Pi Pico displays
# ========== Version 2 FIXED ! 23 May 2022 ==========
class Point:
def __init__(self,x,y):
self.X=x
self.Y=y
def __str__(self):
return "Point(%s,%s)"%(self.X,self.Y)
class Triangle:
def __init__(self,p1,p2,p3):
self.P1=p1
self.P2=p2
self.P3=p3
def __str__(self):
return "Triangle(%s,%s,%s)"%(self.P1,self.P2,self.P3)
def draw(self):
print("I should draw now")
self.fillTri()
# Filled triangle routines ported from http://www.sunshine2k.de/coding/java/TriangleRasterization/TriangleRasterization.html
def sortVerticesAscendingByY(self):
if self.P1.Y > self.P2.Y:
vTmp = self.P1
self.P1 = self.P2
self.P2 = vTmp
if self.P1.Y > self.P3.Y:
vTmp = self.P1
self.P1 = self.P3
self.P3 = vTmp
if self.P2.Y > self.P3.Y:
vTmp = self.P2
self.P2 = self.P3
self.P3 = vTmp
def fillTri(self):
self.sortVerticesAscendingByY()
if self.P2.Y == self.P3.Y:
fillBottomFlatTriangle(self.P1, self.P2, self.P3)
else:
if self.P1.Y == self.P2.Y:
fillTopFlatTriangle(self.P1, self.P2, self.P3)
else:
newx = int(self.P1.X + (float(self.P2.Y - self.P1.Y) / float(self.P3.Y - self.P1.Y)) * (self.P3.X - self.P1.X))
newy = self.P2.Y
pTmp = Point( newx,newy )
# print(pTmp)
fillBottomFlatTriangle(self.P1, self.P2, pTmp)
fillTopFlatTriangle(self.P2, pTmp, self.P3)
def fillBottomFlatTriangle(p1,p2,p3):
# print("BF",p1,p2,p3)
if p2.Y > p3.Y:
ty = p3.Y
p3.Y = p2.Y
p2.Y = ty
tx = p3.X
p3.X = p2.X
p2.X = tx
print(p1,p2,p3)
slope1 = float(p2.X - p1.X) / float (p2.Y - p1.Y)
slope2 = float(p3.X - p1.X) / float (p3.Y - p1.Y)
x1 = p1.X
x2 = p1.X + 0.5
# print("B",p1.Y,p2.Y)
for scanlineY in range(p1.Y,p2.Y):
# print(scanlineY)
# LCD.pixel_span(int(x1), scanlineY, int(x2)-int(x1)) # Switch pixel_span() to hline() / Pimoroni to WS
LCD.hline(int(x1),scanlineY, int(x2)-int(x1),c)
LCD.hline(int(x2),scanlineY, -(int(x2)-int(x1)),c)
# LCD.show() # Here and below
# utime.sleep(0.1) # <===== Uncomment to see how graphic elements are drawn
x1 += slope1
x2 += slope2
# LCD.show() # LCD.show() and utime.sleep(0.1)
def fillTopFlatTriangle(p1,p2,p3):
# print("TF",p1,p2,p3)
slope1 = float(p3.X - p1.X) / float(p3.Y - p1.Y)
slope2 = float(p3.X - p2.X) / float(p3.Y - p2.Y)
x1 = p3.X
x2 = p3.X + 0.5
# print("T",p3.Y,p1.Y-1)
for scanlineY in range (p3.Y,p1.Y-1,-1):
# print(scanlineY)
# LCD.pixel_span(int(x1), scanlineY, int(x2)-int(x1)) # Switch pixel_span() to hline() / Pimoroni to WS
LCD.hline(int(x1),scanlineY, int(x2)-int(x1)+1,c)
LCD.hline(int(x2),scanlineY, -(int(x2)-int(x1)-1),c)
# LCD.show()
# utime.sleep(0.1)
x1 -= slope1
x2 -= slope2
# LCD.show()
# ============== End of Triangles Code ===============
# =========== New GFX Routines ============
def clear(c):
LCD.fill(c)
def triangle(x1,y1,x2,y2,x3,y3,c): # Draw outline triangle
LCD.line(x1,y1,x2,y2,c)
LCD.line(x2,y2,x3,y3,c)
LCD.line(x3,y3,x1,y1,c)
def tri_filled(x1,y1,x2,y2,x3,y3,c): # Draw filled triangle
t=Triangle(Point(x1,y1),Point(x2,y2),Point(x3,y3)) # Define corners
t.fillTri() # Call main code block
def circle(x,y,r,c):
LCD.hline(x-r,y,r*2,c)
for i in range(1,r):
a = int(math.sqrt(r*r-i*i)) # Pythagoras!
LCD.hline(x-a,y+i,a*2,c) # Lower half
LCD.hline(x-a,y-i,a*2,c) # Upper half
def ring(x,y,r,c):
LCD.pixel(x-r,y,c)
LCD.pixel(x+r,y,c)
LCD.pixel(x,y-r,c)
LCD.pixel(x,y+r,c)
for i in range(1,r):
a = int(math.sqrt(r*r-i*i))
LCD.pixel(x-a,y-i,c)
LCD.pixel(x+a,y-i,c)
LCD.pixel(x-a,y+i,c)
LCD.pixel(x+a,y+i,c)
LCD.pixel(x-i,y-a,c)
LCD.pixel(x+i,y-a,c)
LCD.pixel(x-i,y+a,c)
LCD.pixel(x+i,y+a,c)
# ===========Start of FONTS Section=========================
# Standard ASCII 5x8 font
# https://gist.github.com/tdicola/229b3eeddc12d58fb0bc724a9062aa05
FONT_HEIGHT = 8
FONT_WIDTH = 5
FONT = bytes([
0x00, 0x00, 0x00, 0x00, 0x00, # <space>
0x3E, 0x5B, 0x4F, 0x5B, 0x3E,
0x3E, 0x6B, 0x4F, 0x6B, 0x3E,
0x1C, 0x3E, 0x7C, 0x3E, 0x1C,
0x18, 0x3C, 0x7E, 0x3C, 0x18,
0x1C, 0x57, 0x7D, 0x57, 0x1C,
0x1C, 0x5E, 0x7F, 0x5E, 0x1C,
0x00, 0x18, 0x3C, 0x18, 0x00,
0xFF, 0xE7, 0xC3, 0xE7, 0xFF,
0x00, 0x18, 0x24, 0x18, 0x00,
0xFF, 0xE7, 0xDB, 0xE7, 0xFF,
0x30, 0x48, 0x3A, 0x06, 0x0E,
0x26, 0x29, 0x79, 0x29, 0x26,
0x40, 0x7F, 0x05, 0x05, 0x07,
0x40, 0x7F, 0x05, 0x25, 0x3F,
0x5A, 0x3C, 0xE7, 0x3C, 0x5A,
0x7F, 0x3E, 0x1C, 0x1C, 0x08,
0x08, 0x1C, 0x1C, 0x3E, 0x7F,
0x14, 0x22, 0x7F, 0x22, 0x14,
0x5F, 0x5F, 0x00, 0x5F, 0x5F,
0x06, 0x09, 0x7F, 0x01, 0x7F,
0x00, 0x66, 0x89, 0x95, 0x6A,
0x60, 0x60, 0x60, 0x60, 0x60,
0x94, 0xA2, 0xFF, 0xA2, 0x94,
0x08, 0x04, 0x7E, 0x04, 0x08, # UP
0x10, 0x20, 0x7E, 0x20, 0x10, # Down
0x08, 0x08, 0x2A, 0x1C, 0x08, # Right
0x08, 0x1C, 0x2A, 0x08, 0x08, # Left
0x1E, 0x10, 0x10, 0x10, 0x10,
0x0C, 0x1E, 0x0C, 0x1E, 0x0C,
0x30, 0x38, 0x3E, 0x38, 0x30,
0x06, 0x0E, 0x3E, 0x0E, 0x06,
0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x5F, 0x00, 0x00,
0x00, 0x07, 0x00, 0x07, 0x00,
0x14, 0x7F, 0x14, 0x7F, 0x14,
0x24, 0x2A, 0x7F, 0x2A, 0x12,
0x23, 0x13, 0x08, 0x64, 0x62,
0x36, 0x49, 0x56, 0x20, 0x50,
0x00, 0x08, 0x07, 0x03, 0x00,
0x00, 0x1C, 0x22, 0x41, 0x00,
0x00, 0x41, 0x22, 0x1C, 0x00,
0x2A, 0x1C, 0x7F, 0x1C, 0x2A,
0x08, 0x08, 0x3E, 0x08, 0x08,
0x00, 0x80, 0x70, 0x30, 0x00,
0x08, 0x08, 0x08, 0x08, 0x08,
0x00, 0x00, 0x60, 0x60, 0x00,
0x20, 0x10, 0x08, 0x04, 0x02,
0x3E, 0x51, 0x49, 0x45, 0x3E,
0x00, 0x42, 0x7F, 0x40, 0x00,
0x72, 0x49, 0x49, 0x49, 0x46,
0x21, 0x41, 0x49, 0x4D, 0x33,
0x18, 0x14, 0x12, 0x7F, 0x10,
0x27, 0x45, 0x45, 0x45, 0x39,
0x3C, 0x4A, 0x49, 0x49, 0x31,
0x41, 0x21, 0x11, 0x09, 0x07,
0x36, 0x49, 0x49, 0x49, 0x36,
0x46, 0x49, 0x49, 0x29, 0x1E,
0x00, 0x00, 0x14, 0x00, 0x00,
0x00, 0x40, 0x34, 0x00, 0x00,
0x00, 0x08, 0x14, 0x22, 0x41,
0x14, 0x14, 0x14, 0x14, 0x14,
0x00, 0x41, 0x22, 0x14, 0x08,
0x02, 0x01, 0x59, 0x09, 0x06,
0x3E, 0x41, 0x5D, 0x59, 0x4E,
0x7C, 0x12, 0x11, 0x12, 0x7C, # A
0x7F, 0x49, 0x49, 0x49, 0x36,
0x3E, 0x41, 0x41, 0x41, 0x22,
0x7F, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x49, 0x49, 0x49, 0x41,
0x7F, 0x09, 0x09, 0x09, 0x01,
0x3E, 0x41, 0x41, 0x51, 0x73,
0x7F, 0x08, 0x08, 0x08, 0x7F,
0x00, 0x41, 0x7F, 0x41, 0x00,
0x20, 0x40, 0x41, 0x3F, 0x01,
0x7F, 0x08, 0x14, 0x22, 0x41,
0x7F, 0x40, 0x40, 0x40, 0x40,
0x7F, 0x02, 0x1C, 0x02, 0x7F,
0x7F, 0x04, 0x08, 0x10, 0x7F,
0x3E, 0x41, 0x41, 0x41, 0x3E,
0x7F, 0x09, 0x09, 0x09, 0x06,
0x3E, 0x41, 0x51, 0x21, 0x5E,
0x7F, 0x09, 0x19, 0x29, 0x46,
0x26, 0x49, 0x49, 0x49, 0x32,
0x03, 0x01, 0x7F, 0x01, 0x03,
0x3F, 0x40, 0x40, 0x40, 0x3F,
0x1F, 0x20, 0x40, 0x20, 0x1F,
0x3F, 0x40, 0x38, 0x40, 0x3F,
0x63, 0x14, 0x08, 0x14, 0x63,
0x03, 0x04, 0x78, 0x04, 0x03,
0x61, 0x59, 0x49, 0x4D, 0x43,
0x00, 0x7F, 0x41, 0x41, 0x41,
0x02, 0x04, 0x08, 0x10, 0x20,
0x00, 0x41, 0x41, 0x41, 0x7F,
0x04, 0x02, 0x01, 0x02, 0x04,
0x40, 0x40, 0x40, 0x40, 0x40,
0x00, 0x03, 0x07, 0x08, 0x00,
0x20, 0x54, 0x54, 0x78, 0x40,
0x7F, 0x28, 0x44, 0x44, 0x38,
0x38, 0x44, 0x44, 0x44, 0x28,
0x38, 0x44, 0x44, 0x28, 0x7F,
0x38, 0x54, 0x54, 0x54, 0x18,
0x00, 0x08, 0x7E, 0x09, 0x02,
0x18, 0xA4, 0xA4, 0x9C, 0x78,
0x7F, 0x08, 0x04, 0x04, 0x78,
0x00, 0x44, 0x7D, 0x40, 0x00,
0x20, 0x40, 0x40, 0x3D, 0x00,
0x7F, 0x10, 0x28, 0x44, 0x00,
0x00, 0x41, 0x7F, 0x40, 0x00,
0x7C, 0x04, 0x78, 0x04, 0x78,
0x7C, 0x08, 0x04, 0x04, 0x78,
0x38, 0x44, 0x44, 0x44, 0x38,
0xFC, 0x18, 0x24, 0x24, 0x18,
0x18, 0x24, 0x24, 0x18, 0xFC,
0x7C, 0x08, 0x04, 0x04, 0x08,
0x48, 0x54, 0x54, 0x54, 0x24,
0x04, 0x04, 0x3F, 0x44, 0x24,
0x3C, 0x40, 0x40, 0x20, 0x7C,
0x1C, 0x20, 0x40, 0x20, 0x1C,
0x3C, 0x40, 0x30, 0x40, 0x3C,
0x44, 0x28, 0x10, 0x28, 0x44,
0x4C, 0x90, 0x90, 0x90, 0x7C,
0x44, 0x64, 0x54, 0x4C, 0x44,
0x00, 0x08, 0x36, 0x41, 0x00,
0x00, 0x00, 0x77, 0x00, 0x00,
0x00, 0x41, 0x36, 0x08, 0x00,
0x02, 0x01, 0x02, 0x04, 0x02,
0x3C, 0x26, 0x23, 0x26, 0x3C,
0x1E, 0xA1, 0xA1, 0x61, 0x12, # Extension starts here
0x3A, 0x40, 0x40, 0x20, 0x7A,
0x38, 0x54, 0x54, 0x55, 0x59,
0x21, 0x55, 0x55, 0x79, 0x41,
0x22, 0x54, 0x54, 0x78, 0x42, # a-umlaut
0x21, 0x55, 0x54, 0x78, 0x40,
0x20, 0x54, 0x55, 0x79, 0x40,
0x0C, 0x1E, 0x52, 0x72, 0x12,
0x39, 0x55, 0x55, 0x55, 0x59,
0x39, 0x54, 0x54, 0x54, 0x59,
0x39, 0x55, 0x54, 0x54, 0x58,
0x00, 0x00, 0x45, 0x7C, 0x41,
0x00, 0x02, 0x45, 0x7D, 0x42,
0x00, 0x01, 0x45, 0x7C, 0x40,
0x7D, 0x12, 0x11, 0x12, 0x7D, # A-umlaut
0xF0, 0x28, 0x25, 0x28, 0xF0,
0x7C, 0x54, 0x55, 0x45, 0x00,
0x20, 0x54, 0x54, 0x7C, 0x54,
0x7C, 0x0A, 0x09, 0x7F, 0x49,
0x32, 0x49, 0x49, 0x49, 0x32,
0x3A, 0x44, 0x44, 0x44, 0x3A, # o-umlaut
0x32, 0x4A, 0x48, 0x48, 0x30,
0x3A, 0x41, 0x41, 0x21, 0x7A,
0x3A, 0x42, 0x40, 0x20, 0x78,
0x00, 0x9D, 0xA0, 0xA0, 0x7D,
0x3D, 0x42, 0x42, 0x42, 0x3D, # O-umlaut
0x3D, 0x40, 0x40, 0x40, 0x3D,
0x3C, 0x24, 0xFF, 0x24, 0x24,
0x48, 0x7E, 0x49, 0x43, 0x66,
0x2B, 0x2F, 0xFC, 0x2F, 0x2B,
0xFF, 0x09, 0x29, 0xF6, 0x20,
0xC0, 0x88, 0x7E, 0x09, 0x03,
0x20, 0x54, 0x54, 0x79, 0x41,
0x00, 0x00, 0x44, 0x7D, 0x41,
0x30, 0x48, 0x48, 0x4A, 0x32,
0x38, 0x40, 0x40, 0x22, 0x7A,
0x00, 0x7A, 0x0A, 0x0A, 0x72,
0x7D, 0x0D, 0x19, 0x31, 0x7D,
0x26, 0x29, 0x29, 0x2F, 0x28,
0x26, 0x29, 0x29, 0x29, 0x26,
0x30, 0x48, 0x4D, 0x40, 0x20,
0x38, 0x08, 0x08, 0x08, 0x08,
0x08, 0x08, 0x08, 0x08, 0x38,
0x2F, 0x10, 0xC8, 0xAC, 0xBA,
0x2F, 0x10, 0x28, 0x34, 0xFA,
0x00, 0x00, 0x7B, 0x00, 0x00,
0x08, 0x14, 0x2A, 0x14, 0x22,
0x22, 0x14, 0x2A, 0x14, 0x08,
0x55, 0x00, 0x55, 0x00, 0x55, # 176 (25% block) missing in old code
0xAA, 0x55, 0xAA, 0x55, 0xAA, # 50% block
0xFF, 0x55, 0xFF, 0x55, 0xFF, # 75% block
0x00, 0x00, 0x00, 0xFF, 0x00,
0x10, 0x10, 0x10, 0xFF, 0x00,
0x14, 0x14, 0x14, 0xFF, 0x00,
0x10, 0x10, 0xFF, 0x00, 0xFF,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x14, 0x14, 0x14, 0xFC, 0x00,
0x14, 0x14, 0xF7, 0x00, 0xFF,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x14, 0x14, 0xF4, 0x04, 0xFC,
0x14, 0x14, 0x17, 0x10, 0x1F,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0x1F, 0x00,
0x10, 0x10, 0x10, 0xF0, 0x00,
0x00, 0x00, 0x00, 0x1F, 0x10,
0x10, 0x10, 0x10, 0x1F, 0x10,
0x10, 0x10, 0x10, 0xF0, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x10,
0x10, 0x10, 0x10, 0x10, 0x10,
0x10, 0x10, 0x10, 0xFF, 0x10,
0x00, 0x00, 0x00, 0xFF, 0x14,
0x00, 0x00, 0xFF, 0x00, 0xFF,
0x00, 0x00, 0x1F, 0x10, 0x17,
0x00, 0x00, 0xFC, 0x04, 0xF4,
0x14, 0x14, 0x17, 0x10, 0x17,
0x14, 0x14, 0xF4, 0x04, 0xF4,
0x00, 0x00, 0xFF, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x14, 0x14,
0x14, 0x14, 0xF7, 0x00, 0xF7,
0x14, 0x14, 0x14, 0x17, 0x14,
0x10, 0x10, 0x1F, 0x10, 0x1F,
0x14, 0x14, 0x14, 0xF4, 0x14,
0x10, 0x10, 0xF0, 0x10, 0xF0,
0x00, 0x00, 0x1F, 0x10, 0x1F,
0x00, 0x00, 0x00, 0x1F, 0x14,
0x00, 0x00, 0x00, 0xFC, 0x14,
0x00, 0x00, 0xF0, 0x10, 0xF0,
0x10, 0x10, 0xFF, 0x10, 0xFF,
0x14, 0x14, 0x14, 0xFF, 0x14,
0x10, 0x10, 0x10, 0x1F, 0x00,
0x00, 0x00, 0x00, 0xF0, 0x10,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
0xFF, 0xFF, 0xFF, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF,
0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
0x38, 0x44, 0x44, 0x38, 0x44, # alpha - Greek characters start here at 224
0xFC, 0x4A, 0x4A, 0x4A, 0x34, # sharp-s or beta
0x7E, 0x02, 0x02, 0x06, 0x06,
0x02, 0x7E, 0x02, 0x7E, 0x02, # pi
0x63, 0x55, 0x49, 0x41, 0x63,
0x38, 0x44, 0x44, 0x3C, 0x04,
0x40, 0x7E, 0x20, 0x1E, 0x20, # mu
0x06, 0x02, 0x7E, 0x02, 0x02,
0x99, 0xA5, 0xE7, 0xA5, 0x99,
0x1C, 0x2A, 0x49, 0x2A, 0x1C,
0x4C, 0x72, 0x01, 0x72, 0x4C, # omega
0x30, 0x4A, 0x4D, 0x4D, 0x30,
0x30, 0x48, 0x78, 0x48, 0x30,
0xBC, 0x62, 0x5A, 0x46, 0x3D,
0x3E, 0x49, 0x49, 0x49, 0x00,
0x7E, 0x01, 0x01, 0x01, 0x7E, # End of Greek chars
0x2A, 0x2A, 0x2A, 0x2A, 0x2A, # equivalent to 240
0x44, 0x44, 0x5F, 0x44, 0x44, # + or -
0x40, 0x51, 0x4A, 0x44, 0x40, # >=
0x40, 0x44, 0x4A, 0x51, 0x40, # <=
0x00, 0x00, 0xFF, 0x01, 0x03, # top of integral
0xE0, 0x80, 0xFF, 0x00, 0x00, # bottom of integral
0x08, 0x08, 0x6B, 0x6B, 0x08,
0x36, 0x12, 0x36, 0x24, 0x36, # approximately
0x06, 0x0F, 0x09, 0x0F, 0x06, # Degree
0x00, 0x00, 0x18, 0x18, 0x00,
0x00, 0x00, 0x10, 0x10, 0x00,
0x30, 0x40, 0xFF, 0x01, 0x01, # sq root
0x00, 0x1F, 0x01, 0x01, 0x1E, # n superscript
0x00, 0x19, 0x1D, 0x17, 0x12, # squared (^2)
0x00, 0x3C, 0x3C, 0x3C, 0x3C,
0x00, 0x00, 0x00, 0x00, 0x00 # 255 also a <space>
])
def character(asc,xt,yt,sz,r,g,b): # Single character sz is size: 1 or 2
cc = colour(r,g,b)
code = asc * 5 # 5 bytes per character
for ii in range(5):
line = FONT[code + ii]
for yy in range(8):
if (line >> yy) & 0x1:
LCD.pixel(ii*sz+xt,yy*sz+yt,cc)
if sz > 1:
LCD.pixel(ii*sz+xt+1,yy*sz+yt,cc)
LCD.pixel(ii*sz+xt,yy*sz+yt+1,cc)
LCD.pixel(ii*sz+xt+1,yy*sz+yt+1,cc)
if sz == 3:
LCD.pixel(ii*sz+xt, yy*sz+yt+2,cc)
LCD.pixel(ii*sz+xt+1,yy*sz+yt+2,cc)
LCD.pixel(ii*sz+xt+2,yy*sz+yt+2,cc)
LCD.pixel(ii*sz+xt+2,yy*sz+yt,cc)
LCD.pixel(ii*sz+xt+2,yy*sz+yt+1,cc)
def prnt_st(asci,xx,yy,sz,r,g,b): # Text string
if sz == 1: move = 6
if sz == 2: move = 11
if sz == 3: move = 17
for letter in(asci):
asci = ord(letter)
character(asci,xx,yy,sz,r,g,b)
xx = xx + move
def cntr_st(s,y,sz,r,g,b): # Centres text on line y
if sz == 1: w = 6
if sz == 2: w = 11
if sz == 3: w = 17
gap = int((width - len(s) * w)/2)
prnt_st(s,gap,y,sz,r,g,b)
# =========== End of font support routines ===========
# ==== Board now setup ========== MAIN BELOW====================
def end_point(theta, rr): # Calculate end of hand offsets
theta_rad = math.radians(theta)
theta_rad = math.radians(theta)
xx = int(rr * math.sin(theta_rad))
yy = -int(rr * math.cos(theta_rad))
return xx,yy
# https://youtu.be/X6PRcno_WbQ
clear(0) # Clear the screen
xc = 120 # Coordinates of centre
yc = 120
circle(xc,yc,120,colour(255,0,0)) # Large red circle
r = 120 # Tick outer radius - longer radius to stick out by 2 pixels
# Draw the scale ticks - lines from centre
for p in range(0,360,6):
hxn, hyn = end_point(p, r)
LCD.line(120,120,120+hxn,120+hyn,colour(255,255,255))
# Clear centre leaving blue ring with white ticks
circle(xc,yc,110,colour(0,0,0))
r = 120 # Tick outer radius - longer radius to stick out by 2 pixels
# Draw the scale ticks - lines from centre
for p in range(0,360,30):
hxn, hyn = end_point(p, r)
LCD.line(120,120,120+hxn,120+hyn,colour(255,255,255))
# Clear centre leaving blue ring with white ticks
circle(xc,yc,100,colour(0,0,0))
mr = 100 # length of minite hand
hr = 70 # length of hour hand
sr = 100 # length of second hand
# Start time
h = 16
m = 28
s = 00
# === Main loop ===
while True:
s = s + 1 # Add a second
if s == 60: # Adjust secs, mins and hours as necessary
s = 0
m = m + 1
if m == 60:
m = 0
h = h + 1
if h == 12:
h = 0
# Calculate coordinates of end of second pointer
alpha = (s*6)
sxn, syn = end_point(alpha, sr)
# Calculate coordinates of end of hour pointer
alpha = (h*60+m)*360/720
hxn, hyn = end_point(alpha, hr)
# Calculate coordinates of end of minute pointer
alpha = m * 360/60
mxn, myn = end_point(alpha, sr)
# Clear centre circle
circle(xc,yc,102,colour(0,0,0))
# Write Title and Scale values
cntr_st("Raspberry Pi",68,1,255,255,255) # Message
cntr_st("RP2040",88,1,255,255,255)
prnt_st("7",70,186,2,0,255,0)
prnt_st("5",158,186,2,0,255,0)
prnt_st("8",42,160,2,0,255,0)
prnt_st("4",190,160,2,0,255,0)
prnt_st("9",29,115,2,0,255,0)
prnt_st("3",200,115,2,0,255,0)
prnt_st("10",40,70,2,0,255,0)
prnt_st("2",190,70,2,0,255,0)
prnt_st("11",70,35,2,0,255,0)
prnt_st("1",157,35,2,0,255,0)
prnt_st("12",110,25,2,0,255,0)
prnt_st("6",115,200,2,0,255,0)
# Draw Digital Time
hs = "0"+str(h)
ms = "0"+str(m)
ts = hs[-2:] +":"+ms[-2:]
cntr_st(ts,145,3,255,255,0)
# Draw HOUR in red
hxnsm = int(hxn/8)
hynsm = int(hyn/8)
c = colour(255,0,0)
tri_filled(120+hxn, 120+hyn, 120+hynsm, 120-hxnsm, 120-hynsm, 120+hxnsm,c)
circle(xc,yc,9,colour(255,0,0))
# Draw MINUTE in red
mxnsm = int(mxn/15)
mynsm = int(myn/15)
c = colour(255,0,0)
tri_filled(120+mxn, 120+myn, 120+mynsm, 120-mxnsm, 120-mynsm, 120+mxnsm,c)
circle(xc,yc,8,colour(0,0,255))
# Draw second hand in white
c = colour(255,255,255)
LCD.line(xc,yc,120+sxn,120+syn,c)
LCD.show() # Update screen
# time.sleep(0.05) # Short delay
time.sleep(0.9) # sleep for a second!
@dbuggz
Copy link

dbuggz commented May 7, 2023

Hi Alisdair,
firstly, thanks for sharing your code.
A couple of things I noticed:
lines 901-904, setting the time was not obvious, maybe move it to the initializations before the LCD class.
then in the Main Loop, the Hours are checked (line 915) and reset to Zero if they reach 12, but obviously not if you start the time at 16:28, they just go on for ever, making no allowance for 24hr time.
If I was more familiar with Github and Gist, I'd try doing an edit and a pull request.
Dave

@johnmann56
Copy link

Hello Alisdair,
Thanks for posting the project. I noticed on line 348 of this watch_code.py what I believe is a typo:

self._bus = I2C(id=1,scl=Pin(I2C_SDL),sda=Pin(I2C_SDA),freq=100_000)

specifically

scl=Pin(I2C_SDL)

I believe the "I2C_SDL" should be "I2C_SCL".
I hope this helps. Please let me know if this is incorrect.
Respectfully,
John

@sgb1reg
Copy link

sgb1reg commented Apr 3, 2024

is it possible to describe how you rotate the screen?
Thank you very much!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment