Compare commits
36 Commits
89f75c23ef
...
pi/general
| Author | SHA1 | Date | |
|---|---|---|---|
| 436e55a0c5 | |||
|
|
776aee18fc | ||
|
|
41e0a6b81d | ||
|
|
e4da3687b4 | ||
|
|
05cd085b89 | ||
|
|
d0d0b4dc39 | ||
|
|
25420d57b3 | ||
|
|
18984c29a3 | ||
|
|
7555efcba9 | ||
| 379f8e105b | |||
| 3b4d61c56d | |||
| 5ae0c64ba9 | |||
|
|
7f644652bb | ||
| 5c16e6deb7 | |||
|
|
706c7ac21b | ||
| b33c658885 | |||
|
|
2e5ad58978 | ||
|
|
8ffe8cdffb | ||
|
|
b0a5e05e4a | ||
|
|
4e2c714e1f | ||
|
|
7165f56464 | ||
|
|
a25e4b2cb3 | ||
|
|
76bf8c966d | ||
|
|
d63322d304 | ||
|
|
0b2f274719 | ||
|
|
22c32f3538 | ||
|
|
89c975bf17 | ||
|
|
e5cc124dd3 | ||
|
|
a0cec91845 | ||
|
|
1140de0fd4 | ||
|
|
37bdc8bb1b | ||
|
|
33936650c6 | ||
|
|
7eb05ea983 | ||
|
|
dca989a01b | ||
|
|
12dbbd8942 | ||
| 9ca0227214 |
@@ -1,9 +1,25 @@
|
|||||||
{
|
{
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"allow": [
|
"allow": [
|
||||||
"Bash(echo:*)"
|
"Bash(echo:*)",
|
||||||
|
"Bash(idf.py build:*)",
|
||||||
|
"Bash(for f in DSEG14C_BI_50px.c InziuIosevka_Slab_CC_12px.c InziuIosevka_Slab_CC_16px.c InziuIosevka_Slab_CC_20px.c InziuIosevka_Slab_CC_24px.c InziuIosevka_Slab_CC_32px.c)",
|
||||||
|
"Bash(do sed -i '/\\\\.static_bitmap = 0,/d' \"$f\")",
|
||||||
|
"Bash(done)",
|
||||||
|
"Bash(file:*)",
|
||||||
|
"mcp__ide__getDiagnostics",
|
||||||
|
"Bash(python -m py_compile:*)",
|
||||||
|
"WebSearch",
|
||||||
|
"WebFetch(domain:docs.waveshare.com)",
|
||||||
|
"WebFetch(domain:www.waveshare.com)",
|
||||||
|
"WebFetch(domain:raw.githubusercontent.com)",
|
||||||
|
"Bash(npm view:*)",
|
||||||
|
"WebFetch(domain:raw.githubusercontent.com)",
|
||||||
|
"Bash(docker ps:*)",
|
||||||
|
"Bash(python3:*)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"outputStyle": "iseri",
|
"outputStyle": "iseri",
|
||||||
"prefersReducedMotion": false
|
"spinnerTipsEnabled": false,
|
||||||
|
"prefersReducedMotion": true
|
||||||
}
|
}
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,3 +3,6 @@ managed_components/
|
|||||||
sdkconfig
|
sdkconfig
|
||||||
sdkconfig.old
|
sdkconfig.old
|
||||||
dependencies.lock
|
dependencies.lock
|
||||||
|
|
||||||
|
# vscode local settings
|
||||||
|
.vscode/
|
||||||
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"idf.currentSetup": "J:\\esp\\.espressif\\v5.5.2\\esp-idf",
|
|
||||||
"idf.flashType": "UART",
|
|
||||||
"idf.portWin": "COM7",
|
|
||||||
"idf.openOcdConfigs": [
|
|
||||||
"interface/ftdi/esp_ftdi.cfg",
|
|
||||||
"target/esp32s3.cfg"
|
|
||||||
],
|
|
||||||
"idf.customExtraVars": {
|
|
||||||
"IDF_TARGET": "esp32s3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
211
PLAN.md
211
PLAN.md
@@ -1,211 +0,0 @@
|
|||||||
# Pi Dashboard — ESP32-S3 RLCD 4.2" Project Plan
|
|
||||||
|
|
||||||
## Goal
|
|
||||||
|
|
||||||
WebSocket client on the ESP32-S3 RLCD board that receives system status from a
|
|
||||||
Raspberry Pi home server over LAN and renders a monitoring dashboard via LVGL.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## New project location
|
|
||||||
|
|
||||||
Create a new ESP-IDF project directory **outside** the Examples folder, e.g.:
|
|
||||||
|
|
||||||
```
|
|
||||||
J:\dev\arduino\ESP32-S3-RLCD-4.2-main\Projects\pi_dashboard\
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Files to copy from the factory example
|
|
||||||
|
|
||||||
Source root: `Example\ESP-IDF\10_FactoryProgram\`
|
|
||||||
|
|
||||||
### Must copy (build skeleton)
|
|
||||||
|
|
||||||
| File / Dir | Why |
|
|
||||||
|---|---|
|
|
||||||
| `CMakeLists.txt` | Project root cmake — edit project name, strip `EXTRA_COMPONENT_DIRS` if not using ExternLib |
|
|
||||||
| `partitions.csv` | Partition table (8MB app partition, NVS, PHY) |
|
|
||||||
| `sdkconfig.defaults` | Base config — **strip BT lines**, keep SPIRAM/flash/LVGL settings |
|
|
||||||
| `main/CMakeLists.txt` | Main component registration |
|
|
||||||
| `main/idf_component.yml` | Managed component deps — keep `lvgl/lvgl: ^8.4.0`, drop `avi_player` and `esp_new_jpeg`, **add `espressif/esp_websocket_client`** |
|
|
||||||
|
|
||||||
### Must copy (display driver)
|
|
||||||
|
|
||||||
| File | From | Why |
|
|
||||||
|---|---|---|
|
|
||||||
| `display_bsp.h` | `components/port_bsp/` | DisplayPort class — RLCD SPI driver with LUT pixel mapping |
|
|
||||||
| `display_bsp.cpp` | `components/port_bsp/` | Full implementation: SPI init, reset, command sequences, SetPixel, Display |
|
|
||||||
|
|
||||||
These two files are the entire RLCD hardware abstraction. No other display code
|
|
||||||
exists. Copy them into a `components/display_bsp/` component in the new project.
|
|
||||||
|
|
||||||
### Must copy (LVGL port)
|
|
||||||
|
|
||||||
| File | From | Why |
|
|
||||||
|---|---|---|
|
|
||||||
| `lvgl_bsp.h` | `components/app_bsp/` | Lvgl_PortInit, Lvgl_lock/unlock declarations |
|
|
||||||
| `lvgl_bsp.cpp` | `components/app_bsp/` | LVGL display driver registration, double-buffered PSRAM, tick timer, port task on Core 0 |
|
|
||||||
|
|
||||||
Copy into a `components/lvgl_port/` component. This file is self-contained — its
|
|
||||||
only dependency is `lvgl.h` and FreeRTOS.
|
|
||||||
|
|
||||||
### Copy as reference (WiFi STA)
|
|
||||||
|
|
||||||
| File | From | Why |
|
|
||||||
|---|---|---|
|
|
||||||
| `esp_wifi_bsp.h` | `Example/ESP-IDF/02_WIFI_STA/components/esp_wifi_bsp/` | Cleaner than the factory version — no scan-and-destroy, no BLE entanglement |
|
|
||||||
| `esp_wifi_bsp.c` | same | Simple STA init + connect + reconnect skeleton |
|
|
||||||
|
|
||||||
The `02_WIFI_STA` example is a better starting point than the factory program's
|
|
||||||
wifi code. The factory version tears down WiFi for BLE scanning — useless for a
|
|
||||||
persistent connection. You will need to modify the STA code to:
|
|
||||||
- Make SSID/password configurable (NVS or Kconfig)
|
|
||||||
- Add auto-reconnect on disconnect
|
|
||||||
- Signal connection readiness via event group so the WebSocket task knows when to start
|
|
||||||
|
|
||||||
### Do NOT copy
|
|
||||||
|
|
||||||
| Component | Why not |
|
|
||||||
|---|---|
|
|
||||||
| `components/port_bsp/` (everything except display_bsp) | I2C bus, buttons, SD card, ADC, codec — none needed for a dashboard |
|
|
||||||
| `components/ExternLib/` (SensorLib, codec_board) | Sensor drivers and audio codec — irrelevant |
|
|
||||||
| `components/app_bsp/ble_scan_bsp.c` | BLE scanning — not needed, and conflicts with persistent WiFi |
|
|
||||||
| `components/app_bsp/esp_wifi_bsp.c` | Factory version destroys WiFi for BLE — use 02_WIFI_STA instead |
|
|
||||||
| `components/ui_bsp/` | NXP GUI Guider generated UI — you will design your own dashboard layout |
|
|
||||||
| `components/user_app/` | Factory test logic — all replaced by your WebSocket + dashboard code |
|
|
||||||
| `managed_components/` | Auto-downloaded by ESP-IDF component manager from `idf_component.yml` — never copy these |
|
|
||||||
| `build/` | Build artifacts — never copy |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## New project structure
|
|
||||||
|
|
||||||
```
|
|
||||||
pi_dashboard/
|
|
||||||
CMakeLists.txt # from factory, edited
|
|
||||||
partitions.csv # from factory, as-is
|
|
||||||
sdkconfig.defaults # from factory, stripped (no BT)
|
|
||||||
main/
|
|
||||||
CMakeLists.txt # register main.cpp
|
|
||||||
idf_component.yml # lvgl ^8.4.0, esp_websocket_client
|
|
||||||
main.cpp # app_main: wifi init, display init, lvgl init, ws connect, task spawn
|
|
||||||
components/
|
|
||||||
display_bsp/
|
|
||||||
CMakeLists.txt # new: register display_bsp.cpp, REQUIRES driver esp_lcd
|
|
||||||
display_bsp.h # from port_bsp, as-is
|
|
||||||
display_bsp.cpp # from port_bsp, as-is
|
|
||||||
lvgl_port/
|
|
||||||
CMakeLists.txt # new: register lvgl_bsp.cpp, REQUIRES lvgl__lvgl esp_timer
|
|
||||||
lvgl_bsp.h # from app_bsp, as-is
|
|
||||||
lvgl_bsp.cpp # from app_bsp, as-is
|
|
||||||
wifi_sta/
|
|
||||||
CMakeLists.txt # new: register wifi_sta.c, REQUIRES esp_wifi esp_event nvs_flash
|
|
||||||
wifi_sta.h # based on 02_WIFI_STA, extended with reconnect + event group
|
|
||||||
wifi_sta.c # based on 02_WIFI_STA, extended
|
|
||||||
ws_client/
|
|
||||||
CMakeLists.txt # new: register ws_client.cpp, REQUIRES esp_websocket_client
|
|
||||||
ws_client.h # WebSocket connect/disconnect, message callback registration
|
|
||||||
ws_client.cpp # esp_websocket_client wrapper, reconnect logic, JSON parse
|
|
||||||
dashboard_ui/
|
|
||||||
CMakeLists.txt # new: register dashboard_ui.cpp, REQUIRES lvgl__lvgl
|
|
||||||
dashboard_ui.h # UI layout: create/update functions for dashboard widgets
|
|
||||||
dashboard_ui.cpp # LVGL widget creation, label/bar/table updates from parsed data
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## sdkconfig.defaults (modified for this project)
|
|
||||||
|
|
||||||
Strip from the factory version:
|
|
||||||
- `CONFIG_BT_ENABLED=y` and all BT lines (saves ~300KB flash + significant RAM)
|
|
||||||
- `CONFIG_BT_BLE_*` lines
|
|
||||||
|
|
||||||
Keep:
|
|
||||||
- `CONFIG_IDF_TARGET="esp32s3"`
|
|
||||||
- `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`
|
|
||||||
- `CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y`
|
|
||||||
- `CONFIG_PARTITION_TABLE_CUSTOM=y`
|
|
||||||
- `CONFIG_SPIRAM=y` / `CONFIG_SPIRAM_MODE_OCT=y` / `CONFIG_SPIRAM_SPEED_80M=y`
|
|
||||||
- `CONFIG_FREERTOS_HZ=1000`
|
|
||||||
- `CONFIG_LV_MEM_SIZE_KILOBYTES=64`
|
|
||||||
- `CONFIG_LV_DISP_DEF_REFR_PERIOD=1`
|
|
||||||
|
|
||||||
Add:
|
|
||||||
- `CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Runtime architecture
|
|
||||||
|
|
||||||
```
|
|
||||||
Core 0 Core 1
|
|
||||||
------ ------
|
|
||||||
WiFi driver (system) main task (app_main, exits after init)
|
|
||||||
LVGL port task (priority 5) WebSocket event task (priority 3)
|
|
||||||
- lv_timer_handler loop - receives WS messages
|
|
||||||
- holds/releases lvgl_mux - parses JSON
|
|
||||||
- acquires lvgl_mux
|
|
||||||
- updates dashboard widgets
|
|
||||||
- releases lvgl_mux
|
|
||||||
```
|
|
||||||
|
|
||||||
### Boot sequence (app_main)
|
|
||||||
|
|
||||||
1. `wifi_sta_init()` — start WiFi STA, wait for IP
|
|
||||||
2. `RlcdPort.RLCD_Init()` — init display over SPI
|
|
||||||
3. `Lvgl_PortInit(400, 300, flush_cb)` — init LVGL with double-buffered PSRAM
|
|
||||||
4. Create initial dashboard UI (under LVGL lock)
|
|
||||||
5. `ws_client_start("ws://<pi_ip>:<port>")` — connect WebSocket, register message handler
|
|
||||||
6. app_main returns, FreeRTOS tasks take over
|
|
||||||
|
|
||||||
### Data flow
|
|
||||||
|
|
||||||
```
|
|
||||||
Pi server --[WebSocket JSON]--> ESP32 ws_client
|
|
||||||
--> parse message
|
|
||||||
--> Lvgl_lock()
|
|
||||||
--> update lv_label / lv_bar / lv_table widgets
|
|
||||||
--> Lvgl_unlock()
|
|
||||||
--> LVGL port task flushes on next cycle
|
|
||||||
--> flush_cb converts 16-bit to 1-bit, pushes to RLCD
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Pi side (not part of this ESP-IDF project, but for context)
|
|
||||||
|
|
||||||
A simple Python WebSocket server that:
|
|
||||||
- Collects system stats (psutil or /proc reads)
|
|
||||||
- Serializes to JSON
|
|
||||||
- Pushes to connected clients every N seconds
|
|
||||||
|
|
||||||
Example payload:
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"cpu_pct": 23,
|
|
||||||
"mem_pct": 61,
|
|
||||||
"mem_used_mb": 1952,
|
|
||||||
"disk_pct": 44,
|
|
||||||
"cpu_temp": 52,
|
|
||||||
"uptime_hrs": 342,
|
|
||||||
"services": [
|
|
||||||
{"name": "docker", "status": "running"},
|
|
||||||
{"name": "pihole", "status": "running"},
|
|
||||||
{"name": "nginx", "status": "stopped"}
|
|
||||||
],
|
|
||||||
"net_rx_kbps": 1240,
|
|
||||||
"net_tx_kbps": 320
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Key decisions still open
|
|
||||||
|
|
||||||
1. **WiFi credentials** — hardcode for now, or Kconfig menuconfig, or NVS provisioning?
|
|
||||||
2. **Dashboard layout** — what stats matter most? Single screen or multiple pages?
|
|
||||||
3. **Update frequency** — how often should the Pi push? 2s? 5s? On-change?
|
|
||||||
4. **Pi server location** — fixed IP or mDNS discovery?
|
|
||||||
5. **Error states** — what to show when WiFi drops or Pi goes offline?
|
|
||||||
@@ -14,10 +14,13 @@ Raspberry Pi system monitor running on a Waveshare ESP32-S3 RLCD 4.2" board. Con
|
|||||||
Pi (server) ESP32-S3 (client)
|
Pi (server) ESP32-S3 (client)
|
||||||
stats_server.py --WS/JSON--> ws_client --> dashboard_ui (LVGL)
|
stats_server.py --WS/JSON--> ws_client --> dashboard_ui (LVGL)
|
||||||
+ local sensors (SHTC3)
|
+ local sensors (SHTC3)
|
||||||
|
+ RTC clock (PCF85063)
|
||||||
```
|
```
|
||||||
|
|
||||||
The Pi runs a WebSocket server that pushes system stats (CPU, memory, disk, temperature, network, services) as JSON every 2 seconds. The ESP32 parses the JSON and updates LVGL widgets. A data staleness watchdog forces reconnection if the server goes silent.
|
The Pi runs a WebSocket server that pushes system stats (CPU, memory, disk, temperature, network, services) as JSON every 2 seconds. The ESP32 parses the JSON and updates LVGL widgets. A data staleness watchdog forces reconnection if the server goes silent.
|
||||||
|
|
||||||
|
The display uses a two-column layout: left half shows Pi stats (CPU/RAM/DISK bars, CPU temp) and a services table; right half shows a large HH:MM:SS clock (montserrat_36), date with day-of-week, and local sensor readings (room temp, humidity). The services table auto-scrolls when services exceed the visible area; row height and visible row count are measured from LVGL at runtime, so the scroll loop adapts automatically to font, padding, or border changes. The clock updates every second from the on-board RTC, which syncs from the Pi's time when drift exceeds 60 seconds.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
Edit `components/esp_wifi_bsp/wifi_config.h`:
|
Edit `components/esp_wifi_bsp/wifi_config.h`:
|
||||||
|
|||||||
5
components/audio_client/CMakeLists.txt
Normal file
5
components/audio_client/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "audio_client.cpp"
|
||||||
|
REQUIRES espressif__esp_websocket_client port_bsp codec_board lvgl__lvgl
|
||||||
|
PRIV_REQUIRES esp_event json
|
||||||
|
INCLUDE_DIRS "./")
|
||||||
306
components/audio_client/audio_client.cpp
Normal file
306
components/audio_client/audio_client.cpp
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
#include "audio_client.h"
|
||||||
|
#include "codec_bsp.h"
|
||||||
|
#include "esp_websocket_client.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "cJSON.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/queue.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include <esp_heap_caps.h>
|
||||||
|
|
||||||
|
static const char *TAG = "audio_client";
|
||||||
|
|
||||||
|
#define AUDIO_CHUNK_SIZE 4096
|
||||||
|
#define PCM_QUEUE_DEPTH 10
|
||||||
|
#define PLAYBACK_STACK_SIZE (4 * 1024)
|
||||||
|
#define PLAYBACK_PRIORITY 4
|
||||||
|
#define WS_BUFFER_SIZE 8192
|
||||||
|
|
||||||
|
/* Status image constants */
|
||||||
|
#define STATUS_IMG_W 200
|
||||||
|
#define STATUS_IMG_H 200
|
||||||
|
#define STATUS_IMG_BYTES (STATUS_IMG_W * STATUS_IMG_H / 8) /* 5000 */
|
||||||
|
|
||||||
|
static esp_websocket_client_handle_t s_client = NULL;
|
||||||
|
static CodecPort *s_codec = NULL;
|
||||||
|
static QueueHandle_t s_pcm_queue = NULL;
|
||||||
|
static TaskHandle_t s_playback_task = NULL;
|
||||||
|
static volatile audio_state_t s_state = AUDIO_IDLE;
|
||||||
|
static volatile bool s_playing = false;
|
||||||
|
|
||||||
|
/* Status image state */
|
||||||
|
static uint8_t s_img_buf[STATUS_IMG_BYTES];
|
||||||
|
static lv_img_dsc_t s_img_dsc;
|
||||||
|
static volatile bool s_img_pending = false; /* expecting binary frame with image data */
|
||||||
|
static volatile bool s_img_updated = false; /* new image ready for UI consumption */
|
||||||
|
static volatile bool s_need_request_image = false; /* deferred image request on connect */
|
||||||
|
static TaskHandle_t s_img_notify_task = NULL; /* task to wake on new image */
|
||||||
|
|
||||||
|
/* Forward declarations */
|
||||||
|
static void playback_task(void *arg);
|
||||||
|
static void ws_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data);
|
||||||
|
|
||||||
|
/* ---------- Queue helpers ---------- */
|
||||||
|
|
||||||
|
static void flush_queue(void)
|
||||||
|
{
|
||||||
|
uint8_t *chunk;
|
||||||
|
while (xQueueReceive(s_pcm_queue, &chunk, 0) == pdTRUE) {
|
||||||
|
heap_caps_free(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- WebSocket event handler ---------- */
|
||||||
|
|
||||||
|
static void handle_text_frame(const char *data, int len)
|
||||||
|
{
|
||||||
|
cJSON *root = cJSON_ParseWithLength(data, len);
|
||||||
|
if (!root) {
|
||||||
|
ESP_LOGW(TAG, "JSON parse failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON *type = cJSON_GetObjectItem(root, "type");
|
||||||
|
if (!cJSON_IsString(type)) {
|
||||||
|
cJSON_Delete(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(type->valuestring, "status_image") == 0) {
|
||||||
|
ESP_LOGI(TAG, "Status image header received");
|
||||||
|
s_img_pending = true;
|
||||||
|
cJSON_Delete(root);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(type->valuestring, "alarm_start") == 0) {
|
||||||
|
int sr = 24000;
|
||||||
|
int ch = 2;
|
||||||
|
int bits = 16;
|
||||||
|
|
||||||
|
cJSON *item;
|
||||||
|
item = cJSON_GetObjectItem(root, "sample_rate");
|
||||||
|
if (cJSON_IsNumber(item)) sr = item->valueint;
|
||||||
|
item = cJSON_GetObjectItem(root, "channels");
|
||||||
|
if (cJSON_IsNumber(item)) ch = item->valueint;
|
||||||
|
item = cJSON_GetObjectItem(root, "bits");
|
||||||
|
if (cJSON_IsNumber(item)) bits = item->valueint;
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Alarm start: %dHz %dch %dbit", sr, ch, bits);
|
||||||
|
|
||||||
|
/* Flush any stale data */
|
||||||
|
flush_queue();
|
||||||
|
|
||||||
|
/* Open codec for playback */
|
||||||
|
s_codec->CodecPort_SetInfo("es8311", 1, sr, ch, bits);
|
||||||
|
s_codec->CodecPort_SetSpeakerVol(70);
|
||||||
|
|
||||||
|
s_playing = true;
|
||||||
|
s_state = AUDIO_PLAYING;
|
||||||
|
|
||||||
|
} else if (strcmp(type->valuestring, "alarm_stop") == 0) {
|
||||||
|
ESP_LOGI(TAG, "Alarm stop");
|
||||||
|
s_playing = false;
|
||||||
|
|
||||||
|
/* Let playback task drain remaining chunks, then close */
|
||||||
|
vTaskDelay(pdMS_TO_TICKS(100));
|
||||||
|
flush_queue();
|
||||||
|
s_codec->CodecPort_CloseSpeaker();
|
||||||
|
|
||||||
|
s_state = AUDIO_CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
cJSON_Delete(root);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_binary_frame(const uint8_t *data, int len)
|
||||||
|
{
|
||||||
|
/* Status image binary payload */
|
||||||
|
if (s_img_pending) {
|
||||||
|
if (len == STATUS_IMG_BYTES) {
|
||||||
|
memcpy(s_img_buf, data, STATUS_IMG_BYTES);
|
||||||
|
s_img_updated = true;
|
||||||
|
if (s_img_notify_task) {
|
||||||
|
xTaskNotifyGive(s_img_notify_task);
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Status image received (%d bytes)", len);
|
||||||
|
} else {
|
||||||
|
ESP_LOGW(TAG, "Status image size mismatch: got %d, expected %d", len, STATUS_IMG_BYTES);
|
||||||
|
}
|
||||||
|
s_img_pending = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s_playing) return;
|
||||||
|
|
||||||
|
uint8_t *chunk = (uint8_t *)heap_caps_malloc(len, MALLOC_CAP_SPIRAM);
|
||||||
|
if (!chunk) {
|
||||||
|
ESP_LOGW(TAG, "PSRAM alloc failed (%d bytes)", len);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
memcpy(chunk, data, len);
|
||||||
|
|
||||||
|
if (xQueueSend(s_pcm_queue, &chunk, 0) != pdTRUE) {
|
||||||
|
ESP_LOGW(TAG, "PCM queue full, dropping chunk");
|
||||||
|
heap_caps_free(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ws_event_handler(void *arg, esp_event_base_t event_base,
|
||||||
|
int32_t event_id, void *event_data)
|
||||||
|
{
|
||||||
|
esp_websocket_event_data_t *ev = (esp_websocket_event_data_t *)event_data;
|
||||||
|
|
||||||
|
switch (event_id) {
|
||||||
|
case WEBSOCKET_EVENT_CONNECTED:
|
||||||
|
ESP_LOGI(TAG, "Audio WS connected");
|
||||||
|
s_state = AUDIO_CONNECTED;
|
||||||
|
s_img_pending = false;
|
||||||
|
s_need_request_image = true;
|
||||||
|
if (s_img_notify_task) {
|
||||||
|
xTaskNotifyGive(s_img_notify_task);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEBSOCKET_EVENT_DISCONNECTED:
|
||||||
|
ESP_LOGW(TAG, "Audio WS disconnected");
|
||||||
|
s_playing = false;
|
||||||
|
s_img_pending = false;
|
||||||
|
flush_queue();
|
||||||
|
s_state = AUDIO_IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEBSOCKET_EVENT_DATA:
|
||||||
|
if (ev->op_code == 0x01 && ev->data_len > 0) {
|
||||||
|
handle_text_frame(ev->data_ptr, ev->data_len);
|
||||||
|
} else if (ev->op_code == 0x02 && ev->data_len > 0) {
|
||||||
|
handle_binary_frame((const uint8_t *)ev->data_ptr, ev->data_len);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WEBSOCKET_EVENT_ERROR:
|
||||||
|
ESP_LOGE(TAG, "Audio WS error");
|
||||||
|
s_playing = false;
|
||||||
|
s_state = AUDIO_ERROR;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Playback task ---------- */
|
||||||
|
|
||||||
|
static void playback_task(void *arg)
|
||||||
|
{
|
||||||
|
uint8_t *chunk;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (xQueueReceive(s_pcm_queue, &chunk, pdMS_TO_TICKS(500)) == pdTRUE) {
|
||||||
|
if (s_playing) {
|
||||||
|
s_codec->CodecPort_PlayWrite(chunk, AUDIO_CHUNK_SIZE);
|
||||||
|
}
|
||||||
|
heap_caps_free(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Public API ---------- */
|
||||||
|
|
||||||
|
void audio_client_init(const char *uri, void *codec)
|
||||||
|
{
|
||||||
|
s_codec = (CodecPort *)codec;
|
||||||
|
|
||||||
|
/* Initialize status image descriptor */
|
||||||
|
memset(&s_img_dsc, 0, sizeof(s_img_dsc));
|
||||||
|
s_img_dsc.header.cf = LV_IMG_CF_ALPHA_1BIT;
|
||||||
|
s_img_dsc.header.w = STATUS_IMG_W;
|
||||||
|
s_img_dsc.header.h = STATUS_IMG_H;
|
||||||
|
s_img_dsc.data_size = STATUS_IMG_BYTES;
|
||||||
|
s_img_dsc.data = s_img_buf;
|
||||||
|
|
||||||
|
s_pcm_queue = xQueueCreate(PCM_QUEUE_DEPTH, sizeof(uint8_t *));
|
||||||
|
if (!s_pcm_queue) {
|
||||||
|
ESP_LOGE(TAG, "Failed to create PCM queue");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_websocket_client_config_t config = {};
|
||||||
|
config.uri = uri;
|
||||||
|
config.reconnect_timeout_ms = 5000;
|
||||||
|
config.buffer_size = WS_BUFFER_SIZE;
|
||||||
|
|
||||||
|
s_client = esp_websocket_client_init(&config);
|
||||||
|
esp_websocket_register_events(s_client, WEBSOCKET_EVENT_ANY, ws_event_handler, NULL);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Audio client initialized: %s", uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_client_start(void)
|
||||||
|
{
|
||||||
|
if (!s_client) return;
|
||||||
|
|
||||||
|
/* Create playback task pinned to Core 1 */
|
||||||
|
xTaskCreatePinnedToCore(playback_task, "audio_play", PLAYBACK_STACK_SIZE,
|
||||||
|
NULL, PLAYBACK_PRIORITY, &s_playback_task, 1);
|
||||||
|
|
||||||
|
esp_websocket_client_start(s_client);
|
||||||
|
ESP_LOGI(TAG, "Audio client started");
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_client_stop(void)
|
||||||
|
{
|
||||||
|
if (!s_client) return;
|
||||||
|
|
||||||
|
s_playing = false;
|
||||||
|
esp_websocket_client_stop(s_client);
|
||||||
|
flush_queue();
|
||||||
|
|
||||||
|
if (s_playback_task) {
|
||||||
|
vTaskDelete(s_playback_task);
|
||||||
|
s_playback_task = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_state = AUDIO_IDLE;
|
||||||
|
ESP_LOGI(TAG, "Audio client stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
audio_state_t audio_client_get_state(void)
|
||||||
|
{
|
||||||
|
return s_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_client_set_image_notify_task(TaskHandle_t task)
|
||||||
|
{
|
||||||
|
s_img_notify_task = task;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lv_img_dsc_t *audio_client_get_status_image(bool *updated)
|
||||||
|
{
|
||||||
|
if (updated) {
|
||||||
|
*updated = s_img_updated;
|
||||||
|
}
|
||||||
|
return &s_img_dsc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void audio_client_ack_status_image(void)
|
||||||
|
{
|
||||||
|
s_img_updated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool audio_client_send_pending_request(void)
|
||||||
|
{
|
||||||
|
if (!s_need_request_image || !s_client) return false;
|
||||||
|
s_need_request_image = false;
|
||||||
|
static const char REQUEST_IMG_JSON[] = "{\"type\":\"request_image\"}";
|
||||||
|
int ret = esp_websocket_client_send_text(s_client, REQUEST_IMG_JSON, strlen(REQUEST_IMG_JSON), pdMS_TO_TICKS(1000));
|
||||||
|
if (ret < 0) {
|
||||||
|
ESP_LOGE(TAG, "Failed to send image request: %d", ret);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Sent image request to server");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
63
components/audio_client/audio_client.h
Normal file
63
components/audio_client/audio_client.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <freertos/FreeRTOS.h>
|
||||||
|
#include <freertos/task.h>
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
AUDIO_IDLE = 0,
|
||||||
|
AUDIO_CONNECTED,
|
||||||
|
AUDIO_PLAYING,
|
||||||
|
AUDIO_ERROR,
|
||||||
|
} audio_state_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the audio streaming client.
|
||||||
|
* @param uri WebSocket URI (e.g. "ws://192.168.2.199:8766")
|
||||||
|
* @param codec Pointer to CodecPort instance (passed as void* for C linkage)
|
||||||
|
*/
|
||||||
|
void audio_client_init(const char *uri, void *codec);
|
||||||
|
|
||||||
|
/** Start the WebSocket connection and playback task. */
|
||||||
|
void audio_client_start(void);
|
||||||
|
|
||||||
|
/** Stop playback and disconnect. */
|
||||||
|
void audio_client_stop(void);
|
||||||
|
|
||||||
|
/** Get current audio client state. */
|
||||||
|
audio_state_t audio_client_get_state(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a task to be notified (via xTaskNotifyGive) when a new status
|
||||||
|
* image arrives. Call before audio_client_start().
|
||||||
|
*/
|
||||||
|
void audio_client_set_image_notify_task(TaskHandle_t task);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the latest status image descriptor.
|
||||||
|
* @param updated Set to true if a new image arrived since last call.
|
||||||
|
* @return Pointer to the static image descriptor (always valid).
|
||||||
|
*/
|
||||||
|
const lv_img_dsc_t *audio_client_get_status_image(bool *updated);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Acknowledge that the status image was successfully rendered.
|
||||||
|
* Clears the updated flag so subsequent get_status_image calls return false.
|
||||||
|
*/
|
||||||
|
void audio_client_ack_status_image(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send any pending image request to the server.
|
||||||
|
* Call from a task context (not from an event handler).
|
||||||
|
* @return true if a request was sent, false if none pending or send failed.
|
||||||
|
*/
|
||||||
|
bool audio_client_send_pending_request(void);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "dashboard_ui.c"
|
SRCS "dashboard_ui.c"
|
||||||
REQUIRES lvgl__lvgl ws_client
|
REQUIRES lvgl__lvgl ws_client user_fonts
|
||||||
INCLUDE_DIRS "./")
|
INCLUDE_DIRS "./")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "dashboard_ui.h"
|
#include "dashboard_ui.h"
|
||||||
|
#include "user_fonts.h"
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
@@ -6,18 +7,26 @@
|
|||||||
#define SCREEN_W 400
|
#define SCREEN_W 400
|
||||||
#define SCREEN_H 300
|
#define SCREEN_H 300
|
||||||
#define TOP_BAR_H 24
|
#define TOP_BAR_H 24
|
||||||
#define STATS_H 80
|
#define TIME_BAR_H 50
|
||||||
#define MID_H 140
|
#define BOT_H 24
|
||||||
#define BOT_H 32
|
#define MAIN_Y (TOP_BAR_H + TIME_BAR_H) /* 74 */
|
||||||
|
#define MAIN_H (SCREEN_H - MAIN_Y - BOT_H) /* 202 */
|
||||||
|
#define LEFT_COL_W 200
|
||||||
|
#define RIGHT_COL_X 200
|
||||||
|
|
||||||
/* ---------- Static widget handles ---------- */
|
/* ---------- Static widget handles ---------- */
|
||||||
|
|
||||||
/* Top bar */
|
/* Top bar */
|
||||||
static lv_obj_t *lbl_ip;
|
static lv_obj_t *lbl_ip;
|
||||||
static lv_obj_t *lbl_batt;
|
static lv_obj_t *lbl_batt;
|
||||||
static lv_obj_t *lbl_time;
|
static lv_obj_t *lbl_batt_v;
|
||||||
|
static lv_obj_t *bar_batt_top;
|
||||||
static lv_obj_t *lbl_ws;
|
static lv_obj_t *lbl_ws;
|
||||||
|
|
||||||
|
/* Time bar */
|
||||||
|
static lv_obj_t *lbl_clock;
|
||||||
|
static lv_obj_t *lbl_date;
|
||||||
|
|
||||||
/* Pi stats bars + labels */
|
/* Pi stats bars + labels */
|
||||||
static lv_obj_t *bar_cpu;
|
static lv_obj_t *bar_cpu;
|
||||||
static lv_obj_t *lbl_cpu_val;
|
static lv_obj_t *lbl_cpu_val;
|
||||||
@@ -29,19 +38,28 @@ static lv_obj_t *lbl_cpu_temp;
|
|||||||
|
|
||||||
/* Services table */
|
/* Services table */
|
||||||
static lv_obj_t *tbl_services;
|
static lv_obj_t *tbl_services;
|
||||||
|
static int s_service_count;
|
||||||
|
static int s_dup_rows; /* duplicate rows appended for looping */
|
||||||
|
static lv_coord_t s_row_h; /* measured row height in px */
|
||||||
|
|
||||||
/* Local sensors */
|
/* Local sensors (bottom bar) */
|
||||||
static lv_obj_t *lbl_room_temp;
|
static lv_obj_t *lbl_local;
|
||||||
static lv_obj_t *lbl_room_humi;
|
|
||||||
static lv_obj_t *lbl_local_batt;
|
|
||||||
static lv_obj_t *lbl_uptime;
|
static lv_obj_t *lbl_uptime;
|
||||||
|
|
||||||
|
/* Status image placeholder */
|
||||||
|
static lv_obj_t *img_status;
|
||||||
|
|
||||||
/* Network */
|
/* Network */
|
||||||
static lv_obj_t *lbl_net;
|
static lv_obj_t *lbl_net;
|
||||||
|
|
||||||
/* ---------- Style ---------- */
|
/* Auto-scroll timer */
|
||||||
|
static lv_timer_t *s_scroll_timer;
|
||||||
|
|
||||||
|
/* ---------- Styles ---------- */
|
||||||
static lv_style_t style_bar_bg;
|
static lv_style_t style_bar_bg;
|
||||||
static lv_style_t style_bar_ind;
|
static lv_style_t style_bar_ind;
|
||||||
|
static lv_style_t style_bar_batt_bg;
|
||||||
|
static lv_style_t style_bar_batt_ind;
|
||||||
|
|
||||||
static void init_styles(void)
|
static void init_styles(void)
|
||||||
{
|
{
|
||||||
@@ -58,6 +76,19 @@ static void init_styles(void)
|
|||||||
lv_style_set_bg_color(&style_bar_ind, lv_color_black());
|
lv_style_set_bg_color(&style_bar_ind, lv_color_black());
|
||||||
lv_style_set_bg_opa(&style_bar_ind, LV_OPA_COVER);
|
lv_style_set_bg_opa(&style_bar_ind, LV_OPA_COVER);
|
||||||
lv_style_set_radius(&style_bar_ind, 0);
|
lv_style_set_radius(&style_bar_ind, 0);
|
||||||
|
|
||||||
|
/* Battery bar on black top bar: inverted colors */
|
||||||
|
lv_style_init(&style_bar_batt_bg);
|
||||||
|
lv_style_set_bg_color(&style_bar_batt_bg, lv_color_black());
|
||||||
|
lv_style_set_bg_opa(&style_bar_batt_bg, LV_OPA_COVER);
|
||||||
|
lv_style_set_border_color(&style_bar_batt_bg, lv_color_white());
|
||||||
|
lv_style_set_border_width(&style_bar_batt_bg, 1);
|
||||||
|
lv_style_set_radius(&style_bar_batt_bg, 0);
|
||||||
|
|
||||||
|
lv_style_init(&style_bar_batt_ind);
|
||||||
|
lv_style_set_bg_color(&style_bar_batt_ind, lv_color_white());
|
||||||
|
lv_style_set_bg_opa(&style_bar_batt_ind, LV_OPA_COVER);
|
||||||
|
lv_style_set_radius(&style_bar_batt_ind, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static lv_obj_t *create_bar(lv_obj_t *parent, int x, int y, int w, int h)
|
static lv_obj_t *create_bar(lv_obj_t *parent, int x, int y, int w, int h)
|
||||||
@@ -82,7 +113,62 @@ static lv_obj_t *create_label(lv_obj_t *parent, int x, int y, const lv_font_t *f
|
|||||||
return lbl;
|
return lbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Create UI ---------- */
|
/* ---------- Auto-scroll callback ---------- */
|
||||||
|
|
||||||
|
static void scroll_timer_cb(lv_timer_t *timer)
|
||||||
|
{
|
||||||
|
(void)timer;
|
||||||
|
if (s_dup_rows <= 0 || s_row_h <= 0) {
|
||||||
|
/* Too few services or not yet measured — no scrolling */
|
||||||
|
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_OFF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_coord_t cur_y = lv_obj_get_scroll_y(tbl_services);
|
||||||
|
lv_coord_t wrap_y = s_service_count * s_row_h;
|
||||||
|
|
||||||
|
if (cur_y >= wrap_y) {
|
||||||
|
lv_obj_scroll_to_y(tbl_services, 0, LV_ANIM_OFF);
|
||||||
|
cur_y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// lv_obj_scroll_to_y(tbl_services, cur_y + s_row_h, LV_ANIM_OFF);
|
||||||
|
const int delta_y = s_row_h / 8;
|
||||||
|
lv_obj_scroll_to_y(tbl_services, cur_y + delta_y, LV_ANIM_ON);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Measure row height, compute visible rows, append duplicates for seamless loop */
|
||||||
|
static void fill_duplicate_rows(int count)
|
||||||
|
{
|
||||||
|
s_dup_rows = 0;
|
||||||
|
s_row_h = 0;
|
||||||
|
if (count <= 0) return;
|
||||||
|
|
||||||
|
/* Trim table to exactly count rows so measurement is clean */
|
||||||
|
lv_table_set_row_cnt(tbl_services, count);
|
||||||
|
lv_obj_update_layout(tbl_services);
|
||||||
|
|
||||||
|
lv_coord_t content_h = lv_obj_get_self_height(tbl_services);
|
||||||
|
lv_coord_t row_h = content_h / count;
|
||||||
|
if (row_h <= 0) return;
|
||||||
|
|
||||||
|
lv_coord_t visible_h = lv_obj_get_content_height(tbl_services);
|
||||||
|
int visible_rows = (visible_h + row_h - 1) / row_h; /* ceil */
|
||||||
|
|
||||||
|
if (count <= visible_rows) return; /* everything fits — no scrolling */
|
||||||
|
|
||||||
|
for (int i = 0; i < visible_rows; i++) {
|
||||||
|
lv_table_set_cell_value(tbl_services, count + i, 0,
|
||||||
|
lv_table_get_cell_value(tbl_services, i, 0));
|
||||||
|
lv_table_set_cell_value(tbl_services, count + i, 1,
|
||||||
|
lv_table_get_cell_value(tbl_services, i, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
s_dup_rows = visible_rows;
|
||||||
|
s_row_h = row_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Create UI sections ---------- */
|
||||||
|
|
||||||
static void create_top_bar(lv_obj_t *parent)
|
static void create_top_bar(lv_obj_t *parent)
|
||||||
{
|
{
|
||||||
@@ -96,97 +182,153 @@ static void create_top_bar(lv_obj_t *parent)
|
|||||||
lv_obj_set_style_pad_all(bar_cont, 2, 0);
|
lv_obj_set_style_pad_all(bar_cont, 2, 0);
|
||||||
lv_obj_clear_flag(bar_cont, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_clear_flag(bar_cont, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
|
/* IP address — left */
|
||||||
lbl_ip = lv_label_create(bar_cont);
|
lbl_ip = lv_label_create(bar_cont);
|
||||||
lv_obj_set_style_text_color(lbl_ip, lv_color_white(), 0);
|
lv_obj_set_style_text_color(lbl_ip, lv_color_white(), 0);
|
||||||
lv_obj_set_style_text_font(lbl_ip, &lv_font_montserrat_12, 0);
|
lv_obj_set_style_text_font(lbl_ip, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
lv_obj_align(lbl_ip, LV_ALIGN_LEFT_MID, 2, 0);
|
lv_obj_align(lbl_ip, LV_ALIGN_LEFT_MID, 2, 0);
|
||||||
lv_label_set_text(lbl_ip, "N/A");
|
lv_label_set_text(lbl_ip, "N/A");
|
||||||
|
|
||||||
lbl_batt = lv_label_create(bar_cont);
|
/* WS status — center-left */
|
||||||
lv_obj_set_style_text_color(lbl_batt, lv_color_white(), 0);
|
|
||||||
lv_obj_set_style_text_font(lbl_batt, &lv_font_montserrat_12, 0);
|
|
||||||
lv_obj_align(lbl_batt, LV_ALIGN_LEFT_MID, 120, 0);
|
|
||||||
lv_label_set_text(lbl_batt, "Batt:--%");
|
|
||||||
|
|
||||||
lbl_time = lv_label_create(bar_cont);
|
|
||||||
lv_obj_set_style_text_color(lbl_time, lv_color_white(), 0);
|
|
||||||
lv_obj_set_style_text_font(lbl_time, &lv_font_montserrat_12, 0);
|
|
||||||
lv_obj_align(lbl_time, LV_ALIGN_LEFT_MID, 220, 0);
|
|
||||||
lv_label_set_text(lbl_time, "--:--");
|
|
||||||
|
|
||||||
lbl_ws = lv_label_create(bar_cont);
|
lbl_ws = lv_label_create(bar_cont);
|
||||||
lv_obj_set_style_text_color(lbl_ws, lv_color_white(), 0);
|
lv_obj_set_style_text_color(lbl_ws, lv_color_white(), 0);
|
||||||
lv_obj_set_style_text_font(lbl_ws, &lv_font_montserrat_12, 0);
|
lv_obj_set_style_text_font(lbl_ws, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
lv_obj_align(lbl_ws, LV_ALIGN_RIGHT_MID, -2, 0);
|
lv_obj_align(lbl_ws, LV_ALIGN_LEFT_MID, 140, 0);
|
||||||
lv_label_set_text(lbl_ws, "WS:---");
|
lv_label_set_text(lbl_ws, "WS:---");
|
||||||
|
|
||||||
|
/* Battery voltage — left of bar */
|
||||||
|
lbl_batt_v = lv_label_create(bar_cont);
|
||||||
|
lv_obj_set_style_text_color(lbl_batt_v, lv_color_white(), 0);
|
||||||
|
lv_obj_set_style_text_font(lbl_batt_v, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
|
lv_obj_align(lbl_batt_v, LV_ALIGN_RIGHT_MID, -72, 0);
|
||||||
|
lv_label_set_text(lbl_batt_v, "-.--V");
|
||||||
|
|
||||||
|
/* Battery bar (24x10) — right */
|
||||||
|
bar_batt_top = lv_bar_create(bar_cont);
|
||||||
|
lv_obj_set_size(bar_batt_top, 24, 10);
|
||||||
|
lv_obj_align(bar_batt_top, LV_ALIGN_RIGHT_MID, -40, 0);
|
||||||
|
lv_bar_set_range(bar_batt_top, 0, 100);
|
||||||
|
lv_bar_set_value(bar_batt_top, 0, LV_ANIM_OFF);
|
||||||
|
lv_obj_add_style(bar_batt_top, &style_bar_batt_bg, LV_PART_MAIN);
|
||||||
|
lv_obj_add_style(bar_batt_top, &style_bar_batt_ind, LV_PART_INDICATOR);
|
||||||
|
|
||||||
|
/* Battery positive terminal nub */
|
||||||
|
lv_obj_t *batt_nub = lv_obj_create(bar_cont);
|
||||||
|
lv_obj_set_size(batt_nub, 2, 4);
|
||||||
|
lv_obj_align(batt_nub, LV_ALIGN_RIGHT_MID, -38, 0);
|
||||||
|
lv_obj_set_style_bg_color(batt_nub, lv_color_white(), 0);
|
||||||
|
lv_obj_set_style_bg_opa(batt_nub, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_width(batt_nub, 0, 0);
|
||||||
|
lv_obj_set_style_radius(batt_nub, 0, 0);
|
||||||
|
lv_obj_set_style_pad_all(batt_nub, 0, 0);
|
||||||
|
lv_obj_clear_flag(batt_nub, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
|
/* Battery text — right of bar */
|
||||||
|
lbl_batt = lv_label_create(bar_cont);
|
||||||
|
lv_obj_set_style_text_color(lbl_batt, lv_color_white(), 0);
|
||||||
|
lv_obj_set_style_text_font(lbl_batt, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
|
lv_obj_align(lbl_batt, LV_ALIGN_RIGHT_MID, -2, 0);
|
||||||
|
lv_label_set_text(lbl_batt, "--%");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_stats_section(lv_obj_t *parent)
|
static void create_time_bar(lv_obj_t *parent)
|
||||||
{
|
{
|
||||||
int y0 = TOP_BAR_H + 2;
|
lv_obj_t *bar_cont = lv_obj_create(parent);
|
||||||
int col_w = 80;
|
lv_obj_set_pos(bar_cont, 0, TOP_BAR_H);
|
||||||
int bar_w = 50;
|
lv_obj_set_size(bar_cont, SCREEN_W, TIME_BAR_H);
|
||||||
int bar_h = 40;
|
lv_obj_set_style_bg_color(bar_cont, lv_color_white(), 0);
|
||||||
|
lv_obj_set_style_bg_opa(bar_cont, LV_OPA_COVER, 0);
|
||||||
|
lv_obj_set_style_border_color(bar_cont, lv_color_black(), 0);
|
||||||
|
lv_obj_set_style_border_width(bar_cont, 1, 0);
|
||||||
|
lv_obj_set_style_border_side(bar_cont, LV_BORDER_SIDE_BOTTOM, 0);
|
||||||
|
lv_obj_set_style_radius(bar_cont, 0, 0);
|
||||||
|
lv_obj_set_style_pad_all(bar_cont, 0, 0);
|
||||||
|
lv_obj_clear_flag(bar_cont, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
/* Section header */
|
/* Clock HH:MM:SS — left */
|
||||||
create_label(parent, 4, y0, &lv_font_montserrat_12, "PI SERVER STATS");
|
lbl_clock = lv_label_create(bar_cont);
|
||||||
|
lv_obj_set_style_text_font(lbl_clock, &DSEG14C_BI_32px, 0);
|
||||||
|
lv_obj_set_style_text_color(lbl_clock, lv_color_black(), 0);
|
||||||
|
lv_obj_align(lbl_clock, LV_ALIGN_LEFT_MID, 10, 0);
|
||||||
|
lv_label_set_text(lbl_clock, "--:--:--");
|
||||||
|
|
||||||
y0 += 14;
|
/* Date YYYY/MM/DD DAY — right */
|
||||||
|
lbl_date = lv_label_create(bar_cont);
|
||||||
/* CPU */
|
lv_obj_set_style_text_font(lbl_date, &InziuIosevka_Slab_CC_20px, 0);
|
||||||
create_label(parent, 10, y0, &lv_font_montserrat_12, "CPU");
|
lv_obj_set_style_text_color(lbl_date, lv_color_black(), 0);
|
||||||
bar_cpu = create_bar(parent, 10, y0 + 14, bar_w, bar_h);
|
lv_obj_align(lbl_date, LV_ALIGN_RIGHT_MID, -20, 0);
|
||||||
lbl_cpu_val = create_label(parent, 10, y0 + 56, &lv_font_montserrat_14, "--%");
|
lv_label_set_text(lbl_date, "----/--/-- ---");
|
||||||
|
|
||||||
/* RAM */
|
|
||||||
create_label(parent, 10 + col_w, y0, &lv_font_montserrat_12, "RAM");
|
|
||||||
bar_ram = create_bar(parent, 10 + col_w, y0 + 14, bar_w, bar_h);
|
|
||||||
lbl_ram_val = create_label(parent, 10 + col_w, y0 + 56, &lv_font_montserrat_14, "--%");
|
|
||||||
|
|
||||||
/* DISK */
|
|
||||||
create_label(parent, 10 + col_w * 2, y0, &lv_font_montserrat_12, "DISK");
|
|
||||||
bar_disk = create_bar(parent, 10 + col_w * 2, y0 + 14, bar_w, bar_h);
|
|
||||||
lbl_disk_val = create_label(parent, 10 + col_w * 2, y0 + 56, &lv_font_montserrat_14, "--%");
|
|
||||||
|
|
||||||
/* CPU TEMP - no bar, just big value */
|
|
||||||
create_label(parent, 10 + col_w * 3, y0, &lv_font_montserrat_12, "TEMP");
|
|
||||||
lbl_cpu_temp = create_label(parent, 10 + col_w * 3, y0 + 24, &lv_font_montserrat_20, "--C");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_mid_section(lv_obj_t *parent)
|
static void create_main_section(lv_obj_t *parent)
|
||||||
{
|
{
|
||||||
int y0 = TOP_BAR_H + STATS_H + 4;
|
/* === Left column: Services + Pi Vitals === */
|
||||||
|
create_label(parent, 4, MAIN_Y + 1, &InziuIosevka_Slab_CC_12px, "SERVICES");
|
||||||
/* --- Left column: Services --- */
|
|
||||||
create_label(parent, 4, y0, &lv_font_montserrat_12, "SERVICES");
|
|
||||||
|
|
||||||
tbl_services = lv_table_create(parent);
|
tbl_services = lv_table_create(parent);
|
||||||
lv_obj_set_pos(tbl_services, 4, y0 + 14);
|
lv_obj_set_pos(tbl_services, 4, MAIN_Y + 16);
|
||||||
lv_obj_set_size(tbl_services, 195, 110);
|
lv_obj_set_size(tbl_services, 190, 82);
|
||||||
lv_table_set_col_cnt(tbl_services, 2);
|
lv_table_set_col_cnt(tbl_services, 2);
|
||||||
lv_table_set_col_width(tbl_services, 0, 110);
|
lv_table_set_col_width(tbl_services, 0, 110);
|
||||||
lv_table_set_col_width(tbl_services, 1, 70);
|
lv_table_set_col_width(tbl_services, 1, 65);
|
||||||
lv_obj_set_style_text_font(tbl_services, &lv_font_montserrat_12, 0);
|
lv_obj_set_style_text_font(tbl_services, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
lv_obj_set_style_border_color(tbl_services, lv_color_black(), 0);
|
lv_obj_set_style_border_color(tbl_services, lv_color_black(), 0);
|
||||||
lv_obj_set_style_border_width(tbl_services, 1, 0);
|
lv_obj_set_style_border_width(tbl_services, 1, 0);
|
||||||
lv_obj_set_style_pad_ver(tbl_services, 2, LV_PART_ITEMS);
|
lv_obj_set_style_pad_ver(tbl_services, 2, LV_PART_ITEMS);
|
||||||
lv_obj_set_style_pad_hor(tbl_services, 4, LV_PART_ITEMS);
|
lv_obj_set_style_pad_hor(tbl_services, 4, LV_PART_ITEMS);
|
||||||
lv_obj_clear_flag(tbl_services, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_add_flag(tbl_services, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
/* Pre-fill with empty rows */
|
|
||||||
for (int i = 0; i < WS_MAX_SERVICES; i++) {
|
for (int i = 0; i < WS_MAX_SERVICES; i++) {
|
||||||
lv_table_set_cell_value(tbl_services, i, 0, "---");
|
lv_table_set_cell_value(tbl_services, i, 0, "---");
|
||||||
lv_table_set_cell_value(tbl_services, i, 1, "---");
|
lv_table_set_cell_value(tbl_services, i, 1, "---");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Right column: Local sensors --- */
|
/* Timer for each shift of 1/8 line's height */
|
||||||
int rx = 205;
|
s_scroll_timer = lv_timer_create(scroll_timer_cb, 120, NULL);
|
||||||
create_label(parent, rx, y0, &lv_font_montserrat_12, "LOCAL SENSORS");
|
|
||||||
|
|
||||||
lbl_room_temp = create_label(parent, rx, y0 + 18, &lv_font_montserrat_14, "Room: --.-C");
|
/* === Left column: Pi Vitals (below services) === */
|
||||||
lbl_room_humi = create_label(parent, rx, y0 + 38, &lv_font_montserrat_14, "Humi: --%");
|
int rx = 0;
|
||||||
lbl_local_batt = create_label(parent, rx, y0 + 58, &lv_font_montserrat_14, "Batt: --%");
|
int row_h = 18; /* vertical spacing per stat row */
|
||||||
lbl_uptime = create_label(parent, rx, y0 + 78, &lv_font_montserrat_14, "Uptime: --h");
|
int lbl_w = 36; /* width reserved for "CPU " etc */
|
||||||
|
int bar_w = 82;
|
||||||
|
int bar_h = 12;
|
||||||
|
int val_x = rx + 4 + lbl_w + bar_w + 4; /* value label after bar */
|
||||||
|
int temp_x = rx + 155; /* TEMP column, right of value labels */
|
||||||
|
|
||||||
|
/* Pi Vitals header */
|
||||||
|
create_label(parent, rx + 4, 175, &InziuIosevka_Slab_CC_12px, "PI VITALS");
|
||||||
|
|
||||||
|
int ry = 192;
|
||||||
|
|
||||||
|
/* CPU [========] 12% TEMP */
|
||||||
|
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "CPU");
|
||||||
|
bar_cpu = create_bar(parent, rx + 4 + lbl_w, ry, bar_w, bar_h);
|
||||||
|
lbl_cpu_val = create_label(parent, val_x, ry, &InziuIosevka_Slab_CC_12px, "--%");
|
||||||
|
create_label(parent, temp_x, ry, &InziuIosevka_Slab_CC_12px, "TEMP");
|
||||||
|
|
||||||
|
/* RAM [========] 58% 45 C */
|
||||||
|
ry += row_h;
|
||||||
|
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "RAM");
|
||||||
|
bar_ram = create_bar(parent, rx + 4 + lbl_w, ry, bar_w, bar_h);
|
||||||
|
lbl_ram_val = create_label(parent, val_x, ry, &InziuIosevka_Slab_CC_12px, "--%");
|
||||||
|
lbl_cpu_temp = create_label(parent, temp_x, ry, &InziuIosevka_Slab_CC_20px, "-- C");
|
||||||
|
|
||||||
|
/* DISK [========] 44% */
|
||||||
|
ry += row_h;
|
||||||
|
create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "DISK");
|
||||||
|
bar_disk = create_bar(parent, rx + 4 + lbl_w, ry, bar_w, bar_h);
|
||||||
|
lbl_disk_val = create_label(parent, val_x, ry, &InziuIosevka_Slab_CC_12px, "--%");
|
||||||
|
|
||||||
|
/* uptime */
|
||||||
|
ry += row_h;
|
||||||
|
lbl_uptime = create_label(parent, rx + 4, ry, &InziuIosevka_Slab_CC_12px, "Uptime: --h");
|
||||||
|
|
||||||
|
/* === Right column: Status image (200x200) === */
|
||||||
|
img_status = lv_img_create(parent);
|
||||||
|
lv_obj_set_pos(img_status, 200, MAIN_Y + 1);
|
||||||
|
lv_obj_set_size(img_status, 200, 200);
|
||||||
|
lv_obj_set_style_bg_color(img_status, lv_color_white(), 0);
|
||||||
|
lv_obj_set_style_bg_opa(img_status, LV_OPA_COVER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_bottom_bar(lv_obj_t *parent)
|
static void create_bottom_bar(lv_obj_t *parent)
|
||||||
@@ -196,7 +338,7 @@ static void create_bottom_bar(lv_obj_t *parent)
|
|||||||
lv_obj_t *bot_cont = lv_obj_create(parent);
|
lv_obj_t *bot_cont = lv_obj_create(parent);
|
||||||
lv_obj_set_pos(bot_cont, 0, y0);
|
lv_obj_set_pos(bot_cont, 0, y0);
|
||||||
lv_obj_set_size(bot_cont, SCREEN_W, BOT_H);
|
lv_obj_set_size(bot_cont, SCREEN_W, BOT_H);
|
||||||
lv_obj_set_style_bg_color(bot_cont, lv_color_white(), 0);
|
lv_obj_set_style_bg_color(bot_cont, lv_color_black(), 0);
|
||||||
lv_obj_set_style_bg_opa(bot_cont, LV_OPA_COVER, 0);
|
lv_obj_set_style_bg_opa(bot_cont, LV_OPA_COVER, 0);
|
||||||
lv_obj_set_style_border_color(bot_cont, lv_color_black(), 0);
|
lv_obj_set_style_border_color(bot_cont, lv_color_black(), 0);
|
||||||
lv_obj_set_style_border_width(bot_cont, 1, 0);
|
lv_obj_set_style_border_width(bot_cont, 1, 0);
|
||||||
@@ -206,10 +348,17 @@ static void create_bottom_bar(lv_obj_t *parent)
|
|||||||
lv_obj_clear_flag(bot_cont, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_clear_flag(bot_cont, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
lbl_net = lv_label_create(bot_cont);
|
lbl_net = lv_label_create(bot_cont);
|
||||||
lv_obj_set_style_text_font(lbl_net, &lv_font_montserrat_12, 0);
|
lv_obj_set_style_text_font(lbl_net, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
lv_obj_set_style_text_color(lbl_net, lv_color_black(), 0);
|
lv_obj_set_style_text_color(lbl_net, lv_color_white(), 0);
|
||||||
lv_obj_align(lbl_net, LV_ALIGN_LEFT_MID, 0, 0);
|
lv_obj_align(lbl_net, LV_ALIGN_LEFT_MID, 0, 0);
|
||||||
lv_label_set_text(lbl_net, "NETWORK RX: ---- kbps TX: ---- kbps");
|
lv_label_set_text(lbl_net, "NETWORK DOWN: ---- kBps / UP: ---- kBps");
|
||||||
|
|
||||||
|
/* Local sensor readings — right-aligned */
|
||||||
|
lbl_local = lv_label_create(bot_cont);
|
||||||
|
lv_obj_set_style_text_font(lbl_local, &InziuIosevka_Slab_CC_12px, 0);
|
||||||
|
lv_obj_set_style_text_color(lbl_local, lv_color_white(), 0);
|
||||||
|
lv_obj_align(lbl_local, LV_ALIGN_RIGHT_MID, 0, 0);
|
||||||
|
lv_label_set_text(lbl_local, "T: --.- H: --%");
|
||||||
}
|
}
|
||||||
|
|
||||||
void dashboard_ui_create(void)
|
void dashboard_ui_create(void)
|
||||||
@@ -222,8 +371,8 @@ void dashboard_ui_create(void)
|
|||||||
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
lv_obj_clear_flag(scr, LV_OBJ_FLAG_SCROLLABLE);
|
||||||
|
|
||||||
create_top_bar(scr);
|
create_top_bar(scr);
|
||||||
create_stats_section(scr);
|
create_time_bar(scr);
|
||||||
create_mid_section(scr);
|
create_main_section(scr);
|
||||||
create_bottom_bar(scr);
|
create_bottom_bar(scr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,20 +398,23 @@ void dashboard_ui_update_stats(const pi_stats_t *stats)
|
|||||||
lv_label_set_text(lbl_disk_val, buf);
|
lv_label_set_text(lbl_disk_val, buf);
|
||||||
|
|
||||||
/* CPU temp */
|
/* CPU temp */
|
||||||
snprintf(buf, sizeof(buf), "%.0fC", stats->cpu_temp);
|
snprintf(buf, sizeof(buf), "%.0f C", stats->cpu_temp);
|
||||||
lv_label_set_text(lbl_cpu_temp, buf);
|
lv_label_set_text(lbl_cpu_temp, buf);
|
||||||
|
|
||||||
/* Services table */
|
/* Services table */
|
||||||
|
s_service_count = stats->service_count;
|
||||||
for (int i = 0; i < stats->service_count && i < WS_MAX_SERVICES; i++) {
|
for (int i = 0; i < stats->service_count && i < WS_MAX_SERVICES; i++) {
|
||||||
|
const char *tag;
|
||||||
|
switch (stats->services[i].status) {
|
||||||
|
case SVC_RUNNING: tag = "[RUN]"; break;
|
||||||
|
case SVC_WARNING: tag = "[WARN]"; break;
|
||||||
|
default: tag = "[STOP]"; break;
|
||||||
|
}
|
||||||
lv_table_set_cell_value(tbl_services, i, 0, stats->services[i].name);
|
lv_table_set_cell_value(tbl_services, i, 0, stats->services[i].name);
|
||||||
lv_table_set_cell_value(tbl_services, i, 1,
|
lv_table_set_cell_value(tbl_services, i, 1, tag);
|
||||||
stats->services[i].running ? "[RUN]" : "[STOP]");
|
|
||||||
}
|
|
||||||
/* Clear unused rows */
|
|
||||||
for (int i = stats->service_count; i < WS_MAX_SERVICES; i++) {
|
|
||||||
lv_table_set_cell_value(tbl_services, i, 0, "");
|
|
||||||
lv_table_set_cell_value(tbl_services, i, 1, "");
|
|
||||||
}
|
}
|
||||||
|
/* Measure row height, compute visible rows, append duplicates */
|
||||||
|
fill_duplicate_rows(stats->service_count);
|
||||||
|
|
||||||
/* Uptime */
|
/* Uptime */
|
||||||
snprintf(buf, sizeof(buf), "Uptime: %.0fh", stats->uptime_hrs);
|
snprintf(buf, sizeof(buf), "Uptime: %.0fh", stats->uptime_hrs);
|
||||||
@@ -270,34 +422,39 @@ void dashboard_ui_update_stats(const pi_stats_t *stats)
|
|||||||
|
|
||||||
/* Network */
|
/* Network */
|
||||||
char net_buf[64];
|
char net_buf[64];
|
||||||
snprintf(net_buf, sizeof(net_buf), "NETWORK RX: %.0f kbps TX: %.0f kbps",
|
snprintf(net_buf, sizeof(net_buf), "NETWORK DOWN: %.0f kBps / UP: %.0f kBps",
|
||||||
stats->net_rx_kbps, stats->net_tx_kbps);
|
stats->net_rx_kbps, stats->net_tx_kbps);
|
||||||
lv_label_set_text(lbl_net, net_buf);
|
lv_label_set_text(lbl_net, net_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dashboard_ui_update_local(float temp, float humidity, uint8_t battery)
|
void dashboard_ui_update_local(float temp, float humidity, uint8_t battery, bool charging, float voltage)
|
||||||
{
|
{
|
||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "Room: %.1fC", temp);
|
snprintf(buf, sizeof(buf), "T: %.1f H: %.0f%%", temp, humidity);
|
||||||
lv_label_set_text(lbl_room_temp, buf);
|
lv_label_set_text(lbl_local, buf);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "Humi: %.0f%%", humidity);
|
/* Update top bar battery voltage */
|
||||||
lv_label_set_text(lbl_room_humi, buf);
|
snprintf(buf, sizeof(buf), "%.2fV", voltage);
|
||||||
|
lv_label_set_text(lbl_batt_v, buf);
|
||||||
|
|
||||||
snprintf(buf, sizeof(buf), "Batt: %d%%", battery);
|
/* Update top bar battery text + bar */
|
||||||
lv_label_set_text(lbl_local_batt, buf);
|
snprintf(buf, sizeof(buf), charging ? "%d%%C" : "%d%%", battery);
|
||||||
|
|
||||||
/* Also update top bar battery */
|
|
||||||
snprintf(buf, sizeof(buf), "Batt:%d%%", battery);
|
|
||||||
lv_label_set_text(lbl_batt, buf);
|
lv_label_set_text(lbl_batt, buf);
|
||||||
|
lv_bar_set_value(bar_batt_top, battery, LV_ANIM_OFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dashboard_ui_update_time(int h, int m, int s)
|
void dashboard_ui_update_time(int h, int m, int s, int year, int month, int day, int weekday)
|
||||||
{
|
{
|
||||||
char buf[16];
|
static const char *day_names[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||||
snprintf(buf, sizeof(buf), "%02d:%02d", h, m);
|
|
||||||
lv_label_set_text(lbl_time, buf);
|
char buf[32];
|
||||||
|
snprintf(buf, sizeof(buf), "%02d:%02d:%02d", h, m, s);
|
||||||
|
lv_label_set_text(lbl_clock, buf);
|
||||||
|
|
||||||
|
const char *wd = (weekday >= 0 && weekday <= 6) ? day_names[weekday] : "---";
|
||||||
|
snprintf(buf, sizeof(buf), "%04d/%02d/%02d %s", year, month, day, wd);
|
||||||
|
lv_label_set_text(lbl_date, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void dashboard_ui_update_connection(ws_state_t ws_state, bool wifi_connected, const char *ip_str)
|
void dashboard_ui_update_connection(ws_state_t ws_state, bool wifi_connected, const char *ip_str)
|
||||||
@@ -316,3 +473,10 @@ void dashboard_ui_update_connection(ws_state_t ws_state, bool wifi_connected, co
|
|||||||
}
|
}
|
||||||
lv_label_set_text(lbl_ws, ws_str);
|
lv_label_set_text(lbl_ws, ws_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dashboard_ui_update_status_image(const lv_img_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
if (dsc) {
|
||||||
|
lv_img_set_src(img_status, dsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,12 +21,13 @@ void dashboard_ui_update_stats(const pi_stats_t *stats);
|
|||||||
/**
|
/**
|
||||||
* Update local sensor readings. LVGL lock must be held by caller.
|
* Update local sensor readings. LVGL lock must be held by caller.
|
||||||
*/
|
*/
|
||||||
void dashboard_ui_update_local(float temp, float humidity, uint8_t battery);
|
void dashboard_ui_update_local(float temp, float humidity, uint8_t battery, bool charging, float voltage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update time display. LVGL lock must be held by caller.
|
* Update clock and date display. LVGL lock must be held by caller.
|
||||||
|
* weekday: 0=Sun, 1=Mon, ..., 6=Sat
|
||||||
*/
|
*/
|
||||||
void dashboard_ui_update_time(int h, int m, int s);
|
void dashboard_ui_update_time(int h, int m, int s, int year, int month, int day, int weekday);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update connection status indicators. LVGL lock must be held by caller.
|
* Update connection status indicators. LVGL lock must be held by caller.
|
||||||
@@ -34,6 +35,12 @@ void dashboard_ui_update_time(int h, int m, int s);
|
|||||||
*/
|
*/
|
||||||
void dashboard_ui_update_connection(ws_state_t ws_state, bool wifi_connected, const char *ip_str);
|
void dashboard_ui_update_connection(ws_state_t ws_state, bool wifi_connected, const char *ip_str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the status image widget. LVGL lock must be held by caller.
|
||||||
|
* @param dsc Image descriptor (1-bit monochrome, 120x120)
|
||||||
|
*/
|
||||||
|
void dashboard_ui_update_status_image(const lv_img_dsc_t *dsc);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,5 +4,6 @@
|
|||||||
#define WIFI_SSID "Novoyuuparosk_H3C"
|
#define WIFI_SSID "Novoyuuparosk_H3C"
|
||||||
#define WIFI_PASSWORD "northwich"
|
#define WIFI_PASSWORD "northwich"
|
||||||
#define WS_SERVER_URI "ws://192.168.2.199:8765"
|
#define WS_SERVER_URI "ws://192.168.2.199:8765"
|
||||||
|
#define AUDIO_SERVER_URI "ws://192.168.2.199:8766"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -48,3 +48,7 @@ uint8_t Adc_GetBatteryLevel() {
|
|||||||
//ESP_LOGW("Battery","Voltage: %.3f V, Level: %.1f %%",vol,level);
|
//ESP_LOGW("Battery","Voltage: %.3f V, Level: %.1f %%",vol,level);
|
||||||
return (uint8_t)level;
|
return (uint8_t)level;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Adc_IsCharging() {
|
||||||
|
return Adc_GetBatteryVoltage() > 4.10f;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,3 +5,4 @@
|
|||||||
void Adc_PortInit();
|
void Adc_PortInit();
|
||||||
float Adc_GetBatteryVoltage();
|
float Adc_GetBatteryVoltage();
|
||||||
uint8_t Adc_GetBatteryLevel();
|
uint8_t Adc_GetBatteryLevel();
|
||||||
|
bool Adc_IsCharging();
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
idf_component_register(
|
idf_component_register(
|
||||||
SRCS "user_app.cpp" "alert.cpp"
|
SRCS "user_app.cpp" "alert.cpp"
|
||||||
REQUIRES app_bsp
|
REQUIRES app_bsp
|
||||||
PRIV_REQUIRES esp_wifi_bsp ws_client dashboard_ui port_bsp esp_timer codec_board
|
PRIV_REQUIRES esp_wifi_bsp ws_client dashboard_ui port_bsp esp_timer codec_board audio_client
|
||||||
INCLUDE_DIRS "./")
|
INCLUDE_DIRS "./")
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
|
#include "audio_client.h"
|
||||||
#include "codec_bsp.h"
|
#include "codec_bsp.h"
|
||||||
#include "i2c_bsp.h"
|
#include "i2c_bsp.h"
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
@@ -42,7 +43,7 @@ static const int s_beep_count[ALERT_TYPE_COUNT] = {
|
|||||||
0, /* SERVICE_DOWN: triple beep */ /* DISABLED FOR TESTING */
|
0, /* SERVICE_DOWN: triple beep */ /* DISABLED FOR TESTING */
|
||||||
2, /* HIGH_TEMP: double beep */
|
2, /* HIGH_TEMP: double beep */
|
||||||
0, /* WS_DISCONNECT: single beep */ /* DISABLED FOR TESTING */
|
0, /* WS_DISCONNECT: single beep */ /* DISABLED FOR TESTING */
|
||||||
1, /* CONNECT_OK: single short beep */
|
0, /* CONNECT_OK: single short beep */ /* DISABLED FOR TESTING */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void generate_tone_buffer(void)
|
static void generate_tone_buffer(void)
|
||||||
@@ -84,6 +85,7 @@ void alert_trigger(alert_type_t type)
|
|||||||
if (type >= ALERT_TYPE_COUNT) return;
|
if (type >= ALERT_TYPE_COUNT) return;
|
||||||
if (s_muted) return;
|
if (s_muted) return;
|
||||||
if (!s_tone_buf || !s_codec) return;
|
if (!s_tone_buf || !s_codec) return;
|
||||||
|
if (audio_client_get_state() == AUDIO_PLAYING) return; /* don't fight over codec */
|
||||||
|
|
||||||
if (xSemaphoreTake(s_alert_mutex, pdMS_TO_TICKS(50)) != pdTRUE) return;
|
if (xSemaphoreTake(s_alert_mutex, pdMS_TO_TICKS(50)) != pdTRUE) return;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "button_bsp.h"
|
#include "button_bsp.h"
|
||||||
#include "codec_bsp.h"
|
#include "codec_bsp.h"
|
||||||
#include "alert.h"
|
#include "alert.h"
|
||||||
|
#include "audio_client.h"
|
||||||
#include "lvgl_bsp.h"
|
#include "lvgl_bsp.h"
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
@@ -57,6 +58,9 @@ void UserApp_AppInit(void)
|
|||||||
alert_init();
|
alert_init();
|
||||||
alert_set_codec(s_codec);
|
alert_set_codec(s_codec);
|
||||||
|
|
||||||
|
/* Audio streaming client */
|
||||||
|
audio_client_init(AUDIO_SERVER_URI, s_codec);
|
||||||
|
|
||||||
/* WebSocket client init (not started yet) */
|
/* WebSocket client init (not started yet) */
|
||||||
ws_client_init(WS_SERVER_URI);
|
ws_client_init(WS_SERVER_URI);
|
||||||
ws_client_set_data_callback(ws_data_cb);
|
ws_client_set_data_callback(ws_data_cb);
|
||||||
@@ -79,7 +83,12 @@ void UserApp_TaskInit(void)
|
|||||||
ws_client_start();
|
ws_client_start();
|
||||||
|
|
||||||
/* Sensor polling task - Core 1, 4KB stack */
|
/* Sensor polling task - Core 1, 4KB stack */
|
||||||
xTaskCreatePinnedToCore(sensor_task, "sensor", 4 * 1024, NULL, 3, NULL, 1);
|
TaskHandle_t sensor_handle = NULL;
|
||||||
|
xTaskCreatePinnedToCore(sensor_task, "sensor", 4 * 1024, NULL, 3, &sensor_handle, 1);
|
||||||
|
|
||||||
|
/* Start audio streaming client, notify sensor task on new images */
|
||||||
|
audio_client_set_image_notify_task(sensor_handle);
|
||||||
|
audio_client_start();
|
||||||
|
|
||||||
/* Button handling task - Core 1 */
|
/* Button handling task - Core 1 */
|
||||||
xTaskCreatePinnedToCore(button_task, "button", 2 * 1024, NULL, 2, NULL, 1);
|
xTaskCreatePinnedToCore(button_task, "button", 2 * 1024, NULL, 2, NULL, 1);
|
||||||
@@ -89,6 +98,33 @@ void UserApp_TaskInit(void)
|
|||||||
|
|
||||||
/* ---------- WebSocket callbacks ---------- */
|
/* ---------- WebSocket callbacks ---------- */
|
||||||
|
|
||||||
|
static void rtc_sync_if_needed(const pi_stats_t *stats)
|
||||||
|
{
|
||||||
|
if (!stats->time_valid) return;
|
||||||
|
|
||||||
|
rtcTimeStruct_t rtc = {};
|
||||||
|
Rtc_GetTime(&rtc);
|
||||||
|
|
||||||
|
/* Convert both to seconds-since-midnight for comparison */
|
||||||
|
int pi_secs = stats->time_hour * 3600 + stats->time_minute * 60 + stats->time_second;
|
||||||
|
int rtc_secs = rtc.hour * 3600 + rtc.minute * 60 + rtc.second;
|
||||||
|
int delta = pi_secs - rtc_secs;
|
||||||
|
if (delta < 0) delta = -delta;
|
||||||
|
|
||||||
|
/* Also check date mismatch as an immediate trigger */
|
||||||
|
bool date_mismatch = (rtc.year != stats->time_year ||
|
||||||
|
rtc.month != stats->time_month ||
|
||||||
|
rtc.day != stats->time_day);
|
||||||
|
|
||||||
|
if (date_mismatch || delta > 60) {
|
||||||
|
Rtc_SetTime(stats->time_year, stats->time_month, stats->time_day,
|
||||||
|
stats->time_hour, stats->time_minute, stats->time_second);
|
||||||
|
ESP_LOGI(TAG, "RTC synced from Pi: %04d-%02d-%02d %02d:%02d:%02d (drift: %ds)",
|
||||||
|
stats->time_year, stats->time_month, stats->time_day,
|
||||||
|
stats->time_hour, stats->time_minute, stats->time_second, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ws_data_cb(const pi_stats_t *stats)
|
static void ws_data_cb(const pi_stats_t *stats)
|
||||||
{
|
{
|
||||||
/* Check alert conditions */
|
/* Check alert conditions */
|
||||||
@@ -97,12 +133,15 @@ static void ws_data_cb(const pi_stats_t *stats)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < stats->service_count; i++) {
|
for (int i = 0; i < stats->service_count; i++) {
|
||||||
if (!stats->services[i].running) {
|
if (stats->services[i].status != SVC_RUNNING) {
|
||||||
alert_trigger(ALERT_SERVICE_DOWN);
|
alert_trigger(ALERT_SERVICE_DOWN);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sync RTC if Pi time drifts from board clock */
|
||||||
|
rtc_sync_if_needed(stats);
|
||||||
|
|
||||||
/* Update UI under LVGL lock */
|
/* Update UI under LVGL lock */
|
||||||
if (Lvgl_lock(100)) {
|
if (Lvgl_lock(100)) {
|
||||||
dashboard_ui_update_stats(stats);
|
dashboard_ui_update_stats(stats);
|
||||||
@@ -144,28 +183,64 @@ static void sensor_task(void *arg)
|
|||||||
{
|
{
|
||||||
float temp = 0, humidity = 0;
|
float temp = 0, humidity = 0;
|
||||||
rtcTimeStruct_t rtc_time = {};
|
rtcTimeStruct_t rtc_time = {};
|
||||||
|
int sensor_divider = 0;
|
||||||
|
TickType_t last_sensor_tick = xTaskGetTickCount();
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Read SHTC3 */
|
/*
|
||||||
|
* Sleep until either:
|
||||||
|
* - a new status image arrives (xTaskNotifyGive from audio_client), or
|
||||||
|
* - 1 second elapses (clock refresh cadence)
|
||||||
|
*/
|
||||||
|
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000));
|
||||||
|
|
||||||
|
/* Send deferred image request if connect just happened */
|
||||||
|
audio_client_send_pending_request();
|
||||||
|
|
||||||
|
/* Check for status image updates immediately */
|
||||||
|
bool img_updated = false;
|
||||||
|
const lv_img_dsc_t *img = audio_client_get_status_image(&img_updated);
|
||||||
|
if (img_updated && Lvgl_lock(100)) {
|
||||||
|
dashboard_ui_update_status_image(img);
|
||||||
|
Lvgl_unlock();
|
||||||
|
audio_client_ack_status_image();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sensor + clock updates at ~1s cadence (skip if woken early) */
|
||||||
|
TickType_t now = xTaskGetTickCount();
|
||||||
|
if ((now - last_sensor_tick) < pdMS_TO_TICKS(900)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_sensor_tick = now;
|
||||||
|
|
||||||
|
/* Read RTC */
|
||||||
|
Rtc_GetTime(&rtc_time);
|
||||||
|
|
||||||
|
/* Read SHTC3 + battery every 5 seconds */
|
||||||
|
if (sensor_divider == 0) {
|
||||||
s_shtc3->Shtc3_Wakeup();
|
s_shtc3->Shtc3_Wakeup();
|
||||||
vTaskDelay(pdMS_TO_TICKS(20));
|
vTaskDelay(pdMS_TO_TICKS(20));
|
||||||
s_shtc3->Shtc3_ReadTempHumi(&temp, &humidity);
|
s_shtc3->Shtc3_ReadTempHumi(&temp, &humidity);
|
||||||
s_shtc3->Shtc3_Sleep();
|
s_shtc3->Shtc3_Sleep();
|
||||||
|
|
||||||
/* Read RTC */
|
float batt_v = Adc_GetBatteryVoltage();
|
||||||
Rtc_GetTime(&rtc_time);
|
|
||||||
|
|
||||||
/* Read battery */
|
|
||||||
uint8_t batt = Adc_GetBatteryLevel();
|
uint8_t batt = Adc_GetBatteryLevel();
|
||||||
|
bool charging = Adc_IsCharging();
|
||||||
|
|
||||||
/* Update UI under LVGL lock */
|
|
||||||
if (Lvgl_lock(100)) {
|
if (Lvgl_lock(100)) {
|
||||||
dashboard_ui_update_local(temp, humidity, batt);
|
dashboard_ui_update_local(temp, humidity, batt, charging, batt_v);
|
||||||
dashboard_ui_update_time(rtc_time.hour, rtc_time.minute, rtc_time.second);
|
|
||||||
Lvgl_unlock();
|
Lvgl_unlock();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
sensor_divider = (sensor_divider + 1) % 5;
|
||||||
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
/* Update clock */
|
||||||
|
if (Lvgl_lock(100)) {
|
||||||
|
dashboard_ui_update_time(rtc_time.hour, rtc_time.minute, rtc_time.second,
|
||||||
|
rtc_time.year, rtc_time.month, rtc_time.day,
|
||||||
|
rtc_time.week);
|
||||||
|
Lvgl_unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,10 +249,10 @@ static void sensor_task(void *arg)
|
|||||||
static void button_task(void *arg)
|
static void button_task(void *arg)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* Wait for GP18 button event (single click = bit 0) */
|
/* Wait for GP18 button event (single click = bit 0, long press = bit 2) */
|
||||||
EventBits_t bits = xEventGroupWaitBits(
|
EventBits_t bits = xEventGroupWaitBits(
|
||||||
GP18ButtonGroups,
|
GP18ButtonGroups,
|
||||||
set_bit_button(0), /* single press bit */
|
set_bit_button(0) | set_bit_button(2),
|
||||||
pdTRUE, /* clear on exit */
|
pdTRUE, /* clear on exit */
|
||||||
pdFALSE, /* any bit */
|
pdFALSE, /* any bit */
|
||||||
pdMS_TO_TICKS(500)
|
pdMS_TO_TICKS(500)
|
||||||
@@ -186,7 +261,12 @@ static void button_task(void *arg)
|
|||||||
if (bits & set_bit_button(0)) {
|
if (bits & set_bit_button(0)) {
|
||||||
bool muted = !alert_is_muted();
|
bool muted = !alert_is_muted();
|
||||||
alert_mute(muted);
|
alert_mute(muted);
|
||||||
ESP_LOGI(TAG, "GP18 pressed: alerts %s", muted ? "muted" : "unmuted");
|
ESP_LOGI(TAG, "GP18 single press: alerts %s", muted ? "muted" : "unmuted");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bits & set_bit_button(2)) {
|
||||||
|
ESP_LOGI(TAG, "GP18 long press: forcing WS reconnect");
|
||||||
|
ws_client_reconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
components/user_fonts/CMakeLists.txt
Normal file
11
components/user_fonts/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
idf_component_register(
|
||||||
|
SRCS "DSEG14C_BI_32px.c"
|
||||||
|
"DSEG14C_BI_40px.c"
|
||||||
|
"DSEG14C_BI_50px.c"
|
||||||
|
"InziuIosevka_Slab_CC_12px.c"
|
||||||
|
"InziuIosevka_Slab_CC_16px.c"
|
||||||
|
"InziuIosevka_Slab_CC_20px.c"
|
||||||
|
"InziuIosevka_Slab_CC_24px.c"
|
||||||
|
"InziuIosevka_Slab_CC_32px.c"
|
||||||
|
REQUIRES lvgl__lvgl
|
||||||
|
INCLUDE_DIRS "./")
|
||||||
270
components/user_fonts/DSEG14C_BI_32px.c
Normal file
270
components/user_fonts/DSEG14C_BI_32px.c
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Size: 32 px
|
||||||
|
* Bpp: 1
|
||||||
|
* Opts: --bpp 1 --size 32 --no-compress --stride 1 --align 1 --font DSEG14Classic-BoldItalic.ttf --symbols 0123456789:;,. --format lvgl -o DSEG14C_BI_32px.c
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include("lvgl.h")
|
||||||
|
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#include "lvgl.h"
|
||||||
|
#else
|
||||||
|
#include "lvgl/lvgl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DSEG14C_BI_32PX
|
||||||
|
#define DSEG14C_BI_32PX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DSEG14C_BI_32PX
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* BITMAPS
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Store the image of the glyphs*/
|
||||||
|
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||||
|
/* U+002C "," */
|
||||||
|
0x2f, 0xff, 0xf6, 0x80,
|
||||||
|
|
||||||
|
/* U+002E "." */
|
||||||
|
0xee, 0xfe,
|
||||||
|
|
||||||
|
/* U+0030 "0" */
|
||||||
|
0xf, 0xff, 0xf0, 0x3f, 0xff, 0xc3, 0x7f, 0xfe,
|
||||||
|
0xce, 0xff, 0xf7, 0x3c, 0x0, 0x3c, 0xf0, 0x2,
|
||||||
|
0xf3, 0xc0, 0x1b, 0xcf, 0x0, 0xef, 0x3c, 0x3,
|
||||||
|
0xbc, 0xf0, 0xe, 0xf3, 0xc0, 0x3f, 0x8f, 0x0,
|
||||||
|
0xde, 0x78, 0x3, 0x79, 0xe0, 0x1, 0xe7, 0x0,
|
||||||
|
0x3, 0x98, 0x0, 0x6, 0x60, 0x0, 0x19, 0xc0,
|
||||||
|
0x0, 0xe7, 0x80, 0x7, 0x9e, 0xc0, 0x1e, 0x7b,
|
||||||
|
0x0, 0x79, 0xfc, 0x3, 0xc7, 0xf0, 0xf, 0x3f,
|
||||||
|
0xc0, 0x3c, 0xf7, 0x0, 0xf3, 0xd8, 0x3, 0xcf,
|
||||||
|
0x40, 0xf, 0x3c, 0x0, 0x3c, 0xef, 0xff, 0x73,
|
||||||
|
0x7f, 0xfe, 0xc3, 0xff, 0xfc, 0xf, 0xff, 0xf0,
|
||||||
|
|
||||||
|
/* U+0031 "1" */
|
||||||
|
0x0, 0x31, 0xcf, 0x3c, 0xf3, 0xcf, 0x3d, 0xe7,
|
||||||
|
0x9e, 0x78, 0xe1, 0x8e, 0x79, 0xe7, 0x9e, 0xf3,
|
||||||
|
0xcf, 0x3c, 0xf3, 0xcf, 0x1c, 0x20,
|
||||||
|
|
||||||
|
/* U+0032 "2" */
|
||||||
|
0xf, 0xff, 0xf0, 0x3f, 0xff, 0xc0, 0x7f, 0xfe,
|
||||||
|
0xc0, 0xff, 0xf7, 0x0, 0x0, 0x3c, 0x0, 0x0,
|
||||||
|
0xf0, 0x0, 0x3, 0xc0, 0x0, 0xf, 0x0, 0x0,
|
||||||
|
0x3c, 0x0, 0x0, 0xf0, 0x0, 0x7, 0x80, 0x0,
|
||||||
|
0x1e, 0x0, 0x0, 0x78, 0x0, 0x1, 0xe0, 0x7c,
|
||||||
|
0xfb, 0x83, 0xf3, 0xf6, 0x6f, 0xcf, 0xc1, 0xdf,
|
||||||
|
0x3e, 0x7, 0x80, 0x0, 0x1e, 0x0, 0x0, 0x78,
|
||||||
|
0x0, 0x1, 0xe0, 0x0, 0x7, 0x80, 0x0, 0x3e,
|
||||||
|
0x0, 0x0, 0xf0, 0x0, 0x3, 0xc0, 0x0, 0xf,
|
||||||
|
0x0, 0x0, 0x3c, 0x0, 0x0, 0xef, 0xff, 0x3,
|
||||||
|
0x7f, 0xfe, 0x3, 0xff, 0xfc, 0xf, 0xff, 0xf0,
|
||||||
|
|
||||||
|
/* U+0033 "3" */
|
||||||
|
0x1f, 0xff, 0xe0, 0xff, 0xff, 0x3, 0xff, 0xf6,
|
||||||
|
0xf, 0xff, 0x70, 0x0, 0x7, 0x80, 0x0, 0x3c,
|
||||||
|
0x0, 0x1, 0xe0, 0x0, 0xf, 0x0, 0x0, 0x78,
|
||||||
|
0x0, 0x3, 0xc0, 0x0, 0x3c, 0x0, 0x1, 0xe0,
|
||||||
|
0x0, 0xf, 0x0, 0x0, 0x78, 0x3e, 0x7d, 0xc3,
|
||||||
|
0xf3, 0xf6, 0x1f, 0x9f, 0xb0, 0x7c, 0xfb, 0x80,
|
||||||
|
0x0, 0x3c, 0x0, 0x1, 0xe0, 0x0, 0xf, 0x0,
|
||||||
|
0x0, 0xf0, 0x0, 0x7, 0x80, 0x0, 0x3c, 0x0,
|
||||||
|
0x1, 0xe0, 0x0, 0xf, 0x0, 0x0, 0x78, 0x0,
|
||||||
|
0x3, 0xc1, 0xff, 0xee, 0x1f, 0xff, 0xb1, 0xff,
|
||||||
|
0xfe, 0xf, 0xff, 0xf0,
|
||||||
|
|
||||||
|
/* U+0034 "4" */
|
||||||
|
0x20, 0x0, 0x3, 0x0, 0x0, 0xdc, 0x0, 0xe,
|
||||||
|
0xf0, 0x0, 0xf7, 0x80, 0x7, 0xbc, 0x0, 0x3d,
|
||||||
|
0xe0, 0x1, 0xef, 0x0, 0xf, 0x78, 0x0, 0x7b,
|
||||||
|
0xc0, 0x7, 0x9e, 0x0, 0x3d, 0xe0, 0x1, 0xef,
|
||||||
|
0x0, 0xf, 0x77, 0xcf, 0xbb, 0x7e, 0x7e, 0xc3,
|
||||||
|
0xf3, 0xf6, 0xf, 0x9f, 0x70, 0x0, 0x7, 0x80,
|
||||||
|
0x0, 0x3c, 0x0, 0x1, 0xe0, 0x0, 0x1e, 0x0,
|
||||||
|
0x0, 0xf0, 0x0, 0x7, 0x80, 0x0, 0x3c, 0x0,
|
||||||
|
0x1, 0xe0, 0x0, 0xf, 0x0, 0x0, 0x78, 0x0,
|
||||||
|
0x1, 0xc0, 0x0, 0x6, 0x0, 0x0, 0x0,
|
||||||
|
|
||||||
|
/* U+0035 "5" */
|
||||||
|
0x1f, 0xff, 0xe1, 0xff, 0xfe, 0x6f, 0xff, 0xc7,
|
||||||
|
0x7f, 0xf8, 0x78, 0x0, 0x7, 0x80, 0x0, 0x78,
|
||||||
|
0x0, 0x7, 0x80, 0x0, 0x78, 0x0, 0x7, 0x80,
|
||||||
|
0x0, 0x78, 0x0, 0x7, 0x80, 0x0, 0xf0, 0x0,
|
||||||
|
0xf, 0x0, 0x0, 0xef, 0x9f, 0xd, 0xf9, 0xf8,
|
||||||
|
0x1f, 0x9f, 0xb0, 0xf9, 0xf7, 0x0, 0x0, 0xf0,
|
||||||
|
0x0, 0xf, 0x0, 0x0, 0xf0, 0x0, 0x1e, 0x0,
|
||||||
|
0x1, 0xe0, 0x0, 0x1e, 0x0, 0x1, 0xe0, 0x0,
|
||||||
|
0x1e, 0x0, 0x1, 0xe0, 0x0, 0x1e, 0x1f, 0xfe,
|
||||||
|
0xe3, 0xff, 0xf6, 0x7f, 0xff, 0x87, 0xff, 0xf8,
|
||||||
|
|
||||||
|
/* U+0036 "6" */
|
||||||
|
0xf, 0xff, 0xf0, 0x7f, 0xff, 0x8d, 0xff, 0xf8,
|
||||||
|
0x77, 0xff, 0x83, 0xc0, 0x0, 0x1e, 0x0, 0x0,
|
||||||
|
0xf0, 0x0, 0x7, 0x80, 0x0, 0x3c, 0x0, 0x1,
|
||||||
|
0xe0, 0x0, 0xf, 0x0, 0x0, 0x78, 0x0, 0x7,
|
||||||
|
0x80, 0x0, 0x3c, 0x0, 0x1, 0xdf, 0x3e, 0xd,
|
||||||
|
0xf9, 0xf8, 0x6f, 0xcf, 0xdb, 0xbe, 0x7d, 0xde,
|
||||||
|
0x0, 0x1e, 0xf0, 0x0, 0xf7, 0x80, 0x7, 0xbc,
|
||||||
|
0x0, 0x79, 0xe0, 0x3, 0xdf, 0x0, 0x1e, 0xf0,
|
||||||
|
0x0, 0xf7, 0x80, 0x7, 0xbc, 0x0, 0x3d, 0xe0,
|
||||||
|
0x1, 0xee, 0xff, 0xf7, 0x6f, 0xff, 0xd8, 0xff,
|
||||||
|
0xff, 0x7, 0xff, 0xf8,
|
||||||
|
|
||||||
|
/* U+0037 "7" */
|
||||||
|
0x1f, 0xff, 0xe0, 0xff, 0xff, 0x1b, 0xff, 0xf6,
|
||||||
|
0xef, 0xff, 0x77, 0x80, 0x7, 0xbc, 0x0, 0x3d,
|
||||||
|
0xe0, 0x1, 0xef, 0x0, 0xf, 0x78, 0x0, 0x7b,
|
||||||
|
0xc0, 0x3, 0xde, 0x0, 0x3c, 0xf0, 0x1, 0xef,
|
||||||
|
0x0, 0xf, 0x78, 0x0, 0x7b, 0x80, 0x1, 0xd8,
|
||||||
|
0x0, 0x6, 0x0, 0x0, 0x30, 0x0, 0x3, 0x80,
|
||||||
|
0x0, 0x3c, 0x0, 0x1, 0xe0, 0x0, 0xf, 0x0,
|
||||||
|
0x0, 0x70, 0x0, 0x7, 0x80, 0x0, 0x3c, 0x0,
|
||||||
|
0x1, 0xe0, 0x0, 0xf, 0x0, 0x0, 0x78, 0x0,
|
||||||
|
0x3, 0xc0, 0x0, 0xe, 0x0, 0x0, 0x30, 0x0,
|
||||||
|
0x0, 0x0,
|
||||||
|
|
||||||
|
/* U+0038 "8" */
|
||||||
|
0xf, 0xff, 0xf0, 0x3f, 0xff, 0xc3, 0x7f, 0xfe,
|
||||||
|
0xce, 0xff, 0xf7, 0x3c, 0x0, 0x3c, 0xf0, 0x0,
|
||||||
|
0xf3, 0xc0, 0x3, 0xcf, 0x0, 0xf, 0x3c, 0x0,
|
||||||
|
0x3c, 0xf0, 0x0, 0xf3, 0xc0, 0x7, 0x8f, 0x0,
|
||||||
|
0x1e, 0x78, 0x0, 0x79, 0xe0, 0x1, 0xe7, 0x7c,
|
||||||
|
0xfb, 0x9b, 0xf3, 0xf6, 0x6f, 0xcf, 0xd9, 0xdf,
|
||||||
|
0x3e, 0xe7, 0x80, 0x7, 0x9e, 0x0, 0x1e, 0x78,
|
||||||
|
0x0, 0x79, 0xe0, 0x3, 0xc7, 0x80, 0xf, 0x3e,
|
||||||
|
0x0, 0x3c, 0xf0, 0x0, 0xf3, 0xc0, 0x3, 0xcf,
|
||||||
|
0x0, 0xf, 0x3c, 0x0, 0x3c, 0xef, 0xff, 0x73,
|
||||||
|
0x7f, 0xfe, 0xc3, 0xff, 0xfc, 0xf, 0xff, 0xf0,
|
||||||
|
|
||||||
|
/* U+0039 "9" */
|
||||||
|
0x1f, 0xff, 0xe0, 0xff, 0xff, 0x1b, 0xff, 0xf6,
|
||||||
|
0xef, 0xff, 0x77, 0x80, 0x7, 0xbc, 0x0, 0x3d,
|
||||||
|
0xe0, 0x1, 0xef, 0x0, 0xf, 0x78, 0x0, 0x7b,
|
||||||
|
0xc0, 0x3, 0xde, 0x0, 0x3c, 0xf0, 0x1, 0xef,
|
||||||
|
0x0, 0xf, 0x78, 0x0, 0x7b, 0xbe, 0x7d, 0xdb,
|
||||||
|
0xf3, 0xf6, 0x1f, 0x9f, 0xb0, 0x7c, 0xfb, 0x80,
|
||||||
|
0x0, 0x3c, 0x0, 0x1, 0xe0, 0x0, 0xf, 0x0,
|
||||||
|
0x0, 0xf0, 0x0, 0x7, 0x80, 0x0, 0x3c, 0x0,
|
||||||
|
0x1, 0xe0, 0x0, 0xf, 0x0, 0x0, 0x78, 0x0,
|
||||||
|
0x3, 0xc1, 0xff, 0xee, 0x1f, 0xff, 0xb1, 0xff,
|
||||||
|
0xfe, 0xf, 0xff, 0xf0,
|
||||||
|
|
||||||
|
/* U+003A ":" */
|
||||||
|
0x73, 0xde, 0xe0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x73, 0xde, 0xe0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* GLYPH DESCRIPTION
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||||
|
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
|
||||||
|
{.bitmap_index = 0, .adv_w = 418, .box_w = 3, .box_h = 9, .ofs_x = 7, .ofs_y = 5},
|
||||||
|
{.bitmap_index = 4, .adv_w = 0, .box_w = 4, .box_h = 4, .ofs_x = -3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 6, .adv_w = 418, .box_w = 22, .box_h = 32, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 94, .adv_w = 418, .box_w = 6, .box_h = 29, .ofs_x = 18, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 116, .adv_w = 418, .box_w = 22, .box_h = 32, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 204, .adv_w = 418, .box_w = 21, .box_h = 32, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 288, .adv_w = 418, .box_w = 21, .box_h = 30, .ofs_x = 3, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 367, .adv_w = 418, .box_w = 20, .box_h = 32, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 447, .adv_w = 418, .box_w = 21, .box_h = 32, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 531, .adv_w = 418, .box_w = 21, .box_h = 31, .ofs_x = 3, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 613, .adv_w = 418, .box_w = 22, .box_h = 32, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 701, .adv_w = 418, .box_w = 21, .box_h = 32, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 785, .adv_w = 102, .box_w = 5, .box_h = 17, .ofs_x = 1, .ofs_y = 7}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* CHARACTER MAPPING
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const uint8_t glyph_id_ofs_list_0[] = {
|
||||||
|
0, 0, 1, 0, 2, 3, 4, 5,
|
||||||
|
6, 7, 8, 9, 10, 11, 12
|
||||||
|
};
|
||||||
|
|
||||||
|
/*Collect the unicode lists and glyph_id offsets*/
|
||||||
|
static const lv_font_fmt_txt_cmap_t cmaps[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.range_start = 44, .range_length = 15, .glyph_id_start = 1,
|
||||||
|
.unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_0, .list_length = 15, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------
|
||||||
|
* ALL CUSTOM DATA
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
/*Store all the custom data of the font*/
|
||||||
|
static lv_font_fmt_txt_glyph_cache_t cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
static const lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#else
|
||||||
|
static lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#endif
|
||||||
|
.glyph_bitmap = glyph_bitmap,
|
||||||
|
.glyph_dsc = glyph_dsc,
|
||||||
|
.cmaps = cmaps,
|
||||||
|
.kern_dsc = NULL,
|
||||||
|
.kern_scale = 0,
|
||||||
|
.cmap_num = 1,
|
||||||
|
.bpp = 1,
|
||||||
|
.kern_classes = 0,
|
||||||
|
.bitmap_format = 0,
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
.cache = &cache
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const lv_font_t lv_font_montserrat_12;
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* PUBLIC FONT
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Initialize a public general font descriptor*/
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
const lv_font_t DSEG14C_BI_32px = {
|
||||||
|
#else
|
||||||
|
lv_font_t DSEG14C_BI_32px = {
|
||||||
|
#endif
|
||||||
|
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
|
||||||
|
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
|
||||||
|
.line_height = 32, /*The maximum line height required by the font*/
|
||||||
|
.base_line = 0, /*Baseline measured from the bottom of the line*/
|
||||||
|
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
|
||||||
|
.subpx = LV_FONT_SUBPX_NONE,
|
||||||
|
#endif
|
||||||
|
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
|
||||||
|
.underline_position = -4,
|
||||||
|
.underline_thickness = 2,
|
||||||
|
#endif
|
||||||
|
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
|
||||||
|
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
|
||||||
|
.fallback = &lv_font_montserrat_12,
|
||||||
|
#endif
|
||||||
|
.user_data = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*#if DSEG14C_BI_32PX*/
|
||||||
329
components/user_fonts/DSEG14C_BI_40px.c
Normal file
329
components/user_fonts/DSEG14C_BI_40px.c
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Size: 40 px
|
||||||
|
* Bpp: 1
|
||||||
|
* Opts: --bpp 1 --size 40 --no-compress --stride 1 --align 1 --font DSEG14Classic-BoldItalic.ttf --symbols 0123456789:;., --format lvgl -o DSEG14C_BI_40px.c
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include("lvgl.h")
|
||||||
|
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#include "lvgl.h"
|
||||||
|
#else
|
||||||
|
#include "lvgl/lvgl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DSEG14C_BI_40PX
|
||||||
|
#define DSEG14C_BI_40PX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DSEG14C_BI_40PX
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* BITMAPS
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Store the image of the glyphs*/
|
||||||
|
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||||
|
/* U+002C "," */
|
||||||
|
0x19, 0xce, 0xe7, 0x39, 0xce, 0x63, 0x10,
|
||||||
|
|
||||||
|
/* U+002E "." */
|
||||||
|
0x77, 0xff, 0xf7, 0x0,
|
||||||
|
|
||||||
|
/* U+0030 "0" */
|
||||||
|
0x3, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xfc, 0x9,
|
||||||
|
0xff, 0xff, 0xb1, 0xdf, 0xff, 0xf7, 0x1e, 0xff,
|
||||||
|
0xfe, 0xf1, 0xf0, 0x0, 0x1f, 0x1f, 0x0, 0xd,
|
||||||
|
0xf1, 0xf0, 0x0, 0xdf, 0x1f, 0x0, 0x1d, 0xf1,
|
||||||
|
0xf0, 0x3, 0xdf, 0x1f, 0x0, 0x3d, 0xf1, 0xf0,
|
||||||
|
0x3, 0xfe, 0x1f, 0x0, 0x3f, 0xe1, 0xe0, 0x3,
|
||||||
|
0xfe, 0x3e, 0x0, 0x3b, 0xe3, 0xe0, 0x3, 0x3e,
|
||||||
|
0x3e, 0x0, 0x33, 0xe3, 0xe0, 0x0, 0x3e, 0x3c,
|
||||||
|
0x0, 0x1, 0xe3, 0x0, 0x0, 0xe, 0x30, 0x0,
|
||||||
|
0x0, 0xe3, 0x80, 0x0, 0x1e, 0x3c, 0x0, 0x3,
|
||||||
|
0xc3, 0xe6, 0x0, 0x7c, 0x3e, 0xe0, 0x7, 0xc7,
|
||||||
|
0xce, 0x0, 0x7c, 0x7d, 0xe0, 0x7, 0xc7, 0xdc,
|
||||||
|
0x0, 0x7c, 0x7d, 0xc0, 0x7, 0xc7, 0xdc, 0x0,
|
||||||
|
0x7c, 0x7d, 0xc0, 0x7, 0xc7, 0xdc, 0x0, 0x7c,
|
||||||
|
0x7d, 0x80, 0x7, 0xc7, 0xd0, 0x0, 0x7c, 0x7c,
|
||||||
|
0x0, 0xf, 0x87, 0xbf, 0xff, 0x78, 0x67, 0xff,
|
||||||
|
0xfb, 0x85, 0xff, 0xff, 0xd8, 0x3f, 0xff, 0xfe,
|
||||||
|
0x1, 0xff, 0xff, 0xc0,
|
||||||
|
|
||||||
|
/* U+0031 "1" */
|
||||||
|
0x0, 0x3, 0x7, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f,
|
||||||
|
0x1f, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e,
|
||||||
|
0x1e, 0xe, 0x0, 0xe, 0x3e, 0x7c, 0x7c, 0x7c,
|
||||||
|
0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c, 0x7c,
|
||||||
|
0xf8, 0x78, 0x38, 0x18, 0x0,
|
||||||
|
|
||||||
|
/* U+0032 "2" */
|
||||||
|
0x3, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xfc, 0x1,
|
||||||
|
0xff, 0xff, 0x80, 0x1f, 0xff, 0xf3, 0x0, 0xff,
|
||||||
|
0xfe, 0x70, 0x0, 0x0, 0xf, 0x0, 0x0, 0x1,
|
||||||
|
0xf0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x1, 0xf0,
|
||||||
|
0x0, 0x0, 0x1f, 0x0, 0x0, 0x1, 0xf0, 0x0,
|
||||||
|
0x0, 0x3e, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0,
|
||||||
|
0x3e, 0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0x3e,
|
||||||
|
0x0, 0x0, 0x3, 0xe0, 0x0, 0x0, 0x3e, 0x1,
|
||||||
|
0xf8, 0xfd, 0xe0, 0x3f, 0x9f, 0xee, 0x7, 0xfd,
|
||||||
|
0xff, 0x3, 0xbf, 0x9f, 0xe0, 0x3d, 0xf8, 0xfc,
|
||||||
|
0x3, 0xe0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x3,
|
||||||
|
0xc0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x7, 0xc0,
|
||||||
|
0x0, 0x0, 0x7c, 0x0, 0x0, 0x7, 0xc0, 0x0,
|
||||||
|
0x0, 0x7c, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x0,
|
||||||
|
0x7c, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x0, 0x7c,
|
||||||
|
0x0, 0x0, 0x7, 0xbf, 0xff, 0x0, 0x77, 0xff,
|
||||||
|
0xf8, 0x5, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xfe,
|
||||||
|
0x1, 0xff, 0xff, 0xc0,
|
||||||
|
|
||||||
|
/* U+0033 "3" */
|
||||||
|
0xf, 0xff, 0xff, 0x3, 0xff, 0xff, 0xc0, 0x7f,
|
||||||
|
0xff, 0xe0, 0x1f, 0xff, 0xf3, 0x3, 0xff, 0xf9,
|
||||||
|
0xc0, 0x0, 0x0, 0xf0, 0x0, 0x0, 0x7c, 0x0,
|
||||||
|
0x0, 0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1,
|
||||||
|
0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x3e, 0x0,
|
||||||
|
0x0, 0xf, 0x80, 0x0, 0x3, 0xe0, 0x0, 0x0,
|
||||||
|
0xf8, 0x0, 0x0, 0x3e, 0x0, 0x0, 0xf, 0x80,
|
||||||
|
0x0, 0x3, 0xe0, 0x7e, 0x3f, 0x78, 0x3f, 0x9f,
|
||||||
|
0xee, 0x1f, 0xf7, 0xfc, 0x3, 0xf9, 0xfe, 0xe0,
|
||||||
|
0x7e, 0x3f, 0xf8, 0x0, 0x0, 0x7c, 0x0, 0x0,
|
||||||
|
0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0,
|
||||||
|
0x0, 0x0, 0x7c, 0x0, 0x0, 0x1f, 0x0, 0x0,
|
||||||
|
0x7, 0xc0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c,
|
||||||
|
0x0, 0x0, 0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0,
|
||||||
|
0x3, 0xe0, 0x3f, 0xff, 0x78, 0x1f, 0xff, 0xee,
|
||||||
|
0x1f, 0xff, 0xfd, 0x8f, 0xff, 0xff, 0x81, 0xff,
|
||||||
|
0xff, 0xc0,
|
||||||
|
|
||||||
|
/* U+0034 "4" */
|
||||||
|
0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x37, 0x80,
|
||||||
|
0x0, 0x1d, 0xf0, 0x0, 0xf, 0x7c, 0x0, 0x7,
|
||||||
|
0xdf, 0x0, 0x1, 0xf7, 0xc0, 0x0, 0x7d, 0xf0,
|
||||||
|
0x0, 0x1f, 0x7c, 0x0, 0x7, 0xdf, 0x0, 0x3,
|
||||||
|
0xe7, 0xc0, 0x0, 0xf9, 0xf0, 0x0, 0x3e, 0xf8,
|
||||||
|
0x0, 0xf, 0xbe, 0x0, 0x3, 0xef, 0x80, 0x0,
|
||||||
|
0xfb, 0xe0, 0x0, 0x3e, 0xf7, 0xe3, 0xf7, 0xbb,
|
||||||
|
0xf9, 0xfe, 0xe1, 0xff, 0x7f, 0xc0, 0x3f, 0x9f,
|
||||||
|
0xee, 0x7, 0xe3, 0xf7, 0x80, 0x0, 0x7, 0xc0,
|
||||||
|
0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0,
|
||||||
|
0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0,
|
||||||
|
0x0, 0x0, 0x7c, 0x0, 0x0, 0x1f, 0x0, 0x0,
|
||||||
|
0x7, 0xc0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c,
|
||||||
|
0x0, 0x0, 0x3e, 0x0, 0x0, 0x7, 0x80, 0x0,
|
||||||
|
0x0, 0xe0, 0x0, 0x0, 0x18, 0x0, 0x0, 0x0,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+0035 "5" */
|
||||||
|
0xf, 0xff, 0xff, 0x7, 0xff, 0xff, 0x89, 0xff,
|
||||||
|
0xff, 0x8e, 0xff, 0xff, 0x87, 0xbf, 0xff, 0x83,
|
||||||
|
0xe0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0xf8, 0x0,
|
||||||
|
0x0, 0x7c, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1f,
|
||||||
|
0x0, 0x0, 0xf, 0x80, 0x0, 0x7, 0xc0, 0x0,
|
||||||
|
0x3, 0xe0, 0x0, 0x3, 0xe0, 0x0, 0x1, 0xf0,
|
||||||
|
0x0, 0x0, 0xf8, 0x0, 0x0, 0x7c, 0x0, 0x0,
|
||||||
|
0x3d, 0xf8, 0xfc, 0x1d, 0xfc, 0xff, 0x1, 0xff,
|
||||||
|
0x7f, 0xc0, 0x7f, 0x3f, 0xdc, 0x1f, 0x8f, 0xfe,
|
||||||
|
0x0, 0x0, 0x3e, 0x0, 0x0, 0x1f, 0x0, 0x0,
|
||||||
|
0xf, 0x80, 0x0, 0x7, 0xc0, 0x0, 0x3, 0xe0,
|
||||||
|
0x0, 0x1, 0xf0, 0x0, 0x0, 0xf8, 0x0, 0x0,
|
||||||
|
0x7c, 0x0, 0x0, 0x3e, 0x0, 0x0, 0x1f, 0x0,
|
||||||
|
0x0, 0xf, 0x80, 0x0, 0xf, 0x81, 0xff, 0xfb,
|
||||||
|
0xc1, 0xff, 0xfe, 0xe3, 0xff, 0xff, 0xb3, 0xff,
|
||||||
|
0xff, 0xe0, 0xff, 0xff, 0xe0,
|
||||||
|
|
||||||
|
/* U+0036 "6" */
|
||||||
|
0x3, 0xff, 0xff, 0xc0, 0x7f, 0xff, 0xf8, 0x27,
|
||||||
|
0xff, 0xfe, 0xe, 0xff, 0xff, 0x81, 0xef, 0xff,
|
||||||
|
0xe0, 0x3e, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x0,
|
||||||
|
0xf8, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x3, 0xe0,
|
||||||
|
0x0, 0x0, 0x7c, 0x0, 0x0, 0xf, 0x80, 0x0,
|
||||||
|
0x1, 0xf0, 0x0, 0x0, 0x3e, 0x0, 0x0, 0xf,
|
||||||
|
0x80, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x3e, 0x0,
|
||||||
|
0x0, 0x7, 0xc0, 0x0, 0x0, 0xf7, 0xe3, 0xf0,
|
||||||
|
0x1d, 0xfc, 0xff, 0x0, 0x7f, 0xdf, 0xf0, 0x77,
|
||||||
|
0xf3, 0xff, 0xcf, 0x7e, 0x3f, 0xf9, 0xf0, 0x0,
|
||||||
|
0x3e, 0x3e, 0x0, 0x7, 0xc7, 0x80, 0x0, 0xf9,
|
||||||
|
0xf0, 0x0, 0x1f, 0x3e, 0x0, 0x3, 0xe7, 0xc0,
|
||||||
|
0x0, 0x7c, 0xf8, 0x0, 0xf, 0x9f, 0x0, 0x1,
|
||||||
|
0xf3, 0xe0, 0x0, 0x3e, 0x7c, 0x0, 0x7, 0xcf,
|
||||||
|
0x80, 0x0, 0xf9, 0xf0, 0x0, 0x3e, 0x3d, 0xff,
|
||||||
|
0xfb, 0xc7, 0x7f, 0xff, 0xb8, 0xbf, 0xff, 0xfb,
|
||||||
|
0xf, 0xff, 0xff, 0x80, 0xff, 0xff, 0xe0,
|
||||||
|
|
||||||
|
/* U+0037 "7" */
|
||||||
|
0xf, 0xff, 0xff, 0x3, 0xff, 0xff, 0xc2, 0x7f,
|
||||||
|
0xff, 0xed, 0xdf, 0xff, 0xf7, 0x7b, 0xff, 0xfb,
|
||||||
|
0xdf, 0x0, 0x1, 0xf7, 0xc0, 0x0, 0x7d, 0xf0,
|
||||||
|
0x0, 0x1f, 0x7c, 0x0, 0x7, 0xdf, 0x0, 0x1,
|
||||||
|
0xf7, 0xc0, 0x0, 0x7d, 0xf0, 0x0, 0x3e, 0x7c,
|
||||||
|
0x0, 0xf, 0x9e, 0x0, 0x3, 0xef, 0x80, 0x0,
|
||||||
|
0xfb, 0xe0, 0x0, 0x3e, 0xf8, 0x0, 0xf, 0xbe,
|
||||||
|
0x0, 0x3, 0xee, 0x0, 0x0, 0x7b, 0x0, 0x0,
|
||||||
|
0xe, 0x0, 0x0, 0x3, 0x80, 0x0, 0x1, 0xe0,
|
||||||
|
0x0, 0x0, 0xf0, 0x0, 0x0, 0x7c, 0x0, 0x0,
|
||||||
|
0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0,
|
||||||
|
0x0, 0x0, 0x7c, 0x0, 0x0, 0x1f, 0x0, 0x0,
|
||||||
|
0x7, 0xc0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c,
|
||||||
|
0x0, 0x0, 0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0,
|
||||||
|
0x3, 0xe0, 0x0, 0x0, 0x78, 0x0, 0x0, 0xe,
|
||||||
|
0x0, 0x0, 0x1, 0x80,
|
||||||
|
|
||||||
|
/* U+0038 "8" */
|
||||||
|
0x3, 0xff, 0xff, 0xc0, 0x3f, 0xff, 0xfc, 0x9,
|
||||||
|
0xff, 0xff, 0x81, 0xdf, 0xff, 0xf3, 0x1e, 0xff,
|
||||||
|
0xfe, 0x71, 0xf0, 0x0, 0xf, 0x1f, 0x0, 0x1,
|
||||||
|
0xf1, 0xf0, 0x0, 0x1f, 0x1f, 0x0, 0x1, 0xf1,
|
||||||
|
0xf0, 0x0, 0x1f, 0x1f, 0x0, 0x1, 0xf1, 0xf0,
|
||||||
|
0x0, 0x3e, 0x1f, 0x0, 0x3, 0xe1, 0xf0, 0x0,
|
||||||
|
0x3e, 0x3e, 0x0, 0x3, 0xe3, 0xe0, 0x0, 0x3e,
|
||||||
|
0x3e, 0x0, 0x3, 0xe3, 0xe0, 0x0, 0x3e, 0x3d,
|
||||||
|
0xf8, 0xfd, 0xe3, 0xbf, 0x9f, 0xee, 0x7, 0xfd,
|
||||||
|
0xff, 0x3, 0xbf, 0x9f, 0xfe, 0x3d, 0xf8, 0xff,
|
||||||
|
0xe3, 0xe0, 0x0, 0x7c, 0x3e, 0x0, 0x7, 0xc3,
|
||||||
|
0xc0, 0x0, 0x7c, 0x7c, 0x0, 0x7, 0xc7, 0xc0,
|
||||||
|
0x0, 0x7c, 0x7c, 0x0, 0x7, 0xc7, 0xc0, 0x0,
|
||||||
|
0x7c, 0x7c, 0x0, 0x7, 0xc7, 0xc0, 0x0, 0x7c,
|
||||||
|
0x7c, 0x0, 0x7, 0xc7, 0xc0, 0x0, 0x7c, 0x7c,
|
||||||
|
0x0, 0xf, 0x87, 0xbf, 0xff, 0x78, 0x77, 0xff,
|
||||||
|
0xfb, 0x85, 0xff, 0xff, 0xd8, 0x3f, 0xff, 0xfe,
|
||||||
|
0x1, 0xff, 0xff, 0xc0,
|
||||||
|
|
||||||
|
/* U+0039 "9" */
|
||||||
|
0xf, 0xff, 0xff, 0x3, 0xff, 0xff, 0xc2, 0x7f,
|
||||||
|
0xff, 0xe1, 0xdf, 0xff, 0xf3, 0x7b, 0xff, 0xf9,
|
||||||
|
0xdf, 0x0, 0x0, 0xf7, 0xc0, 0x0, 0x7d, 0xf0,
|
||||||
|
0x0, 0x1f, 0x7c, 0x0, 0x7, 0xdf, 0x0, 0x1,
|
||||||
|
0xf7, 0xc0, 0x0, 0x7d, 0xf0, 0x0, 0x3e, 0x7c,
|
||||||
|
0x0, 0xf, 0x9f, 0x0, 0x3, 0xef, 0x80, 0x0,
|
||||||
|
0xfb, 0xe0, 0x0, 0x3e, 0xf8, 0x0, 0xf, 0xbe,
|
||||||
|
0x0, 0x3, 0xef, 0x7e, 0x3f, 0x7b, 0xbf, 0x9f,
|
||||||
|
0xee, 0x1f, 0xf7, 0xfc, 0x3, 0xf9, 0xfe, 0xe0,
|
||||||
|
0x7e, 0x3f, 0xf8, 0x0, 0x0, 0x7c, 0x0, 0x0,
|
||||||
|
0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0, 0x1, 0xf0,
|
||||||
|
0x0, 0x0, 0x7c, 0x0, 0x0, 0x1f, 0x0, 0x0,
|
||||||
|
0x7, 0xc0, 0x0, 0x1, 0xf0, 0x0, 0x0, 0x7c,
|
||||||
|
0x0, 0x0, 0x1f, 0x0, 0x0, 0x7, 0xc0, 0x0,
|
||||||
|
0x3, 0xe0, 0x3f, 0xff, 0x78, 0x1f, 0xff, 0xee,
|
||||||
|
0x1f, 0xff, 0xfd, 0x8f, 0xff, 0xff, 0x81, 0xff,
|
||||||
|
0xff, 0xc0,
|
||||||
|
|
||||||
|
/* U+003A ":" */
|
||||||
|
0x39, 0xf7, 0xdf, 0x38, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x73, 0xef, 0xbe, 0x70
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* GLYPH DESCRIPTION
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||||
|
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
|
||||||
|
{.bitmap_index = 0, .adv_w = 522, .box_w = 5, .box_h = 11, .ofs_x = 8, .ofs_y = 6},
|
||||||
|
{.bitmap_index = 7, .adv_w = 0, .box_w = 5, .box_h = 5, .ofs_x = -4, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 11, .adv_w = 522, .box_w = 28, .box_h = 40, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 151, .adv_w = 522, .box_w = 8, .box_h = 37, .ofs_x = 22, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 188, .adv_w = 522, .box_w = 28, .box_h = 40, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 328, .adv_w = 522, .box_w = 26, .box_h = 40, .ofs_x = 4, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 458, .adv_w = 522, .box_w = 26, .box_h = 37, .ofs_x = 4, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 579, .adv_w = 522, .box_w = 25, .box_h = 40, .ofs_x = 4, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 704, .adv_w = 522, .box_w = 27, .box_h = 40, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 839, .adv_w = 522, .box_w = 26, .box_h = 38, .ofs_x = 4, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 963, .adv_w = 522, .box_w = 28, .box_h = 40, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1103, .adv_w = 522, .box_w = 26, .box_h = 40, .ofs_x = 4, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1233, .adv_w = 128, .box_w = 6, .box_h = 21, .ofs_x = 1, .ofs_y = 9}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* CHARACTER MAPPING
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const uint8_t glyph_id_ofs_list_0[] = {
|
||||||
|
0, 0, 1, 0, 2, 3, 4, 5,
|
||||||
|
6, 7, 8, 9, 10, 11, 12
|
||||||
|
};
|
||||||
|
|
||||||
|
/*Collect the unicode lists and glyph_id offsets*/
|
||||||
|
static const lv_font_fmt_txt_cmap_t cmaps[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.range_start = 44, .range_length = 15, .glyph_id_start = 1,
|
||||||
|
.unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_0, .list_length = 15, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------
|
||||||
|
* ALL CUSTOM DATA
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
/*Store all the custom data of the font*/
|
||||||
|
static lv_font_fmt_txt_glyph_cache_t cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
static const lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#else
|
||||||
|
static lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#endif
|
||||||
|
.glyph_bitmap = glyph_bitmap,
|
||||||
|
.glyph_dsc = glyph_dsc,
|
||||||
|
.cmaps = cmaps,
|
||||||
|
.kern_dsc = NULL,
|
||||||
|
.kern_scale = 0,
|
||||||
|
.cmap_num = 1,
|
||||||
|
.bpp = 1,
|
||||||
|
.kern_classes = 0,
|
||||||
|
.bitmap_format = 0,
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
.cache = &cache
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const lv_font_t lv_font_montserrat_12;
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* PUBLIC FONT
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Initialize a public general font descriptor*/
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
const lv_font_t DSEG14C_BI_40px = {
|
||||||
|
#else
|
||||||
|
lv_font_t DSEG14C_BI_40px = {
|
||||||
|
#endif
|
||||||
|
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
|
||||||
|
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
|
||||||
|
.line_height = 40, /*The maximum line height required by the font*/
|
||||||
|
.base_line = 0, /*Baseline measured from the bottom of the line*/
|
||||||
|
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
|
||||||
|
.subpx = LV_FONT_SUBPX_NONE,
|
||||||
|
#endif
|
||||||
|
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
|
||||||
|
.underline_position = -5,
|
||||||
|
.underline_thickness = 2,
|
||||||
|
#endif
|
||||||
|
|
||||||
|
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
|
||||||
|
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
|
||||||
|
.fallback = &lv_font_montserrat_12,
|
||||||
|
#endif
|
||||||
|
.user_data = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*#if DSEG14C_BI_40PX*/
|
||||||
417
components/user_fonts/DSEG14C_BI_50px.c
Normal file
417
components/user_fonts/DSEG14C_BI_50px.c
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Size: 50 px
|
||||||
|
* Bpp: 1
|
||||||
|
* Opts: --bpp 1 --size 50 --no-compress --stride 1 --align 1 --font DSEG14Classic-BoldItalic.ttf --symbols 0123456789:;., --format lvgl -o DSEG14C_BI_50px.c
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include("lvgl.h")
|
||||||
|
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#include "lvgl.h"
|
||||||
|
#else
|
||||||
|
#include "lvgl/lvgl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DSEG14C_BI_50PX
|
||||||
|
#define DSEG14C_BI_50PX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DSEG14C_BI_50PX
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* BITMAPS
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Store the image of the glyphs*/
|
||||||
|
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||||
|
/* U+002C "," */
|
||||||
|
0x19, 0xce, 0xff, 0xff, 0xde, 0xf7, 0xbd, 0xcc,
|
||||||
|
0x60,
|
||||||
|
|
||||||
|
/* U+002E "." */
|
||||||
|
0x7b, 0xef, 0xff, 0xf9, 0xe0,
|
||||||
|
|
||||||
|
/* U+0030 "0" */
|
||||||
|
0x1, 0xff, 0xff, 0xff, 0x0, 0x7f, 0xff, 0xff,
|
||||||
|
0xf0, 0x17, 0xff, 0xff, 0xfd, 0x7, 0x7f, 0xff,
|
||||||
|
0xfe, 0x60, 0xf7, 0xff, 0xff, 0x9e, 0x1f, 0x7f,
|
||||||
|
0xff, 0xe7, 0xc3, 0xf0, 0x0, 0x1, 0xf0, 0xfe,
|
||||||
|
0x0, 0x3, 0x7e, 0x1f, 0xc0, 0x0, 0x6f, 0xc3,
|
||||||
|
0xf8, 0x0, 0x1d, 0xf8, 0x7e, 0x0, 0x7, 0xbf,
|
||||||
|
0xf, 0xc0, 0x0, 0xf7, 0xe1, 0xf8, 0x0, 0x1e,
|
||||||
|
0xfc, 0x3f, 0x0, 0x7, 0xdf, 0x87, 0xe0, 0x0,
|
||||||
|
0xfb, 0xf0, 0xfc, 0x0, 0x1e, 0xfe, 0x1f, 0x80,
|
||||||
|
0x3, 0xdf, 0xc3, 0xf0, 0x0, 0x7b, 0xf8, 0xfe,
|
||||||
|
0x0, 0xe, 0x7e, 0x1f, 0xc0, 0x1, 0xcf, 0xc3,
|
||||||
|
0xf8, 0x0, 0x31, 0xf8, 0x7e, 0x0, 0x0, 0x3f,
|
||||||
|
0xf, 0x80, 0x0, 0x3, 0xe1, 0xe0, 0x0, 0x0,
|
||||||
|
0x3c, 0x0, 0x0, 0x0, 0x0, 0x7, 0x0, 0x0,
|
||||||
|
0x0, 0x70, 0xf0, 0x0, 0x0, 0x1e, 0x1f, 0x0,
|
||||||
|
0x0, 0x7, 0xc3, 0xf0, 0x0, 0x3, 0xf8, 0x7e,
|
||||||
|
0x30, 0x0, 0x7e, 0x1f, 0xce, 0x0, 0xf, 0xc3,
|
||||||
|
0xf9, 0xc0, 0x1, 0xf8, 0x7f, 0x78, 0x0, 0x3f,
|
||||||
|
0xf, 0xdf, 0x0, 0x7, 0xe1, 0xfb, 0xe0, 0x0,
|
||||||
|
0xfc, 0x3f, 0x78, 0x0, 0x1f, 0x87, 0xef, 0x0,
|
||||||
|
0x3, 0xf0, 0xfd, 0xe0, 0x0, 0x7e, 0x1f, 0xbc,
|
||||||
|
0x0, 0x1f, 0xc3, 0xf7, 0x0, 0x3, 0xf8, 0x7e,
|
||||||
|
0xe0, 0x0, 0x7f, 0x1f, 0xd8, 0x0, 0xf, 0xc3,
|
||||||
|
0xfa, 0x0, 0x1, 0xf8, 0x7e, 0x0, 0x0, 0x3f,
|
||||||
|
0xf, 0x9f, 0xff, 0xfb, 0xe1, 0xe7, 0xff, 0xff,
|
||||||
|
0xbc, 0x3b, 0xff, 0xff, 0xfb, 0x82, 0xff, 0xff,
|
||||||
|
0xff, 0xa0, 0x3f, 0xff, 0xff, 0xf8, 0x3, 0xff,
|
||||||
|
0xff, 0xfc, 0x0,
|
||||||
|
|
||||||
|
/* U+0031 "1" */
|
||||||
|
0x1, 0x80, 0xf0, 0x7c, 0x3f, 0x1f, 0x87, 0xe1,
|
||||||
|
0xf8, 0x7e, 0x1f, 0x87, 0xe1, 0xf8, 0x7e, 0x1f,
|
||||||
|
0x8f, 0xe3, 0xf8, 0xfc, 0x3f, 0xf, 0xc3, 0xf0,
|
||||||
|
0x7c, 0xf, 0x1, 0xc0, 0x0, 0x3c, 0x1f, 0xf,
|
||||||
|
0xc7, 0xf1, 0xf8, 0x7e, 0x1f, 0x87, 0xe1, 0xf8,
|
||||||
|
0x7e, 0x1f, 0x87, 0xe3, 0xf8, 0xfe, 0x3f, 0x8f,
|
||||||
|
0xc3, 0xf0, 0xfc, 0x1f, 0x3, 0xc0, 0x70, 0x8,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+0032 "2" */
|
||||||
|
0x1, 0xff, 0xff, 0xff, 0x0, 0x7f, 0xff, 0xff,
|
||||||
|
0xf0, 0x7, 0xff, 0xff, 0xfc, 0x0, 0x7f, 0xff,
|
||||||
|
0xfe, 0x60, 0x7, 0xff, 0xff, 0x9e, 0x0, 0x7f,
|
||||||
|
0xff, 0xe7, 0xc0, 0x0, 0x0, 0x1, 0xf0, 0x0,
|
||||||
|
0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0xf, 0xc0,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x0,
|
||||||
|
0xfc, 0x0, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x0,
|
||||||
|
0x3, 0xf0, 0x0, 0x0, 0x0, 0xfe, 0x0, 0x0,
|
||||||
|
0x0, 0x1f, 0xc0, 0x0, 0x0, 0x3, 0xf8, 0x0,
|
||||||
|
0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0xf, 0xc0,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x1f, 0xc3, 0xfb, 0xe0, 0xf, 0xf8, 0xff,
|
||||||
|
0xbc, 0x3, 0xff, 0xbf, 0xf3, 0x87, 0x7f, 0xe7,
|
||||||
|
0xff, 0x0, 0xf7, 0xfc, 0x7f, 0x80, 0x1f, 0x7f,
|
||||||
|
0xf, 0xe0, 0x3, 0xf0, 0x0, 0x0, 0x0, 0x7e,
|
||||||
|
0x0, 0x0, 0x0, 0x1f, 0xc0, 0x0, 0x0, 0x3,
|
||||||
|
0xf8, 0x0, 0x0, 0x0, 0x7f, 0x0, 0x0, 0x0,
|
||||||
|
0xf, 0xc0, 0x0, 0x0, 0x1, 0xf8, 0x0, 0x0,
|
||||||
|
0x0, 0x3f, 0x0, 0x0, 0x0, 0x7, 0xe0, 0x0,
|
||||||
|
0x0, 0x0, 0xfc, 0x0, 0x0, 0x0, 0x1f, 0x80,
|
||||||
|
0x0, 0x0, 0x3, 0xf0, 0x0, 0x0, 0x0, 0x7e,
|
||||||
|
0x0, 0x0, 0x0, 0x1f, 0xc0, 0x0, 0x0, 0x3,
|
||||||
|
0xf8, 0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0,
|
||||||
|
0xf, 0x9f, 0xff, 0xf8, 0x1, 0xe7, 0xff, 0xff,
|
||||||
|
0x80, 0x3b, 0xff, 0xff, 0xf8, 0x2, 0xff, 0xff,
|
||||||
|
0xff, 0x80, 0x3f, 0xff, 0xff, 0xf8, 0x3, 0xff,
|
||||||
|
0xff, 0xfc, 0x0,
|
||||||
|
|
||||||
|
/* U+0033 "3" */
|
||||||
|
0x7, 0xff, 0xff, 0xfc, 0x7, 0xff, 0xff, 0xff,
|
||||||
|
0x1, 0xff, 0xff, 0xff, 0x0, 0x7f, 0xff, 0xfe,
|
||||||
|
0x60, 0x1f, 0xff, 0xfe, 0x78, 0x7, 0xff, 0xfe,
|
||||||
|
0x7c, 0x0, 0x0, 0x0, 0x7c, 0x0, 0x0, 0x0,
|
||||||
|
0x7e, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x0, 0x0,
|
||||||
|
0x1f, 0x80, 0x0, 0x0, 0xf, 0xc0, 0x0, 0x0,
|
||||||
|
0x7, 0xe0, 0x0, 0x0, 0x3, 0xf0, 0x0, 0x0,
|
||||||
|
0x1, 0xf8, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0,
|
||||||
|
0x0, 0xfe, 0x0, 0x0, 0x0, 0x7f, 0x0, 0x0,
|
||||||
|
0x0, 0x3f, 0x80, 0x0, 0x0, 0x1f, 0x80, 0x0,
|
||||||
|
0x0, 0xf, 0xc0, 0x0, 0x0, 0x7, 0xe0, 0x0,
|
||||||
|
0x0, 0x3, 0xf0, 0x7, 0xf0, 0xfe, 0xf8, 0xf,
|
||||||
|
0xf8, 0xff, 0xbc, 0xf, 0xfe, 0xff, 0xce, 0x7,
|
||||||
|
0xfe, 0x7f, 0xf0, 0x1, 0xff, 0x1f, 0xe7, 0x80,
|
||||||
|
0x7f, 0xf, 0xe7, 0xc0, 0x0, 0x0, 0x7, 0xe0,
|
||||||
|
0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x3, 0xf0,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0xfc,
|
||||||
|
0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0xf,
|
||||||
|
0xc0, 0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x7,
|
||||||
|
0xf0, 0x0, 0x0, 0x3, 0xf8, 0x0, 0x0, 0x1,
|
||||||
|
0xfc, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0,
|
||||||
|
0x7e, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x7f, 0xff,
|
||||||
|
0xef, 0x80, 0x7f, 0xff, 0xfb, 0xc0, 0xff, 0xff,
|
||||||
|
0xfe, 0xe0, 0xff, 0xff, 0xff, 0xa0, 0xff, 0xff,
|
||||||
|
0xff, 0xe0, 0x3f, 0xff, 0xff, 0xc0, 0x0,
|
||||||
|
|
||||||
|
/* U+0034 "4" */
|
||||||
|
0x10, 0x0, 0x0, 0x0, 0x1c, 0x0, 0x0, 0x1,
|
||||||
|
0x8e, 0x0, 0x0, 0x1, 0xe7, 0x80, 0x0, 0x1,
|
||||||
|
0xf3, 0xe0, 0x0, 0x1, 0xf3, 0xf8, 0x0, 0x1,
|
||||||
|
0xf9, 0xfc, 0x0, 0x0, 0xfc, 0xfe, 0x0, 0x0,
|
||||||
|
0x7e, 0x7e, 0x0, 0x0, 0x3f, 0x3f, 0x0, 0x0,
|
||||||
|
0x1f, 0x9f, 0x80, 0x0, 0xf, 0xcf, 0xc0, 0x0,
|
||||||
|
0x7, 0xe7, 0xe0, 0x0, 0x3, 0xf3, 0xf0, 0x0,
|
||||||
|
0x3, 0xf9, 0xf8, 0x0, 0x1, 0xfc, 0xfc, 0x0,
|
||||||
|
0x0, 0xfe, 0x7e, 0x0, 0x0, 0x7e, 0x7f, 0x0,
|
||||||
|
0x0, 0x3f, 0x3f, 0x80, 0x0, 0x1f, 0x9f, 0x80,
|
||||||
|
0x0, 0xf, 0xcf, 0x9f, 0xc3, 0xfb, 0xe7, 0xbf,
|
||||||
|
0xe3, 0xfe, 0xf3, 0xbf, 0xfb, 0xff, 0x38, 0x1f,
|
||||||
|
0xf9, 0xff, 0xc0, 0x7, 0xfc, 0x7f, 0x9e, 0x1,
|
||||||
|
0xfc, 0x3f, 0x9f, 0x0, 0x0, 0x0, 0x3f, 0x80,
|
||||||
|
0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0xf, 0xc0,
|
||||||
|
0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x3, 0xf0,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0xfc,
|
||||||
|
0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0x1f,
|
||||||
|
0xc0, 0x0, 0x0, 0xf, 0xe0, 0x0, 0x0, 0x7,
|
||||||
|
0xf0, 0x0, 0x0, 0x3, 0xf0, 0x0, 0x0, 0x1,
|
||||||
|
0xf8, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0,
|
||||||
|
0x3e, 0x0, 0x0, 0x0, 0xf, 0x0, 0x0, 0x0,
|
||||||
|
0x3, 0x80, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
|
||||||
|
/* U+0035 "5" */
|
||||||
|
0x7, 0xff, 0xff, 0xfc, 0x1f, 0xff, 0xff, 0xfc,
|
||||||
|
0x5f, 0xff, 0xff, 0xf1, 0xdf, 0xff, 0xff, 0x83,
|
||||||
|
0xdf, 0xff, 0xfe, 0x7, 0xdf, 0xff, 0xf8, 0xf,
|
||||||
|
0xc0, 0x0, 0x0, 0x3f, 0x80, 0x0, 0x0, 0x7f,
|
||||||
|
0x0, 0x0, 0x0, 0xfe, 0x0, 0x0, 0x1, 0xf8,
|
||||||
|
0x0, 0x0, 0x3, 0xf0, 0x0, 0x0, 0x7, 0xe0,
|
||||||
|
0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0x1f, 0x80,
|
||||||
|
0x0, 0x0, 0x3f, 0x0, 0x0, 0x0, 0x7e, 0x0,
|
||||||
|
0x0, 0x0, 0xfc, 0x0, 0x0, 0x1, 0xf8, 0x0,
|
||||||
|
0x0, 0x7, 0xf0, 0x0, 0x0, 0xf, 0xe0, 0x0,
|
||||||
|
0x0, 0x1f, 0x80, 0x0, 0x0, 0x3e, 0x7f, 0xf,
|
||||||
|
0xe0, 0x7b, 0xfe, 0x3f, 0xe0, 0xef, 0xfe, 0xff,
|
||||||
|
0xc0, 0x1f, 0xf9, 0xff, 0xc0, 0x1f, 0xf1, 0xfe,
|
||||||
|
0x78, 0x1f, 0xc3, 0xf9, 0xf0, 0x0, 0x0, 0x7,
|
||||||
|
0xe0, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0xfc,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x3, 0xf0,
|
||||||
|
0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0xf, 0xc0,
|
||||||
|
0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0x7f, 0x0,
|
||||||
|
0x0, 0x0, 0xfe, 0x0, 0x0, 0x1, 0xfc, 0x0,
|
||||||
|
0x0, 0x3, 0xf0, 0x0, 0x0, 0x7, 0xe0, 0x0,
|
||||||
|
0x0, 0xf, 0xc0, 0x7f, 0xff, 0xef, 0x81, 0xff,
|
||||||
|
0xff, 0xef, 0xf, 0xff, 0xff, 0xee, 0x3f, 0xff,
|
||||||
|
0xff, 0xe8, 0xff, 0xff, 0xff, 0xe0, 0xff, 0xff,
|
||||||
|
0xff, 0x0,
|
||||||
|
|
||||||
|
/* U+0036 "6" */
|
||||||
|
0x1, 0xff, 0xff, 0xff, 0x1, 0xff, 0xff, 0xff,
|
||||||
|
0xc1, 0x7f, 0xff, 0xff, 0xc1, 0xdf, 0xff, 0xff,
|
||||||
|
0x80, 0xf7, 0xff, 0xff, 0x80, 0x7d, 0xff, 0xff,
|
||||||
|
0x80, 0x3f, 0x0, 0x0, 0x0, 0x3f, 0x80, 0x0,
|
||||||
|
0x0, 0x1f, 0xc0, 0x0, 0x0, 0xf, 0xe0, 0x0,
|
||||||
|
0x0, 0x7, 0xe0, 0x0, 0x0, 0x3, 0xf0, 0x0,
|
||||||
|
0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0xfc, 0x0,
|
||||||
|
0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x3f, 0x0,
|
||||||
|
0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0xf, 0xc0,
|
||||||
|
0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x7, 0xf0,
|
||||||
|
0x0, 0x0, 0x3, 0xf8, 0x0, 0x0, 0x1, 0xf8,
|
||||||
|
0x0, 0x0, 0x0, 0xf9, 0xfc, 0x3f, 0x80, 0x7b,
|
||||||
|
0xfe, 0x3f, 0xe0, 0x3b, 0xff, 0xbf, 0xf0, 0x1d,
|
||||||
|
0xff, 0x9f, 0xfd, 0xcf, 0x7f, 0xc7, 0xf9, 0xe7,
|
||||||
|
0xdf, 0xc3, 0xfb, 0xf3, 0xf0, 0x0, 0x3, 0xf9,
|
||||||
|
0xf8, 0x0, 0x1, 0xf9, 0xfc, 0x0, 0x0, 0xfc,
|
||||||
|
0xfe, 0x0, 0x0, 0x7e, 0x7f, 0x0, 0x0, 0x3f,
|
||||||
|
0x3f, 0x0, 0x0, 0x1f, 0x9f, 0x80, 0x0, 0xf,
|
||||||
|
0xcf, 0xc0, 0x0, 0x7, 0xe7, 0xe0, 0x0, 0x3,
|
||||||
|
0xf3, 0xf0, 0x0, 0x1, 0xf9, 0xf8, 0x0, 0x1,
|
||||||
|
0xfc, 0xfc, 0x0, 0x0, 0xfe, 0x7e, 0x0, 0x0,
|
||||||
|
0x7f, 0x7f, 0x0, 0x0, 0x3f, 0x3f, 0x80, 0x0,
|
||||||
|
0x1f, 0x9f, 0x80, 0x0, 0xf, 0xcf, 0x9f, 0xff,
|
||||||
|
0xfb, 0xe7, 0x9f, 0xff, 0xfe, 0xf3, 0xbf, 0xff,
|
||||||
|
0xff, 0xb8, 0xbf, 0xff, 0xff, 0xe8, 0x3f, 0xff,
|
||||||
|
0xff, 0xf8, 0xf, 0xff, 0xff, 0xf0, 0x0,
|
||||||
|
|
||||||
|
/* U+0037 "7" */
|
||||||
|
0x7, 0xff, 0xff, 0xfc, 0x7, 0xff, 0xff, 0xff,
|
||||||
|
0x5, 0xff, 0xff, 0xff, 0x47, 0x7f, 0xff, 0xfe,
|
||||||
|
0x73, 0xdf, 0xff, 0xfe, 0x79, 0xf7, 0xff, 0xfe,
|
||||||
|
0x7c, 0xfc, 0x0, 0x0, 0x7c, 0xfe, 0x0, 0x0,
|
||||||
|
0x7e, 0x7f, 0x0, 0x0, 0x3f, 0x3f, 0x80, 0x0,
|
||||||
|
0x1f, 0x9f, 0x80, 0x0, 0xf, 0xcf, 0xc0, 0x0,
|
||||||
|
0x7, 0xe7, 0xe0, 0x0, 0x3, 0xf3, 0xf0, 0x0,
|
||||||
|
0x1, 0xf9, 0xf8, 0x0, 0x0, 0xfc, 0xfc, 0x0,
|
||||||
|
0x0, 0xfe, 0x7e, 0x0, 0x0, 0x7f, 0x3f, 0x0,
|
||||||
|
0x0, 0x3f, 0xbf, 0x80, 0x0, 0x1f, 0x9f, 0xc0,
|
||||||
|
0x0, 0xf, 0xcf, 0xe0, 0x0, 0x7, 0xe7, 0xe0,
|
||||||
|
0x0, 0x3, 0xf3, 0xe0, 0x0, 0x0, 0xf9, 0xe0,
|
||||||
|
0x0, 0x0, 0x3c, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x7, 0x0, 0x0, 0x0, 0x7, 0x80,
|
||||||
|
0x0, 0x0, 0xf, 0xc0, 0x0, 0x0, 0xf, 0xe0,
|
||||||
|
0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x3, 0xf0,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0xfc,
|
||||||
|
0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0xf,
|
||||||
|
0xc0, 0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x7,
|
||||||
|
0xf0, 0x0, 0x0, 0x3, 0xf8, 0x0, 0x0, 0x1,
|
||||||
|
0xfc, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0,
|
||||||
|
0x7e, 0x0, 0x0, 0x0, 0x1f, 0x0, 0x0, 0x0,
|
||||||
|
0xf, 0x80, 0x0, 0x0, 0x3, 0xc0, 0x0, 0x0,
|
||||||
|
0x0, 0xe0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
|
||||||
|
/* U+0038 "8" */
|
||||||
|
0x1, 0xff, 0xff, 0xff, 0x0, 0x7f, 0xff, 0xff,
|
||||||
|
0xf0, 0x17, 0xff, 0xff, 0xfc, 0x7, 0x7f, 0xff,
|
||||||
|
0xfe, 0x60, 0xf7, 0xff, 0xff, 0x9e, 0x1f, 0x7f,
|
||||||
|
0xff, 0xe7, 0xc3, 0xf0, 0x0, 0x1, 0xf0, 0xfe,
|
||||||
|
0x0, 0x0, 0x7e, 0x1f, 0xc0, 0x0, 0xf, 0xc3,
|
||||||
|
0xf8, 0x0, 0x1, 0xf8, 0x7e, 0x0, 0x0, 0x3f,
|
||||||
|
0xf, 0xc0, 0x0, 0x7, 0xe1, 0xf8, 0x0, 0x0,
|
||||||
|
0xfc, 0x3f, 0x0, 0x0, 0x1f, 0x87, 0xe0, 0x0,
|
||||||
|
0x3, 0xf0, 0xfc, 0x0, 0x0, 0xfe, 0x1f, 0x80,
|
||||||
|
0x0, 0x1f, 0xc3, 0xf0, 0x0, 0x3, 0xf8, 0x7e,
|
||||||
|
0x0, 0x0, 0x7e, 0x1f, 0xc0, 0x0, 0xf, 0xc3,
|
||||||
|
0xf8, 0x0, 0x1, 0xf8, 0x7e, 0x0, 0x0, 0x3f,
|
||||||
|
0xf, 0x9f, 0xc3, 0xfb, 0xe1, 0xef, 0xf8, 0xff,
|
||||||
|
0xbc, 0x3b, 0xff, 0xbf, 0xf3, 0x87, 0x7f, 0xe7,
|
||||||
|
0xff, 0x70, 0xf7, 0xfc, 0x7f, 0x9e, 0x1f, 0x7f,
|
||||||
|
0xf, 0xef, 0xc3, 0xf0, 0x0, 0x3, 0xf8, 0x7e,
|
||||||
|
0x0, 0x0, 0x7e, 0x1f, 0xc0, 0x0, 0xf, 0xc3,
|
||||||
|
0xf8, 0x0, 0x1, 0xf8, 0x7f, 0x0, 0x0, 0x3f,
|
||||||
|
0xf, 0xc0, 0x0, 0x7, 0xe1, 0xf8, 0x0, 0x0,
|
||||||
|
0xfc, 0x3f, 0x0, 0x0, 0x1f, 0x87, 0xe0, 0x0,
|
||||||
|
0x3, 0xf0, 0xfc, 0x0, 0x0, 0x7e, 0x1f, 0x80,
|
||||||
|
0x0, 0x1f, 0xc3, 0xf0, 0x0, 0x3, 0xf8, 0x7e,
|
||||||
|
0x0, 0x0, 0x7f, 0x1f, 0xc0, 0x0, 0xf, 0xc3,
|
||||||
|
0xf8, 0x0, 0x1, 0xf8, 0x7e, 0x0, 0x0, 0x3f,
|
||||||
|
0xf, 0x9f, 0xff, 0xfb, 0xe1, 0xe7, 0xff, 0xff,
|
||||||
|
0xbc, 0x3b, 0xff, 0xff, 0xfb, 0x82, 0xff, 0xff,
|
||||||
|
0xff, 0xa0, 0x3f, 0xff, 0xff, 0xf8, 0x3, 0xff,
|
||||||
|
0xff, 0xfc, 0x0,
|
||||||
|
|
||||||
|
/* U+0039 "9" */
|
||||||
|
0x7, 0xff, 0xff, 0xfc, 0x7, 0xff, 0xff, 0xff,
|
||||||
|
0x5, 0xff, 0xff, 0xff, 0x7, 0x7f, 0xff, 0xfe,
|
||||||
|
0x63, 0xdf, 0xff, 0xfe, 0x79, 0xf7, 0xff, 0xfe,
|
||||||
|
0x7c, 0xfc, 0x0, 0x0, 0x7c, 0xfe, 0x0, 0x0,
|
||||||
|
0x7e, 0x7f, 0x0, 0x0, 0x3f, 0x3f, 0x80, 0x0,
|
||||||
|
0x1f, 0x9f, 0x80, 0x0, 0xf, 0xcf, 0xc0, 0x0,
|
||||||
|
0x7, 0xe7, 0xe0, 0x0, 0x3, 0xf3, 0xf0, 0x0,
|
||||||
|
0x1, 0xf9, 0xf8, 0x0, 0x0, 0xfc, 0xfc, 0x0,
|
||||||
|
0x0, 0xfe, 0x7e, 0x0, 0x0, 0x7f, 0x3f, 0x0,
|
||||||
|
0x0, 0x3f, 0x9f, 0x80, 0x0, 0x1f, 0x9f, 0xc0,
|
||||||
|
0x0, 0xf, 0xcf, 0xe0, 0x0, 0x7, 0xe7, 0xe0,
|
||||||
|
0x0, 0x3, 0xf3, 0xe7, 0xf0, 0xfe, 0xf9, 0xef,
|
||||||
|
0xf8, 0xff, 0xbc, 0xef, 0xfe, 0xff, 0xce, 0x7,
|
||||||
|
0xfe, 0x7f, 0xf0, 0x1, 0xff, 0x1f, 0xe7, 0x80,
|
||||||
|
0x7f, 0xf, 0xe7, 0xc0, 0x0, 0x0, 0x7, 0xe0,
|
||||||
|
0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x3, 0xf0,
|
||||||
|
0x0, 0x0, 0x1, 0xf8, 0x0, 0x0, 0x0, 0xfc,
|
||||||
|
0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0x0, 0x3f,
|
||||||
|
0x0, 0x0, 0x0, 0x1f, 0x80, 0x0, 0x0, 0xf,
|
||||||
|
0xc0, 0x0, 0x0, 0x7, 0xe0, 0x0, 0x0, 0x7,
|
||||||
|
0xf0, 0x0, 0x0, 0x3, 0xf8, 0x0, 0x0, 0x1,
|
||||||
|
0xfc, 0x0, 0x0, 0x0, 0xfc, 0x0, 0x0, 0x0,
|
||||||
|
0x7e, 0x0, 0x0, 0x0, 0x3f, 0x0, 0x7f, 0xff,
|
||||||
|
0xef, 0x80, 0x7f, 0xff, 0xfb, 0xc0, 0xff, 0xff,
|
||||||
|
0xfe, 0xe0, 0xff, 0xff, 0xff, 0xa0, 0xff, 0xff,
|
||||||
|
0xff, 0xe0, 0x3f, 0xff, 0xff, 0xc0, 0x0,
|
||||||
|
|
||||||
|
/* U+003A ":" */
|
||||||
|
0x1e, 0x3f, 0x3f, 0x3f, 0x3f, 0x1e, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
|
||||||
|
0x0, 0x0, 0x0, 0x0, 0x0, 0x78, 0xfc, 0xfc,
|
||||||
|
0xfc, 0xfc, 0x78
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* GLYPH DESCRIPTION
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||||
|
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
|
||||||
|
{.bitmap_index = 0, .adv_w = 653, .box_w = 5, .box_h = 14, .ofs_x = 11, .ofs_y = 7},
|
||||||
|
{.bitmap_index = 9, .adv_w = 0, .box_w = 6, .box_h = 6, .ofs_x = -5, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 14, .adv_w = 653, .box_w = 35, .box_h = 50, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 233, .adv_w = 653, .box_w = 10, .box_h = 45, .ofs_x = 28, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 290, .adv_w = 653, .box_w = 35, .box_h = 50, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 509, .adv_w = 653, .box_w = 33, .box_h = 50, .ofs_x = 5, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 716, .adv_w = 653, .box_w = 33, .box_h = 46, .ofs_x = 5, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 906, .adv_w = 653, .box_w = 31, .box_h = 50, .ofs_x = 5, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1100, .adv_w = 653, .box_w = 33, .box_h = 50, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1307, .adv_w = 653, .box_w = 33, .box_h = 48, .ofs_x = 5, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 1505, .adv_w = 653, .box_w = 35, .box_h = 50, .ofs_x = 3, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1724, .adv_w = 653, .box_w = 33, .box_h = 50, .ofs_x = 5, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1931, .adv_w = 160, .box_w = 8, .box_h = 27, .ofs_x = 1, .ofs_y = 11}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* CHARACTER MAPPING
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const uint8_t glyph_id_ofs_list_0[] = {
|
||||||
|
0, 0, 1, 0, 2, 3, 4, 5,
|
||||||
|
6, 7, 8, 9, 10, 11, 12
|
||||||
|
};
|
||||||
|
|
||||||
|
/*Collect the unicode lists and glyph_id offsets*/
|
||||||
|
static const lv_font_fmt_txt_cmap_t cmaps[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.range_start = 44, .range_length = 15, .glyph_id_start = 1,
|
||||||
|
.unicode_list = NULL, .glyph_id_ofs_list = glyph_id_ofs_list_0, .list_length = 15, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_FULL
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------
|
||||||
|
* ALL CUSTOM DATA
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
/*Store all the custom data of the font*/
|
||||||
|
static lv_font_fmt_txt_glyph_cache_t cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
static const lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#else
|
||||||
|
static lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#endif
|
||||||
|
.glyph_bitmap = glyph_bitmap,
|
||||||
|
.glyph_dsc = glyph_dsc,
|
||||||
|
.cmaps = cmaps,
|
||||||
|
.kern_dsc = NULL,
|
||||||
|
.kern_scale = 0,
|
||||||
|
.cmap_num = 1,
|
||||||
|
.bpp = 1,
|
||||||
|
.kern_classes = 0,
|
||||||
|
.bitmap_format = 0,
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
.cache = &cache
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const lv_font_t lv_font_montserrat_12;
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* PUBLIC FONT
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Initialize a public general font descriptor*/
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
const lv_font_t DSEG14C_BI_50px = {
|
||||||
|
#else
|
||||||
|
lv_font_t DSEG14C_BI_50px = {
|
||||||
|
#endif
|
||||||
|
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
|
||||||
|
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
|
||||||
|
.line_height = 50, /*The maximum line height required by the font*/
|
||||||
|
.base_line = 0, /*Baseline measured from the bottom of the line*/
|
||||||
|
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
|
||||||
|
.subpx = LV_FONT_SUBPX_NONE,
|
||||||
|
#endif
|
||||||
|
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
|
||||||
|
.underline_position = -6,
|
||||||
|
.underline_thickness = 3,
|
||||||
|
#endif
|
||||||
|
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
|
||||||
|
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
|
||||||
|
.fallback = &lv_font_montserrat_12,
|
||||||
|
#endif
|
||||||
|
.user_data = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*#if DSEG14C_BI_50PX*/
|
||||||
924
components/user_fonts/InziuIosevka_Slab_CC_12px.c
Normal file
924
components/user_fonts/InziuIosevka_Slab_CC_12px.c
Normal file
@@ -0,0 +1,924 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Size: 12 px
|
||||||
|
* Bpp: 1
|
||||||
|
* Opts: --bpp 1 --size 12 --no-compress --stride 1 --align 1 --font Inziu-IosevkaCC-Slab-J-Regular-04.ttf --range 0-254 --format lvgl -o InziuIosevka_Slab_CC_12px.c
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include("lvgl.h")
|
||||||
|
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||||
|
#include "lvgl.h"
|
||||||
|
#else
|
||||||
|
#include "lvgl/lvgl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef INZIUIOSEVKA_SLAB_CC_12PX
|
||||||
|
#define INZIUIOSEVKA_SLAB_CC_12PX 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if INZIUIOSEVKA_SLAB_CC_12PX
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* BITMAPS
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Store the image of the glyphs*/
|
||||||
|
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||||
|
/* U+000D "\r" */
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+0020 " " */
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+0021 "!" */
|
||||||
|
0x55, 0x50, 0xf0,
|
||||||
|
|
||||||
|
/* U+0022 "\"" */
|
||||||
|
0xb6, 0xd0,
|
||||||
|
|
||||||
|
/* U+0023 "#" */
|
||||||
|
0x52, 0x95, 0xf5, 0x29, 0x4a, 0xfa, 0x94, 0xa0,
|
||||||
|
|
||||||
|
/* U+0024 "$" */
|
||||||
|
0x21, 0x1d, 0xda, 0x51, 0x86, 0x2d, 0x7c, 0x42,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+0025 "%" */
|
||||||
|
0x99, 0xa2, 0x24, 0x45, 0x99,
|
||||||
|
|
||||||
|
/* U+0026 "&" */
|
||||||
|
0x64, 0xa5, 0x66, 0x36, 0xf3, 0x8b, 0xc0,
|
||||||
|
|
||||||
|
/* U+0027 "'" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+0028 "(" */
|
||||||
|
0x2, 0x44, 0x88, 0x88, 0x88, 0x84, 0x42,
|
||||||
|
|
||||||
|
/* U+0029 ")" */
|
||||||
|
0x4, 0x22, 0x11, 0x11, 0x11, 0x12, 0x24,
|
||||||
|
|
||||||
|
/* U+002A "*" */
|
||||||
|
0x21, 0x3e, 0xa5, 0x0,
|
||||||
|
|
||||||
|
/* U+002B "+" */
|
||||||
|
0x21, 0x3e, 0x42, 0x0,
|
||||||
|
|
||||||
|
/* U+002C "," */
|
||||||
|
0xf6, 0x0,
|
||||||
|
|
||||||
|
/* U+002D "-" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+002E "." */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+002F "/" */
|
||||||
|
0x11, 0x12, 0x22, 0x24, 0x44, 0x48, 0x88,
|
||||||
|
|
||||||
|
/* U+0030 "0" */
|
||||||
|
0x74, 0x63, 0x3a, 0xd7, 0x31, 0xcb, 0x80,
|
||||||
|
|
||||||
|
/* U+0031 "1" */
|
||||||
|
0x67, 0x8, 0x42, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+0032 "2" */
|
||||||
|
0x72, 0x42, 0x11, 0x19, 0x88, 0x4f, 0xc0,
|
||||||
|
|
||||||
|
/* U+0033 "3" */
|
||||||
|
0x72, 0x42, 0x33, 0xc, 0x21, 0x4b, 0x80,
|
||||||
|
|
||||||
|
/* U+0034 "4" */
|
||||||
|
0x11, 0x8c, 0xa5, 0x4b, 0xe2, 0x11, 0xc0,
|
||||||
|
|
||||||
|
/* U+0035 "5" */
|
||||||
|
0xfc, 0x61, 0xf, 0x4c, 0x21, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+0036 "6" */
|
||||||
|
0x11, 0x11, 0x8f, 0x46, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+0037 "7" */
|
||||||
|
0xfc, 0x84, 0x22, 0x10, 0x84, 0x42, 0x0,
|
||||||
|
|
||||||
|
/* U+0038 "8" */
|
||||||
|
0x74, 0x63, 0x37, 0x2a, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+0039 "9" */
|
||||||
|
0x74, 0x63, 0x18, 0xbc, 0x31, 0xdb, 0x80,
|
||||||
|
|
||||||
|
/* U+003A ":" */
|
||||||
|
0xf0, 0x3c,
|
||||||
|
|
||||||
|
/* U+003B ";" */
|
||||||
|
0xf0, 0x3d, 0x80,
|
||||||
|
|
||||||
|
/* U+003C "<" */
|
||||||
|
0x0, 0x99, 0x88, 0x20, 0xc3,
|
||||||
|
|
||||||
|
/* U+003D "=" */
|
||||||
|
0xf0, 0xf,
|
||||||
|
|
||||||
|
/* U+003E ">" */
|
||||||
|
0x2, 0xc, 0x30, 0x89, 0x98,
|
||||||
|
|
||||||
|
/* U+003F "?" */
|
||||||
|
0x74, 0x42, 0x11, 0x10, 0x80, 0x63, 0x0,
|
||||||
|
|
||||||
|
/* U+0040 "@" */
|
||||||
|
0x74, 0x42, 0x1e, 0xd6, 0xb5, 0xad, 0x6b, 0xb0,
|
||||||
|
|
||||||
|
/* U+0041 "A" */
|
||||||
|
0x30, 0xc3, 0xc, 0x31, 0x24, 0x9e, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+0042 "B" */
|
||||||
|
0xf1, 0x24, 0x92, 0x49, 0xc4, 0xd1, 0x4f, 0xe0,
|
||||||
|
|
||||||
|
/* U+0043 "C" */
|
||||||
|
0x74, 0xe1, 0x8, 0x42, 0x10, 0x9b, 0x80,
|
||||||
|
|
||||||
|
/* U+0044 "D" */
|
||||||
|
0xf9, 0x34, 0x51, 0x45, 0x14, 0x51, 0x4f, 0xe0,
|
||||||
|
|
||||||
|
/* U+0045 "E" */
|
||||||
|
0xfa, 0x50, 0x84, 0x39, 0x8, 0x4f, 0xc0,
|
||||||
|
|
||||||
|
/* U+0046 "F" */
|
||||||
|
0xfa, 0x50, 0x84, 0x39, 0x8, 0x47, 0x0,
|
||||||
|
|
||||||
|
/* U+0047 "G" */
|
||||||
|
0x74, 0x61, 0x8, 0x4e, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+0048 "H" */
|
||||||
|
0xcd, 0x24, 0x92, 0x79, 0x24, 0x92, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+0049 "I" */
|
||||||
|
0xe9, 0x24, 0x92, 0x5c,
|
||||||
|
|
||||||
|
/* U+004A "J" */
|
||||||
|
0x71, 0x11, 0x11, 0x11, 0x9e,
|
||||||
|
|
||||||
|
/* U+004B "K" */
|
||||||
|
0xed, 0x25, 0x14, 0x71, 0xc5, 0x12, 0x4b, 0xb0,
|
||||||
|
|
||||||
|
/* U+004C "L" */
|
||||||
|
0xe2, 0x10, 0x84, 0x21, 0x8, 0x4f, 0xc0,
|
||||||
|
|
||||||
|
/* U+004D "M" */
|
||||||
|
0xcd, 0x27, 0x9e, 0x79, 0x64, 0x92, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+004E "N" */
|
||||||
|
0xcd, 0xa6, 0x9a, 0x69, 0xa5, 0x96, 0x5b, 0x20,
|
||||||
|
|
||||||
|
/* U+004F "O" */
|
||||||
|
0x74, 0x63, 0x18, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+0050 "P" */
|
||||||
|
0xf2, 0x52, 0x94, 0xb9, 0x8, 0x46, 0x0,
|
||||||
|
|
||||||
|
/* U+0051 "Q" */
|
||||||
|
0x74, 0x63, 0x18, 0xc6, 0x31, 0x8b, 0x88, 0x30,
|
||||||
|
|
||||||
|
/* U+0052 "R" */
|
||||||
|
0xf9, 0x14, 0x51, 0x45, 0xe5, 0x92, 0x4b, 0x90,
|
||||||
|
|
||||||
|
/* U+0053 "S" */
|
||||||
|
0x69, 0x88, 0x42, 0x11, 0x96,
|
||||||
|
|
||||||
|
/* U+0054 "T" */
|
||||||
|
0xfd, 0x48, 0x42, 0x10, 0x84, 0x23, 0x80,
|
||||||
|
|
||||||
|
/* U+0055 "U" */
|
||||||
|
0xcd, 0x24, 0x92, 0x49, 0x24, 0x92, 0x48, 0xc0,
|
||||||
|
|
||||||
|
/* U+0056 "V" */
|
||||||
|
0xcd, 0x24, 0x92, 0x48, 0xc3, 0xc, 0x30, 0xc0,
|
||||||
|
|
||||||
|
/* U+0057 "W" */
|
||||||
|
0xcd, 0x24, 0x92, 0x49, 0xa7, 0x9e, 0x31, 0x40,
|
||||||
|
|
||||||
|
/* U+0058 "X" */
|
||||||
|
0xcd, 0x24, 0x8c, 0x30, 0xc3, 0x12, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+0059 "Y" */
|
||||||
|
0xee, 0x89, 0x11, 0x42, 0x82, 0x4, 0x8, 0x10,
|
||||||
|
0x70,
|
||||||
|
|
||||||
|
/* U+005A "Z" */
|
||||||
|
0xfc, 0x84, 0x42, 0x31, 0x8, 0x8f, 0xc0,
|
||||||
|
|
||||||
|
/* U+005B "[" */
|
||||||
|
0xf8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x8f,
|
||||||
|
|
||||||
|
/* U+005C "\\" */
|
||||||
|
0x88, 0x84, 0x44, 0x42, 0x22, 0x21, 0x11,
|
||||||
|
|
||||||
|
/* U+005D "]" */
|
||||||
|
0xf1, 0x11, 0x11, 0x11, 0x11, 0x11, 0x1f,
|
||||||
|
|
||||||
|
/* U+005E "^" */
|
||||||
|
0x6f, 0x90,
|
||||||
|
|
||||||
|
/* U+005F "_" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+0060 "`" */
|
||||||
|
0x88, 0x80,
|
||||||
|
|
||||||
|
/* U+0061 "a" */
|
||||||
|
0x71, 0x20, 0x9e, 0x4a, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+0062 "b" */
|
||||||
|
0xc1, 0x4, 0x1e, 0x45, 0x14, 0x51, 0x47, 0xe0,
|
||||||
|
|
||||||
|
/* U+0063 "c" */
|
||||||
|
0x74, 0xe1, 0x8, 0x4d, 0xc0,
|
||||||
|
|
||||||
|
/* U+0064 "d" */
|
||||||
|
0x18, 0x20, 0x9e, 0x8a, 0x28, 0xa2, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+0065 "e" */
|
||||||
|
0x74, 0x63, 0xf8, 0x45, 0xc0,
|
||||||
|
|
||||||
|
/* U+0066 "f" */
|
||||||
|
0x75, 0x4f, 0x44, 0x44, 0x4f,
|
||||||
|
|
||||||
|
/* U+0067 "g" */
|
||||||
|
0x7c, 0x63, 0x17, 0x41, 0xd3, 0x9b, 0x80,
|
||||||
|
|
||||||
|
/* U+0068 "h" */
|
||||||
|
0xc1, 0x4, 0x1c, 0x49, 0x24, 0x92, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+0069 "i" */
|
||||||
|
0x60, 0x1, 0xc2, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+006A "j" */
|
||||||
|
0x10, 0x7, 0x11, 0x11, 0x11, 0x19, 0x60,
|
||||||
|
|
||||||
|
/* U+006B "k" */
|
||||||
|
0xc1, 0x4, 0x13, 0x51, 0x47, 0x14, 0x4b, 0xb0,
|
||||||
|
|
||||||
|
/* U+006C "l" */
|
||||||
|
0xe1, 0x8, 0x42, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+006D "m" */
|
||||||
|
0xfd, 0x6b, 0x5a, 0xd6, 0xa0,
|
||||||
|
|
||||||
|
/* U+006E "n" */
|
||||||
|
0xf1, 0x24, 0x92, 0x49, 0x2c, 0xc0,
|
||||||
|
|
||||||
|
/* U+006F "o" */
|
||||||
|
0x74, 0x63, 0x18, 0xc5, 0xc0,
|
||||||
|
|
||||||
|
/* U+0070 "p" */
|
||||||
|
0xf9, 0x14, 0x51, 0x45, 0x17, 0x90, 0x43, 0x80,
|
||||||
|
|
||||||
|
/* U+0071 "q" */
|
||||||
|
0x7e, 0x28, 0xa2, 0x8a, 0x27, 0x82, 0x8, 0x70,
|
||||||
|
|
||||||
|
/* U+0072 "r" */
|
||||||
|
0xfa, 0x10, 0x84, 0x23, 0x80,
|
||||||
|
|
||||||
|
/* U+0073 "s" */
|
||||||
|
0x74, 0xa0, 0xe1, 0x49, 0xc0,
|
||||||
|
|
||||||
|
/* U+0074 "t" */
|
||||||
|
0x42, 0x11, 0xe4, 0x21, 0x8, 0x49, 0x80,
|
||||||
|
|
||||||
|
/* U+0075 "u" */
|
||||||
|
0xc9, 0x24, 0x92, 0x49, 0x23, 0xc0,
|
||||||
|
|
||||||
|
/* U+0076 "v" */
|
||||||
|
0xcd, 0x24, 0x92, 0x30, 0xc3, 0x0,
|
||||||
|
|
||||||
|
/* U+0077 "w" */
|
||||||
|
0xcd, 0x24, 0x9a, 0x78, 0xc3, 0x0,
|
||||||
|
|
||||||
|
/* U+0078 "x" */
|
||||||
|
0xcd, 0x23, 0xc, 0x31, 0x2c, 0xc0,
|
||||||
|
|
||||||
|
/* U+0079 "y" */
|
||||||
|
0xcd, 0x24, 0x92, 0x30, 0xc3, 0x8, 0x21, 0x80,
|
||||||
|
|
||||||
|
/* U+007A "z" */
|
||||||
|
0xfc, 0x8c, 0x44, 0x67, 0xe0,
|
||||||
|
|
||||||
|
/* U+007B "{" */
|
||||||
|
0x36, 0x46, 0x22, 0xc6, 0x22, 0x44, 0x63,
|
||||||
|
|
||||||
|
/* U+007C "|" */
|
||||||
|
0xff, 0xfc,
|
||||||
|
|
||||||
|
/* U+007D "}" */
|
||||||
|
0xc6, 0x26, 0x44, 0x34, 0x44, 0x22, 0x6c,
|
||||||
|
|
||||||
|
/* U+007E "~" */
|
||||||
|
0x1, 0xa9, 0x80,
|
||||||
|
|
||||||
|
/* U+00A0 " " */
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+00A1 "¡" */
|
||||||
|
0xf0, 0x55, 0x50,
|
||||||
|
|
||||||
|
/* U+00A2 "¢" */
|
||||||
|
0x23, 0xa7, 0x8, 0x42, 0x6e, 0x20,
|
||||||
|
|
||||||
|
/* U+00A3 "£" */
|
||||||
|
0x32, 0x90, 0x8e, 0x21, 0x8, 0xc7, 0xc0,
|
||||||
|
|
||||||
|
/* U+00A4 "¤" */
|
||||||
|
0xfc, 0x63, 0x1f, 0x80,
|
||||||
|
|
||||||
|
/* U+00A5 "¥" */
|
||||||
|
0xcd, 0x24, 0x9c, 0x31, 0xe2, 0x1e, 0x20, 0xc0,
|
||||||
|
|
||||||
|
/* U+00A6 "¦" */
|
||||||
|
0xfc, 0xfc,
|
||||||
|
|
||||||
|
/* U+00A7 "§" */
|
||||||
|
0x74, 0x61, 0x6, 0x6e, 0x31, 0xd9, 0x82, 0x18,
|
||||||
|
0xb8,
|
||||||
|
|
||||||
|
/* U+00A8 "¨" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+00A9 "©" */
|
||||||
|
0x3e, 0x31, 0xb7, 0x72, 0x99, 0xc, 0x86, 0x53,
|
||||||
|
0xbb, 0x63, 0x1f, 0x0,
|
||||||
|
|
||||||
|
/* U+00AA "ª" */
|
||||||
|
0x7f, 0x9f, 0xf,
|
||||||
|
|
||||||
|
/* U+00AB "«" */
|
||||||
|
0x4a, 0xa9, 0x45, 0x28, 0xa0,
|
||||||
|
|
||||||
|
/* U+00AC "¬" */
|
||||||
|
0xf1, 0x10,
|
||||||
|
|
||||||
|
/* U+00AD "" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+00AE "®" */
|
||||||
|
0x3e, 0x31, 0xb7, 0x72, 0x99, 0x4c, 0xc6, 0x53,
|
||||||
|
0xab, 0x63, 0x1f, 0x0,
|
||||||
|
|
||||||
|
/* U+00AF "¯" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+00B0 "°" */
|
||||||
|
0xf7, 0x80,
|
||||||
|
|
||||||
|
/* U+00B1 "±" */
|
||||||
|
0x21, 0x9, 0xf2, 0x10, 0x1f,
|
||||||
|
|
||||||
|
/* U+00B2 "²" */
|
||||||
|
0x79, 0x16, 0x4f,
|
||||||
|
|
||||||
|
/* U+00B3 "³" */
|
||||||
|
0x71, 0x31, 0x97,
|
||||||
|
|
||||||
|
/* U+00B4 "´" */
|
||||||
|
0x2a, 0x0,
|
||||||
|
|
||||||
|
/* U+00B5 "µ" */
|
||||||
|
0xc9, 0x24, 0x92, 0x49, 0x27, 0xd0, 0x43, 0x0,
|
||||||
|
|
||||||
|
/* U+00B6 "¶" */
|
||||||
|
0x6d, 0x6b, 0x5a, 0xb4, 0xa5, 0x29, 0x40,
|
||||||
|
|
||||||
|
/* U+00B7 "·" */
|
||||||
|
0xf0,
|
||||||
|
|
||||||
|
/* U+00B8 "¸" */
|
||||||
|
0x47, 0x80,
|
||||||
|
|
||||||
|
/* U+00B9 "¹" */
|
||||||
|
0xc9, 0x25, 0xc0,
|
||||||
|
|
||||||
|
/* U+00BA "º" */
|
||||||
|
0xf9, 0x9f, 0xf,
|
||||||
|
|
||||||
|
/* U+00BB "»" */
|
||||||
|
0xa2, 0x8a, 0x55, 0x2a, 0x80,
|
||||||
|
|
||||||
|
/* U+00BC "¼" */
|
||||||
|
0x26, 0x22, 0x26, 0xf, 0x2, 0x66, 0x62,
|
||||||
|
|
||||||
|
/* U+00BD "½" */
|
||||||
|
0x23, 0x8, 0x42, 0x38, 0x1f, 0x3, 0x84, 0x44,
|
||||||
|
0x38,
|
||||||
|
|
||||||
|
/* U+00BE "¾" */
|
||||||
|
0x62, 0x62, 0x60, 0xf0, 0x26, 0xaf, 0x20,
|
||||||
|
|
||||||
|
/* U+00BF "¿" */
|
||||||
|
0x63, 0x0, 0x42, 0x22, 0x10, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00C0 "À" */
|
||||||
|
0x20, 0x80, 0xc, 0x30, 0xc3, 0xc, 0x49, 0x27,
|
||||||
|
0x92, 0xcc,
|
||||||
|
|
||||||
|
/* U+00C1 "Á" */
|
||||||
|
0x10, 0x40, 0xc, 0x30, 0xc3, 0xc, 0x49, 0x27,
|
||||||
|
0x92, 0xcc,
|
||||||
|
|
||||||
|
/* U+00C2 "Â" */
|
||||||
|
0x0, 0xc0, 0xc, 0x30, 0xc3, 0xc, 0x49, 0x27,
|
||||||
|
0x92, 0xcc,
|
||||||
|
|
||||||
|
/* U+00C3 "Ã" */
|
||||||
|
0x1, 0xa1, 0xc, 0x30, 0xc3, 0xc, 0x49, 0x27,
|
||||||
|
0x92, 0xcc,
|
||||||
|
|
||||||
|
/* U+00C4 "Ä" */
|
||||||
|
0x78, 0x3, 0xc, 0x30, 0xc3, 0x12, 0x49, 0xe4,
|
||||||
|
0xb3,
|
||||||
|
|
||||||
|
/* U+00C5 "Å" */
|
||||||
|
0x30, 0xc3, 0xc, 0x30, 0xc3, 0xc, 0x49, 0x27,
|
||||||
|
0x92, 0xcc,
|
||||||
|
|
||||||
|
/* U+00C6 "Æ" */
|
||||||
|
0x3c, 0xd3, 0xc, 0x39, 0x45, 0x1c, 0x57, 0xf0,
|
||||||
|
|
||||||
|
/* U+00C7 "Ç" */
|
||||||
|
0x79, 0x88, 0x88, 0x88, 0x97, 0x22, 0x60,
|
||||||
|
|
||||||
|
/* U+00C8 "È" */
|
||||||
|
0x41, 0x1, 0xf4, 0xa1, 0x8, 0x72, 0x10, 0x9f,
|
||||||
|
0x80,
|
||||||
|
|
||||||
|
/* U+00C9 "É" */
|
||||||
|
0x11, 0x81, 0xf4, 0xa1, 0x8, 0x72, 0x10, 0x9f,
|
||||||
|
0x80,
|
||||||
|
|
||||||
|
/* U+00CA "Ê" */
|
||||||
|
0x23, 0x91, 0xf4, 0xa1, 0x8, 0x72, 0x10, 0x9f,
|
||||||
|
0x80,
|
||||||
|
|
||||||
|
/* U+00CB "Ë" */
|
||||||
|
0x78, 0x3e, 0x94, 0x21, 0xe, 0x42, 0x13, 0xf0,
|
||||||
|
|
||||||
|
/* U+00CC "Ì" */
|
||||||
|
0x88, 0x74, 0x92, 0x49, 0x2e,
|
||||||
|
|
||||||
|
/* U+00CD "Í" */
|
||||||
|
0x28, 0x74, 0x92, 0x49, 0x2e,
|
||||||
|
|
||||||
|
/* U+00CE "Î" */
|
||||||
|
0x22, 0x80, 0xe2, 0x10, 0x84, 0x21, 0x8, 0x47,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+00CF "Ï" */
|
||||||
|
0xf0, 0x72, 0x22, 0x22, 0x22, 0x27,
|
||||||
|
|
||||||
|
/* U+00D0 "Ð" */
|
||||||
|
0xf9, 0x34, 0x51, 0x47, 0xd4, 0x51, 0x4f, 0xe0,
|
||||||
|
|
||||||
|
/* U+00D1 "Ñ" */
|
||||||
|
0x1, 0xa1, 0x33, 0x69, 0xa6, 0x9a, 0x69, 0x65,
|
||||||
|
0x96, 0xc8,
|
||||||
|
|
||||||
|
/* U+00D2 "Ò" */
|
||||||
|
0x41, 0x8, 0xe8, 0xc6, 0x31, 0x8c, 0x63, 0x17,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+00D3 "Ó" */
|
||||||
|
0x11, 0x8, 0xe8, 0xc6, 0x31, 0x8c, 0x63, 0x17,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+00D4 "Ô" */
|
||||||
|
0x22, 0x80, 0xe8, 0xc6, 0x31, 0x8c, 0x63, 0x17,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+00D5 "Õ" */
|
||||||
|
0x3, 0x80, 0xe8, 0xc6, 0x31, 0x8c, 0x63, 0x17,
|
||||||
|
0x0,
|
||||||
|
|
||||||
|
/* U+00D6 "Ö" */
|
||||||
|
0xf0, 0x1d, 0x18, 0xc6, 0x31, 0x8c, 0x62, 0xe0,
|
||||||
|
|
||||||
|
/* U+00D7 "×" */
|
||||||
|
0x85, 0x23, 0xc, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+00D8 "Ø" */
|
||||||
|
0x3, 0xe7, 0x3b, 0xd6, 0xbd, 0xce, 0x7c, 0x0,
|
||||||
|
|
||||||
|
/* U+00D9 "Ù" */
|
||||||
|
0x20, 0x80, 0x33, 0x49, 0x24, 0x92, 0x49, 0x24,
|
||||||
|
0x92, 0x30,
|
||||||
|
|
||||||
|
/* U+00DA "Ú" */
|
||||||
|
0x10, 0x40, 0x33, 0x49, 0x24, 0x92, 0x49, 0x24,
|
||||||
|
0x92, 0x30,
|
||||||
|
|
||||||
|
/* U+00DB "Û" */
|
||||||
|
0x31, 0xe0, 0x33, 0x49, 0x24, 0x92, 0x49, 0x24,
|
||||||
|
0x92, 0x30,
|
||||||
|
|
||||||
|
/* U+00DC "Ü" */
|
||||||
|
0x78, 0xc, 0xd2, 0x49, 0x24, 0x92, 0x49, 0x24,
|
||||||
|
0x8c,
|
||||||
|
|
||||||
|
/* U+00DD "Ý" */
|
||||||
|
0x8, 0x20, 0x7, 0x74, 0x48, 0x8a, 0x14, 0x10,
|
||||||
|
0x20, 0x40, 0x83, 0x80,
|
||||||
|
|
||||||
|
/* U+00DE "Þ" */
|
||||||
|
0xc2, 0x1c, 0x94, 0xa5, 0x2e, 0x46, 0x0,
|
||||||
|
|
||||||
|
/* U+00DF "ß" */
|
||||||
|
0x31, 0x24, 0x92, 0x49, 0x44, 0xd1, 0x4f, 0x60,
|
||||||
|
|
||||||
|
/* U+00E0 "à" */
|
||||||
|
0x40, 0x80, 0x1c, 0x48, 0x27, 0x92, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+00E1 "á" */
|
||||||
|
0x10, 0xc2, 0x1c, 0x48, 0x27, 0x92, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+00E2 "â" */
|
||||||
|
0x31, 0x64, 0x1c, 0x48, 0x27, 0x92, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+00E3 "ã" */
|
||||||
|
0x69, 0x60, 0x1c, 0x48, 0x27, 0x92, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+00E4 "ä" */
|
||||||
|
0x78, 0x0, 0x1c, 0x48, 0x27, 0x92, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+00E5 "å" */
|
||||||
|
0x30, 0xc3, 0x1c, 0x48, 0x27, 0x92, 0x89, 0xf0,
|
||||||
|
|
||||||
|
/* U+00E6 "æ" */
|
||||||
|
0x7d, 0x4b, 0xfa, 0x57, 0xe0,
|
||||||
|
|
||||||
|
/* U+00E7 "ç" */
|
||||||
|
0x79, 0x88, 0x89, 0x72, 0x26,
|
||||||
|
|
||||||
|
/* U+00E8 "è" */
|
||||||
|
0x41, 0x8, 0xe8, 0xc7, 0xf0, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00E9 "é" */
|
||||||
|
0x11, 0x8, 0xe8, 0xc7, 0xf0, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00EA "ê" */
|
||||||
|
0x22, 0x80, 0xe8, 0xc7, 0xf0, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00EB "ë" */
|
||||||
|
0xf0, 0x0, 0xe8, 0xc7, 0xf0, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00EC "ì" */
|
||||||
|
0x41, 0x9, 0xc2, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+00ED "í" */
|
||||||
|
0x11, 0x1, 0xc2, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+00EE "î" */
|
||||||
|
0x62, 0x81, 0xc2, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+00EF "ï" */
|
||||||
|
0xf0, 0x1, 0xc2, 0x10, 0x84, 0x27, 0xc0,
|
||||||
|
|
||||||
|
/* U+00F0 "ð" */
|
||||||
|
0x41, 0xe, 0xa7, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00F1 "ñ" */
|
||||||
|
0x29, 0x60, 0x3c, 0x49, 0x24, 0x92, 0x4b, 0x30,
|
||||||
|
|
||||||
|
/* U+00F2 "ò" */
|
||||||
|
0x41, 0x8, 0xe8, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00F3 "ó" */
|
||||||
|
0x11, 0x8, 0xe8, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00F4 "ô" */
|
||||||
|
0x22, 0x80, 0xe8, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00F5 "õ" */
|
||||||
|
0x6d, 0x80, 0xe8, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00F6 "ö" */
|
||||||
|
0xf0, 0x0, 0xe8, 0xc6, 0x31, 0x8b, 0x80,
|
||||||
|
|
||||||
|
/* U+00F7 "÷" */
|
||||||
|
0x30, 0x0, 0x3f, 0x0, 0x3, 0x0,
|
||||||
|
|
||||||
|
/* U+00F8 "ø" */
|
||||||
|
0x7c, 0xef, 0x5e, 0xe7, 0xc0,
|
||||||
|
|
||||||
|
/* U+00F9 "ù" */
|
||||||
|
0x20, 0x80, 0x32, 0x49, 0x24, 0x92, 0x48, 0xf0,
|
||||||
|
|
||||||
|
/* U+00FA "ú" */
|
||||||
|
0x10, 0x40, 0x32, 0x49, 0x24, 0x92, 0x48, 0xf0,
|
||||||
|
|
||||||
|
/* U+00FB "û" */
|
||||||
|
0x31, 0xe0, 0x32, 0x49, 0x24, 0x92, 0x48, 0xf0,
|
||||||
|
|
||||||
|
/* U+00FC "ü" */
|
||||||
|
0x78, 0x0, 0x32, 0x49, 0x24, 0x92, 0x48, 0xf0,
|
||||||
|
|
||||||
|
/* U+00FD "ý" */
|
||||||
|
0x18, 0x40, 0x33, 0x49, 0x24, 0x8c, 0x30, 0xc2,
|
||||||
|
0x8, 0x60,
|
||||||
|
|
||||||
|
/* U+00FE "þ" */
|
||||||
|
0xc1, 0x4, 0x1e, 0x45, 0x14, 0x51, 0x45, 0xe4,
|
||||||
|
0x10, 0xe0
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* GLYPH DESCRIPTION
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
|
||||||
|
{.bitmap_index = 0, .adv_w = 0, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0} /* id = 0 reserved */,
|
||||||
|
{.bitmap_index = 0, .adv_w = 192, .box_w = 1, .box_h = 1, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1, .adv_w = 96, .box_w = 1, .box_h = 1, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 2, .adv_w = 96, .box_w = 2, .box_h = 10, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 5, .adv_w = 96, .box_w = 3, .box_h = 4, .ofs_x = 1, .ofs_y = 6},
|
||||||
|
{.bitmap_index = 7, .adv_w = 96, .box_w = 5, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
|
||||||
|
{.bitmap_index = 15, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 1, .ofs_y = -1},
|
||||||
|
{.bitmap_index = 24, .adv_w = 96, .box_w = 4, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 29, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 36, .adv_w = 96, .box_w = 1, .box_h = 4, .ofs_x = 3, .ofs_y = 6},
|
||||||
|
{.bitmap_index = 37, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 2, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 44, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 0, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 51, .adv_w = 96, .box_w = 5, .box_h = 5, .ofs_x = 1, .ofs_y = 6},
|
||||||
|
{.bitmap_index = 55, .adv_w = 96, .box_w = 5, .box_h = 5, .ofs_x = 1, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 59, .adv_w = 96, .box_w = 2, .box_h = 5, .ofs_x = 2, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 61, .adv_w = 96, .box_w = 4, .box_h = 1, .ofs_x = 1, .ofs_y = 4},
|
||||||
|
{.bitmap_index = 62, .adv_w = 96, .box_w = 2, .box_h = 2, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 63, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 70, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 77, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 84, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 91, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 98, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 105, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 112, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 119, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 126, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 133, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 140, .adv_w = 96, .box_w = 2, .box_h = 7, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 142, .adv_w = 96, .box_w = 2, .box_h = 10, .ofs_x = 2, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 145, .adv_w = 96, .box_w = 5, .box_h = 8, .ofs_x = 1, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 150, .adv_w = 96, .box_w = 4, .box_h = 4, .ofs_x = 1, .ofs_y = 3},
|
||||||
|
{.bitmap_index = 152, .adv_w = 96, .box_w = 5, .box_h = 8, .ofs_x = 0, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 157, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 164, .adv_w = 96, .box_w = 5, .box_h = 12, .ofs_x = 1, .ofs_y = -1},
|
||||||
|
{.bitmap_index = 172, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 180, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 188, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 195, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 203, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 210, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 217, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 224, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 232, .adv_w = 96, .box_w = 3, .box_h = 10, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 236, .adv_w = 96, .box_w = 4, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 241, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 249, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 256, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 264, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 272, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 279, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 286, .adv_w = 96, .box_w = 5, .box_h = 12, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 294, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 302, .adv_w = 96, .box_w = 4, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 307, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 314, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 322, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 330, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 338, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 346, .adv_w = 96, .box_w = 7, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 355, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 362, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 369, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 376, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 383, .adv_w = 96, .box_w = 4, .box_h = 3, .ofs_x = 1, .ofs_y = 7},
|
||||||
|
{.bitmap_index = 385, .adv_w = 96, .box_w = 4, .box_h = 1, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 386, .adv_w = 96, .box_w = 3, .box_h = 3, .ofs_x = 1, .ofs_y = 7},
|
||||||
|
{.bitmap_index = 388, .adv_w = 96, .box_w = 6, .box_h = 7, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 394, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 402, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 407, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 415, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 420, .adv_w = 96, .box_w = 4, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 425, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 432, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 440, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 447, .adv_w = 96, .box_w = 4, .box_h = 13, .ofs_x = 0, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 454, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 462, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 469, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 474, .adv_w = 96, .box_w = 6, .box_h = 7, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 480, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 485, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 493, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 1, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 501, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 506, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 511, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 518, .adv_w = 96, .box_w = 6, .box_h = 7, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 524, .adv_w = 96, .box_w = 6, .box_h = 7, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 530, .adv_w = 96, .box_w = 6, .box_h = 7, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 536, .adv_w = 96, .box_w = 6, .box_h = 7, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 542, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 550, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 555, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 562, .adv_w = 96, .box_w = 1, .box_h = 14, .ofs_x = 3, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 564, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 571, .adv_w = 96, .box_w = 6, .box_h = 3, .ofs_x = 0, .ofs_y = 3},
|
||||||
|
{.bitmap_index = 574, .adv_w = 96, .box_w = 1, .box_h = 1, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 575, .adv_w = 96, .box_w = 2, .box_h = 10, .ofs_x = 2, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 578, .adv_w = 96, .box_w = 5, .box_h = 9, .ofs_x = 1, .ofs_y = -1},
|
||||||
|
{.bitmap_index = 584, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 591, .adv_w = 192, .box_w = 5, .box_h = 5, .ofs_x = 3, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 595, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 603, .adv_w = 96, .box_w = 1, .box_h = 14, .ofs_x = 3, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 605, .adv_w = 192, .box_w = 5, .box_h = 14, .ofs_x = 3, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 614, .adv_w = 96, .box_w = 4, .box_h = 1, .ofs_x = 1, .ofs_y = 8},
|
||||||
|
{.bitmap_index = 615, .adv_w = 192, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 627, .adv_w = 96, .box_w = 4, .box_h = 6, .ofs_x = 1, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 630, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 635, .adv_w = 96, .box_w = 4, .box_h = 3, .ofs_x = 1, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 637, .adv_w = 96, .box_w = 4, .box_h = 1, .ofs_x = 1, .ofs_y = 4},
|
||||||
|
{.bitmap_index = 638, .adv_w = 192, .box_w = 9, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 650, .adv_w = 96, .box_w = 4, .box_h = 1, .ofs_x = 1, .ofs_y = 8},
|
||||||
|
{.bitmap_index = 651, .adv_w = 96, .box_w = 3, .box_h = 3, .ofs_x = 2, .ofs_y = 7},
|
||||||
|
{.bitmap_index = 653, .adv_w = 192, .box_w = 5, .box_h = 8, .ofs_x = 4, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 658, .adv_w = 96, .box_w = 4, .box_h = 6, .ofs_x = 1, .ofs_y = 4},
|
||||||
|
{.bitmap_index = 661, .adv_w = 96, .box_w = 4, .box_h = 6, .ofs_x = 1, .ofs_y = 4},
|
||||||
|
{.bitmap_index = 664, .adv_w = 96, .box_w = 3, .box_h = 3, .ofs_x = 2, .ofs_y = 7},
|
||||||
|
{.bitmap_index = 666, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 674, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 681, .adv_w = 192, .box_w = 2, .box_h = 2, .ofs_x = 5, .ofs_y = 4},
|
||||||
|
{.bitmap_index = 682, .adv_w = 96, .box_w = 3, .box_h = 3, .ofs_x = 2, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 684, .adv_w = 96, .box_w = 3, .box_h = 6, .ofs_x = 2, .ofs_y = 4},
|
||||||
|
{.bitmap_index = 687, .adv_w = 96, .box_w = 4, .box_h = 6, .ofs_x = 1, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 690, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 0, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 695, .adv_w = 96, .box_w = 4, .box_h = 14, .ofs_x = 0, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 702, .adv_w = 96, .box_w = 5, .box_h = 14, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 711, .adv_w = 96, .box_w = 4, .box_h = 13, .ofs_x = 1, .ofs_y = -2},
|
||||||
|
{.bitmap_index = 718, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 725, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 735, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 745, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 755, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 765, .adv_w = 96, .box_w = 6, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 774, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 784, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 792, .adv_w = 96, .box_w = 4, .box_h = 13, .ofs_x = 1, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 799, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 808, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 817, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 826, .adv_w = 96, .box_w = 5, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 834, .adv_w = 96, .box_w = 3, .box_h = 13, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 839, .adv_w = 96, .box_w = 3, .box_h = 13, .ofs_x = 2, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 844, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 853, .adv_w = 96, .box_w = 4, .box_h = 12, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 859, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 867, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 877, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 886, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 895, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 904, .adv_w = 96, .box_w = 5, .box_h = 13, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 913, .adv_w = 96, .box_w = 5, .box_h = 12, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 921, .adv_w = 192, .box_w = 6, .box_h = 6, .ofs_x = 3, .ofs_y = 2},
|
||||||
|
{.bitmap_index = 926, .adv_w = 96, .box_w = 5, .box_h = 12, .ofs_x = 1, .ofs_y = -1},
|
||||||
|
{.bitmap_index = 934, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 944, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 954, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 964, .adv_w = 96, .box_w = 6, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 973, .adv_w = 96, .box_w = 7, .box_h = 13, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 985, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 992, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1000, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1008, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1016, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1024, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1032, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1040, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1048, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1053, .adv_w = 96, .box_w = 4, .box_h = 10, .ofs_x = 1, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 1058, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1065, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1072, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1079, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1086, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1093, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1100, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1107, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1114, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1121, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1129, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1136, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1143, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1150, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1157, .adv_w = 96, .box_w = 5, .box_h = 10, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1164, .adv_w = 192, .box_w = 6, .box_h = 7, .ofs_x = 3, .ofs_y = 1},
|
||||||
|
{.bitmap_index = 1170, .adv_w = 96, .box_w = 5, .box_h = 7, .ofs_x = 1, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1175, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1183, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1191, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1199, .adv_w = 96, .box_w = 6, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
|
||||||
|
{.bitmap_index = 1207, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = -3},
|
||||||
|
{.bitmap_index = 1217, .adv_w = 96, .box_w = 6, .box_h = 13, .ofs_x = 0, .ofs_y = -3}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*---------------------
|
||||||
|
* CHARACTER MAPPING
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*Collect the unicode lists and glyph_id offsets*/
|
||||||
|
static const lv_font_fmt_txt_cmap_t cmaps[] =
|
||||||
|
{
|
||||||
|
{
|
||||||
|
.range_start = 13, .range_length = 1, .glyph_id_start = 1,
|
||||||
|
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.range_start = 32, .range_length = 95, .glyph_id_start = 2,
|
||||||
|
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.range_start = 160, .range_length = 95, .glyph_id_start = 97,
|
||||||
|
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*--------------------
|
||||||
|
* ALL CUSTOM DATA
|
||||||
|
*--------------------*/
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
/*Store all the custom data of the font*/
|
||||||
|
static lv_font_fmt_txt_glyph_cache_t cache;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
static const lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#else
|
||||||
|
static lv_font_fmt_txt_dsc_t font_dsc = {
|
||||||
|
#endif
|
||||||
|
.glyph_bitmap = glyph_bitmap,
|
||||||
|
.glyph_dsc = glyph_dsc,
|
||||||
|
.cmaps = cmaps,
|
||||||
|
.kern_dsc = NULL,
|
||||||
|
.kern_scale = 0,
|
||||||
|
.cmap_num = 3,
|
||||||
|
.bpp = 1,
|
||||||
|
.kern_classes = 0,
|
||||||
|
.bitmap_format = 0,
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
.cache = &cache
|
||||||
|
#endif
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
extern const lv_font_t lv_font_montserrat_12;
|
||||||
|
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* PUBLIC FONT
|
||||||
|
*----------------*/
|
||||||
|
|
||||||
|
/*Initialize a public general font descriptor*/
|
||||||
|
#if LVGL_VERSION_MAJOR >= 8
|
||||||
|
const lv_font_t InziuIosevka_Slab_CC_12px = {
|
||||||
|
#else
|
||||||
|
lv_font_t InziuIosevka_Slab_CC_12px = {
|
||||||
|
#endif
|
||||||
|
.get_glyph_dsc = lv_font_get_glyph_dsc_fmt_txt, /*Function pointer to get glyph's data*/
|
||||||
|
.get_glyph_bitmap = lv_font_get_bitmap_fmt_txt, /*Function pointer to get glyph's bitmap*/
|
||||||
|
.line_height = 16, /*The maximum line height required by the font*/
|
||||||
|
.base_line = 3, /*Baseline measured from the bottom of the line*/
|
||||||
|
#if !(LVGL_VERSION_MAJOR == 6 && LVGL_VERSION_MINOR == 0)
|
||||||
|
.subpx = LV_FONT_SUBPX_NONE,
|
||||||
|
#endif
|
||||||
|
#if LV_VERSION_CHECK(7, 4, 0) || LVGL_VERSION_MAJOR >= 8
|
||||||
|
.underline_position = -2,
|
||||||
|
.underline_thickness = 1,
|
||||||
|
#endif
|
||||||
|
.dsc = &font_dsc, /*The custom font data. Will be accessed by `get_glyph_bitmap/dsc` */
|
||||||
|
#if LV_VERSION_CHECK(8, 2, 0) || LVGL_VERSION_MAJOR >= 9
|
||||||
|
.fallback = &lv_font_montserrat_12,
|
||||||
|
#endif
|
||||||
|
.user_data = NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /*#if INZIUIOSEVKA_SLAB_CC_12PX*/
|
||||||
1036
components/user_fonts/InziuIosevka_Slab_CC_16px.c
Normal file
1036
components/user_fonts/InziuIosevka_Slab_CC_16px.c
Normal file
File diff suppressed because it is too large
Load Diff
1136
components/user_fonts/InziuIosevka_Slab_CC_20px.c
Normal file
1136
components/user_fonts/InziuIosevka_Slab_CC_20px.c
Normal file
File diff suppressed because it is too large
Load Diff
1289
components/user_fonts/InziuIosevka_Slab_CC_24px.c
Normal file
1289
components/user_fonts/InziuIosevka_Slab_CC_24px.c
Normal file
File diff suppressed because it is too large
Load Diff
1641
components/user_fonts/InziuIosevka_Slab_CC_32px.c
Normal file
1641
components/user_fonts/InziuIosevka_Slab_CC_32px.c
Normal file
File diff suppressed because it is too large
Load Diff
26
components/user_fonts/user_fonts.h
Normal file
26
components/user_fonts/user_fonts.h
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef USER_FONTS_H
|
||||||
|
#define USER_FONTS_H
|
||||||
|
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* DSEG14 Classic Bold Italic — segmented display digits only (0-9 : ; , .) */
|
||||||
|
extern const lv_font_t DSEG14C_BI_32px;
|
||||||
|
extern const lv_font_t DSEG14C_BI_40px;
|
||||||
|
extern const lv_font_t DSEG14C_BI_50px;
|
||||||
|
|
||||||
|
/* Inziu Iosevka Slab CC — monospace, Latin range 0-254 */
|
||||||
|
extern const lv_font_t InziuIosevka_Slab_CC_12px;
|
||||||
|
extern const lv_font_t InziuIosevka_Slab_CC_16px;
|
||||||
|
extern const lv_font_t InziuIosevka_Slab_CC_20px;
|
||||||
|
extern const lv_font_t InziuIosevka_Slab_CC_24px;
|
||||||
|
extern const lv_font_t InziuIosevka_Slab_CC_32px;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -92,10 +92,40 @@ static void parse_stats_json(const char *data, int len)
|
|||||||
s_stats.services[i].name[WS_SERVICE_NAME_LEN - 1] = '\0';
|
s_stats.services[i].name[WS_SERVICE_NAME_LEN - 1] = '\0';
|
||||||
}
|
}
|
||||||
if (status && status->valuestring) {
|
if (status && status->valuestring) {
|
||||||
s_stats.services[i].running = (strcmp(status->valuestring, "running") == 0);
|
if (strcmp(status->valuestring, "running") == 0) {
|
||||||
|
s_stats.services[i].status = SVC_RUNNING;
|
||||||
|
} else if (strcmp(status->valuestring, "warning") == 0) {
|
||||||
|
s_stats.services[i].status = SVC_WARNING;
|
||||||
|
} else {
|
||||||
|
s_stats.services[i].status = SVC_STOPPED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse local_time object for RTC sync */
|
||||||
|
cJSON *lt = cJSON_GetObjectItem(root, "local_time");
|
||||||
|
if (cJSON_IsObject(lt)) {
|
||||||
|
cJSON *y = cJSON_GetObjectItem(lt, "y");
|
||||||
|
cJSON *mo = cJSON_GetObjectItem(lt, "mo");
|
||||||
|
cJSON *d = cJSON_GetObjectItem(lt, "d");
|
||||||
|
cJSON *h = cJSON_GetObjectItem(lt, "h");
|
||||||
|
cJSON *m = cJSON_GetObjectItem(lt, "m");
|
||||||
|
cJSON *s = cJSON_GetObjectItem(lt, "s");
|
||||||
|
if (y && mo && d && h && m && s) {
|
||||||
|
s_stats.time_year = (uint16_t)y->valuedouble;
|
||||||
|
s_stats.time_month = (uint8_t)mo->valuedouble;
|
||||||
|
s_stats.time_day = (uint8_t)d->valuedouble;
|
||||||
|
s_stats.time_hour = (uint8_t)h->valuedouble;
|
||||||
|
s_stats.time_minute = (uint8_t)m->valuedouble;
|
||||||
|
s_stats.time_second = (uint8_t)s->valuedouble;
|
||||||
|
s_stats.time_valid = true;
|
||||||
|
} else {
|
||||||
|
s_stats.time_valid = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s_stats.time_valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
s_stats.valid = true;
|
s_stats.valid = true;
|
||||||
xSemaphoreGive(s_stats_mutex);
|
xSemaphoreGive(s_stats_mutex);
|
||||||
@@ -180,6 +210,14 @@ void ws_client_stop(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ws_client_reconnect(void)
|
||||||
|
{
|
||||||
|
if (!s_client) return;
|
||||||
|
ESP_LOGI(TAG, "Manual WS reconnect triggered");
|
||||||
|
esp_websocket_client_close(s_client, pdMS_TO_TICKS(2000));
|
||||||
|
s_last_data_tick = xTaskGetTickCount();
|
||||||
|
}
|
||||||
|
|
||||||
ws_state_t ws_client_get_state(void)
|
ws_state_t ws_client_get_state(void)
|
||||||
{
|
{
|
||||||
return s_state;
|
return s_state;
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define WS_MAX_SERVICES 8
|
#define WS_MAX_SERVICES 20
|
||||||
#define WS_SERVICE_NAME_LEN 16
|
#define WS_SERVICE_NAME_LEN 16
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -18,9 +18,15 @@ typedef enum {
|
|||||||
WS_STATE_ERROR,
|
WS_STATE_ERROR,
|
||||||
} ws_state_t;
|
} ws_state_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SVC_STOPPED = 0,
|
||||||
|
SVC_WARNING,
|
||||||
|
SVC_RUNNING,
|
||||||
|
} ws_svc_status_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char name[WS_SERVICE_NAME_LEN];
|
char name[WS_SERVICE_NAME_LEN];
|
||||||
bool running;
|
ws_svc_status_t status;
|
||||||
} ws_service_t;
|
} ws_service_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -36,6 +42,15 @@ typedef struct {
|
|||||||
uint8_t service_count;
|
uint8_t service_count;
|
||||||
uint32_t last_update; // timestamp from server
|
uint32_t last_update; // timestamp from server
|
||||||
bool valid; // set true after first successful parse
|
bool valid; // set true after first successful parse
|
||||||
|
|
||||||
|
/* Broken-down local time from Pi for RTC sync */
|
||||||
|
uint16_t time_year;
|
||||||
|
uint8_t time_month;
|
||||||
|
uint8_t time_day;
|
||||||
|
uint8_t time_hour;
|
||||||
|
uint8_t time_minute;
|
||||||
|
uint8_t time_second;
|
||||||
|
bool time_valid; // true when local_time object was parsed
|
||||||
} pi_stats_t;
|
} pi_stats_t;
|
||||||
|
|
||||||
typedef void (*ws_data_callback_t)(const pi_stats_t *stats);
|
typedef void (*ws_data_callback_t)(const pi_stats_t *stats);
|
||||||
@@ -44,6 +59,7 @@ typedef void (*ws_state_callback_t)(ws_state_t state);
|
|||||||
void ws_client_init(const char *uri);
|
void ws_client_init(const char *uri);
|
||||||
void ws_client_start(void);
|
void ws_client_start(void);
|
||||||
void ws_client_stop(void);
|
void ws_client_stop(void);
|
||||||
|
void ws_client_reconnect(void);
|
||||||
ws_state_t ws_client_get_state(void);
|
ws_state_t ws_client_get_state(void);
|
||||||
void ws_client_get_stats(pi_stats_t *out);
|
void ws_client_get_stats(pi_stats_t *out);
|
||||||
void ws_client_set_data_callback(ws_data_callback_t cb);
|
void ws_client_set_data_callback(ws_data_callback_t cb);
|
||||||
|
|||||||
9
pi/.gitignore
vendored
Normal file
9
pi/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# python artifacts
|
||||||
|
*/__pycache__
|
||||||
|
__pycache__/
|
||||||
|
*.pyo
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# configs
|
||||||
|
config/
|
||||||
|
!config/alarms.sample.json
|
||||||
36
pi/PLAN.md
Normal file
36
pi/PLAN.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Pi Servers -- Roadmap
|
||||||
|
|
||||||
|
## Docker Compose
|
||||||
|
|
||||||
|
Containerize the pi servers for easier deployment.
|
||||||
|
|
||||||
|
### Options
|
||||||
|
|
||||||
|
1. **Single service** -- `run_all.py` as the entrypoint, both servers in one container
|
||||||
|
2. **Split services** -- separate containers for `stats_server.py` and `contents_server.py`
|
||||||
|
|
||||||
|
Single service is simpler. Split services allow independent scaling and restarts.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
- Volume mount `assets/` and `config/alarms.json` so they're editable without rebuilding
|
||||||
|
- Expose ports 8765 and 8766
|
||||||
|
- Network mode `host` or a bridge with known IPs for ESP32 discovery
|
||||||
|
- Restart policy: `unless-stopped`
|
||||||
|
|
||||||
|
## Repository Extraction
|
||||||
|
|
||||||
|
The `pi/` directory will become its own git repository.
|
||||||
|
|
||||||
|
### Steps
|
||||||
|
|
||||||
|
1. Extract `pi/` into a standalone repo with its own `README.md`, `requirements.txt`, and CI
|
||||||
|
2. Add it back to this project as a git submodule
|
||||||
|
3. The interface contract between the two repos is the WebSocket protocol -- JSON schemas and binary frame formats documented in `README.md`
|
||||||
|
|
||||||
|
### Benefits
|
||||||
|
|
||||||
|
- Independent versioning and release cycle
|
||||||
|
- Pi-side contributors don't need the ESP-IDF toolchain
|
||||||
|
- CI can test the Python servers in isolation
|
||||||
|
- Cleaner separation of concerns between embedded firmware and host services
|
||||||
132
pi/README.md
Normal file
132
pi/README.md
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# Pi Dashboard Servers
|
||||||
|
|
||||||
|
WebSocket servers that feed system stats, alarm audio, and status images to the ESP32-S3 RLCD dashboard.
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
pi/
|
||||||
|
run_all.py # Launches both servers as child processes
|
||||||
|
stats_server.py # Real system stats over WebSocket (port 8765)
|
||||||
|
contents_server.py # Alarm audio + status images over WebSocket (port 8766)
|
||||||
|
mock_server.py # Drop-in replacement for stats_server with random data
|
||||||
|
audio_handler.py # WAV loading, PCM chunking, alarm streaming
|
||||||
|
image_handler.py # PNG to 1-bit monochrome conversion, alpha compositing
|
||||||
|
alarm_scheduler.py # Loads and validates alarm config, checks firing schedule
|
||||||
|
requirements.txt
|
||||||
|
config/
|
||||||
|
alarms.json # Alarm schedule configuration
|
||||||
|
assets/
|
||||||
|
alarm/ # WAV files for alarm audio
|
||||||
|
img/ # Status images (idle.png, on_alarm.png)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
Python 3.10+
|
||||||
|
|
||||||
|
```
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
Dependencies: `websockets`, `psutil`, `Pillow`
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
Start both servers:
|
||||||
|
|
||||||
|
```
|
||||||
|
python run_all.py # both servers, default config
|
||||||
|
python run_all.py --config path/to.json # both servers, custom config
|
||||||
|
```
|
||||||
|
|
||||||
|
Or run individually:
|
||||||
|
|
||||||
|
```
|
||||||
|
python stats_server.py # port 8765 only
|
||||||
|
python contents_server.py --config path/to.json # port 8766, custom config
|
||||||
|
python mock_server.py # port 8765, random data (no psutil needed)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Servers
|
||||||
|
|
||||||
|
### stats_server.py -- port 8765
|
||||||
|
|
||||||
|
Pushes a JSON object every 2 seconds with real system metrics from `psutil`:
|
||||||
|
|
||||||
|
- `cpu_pct`, `mem_pct`, `mem_used_mb`, `disk_pct`
|
||||||
|
- `cpu_temp` (reads `/sys/class/thermal/` as fallback)
|
||||||
|
- `uptime_hrs`, `net_rx_kbps`, `net_tx_kbps`
|
||||||
|
- `services` (mocked until systemd integration)
|
||||||
|
- `local_time` fields for RTC sync (`y`, `mo`, `d`, `h`, `m`, `s`)
|
||||||
|
|
||||||
|
### contents_server.py -- port 8766
|
||||||
|
|
||||||
|
Serves alarm audio and status images. Protocol:
|
||||||
|
|
||||||
|
**Status image:**
|
||||||
|
1. Text frame: `{"type":"status_image","width":120,"height":120}`
|
||||||
|
2. Binary frame: 1-bit monochrome bitmap (1800 bytes)
|
||||||
|
|
||||||
|
**Alarm audio:**
|
||||||
|
1. Text frame: `{"type":"alarm_start","sample_rate":N,"channels":N,"bits":N}`
|
||||||
|
2. Binary frames: raw PCM chunks (4096 bytes each, paced at ~90% real-time)
|
||||||
|
3. Text frame: `{"type":"alarm_stop"}`
|
||||||
|
|
||||||
|
Loads alarm config from `config/alarms.json` (override with `--config`). Checks schedule every 5 seconds, fires once per matched minute. If no config or empty config, sends idle image and blocks forever. On alarm: switches to alarm image, streams audio, switches back to idle.
|
||||||
|
|
||||||
|
### mock_server.py -- port 8765
|
||||||
|
|
||||||
|
Same JSON schema and 2-second push interval as `stats_server.py`, but all values are randomized. No `psutil` dependency -- useful for development on non-Pi machines.
|
||||||
|
|
||||||
|
Does not include `local_time` fields.
|
||||||
|
|
||||||
|
## Alarm Configuration
|
||||||
|
|
||||||
|
Config file: `config/alarms.json` -- a single alarm object or an array of alarm objects.
|
||||||
|
|
||||||
|
Example with two alarms:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"alarm_time": "0730",
|
||||||
|
"alarm_days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
|
||||||
|
"alarm_audio": "assets/alarm/alarm_test.wav",
|
||||||
|
"alarm_image": "assets/img/on_alarm.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alarm_time": "2300",
|
||||||
|
"alarm_audio": "assets/alarm/sleep.wav",
|
||||||
|
"alarm_image": "assets/img/sleep.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Field | Type | Required | Description |
|
||||||
|
|-------|------|----------|-------------|
|
||||||
|
| `alarm_time` | `string` | Yes | 4-digit HHMM, 24-hour. Fires on the matched minute. |
|
||||||
|
| `alarm_days` | `string[]` | No | 3-letter abbreviations: `Mon`–`Sun`. If omitted, fires every day. |
|
||||||
|
| `alarm_dates` | `string[]` | No | `MM/DD` strings. Ignored if `alarm_days` is also set. |
|
||||||
|
| `alarm_audio` | `string` | No | WAV path, relative to `pi/`. Default: `assets/alarm/alarm_test.wav`. |
|
||||||
|
| `alarm_image` | `string` | No | Status PNG path, relative to `pi/`. Default: `assets/img/on_alarm.png`. |
|
||||||
|
|
||||||
|
If both `alarm_days` and `alarm_dates` are present, `alarm_days` takes priority.
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
|
||||||
|
### audio_handler.py
|
||||||
|
|
||||||
|
- `find_wav(path=None)` -- uses the given path if it exists, otherwise falls back to glob in `assets/alarm/`
|
||||||
|
- `read_wav(path)` -- reads WAV, returns `(pcm_bytes, sample_rate, channels, bits)`
|
||||||
|
- `stream_alarm(ws, pcm, sr, ch, bits)` -- streams one alarm cycle over WebSocket
|
||||||
|
|
||||||
|
### image_handler.py
|
||||||
|
|
||||||
|
- `load_status_image(path)` -- loads PNG, composites transparency onto white, converts to 1-bit 120x120 monochrome bitmap (black=1, MSB-first)
|
||||||
|
- `send_status_image(ws, img_bytes)` -- sends status image header + binary over WebSocket
|
||||||
|
|
||||||
|
### alarm_scheduler.py
|
||||||
|
|
||||||
|
- `load_config(path)` -- reads and validates alarm JSON; returns list of alarm dicts or `None`
|
||||||
|
- `should_fire(config)` -- checks a single alarm entry against current local time
|
||||||
135
pi/alarm_scheduler.py
Normal file
135
pi/alarm_scheduler.py
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
"""Alarm scheduler — load config and check firing schedule."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
DEFAULT_CONFIG_PATH = Path(__file__).parent / "config" / "alarms.json"
|
||||||
|
|
||||||
|
VALID_DAYS = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}
|
||||||
|
TIME_RE = re.compile(r"^([01]\d|2[0-3])[0-5]\d$")
|
||||||
|
DATE_RE = re.compile(r"^(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])$")
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_entry(entry: dict, index: int) -> dict | None:
|
||||||
|
"""Validate a single alarm entry. Returns it if valid, None otherwise."""
|
||||||
|
if not isinstance(entry, dict):
|
||||||
|
log.warning("Alarm #%d: expected object, got %s", index, type(entry).__name__)
|
||||||
|
return None
|
||||||
|
|
||||||
|
alarm_time = entry.get("alarm_time")
|
||||||
|
if alarm_time is None:
|
||||||
|
log.warning("Alarm #%d: missing required field 'alarm_time'", index)
|
||||||
|
return None
|
||||||
|
if not isinstance(alarm_time, str) or not TIME_RE.match(alarm_time):
|
||||||
|
log.warning("Alarm #%d: invalid alarm_time '%s' — must be 4-digit HHMM", index, alarm_time)
|
||||||
|
return None
|
||||||
|
|
||||||
|
alarm_days = entry.get("alarm_days")
|
||||||
|
if alarm_days is not None:
|
||||||
|
if not isinstance(alarm_days, list) or not all(isinstance(d, str) for d in alarm_days):
|
||||||
|
log.warning("Alarm #%d: alarm_days must be a list of strings", index)
|
||||||
|
return None
|
||||||
|
bad = [d for d in alarm_days if d not in VALID_DAYS]
|
||||||
|
if bad:
|
||||||
|
log.warning("Alarm #%d: invalid day abbreviations: %s", index, bad)
|
||||||
|
return None
|
||||||
|
|
||||||
|
alarm_dates = entry.get("alarm_dates")
|
||||||
|
if alarm_dates is not None:
|
||||||
|
if not isinstance(alarm_dates, list) or not all(isinstance(d, str) for d in alarm_dates):
|
||||||
|
log.warning("Alarm #%d: alarm_dates must be a list of strings", index)
|
||||||
|
return None
|
||||||
|
bad = [d for d in alarm_dates if not DATE_RE.match(d)]
|
||||||
|
if bad:
|
||||||
|
log.warning("Alarm #%d: invalid date formats (expected MM/DD): %s", index, bad)
|
||||||
|
return None
|
||||||
|
|
||||||
|
log.info("Alarm #%d: time=%s days=%s", index, alarm_time, alarm_days or "(every day)")
|
||||||
|
return entry
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(path: Path) -> list[dict] | None:
|
||||||
|
"""Read and validate alarm config JSON.
|
||||||
|
|
||||||
|
Accepts either a single alarm object or an array of alarm objects.
|
||||||
|
Returns a list of valid alarm dicts, or None if the file is missing,
|
||||||
|
empty, or contains no valid entries.
|
||||||
|
Never raises — logs warnings and returns None on any problem.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
text = path.read_text(encoding="utf-8").strip()
|
||||||
|
except FileNotFoundError:
|
||||||
|
log.warning("Config file not found: %s", path)
|
||||||
|
return None
|
||||||
|
except OSError as e:
|
||||||
|
log.warning("Cannot read config %s: %s", path, e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
log.warning("Config file is empty: %s", path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(text)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
log.warning("Invalid JSON in %s: %s", path, e)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
log.info("Config is empty — no alarms configured")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Normalize to list
|
||||||
|
if isinstance(data, dict):
|
||||||
|
entries = [data]
|
||||||
|
elif isinstance(data, list):
|
||||||
|
entries = data
|
||||||
|
else:
|
||||||
|
log.warning("Config must be a JSON object or array, got %s", type(data).__name__)
|
||||||
|
return None
|
||||||
|
|
||||||
|
valid = []
|
||||||
|
for i, entry in enumerate(entries):
|
||||||
|
result = _validate_entry(entry, i)
|
||||||
|
if result is not None:
|
||||||
|
valid.append(result)
|
||||||
|
|
||||||
|
if not valid:
|
||||||
|
log.warning("No valid alarm entries in %s", path)
|
||||||
|
return None
|
||||||
|
|
||||||
|
log.info("Loaded %d alarm(s) from %s", len(valid), path)
|
||||||
|
return valid
|
||||||
|
|
||||||
|
|
||||||
|
def should_fire(config: dict) -> bool:
|
||||||
|
"""Check if a single alarm entry should fire right now.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- alarm_time must match current HHMM
|
||||||
|
- If alarm_days is present, today's 3-letter abbreviation must be in the list
|
||||||
|
- If alarm_days is absent but alarm_dates is present, today's MM/DD must match
|
||||||
|
- If neither alarm_days nor alarm_dates is present, fires every day
|
||||||
|
- If both are present, alarm_days wins (alarm_dates ignored)
|
||||||
|
"""
|
||||||
|
now = datetime.now()
|
||||||
|
current_hhmm = now.strftime("%H%M")
|
||||||
|
|
||||||
|
if config["alarm_time"] != current_hhmm:
|
||||||
|
return False
|
||||||
|
|
||||||
|
alarm_days = config.get("alarm_days")
|
||||||
|
alarm_dates = config.get("alarm_dates")
|
||||||
|
|
||||||
|
if alarm_days is not None:
|
||||||
|
return now.strftime("%a") in alarm_days
|
||||||
|
|
||||||
|
if alarm_dates is not None:
|
||||||
|
return now.strftime("%m/%d") in alarm_dates
|
||||||
|
|
||||||
|
return True
|
||||||
55
pi/assets/alarm/alarm_test.lab
Normal file
55
pi/assets/alarm/alarm_test.lab
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
0 700000 j
|
||||||
|
700000 1440000 u
|
||||||
|
1440000 2370000 u
|
||||||
|
2370000 3110000 i
|
||||||
|
3110000 3710000 ch
|
||||||
|
3710000 4380000 i
|
||||||
|
4380000 4800000 g
|
||||||
|
4800000 5560000 a
|
||||||
|
5560000 6180000 ts
|
||||||
|
6180000 6850000 u
|
||||||
|
6850000 7500000 j
|
||||||
|
7500000 8210000 u
|
||||||
|
8210000 9130000 u
|
||||||
|
9130000 9880000 i
|
||||||
|
9880000 10460000 ch
|
||||||
|
10460000 11150000 i
|
||||||
|
11150000 11690000 g
|
||||||
|
11690000 12470000 a
|
||||||
|
12470000 13100000 ts
|
||||||
|
13100000 13770000 u
|
||||||
|
13770000 14420000 j
|
||||||
|
14420000 15140000 u
|
||||||
|
15140000 16070000 u
|
||||||
|
16070000 16810000 i
|
||||||
|
16810000 17420000 ch
|
||||||
|
17420000 18080000 i
|
||||||
|
18080000 18610000 g
|
||||||
|
18610000 19410000 a
|
||||||
|
19410000 20020000 ts
|
||||||
|
20020000 20680000 u
|
||||||
|
20680000 21320000 j
|
||||||
|
21320000 22030000 u
|
||||||
|
22030000 22900000 u
|
||||||
|
22900000 23640000 i
|
||||||
|
23640000 24250000 ch
|
||||||
|
24250000 24920000 i
|
||||||
|
24920000 25460000 g
|
||||||
|
25460000 26200000 a
|
||||||
|
26200000 26840000 ts
|
||||||
|
26840000 27480000 u
|
||||||
|
27480000 28130000 j
|
||||||
|
28130000 28830000 u
|
||||||
|
28830000 29720000 u
|
||||||
|
29720000 30440000 i
|
||||||
|
30440000 31040000 ch
|
||||||
|
31040000 31750000 i
|
||||||
|
31750000 32600000 by
|
||||||
|
32600000 33320000 o
|
||||||
|
33320000 34120000 o
|
||||||
|
34120000 34740000 j
|
||||||
|
34740000 35350000 a
|
||||||
|
35350000 35870000 s
|
||||||
|
35870000 36510000 u
|
||||||
|
36510000 36960000 t
|
||||||
|
36960000 38220000 o
|
||||||
BIN
pi/assets/alarm/alarm_test.wav
Normal file
BIN
pi/assets/alarm/alarm_test.wav
Normal file
Binary file not shown.
42
pi/assets/alarm/sleep.lab
Normal file
42
pi/assets/alarm/sleep.lab
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
0 850000 s
|
||||||
|
850000 1580000 u
|
||||||
|
1580000 2220000 i
|
||||||
|
2220000 2660000 m
|
||||||
|
2660000 3370000 i
|
||||||
|
3370000 4140000 N
|
||||||
|
4140000 4610000 g
|
||||||
|
4610000 5780000 a
|
||||||
|
5780000 13780000 pau
|
||||||
|
13780000 14350000 k
|
||||||
|
14350000 15010000 i
|
||||||
|
15010000 15730000 e
|
||||||
|
15730000 16160000 t
|
||||||
|
16160000 16800000 e
|
||||||
|
16800000 17260000 k
|
||||||
|
17260000 17840000 u
|
||||||
|
17840000 18390000 d
|
||||||
|
18390000 19090000 a
|
||||||
|
19090000 19700000 s
|
||||||
|
19700000 20390000 a
|
||||||
|
20390000 20830000 r
|
||||||
|
20830000 22120000 i
|
||||||
|
22120000 23620000 pau
|
||||||
|
23620000 24390000 a
|
||||||
|
24390000 24810000 r
|
||||||
|
24810000 25430000 i
|
||||||
|
25430000 25860000 g
|
||||||
|
25860000 26550000 a
|
||||||
|
26550000 27000000 t
|
||||||
|
27000000 27780000 o
|
||||||
|
27780000 28520000 o
|
||||||
|
28520000 29000000 g
|
||||||
|
29000000 29740000 o
|
||||||
|
29740000 30260000 z
|
||||||
|
30260000 31110000 a
|
||||||
|
31110000 31790000 i
|
||||||
|
31790000 32190000 m
|
||||||
|
32190000 32870000 a
|
||||||
|
32870000 33480000 sh
|
||||||
|
33480000 34040000 i
|
||||||
|
34040000 34520000 t
|
||||||
|
34520000 35950000 a
|
||||||
BIN
pi/assets/alarm/sleep.wav
Normal file
BIN
pi/assets/alarm/sleep.wav
Normal file
Binary file not shown.
BIN
pi/assets/img/idle.png
Normal file
BIN
pi/assets/img/idle.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
pi/assets/img/on_alarm.png
Normal file
BIN
pi/assets/img/on_alarm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
BIN
pi/assets/img/sleep.png
Normal file
BIN
pi/assets/img/sleep.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
114
pi/audio_handler.py
Normal file
114
pi/audio_handler.py
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
"""Audio alarm functions — WAV loading and PCM streaming."""
|
||||||
|
|
||||||
|
import array
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import math
|
||||||
|
import wave
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CHUNK_SIZE = 4096
|
||||||
|
AUDIO_DIR = Path(__file__).parent / "assets" / "alarm"
|
||||||
|
|
||||||
|
|
||||||
|
def find_wav(path: Path | None = None) -> Path:
|
||||||
|
"""Return a WAV file path.
|
||||||
|
|
||||||
|
If *path* is given and points to an existing file, use it directly.
|
||||||
|
Otherwise fall back to the first .wav found in the alarm assets directory.
|
||||||
|
"""
|
||||||
|
if path is not None:
|
||||||
|
p = Path(path)
|
||||||
|
if p.is_file():
|
||||||
|
log.info("Using audio file: %s", p)
|
||||||
|
return p
|
||||||
|
log.warning("Specified audio path not found: %s — falling back to glob", p)
|
||||||
|
|
||||||
|
wavs = list(AUDIO_DIR.glob("*.wav"))
|
||||||
|
if not wavs:
|
||||||
|
raise FileNotFoundError(f"No .wav files found in {AUDIO_DIR}")
|
||||||
|
log.info("Using audio file: %s", wavs[0].name)
|
||||||
|
return wavs[0]
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_pcm(pcm: bytes, bits: int) -> bytes:
|
||||||
|
"""Peak-normalize PCM data to 0 dBFS.
|
||||||
|
|
||||||
|
Supports 8-bit (unsigned) and 16-bit (signed) PCM.
|
||||||
|
Returns the original bytes unchanged if already at 0 dB or silent.
|
||||||
|
"""
|
||||||
|
if bits == 16:
|
||||||
|
samples = array.array("h", pcm) # signed 16-bit
|
||||||
|
peak = max(abs(s) for s in samples) if samples else 0
|
||||||
|
if peak == 0 or peak == 32767:
|
||||||
|
return pcm
|
||||||
|
scale = 32767 / peak
|
||||||
|
samples = array.array("h", (min(32767, max(-32768, int(s * scale))) for s in samples))
|
||||||
|
elif bits == 8:
|
||||||
|
samples = array.array("B", pcm) # unsigned 8-bit, center=128
|
||||||
|
peak = max(abs(s - 128) for s in samples) if samples else 0
|
||||||
|
if peak == 0 or peak == 127:
|
||||||
|
return pcm
|
||||||
|
scale = 127 / peak
|
||||||
|
samples = array.array("B", (max(0, min(255, int((s - 128) * scale) + 128)) for s in samples))
|
||||||
|
else:
|
||||||
|
log.warning("Normalization not supported for %d-bit audio, skipping", bits)
|
||||||
|
return pcm
|
||||||
|
|
||||||
|
gain_db = 20 * __import__("math").log10(scale) if scale > 0 else 0
|
||||||
|
log.info("Normalized: peak %d → 0 dBFS (gain %.1f dB)", peak, gain_db)
|
||||||
|
return samples.tobytes()
|
||||||
|
|
||||||
|
|
||||||
|
def read_wav(path: Path) -> tuple[bytes, int, int, int]:
|
||||||
|
"""Read WAV file, normalize to 0 dBFS, return (pcm_data, sample_rate, channels, bits)."""
|
||||||
|
try:
|
||||||
|
wf = wave.open(str(path), "rb")
|
||||||
|
except wave.Error as e:
|
||||||
|
raise ValueError(
|
||||||
|
f"{path.name}: unsupported WAV format ({e}). "
|
||||||
|
"Only 8/16-bit integer PCM is supported — no 32-bit float."
|
||||||
|
) from e
|
||||||
|
with wf:
|
||||||
|
sr = wf.getframerate()
|
||||||
|
ch = wf.getnchannels()
|
||||||
|
bits = wf.getsampwidth() * 8
|
||||||
|
pcm = wf.readframes(wf.getnframes())
|
||||||
|
log.info("WAV loaded: %dHz %dch %dbit, %.1fs, %d bytes",
|
||||||
|
sr, ch, bits, len(pcm) / (sr * ch * (bits // 8)), len(pcm))
|
||||||
|
pcm = _normalize_pcm(pcm, bits)
|
||||||
|
return pcm, sr, ch, bits
|
||||||
|
|
||||||
|
|
||||||
|
def chunk_bytes(data: bytes, size: int):
|
||||||
|
"""Yield data in fixed-size chunks."""
|
||||||
|
for i in range(0, len(data), size):
|
||||||
|
yield data[i : i + size]
|
||||||
|
|
||||||
|
|
||||||
|
async def stream_alarm(ws, pcm: bytes, sr: int, ch: int, bits: int):
|
||||||
|
"""Stream one alarm cycle to the connected client."""
|
||||||
|
bytes_per_sec = sr * ch * (bits // 8)
|
||||||
|
chunk_duration = CHUNK_SIZE / bytes_per_sec
|
||||||
|
pace_delay = chunk_duration * 0.9 # 90% real-time to avoid underrun
|
||||||
|
|
||||||
|
total_chunks = (len(pcm) + CHUNK_SIZE - 1) // CHUNK_SIZE
|
||||||
|
|
||||||
|
start_msg = json.dumps({
|
||||||
|
"type": "alarm_start",
|
||||||
|
"sample_rate": sr,
|
||||||
|
"channels": ch,
|
||||||
|
"bits": bits,
|
||||||
|
})
|
||||||
|
await ws.send(start_msg)
|
||||||
|
log.info("Sent alarm_start (%d chunks, pace %.1fms)", total_chunks, pace_delay * 1000)
|
||||||
|
|
||||||
|
for i, chunk in enumerate(chunk_bytes(pcm, CHUNK_SIZE)):
|
||||||
|
await ws.send(chunk)
|
||||||
|
await asyncio.sleep(pace_delay)
|
||||||
|
|
||||||
|
await ws.send(json.dumps({"type": "alarm_stop"}))
|
||||||
|
log.info("Sent alarm_stop")
|
||||||
13
pi/config/alarms.sample.json
Normal file
13
pi/config/alarms.sample.json
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"alarm_time": "0700",
|
||||||
|
"alarm_days": ["Mon", "Tue", "Wed", "Thu", "Fri"],
|
||||||
|
"alarm_audio": "assets/alarm/alarm_test.wav",
|
||||||
|
"alarm_image": "assets/img/on_alarm.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alarm_time": "2330",
|
||||||
|
"alarm_audio": "assets/alarm/sleep.wav",
|
||||||
|
"alarm_image": "assets/img/sleep.png"
|
||||||
|
}
|
||||||
|
]
|
||||||
132
pi/contents_server.py
Normal file
132
pi/contents_server.py
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
"""
|
||||||
|
Contents server — serves alarm audio and status images over WebSocket.
|
||||||
|
|
||||||
|
Streams WAV PCM chunks and pushes 1-bit monochrome status images to the
|
||||||
|
connected ESP32 dashboard client on port 8766.
|
||||||
|
|
||||||
|
Protocol:
|
||||||
|
Status image:
|
||||||
|
1. Text frame: {"type":"status_image","width":200,"height":200}
|
||||||
|
2. Binary frame: 1-bit monochrome bitmap
|
||||||
|
|
||||||
|
Alarm audio:
|
||||||
|
1. Text frame: {"type":"alarm_start","sample_rate":N,"channels":N,"bits":N}
|
||||||
|
2. Binary frames: raw PCM chunks (4096 bytes each, paced at ~90% real-time)
|
||||||
|
3. Text frame: {"type":"alarm_stop"}
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import websockets
|
||||||
|
|
||||||
|
from alarm_scheduler import DEFAULT_CONFIG_PATH, load_config, should_fire
|
||||||
|
from audio_handler import find_wav, read_wav, stream_alarm
|
||||||
|
from image_handler import IMG_DIR, load_status_image, send_status_image
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
|
||||||
|
log = logging.getLogger("contents_server")
|
||||||
|
|
||||||
|
PORT = 8766
|
||||||
|
PI_DIR = Path(__file__).parent
|
||||||
|
|
||||||
|
# Set by main(), read by handler()
|
||||||
|
_config_path: Path = DEFAULT_CONFIG_PATH
|
||||||
|
|
||||||
|
TICK_INTERVAL = 5 # seconds between schedule checks
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_path(relative: str) -> Path:
|
||||||
|
"""Resolve a config path relative to pi/ directory."""
|
||||||
|
p = Path(relative)
|
||||||
|
if not p.is_absolute():
|
||||||
|
p = PI_DIR / p
|
||||||
|
return p
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_alarm(entry: dict) -> dict:
|
||||||
|
"""Pre-resolve paths and load resources for a single alarm entry."""
|
||||||
|
audio_path = find_wav(_resolve_path(entry.get("alarm_audio", "assets/alarm/alarm_test.wav")))
|
||||||
|
alarm_img_path = _resolve_path(entry.get("alarm_image", "assets/img/on_alarm.png"))
|
||||||
|
pcm, sr, ch, bits = read_wav(audio_path)
|
||||||
|
img = load_status_image(alarm_img_path)
|
||||||
|
return {
|
||||||
|
"config": entry,
|
||||||
|
"pcm": pcm, "sr": sr, "ch": ch, "bits": bits,
|
||||||
|
"img": img,
|
||||||
|
"last_fired": None,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def handler(ws):
|
||||||
|
"""Handle a single WebSocket connection."""
|
||||||
|
remote = ws.remote_address
|
||||||
|
log.info("Client connected: %s:%d", remote[0], remote[1])
|
||||||
|
|
||||||
|
configs = load_config(_config_path)
|
||||||
|
img_idle = load_status_image(IMG_DIR / "idle.png")
|
||||||
|
current_img = img_idle
|
||||||
|
|
||||||
|
alarms = [_prepare_alarm(entry) for entry in configs] if configs else []
|
||||||
|
|
||||||
|
async def alarm_ticker():
|
||||||
|
nonlocal current_img
|
||||||
|
if not alarms:
|
||||||
|
log.info("No alarms configured — idling forever")
|
||||||
|
await asyncio.Future()
|
||||||
|
return
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for alarm in alarms:
|
||||||
|
if should_fire(alarm["config"]):
|
||||||
|
current_minute = datetime.now().strftime("%Y%m%d%H%M")
|
||||||
|
|
||||||
|
if current_minute != alarm["last_fired"]:
|
||||||
|
alarm["last_fired"] = current_minute
|
||||||
|
log.info("Alarm firing: %s at %s",
|
||||||
|
alarm["config"]["alarm_time"], current_minute)
|
||||||
|
current_img = alarm["img"]
|
||||||
|
await send_status_image(ws, current_img)
|
||||||
|
await stream_alarm(ws, alarm["pcm"], alarm["sr"],
|
||||||
|
alarm["ch"], alarm["bits"])
|
||||||
|
# let the image persist a bit more
|
||||||
|
await asyncio.sleep(1)
|
||||||
|
current_img = img_idle
|
||||||
|
await send_status_image(ws, current_img)
|
||||||
|
|
||||||
|
await asyncio.sleep(TICK_INTERVAL)
|
||||||
|
|
||||||
|
async def receiver():
|
||||||
|
async for msg in ws:
|
||||||
|
try:
|
||||||
|
data = json.loads(msg)
|
||||||
|
except (json.JSONDecodeError, TypeError):
|
||||||
|
continue
|
||||||
|
if data.get("type") == "request_image":
|
||||||
|
log.info("Client requested image — sending current (%d bytes)",
|
||||||
|
len(current_img))
|
||||||
|
await send_status_image(ws, current_img)
|
||||||
|
|
||||||
|
try:
|
||||||
|
await asyncio.gather(alarm_ticker(), receiver())
|
||||||
|
except websockets.exceptions.ConnectionClosed:
|
||||||
|
log.info("Client disconnected: %s:%d", remote[0], remote[1])
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
log.info("Contents server starting on port %d", PORT)
|
||||||
|
async with websockets.serve(handler, "0.0.0.0", PORT):
|
||||||
|
await asyncio.Future()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = argparse.ArgumentParser(description="Alarm contents server")
|
||||||
|
parser.add_argument("--config", type=Path, default=DEFAULT_CONFIG_PATH,
|
||||||
|
help="Path to alarm config JSON (default: %(default)s)")
|
||||||
|
args = parser.parse_args()
|
||||||
|
_config_path = args.config
|
||||||
|
asyncio.run(main())
|
||||||
52
pi/image_handler.py
Normal file
52
pi/image_handler.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
"""Status image functions — loading, alpha compositing, and WS transmission."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
IMG_DIR = Path(__file__).parent / "assets" / "img"
|
||||||
|
STATUS_IMG_SIZE = 200
|
||||||
|
MONOCHROME_THRESHOLD = 180
|
||||||
|
|
||||||
|
|
||||||
|
def load_status_image(path: Path) -> bytes:
|
||||||
|
"""Load a PNG, convert to 1-bit 200x200 monochrome bitmap (MSB-first, black=1).
|
||||||
|
|
||||||
|
Transparent pixels are composited onto white so they don't render as black.
|
||||||
|
"""
|
||||||
|
img = Image.open(path)
|
||||||
|
|
||||||
|
# Composite transparent pixels onto white background
|
||||||
|
if img.mode in ("RGBA", "LA", "PA"):
|
||||||
|
bg = Image.new("RGBA", img.size, (255, 255, 255, 255))
|
||||||
|
bg.paste(img, mask=img.split()[-1])
|
||||||
|
img = bg
|
||||||
|
|
||||||
|
img = img.convert("L")
|
||||||
|
|
||||||
|
# Resize to fit within 120x120, preserving aspect ratio
|
||||||
|
img.thumbnail((STATUS_IMG_SIZE, STATUS_IMG_SIZE), Image.LANCZOS)
|
||||||
|
|
||||||
|
# Paste centered onto white canvas
|
||||||
|
canvas = Image.new("L", (STATUS_IMG_SIZE, STATUS_IMG_SIZE), 255)
|
||||||
|
x_off = (STATUS_IMG_SIZE - img.width) // 2
|
||||||
|
y_off = (STATUS_IMG_SIZE - img.height) // 2
|
||||||
|
canvas.paste(img, (x_off, y_off))
|
||||||
|
|
||||||
|
# Threshold to 1-bit: black (< MONOCHROME_THRESHOLD) -> 1, white -> 0
|
||||||
|
bw = canvas.point(lambda p: 1 if p < MONOCHROME_THRESHOLD else 0, "1")
|
||||||
|
raw = bw.tobytes()
|
||||||
|
log.info("Status image loaded: %s -> %d bytes", path.name, len(raw))
|
||||||
|
return raw
|
||||||
|
|
||||||
|
|
||||||
|
async def send_status_image(ws, img_bytes: bytes):
|
||||||
|
"""Send a status image over the WebSocket (text header + binary payload)."""
|
||||||
|
header = json.dumps({"type": "status_image", "width": STATUS_IMG_SIZE, "height": STATUS_IMG_SIZE})
|
||||||
|
await ws.send(header)
|
||||||
|
await ws.send(img_bytes)
|
||||||
|
log.info("Sent status image (%d bytes)", len(img_bytes))
|
||||||
@@ -1 +1,3 @@
|
|||||||
websockets>=12.0
|
websockets>=12.0
|
||||||
|
psutil>=5.9.0
|
||||||
|
Pillow>=10.0
|
||||||
|
|||||||
19
pi/run_all.py
Normal file
19
pi/run_all.py
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Launch stats_server and contents_server as child processes."""
|
||||||
|
import subprocess, sys, signal
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
d = Path(__file__).parent
|
||||||
|
|
||||||
|
# Forward any CLI args (e.g. --config) to contents_server
|
||||||
|
extra_args = sys.argv[1:]
|
||||||
|
|
||||||
|
procs = [
|
||||||
|
subprocess.Popen([sys.executable, d / "stats_server.py"]),
|
||||||
|
subprocess.Popen([sys.executable, d / "contents_server.py"] + extra_args),
|
||||||
|
]
|
||||||
|
signal.signal(signal.SIGINT, lambda *_: [p.terminate() for p in procs])
|
||||||
|
signal.signal(signal.SIGTERM, lambda *_: [p.terminate() for p in procs])
|
||||||
|
print(f"Running stats_server (PID {procs[0].pid}) + contents_server (PID {procs[1].pid})")
|
||||||
|
for p in procs:
|
||||||
|
p.wait()
|
||||||
163
pi/stats_server.py
Normal file
163
pi/stats_server.py
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""WebSocket server that sends real Pi system stats every 2 seconds.
|
||||||
|
|
||||||
|
Drop-in replacement for mock_server.py. Same port (8765), same JSON schema,
|
||||||
|
same 2s push interval. Services remain mocked until systemd integration is added.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import psutil
|
||||||
|
import websockets
|
||||||
|
|
||||||
|
# Prime the CPU percent counter (first call always returns 0.0)
|
||||||
|
psutil.cpu_percent(interval=None)
|
||||||
|
|
||||||
|
# Network baseline for delta calculation
|
||||||
|
_prev_net = psutil.net_io_counters()
|
||||||
|
_prev_net_time = time.monotonic()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_cpu_temp() -> float:
|
||||||
|
"""Read CPU temperature with fallback for different Pi OS versions."""
|
||||||
|
try:
|
||||||
|
temps = psutil.sensors_temperatures()
|
||||||
|
if "cpu_thermal" in temps and temps["cpu_thermal"]:
|
||||||
|
return round(temps["cpu_thermal"][0].current, 1)
|
||||||
|
except (AttributeError, KeyError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Fallback: read sysfs directly (value is in millidegrees)
|
||||||
|
thermal_path = Path("/sys/class/thermal/thermal_zone0/temp")
|
||||||
|
try:
|
||||||
|
millidegrees = int(thermal_path.read_text().strip())
|
||||||
|
return round(millidegrees / 1000.0, 1)
|
||||||
|
except (FileNotFoundError, ValueError, PermissionError):
|
||||||
|
return 0.0
|
||||||
|
|
||||||
|
|
||||||
|
def _get_net_throughput() -> tuple[float, float]:
|
||||||
|
"""Calculate network rx/tx in kbps since last call."""
|
||||||
|
global _prev_net, _prev_net_time
|
||||||
|
|
||||||
|
now = time.monotonic()
|
||||||
|
current = psutil.net_io_counters()
|
||||||
|
elapsed = now - _prev_net_time
|
||||||
|
|
||||||
|
if elapsed <= 0:
|
||||||
|
return 0.0, 0.0
|
||||||
|
|
||||||
|
rx_kbps = round((current.bytes_recv - _prev_net.bytes_recv) * 8 / (elapsed * 1000), 1)
|
||||||
|
tx_kbps = round((current.bytes_sent - _prev_net.bytes_sent) * 8 / (elapsed * 1000), 1)
|
||||||
|
|
||||||
|
_prev_net = current
|
||||||
|
_prev_net_time = now
|
||||||
|
|
||||||
|
return rx_kbps, tx_kbps
|
||||||
|
|
||||||
|
# only services that matter
|
||||||
|
SERVICES_ALIASES = {
|
||||||
|
"gitea": "gitea",
|
||||||
|
"samba": "samba",
|
||||||
|
"pihole": "pihole",
|
||||||
|
"qbittorrent": "qbittorrent",
|
||||||
|
"frpc-primary": "frpc (ny)",
|
||||||
|
"pinepods": "pinepods",
|
||||||
|
"frpc-ssh": "frpc (ssh)",
|
||||||
|
"jellyfin": "jellyfin",
|
||||||
|
}
|
||||||
|
def _get_docker_services() -> list[dict]:
|
||||||
|
"""Query Docker for real container statuses with ternary status model."""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "ps", "-a", "--format", "{{.Names}}\t{{.Status}}"],
|
||||||
|
capture_output=True, text=True, timeout=5,
|
||||||
|
)
|
||||||
|
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
||||||
|
return []
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
services = []
|
||||||
|
for line in result.stdout.strip().splitlines():
|
||||||
|
parts = line.split("\t", 1)
|
||||||
|
if len(parts) != 2:
|
||||||
|
continue
|
||||||
|
name, raw_status = parts
|
||||||
|
|
||||||
|
if (name in SERVICES_ALIASES):
|
||||||
|
if raw_status.startswith("Up"):
|
||||||
|
if "unhealthy" in raw_status or "Restarting" in raw_status:
|
||||||
|
status = "warning"
|
||||||
|
else:
|
||||||
|
status = "running"
|
||||||
|
else:
|
||||||
|
status = "stopped"
|
||||||
|
services.append({"name": SERVICES_ALIASES[name], "status": status})
|
||||||
|
|
||||||
|
# Sort: warnings first, then stopped, then running (problems float to top)
|
||||||
|
order = {"warning": 0, "stopped": 1, "running": 2}
|
||||||
|
services.sort(key=lambda s: order.get(s["status"], 3))
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
|
||||||
|
def _local_time_fields() -> dict:
|
||||||
|
"""Current local time as broken-down fields for RTC sync."""
|
||||||
|
now = datetime.now()
|
||||||
|
return {
|
||||||
|
"y": now.year,
|
||||||
|
"mo": now.month,
|
||||||
|
"d": now.day,
|
||||||
|
"h": now.hour,
|
||||||
|
"m": now.minute,
|
||||||
|
"s": now.second,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_stats() -> dict:
|
||||||
|
mem = psutil.virtual_memory()
|
||||||
|
disk = psutil.disk_usage("/mnt/buffalo")
|
||||||
|
rx_kbps, tx_kbps = _get_net_throughput()
|
||||||
|
|
||||||
|
return {
|
||||||
|
"cpu_pct": psutil.cpu_percent(interval=None),
|
||||||
|
"mem_pct": round(mem.percent, 1),
|
||||||
|
"mem_used_mb": int(mem.used // (1024 * 1024)),
|
||||||
|
"disk_pct": round(disk.percent, 1),
|
||||||
|
"cpu_temp": _get_cpu_temp(),
|
||||||
|
"uptime_hrs": round((time.time() - psutil.boot_time()) / 3600, 1),
|
||||||
|
"net_rx_kbps": rx_kbps / 8,
|
||||||
|
"net_tx_kbps": tx_kbps / 8, # kByte/s for humans
|
||||||
|
"services": _get_docker_services(),
|
||||||
|
"timestamp": int(time.time()),
|
||||||
|
"local_time": _local_time_fields(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async def handler(websocket):
|
||||||
|
addr = websocket.remote_address
|
||||||
|
print(f"Client connected: {addr}")
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
stats = generate_stats()
|
||||||
|
await websocket.send(json.dumps(stats))
|
||||||
|
await asyncio.sleep(2)
|
||||||
|
except websockets.ConnectionClosed:
|
||||||
|
print(f"Client disconnected: {addr}")
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
print("Pi stats server starting on ws://0.0.0.0:8765")
|
||||||
|
async with websockets.serve(handler, "0.0.0.0", 8765):
|
||||||
|
await asyncio.Future() # run forever
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
Reference in New Issue
Block a user