Files
jdillenburg-esphome/font_5x7_aa.h
John Dillenburg 7dc846a6eb matthiazzz - The current adxl345 component in ESPHome relies on multiple external Adafruit libraries (Adafruit Unified Sensor, Adafruit BusIO, Adafruit ADXL345). This causes unnecessary dependencies, compilation overhead, and sometimes incompatibilities when used with tca9548a multiplexers.
I modified the component to remove all Adafruit library dependencies and instead use a lightweight, self-contained ESPHome driver for ADXL345. After these changes, multiple ADXL345 sensors can be used in combination with a TCA9548A I²C multiplexer without conflicts.
2025-09-06 15:38:59 -05:00

308 lines
12 KiB
C++

// font_5x7_aa.h - 5x7 pixel anti-aliased font for LED Matrix
#pragma once
#include <cmath>
// 5x7 font data - each character is 5 bytes (5 columns x 7 rows)
// Bit 0 is top row, bit 6 is bottom row
const uint8_t font_5x7[][5] = {
{0x00, 0x00, 0x00, 0x00, 0x00}, // 32 space
{0x00, 0x00, 0x5F, 0x00, 0x00}, // 33 !
{0x00, 0x07, 0x00, 0x07, 0x00}, // 34 "
{0x14, 0x7F, 0x14, 0x7F, 0x14}, // 35 #
{0x24, 0x2A, 0x7F, 0x2A, 0x12}, // 36 $
{0x23, 0x13, 0x08, 0x64, 0x62}, // 37 %
{0x36, 0x49, 0x56, 0x20, 0x50}, // 38 &
{0x00, 0x08, 0x07, 0x03, 0x00}, // 39 '
{0x00, 0x1C, 0x22, 0x41, 0x00}, // 40 (
{0x00, 0x41, 0x22, 0x1C, 0x00}, // 41 )
{0x2A, 0x1C, 0x7F, 0x1C, 0x2A}, // 42 *
{0x08, 0x08, 0x3E, 0x08, 0x08}, // 43 +
{0x00, 0x80, 0x70, 0x30, 0x00}, // 44 ,
{0x08, 0x08, 0x08, 0x08, 0x08}, // 45 -
{0x00, 0x00, 0x60, 0x60, 0x00}, // 46 .
{0x20, 0x10, 0x08, 0x04, 0x02}, // 47 /
{0x3E, 0x51, 0x49, 0x45, 0x3E}, // 48 0
{0x00, 0x42, 0x7F, 0x40, 0x00}, // 49 1
{0x72, 0x49, 0x49, 0x49, 0x46}, // 50 2
{0x21, 0x41, 0x49, 0x4D, 0x33}, // 51 3
{0x18, 0x14, 0x12, 0x7F, 0x10}, // 52 4
{0x27, 0x45, 0x45, 0x45, 0x39}, // 53 5
{0x3C, 0x4A, 0x49, 0x49, 0x31}, // 54 6
{0x41, 0x21, 0x11, 0x09, 0x07}, // 55 7
{0x36, 0x49, 0x49, 0x49, 0x36}, // 56 8
{0x46, 0x49, 0x49, 0x29, 0x1E}, // 57 9
{0x00, 0x00, 0x14, 0x00, 0x00}, // 58 :
{0x00, 0x40, 0x34, 0x00, 0x00}, // 59 ;
{0x00, 0x08, 0x14, 0x22, 0x41}, // 60 <
{0x14, 0x14, 0x14, 0x14, 0x14}, // 61 =
{0x00, 0x41, 0x22, 0x14, 0x08}, // 62 >
{0x02, 0x01, 0x59, 0x09, 0x06}, // 63 ?
{0x3E, 0x41, 0x5D, 0x59, 0x4E}, // 64 @
{0x7C, 0x12, 0x11, 0x12, 0x7C}, // 65 A
{0x7F, 0x49, 0x49, 0x49, 0x36}, // 66 B
{0x3E, 0x41, 0x41, 0x41, 0x22}, // 67 C
{0x7F, 0x41, 0x41, 0x41, 0x3E}, // 68 D
{0x7F, 0x49, 0x49, 0x49, 0x41}, // 69 E
{0x7F, 0x09, 0x09, 0x09, 0x01}, // 70 F
{0x3E, 0x41, 0x41, 0x51, 0x73}, // 71 G
{0x7F, 0x08, 0x08, 0x08, 0x7F}, // 72 H
{0x00, 0x41, 0x7F, 0x41, 0x00}, // 73 I
{0x20, 0x40, 0x41, 0x3F, 0x01}, // 74 J
{0x7F, 0x08, 0x14, 0x22, 0x41}, // 75 K
{0x7F, 0x40, 0x40, 0x40, 0x40}, // 76 L
{0x7F, 0x02, 0x1C, 0x02, 0x7F}, // 77 M
{0x7F, 0x04, 0x08, 0x10, 0x7F}, // 78 N
{0x3E, 0x41, 0x41, 0x41, 0x3E}, // 79 O
{0x7F, 0x09, 0x09, 0x09, 0x06}, // 80 P
{0x3E, 0x41, 0x51, 0x21, 0x5E}, // 81 Q
{0x7F, 0x09, 0x19, 0x29, 0x46}, // 82 R
{0x26, 0x49, 0x49, 0x49, 0x32}, // 83 S
{0x03, 0x01, 0x7F, 0x01, 0x03}, // 84 T
{0x3F, 0x40, 0x40, 0x40, 0x3F}, // 85 U
{0x1F, 0x20, 0x40, 0x20, 0x1F}, // 86 V
{0x3F, 0x40, 0x38, 0x40, 0x3F}, // 87 W
{0x63, 0x14, 0x08, 0x14, 0x63}, // 88 X
{0x03, 0x04, 0x78, 0x04, 0x03}, // 89 Y
{0x61, 0x59, 0x49, 0x4D, 0x43}, // 90 Z
{0x00, 0x7F, 0x41, 0x41, 0x41}, // 91 [
{0x02, 0x04, 0x08, 0x10, 0x20}, // 92 backslash
{0x41, 0x41, 0x41, 0x7F, 0x00}, // 93 ]
{0x04, 0x02, 0x01, 0x02, 0x04}, // 94 ^
{0x40, 0x40, 0x40, 0x40, 0x40}, // 95 _
{0x00, 0x03, 0x07, 0x08, 0x00}, // 96 `
{0x20, 0x54, 0x54, 0x78, 0x40}, // 97 a
{0x7F, 0x28, 0x44, 0x44, 0x38}, // 98 b
{0x38, 0x44, 0x44, 0x44, 0x28}, // 99 c
{0x38, 0x44, 0x44, 0x28, 0x7F}, // 100 d
{0x38, 0x54, 0x54, 0x54, 0x18}, // 101 e
{0x00, 0x08, 0x7E, 0x09, 0x02}, // 102 f
{0x18, 0xA4, 0xA4, 0x9C, 0x78}, // 103 g
{0x7F, 0x08, 0x04, 0x04, 0x78}, // 104 h
{0x00, 0x44, 0x7D, 0x40, 0x00}, // 105 i
{0x20, 0x40, 0x40, 0x3D, 0x00}, // 106 j
{0x7F, 0x10, 0x28, 0x44, 0x00}, // 107 k
{0x00, 0x41, 0x7F, 0x40, 0x00}, // 108 l
{0x7C, 0x04, 0x78, 0x04, 0x78}, // 109 m
{0x7C, 0x08, 0x04, 0x04, 0x78}, // 110 n
{0x38, 0x44, 0x44, 0x44, 0x38}, // 111 o
{0xFC, 0x18, 0x24, 0x24, 0x18}, // 112 p
{0x18, 0x24, 0x24, 0x18, 0xFC}, // 113 q
{0x7C, 0x08, 0x04, 0x04, 0x08}, // 114 r
{0x48, 0x54, 0x54, 0x54, 0x24}, // 115 s
{0x04, 0x04, 0x3F, 0x44, 0x24}, // 116 t
{0x3C, 0x40, 0x40, 0x20, 0x7C}, // 117 u
{0x1C, 0x20, 0x40, 0x20, 0x1C}, // 118 v
{0x3C, 0x40, 0x30, 0x40, 0x3C}, // 119 w
{0x44, 0x28, 0x10, 0x28, 0x44}, // 120 x
{0x4C, 0x90, 0x90, 0x90, 0x7C}, // 121 y
{0x44, 0x64, 0x54, 0x4C, 0x44}, // 122 z
{0x00, 0x08, 0x36, 0x41, 0x00}, // 123 {
{0x00, 0x00, 0x77, 0x00, 0x00}, // 124 |
{0x00, 0x41, 0x36, 0x08, 0x00}, // 125 }
{0x02, 0x01, 0x02, 0x04, 0x02}, // 126 ~
};
// Helper function to draw a character at position
void draw_char(light::AddressableLight &it, char c, int x_pos, int y_pos, Color color) {
if (c < 32 || c > 126) return; // Character out of range
const uint8_t *char_data = font_5x7[c - 32];
for (int col = 0; col < 5; col++) {
int x = x_pos + col;
if (x < 0 || x >= 32) continue; // Skip if out of bounds
uint8_t column = char_data[col];
for (int row = 0; row < 7; row++) {
if (column & (1 << row)) {
int y = y_pos + row;
if (y < 0 || y >= 8) continue; // Skip if out of bounds
// Convert to LED index with serpentine mapping
int flipped_y = 7 - y;
int led_index;
if (flipped_y % 2 == 0) {
led_index = flipped_y * 32 + x;
} else {
led_index = flipped_y * 32 + (31 - x);
}
if (led_index >= 0 && led_index < 256) {
it[led_index] = color;
}
}
}
}
}
// Helper function to draw a string
void draw_string(light::AddressableLight &it, const std::string &text, int x_pos, int y_pos, Color color) {
int current_x = x_pos;
for (size_t i = 0; i < text.length(); i++) {
draw_char(it, text[i], current_x, y_pos, color);
current_x += 6; // 5 pixels for char + 1 pixel spacing
}
}
// Calculate text width in pixels
int get_text_width(const std::string &text) {
return text.length() * 6 - 1; // 6 pixels per char minus 1 for last spacing
}
// Anti-aliased drawing with sub-pixel positioning
void draw_char_aa(light::AddressableLight &it, char c, float x_pos, float y_pos, Color color, float brightness = 1.0) {
if (c < 32 || c > 126) return;
const uint8_t *char_data = font_5x7[c - 32];
// Calculate fractional offsets
int x_start = (int)floor(x_pos);
int y_start = (int)floor(y_pos);
float x_frac = x_pos - x_start;
float y_frac = y_pos - y_start;
// Draw with 2x2 supersampling for anti-aliasing
for (int col = 0; col < 5; col++) {
uint8_t column = char_data[col];
for (int row = 0; row < 7; row++) {
if (column & (1 << row)) {
// Calculate anti-aliased pixel coverage for 4 affected pixels
float coverage[2][2] = {
{(1.0f - x_frac) * (1.0f - y_frac), x_frac * (1.0f - y_frac)},
{(1.0f - x_frac) * y_frac, x_frac * y_frac}
};
// Draw to up to 4 pixels with appropriate brightness
for (int dy = 0; dy < 2; dy++) {
for (int dx = 0; dx < 2; dx++) {
int x = x_start + col + dx;
int y = y_start + row + dy;
if (x >= 0 && x < 32 && y >= 0 && y < 8) {
// Convert to LED index
int flipped_y = 7 - y;
int led_index;
if (flipped_y % 2 == 0) {
led_index = flipped_y * 32 + x;
} else {
led_index = flipped_y * 32 + (31 - x);
}
if (led_index >= 0 && led_index < 256) {
// Apply anti-aliased brightness
float pixel_brightness = coverage[dy][dx] * brightness;
// Blend with existing pixel
Color existing = it[led_index].get();
Color new_color(
existing.r + (color.r - existing.r) * pixel_brightness,
existing.g + (color.g - existing.g) * pixel_brightness,
existing.b + (color.b - existing.b) * pixel_brightness
);
it[led_index] = new_color;
}
}
}
}
}
}
}
}
// Helper for edge smoothing using 3x3 kernel
void apply_edge_smoothing(light::AddressableLight &it, int x, int y, Color color, float strength) {
// 3x3 Gaussian kernel for smoothing
const float kernel[3][3] = {
{0.075f, 0.124f, 0.075f},
{0.124f, 0.204f, 0.124f},
{0.075f, 0.124f, 0.075f}
};
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
int px = x + dx;
int py = y + dy;
if (px >= 0 && px < 32 && py >= 0 && py < 8) {
int flipped_y = 7 - py;
int led_index;
if (flipped_y % 2 == 0) {
led_index = flipped_y * 32 + px;
} else {
led_index = flipped_y * 32 + (31 - px);
}
if (led_index >= 0 && led_index < 256) {
float weight = kernel[dy + 1][dx + 1] * strength;
Color existing = it[led_index].get();
Color new_color(
existing.r + (color.r - existing.r) * weight,
existing.g + (color.g - existing.g) * weight,
existing.b + (color.b - existing.b) * weight
);
it[led_index] = new_color;
}
}
}
}
}
// Improved string drawing with anti-aliasing
void draw_string_aa(light::AddressableLight &it, const std::string &text, float x_pos, float y_pos, Color color) {
float current_x = x_pos;
for (size_t i = 0; i < text.length(); i++) {
draw_char_aa(it, text[i], current_x, y_pos, color);
current_x += 6.0f; // Can use fractional spacing
}
}
// Alternative: Pre-rendered anti-aliased font (2-bit per pixel)
// This would store 4 brightness levels per pixel for better quality
struct AAGlyph {
uint8_t width;
uint8_t height;
uint8_t data[35]; // 5x7 pixels, 2 bits each = 70 bits = ~9 bytes
};
// Function to render with pre-computed anti-aliased glyphs
void draw_aa_glyph(light::AddressableLight &it, const AAGlyph &glyph, int x, int y, Color color) {
for (int row = 0; row < glyph.height; row++) {
for (int col = 0; col < glyph.width; col++) {
// Extract 2-bit brightness value (0, 85, 170, 255)
int bit_index = (row * glyph.width + col) * 2;
int byte_index = bit_index / 8;
int bit_offset = bit_index % 8;
uint8_t brightness_bits = (glyph.data[byte_index] >> bit_offset) & 0x03;
float brightness = brightness_bits / 3.0f;
if (brightness > 0) {
int px = x + col;
int py = y + row;
if (px >= 0 && px < 32 && py >= 0 && py < 8) {
int flipped_y = 7 - py;
int led_index;
if (flipped_y % 2 == 0) {
led_index = flipped_y * 32 + px;
} else {
led_index = flipped_y * 32 + (31 - px);
}
if (led_index >= 0 && led_index < 256) {
it[led_index] = Color(
color.r * brightness,
color.g * brightness,
color.b * brightness
);
}
}
}
}
}
}