Advertisement
ramom007

Pico Beats and Time

Jun 28th, 2025 (edited)
28
0
13 days
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 6.15 KB | Fixit | 0 0
  1. import network
  2. import ntptime
  3. import utime
  4. import framebuf
  5. from machine import Pin, SPI
  6.  
  7. print("=== BOOT main.py ===")
  8.  
  9. # ——— Wi-Fi & NTP ———
  10. SSID     = "##"
  11. PASSWORD = "##"
  12. ntptime.host = "91.189.89.198"
  13. TZ_OFFSET  = 0
  14.  
  15. wlan = network.WLAN(network.STA_IF)
  16. offline_mode = False
  17.  
  18. def connect_wifi():
  19.     wlan.active(True)
  20.     wlan.connect(SSID, PASSWORD)
  21.     print("Connecting to Wi-Fi", end="")
  22.     for _ in range(15):
  23.         if wlan.isconnected(): break
  24.         utime.sleep(1); print(".", end="")
  25.     print()
  26.     if not wlan.isconnected():
  27.         raise RuntimeError("Wi-Fi failed")
  28.     print("Wi-Fi up, IP:", wlan.ifconfig()[0])
  29.  
  30. def ensure_wifi():
  31.     if not wlan.isconnected():
  32.         print("Wi-Fi dropped, reconnecting…")
  33.         try:
  34.             connect_wifi()
  35.         except Exception as e:
  36.             print("Reconnect failed:", e)
  37.  
  38. # ——— EPD driver ———
  39. EPD_PW, EPD_PH = 104, 212
  40. EPD_LW, EPD_LH = EPD_PH, 104
  41.  
  42. RST_PIN, DC_PIN, CS_PIN, BUSY_PIN = 12, 8, 9, 13
  43.  
  44. class EPD:
  45.     def __init__(self):
  46.         self.rst  = Pin(RST_PIN,  Pin.OUT)
  47.         self.dc   = Pin(DC_PIN,   Pin.OUT)
  48.         self.cs   = Pin(CS_PIN,   Pin.OUT)
  49.         self.busy = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP)
  50.         self.spi  = SPI(1)
  51.         self.spi.init(baudrate=4_000_000, polarity=0, phase=0)
  52.  
  53.         buf = EPD_PW*EPD_PH//8
  54.         self.bw = bytearray(buf)
  55.         self.rd = bytearray(buf)
  56.         self.fb_black = framebuf.FrameBuffer(self.bw, EPD_PW, EPD_PH, framebuf.MONO_HLSB)
  57.         self.fb_red   = framebuf.FrameBuffer(self.rd, EPD_PW, EPD_PH, framebuf.MONO_HLSB)
  58.  
  59.         lbuf = EPD_LW*EPD_LH//8
  60.         self.lb = bytearray(lbuf)
  61.         self.lr = bytearray(lbuf)
  62.         self.lfb_b = framebuf.FrameBuffer(self.lb, EPD_LW, EPD_LH, framebuf.MONO_VLSB)
  63.         self.lfb_r = framebuf.FrameBuffer(self.lr, EPD_LW, EPD_LH, framebuf.MONO_VLSB)
  64.  
  65.         self._init_panel()
  66.  
  67.     def _write(self, dc, data):
  68.         self.dc.value(dc); self.cs.value(0)
  69.         self.spi.write(data if isinstance(data,(bytes,bytearray)) else bytearray(data))
  70.         self.cs.value(1)
  71.  
  72.     def cmd(self, c):   self._write(0, [c])
  73.     def data(self,b):  self._write(1, b)
  74.  
  75.     def _busy(self):
  76.         self.cmd(0x71)
  77.         while not self.busy.value():
  78.             utime.sleep_ms(10); self.cmd(0x71)
  79.  
  80.     def _init_panel(self):
  81.         self.rst.value(1); utime.sleep_ms(50)
  82.         self.rst.value(0); utime.sleep_ms(2)
  83.         self.rst.value(1); utime.sleep_ms(50)
  84.         self.cmd(0x04); self._busy()
  85.         self.cmd(0x00); self.data([0x0F,0x89])
  86.         self.cmd(0x61); self.data([EPD_PW, (EPD_PH>>8)&0xFF, EPD_PH&0xFF])
  87.         self.cmd(0x50); self.data([0x77])
  88.  
  89.     def _rotate(self):
  90.         self.fb_black.fill(1); self.fb_red.fill(1)
  91.         for x in range(EPD_LW):
  92.             for y in range(EPD_LH):
  93.                 if self.lfb_b.pixel(x,y)==0:
  94.                     self.fb_black.pixel(y, EPD_LW-1-x, 0)
  95.                 if self.lfb_r.pixel(x,y)==0:
  96.                     self.fb_red.pixel(  y, EPD_LW-1-x, 0)
  97.  
  98.     def display(self):
  99.         self._rotate()
  100.         self.cmd(0x10); self.data(self.bw)
  101.         self.cmd(0x13); self.data(self.rd)
  102.         self.cmd(0x12); self._busy()
  103.  
  104.     def clear(self):
  105.         w = EPD_PW*EPD_PH//8
  106.         self.cmd(0x10); self.data([0xFF]*w)
  107.         self.cmd(0x13); self.data([0xFF]*w)
  108.         self.cmd(0x12); self._busy()
  109.         self.lfb_b.fill(1); self.lfb_r.fill(1)
  110.  
  111.     def draw_text2x(self, fb, txt, x, y, col):
  112.         w,h = len(txt)*8, 8
  113.         tmp = bytearray(w*h//8)
  114.         tf = framebuf.FrameBuffer(tmp, w, h, framebuf.MONO_HLSB)
  115.         tf.fill(1); tf.text(txt,0,0,0)
  116.         for px in range(w):
  117.             for py in range(h):
  118.                 if tf.pixel(px,py)==0:
  119.                     fb.pixel(x+px*2,   y+py*2,   col)
  120.                     fb.pixel(x+px*2+1, y+py*2,   col)
  121.                     fb.pixel(x+px*2,   y+py*2+1, col)
  122.                     fb.pixel(x+px*2+1, y+py*2+1, col)
  123.  
  124. def update_dynamic(epd, hh, mm, ss, y0, margin, th_main, th_beat):
  125.     # HH:MM
  126.     t = "{:02d}:{:02d}".format(hh, mm)
  127.     w = len(t)*16; x = (EPD_LW - w)//2
  128.     epd.lfb_b.fill_rect(x, y0, w, th_main, 1)
  129.     epd.draw_text2x(epd.lfb_b, t, x, y0, 0)
  130.     # BMT-beats
  131.     bmt_h = (hh - 1) % 24
  132.     secs  = bmt_h*3600 + mm*60 + ss
  133.     beats = int(secs/86.4)
  134.     bt = "@{:03d}".format(beats)
  135.     w2 = len(bt)*16; x2 = (EPD_LW - w2)//2
  136.     y2 = y0 + th_main + margin
  137.     epd.lfb_b.fill_rect(x2, y2, w2, th_beat, 1)
  138.     epd.draw_text2x(epd.lfb_r, "@", x2, y2, 0)
  139.     epd.draw_text2x(epd.lfb_b, "{:03d}".format(beats), x2+16, y2, 0)
  140.  
  141. if __name__=="__main__":
  142.     # try Wi-Fi + NTP with pause
  143.     try:
  144.         connect_wifi()
  145.         print("Waiting 5 s for network…"); utime.sleep(5)
  146.         print("Sync NTP…", end="")
  147.         ntptime.settime()
  148.         print(" OK")
  149.     except Exception as e:
  150.         print("Wi-Fi/NTP failed:", e)
  151.         print("Falling back offline in 5 s…"); utime.sleep(5)
  152.         offline_mode = True
  153.         start_ticks = utime.ticks_ms()
  154.  
  155.     epd = EPD()
  156.  
  157.     # layout
  158.     margin  = 4; th_main = 16; th_beat = 16
  159.     y0      = (EPD_LH - (th_main+margin+th_beat))//2
  160.  
  161.     # draw static UI
  162.     epd.clear()
  163.     for i in range(4):
  164.         o=6+i; epd.lfb_b.rect(o,o,EPD_LW-2*o,EPD_LH-2*o,0)
  165.     for i in range(2):
  166.         o=10+i; epd.lfb_r.rect(o,o,EPD_LW-2*o,EPD_LH-2*o,0)
  167.  
  168.     # "Amsterdam"
  169.     am="Amsterdam"
  170.     x_am=(EPD_LW-len(am)*8)//2; y_am=y0-margin-8
  171.     epd.lfb_b.text(am,x_am,y_am,0)
  172.  
  173.     # lower logo
  174.     dot=16; logo_w=dot+4+len("beat")*8
  175.     lx=(EPD_LW-logo_w)//2
  176.     ly=y0+th_main+margin+th_beat+margin
  177.     epd.lfb_r.fill_rect(lx,ly,dot,dot,0)
  178.     epd.lfb_b.text("beat",lx+dot+4,ly+(dot-8)//2,0)
  179.  
  180.     # main loop
  181.     while True:
  182.         if offline_mode:
  183.             secs = utime.ticks_diff(utime.ticks_ms(), start_ticks)//1000
  184.             hh=(12+secs//3600)%24; mm=(secs//60)%60; ss=secs%60
  185.         else:
  186.             ensure_wifi()
  187.             tm = utime.localtime(utime.time()+TZ_OFFSET)
  188.             hh,mm,ss = tm[3],tm[4],tm[5]
  189.  
  190.         update_dynamic(epd, hh, mm, ss, y0, margin, th_main, th_beat)
  191.         epd.display()
  192.         utime.sleep(60 - ss)
  193.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement