v.0.10.0 redesign prj structure, multi dev containers
This commit is contained in:
34
pico-client/.devcontainer/Dockerfile
Normal file
34
pico-client/.devcontainer/Dockerfile
Normal file
@@ -0,0 +1,34 @@
|
||||
#Moin from VSCode
|
||||
|
||||
# FROM python:3.13-slim
|
||||
# RUN apt-get update && apt-get install -y \
|
||||
# git \
|
||||
# curl \
|
||||
# wget \
|
||||
# && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
FROM python:3.13-alpine
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
curl \
|
||||
wget \
|
||||
bash \
|
||||
build-base \
|
||||
linux-headers \
|
||||
# Fish Shell hinzufügen
|
||||
fish
|
||||
|
||||
# mpremote Symlink in postCreateCommand erstellen
|
||||
# Oder direkt in der Dockerfile:
|
||||
RUN pip install mpremote && \
|
||||
ln -sf /usr/local/bin/mpremote /usr/bin/mpremote || true
|
||||
|
||||
# # Optional: Benutzer erstellen
|
||||
RUN adduser -D -s /bin/bash vscode
|
||||
USER vscode
|
||||
|
||||
WORKDIR /workspace
|
||||
|
||||
# Standard Shell auf Fish setzen (optional)
|
||||
SHELL ["/usr/bin/fish", "-c"]
|
||||
45
pico-client/.devcontainer/devcontainer.json
Normal file
45
pico-client/.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,45 @@
|
||||
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
||||
{
|
||||
"name": "Python 3",
|
||||
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
||||
"build": {
|
||||
"dockerfile": "Dockerfile"
|
||||
},
|
||||
// "image": "mcr.microsoft.com/devcontainers/python:1-3.12",
|
||||
// "image": "python:latest",
|
||||
//"image": "python:3.13-slim",
|
||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||
// "features": {},
|
||||
// Configure tool-specific properties.
|
||||
"customizations": {
|
||||
// Configure properties specific to VS Code.
|
||||
"vscode": {
|
||||
"settings": {},
|
||||
"extensions": [
|
||||
"ms-python.python",
|
||||
"frhtylcn.pythonsnippets",
|
||||
"kevinrose.vsc-python-indent",
|
||||
"wayou.vscode-todo-highlight",
|
||||
"charliermarsh.ruff",
|
||||
"wokwi.wokwi-vscode",
|
||||
"humao.rest-client"
|
||||
]
|
||||
}
|
||||
},
|
||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||
// "forwardPorts": [9000],
|
||||
// Use 'portsAttributes' to set default properties for specific forwarded ports.
|
||||
// More info: https://containers.dev/implementors/json_reference/#port-attributes
|
||||
"portsAttributes": {
|
||||
"9000": {
|
||||
"label": "Flask Application",
|
||||
"onAutoForward": "notify"
|
||||
}
|
||||
},
|
||||
// Use 'postCreateCommand' to run commands after the container is created.
|
||||
"postCreateCommand": "pip3 install -r requirements.txt",
|
||||
// "postCreateCommand": "apt-get update && apt-get install -y git && pip3 install -r requirements.txt"
|
||||
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
||||
// "remoteUser": "root"
|
||||
}
|
||||
0
pico-client/__init__.py
Normal file
0
pico-client/__init__.py
Normal file
29
pico-client/async.py
Normal file
29
pico-client/async.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import uasyncio as asyncio # type: ignore
|
||||
|
||||
|
||||
async def task1():
|
||||
for i in range(5):
|
||||
print('Task 1:', i)
|
||||
await asyncio.sleep(1) # Non-blocking sleep
|
||||
|
||||
|
||||
async def task2():
|
||||
for i in range(5):
|
||||
print('Task 2:', i)
|
||||
await asyncio.sleep(2) # Different sleep time
|
||||
|
||||
|
||||
async def task3():
|
||||
for i in range(5):
|
||||
print('Task 3:', i)
|
||||
await asyncio.sleep(4) # Different sleep time
|
||||
|
||||
|
||||
async def main():
|
||||
# Run both tasks concurrently
|
||||
await asyncio.gather(task1(), task2(), task3())
|
||||
print("we're done.")
|
||||
|
||||
|
||||
# Run the event loop
|
||||
asyncio.run(main())
|
||||
9
pico-client/classes/__init__.py
Normal file
9
pico-client/classes/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .current_condition import CurrentCondition
|
||||
from .air_quality import AirQuality
|
||||
from .condition import Condition
|
||||
from .location import Location
|
||||
from .response_status import ResponseStatus
|
||||
from .weather_response import WeatherResponse
|
||||
from .weather import Weather
|
||||
|
||||
__all__ = ["Weather", "AirQuality","Condition","CurrentCondition","Location", "ResponseStatus","WeatherResponse"]
|
||||
15
pico-client/classes/air_quality.py
Normal file
15
pico-client/classes/air_quality.py
Normal file
@@ -0,0 +1,15 @@
|
||||
class AirQuality:
|
||||
def __init__(self, **kwargs):
|
||||
# Required pollutants
|
||||
self.co = float(kwargs.get('co', 0))
|
||||
self.no2 = float(kwargs.get('no2', 0))
|
||||
self.o3 = float(kwargs.get('o3', 0))
|
||||
self.so2 = float(kwargs.get('so2', 0))
|
||||
self.pm2_5 = float(kwargs.get('pm2_5'))
|
||||
self.pm10 = float(kwargs.get('pm10', 0))
|
||||
# Air quality indices
|
||||
self.us_epa_index = int(kwargs.get('us-epa-index', 0))
|
||||
self.gb_defra_index = int(kwargs.get('gb-defra-index', 0))
|
||||
|
||||
def __repr__(self):
|
||||
return f'AirQuality(CO={self.co}, NO2={self.no2}, O3={self.o3}, PM2.5={self.pm2_5})'
|
||||
7
pico-client/classes/condition.py
Normal file
7
pico-client/classes/condition.py
Normal file
@@ -0,0 +1,7 @@
|
||||
class Condition:
|
||||
def __init__(self, **kwargs):
|
||||
self.text = kwargs.get('text')
|
||||
self.icon = kwargs.get('icon')
|
||||
|
||||
def __repr__(self):
|
||||
return f'Condition(text={self.text}, icon={self.icon})'
|
||||
24
pico-client/classes/current_condition.py
Normal file
24
pico-client/classes/current_condition.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from .condition import Condition
|
||||
from .air_quality import AirQuality
|
||||
|
||||
class CurrentCondition:
|
||||
def __init__(
|
||||
self,
|
||||
# condition,
|
||||
# air_quality,
|
||||
**kwargs,
|
||||
):
|
||||
self.last_updated = kwargs.get('last_updated', '')
|
||||
self.temp_c = float(kwargs.get('temp_c', 0))
|
||||
self.is_day = int(kwargs.get('is_day', 0))
|
||||
self.condition = Condition(**kwargs.get('condition'))
|
||||
self.wind_kph = float(kwargs.get('wind_kph', 0))
|
||||
self.wind_dir = kwargs.get('wind_dir', '')
|
||||
self.pressure_mb = float(kwargs.get('pressure_mb', 0))
|
||||
self.humidity = int(kwargs.get('humidity', 0))
|
||||
self.cloud = int(kwargs.get('cloud', 0))
|
||||
self.feelslike_c = float(kwargs.get('feelslike_c', 0))
|
||||
self.air_quality = AirQuality(**kwargs.get('air_quality'))
|
||||
|
||||
def __repr__(self):
|
||||
return f'CurrentCondition(condition={self.condition}, air_quality={self.air_quality}, last_updated={self.last_updated}, temp_c={self.temp_c}, wind_dir={self.wind_dir}, wind_kph={self.wind_kph})'
|
||||
9
pico-client/classes/location.py
Normal file
9
pico-client/classes/location.py
Normal file
@@ -0,0 +1,9 @@
|
||||
class Location:
|
||||
def __init__(self, **kwargs):
|
||||
self.name = kwargs.get('name', '')
|
||||
self.region = kwargs.get('region', '')
|
||||
self.country = kwargs.get('country', '')
|
||||
self.localtime = kwargs.get('localtime', '')
|
||||
|
||||
def __repr__(self):
|
||||
return f"Location(name='{self.name}', region='{self.region}', country='{self.country}', localtime='{self.localtime}')"
|
||||
46
pico-client/classes/response_status.py
Normal file
46
pico-client/classes/response_status.py
Normal file
@@ -0,0 +1,46 @@
|
||||
class ResponseStatus:
|
||||
|
||||
def __init__(self, code: int=0, message:str="", error_text:str=""):
|
||||
self._code = code
|
||||
self._message = message
|
||||
self._error_text = error_text
|
||||
|
||||
@property
|
||||
def error_text(self):
|
||||
"""Get the status code"""
|
||||
return self._error_text
|
||||
|
||||
@error_text.setter
|
||||
def error_text(self, value):
|
||||
self._error_text = value
|
||||
|
||||
# Status Code property with validation
|
||||
@property
|
||||
def code(self):
|
||||
"""Get the status code"""
|
||||
return self._code
|
||||
|
||||
@code.setter
|
||||
def code(self, value):
|
||||
"""Set the status code with validation"""
|
||||
if not isinstance(value, int):
|
||||
raise TypeError("code must be an integer")
|
||||
if value < 0 or value > 599:
|
||||
raise ValueError("code must be between 0 and 599")
|
||||
self._code = value
|
||||
|
||||
# Status Message property with validation
|
||||
@property
|
||||
def message(self):
|
||||
"""Get the status message"""
|
||||
return self._message
|
||||
|
||||
@message.setter
|
||||
def message(self, value):
|
||||
"""Set the status message with validation"""
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("message must be a string")
|
||||
self._message = value
|
||||
|
||||
def __repr__(self):
|
||||
return f'ResponseStatus(code={self.code}, message={self.message})'
|
||||
9
pico-client/classes/weather.py
Normal file
9
pico-client/classes/weather.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .location import Location
|
||||
from .current_condition import CurrentCondition
|
||||
class Weather:
|
||||
def __init__(self, location: Location, current: CurrentCondition):
|
||||
self.location = location
|
||||
self.current = current
|
||||
|
||||
def __repr__(self):
|
||||
return f'Weather(condition={self.location}, current={self.current}'
|
||||
48
pico-client/classes/weather_response.py
Normal file
48
pico-client/classes/weather_response.py
Normal file
@@ -0,0 +1,48 @@
|
||||
from .weather import Weather
|
||||
from .response_status import ResponseStatus
|
||||
|
||||
class WeatherResponse:
|
||||
_response_status: ResponseStatus
|
||||
_weather: Weather
|
||||
|
||||
def __init__(self):
|
||||
self._weather = None
|
||||
self._response_status = None
|
||||
|
||||
# Weather property with getter and setter
|
||||
@property
|
||||
def weather(self)->Weather:
|
||||
"""Get the weather data"""
|
||||
return self._weather
|
||||
|
||||
@weather.setter
|
||||
def weather(self, value:Weather)->None:
|
||||
"""Set the weather data with validation"""
|
||||
if value is not None and not isinstance(value, Weather):
|
||||
raise TypeError("weather must be an instance of Weather class")
|
||||
self._weather = value
|
||||
|
||||
# ResponseStatus property with getter and setter
|
||||
@property
|
||||
def response_status(self)->ResponseStatus:
|
||||
"""Get the response status"""
|
||||
return self._response_status
|
||||
|
||||
@response_status.setter
|
||||
def response_status(self, value:ResponseStatus)->None:
|
||||
"""Set the response status with validation"""
|
||||
if value is not None and not isinstance(value, ResponseStatus):
|
||||
raise TypeError("response_status must be an instance of ResponseStatus class")
|
||||
self._response_status = value
|
||||
|
||||
# Additional utility methods
|
||||
def is_successful(self)->bool:
|
||||
"""Check if the response was successful"""
|
||||
return self._response_status is not None
|
||||
|
||||
def has_weather_data(self)->bool:
|
||||
"""Check if weather data is available"""
|
||||
return self._weather is not None
|
||||
|
||||
def __str__(self)->str:
|
||||
return f"WeatherResponse(weather={self._weather}, status={self._response_status})"
|
||||
25
pico-client/deploy.fish
Executable file
25
pico-client/deploy.fish
Executable file
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/fish
|
||||
|
||||
echo "=== MicroPython Deployment Script ==="
|
||||
|
||||
echo "1. Create pico-client directories on device..."
|
||||
mpremote connect port:rfc2217://localhost:4000 mkdir restapi
|
||||
mpremote connect port:rfc2217://localhost:4000 mkdir classes
|
||||
mpremote connect port:rfc2217://localhost:4000 mkdir display
|
||||
mpremote connect port:rfc2217://localhost:4000 mkdir tryout
|
||||
mpremote connect port:rfc2217://localhost:4000 mkdir utils
|
||||
mpremote connect port:rfc2217://localhost:4000 mkdir web
|
||||
|
||||
|
||||
echo "1. Copying pico-client directories to device..."
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r restapi/mock :
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r classes :
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r display :
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r tryout :
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r utils :
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r web :
|
||||
|
||||
echo "2. Starting main.py..."
|
||||
mpremote connect port:rfc2217://localhost:4000 run main.py
|
||||
|
||||
echo "=== Deployment Complete ==="
|
||||
4
pico-client/display/__init__.py
Normal file
4
pico-client/display/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from .neopixel_64x64 import NeoPixel_64x64
|
||||
|
||||
__all__ = ["NeoPixel_64x64"]
|
||||
|
||||
5
pico-client/display/emoji/__init__.py
Normal file
5
pico-client/display/emoji/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .emoji_5x7 import emoji_5x7
|
||||
from .emoji_8x8 import emoji_8x8
|
||||
from .emoji_16x16 import emoji_16x16
|
||||
|
||||
__all__=["emoji_5x7","emoji_8x8","emoji_16x16"]
|
||||
30
pico-client/display/emoji/emoji_16x16.py
Normal file
30
pico-client/display/emoji/emoji_16x16.py
Normal file
@@ -0,0 +1,30 @@
|
||||
emoji_16x16 = {
|
||||
'☀️': [0x8001, 0x4002, 0x2004, 0x1188, 0xDB0, 0x7E0, 0x3C0, 0xFFFF, 0xFFFF, 0x3C0, 0x7E0, 0xDB0, 0x1188, 0x2004, 0x4002, 0x8001],
|
||||
'☁️': [0x00, 0x00, 0xF0, 0x1F8, 0x3FC, 0x7FE, 0x7FE, 0xFFF, 0xFFF, 0xFFF, 0x7FE, 0x7FE, 0x3FC, 0x1F8, 0xF0, 0x00],
|
||||
'⚽': [0x3F0, 0xFFC, 0x1FFE, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x1FFE, 0xFFC, 0x3F0, 0x00],
|
||||
'✈️': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'❤️': [0x00, 0x00, 0x618, 0xF3C, 0x1FFE, 0x3FFF, 0x3FFF, 0x7FFF, 0x7FFE, 0x3FFC, 0x3FF8, 0x1FF0, 0xFE0, 0x7C0, 0x380, 0x00],
|
||||
'⭐': [0x60, 0x60, 0xF0, 0xF0, 0x1F8, 0x3FC, 0x7FE, 0xFFF, 0xFFF, 0x7FE, 0x3FC, 0x1F8, 0xF0, 0xF0, 0x60, 0x60],
|
||||
'🌙': [0x00, 0x3F0, 0x7F8, 0xFFC, 0x1FFE, 0x1FFF, 0x1FFF, 0x1FFE, 0x1FFC, 0x1FF8, 0x1FF0, 0x1FE0, 0x1FC0, 0x1F80, 0x1F00, 0x00],
|
||||
'🍎': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'🍕': [0x3F0, 0xFFC, 0x1FFE, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x1FFE, 0xFFC, 0x3F0, 0x00],
|
||||
'🎃': [0x3F0, 0xFFC, 0x1FFE, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x1FFE, 0xFFC, 0x3F0, 0x00],
|
||||
'🎄': [0x60, 0xF0, 0xF0, 0x1F8, 0x1F8, 0x3FC, 0x3FC, 0x7FE, 0x7FE, 0xFFF, 0xFFF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0],
|
||||
'🏀': [0x3F0, 0xFFC, 0x1FFE, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x3FFF, 0x1FFE, 0xFFC, 0x3F0, 0x00],
|
||||
'🐦': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'🐱': [0x3F0, 0xFFC, 0x1FFE, 0x3FFF, 0x3FFF, 0x37FB, 0x37FB, 0x37FB, 0x37FB, 0x37FB, 0x3FFF, 0x3FFF, 0x1FFE, 0xFFC, 0x3F0, 0x00],
|
||||
'🐶': [0x3F0, 0xFFC, 0x1FFE, 0x3FFF, 0x3FFF, 0x37FB, 0x37FB, 0x37FB, 0x37FB, 0x37FB, 0x3FFF, 0x3FFF, 0x1FFE, 0xFFC, 0x3F0, 0x00],
|
||||
'💙': [0x00, 0x00, 0x618, 0xF3C, 0x1FFE, 0x3FFF, 0x3FFF, 0x7FFF, 0x7FFE, 0x3FFC, 0x3FF8, 0x1FF0, 0xFE0, 0x7C0, 0x380, 0x00],
|
||||
'💻': [0x00, 0x3FFF, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x2001, 0x3FFF, 0x1FFE, 0xFFC, 0x00],
|
||||
'📱': [0xFFFF, 0x8001, 0x8001, 0x9FF9, 0x9009, 0x9009, 0x9009, 0x9009, 0x9009, 0x9009, 0x9009, 0x9009, 0x9FF9, 0x8001, 0x8001, 0xFFFF],
|
||||
'🔑': [0x00, 0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF],
|
||||
'🔒': [0xFC, 0x1FE, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF],
|
||||
'😀': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7FFE, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x7FFE, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0, 0x00],
|
||||
'😂': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7C3E, 0xF99F, 0xF3CF, 0xF3CF, 0xF3CF, 0xF3CF, 0xF99F, 0x7C3E, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0],
|
||||
'😊': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF00F, 0xF00F, 0xF00F, 0xF00F, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0],
|
||||
'😍': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF3CF, 0xE7E7, 0xE7E7, 0xF3CF, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0],
|
||||
'😎': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7FFE, 0xFFFF, 0xF00F, 0xF00F, 0xF00F, 0xF00F, 0xFFFF, 0x7FFE, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0],
|
||||
'😠': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF3CF, 0xE667, 0xE667, 0xF3CF, 0xF81F, 0x7C3E, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0],
|
||||
'😢': [0x7E0, 0x1FF8, 0x3FFC, 0x7FFE, 0x7C3E, 0xF81F, 0xF00F, 0xF3CF, 0xF3CF, 0xF00F, 0xF99F, 0x7C3E, 0x7FFE, 0x3FFC, 0x1FF8, 0x7E0],
|
||||
'🚗': [0x00, 0x00, 0xFC, 0x1FE, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x3FF, 0x1FE, 0xFC, 0x00, 0x00],
|
||||
}
|
||||
81
pico-client/display/emoji/emoji_5x7.py
Normal file
81
pico-client/display/emoji/emoji_5x7.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# Emoji symbols for LED matrix (5x7)
|
||||
emoji_5x7 = {
|
||||
# Basic Smileys
|
||||
'😀': [0x0A, 0x00, 0x11, 0x11, 0x00, 0x11, 0x0E], # Grinning face
|
||||
'😊': [0x0A, 0x00, 0x11, 0x11, 0x00, 0x0A, 0x04], # Smiling face
|
||||
'😂': [0x0A, 0x00, 0x1F, 0x11, 0x00, 0x0A, 0x04], # Laughing with tears
|
||||
'😍': [0x0A, 0x00, 0x15, 0x0E, 0x04, 0x0A, 0x11], # Heart eyes
|
||||
'😎': [0x0A, 0x00, 0x11, 0x11, 0x0E, 0x1F, 0x00], # Cool sunglasses
|
||||
'😢': [0x0A, 0x00, 0x11, 0x11, 0x00, 0x0A, 0x04], # Crying face
|
||||
'😠': [0x0A, 0x00, 0x11, 0x11, 0x00, 0x15, 0x0A], # Angry face
|
||||
'😴': [0x0A, 0x00, 0x11, 0x11, 0x00, 0x0E, 0x1F], # Sleeping face
|
||||
# Hearts
|
||||
'❤️': [0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00], # Red heart
|
||||
'💙': [0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00], # Blue heart
|
||||
'💚': [0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00], # Green heart
|
||||
'💛': [0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00], # Yellow heart
|
||||
'💜': [0x00, 0x0A, 0x1F, 0x1F, 0x0E, 0x04, 0x00], # Purple heart
|
||||
# Hands & Gestures
|
||||
'👍': [0x04, 0x04, 0x04, 0x04, 0x15, 0x0E, 0x04], # Thumbs up
|
||||
'👎': [0x04, 0x0E, 0x15, 0x04, 0x04, 0x04, 0x04], # Thumbs down
|
||||
'👏': [0x00, 0x15, 0x15, 0x15, 0x15, 0x0A, 0x0A], # Clapping hands
|
||||
'🙌': [0x11, 0x0A, 0x04, 0x04, 0x04, 0x0A, 0x11], # Raising hands
|
||||
# Common Symbols
|
||||
'⭐': [0x04, 0x04, 0x1F, 0x0E, 0x1F, 0x04, 0x04], # Star
|
||||
'🌟': [0x04, 0x15, 0x0E, 0x1F, 0x0E, 0x15, 0x04], # Glowing star
|
||||
'🎉': [0x04, 0x1F, 0x04, 0x1F, 0x04, 0x0A, 0x11], # Party popper
|
||||
'🎂': [0x0E, 0x11, 0x1F, 0x1F, 0x1F, 0x0A, 0x1F], # Birthday cake
|
||||
# Weather & Nature
|
||||
'☀️': [0x11, 0x0A, 0x1F, 0x0E, 0x1F, 0x0A, 0x11], # Sun
|
||||
'🌙': [0x0C, 0x12, 0x12, 0x0C, 0x00, 0x00, 0x00], # Crescent moon
|
||||
'☁️': [0x00, 0x0E, 0x1F, 0x1F, 0x1F, 0x00, 0x00], # Cloud
|
||||
'🌧️': [0x0E, 0x1F, 0x1F, 0x0A, 0x04, 0x0A, 0x04], # Cloud with rain
|
||||
'⚡': [0x04, 0x04, 0x06, 0x1C, 0x04, 0x04, 0x04], # Lightning
|
||||
'❄️': [0x0A, 0x1F, 0x0A, 0x04, 0x0A, 0x1F, 0x0A], # Snowflake
|
||||
# Objects
|
||||
'📱': [0x1F, 0x11, 0x15, 0x15, 0x15, 0x11, 0x1F], # Mobile phone
|
||||
'💻': [0x1F, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1F], # Laptop
|
||||
'⌚': [0x0E, 0x11, 0x15, 0x15, 0x15, 0x11, 0x0E], # Watch
|
||||
'🔑': [0x04, 0x0E, 0x04, 0x0E, 0x15, 0x15, 0x0E], # Key
|
||||
# Arrows
|
||||
'⬆️': [0x04, 0x0E, 0x15, 0x04, 0x04, 0x04, 0x04], # Up arrow
|
||||
'⬇️': [0x04, 0x04, 0x04, 0x04, 0x15, 0x0E, 0x04], # Down arrow
|
||||
'⬅️': [0x04, 0x08, 0x1F, 0x08, 0x04, 0x00, 0x00], # Left arrow
|
||||
'➡️': [0x04, 0x02, 0x1F, 0x02, 0x04, 0x00, 0x00], # Right arrow
|
||||
# Status Indicators
|
||||
'✅': [0x00, 0x01, 0x02, 0x14, 0x08, 0x00, 0x00], # Check mark button
|
||||
'❌': [0x11, 0x0A, 0x04, 0x0A, 0x11, 0x00, 0x00], # Cross mark
|
||||
'⚠️': [0x04, 0x04, 0x04, 0x15, 0x0E, 0x04, 0x04], # Warning
|
||||
'🚨': [0x04, 0x0E, 0x1F, 0x04, 0x1F, 0x04, 0x04], # Alarm
|
||||
# Food & Drink
|
||||
'🍕': [0x0E, 0x1F, 0x1F, 0x15, 0x1F, 0x1F, 0x0E], # Pizza
|
||||
'🍎': [0x04, 0x0A, 0x11, 0x1F, 0x1F, 0x0E, 0x04], # Apple
|
||||
'☕': [0x0E, 0x11, 0x1F, 0x11, 0x0E, 0x04, 0x04], # Coffee
|
||||
# Animals
|
||||
'🐱': [0x0E, 0x0A, 0x0E, 0x1F, 0x15, 0x11, 0x0A], # Cat face
|
||||
'🐶': [0x0E, 0x0A, 0x0E, 0x1F, 0x15, 0x15, 0x0A], # Dog face
|
||||
'🐭': [0x0C, 0x12, 0x0C, 0x1E, 0x0B, 0x0B, 0x06], # Mouse face
|
||||
# Holidays
|
||||
'🎄': [0x04, 0x04, 0x0E, 0x0E, 0x1F, 0x04, 0x04], # Christmas tree
|
||||
'🎃': [0x0E, 0x15, 0x1F, 0x1B, 0x1F, 0x15, 0x0E], # Jack-o-lantern
|
||||
'🎅': [0x04, 0x0E, 0x04, 0x0E, 0x1F, 0x0A, 0x04], # Santa Claus
|
||||
# Technology
|
||||
'🔒': [0x0E, 0x11, 0x11, 0x1F, 0x1B, 0x1B, 0x1F], # Locked
|
||||
'🔓': [0x0E, 0x10, 0x10, 0x1F, 0x1B, 0x1B, 0x1F], # Unlocked
|
||||
'🔋': [0x1F, 0x11, 0x15, 0x15, 0x15, 0x11, 0x1F], # Battery
|
||||
'🔍': [0x00, 0x0E, 0x01, 0x0D, 0x15, 0x15, 0x0D], # Magnifying glass
|
||||
# Music
|
||||
'🎵': [0x02, 0x03, 0x02, 0x0E, 0x1E, 0x0C, 0x00], # Music note
|
||||
'🎶': [0x0A, 0x0A, 0x0A, 0x0A, 0x0E, 0x0E, 0x04], # Multiple music notes
|
||||
# Sports
|
||||
'⚽': [0x0E, 0x11, 0x15, 0x15, 0x15, 0x11, 0x0E], # Soccer ball
|
||||
'🏀': [0x0E, 0x11, 0x17, 0x15, 0x17, 0x11, 0x0E], # Basketball
|
||||
# Time
|
||||
'🕐': [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], # One o'clock
|
||||
'🕒': [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], # Three o'clock
|
||||
'🕔': [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], # Five o'clock
|
||||
# Miscellaneous
|
||||
'💡': [0x04, 0x0A, 0x0A, 0x0A, 0x1F, 0x0E, 0x04], # Light bulb
|
||||
'💰': [0x00, 0x1F, 0x15, 0x1F, 0x15, 0x1F, 0x00], # Money bag
|
||||
'🎁': [0x1F, 0x11, 0x1F, 0x1F, 0x0A, 0x0A, 0x0A], # Wrapped gift
|
||||
}
|
||||
52
pico-client/display/emoji/emoji_8x8.py
Normal file
52
pico-client/display/emoji/emoji_8x8.py
Normal file
@@ -0,0 +1,52 @@
|
||||
# Emoji symbols for LED matrix (8x8)
|
||||
emoji_8x8 = {
|
||||
# Basic Smileys
|
||||
'😀': [0x3C, 0x42, 0xA5, 0x81, 0xA5, 0x99, 0x42, 0x3C], # Grinning face
|
||||
'😊': [0x3C, 0x42, 0xA5, 0x81, 0x81, 0x99, 0x42, 0x3C], # Smiling face
|
||||
'😂': [0x3C, 0x42, 0xA5, 0x89, 0x89, 0x95, 0x42, 0x3C], # Laughing with tears
|
||||
'😍': [0x3C, 0x42, 0xA5, 0x81, 0x99, 0x66, 0x42, 0x3C], # Heart eyes
|
||||
'😎': [0x3C, 0x42, 0xA5, 0x81, 0x81, 0xFF, 0x7E, 0x3C], # Cool sunglasses
|
||||
'😢': [0x3C, 0x42, 0xA5, 0x81, 0x81, 0x99, 0x52, 0x3C], # Crying face
|
||||
'😠': [0x3C, 0x42, 0xA5, 0x81, 0x81, 0x99, 0x5A, 0x3C], # Angry face
|
||||
'😴': [0x3C, 0x42, 0xA5, 0x81, 0x81, 0x81, 0x7E, 0x3C], # Sleeping face
|
||||
# Hearts
|
||||
'💙': [0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Blue heart
|
||||
'💚': [0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Green heart
|
||||
'❤️': [0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Red heart !!! NOT WORKING !!!
|
||||
'💛': [0x00, 0x66, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Yellow heart
|
||||
# Hands & Gestures
|
||||
'👍': [0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x3C, 0x18], # Thumbs up
|
||||
'👎': [0x18, 0x3C, 0x7E, 0x18, 0x18, 0x18, 0x18, 0x18], # Thumbs down
|
||||
'👏': [0x00, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x3C, 0x18], # Clapping hands
|
||||
# Common Symbols
|
||||
'⭐': [0x18, 0x18, 0x3C, 0xFF, 0xFF, 0x3C, 0x18, 0x18], # Star
|
||||
'🌟': [0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Glowing star
|
||||
'🎉': [0x18, 0x3C, 0x7E, 0x18, 0x18, 0x7E, 0x3C, 0x18], # Party popper
|
||||
# Weather & Nature
|
||||
'☀️': [0x42, 0x81, 0x5A, 0x3C, 0x3C, 0x5A, 0x81, 0x42], # Sun with rays
|
||||
'🌙': [0x3C, 0x42, 0x84, 0x84, 0x84, 0x84, 0x42, 0x3C], # Crescent moon
|
||||
'☁️': [0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x00], # Cloud
|
||||
'🌧️': [0x3C, 0x7E, 0xFF, 0xFF, 0x24, 0x48, 0x24, 0x48], # Cloud with rain
|
||||
'⚡': [0x08, 0x0C, 0x0E, 0xFF, 0x0E, 0x0C, 0x08, 0x00], # Lightning
|
||||
# Objects
|
||||
'📱': [0xFF, 0x81, 0xBD, 0xA5, 0xA5, 0xBD, 0x81, 0xFF], # Mobile phone
|
||||
'💻': [0xFF, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0xFF], # Laptop
|
||||
# Arrows
|
||||
'⬆️': [0x18, 0x3C, 0x7E, 0xFF, 0x18, 0x18, 0x18, 0x18], # Up arrow
|
||||
'⬇️': [0x18, 0x18, 0x18, 0x18, 0xFF, 0x7E, 0x3C, 0x18], # Down arrow
|
||||
# Status Indicators
|
||||
'✅': [0x00, 0x01, 0x03, 0x16, 0x6C, 0xD8, 0x70, 0x20], # Check mark
|
||||
'❌': [0x81, 0x42, 0x24, 0x18, 0x18, 0x24, 0x42, 0x81], # Cross mark
|
||||
# Food & Drink
|
||||
'🍕': [0x3C, 0x7E, 0xFF, 0xDB, 0xFF, 0x7E, 0x3C, 0x00], # Pizza
|
||||
'🍎': [0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Apple
|
||||
# Animals
|
||||
'🐱': [0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x5A, 0x3C], # Cat face
|
||||
'🐶': [0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x99, 0x42, 0x3C], # Dog face
|
||||
# Music
|
||||
'🎵': [0x00, 0x03, 0x0F, 0x3F, 0x3F, 0x0F, 0x03, 0x00], # Music note
|
||||
# Sports
|
||||
'⚽': [0x3C, 0x42, 0x81, 0x81, 0x81, 0x81, 0x42, 0x3C], # Soccer ball
|
||||
# Miscellaneous
|
||||
'💡': [0x18, 0x3C, 0x7E, 0xFF, 0xFF, 0x7E, 0x3C, 0x18], # Light bulb
|
||||
}
|
||||
9
pico-client/display/fonts/__init__.py
Normal file
9
pico-client/display/fonts/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from .font_3x5 import font_3x5
|
||||
from .font_5x7 import font_5x7
|
||||
from .font_8x8 import font_8x8
|
||||
from .font_16x16 import font_16x16
|
||||
|
||||
fonts_installed = [font_3x5, font_5x7, font_8x8, font_16x16]
|
||||
|
||||
__all__ = ['font_3x5', 'font_5x7', 'font_8x8', 'font_16x16', 'fonts_installed']
|
||||
|
||||
107
pico-client/display/fonts/font_16x16.py
Normal file
107
pico-client/display/fonts/font_16x16.py
Normal file
@@ -0,0 +1,107 @@
|
||||
font_16x16 = {
|
||||
' ': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'!': [0x06, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x06, 0x00, 0x0F, 0x0F, 0x00, 0x00],
|
||||
'"': [0x363, 0x363, 0x363, 0x363, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'#': [0x318, 0x318, 0x318, 0xFFF, 0xFFF, 0x318, 0x318, 0x318, 0x318, 0xFFF, 0xFFF, 0x318, 0x318, 0x318, 0x00, 0x00],
|
||||
'$': [0x78, 0xFE, 0x1CF, 0x1C7, 0x1C0, 0xF8, 0x7C, 0x0F, 0x07, 0x1C7, 0x1CF, 0xFE, 0x78, 0x10, 0x00, 0x00],
|
||||
'%': [0xE0E, 0x1B0E, 0x1B1C, 0xE38, 0x70, 0xE0, 0x1C0, 0x380, 0x70E, 0xE1B, 0x1C1B, 0x380E, 0x00, 0x00, 0x00, 0x00],
|
||||
'&': [0x7C0, 0xEE0, 0x1C70, 0x1C70, 0xEE0, 0x7C0, 0xFC7, 0x1CE7, 0x387E, 0x383C, 0x387E, 0x1CFF, 0xFE7, 0x7C0, 0x00, 0x00],
|
||||
"'": [0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'(': [0x07, 0x0E, 0x1C, 0x1C, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x1C, 0x1C, 0x0E, 0x07, 0x00, 0x00],
|
||||
')': [0x38, 0x1C, 0x0E, 0x0E, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x0E, 0x0E, 0x1C, 0x38, 0x00, 0x00],
|
||||
'*': [0x30, 0x1B3, 0xFC, 0x78, 0xFC, 0x1B3, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'+': [0x00, 0x00, 0x60, 0x60, 0x60, 0x60, 0x3FF, 0x3FF, 0x60, 0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00],
|
||||
',': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x07, 0x03, 0x06, 0x00, 0x00],
|
||||
'-': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3FF, 0x3FF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'.': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00],
|
||||
'/': [0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x1C0, 0x380, 0x700, 0xE00, 0xC00, 0x00, 0x00, 0x00, 0x00],
|
||||
'0': [0x1F8, 0x3FC, 0x70E, 0x606, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0x606, 0x70E, 0x3FC, 0x1F8, 0x00, 0x00],
|
||||
'1': [0x0C, 0x1C, 0x3C, 0x7C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x7F, 0x7F, 0x00, 0x00],
|
||||
'2': [0xFC, 0x1FE, 0x387, 0x303, 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x1C0, 0x3FF, 0x3FF, 0x00, 0x00],
|
||||
'3': [0xFC, 0x1FE, 0x387, 0x303, 0x03, 0x07, 0x7E, 0x7E, 0x07, 0x03, 0x303, 0x387, 0x1FE, 0xFC, 0x00, 0x00],
|
||||
'4': [0x0E, 0x1E, 0x3E, 0x6E, 0xCE, 0x18E, 0x30E, 0x60E, 0x7FF, 0x7FF, 0x0E, 0x0E, 0x0E, 0x0E, 0x00, 0x00],
|
||||
'5': [0x3FF, 0x3FF, 0x300, 0x300, 0x300, 0x3FC, 0x3FE, 0x07, 0x03, 0x03, 0x303, 0x387, 0x1FE, 0xFC, 0x00, 0x00],
|
||||
'6': [0xFC, 0x1FE, 0x387, 0x303, 0x300, 0x37C, 0x3FE, 0x387, 0x303, 0x303, 0x303, 0x387, 0x1FE, 0xFC, 0x00, 0x00],
|
||||
'7': [0x3FF, 0x3FF, 0x03, 0x07, 0x0E, 0x1C, 0x38, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00, 0x00],
|
||||
'8': [0xFC, 0x1FE, 0x387, 0x303, 0x303, 0x387, 0x1FE, 0xFC, 0x1FE, 0x387, 0x303, 0x303, 0x387, 0x1FE, 0xFC, 0x00],
|
||||
'9': [0xFC, 0x1FE, 0x387, 0x303, 0x303, 0x387, 0x1FF, 0xFF, 0x03, 0x03, 0x303, 0x387, 0x1FE, 0xFC, 0x00, 0x00],
|
||||
':': [0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00],
|
||||
';': [0x00, 0x00, 0x00, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x06, 0x03, 0x06, 0x00, 0x00],
|
||||
'<': [0x03, 0x0F, 0x3C, 0xF0, 0x3C0, 0x700, 0x3C0, 0xF0, 0x3C, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'=': [0x00, 0x00, 0x00, 0x00, 0x3FF, 0x3FF, 0x00, 0x00, 0x3FF, 0x3FF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'>': [0x600, 0x780, 0x1E0, 0x78, 0x1E, 0x07, 0x1E, 0x78, 0x1E0, 0x780, 0x600, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'?': [0x7C, 0xFE, 0x1C7, 0x183, 0x03, 0x07, 0x0E, 0x1C, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00],
|
||||
'@': [0x1F8, 0x3FC, 0x70E, 0x606, 0xE3F, 0xE7F, 0xE67, 0xE67, 0xE67, 0xE7F, 0xE3E, 0x600, 0x70E, 0x3FC, 0x1F8, 0x00],
|
||||
'A': [0x70, 0xF8, 0xF8, 0x1DC, 0x1DC, 0x38E, 0x38E, 0x38E, 0x3FE, 0x7FF, 0x707, 0x707, 0x707, 0x707, 0x00, 0x00],
|
||||
'B': [0x7F8, 0x7FC, 0x70E, 0x70E, 0x70E, 0x71C, 0x7F8, 0x7FC, 0x70E, 0x707, 0x707, 0x707, 0x70E, 0x7FE, 0x7FC, 0x00],
|
||||
'C': [0x1FC, 0x3FE, 0x787, 0x703, 0xE00, 0xE00, 0xE00, 0xE00, 0xE00, 0xE00, 0x703, 0x787, 0x3FE, 0x1FC, 0x00, 0x00],
|
||||
'D': [0xFF0, 0xFFC, 0xE1E, 0xE0E, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE0E, 0xE1E, 0xFFC, 0xFF0, 0x00, 0x00],
|
||||
'E': [0x7FF, 0x7FF, 0x700, 0x700, 0x700, 0x700, 0x7FC, 0x7FC, 0x700, 0x700, 0x700, 0x700, 0x7FF, 0x7FF, 0x00, 0x00],
|
||||
'F': [0x7FF, 0x7FF, 0x700, 0x700, 0x700, 0x700, 0x7FC, 0x7FC, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x00, 0x00],
|
||||
'G': [0x1FC, 0x3FE, 0x787, 0x703, 0xE00, 0xE00, 0xE3F, 0xE3F, 0xE07, 0xE07, 0x707, 0x787, 0x3FF, 0x1FF, 0x00, 0x00],
|
||||
'H': [0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xFFF, 0xFFF, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0x00, 0x00],
|
||||
'I': [0x7F, 0x7F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x7F, 0x7F, 0x00, 0x00],
|
||||
'J': [0x7F, 0x7F, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x71C, 0x71C, 0x738, 0x3F8, 0x1F0, 0x00, 0x00],
|
||||
'K': [0xE0E, 0xE1C, 0xE38, 0xE70, 0xEE0, 0xFC0, 0xFC0, 0xFE0, 0xEF0, 0xE78, 0xE3C, 0xE1C, 0xE0E, 0xE07, 0x00, 0x00],
|
||||
'L': [0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x700, 0x7FF, 0x7FF, 0x00, 0x00],
|
||||
'M': [0x1C07, 0x1E0F, 0x1E0F, 0x1F1F, 0x1F1F, 0x1DB7, 0x1DB7, 0x1DB7, 0x1CE7, 0x1CE7, 0x1CE7, 0x1C07, 0x1C07, 0x1C07, 0x00, 0x00],
|
||||
'N': [0xE07, 0xF07, 0xF07, 0xF87, 0xFC7, 0xEE7, 0xE77, 0xE77, 0xE3F, 0xE1F, 0xE1F, 0xE0F, 0xE07, 0xE07, 0x00, 0x00],
|
||||
'O': [0x1F8, 0x3FC, 0x78E, 0x706, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0x706, 0x78E, 0x3FC, 0x1F8, 0x00, 0x00],
|
||||
'P': [0x7F8, 0x7FC, 0x70E, 0x707, 0x707, 0x707, 0x70E, 0x7FC, 0x7F8, 0x700, 0x700, 0x700, 0x700, 0x700, 0x00, 0x00],
|
||||
'Q': [0x1F8, 0x3FC, 0x78E, 0x706, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE67, 0x77E, 0x7BE, 0x3FC, 0x1F7, 0x00, 0x00],
|
||||
'R': [0xFF0, 0xFF8, 0xE1C, 0xE0E, 0xE0E, 0xE0E, 0xE1C, 0xFF8, 0xFF0, 0xE78, 0xE3C, 0xE1E, 0xE0E, 0xE07, 0x00, 0x00],
|
||||
'S': [0x1FC, 0x3FE, 0x787, 0x703, 0x700, 0x780, 0x3FC, 0xFE, 0x07, 0x03, 0x703, 0x787, 0x3FE, 0x1FC, 0x00, 0x00],
|
||||
'T': [0x7FF, 0x7FF, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00, 0x00],
|
||||
'U': [0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0x70E, 0x7FE, 0x1F8, 0x00, 0x00],
|
||||
'V': [0xE07, 0xE07, 0xE07, 0x70E, 0x70E, 0x70E, 0x39C, 0x39C, 0x39C, 0x1F8, 0x1F8, 0x1F8, 0xF0, 0xF0, 0x00, 0x00],
|
||||
'W': [0xE0E, 0xE0E, 0xE0E, 0xE0E, 0xE0E, 0xE0E, 0x6B6, 0x6B6, 0x6B6, 0x7F7, 0x7F7, 0x7F7, 0x3E3, 0x3E3, 0x00, 0x00],
|
||||
'X': [0xE07, 0x70E, 0x70E, 0x39C, 0x1F8, 0x1F8, 0xF0, 0xF0, 0x1F8, 0x1F8, 0x39C, 0x70E, 0x70E, 0xE07, 0x00, 0x00],
|
||||
'Y': [0xE07, 0x70E, 0x70E, 0x39C, 0x39C, 0x1F8, 0x1F8, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0x00, 0x00],
|
||||
'Z': [0x7FF, 0x7FF, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x1C0, 0x380, 0x700, 0xE00, 0xE00, 0x7FF, 0x7FF, 0x00, 0x00],
|
||||
'[': [0x3F, 0x3F, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3F, 0x3F, 0x00],
|
||||
'\\': [0x3000, 0x3800, 0x1C00, 0xE00, 0x700, 0x380, 0x1C0, 0xE0, 0x70, 0x38, 0x1C, 0x0E, 0x07, 0x03, 0x00, 0x00],
|
||||
']': [0x3F, 0x3F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x3F, 0x3F, 0x00],
|
||||
'^': [0x30, 0x78, 0xFC, 0x1CE, 0x387, 0x303, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'_': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFFFF, 0xFFFF],
|
||||
'`': [0x0C, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'a': [0x00, 0x00, 0x00, 0x7C, 0xFE, 0x1C7, 0x07, 0x7F, 0xFF, 0x1C7, 0x387, 0x387, 0x1FF, 0xF7, 0x00, 0x00],
|
||||
'b': [0x380, 0x380, 0x380, 0x3BC, 0x3FE, 0x3CF, 0x387, 0x387, 0x387, 0x387, 0x387, 0x3CF, 0x3FE, 0x3BC, 0x00, 0x00],
|
||||
'c': [0x00, 0x00, 0x00, 0x7C, 0xFE, 0x1C7, 0x383, 0x380, 0x380, 0x380, 0x383, 0x1C7, 0xFE, 0x7C, 0x00, 0x00],
|
||||
'd': [0x07, 0x07, 0x07, 0x77, 0xFF, 0x1CF, 0x387, 0x387, 0x387, 0x387, 0x387, 0x1CF, 0xFF, 0x77, 0x00, 0x00],
|
||||
'e': [0x00, 0x00, 0x00, 0x7C, 0xFE, 0x1C7, 0x383, 0x3FF, 0x3FF, 0x380, 0x381, 0x1C3, 0xFF, 0x7C, 0x00, 0x00],
|
||||
'f': [0x1F, 0x3F, 0x78, 0x70, 0x1FE, 0x1FE, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x00, 0x00],
|
||||
'g': [0x00, 0x00, 0x00, 0x77, 0xFF, 0x1CF, 0x387, 0x387, 0x387, 0x387, 0x1CF, 0xFF, 0x77, 0x07, 0x1FE, 0x1FC],
|
||||
'h': [0x380, 0x380, 0x380, 0x3BC, 0x3FE, 0x3CF, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x00, 0x00],
|
||||
'i': [0x0C, 0x0C, 0x00, 0x3C, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x3F, 0x00, 0x00],
|
||||
'j': [0x03, 0x03, 0x00, 0x0F, 0x0F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0xE3, 0xE3, 0x7E, 0x3C],
|
||||
'k': [0x700, 0x700, 0x700, 0x70E, 0x71C, 0x738, 0x770, 0x7E0, 0x7E0, 0x770, 0x738, 0x71C, 0x70E, 0x707, 0x00, 0x00],
|
||||
'l': [0x3C, 0x3C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x3F, 0x00, 0x00],
|
||||
'm': [0x00, 0x00, 0x00, 0xEE7, 0xFFF, 0xF3B, 0xE3B, 0xE3B, 0xE3B, 0xE3B, 0xE3B, 0xE3B, 0xE3B, 0xE3B, 0x00, 0x00],
|
||||
'n': [0x00, 0x00, 0x00, 0x3BC, 0x3FE, 0x3CF, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x00, 0x00],
|
||||
'o': [0x00, 0x00, 0x00, 0x7C, 0xFE, 0x1C7, 0x383, 0x383, 0x383, 0x383, 0x383, 0x1C7, 0xFE, 0x7C, 0x00, 0x00],
|
||||
'p': [0x00, 0x00, 0x00, 0x3BC, 0x3FE, 0x3CF, 0x387, 0x387, 0x387, 0x387, 0x3CF, 0x3FE, 0x3BC, 0x380, 0x380, 0x380],
|
||||
'q': [0x00, 0x00, 0x00, 0x77, 0xFF, 0x1CF, 0x387, 0x387, 0x387, 0x387, 0x1CF, 0xFF, 0x77, 0x07, 0x07, 0x07],
|
||||
'r': [0x00, 0x00, 0x00, 0x3BC, 0x3FE, 0x3CF, 0x387, 0x380, 0x380, 0x380, 0x380, 0x380, 0x380, 0x380, 0x00, 0x00],
|
||||
's': [0x00, 0x00, 0x00, 0xFC, 0x1FE, 0x387, 0x380, 0x1F8, 0xFE, 0x07, 0x07, 0x387, 0x3FE, 0x1FC, 0x00, 0x00],
|
||||
't': [0x38, 0x38, 0x38, 0xFF, 0xFF, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x1F, 0x0F, 0x00, 0x00],
|
||||
'u': [0x00, 0x00, 0x00, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x1CF, 0x1FF, 0x77, 0x00, 0x00],
|
||||
'v': [0x00, 0x00, 0x00, 0x707, 0x707, 0x38E, 0x38E, 0x38E, 0x1DC, 0x1DC, 0xF8, 0xF8, 0x70, 0x70, 0x00, 0x00],
|
||||
'w': [0x00, 0x00, 0x00, 0xE0E, 0xE0E, 0xE0E, 0x6B6, 0x6B6, 0x6B6, 0x7F7, 0x7F7, 0x3E3, 0x3E3, 0x1C1, 0x00, 0x00],
|
||||
'x': [0x00, 0x00, 0x00, 0x707, 0x38E, 0x1DC, 0xF8, 0x70, 0x70, 0xF8, 0x1DC, 0x38E, 0x707, 0x707, 0x00, 0x00],
|
||||
'y': [0x00, 0x00, 0x00, 0x707, 0x707, 0x38E, 0x38E, 0x1DC, 0x1DC, 0xF8, 0xF8, 0x70, 0x70, 0xE0, 0x1C0, 0x00],
|
||||
'z': [0x00, 0x00, 0x00, 0x3FF, 0x3FF, 0x0E, 0x1C, 0x38, 0x70, 0xE0, 0x1C0, 0x380, 0x3FF, 0x3FF, 0x00, 0x00],
|
||||
'{': [0x0F, 0x1E, 0x1C, 0x1C, 0x1C, 0x1C, 0x38, 0x70, 0x38, 0x1C, 0x1C, 0x1C, 0x1C, 0x1E, 0x0F, 0x00],
|
||||
'|': [0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00],
|
||||
'}': [0x78, 0x3C, 0x1C, 0x1C, 0x1C, 0x1C, 0x0E, 0x07, 0x0E, 0x1C, 0x1C, 0x1C, 0x1C, 0x3C, 0x78, 0x00],
|
||||
'~': [0x00, 0x00, 0x00, 0x00, 0x00, 0x1C7, 0x3EF, 0x77E, 0x738, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'°': [0x0E, 0x1F, 0x1F, 0x1F, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'Ä': [0x18C, 0x18C, 0x00, 0xF8, 0x1FC, 0x38E, 0x38E, 0x38E, 0x3FE, 0x7FF, 0x707, 0x707, 0x707, 0x707, 0x00, 0x00],
|
||||
'Ö': [0x318, 0x318, 0x00, 0x1F8, 0x3FC, 0x78E, 0x706, 0xE07, 0xE07, 0xE07, 0xE07, 0x706, 0x78E, 0x3FC, 0x1F8, 0x00],
|
||||
'Ü': [0x318, 0x318, 0x00, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0xE07, 0x70E, 0x7FE, 0x1F8, 0x00],
|
||||
'ß': [0x7C, 0xFE, 0x1C7, 0x1C7, 0x1C7, 0x1CE, 0x1FC, 0x1FE, 0x1CF, 0x1C7, 0x1C7, 0x1CF, 0x1FE, 0x1BC, 0x180, 0x180],
|
||||
'ä': [0xC6, 0xC6, 0x00, 0x7C, 0xFE, 0x1C7, 0x07, 0x7F, 0xFF, 0x1C7, 0x387, 0x387, 0x1FF, 0xF7, 0x00, 0x00],
|
||||
'ö': [0xC6, 0xC6, 0x00, 0x7C, 0xFE, 0x1C7, 0x383, 0x383, 0x383, 0x383, 0x383, 0x1C7, 0xFE, 0x7C, 0x00, 0x00],
|
||||
'ü': [0xC6, 0xC6, 0x00, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x387, 0x1CF, 0x1FF, 0x77, 0x00, 0x00],
|
||||
'•': [0x00, 0x00, 0x00, 0x00, 0x3C, 0x7E, 0xFF, 0xFF, 0xFF, 0xFF, 0x7E, 0x3C, 0x00, 0x00, 0x00, 0x00],
|
||||
'€': [0x1F8, 0x7FC, 0xF0E, 0x1C07, 0x1FC0, 0x1FC0, 0x3800, 0x1FC0, 0x1FC0, 0x3800, 0x1FC0, 0x1FC0, 0x1C07, 0xF0E, 0x7FC, 0x1F8],
|
||||
}
|
||||
115
pico-client/display/fonts/font_3x5.py
Normal file
115
pico-client/display/fonts/font_3x5.py
Normal file
@@ -0,0 +1,115 @@
|
||||
font_3x5 = {
|
||||
# Jedes Zeichen ist genau 3 Pixel breit, 5 Pixel hoch
|
||||
' ': [0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'!': [0x01, 0x01, 0x01, 0x00, 0x01],
|
||||
'"': [0x05, 0x05, 0x00, 0x00, 0x00],
|
||||
'#': [0x05, 0x07, 0x05, 0x07, 0x05],
|
||||
'$': [0x07, 0x05, 0x07, 0x05, 0x07],
|
||||
'%': [0x05, 0x01, 0x02, 0x04, 0x05],
|
||||
'&': [0x02, 0x05, 0x02, 0x05, 0x06],
|
||||
"'": [0x01, 0x01, 0x00, 0x00, 0x00],
|
||||
'(': [0x02, 0x04, 0x04, 0x04, 0x02],
|
||||
')': [0x04, 0x02, 0x02, 0x02, 0x04],
|
||||
'*': [0x00, 0x05, 0x02, 0x05, 0x00],
|
||||
'+': [0x00, 0x02, 0x07, 0x02, 0x00],
|
||||
',': [0x00, 0x00, 0x00, 0x02, 0x04],
|
||||
'-': [0x00, 0x00, 0x07, 0x00, 0x00],
|
||||
'.': [0x00, 0x00, 0x00, 0x00, 0x02],
|
||||
'/': [0x01, 0x01, 0x02, 0x04, 0x04],
|
||||
# Zahlen
|
||||
'0': [0x07, 0x05, 0x05, 0x05, 0x07],
|
||||
'1': [0x02, 0x06, 0x02, 0x02, 0x07],
|
||||
'2': [0x07, 0x01, 0x07, 0x04, 0x07],
|
||||
'3': [0x07, 0x01, 0x07, 0x01, 0x07],
|
||||
'4': [0x05, 0x05, 0x07, 0x01, 0x01],
|
||||
'5': [0x07, 0x04, 0x07, 0x01, 0x07],
|
||||
'6': [0x07, 0x04, 0x07, 0x05, 0x07],
|
||||
'7': [0x07, 0x01, 0x01, 0x01, 0x01],
|
||||
'8': [0x07, 0x05, 0x07, 0x05, 0x07],
|
||||
'9': [0x07, 0x05, 0x07, 0x01, 0x07],
|
||||
# Großbuchstaben
|
||||
'A': [0x02, 0x05, 0x07, 0x05, 0x05],
|
||||
'B': [0x06, 0x05, 0x06, 0x05, 0x06],
|
||||
'C': [0x07, 0x04, 0x04, 0x04, 0x07],
|
||||
'D': [0x06, 0x05, 0x05, 0x05, 0x06],
|
||||
'E': [0x07, 0x04, 0x07, 0x04, 0x07],
|
||||
'F': [0x07, 0x04, 0x07, 0x04, 0x04],
|
||||
'G': [0x07, 0x04, 0x05, 0x05, 0x07],
|
||||
'H': [0x05, 0x05, 0x07, 0x05, 0x05],
|
||||
'I': [0x07, 0x02, 0x02, 0x02, 0x07],
|
||||
'J': [0x07, 0x02, 0x02, 0x02, 0x06],
|
||||
'K': [0x05, 0x05, 0x06, 0x05, 0x05],
|
||||
'L': [0x04, 0x04, 0x04, 0x04, 0x07],
|
||||
'M': [0x05, 0x07, 0x07, 0x05, 0x05],
|
||||
'N': [0x05, 0x07, 0x07, 0x07, 0x05],
|
||||
'O': [0x07, 0x05, 0x05, 0x05, 0x07],
|
||||
'P': [0x07, 0x05, 0x07, 0x04, 0x04],
|
||||
'Q': [0x07, 0x05, 0x05, 0x07, 0x03],
|
||||
'R': [0x07, 0x05, 0x06, 0x05, 0x05],
|
||||
'S': [0x07, 0x04, 0x07, 0x01, 0x07],
|
||||
'T': [0x07, 0x02, 0x02, 0x02, 0x02],
|
||||
'U': [0x05, 0x05, 0x05, 0x05, 0x07],
|
||||
'V': [0x05, 0x05, 0x05, 0x05, 0x02],
|
||||
'W': [0x05, 0x05, 0x07, 0x07, 0x05],
|
||||
'X': [0x05, 0x05, 0x02, 0x05, 0x05],
|
||||
'Y': [0x05, 0x05, 0x07, 0x02, 0x02],
|
||||
'Z': [0x07, 0x01, 0x02, 0x04, 0x07],
|
||||
# Kleinbuchstaben
|
||||
'a': [0x00, 0x07, 0x05, 0x05, 0x07],
|
||||
'b': [0x04, 0x06, 0x05, 0x05, 0x06],
|
||||
'c': [0x00, 0x07, 0x04, 0x04, 0x07],
|
||||
'd': [0x01, 0x07, 0x05, 0x05, 0x07],
|
||||
'e': [0x00, 0x07, 0x07, 0x04, 0x07],
|
||||
'f': [0x03, 0x04, 0x06, 0x04, 0x04],
|
||||
'g': [0x07, 0x05, 0x07, 0x01, 0x06],
|
||||
'h': [0x04, 0x06, 0x05, 0x05, 0x05],
|
||||
'i': [0x02, 0x00, 0x02, 0x02, 0x02],
|
||||
'j': [0x02, 0x00, 0x02, 0x02, 0x06],
|
||||
'k': [0x04, 0x05, 0x06, 0x05, 0x05],
|
||||
'l': [0x06, 0x02, 0x02, 0x02, 0x02],
|
||||
'm': [0x00, 0x07, 0x07, 0x05, 0x05],
|
||||
'n': [0x00, 0x06, 0x05, 0x05, 0x05],
|
||||
'o': [0x00, 0x07, 0x05, 0x05, 0x07],
|
||||
'p': [0x00, 0x06, 0x05, 0x06, 0x04],
|
||||
'q': [0x00, 0x07, 0x05, 0x07, 0x01],
|
||||
'r': [0x00, 0x06, 0x05, 0x04, 0x04],
|
||||
's': [0x00, 0x07, 0x06, 0x03, 0x07],
|
||||
't': [0x02, 0x07, 0x02, 0x02, 0x03],
|
||||
'u': [0x00, 0x05, 0x05, 0x05, 0x07],
|
||||
'v': [0x00, 0x05, 0x05, 0x05, 0x02],
|
||||
'w': [0x00, 0x05, 0x05, 0x07, 0x05],
|
||||
'x': [0x00, 0x05, 0x02, 0x02, 0x05],
|
||||
'y': [0x00, 0x05, 0x05, 0x07, 0x01],
|
||||
'z': [0x00, 0x07, 0x02, 0x04, 0x07],
|
||||
# Sonderzeichen
|
||||
':': [0x00, 0x02, 0x00, 0x02, 0x00],
|
||||
';': [0x00, 0x02, 0x00, 0x02, 0x04],
|
||||
'<': [0x01, 0x02, 0x04, 0x02, 0x01],
|
||||
'=': [0x00, 0x07, 0x00, 0x07, 0x00],
|
||||
'>': [0x04, 0x02, 0x01, 0x02, 0x04],
|
||||
'?': [0x07, 0x01, 0x02, 0x00, 0x02],
|
||||
'@': [0x07, 0x05, 0x05, 0x04, 0x07],
|
||||
'[': [0x07, 0x04, 0x04, 0x04, 0x07],
|
||||
'\\': [0x04, 0x04, 0x02, 0x01, 0x01],
|
||||
']': [0x07, 0x01, 0x01, 0x01, 0x07],
|
||||
'^': [0x02, 0x05, 0x00, 0x00, 0x00],
|
||||
'_': [0x00, 0x00, 0x00, 0x00, 0x07],
|
||||
'`': [0x04, 0x02, 0x00, 0x00, 0x00],
|
||||
'{': [0x03, 0x02, 0x06, 0x02, 0x03],
|
||||
'|': [0x02, 0x02, 0x02, 0x02, 0x02],
|
||||
'}': [0x06, 0x02, 0x03, 0x02, 0x06],
|
||||
'~': [0x00, 0x05, 0x0A, 0x00, 0x00],
|
||||
'°': [0x02, 0x05, 0x02, 0x00, 0x00],
|
||||
|
||||
# DEUTSCHE UMLAUTE - Großbuchstaben
|
||||
'Ä': [0x05, 0x02, 0x05, 0x07, 0x05], # A mit Umlautpunkten
|
||||
'Ö': [0x05, 0x07, 0x05, 0x05, 0x07], # O mit Umlautpunkten
|
||||
'Ü': [0x05, 0x05, 0x05, 0x05, 0x07], # U mit Umlautpunkten
|
||||
'ẞ': [0x07, 0x05, 0x07, 0x05, 0x05], # Scharfes S (Groß)
|
||||
|
||||
# DEUTSCHE UMLAUTE - Kleinbuchstaben
|
||||
'ä': [0x05, 0x00, 0x07, 0x05, 0x07], # a mit Umlautpunkten
|
||||
'ö': [0x05, 0x00, 0x07, 0x05, 0x07], # o mit Umlautpunkten
|
||||
'ü': [0x05, 0x00, 0x05, 0x05, 0x07], # u mit Umlautpunkten
|
||||
'ß': [0x06, 0x05, 0x06, 0x04, 0x04], # scharfes s (Klein)
|
||||
}
|
||||
127
pico-client/display/fonts/font_5x7.py
Normal file
127
pico-client/display/fonts/font_5x7.py
Normal file
@@ -0,0 +1,127 @@
|
||||
# fonts.py - Optimized fonts for LED matrix with character width adjustment
|
||||
|
||||
# 5x7 Font (Optimized Width)
|
||||
font_5x7 = {
|
||||
# Uppercase letters (A-Z) - Optimized for better spacing
|
||||
'A': [0x0E, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11], # Width: 5
|
||||
'B': [0x1E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E], # Width: 5
|
||||
'C': [0x0E, 0x11, 0x10, 0x10, 0x10, 0x11, 0x0E], # Width: 5
|
||||
'D': [0x1E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1E], # Width: 5
|
||||
'E': [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x1F], # Width: 5
|
||||
'F': [0x1F, 0x10, 0x10, 0x1E, 0x10, 0x10, 0x10], # Width: 5
|
||||
'G': [0x0E, 0x11, 0x10, 0x13, 0x11, 0x11, 0x0F], # Width: 5
|
||||
'H': [0x11, 0x11, 0x11, 0x1F, 0x11, 0x11, 0x11], # Width: 5
|
||||
# 'I': [0x0E, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E], # Width: 3
|
||||
'I': [0x07, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07], # Width: 3
|
||||
'J': [0x07, 0x02, 0x02, 0x02, 0x02, 0x12, 0x0C], # Width: 4
|
||||
'K': [0x11, 0x12, 0x14, 0x18, 0x14, 0x12, 0x11], # Width: 5
|
||||
'L': [0x10, 0x10, 0x10, 0x10, 0x10, 0x10, 0x1F], # Width: 5
|
||||
'M': [0x11, 0x1B, 0x15, 0x15, 0x11, 0x11, 0x11], # Width: 5
|
||||
'N': [0x11, 0x19, 0x19, 0x15, 0x13, 0x13, 0x11], # Width: 5
|
||||
'O': [0x0E, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], # Width: 5
|
||||
'P': [0x1E, 0x11, 0x11, 0x1E, 0x10, 0x10, 0x10], # Width: 5
|
||||
'Q': [0x0E, 0x11, 0x11, 0x11, 0x15, 0x12, 0x0D], # Width: 5
|
||||
'R': [0x1E, 0x11, 0x11, 0x1E, 0x14, 0x12, 0x11], # Width: 5
|
||||
'S': [0x0F, 0x10, 0x10, 0x0E, 0x01, 0x01, 0x1E], # Width: 5
|
||||
'T': [0x1F, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04], # Width: 5
|
||||
'U': [0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x0E], # Width: 5
|
||||
'V': [0x11, 0x11, 0x11, 0x11, 0x11, 0x0A, 0x04], # Width: 5
|
||||
'W': [0x11, 0x11, 0x11, 0x15, 0x15, 0x15, 0x0A], # Width: 5
|
||||
'X': [0x11, 0x11, 0x0A, 0x04, 0x0A, 0x11, 0x11], # Width: 5
|
||||
'Y': [0x11, 0x11, 0x0A, 0x04, 0x04, 0x04, 0x04], # Width: 5
|
||||
'Z': [0x1F, 0x01, 0x02, 0x04, 0x08, 0x10, 0x1F], # Width: 5
|
||||
|
||||
# German Uppercase Umlaute
|
||||
'Ä': [0x0A, 0x00, 0x0E, 0x11, 0x1F, 0x11, 0x11], # Width: 5 (A Umlaut)
|
||||
'Ö': [0x0A, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], # Width: 5 (O Umlaut)
|
||||
'Ü': [0x0A, 0x00, 0x11, 0x11, 0x11, 0x11, 0x0E], # Width: 5 (U Umlaut)
|
||||
'ẞ': [0x0E, 0x11, 0x11, 0x1E, 0x11, 0x11, 0x1E], # Width: 5 (Eszett - scharfes S)
|
||||
|
||||
# Lowercase letters (a-z) - Optimized for compact display
|
||||
'a': [0x00, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], # Width: 4
|
||||
'b': [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x1E], # Width: 5
|
||||
'c': [0x00, 0x00, 0x0E, 0x11, 0x10, 0x11, 0x0E], # Width: 4
|
||||
'd': [0x01, 0x01, 0x0D, 0x13, 0x11, 0x11, 0x0F], # Width: 5
|
||||
'e': [0x00, 0x00, 0x0E, 0x11, 0x1F, 0x10, 0x0E], # Width: 4
|
||||
'f': [0x06, 0x09, 0x08, 0x1C, 0x08, 0x08, 0x08], # Width: 4
|
||||
'g': [0x00, 0x0F, 0x11, 0x11, 0x0F, 0x01, 0x0E], # Width: 5
|
||||
'h': [0x10, 0x10, 0x16, 0x19, 0x11, 0x11, 0x11], # Width: 5
|
||||
# 'i': [0x00, 0x04, 0x00, 0x0C, 0x04, 0x04, 0x0E], # Width: 2
|
||||
'i': [0x00, 0x02, 0x00, 0x06, 0x02, 0x02, 0x07], # Width: 2
|
||||
'j': [0x00, 0x02, 0x00, 0x06, 0x02, 0x12, 0x0C], # Width: 3
|
||||
'k': [0x10, 0x10, 0x12, 0x14, 0x18, 0x14, 0x12], # Width: 4
|
||||
# 'l': [0x0C, 0x04, 0x04, 0x04, 0x04, 0x04, 0x0E], # Width: 3
|
||||
'l': [0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x07], # Width: 3
|
||||
'm': [0x00, 0x00, 0x1A, 0x15, 0x15, 0x15, 0x15], # Width: 5
|
||||
'n': [0x00, 0x00, 0x16, 0x19, 0x11, 0x11, 0x11], # Width: 5
|
||||
'o': [0x00, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], # Width: 4
|
||||
'p': [0x00, 0x00, 0x1E, 0x11, 0x1E, 0x10, 0x10], # Width: 5
|
||||
'q': [0x00, 0x00, 0x0D, 0x13, 0x0F, 0x01, 0x01], # Width: 5
|
||||
'r': [0x00, 0x00, 0x16, 0x19, 0x10, 0x10, 0x10], # Width: 5
|
||||
's': [0x00, 0x00, 0x0E, 0x10, 0x0E, 0x01, 0x1E], # Width: 4
|
||||
't': [0x08, 0x08, 0x1C, 0x08, 0x08, 0x09, 0x06], # Width: 4
|
||||
'u': [0x00, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D], # Width: 5
|
||||
'v': [0x00, 0x00, 0x11, 0x11, 0x11, 0x0A, 0x04], # Width: 5
|
||||
'w': [0x00, 0x00, 0x11, 0x11, 0x15, 0x15, 0x0A], # Width: 5
|
||||
'x': [0x00, 0x00, 0x11, 0x0A, 0x04, 0x0A, 0x11], # Width: 5
|
||||
'y': [0x00, 0x00, 0x11, 0x11, 0x0F, 0x01, 0x0E], # Width: 5
|
||||
'z': [0x00, 0x00, 0x1F, 0x02, 0x04, 0x08, 0x1F], # Width: 5
|
||||
# German Lowercase Umlaute
|
||||
'ä': [0x0A, 0x00, 0x0E, 0x01, 0x0F, 0x11, 0x0F], # Width: 4 (a Umlaut)
|
||||
'ö': [0x0A, 0x00, 0x0E, 0x11, 0x11, 0x11, 0x0E], # Width: 4 (o Umlaut)
|
||||
'ü': [0x0A, 0x00, 0x11, 0x11, 0x11, 0x13, 0x0D], # Width: 5 (u Umlaut)
|
||||
'ß': [0x00, 0x00, 0x0E, 0x11, 0x1E, 0x11, 0x1E], # Width: 5 (Eszett - scharfes S)
|
||||
|
||||
# Numbers (0-9) - Optimized for consistent width
|
||||
'0': [0x0E, 0x11, 0x13, 0x15, 0x19, 0x11, 0x0E], # Width: 5
|
||||
'1': [0x02, 0x06, 0x02, 0x02, 0x02, 0x02, 0x07], # Width: 3
|
||||
# '1': [0x04, 0x0C, 0x04, 0x04, 0x04, 0x04, 0x0E], # Width: 3
|
||||
# '1': [0x08, 0x18, 0x08, 0x08, 0x08, 0x08, 0x1C], # Width: 3
|
||||
'2': [0x0E, 0x11, 0x01, 0x02, 0x04, 0x08, 0x1F], # Width: 5
|
||||
'3': [0x1F, 0x02, 0x04, 0x02, 0x01, 0x11, 0x0E], # Width: 5
|
||||
'4': [0x02, 0x06, 0x0A, 0x12, 0x1F, 0x02, 0x02], # Width: 5
|
||||
'5': [0x1F, 0x10, 0x1E, 0x01, 0x01, 0x11, 0x0E], # Width: 5
|
||||
'6': [0x06, 0x08, 0x10, 0x1E, 0x11, 0x11, 0x0E], # Width: 5
|
||||
'7': [0x1F, 0x01, 0x02, 0x04, 0x08, 0x08, 0x08], # Width: 5
|
||||
'8': [0x0E, 0x11, 0x11, 0x0E, 0x11, 0x11, 0x0E], # Width: 5
|
||||
'9': [0x0E, 0x11, 0x11, 0x0F, 0x01, 0x02, 0x0C], # Width: 5
|
||||
# Punctuation and symbols - Optimized widths
|
||||
' ': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # Width: 2
|
||||
# '!': [0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x04], # Width: 1
|
||||
'!': [0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01], # Width: 1
|
||||
'?': [0x0E, 0x11, 0x02, 0x04, 0x04, 0x00, 0x04], # Width: 5
|
||||
'.': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01], # Width: 1
|
||||
',': [0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02], # Width: 1
|
||||
':': [0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00], # Width: 1
|
||||
';': [0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x02], # Width: 1
|
||||
"'": [0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00], # Width: 1
|
||||
'"': [0x05, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00], # Width: 3
|
||||
'-': [0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00], # Width: 5
|
||||
'_': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F], # Width: 5
|
||||
'+': [0x00, 0x04, 0x04, 0x1F, 0x04, 0x04, 0x00], # Width: 5
|
||||
'=': [0x00, 0x00, 0x1F, 0x00, 0x1F, 0x00, 0x00], # Width: 5
|
||||
'*': [0x00, 0x0A, 0x04, 0x1F, 0x04, 0x0A, 0x00], # Width: 5
|
||||
'/': [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x00], # Width: 5
|
||||
'\\': [0x00, 0x10, 0x08, 0x04, 0x02, 0x01, 0x00], # Width: 5
|
||||
'(': [0x01, 0x02, 0x04, 0x04, 0x04, 0x02, 0x01], # Width: 3
|
||||
')': [0x04, 0x02, 0x01, 0x01, 0x01, 0x02, 0x04], # Width: 3
|
||||
'[': [0x07, 0x04, 0x04, 0x04, 0x04, 0x04, 0x07], # Width: 3
|
||||
']': [0x07, 0x01, 0x01, 0x01, 0x01, 0x01, 0x07], # Width: 3
|
||||
'{': [0x03, 0x02, 0x02, 0x04, 0x02, 0x02, 0x03], # Width: 3
|
||||
'}': [0x06, 0x02, 0x02, 0x01, 0x02, 0x02, 0x06], # Width: 3
|
||||
'<': [0x00, 0x01, 0x02, 0x04, 0x02, 0x01, 0x00], # Width: 4
|
||||
'>': [0x00, 0x04, 0x02, 0x01, 0x02, 0x04, 0x00], # Width: 4
|
||||
'@': [0x0E, 0x11, 0x17, 0x15, 0x17, 0x10, 0x0E], # Width: 5
|
||||
'#': [0x0A, 0x0A, 0x1F, 0x0A, 0x1F, 0x0A, 0x0A], # Width: 5
|
||||
'$': [0x04, 0x0F, 0x14, 0x0E, 0x05, 0x1E, 0x04], # Width: 5
|
||||
'%': [0x18, 0x19, 0x02, 0x04, 0x08, 0x13, 0x03], # Width: 5
|
||||
'&': [0x0C, 0x12, 0x14, 0x08, 0x15, 0x12, 0x0D], # Width: 5
|
||||
'^': [0x04, 0x0A, 0x11, 0x00, 0x00, 0x00, 0x00], # Width: 5
|
||||
'~': [0x00, 0x00, 0x00, 0x0D, 0x12, 0x00, 0x00], # Width: 5
|
||||
# Special characters
|
||||
'°': [0x07, 0x05, 0x07, 0x00, 0x00, 0x00, 0x00], # Width: 3 (Degree symbol)
|
||||
'|': [0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01], # Width: 1 (Vertical bar)
|
||||
'`': [0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00], # Width: 2 (Backtick)
|
||||
'¶': [0x0F, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D], # Width: 5 (Paragraph symbol)
|
||||
'•': [0x00, 0x00, 0x02, 0x07, 0x02, 0x00, 0x00], # Width: 3 (Bullet point)
|
||||
}
|
||||
104
pico-client/display/fonts/font_8x8.py
Normal file
104
pico-client/display/fonts/font_8x8.py
Normal file
@@ -0,0 +1,104 @@
|
||||
font_8x8 = {
|
||||
' ': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'!': [0x03, 0x03, 0x03, 0x03, 0x00, 0x00, 0x03, 0x00],
|
||||
'"': [0x1B, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'#': [0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00],
|
||||
'$': [0x0C, 0x1F, 0x30, 0x1E, 0x03, 0x3E, 0x0C, 0x00],
|
||||
'%': [0x31, 0x33, 0x06, 0x0C, 0x18, 0x33, 0x23, 0x00],
|
||||
'&': [0x3C, 0x66, 0x3C, 0x38, 0x67, 0x66, 0x3F, 0x00],
|
||||
"'": [0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'(': [0x03, 0x06, 0x0C, 0x0C, 0x0C, 0x06, 0x03, 0x00],
|
||||
')': [0x0C, 0x06, 0x03, 0x03, 0x03, 0x06, 0x0C, 0x00],
|
||||
'*': [0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00],
|
||||
'+': [0x00, 0x0C, 0x0C, 0x3F, 0x0C, 0x0C, 0x00, 0x00],
|
||||
',': [0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x06],
|
||||
'-': [0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00],
|
||||
'.': [0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x03, 0x00],
|
||||
'/': [0x00, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x00, 0x00],
|
||||
'0': [0x1E, 0x33, 0x37, 0x3B, 0x33, 0x33, 0x1E, 0x00],
|
||||
'1': [0x0C, 0x1C, 0x0C, 0x0C, 0x0C, 0x0C, 0x3F, 0x00],
|
||||
'2': [0x1E, 0x33, 0x03, 0x06, 0x0C, 0x18, 0x3F, 0x00],
|
||||
'3': [0x1E, 0x33, 0x03, 0x0E, 0x03, 0x33, 0x1E, 0x00],
|
||||
'4': [0x06, 0x0E, 0x1E, 0x36, 0x3F, 0x06, 0x06, 0x00],
|
||||
'5': [0x3F, 0x30, 0x3E, 0x03, 0x03, 0x33, 0x1E, 0x00],
|
||||
'6': [0x1E, 0x33, 0x30, 0x3E, 0x33, 0x33, 0x1E, 0x00],
|
||||
'7': [0x3F, 0x03, 0x06, 0x0C, 0x0C, 0x0C, 0x0C, 0x00],
|
||||
'8': [0x1E, 0x33, 0x33, 0x1E, 0x33, 0x33, 0x1E, 0x00],
|
||||
'9': [0x1E, 0x33, 0x33, 0x1F, 0x03, 0x33, 0x1E, 0x00],
|
||||
':': [0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00],
|
||||
';': [0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x06],
|
||||
'<': [0x00, 0x03, 0x06, 0x0C, 0x06, 0x03, 0x00, 0x00],
|
||||
'=': [0x00, 0x00, 0x3F, 0x00, 0x3F, 0x00, 0x00, 0x00],
|
||||
'>': [0x00, 0x0C, 0x06, 0x03, 0x06, 0x0C, 0x00, 0x00],
|
||||
'?': [0x1E, 0x33, 0x06, 0x0C, 0x0C, 0x00, 0x0C, 0x00],
|
||||
'@': [0x3C, 0x42, 0x99, 0xA5, 0xA5, 0x9E, 0x40, 0x3C],
|
||||
'A': [0x0C, 0x1E, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x00],
|
||||
'B': [0x3E, 0x33, 0x33, 0x3E, 0x33, 0x33, 0x3E, 0x00],
|
||||
'C': [0x1E, 0x33, 0x30, 0x30, 0x30, 0x33, 0x1E, 0x00],
|
||||
'D': [0x3C, 0x36, 0x33, 0x33, 0x33, 0x36, 0x3C, 0x00],
|
||||
'E': [0x3F, 0x30, 0x30, 0x3C, 0x30, 0x30, 0x3F, 0x00],
|
||||
'F': [0x3F, 0x30, 0x30, 0x3C, 0x30, 0x30, 0x30, 0x00],
|
||||
'G': [0x1E, 0x33, 0x30, 0x37, 0x33, 0x33, 0x1E, 0x00],
|
||||
'H': [0x33, 0x33, 0x33, 0x3F, 0x33, 0x33, 0x33, 0x00],
|
||||
'I': [0x0F, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0F, 0x00],
|
||||
'J': [0x0F, 0x06, 0x06, 0x06, 0x06, 0x36, 0x1C, 0x00],
|
||||
'K': [0x33, 0x36, 0x3C, 0x38, 0x3C, 0x36, 0x33, 0x00],
|
||||
'L': [0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x3F, 0x00],
|
||||
'M': [0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00],
|
||||
'N': [0x33, 0x3B, 0x3F, 0x3F, 0x37, 0x33, 0x33, 0x00],
|
||||
'O': [0x1E, 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00],
|
||||
'P': [0x3E, 0x33, 0x33, 0x3E, 0x30, 0x30, 0x30, 0x00],
|
||||
'Q': [0x1E, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x07, 0x00],
|
||||
'R': [0x3E, 0x33, 0x33, 0x3E, 0x3C, 0x36, 0x33, 0x00],
|
||||
'S': [0x1E, 0x33, 0x30, 0x1E, 0x03, 0x33, 0x1E, 0x00],
|
||||
'T': [0x3F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x00],
|
||||
'U': [0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x00],
|
||||
'V': [0x33, 0x33, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00],
|
||||
'W': [0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00],
|
||||
'X': [0x33, 0x33, 0x1E, 0x0C, 0x1E, 0x33, 0x33, 0x00],
|
||||
'Y': [0x33, 0x33, 0x33, 0x1E, 0x0C, 0x0C, 0x0C, 0x00],
|
||||
'Z': [0x3F, 0x03, 0x06, 0x0C, 0x18, 0x30, 0x3F, 0x00],
|
||||
'[': [0x0F, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0F, 0x00],
|
||||
'\\': [0x00, 0x30, 0x18, 0x0C, 0x06, 0x03, 0x00, 0x00],
|
||||
']': [0x0F, 0x03, 0x03, 0x03, 0x03, 0x03, 0x0F, 0x00],
|
||||
'^': [0x0C, 0x1E, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'_': [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F],
|
||||
'`': [0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
||||
'a': [0x00, 0x00, 0x1E, 0x03, 0x1F, 0x33, 0x1F, 0x00],
|
||||
'b': [0x30, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x3E, 0x00],
|
||||
'c': [0x00, 0x00, 0x1E, 0x33, 0x30, 0x33, 0x1E, 0x00],
|
||||
'd': [0x03, 0x03, 0x1F, 0x33, 0x33, 0x33, 0x1F, 0x00],
|
||||
'e': [0x00, 0x00, 0x1E, 0x33, 0x3F, 0x30, 0x1E, 0x00],
|
||||
'f': [0x0E, 0x1B, 0x18, 0x3C, 0x18, 0x18, 0x18, 0x00],
|
||||
'g': [0x00, 0x00, 0x1F, 0x33, 0x33, 0x1F, 0x03, 0x3E],
|
||||
'h': [0x30, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x00],
|
||||
'i': [0x06, 0x00, 0x0E, 0x06, 0x06, 0x06, 0x0F, 0x00],
|
||||
'j': [0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x33, 0x1E],
|
||||
'k': [0x30, 0x30, 0x33, 0x36, 0x3C, 0x36, 0x33, 0x00],
|
||||
'l': [0x0E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x0F, 0x00],
|
||||
'm': [0x00, 0x00, 0x66, 0x7F, 0x7F, 0x6B, 0x63, 0x00],
|
||||
'n': [0x00, 0x00, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x00],
|
||||
'o': [0x00, 0x00, 0x1E, 0x33, 0x33, 0x33, 0x1E, 0x00],
|
||||
'p': [0x00, 0x00, 0x3E, 0x33, 0x33, 0x3E, 0x30, 0x30],
|
||||
'q': [0x00, 0x00, 0x1F, 0x33, 0x33, 0x1F, 0x03, 0x03],
|
||||
'r': [0x00, 0x00, 0x3E, 0x33, 0x30, 0x30, 0x30, 0x00],
|
||||
's': [0x00, 0x00, 0x1F, 0x30, 0x1E, 0x03, 0x3E, 0x00],
|
||||
't': [0x0C, 0x0C, 0x1F, 0x0C, 0x0C, 0x0C, 0x07, 0x00],
|
||||
'u': [0x00, 0x00, 0x33, 0x33, 0x33, 0x33, 0x1F, 0x00],
|
||||
'v': [0x00, 0x00, 0x33, 0x33, 0x33, 0x1E, 0x0C, 0x00],
|
||||
'w': [0x00, 0x00, 0x63, 0x6B, 0x7F, 0x3E, 0x36, 0x00],
|
||||
'x': [0x00, 0x00, 0x33, 0x1E, 0x0C, 0x1E, 0x33, 0x00],
|
||||
'y': [0x00, 0x00, 0x33, 0x33, 0x33, 0x1F, 0x06, 0x3C],
|
||||
'z': [0x00, 0x00, 0x3F, 0x06, 0x0C, 0x18, 0x3F, 0x00],
|
||||
'{': [0x07, 0x0C, 0x0C, 0x38, 0x0C, 0x0C, 0x07, 0x00],
|
||||
'|': [0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00],
|
||||
'}': [0x38, 0x0C, 0x0C, 0x07, 0x0C, 0x0C, 0x38, 0x00],
|
||||
'~': [0x00, 0x00, 0x00, 0x1B, 0x36, 0x00, 0x00, 0x00],
|
||||
'°': [0x0E, 0x11, 0x11, 0x0E, 0x00, 0x00, 0x00, 0x00],
|
||||
'¶': [0x1F, 0x3D, 0x3D, 0x3D, 0x1D, 0x05, 0x05, 0x00],
|
||||
'•': [0x00, 0x00, 0x06, 0x0F, 0x0F, 0x06, 0x00, 0x00],
|
||||
'♠': [0x1C, 0x3E, 0x7F, 0x7F, 0x1C, 0x1C, 0x3E, 0x00],
|
||||
'♣': [0x1C, 0x1C, 0x3E, 0x7F, 0x36, 0x1C, 0x3E, 0x00],
|
||||
'♥': [0x36, 0x7F, 0x7F, 0x7F, 0x3E, 0x1C, 0x08, 0x00],
|
||||
'♦': [0x08, 0x1C, 0x3E, 0x7F, 0x3E, 0x1C, 0x08, 0x00],
|
||||
}
|
||||
418
pico-client/display/neopixel_64x64.py
Normal file
418
pico-client/display/neopixel_64x64.py
Normal file
@@ -0,0 +1,418 @@
|
||||
from machine import Pin # type: ignore
|
||||
from neopixel import NeoPixel # type: ignore
|
||||
from utils import char_width
|
||||
|
||||
from display import fonts
|
||||
from utils import colors
|
||||
from utils.time_utils import (
|
||||
get_german_timestamp_short,
|
||||
get_datetime_string,
|
||||
get_german_time_ticks,
|
||||
get_german_date_ticks,
|
||||
)
|
||||
import time
|
||||
import math
|
||||
|
||||
|
||||
class NeoPixel_64x64(NeoPixel):
|
||||
MATRIX_WIDTH = 64
|
||||
MATRIX_HEIGHT = 64
|
||||
|
||||
def __init__(self, pin=28):
|
||||
"""
|
||||
Initialize LED Display
|
||||
|
||||
Args:
|
||||
width: Matrix width in pixels
|
||||
height: Matrix height in pixels
|
||||
pin: NeoPixel data pin
|
||||
"""
|
||||
super().__init__(Pin(pin), self.MATRIX_WIDTH * self.MATRIX_HEIGHT)
|
||||
|
||||
# Font configuration
|
||||
self.set_font(fonts.font_5x7)
|
||||
|
||||
def set_font(self, font):
|
||||
"""
|
||||
Set the current font for text rendering
|
||||
|
||||
Args:
|
||||
font: Font dictionary from fonts_array
|
||||
"""
|
||||
self.selected_font = font
|
||||
|
||||
# Höhe des Fonts, Anzahl der Zeilen für jedes Zeichen
|
||||
# wir holen den ersten Wert des Fonts
|
||||
first_char = next(iter(self.selected_font))
|
||||
self.font_height = len(self.selected_font[first_char])
|
||||
|
||||
def set_pixel(self, x, y, color):
|
||||
"""
|
||||
Set a single pixel to the specified color
|
||||
|
||||
Args:
|
||||
x: X coordinate
|
||||
y: Y coordinate
|
||||
color: RGB color tuple
|
||||
"""
|
||||
index = y * self.MATRIX_WIDTH + x
|
||||
if 0 <= x < self.MATRIX_WIDTH and 0 <= y < self.MATRIX_HEIGHT:
|
||||
self[index] = color
|
||||
|
||||
def clear(self):
|
||||
"""Clear the entire display (turn off all pixels)"""
|
||||
for i in range(len(self)):
|
||||
self[i] = colors.BLACK
|
||||
self.write()
|
||||
|
||||
def clear_box(
|
||||
self,
|
||||
from_row: int,
|
||||
from_col: int,
|
||||
to_row: int,
|
||||
to_col: int,
|
||||
color: tuple[3] = colors.BLACK,
|
||||
) -> None:
|
||||
"""löscht einen Bereich (Box) im Display
|
||||
|
||||
Args:
|
||||
from_row (int): Start-Zeile
|
||||
from_col (int): Start-Spalte
|
||||
to_row (int): End-Zeile
|
||||
to_col (int): End-Spalte
|
||||
"""
|
||||
for row in range(from_row, to_row):
|
||||
for col in range(from_col, to_col + 1):
|
||||
idx: int = row * self.MATRIX_WIDTH + col
|
||||
self[idx] = color
|
||||
|
||||
self.write()
|
||||
|
||||
def clear_row(self, row: int, effect: bool = False) -> None:
|
||||
"""löscht eine Zeile im Display entsprechend des eingestellten Fonts
|
||||
|
||||
Args:
|
||||
row (int): in Pixel start bei 0 !!!
|
||||
effect (bool, optional): zeilenweise bzw. pixelweise löschen. Defaults to False.
|
||||
"""
|
||||
start = row * self.MATRIX_WIDTH
|
||||
ende = start + self.font_height * self.MATRIX_WIDTH - 1
|
||||
|
||||
print(f"clear row: {row} --> pixels {start} to {ende}")
|
||||
for i in range(start, ende):
|
||||
self[i] = colors.BLACK
|
||||
if effect and i % self.MATRIX_WIDTH == 0:
|
||||
self.write()
|
||||
|
||||
self.write()
|
||||
|
||||
def draw_letter(self, letter, x, y, color):
|
||||
"""
|
||||
Draw a single letter using current font with optimized width
|
||||
|
||||
Args:
|
||||
letter: Character to draw
|
||||
x: X position
|
||||
y: Y position
|
||||
color: RGB color tuple
|
||||
"""
|
||||
|
||||
if letter in self.selected_font:
|
||||
char_data = self.selected_font[letter]
|
||||
charwidth = char_width(char_data)
|
||||
|
||||
# background for the letter (full font size)
|
||||
[
|
||||
# print(xpos, ypos)
|
||||
self.set_pixel(xpos, ypos, colors.GRAY)
|
||||
for xpos in range(
|
||||
x, x + charwidth
|
||||
) # 8 because full with of character representation
|
||||
for ypos in range(y, y + self.font_height)
|
||||
]
|
||||
|
||||
for row in range(self.font_height):
|
||||
row_data = char_data[row]
|
||||
|
||||
for col in range(charwidth):
|
||||
# Check if pixel should be lit (MSB first)
|
||||
# Only check bits within the actual character width
|
||||
if row_data & (1 << ((charwidth - 1) - col)):
|
||||
self.set_pixel(x + col, y + row, color)
|
||||
else:
|
||||
print(f"oops, letter does not exist in the font -> {letter}")
|
||||
|
||||
def draw_text(self, text, x, y, color):
|
||||
"""
|
||||
Draw text with optimized character spacing
|
||||
|
||||
Args:
|
||||
text: Text to draw
|
||||
x: Starting X position
|
||||
y: Starting Y position
|
||||
color: RGB color tuple
|
||||
"""
|
||||
current_x = x
|
||||
for char in text:
|
||||
self.draw_letter(char, current_x, y, color)
|
||||
# Move cursor by character width + 1 pixel spacing
|
||||
charwidth = char_width(self.selected_font[char])
|
||||
current_x += charwidth + 1
|
||||
|
||||
def show_hello(self):
|
||||
"""Display HELLO with timestamp"""
|
||||
self.clear()
|
||||
|
||||
# Draw HELLO in rainbow colors
|
||||
self.draw_text("HELLO!", 6, 4, colors.RAINBOW[2])
|
||||
|
||||
# Show timestamp
|
||||
datetimestr = get_german_timestamp_short()
|
||||
self.draw_text(datetimestr, 2, 15, colors.RAINBOW[4])
|
||||
|
||||
self.write()
|
||||
|
||||
def vertical_floating_text(
|
||||
self, text, x, color=colors.RAINBOW[0], float_range=3, speed=0.2, duration=10
|
||||
):
|
||||
"""
|
||||
Vertical floating text animation
|
||||
|
||||
Args:
|
||||
text: Text to display
|
||||
x: X position (fixed)
|
||||
color: Text color
|
||||
float_range: How many pixels to float up/down
|
||||
speed: Animation speed
|
||||
duration: How long to run animation in seconds
|
||||
"""
|
||||
start_time = time.time()
|
||||
|
||||
while time.time() - start_time < duration:
|
||||
# Calculate floating offset using sine wave
|
||||
offset = math.sin(time.time() * speed) * float_range
|
||||
current_y = int(
|
||||
self.MATRIX_HEIGHT // 2 + offset - (len(text) * self.font_height) // 2
|
||||
)
|
||||
|
||||
self.clear()
|
||||
|
||||
# Draw each letter vertically
|
||||
for i, char in enumerate(text):
|
||||
char_y = current_y + (i * self.font_height)
|
||||
# Keep text within matrix bounds
|
||||
if 0 <= char_y < self.MATRIX_HEIGHT - self.font_height:
|
||||
self.draw_letter(char, x, char_y, color)
|
||||
|
||||
self.write()
|
||||
time.sleep(0.05)
|
||||
|
||||
def horizontal_floating_text(
|
||||
self, text, y, color=colors.RAINBOW[0], float_range=3, speed=0.2, duration=10
|
||||
):
|
||||
"""
|
||||
Horizontal floating text animation
|
||||
|
||||
Args:
|
||||
text: Text to display
|
||||
y: Y position (fixed)
|
||||
color: Text color
|
||||
float_range: How many pixels to float left/right
|
||||
speed: Animation speed
|
||||
duration: How long to run animation in seconds
|
||||
"""
|
||||
start_time = time.time()
|
||||
counter = 0
|
||||
|
||||
while time.time() - start_time < duration:
|
||||
# Calculate floating offset using sine wave
|
||||
offset = math.sin(counter) * float_range
|
||||
current_x = int(offset) # to right
|
||||
|
||||
self.clear()
|
||||
|
||||
# Draw text at floating position
|
||||
for i, char in enumerate(text):
|
||||
charwidth = char_width(self.selected_font[char])
|
||||
char_x = current_x + (i * (charwidth + 1))
|
||||
# Keep text within matrix bounds
|
||||
if 0 <= char_x < self.MATRIX_WIDTH - charwidth:
|
||||
self.draw_letter(char, char_x, y, color)
|
||||
|
||||
self.write()
|
||||
counter += speed
|
||||
time.sleep(0.05)
|
||||
|
||||
def rotate_text_left_continuous(
|
||||
self, text, y, color=colors.RAINBOW[0], speed=1.0, duration=10
|
||||
):
|
||||
"""
|
||||
Continuous left rotation - text wraps around seamlessly
|
||||
|
||||
Args:
|
||||
text: Text to display
|
||||
y: Y position (fixed)
|
||||
color: Text color
|
||||
speed: Movement speed
|
||||
duration: How long to run animation in seconds
|
||||
"""
|
||||
start_time = time.time()
|
||||
# hier müssen wir die Länge/Breite in Pixel des gesamten Textes berechnen
|
||||
# text_width_overall = len(text) * (self.font_width + 1)
|
||||
### TODO: ACHTUNG: noch zu testen !!!
|
||||
# Breite jedes Zeichens
|
||||
text_width_overall = sum(
|
||||
[char_width(self.selected_font[char]) for char in text]
|
||||
)
|
||||
|
||||
position = 0
|
||||
|
||||
while time.time() - start_time < duration:
|
||||
self.clear()
|
||||
|
||||
# Draw text at current position
|
||||
for i, char in enumerate(text):
|
||||
charwidth = char_width(self.selected_font[char])
|
||||
char_x = int(position + (i * (charwidth + 1)))
|
||||
# Handle wrapping
|
||||
if char_x < -charwidth:
|
||||
char_x += self.MATRIX_WIDTH + text_width_overall
|
||||
if 0 <= char_x < self.MATRIX_WIDTH:
|
||||
self.draw_letter(char, char_x, y, color)
|
||||
|
||||
self.write()
|
||||
|
||||
# Move left and wrap around
|
||||
position -= speed
|
||||
if position < -text_width_overall:
|
||||
position = self.MATRIX_WIDTH
|
||||
|
||||
time.sleep(0.05)
|
||||
|
||||
def draw_rectangle(self, x, y, width, height, color, fill=False):
|
||||
"""
|
||||
Draw a rectangle
|
||||
|
||||
Args:
|
||||
x: Top-left X position
|
||||
y: Top-left Y position
|
||||
width: Rectangle width
|
||||
height: Rectangle height
|
||||
color: RGB color tuple
|
||||
fill: Whether to fill the rectangle
|
||||
"""
|
||||
if fill:
|
||||
for i in range(height):
|
||||
for j in range(width):
|
||||
self.set_pixel(x + j, y + i, color)
|
||||
else:
|
||||
# Top and bottom edges
|
||||
for i in range(width):
|
||||
self.set_pixel(x + i, y, color)
|
||||
self.set_pixel(x + i, y + height - 1, color)
|
||||
# Left and right edges
|
||||
for i in range(height):
|
||||
self.set_pixel(x, y + i, color)
|
||||
self.set_pixel(x + width - 1, y + i, color)
|
||||
|
||||
def draw_line(self, x1, y1, x2, y2, color):
|
||||
"""
|
||||
Draw a line using Bresenham's algorithm
|
||||
|
||||
Args:
|
||||
x1, y1: Start coordinates
|
||||
x2, y2: End coordinates
|
||||
color: RGB color tuple
|
||||
"""
|
||||
dx = abs(x2 - x1)
|
||||
dy = abs(y2 - y1)
|
||||
sx = 1 if x1 < x2 else -1
|
||||
sy = 1 if y1 < y2 else -1
|
||||
err = dx - dy
|
||||
|
||||
while True:
|
||||
self.set_pixel(x1, y1, color)
|
||||
if x1 == x2 and y1 == y2:
|
||||
break
|
||||
e2 = 2 * err
|
||||
if e2 > -dy:
|
||||
err -= dy
|
||||
x1 += sx
|
||||
if e2 < dx:
|
||||
err += dx
|
||||
y1 += sy
|
||||
|
||||
def write_text(self, text: str, xpos: int, ypos: int, color=colors.WHITE) -> None:
|
||||
self.draw_text(text, xpos, ypos, color) # Pixel setzen
|
||||
self.write() # und anzeigen
|
||||
|
||||
def screen_text(self, text: str):
|
||||
"""Text für einen Screen anpassen,
|
||||
Anzahl der Zeichen je Zeile begrenzen,
|
||||
ebenso wird die Höhe des Screen brücksichtigt
|
||||
|
||||
|
||||
Args:
|
||||
text (str): Text der ausgegeben werden soll
|
||||
font: Berechnungen für den Font
|
||||
height (int): Pixel
|
||||
width (int): Pixel
|
||||
"""
|
||||
|
||||
def text_per_row(txt):
|
||||
pixs = 0
|
||||
visible_text = ""
|
||||
for a in txt:
|
||||
pixs += char_width(self.selected_font[a]) + 1
|
||||
if pixs > self.MATRIX_WIDTH:
|
||||
# Zeilenende erreicht
|
||||
break
|
||||
visible_text += a
|
||||
|
||||
return visible_text
|
||||
|
||||
# Ganzzahl Division
|
||||
max_visible_rows = self.MATRIX_HEIGHT // self.font_height
|
||||
print(f"rows_visible: {max_visible_rows}")
|
||||
|
||||
text_left = text
|
||||
scn_txt = []
|
||||
for _ in range(max_visible_rows):
|
||||
visible_text = text_per_row(text_left)
|
||||
visible_text_len = len(visible_text)
|
||||
text_left = text_left[visible_text_len:]
|
||||
|
||||
scn_txt.append(visible_text)
|
||||
|
||||
if not text_left:
|
||||
break
|
||||
|
||||
scr_txt_dict = {"visible": scn_txt, "invisible": text_left}
|
||||
|
||||
return scr_txt_dict
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
# Create display instance
|
||||
display = NeoPixel_64x64()
|
||||
|
||||
print("LED Matrix Display Initialized")
|
||||
print(get_datetime_string())
|
||||
print(f"German time: {get_german_time_ticks()}")
|
||||
print(f"German date: {get_german_date_ticks()}")
|
||||
|
||||
# Demo various functions
|
||||
display.show_hello()
|
||||
time.sleep(3)
|
||||
|
||||
display.vertical_floating_text("HELLO", 30, colors.RAINBOW[0], 5, 0.15, 5)
|
||||
display.horizontal_floating_text("FLOAT", 28, colors.RAINBOW[1], 10, 0.1, 5)
|
||||
display.rotate_text_left_continuous("ROTATE FLOAT", 28, speed=8.0, duration=5)
|
||||
|
||||
# Draw some shapes
|
||||
display.clear()
|
||||
display.draw_rectangle(5, 5, 10, 10, colors.RAINBOW[0])
|
||||
display.draw_rectangle(20, 20, 15, 8, colors.RAINBOW[2], fill=True)
|
||||
display.draw_line(0, 0, 63, 63, colors.RAINBOW[4])
|
||||
display.write()
|
||||
BIN
pico-client/firmware/Pico/RPI_PICO-20250911-v1.26.1.uf2
Normal file
BIN
pico-client/firmware/Pico/RPI_PICO-20250911-v1.26.1.uf2
Normal file
Binary file not shown.
BIN
pico-client/firmware/Pico/RPI_PICO_W-20250911-v1.26.1.uf2
Normal file
BIN
pico-client/firmware/Pico/RPI_PICO_W-20250911-v1.26.1.uf2
Normal file
Binary file not shown.
BIN
pico-client/firmware/Pico2/RPI_PICO2_W-20250911-v1.26.1.uf2
Normal file
BIN
pico-client/firmware/Pico2/RPI_PICO2_W-20250911-v1.26.1.uf2
Normal file
Binary file not shown.
77
pico-client/main.py
Normal file
77
pico-client/main.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from display import NeoPixel_64x64
|
||||
from display.fonts import font_5x7
|
||||
from tryout import Font_Checker, Weather_Checker, Emoji_Checker
|
||||
from utils import show_system_load
|
||||
from utils import (
|
||||
sync_ntp_time,
|
||||
get_datetime_string,
|
||||
get_german_datetime,
|
||||
) # Time-related functions
|
||||
from utils import SimpleCounter, colors
|
||||
import time
|
||||
from utils.digital_clock import DigitalClock
|
||||
import uasyncio as asyncio # type: ignore
|
||||
from web import Wlan
|
||||
|
||||
CITY_LIST: list[str] = sorted(
|
||||
["Großhansdorf", "Columbus", "London", "Ebeltoft", "Tokio"]
|
||||
)
|
||||
|
||||
|
||||
async def weather_check_task(weather_checker: Weather_Checker):
|
||||
while True:
|
||||
for city in CITY_LIST:
|
||||
weather_checker.check(city=city, lang="de", test_mode=False)
|
||||
print(f"Checked {city}")
|
||||
await asyncio.sleep(3 * 60) # Non-blocking sleep
|
||||
|
||||
|
||||
async def print_time_task() -> None:
|
||||
bottom_ypos = display.MATRIX_HEIGHT - display.font_height
|
||||
simpleCnt: SimpleCounter = SimpleCounter()
|
||||
digitalClock: DigitalClock = DigitalClock(display, 0, bottom_ypos)
|
||||
while True:
|
||||
dt1: str = get_datetime_string()
|
||||
simpleCnt += 1
|
||||
print(f"print_time_task running... {simpleCnt.value % 16} {dt1}")
|
||||
|
||||
await digitalClock.tick()
|
||||
await asyncio.sleep(2)
|
||||
|
||||
|
||||
async def sync_ntp_time_task() -> None:
|
||||
while True:
|
||||
sync_ntp_time()
|
||||
await asyncio.sleep(60)
|
||||
|
||||
|
||||
async def main(weather_checker: Weather_Checker) -> None:
|
||||
# Run both tasks concurrently
|
||||
await asyncio.gather(
|
||||
sync_ntp_time_task(), weather_check_task(weather_checker), print_time_task()
|
||||
)
|
||||
|
||||
|
||||
# Programm Startpunkt
|
||||
if __name__ == "__main__":
|
||||
display = NeoPixel_64x64()
|
||||
wlan: Wlan = Wlan()
|
||||
wlan.connect(ssid="Wokwi-Wlan", password="12345678")
|
||||
|
||||
# font_checker : Font_Checker = Font_Checker( display)
|
||||
# font_checker.fonts_check(pretty=False)
|
||||
|
||||
# emoji_checker : Emoji_Checker = Emoji_Checker(display)
|
||||
# emoji_checker.check()
|
||||
|
||||
# tryout.weather_check(display, test_mode=False)
|
||||
display.set_font(font_5x7)
|
||||
weather_checker: Weather_Checker = Weather_Checker(display=display)
|
||||
|
||||
# while True:
|
||||
# for city in sorted(CITY_LIST):
|
||||
# weather_checker.check(city=city, lang="de", test_mode=False)
|
||||
# time.sleep(5*60)
|
||||
|
||||
# show_system_load()
|
||||
asyncio.run(main(weather_checker))
|
||||
7
pico-client/readme.md
Normal file
7
pico-client/readme.md
Normal file
@@ -0,0 +1,7 @@
|
||||
### copy sources on the device
|
||||
|
||||
mpremote connect port:rfc2217://localhost:4000 cp -r app :
|
||||
|
||||
### run the main function
|
||||
|
||||
mpremote connect port:rfc2217://localhost:4000 run main.py
|
||||
1
pico-client/requirements.txt
Normal file
1
pico-client/requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
flask
|
||||
57
pico-client/restapi/mock/weather.json
Normal file
57
pico-client/restapi/mock/weather.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"location": {
|
||||
"name": "Columbus",
|
||||
"region": "Ohio",
|
||||
"country": "United States of America",
|
||||
"lat": 39.9611,
|
||||
"lon": -82.9989,
|
||||
"tz_id": "America/New_York",
|
||||
"localtime_epoch": 1762951576,
|
||||
"localtime": "2025-11-12 07:46"
|
||||
},
|
||||
"current": {
|
||||
"last_updated_epoch": 1762951500,
|
||||
"last_updated": "2025-11-12 07:45",
|
||||
"temp_c": 2.8,
|
||||
"temp_f": 37.0,
|
||||
"is_day": 1,
|
||||
"condition": {
|
||||
"text": "Partly cloudy",
|
||||
"icon": "//cdn.weatherapi.com/weather/64x64/day/116.png",
|
||||
"code": 1003
|
||||
},
|
||||
"wind_mph": 11.6,
|
||||
"wind_kph": 18.7,
|
||||
"wind_degree": 235,
|
||||
"wind_dir": "SW",
|
||||
"pressure_mb": 1012.0,
|
||||
"pressure_in": 29.88,
|
||||
"precip_mm": 0.0,
|
||||
"precip_in": 0.0,
|
||||
"humidity": 67,
|
||||
"cloud": 75,
|
||||
"feelslike_c": -1.5,
|
||||
"feelslike_f": 29.2,
|
||||
"windchill_c": -2.1,
|
||||
"windchill_f": 28.2,
|
||||
"heatindex_c": 0.4,
|
||||
"heatindex_f": 32.6,
|
||||
"dewpoint_c": -2.2,
|
||||
"dewpoint_f": 28.0,
|
||||
"vis_km": 16.0,
|
||||
"vis_miles": 9.0,
|
||||
"uv": 0.0,
|
||||
"gust_mph": 18.3,
|
||||
"gust_kph": 29.5,
|
||||
"air_quality": {
|
||||
"co": 216.85,
|
||||
"no2": 8.35,
|
||||
"o3": 54.0,
|
||||
"so2": 2.75,
|
||||
"pm2_5": 9.45,
|
||||
"pm10": 9.55,
|
||||
"us-epa-index": 1,
|
||||
"gb-defra-index": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
12
pico-client/restapi/weather.http
Normal file
12
pico-client/restapi/weather.http
Normal file
@@ -0,0 +1,12 @@
|
||||
@city = "columbus"
|
||||
@lang = en
|
||||
@forecast_days = 3
|
||||
# could be 60(mins) or 24(day)
|
||||
@time_period_forecast = 60
|
||||
@key = 3545ce42d0ba436e8dc164532250410
|
||||
|
||||
### aktuelles Wetter
|
||||
GET https://api.weatherapi.com/v1/current.json?key={{key}}&q={{city}}&aqi=yes&lang={{lang}}
|
||||
|
||||
### Vorhersage für n Tage (max 3 Tage for Free-Plan!)
|
||||
GET https://api.weatherapi.com/v1/forecast.json?q={{city}}&days={{forecast_days}}&tp={{time_period_forecast}}&key={{key}}&lang=de
|
||||
4
pico-client/run.fish
Executable file
4
pico-client/run.fish
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/usr/bin/fish
|
||||
|
||||
echo "Starting main.py..."
|
||||
mpremote connect port:rfc2217://localhost:4000 run main.py
|
||||
5
pico-client/tryout/__init__.py
Normal file
5
pico-client/tryout/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from .font_checker import Font_Checker
|
||||
from .weather_checker import Weather_Checker
|
||||
from .emoji_checker import Emoji_Checker
|
||||
|
||||
__all__ = [ 'Font_Checker', 'Weather_Checker', 'Emoji_Checker']
|
||||
19
pico-client/tryout/emoji_checker.py
Normal file
19
pico-client/tryout/emoji_checker.py
Normal file
@@ -0,0 +1,19 @@
|
||||
from display import NeoPixel_64x64
|
||||
from display.emoji import emoji_8x8, emoji_16x16
|
||||
from utils import colors
|
||||
|
||||
|
||||
class Emoji_Checker:
|
||||
def __init__(self, display: NeoPixel_64x64):
|
||||
self.display = display
|
||||
|
||||
def check(self):
|
||||
self.display.clear()
|
||||
|
||||
# try emoji
|
||||
self.display.set_font(emoji_8x8)
|
||||
self.display.write_text("😀😂✅😎💙", 0, 0, color=colors.GREEN)
|
||||
|
||||
# try emoji
|
||||
self.display.set_font(emoji_16x16)
|
||||
self.display.write_text("🌙💙🔑", 0, 10, color=colors.ORANGE)
|
||||
40
pico-client/tryout/font_checker.py
Normal file
40
pico-client/tryout/font_checker.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from display import NeoPixel_64x64
|
||||
from utils import align_font, colors
|
||||
import display.fonts as fonts
|
||||
|
||||
|
||||
class Font_Checker:
|
||||
font = fonts.font_3x5
|
||||
|
||||
def __init__(self, display: NeoPixel_64x64):
|
||||
self.display = display
|
||||
|
||||
def font_pretty(self, font):
|
||||
pretty_font = align_font(font, debug=False)
|
||||
print(pretty_font)
|
||||
|
||||
def fonts_check(self, pretty=False) -> None:
|
||||
for fnt in fonts.fonts_installed:
|
||||
self.display.clear()
|
||||
self.display.set_font(fnt)
|
||||
|
||||
height = self.display.font_height
|
||||
|
||||
alphanum: str = "".join(sorted(self.font.keys()))
|
||||
|
||||
text_left = alphanum
|
||||
while text_left:
|
||||
# Text entsprechend des Display splitten
|
||||
scr_txt_dict = self.display.screen_text(text=text_left)
|
||||
print(f"scr_txt: {scr_txt_dict}")
|
||||
|
||||
self.display.clear()
|
||||
for idx, row_text in enumerate(scr_txt_dict["visible"]):
|
||||
ypos = height * idx
|
||||
# display.clear_row(ypos)
|
||||
self.display.write_text(row_text, 0, ypos, colors.RAINBOW[idx % 6])
|
||||
|
||||
text_left = scr_txt_dict["invisible"]
|
||||
|
||||
if pretty:
|
||||
self.font_pretty(self.font)
|
||||
168
pico-client/tryout/weather_checker.py
Normal file
168
pico-client/tryout/weather_checker.py
Normal file
@@ -0,0 +1,168 @@
|
||||
import urequests # type: ignore
|
||||
import json
|
||||
|
||||
from classes import (
|
||||
Weather,
|
||||
Location,
|
||||
CurrentCondition,
|
||||
WeatherResponse,
|
||||
ResponseStatus,
|
||||
)
|
||||
from display import NeoPixel_64x64
|
||||
import utils.colors as colors
|
||||
from utils import URLEncoder, http_message, get_datetime_string
|
||||
|
||||
API_KEY = "3545ce42d0ba436e8dc164532250410"
|
||||
ACTUAL_WEATHER_URL = "http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&aqi=yes&lang={lang}"
|
||||
|
||||
|
||||
class Weather_Checker:
|
||||
def __init__(self, display: NeoPixel_64x64):
|
||||
self.display = display
|
||||
|
||||
def mock_weather_data(self):
|
||||
filepath = "restapi/mock-weather.json"
|
||||
try:
|
||||
with open(filepath, "r", encoding="utf-8") as file:
|
||||
return json.load(file)
|
||||
except OSError: # Use OSError instead of FileNotFoundError
|
||||
print(f"Error: File '{filepath}' not found.")
|
||||
return None
|
||||
except FileNotFoundError:
|
||||
print(f"Error: File '{filepath}' not found.")
|
||||
return None
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"Error: Invalid JSON in '{filepath}': {e}")
|
||||
return None
|
||||
|
||||
def _build_weather(self, json_resp) -> Weather:
|
||||
loc = Location(**json_resp["location"])
|
||||
cc = CurrentCondition(**json_resp["current"])
|
||||
|
||||
weather = Weather(location=loc, current=cc)
|
||||
return weather
|
||||
|
||||
def actual_weather(self, city, lang, test_mode=False) -> WeatherResponse:
|
||||
wr: WeatherResponse = WeatherResponse()
|
||||
weather_url = ACTUAL_WEATHER_URL.format(API_KEY=API_KEY, city=city, lang=lang)
|
||||
|
||||
if not test_mode:
|
||||
response_status: ResponseStatus = ResponseStatus()
|
||||
|
||||
print(f"query: {weather_url}")
|
||||
r = urequests.get(weather_url)
|
||||
print("Status-Code:", r.status_code)
|
||||
json_resp = r.json()
|
||||
print("json_resp:", json_resp)
|
||||
|
||||
response_status.code = r.status_code
|
||||
response_status.message = http_message[r.status_code]
|
||||
|
||||
if r.status_code == 200:
|
||||
wr.weather = self._build_weather(json_resp)
|
||||
else:
|
||||
response_status.error_text = json_resp["error"]["message"]
|
||||
|
||||
wr.response_status = response_status
|
||||
|
||||
r.close()
|
||||
else:
|
||||
print("Status-Code: test_mode")
|
||||
# json_resp = WEATHER_QUERY_MOCK
|
||||
json_resp = self.mock_weather_data()
|
||||
wr.weather = self._build_weather(json_resp)
|
||||
wr.response_status = ResponseStatus(code=200, message="OK (Test-Mode)")
|
||||
|
||||
return wr
|
||||
|
||||
def check(self, city: str, lang: str, test_mode: bool = False):
|
||||
bottom_ypos = self.display.MATRIX_HEIGHT - self.display.font_height
|
||||
try:
|
||||
self.display.clear()
|
||||
|
||||
city_encoded = URLEncoder.encode_utf8(city) # url_encode
|
||||
w_resp: WeatherResponse = self.actual_weather(
|
||||
city=city_encoded, lang=lang, test_mode=test_mode
|
||||
)
|
||||
|
||||
if w_resp.response_status.code == 200:
|
||||
ypos = 0
|
||||
self.display.write_text(
|
||||
f"{str(w_resp.weather.location.name)}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[0],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"{str(w_resp.weather.location.region)}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[0],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"{str(w_resp.weather.location.localtime)[:10]}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[3],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
|
||||
self.display.write_text(
|
||||
f"{str(w_resp.weather.current.temp_c)}°C",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[1],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"{str(w_resp.weather.current.condition.text)}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[2],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"upd:{str(w_resp.weather.current.last_updated)[-5:]}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[3],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"cur:{str(w_resp.weather.location.localtime)[-5:]}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[4],
|
||||
)
|
||||
else:
|
||||
ypos = 0
|
||||
self.display.write_text(
|
||||
f"Code:{w_resp.response_status.code}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[0],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"{w_resp.response_status.message}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[1],
|
||||
)
|
||||
ypos += self.display.font_height + 1
|
||||
self.display.write_text(
|
||||
f"{w_resp.response_status.error_text}",
|
||||
0,
|
||||
ypos,
|
||||
color=colors.RAINBOW[2],
|
||||
)
|
||||
|
||||
# unten rechts in die Ecke
|
||||
|
||||
self.display.write_text("done.", 39, bottom_ypos, color=colors.NEON_GREEN)
|
||||
except OSError as e:
|
||||
print(f"Error: connection closed - {e}")
|
||||
finally:
|
||||
print("finally, check done.")
|
||||
8
pico-client/utils/__init__.py
Normal file
8
pico-client/utils/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from .system_load import show_system_load
|
||||
from .colors import *
|
||||
from .time_utils import *
|
||||
from .font_utils import *
|
||||
from .math_utils import *
|
||||
from .url_encode import URLEncoder
|
||||
from .http_utils import http_message
|
||||
from .simple_counter import SimpleCounter
|
||||
98
pico-client/utils/colors.py
Normal file
98
pico-client/utils/colors.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# colors.py - Color library for LED matrix
|
||||
|
||||
# Basic Colors
|
||||
RED = (255, 0, 0)
|
||||
GREEN = (0, 255, 0)
|
||||
BLUE = (0, 0, 255)
|
||||
|
||||
# Primary Colors
|
||||
YELLOW = (255, 255, 0)
|
||||
MAGENTA = (255, 0, 255)
|
||||
CYAN = (0, 255, 255)
|
||||
|
||||
# White and Black
|
||||
WHITE = (255, 255, 255)
|
||||
BLACK = (0, 0, 0)
|
||||
|
||||
# Grayscale
|
||||
GRAY = (128, 128, 128)
|
||||
LIGHT_GRAY = (192, 192, 192)
|
||||
DARK_GRAY = (64, 64, 64)
|
||||
|
||||
# Warm Colors
|
||||
ORANGE = (255, 165, 0)
|
||||
PINK = (255, 192, 203)
|
||||
HOT_PINK = (255, 105, 180)
|
||||
CORAL = (255, 127, 80)
|
||||
TOMATO = (255, 99, 71)
|
||||
|
||||
# Cool Colors
|
||||
PURPLE = (128, 0, 128)
|
||||
INDIGO = (75, 0, 130)
|
||||
VIOLET = (238, 130, 238)
|
||||
LAVENDER = (230, 230, 250)
|
||||
|
||||
# Earth Tones
|
||||
BROWN = (165, 42, 42)
|
||||
CHOCOLATE = (210, 105, 30)
|
||||
SANDY_BROWN = (244, 164, 96)
|
||||
GOLD = (255, 215, 0)
|
||||
|
||||
# Bright Colors
|
||||
LIME = (0, 255, 0)
|
||||
AQUA = (0, 255, 255)
|
||||
TURQUOISE = (64, 224, 208)
|
||||
SPRING_GREEN = (0, 255, 127)
|
||||
|
||||
# Pastel Colors
|
||||
PASTEL_RED = (255, 128, 128)
|
||||
PASTEL_GREEN = (128, 255, 128)
|
||||
PASTEL_BLUE = (128, 128, 255)
|
||||
PASTEL_YELLOW = (255, 255, 128)
|
||||
PASTEL_PURPLE = (255, 128, 255)
|
||||
PASTEL_CYAN = (128, 255, 255)
|
||||
|
||||
# Neon Colors
|
||||
NEON_RED = (255, 0, 0)
|
||||
NEON_GREEN = (57, 255, 20)
|
||||
NEON_BLUE = (0, 0, 255)
|
||||
NEON_YELLOW = (255, 255, 0)
|
||||
NEON_PINK = (255, 0, 128)
|
||||
NEON_ORANGE = (255, 128, 0)
|
||||
|
||||
# Rainbow Colors (for rainbow effects)
|
||||
RAINBOW = [
|
||||
(255, 0, 0), # Red
|
||||
(255, 127, 0), # Orange
|
||||
(255, 255, 0), # Yellow
|
||||
(0, 255, 0), # Green
|
||||
(0, 0, 255), # Blue
|
||||
(75, 0, 130), # Indigo
|
||||
(148, 0, 211), # Violet
|
||||
]
|
||||
|
||||
# Holiday Colors
|
||||
CHRISTMAS_RED = (255, 0, 0)
|
||||
CHRISTMAS_GREEN = (0, 255, 0)
|
||||
HALLOWEEN_ORANGE = (255, 140, 0)
|
||||
HALLOWEEN_PURPLE = (128, 0, 128)
|
||||
|
||||
|
||||
# Utility function to create custom colors
|
||||
def rgb(r, g, b):
|
||||
"""Create a color from RGB values (0-255)"""
|
||||
return (r, g, b)
|
||||
|
||||
|
||||
def hex_to_rgb(hex_color):
|
||||
"""Convert hex color (#RRGGBB) to RGB tuple"""
|
||||
hex_color = hex_color.lstrip('#')
|
||||
return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
|
||||
|
||||
|
||||
def fade_color(color1, color2, factor):
|
||||
"""Fade between two colors (factor 0.0 to 1.0)"""
|
||||
r = int(color1[0] + (color2[0] - color1[0]) * factor)
|
||||
g = int(color1[1] + (color2[1] - color1[1]) * factor)
|
||||
b = int(color1[2] + (color2[2] - color1[2]) * factor)
|
||||
return (r, g, b)
|
||||
58
pico-client/utils/digital_clock.py
Normal file
58
pico-client/utils/digital_clock.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from display.neopixel_64x64 import NeoPixel_64x64
|
||||
from utils import get_datetime_string, text_width, find_first_mismatch
|
||||
from utils import colors
|
||||
|
||||
|
||||
class DigitalClock:
|
||||
stored_time_str: str = "99:99:99"
|
||||
text_color: tuple[int[3]] = colors.NEON_YELLOW
|
||||
clear_color: tuple[int[3]] = colors.BLACK
|
||||
|
||||
def __init__(self, display: NeoPixel_64x64, xpos: int, ypos: int):
|
||||
self.display = display
|
||||
self.xpos = xpos
|
||||
self.ypos = ypos
|
||||
|
||||
def _toggle_clear_color(self) -> tuple[int]:
|
||||
self.clear_color = (
|
||||
colors.BLACK if self.clear_color != colors.BLACK else colors.MAGENTA
|
||||
)
|
||||
return self.clear_color
|
||||
|
||||
def _text_width(self, text: str) -> int:
|
||||
return text_width(text, self.display.selected_font)
|
||||
|
||||
async def tick(self):
|
||||
time_str: str = get_datetime_string("time")[:8]
|
||||
|
||||
# ab welcher Position malen wir den String neu
|
||||
mismatch_pos: int = find_first_mismatch(self.stored_time_str, time_str)
|
||||
|
||||
untouched_part: str = time_str[:mismatch_pos]
|
||||
fresh_part: str = time_str[mismatch_pos:]
|
||||
part_to_clear: str = self.stored_time_str[mismatch_pos:]
|
||||
|
||||
print(f"untouched_part: {untouched_part}[{part_to_clear}-->{fresh_part}] ")
|
||||
|
||||
textwidth: int = self._text_width(self.stored_time_str)
|
||||
print(f"{self.stored_time_str}: #{textwidth}")
|
||||
|
||||
textwidth_untouched_part: int = self._text_width(untouched_part)
|
||||
clear_x_start_pos: int = self.xpos + textwidth_untouched_part + 1
|
||||
|
||||
textwidth_part_to_clear: int = self._text_width(part_to_clear)
|
||||
clear_x_end_pos: int = clear_x_start_pos + textwidth_part_to_clear - 1
|
||||
|
||||
self.display.clear_box(
|
||||
self.ypos,
|
||||
clear_x_start_pos,
|
||||
self.ypos + self.display.font_height,
|
||||
clear_x_end_pos,
|
||||
color=self.clear_color,
|
||||
)
|
||||
|
||||
self.display.write_text(
|
||||
fresh_part, clear_x_start_pos, self.ypos, color=self.text_color
|
||||
)
|
||||
|
||||
self.stored_time_str = time_str
|
||||
108
pico-client/utils/font_utils.py
Normal file
108
pico-client/utils/font_utils.py
Normal file
@@ -0,0 +1,108 @@
|
||||
from .math_utils import show_byte_matrix
|
||||
|
||||
|
||||
def text_width(text: str, font: dict[str, list[int]]) -> int:
|
||||
text_width_overall: int = sum([char_width(font[char]) for char in text])
|
||||
|
||||
return text_width_overall + len(text) - 1
|
||||
|
||||
|
||||
def char_width(char_matrix) -> int:
|
||||
"""Berechnung der Bits für die Zeichenbreite
|
||||
|
||||
Args:
|
||||
char_matrix (int): Zeichen als Array[int]
|
||||
|
||||
Returns:
|
||||
int: Anzahl Bits für die Zeichenbreite
|
||||
"""
|
||||
max_val = max(char_matrix)
|
||||
|
||||
val = max_val
|
||||
cw = 0
|
||||
while 0xFFFFFFFF & val:
|
||||
"""rechts shiften, bis alles Nullen da sind"""
|
||||
val >>= 1
|
||||
cw += 1
|
||||
|
||||
return cw
|
||||
|
||||
|
||||
def shift_letter_right(char, letter, debug=False) -> bool:
|
||||
"""Prüfe ob das Zeichen auch komplett nach rechts gezogen ist
|
||||
|
||||
Args:
|
||||
letter (_type_): Array[0..nBytes]
|
||||
|
||||
Returns:
|
||||
bool: _description_
|
||||
"""
|
||||
|
||||
def isLetterRight(letter):
|
||||
def isByteRight(byte):
|
||||
return True if 1 & byte == 1 else False
|
||||
|
||||
for byte in letter:
|
||||
if isByteRight(byte):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def shiftLetterRight(letter):
|
||||
def shift(letter):
|
||||
return [byte >> 1 for byte in letter]
|
||||
|
||||
shifted = letter
|
||||
for i in range(8): # max 1 Bit's
|
||||
shifted = shift(shifted)
|
||||
if isLetterRight(shifted):
|
||||
break
|
||||
|
||||
return shifted
|
||||
|
||||
if isLetterRight(letter):
|
||||
return letter
|
||||
|
||||
# letter is not right shifted
|
||||
shifted = shiftLetterRight(letter)
|
||||
|
||||
if debug:
|
||||
print("origin:")
|
||||
show_byte_matrix(char, letter)
|
||||
print("shifted")
|
||||
show_byte_matrix(char, shifted)
|
||||
|
||||
return shifted
|
||||
|
||||
|
||||
def align_font(font, debug=False):
|
||||
chars = [char for char in font]
|
||||
print(chars)
|
||||
|
||||
# Print the dictionary
|
||||
print("font_pretty = {")
|
||||
for char in sorted(font.keys()):
|
||||
shifted = shift_letter_right(char=char, letter=font[char], debug=debug)
|
||||
hex_values = [f"0x{val:02X}" for val in shifted]
|
||||
print(f"\t'{char}': [{', '.join(hex_values)}],")
|
||||
print("}")
|
||||
|
||||
# return f'#keys: {len(list(font.keys()))}'
|
||||
return f"#keys: {len(font)}"
|
||||
|
||||
|
||||
def find_first_mismatch(str1: str, str2: str) -> int:
|
||||
"""
|
||||
Compare two strings of equal length and return the position of first mismatch.
|
||||
|
||||
Args:
|
||||
str1 (str): First string
|
||||
str2 (str): Second string (same length as str1)
|
||||
|
||||
Returns:
|
||||
int: Position of first mismatch, or -1 if strings are identical
|
||||
"""
|
||||
for i in range(len(str1)):
|
||||
if str1[i] != str2[i]:
|
||||
return i
|
||||
return -1
|
||||
6
pico-client/utils/http_utils.py
Normal file
6
pico-client/utils/http_utils.py
Normal file
@@ -0,0 +1,6 @@
|
||||
http_message = {
|
||||
# Jedes Zeichen ist genau 3 Pixel breit, 5 Pixel hoch
|
||||
200: "OK",
|
||||
400: "Bad Request",
|
||||
401: "Unauthorized",
|
||||
}
|
||||
9
pico-client/utils/math_utils.py
Normal file
9
pico-client/utils/math_utils.py
Normal file
@@ -0,0 +1,9 @@
|
||||
def show_byte_matrix(char, matrix):
|
||||
print(f'byte_matrix: char({char})')
|
||||
matrix_str = [f'0x{byte:02X}' for byte in matrix]
|
||||
[print(f'{matrix_str[idx]} {number_to_bitarray_msb(byte)}') for idx, byte in enumerate(matrix)]
|
||||
|
||||
|
||||
def number_to_bitarray_msb(number, bits=8):
|
||||
"""Convert 8/16-bit number to bit array (MSB first)"""
|
||||
return [(number >> i) & 1 for i in range(bits - 1, -1, -1)]
|
||||
39
pico-client/utils/simple_counter.py
Normal file
39
pico-client/utils/simple_counter.py
Normal file
@@ -0,0 +1,39 @@
|
||||
class SimpleCounter:
|
||||
_value: int
|
||||
|
||||
def __init__(self):
|
||||
self._value = 0
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, value):
|
||||
self._value = value
|
||||
|
||||
# Arithmetic operators
|
||||
def __add__(self, other):
|
||||
return self._value + other
|
||||
|
||||
def __sub__(self, other):
|
||||
return self._value - other
|
||||
|
||||
# In-place operators
|
||||
def __iadd__(self, other):
|
||||
self._value += other
|
||||
return self
|
||||
|
||||
def __isub__(self, other):
|
||||
self._value -= other
|
||||
return self
|
||||
|
||||
# Conversion and representation
|
||||
def __int__(self):
|
||||
return self._value
|
||||
|
||||
def __str__(self):
|
||||
return str(self._value)
|
||||
|
||||
def __repr__(self):
|
||||
return f"SimpleCounter(value={self._value})"
|
||||
42
pico-client/utils/system_load.py
Normal file
42
pico-client/utils/system_load.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import gc
|
||||
import time
|
||||
|
||||
|
||||
def show_system_load():
|
||||
"""Display basic system load information"""
|
||||
gc.collect() # Run garbage collection first
|
||||
|
||||
mem_free = gc.mem_free()
|
||||
mem_alloc = gc.mem_alloc()
|
||||
mem_total = mem_free + mem_alloc
|
||||
mem_usage = (mem_alloc / mem_total) * 100 if mem_total > 0 else 0
|
||||
|
||||
print('=== System Load ===')
|
||||
print(f'Memory: {mem_alloc}/{mem_total} bytes ({mem_usage:.1f}%)')
|
||||
print(f'Free: {mem_free} bytes')
|
||||
print(f'Garbage: {gc.mem_free() - mem_free} bytes')
|
||||
|
||||
try:
|
||||
# CPU load approximation (not available on all ports)
|
||||
start_time = time.ticks_ms()
|
||||
# Do some work to measure CPU
|
||||
for i in range(1000):
|
||||
_ = i * i
|
||||
end_time = time.ticks_ms()
|
||||
cpu_time = time.ticks_diff(end_time, start_time)
|
||||
print(f'CPU load: ~{cpu_time}ms for 1000 iterations')
|
||||
except (AttributeError, TypeError, ValueError) as e:
|
||||
print(f'CPU measurement not available: {e}')
|
||||
|
||||
print('==================')
|
||||
|
||||
|
||||
def go():
|
||||
# Run periodically
|
||||
while True:
|
||||
show_system_load()
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
go()
|
||||
136
pico-client/utils/time_utils.py
Normal file
136
pico-client/utils/time_utils.py
Normal file
@@ -0,0 +1,136 @@
|
||||
import time
|
||||
import ntptime # type: ignore
|
||||
|
||||
_UTC_OFFSET: int = 1 * 3600 # +1 or +2 hours for CEST, adjust for your timezone
|
||||
|
||||
|
||||
def local_time_with_offset() -> int:
|
||||
# Set timezone offset (in seconds)
|
||||
current_time = time.time() + _UTC_OFFSET
|
||||
return time.localtime(current_time)
|
||||
|
||||
|
||||
def get_datetime_string(format="full") -> str:
|
||||
"""
|
||||
Return date/time as string with different formats
|
||||
|
||||
Args:
|
||||
format: "full", "date", "time", "short", "ticks"
|
||||
"""
|
||||
try:
|
||||
year, month, day, hour, minute, second, weekday, yearday = (
|
||||
local_time_with_offset()
|
||||
)
|
||||
|
||||
if format == "full":
|
||||
return (
|
||||
f"{year:04d}-{month:02d}-{day:02d} {hour:02d}:{minute:02d}:{second:02d}"
|
||||
)
|
||||
elif format == "date":
|
||||
return f"{year:04d}-{month:02d}-{day:02d}"
|
||||
elif format == "time":
|
||||
return f"{hour:02d}:{minute:02d}:{second:02d}"
|
||||
elif format == "short":
|
||||
return f"{month:02d}/{day:02d} {hour:02d}:{minute:02d}"
|
||||
else:
|
||||
return f"Ticks: {time.ticks_ms()} ms"
|
||||
|
||||
except:
|
||||
return f"Ticks: {time.ticks_ms()} ms"
|
||||
|
||||
|
||||
def get_german_datetime() -> str:
|
||||
"""Return German date and time"""
|
||||
try:
|
||||
year, month, day, hour, minute, second, weekday, yearday = (
|
||||
local_time_with_offset()
|
||||
)
|
||||
|
||||
weekdays = ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
|
||||
weekday_name = weekdays[weekday]
|
||||
|
||||
return f"{weekday_name}.{day:02d}.{month:02d}.{year} {hour:02d}:{minute:02d}"
|
||||
|
||||
except:
|
||||
ticks = time.ticks_ms() // 1000
|
||||
return f"Zeit: {ticks} sek"
|
||||
|
||||
|
||||
def get_german_timestamp_short() -> str:
|
||||
"""Get German timestamp with short months (for Wokwi)"""
|
||||
ticks = time.ticks_ms()
|
||||
day = (ticks // 86400000) % 31 + 1
|
||||
month = (ticks // 2592000000) % 12 + 1
|
||||
hour = (ticks // 3600000) % 24
|
||||
minute = (ticks // 60000) % 60
|
||||
|
||||
months = [
|
||||
"Jan",
|
||||
"Feb",
|
||||
"Mär",
|
||||
"Apr",
|
||||
"Mai",
|
||||
"Jun",
|
||||
"Jul",
|
||||
"Aug",
|
||||
"Sep",
|
||||
"Okt",
|
||||
"Nov",
|
||||
"Dez",
|
||||
]
|
||||
month_name = months[month - 1]
|
||||
|
||||
return f"{day}.{month_name} {hour:02d}:{minute:02d}"
|
||||
|
||||
|
||||
def get_german_time_ticks() -> str:
|
||||
"""Get German time using ticks (works in Wokwi)"""
|
||||
ticks = time.ticks_ms()
|
||||
|
||||
# Simulate time progression
|
||||
total_seconds = ticks // 1000
|
||||
hours = (total_seconds // 3600) % 24
|
||||
minutes = (total_seconds // 60) % 60
|
||||
seconds = total_seconds % 60
|
||||
|
||||
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
||||
|
||||
|
||||
def get_german_date_ticks() -> str:
|
||||
"""Get German date using ticks"""
|
||||
ticks = time.ticks_ms()
|
||||
|
||||
# Simulate date progression (starting from Jan 15, 2024)
|
||||
days_passed = ticks // (24 * 3600 * 1000)
|
||||
day = 15 + (days_passed % 28) # Simple month simulation
|
||||
month = 1 + (days_passed // 28) % 12
|
||||
year = 2024 + (days_passed // (28 * 12))
|
||||
|
||||
months = [
|
||||
"JAN",
|
||||
"FEB",
|
||||
"MAR",
|
||||
"APR",
|
||||
"MAI",
|
||||
"JUN",
|
||||
"JUL",
|
||||
"AUG",
|
||||
"SEP",
|
||||
"OKT",
|
||||
"NOV",
|
||||
"DEZ",
|
||||
]
|
||||
month_name = months[month - 1]
|
||||
|
||||
return f"{day:02d}.{month_name}.{str(year)[-2:]}"
|
||||
|
||||
|
||||
def sync_ntp_time() -> bool:
|
||||
try:
|
||||
print("Syncing time via NTP...")
|
||||
ntptime.settime() # Default uses pool.ntp.org
|
||||
print("Time synchronized successfully!")
|
||||
return True
|
||||
except Exception as e:
|
||||
print("NTP sync failed:", e)
|
||||
return False
|
||||
68
pico-client/utils/url_encode.py
Normal file
68
pico-client/utils/url_encode.py
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
class URLEncoder:
|
||||
@staticmethod
|
||||
def encode(string):
|
||||
"""URL encode a string without using string methods"""
|
||||
result = []
|
||||
safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"
|
||||
|
||||
for char in string:
|
||||
if char in safe_chars:
|
||||
result.append(char)
|
||||
else:
|
||||
# result.append('%' + format(ord(char), '02X'))
|
||||
# Converted to % formatting:
|
||||
special_char = "%%%02X" % ord(char)
|
||||
|
||||
print( f"{char} --> {special_char}")
|
||||
|
||||
result.append(special_char)
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
@staticmethod
|
||||
def encode_utf8(string):
|
||||
"""URL encode a string with proper UTF-8 handling"""
|
||||
result = []
|
||||
safe_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~"
|
||||
|
||||
for char in string:
|
||||
if char in safe_chars:
|
||||
result.append(char)
|
||||
else:
|
||||
# UTF-8 encoding for proper URL encoding
|
||||
utf8_bytes = char.encode('utf-8')
|
||||
for byte in utf8_bytes:
|
||||
result.append(f"%{byte:02X}")
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
@staticmethod
|
||||
def encode_plus(string):
|
||||
encoded = URLEncoder.encode(string)
|
||||
return encoded.replace('%20', '+')
|
||||
|
||||
@staticmethod
|
||||
def decode(encoded_string):
|
||||
result = []
|
||||
i = 0
|
||||
while i < len(encoded_string):
|
||||
if encoded_string[i] == '%' and i + 2 < len(encoded_string):
|
||||
hex_code = encoded_string[i+1:i+3]
|
||||
result.append(chr(int(hex_code, 16)))
|
||||
i += 3
|
||||
elif encoded_string[i] == '+':
|
||||
result.append(' ')
|
||||
i += 1
|
||||
else:
|
||||
result.append(encoded_string[i])
|
||||
i += 1
|
||||
return ''.join(result)
|
||||
|
||||
@staticmethod
|
||||
def encode_params(params):
|
||||
pairs = []
|
||||
for key, value in params.items():
|
||||
pairs.append(f"{URLEncoder.encode(str(key))}={URLEncoder.encode(str(value))}")
|
||||
return '&'.join(pairs)
|
||||
|
||||
3
pico-client/web/__init__.py
Normal file
3
pico-client/web/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from .wlan import Wlan
|
||||
|
||||
__all__=["Wlan"]
|
||||
22
pico-client/web/wlan.py
Normal file
22
pico-client/web/wlan.py
Normal file
@@ -0,0 +1,22 @@
|
||||
import time
|
||||
import network # type: ignore
|
||||
|
||||
API_KEY = "3545ce42d0ba436e8dc164532250410"
|
||||
ACTUAL_WEATHER_URL = "http://api.weatherapi.com/v1/current.json?key={API_KEY}&q={city}&aqi=yes&lang={lang}"
|
||||
|
||||
|
||||
class Wlan:
|
||||
def __init__(self):
|
||||
print("Wlan::__init__")
|
||||
self.wlan = network.WLAN(network.STA_IF)
|
||||
|
||||
def connect(self, ssid: str, password: str):
|
||||
self.wlan.active(True)
|
||||
self.wlan.connect(ssid, password)
|
||||
|
||||
while not self.wlan.isconnected:
|
||||
print("connecting, please wait ...")
|
||||
time.sleep(1)
|
||||
|
||||
time.sleep(0.25)
|
||||
print("connected! IP=", self.wlan.ifconfig()[0])
|
||||
7
pico-client/wokwi.toml
Normal file
7
pico-client/wokwi.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[wokwi]
|
||||
version = 1
|
||||
firmware = "firmware/Pico/RPI_PICO_W-20250911-v1.26.1.uf2"
|
||||
#firmware = "firmware/Pico2/RPI_PICO2_W-20250911-v1.26.1.uf2"
|
||||
|
||||
rfc2217ServerPort = 4000
|
||||
#gdbServerPort = 3333
|
||||
Reference in New Issue
Block a user