v.0.3.0 calculate char_width with shift operator

This commit is contained in:
tiijay
2025-10-23 14:31:09 +02:00
parent 2eea871408
commit a66b00989f
8 changed files with 377 additions and 182 deletions

View File

@@ -1,7 +1,7 @@
# Emoji symbols for LED matrix (16x16) # Emoji symbols for LED matrix (16x16)
emoji_16x16 = { emoji_16x16 = {
# Basic Smileys # === SMILEYS & EMOTIONS ===
'😀': [ '😀': [ # Grinning Face
0x07E0, 0x07E0,
0x1FF8, 0x1FF8,
0x3FFC, 0x3FFC,
@@ -18,8 +18,8 @@ emoji_16x16 = {
0x1FF8, 0x1FF8,
0x07E0, 0x07E0,
0x0000, 0x0000,
], # Grinning face (circle outline) ],
'😊': [ '😊': [ # Smiling Face with Smiling Eyes
0x07E0, 0x07E0,
0x1FF8, 0x1FF8,
0x3FFC, 0x3FFC,
@@ -36,8 +36,8 @@ emoji_16x16 = {
0x3FFC, 0x3FFC,
0x1FF8, 0x1FF8,
0x07E0, 0x07E0,
], # Smiling face ],
'😂': [ '😂': [ # Face with Tears of Joy
0x07E0, 0x07E0,
0x1FF8, 0x1FF8,
0x3FFC, 0x3FFC,
@@ -54,8 +54,8 @@ emoji_16x16 = {
0x3FFC, 0x3FFC,
0x1FF8, 0x1FF8,
0x07E0, 0x07E0,
], # Laughing with tears ],
'😍': [ '😍': [ # Smiling Face with Heart-Eyes
0x07E0, 0x07E0,
0x1FF8, 0x1FF8,
0x3FFC, 0x3FFC,
@@ -72,8 +72,8 @@ emoji_16x16 = {
0x3FFC, 0x3FFC,
0x1FF8, 0x1FF8,
0x07E0, 0x07E0,
], # Heart eyes ],
'😎': [ '😎': [ # Smiling Face with Sunglasses
0x07E0, 0x07E0,
0x1FF8, 0x1FF8,
0x3FFC, 0x3FFC,
@@ -90,9 +90,45 @@ emoji_16x16 = {
0x3FFC, 0x3FFC,
0x1FF8, 0x1FF8,
0x07E0, 0x07E0,
], # Cool sunglasses ],
# Hearts '😢': [ # Crying Face
'❤️': [ 0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7C3E,
0xF81F,
0xF00F,
0xF3CF,
0xF3CF,
0xF00F,
0xF99F,
0x7C3E,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
],
'😠': [ # Angry Face
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7C3E,
0xF81F,
0xF3CF,
0xE667,
0xE667,
0xF3CF,
0xF81F,
0x7C3E,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
],
# === HEARTS ===
'❤️': [ # Red Heart
0x0000, 0x0000,
0x0000, 0x0000,
0x0C30, 0x0C30,
@@ -109,9 +145,27 @@ emoji_16x16 = {
0x0F80, 0x0F80,
0x0700, 0x0700,
0x0000, 0x0000,
], # Red heart ],
# Weather & Nature '💙': [ # Blue Heart
'☀️': [ 0x0000,
0x0000,
0x0C30,
0x1E78,
0x3FFC,
0x7FFE,
0x7FFE,
0xFFFE,
0xFFFC,
0x7FF8,
0x7FF0,
0x3FE0,
0x1FC0,
0x0F80,
0x0700,
0x0000,
],
# === WEATHER & NATURE ===
'☀️': [ # Sun
0x8001, 0x8001,
0x4002, 0x4002,
0x2004, 0x2004,
@@ -128,8 +182,8 @@ emoji_16x16 = {
0x2004, 0x2004,
0x4002, 0x4002,
0x8001, 0x8001,
], # Sun with detailed rays ],
'🌙': [ '🌙': [ # Crescent Moon
0x0000, 0x0000,
0x1F80, 0x1F80,
0x3FC0, 0x3FC0,
@@ -146,8 +200,8 @@ emoji_16x16 = {
0xFC00, 0xFC00,
0xF800, 0xF800,
0x0000, 0x0000,
], # Crescent moon ],
'': [ '': [ # Star
0x0180, 0x0180,
0x0180, 0x0180,
0x03C0, 0x03C0,
@@ -164,9 +218,27 @@ emoji_16x16 = {
0x03C0, 0x03C0,
0x0180, 0x0180,
0x0180, 0x0180,
], # Star ],
# Objects '☁️': [ # Cloud
'📱': [ 0x0000,
0x0000,
0x0F00,
0x1F80,
0x3FC0,
0x7FE0,
0x7FE0,
0xFFF0,
0xFFF0,
0xFFF0,
0x7FE0,
0x7FE0,
0x3FC0,
0x1F80,
0x0F00,
0x0000,
],
# === TECHNOLOGY ===
'📱': [ # Mobile Phone
0xFFFF, 0xFFFF,
0x8001, 0x8001,
0x8001, 0x8001,
@@ -183,8 +255,8 @@ emoji_16x16 = {
0x8001, 0x8001,
0x8001, 0x8001,
0xFFFF, 0xFFFF,
], # Smartphone ],
'💻': [ '💻': [ # Laptop
0x0000, 0x0000,
0x7FFE, 0x7FFE,
0x4002, 0x4002,
@@ -201,65 +273,8 @@ emoji_16x16 = {
0x3FFC, 0x3FFC,
0x1FF8, 0x1FF8,
0x0000, 0x0000,
], # Laptop ],
# Animals '🔒': [ # Lock
'🐱': [
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
], # Cat face
'🐶': [
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
], # Dog face
# Food
'🍕': [
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
], # Pizza
# Technology
'🔒': [
0x0FC0, 0x0FC0,
0x1FE0, 0x1FE0,
0x3FF0, 0x3FF0,
@@ -276,28 +291,82 @@ emoji_16x16 = {
0x3FF0, 0x3FF0,
0x3FF0, 0x3FF0,
0x3FF0, 0x3FF0,
], # Lock ],
# Vehicles '🔑': [ # Key
'🚗': [
0x0000, 0x0000,
0x0000, 0x0000,
0x0FC0, 0x0F00,
0x1FE0, 0x1F80,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x3FF0, 0x3FC0,
0x1FE0, 0x3FC0,
0x0FC0, 0x3FC0,
0x3FC0,
0x3FC0,
],
# === ANIMALS ===
'🐱': [ # Cat Face
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
],
'🐶': [ # Dog Face
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x6FF6,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
],
'🐦': [ # Bird
0x0000, 0x0000,
0x0000, 0x0000,
], # Car 0x0000,
# Sports 0x0000,
'': [ 0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
],
# === FOOD ===
'🍕': [ # Pizza
0x07E0, 0x07E0,
0x1FF8, 0x1FF8,
0x3FFC, 0x3FFC,
@@ -314,9 +383,101 @@ emoji_16x16 = {
0x1FF8, 0x1FF8,
0x07E0, 0x07E0,
0x0000, 0x0000,
], # Soccer ball ],
# Holidays '🍎': [ # Red Apple
'🎄': [ 0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
],
# === VEHICLES ===
'🚗': [ # Car
0x0000,
0x0000,
0x0FC0,
0x1FE0,
0x3FF0,
0x3FF0,
0x3FF0,
0x3FF0,
0x3FF0,
0x3FF0,
0x3FF0,
0x3FF0,
0x1FE0,
0x0FC0,
0x0000,
0x0000,
],
'✈️': [ # Airplane
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
0x0000,
],
# === SPORTS ===
'': [ # Soccer Ball
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
],
'🏀': [ # Basketball
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
],
# === HOLIDAYS ===
'🎄': [ # Christmas Tree
0x0180, 0x0180,
0x03C0, 0x03C0,
0x03C0, 0x03C0,
@@ -333,5 +494,23 @@ emoji_16x16 = {
0x03C0, 0x03C0,
0x03C0, 0x03C0,
0x03C0, 0x03C0,
], # Christmas tree ],
'🎃': [ # Jack-O-Lantern
0x07E0,
0x1FF8,
0x3FFC,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x7FFE,
0x3FFC,
0x1FF8,
0x07E0,
0x0000,
],
} }

View File

@@ -221,9 +221,9 @@ char_width = {
} }
def get_char_width(char): def get_char_width(char, default_witdth=5):
"""Get the display width of a character for proper spacing""" """Get the display width of a character for proper spacing"""
return char_width.get(char, 5) # Default to 5 if character not found return char_width.get(char, default_witdth) # Default to 5 if character not found
def get_text_width(text): def get_text_width(text):

View File

@@ -6,6 +6,7 @@ from .font_16x16 import font_16x16
from ..emoji.emoji_5x7 import emoji_5x7 from ..emoji.emoji_5x7 import emoji_5x7
from ..emoji.emoji_8x8 import emoji_8x8 from ..emoji.emoji_8x8 import emoji_8x8
from ..emoji.emoji_16x16 import emoji_16x16 from ..emoji.emoji_16x16 import emoji_16x16
from ...utils.utils import number_to_bitarray_msb
fonts_meta_ = { fonts_meta_ = {
3: {'w': 3, 'h': 5}, 3: {'w': 3, 'h': 5},
@@ -34,3 +35,24 @@ def fonts_meta(font):
return fonts_meta_[16] return fonts_meta_[16]
else: else:
return None return None
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

View File

@@ -1,6 +1,6 @@
from machine import Pin, RTC from machine import Pin, RTC
from neopixel import NeoPixel from neopixel import NeoPixel
from .fonts.fonts_utils import fonts_meta from .fonts.fonts_utils import fonts_meta, char_width as charwidth
from .fonts.font_5x7 import font_5x7 from .fonts.font_5x7 import font_5x7
from .fonts.font_5x7_opt import font_5x7 as font_5x7_opt, get_char_width from .fonts.font_5x7_opt import font_5x7 as font_5x7_opt, get_char_width
@@ -124,17 +124,20 @@ class NeoPixel_64x64(NeoPixel):
color: RGB color tuple color: RGB color tuple
""" """
# background for the letter (full font size)
[
# print(xpos, ypos)
self.set_pixel(xpos, ypos, GRAY)
for xpos in range(x, x + 8) # 8 because full with of character representation
for ypos in range(y, y + self.font_height)
]
if letter in self.selected_font: if letter in self.selected_font:
char_data = self.selected_font[letter] char_data = self.selected_font[letter]
char_width = get_char_width(letter) # char_width = get_char_width(letter, default_witdth=16)
char_width = charwidth(char_data)
# background for the letter (full font size)
[
# print(xpos, ypos)
self.set_pixel(xpos, ypos, GRAY)
for xpos in range(
x, x + char_width
) # 8 because full with of character representation
for ypos in range(y, y + self.font_height)
]
for row in range(self.font_height): for row in range(self.font_height):
row_data = char_data[row] row_data = char_data[row]

View File

@@ -7,9 +7,9 @@ def show_byte_matrix(char, matrix):
[print(f'{matrix_str[idx]} {number_to_bitarray_msb(byte)}') for idx, byte in enumerate(matrix)] [print(f'{matrix_str[idx]} {number_to_bitarray_msb(byte)}') for idx, byte in enumerate(matrix)]
def number_to_bitarray_msb(number): def number_to_bitarray_msb(number, bits=8):
"""Convert 8-bit number to bit array (MSB first)""" """Convert 8/16-bit number to bit array (MSB first)"""
return [(number >> i) & 1 for i in range(7, -1, -1)] return [(number >> i) & 1 for i in range(bits - 1, -1, -1)]
def is_letter_assigned_right(char, letter) -> bool: def is_letter_assigned_right(char, letter) -> bool:

39
main.py
View File

@@ -17,28 +17,23 @@ display = NeoPixel_64x64()
def emoji_test(): def emoji_test():
display.clear() display.clear()
# display.set_font(font_8x8)
# display.show_hello()
# display.rotate_text_left_continuous('FLOATING TEXT', 0, speed=8.0, duration=30)
display.set_font(font_16x16)
display.draw_text('ABCD', 1, 1, color=GOLD)
# try emoji # try emoji
display.set_font(emoji_8x8) display.set_font(emoji_8x8)
display.draw_text('😀', 1, 20, color=GREEN) display.write_text('😀', 0, 0, color=GREEN)
display.draw_text('', 1, 29) display.write_text('😂', 0, 29)
display.draw_text('', 1, 38) # display.write_text('✅', 0, 38)
display.draw_text('😎', 1, 47, color=RED) # display.write_text('😎', 0, 47, color=RED)
display.draw_text('💙', 10, 47) # display.write_text('💙', 10, 47)
display.draw_text('💚', 19, 47) # display.write_text('💚', 19, 47)
display.draw_text('💛', 28, 47) # display.write_text('💛', 28, 47)
char_row = '😀'
utils.is_letter_assigned_right(char_row, emoji_8x8[char_row])
# try emoji # try emoji
# display.set_font(emoji_16x16) display.set_font(emoji_16x16)
# display.draw_text('😀', 10, 20, color=ORANGE) display.write_text('🌙', 0, 10, color=ORANGE)
display.write()
def weather_check(test_mode: bool = False): def weather_check(test_mode: bool = False):
@@ -58,7 +53,7 @@ def weather_check(test_mode: bool = False):
display.draw_text('weather data', 0, 16, color=RAINBOW[1]) display.draw_text('weather data', 0, 16, color=RAINBOW[1])
display.write() display.write()
w_resp = wlan.actual_weather(test_mode=test_mode) w_resp = wlan.actual_weather(lang='en', test_mode=test_mode)
display.clear() display.clear()
@@ -77,10 +72,10 @@ def weather_check(test_mode: bool = False):
display.write_text(f'{str(w_resp.current.condition.text)}°C', 0, ypos, color=RAINBOW[1]) display.write_text(f'{str(w_resp.current.condition.text)}°C', 0, ypos, color=RAINBOW[1])
ypos += delta ypos += delta
display.write_text( display.write_text(
f'upd: {str(w_resp.current.last_updated)[-5:]}', 0, ypos, color=RAINBOW[2] f'upd:{str(w_resp.current.last_updated)[-5:]}', 0, ypos, color=RAINBOW[2]
) )
ypos += delta ypos += delta
display.write_text(f'cur: {str(w_resp.location.localtime)[-5:]}', 0, ypos, color=RAINBOW[3]) display.write_text(f'cur:{str(w_resp.location.localtime)[-5:]}', 0, ypos, color=RAINBOW[3])
ypos += 3 * delta ypos += 3 * delta
display.write_text('ready.', 30, ypos, color=WHITE) display.write_text('ready.', 30, ypos, color=WHITE)
@@ -106,10 +101,6 @@ def font_5x7_test() -> None:
def bit_arrays(): def bit_arrays():
# def number_to_bitarray_msb(number):
# """Convert 8-bit number to bit array (MSB first)"""
# return [(number >> i) & 1 for i in range(7, -1, -1)]
# c = 'A' # c = 'A'
# print(c) # print(c)
# [print(utils.number_to_bitarray_msb(num)) for num in font_5x7[c]] # [print(utils.number_to_bitarray_msb(num)) for num in font_5x7[c]]

View File

@@ -6,50 +6,50 @@
"lat": 53.6667, "lat": 53.6667,
"lon": 10.2833, "lon": 10.2833,
"tz_id": "Europe/Berlin", "tz_id": "Europe/Berlin",
"localtime_epoch": 1760977041, "localtime_epoch": 1761209139,
"localtime": "2025-10-20 18:17" "localtime": "2025-10-23 10:45"
}, },
"current": { "current": {
"last_updated_epoch": 1760976900, "last_updated_epoch": 1761209100,
"last_updated": "2025-10-20 18:15", "last_updated": "2025-10-23 10:45",
"temp_c": 12.3, "temp_c": 12.4,
"temp_f": 54.1, "temp_f": 54.3,
"is_day": 0, "is_day": 1,
"condition": { "condition": {
"text": "leichter Regenfall", "text": "Light rain shower",
"icon": "//cdn.weatherapi.com/weather/64x64/night/296.png", "icon": "//cdn.weatherapi.com/weather/64x64/day/353.png",
"code": 1183 "code": 1240
}, },
"wind_mph": 12.1, "wind_mph": 13.6,
"wind_kph": 19.4, "wind_kph": 22.0,
"wind_degree": 151, "wind_degree": 147,
"wind_dir": "SSE", "wind_dir": "SSE",
"pressure_mb": 999.0, "pressure_mb": 983.0,
"pressure_in": 29.5, "pressure_in": 29.03,
"precip_mm": 0.0, "precip_mm": 2.34,
"precip_in": 0.0, "precip_in": 0.09,
"humidity": 77, "humidity": 100,
"cloud": 100, "cloud": 75,
"feelslike_c": 10.3, "feelslike_c": 10.3,
"feelslike_f": 50.6, "feelslike_f": 50.5,
"windchill_c": 10.1, "windchill_c": 10.5,
"windchill_f": 50.1, "windchill_f": 50.9,
"heatindex_c": 12.1, "heatindex_c": 12.6,
"heatindex_f": 53.8, "heatindex_f": 54.6,
"dewpoint_c": 7.7, "dewpoint_c": 12.0,
"dewpoint_f": 45.8, "dewpoint_f": 53.6,
"vis_km": 10.0, "vis_km": 8.0,
"vis_miles": 6.0, "vis_miles": 4.0,
"uv": 0.0, "uv": 0.0,
"gust_mph": 19.8, "gust_mph": 21.1,
"gust_kph": 31.9, "gust_kph": 33.9,
"air_quality": { "air_quality": {
"co": 171.678, "co": 152.678,
"no2": 9.778, "no2": 8.378,
"o3": 54.0, "o3": 35.0,
"so2": 1.678, "so2": 0.878,
"pm2_5": 10.078, "pm2_5": 7.878,
"pm10": 12.578, "pm10": 8.978,
"us-epa-index": 1, "us-epa-index": 1,
"gb-defra-index": 1 "gb-defra-index": 1
} }

View File

@@ -1,5 +1,5 @@
@city = "grosshansdorf" @city = "grosshansdorf"
@lang = de @lang = en
@forecast_days = 3 @forecast_days = 3
# could be 60(mins) or 24(day) # could be 60(mins) or 24(day)
@time_period_forecast = 60 @time_period_forecast = 60