This commit is contained in:
Fred Boniface 2024-11-19 20:19:14 +00:00
commit abadccf612
30 changed files with 1471 additions and 0 deletions

7
LICENSE Normal file
View File

@ -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.

720
Pico_ePaper_2_13_v4.py Normal file
View File

@ -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()

24
README.md Normal file
View File

@ -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.

5
config.py Normal file
View File

@ -0,0 +1,5 @@
WIFI_SSID = ""
WIFI_PASS = ""
WEATHER_URL = "https://api.open-meteo.com/"
LATLONG = (0.00,0.00)

179
ezFBfont.py Normal file
View File

@ -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

View File

@ -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

View File

@ -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\x18<f\x00\x00\x00\x00',
43:b'\x00\x00\x00\x00\x18\x18~\x18\x18\x00\x00\x00\x00\x00',
44:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x180\x00\x00',
45:b'\x00\x00\x00\x00\x00\x00~\x00\x00\x00\x00\x00\x00\x00',
46:b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x18\x18\x00\x00\x00',
47:b'\x06\x06\x0c\x0c\x18\x1800``\xc0\xc0\x00\x00',
48:b'\x00|\xc6\xc6\xce\xde\xf6\xe6\xc6\xc6|\x00\x00\x00',
49:b'\x00\x188xX\x18\x18\x18\x18\x18~\x00\x00\x00',
50:b'\x00|\xc6\x06\x06\x0c\x180`\xc6\xfe\x00\x00\x00',
51:b'\x00|\xc6\x06\x06<\x06\x06\x06\xc6|\x00\x00\x00',
52:b'\x00\xc0\xc0\xcc\xcc\xcc\xcc\xfe\x0c\x0c\x0c\x00\x00\x00',
53:b'\x00\xfe\xc6\xc0\xc0\xfc\x06\x06\x06\xc6|\x00\x00\x00',
54:b'\x00|\xc6\xc0\xc0\xfc\xc6\xc6\xc6\xc6|\x00\x00\x00',
55:b'\x00\xfe\xc6\x06\x06\x0c\x180000\x00\x00\x00',
56:b'\x00|\xc6\xc6\xc6|\xc6\xc6\xc6\xc6|\x00\x00\x00',
57:b'\x00|\xc6\xc6\xc6\xc6~\x06\x06\xc6|\x00\x00\x00',
58:b'\x00\x00\x00\x00\x18\x18\x00\x00\x00\x18\x18\x00\x00\x00',
59:b'\x00\x00\x00\x00\x18\x18\x00\x00\x00\x18\x180\x00\x00',
60:b'\x00\x06\x0c\x180``0\x18\x0c\x06\x00\x00\x00',
61:b'\x00\x00\x00\x00~\x00\x00~\x00\x00\x00\x00\x00\x00',
62:b'\x00`0\x18\x0c\x06\x06\x0c\x180`\x00\x00\x00',
63:b'\x00|\xc6\x06\x0c\x1800\x0000\x00\x00\x00',
64:b'\x00\x00|\xc2\xda\xda\xda\xda\xde\xc0|\x00\x00\x00',
65:b'\x00|\xc6\xc6\xc6\xfe\xc6\xc6\xc6\xc6\xc6\x00\x00\x00',
66:b'\x00\xfc\xc6\xc6\xc6\xfc\xc6\xc6\xc6\xc6\xfc\x00\x00\x00',
67:b'\x00~\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0~\x00\x00\x00',
68:b'\x00\xfc\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xfc\x00\x00\x00',
69:b'\x00~\xc0\xc0\xc0\xf8\xc0\xc0\xc0\xc0~\x00\x00\x00',
70:b'\x00~\xc0\xc0\xc0\xf8\xc0\xc0\xc0\xc0\xc0\x00\x00\x00',
71:b'\x00~\xc0\xc0\xc0\xde\xc6\xc6\xc6\xc6~\x00\x00\x00',
72:b'\x00\xc6\xc6\xc6\xc6\xfe\xc6\xc6\xc6\xc6\xc6\x00\x00\x00',
73:b'\x00~\x18\x18\x18\x18\x18\x18\x18\x18~\x00\x00\x00',
74:b'\x00~\x18\x18\x18\x18\x18\x18\x18\x18\xf0\x00\x00\x00',
75:b'\x00\xc6\xc6\xc6\xcc\xf8\xcc\xc6\xc6\xc6\xc6\x00\x00\x00',
76:b'\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0~\x00\x00\x00',
77:b'\x00\xc6\xee\xfe\xd6\xc6\xc6\xc6\xc6\xc6\xc6\x00\x00\x00',
78:b'\x00\xc6\xc6\xe6\xe6\xd6\xd6\xce\xce\xc6\xc6\x00\x00\x00',
79:b'\x00|\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6|\x00\x00\x00',
80:b'\x00\xfc\xc6\xc6\xc6\xfc\xc0\xc0\xc0\xc0\xc0\x00\x00\x00',
81:b'\x00|\xc6\xc6\xc6\xc6\xc6\xc6\xd6\xd6|\x18\x0c\x00',
82:b'\x00\xfc\xc6\xc6\xc6\xfc\xc6\xc6\xc6\xc6\xc6\x00\x00\x00',
83:b'\x00~\xc0\xc0\xc0|\x06\x06\x06\x06\xfc\x00\x00\x00',
84:b'\x00\xff\x18\x18\x18\x18\x18\x18\x18\x18\x18\x00\x00\x00',
85:b'\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6\xc6~\x00\x00\x00',
86:b'\x00\xc6\xc6\xc6\xc6\xc6\xc6\xc6l8\x10\x00\x00\x00',
87:b'\x00\xc6\xc6\xc6\xc6\xc6\xc6\xd6\xfe\xee\xc6\x00\x00\x00',
88:b'\x00\xc6\xc6\xc6l8l\xc6\xc6\xc6\xc6\x00\x00\x00',
89:b'\x00\xc6\xc6\xc6\xc6~\x06\x06\x06\x06\xfc\x00\x00\x00',
90:b'\x00\xfe\x06\x06\x0c\x180`\xc0\xc0\xfe\x00\x00\x00',
91:b'>0000000000>\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

BIN
ico/weather-cloudy.bin Normal file

Binary file not shown.

BIN
ico/weather-fog.bin Normal file

Binary file not shown.

BIN
ico/weather-hail.bin Normal file

Binary file not shown.

BIN
ico/weather-hazy.bin Normal file

Binary file not shown.

Binary file not shown.

BIN
ico/weather-lightning.bin Normal file

Binary file not shown.

Binary file not shown.

BIN
ico/weather-night.bin Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ico/weather-pouring.bin Normal file

Binary file not shown.

BIN
ico/weather-rainy.bin Normal file

Binary file not shown.

BIN
ico/weather-snowy-heavy.bin Normal file

Binary file not shown.

BIN
ico/weather-snowy-rainy.bin Normal file

Binary file not shown.

BIN
ico/weather-snowy.bin Normal file

Binary file not shown.

BIN
ico/weather-sunny.bin Normal file

Binary file not shown.

BIN
ico/weather-sunset-down.bin Normal file

Binary file not shown.

BIN
ico/weather-sunset-up.bin Normal file

Binary file not shown.

131
main.py Normal file
View File

@ -0,0 +1,131 @@
#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.
from Pico_ePaper_2_13_v4 import EPD_2in13_V4_Landscape
from ezFBfont import ezFBfont
import ezFBfont_spleen_16x32_ascii_30 as spleen30Font
import ezFBfont_spleen_8x16_ascii_14 as spleen14Font
import ntptime, network, utime
import config, open_meteo
epd = EPD_2in13_V4_Landscape()
WHT = 0xff
BLK = 0x00
spleen30wht = ezFBfont(epd, spleen30Font, fg=WHT, bg=BLK)
spleen30blk = ezFBfont(epd, spleen30Font, fg=BLK, bg=WHT)
spleen14wht = ezFBfont(epd, spleen14Font, fg=WHT, bg=BLK)
spleen14blk = ezFBfont(epd, spleen14Font, fg=BLK, bg=WHT)
epd.fill(WHT)
epd.display(epd.buffer)
## Functions
def update_time():
try:
ntptime.host = "time.fjla.net"
ntptime.settime()
except Exception as e:
raise e
# Load icon into buffer
def blit_to_framebuffer(framebuffer, fb_width, x, y, image_bytes, img_width, img_height):
for row in range(img_height):
for col in range(img_width):
byte_index = (row * (img_width // 8)) + (col // 8)
bit_index = 7 - (col % 8)
bit = (image_bytes[byte_index] >> 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)

63
open_meteo.py Normal file
View File

@ -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]}&current=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
}