Display UI: ST7735 Treiber + Frame-Scroller + Speicheranzeige

- ST7735 SPI-Treiber für 0.96 Zoll Display (80x160 Pixel)
- ASCII-Font 6x8 mit Text-Ausgabe
- Frame-Counter (gross, rot)
- SD-Speicher-Balken (grün/gelb/rot)
- Frame-Scroller: rollende Liste der letzten 7 Frames
  - RSSI-Farbbalken (rot/gelb/grün)
  - Message-Type (B=BSM, C=CAM, D=DENM)
  - BSSID Hex + Kanal/RSSI
- Display UI Task aktualisiert alle 2 Sekunden
- Frame Writer sendet Frames an Scroller Queue
- Channel Hopper aktualisiert g_v2x_channel_idx
This commit is contained in:
Clawdia
2026-05-09 11:47:55 +02:00
parent 874dd969b6
commit 240a374148
7 changed files with 896 additions and 3 deletions
@@ -0,0 +1,192 @@
/**
* ASCII-Font (6x8 Pixel) + Text-Ausgabe
*/
#include "esp_log.h"
#include "v2x_display.h"
#include "esp_lcd_panel_ops.h"
extern esp_lcd_panel_handle_t s_panel;
extern bool s_initialized;
static const uint8_t font6x8[][6] = {
{0x00,0x00,0x00,0x00,0x00,0x00}, // space
{0x00,0x00,0x5F,0x00,0x00,0x00}, // !
{0x00,0x07,0x00,0x07,0x00,0x00}, // "
{0x14,0x7F,0x14,0x7F,0x14,0x00}, // #
{0x24,0x2A,0x7F,0x2A,0x12,0x00}, // $
{0x23,0x13,0x08,0x64,0x62,0x00}, // %
{0x36,0x49,0x55,0x22,0x50,0x00}, // &
{0x00,0x05,0x03,0x00,0x00,0x00}, // '
{0x00,0x1C,0x22,0x41,0x00,0x00}, // (
{0x00,0x41,0x22,0x1C,0x00,0x00}, // )
{0x14,0x08,0x3E,0x08,0x14,0x00}, // *
{0x08,0x08,0x3E,0x08,0x08,0x00}, // +
{0x00,0x50,0x30,0x00,0x00,0x00}, // ,
{0x08,0x08,0x08,0x08,0x08,0x00}, // -
{0x00,0x60,0x60,0x00,0x00,0x00}, // .
{0x20,0x10,0x08,0x04,0x02,0x00}, // /
{0x3E,0x51,0x49,0x45,0x3E,0x00}, // 0
{0x00,0x42,0x7F,0x40,0x00,0x00}, // 1
{0x42,0x61,0x51,0x49,0x46,0x00}, // 2
{0x21,0x41,0x45,0x4B,0x31,0x00}, // 3
{0x18,0x14,0x12,0x7F,0x10,0x00}, // 4
{0x27,0x45,0x45,0x45,0x39,0x00}, // 5
{0x3C,0x4A,0x49,0x49,0x30,0x00}, // 6
{0x01,0x71,0x09,0x05,0x03,0x00}, // 7
{0x36,0x49,0x49,0x49,0x36,0x00}, // 8
{0x06,0x49,0x49,0x29,0x1E,0x00}, // 9
{0x00,0x36,0x36,0x00,0x00,0x00}, // :
{0x00,0x56,0x36,0x00,0x00,0x00}, // ;
{0x08,0x14,0x22,0x41,0x00,0x00}, // <
{0x14,0x14,0x14,0x14,0x14,0x00}, // =
{0x00,0x41,0x22,0x14,0x08,0x00}, // >
{0x02,0x01,0x51,0x09,0x06,0x00}, // ?
{0x3E,0x41,0x5D,0x55,0x1E,0x00}, // A
{0x7F,0x09,0x09,0x09,0x7E,0x00}, // B
{0x7F,0x49,0x49,0x49,0x36,0x00}, // C
{0x3E,0x41,0x41,0x21,0x1E,0x00}, // D
{0x7F,0x49,0x49,0x49,0x41,0x00}, // E
{0x7F,0x49,0x49,0x45,0x43,0x00}, // F
{0x7F,0x49,0x49,0x49,0x38,0x00}, // G
{0x7F,0x08,0x08,0x08,0x7F,0x00}, // H
{0x00,0x41,0x7F,0x41,0x00,0x00}, // I
{0x20,0x40,0x41,0x3F,0x01,0x00}, // J
{0x7F,0x08,0x14,0x22,0x41,0x00}, // K
{0x7F,0x40,0x40,0x40,0x40,0x00}, // L
{0x7F,0x02,0x0C,0x02,0x7F,0x00}, // M
{0x7F,0x04,0x08,0x10,0x7F,0x00}, // N
{0x3E,0x41,0x41,0x41,0x3E,0x00}, // O
{0x7F,0x09,0x09,0x09,0x06,0x00}, // P
{0x3E,0x41,0x51,0x21,0x5E,0x00}, // Q
{0x7F,0x09,0x19,0x29,0x46,0x00}, // R
{0x46,0x49,0x49,0x49,0x31,0x00}, // S
{0x01,0x01,0x7F,0x01,0x01,0x00}, // T
{0x3F,0x40,0x40,0x40,0x3F,0x00}, // U
{0x1F,0x20,0x40,0x20,0x1F,0x00}, // V
{0x3F,0x40,0x38,0x40,0x3F,0x00}, // W
{0x63,0x14,0x08,0x14,0x63,0x00}, // X
{0x07,0x08,0x70,0x08,0x07,0x00}, // Y
{0x61,0x51,0x49,0x45,0x43,0x00}, // Z
{0x00,0x7F,0x41,0x41,0x00,0x00}, // [
{0x55,0x2A,0x55,0x2A,0x55,0x00}, // \
{0x00,0x41,0x41,0x7F,0x00,0x00}, // ]
{0x04,0x02,0x01,0x02,0x04,0x00}, // ^
{0x40,0x40,0x40,0x40,0x40,0x00}, // _
{0x00,0x01,0x02,0x04,0x00,0x00}, // `
{0x00,0x20,0x3E,0x21,0x3E,0x00}, // a
{0x7F,0x28,0x44,0x44,0x38,0x00}, // b
{0x38,0x44,0x44,0x44,0x20,0x00}, // c
{0x38,0x44,0x44,0x28,0x7F,0x00}, // d
{0x38,0x54,0x54,0x54,0x18,0x00}, // e
{0x08,0x7E,0x09,0x01,0x02,0x00}, // f
{0x0C,0x52,0x52,0x52,0x3E,0x00}, // g
{0x7F,0x08,0x04,0x04,0x78,0x00}, // h
{0x00,0x44,0x7D,0x40,0x00,0x00}, // i
{0x20,0x40,0x44,0x3D,0x00,0x00}, // j
{0x7F,0x10,0x28,0x44,0x00,0x00}, // k
{0x00,0x41,0x7F,0x40,0x00,0x00}, // l
{0x7C,0x04,0x18,0x04,0x78,0x00}, // m
{0x7C,0x08,0x04,0x04,0x78,0x00}, // n
{0x38,0x44,0x44,0x44,0x38,0x00}, // o
{0x7C,0x14,0x14,0x14,0x08,0x00}, // p
{0x08,0x14,0x14,0x18,0x7C,0x00}, // q
{0x7F,0x08,0x04,0x04,0x08,0x00}, // r
{0x48,0x54,0x54,0x54,0x20,0x00}, // s
{0x04,0x3F,0x44,0x40,0x20,0x00}, // t
{0x3C,0x40,0x40,0x20,0x7C,0x00}, // u
{0x1C,0x20,0x40,0x20,0x1C,0x00}, // v
{0x3C,0x40,0x30,0x40,0x3C,0x00}, // w
{0x44,0x28,0x10,0x28,0x44,0x00}, // x
{0x0C,0x50,0x50,0x50,0x3C,0x00}, // y
{0x44,0x64,0x54,0x4C,0x44,0x00}, // z
{0x00,0x08,0x36,0x41,0x00,0x00}, // {
{0x00,0x00,0x7F,0x00,0x00,0x00}, // |
{0x00,0x41,0x36,0x08,0x00,0x00}, // }
{0x10,0x08,0x08,0x10,0x08,0x00}, // ~
};
esp_err_t v2x_display_text(int16_t x, int16_t y, const char *text,
uint16_t color, disp_font_t font)
{
if (!s_initialized) return ESP_ERR_INVALID_STATE;
uint8_t char_bytes[6];
int16_t tx = x;
for (int ci = 0; text[ci] && tx < DISP_W; ci++) {
int idx = text[ci] - FONT_START;
if (idx < 0 || idx >= 95) continue;
memcpy(char_bytes, font6x8[idx], 6);
for (int c = 0; c < 6; c++) {
uint16_t row = char_bytes[c];
for (int r = 0; r < 8; r++) {
if (row & 0x01) {
uint16_t pixel = color;
int px = tx + c;
int py = y + r;
if (px >= 0 && px < DISP_W && py >= 0 && py < DISP_H) {
esp_lcd_panel_draw_bitmap(s_panel, px, py, px + 1, py + 1, &pixel);
}
}
row >>= 1;
}
}
tx += 7;
}
return ESP_OK;
}
esp_err_t v2x_display_frame_count(uint32_t count)
{
if (!s_initialized) return ESP_ERR_INVALID_STATE;
char buf[32];
snprintf(buf, sizeof(buf), "%lu", (unsigned long)count);
int len = strlen(buf);
int x = (DISP_W - len * 7) / 2;
uint8_t char_bytes[6];
for (int ci = 0; ci < len; ci++) {
int idx = buf[ci] - FONT_START;
if (idx < 0 || idx >= 95) continue;
memcpy(char_bytes, font6x8[idx], 6);
for (int c = 0; c < 6; c++) {
uint16_t row = char_bytes[c];
for (int r = 0; r < 8; r++) {
if (row & 0x01) {
uint16_t pixel = DISP_COLOR_RED;
int px = x + ci * 18 + c * 3;
for (int dy = 0; dy < 3; dy++) {
for (int dx = 0; dx < 3; dx++) {
int py = y + r * 3 + dy;
if (px + dx >= 0 && px + dx < DISP_W && py < DISP_H) {
esp_lcd_panel_draw_bitmap(s_panel, px + dx, py, px + dx + 1, py + 1, &pixel);
}
}
}
}
row >>= 1;
}
}
}
return ESP_OK;
}
esp_err_t v2x_display_storage_bar(int16_t x, int16_t y, int16_t w, int16_t h, float pct)
{
pct = (pct < 0.0f) ? 0.0f : (pct > 1.0f) ? 1.0f : pct;
v2x_display_rect(x, y, w, h, DISP_COLOR_GRAY, true);
uint16_t fg = DISP_COLOR_GREEN;
if (pct < 0.3f) fg = DISP_COLOR_RED;
else if (pct < 0.6f) fg = DISP_COLOR_YELLOW;
int16_t fw = (int16_t)(w * pct);
v2x_display_rect(x, y, fw, h, fg, true);
return ESP_OK;
}