mirror of
https://github.com/markqvist/RNode_Firmware.git
synced 2026-04-27 22:35:36 +00:00
Add LVGL watch GUI with tileview navigation and serial screenshot
LVGL 9.5.0 integration replacing direct framebuffer rendering. Watch face with time, date, status bar (mode/battery), and three complications (LoRa/GPS/Steps). Tileview swipe navigation between five screens: watch face, radio status, GPS, messages, settings. Haptic feedback on screen transitions via DRV2605. Radio status screen shows frequency, LoRa params, RSSI bar gauge, channel utilization, BLE state, and packet counts. GPS screen shows coordinates, fix quality, altitude/speed, and beacon status. Serial screenshot tool (scripts/screenshot.py) captures display contents over USB CDC by sending trigger bytes and receiving the shadow framebuffer as raw RGB565. Build changes: - FlashSize=16M in FQBN (bootloader embeds flash size, defaults 4MB) - PSRAM=enabled for LVGL draw buffers and screenshot shadow buffer - Custom 8MB app partition (partition_twatch.csv) for 16MB flash - flash-twatch_ultra-full make target for full bootloader+partition+app flash Architecture notes in code: - display_init() must run BEFORE xl9555_init() (display power gate defaults high at power-on, reordering causes black screen) - LVGL draw buffers use separate swap buffer for RGB565 byte-order conversion to avoid corrupting LVGL's internal buffer state - Touch input registered via function pointer to decouple Gui.h from touch library include order
This commit is contained in:
parent
c31b1908a7
commit
2c0c9f3d5a
8 changed files with 3820 additions and 64 deletions
61
Display.h
61
Display.h
|
|
@ -14,9 +14,8 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#if BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
// T-Watch Ultra uses CO5300 QSPI AMOLED — separate display path
|
||||
// T-Watch Ultra — LVGL GUI on CO5300 QSPI AMOLED (410x502)
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Fonts/FreeSansBold24pt7b.h>
|
||||
#include "Fonts/Org_01.h"
|
||||
#include "XL9555.h"
|
||||
#include "CO5300.h"
|
||||
|
|
@ -27,16 +26,14 @@
|
|||
bool recondition_display = false;
|
||||
void ext_fb_enable() { }
|
||||
void ext_fb_disable() { }
|
||||
uint8_t fb[0]; // empty framebuffer stub
|
||||
uint8_t fb[0];
|
||||
|
||||
bool display_blanked = false;
|
||||
uint32_t last_unblank_event = 0;
|
||||
uint32_t display_blanking_timeout = 0; // Disabled until button wake is implemented
|
||||
uint32_t display_blanking_timeout = 0;
|
||||
|
||||
// Partial framebuffer for clock region (410 x 60 = ~49KB, fits in DMA memory)
|
||||
#define CLOCK_FB_W CO5300_WIDTH
|
||||
#define CLOCK_FB_H 60
|
||||
static uint16_t *clock_fb = NULL;
|
||||
// LVGL GUI (must come after variable declarations above)
|
||||
#include "Gui.h"
|
||||
|
||||
void display_unblank() {
|
||||
if (display_blanked) {
|
||||
|
|
@ -44,6 +41,7 @@
|
|||
xl9555_set(EXPANDS_DISP_EN, true);
|
||||
co5300_set_brightness(128);
|
||||
display_blanked = false;
|
||||
if (gui_screen) lv_obj_invalidate(gui_screen);
|
||||
}
|
||||
last_unblank_event = millis();
|
||||
}
|
||||
|
|
@ -52,21 +50,16 @@
|
|||
if (!co5300_init()) return false;
|
||||
co5300_set_brightness(128);
|
||||
|
||||
// Enable blanking — touch input now available to unblank
|
||||
display_blanking_enabled = true;
|
||||
display_blanking_timeout = 10000; // 10 seconds
|
||||
|
||||
// Allocate partial framebuffer for clock region (PSRAM preferred for large buffers)
|
||||
clock_fb = (uint16_t *)heap_caps_malloc(CLOCK_FB_W * CLOCK_FB_H * 2, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (!clock_fb) clock_fb = (uint16_t *)malloc(CLOCK_FB_W * CLOCK_FB_H * 2);
|
||||
display_blanking_timeout = 10000;
|
||||
|
||||
if (!gui_init()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void update_display(bool force = false) {
|
||||
if (!co5300_ready) return;
|
||||
|
||||
// Handle display blanking
|
||||
if (display_blanking_enabled && !display_blanked) {
|
||||
if (millis() - last_unblank_event > display_blanking_timeout) {
|
||||
co5300_set_brightness(0);
|
||||
|
|
@ -77,45 +70,9 @@
|
|||
}
|
||||
}
|
||||
if (display_blanked && !force) return;
|
||||
if (!clock_fb) return;
|
||||
|
||||
display_updating = true;
|
||||
|
||||
// Render clock in partial framebuffer
|
||||
memset(clock_fb, 0, CLOCK_FB_W * CLOCK_FB_H * 2);
|
||||
|
||||
char time_str[6];
|
||||
snprintf(time_str, sizeof(time_str), "%02d:%02d", rtc_hour, rtc_minute);
|
||||
|
||||
// Draw time centered, large font
|
||||
const GFXfont *font = &FreeSansBold24pt7b;
|
||||
uint16_t tw = co5300_draw_string(clock_fb, CLOCK_FB_W, CLOCK_FB_H,
|
||||
0, 45, time_str, CO5300_WHITE, font);
|
||||
// Re-render centered
|
||||
if (tw > 0 && tw < CLOCK_FB_W) {
|
||||
memset(clock_fb, 0, CLOCK_FB_W * CLOCK_FB_H * 2);
|
||||
co5300_draw_string(clock_fb, CLOCK_FB_W, CLOCK_FB_H,
|
||||
(CLOCK_FB_W - tw) / 2, 45, time_str, CO5300_WHITE, font);
|
||||
}
|
||||
|
||||
// Push clock region to display (upper area)
|
||||
co5300_push_pixels(0, 80, CLOCK_FB_W, CLOCK_FB_H, clock_fb);
|
||||
|
||||
// Render status line below clock
|
||||
memset(clock_fb, 0, CLOCK_FB_W * CLOCK_FB_H * 2);
|
||||
|
||||
char status[64];
|
||||
snprintf(status, sizeof(status), "%s %d%% %dsats %lus",
|
||||
radio_online ? "RADIO" : "idle",
|
||||
(int)battery_percent,
|
||||
gps_sats,
|
||||
millis() / 1000);
|
||||
|
||||
co5300_draw_string(clock_fb, CLOCK_FB_W, CLOCK_FB_H,
|
||||
10, 15, status, CO5300_GREY, &Org_01);
|
||||
|
||||
co5300_push_pixels(0, 150, CLOCK_FB_W, CLOCK_FB_H, clock_fb);
|
||||
|
||||
gui_update();
|
||||
display_updating = false;
|
||||
}
|
||||
|
||||
|
|
|
|||
1904
Fonts/montserrat_bold_28.c
Normal file
1904
Fonts/montserrat_bold_28.c
Normal file
File diff suppressed because it is too large
Load diff
984
Fonts/montserrat_bold_96.c
Normal file
984
Fonts/montserrat_bold_96.c
Normal file
|
|
@ -0,0 +1,984 @@
|
|||
/*******************************************************************************
|
||||
* Size: 96 px
|
||||
* Bpp: 4
|
||||
* Opts: --bpp 4 --size 96 --font /home/ian/Arduino/libraries/lvgl/tests/src/test_files/fonts/Montserrat-Bold.ttf -r 0x30-0x3A,0x20 --format lvgl -o /home/ian/Code/RNode_Firmware/Fonts/montserrat_bold_96.c --lv-include lvgl.h --lv-font-name montserrat_bold_96
|
||||
******************************************************************************/
|
||||
|
||||
#ifdef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl.h"
|
||||
#endif
|
||||
|
||||
#ifndef MONTSERRAT_BOLD_96
|
||||
#define MONTSERRAT_BOLD_96 1
|
||||
#endif
|
||||
|
||||
#if MONTSERRAT_BOLD_96
|
||||
|
||||
/*-----------------
|
||||
* BITMAPS
|
||||
*----------------*/
|
||||
|
||||
/*Store the image of the glyphs*/
|
||||
static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
|
||||
/* U+0020 " " */
|
||||
|
||||
/* U+0030 "0" */
|
||||
0x0, 0xff, 0xe7, 0x11, 0xa2, 0x46, 0x40, 0x1f,
|
||||
0xfe, 0x33, 0x7a, 0xdf, 0xdc, 0xbb, 0xb3, 0x7f,
|
||||
0xad, 0xcc, 0x3, 0xff, 0xb6, 0x51, 0xb9, 0xa,
|
||||
0x40, 0x1f, 0xc2, 0x91, 0x9d, 0x24, 0x1, 0xff,
|
||||
0xd6, 0x2a, 0xd7, 0x20, 0xf, 0xfe, 0x28, 0xb6,
|
||||
0xd9, 0x80, 0x7f, 0xf4, 0x86, 0x75, 0x40, 0x3f,
|
||||
0xf9, 0x69, 0x94, 0x20, 0x1f, 0xfc, 0xf4, 0xf6,
|
||||
0x0, 0xff, 0xe7, 0xaf, 0x28, 0x7, 0xff, 0x35,
|
||||
0xec, 0x40, 0x3f, 0xfa, 0x55, 0x20, 0x1f, 0xfc,
|
||||
0xba, 0x80, 0xf, 0xfe, 0xab, 0x60, 0x7, 0xff,
|
||||
0x26, 0x94, 0x3, 0xff, 0xae, 0x78, 0x1, 0xff,
|
||||
0xc7, 0xa5, 0x0, 0xff, 0xec, 0x9d, 0x80, 0x7f,
|
||||
0xf1, 0x61, 0x40, 0x3f, 0xfb, 0x69, 0x20, 0x1f,
|
||||
0xfc, 0x34, 0x70, 0xf, 0xfe, 0xeb, 0x28, 0x7,
|
||||
0xff, 0x4, 0x68, 0x3, 0xff, 0xbf, 0x44, 0x1,
|
||||
0xff, 0xc0, 0xb1, 0x0, 0xff, 0xf0, 0x70, 0x7,
|
||||
0xfc, 0x6a, 0x1, 0xff, 0xc6, 0x49, 0xce, 0xfe,
|
||||
0xd9, 0x50, 0xf, 0xfe, 0x31, 0xa0, 0x7, 0xfb,
|
||||
0xc0, 0x3f, 0xf8, 0xa7, 0xb6, 0xc6, 0x20, 0x24,
|
||||
0xd5, 0xca, 0x1, 0xff, 0xc5, 0x90, 0xf, 0xe5,
|
||||
0x30, 0xf, 0xfe, 0x24, 0x61, 0x0, 0x7f, 0xd,
|
||||
0x50, 0x3, 0xff, 0x88, 0x4c, 0x1, 0xfa, 0x40,
|
||||
0x3f, 0xf8, 0x92, 0xe0, 0x1f, 0xfc, 0x15, 0xb0,
|
||||
0xf, 0xfe, 0x25, 0x80, 0x7c, 0x46, 0x1, 0xff,
|
||||
0xc3, 0x56, 0x0, 0xff, 0xe1, 0xa4, 0x0, 0x7f,
|
||||
0xf0, 0xc9, 0x0, 0x3d, 0x60, 0x1f, 0xfc, 0x31,
|
||||
0xa0, 0xf, 0xfe, 0x2b, 0x98, 0x7, 0xff, 0xf,
|
||||
0x80, 0x3c, 0xe0, 0x1f, 0xfc, 0x38, 0x0, 0xff,
|
||||
0xe3, 0xf8, 0x7, 0xff, 0xd, 0x44, 0x3, 0x20,
|
||||
0x80, 0x7f, 0xf0, 0x85, 0x80, 0x3f, 0xf8, 0xe6,
|
||||
0x80, 0x1f, 0xfc, 0x34, 0x0, 0xd8, 0x1, 0xff,
|
||||
0xc3, 0x90, 0xf, 0xfe, 0x4f, 0x0, 0x7f, 0xf0,
|
||||
0xfc, 0x3, 0x20, 0x7, 0xff, 0xd, 0x80, 0x3f,
|
||||
0xf9, 0x2a, 0x20, 0x1f, 0xfc, 0x24, 0x0, 0x84,
|
||||
0xc0, 0x3f, 0xf8, 0x44, 0x20, 0x1f, 0xfc, 0xa4,
|
||||
0x0, 0xff, 0xe1, 0x9, 0x0, 0x14, 0x3, 0xff,
|
||||
0x86, 0xa0, 0x1f, 0xfc, 0xbf, 0x0, 0xff, 0xe1,
|
||||
0xa0, 0x0, 0xc0, 0x3f, 0xf8, 0x7a, 0x1, 0xff,
|
||||
0xcb, 0x70, 0xf, 0xfe, 0x1f, 0x0, 0x38, 0x3,
|
||||
0xff, 0x86, 0xe0, 0x1f, 0xfc, 0xb2, 0x0, 0xff,
|
||||
0xe1, 0x90, 0x0, 0x80, 0x3f, 0xf8, 0x64, 0x1,
|
||||
0xff, 0xcc, 0x30, 0xf, 0xfe, 0x13, 0x80, 0x14,
|
||||
0x3, 0xff, 0x84, 0x40, 0x1f, 0xfc, 0xd6, 0x0,
|
||||
0xff, 0xe1, 0x10, 0x0, 0x40, 0x3f, 0xf8, 0x4c,
|
||||
0x1, 0xff, 0xcd, 0x30, 0xf, 0xfe, 0x18, 0x88,
|
||||
0x3, 0xff, 0x86, 0x20, 0x1f, 0xfc, 0xde, 0x0,
|
||||
0xff, 0xe1, 0x98, 0x7, 0xff, 0x10, 0xc0, 0x3f,
|
||||
0xf9, 0xa2, 0x1, 0xff, 0xc3, 0x13, 0x0, 0xff,
|
||||
0xe1, 0x88, 0x7, 0xff, 0x34, 0xc0, 0x3f, 0xf8,
|
||||
0x82, 0x1, 0xff, 0xed, 0x70, 0xf, 0xfe, 0x27,
|
||||
0x80, 0x7f, 0xf3, 0x44, 0x3, 0xff, 0x86, 0x20,
|
||||
0x1f, 0xfc, 0x4f, 0x0, 0xff, 0xe6, 0x88, 0x7,
|
||||
0xff, 0x10, 0x40, 0x3f, 0xfd, 0xa2, 0x1, 0xff,
|
||||
0xde, 0x30, 0xf, 0xfe, 0x1b, 0x98, 0x7, 0xff,
|
||||
0xc, 0x40, 0x3f, 0xfb, 0xa2, 0x20, 0xf, 0xfe,
|
||||
0x19, 0x80, 0x7f, 0xf3, 0x44, 0x3, 0xff, 0xa8,
|
||||
0xc0, 0x1f, 0xfc, 0xde, 0x0, 0xff, 0xe1, 0x98,
|
||||
0x10, 0x7, 0xff, 0x8, 0x40, 0x3f, 0xf9, 0xa4,
|
||||
0x1, 0xff, 0xc2, 0x11, 0x3, 0x0, 0x7f, 0xf0,
|
||||
0x88, 0x3, 0xff, 0x9a, 0xc0, 0x1f, 0xfc, 0x22,
|
||||
0x0, 0x18, 0x7, 0xff, 0xc, 0xc0, 0x3f, 0xf9,
|
||||
0x84, 0x1, 0xff, 0xc2, 0x60, 0x7, 0x0, 0x7f,
|
||||
0xf0, 0xd8, 0x3, 0xff, 0x96, 0x60, 0x1f, 0xfc,
|
||||
0x32, 0x0, 0x28, 0x7, 0xff, 0xf, 0x0, 0x3f,
|
||||
0xf9, 0x68, 0x1, 0xff, 0xc3, 0xd0, 0x1, 0x80,
|
||||
0x7f, 0xf0, 0xdc, 0x3, 0xff, 0x97, 0x80, 0x1f,
|
||||
0xfc, 0x34, 0x0, 0x90, 0x3, 0xff, 0x84, 0x26,
|
||||
0x1, 0xff, 0xc9, 0x14, 0x0, 0xff, 0xe1, 0x18,
|
||||
0x80, 0x58, 0x1, 0xff, 0xc3, 0xa0, 0xf, 0xfe,
|
||||
0x4b, 0x0, 0x7f, 0xf0, 0xd0, 0x3, 0x20, 0x7,
|
||||
0xff, 0xd, 0x84, 0x3, 0xff, 0x91, 0x40, 0x1f,
|
||||
0xfc, 0x3c, 0x0, 0xc6, 0x40, 0x1f, 0xfc, 0x39,
|
||||
0x0, 0xff, 0xe3, 0xb1, 0x0, 0x7f, 0xf0, 0x85,
|
||||
0x0, 0x3a, 0x80, 0x3f, 0xf8, 0x6e, 0x40, 0x1f,
|
||||
0xfc, 0x68, 0x0, 0xff, 0xe1, 0xb0, 0x7, 0x98,
|
||||
0x3, 0xff, 0x89, 0xe0, 0x1f, 0xfc, 0x5a, 0x10,
|
||||
0xf, 0xfe, 0x1d, 0x0, 0x78, 0x54, 0x3, 0xff,
|
||||
0x86, 0x56, 0x1, 0xff, 0xc3, 0x85, 0x0, 0xff,
|
||||
0xe1, 0xa1, 0x0, 0x7d, 0x20, 0x1f, 0xfc, 0x44,
|
||||
0xc1, 0x0, 0xff, 0xe0, 0x53, 0x80, 0x7f, 0xf1,
|
||||
0x3c, 0x3, 0xf1, 0xa8, 0x7, 0xff, 0x10, 0xf9,
|
||||
0xc4, 0x3, 0xf3, 0x72, 0x80, 0x7f, 0xf1, 0x11,
|
||||
0x0, 0x1f, 0xd6, 0x1, 0xff, 0xc6, 0x8e, 0xb7,
|
||||
0x55, 0x9e, 0xba, 0x44, 0x3, 0xff, 0x8b, 0x20,
|
||||
0x1f, 0xe1, 0x70, 0xf, 0xfe, 0x3a, 0x45, 0x55,
|
||||
0xa, 0x20, 0x1f, 0xfc, 0x66, 0x20, 0xf, 0xfa,
|
||||
0xc, 0x3, 0xff, 0xbe, 0x52, 0x1, 0xff, 0xc1,
|
||||
0xd1, 0x0, 0xff, 0xef, 0x78, 0x7, 0xff, 0x8,
|
||||
0x68, 0x3, 0xff, 0xbb, 0x24, 0x1, 0xff, 0xc3,
|
||||
0x49, 0x0, 0xff, 0xed, 0xc3, 0x0, 0x7f, 0xf1,
|
||||
0x5a, 0x40, 0x3f, 0xfb, 0x2e, 0xe0, 0xf, 0xfe,
|
||||
0x3b, 0x48, 0x7, 0xff, 0x5d, 0xe0, 0x3, 0xff,
|
||||
0x92, 0xd8, 0x20, 0x1f, 0xfd, 0x31, 0xb8, 0x0,
|
||||
0xff, 0xe5, 0x9f, 0xa8, 0x7, 0xff, 0x44, 0xfd,
|
||||
0x0, 0x3f, 0xf9, 0xa3, 0x54, 0x20, 0xf, 0xfe,
|
||||
0x68, 0xce, 0x8, 0x7, 0xff, 0x41, 0x76, 0x48,
|
||||
0x3, 0xff, 0x92, 0x31, 0xcc, 0x1, 0xff, 0xd4,
|
||||
0x6d, 0xc6, 0x10, 0xf, 0xfe, 0x18, 0xad, 0xf3,
|
||||
0x80, 0x7f, 0xf6, 0xe, 0x7b, 0x65, 0x90, 0x84,
|
||||
0x2, 0x12, 0x46, 0x9c, 0xea, 0x40, 0xf, 0xfe,
|
||||
0x38,
|
||||
|
||||
/* U+0031 "1" */
|
||||
0x39, 0x9f, 0xfe, 0x83, 0x37, 0xff, 0x4b, 0x0,
|
||||
0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff,
|
||||
0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xa4, 0xbf,
|
||||
0xff, 0xfc, 0x11, 0x0, 0xff, 0xff, 0x80, 0x7f,
|
||||
0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0,
|
||||
0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff,
|
||||
0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80,
|
||||
0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff,
|
||||
0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3,
|
||||
0xff, 0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff,
|
||||
0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f,
|
||||
0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7, 0xff, 0xfa,
|
||||
0x5d, 0xff, 0xf8, 0x5e,
|
||||
|
||||
/* U+0032 "2" */
|
||||
0x0, 0xff, 0xe6, 0x9, 0x16, 0x10, 0xf, 0xfe,
|
||||
0xe9, 0x34, 0x5e, 0xff, 0xbb, 0x77, 0x77, 0xf6,
|
||||
0x4b, 0x10, 0x7, 0xff, 0x51, 0x27, 0x75, 0x2e,
|
||||
0x84, 0x1, 0xfc, 0x26, 0xd3, 0xbd, 0x26, 0x1,
|
||||
0xff, 0xce, 0x17, 0xeb, 0x62, 0x0, 0xff, 0xe3,
|
||||
0x8b, 0x66, 0xb0, 0x7, 0xff, 0x28, 0x67, 0xa0,
|
||||
0x40, 0x3f, 0xf9, 0xa5, 0x3a, 0xa0, 0x1f, 0xfc,
|
||||
0x77, 0xe6, 0x0, 0xff, 0xe9, 0x15, 0x50, 0x40,
|
||||
0x3f, 0xf8, 0x65, 0xb0, 0x1, 0xff, 0xd6, 0x5f,
|
||||
0x30, 0xf, 0xfe, 0xb, 0x69, 0x0, 0x7f, 0xf6,
|
||||
0x7, 0x10, 0x3, 0xfe, 0x99, 0x0, 0x7f, 0xf7,
|
||||
0x2c, 0xc0, 0x3f, 0xa5, 0x80, 0x3f, 0xfb, 0xda,
|
||||
0x20, 0x1f, 0x4b, 0x0, 0x7f, 0xf7, 0xc7, 0x40,
|
||||
0x3c, 0xec, 0x1, 0xff, 0xe1, 0x35, 0x0, 0xc7,
|
||||
0x0, 0x1f, 0xfe, 0x3b, 0x0, 0xd4, 0x1, 0xff,
|
||||
0xe4, 0x15, 0x0, 0xa6, 0x84, 0x3, 0xff, 0x88,
|
||||
0x2b, 0x39, 0xdf, 0xee, 0xdb, 0x84, 0x0, 0xff,
|
||||
0xe3, 0xf0, 0x6, 0x5f, 0x50, 0xf, 0xfe, 0xa,
|
||||
0xdf, 0x53, 0x18, 0x80, 0x4, 0x91, 0xef, 0x54,
|
||||
0x3, 0xff, 0x8a, 0x80, 0x1c, 0x35, 0x62, 0x1,
|
||||
0xfc, 0x79, 0x48, 0x1, 0xff, 0xc0, 0x2a, 0xa0,
|
||||
0x7, 0xff, 0x15, 0x0, 0x3c, 0x9c, 0xc0, 0x1f,
|
||||
0x3e, 0x18, 0x7, 0xff, 0x11, 0x64, 0x3, 0xff,
|
||||
0x88, 0x60, 0x1f, 0xa6, 0xc8, 0x3, 0x44, 0x0,
|
||||
0x3f, 0xf8, 0xec, 0x60, 0x1f, 0xfc, 0x3e, 0x0,
|
||||
0xfe, 0x4d, 0x70, 0x4, 0x38, 0x7, 0xff, 0x26,
|
||||
0x80, 0x3f, 0xf8, 0x66, 0x1, 0xff, 0x46, 0x3b,
|
||||
0x80, 0x3f, 0xf9, 0x4e, 0x1, 0xff, 0xc3, 0x10,
|
||||
0xf, 0xfe, 0x1, 0xc0, 0x7, 0xff, 0x2c, 0x40,
|
||||
0x3f, 0xfd, 0x42, 0x1, 0xff, 0xc2, 0x10, 0xf,
|
||||
0xfe, 0xd0, 0x80, 0x7f, 0xf0, 0x8c, 0x3, 0xff,
|
||||
0xb2, 0x40, 0x1f, 0xfc, 0x3e, 0x0, 0xff, 0xec,
|
||||
0xb8, 0x7, 0xff, 0xc, 0xc0, 0x3f, 0xfb, 0x3e,
|
||||
0x1, 0xff, 0xc3, 0x50, 0xf, 0xfe, 0xc0, 0xa8,
|
||||
0x7, 0xff, 0x8, 0x44, 0x1, 0xff, 0xd8, 0x80,
|
||||
0xf, 0xfe, 0x1b, 0x80, 0x7f, 0xf6, 0x9, 0x80,
|
||||
0x3f, 0xf8, 0x7a, 0x1, 0xff, 0xd8, 0xe0, 0xf,
|
||||
0xfe, 0x19, 0x20, 0x7, 0xff, 0x5e, 0xc, 0x3,
|
||||
0xff, 0x87, 0x0, 0x1f, 0xfd, 0x76, 0x70, 0xf,
|
||||
0xfe, 0x19, 0xa8, 0x7, 0xff, 0x59, 0x64, 0x3,
|
||||
0xff, 0x89, 0xe0, 0x1f, 0xfd, 0x66, 0xa0, 0xf,
|
||||
0xfe, 0x23, 0x98, 0x7, 0xff, 0x55, 0xa4, 0x3,
|
||||
0xff, 0x88, 0x70, 0x1, 0xff, 0xd5, 0x79, 0x0,
|
||||
0xff, 0xe2, 0xe, 0x80, 0x7f, 0xf5, 0x62, 0x0,
|
||||
0x1f, 0xfc, 0x5d, 0x10, 0xf, 0xfe, 0xa4, 0x38,
|
||||
0x7, 0xff, 0x16, 0x8c, 0x3, 0xff, 0xa9, 0x2e,
|
||||
0x1, 0xff, 0xc5, 0x95, 0x0, 0xff, 0xea, 0x53,
|
||||
0x0, 0x7f, 0xf1, 0x61, 0x80, 0x3f, 0xfa, 0x94,
|
||||
0xa0, 0x1f, 0xfc, 0x59, 0x70, 0xf, 0xfe, 0xa5,
|
||||
0xa8, 0x7, 0xff, 0x16, 0x58, 0x3, 0xff, 0xa9,
|
||||
0x68, 0x1, 0xff, 0xc5, 0x96, 0x0, 0xff, 0xea,
|
||||
0x62, 0x0, 0x7f, 0xf1, 0x69, 0x80, 0x3f, 0xfa,
|
||||
0x63, 0x86, 0x1, 0xff, 0xc5, 0xb5, 0x0, 0xff,
|
||||
0xe9, 0x8e, 0x18, 0x7, 0xff, 0x16, 0xd0, 0x3,
|
||||
0xff, 0xa6, 0x38, 0x40, 0x1f, 0xfc, 0x5c, 0x40,
|
||||
0xf, 0xfe, 0x99, 0x61, 0x0, 0x7f, 0xf1, 0x7,
|
||||
0xc, 0x3, 0xff, 0xa6, 0x58, 0x40, 0x1f, 0xfc,
|
||||
0x41, 0xc3, 0x0, 0xff, 0xe9, 0x96, 0x8, 0x7,
|
||||
0xff, 0x10, 0x70, 0x80, 0x3f, 0xfa, 0x67, 0x82,
|
||||
0x1, 0xff, 0xc4, 0x2f, 0x20, 0xf, 0xfe, 0x99,
|
||||
0xe0, 0x80, 0x7f, 0xf1, 0xb, 0x4, 0x3, 0xff,
|
||||
0xa6, 0x98, 0x1, 0xff, 0xc5, 0x3c, 0x10, 0xf,
|
||||
0xfe, 0x9a, 0x58, 0x7, 0xff, 0x14, 0xf4, 0x40,
|
||||
0x3f, 0xfa, 0x6b, 0x60, 0x1f, 0xfc, 0x54, 0xc1,
|
||||
0x0, 0xff, 0xe9, 0xb5, 0x0, 0x7f, 0xf1, 0x52,
|
||||
0xc0, 0x3f, 0xfa, 0x8d, 0x20, 0x1f, 0xfc, 0x66,
|
||||
0xdd, 0xff, 0xe7, 0xe0, 0x6, 0x79, 0x0, 0xff,
|
||||
0xe3, 0x91, 0x7f, 0xf4, 0xc, 0x2, 0x78, 0x0,
|
||||
0xff, 0xf4, 0x50, 0x7, 0xff, 0xfc, 0x3, 0xff,
|
||||
0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80,
|
||||
0x7f, 0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff,
|
||||
0xf0, 0xf, 0xf8,
|
||||
|
||||
/* U+0033 "3" */
|
||||
0x0, 0xd7, 0xff, 0xff, 0xe2, 0x40, 0xf, 0xff,
|
||||
0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1,
|
||||
0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff,
|
||||
0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xfc, 0xb1, 0x50,
|
||||
0xf, 0xff, 0x4e, 0x88, 0x7, 0xd8, 0xcd, 0xff,
|
||||
0xcf, 0x30, 0xf, 0xfe, 0x25, 0x98, 0x7, 0xe7,
|
||||
0x99, 0xff, 0xe7, 0x40, 0x80, 0x7f, 0xf0, 0xe5,
|
||||
0x0, 0x3f, 0xfb, 0x1a, 0x40, 0x1f, 0xfc, 0x27,
|
||||
0x60, 0xf, 0xfe, 0xc5, 0x18, 0x7, 0xff, 0x9,
|
||||
0x60, 0x3, 0xff, 0xb1, 0x2a, 0x1, 0xff, 0xc2,
|
||||
0x3a, 0x0, 0xff, 0xec, 0x3b, 0x0, 0x7f, 0xf0,
|
||||
0x8b, 0x40, 0x3f, 0xfb, 0xb, 0x0, 0x1f, 0xfc,
|
||||
0x21, 0xf1, 0x0, 0xff, 0xeb, 0x9d, 0x0, 0x7f,
|
||||
0xf0, 0xf4, 0x80, 0x3f, 0xfa, 0xe5, 0xa0, 0x1f,
|
||||
0xfc, 0x3a, 0x30, 0xf, 0xfe, 0xb8, 0xf8, 0x80,
|
||||
0x7f, 0xf0, 0xa1, 0x40, 0x3f, 0xfb, 0x1a, 0x40,
|
||||
0x1f, 0xfc, 0x26, 0x70, 0xf, 0xfe, 0xc5, 0x18,
|
||||
0x7, 0xff, 0x9, 0x24, 0x3, 0xff, 0xb1, 0x2a,
|
||||
0x1, 0xff, 0xc2, 0x3b, 0x0, 0xff, 0xec, 0x3b,
|
||||
0x0, 0x7f, 0xf0, 0x8b, 0x40, 0x3f, 0xfb, 0xb,
|
||||
0x0, 0x1f, 0xfc, 0x21, 0xf1, 0x0, 0xff, 0xeb,
|
||||
0x9d, 0x0, 0x7f, 0xf0, 0xd0, 0x40, 0x3f, 0xfb,
|
||||
0x1a, 0x1, 0xff, 0xc4, 0x5c, 0xfc, 0x72, 0x0,
|
||||
0xff, 0xea, 0x8, 0x7, 0xff, 0x1c, 0xe3, 0x71,
|
||||
0x0, 0x3f, 0xfd, 0x27, 0x78, 0x60, 0x1f, 0xfe,
|
||||
0x93, 0xc9, 0x0, 0xff, 0xf5, 0x36, 0x8, 0x7,
|
||||
0xff, 0xa4, 0xf0, 0x40, 0x3f, 0xfd, 0x25, 0x80,
|
||||
0x1f, 0xfe, 0xa2, 0xa0, 0xf, 0xff, 0x52, 0xa8,
|
||||
0x3, 0xff, 0xd5, 0x62, 0x1, 0xff, 0xc9, 0x44,
|
||||
0xf8, 0xcc, 0x42, 0x1, 0xff, 0xc9, 0x19, 0x0,
|
||||
0xff, 0xe4, 0xdd, 0xfd, 0x98, 0xde, 0xe6, 0x41,
|
||||
0x0, 0x7f, 0xf1, 0x98, 0x3, 0xff, 0xa8, 0x26,
|
||||
0xfb, 0x64, 0x1, 0xff, 0xc4, 0x15, 0x0, 0xff,
|
||||
0xeb, 0xa6, 0xa8, 0x7, 0xff, 0x13, 0xc0, 0x3f,
|
||||
0xfb, 0x34, 0x80, 0x1f, 0xfc, 0x34, 0x0, 0xff,
|
||||
0xed, 0x48, 0x7, 0xff, 0xc, 0x44, 0x1, 0xff,
|
||||
0xd9, 0x24, 0x0, 0xff, 0xe1, 0x90, 0x7, 0xff,
|
||||
0x6b, 0x40, 0x3f, 0xf8, 0x6e, 0x1, 0xff, 0xda,
|
||||
0x30, 0xf, 0xfe, 0x18, 0x80, 0x7f, 0xff, 0xc0,
|
||||
0x3f, 0xf8, 0xc6, 0x1, 0xff, 0xc3, 0x10, 0xf,
|
||||
0xfe, 0xd6, 0x0, 0x7f, 0xf0, 0xdc, 0x3, 0xcf,
|
||||
0xc8, 0x1, 0xff, 0xd0, 0x15, 0x0, 0xff, 0xe1,
|
||||
0x90, 0x7, 0xa4, 0x6e, 0x88, 0x3, 0xff, 0x9d,
|
||||
0x60, 0x1f, 0xfc, 0x32, 0x10, 0xe, 0x71, 0x0,
|
||||
0x2e, 0xc9, 0x0, 0x7f, 0xf2, 0xdd, 0x40, 0x3f,
|
||||
0xf8, 0x6e, 0x1, 0xe9, 0x0, 0xe6, 0xdb, 0x50,
|
||||
0xf, 0xfe, 0x38, 0xd4, 0x0, 0x7f, 0xf1, 0x3c,
|
||||
0x3, 0x9c, 0x40, 0x3e, 0x4a, 0xfa, 0x61, 0x0,
|
||||
0xff, 0xe0, 0x94, 0x72, 0x80, 0x7f, 0xf1, 0x5,
|
||||
0x40, 0x3a, 0x40, 0x3f, 0xe5, 0x9e, 0xe5, 0xc3,
|
||||
0x2a, 0x19, 0x91, 0x5e, 0x77, 0x4e, 0x1, 0xff,
|
||||
0xc6, 0x70, 0xe, 0x71, 0x0, 0xff, 0xe1, 0xa,
|
||||
0x3c, 0xd5, 0xe6, 0x2e, 0xa1, 0x88, 0x3, 0xff,
|
||||
0x91, 0x0, 0x1d, 0x20, 0x1f, 0xfe, 0x37, 0x10,
|
||||
0xc, 0xe2, 0x1, 0xff, 0xe2, 0x28, 0x0, 0xe9,
|
||||
0x0, 0xff, 0xf1, 0xf0, 0x7, 0x38, 0x80, 0x7f,
|
||||
0xf8, 0xa4, 0xc0, 0x3a, 0x40, 0x3f, 0xfc, 0x52,
|
||||
0xc0, 0x1e, 0xc3, 0x0, 0xff, 0xf0, 0x4b, 0x0,
|
||||
0x7c, 0x59, 0x22, 0x1, 0xff, 0xdc, 0x1c, 0x60,
|
||||
0xf, 0xf3, 0x74, 0x88, 0x7, 0xff, 0x65, 0xbc,
|
||||
0xc0, 0x3f, 0xf8, 0x2d, 0xd4, 0x80, 0x1f, 0xfd,
|
||||
0x53, 0xc9, 0x10, 0xf, 0xfe, 0x22, 0xde, 0xc1,
|
||||
0x80, 0x7f, 0xf4, 0x17, 0x30, 0x60, 0x1f, 0xfc,
|
||||
0x82, 0x7c, 0xea, 0x63, 0x0, 0xff, 0xe4, 0xa,
|
||||
0xcf, 0x51, 0x80, 0x7f, 0xf3, 0x45, 0x67, 0x3f,
|
||||
0x69, 0xd5, 0x8, 0x44, 0x1, 0x8, 0x88, 0xda,
|
||||
0x2b, 0x7a, 0x98, 0x40, 0x3f, 0xf8, 0x60,
|
||||
|
||||
/* U+0034 "4" */
|
||||
0x0, 0xff, 0xea, 0x24, 0x47, 0xff, 0xd, 0x80,
|
||||
0x3f, 0xfc, 0x45, 0x6e, 0xff, 0xfc, 0x32, 0x0,
|
||||
0xff, 0xf1, 0x70, 0x7, 0xff, 0xd, 0xd0, 0x3,
|
||||
0xff, 0xc3, 0x6, 0x1, 0xff, 0xc2, 0x38, 0x0,
|
||||
0xff, 0xf0, 0xa3, 0x80, 0x7f, 0xf0, 0xf8, 0x3,
|
||||
0xff, 0xc2, 0x34, 0x1, 0xff, 0xc3, 0xa2, 0x0,
|
||||
0xff, 0xf0, 0xe8, 0x80, 0x7f, 0xf0, 0x95, 0x40,
|
||||
0x1f, 0xfe, 0x18, 0x30, 0xf, 0xfe, 0x11, 0x50,
|
||||
0x7, 0xff, 0x85, 0x1c, 0x3, 0xff, 0x87, 0xc0,
|
||||
0x1f, 0xfe, 0x11, 0xa0, 0xf, 0xfe, 0x1c, 0x18,
|
||||
0x7, 0xff, 0x86, 0x84, 0x3, 0xff, 0x84, 0x8e,
|
||||
0x1, 0xff, 0xe1, 0x74, 0x0, 0xff, 0xe1, 0xd,
|
||||
0x0, 0x7f, 0xf8, 0x4e, 0x0, 0x3f, 0xf8, 0x74,
|
||||
0x20, 0x1f, 0xfe, 0x1, 0xd0, 0xf, 0xfe, 0x1b,
|
||||
0x20, 0x7, 0xff, 0x86, 0xc4, 0x3, 0xff, 0x84,
|
||||
0x52, 0x1, 0xff, 0xe1, 0x65, 0x0, 0xff, 0xe1,
|
||||
0xf8, 0x7, 0xff, 0x84, 0xa4, 0x3, 0xff, 0x87,
|
||||
0x24, 0x1, 0xff, 0xe1, 0xf0, 0xf, 0xfe, 0x1a,
|
||||
0x30, 0x7, 0xff, 0x86, 0x88, 0x3, 0xff, 0x84,
|
||||
0x34, 0x1, 0xff, 0xe1, 0x55, 0x0, 0x7f, 0xf0,
|
||||
0xe8, 0x40, 0x3f, 0xfc, 0x5, 0x40, 0x1f, 0xfc,
|
||||
0x37, 0x40, 0xf, 0xff, 0xf, 0x80, 0x7f, 0xf0,
|
||||
0xce, 0x0, 0x3f, 0xfc, 0x32, 0x40, 0x1f, 0xfc,
|
||||
0x3e, 0x0, 0xff, 0xf0, 0xab, 0x0, 0x7f, 0xf0,
|
||||
0xe8, 0x80, 0x3f, 0xfc, 0x5, 0x60, 0x1f, 0xfc,
|
||||
0x35, 0x50, 0x7, 0xff, 0x87, 0x84, 0x3, 0xff,
|
||||
0x84, 0x54, 0x1, 0xff, 0xe1, 0x83, 0x0, 0xff,
|
||||
0xe1, 0xf0, 0x7, 0xff, 0x85, 0x1c, 0x3, 0xff,
|
||||
0x87, 0x6, 0x1, 0xff, 0xe0, 0x1a, 0x0, 0xff,
|
||||
0xe1, 0xa3, 0x80, 0x7c, 0x51, 0x1f, 0xfc, 0x22,
|
||||
0x0, 0xff, 0xe5, 0x50, 0x80, 0x7f, 0xf0, 0x86,
|
||||
0x80, 0x3f, 0x3b, 0xff, 0xf1, 0x0, 0x3f, 0xf9,
|
||||
0x2e, 0x80, 0x1f, 0xfc, 0x3a, 0x10, 0xf, 0xff,
|
||||
0x1, 0xc0, 0x7, 0xff, 0xd, 0x90, 0x3, 0xff,
|
||||
0xc0, 0x3a, 0x1, 0xff, 0xc3, 0x29, 0x0, 0xff,
|
||||
0xf0, 0xd0, 0x80, 0x7f, 0xf0, 0xfc, 0x3, 0xff,
|
||||
0xc2, 0xe8, 0x1, 0xff, 0xc3, 0x92, 0x0, 0xff,
|
||||
0xf0, 0x1c, 0x0, 0x7f, 0xf0, 0xd1, 0x80, 0x3f,
|
||||
0xfc, 0x3c, 0x1, 0xff, 0xc3, 0x1a, 0x0, 0xff,
|
||||
0xf0, 0xd1, 0x0, 0x7f, 0xf0, 0xe8, 0x40, 0x3f,
|
||||
0xfc, 0xc, 0xa0, 0x1f, 0xfc, 0x37, 0x40, 0xf,
|
||||
0xff, 0x1, 0x48, 0x7, 0xff, 0xc, 0xe0, 0x3,
|
||||
0xff, 0xc3, 0xe0, 0x1f, 0xfc, 0x4e, 0x0, 0xff,
|
||||
0xf0, 0xc9, 0x0, 0x7f, 0xf1, 0x3f, 0x77, 0xff,
|
||||
0x89, 0x40, 0x1f, 0xfc, 0x2a, 0xdd, 0xff, 0x18,
|
||||
0x2b, 0x0, 0x7f, 0xf1, 0x48, 0xbf, 0xf8, 0xa0,
|
||||
0x1f, 0xfc, 0x42, 0x2f, 0xf3, 0x85, 0x0, 0x7f,
|
||||
0xff, 0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0,
|
||||
0xf, 0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff,
|
||||
0xfe, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff, 0x80,
|
||||
0x7f, 0xff, 0xc0, 0x3f, 0xfa, 0x1f, 0xff, 0xfd,
|
||||
0x82, 0x0, 0xff, 0xe1, 0x57, 0xff, 0xf9, 0x0,
|
||||
0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf, 0xff,
|
||||
0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1,
|
||||
0xff, 0xff, 0x0, 0xff, 0xff, 0x80, 0x7f, 0xff,
|
||||
0xc0, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf,
|
||||
0xff, 0xf8, 0x7, 0xff, 0x82, 0xdd, 0xff, 0xf8,
|
||||
0x60, 0x1f, 0xf0,
|
||||
|
||||
/* U+0035 "5" */
|
||||
0x0, 0xfe, 0xaf, 0xff, 0xfe, 0xe3, 0x0, 0x7f,
|
||||
0xf0, 0x44, 0x3, 0xff, 0xd4, 0xc0, 0x1f, 0xfe,
|
||||
0xa3, 0x0, 0xff, 0xf5, 0x8, 0x7, 0xff, 0xa4,
|
||||
0x40, 0x3f, 0xfd, 0x46, 0x1, 0xff, 0xea, 0x60,
|
||||
0xf, 0xff, 0x50, 0x80, 0x7f, 0xfa, 0x88, 0x3,
|
||||
0xff, 0xd5, 0xe0, 0x1f, 0xfe, 0xa2, 0x0, 0xff,
|
||||
0xf5, 0x8, 0x7, 0xff, 0x9, 0x9b, 0xff, 0x9e,
|
||||
0xa0, 0x1f, 0xfc, 0x6, 0x0, 0xff, 0xe0, 0xd4,
|
||||
0xcf, 0xff, 0x3c, 0xc0, 0x3f, 0xf8, 0x6, 0x1,
|
||||
0xff, 0xc1, 0x60, 0xf, 0xfe, 0xe0, 0x80, 0x7f,
|
||||
0xf0, 0x44, 0x3, 0xff, 0xb6, 0x20, 0x1f, 0xfc,
|
||||
0x22, 0x0, 0xff, 0xed, 0x98, 0x7, 0xff, 0xa9,
|
||||
0x80, 0x3f, 0xf8, 0x22, 0x1, 0xff, 0xdc, 0x10,
|
||||
0xf, 0xfe, 0x9, 0x0, 0x7f, 0xf7, 0x8, 0x3,
|
||||
0xff, 0x82, 0xe0, 0x1f, 0xfd, 0xcf, 0x0, 0xff,
|
||||
0xe0, 0x90, 0x7, 0xff, 0x70, 0x80, 0x3f, 0xf8,
|
||||
0x22, 0x1, 0xff, 0xdc, 0x10, 0xf, 0xfe, 0xf,
|
||||
0x0, 0x7f, 0xf7, 0x18, 0x3, 0xff, 0x82, 0x60,
|
||||
0x1f, 0xfd, 0xc3, 0x0, 0xff, 0xe0, 0xd5, 0xdf,
|
||||
0xaa, 0x65, 0xc, 0x84, 0x1, 0xff, 0xcf, 0x10,
|
||||
0xf, 0xfe, 0x12, 0x27, 0x95, 0x98, 0xf3, 0x7b,
|
||||
0xfb, 0x4c, 0x40, 0x1f, 0xfc, 0x91, 0x0, 0xff,
|
||||
0xe8, 0x92, 0xce, 0xe3, 0x88, 0x7, 0xff, 0x18,
|
||||
0xc0, 0x3f, 0xfa, 0xa7, 0x1d, 0x22, 0x1, 0xff,
|
||||
0xc4, 0x60, 0xf, 0xfe, 0xc3, 0x74, 0x0, 0x7f,
|
||||
0xf0, 0xc4, 0x3, 0xff, 0xb4, 0xf8, 0x40, 0x1f,
|
||||
0xfc, 0x12, 0x0, 0xff, 0xed, 0x9e, 0xa0, 0x7,
|
||||
0xff, 0x3, 0xc0, 0x3f, 0xfb, 0xb6, 0x80, 0x1f,
|
||||
0xf1, 0x0, 0x7f, 0xf7, 0xac, 0xc0, 0x3f, 0xc2,
|
||||
0x1, 0xff, 0xdf, 0xd1, 0x0, 0xfe, 0x60, 0xf,
|
||||
0xfe, 0xf8, 0xd8, 0x7, 0xf1, 0x80, 0x7f, 0xf8,
|
||||
0x14, 0x80, 0x3f, 0x8, 0x7, 0xff, 0x86, 0x0,
|
||||
0x3f, 0x7f, 0xff, 0xf2, 0x3b, 0x2e, 0x58, 0xc0,
|
||||
0x3f, 0xf9, 0xa, 0x20, 0x1f, 0xfc, 0xf1, 0x34,
|
||||
0x69, 0xcf, 0x93, 0x0, 0xff, 0xe3, 0x38, 0x7,
|
||||
0xff, 0x59, 0xb2, 0x40, 0x3f, 0xf8, 0xb4, 0x1,
|
||||
0xff, 0xd8, 0x6c, 0x0, 0xff, 0xe2, 0x18, 0x7,
|
||||
0xff, 0x64, 0xe4, 0x3, 0xff, 0x88, 0x40, 0x1f,
|
||||
0xfd, 0x96, 0x10, 0xf, 0xfe, 0x1b, 0x80, 0x7f,
|
||||
0xf6, 0x98, 0x3, 0xff, 0x86, 0x60, 0x1f, 0xfd,
|
||||
0xad, 0x0, 0xff, 0xe1, 0x88, 0x7, 0xff, 0x69,
|
||||
0x80, 0x3f, 0xf8, 0x7e, 0x1, 0xff, 0xda, 0x70,
|
||||
0xf, 0xfe, 0x1f, 0x80, 0x7f, 0xf6, 0x88, 0x3,
|
||||
0xff, 0x86, 0x20, 0x1f, 0x8, 0x7, 0xff, 0x4b,
|
||||
0x0, 0x3f, 0xf8, 0x64, 0x1, 0xe5, 0xe6, 0x0,
|
||||
0xff, 0xe8, 0xa, 0x80, 0x7f, 0xf0, 0xd4, 0x3,
|
||||
0xd0, 0x13, 0x86, 0x1, 0xff, 0xce, 0xb0, 0xf,
|
||||
0xfe, 0x18, 0x88, 0x3, 0x94, 0x80, 0x7, 0x96,
|
||||
0x60, 0x1f, 0xfc, 0xb7, 0x50, 0xf, 0xfe, 0x1a,
|
||||
0x0, 0x7a, 0x0, 0x39, 0x33, 0xc, 0x20, 0x1f,
|
||||
0xfc, 0x61, 0xa8, 0x0, 0xff, 0xe2, 0x78, 0x7,
|
||||
0x29, 0x0, 0x7c, 0x73, 0xd6, 0xc4, 0x1, 0xff,
|
||||
0xc1, 0x28, 0xe5, 0x0, 0xff, 0xe2, 0xa8, 0x7,
|
||||
0x40, 0x7, 0xfc, 0x93, 0xbd, 0x72, 0xea, 0x86,
|
||||
0x64, 0x57, 0x9d, 0xd3, 0x80, 0x7f, 0xf1, 0x98,
|
||||
0x3, 0x98, 0x80, 0x3f, 0xf8, 0x42, 0x8d, 0x15,
|
||||
0x79, 0x8b, 0xa8, 0x62, 0x0, 0xff, 0xe4, 0x40,
|
||||
0x7, 0x40, 0x7, 0xff, 0x8d, 0xc4, 0x3, 0x30,
|
||||
0x80, 0x7f, 0xf8, 0x8a, 0x0, 0x3a, 0x0, 0x3f,
|
||||
0xfc, 0x7c, 0x1, 0xcc, 0x20, 0x1f, 0xfe, 0x29,
|
||||
0x30, 0xe, 0xb0, 0xf, 0xff, 0x14, 0xb0, 0x7,
|
||||
0xb1, 0x0, 0x3f, 0xfc, 0x12, 0xc0, 0x1f, 0xd,
|
||||
0xd1, 0x0, 0x7f, 0xf7, 0x7, 0x18, 0x3, 0xfc,
|
||||
0xbb, 0x24, 0x1, 0xff, 0xd9, 0x6f, 0x30, 0xf,
|
||||
0xfe, 0xb, 0x6d, 0xa0, 0x7, 0xff, 0x54, 0xf2,
|
||||
0x44, 0x3, 0xff, 0x88, 0x97, 0xd2, 0x80, 0x1f,
|
||||
0xfd, 0x5, 0xcc, 0x18, 0x7, 0xff, 0x20, 0x5a,
|
||||
0xfe, 0xdc, 0xc0, 0x3f, 0xf9, 0x2, 0xb3, 0xd4,
|
||||
0x60, 0x1f, 0xfc, 0xe4, 0x8c, 0xfd, 0xa7, 0x64,
|
||||
0x21, 0x10, 0x4, 0x22, 0x23, 0x68, 0xad, 0xea,
|
||||
0x61, 0x0, 0xff, 0xe1, 0x80,
|
||||
|
||||
/* U+0036 "6" */
|
||||
0x0, 0xff, 0xe7, 0x12, 0xbd, 0x66, 0xf7, 0xfe,
|
||||
0xed, 0xca, 0x86, 0x30, 0xf, 0xfe, 0xb8, 0xad,
|
||||
0x76, 0xd4, 0x29, 0x90, 0x80, 0x61, 0x23, 0x57,
|
||||
0x9c, 0xfc, 0x73, 0x0, 0xff, 0xe8, 0xa5, 0xf5,
|
||||
0x28, 0x80, 0x7f, 0xf1, 0xce, 0x33, 0x60, 0x40,
|
||||
0x3f, 0xf9, 0x8d, 0xb6, 0x80, 0x1f, 0xfc, 0xe2,
|
||||
0x7e, 0x91, 0x0, 0xff, 0xe4, 0x26, 0xc9, 0x0,
|
||||
0x7f, 0xf4, 0xdb, 0x98, 0x3, 0xff, 0x8a, 0x35,
|
||||
0x64, 0x1, 0xff, 0xd7, 0x20, 0xf, 0xfe, 0x22,
|
||||
0x7a, 0x80, 0x7f, 0xf6, 0xd, 0x0, 0x3f, 0xf8,
|
||||
0x6f, 0x62, 0x1, 0xff, 0xd9, 0x80, 0xf, 0xfe,
|
||||
0x1c, 0x40, 0x3, 0xff, 0xb4, 0x68, 0x1, 0xff,
|
||||
0xc2, 0xa7, 0x0, 0xff, 0xed, 0xc0, 0x7, 0xff,
|
||||
0xa, 0x14, 0x3, 0xff, 0xb6, 0x68, 0x1, 0xff,
|
||||
0xc1, 0x67, 0x0, 0xff, 0xee, 0x40, 0x7, 0xff,
|
||||
0x4, 0xe4, 0x3, 0xff, 0x98, 0x24, 0x50, 0x80,
|
||||
0x7f, 0xc6, 0x80, 0x1f, 0xfc, 0x1d, 0x0, 0xff,
|
||||
0xe4, 0x13, 0xd7, 0x7f, 0x6e, 0xdd, 0xfd, 0x70,
|
||||
0xa2, 0x1, 0xd0, 0x1, 0xff, 0xc1, 0x81, 0x0,
|
||||
0xff, 0xe3, 0x36, 0xea, 0x14, 0x40, 0x3e, 0x14,
|
||||
0x7a, 0xea, 0x40, 0x1, 0xa0, 0x7, 0xff, 0x0,
|
||||
0xdc, 0x3, 0xff, 0x8a, 0x9b, 0x24, 0x1, 0xff,
|
||||
0xc4, 0x5b, 0xb1, 0x40, 0x7, 0xff, 0x7, 0xc0,
|
||||
0x3f, 0xf8, 0xb3, 0x64, 0x1, 0xff, 0xc8, 0x4d,
|
||||
0x40, 0xf, 0xfe, 0x2, 0x98, 0x7, 0xff, 0x12,
|
||||
0xd8, 0x3, 0xff, 0xb3, 0x20, 0x1f, 0xfc, 0x4b,
|
||||
0x40, 0xf, 0xfe, 0xc9, 0x98, 0x3, 0xff, 0x87,
|
||||
0x28, 0x1, 0xff, 0xda, 0x90, 0xf, 0xfe, 0x1a,
|
||||
0x30, 0x7, 0xff, 0x6d, 0x40, 0x3f, 0xf8, 0x72,
|
||||
0x1, 0xff, 0xdb, 0x50, 0xf, 0xfe, 0x1b, 0x10,
|
||||
0x7, 0xff, 0x6f, 0x0, 0x3f, 0xf8, 0x74, 0x1,
|
||||
0xff, 0xdc, 0x40, 0xf, 0xfe, 0x11, 0x18, 0x7,
|
||||
0xff, 0x6c, 0x48, 0x3, 0xff, 0x84, 0xa0, 0x1f,
|
||||
0xfd, 0xc4, 0x0, 0xff, 0xe1, 0xe0, 0x7, 0xf1,
|
||||
0xb4, 0x55, 0xdd, 0x53, 0xc, 0x60, 0x1f, 0xfc,
|
||||
0x7d, 0x0, 0xff, 0xe1, 0xa0, 0x7, 0x13, 0xe7,
|
||||
0xe4, 0xba, 0xa2, 0x4a, 0xcf, 0x39, 0xf6, 0xe2,
|
||||
0x1, 0xff, 0xc3, 0x20, 0xf, 0xfe, 0x11, 0x0,
|
||||
0x64, 0xbd, 0x83, 0x0, 0xff, 0xe1, 0x24, 0x75,
|
||||
0x18, 0x7, 0xff, 0x5, 0x80, 0x3f, 0xf8, 0x4c,
|
||||
0x0, 0x2b, 0xb2, 0x0, 0x7f, 0xf2, 0x17, 0x2c,
|
||||
0x80, 0x3f, 0xe3, 0x0, 0xff, 0xe1, 0x8, 0x36,
|
||||
0xa0, 0x7, 0xff, 0x31, 0x35, 0xc0, 0x3f, 0xf9,
|
||||
0xa7, 0x32, 0x0, 0xff, 0xe8, 0x45, 0x88, 0x7,
|
||||
0xc2, 0x1, 0xff, 0xc3, 0x66, 0x0, 0x7f, 0xf4,
|
||||
0x93, 0x4, 0x3, 0xc6, 0x1, 0xff, 0xe4, 0x2c,
|
||||
0x10, 0xe, 0x10, 0xf, 0xff, 0x29, 0x68, 0x7,
|
||||
0xff, 0xb0, 0xe0, 0x3, 0xff, 0xd8, 0xe8, 0x1,
|
||||
0xff, 0xec, 0x80, 0x8, 0x40, 0x3f, 0xf9, 0x62,
|
||||
0xd5, 0xbf, 0xf6, 0xdc, 0x18, 0x7, 0xff, 0x18,
|
||||
0xd4, 0x0, 0x60, 0x1f, 0xfc, 0x91, 0x9e, 0x95,
|
||||
0x20, 0x8, 0x91, 0xf3, 0x54, 0x3, 0xff, 0x8b,
|
||||
0x20, 0x1f, 0xfc, 0xb4, 0xf6, 0x0, 0xff, 0x8a,
|
||||
0xa4, 0x3, 0xff, 0x88, 0x64, 0x2, 0x1, 0xff,
|
||||
0xc7, 0x4b, 0x10, 0xf, 0xfe, 0x13, 0x50, 0x7,
|
||||
0xff, 0x11, 0x40, 0x2, 0x1, 0xff, 0xc5, 0x2b,
|
||||
0x0, 0xff, 0xe2, 0xac, 0x0, 0x7f, 0xf0, 0xf4,
|
||||
0x0, 0x40, 0x1f, 0xfc, 0x59, 0x0, 0xff, 0xe3,
|
||||
0xb9, 0x0, 0x7f, 0xf0, 0x90, 0x0, 0xc0, 0x1f,
|
||||
0xfc, 0x43, 0x40, 0xf, 0xfe, 0x45, 0x0, 0x7f,
|
||||
0xf0, 0x84, 0x1, 0xa0, 0x1f, 0xfc, 0x4b, 0x0,
|
||||
0xff, 0xe4, 0xa8, 0x7, 0xff, 0xc, 0x40, 0x80,
|
||||
0x3f, 0xf8, 0x86, 0x1, 0xff, 0xc9, 0x20, 0xf,
|
||||
0xfe, 0x19, 0x82, 0x0, 0x7f, 0xf1, 0x18, 0x3,
|
||||
0xff, 0x94, 0x20, 0x1f, 0xfc, 0x21, 0x1, 0x20,
|
||||
0xf, 0xfe, 0xd1, 0x80, 0x7f, 0xf0, 0x84, 0x0,
|
||||
0x80, 0x1f, 0xfc, 0x31, 0x0, 0xff, 0xe5, 0x18,
|
||||
0x7, 0xff, 0x8, 0xc0, 0x1a, 0x1, 0xff, 0xc3,
|
||||
0x50, 0xf, 0xfe, 0x48, 0x88, 0x3, 0xff, 0x84,
|
||||
0x20, 0x4, 0x10, 0xf, 0xfe, 0x16, 0x80, 0x7f,
|
||||
0xf2, 0x5c, 0x3, 0xff, 0x84, 0x40, 0x19, 0x80,
|
||||
0x3f, 0xf8, 0x48, 0x40, 0x1f, 0xfc, 0x8a, 0x0,
|
||||
0xff, 0xe1, 0x38, 0x6, 0xa0, 0xf, 0xfe, 0x1c,
|
||||
0x0, 0x7f, 0xf1, 0xd0, 0xc0, 0x3f, 0xf8, 0x5a,
|
||||
0x1, 0x89, 0x0, 0x3f, 0xf8, 0x4a, 0xe0, 0x1f,
|
||||
0xfc, 0x51, 0xa0, 0xf, 0xfe, 0x1a, 0x80, 0x74,
|
||||
0x0, 0x7f, 0xf0, 0xe1, 0xc0, 0x3f, 0xf8, 0x65,
|
||||
0x82, 0x1, 0xff, 0xc2, 0x32, 0x0, 0xe3, 0x50,
|
||||
0xf, 0xfe, 0x1c, 0x59, 0x0, 0x7f, 0xf0, 0x1b,
|
||||
0x8, 0x3, 0xff, 0x87, 0x40, 0x1f, 0x58, 0x7,
|
||||
0xff, 0x11, 0x36, 0x94, 0x40, 0x3c, 0x4f, 0xb2,
|
||||
0x20, 0x1f, 0xfc, 0x32, 0x60, 0xf, 0x86, 0x0,
|
||||
0x3f, 0xf8, 0xab, 0x5d, 0xcd, 0xcc, 0x6f, 0xec,
|
||||
0x10, 0x7, 0xff, 0x16, 0x0, 0x3f, 0x9d, 0x40,
|
||||
0x3f, 0xf8, 0xe2, 0x46, 0x62, 0x0, 0xff, 0xe4,
|
||||
0x22, 0x80, 0x7f, 0xa8, 0x80, 0x3f, 0xfb, 0xc3,
|
||||
0x40, 0x1f, 0xfc, 0xf, 0x10, 0xf, 0xfe, 0xee,
|
||||
0x88, 0x7, 0xff, 0x0, 0xbc, 0x80, 0x3f, 0xfb,
|
||||
0x76, 0x60, 0x1f, 0xfc, 0x21, 0xc3, 0x0, 0xff,
|
||||
0xec, 0xda, 0x0, 0x7f, 0xf1, 0x7, 0x18, 0x3,
|
||||
0xff, 0xac, 0x5a, 0x80, 0x1f, 0xfc, 0x79, 0xb1,
|
||||
0x0, 0xff, 0xe9, 0xae, 0x90, 0x7, 0xff, 0x25,
|
||||
0x3a, 0x4, 0x3, 0xff, 0x9e, 0x79, 0x40, 0x1f,
|
||||
0xfc, 0xd7, 0xe9, 0x20, 0xf, 0xfe, 0x5a, 0x5e,
|
||||
0x18, 0x7, 0xff, 0x41, 0xb7, 0x1c, 0x80, 0x3f,
|
||||
0xf8, 0xa6, 0xfb, 0x68, 0x1, 0xff, 0xd4, 0x38,
|
||||
0xde, 0xb8, 0x63, 0x21, 0x10, 0x0, 0x48, 0xd6,
|
||||
0x2b, 0xb2, 0x8, 0x3, 0xff, 0x86,
|
||||
|
||||
/* U+0037 "7" */
|
||||
0x6d, 0xdf, 0xff, 0x4a, 0x71, 0x17, 0xff, 0xa4,
|
||||
0x40, 0x3f, 0xff, 0xe0, 0x1f, 0xff, 0xf0, 0xf,
|
||||
0xff, 0xf8, 0x7, 0xff, 0xfc, 0x3, 0xff, 0xfe,
|
||||
0x1, 0xff, 0xff, 0x0, 0xff, 0xf0, 0x8, 0x7,
|
||||
0xff, 0xa4, 0x90, 0x3, 0xff, 0xd3, 0x60, 0x1f,
|
||||
0xfc, 0x3a, 0xff, 0xff, 0xe4, 0xa8, 0x7, 0xff,
|
||||
0x11, 0x80, 0x3f, 0xfb, 0x2, 0xa0, 0x1f, 0xfc,
|
||||
0x36, 0x0, 0xff, 0xec, 0xc8, 0x7, 0xff, 0x12,
|
||||
0xc0, 0x3f, 0xfb, 0x2c, 0x1, 0xff, 0xc3, 0x42,
|
||||
0x0, 0xff, 0xec, 0x30, 0x80, 0x7f, 0xf0, 0xfc,
|
||||
0x3, 0xff, 0xb3, 0x60, 0x1f, 0xfc, 0x32, 0x40,
|
||||
0xf, 0xfe, 0xc2, 0x10, 0x7, 0xff, 0xe, 0xc0,
|
||||
0x3f, 0xfb, 0x3e, 0x1, 0xff, 0xc3, 0x16, 0x0,
|
||||
0xff, 0xec, 0x12, 0x0, 0x7f, 0xf0, 0xd8, 0x3,
|
||||
0xff, 0xb3, 0x60, 0x1f, 0xfc, 0x49, 0x0, 0xff,
|
||||
0xec, 0xb0, 0x7, 0xff, 0xd, 0x44, 0x3, 0xa3,
|
||||
0xff, 0xff, 0x82, 0xa0, 0x1f, 0xfc, 0x47, 0x0,
|
||||
0xff, 0xe2, 0x48, 0x7, 0xff, 0x66, 0x40, 0x3f,
|
||||
0xf8, 0x66, 0x60, 0xf, 0xfe, 0xc2, 0x88, 0x7,
|
||||
0xff, 0xe, 0x40, 0x3f, 0xfb, 0x32, 0x1, 0xff,
|
||||
0xc3, 0x15, 0x0, 0xff, 0xec, 0x19, 0x80, 0x3f,
|
||||
0xf8, 0x6c, 0x1, 0xff, 0xd9, 0x90, 0xf, 0xfe,
|
||||
0x24, 0x80, 0x7f, 0xf6, 0x5, 0x40, 0x3f, 0xf8,
|
||||
0x6a, 0x20, 0x1f, 0xfd, 0x89, 0x0, 0xff, 0xe2,
|
||||
0x48, 0x7, 0xff, 0x65, 0x80, 0x3f, 0xf8, 0x66,
|
||||
0x60, 0xf, 0xfe, 0xc3, 0x8, 0x7, 0xff, 0xe,
|
||||
0x40, 0x3f, 0xfb, 0x36, 0x1, 0xff, 0xc3, 0x15,
|
||||
0x0, 0xff, 0xec, 0x21, 0x0, 0x7f, 0xf0, 0xe4,
|
||||
0x3, 0xff, 0xb3, 0xe0, 0x1f, 0xfc, 0x46, 0x0,
|
||||
0xff, 0xec, 0x12, 0x0, 0x7f, 0xf0, 0xd8, 0x40,
|
||||
0x3f, 0xfb, 0x16, 0x1, 0xff, 0xc4, 0xb0, 0xf,
|
||||
0xfe, 0xc0, 0xb0, 0x7, 0xff, 0xd, 0x8, 0x3,
|
||||
0xff, 0xb0, 0xc0, 0x1f, 0xfc, 0x4f, 0x0, 0xff,
|
||||
0xec, 0xc8, 0x7, 0xff, 0xc, 0x90, 0x3, 0xff,
|
||||
0xb0, 0xa2, 0x1, 0xff, 0xc3, 0xa0, 0xf, 0xfe,
|
||||
0xcc, 0x80, 0x7f, 0xf1, 0x1c, 0x3, 0xff, 0xb0,
|
||||
0x66, 0x0, 0xff, 0xe1, 0xb0, 0x7, 0xff, 0x66,
|
||||
0x40, 0x3f, 0xf8, 0x96, 0x1, 0xff, 0xd8, 0x15,
|
||||
0x0, 0xff, 0xe1, 0xa1, 0x0, 0x7f, 0xf6, 0x24,
|
||||
0x3, 0xff, 0x89, 0xe0, 0x1f, 0xfd, 0x96, 0x0,
|
||||
0xff, 0xe1, 0x92, 0x0, 0x7f, 0xf6, 0x18, 0x40,
|
||||
0x3f, 0xf8, 0x76, 0x1, 0xff, 0xd9, 0xb0, 0xf,
|
||||
0xfe, 0x18, 0xb0, 0x7, 0xff, 0x61, 0x8, 0x3,
|
||||
0xff, 0x86, 0xc0, 0x1f, 0xfd, 0x9f, 0x0, 0xff,
|
||||
0xe2, 0x48, 0x7, 0xff, 0x60, 0x90, 0x3, 0xff,
|
||||
0x86, 0xa2, 0x1, 0xff, 0xd8, 0xb0, 0xf, 0xfe,
|
||||
0x24, 0x80, 0x7f, 0xf6, 0x5, 0x80, 0x3f, 0xf8,
|
||||
0x66, 0x60, 0xf, 0xfe, 0xc3, 0x0, 0x7f, 0xf1,
|
||||
0x24, 0x3, 0xff, 0xb3, 0x20, 0x1f, 0xfc, 0x31,
|
||||
0x50, 0xf, 0xfe, 0xc2, 0x88, 0x7, 0xff, 0xd,
|
||||
0x80, 0x3f, 0xfb, 0x32, 0x1, 0xff, 0xc4, 0x90,
|
||||
0xf, 0xfe, 0xc1, 0x98, 0x3, 0xff, 0x86, 0xa2,
|
||||
0x1, 0xff, 0xd8, 0x90, 0xf, 0xfe, 0x24, 0x80,
|
||||
0x7f, 0xf6, 0x5, 0x40, 0x3f, 0xf8, 0x66, 0x60,
|
||||
0xf, 0xfe, 0xc4, 0x80, 0x7f, 0xf1, 0x24, 0x3,
|
||||
0xff, 0xb2, 0xc0, 0x1f, 0xfc, 0x31, 0x50, 0xf,
|
||||
0xfe, 0xc3, 0x8, 0x7, 0xff, 0xe, 0x40, 0x3f,
|
||||
0xfb, 0x36, 0x1, 0xff, 0xc4, 0x60, 0xf, 0xfe,
|
||||
0xc2, 0x10, 0x7, 0xff, 0xd, 0x84, 0x3, 0xff,
|
||||
0x98,
|
||||
|
||||
/* U+0038 "8" */
|
||||
0x0, 0xff, 0xe5, 0x9, 0xab, 0x3c, 0x44, 0xec,
|
||||
0xa8, 0x20, 0x1f, 0xfd, 0xc4, 0x8b, 0xfe, 0xca,
|
||||
0x98, 0x77, 0xa2, 0x6a, 0xfb, 0xf2, 0x54, 0x40,
|
||||
0x3f, 0xfa, 0x89, 0x5f, 0x6e, 0x80, 0x1f, 0xfc,
|
||||
0x33, 0x6a, 0xeb, 0x60, 0xf, 0xfe, 0x83, 0x6d,
|
||||
0xa8, 0x7, 0xff, 0x2d, 0x27, 0xe0, 0x40, 0x3f,
|
||||
0xf9, 0x69, 0xd2, 0x40, 0x1f, 0xfd, 0x7, 0xe7,
|
||||
0x0, 0xff, 0xe4, 0xd, 0x58, 0x80, 0x7f, 0xf5,
|
||||
0x23, 0x4c, 0x3, 0xff, 0x8a, 0x58, 0xa0, 0x1f,
|
||||
0xfd, 0x72, 0xc5, 0x0, 0xff, 0xe1, 0x9e, 0x90,
|
||||
0x7, 0xff, 0x6a, 0x9c, 0x3, 0xff, 0x82, 0x5a,
|
||||
0x1, 0xff, 0xdd, 0x86, 0x0, 0xff, 0x87, 0xc4,
|
||||
0x3, 0xff, 0xbd, 0x28, 0x1, 0xfe, 0x82, 0x0,
|
||||
0xff, 0xf0, 0x48, 0x7, 0xf1, 0xb0, 0x7, 0xff,
|
||||
0x84, 0xa0, 0x3, 0xf4, 0x0, 0x7f, 0xf1, 0x85,
|
||||
0xeb, 0x7b, 0xfd, 0xd7, 0x4, 0x1, 0xff, 0xc6,
|
||||
0x71, 0x0, 0xf1, 0x20, 0x7, 0xff, 0x10, 0x6b,
|
||||
0xa1, 0x48, 0x40, 0x2, 0x8f, 0xb8, 0x60, 0x1f,
|
||||
0xfc, 0x56, 0x0, 0xf2, 0x80, 0x7f, 0xf1, 0xf,
|
||||
0xd4, 0x3, 0xfe, 0x3c, 0x70, 0xf, 0xfe, 0x25,
|
||||
0x0, 0x7b, 0x0, 0x3f, 0xf8, 0x67, 0x82, 0x1,
|
||||
0xff, 0xc2, 0x87, 0x0, 0xff, 0xe1, 0x90, 0x80,
|
||||
0x72, 0x80, 0x7f, 0xf0, 0xfc, 0x3, 0xff, 0x8b,
|
||||
0x4, 0x1, 0xff, 0xc3, 0x50, 0xc, 0x22, 0x0,
|
||||
0xff, 0xe1, 0x21, 0x80, 0x7f, 0xf1, 0xac, 0x3,
|
||||
0xff, 0x86, 0x40, 0x18, 0xc0, 0x3f, 0xf8, 0x78,
|
||||
0x1, 0xff, 0xc7, 0x70, 0xf, 0xfe, 0x18, 0x80,
|
||||
0x61, 0x0, 0xff, 0xe1, 0x98, 0x7, 0xff, 0x1c,
|
||||
0x40, 0x3f, 0xf8, 0x7c, 0x1, 0xff, 0xc8, 0x70,
|
||||
0xf, 0xfe, 0x40, 0x80, 0x7f, 0xf0, 0x84, 0x3,
|
||||
0x10, 0x7, 0xff, 0xd, 0x80, 0x3f, 0xf9, 0x2,
|
||||
0x1, 0xff, 0xc2, 0xf0, 0xc, 0x20, 0x1f, 0xfc,
|
||||
0x32, 0x0, 0xff, 0xe3, 0x88, 0x7, 0xff, 0xc,
|
||||
0x40, 0x38, 0x80, 0x3f, 0xf8, 0x56, 0x1, 0xff,
|
||||
0xc7, 0x70, 0xf, 0xfe, 0x1a, 0x0, 0x72, 0x0,
|
||||
0x7f, 0xf0, 0x8d, 0x0, 0x3f, 0xf8, 0xd6, 0x1,
|
||||
0xff, 0xc2, 0x12, 0x0, 0xed, 0x0, 0xff, 0xe1,
|
||||
0xc8, 0x80, 0x7f, 0xf1, 0x24, 0x80, 0x3f, 0xf8,
|
||||
0x4e, 0x1, 0xe4, 0x20, 0xf, 0xfe, 0x11, 0x61,
|
||||
0x0, 0x7f, 0xf0, 0xa5, 0x80, 0x3f, 0xf8, 0x74,
|
||||
0x1, 0xf5, 0x80, 0x7f, 0xf0, 0xcb, 0x5c, 0x40,
|
||||
0x3f, 0xcb, 0xac, 0x1, 0xff, 0xc3, 0x43, 0x0,
|
||||
0xf9, 0x8c, 0x3, 0xff, 0x89, 0x1d, 0x70, 0xa8,
|
||||
0x68, 0xaf, 0x5f, 0x44, 0x1, 0xff, 0xc4, 0x90,
|
||||
0xf, 0xee, 0x0, 0xff, 0xe3, 0x23, 0xd5, 0xe5,
|
||||
0xd4, 0x28, 0x7, 0xff, 0x1a, 0x8, 0x3, 0xf8,
|
||||
0xac, 0x3, 0xff, 0xbe, 0xce, 0x1, 0xff, 0x25,
|
||||
0x0, 0x7f, 0xf7, 0x56, 0x40, 0x3f, 0xf8, 0x2b,
|
||||
0x82, 0x1, 0xff, 0xda, 0x8a, 0x0, 0xff, 0xe1,
|
||||
0x9f, 0x88, 0x7, 0xff, 0x62, 0xdc, 0x3, 0xff,
|
||||
0x88, 0x36, 0x20, 0x1f, 0xfd, 0x89, 0x30, 0xf,
|
||||
0xfe, 0x1a, 0xf2, 0x80, 0x7f, 0xf6, 0x4b, 0x24,
|
||||
0x3, 0xff, 0x83, 0x14, 0x1, 0xff, 0xdd, 0x6c,
|
||||
0x10, 0xf, 0xf5, 0x38, 0x7, 0xff, 0x7c, 0xfc,
|
||||
0x40, 0x3f, 0x4a, 0x80, 0x7f, 0xf8, 0x47, 0x4,
|
||||
0x3, 0xcc, 0xc0, 0xf, 0xfe, 0x41, 0x22, 0xb2,
|
||||
0xa1, 0x80, 0x7f, 0xf2, 0xa, 0x80, 0x38, 0x64,
|
||||
0x3, 0xff, 0x8c, 0xd7, 0xfb, 0x75, 0x35, 0x79,
|
||||
0xf8, 0xe2, 0x1, 0xff, 0xc5, 0x46, 0x0, 0xd6,
|
||||
0x1, 0xff, 0xc5, 0x6f, 0x94, 0x0, 0xfe, 0x38,
|
||||
0xe9, 0x10, 0xf, 0xfe, 0x24, 0x88, 0x0, 0x94,
|
||||
0x3, 0xff, 0x89, 0x92, 0x1, 0xff, 0xc3, 0x6f,
|
||||
0x30, 0xf, 0xfe, 0x23, 0x0, 0x28, 0x3, 0xff,
|
||||
0x89, 0x86, 0x1, 0xff, 0xc5, 0x1c, 0x30, 0xf,
|
||||
0xfe, 0x1c, 0x80, 0x14, 0x3, 0xff, 0x87, 0x6,
|
||||
0x1, 0xff, 0xc8, 0xe0, 0xf, 0xfe, 0x18, 0x98,
|
||||
0x90, 0x7, 0xff, 0x8, 0x5c, 0x3, 0xff, 0x92,
|
||||
0x4c, 0x1, 0xff, 0xc3, 0x44, 0x0, 0x7f, 0xf0,
|
||||
0xdc, 0x3, 0xff, 0x97, 0x40, 0x1f, 0xfc, 0x3c,
|
||||
0x20, 0xf, 0xfe, 0x1e, 0x80, 0x7f, 0xf2, 0xcc,
|
||||
0x3, 0xff, 0x86, 0xde, 0x1, 0xff, 0xc3, 0x70,
|
||||
0xf, 0xfe, 0x61, 0x0, 0x7f, 0xf0, 0x8c, 0x40,
|
||||
0x3f, 0xf8, 0x62, 0x1, 0xff, 0xcc, 0x10, 0xf,
|
||||
0xfe, 0x10, 0x80, 0x7f, 0xf1, 0x4, 0x3, 0xff,
|
||||
0x98, 0x20, 0x1f, 0xfc, 0x31, 0x0, 0xff, 0xe1,
|
||||
0xb8, 0x7, 0xff, 0x30, 0x80, 0x3f, 0xf8, 0x43,
|
||||
0xe0, 0x1f, 0xfc, 0x3d, 0x0, 0xff, 0xe5, 0x98,
|
||||
0x7, 0xff, 0xc, 0xcc, 0x1, 0xff, 0xc3, 0x70,
|
||||
0xf, 0xfe, 0x5d, 0x0, 0x7f, 0xf0, 0xdd, 0x40,
|
||||
0x3f, 0xf8, 0x62, 0xe0, 0x1f, 0xfc, 0x92, 0x60,
|
||||
0xf, 0xfe, 0x1e, 0x9, 0x0, 0x7f, 0xf0, 0xe0,
|
||||
0xc0, 0x3f, 0xf9, 0x1c, 0x1, 0xff, 0xc4, 0x50,
|
||||
0x50, 0xf, 0xfe, 0x26, 0x18, 0x7, 0xff, 0x14,
|
||||
0x70, 0xc0, 0x3f, 0xf8, 0x64, 0x61, 0x20, 0x1f,
|
||||
0xfc, 0x5c, 0x80, 0xf, 0xfe, 0x1b, 0x79, 0x80,
|
||||
0x7f, 0xf1, 0x28, 0x0, 0x2a, 0x1, 0xff, 0xc5,
|
||||
0x7e, 0x93, 0x0, 0xfe, 0x27, 0xd9, 0x10, 0xf,
|
||||
0xfe, 0x2b, 0x0, 0x56, 0x1, 0xff, 0xc6, 0x16,
|
||||
0xcf, 0xca, 0xa4, 0xca, 0xb3, 0xf6, 0x8, 0x3,
|
||||
0xff, 0x8d, 0x22, 0x1, 0xc, 0x0, 0x7f, 0xf2,
|
||||
0x4d, 0x54, 0xcc, 0x53, 0x0, 0xff, 0xe4, 0x9b,
|
||||
0x0, 0x73, 0xa0, 0x7, 0xff, 0x8c, 0x78, 0x3,
|
||||
0xeb, 0x30, 0xf, 0xff, 0x16, 0x90, 0x7, 0xed,
|
||||
0x30, 0xf, 0xff, 0x5, 0x98, 0x7, 0xf0, 0xe2,
|
||||
0x0, 0x7f, 0xf7, 0x47, 0x50, 0x3, 0xff, 0x81,
|
||||
0x70, 0x1, 0xff, 0xdb, 0x4f, 0x20, 0xf, 0xfe,
|
||||
0x13, 0xe9, 0x80, 0x7f, 0xf5, 0xc6, 0xac, 0x40,
|
||||
0x3f, 0xf8, 0x85, 0x96, 0x60, 0x1f, 0xfd, 0x31,
|
||||
0x8e, 0x50, 0xf, 0xfe, 0x42, 0x65, 0xa8, 0x7,
|
||||
0xff, 0x3c, 0xe7, 0x9c, 0x3, 0xff, 0x98, 0x95,
|
||||
0xf4, 0xa4, 0x1, 0xff, 0xc7, 0x14, 0x9e, 0xc6,
|
||||
0x0, 0xff, 0xe8, 0xad, 0x6f, 0x5c, 0x32, 0x10,
|
||||
0x88, 0x2, 0x11, 0x12, 0x34, 0x56, 0xf5, 0xb0,
|
||||
0x80, 0x7f, 0xf0, 0xc0,
|
||||
|
||||
/* U+0039 "9" */
|
||||
0x0, 0xff, 0xe3, 0xa3, 0xce, 0x6f, 0x7f, 0xdd,
|
||||
0xb9, 0x4e, 0x82, 0x1, 0xff, 0xd9, 0x16, 0xbf,
|
||||
0xb8, 0x63, 0x21, 0x0, 0x84, 0x8d, 0x62, 0xfb,
|
||||
0x92, 0x60, 0x1f, 0xfd, 0x43, 0x9e, 0x94, 0x0,
|
||||
0xff, 0xe2, 0x8b, 0x67, 0x40, 0x80, 0x7f, 0xf3,
|
||||
0xca, 0xb1, 0x80, 0x3f, 0xf9, 0x62, 0xfd, 0x2,
|
||||
0x1, 0xff, 0xcc, 0x7d, 0x50, 0xf, 0xfe, 0x83,
|
||||
0xf2, 0x80, 0x7f, 0xf2, 0x46, 0xe0, 0x3, 0xff,
|
||||
0xa9, 0x56, 0x20, 0x1f, 0xfc, 0x62, 0xf4, 0x0,
|
||||
0xff, 0xeb, 0x27, 0x90, 0x7, 0xff, 0x10, 0xb0,
|
||||
0x40, 0x3f, 0xfb, 0x3, 0x86, 0x1, 0xff, 0xc2,
|
||||
0x2c, 0x10, 0xf, 0xfe, 0xd0, 0xe1, 0x80, 0x7f,
|
||||
0xf0, 0x78, 0x40, 0x3f, 0xfb, 0xba, 0x20, 0x1f,
|
||||
0xf4, 0x98, 0x7, 0xff, 0x78, 0x74, 0x3, 0xfc,
|
||||
0x6c, 0x1, 0xff, 0xc8, 0x23, 0x38, 0x40, 0x3f,
|
||||
0xf8, 0xe7, 0x20, 0x1f, 0xde, 0x1, 0xff, 0xc5,
|
||||
0x17, 0xcf, 0xdc, 0xcb, 0xbe, 0xd8, 0x3, 0xff,
|
||||
0x8a, 0xc6, 0x1, 0xf2, 0x18, 0x7, 0xff, 0x11,
|
||||
0xfa, 0xc, 0x3, 0xe4, 0x9f, 0x70, 0xf, 0xfe,
|
||||
0x27, 0x80, 0x7d, 0xc0, 0x1f, 0xfc, 0x4b, 0x80,
|
||||
0xf, 0xfe, 0xc, 0x58, 0x7, 0xff, 0xc, 0xd8,
|
||||
0x3, 0xca, 0x1, 0xff, 0xc3, 0xb4, 0x0, 0xff,
|
||||
0xe1, 0xa6, 0x0, 0x7f, 0xf0, 0xe0, 0x3, 0x90,
|
||||
0x3, 0xff, 0x86, 0xe8, 0x1, 0xff, 0xc5, 0x38,
|
||||
0x0, 0xff, 0xe1, 0xa, 0x0, 0x6c, 0x0, 0xff,
|
||||
0xe1, 0xc, 0x0, 0x7f, 0xf1, 0xdc, 0x40, 0x3f,
|
||||
0xf8, 0x5e, 0x1, 0x8c, 0x3, 0xff, 0x84, 0xe0,
|
||||
0x1f, 0xfc, 0x96, 0x0, 0xff, 0xe1, 0x21, 0x0,
|
||||
0x4a, 0x1, 0xff, 0xc2, 0xc0, 0xf, 0xfe, 0x4d,
|
||||
0x80, 0x7f, 0xf0, 0xd4, 0x2, 0x10, 0xf, 0xfe,
|
||||
0x13, 0x80, 0x7f, 0xf2, 0x48, 0x3, 0xff, 0x87,
|
||||
0x80, 0x1f, 0xfc, 0x63, 0x0, 0xff, 0xe4, 0x88,
|
||||
0x7, 0xff, 0xd, 0x0, 0x2, 0x1, 0xff, 0xc3,
|
||||
0x30, 0xf, 0xfe, 0xd1, 0x80, 0x80, 0x7f, 0xf0,
|
||||
0xc4, 0x3, 0xff, 0x92, 0x20, 0x1f, 0xfc, 0x44,
|
||||
0x0, 0xff, 0xe2, 0xa8, 0x7, 0xff, 0x25, 0x0,
|
||||
0x3f, 0xf8, 0x9a, 0x0, 0x10, 0xf, 0xfe, 0x16,
|
||||
0x80, 0x7f, 0xf2, 0x7c, 0x3, 0xff, 0x88, 0xc0,
|
||||
0x5, 0x0, 0xff, 0xe1, 0x21, 0x0, 0x7f, 0xf1,
|
||||
0xc9, 0x40, 0x3f, 0xf8, 0x84, 0x0, 0x30, 0xf,
|
||||
0xfe, 0x1c, 0x80, 0x7f, 0xf1, 0xe4, 0x3, 0xff,
|
||||
0x8a, 0x22, 0xc, 0x0, 0xff, 0xe1, 0xa4, 0x0,
|
||||
0x7f, 0xf1, 0x61, 0x0, 0x3f, 0xf8, 0xc6, 0x8,
|
||||
0x1, 0xff, 0xc4, 0x78, 0x0, 0xff, 0xe1, 0xcb,
|
||||
0x80, 0x7f, 0xf1, 0xc4, 0x0, 0xc0, 0x1f, 0xfc,
|
||||
0x47, 0xc4, 0x0, 0xff, 0x97, 0x58, 0x3, 0xff,
|
||||
0x90, 0xe0, 0xa, 0x0, 0xff, 0xe2, 0x9d, 0xec,
|
||||
0x21, 0x8, 0x0, 0x92, 0x3a, 0x88, 0x3, 0xff,
|
||||
0x92, 0x20, 0x3, 0x50, 0xf, 0xfe, 0x31, 0x3d,
|
||||
0xef, 0x7f, 0xb6, 0xdc, 0x40, 0x3f, 0xf9, 0xf0,
|
||||
0x1, 0xff, 0xe8, 0x30, 0x8, 0xa4, 0x3, 0xff,
|
||||
0xd8, 0xca, 0x1, 0xff, 0xe6, 0x30, 0xe, 0xa4,
|
||||
0x0, 0xff, 0xf6, 0x5a, 0x0, 0x7f, 0xf9, 0x4,
|
||||
0x3, 0xeb, 0x60, 0xf, 0xfe, 0x91, 0xc8, 0x7,
|
||||
0xff, 0xd, 0x80, 0x3f, 0x4d, 0x0, 0x7f, 0xf4,
|
||||
0x1f, 0x0, 0x3f, 0xf8, 0x86, 0x1, 0xfc, 0xbc,
|
||||
0xc0, 0x1f, 0xfc, 0xc2, 0xc8, 0xe, 0x0, 0xff,
|
||||
0xe1, 0x88, 0x7, 0xf8, 0x67, 0x58, 0x3, 0xff,
|
||||
0x90, 0x55, 0xa6, 0x0, 0x20, 0xf, 0xfe, 0x11,
|
||||
0x0, 0x7f, 0xf0, 0x4a, 0x7a, 0x54, 0x3, 0xff,
|
||||
0x84, 0x4f, 0x9a, 0xa0, 0x19, 0xc0, 0x3f, 0xf8,
|
||||
0x4e, 0x1, 0xff, 0xc3, 0x16, 0xaf, 0xca, 0x76,
|
||||
0x54, 0x49, 0x5e, 0x2f, 0xb6, 0xc, 0x3, 0xc4,
|
||||
0x1, 0xff, 0xc2, 0xd0, 0xf, 0xfe, 0x39, 0xac,
|
||||
0x4d, 0x5d, 0xd5, 0xe, 0x82, 0x1, 0xf9, 0x0,
|
||||
0x3f, 0xf8, 0x66, 0x1, 0xff, 0xdc, 0xd0, 0xf,
|
||||
0xfe, 0x1a, 0x0, 0x7f, 0xf7, 0x18, 0x3, 0xff,
|
||||
0x84, 0x60, 0x1f, 0xfd, 0xc6, 0x0, 0xff, 0xe1,
|
||||
0xd8, 0x7, 0xff, 0x72, 0x0, 0x3f, 0xf8, 0x6a,
|
||||
0x1, 0xff, 0xdb, 0x81, 0x0, 0xff, 0xe1, 0x19,
|
||||
0x0, 0x7f, 0xf6, 0x8d, 0xc0, 0x3f, 0xf8, 0x72,
|
||||
0x1, 0xff, 0xda, 0x2d, 0x0, 0xff, 0xe1, 0x8a,
|
||||
0x80, 0x7f, 0xf6, 0x4b, 0x4, 0x3, 0xff, 0x87,
|
||||
0x20, 0x1f, 0xfd, 0x94, 0xc1, 0x0, 0xff, 0xe1,
|
||||
0x8b, 0x80, 0x7f, 0xf0, 0x7, 0x98, 0x3, 0xff,
|
||||
0x90, 0x33, 0x62, 0x1, 0xff, 0xc4, 0xb0, 0xf,
|
||||
0xfe, 0xc, 0x8c, 0xeb, 0x0, 0x7f, 0xf1, 0x6,
|
||||
0x3d, 0x80, 0x3f, 0xf8, 0xa8, 0xa0, 0x1f, 0xfc,
|
||||
0x1, 0x70, 0x1, 0x4f, 0xdb, 0xa9, 0x0, 0x7c,
|
||||
0x28, 0xf9, 0xce, 0x20, 0x1f, 0xfc, 0x6a, 0x0,
|
||||
0xff, 0xe0, 0xc8, 0x7, 0x92, 0x2b, 0x7f, 0xb9,
|
||||
0xba, 0xef, 0xeb, 0x83, 0x0, 0xff, 0xe4, 0x50,
|
||||
0x80, 0x7f, 0xf0, 0x5, 0xc0, 0x3f, 0xe1, 0x11,
|
||||
0x10, 0x40, 0x3f, 0xf9, 0x8e, 0xa0, 0x1f, 0xfc,
|
||||
0x19, 0x0, 0xff, 0xee, 0x24, 0x0, 0x7f, 0xf0,
|
||||
0x45, 0xc0, 0x3f, 0xfb, 0x67, 0x60, 0x1f, 0xfc,
|
||||
0x28, 0x0, 0xff, 0xed, 0xa6, 0x0, 0x7f, 0xf0,
|
||||
0x85, 0x80, 0x3f, 0xfb, 0x4d, 0x60, 0x1f, 0xfc,
|
||||
0x38, 0x0, 0xff, 0xed, 0x44, 0x80, 0x7f, 0xf0,
|
||||
0xc5, 0x80, 0x3f, 0xfb, 0x7, 0xae, 0x1, 0xff,
|
||||
0xc4, 0x21, 0x0, 0xff, 0xeb, 0xc, 0xe1, 0x0,
|
||||
0x7f, 0xf1, 0x4f, 0xa0, 0x40, 0x3f, 0xfa, 0x42,
|
||||
0xfc, 0xc0, 0x1f, 0xfc, 0x97, 0xe9, 0x30, 0xf,
|
||||
0xfe, 0x71, 0xcf, 0x40, 0x7, 0xff, 0x35, 0xb3,
|
||||
0x61, 0x0, 0x3f, 0xf9, 0x9, 0x1b, 0x8c, 0x1,
|
||||
0xff, 0xd1, 0x27, 0xbf, 0xda, 0x75, 0x32, 0x10,
|
||||
0xc, 0x24, 0x6a, 0xf3, 0xbf, 0x6e, 0x40, 0x1f,
|
||||
0xfc, 0xa0,
|
||||
|
||||
/* U+003A ":" */
|
||||
0x0, 0xf2, 0xcd, 0xe5, 0xd3, 0x8, 0x7, 0xf3,
|
||||
0x75, 0x32, 0x1a, 0x2c, 0xf4, 0x0, 0x7d, 0x52,
|
||||
0x20, 0x1f, 0x9f, 0x4, 0x3, 0x52, 0x80, 0x7f,
|
||||
0xc7, 0xa0, 0x13, 0x28, 0x7, 0xff, 0x4, 0xe4,
|
||||
0x1, 0x0, 0x1f, 0xfc, 0x36, 0x14, 0x10, 0xf,
|
||||
0xfe, 0x23, 0x60, 0x7, 0xff, 0x17, 0x8, 0x3,
|
||||
0xff, 0x8a, 0xe2, 0x1, 0xff, 0xc5, 0x11, 0x0,
|
||||
0x7f, 0xf1, 0x44, 0x80, 0x3f, 0xf8, 0xaf, 0x80,
|
||||
0x1f, 0xfc, 0x5d, 0x41, 0x0, 0xff, 0xe2, 0x38,
|
||||
0x40, 0x7, 0xff, 0xd, 0x84, 0x19, 0x0, 0x3f,
|
||||
0xf8, 0x25, 0x20, 0x15, 0xa0, 0x7, 0xfc, 0x5e,
|
||||
0x1, 0xd6, 0xe0, 0x1f, 0xcb, 0xa4, 0x1, 0xe8,
|
||||
0xe8, 0x31, 0x1, 0x37, 0xda, 0x0, 0xfe, 0x17,
|
||||
0xce, 0xfe, 0xc8, 0x20, 0xf, 0xff, 0xf8, 0x7,
|
||||
0xff, 0xfc, 0x3, 0xff, 0xfe, 0x1, 0xff, 0xfb,
|
||||
0x59, 0xbc, 0xba, 0x61, 0x0, 0xfe, 0x6e, 0xa6,
|
||||
0x43, 0x45, 0x9e, 0x80, 0xf, 0xaa, 0x44, 0x3,
|
||||
0xf3, 0xe0, 0x80, 0x6a, 0x50, 0xf, 0xf8, 0xf4,
|
||||
0x2, 0x65, 0x0, 0xff, 0xe0, 0x9c, 0x80, 0x20,
|
||||
0x3, 0xff, 0x86, 0xc2, 0x82, 0x1, 0xff, 0xc4,
|
||||
0x6c, 0x0, 0xff, 0xe2, 0xe1, 0x0, 0x7f, 0xf1,
|
||||
0x5c, 0x40, 0x3f, 0xf8, 0xa2, 0x20, 0xf, 0xfe,
|
||||
0x28, 0x90, 0x7, 0xff, 0x15, 0xf0, 0x3, 0xff,
|
||||
0x8b, 0xa8, 0x20, 0x1f, 0xfc, 0x47, 0x8, 0x0,
|
||||
0xff, 0xe1, 0xb0, 0x83, 0x20, 0x7, 0xff, 0x4,
|
||||
0xa4, 0x2, 0xb4, 0x0, 0xff, 0x8b, 0xc0, 0x3a,
|
||||
0xdc, 0x3, 0xf9, 0x74, 0x80, 0x3d, 0x1d, 0x6,
|
||||
0x20, 0x26, 0xfb, 0x40, 0x18
|
||||
};
|
||||
|
||||
|
||||
/*---------------------
|
||||
* 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 = 435, .box_w = 0, .box_h = 0, .ofs_x = 0, .ofs_y = 0},
|
||||
{.bitmap_index = 0, .adv_w = 1043, .box_w = 59, .box_h = 71, .ofs_x = 3, .ofs_y = -1},
|
||||
{.bitmap_index = 801, .adv_w = 602, .box_w = 30, .box_h = 69, .ofs_x = 0, .ofs_y = -1},
|
||||
{.bitmap_index = 901, .adv_w = 906, .box_w = 54, .box_h = 70, .ofs_x = 0, .ofs_y = 0},
|
||||
{.bitmap_index = 1472, .adv_w = 909, .box_w = 55, .box_h = 69, .ofs_x = -1, .ofs_y = -1},
|
||||
{.bitmap_index = 2007, .adv_w = 1058, .box_w = 64, .box_h = 69, .ofs_x = 2, .ofs_y = -1},
|
||||
{.bitmap_index = 2466, .adv_w = 914, .box_w = 55, .box_h = 69, .ofs_x = 0, .ofs_y = -1},
|
||||
{.bitmap_index = 3031, .adv_w = 978, .box_w = 57, .box_h = 70, .ofs_x = 3, .ofs_y = -1},
|
||||
{.bitmap_index = 3813, .adv_w = 952, .box_w = 55, .box_h = 68, .ofs_x = 2, .ofs_y = 0},
|
||||
{.bitmap_index = 4286, .adv_w = 1014, .box_w = 57, .box_h = 71, .ofs_x = 3, .ofs_y = -1},
|
||||
{.bitmap_index = 5114, .adv_w = 978, .box_w = 57, .box_h = 70, .ofs_x = 1, .ofs_y = -1},
|
||||
{.bitmap_index = 5892, .adv_w = 402, .box_w = 19, .box_h = 54, .ofs_x = 3, .ofs_y = -1}
|
||||
};
|
||||
|
||||
/*---------------------
|
||||
* CHARACTER MAPPING
|
||||
*--------------------*/
|
||||
|
||||
|
||||
|
||||
/*Collect the unicode lists and glyph_id offsets*/
|
||||
static const lv_font_fmt_txt_cmap_t cmaps[] =
|
||||
{
|
||||
{
|
||||
.range_start = 32, .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 = 48, .range_length = 11, .glyph_id_start = 2,
|
||||
.unicode_list = NULL, .glyph_id_ofs_list = NULL, .list_length = 0, .type = LV_FONT_FMT_TXT_CMAP_FORMAT0_TINY
|
||||
}
|
||||
};
|
||||
|
||||
/*-----------------
|
||||
* KERNING
|
||||
*----------------*/
|
||||
|
||||
|
||||
/*Pair left and right glyphs for kerning*/
|
||||
static const uint8_t kern_pair_glyph_ids[] =
|
||||
{
|
||||
2, 3,
|
||||
2, 4,
|
||||
2, 5,
|
||||
2, 9,
|
||||
4, 6,
|
||||
5, 4,
|
||||
5, 5,
|
||||
5, 7,
|
||||
5, 9,
|
||||
6, 3,
|
||||
6, 5,
|
||||
6, 9,
|
||||
6, 10,
|
||||
6, 11,
|
||||
6, 12,
|
||||
7, 4,
|
||||
7, 5,
|
||||
7, 7,
|
||||
7, 9,
|
||||
8, 9,
|
||||
9, 2,
|
||||
9, 3,
|
||||
9, 5,
|
||||
9, 6,
|
||||
9, 7,
|
||||
9, 8,
|
||||
9, 10,
|
||||
9, 12,
|
||||
11, 3,
|
||||
11, 4,
|
||||
11, 5,
|
||||
11, 9,
|
||||
12, 9
|
||||
};
|
||||
|
||||
/* Kerning between the respective left and right glyphs
|
||||
* 4.4 format which needs to scaled with `kern_scale`*/
|
||||
static const int8_t kern_pair_values[] =
|
||||
{
|
||||
-6, -6, -15, -6, -15, -8, -8, -15,
|
||||
-14, -38, -11, -65, 15, -6, 12, -8,
|
||||
-8, -8, -18, -6, -31, 15, -11, -100,
|
||||
-26, -31, -26, -31, -6, -6, -15, -6,
|
||||
6
|
||||
};
|
||||
|
||||
/*Collect the kern pair's data in one place*/
|
||||
static const lv_font_fmt_txt_kern_pair_t kern_pairs =
|
||||
{
|
||||
.glyph_ids = kern_pair_glyph_ids,
|
||||
.values = kern_pair_values,
|
||||
.pair_cnt = 33,
|
||||
.glyph_ids_size = 0
|
||||
};
|
||||
|
||||
/*--------------------
|
||||
* 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 = &kern_pairs,
|
||||
.kern_scale = 16,
|
||||
.cmap_num = 2,
|
||||
.bpp = 4,
|
||||
.kern_classes = 0,
|
||||
.bitmap_format = 1,
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
.cache = &cache
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*-----------------
|
||||
* PUBLIC FONT
|
||||
*----------------*/
|
||||
|
||||
/*Initialize a public general font descriptor*/
|
||||
#if LVGL_VERSION_MAJOR >= 8
|
||||
const lv_font_t montserrat_bold_96 = {
|
||||
#else
|
||||
lv_font_t montserrat_bold_96 = {
|
||||
#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 = 71, /*The maximum line height required by the font*/
|
||||
.base_line = 1, /*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 = -10,
|
||||
.underline_thickness = 5,
|
||||
#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 = NULL,
|
||||
#endif
|
||||
.user_data = NULL,
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif /*#if MONTSERRAT_BOLD_96*/
|
||||
|
||||
749
Gui.h
Normal file
749
Gui.h
Normal file
|
|
@ -0,0 +1,749 @@
|
|||
// R-Watch GUI — LVGL integration for T-Watch Ultimate
|
||||
// Tileview navigation with watch face, radio status, GPS, messages, settings
|
||||
// Requires: CO5300.h (display), XL9555.h (power control), DRV2605.h (haptic)
|
||||
|
||||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#if BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
|
||||
#include <lvgl.h>
|
||||
|
||||
// Custom font: 96px Montserrat Bold for time display (digits + colon only)
|
||||
#include "Fonts/montserrat_bold_96.c"
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Color palette (24-bit hex for lv_color_hex)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define GUI_COL_BLACK 0x000000
|
||||
#define GUI_COL_WHITE 0xFFFFEF // Bone white
|
||||
#define GUI_COL_DIM 0x404040 // Dividers, inactive labels
|
||||
#define GUI_COL_MID 0x808080 // Secondary text
|
||||
#define GUI_COL_AMBER 0xFFA500 // LoRa / radio accent
|
||||
#define GUI_COL_TEAL 0x00FFC0 // GPS accent
|
||||
#define GUI_COL_BLUE 0x4080FF // BLE accent
|
||||
#define GUI_COL_RED 0xFF0000 // Warnings
|
||||
#define GUI_COL_GREEN 0x00FF00 // Confirmations
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Layout constants (410x502 display)
|
||||
// ---------------------------------------------------------------------------
|
||||
#define GUI_W CO5300_WIDTH
|
||||
#define GUI_H CO5300_HEIGHT
|
||||
#define GUI_PAD 24
|
||||
|
||||
// Watch face zones (adjusted for 96px time font)
|
||||
#define GUI_STATUS_Y 10
|
||||
#define GUI_TIME_Y 40
|
||||
#define GUI_DATE_Y 150
|
||||
#define GUI_RULE1_Y 190
|
||||
#define GUI_COMP_Y 200
|
||||
#define GUI_COMP_H 90
|
||||
#define GUI_RULE2_Y 295
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LVGL core objects
|
||||
// ---------------------------------------------------------------------------
|
||||
static lv_display_t *gui_display = NULL;
|
||||
static lv_indev_t *gui_indev = NULL;
|
||||
|
||||
#define GUI_BUF_LINES 120 // Must be >= tallest glyph (96px time font)
|
||||
static uint8_t *gui_buf1 = NULL;
|
||||
static uint8_t *gui_buf2 = NULL;
|
||||
|
||||
// Tileview and tiles
|
||||
static lv_obj_t *gui_tileview = NULL;
|
||||
static lv_obj_t *gui_tile_watch = NULL;
|
||||
static lv_obj_t *gui_tile_radio = NULL;
|
||||
static lv_obj_t *gui_tile_gps = NULL;
|
||||
static lv_obj_t *gui_tile_msg = NULL;
|
||||
static lv_obj_t *gui_tile_set = NULL;
|
||||
|
||||
// Convenience alias for display_unblank invalidation
|
||||
static lv_obj_t *gui_screen = NULL;
|
||||
|
||||
// Watch face widgets
|
||||
static lv_obj_t *gui_mode_label = NULL;
|
||||
static lv_obj_t *gui_batt_label = NULL;
|
||||
static lv_obj_t *gui_time_label = NULL;
|
||||
static lv_obj_t *gui_date_label = NULL;
|
||||
static lv_obj_t *gui_lora_value = NULL;
|
||||
static lv_obj_t *gui_lora_label = NULL;
|
||||
static lv_obj_t *gui_gps_value = NULL;
|
||||
static lv_obj_t *gui_gps_label = NULL;
|
||||
static lv_obj_t *gui_steps_value = NULL;
|
||||
static lv_obj_t *gui_steps_label = NULL;
|
||||
|
||||
// Radio status widgets
|
||||
static lv_obj_t *gui_radio_freq = NULL;
|
||||
static lv_obj_t *gui_radio_params = NULL;
|
||||
static lv_obj_t *gui_radio_rssi_bar = NULL;
|
||||
static lv_obj_t *gui_radio_rssi_lbl = NULL;
|
||||
static lv_obj_t *gui_radio_util = NULL;
|
||||
static lv_obj_t *gui_radio_ble = NULL;
|
||||
static lv_obj_t *gui_radio_pkts = NULL;
|
||||
|
||||
// GPS screen widgets
|
||||
static lv_obj_t *gui_gps_coords = NULL;
|
||||
static lv_obj_t *gui_gps_fix = NULL;
|
||||
static lv_obj_t *gui_gps_alt = NULL;
|
||||
static lv_obj_t *gui_gps_beacon = NULL;
|
||||
|
||||
// Touch input via function pointer (set by .ino after touch init)
|
||||
typedef bool (*gui_touch_fn_t)(int16_t *x, int16_t *y);
|
||||
static gui_touch_fn_t gui_touch_fn = NULL;
|
||||
|
||||
// Data update throttle
|
||||
static uint32_t gui_last_data_update = 0;
|
||||
#define GUI_DATA_UPDATE_MS 500
|
||||
|
||||
// Track current tile for haptic feedback
|
||||
static uint8_t gui_last_tile_col = 1;
|
||||
static uint8_t gui_last_tile_row = 1;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LVGL display flush callback
|
||||
// ---------------------------------------------------------------------------
|
||||
// Swap buffer for byte-order conversion (avoids corrupting LVGL's draw buffer)
|
||||
static uint16_t *gui_swap_buf = NULL;
|
||||
#define GUI_SWAP_BUF_PX (GUI_W * GUI_BUF_LINES)
|
||||
|
||||
// Shadow framebuffer for screenshots (native RGB565 LE, readable via JTAG)
|
||||
// Use: openocd -c "dump_image /tmp/screen.bin [gui_screenshot_buf address] 411640"
|
||||
// Then convert with scripts/screenshot.py
|
||||
uint16_t *gui_screenshot_buf = NULL; // non-static: visible to JTAG/nm for screenshots
|
||||
|
||||
static void gui_flush_cb(lv_display_t *disp, const lv_area_t *area, uint8_t *px_map) {
|
||||
uint16_t x1 = area->x1;
|
||||
uint16_t y1 = area->y1;
|
||||
uint16_t w = area->x2 - area->x1 + 1;
|
||||
uint16_t h = area->y2 - area->y1 + 1;
|
||||
|
||||
uint16_t *src = (uint16_t *)px_map;
|
||||
uint32_t count = (uint32_t)w * h;
|
||||
|
||||
// Copy pre-swap pixels to shadow framebuffer for JTAG screenshot
|
||||
if (gui_screenshot_buf) {
|
||||
for (uint16_t row = 0; row < h; row++) {
|
||||
memcpy(&gui_screenshot_buf[(y1 + row) * GUI_W + x1],
|
||||
&src[row * w], w * sizeof(uint16_t));
|
||||
}
|
||||
}
|
||||
|
||||
// Byte-swap RGB565 into separate buffer for CO5300 SPI (big-endian)
|
||||
if (gui_swap_buf && count <= GUI_SWAP_BUF_PX) {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
gui_swap_buf[i] = (src[i] >> 8) | (src[i] << 8);
|
||||
}
|
||||
co5300_push_pixels(x1, y1, w, h, gui_swap_buf);
|
||||
} else {
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
src[i] = (src[i] >> 8) | (src[i] << 8);
|
||||
}
|
||||
co5300_push_pixels(x1, y1, w, h, src);
|
||||
}
|
||||
|
||||
lv_display_flush_ready(disp);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LVGL touch input read callback
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_touch_read_cb(lv_indev_t *indev, lv_indev_data_t *data) {
|
||||
int16_t tx, ty;
|
||||
if (gui_touch_fn && gui_touch_fn(&tx, &ty)) {
|
||||
data->point.x = tx;
|
||||
data->point.y = ty;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
last_unblank_event = millis();
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
void gui_set_touch_handler(gui_touch_fn_t fn) {
|
||||
gui_touch_fn = fn;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_style_black_container(lv_obj_t *obj) {
|
||||
lv_obj_set_style_bg_color(obj, lv_color_hex(GUI_COL_BLACK), 0);
|
||||
lv_obj_set_style_bg_opa(obj, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(obj, 0, 0);
|
||||
lv_obj_set_style_pad_all(obj, 0, 0);
|
||||
lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE);
|
||||
}
|
||||
|
||||
static lv_obj_t *gui_create_rule(lv_obj_t *parent, lv_coord_t y) {
|
||||
static lv_point_precise_t rule_pts[] = {
|
||||
{GUI_PAD, 0}, {GUI_W - GUI_PAD, 0}
|
||||
};
|
||||
lv_obj_t *line = lv_line_create(parent);
|
||||
lv_line_set_points(line, rule_pts, 2);
|
||||
lv_obj_set_style_line_color(line, lv_color_hex(GUI_COL_DIM), 0);
|
||||
lv_obj_set_style_line_width(line, 1, 0);
|
||||
lv_obj_set_pos(line, 0, y);
|
||||
return line;
|
||||
}
|
||||
|
||||
static lv_obj_t *gui_label(lv_obj_t *parent, const lv_font_t *font,
|
||||
uint32_t color, const char *text) {
|
||||
lv_obj_t *lbl = lv_label_create(parent);
|
||||
lv_obj_set_style_text_font(lbl, font, 0);
|
||||
lv_obj_set_style_text_color(lbl, lv_color_hex(color), 0);
|
||||
lv_label_set_text(lbl, text);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
static lv_obj_t *gui_label_at(lv_obj_t *parent, const lv_font_t *font,
|
||||
uint32_t color, const char *text,
|
||||
lv_coord_t x, lv_coord_t y) {
|
||||
lv_obj_t *lbl = gui_label(parent, font, color, text);
|
||||
lv_obj_set_pos(lbl, x, y);
|
||||
return lbl;
|
||||
}
|
||||
|
||||
static void gui_create_complication(lv_obj_t *parent, lv_coord_t x, lv_coord_t w,
|
||||
uint32_t color, const char *label_text,
|
||||
lv_obj_t **value_out, lv_obj_t **label_out) {
|
||||
lv_obj_t *cell = lv_obj_create(parent);
|
||||
lv_obj_remove_style_all(cell);
|
||||
lv_obj_set_size(cell, w, GUI_COMP_H);
|
||||
lv_obj_set_pos(cell, x, 0);
|
||||
|
||||
lv_obj_t *val = gui_label(cell, &lv_font_montserrat_20, color, "--");
|
||||
lv_obj_set_width(val, w);
|
||||
lv_obj_set_style_text_align(val, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_align(val, LV_ALIGN_TOP_MID, 0, 4);
|
||||
|
||||
lv_obj_t *lbl = gui_label(cell, &lv_font_montserrat_14, GUI_COL_DIM, label_text);
|
||||
lv_obj_set_width(lbl, w);
|
||||
lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_align(lbl, LV_ALIGN_TOP_MID, 0, 46);
|
||||
|
||||
if (value_out) *value_out = val;
|
||||
if (label_out) *label_out = lbl;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen: Watch Face (center tile)
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_create_watchface(lv_obj_t *parent) {
|
||||
gui_style_black_container(parent);
|
||||
|
||||
// Status bar
|
||||
gui_mode_label = gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_MID, "IDLE", GUI_PAD, GUI_STATUS_Y);
|
||||
gui_batt_label = gui_label(parent, &lv_font_montserrat_14, GUI_COL_MID, "--%");
|
||||
lv_obj_align(gui_batt_label, LV_ALIGN_TOP_RIGHT, -GUI_PAD, GUI_STATUS_Y);
|
||||
|
||||
// Time (96px custom font — digits and colon only)
|
||||
gui_time_label = gui_label(parent, &lv_font_montserrat_48, GUI_COL_WHITE, "00:00");
|
||||
// TODO: custom 96px font not rendering — using built-in 48 until debugged
|
||||
lv_obj_set_style_text_letter_space(gui_time_label, 2, 0);
|
||||
lv_obj_set_width(gui_time_label, GUI_W);
|
||||
lv_obj_set_style_text_align(gui_time_label, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_set_pos(gui_time_label, 0, GUI_TIME_Y);
|
||||
|
||||
// Date
|
||||
gui_date_label = gui_label(parent, &lv_font_montserrat_20, GUI_COL_MID, "--- -- ---");
|
||||
lv_obj_set_width(gui_date_label, GUI_W);
|
||||
lv_obj_set_style_text_align(gui_date_label, LV_TEXT_ALIGN_CENTER, 0);
|
||||
lv_obj_set_pos(gui_date_label, 0, GUI_DATE_Y);
|
||||
|
||||
// Rule 1
|
||||
gui_create_rule(parent, GUI_RULE1_Y);
|
||||
|
||||
// Complications
|
||||
lv_obj_t *comp = lv_obj_create(parent);
|
||||
lv_obj_remove_style_all(comp);
|
||||
lv_obj_set_size(comp, GUI_W, GUI_COMP_H);
|
||||
lv_obj_set_pos(comp, 0, GUI_COMP_Y);
|
||||
lv_obj_clear_flag(comp, LV_OBJ_FLAG_SCROLLABLE);
|
||||
|
||||
int cw = (GUI_W - GUI_PAD * 2) / 3;
|
||||
gui_create_complication(comp, GUI_PAD, cw, GUI_COL_AMBER, "LoRa", &gui_lora_value, &gui_lora_label);
|
||||
gui_create_complication(comp, GUI_PAD + cw, cw, GUI_COL_TEAL, "GPS", &gui_gps_value, &gui_gps_label);
|
||||
gui_create_complication(comp, GUI_PAD + cw * 2, cw, GUI_COL_WHITE, "Steps", &gui_steps_value, &gui_steps_label);
|
||||
|
||||
// Rule 2
|
||||
gui_create_rule(parent, GUI_RULE2_Y);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen: Radio Status (top tile — swipe down from watch face)
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_create_radio_screen(lv_obj_t *parent) {
|
||||
gui_style_black_container(parent);
|
||||
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "RADIO STATUS", GUI_PAD, 12);
|
||||
|
||||
// Frequency
|
||||
gui_radio_freq = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_AMBER, "--- MHz", GUI_PAD, 40);
|
||||
|
||||
// LoRa parameters
|
||||
gui_radio_params = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_MID, "SF- BW- CR-", GUI_PAD, 80);
|
||||
|
||||
// RSSI
|
||||
gui_create_rule(parent, 115);
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "RSSI", GUI_PAD, 125);
|
||||
gui_radio_rssi_lbl = gui_label(parent, &lv_font_montserrat_20, GUI_COL_AMBER, "---");
|
||||
lv_obj_align(gui_radio_rssi_lbl, LV_ALIGN_TOP_RIGHT, -GUI_PAD, 122);
|
||||
|
||||
lv_obj_t *bar = lv_bar_create(parent);
|
||||
lv_obj_set_size(bar, GUI_W - GUI_PAD * 2, 20);
|
||||
lv_obj_set_pos(bar, GUI_PAD, 150);
|
||||
lv_bar_set_range(bar, -140, -40);
|
||||
lv_bar_set_value(bar, -140, LV_ANIM_OFF);
|
||||
lv_obj_set_style_bg_color(bar, lv_color_hex(GUI_COL_DIM), 0);
|
||||
lv_obj_set_style_bg_color(bar, lv_color_hex(GUI_COL_AMBER), LV_PART_INDICATOR);
|
||||
lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, LV_PART_INDICATOR);
|
||||
gui_radio_rssi_bar = bar;
|
||||
|
||||
// Channel utilization
|
||||
gui_create_rule(parent, 185);
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "CHANNEL", GUI_PAD, 198);
|
||||
gui_radio_util = gui_label(parent, &lv_font_montserrat_20, GUI_COL_MID, "-- %");
|
||||
lv_obj_align(gui_radio_util, LV_ALIGN_TOP_RIGHT, -GUI_PAD, 195);
|
||||
|
||||
// BLE status
|
||||
gui_create_rule(parent, 230);
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "BLE", GUI_PAD, 243);
|
||||
gui_radio_ble = gui_label(parent, &lv_font_montserrat_20, GUI_COL_BLUE, "---");
|
||||
lv_obj_align(gui_radio_ble, LV_ALIGN_TOP_RIGHT, -GUI_PAD, 240);
|
||||
|
||||
// Packet counts
|
||||
gui_create_rule(parent, 275);
|
||||
gui_radio_pkts = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_MID,
|
||||
"RX: 0 TX: 0", GUI_PAD, 290);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen: GPS (right tile — swipe right from watch face)
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_create_gps_screen(lv_obj_t *parent) {
|
||||
gui_style_black_container(parent);
|
||||
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "GPS", GUI_PAD, 12);
|
||||
|
||||
// Coordinates
|
||||
gui_gps_coords = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_TEAL,
|
||||
"-- --", GUI_PAD, 40);
|
||||
|
||||
// Fix quality
|
||||
gui_create_rule(parent, 110);
|
||||
gui_gps_fix = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_MID,
|
||||
"Sats: -- HDOP: --", GUI_PAD, 125);
|
||||
|
||||
// Altitude and speed
|
||||
gui_create_rule(parent, 160);
|
||||
gui_gps_alt = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_MID,
|
||||
"Alt: -- Spd: --", GUI_PAD, 175);
|
||||
|
||||
// Beacon status
|
||||
gui_create_rule(parent, 215);
|
||||
gui_gps_beacon = gui_label_at(parent, &lv_font_montserrat_20, GUI_COL_AMBER,
|
||||
"Beacon: --", GUI_PAD, 230);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen: Messages (left tile — swipe left from watch face)
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_create_msg_screen(lv_obj_t *parent) {
|
||||
gui_style_black_container(parent);
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "MESSAGES", GUI_PAD, 12);
|
||||
lv_obj_t *lbl = gui_label(parent, &lv_font_montserrat_20, GUI_COL_DIM, "No messages");
|
||||
lv_obj_align(lbl, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screen: Settings (bottom tile — swipe up from watch face)
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_create_settings_screen(lv_obj_t *parent) {
|
||||
gui_style_black_container(parent);
|
||||
gui_label_at(parent, &lv_font_montserrat_14, GUI_COL_DIM, "SETTINGS", GUI_PAD, 12);
|
||||
lv_obj_t *lbl = gui_label(parent, &lv_font_montserrat_20, GUI_COL_MID, "Coming soon");
|
||||
lv_obj_align(lbl, LV_ALIGN_CENTER, 0, 0);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tileview change event — haptic feedback
|
||||
// ---------------------------------------------------------------------------
|
||||
static void gui_tile_change_cb(lv_event_t *e) {
|
||||
lv_obj_t *tv = (lv_obj_t *)lv_event_get_target(e);
|
||||
lv_obj_t *tile = (lv_obj_t *)lv_tileview_get_tile_active(tv);
|
||||
if (!tile) return;
|
||||
|
||||
// Get tile position
|
||||
lv_coord_t col = lv_obj_get_x(tile) / GUI_W;
|
||||
lv_coord_t row = lv_obj_get_y(tile) / GUI_H;
|
||||
|
||||
if (col != gui_last_tile_col || row != gui_last_tile_row) {
|
||||
gui_last_tile_col = col;
|
||||
gui_last_tile_row = row;
|
||||
#if defined(DRV2605_H)
|
||||
if (drv2605_ready) drv2605_play(HAPTIC_TRANSITION);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Update all screen data from firmware globals
|
||||
// ---------------------------------------------------------------------------
|
||||
static const char *gui_month_names[] = {"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
|
||||
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};
|
||||
|
||||
static void gui_update_data() {
|
||||
if (!gui_time_label) return;
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - gui_last_data_update < GUI_DATA_UPDATE_MS) return;
|
||||
gui_last_data_update = now;
|
||||
|
||||
// ---- Watch face ----
|
||||
lv_label_set_text_fmt(gui_time_label, "%02d:%02d", rtc_hour, rtc_minute);
|
||||
|
||||
#if HAS_RTC == true
|
||||
if (rtc_year > 0) {
|
||||
const char *mon = (rtc_month >= 1 && rtc_month <= 12) ? gui_month_names[rtc_month - 1] : "---";
|
||||
lv_label_set_text_fmt(gui_date_label, "%d %s %d", rtc_day, mon, rtc_year);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Mode
|
||||
if (bt_state == BT_STATE_CONNECTED) {
|
||||
lv_label_set_text(gui_mode_label, "MODEM");
|
||||
lv_obj_set_style_text_color(gui_mode_label, lv_color_hex(GUI_COL_BLUE), 0);
|
||||
}
|
||||
#if HAS_GPS == true
|
||||
else if (beacon_mode_active) {
|
||||
lv_label_set_text(gui_mode_label, "BEACON");
|
||||
lv_obj_set_style_text_color(gui_mode_label, lv_color_hex(GUI_COL_AMBER), 0);
|
||||
}
|
||||
#endif
|
||||
else if (radio_online) {
|
||||
lv_label_set_text(gui_mode_label, "RADIO");
|
||||
lv_obj_set_style_text_color(gui_mode_label, lv_color_hex(GUI_COL_MID), 0);
|
||||
} else {
|
||||
lv_label_set_text(gui_mode_label, "IDLE");
|
||||
lv_obj_set_style_text_color(gui_mode_label, lv_color_hex(GUI_COL_DIM), 0);
|
||||
}
|
||||
|
||||
// Battery
|
||||
if (battery_state == BATTERY_STATE_CHARGING) {
|
||||
lv_label_set_text_fmt(gui_batt_label, "%d%% +", (int)battery_percent);
|
||||
} else {
|
||||
lv_label_set_text_fmt(gui_batt_label, "%d%%", (int)battery_percent);
|
||||
}
|
||||
lv_obj_align(gui_batt_label, LV_ALIGN_TOP_RIGHT, -GUI_PAD, GUI_STATUS_Y);
|
||||
|
||||
// LoRa complication
|
||||
if (radio_online) {
|
||||
if (last_rssi > -292) {
|
||||
lv_label_set_text_fmt(gui_lora_value, "%d", last_rssi);
|
||||
} else {
|
||||
lv_label_set_text(gui_lora_value, "---");
|
||||
}
|
||||
lv_obj_set_style_text_color(gui_lora_value, lv_color_hex(GUI_COL_AMBER), 0);
|
||||
} else {
|
||||
lv_label_set_text(gui_lora_value, "OFF");
|
||||
lv_obj_set_style_text_color(gui_lora_value, lv_color_hex(GUI_COL_DIM), 0);
|
||||
}
|
||||
|
||||
// GPS complication
|
||||
#if HAS_GPS == true
|
||||
if (gps_sats > 0) {
|
||||
lv_label_set_text_fmt(gui_gps_value, "%d sats", gps_sats);
|
||||
lv_obj_set_style_text_color(gui_gps_value, lv_color_hex(GUI_COL_TEAL), 0);
|
||||
} else {
|
||||
lv_label_set_text(gui_gps_value, "no fix");
|
||||
lv_obj_set_style_text_color(gui_gps_value, lv_color_hex(GUI_COL_DIM), 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Steps placeholder
|
||||
lv_label_set_text(gui_steps_value, "--");
|
||||
|
||||
// ---- Radio status screen ----
|
||||
if (gui_radio_freq) {
|
||||
if (lora_freq > 0) {
|
||||
lv_label_set_text_fmt(gui_radio_freq, "%.3f MHz", (float)lora_freq / 1000000.0);
|
||||
} else {
|
||||
lv_label_set_text(gui_radio_freq, "--- MHz");
|
||||
}
|
||||
|
||||
lv_label_set_text_fmt(gui_radio_params, "SF%d BW%lu CR4/%d",
|
||||
lora_sf, lora_bw / 1000, lora_cr);
|
||||
|
||||
if (radio_online && last_rssi > -292) {
|
||||
lv_bar_set_value(gui_radio_rssi_bar, last_rssi, LV_ANIM_ON);
|
||||
lv_label_set_text_fmt(gui_radio_rssi_lbl, "%d dBm", last_rssi);
|
||||
} else {
|
||||
lv_bar_set_value(gui_radio_rssi_bar, -140, LV_ANIM_OFF);
|
||||
lv_label_set_text(gui_radio_rssi_lbl, "---");
|
||||
}
|
||||
|
||||
lv_label_set_text_fmt(gui_radio_util, "%.1f%%",
|
||||
(float)local_channel_util / 100.0);
|
||||
|
||||
if (bt_state == BT_STATE_CONNECTED) {
|
||||
lv_label_set_text(gui_radio_ble, "Connected");
|
||||
lv_obj_set_style_text_color(gui_radio_ble, lv_color_hex(GUI_COL_BLUE), 0);
|
||||
} else if (bt_state == BT_STATE_ON) {
|
||||
lv_label_set_text(gui_radio_ble, "Advertising");
|
||||
lv_obj_set_style_text_color(gui_radio_ble, lv_color_hex(GUI_COL_MID), 0);
|
||||
} else {
|
||||
lv_label_set_text(gui_radio_ble, "Off");
|
||||
lv_obj_set_style_text_color(gui_radio_ble, lv_color_hex(GUI_COL_DIM), 0);
|
||||
}
|
||||
|
||||
lv_label_set_text_fmt(gui_radio_pkts, "RX: %lu TX: %lu", stat_rx, stat_tx);
|
||||
}
|
||||
|
||||
// ---- GPS screen ----
|
||||
#if HAS_GPS == true
|
||||
if (gui_gps_coords) {
|
||||
if (gps_sats > 0 && gps_lat != 0.0) {
|
||||
lv_label_set_text_fmt(gui_gps_coords, "%.6f\n%.6f", gps_lat, gps_lon);
|
||||
lv_obj_set_style_text_color(gui_gps_coords, lv_color_hex(GUI_COL_TEAL), 0);
|
||||
} else {
|
||||
lv_label_set_text(gui_gps_coords, "No fix");
|
||||
lv_obj_set_style_text_color(gui_gps_coords, lv_color_hex(GUI_COL_DIM), 0);
|
||||
}
|
||||
|
||||
lv_label_set_text_fmt(gui_gps_fix, "Sats: %d HDOP: %.1f", gps_sats, gps_hdop);
|
||||
|
||||
lv_label_set_text_fmt(gui_gps_alt, "Alt: %.0fm Spd: %.1fkm/h", gps_alt, gps_speed);
|
||||
|
||||
if (beacon_mode_active) {
|
||||
lv_label_set_text(gui_gps_beacon, "Beacon: active");
|
||||
lv_obj_set_style_text_color(gui_gps_beacon, lv_color_hex(GUI_COL_AMBER), 0);
|
||||
} else {
|
||||
lv_label_set_text(gui_gps_beacon, "Beacon: off");
|
||||
lv_obj_set_style_text_color(gui_gps_beacon, lv_color_hex(GUI_COL_DIM), 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Initialize LVGL and create all screens
|
||||
// ---------------------------------------------------------------------------
|
||||
bool gui_init() {
|
||||
lv_init();
|
||||
|
||||
// --- Display driver ---
|
||||
gui_display = lv_display_create(GUI_W, GUI_H);
|
||||
if (!gui_display) return false;
|
||||
lv_display_set_flush_cb(gui_display, gui_flush_cb);
|
||||
|
||||
uint32_t buf_size = GUI_W * GUI_BUF_LINES * sizeof(uint16_t);
|
||||
gui_buf1 = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
gui_buf2 = (uint8_t *)heap_caps_malloc(buf_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (!gui_buf1 || !gui_buf2) {
|
||||
if (gui_buf1) free(gui_buf1);
|
||||
if (gui_buf2) free(gui_buf2);
|
||||
gui_buf2 = NULL;
|
||||
gui_buf1 = (uint8_t *)malloc(buf_size);
|
||||
if (!gui_buf1) return false;
|
||||
}
|
||||
lv_display_set_buffers(gui_display, gui_buf1, gui_buf2, buf_size,
|
||||
LV_DISPLAY_RENDER_MODE_PARTIAL);
|
||||
|
||||
// Swap buffer for byte-order conversion (same size as draw buffer)
|
||||
gui_swap_buf = (uint16_t *)heap_caps_malloc(GUI_W * GUI_BUF_LINES * sizeof(uint16_t),
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (!gui_swap_buf) gui_swap_buf = (uint16_t *)malloc(GUI_W * GUI_BUF_LINES * sizeof(uint16_t));
|
||||
|
||||
// Shadow framebuffer for JTAG screenshots (410*502*2 = 411,640 bytes)
|
||||
gui_screenshot_buf = (uint16_t *)heap_caps_malloc(GUI_W * GUI_H * sizeof(uint16_t),
|
||||
MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
if (gui_screenshot_buf) {
|
||||
memset(gui_screenshot_buf, 0, GUI_W * GUI_H * sizeof(uint16_t));
|
||||
Serial.printf("[gui] screenshot buf @ %p (%u bytes)\n",
|
||||
gui_screenshot_buf, GUI_W * GUI_H * 2);
|
||||
}
|
||||
|
||||
// --- Input driver ---
|
||||
gui_indev = lv_indev_create();
|
||||
if (gui_indev) {
|
||||
lv_indev_set_type(gui_indev, LV_INDEV_TYPE_POINTER);
|
||||
lv_indev_set_read_cb(gui_indev, gui_touch_read_cb);
|
||||
}
|
||||
|
||||
// --- Screen setup ---
|
||||
gui_screen = lv_screen_active();
|
||||
gui_style_black_container(gui_screen);
|
||||
|
||||
// --- Tileview: 3x3 grid, 5 populated tiles ---
|
||||
// (1,0) Radio
|
||||
// (0,1) GPS (1,1) Watch (2,1) Messages
|
||||
// (1,2) Settings
|
||||
gui_tileview = lv_tileview_create(gui_screen);
|
||||
lv_obj_set_style_bg_color(gui_tileview, lv_color_hex(GUI_COL_BLACK), 0);
|
||||
lv_obj_set_style_bg_opa(gui_tileview, LV_OPA_COVER, 0);
|
||||
lv_obj_set_style_border_width(gui_tileview, 0, 0);
|
||||
lv_obj_set_style_pad_all(gui_tileview, 0, 0);
|
||||
// Note: do NOT clear LV_OBJ_FLAG_SCROLLABLE — tileview needs it for swipe
|
||||
lv_obj_set_size(gui_tileview, GUI_W, GUI_H);
|
||||
|
||||
gui_tile_watch = lv_tileview_add_tile(gui_tileview, 1, 1, LV_DIR_ALL);
|
||||
gui_tile_radio = lv_tileview_add_tile(gui_tileview, 1, 0, LV_DIR_BOTTOM);
|
||||
gui_tile_gps = lv_tileview_add_tile(gui_tileview, 0, 1, LV_DIR_RIGHT);
|
||||
gui_tile_msg = lv_tileview_add_tile(gui_tileview, 2, 1, LV_DIR_LEFT);
|
||||
gui_tile_set = lv_tileview_add_tile(gui_tileview, 1, 2, LV_DIR_TOP);
|
||||
|
||||
// Start on watch face
|
||||
lv_tileview_set_tile(gui_tileview, gui_tile_watch, LV_ANIM_OFF);
|
||||
|
||||
// Haptic feedback on tile change
|
||||
lv_obj_add_event_cb(gui_tileview, gui_tile_change_cb, LV_EVENT_VALUE_CHANGED, NULL);
|
||||
|
||||
// --- Create screen content ---
|
||||
gui_create_watchface(gui_tile_watch);
|
||||
gui_create_radio_screen(gui_tile_radio);
|
||||
gui_create_gps_screen(gui_tile_gps);
|
||||
gui_create_msg_screen(gui_tile_msg);
|
||||
gui_create_settings_screen(gui_tile_set);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Screenshot: dump framebuffer as raw RGB565 to a file on SPIFFS,
|
||||
// or output dimensions to serial for external tools.
|
||||
// Call gui_screenshot() to write /screenshot.raw to SPIFFS (if mounted),
|
||||
// or read gui_screenshot_buf directly via debugger.
|
||||
// ---------------------------------------------------------------------------
|
||||
// Serial screenshot protocol:
|
||||
// Trigger: host sends 4 bytes [0x52, 0x57, 0x53, 0x53] ("RWSS" = R-Watch Screen Shot)
|
||||
// Response: "RWSS" + uint16_t(width) + uint16_t(height) + raw RGB565 LE pixels
|
||||
#define GUI_SS_MAGIC_0 0x52
|
||||
#define GUI_SS_MAGIC_1 0x57
|
||||
#define GUI_SS_MAGIC_2 0x53
|
||||
#define GUI_SS_MAGIC_3 0x53
|
||||
static uint8_t gui_ss_state = 0;
|
||||
|
||||
void gui_check_screenshot_trigger(uint8_t byte_in) {
|
||||
const uint8_t magic[] = {GUI_SS_MAGIC_0, GUI_SS_MAGIC_1, GUI_SS_MAGIC_2, GUI_SS_MAGIC_3};
|
||||
if (byte_in == magic[gui_ss_state]) {
|
||||
gui_ss_state++;
|
||||
if (gui_ss_state == 4) {
|
||||
gui_ss_state = 0;
|
||||
if (gui_screenshot_buf) {
|
||||
Serial.write(magic, 4);
|
||||
uint16_t w = GUI_W, h = GUI_H;
|
||||
Serial.write((uint8_t *)&w, 2);
|
||||
Serial.write((uint8_t *)&h, 2);
|
||||
Serial.write((uint8_t *)gui_screenshot_buf, GUI_W * GUI_H * 2);
|
||||
Serial.flush();
|
||||
} else {
|
||||
// Buffer not allocated — send error marker
|
||||
Serial.write(magic, 4);
|
||||
uint16_t w = 0, h = 0;
|
||||
Serial.write((uint8_t *)&w, 2);
|
||||
Serial.write((uint8_t *)&h, 2);
|
||||
Serial.flush();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gui_ss_state = (byte_in == magic[0]) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
void gui_screenshot_info() {
|
||||
if (gui_screenshot_buf) {
|
||||
Serial.printf("[screenshot] addr=%p size=%u w=%d h=%d\n",
|
||||
gui_screenshot_buf, GUI_W * GUI_H * 2, GUI_W, GUI_H);
|
||||
} else {
|
||||
Serial.println("[screenshot] buffer not allocated");
|
||||
}
|
||||
}
|
||||
|
||||
// Write screenshot to SD card as raw RGB565 + BMP header
|
||||
#if HAS_SD
|
||||
#include <SD.h>
|
||||
bool gui_screenshot_sd(const char *path = "/screenshot.bmp") {
|
||||
if (!gui_screenshot_buf) return false;
|
||||
|
||||
// Init SD on shared SPI bus
|
||||
SPI.begin(SD_CLK, SD_MISO, SD_MOSI, SD_CS);
|
||||
if (!SD.begin(SD_CS, SPI)) {
|
||||
Serial.println("[screenshot] SD init failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
File f = SD.open(path, FILE_WRITE);
|
||||
if (!f) {
|
||||
Serial.println("[screenshot] file open failed");
|
||||
SD.end();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write BMP header (RGB565 LE, top-down)
|
||||
uint32_t img_size = GUI_W * GUI_H * 2;
|
||||
uint32_t file_size = 14 + 40 + 12 + img_size; // file + info + masks + pixels
|
||||
uint32_t data_offset = 14 + 40 + 12;
|
||||
|
||||
// BMP file header (14 bytes)
|
||||
uint8_t bmp_hdr[14] = {'B', 'M'};
|
||||
memcpy(&bmp_hdr[2], &file_size, 4);
|
||||
memset(&bmp_hdr[6], 0, 4); // reserved
|
||||
memcpy(&bmp_hdr[10], &data_offset, 4);
|
||||
f.write(bmp_hdr, 14);
|
||||
|
||||
// DIB header (BITMAPINFOHEADER, 40 bytes)
|
||||
uint8_t dib[40] = {};
|
||||
uint32_t dib_size = 40;
|
||||
int32_t bmp_w = GUI_W;
|
||||
int32_t bmp_h = -GUI_H; // negative = top-down
|
||||
uint16_t planes = 1;
|
||||
uint16_t bpp = 16;
|
||||
uint32_t compression = 3; // BI_BITFIELDS
|
||||
memcpy(&dib[0], &dib_size, 4);
|
||||
memcpy(&dib[4], &bmp_w, 4);
|
||||
memcpy(&dib[8], &bmp_h, 4);
|
||||
memcpy(&dib[12], &planes, 2);
|
||||
memcpy(&dib[14], &bpp, 2);
|
||||
memcpy(&dib[16], &compression, 4);
|
||||
memcpy(&dib[20], &img_size, 4);
|
||||
f.write(dib, 40);
|
||||
|
||||
// RGB565 bitmasks (R, G, B)
|
||||
uint32_t mask_r = 0xF800, mask_g = 0x07E0, mask_b = 0x001F;
|
||||
f.write((uint8_t *)&mask_r, 4);
|
||||
f.write((uint8_t *)&mask_g, 4);
|
||||
f.write((uint8_t *)&mask_b, 4);
|
||||
|
||||
// Pixel data (RGB565 LE, row by row)
|
||||
// BMP rows must be 4-byte aligned; 410*2=820 bytes per row, 820%4=0, no padding needed
|
||||
f.write((uint8_t *)gui_screenshot_buf, img_size);
|
||||
|
||||
f.close();
|
||||
SD.end();
|
||||
|
||||
// Restart LoRa SPI after SD use (shared bus)
|
||||
SPI.end();
|
||||
|
||||
Serial.printf("[screenshot] saved %s (%u bytes)\n", path, file_size);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Main GUI update — called from update_display()
|
||||
// ---------------------------------------------------------------------------
|
||||
void gui_update() {
|
||||
static uint32_t last_tick = 0;
|
||||
uint32_t now = millis();
|
||||
lv_tick_inc(now - last_tick);
|
||||
last_tick = now;
|
||||
|
||||
gui_update_data();
|
||||
lv_timer_handler();
|
||||
}
|
||||
|
||||
#endif // BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
#endif // GUI_H
|
||||
17
Makefile
17
Makefile
|
|
@ -55,6 +55,7 @@ prep-esp32:
|
|||
arduino-cli lib install "XPowersLib"
|
||||
arduino-cli lib install "Crypto"
|
||||
arduino-cli lib install "TinyGPSPlus"
|
||||
arduino-cli lib install "lvgl@9.5.0"
|
||||
|
||||
prep-samd:
|
||||
arduino-cli core update-index --config-file arduino-cli.yaml
|
||||
|
|
@ -117,11 +118,16 @@ deploy-tbeam_supreme: firmware-tbeam_supreme free-port
|
|||
@sleep 3
|
||||
rnodeconf $(PORT) --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin)
|
||||
|
||||
# T-Watch Ultra: 16MB flash, 8MB app partition, LVGL GUI
|
||||
# FlashSize=16M is critical — the bootloader embeds flash size and defaults to 4MB.
|
||||
# Without it, flash mapping fails silently beyond 0x400000 (black screen, no crash).
|
||||
# partition_twatch.csv must be copied to the Arduino ESP32 tools/partitions/ directory.
|
||||
# When changing partition scheme, flash ALL THREE binaries (bootloader + partition + app).
|
||||
firmware-twatch_ultra:
|
||||
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc" -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x45"
|
||||
arduino-cli compile --log --fqbn "esp32:esp32:esp32s3:CDCOnBoot=cdc,FlashSize=16M,PSRAM=enabled" -e --build-property "build.partitions=partition_twatch" --build-property "upload.maximum_size=8388608" --build-property "compiler.cpp.extra_flags=-DBOARD_MODEL=0x45"
|
||||
|
||||
upload-twatch_ultra: firmware-twatch_ultra
|
||||
@echo "Flashing T-Watch Ultra app via JTAG (no BOOT+RST needed)..."
|
||||
@echo "Flashing T-Watch Ultra via JTAG..."
|
||||
$(HOME)/.arduino15/packages/esp32/tools/openocd-esp32/v0.12.0-esp32-20230921/bin/openocd \
|
||||
-s $(HOME)/.arduino15/packages/esp32/tools/openocd-esp32/v0.12.0-esp32-20230921/share/openocd/scripts \
|
||||
-f board/esp32s3-builtin.cfg \
|
||||
|
|
@ -129,6 +135,13 @@ upload-twatch_ultra: firmware-twatch_ultra
|
|||
@sleep 5
|
||||
rnodeconf /dev/ttyACM4 --firmware-hash $$(./partition_hashes ./build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin)
|
||||
|
||||
# Full flash including bootloader and partition table (needed after partition changes)
|
||||
flash-twatch_ultra-full: firmware-twatch_ultra
|
||||
esptool.py --chip esp32s3 --port /dev/ttyACM4 --baud 921600 write_flash \
|
||||
0x0000 build/esp32.esp32.esp32s3/RNode_Firmware.ino.bootloader.bin \
|
||||
0x8000 build/esp32.esp32.esp32s3/RNode_Firmware.ino.partitions.bin \
|
||||
0x10000 build/esp32.esp32.esp32s3/RNode_Firmware.ino.bin
|
||||
|
||||
firmware-lora32_v10: check_bt_buffers
|
||||
arduino-cli compile --log --fqbn esp32:esp32:ttgo-lora32 -e --build-property "build.partitions=no_ota" --build-property "upload.maximum_size=2097152" --build-property "compiler.cpp.extra_flags=\"-DBOARD_MODEL=0x39\""
|
||||
|
||||
|
|
|
|||
|
|
@ -280,6 +280,12 @@ void setup() {
|
|||
display_add_callback(work_while_waiting);
|
||||
#endif
|
||||
|
||||
// T-Watch init order: display_init() MUST run BEFORE xl9555_init().
|
||||
// The XL9555 GPIO expander controls the display power gate (EXPANDS_DISP_EN),
|
||||
// but its outputs default HIGH at power-on, so the display is powered before
|
||||
// the expander is explicitly configured. Moving display_init() after xl9555_init()
|
||||
// causes a black screen because the power gate cycling disrupts the CO5300 QSPI
|
||||
// init sequence. Do not reorder.
|
||||
display_unblank();
|
||||
disp_ready = display_init();
|
||||
update_display();
|
||||
|
|
@ -295,7 +301,7 @@ void setup() {
|
|||
xl9555_init();
|
||||
xl9555_enable_lora_antenna();
|
||||
xl9555_set(EXPANDS_DRV_EN, true); // Enable haptic motor driver
|
||||
xl9555_set(EXPANDS_DISP_EN, true); // Enable display power gate
|
||||
xl9555_set(EXPANDS_DISP_EN, true); // Confirm display power gate on
|
||||
xl9555_set(EXPANDS_TOUCH_RST, true); // Release touch reset
|
||||
delay(100);
|
||||
drv2605_init();
|
||||
|
|
@ -306,6 +312,14 @@ void setup() {
|
|||
if (touch.begin(Wire, 0x1A, I2C_SDA, I2C_SCL)) {
|
||||
touch_ready = true;
|
||||
attachInterrupt(TP_INT, touch_isr, FALLING);
|
||||
|
||||
// Register touch with LVGL GUI
|
||||
#if HAS_DISPLAY == true
|
||||
gui_set_touch_handler([](int16_t *x, int16_t *y) -> bool {
|
||||
if (!touch_ready) return false;
|
||||
return touch.getPoint(x, y, 1) > 0;
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
// Init speaker (BLDO2 already enabled by PMU init) and microphone
|
||||
|
|
@ -2021,19 +2035,33 @@ void loop() {
|
|||
input_read();
|
||||
#endif
|
||||
|
||||
// Touch panel event handling
|
||||
// Touch panel — IRQ-driven display wake (LVGL handles touch input via polling)
|
||||
#if BOARD_MODEL == BOARD_TWATCH_ULT
|
||||
if (touch_ready && touch_irq) {
|
||||
touch_irq = false;
|
||||
int16_t tx, ty;
|
||||
if (touch.getPoint(&tx, &ty, 1) > 0) {
|
||||
// Touch detected — unblank display and update activity
|
||||
#if HAS_DISPLAY
|
||||
display_unblank();
|
||||
#endif
|
||||
last_unblank_event = millis();
|
||||
#if HAS_DISPLAY
|
||||
if (display_blanked) display_unblank();
|
||||
#endif
|
||||
}
|
||||
|
||||
// Screenshot: long-press BOOT button (GPIO 0) for 2 seconds
|
||||
#if HAS_SD && HAS_DISPLAY
|
||||
{
|
||||
static uint32_t btn_down_since = 0;
|
||||
static bool btn_screenshot_taken = false;
|
||||
if (digitalRead(0) == LOW) {
|
||||
if (btn_down_since == 0) btn_down_since = millis();
|
||||
if (!btn_screenshot_taken && millis() - btn_down_since > 2000) {
|
||||
btn_screenshot_taken = true;
|
||||
if (drv2605_ready) drv2605_play(HAPTIC_DOUBLE_CLICK);
|
||||
gui_screenshot_sd();
|
||||
}
|
||||
} else {
|
||||
btn_down_since = 0;
|
||||
btn_screenshot_taken = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Deferred BHI260AP init — runs once after boot is complete
|
||||
|
|
@ -2304,7 +2332,11 @@ void buffer_serial() {
|
|||
#else
|
||||
while (c < MAX_CYCLES && Serial.available()) {
|
||||
c++;
|
||||
if (!fifo_isfull(&channelFIFO[CHANNEL_USB])) { fifo_push(&channelFIFO[CHANNEL_USB], Serial.read()); }
|
||||
uint8_t sb = Serial.read();
|
||||
#if BOARD_MODEL == BOARD_TWATCH_ULT && HAS_DISPLAY
|
||||
gui_check_screenshot_trigger(sb);
|
||||
#endif
|
||||
if (!fifo_isfull(&channelFIFO[CHANNEL_USB])) { fifo_push(&channelFIFO[CHANNEL_USB], sb); }
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
9
partition_twatch.csv
Normal file
9
partition_twatch.csv
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# T-Watch Ultimate partition table — 16MB flash, no OTA, large app
|
||||
# Requires FlashSize=16M in the Arduino FQBN or the bootloader can't map beyond 4MB.
|
||||
# Copy this file to ~/.arduino15/packages/esp32/hardware/esp32/<ver>/tools/partitions/
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
nvs, data, nvs, 0x9000, 0x5000,
|
||||
otadata, data, ota, 0xe000, 0x2000,
|
||||
app0, app, ota_0, 0x10000, 0x800000,
|
||||
spiffs, data, spiffs, 0x810000, 0x7E0000,
|
||||
coredump, data, coredump,0xFF0000, 0x10000,
|
||||
|
108
scripts/screenshot.py
Executable file
108
scripts/screenshot.py
Executable file
|
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
R-Watch Screenshot — capture display via USB serial
|
||||
|
||||
Sends trigger bytes, firmware dumps shadow framebuffer as raw RGB565.
|
||||
Handles KISS protocol data interleaved in the stream.
|
||||
|
||||
Usage:
|
||||
./scripts/screenshot.py # default port + output
|
||||
./scripts/screenshot.py -p /dev/ttyACM4 # specify port
|
||||
./scripts/screenshot.py -o /tmp/watch.png # specify output
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import struct
|
||||
import sys
|
||||
import time
|
||||
|
||||
WIDTH = 410
|
||||
HEIGHT = 502
|
||||
PIXEL_BYTES = WIDTH * HEIGHT * 2 # 411,640 bytes
|
||||
MAGIC = b"RWSS"
|
||||
HEADER_SIZE = 8 # RWSS + uint16 width + uint16 height
|
||||
|
||||
|
||||
def capture(port, output_path):
|
||||
try:
|
||||
import serial
|
||||
except ImportError:
|
||||
sys.exit("pip install pyserial")
|
||||
try:
|
||||
from PIL import Image
|
||||
except ImportError:
|
||||
sys.exit("pip install Pillow")
|
||||
|
||||
s = serial.Serial(port, 115200, timeout=1)
|
||||
|
||||
# Drain any pending data
|
||||
time.sleep(0.2)
|
||||
s.reset_input_buffer()
|
||||
|
||||
# Send trigger
|
||||
s.write(MAGIC)
|
||||
s.flush()
|
||||
|
||||
# Scan stream for the magic response header
|
||||
# The firmware may send KISS frames before/after the screenshot data
|
||||
buf = b""
|
||||
deadline = time.time() + 10
|
||||
magic_idx = -1
|
||||
|
||||
while time.time() < deadline:
|
||||
chunk = s.read(max(1, s.in_waiting))
|
||||
if not chunk:
|
||||
continue
|
||||
buf += chunk
|
||||
|
||||
magic_idx = buf.find(MAGIC)
|
||||
if magic_idx >= 0 and len(buf) >= magic_idx + HEADER_SIZE:
|
||||
break
|
||||
|
||||
if magic_idx < 0:
|
||||
s.close()
|
||||
sys.exit(f"Magic not found in {len(buf)} bytes of response")
|
||||
|
||||
# Parse header
|
||||
hdr = buf[magic_idx:magic_idx + HEADER_SIZE]
|
||||
w, h = struct.unpack("<HH", hdr[4:8])
|
||||
expected = w * h * 2
|
||||
|
||||
# Collect pixel data (may already have some in buf)
|
||||
data = buf[magic_idx + HEADER_SIZE:]
|
||||
while len(data) < expected and time.time() < deadline:
|
||||
chunk = s.read(min(expected - len(data), 32768))
|
||||
if chunk:
|
||||
data += chunk
|
||||
|
||||
s.close()
|
||||
|
||||
received = len(data)
|
||||
if received < expected:
|
||||
print(f"Warning: got {received}/{expected} bytes ({received*100//expected}%)")
|
||||
|
||||
# Convert RGB565 LE to PNG
|
||||
img = Image.new("RGB", (w, h))
|
||||
pixels = img.load()
|
||||
npx = min(w * h, received // 2)
|
||||
for i in range(npx):
|
||||
pixel = struct.unpack_from("<H", data, i * 2)[0]
|
||||
r = ((pixel >> 11) & 0x1F) * 255 // 31
|
||||
g = ((pixel >> 5) & 0x3F) * 255 // 63
|
||||
b = (pixel & 0x1F) * 255 // 31
|
||||
pixels[i % w, i // w] = (r, g, b)
|
||||
|
||||
img.save(output_path)
|
||||
print(f"Saved: {output_path} ({w}x{h}, {npx} pixels, {received} bytes)")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="R-Watch screenshot")
|
||||
parser.add_argument("-p", "--port", default="/dev/ttyACM4")
|
||||
parser.add_argument("-o", "--output", default="/tmp/watch_screenshot.png")
|
||||
args = parser.parse_args()
|
||||
capture(args.port, args.output)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue