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:
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Frame-Scroller — rollende Liste der letzten Frames
|
||||
*
|
||||
* Zeigt bis zu 7 Frames an, jeder Frame = 1 Zeile (12px hoch):
|
||||
* [RSSI-Balk] X #####-#####-##### CH:161 RSSI:-45
|
||||
*
|
||||
* Der Scroller aktualisiert sich nur wenn sich ein Frame ändert.
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "v2x_display.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
|
||||
extern esp_lcd_panel_handle_t s_panel;
|
||||
extern bool s_initialized;
|
||||
|
||||
static const char *TAG = "v2x-scroller";
|
||||
|
||||
/* Scroller-Konfiguration */
|
||||
#define SCROLLER_FRAMES_VISIBLE 7 /* Frames sichtbar */
|
||||
#define SCROLLER_LINE_HEIGHT 13 /* Pixel pro Zeile */
|
||||
#define SCROLLER_START_Y 28 /* Erste Zeile beginnt bei Y=28 */
|
||||
#define SCROLLER_MAX_ENTRIES 64 /* Frames im Buffer */
|
||||
|
||||
/* Frame-Daten für den Scroller */
|
||||
typedef struct {
|
||||
char msg_char; /* 'B', 'C', 'D', '?' */
|
||||
int8_t rssi; /* RSSI dBm */
|
||||
uint8_t ch; /* Kanal */
|
||||
char bssid_hex[9]; /* #####-#####-##### */
|
||||
} scroller_entry_t;
|
||||
|
||||
static scroller_entry_t s_entries[SCROLLER_MAX_ENTRIES];
|
||||
static uint32_t s_entry_count = 0;
|
||||
|
||||
/* RSSI-Balk als ASCII (20 Zeichen breit) */
|
||||
static void rssi_to_bar(int8_t rssi, char *bar, int len)
|
||||
{
|
||||
if (rssi <= -100) rssi = -100;
|
||||
if (rssi >= -20) rssi = -20;
|
||||
|
||||
float pct = (float)(rssi + 100) / 80.0f;
|
||||
int filled = (int)(pct * 20);
|
||||
|
||||
for (int i = 0; i < len - 1; i++) {
|
||||
bar[i] = (i < filled) ? '\x5C' : '\xC1'; /* ▌ und ░ (block chars) */
|
||||
}
|
||||
bar[len - 1] = '\0';
|
||||
}
|
||||
|
||||
/* BSSID als #####-#####-##### hex */
|
||||
static void bssid_to_str(const uint8_t *bssid, char *str)
|
||||
{
|
||||
snprintf(str, 14, "%02X-%02X-%02X-%02X-%02X-%02X",
|
||||
bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
|
||||
}
|
||||
|
||||
/* Scroller-Eintrag hinzufügen */
|
||||
void scroller_add_entry(uint8_t msg_type, int8_t rssi, uint8_t ch, const uint8_t *bssid)
|
||||
{
|
||||
scroller_entry_t *e = &s_entries[s_entry_count % SCROLLER_MAX_ENTRIES];
|
||||
|
||||
/* Msg-Type Character */
|
||||
switch (msg_type) {
|
||||
case 1: e->msg_char = 'B'; break; /* BSM */
|
||||
case 2: e->msg_char = 'C'; break; /* CAM */
|
||||
case 3: e->msg_char = 'D'; break; /* DENM */
|
||||
default: e->msg_char = '?'; break;
|
||||
}
|
||||
|
||||
e->rssi = rssi;
|
||||
e->ch = ch;
|
||||
if (bssid) {
|
||||
bssid_to_str(bssid, e->bssid_hex);
|
||||
} else {
|
||||
snprintf(e->bssid_hex, 14, "----------");
|
||||
}
|
||||
|
||||
s_entry_count++;
|
||||
}
|
||||
|
||||
/* Scroller neu zeichnen (vollständig) */
|
||||
void scroller_redraw(void)
|
||||
{
|
||||
if (!s_initialized) return;
|
||||
|
||||
int32_t total = s_entry_count;
|
||||
int start = (total > SCROLLER_FRAMES_VISIBLE) ?
|
||||
(total - SCROLLER_FRAMES_VISIBLE) : 0;
|
||||
|
||||
/* Hintergrund zeichnen */
|
||||
int total_h = SCROLLER_FRAMES_VISIBLE * SCROLLER_LINE_HEIGHT;
|
||||
v2x_display_rect(0, SCROLLER_START_Y, DISP_W, total_h, DISP_COLOR_BLACK, true);
|
||||
|
||||
/* Zeilenweise rendern (von oben nach unten = neueste Frames zuerst) */
|
||||
for (int i = SCROLLER_FRAMES_VISIBLE - 1; i >= 0; i--) {
|
||||
int idx = (start + i) % SCROLLER_MAX_ENTRIES;
|
||||
scroller_entry_t *e = &s_entries[idx];
|
||||
int y = SCROLLER_START_Y + (SCROLLER_FRAMES_VISIBLE - 1 - i) * SCROLLER_LINE_HEIGHT;
|
||||
|
||||
/* RSSI-Farbbalk links (5px breit, 11px hoch) */
|
||||
uint16_t bar_color = DISP_COLOR_RED;
|
||||
float pct = (float)(e->rssi + 100) / 80.0f;
|
||||
if (pct > 0.6f) bar_color = DISP_COLOR_GREEN;
|
||||
else if (pct > 0.3f) bar_color = DISP_COLOR_YELLOW;
|
||||
|
||||
v2x_display_rect(2, y + 1, 5, 11, bar_color, true);
|
||||
|
||||
/* Msg-Type Zeichen (grün/cyan/rot/gray) */
|
||||
uint16_t msg_color = DISP_COLOR_GRAY;
|
||||
if (e->msg_char == 'B') msg_color = DISP_COLOR_GREEN;
|
||||
else if (e->msg_char == 'C') msg_color = DISP_COLOR_CYAN;
|
||||
else if (e->msg_char == 'D') msg_color = DISP_COLOR_RED;
|
||||
|
||||
char msg_line[2] = { e->msg_char, '\0' };
|
||||
v2x_display_text(9, y + 1, msg_line, msg_color, FONT_6x8);
|
||||
|
||||
/* BSSID als Hex (blau) */
|
||||
v2x_display_text(16, y + 1, e->bssid_hex, DISP_COLOR_BLUE, FONT_6x8);
|
||||
|
||||
/* Kanal + RSSI (weiß) */
|
||||
char ch_buf[20];
|
||||
snprintf(ch_buf, sizeof(ch_buf), "CH:%d %3d", e->ch, e->rssi);
|
||||
v2x_display_text(60, y + 1, ch_buf, DISP_COLOR_WHITE, FONT_6x8);
|
||||
}
|
||||
}
|
||||
|
||||
/* Geänderte Zeile neu zeichnen (nur die betroffene Zeile — schnell) */
|
||||
void scroller_redraw_line(uint32_t entry_idx)
|
||||
{
|
||||
if (!s_initialized) return;
|
||||
|
||||
int32_t total = s_entry_count;
|
||||
if (entry_idx < (total > SCROLLER_FRAMES_VISIBLE ? total - SCROLLER_FRAMES_VISIBLE : 0)) {
|
||||
return; /* Zu alt, nicht sichtbar */
|
||||
}
|
||||
|
||||
int start = (total > SCROLLER_FRAMES_VISIBLE) ?
|
||||
(total - SCROLLER_FRAMES_VISIBLE) : 0;
|
||||
|
||||
int line_idx = (int)(entry_idx - start);
|
||||
if (line_idx < 0 || line_idx >= SCROLLER_FRAMES_VISIBLE) return;
|
||||
|
||||
int idx = entry_idx % SCROLLER_MAX_ENTRIES;
|
||||
scroller_entry_t *e = &s_entries[idx];
|
||||
int y = SCROLLER_START_Y + (SCROLLER_FRAMES_VISIBLE - 1 - line_idx) * SCROLLER_LINE_HEIGHT;
|
||||
|
||||
/* Zeile löschen */
|
||||
v2x_display_rect(0, y, DISP_W, SCROLLER_LINE_HEIGHT, DISP_COLOR_BLACK, true);
|
||||
|
||||
/* Neu zeichnen */
|
||||
uint16_t bar_color = DISP_COLOR_RED;
|
||||
float pct = (float)(e->rssi + 100) / 80.0f;
|
||||
if (pct > 0.6f) bar_color = DISP_COLOR_GREEN;
|
||||
else if (pct > 0.3f) bar_color = DISP_COLOR_YELLOW;
|
||||
|
||||
v2x_display_rect(2, y + 1, 5, 11, bar_color, true);
|
||||
|
||||
uint16_t msg_color = DISP_COLOR_GRAY;
|
||||
if (e->msg_char == 'B') msg_color = DISP_COLOR_GREEN;
|
||||
else if (e->msg_char == 'C') msg_color = DISP_COLOR_CYAN;
|
||||
else if (e->msg_char == 'D') msg_color = DISP_COLOR_RED;
|
||||
|
||||
char msg_line[2] = { e->msg_char, '\0' };
|
||||
v2x_display_text(9, y + 1, msg_line, msg_color, FONT_6x8);
|
||||
|
||||
v2x_display_text(16, y + 1, e->bssid_hex, DISP_COLOR_BLUE, FONT_6x8);
|
||||
|
||||
char ch_buf[20];
|
||||
snprintf(ch_buf, sizeof(ch_buf), "CH:%d %3d", e->ch, e->rssi);
|
||||
v2x_display_text(60, y + 1, ch_buf, DISP_COLOR_WHITE, FONT_6x8);
|
||||
}
|
||||
Reference in New Issue
Block a user