From abadccf6121adee9282a0bb4a4582518d924c846 Mon Sep 17 00:00:00 2001 From: Fred Boniface Date: Tue, 19 Nov 2024 20:19:14 +0000 Subject: [PATCH] Initial --- LICENSE | 7 + Pico_ePaper_2_13_v4.py | 720 ++++++++++++++++++++++++++++ README.md | 24 + config.py | 5 + ezFBfont.py | 179 +++++++ ezFBfont_spleen_16x32_ascii_30.py | 171 +++++++ ezFBfont_spleen_8x16_ascii_14.py | 171 +++++++ ico/weather-cloudy.bin | Bin 0 -> 648 bytes ico/weather-fog.bin | Bin 0 -> 648 bytes ico/weather-hail.bin | Bin 0 -> 648 bytes ico/weather-hazy.bin | Bin 0 -> 648 bytes ico/weather-lightning-rainy.bin | Bin 0 -> 648 bytes ico/weather-lightning.bin | Bin 0 -> 648 bytes ico/weather-night-partly-cloudy.bin | Bin 0 -> 648 bytes ico/weather-night.bin | Bin 0 -> 648 bytes ico/weather-partly-cloudy.bin | Bin 0 -> 648 bytes ico/weather-partly-lightning.bin | Bin 0 -> 648 bytes ico/weather-partly-rainy.bin | Bin 0 -> 648 bytes ico/weather-partly-snowy-rainy.bin | Bin 0 -> 648 bytes ico/weather-partly-snowy.bin | Bin 0 -> 648 bytes ico/weather-pouring.bin | Bin 0 -> 648 bytes ico/weather-rainy.bin | Bin 0 -> 648 bytes ico/weather-snowy-heavy.bin | Bin 0 -> 648 bytes ico/weather-snowy-rainy.bin | Bin 0 -> 648 bytes ico/weather-snowy.bin | Bin 0 -> 648 bytes ico/weather-sunny.bin | Bin 0 -> 648 bytes ico/weather-sunset-down.bin | Bin 0 -> 128 bytes ico/weather-sunset-up.bin | Bin 0 -> 128 bytes main.py | 131 +++++ open_meteo.py | 63 +++ 30 files changed, 1471 insertions(+) create mode 100644 LICENSE create mode 100644 Pico_ePaper_2_13_v4.py create mode 100644 README.md create mode 100644 config.py create mode 100644 ezFBfont.py create mode 100644 ezFBfont_spleen_16x32_ascii_30.py create mode 100644 ezFBfont_spleen_8x16_ascii_14.py create mode 100644 ico/weather-cloudy.bin create mode 100644 ico/weather-fog.bin create mode 100644 ico/weather-hail.bin create mode 100644 ico/weather-hazy.bin create mode 100644 ico/weather-lightning-rainy.bin create mode 100644 ico/weather-lightning.bin create mode 100644 ico/weather-night-partly-cloudy.bin create mode 100644 ico/weather-night.bin create mode 100644 ico/weather-partly-cloudy.bin create mode 100644 ico/weather-partly-lightning.bin create mode 100644 ico/weather-partly-rainy.bin create mode 100644 ico/weather-partly-snowy-rainy.bin create mode 100644 ico/weather-partly-snowy.bin create mode 100644 ico/weather-pouring.bin create mode 100644 ico/weather-rainy.bin create mode 100644 ico/weather-snowy-heavy.bin create mode 100644 ico/weather-snowy-rainy.bin create mode 100644 ico/weather-snowy.bin create mode 100644 ico/weather-sunny.bin create mode 100644 ico/weather-sunset-down.bin create mode 100644 ico/weather-sunset-up.bin create mode 100644 main.py create mode 100644 open_meteo.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..7fd33be --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright 2024 Frederick Boniface + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Pico_ePaper_2_13_v4.py b/Pico_ePaper_2_13_v4.py new file mode 100644 index 0000000..6e97da7 --- /dev/null +++ b/Pico_ePaper_2_13_v4.py @@ -0,0 +1,720 @@ +# ***************************************************************************** +# * | File : Pico_ePaper-2.13_V3.py +# * | Author : Waveshare team +# * | Function : Electronic paper driver +# * | Info : +# *---------------- +# * | This version: V1.0 +# * | Date : 2021-11-01 +# # | Info : python demo +# ----------------------------------------------------------------------------- +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documnetation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS OR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + +from machine import Pin, SPI +import framebuf +import utime + + +EPD_WIDTH = 122 +EPD_HEIGHT = 250 + +RST_PIN = 12 +DC_PIN = 8 +CS_PIN = 9 +BUSY_PIN = 13 + +class EPD_2in13_V4_Portrait(framebuf.FrameBuffer): + def __init__(self): + self.reset_pin = Pin(RST_PIN, Pin.OUT) + + self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP) + self.cs_pin = Pin(CS_PIN, Pin.OUT) + if EPD_WIDTH % 8 == 0: + self.width = EPD_WIDTH + else : + self.width = (EPD_WIDTH // 8) * 8 + 8 + self.height = EPD_HEIGHT + + self.spi = SPI(1) + self.spi.init(baudrate=4000_000) + self.dc_pin = Pin(DC_PIN, Pin.OUT) + + self.buffer = bytearray(self.height * self.width // 8) + super().__init__(self.buffer, self.width, self.height, framebuf.MONO_HLSB) + self.init() + + ''' + function :Change the pin state + parameter: + pin : pin + value : state + ''' + def digital_write(self, pin, value): + pin.value(value) + + ''' + function : Read the pin state + parameter: + pin : pin + ''' + def digital_read(self, pin): + return pin.value() + + ''' + function : The time delay function + parameter: + delaytime : ms + ''' + def delay_ms(self, delaytime): + utime.sleep(delaytime / 1000.0) + + ''' + function : Write data to SPI + parameter: + data : data + ''' + def spi_writebyte(self, data): + self.spi.write(bytearray(data)) + + ''' + function :Hardware reset + parameter: + ''' + def reset(self): + self.digital_write(self.reset_pin, 1) + self.delay_ms(20) + self.digital_write(self.reset_pin, 0) + self.delay_ms(2) + self.digital_write(self.reset_pin, 1) + self.delay_ms(20) + + ''' + function :send command + parameter: + command : Command register + ''' + def send_command(self, command): + self.digital_write(self.dc_pin, 0) + self.digital_write(self.cs_pin, 0) + self.spi_writebyte([command]) + self.digital_write(self.cs_pin, 1) + + ''' + function :send data + parameter: + data : Write data + ''' + def send_data(self, data): + self.digital_write(self.dc_pin, 1) + self.digital_write(self.cs_pin, 0) + self.spi_writebyte([data]) + self.digital_write(self.cs_pin, 1) + + def send_data1(self, buf): + self.digital_write(self.dc_pin, 1) + self.digital_write(self.cs_pin, 0) + self.spi.write(bytearray(buf)) + self.digital_write(self.cs_pin, 1) + + ''' + function :Wait until the busy_pin goes LOW + parameter: + ''' + def ReadBusy(self): + print('busy') + self.delay_ms(10) + while(self.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + self.delay_ms(10) + print('busy release') + + ''' + function : Turn On Display + parameter: + ''' + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xf7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Fast + parameter: + ''' + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Part + parameter: + ''' + def TurnOnDisplayPart(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xff) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Setting the display window + parameter: + Xstart : X-axis starting position + Ystart : Y-axis starting position + Xend : End position of X-axis + Yend : End position of Y-axis + ''' + def SetWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((Xstart >> 3) & 0xFF) + self.send_data((Xend >> 3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0xFF) + self.send_data(Yend & 0xFF) + self.send_data((Yend >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + Xstart : X-axis starting position + Ystart : Y-axis starting position + ''' + def SetCursor(self, Xstart, Ystart): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + self.send_data(Xstart & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0xFF) + + ''' + function : Initialize the e-Paper register + parameter: + ''' + def init(self): + print('init') + self.reset() + self.delay_ms(100) + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x03) + + self.SetWindows(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x3C) # BorderWaveform + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.ReadBusy() + + ''' + function : Initialize the e-Paper fast register + parameter: + ''' + def init_fast(self): + print('init_fast') + self.reset() + self.delay_ms(100) + + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_command(0x80) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindow(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + return 0 + + ''' + function : Clear screen + parameter: + ''' + def Clear(self): + self.send_command(0x24) + self.send_data1([0xff] * self.height * int(self.width / 8)) + + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and displays + parameter: + image : Image data + ''' + def display(self, image): + self.send_command(0x24) + self.send_data1(image) + self.TurnOnDisplay() + + def display_fast(self, image): + self.send_command(0x24) + self.send_data2(image) + self.TurnOnDisplay_Fast() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + def Display_Base(self, image): + self.send_command(0x24) + self.send_data1(image) + + self.send_command(0x26) + self.send_data1(image) + + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and partial refresh + parameter: + image : Image data + ''' + def displayPartial(self, image): + self.reset() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x01) # Driver output control + self.send_data(0xF9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x03) + + self.SetWindows(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + self.send_data1(image) + self.TurnOnDisplayPart() + + ''' + function : Enter sleep mode + parameter: + ''' + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + self.delay_ms(100) + + +class EPD_2in13_V4_Landscape(framebuf.FrameBuffer): + def __init__(self): + self.reset_pin = Pin(RST_PIN, Pin.OUT) + + self.busy_pin = Pin(BUSY_PIN, Pin.IN, Pin.PULL_UP) + self.cs_pin = Pin(CS_PIN, Pin.OUT) + if EPD_WIDTH % 8 == 0: + self.width = EPD_WIDTH + else : + self.width = (EPD_WIDTH // 8) * 8 + 8 + + self.height = EPD_HEIGHT + + self.spi = SPI(1) + self.spi.init(baudrate=4000_000) + self.dc_pin = Pin(DC_PIN, Pin.OUT) + + self.buffer = bytearray(self.height * self.width // 8) + super().__init__(self.buffer, self.height, self.width, framebuf.MONO_VLSB) + self.init() + + def digital_write(self, pin, value): + pin.value(value) + + def digital_read(self, pin): + return pin.value() + + def delay_ms(self, delaytime): + utime.sleep(delaytime / 1000.0) + + def spi_writebyte(self, data): + self.spi.write(bytearray(data)) + + def reset(self): + self.digital_write(self.reset_pin, 1) + self.delay_ms(20) + self.digital_write(self.reset_pin, 0) + self.delay_ms(2) + self.digital_write(self.reset_pin, 1) + self.delay_ms(20) + + def send_command(self, command): + self.digital_write(self.dc_pin, 0) + self.digital_write(self.cs_pin, 0) + self.spi_writebyte([command]) + self.digital_write(self.cs_pin, 1) + + def send_data(self, data): + self.digital_write(self.dc_pin, 1) + self.digital_write(self.cs_pin, 0) + self.spi_writebyte([data]) + self.digital_write(self.cs_pin, 1) + + def send_data1(self, buf): + self.digital_write(self.dc_pin, 1) + self.digital_write(self.cs_pin, 0) + self.spi.write(bytearray(buf)) + self.digital_write(self.cs_pin, 1) + + def ReadBusy(self): + print('busy') + self.delay_ms(10) + while(self.digital_read(self.busy_pin) == 1): # 0: idle, 1: busy + self.delay_ms(10) + print('busy release') + + ''' + function : Turn On Display + parameter: + ''' + def TurnOnDisplay(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xf7) + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Fast + parameter: + ''' + def TurnOnDisplay_Fast(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xC7) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Turn On Display Part + parameter: + ''' + def TurnOnDisplayPart(self): + self.send_command(0x22) # Display Update Control + self.send_data(0xff) # fast:0x0c, quality:0x0f, 0xcf + self.send_command(0x20) # Activate Display Update Sequence + self.ReadBusy() + + ''' + function : Setting the display window + parameter: + Xstart : X-axis starting position + Ystart : Y-axis starting position + Xend : End position of X-axis + Yend : End position of Y-axis + ''' + def SetWindows(self, Xstart, Ystart, Xend, Yend): + self.send_command(0x44) # SET_RAM_X_ADDRESS_START_END_POSITION + self.send_data((Xstart >> 3) & 0xFF) + self.send_data((Xend >> 3) & 0xFF) + + self.send_command(0x45) # SET_RAM_Y_ADDRESS_START_END_POSITION + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0xFF) + self.send_data(Yend & 0xFF) + self.send_data((Yend >> 8) & 0xFF) + + ''' + function : Set Cursor + parameter: + Xstart : X-axis starting position + Ystart : Y-axis starting position + ''' + def SetCursor(self, Xstart, Ystart): + self.send_command(0x4E) # SET_RAM_X_ADDRESS_COUNTER + self.send_data(Xstart & 0xFF) + + self.send_command(0x4F) # SET_RAM_Y_ADDRESS_COUNTER + self.send_data(Ystart & 0xFF) + self.send_data((Ystart >> 8) & 0xFF) + + ''' + function : Initialize the e-Paper register + parameter: + ''' + def init(self): + print('init') + self.reset() + self.delay_ms(100) + + self.ReadBusy() + self.send_command(0x12) # SWRESET + self.ReadBusy() + + self.send_command(0x01) # Driver output control + self.send_data(0xf9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) #data entry mode + self.send_data(0x07) + + self.SetWindows(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x3C) # BorderWaveform + self.send_data(0x05) + + self.send_command(0x21) # Display update control + self.send_data(0x00) + self.send_data(0x80) + + self.send_command(0x18) # Read built-in temperature sensor + self.send_data(0x80) + + self.ReadBusy() + + ''' + function : Initialize the e-Paper fast register + parameter: + ''' + def init_fast(self): + print('init_fast') + self.reset() + self.delay_ms(100) + + self.send_command(0x12) #SWRESET + self.ReadBusy() + + self.send_command(0x18) # Read built-in temperature sensor + self.send_command(0x80) + + self.send_command(0x11) # data entry mode + self.send_data(0x07) + + self.SetWindow(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x22) # Load temperature value + self.send_data(0xB1) + self.send_command(0x20) + self.ReadBusy() + + self.send_command(0x1A) # Write to temperature register + self.send_data(0x64) + self.send_data(0x00) + + self.send_command(0x22) # Load temperature value + self.send_data(0x91) + self.send_command(0x20) + self.ReadBusy() + + return 0 + + ''' + function : Clear screen + parameter: + ''' + def Clear(self): + self.send_command(0x24) + self.send_data1([0xff] * self.height * int(self.width / 8)) + + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and displays + parameter: + image : Image data + ''' + def display(self, image): + self.send_command(0x24) + for j in range(int(self.width / 8) - 1, -1, -1): + for i in range(0, self.height): + self.send_data(image[i + j * self.height]) + self.TurnOnDisplay() + + def display_fast(self, image): + self.send_command(0x24) + for j in range(int(self.width / 8) - 1, -1, -1): + for i in range(0, self.height): + self.send_data(image[i + j * self.height]) + self.TurnOnDisplay_Fast() + + ''' + function : Refresh a base image + parameter: + image : Image data + ''' + def Display_Base(self, image): + self.send_command(0x24) + for j in range(int(self.width / 8) - 1, -1, -1): + for i in range(0, self.height): + self.send_data(image[i + j * self.height]) + + self.send_command(0x26) + for j in range(int(self.width / 8) - 1, -1, -1): + for i in range(0, self.height): + self.send_data(image[i + j * self.height]) + + self.TurnOnDisplay() + + ''' + function : Sends the image buffer in RAM to e-Paper and partial refresh + parameter: + image : Image data + ''' + def displayPartial(self, image): + self.reset() + + self.send_command(0x3C) # BorderWavefrom + self.send_data(0x80) + + self.send_command(0x01) # Driver output control + self.send_data(0xF9) + self.send_data(0x00) + self.send_data(0x00) + + self.send_command(0x11) # data entry mode + self.send_data(0x07) + + self.SetWindows(0, 0, self.width-1, self.height-1) + self.SetCursor(0, 0) + + self.send_command(0x24) # WRITE_RAM + for j in range(int(self.width / 8) - 1, -1, -1): + for i in range(0, self.height): + self.send_data(image[i + j * self.height]) + self.TurnOnDisplayPart() + + ''' + function : Enter sleep mode + parameter: + ''' + def sleep(self): + self.send_command(0x10) #enter deep sleep + self.send_data(0x01) + self.delay_ms(100) + + +if __name__=='__main__': + epd = EPD_2in13_V4_Landscape() + epd.Clear() + + epd.fill(0xff) + epd.text("Waveshare", 0, 10, 0x00) + epd.text("ePaper-2.13_V4", 0, 20, 0x00) + epd.text("Raspberry Pico", 0, 30, 0x00) + epd.text("Hello World", 0, 40, 0x00) + epd.display(epd.buffer) + epd.delay_ms(2000) + + epd.vline(5, 55, 60, 0x00) + epd.vline(100, 55, 60, 0x00) + epd.hline(5, 55, 95, 0x00) + epd.hline(5, 115, 95, 0x00) + epd.line(5, 55, 100, 115, 0x00) + epd.line(100, 55, 5, 115, 0x00) + epd.display(epd.buffer) + epd.delay_ms(2000) + + epd.rect(130, 10, 40, 80, 0x00) + epd.fill_rect(190, 10, 40, 80, 0x00) + epd.Display_Base(epd.buffer) + epd.delay_ms(2000) + + epd.init() + for i in range(0, 10): + epd.fill_rect(175, 105, 10, 10, 0xff) + epd.text(str(i), 177, 106, 0x00) + epd.displayPartial(epd.buffer) + + print("sleep") + epd.init() + epd.Clear() + epd.delay_ms(2000) + epd.sleep() + + + + epd = EPD_2in13_V4_Portrait() + epd.Clear() + + epd.fill(0xff) + epd.text("Waveshare", 0, 10, 0x00) + epd.text("ePaper-2.13_V4", 0, 30, 0x00) + epd.text("Raspberry Pico", 0, 50, 0x00) + epd.text("Hello World", 0, 70, 0x00) + epd.display(epd.buffer) + epd.delay_ms(2000) + + epd.vline(10, 90, 60, 0x00) + epd.vline(90, 90, 60, 0x00) + epd.hline(10, 90, 80, 0x00) + epd.hline(10, 150, 80, 0x00) + epd.line(10, 90, 90, 150, 0x00) + epd.line(90, 90, 10, 150, 0x00) + epd.display(epd.buffer) + epd.delay_ms(2000) + + epd.rect(10, 180, 50, 40, 0x00) + epd.fill_rect(60, 180, 50, 40, 0x00) + epd.Display_Base(epd.buffer) + epd.delay_ms(2000) + + epd.init() + for i in range(0, 10): + epd.fill_rect(40, 230, 40, 10, 0xff) + epd.text(str(i), 60, 230, 0x00) + epd.displayPartial(epd.buffer) + + print("sleep") + epd.init() + epd.Clear() + epd.delay_ms(2000) + epd.sleep() \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..01c1afd --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# PicoWidget + +PicoWidget is a basic program written for a Raspberry Pi Pico W paired with a Waveshare 2.13" (v4) ePaper display. Note that the program is written for the black/white display which supports partial updates. + +## Use + +Install the MicroPython u2f file on to your Pico, then use Thonny or VSCode to upload the `.py` files and create an `ico` directory and upload the `bin` files there. + +Adjust the values in `config.py`: + - WIFI_SSID = Your WiFi SSID + - WIFI_PASS = Your WiFi Password + - LATLONG = (lat,long). + +Then just power the Pico and you'll have tour very own PicoWidget. + +## License + +This project incorporates files from `ezFBfont` which is licensed under the MIT license. + +This project incorporates font data from `ezFBfont` which was originally sourced from `Frederic Cambus` under the BSD-3-Clause license. + +Files sourced from other projects all contain licensing details within the files. + +All other files are released under the MIT license. \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..5e3e4e0 --- /dev/null +++ b/config.py @@ -0,0 +1,5 @@ +WIFI_SSID = "" +WIFI_PASS = "" + +WEATHER_URL = "https://api.open-meteo.com/" +LATLONG = (0.00,0.00) \ No newline at end of file diff --git a/ezFBfont.py b/ezFBfont.py new file mode 100644 index 0000000..edad5dc --- /dev/null +++ b/ezFBfont.py @@ -0,0 +1,179 @@ +# ezFBfont.py() : a simple string writer for small mono displays and user selected fonts. +# See MARQUEE.md for documentation + +# Extensively re-worked from the 'writer' class by +# Peter Hinch: +# https://github.com/peterhinch/micropython-font-to-py +# - Released under the MIT License (MIT). See LICENSE. +# - Copyright (c) 2019-2021 Peter Hinch + +import framebuf + +# Basic string writing class +class ezFBfont(): + + def __init__(self, device, + font, + fg = 1, + bg = 0, + tkey = -1, + halign = 'left', + valign = 'top', + vgap = 0, + hgap = 0, + split = '\n', + cswap = False, + verbose = False): + + self._device = device + self._font = font + self.name = self._font.__name__ + + # font and color; only monochrome HLSB fonts are supported + self._font_format = framebuf.MONO_HLSB + self._font_colors = 2 + self._palette_format = framebuf.RGB565 # support up to 65536 colors when blitting + # byte order for 16bit colors + self._cswap = cswap + # inform + if verbose: + fstr = '{} : initialised: height: {}, {} width: {}, baseline: {}' + print(fstr.format(self.name, self._font.height(), + 'fixed' if self._font.monospaced() else 'max', + self._font.max_width(), self._font.baseline())) + # apply init color and alignment as default + self.set_default(fg, bg, tkey, halign, valign, hgap, vgap, split, verbose) + + def _check_halign(self, h): + if h not in ('left','center','right'): + raise ValueError('Unknown horizontal alignment: ' + h) + return h + + def _check_valign(self, v): + if v not in ('top','center','baseline','bottom'): + raise ValueError('Unknown vertical alignment: ' + v) + return v + + def _line_size(self, string): + x = 0 + for char in string: + _, _, char_width = self._font.get_ch(char) + x += char_width + self.hgap if char_width > 0 else 0 + x = x - self.hgap if x != 0 else x # remove any trailing hgap + return x, self._font.height() + + def _swap_bytes(self, color): + # flip the left and right bytes in a 16 bit color word if required + return ((color & 255) << 8) + (color >> 8) if self._cswap else color + + def _put_char(self, char, x, y, fg, bg, tkey): + # fetch the glyph + glyph, char_height, char_width = self._font.get_ch(char) + if glyph is None: + return None, None # Nothing to write + # buffers + palette_buf = bytearray(self._font_colors * 2) + buf = bytearray(glyph) + # assemble color map + palette = framebuf.FrameBuffer(palette_buf, self._font_colors, 1, self._palette_format) + palette.pixel(0, 0, self._swap_bytes(bg)) + palette.pixel(self._font_colors -1, 0, self._swap_bytes(fg)) + # fetch and blit the glyph + charbuf = framebuf.FrameBuffer(buf, char_width, char_height, self._font_format) + self._device.blit(charbuf, x, y, tkey, palette) + return char_width, char_height + + def set_default(self, fg=None, bg=None, tkey=None, + halign=None, valign=None, hgap=None, vgap=None, split=None, verbose=None): + # Sets the default value for all supplied arguments + self.fg = self.fg if fg is None else fg + self.bg = self.bg if bg is None else bg + self.tkey = self.tkey if tkey is None else tkey + self.halign = self.halign if halign is None else self._check_halign(halign) + self.valign = self.valign if valign is None else self._check_valign(valign) + self.hgap = self.hgap if hgap is None else hgap + self.vgap = self.vgap if vgap is None else vgap + self.split = self.split if split is None else split + self._verbose = self._verbose if verbose is None else verbose + if self._verbose: + fstr = '{} = fg: {}, bg: {}, tkey: {}, halign: {}, valign: {}, hgap: {}, vgap: {}, split: {}' + print(fstr.format(self.name, self.fg, self.bg, self.tkey, + self.halign, self.valign, self.hgap, self.vgap, repr(split))) + + def size(self, string): + if len(string) == 0: + return 0, 0 + lines = string.split(self.split) + w = 0 + for line in lines: + x, _ = self._line_size(line) + w = max(w, x) # record the widest line + h = (len(lines) * (self._font.height() + self.vgap)) - self.vgap + return w, h + + def rect(self, string, x, y, halign=None, valign=None): + if len(string) == 0: + return x, y, 0, 0 + # apply alignment overrides + halign = self.halign if halign is None else self._check_halign(halign) + valign = self.valign if valign is None else self._check_valign(valign) + # get the x,y size of the rendered string + wide, high = self.size(string) + # apply alignment + xmin = x + if halign == 'center': + xmin = int(x - (wide / 2)) + elif halign == 'right': + xmin = x - wide + ymin = y + if valign == 'baseline': + ymin = y - self._font.baseline() + elif valign == 'center': + ymin = int(y - (high / 2)) + elif valign == 'bottom': + ymin = y - high + # return the result + return xmin,ymin,wide,high + + def write(self, string, x, y, fg=None, bg=None, tkey=None, + halign=None, valign=None): + if len(string) == 0: + return True + all_chars = True + # Argument overrides + fg = self.fg if fg is None else fg + bg = self.bg if bg is None else bg + tkey = self.tkey if tkey is None else tkey + halign = self.halign if halign is None else self._check_halign(halign) + valign = self.valign if valign is None else self._check_valign(valign) + # Break the string into lines + lines = string.split(self.split) + # vertical alignment + high = (len(lines) * (self._font.height() + self.vgap)) - self.vgap + ypos = y + if valign == 'baseline': + ypos = y - self._font.baseline() + 1 + elif valign == 'center': + ypos = int(y - (high / 2)) + elif valign == 'bottom': + ypos = y - high + for line in lines: + wide, high = self._line_size(line) + # horizontal alignment + if halign == 'left': + xpos = x + elif halign == 'right': + xpos = x - wide + else: + xpos = int(x - (wide / 2)) + # write the line + for char in line: + cx, _ = self._put_char(char, xpos, ypos, fg, bg, tkey) + if cx is None: + if self._verbose: + print('{}: missing char: {} (0x{:02X})'.format(self.name, repr(char), ord(char))) + all_chars = False + else: + xpos += cx + self.hgap + ypos += high + self.vgap + return all_chars \ No newline at end of file diff --git a/ezFBfont_spleen_16x32_ascii_30.py b/ezFBfont_spleen_16x32_ascii_30.py new file mode 100644 index 0000000..9bb5750 --- /dev/null +++ b/ezFBfont_spleen_16x32_ascii_30.py @@ -0,0 +1,171 @@ +''' + ezFBfont_spleen_16x32_ascii_30 : generated as part of the microPyEZfonts repository + https://github.com/easytarget/microPyEZfonts + + This font definition can be used with the "ezFBfont" class provided there. + It can also be used with the "writer" class from Peter Hinches micropython + font-to-py tool: https://github.com/peterhinch/micropython-font-to-py + + Original spleen_16x32.bdf font file was sourced from the U8G2 project: + https://github.com/olikraus/u8g2 + +''' +# Code generated by bdf2dict.py +# Font: spleen_16x32 +# Cmd: ['bdf2dict.py'], ['Latin-1-bdf-sources/spleen-16x32.bdf', '_', './ascii-char.set'] +# Date: 2024-07-31 14:57:34 + +# Customised by Fred Boniface 2024-11-18 +''' + Original Copyright, Comments and Notices from source: + + COPYRIGHT /* + COPYRIGHT * Spleen 16x32 1.9.1 + COPYRIGHT * Copyright (c) 2018-2022, Frederic Cambus + COPYRIGHT * https://www.cambus.net/ + COPYRIGHT * + COPYRIGHT * Created: 2018-08-12 + COPYRIGHT * Last Updated: 2020-10-10 + COPYRIGHT * + COPYRIGHT * Spleen is released under the BSD 2-Clause license. + COPYRIGHT * See LICENSE file for details. + COPYRIGHT * + COPYRIGHT * SPDX-License-Identifier: BSD-2-Clause + COPYRIGHT */ + + COMMENT "Copyright (c) 2018-2022, Frederic Cambus" +''' +version = '0.33' +name = '-misc-spleen-medium-r-normal--32-320-72-72-c-160-iso10646-1' +family = 'spleen' +weight = 'medium' +size = 32 + +def height(): + return 30 + +def baseline(): + return 24 + +def max_width(): + return 16 + +def hmap(): + return True + +def reverse(): + return False + +def monospaced(): + return True + +def min_ch(): + return 32 + +def max_ch(): + return 176 + +_g = { + 32:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 33:b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 34:b'\x00\x00\x00\x00\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 35:b'\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x18\x18\x18\x18\x18\x18\x7f\xfe\x7f\xfe\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x7f\xfe\x7f\xfe\x18\x18\x18\x18\x18\x18\x18\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 36:b'\x00\x00\x00\x00\x01\x80\x01\x80\x0f\xfc\x1f\xfc9\x801\x801\x801\x801\x809\x80\x1f\xf0\x0f\xf8\x01\x9c\x01\x8c\x01\x8c\x01\x8c\x01\x8c\x01\x8c\x01\x8c\x01\x9c?\xf8?\xf0\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00', + 37:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x0e\x0c\x1b\x18\x1b\x18\x1b0\x0e0\x00`\x00`\x00\xc0\x00\xc0\x01\x80\x01\x80\x03\x00\x03\x00\x068\x06l\x0cl\x0cl\x188\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 38:b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xe0\x0f\xf0\x1c8\x18\x18\x18\x18\x18\x18\x18\x18\x1c8\x0f\xf0\x07\xe0\x1f\x80?\xc0p\xec`|`8`0`8p|?\xee\x1f\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 39:b'\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 40:b'\x00\x00\x00<\x00\xfc\x01\xe0\x03\x80\x07\x00\x06\x00\x0e\x00\x0c\x00\x1c\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x1c\x00\x0c\x00\x0e\x00\x06\x00\x07\x00\x03\x80\x01\xe0\x00\xfc\x00<\x00\x00\x00\x00\x00\x00', + 41:b'\x00\x00<\x00?\x00\x07\x80\x01\xc0\x00\xe0\x00`\x00p\x000\x008\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x008\x000\x00p\x00`\x00\xe0\x01\xc0\x07\x80?\x00<\x00\x00\x00\x00\x00\x00\x00', + 42:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x1c8\x0ep\x07\xe0\x03\xc0\x03\xc0\x7f\xfe\x7f\xfe\x03\xc0\x03\xc0\x07\xe0\x0ep\x1c8\x18\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 43:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x1f\xf8\x1f\xf8\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 44:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x03\x80\x07\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 45:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 46:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 47:b'\x00\x00\x00\x06\x00\x06\x00\x0c\x00\x0c\x00\x18\x00\x18\x000\x000\x00`\x00`\x00\xc0\x00\xc0\x01\x80\x01\x80\x03\x00\x03\x00\x06\x00\x06\x00\x0c\x00\x0c\x00\x18\x00\x18\x000\x000\x00`\x00`\x00\x00\x00\x00\x00\x00\x00', + 48:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x1c0<0|0\xec1\xcc3\x8c7\x0c>\x0c<\x0c8\x0c0\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 49:b'\x00\x00\x00\x00\x00\x00\x00\x00\x03\x80\x07\x80\r\x80\x19\x80\x11\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x1f\xf8\x1f\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 50:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x18\x000\x00`\x00\xc0\x01\x80\x03\x00\x06\x00\x0c\x00\x18\x000\x0c0\x0c?\xfc?\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 51:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x18\x07\xf0\x07\xf0\x00\x18\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 52:b'\x00\x00\x00\x00\x00\x00\x00\x000\x000\x000\x000\x000000000000000000?\xfc?\xfc\x000\x000\x000\x000\x000\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 53:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc0\x0c0\x0c0\x000\x000\x000\x00?\xf0?\xf8\x00\x1c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 54:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x000\x000\x000\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 55:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc0\x0c0\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x18\x000\x00`\x00\xc0\x01\x80\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 56:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x0c0\x0c\x18\x18\x0f\xf0\x0f\xf0\x18\x180\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 57:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 58:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 59:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x03\x80\x07\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 60:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x008\x00p\x00\xe0\x01\xc0\x03\x80\x07\x00\x0e\x00\x1c\x008\x008\x00\x1c\x00\x0e\x00\x07\x00\x03\x80\x01\xc0\x00\xe0\x00p\x008\x00\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 61:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 62:b'\x00\x00\x00\x00\x00\x00\x00\x008\x00\x1c\x00\x0e\x00\x07\x00\x03\x80\x01\xc0\x00\xe0\x00p\x008\x00\x1c\x00\x1c\x008\x00p\x00\xe0\x01\xc0\x03\x80\x07\x00\x0e\x00\x1c\x008\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 63:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c\x00\x0c\x00\x0c\x00\x18\x000\x00`\x00\xc0\x00\xc0\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 64:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c1\xcc1\xcc1\xcc1\xcc1\xcc1\xcc1\xfc1\xfc0\x000\x008\x00\x1f\xf8\x0f\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 65:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c?\xfc?\xfc0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 66:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x18?\xf0?\xf00\x180\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x1c?\xf8?\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 67:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x008\x00\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 68:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x1c?\xf8?\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 69:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x000\x000\x000\x000\x00?\xe0?\xe00\x000\x000\x000\x000\x000\x008\x00\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 70:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x000\x000\x000\x000\x00?\xe0?\xe00\x000\x000\x000\x000\x000\x000\x000\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 71:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x000\x000\x000\x000\x000\xfc0\xfc0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 72:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c?\xfc?\xfc0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 73:b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\xf8\x1f\xf8\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x1f\xf8\x1f\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 74:b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\xf8\x1f\xf8\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x03\x80\x7f\x00~\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 75:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x18000`?\xc0?\xc00`000\x180\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 76:b'\x00\x00\x00\x00\x00\x00\x00\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x008\x00\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 77:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c8\x1c<<>|7\xec3\xcc1\x8c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 78:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c8\x0c8\x0c<\x0c<\x0c6\x0c6\x0c3\x0c3\x0c1\x8c1\x8c0\xcc0\xcc0l0l0<0<0\x1c0\x1c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 79:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 80:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x1c?\xf8?\xf00\x000\x000\x000\x000\x000\x000\x000\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 81:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c1\x8c1\x8c0\xcc8\xdc\x1f\xf8\x0f\xf0\x000\x000\x00\x18\x00\x18\x00\x00\x00\x00', + 82:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x18?\xf0?\xf00\x180\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 83:b'\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x000\x000\x000\x008\x00\x1f\xf0\x0f\xf8\x00\x1c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x1c?\xf8?\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 84:b'\x00\x00\x00\x00\x00\x00\x00\x00\x7f\xfe\x7f\xfe\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 85:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 86:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1c8\x0ep\x07\xe0\x03\xc0\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 87:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c1\x8c3\xcc7\xec>|<<8\x1c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 88:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1c8\x0ep\x07\xe0\x07\xe0\x0ep\x1c88\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 89:b'\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x1c?\xf8?\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 90:b'\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x18\x000\x00`\x00\xc0\x01\x80\x03\x00\x06\x00\x0c\x00\x18\x000\x000\x000\x00?\xfc?\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 91:b'\x0f\xfc\x0f\xfc\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0f\xfc\x0f\xfc\x00\x00\x00\x00', + 92:b'\x00\x00`\x00`\x000\x000\x00\x18\x00\x18\x00\x0c\x00\x0c\x00\x06\x00\x06\x00\x03\x00\x03\x00\x01\x80\x01\x80\x00\xc0\x00\xc0\x00`\x00`\x000\x000\x00\x18\x00\x18\x00\x0c\x00\x0c\x00\x06\x00\x06\x00\x00\x00\x00\x00\x00', + 93:b'?\xf0?\xf0\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000\x000?\xf0?\xf0\x00\x00\x00\x00', + 94:b'\x00\x00\x00\x00\x01\x80\x03\xc0\x07\xe0\x0ep\x1c88\x1cp\x0e`\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 95:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7f\xfe\x7f\xfe\x00\x00\x00\x00', + 96:b'\x00\x00\x00\x00\x06\x00\x07\x00\x03\x80\x01\xc0\x00\xe0\x00`\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 97:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\xf0\x1f\xf8\x00\x1c\x00\x0c\x00\x0c\x0f\xfc\x1f\xfc8\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 98:b'\x00\x00\x00\x00\x00\x00\x00\x000\x000\x000\x000\x000\x000\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x1c?\xf8?\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 99:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x000\x000\x000\x000\x000\x000\x008\x00\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 100:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x0f\xfc\x1f\xfc8\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 101:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x0c0\x0c0\x0c0\x0c?\xfc?\xfc0\x000\x000\x008\x00\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 102:b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\xf8\x03\xf8\x07\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x1f\xe0\x1f\xe0\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 103:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x18\x00\x0c\x00\x0c\x00\x1c?\xf8?\xf0', + 104:b'\x00\x00\x00\x00\x00\x00\x00\x000\x000\x000\x000\x000\x000\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 105:b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x07\x80\x07\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\xe0\x01\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 106:b'\x00\x00\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x03\x80\x1f\x00\x1e\x00', + 107:b'\x00\x00\x00\x00\x00\x00\x00\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x180\x18p\x18\xe0\x19\xc0\x1b\x80\x1f\x00\x1f\x00\x1b\x80\x19\xc0\x18\xe0\x18p\x188\x18\x1c\x18\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 108:b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x07\x00\x03\xf8\x01\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 109:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00>p>x1\x9c1\x8c1\x8c1\x8c1\x8c1\x8c1\x8c1\x8c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 110:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 111:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xf0\x1f\xf88\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1f\xf8\x0f\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 112:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xf0?\xf80\x1c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x1c?\xf8?\xf00\x000\x000\x000\x000\x000\x00', + 113:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c\x00\x0c', + 114:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x0c0\x0c0\x000\x000\x000\x000\x000\x000\x000\x000\x000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 115:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\xfc\x1f\xfc8\x000\x000\x008\x00\x1f\xf0\x0f\xf8\x00\x1c\x00\x0c\x00\x0c\x00\x1c?\xf8?\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 116:b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x1f\xe0\x1f\xe0\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x07\x00\x03\xf8\x01\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 117:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 118:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x1c\x1c8\x0ep\x07\xe0\x03\xc0\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 119:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c1\x8c1\x8c1\x8c1\x8c1\x8c1\x8c1\x8c9\x8c\x1e|\x0e|\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 120:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x0c8\x1c\x1c8\x0ep\x07\xe0\x03\xc0\x03\xc0\x07\xe0\x0ep\x1c8\x18\x188\x1c0\x0c0\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 121:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x000\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c0\x0c8\x0c\x1f\xfc\x0f\xfc\x00\x0c\x00\x0c\x00\x0c\x00\x1c?\xf8?\xf0', + 122:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00?\xfc?\xfc\x00\x0c\x00\x18\x000\x00`\x00\xc0\x01\x80\x03\x00\x06\x00\x0c\x00\x18\x00?\xfc?\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 123:b'\x00\xfc\x01\xfc\x03\x80\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x07\x00>\x00<\x00<\x00>\x00\x07\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x80\x01\xfc\x00\xfc\x00\x00\x00\x00', + 124:b'\x00\x00\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x00\x00\x00\x00\x00\x00\x00\x00', + 125:b'?\x00?\x80\x01\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xe0\x00|\x00<\x00<\x00|\x00\xe0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x01\xc0?\x80?\x00\x00\x00\x00\x00', + 126:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x0c\x1f\x0c;\x9c1\xf80\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 176:b'\x00\x00\x00\x00\x03\xc0\x07\xe0\x0ep\x0c0\x0c0\x0c0\x0ep\x07\xe0\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', +} + +def get_ch(ch): + c = ord(ch) + if c not in _g.keys(): + return None, 0, 0 + return memoryview(_g[c]), 30, 16 diff --git a/ezFBfont_spleen_8x16_ascii_14.py b/ezFBfont_spleen_8x16_ascii_14.py new file mode 100644 index 0000000..495efaa --- /dev/null +++ b/ezFBfont_spleen_8x16_ascii_14.py @@ -0,0 +1,171 @@ +''' + ezFBfont_spleen_8x16_ascii_14 : generated as part of the microPyEZfonts repository + https://github.com/easytarget/microPyEZfonts + + This font definition can be used with the "ezFBfont" class provided there. + It can also be used with the "writer" class from Peter Hinches micropython + font-to-py tool: https://github.com/peterhinch/micropython-font-to-py + + Original spleen_8x16.bdf font file was sourced from the U8G2 project: + https://github.com/olikraus/u8g2 + +''' +# Code generated by bdf2dict.py +# Font: spleen_8x16 +# Cmd: ['bdf2dict.py'], ['Latin-1-bdf-sources/spleen-8x16.bdf', '_', './ascii-char.set'] +# Date: 2024-07-31 14:57:38 + +# Customised by Fred Boniface 2024-11-18 +''' + Original Copyright, Comments and Notices from source: + + COPYRIGHT /* + COPYRIGHT * Spleen 8x16 1.9.1 + COPYRIGHT * Copyright (c) 2018-2022, Frederic Cambus + COPYRIGHT * https://www.cambus.net/ + COPYRIGHT * + COPYRIGHT * Created: 2018-08-11 + COPYRIGHT * Last Updated: 2020-10-10 + COPYRIGHT * + COPYRIGHT * Spleen is released under the BSD 2-Clause license. + COPYRIGHT * See LICENSE file for details. + COPYRIGHT * + COPYRIGHT * SPDX-License-Identifier: BSD-2-Clause + COPYRIGHT */ + + COMMENT "Copyright (c) 2018-2022, Frederic Cambus" +''' +version = '0.33' +name = '-misc-spleen-medium-r-normal--16-160-72-72-c-80-iso10646-1' +family = 'spleen' +weight = 'medium' +size = 16 + +def height(): + return 14 + +def baseline(): + return 11 + +def max_width(): + return 8 + +def hmap(): + return True + +def reverse(): + return False + +def monospaced(): + return True + +def min_ch(): + return 32 + +def max_ch(): + return 176 + +_g = { + 32:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 33:b'\x00\x18\x18\x18\x18\x18\x18\x18\x00\x18\x18\x00\x00\x00', + 34:b'ffff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 35:b'\x00ll\xfellll\xfell\x00\x00\x00', + 36:b'\x10~\xd0\xd0\xd0|\x16\x16\x16\x16\xfc\x10\x00\x00', + 37:b'\x00\x06fl\x0c\x18\x1806f`\x00\x00\x00', + 38:b'\x008lll8p\xda\xcc\xccz\x00\x00\x00', + 39:b'\x18\x18\x18\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 40:b'\x0e\x1800````00\x18\x0e\x00\x00', + 41:b'p\x18\x0c\x0c\x06\x06\x06\x06\x0c\x0c\x18p\x00\x00', + 42:b'\x00\x00\x00f<\x18\xff\x180000000000>\x00\x00', + 92:b'\xc0\xc0``00\x18\x18\x0c\x0c\x06\x06\x00\x00', + 93:b'|\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c\x0c|\x00\x00', + 94:b'\x108l\xc6\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 95:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xfe', + 96:b'0\x18\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00', + 97:b'\x00\x00\x00\x00|\x06~\xc6\xc6\xc6~\x00\x00\x00', + 98:b'\x00\xc0\xc0\xc0\xfc\xc6\xc6\xc6\xc6\xc6\xfc\x00\x00\x00', + 99:b'\x00\x00\x00\x00~\xc0\xc0\xc0\xc0\xc0~\x00\x00\x00', + 100:b'\x00\x06\x06\x06~\xc6\xc6\xc6\xc6\xc6~\x00\x00\x00', + 101:b'\x00\x00\x00\x00~\xc6\xc6\xfe\xc0\xc0~\x00\x00\x00', + 102:b'\x00\x1e000|00000\x00\x00\x00', + 103:b'\x00\x00\x00\x00~\xc6\xc6\xc6\xc6\xc6|\x06\x06\xfc', + 104:b'\x00\xc0\xc0\xc0\xfc\xc6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00', + 105:b'\x00\x18\x18\x008\x18\x18\x18\x18\x18\x1c\x00\x00\x00', + 106:b'\x00\x18\x18\x00\x18\x18\x18\x18\x18\x18\x18\x18\x18p', + 107:b'\x00\xc0\xc0\xc0\xcc\xd8\xf0\xf0\xd8\xcc\xc6\x00\x00\x00', + 108:b'\x00000000000\x1c\x00\x00\x00', + 109:b'\x00\x00\x00\x00\xec\xd6\xd6\xd6\xd6\xc6\xc6\x00\x00\x00', + 110:b'\x00\x00\x00\x00\xfc\xc6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00', + 111:b'\x00\x00\x00\x00|\xc6\xc6\xc6\xc6\xc6|\x00\x00\x00', + 112:b'\x00\x00\x00\x00\xfc\xc6\xc6\xc6\xc6\xc6\xfc\xc0\xc0\xc0', + 113:b'\x00\x00\x00\x00~\xc6\xc6\xc6\xc6\xc6~\x06\x06\x06', + 114:b'\x00\x00\x00\x00~\xc6\xc0\xc0\xc0\xc0\xc0\x00\x00\x00', + 115:b'\x00\x00\x00\x00~\xc0\xc0|\x06\x06\xfc\x00\x00\x00', + 116:b'\x00000|00000\x1e\x00\x00\x00', + 117:b'\x00\x00\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6~\x00\x00\x00', + 118:b'\x00\x00\x00\x00\xc6\xc6\xc6\xc6l8\x10\x00\x00\x00', + 119:b'\x00\x00\x00\x00\xc6\xc6\xd6\xd6\xd6\xd6n\x00\x00\x00', + 120:b'\x00\x00\x00\x00\xc6l88l\xc6\xc6\x00\x00\x00', + 121:b'\x00\x00\x00\x00\xc6\xc6\xc6\xc6\xc6\xc6~\x06\x06\xfc', + 122:b'\x00\x00\x00\x00\xfe\x06\x0c\x180`\xfe\x00\x00\x00', + 123:b'\x0e\x18\x18\x18\x18pp\x18\x18\x18\x18\x0e\x00\x00', + 124:b'\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x18\x00\x00', + 125:b'p\x18\x18\x18\x18\x0e\x0e\x18\x18\x18\x18p\x00\x00', + 126:b'\x00\x00\x00\x00\x002~L\x00\x00\x00\x00\x00\x00', + 176:b'8ll8\x00\x00\x00\x00\x00\x00\x00\x00', +} + +def get_ch(ch): + c = ord(ch) + if c not in _g.keys(): + return None, 0, 0 + return memoryview(_g[c]), 14, 8 diff --git a/ico/weather-cloudy.bin b/ico/weather-cloudy.bin new file mode 100644 index 0000000000000000000000000000000000000000..b7357e8ffd9dec2a0b788b20b76c71352d5f3c57 GIT binary patch literal 648 zcmd6i!41PO2t~0t_?Qvcq~0v`Xh>beBjo5q6v_uT>IN+m=n0$uf#_d?O>>pajs&qY z!OU*xAoh*|s=aIkoYVrgNfPris7^dJB@Uryrvj)@0|?;qY$&^@0UksptR2{(0f|^a zPO70L7rB#%Yi#Lf>Xy+_(S(se?U0U+&n&1P;4jelQ|6j-7^{;O#4}C(vRuQx**ZRn XL&hizlx}EASQp_})_}MrJbql>FGzZf literal 0 HcmV?d00001 diff --git a/ico/weather-fog.bin b/ico/weather-fog.bin new file mode 100644 index 0000000000000000000000000000000000000000..5d5c6a6344a10bf62ba9441fc1f63d65e97c02eb GIT binary patch literal 648 zcmb`DF%H8Z3`K1*FmeRnq};4U>dUyx?@la>pAHJ7l7u zrXdAT3_3;El7*71Lj#sp(gI6|D%Buq8-N! literal 0 HcmV?d00001 diff --git a/ico/weather-hail.bin b/ico/weather-hail.bin new file mode 100644 index 0000000000000000000000000000000000000000..e6d909e58f9554f79ee57bfb4ebbe1eca328d108 GIT binary patch literal 648 zcmbV|F>VAQ3`LD7C^-U0@x`LR-po=i!V#vV7_IkzL!vY(vX&U0{@C9(B5y`^+m+R? z4eIv>cmIJwN>GeY{L5#+Ej^$vX;I&%l*M0alEbJENrXZ-LL)qJIzOK{(GidWR3tk+ zU{QP$ZYklVPeCU{^HFy{x5gAG)@;MvAbPbyMd~+{b(H)IEOOF9RRII0)JZ@KZk5+r zuPlr~rLOD&dS$<$RG-NgsxXH5f)7DCE7xrSShsNK8oP*mfFt-0jKr5={0;4Ka@0;$ YpU21JK~ViK=#vc*YH?xMao~;X1u7ngWB>pF literal 0 HcmV?d00001 diff --git a/ico/weather-hazy.bin b/ico/weather-hazy.bin new file mode 100644 index 0000000000000000000000000000000000000000..08f0a3c039e26be2b157e032ab7cecb33242f53f GIT binary patch literal 648 zcmb`BF%H8Z3`LC?JaU9wq!KqPa5Ij=)|D{>;@1udQdKExqIy72pYQ)o^uKu=QO_^H zt{B8Y7J0x_$PD3^w}^=|(NbmIRe_aJ+^UL0RdB_lu|f{$Ry-~P*dXBrD;bYc F_6GQof|vjR literal 0 HcmV?d00001 diff --git a/ico/weather-lightning-rainy.bin b/ico/weather-lightning-rainy.bin new file mode 100644 index 0000000000000000000000000000000000000000..5330281f1b2e4ceb363e1cd1cefcf7c8e53e515e GIT binary patch literal 648 zcmb`E&27XW5QPVEAU7IVWg3Y>E2$)SJ7cwtPy$B|mM8BGUgsVK3H#Ihyy1b!A6K-t zI;)L>eL4)4Ibh#UW2o@7FGAGF6&H7pj{CLZTW$2?uuN=NsE6esTcw zfN0bx8=AfdyCh~r55XjYNnZ1;VWdZzwgr8Gl%X3VC_N!f02?DgAr&Tye*q~(x|@j; zm8G*7mI!I$7`0Il-fY7;Mmz|?dT`)<0$?8dBiP81c7shtr8_HkdPz1)!=F$cqV?Jt o5pA|0Kh9Ul_Y?F2NUmZGad>~h?E*PX_0K628J1Ivkyl= zotX*SgkzroaG^*GBUOm_K^xdD52S8B^1WH?TFTYJIA|@l2vMgAE*yMTy}rEZTi_*7m>6&vn?d8(S-{4s2}Q^V*mPYdI-9q6Fp|byB3MS_!u2oo Y@#gRg<&3;%`}!OAbLn>CapIrnUx*ukPyhe` literal 0 HcmV?d00001 diff --git a/ico/weather-night-partly-cloudy.bin b/ico/weather-night-partly-cloudy.bin new file mode 100644 index 0000000000000000000000000000000000000000..733a750dba76af99b2540e07535a8128bfa553c7 GIT binary patch literal 648 zcmbV|F>b>!3`Hdfh(;cPNAc0*PQ8|Z3>|ZX8ps7adPu_lPl-l=0v$>(5A^@!r&9W} zF7a2_NlQQrus(kU#mZmW~_)*WPP z+XNkc*>(8k&|wq%H1~x*t3wV*Y3Z)P kqBj`#qch%5dmhh*H^ZH*6*0U{U{n??;^60ynBhP510(3WTmS$7 literal 0 HcmV?d00001 diff --git a/ico/weather-night.bin b/ico/weather-night.bin new file mode 100644 index 0000000000000000000000000000000000000000..fad8d934d39f30f43d7f1f81f7c193abb3e5271a GIT binary patch literal 648 zcmbV}F>=Hp3`IpKC}~q<#yLhhT`4D6IGT^b5nQ_1vGOEvigX)|i^rGH7qZB!(bt8v z|Al-jd8|~K@GkWk$|IYFWDC*_*~F|lpzQ&t z=#YY`Z3`W6Y!IIfPqj0Tsl+KHB6prAMg(CO?aD0io1O@p70v~=P@^72Z%yfT3 zUIuB&3*hj`r$Hw2!#&eZEDgWxXdVmb8NQ?%P*jIgRh<4aYMDF?Sf25!-!UeXap|CAPR zm+HOpSsVx`i;zr8wiYWQ-s|=a-6prE7kFs~NWK$Y%*3SE~3`WHeY)A*jCAa|3P#Vw~Gj3!{xNw8@%1?s53mn)4$6Nh;`oxL+?}W#y zi-$otK(7*s{fMe@7%+_Dtse{nQa`I3O8r|^kx8A~AOc_0qFz!FM*oyD z;UUSr65kj@YQaWJG>Zul_nL30wz;LA;G?#c>zVa;fJhw$ts1snU^>J%i;x8-w;^m^M;RK K@P0@Bx_Ro&_X`Gry_$#QZDxQe0##XDwn;HU-41m_b^C)u+4zF<@(Mh{B3`1L=S#GJU8;L! zyikUz6AD}0YvmC4+AdJr+}z*bid-Q1pK$xw2}g_&3fr%L2@4Ir32UbC?*(jJxBLXztJK(Ko1{~F*_nKwZqFIYf{?rYN48HKCD#Nb@iUY~_ RU)WP^@htK#n#3j?_yxH;yjK7K literal 0 HcmV?d00001 diff --git a/ico/weather-partly-snowy.bin b/ico/weather-partly-snowy.bin new file mode 100644 index 0000000000000000000000000000000000000000..fdfb7b0c45cbaf286f2082b0e622955c53ddf953 GIT binary patch literal 648 zcma)(F>=Kq3`Ccqpveq5!RIF7X2HH0Gr0&y@Jt#C=PN4-ev>9H@UtYeD;fCT6RuSk zrUK8n;T3^L2Jmh*1baeX02C|XXS+QXL>!VxH_9Tzpoq9=y{liVBRjO7{C-1l5QL}Q z;wkZyFq zkj*RO$t-S2yb{%7W)t&THZ+}FQ!g+@4iIc7oZe@mjI{uTed{dTa6}WPM};?b>k1F< zx@*mc!HvhK0vi?z5CnxPwL`8VSEEgtSuUNmIk0RpMN#BgWzYtaRyLb(|1a2jI0onF U`r@ws!gsd};y4zp0Q4ULziYp`z5oCK literal 0 HcmV?d00001 diff --git a/ico/weather-pouring.bin b/ico/weather-pouring.bin new file mode 100644 index 0000000000000000000000000000000000000000..9d7f1f4a8e79db2a35c21d7f3c9dfcabe95d65a5 GIT binary patch literal 648 zcmbu+L2d&f3;;kAOZCxD;3HM}S(YdtBkD)oA8_;mEwv5FrBV+q63gJlW?2CK*l@O$ z$+IuyITq%eFH~2uNOREqoDvvN0<4fDKMRohtI`0Y&`}{d$g4TB z08Xz_swIhI07b2Qg6(9@YVg``w(LtR(sEmn1t>jNkJ!=;(hYnf>0TV@4Ba50Tosjl za&xHRp``~QG2^~TpkE*>39xOXy78ak8>aM;1ONa4 literal 0 HcmV?d00001 diff --git a/ico/weather-rainy.bin b/ico/weather-rainy.bin new file mode 100644 index 0000000000000000000000000000000000000000..db7f044a6734123cdbf6354616193ea6689d0ef6 GIT binary patch literal 648 zcmbV~L2AS>3`J$U@X{A3_$Kwu>V#g*FpFNpN61nO6ZL;N6PQ9bBa-kU$@-EKkvA<` zUnjF23yRGJX7&mJo=#V?hYg2QnnPA8e!dJz%24eRy-?dE5E82q3gN`v`1r!@ih$ry zMWV4r-EfFi>y+fR>M2lyp+;VM)*KbJ&(ep58ILN{;h($ literal 0 HcmV?d00001 diff --git a/ico/weather-snowy-heavy.bin b/ico/weather-snowy-heavy.bin new file mode 100644 index 0000000000000000000000000000000000000000..c184c852ffe92023d5280c835b6100b61ad365db GIT binary patch literal 648 zcmaKp!EwbP3`E6n(2){I899YkQbG#I#O@@mkWTm=@K533v9c@hpUEL%#`g0ft%MNy zZ$+zfJ6WHQ?U=BcO|S(4P${W>S#vm~IHV!T^K%m&+$vR~8*RG;g3^tU2`8S7uMa#% zco7L+x`;%hN7(`RN*SW=l}|w>EO@MXR#DQGn(Kr%K}yd%6@)I(0Z33Hfg+$f1i%{= zz-<(VbUO%fq`MJx=-mvEdh8AvfI02KTIdDi;XI!%K$}#A{Q`JOV=&I5=0}8;S%dx? zx`e58(R~Fc0MZWqoNy{T5&gcPpV8;1{XP0bwLK%AF^!26lGlwDEv*YLs{whyV=G)S U;#qKzv9iSPfyyPwEC2pnKRFe*1^@s6 literal 0 HcmV?d00001 diff --git a/ico/weather-snowy-rainy.bin b/ico/weather-snowy-rainy.bin new file mode 100644 index 0000000000000000000000000000000000000000..e26c0559d29ddc7b451cb49664b8a431514619d6 GIT binary patch literal 648 zcmb`^!EFQ~3Qh#`SHk`6&9LM6_ATF}xWC6$RfA-ScU5u`4J4n#ye!Jz9%KJm6t zxr?0mHS)wmiO|DC6^P9@LjpF)X>~2*dU&6g69RnRIWBO!b6nv1#4JIz+eJmyW-|db ta!N+hiOfSxHb5iWtNh}5tVkOxr@o(fu{si}SY=|qj>qe)_m=zio)FPn8-e;S;toRz&{! z1T?QqI2;+L1Hgr<+PKpc5nt$rQ;H!C$yT4c#G#?mB-lasDw`teX^O0P^1bV4=W885 zw1LKn06n8r7JVvCi5ijo6n0WTlJ`SnOWSF=Il4p2k*n@7bs~<5u&|Ap{EBW>VR#E6 ztSKkcYsAU}1BXbBLkAphsU`FcvTK#%3=P!a@c77IQa;iD&ZS#j z2f$mIaBJj{8kj<^3BP=^6mcR7MV|XY6jw&}t12Ev!RLsL71E5|NsUhcD%|m`zzYbA znz@~3@e|l6R*gtlNPwzB>N a*>uvOSnkPV5gdflgQGE1i~D4|!}1rCQtZtKAF%g-e84XM;{mfx%>f1r83u*|J}{R5|NjRJLzOcy dNJua+$XGBi$W<^f@PA-nV1EEH4`enP005)Q8D;QtZtKAF%g-e84XM;{mfx%>f1r83u*|J}{R5|NjRJLzN@U bV1K~C!2f}PL9T*> bit_index) & 1 + + framebuffer.pixel(x + col, y + row, bit) + +# Load weather data into buffer +def update_weather(): + # Get and load weather data + weather = open_meteo.get_weather() + blit_to_framebuffer(epd, epd.width, 3, 5, weather['icon'], 72, 72) + spleen30wht.write(f"{str(weather['current_temp'])} C", 3, 82) + spleen14wht.write(f"Min: {str(weather['min'])} C", 82, 10) + spleen14wht.write(f"Max: {str(weather['max'])} C", 82, 25) + #epd.image1Gray.text("Apparent min: " + str(weather['apparent_min']) + "C", 75, 25, WHITE) + #epd.image1Gray.text("Apparent max: " + str(weather['apparent_max']) + "C", 75, 35, WHITE) + return + +def get_day(day_no): + days = ("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday") + return days[day_no] + +## Init +spleen30blk.write("PicoWidget-v0.1", 5, 5) +epd.text("Initialising", 5, 40, BLK) +epd.display(epd.buffer) +epd.displayPartial(epd.buffer) + +wlan = network.WLAN(network.STA_IF) +wlan.active(True) +wlan.connect(config.WIFI_SSID, config.WIFI_PASS) +max_attempts = 10 +attempt = 0 +while not wlan.isconnected() and attempt < max_attempts: + utime.sleep(1) + attempt += 1 + +if wlan.isconnected(): + epd.text(f"WiFi Connection ({config.WIFI_SSID}): OK", 5, 52, BLK) +else: + epd.text(f"WiFi Connection ({config.WiFi_SSID}): FAIL", 5, 52, BLK) + +try: + update_time() + epd.text(f"NTP Update: OK", 5, 64, BLK) +except Exception as e: + epd.text(f"NTP Update: FAIL", 5, 64, BLK) + print(e) + +epd.text(f"Location: {config.LATLONG}", 5, 76, BLK) +epd.text(f"Buffering display...", 5, 88, BLK) +epd.displayPartial(epd.buffer) + +tick = 0 +displayed_time = None +displayed_msg = None + +while True: + partial_display_needed = False + full_display_needed = False + + if tick % 360 == 0: + epd.fill(BLK) + update_weather() + full_display_needed = True + update_time() + + t = utime.localtime() + t_str = "{:02}:{:02}".format(t[3], t[4]) + d_str = "{:02}/{:02}/{:4}".format(t[2], t[1], t[0]) + if t_str != displayed_time or full_display_needed: + spleen30wht.write(t_str, 160, 60) + spleen14wht.write(get_day(t[6]), 160, 90) + spleen14wht.write(d_str, 160, 110) + displayed_time = t_str + partial_display_needed = True + + if not full_display_needed: + if partial_display_needed: + epd.displayPartial(epd.buffer) + + if full_display_needed: + epd.display(epd.buffer) + epd.displayPartial(epd.buffer) + + tick += 1 + if tick >= 7200: + tick = 0 + + utime.sleep(20) \ No newline at end of file diff --git a/open_meteo.py b/open_meteo.py new file mode 100644 index 0000000..c56befb --- /dev/null +++ b/open_meteo.py @@ -0,0 +1,63 @@ +#Copyright 2024 Frederick Boniface +# +#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +# +#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +# +#THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +import urequests + +import config + +def get_weather(): + URL = f"https://api.open-meteo.com/v1/forecast?latitude={config.LATLONG[0]}&longitude={config.LATLONG[1]}¤t=temperature_2m&daily=weather_code,temperature_2m_max,temperature_2m_min,sunrise,sunset,wind_speed_10m_max,wind_gusts_10m_max&timezone=Europe%2FLondon&forecast_days=1" + res = urequests.get(URL) + return parse_weather(res.json()) + +def parse_weather(weather): + output = { + "current_temp": weather['current']['temperature_2m'], + "max": weather['daily']['temperature_2m_max'][0], + "min": weather['daily']['temperature_2m_min'][0], + "sunrise": weather['daily']['sunrise'][0], + "sunset": weather['daily']['sunset'][0], + "icon": get_weather_icon(weather['daily']['weather_code'][0]), + } + return output + +def get_weather_icon(code): + with open (weather_icon_paths[code], "rb") as file: + raw_bytes = file.read() + return raw_bytes + +weather_icon_paths = { + 0: "/ico/weather-sunny.bin", #Clear Sky + 1: "/ico/weather-sunny.bin", #Mainly Clear + 2: "/ico/weather-partly-cloudy.bin", #Partly Cloudy + 3: "/ico/weather-cloudy.bin", #Overcast + 45: "/ico/weather-fog.bin", #Fog + 48: "/ico/weather-fog.bin", #Depositing Rime Fog + 51: "/ico/weather-partly-rainy.bin", #Light Drizzle + 53: "/ico/weather-partly-rainy.bin", #Moderate Drizzle + 55: "/ico/weather-partly-rainy.bin", #Dense Drizzle + 56: "/ico/weather-snowy-rainy.bin", #Light Freezing Drizzle + 57: "/ico/weather-snowy-rainy.bin", #Dense Freezing Drizzle + 61: "/ico/weather-rainy.bin", #Light Rain + 63: "/ico/weather-rainy.bin", #Moderate Rain + 65: "/ico/weather-pouring.bin", #Heavy Rain + 66: "/ico/weather-hail.bin", #Light Freezing Rain + 67: "/ico/weather-hail.bin", #Heavy Freezing Rain + 71: "/ico/weather-snowy.bin", #Slight Snowfall + 73: "/ico/weather-snowy.bin", #Moderate Snowfall + 75: "/ico/weather-snowy-heavy.bin", #Heavy Snowfall + 77: "/ico/weather-snowy-heavy.bin", #Snow Grains + 80: "/ico/weather-partly-rainy.bin", #Slight Rain Showers + 81: "/ico/weather-partly-rainy.bin", #Moderate Rain Showers + 82: "/ico/weather-pouring.bin", #Violent Rain Showers + 85: "/ico/weather-partly-snowy.bin", #Slight Snow Showers + 86: "/ico/weather-partly-snowy.bin", #Heavy Snow Showers + 95: "/ico/weather-partly-lightning.bin", #Slight or Moderate Thunderstorm + 96: "/ico/weather-lightning.bin", #Thunderstorm with Slight Hail + 99: "/ico/weather-lightning-rainy.bin", #Thunderstorm with Heavy Hail +} \ No newline at end of file