feat(ui-lvgl): offline raster map from the microSD

Map screen (Home > Map): Web-Mercator tiles from /maps/{z}/{x}/{y}.png on
the card (Meshtastic MUI layout; per-column TPK1 .pak also read), drag to
pan, +/- zoom, re-centre/follow GPS, Nearby nodes with a position as
markers opening node detail. Tiles decode one per loop pass into a
12-slot RGB565 cache in PSRAM. map/TileProvider.h is the swap point for a
later vector renderer: the screen only calls renderTile(). The tile set's
credit line comes from maps/attribution.txt.

L2 mounts the card over SDMMC 1-bit (CLK 2, CMD 3, D0 1). LVGL's lodepng
is enabled; note its LVGL patch returns an lv_draw_buf_t*, not pixels.
LVGL pool 2 MB for PNG decoding. Fonts gain map / users icons.

tools/maps/fetch_tiles.py prepares a card for an area (presets for
Poland), requires an explicit tile URL (tile.openstreetmap.org blocks
offline use) and stops on placeholder images. The LVGL sim preloads
web/maps into the L2's /sdcard and puts its GPS on Kraków.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-09-25 10:57:46 +02:00
co-authored by Claude Opus 5.5
parent 3e5ccd28c7
commit 64321bf12b
18 changed files with 1021 additions and 133 deletions
+1
View File
@@ -23,6 +23,7 @@ tools/gpx/
sim_data/ sim_data/
variants/sim/tools/emsdk/ variants/sim/tools/emsdk/
variants/sim/web/build/ variants/sim/web/build/
variants/sim/web/maps/
# graphify # graphify
graphify-out/ graphify-out/
+1
View File
@@ -147,6 +147,7 @@ consumes keys only.
- Theme tokens (colours, spacing, typography) in one place — the first chance to realise the "Amber Trace" direction in colour. - Theme tokens (colours, spacing, typography) in one place — the first chance to realise the "Amber Trace" direction in colour.
- Fonts ✅ Noto Sans generated by `ui-lvgl/fonts/generate.sh` (lv_font_conv): European Latin (Latin-1, Extended-A/B), Greek, Cyrillic + supplement, typographic punctuation, currency, LVGL symbols; 12/14/16/20 px, clock 40 px digits only. On-screen keyboard (`ui-lvgl/Keyboard.h`), phone-style: one layout per script (Latin QWERTY / Cyrillic ЙЦУКЕН / Greek) chosen by the same `keyboard_main_alphabet` / `keyboard_alt_alphabet` prefs as `ui-new` (globe key switches), hold a key for its variants; compose limit counted in UTF-8 bytes. Long-press variants and UTF-8 case mapping are shared with `ui-new` in `ui-core/KeyboardData.h`. - Fonts ✅ Noto Sans generated by `ui-lvgl/fonts/generate.sh` (lv_font_conv): European Latin (Latin-1, Extended-A/B), Greek, Cyrillic + supplement, typographic punctuation, currency, LVGL symbols; 12/14/16/20 px, clock 40 px digits only. On-screen keyboard (`ui-lvgl/Keyboard.h`), phone-style: one layout per script (Latin QWERTY / Cyrillic ЙЦУКЕН / Greek) chosen by the same `keyboard_main_alphabet` / `keyboard_alt_alphabet` prefs as `ui-new` (globe key switches), hold a key for its variants; compose limit counted in UTF-8 bytes. Long-press variants and UTF-8 case mapping are shared with `ui-new` in `ui-core/KeyboardData.h`.
- Simulator ✅: `SIM_UI=lvgl variants/sim/build_wasm.sh` builds the same `ui-lvgl` code for the browser (`SimLcdDisplay` in `variants/sim/SimDisplayDriver.h` blits LVGL's RGB565 flush to a 320×240 canvas; the `SIM_PLATFORM` branch of `ui-lvgl/LvglPort.h` feeds the mouse in as touch). LVGL comes from the L2 env's PlatformIO libdeps; its objects are cached in `web/build/obj_lvgl/`. `web/lvgl.html` runs it next to a `ui-new` peer on a direct link (buttons: advert, peer DM, peer → Public, the board button) and exposes `window.sim` (`tap`, `hold`, `touch`, `peerDM`, `peerChannel`) for headless checks. - Simulator ✅: `SIM_UI=lvgl variants/sim/build_wasm.sh` builds the same `ui-lvgl` code for the browser (`SimLcdDisplay` in `variants/sim/SimDisplayDriver.h` blits LVGL's RGB565 flush to a 320×240 canvas; the `SIM_PLATFORM` branch of `ui-lvgl/LvglPort.h` feeds the mouse in as touch). LVGL comes from the L2 env's PlatformIO libdeps; its objects are cached in `web/build/obj_lvgl/`. `web/lvgl.html` runs it next to a `ui-new` peer on a direct link (buttons: advert, peer DM, peer → Public, the board button) and exposes `window.sim` (`tap`, `hold`, `touch`, `peerDM`, `peerChannel`) for headless checks.
- Map (in progress): offline Web-Mercator raster tiles on the microSD (`/maps/{z}/{x}/{y}.png`, the Meshtastic MUI layout; optional per-column `.pak` from PR #3381's packer), SDMMC 1-bit mount in `LvglPort.h`. `ui-lvgl/map/TileProvider.h` is the swap point: the screen and `map/TileCache.h` (12 decoded RGB565 tiles in PSRAM, LRU) only call `renderTile(z, x, y, rgb565)`, so a vector renderer can replace the PNG provider later. `MapScreen.h`: pan by drag, +/-, re-centre/follow GPS, Nearby nodes with a position as markers (tap = node detail), one tile decoded per loop pass. `maps/attribution.txt` is shown on the map. Planned split (user, 2026-09-25): a Nodes map (every contact with a position) and a separate Navigation map (waypoints + live shares only, for walking to a target). Tiles come from `tools/maps/fetch_tiles.py` (no default server: tile.openstreetmap.org blocks offline use) or, next, an on-device WiFi download of the visible area.
- PR #3381's LVGL UI is a reference for platform glue (flush, GT911, PSRAM pool) only; its screens are not adopted. - PR #3381's LVGL UI is a reference for platform glue (flush, GT911, PSRAM pool) only; its screens are not adopted.
## Migration plan ## Migration plan
+21 -2
View File
@@ -1,9 +1,14 @@
#pragma once #pragma once
// LVGL <-> board glue: display flush and pointer input. One implementation per // LVGL <-> board glue: display flush, pointer input and the storage card (map
// display stack; the rest of ui-lvgl never touches the hardware directly. // tiles, mounted at /sdcard). One implementation per board; the rest of
// ui-lvgl never touches the hardware directly.
// //
// Single-TU fragment: included by ui-lvgl/UITask.cpp only. // Single-TU fragment: included by ui-lvgl/UITask.cpp only.
#if defined(SEEED_WIO_TRACKER_L2)
#include <SD_MMC.h>
#endif
namespace lvport { namespace lvport {
#if defined(SEEED_WIO_TRACKER_L2) #if defined(SEEED_WIO_TRACKER_L2)
@@ -69,6 +74,17 @@ static bool touched() {
static void swallowTouch() { s_swallow = true; } static void swallowTouch() { s_swallow = true; }
// microSD over SDMMC, 1-bit (CLK 2, CMD 3, D0 1); its power rail (expander
// P14) is switched on in WioTrackerL2Board::begin(). Retried on every call
// until it works, so a card inserted later is picked up the next time.
static bool mountStorage() {
static bool mounted = false;
if (mounted) return true;
SD_MMC.setPins(2, 3, 1);
mounted = SD_MMC.begin("/sdcard", true /* 1-bit */);
return mounted;
}
#elif defined(SIM_PLATFORM) && defined(__EMSCRIPTEN__) #elif defined(SIM_PLATFORM) && defined(__EMSCRIPTEN__)
// Browser simulator (variants/sim/build_wasm_lvgl.sh): SimLcdDisplay blits to // Browser simulator (variants/sim/build_wasm_lvgl.sh): SimLcdDisplay blits to
// a <canvas>, the host page feeds the mouse in as touch. // a <canvas>, the host page feeds the mouse in as touch.
@@ -109,6 +125,9 @@ static bool begin() {
static bool touched() { return SimLcdDisplay::touchState().down; } static bool touched() { return SimLcdDisplay::touchState().down; }
static void swallowTouch() { s_swallow = true; } static void swallowTouch() { s_swallow = true; }
// The host page preloads map tiles into the in-memory FS under /sdcard/maps.
static bool mountStorage() { return true; }
#else #else
#error "ui-lvgl: no LVGL port for this board (see LvglPort.h)" #error "ui-lvgl: no LVGL port for this board (see LvglPort.h)"
#endif #endif
@@ -0,0 +1,332 @@
#pragma once
// Map screen: offline Web-Mercator tiles from the card (map/TileProvider.h,
// cached by map/TileCache.h) under own position and the nodes Nearby knows a
// position for. Drag to pan, +/- to zoom, the crosshair re-centres and
// follows the GPS again; tap a marker for its node detail.
//
// Tiles decode one per loop pass (a PNG takes tens of ms), so panning stays
// responsive and the radio keeps being serviced while a view fills in.
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp after the Nearby section.
#include <math.h>
#include "map/TileProvider.h"
#include "map/TileCache.h"
namespace mapview {
static RasterTileProvider s_raster("/sdcard/maps");
static TileProvider* s_provider = &s_raster; // the one place to swap in a vector renderer
static TileCache s_cache;
static const int MIN_Z = 3, MAX_Z = 18;
static const int GRID_COLS = 3, GRID_ROWS = 2; // covers 320x218 at any offset
// Fallback centre with no GPS fix and no saved position: Poland.
static const double DEFAULT_LAT = 52.0, DEFAULT_LON = 19.4;
static const int DEFAULT_Z = 6;
static double lonToTileX(double lon, int z) { return (lon + 180.0) / 360.0 * (double)(1 << z); }
static double latToTileY(double lat, int z) {
double r = lat * M_PI / 180.0;
return (1.0 - asinh(tan(r)) / M_PI) / 2.0 * (double)(1 << z);
}
// Markers: one per positioned Nearby row, remembered so layout can reposition them.
struct Mark { int32_t lat_e6, lon_e6; int row; lv_obj_t* obj; };
static Mark s_marks[NearbyModel::MAX_NEARBY];
static int s_mark_count = 0;
static int s_drag = 0; // px moved in the current press: a drag isn't a tap
static bool s_available = false; // provider has data; checked when the map opens, not per frame
} // namespace mapview
static void onMapPress(lv_event_t* e) {
lv_event_code_t code = lv_event_get_code(e);
if (code == LV_EVENT_PRESSED) { mapview::s_drag = 0; return; }
lv_point_t v;
lv_indev_get_vect(lv_indev_active(), &v);
if (v.x == 0 && v.y == 0) return;
mapview::s_drag += abs(v.x) + abs(v.y);
s_ui->mapPan(v.x, v.y);
}
static void onMapZoomIn(lv_event_t* e) { (void)e; s_ui->mapZoom(+1); }
static void onMapZoomOut(lv_event_t* e) { (void)e; s_ui->mapZoom(-1); }
static void onMapCenter(lv_event_t* e) { (void)e; s_ui->mapCenterOnMe(); }
static void onMapMarker(lv_event_t* e) {
if (mapview::s_drag > 8) return; // the press was a pan that ended on a marker
s_ui->mapOpenMarker((int)(uintptr_t)lv_event_get_user_data(e));
}
static lv_obj_t* mapButton(lv_obj_t* parent, const char* text, lv_event_cb_t cb) {
lv_obj_t* b = lv_button_create(parent);
lv_obj_set_size(b, 40, 40);
lv_obj_set_style_radius(b, theme::RADIUS, 0);
lv_obj_set_style_shadow_width(b, 0, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::BG), 0);
lv_obj_set_style_bg_opa(b, LV_OPA_80, 0);
lv_obj_set_style_bg_color(b, lv_color_hex(theme::SURFACE_2), LV_STATE_PRESSED);
lv_obj_set_style_border_color(b, lv_color_hex(theme::SURFACE_2), 0);
lv_obj_set_style_border_width(b, 1, 0);
lv_obj_add_event_cb(b, cb, LV_EVENT_CLICKED, NULL);
lv_obj_center(label(b, text, THEME_FONT_TITLE, theme::TEXT));
return b;
}
static lv_obj_t* mapPill(lv_obj_t* parent, const char* text) {
lv_obj_t* l = label(parent, text, THEME_FONT_SMALL, theme::TEXT);
lv_obj_set_style_bg_color(l, lv_color_hex(theme::BG), 0);
lv_obj_set_style_bg_opa(l, LV_OPA_70, 0);
lv_obj_set_style_pad_hor(l, 5, 0);
lv_obj_set_style_pad_ver(l, 1, 0);
lv_obj_set_style_radius(l, 4, 0);
return l;
}
void UITask::showMap() {
_screen = SCR_MAP;
mapview::s_available = lvport::mountStorage() && mapview::s_provider->available();
if (_map_z == 0) { // first open: own position, else the node's own advert position, else Poland
int32_t lat, lon;
double la = mapview::DEFAULT_LAT, lo = mapview::DEFAULT_LON;
int z = mapview::DEFAULT_Z;
if (_core->course.currentLocation(lat, lon)) { la = lat / 1e6; lo = lon / 1e6; z = 15; }
else if (_sensors && (_sensors->node_lat != 0 || _sensors->node_lon != 0)) { // saved / app-set position
la = _sensors->node_lat; lo = _sensors->node_lon; z = 13;
}
_map_z = z;
_map_cx = mapview::lonToTileX(lo, z);
_map_cy = mapview::latToTileY(la, z);
}
buildMap();
}
void UITask::buildMap() {
lv_obj_t* body = newScreen(NULL, false);
lv_obj_set_layout(body, LV_LAYOUT_NONE);
lv_obj_set_style_pad_all(body, 0, 0);
lv_obj_remove_flag(body, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_style_bg_color(body, lv_color_hex(0x1A1A1E), 0); // unloaded / missing tiles
lv_obj_add_flag(body, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_event_cb(body, onMapPress, LV_EVENT_PRESSED, NULL);
lv_obj_add_event_cb(body, onMapPress, LV_EVENT_PRESSING, NULL);
_map_area = body;
for (int i = 0; i < mapview::GRID_COLS * mapview::GRID_ROWS; i++) {
_map_tiles[i] = lv_image_create(body);
lv_obj_add_flag(_map_tiles[i], LV_OBJ_FLAG_HIDDEN);
}
// Marker layer: same size as the map, lets presses through to it.
_map_marks = lv_obj_create(body);
lv_obj_remove_style_all(_map_marks);
lv_obj_set_size(_map_marks, LV_PCT(100), LV_PCT(100));
lv_obj_remove_flag(_map_marks, LV_OBJ_FLAG_CLICKABLE);
lv_obj_remove_flag(_map_marks, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(_map_marks, LV_OBJ_FLAG_EVENT_BUBBLE);
_map_me = lv_obj_create(_map_marks);
lv_obj_remove_style_all(_map_me);
lv_obj_set_size(_map_me, 16, 16);
lv_obj_set_style_radius(_map_me, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_color(_map_me, lv_color_hex(theme::ACCENT), 0);
lv_obj_set_style_bg_opa(_map_me, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(_map_me, lv_color_hex(theme::BG), 0);
lv_obj_set_style_border_width(_map_me, 3, 0);
lv_obj_remove_flag(_map_me, LV_OBJ_FLAG_CLICKABLE);
lv_obj_add_flag(_map_me, LV_OBJ_FLAG_HIDDEN);
// Controls
lv_obj_t* back = mapButton(body, LV_SYMBOL_LEFT, onBack);
lv_obj_align(back, LV_ALIGN_TOP_LEFT, 6, 6);
lv_obj_align(mapButton(body, LV_SYMBOL_PLUS, onMapZoomIn), LV_ALIGN_TOP_RIGHT, -6, 6);
lv_obj_align(mapButton(body, LV_SYMBOL_MINUS, onMapZoomOut), LV_ALIGN_TOP_RIGHT, -6, 52);
lv_obj_align(mapButton(body, LV_SYMBOL_GPS, onMapCenter), LV_ALIGN_BOTTOM_RIGHT, -6, -6);
_map_zoom_lbl = mapPill(body, "");
lv_obj_align(_map_zoom_lbl, LV_ALIGN_TOP_LEFT, 52, 16);
lv_obj_t* attr = mapPill(body, mapview::s_provider->attribution()); // required by the data licences
lv_label_set_long_mode(attr, LV_LABEL_LONG_WRAP);
lv_obj_set_style_max_width(attr, 250, 0);
lv_obj_set_width(attr, LV_SIZE_CONTENT);
lv_obj_align(attr, LV_ALIGN_BOTTOM_LEFT, 6, -6);
_map_hint = mapPill(body, "");
lv_label_set_long_mode(_map_hint, LV_LABEL_LONG_WRAP);
lv_obj_set_width(_map_hint, 200);
lv_obj_set_style_text_align(_map_hint, LV_TEXT_ALIGN_CENTER, 0);
lv_obj_center(_map_hint);
lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN);
lv_obj_update_layout(body);
mapview::s_mark_count = 0; // the previous markers went with the previous screen
rebuildMapMarkers();
layoutMap();
}
// Places tiles and markers for the current centre / zoom; queues tiles that
// aren't decoded yet for mapLoop().
void UITask::layoutMap() {
if (!_map_area) return;
int w = lv_obj_get_width(_map_area), h = lv_obj_get_height(_map_area);
double left = _map_cx * mapview::TILE_PX - w / 2.0; // world px of the view's top-left
double top = _map_cy * mapview::TILE_PX - h / 2.0;
int tx0 = (int)floor(left / mapview::TILE_PX), ty0 = (int)floor(top / mapview::TILE_PX);
int n = 1 << _map_z;
bool have_provider = mapview::s_available;
int shown = 0, missing = 0;
_map_pending = false;
for (int j = 0; j < mapview::GRID_ROWS; j++) {
for (int i = 0; i < mapview::GRID_COLS; i++) {
lv_obj_t* img = _map_tiles[j * mapview::GRID_COLS + i];
int tx = tx0 + i, ty = ty0 + j;
int px = (int)lround(tx * (double)mapview::TILE_PX - left);
int py = (int)lround(ty * (double)mapview::TILE_PX - top);
bool on_screen = px < w && py < h && px + mapview::TILE_PX > 0 && py + mapview::TILE_PX > 0;
mapview::TileCache::Slot* s = nullptr;
if (have_provider && on_screen && ty >= 0 && ty < n) {
s = mapview::s_cache.find(_map_z, ((tx % n) + n) % n, ty);
if (!s) _map_pending = true;
}
if (s && s->present) {
if (lv_image_get_src(img) != &s->dsc) lv_image_set_src(img, &s->dsc);
lv_obj_set_pos(img, px, py);
lv_obj_remove_flag(img, LV_OBJ_FLAG_HIDDEN);
shown++;
} else {
lv_obj_add_flag(img, LV_OBJ_FLAG_HIDDEN);
if (s) missing++;
}
}
}
// Own position
int32_t lat, lon;
if (_core->course.currentLocation(lat, lon)) {
double x = mapview::lonToTileX(lon / 1e6, _map_z) * mapview::TILE_PX - left;
double y = mapview::latToTileY(lat / 1e6, _map_z) * mapview::TILE_PX - top;
lv_obj_set_pos(_map_me, (int)x - 8, (int)y - 8);
lv_obj_remove_flag(_map_me, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_add_flag(_map_me, LV_OBJ_FLAG_HIDDEN);
}
for (int k = 0; k < mapview::s_mark_count; k++) {
const mapview::Mark& m = mapview::s_marks[k];
double x = mapview::lonToTileX(m.lon_e6 / 1e6, _map_z) * mapview::TILE_PX - left;
double y = mapview::latToTileY(m.lat_e6 / 1e6, _map_z) * mapview::TILE_PX - top;
lv_obj_set_pos(m.obj, (int)x - 6, (int)y - 6); // the dot's centre on the spot; name to its right
}
lv_label_set_text_fmt(_map_zoom_lbl, "z%d", _map_z);
const char* hint = !have_provider ? "No map on the SD card.\nPut tiles in /maps (tools/maps)."
: (!_map_pending && shown == 0 && missing > 0) ? "No tiles here at this zoom." : nullptr;
if (hint) { lv_label_set_text(_map_hint, hint); lv_obj_remove_flag(_map_hint, LV_OBJ_FLAG_HIDDEN); }
else lv_obj_add_flag(_map_hint, LV_OBJ_FLAG_HIDDEN);
}
// One tile decode per call, nearest to the centre first.
void UITask::mapLoop() {
if ((int32_t)(millis() - _next_map_marks_ms) >= 0) {
_next_map_marks_ms = millis() + 3000;
if (_map_follow) {
int32_t lat, lon;
if (_core->course.currentLocation(lat, lon)) {
_map_cx = mapview::lonToTileX(lon / 1e6, _map_z);
_map_cy = mapview::latToTileY(lat / 1e6, _map_z);
}
}
rebuildMapMarkers();
layoutMap();
}
if (!_map_pending || !_map_area) return;
int w = lv_obj_get_width(_map_area), h = lv_obj_get_height(_map_area);
double left = _map_cx * mapview::TILE_PX - w / 2.0, top = _map_cy * mapview::TILE_PX - h / 2.0;
int tx0 = (int)floor(left / mapview::TILE_PX), ty0 = (int)floor(top / mapview::TILE_PX);
int n = 1 << _map_z;
int best_x = 0, best_y = 0;
double best_d = 1e18;
for (int j = 0; j < mapview::GRID_ROWS; j++) {
for (int i = 0; i < mapview::GRID_COLS; i++) {
int tx = tx0 + i, ty = ty0 + j;
double px = tx * (double)mapview::TILE_PX - left, py = ty * (double)mapview::TILE_PX - top;
if (px >= w || py >= h || px + mapview::TILE_PX <= 0 || py + mapview::TILE_PX <= 0) continue;
if (ty < 0 || ty >= n) continue;
int wx = ((tx % n) + n) % n;
if (mapview::s_cache.find(_map_z, wx, ty)) continue;
double d = fabs(px + 128 - w / 2.0) + fabs(py + 128 - h / 2.0);
if (d < best_d) { best_d = d; best_x = wx; best_y = ty; }
}
}
if (best_d < 1e18) mapview::s_cache.load(*mapview::s_provider, _map_z, best_x, best_y);
layoutMap();
}
void UITask::rebuildMapMarkers() {
if (!_map_marks) return;
for (int k = 0; k < mapview::s_mark_count; k++) lv_obj_delete(mapview::s_marks[k].obj);
mapview::s_mark_count = 0;
_nearby->refreshStored();
for (int i = 0; i < _nearby->count() && mapview::s_mark_count < NearbyModel::MAX_NEARBY; i++) {
const NearbyModel::Entry& e = _nearby->at(i);
if (e.lat_e6 == 0 && e.lon_e6 == 0) continue;
uint32_t col = e.is_live ? theme::OK : e.type == ADV_TYPE_CHAT ? theme::TEXT : theme::TEXT_MUTED;
// One clickable object per marker: a dot plus the name beside it.
lv_obj_t* m = lv_obj_create(_map_marks);
lv_obj_remove_style_all(m);
lv_obj_set_size(m, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
lv_obj_set_flex_flow(m, LV_FLEX_FLOW_ROW);
lv_obj_set_flex_align(m, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER);
lv_obj_set_style_pad_column(m, 3, 0);
lv_obj_add_flag(m, LV_OBJ_FLAG_EVENT_BUBBLE); // a drag starting here still pans
lv_obj_add_event_cb(m, onMapMarker, LV_EVENT_CLICKED, (void*)(uintptr_t)mapview::s_mark_count);
lv_obj_t* dot = lv_obj_create(m);
lv_obj_remove_style_all(dot);
lv_obj_set_size(dot, 12, 12);
lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0);
lv_obj_set_style_bg_color(dot, lv_color_hex(col), 0);
lv_obj_set_style_bg_opa(dot, LV_OPA_COVER, 0);
lv_obj_set_style_border_color(dot, lv_color_hex(theme::BG), 0);
lv_obj_set_style_border_width(dot, 2, 0);
lv_obj_remove_flag(dot, LV_OBJ_FLAG_CLICKABLE);
lv_obj_t* name = mapPill(m, e.name[0] ? e.name : "?");
lv_obj_remove_flag(name, LV_OBJ_FLAG_CLICKABLE);
mapview::Mark& mk = mapview::s_marks[mapview::s_mark_count++];
mk.lat_e6 = e.lat_e6; mk.lon_e6 = e.lon_e6; mk.row = i; mk.obj = m;
}
}
void UITask::mapPan(int dx, int dy) {
_map_cx -= dx / (double)mapview::TILE_PX;
_map_cy -= dy / (double)mapview::TILE_PX;
double n = (double)(1 << _map_z);
if (_map_cy < 0) _map_cy = 0;
if (_map_cy > n) _map_cy = n;
_map_follow = false;
layoutMap();
}
void UITask::mapZoom(int delta) {
int z = _map_z + delta;
if (z < mapview::MIN_Z || z > mapview::MAX_Z) return;
double f = delta > 0 ? 2.0 : 0.5;
_map_cx *= f; _map_cy *= f;
_map_z = z;
layoutMap();
}
void UITask::mapCenterOnMe() {
int32_t lat, lon;
if (!_core->course.currentLocation(lat, lon)) { showToast("No GPS fix"); return; }
_map_follow = true;
_map_cx = mapview::lonToTileX(lon / 1e6, _map_z);
_map_cy = mapview::latToTileY(lat / 1e6, _map_z);
layoutMap();
}
void UITask::mapOpenMarker(int idx) {
if (idx < 0 || idx >= mapview::s_mark_count) return;
openNode(mapview::s_marks[idx].row);
_node_from_map = true;
}
+2
View File
@@ -31,4 +31,6 @@ namespace theme {
// Extra FontAwesome glyphs baked into ui_font_12..20 beyond LVGL's LV_SYMBOL_*. // Extra FontAwesome glyphs baked into ui_font_12..20 beyond LVGL's LV_SYMBOL_*.
#define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite #define UI_SYMBOL_STAR "\xEF\x80\x85" // U+F005, favourite
#define UI_SYMBOL_MAP "\xEF\x89\xB9" // U+F279
#define UI_SYMBOL_USERS "\xEF\x83\x80" // U+F0C0, Nearby
} }
+13 -2
View File
@@ -158,6 +158,7 @@ void UITask::loop() {
refreshNode(); refreshNode();
} }
} }
if (_screen == SCR_MAP) mapLoop();
if (_screen == SCR_THREAD && (int32_t)(millis() - _next_thread_check_ms) >= 0) { if (_screen == SCR_THREAD && (int32_t)(millis() - _next_thread_check_ms) >= 0) {
_next_thread_check_ms = millis() + 500; _next_thread_check_ms = millis() + 500;
uint32_t sig = threadSignature(); uint32_t sig = threadSignature();
@@ -343,6 +344,8 @@ lv_obj_t* UITask::newScreen(const char* title, bool with_back) {
_nearby_list = _nearby_status = _nearby_sort_lbl = _nearby_chips = nullptr; _nearby_list = _nearby_status = _nearby_sort_lbl = _nearby_chips = nullptr;
_node_info = _node_ping = _node_delete_lbl = nullptr; _node_info = _node_ping = _node_delete_lbl = nullptr;
_scan_overlay = _scan_list = _scan_status = nullptr; // the popup went with the old screen _scan_overlay = _scan_list = _scan_status = nullptr; // the popup went with the old screen
_map_area = _map_marks = _map_me = _map_zoom_lbl = _map_hint = nullptr;
for (lv_obj_t*& t : _map_tiles) t = nullptr;
lv_obj_t* prev = lv_screen_active(); lv_obj_t* prev = lv_screen_active();
lv_obj_t* scr = lv_obj_create(NULL); lv_obj_t* scr = lv_obj_create(NULL);
styleSurface(scr, theme::BG); styleSurface(scr, theme::BG);
@@ -395,11 +398,13 @@ void UITask::back() {
if (_scan_overlay) closeScanPopup(); if (_scan_overlay) closeScanPopup();
else showHome(); else showHome();
break; break;
case SCR_NODE: // back to where the node was picked: the list, or the scan popup over it case SCR_NODE: // back to where the node was picked: map, the list, or the scan popup over it
if (_node_from_map) { showMap(); break; }
_screen = SCR_NEARBY; _screen = SCR_NEARBY;
buildNearby(); buildNearby();
if (_node_from_scan) showScanPopup(); if (_node_from_scan) showScanPopup();
break; break;
case SCR_MAP: showHome(); break;
default: break; default: break;
} }
} }
@@ -410,6 +415,7 @@ static void onOpenChats(lv_event_t* e) { (void)e; s_ui->showChats(); }
static void onOpenSettings(lv_event_t* e) { (void)e; s_ui->showSettings(); } static void onOpenSettings(lv_event_t* e) { (void)e; s_ui->showSettings(); }
static void onOpenNearby(lv_event_t* e) { (void)e; s_ui->showNearby(); } static void onOpenNearby(lv_event_t* e) { (void)e; s_ui->showNearby(); }
static void onOpenMap(lv_event_t* e) { (void)e; s_ui->showMap(); }
// Home tile: icon over label, one of a row of three; optional amber value // Home tile: icon over label, one of a row of three; optional amber value
// (unread count) in its top-right corner. // (unread count) in its top-right corner.
@@ -455,7 +461,8 @@ void UITask::buildHome() {
lv_obj_set_flex_flow(tiles, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(tiles, LV_FLEX_FLOW_ROW);
lv_obj_set_style_pad_column(tiles, theme::GAP, 0); lv_obj_set_style_pad_column(tiles, theme::GAP, 0);
homeTile(tiles, LV_SYMBOL_ENVELOPE, "Messages", onOpenChats, &_home_unread); homeTile(tiles, LV_SYMBOL_ENVELOPE, "Messages", onOpenChats, &_home_unread);
homeTile(tiles, LV_SYMBOL_GPS, "Nearby", onOpenNearby, NULL); homeTile(tiles, UI_SYMBOL_USERS, "Nearby", onOpenNearby, NULL);
homeTile(tiles, UI_SYMBOL_MAP, "Map", onOpenMap, NULL);
homeTile(tiles, LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, NULL); homeTile(tiles, LV_SYMBOL_SETTINGS, "Settings", onOpenSettings, NULL);
} }
@@ -804,6 +811,7 @@ void UITask::openScanNode(int row) {
if (row < 0 || row >= _scan->count()) return; if (row < 0 || row >= _scan->count()) return;
s_node = _scan->at(row); s_node = _scan->at(row);
_node_from_scan = true; _node_from_scan = true;
_node_from_map = false;
_scanning = false; _scanning = false;
_pinging = false; _pinging = false;
_delete_armed_ms = 0; _delete_armed_ms = 0;
@@ -876,6 +884,7 @@ void UITask::openNode(int row) {
if (row < 0 || row >= _nearby->count()) return; if (row < 0 || row >= _nearby->count()) return;
s_node = _nearby->at(row); s_node = _nearby->at(row);
_node_from_scan = false; _node_from_scan = false;
_node_from_map = false;
_pinging = false; _pinging = false;
_delete_armed_ms = 0; _delete_armed_ms = 0;
_screen = SCR_NODE; _screen = SCR_NODE;
@@ -1044,6 +1053,8 @@ void UITask::nodeAction(uint8_t action) {
} }
} }
#include "MapScreen.h"
// ── Conversation ────────────────────────────────────────────────────────────── // ── Conversation ──────────────────────────────────────────────────────────────
static void onKeyboard(lv_event_t* e) { static void onKeyboard(lv_event_t* e) {
+24 -1
View File
@@ -39,6 +39,11 @@ public:
void showContacts(); void showContacts();
void showSettings(); void showSettings();
void showNearby(); void showNearby();
void showMap();
void mapZoom(int delta);
void mapCenterOnMe();
void mapPan(int dx, int dy);
void mapOpenMarker(int idx);
void setNearbyFilter(uint8_t f); void setNearbyFilter(uint8_t f);
void toggleNearbySort(); void toggleNearbySort();
void startNearbyScan(); void startNearbyScan();
@@ -59,7 +64,7 @@ public:
void openAdminFor(const ContactInfo& ci, bool from_picker) { (void)ci; (void)from_picker; } void openAdminFor(const ContactInfo& ci, bool from_picker) { (void)ci; (void)from_picker; }
private: private:
enum Screen : uint8_t { SCR_HOME, SCR_CHATS, SCR_CONTACTS, SCR_THREAD, SCR_SETTINGS, SCR_NEARBY, SCR_NODE }; enum Screen : uint8_t { SCR_HOME, SCR_CHATS, SCR_CONTACTS, SCR_THREAD, SCR_SETTINGS, SCR_NEARBY, SCR_NODE, SCR_MAP };
void buildStatusBar(); void buildStatusBar();
void refreshStatusBar(); void refreshStatusBar();
@@ -74,6 +79,10 @@ private:
uint32_t nearbySignature() const; uint32_t nearbySignature() const;
void showScanPopup(); void showScanPopup();
void refreshScanPopup(); void refreshScanPopup();
void buildMap();
void layoutMap();
void mapLoop();
void rebuildMapMarkers();
void buildNode(); void buildNode();
void refreshNode(); void refreshNode();
void buildThread(); void buildThread();
@@ -138,4 +147,18 @@ private:
lv_obj_t* _node_info = nullptr; lv_obj_t* _node_info = nullptr;
lv_obj_t* _node_ping = nullptr; lv_obj_t* _node_ping = nullptr;
lv_obj_t* _node_delete_lbl = nullptr; lv_obj_t* _node_delete_lbl = nullptr;
bool _node_from_map = false;
// Map (SCR_MAP): view centre in fractional tile coords at zoom _map_z
double _map_cx = 0, _map_cy = 0;
int _map_z = 0; // 0 = not initialised yet
bool _map_follow = true; // keep centred on own position until panned
bool _map_pending = false; // visible tiles still to decode
uint32_t _next_map_marks_ms = 0;
lv_obj_t* _map_area = nullptr;
lv_obj_t* _map_tiles[6] = {nullptr};
lv_obj_t* _map_marks = nullptr;
lv_obj_t* _map_me = nullptr;
lv_obj_t* _map_zoom_lbl = nullptr;
lv_obj_t* _map_hint = nullptr;
}; };
@@ -26,8 +26,9 @@ FA_URL=https://raw.githubusercontent.com/lvgl/lvgl/v9.2.2/scripts/built_in_font/
TEXT="0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3" TEXT="0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3"
TEXT="$TEXT,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122" TEXT="$TEXT,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122"
# LVGL built-in symbols (same list as lvgl/scripts/built_in_font/built_in_font_gen.py) # LVGL built-in symbols (same list as lvgl/scripts/built_in_font/built_in_font_gen.py)
# plus 61612 = globe (keyboard script key), 61445 = star (favourite) # plus 61612 = globe (keyboard script key), 61445 = star (favourite),
SYMS="61612,61445,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650" # 62073 = map, 61632 = users (home tiles)
SYMS="61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650"
CONV="npx -y lv_font_conv@1.5.3" CONV="npx -y lv_font_conv@1.5.3"
gen() { # size weight ranges name gen() { # size weight ranges name
@@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 12 px * Size: 12 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 12 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_12 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_12.c * Opts: --bpp 4 --size 12 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_12 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_12.c
******************************************************************************/ ******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE #ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -6699,6 +6699,18 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xe, 0x0, 0x44, 0x3a, 0xc0, 0x8, 0xf8, 0x60, 0xe, 0x0, 0x44, 0x3a, 0xc0, 0x8, 0xf8, 0x60,
0x13, 0xdb, 0x56, 0x16, 0x18, 0x0, 0x13, 0xdb, 0x56, 0x16, 0x18, 0x0,
/* U+F0C0 "" */
0x0, 0xe1, 0x9c, 0x91, 0x0, 0xf1, 0x20, 0x2,
0x98, 0xda, 0x80, 0x8, 0x40, 0x3b, 0x70, 0x8,
0x1, 0x20, 0xc5, 0xe8, 0x98, 0x0, 0x80, 0x3c,
0x24, 0x0, 0x32, 0xa6, 0xb0, 0x90, 0xa, 0x42,
0xda, 0x88, 0x98, 0xc, 0xd, 0xb5, 0xac, 0xe,
0x18, 0x5e, 0xcc, 0x23, 0x77, 0x33, 0xc1, 0x8b,
0x33, 0xc8, 0x14, 0x32, 0x2b, 0x29, 0xf0, 0x90,
0x9, 0xd3, 0xa0, 0x3, 0xe8, 0xf4, 0x78, 0xb4,
0x20, 0xf, 0x89, 0x2e, 0x0, 0x25, 0x0, 0xf9,
0x40, 0x20,
/* U+F0C4 "" */ /* U+F0C4 "" */
0x7, 0x93, 0x0, 0xc4, 0x40, 0xa8, 0x6c, 0x20, 0x7, 0x93, 0x0, 0xc4, 0x40, 0xa8, 0x6c, 0x20,
0x4, 0x6e, 0x95, 0x4d, 0xc0, 0xa1, 0xe, 0xc, 0x4, 0x6e, 0x95, 0x4d, 0xc0, 0xa1, 0xe, 0xc,
@@ -6841,6 +6853,13 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x34, 0x88, 0xff, 0x30, 0xc6, 0x7f, 0xff, 0xf0, 0x34, 0x88, 0xff, 0x30, 0xc6, 0x7f, 0xff, 0xf0,
0x38, 0x40, 0x38, 0x40,
/* U+F279 "" */
0x0, 0x1b, 0xab, 0x8, 0x7, 0x33, 0xb6, 0xe5,
0x94, 0xf6, 0x38, 0xc7, 0x4e, 0xc9, 0x0, 0x71,
0xc1, 0x38, 0x80, 0x7f, 0xff, 0xc0, 0x3f, 0xf8,
0x25, 0x2a, 0xc2, 0x1, 0xc9, 0x2b, 0x7a, 0xa5,
0x3d, 0x8e, 0x2f, 0xb6, 0x60,
/* U+F287 "" */ /* U+F287 "" */
0x0, 0xff, 0xe5, 0x12, 0xfd, 0x80, 0x7f, 0xcb, 0x0, 0xff, 0xe5, 0x12, 0xfd, 0x80, 0x7f, 0xcb,
0xdc, 0x5, 0x0, 0xf8, 0x40, 0x28, 0xdb, 0xb6, 0xdc, 0x5, 0x0, 0xf8, 0x40, 0x28, 0xdb, 0xb6,
@@ -7967,30 +7986,32 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 32689, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 32689, .adv_w = 192, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32746, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 32746, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 32810, .adv_w = 186, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 32810, .adv_w = 186, .box_w = 12, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 32880, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 32880, .adv_w = 240, .box_w = 15, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32941, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 32954, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 32981, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 33015, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33025, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 33055, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33057, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33099, .adv_w = 168, .box_w = 11, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33096, .adv_w = 120, .box_w = 9, .box_h = 13, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 33131, .adv_w = 192, .box_w = 12, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33142, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 33170, .adv_w = 120, .box_w = 9, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33187, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 33216, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33240, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33261, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33296, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 33314, .adv_w = 216, .box_w = 14, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33359, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 33370, .adv_w = 192, .box_w = 14, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 33382, .adv_w = 240, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 33433, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33457, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33456, .adv_w = 240, .box_w = 15, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33500, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33531, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33549, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33574, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33598, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33623, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33647, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33672, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33689, .adv_w = 240, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 33721, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 33754, .adv_w = 168, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 33763, .adv_w = 216, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33818, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 33800, .adv_w = 240, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 33861, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 33865, .adv_w = 168, .box_w = 10, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33928, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 33929, .adv_w = 168, .box_w = 11, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 33983, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 33972, .adv_w = 192, .box_w = 13, .box_h = 13, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 34012, .adv_w = 193, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0} {.bitmap_index = 34039, .adv_w = 240, .box_w = 15, .box_h = 9, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 34094, .adv_w = 144, .box_w = 9, .box_h = 13, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 34123, .adv_w = 193, .box_w = 13, .box_h = 9, .ofs_x = 0, .ofs_y = 0}
}; };
/*--------------------- /*---------------------
@@ -8019,9 +8040,10 @@ static const uint16_t unicode_list_12[] = {
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
}; };
/*Collect the unicode lists and glyph_id offsets*/ /*Collect the unicode lists and glyph_id offsets*/
@@ -8077,7 +8099,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
}, },
{ {
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017, .range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY .unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
} }
}; };
@@ -8224,7 +8246,7 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Map glyph_ids to kern right classes*/ /*Map glyph_ids to kern right classes*/
@@ -8365,7 +8387,7 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Kern values between classes*/ /*Kern values between classes*/
@@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 14 px * Size: 14 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 14 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_14 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_14.c * Opts: --bpp 4 --size 14 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_14 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_14.c
******************************************************************************/ ******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE #ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -7635,6 +7635,21 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x57, 0x9, 0x20, 0x2a, 0x5a, 0xa0, 0x1c, 0xec, 0x57, 0x9, 0x20, 0x2a, 0x5a, 0xa0, 0x1c, 0xec,
0xf9, 0xe5, 0x44, 0x1, 0x0, 0xf9, 0xe5, 0x44, 0x1, 0x0,
/* U+F0C0 "" */
0x0, 0xfc, 0x62, 0x1, 0xff, 0xc2, 0x3e, 0xce,
0xa0, 0xf, 0xc2, 0xe6, 0x0, 0xd1, 0x0, 0x2b,
0x80, 0x19, 0x40, 0x2f, 0x8c, 0x23, 0x10, 0xd,
0x81, 0x53, 0x4c, 0x2, 0x20, 0x3, 0x8, 0x7,
0x8, 0x30, 0x3, 0x80, 0x60, 0x55, 0x9, 0x0,
0x34, 0x84, 0x9, 0x50, 0x1, 0xb2, 0xc0, 0x16,
0xe6, 0x98, 0x60, 0x99, 0xe4, 0x11, 0xdb, 0xca,
0x4b, 0x59, 0x6c, 0x83, 0x9b, 0xae, 0x27, 0x0,
0x41, 0x6d, 0x6f, 0xfa, 0xaf, 0x38, 0x40, 0x54,
0x40, 0x52, 0x0, 0x3e, 0x29, 0x80, 0xb, 0xf3,
0x8, 0x1, 0xfc, 0x8d, 0x98, 0x71, 0x32, 0x0,
0xff, 0xe0, 0x19, 0x80, 0x32, 0x99, 0x17, 0xe8,
0x0, 0xc0,
/* U+F0C4 "" */ /* U+F0C4 "" */
0x4, 0x86, 0x0, 0xf0, 0x80, 0x1a, 0xde, 0x68, 0x4, 0x86, 0x0, 0xf0, 0x80, 0x1a, 0xde, 0x68,
0x2, 0x2d, 0xed, 0x18, 0xc, 0x35, 0x10, 0x1f, 0x2, 0x2d, 0xed, 0x18, 0xc, 0x35, 0x10, 0x1f,
@@ -7801,6 +7816,14 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xff, 0xc1, 0xe, 0x1a, 0xff, 0xff, 0xe1, 0xa8, 0xff, 0xc1, 0xe, 0x1a, 0xff, 0xff, 0xe1, 0xa8,
0x0, 0x0,
/* U+F279 "" */
0x0, 0x84, 0x44, 0x1, 0xfc, 0x80, 0x73, 0xcf,
0x3f, 0x4a, 0x1, 0xc, 0x75, 0x5e, 0x30, 0x6,
0x5a, 0xf7, 0x5e, 0x71, 0x4, 0x0, 0xfe, 0xd2,
0x0, 0xff, 0xff, 0x80, 0x7f, 0xf7, 0xc5, 0xdd,
0x2e, 0x40, 0x1e, 0x4d, 0x38, 0xe8, 0x12, 0x8d,
0xd3, 0x91, 0x23, 0xac, 0xc0,
/* U+F287 "" */ /* U+F287 "" */
0x0, 0xfe, 0x7b, 0x20, 0xf, 0xfe, 0xd, 0x6c, 0x0, 0xfe, 0x7b, 0x20, 0xf, 0xfe, 0xd, 0x6c,
0x24, 0x0, 0x7f, 0xf0, 0x28, 0xf8, 0x83, 0xc0, 0x24, 0x0, 0x7f, 0xf0, 0x28, 0xf8, 0x83, 0xc0,
@@ -8939,30 +8962,32 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 40145, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 40145, .adv_w = 224, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40217, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 40217, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40296, .adv_w = 217, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 40296, .adv_w = 217, .box_w = 14, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40389, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 40389, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40470, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 40487, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40518, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 40568, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40574, .adv_w = 196, .box_w = 13, .box_h = 12, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 40616, .adv_w = 196, .box_w = 13, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40614, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 40672, .adv_w = 196, .box_w = 13, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 40667, .adv_w = 140, .box_w = 10, .box_h = 15, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 40712, .adv_w = 224, .box_w = 14, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 40723, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 40765, .adv_w = 140, .box_w = 10, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 40786, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 40821, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40852, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 40884, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 40925, .adv_w = 224, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 40950, .adv_w = 252, .box_w = 16, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41002, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 41023, .adv_w = 224, .box_w = 16, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41034, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 41100, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41131, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 41132, .adv_w = 280, .box_w = 18, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41183, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 41229, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41238, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 41281, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41293, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 41336, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41348, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 41391, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41397, .adv_w = 280, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 41446, .adv_w = 280, .box_w = 18, .box_h = 10, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41477, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 41495, .adv_w = 252, .box_w = 16, .box_h = 13, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41561, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 41540, .adv_w = 280, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 41617, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 41620, .adv_w = 196, .box_w = 12, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41694, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 41704, .adv_w = 196, .box_w = 13, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41772, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 41760, .adv_w = 224, .box_w = 15, .box_h = 15, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 41808, .adv_w = 225, .box_w = 15, .box_h = 10, .ofs_x = 0, .ofs_y = 0} {.bitmap_index = 41837, .adv_w = 280, .box_w = 18, .box_h = 11, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 41915, .adv_w = 168, .box_w = 11, .box_h = 15, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 41951, .adv_w = 225, .box_w = 15, .box_h = 10, .ofs_x = 0, .ofs_y = 0}
}; };
/*--------------------- /*---------------------
@@ -8991,9 +9016,10 @@ static const uint16_t unicode_list_12[] = {
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
}; };
/*Collect the unicode lists and glyph_id offsets*/ /*Collect the unicode lists and glyph_id offsets*/
@@ -9049,7 +9075,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
}, },
{ {
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017, .range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY .unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
} }
}; };
@@ -9196,7 +9222,7 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Map glyph_ids to kern right classes*/ /*Map glyph_ids to kern right classes*/
@@ -9337,7 +9363,7 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Kern values between classes*/ /*Kern values between classes*/
@@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 16 px * Size: 16 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 16 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_16 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_16.c * Opts: --bpp 4 --size 16 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_16 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_16.c
******************************************************************************/ ******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE #ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -8728,6 +8728,22 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x40, 0x8, 0x63, 0xd3, 0x0, 0xe6, 0xe0, 0xf5, 0x40, 0x8, 0x63, 0xd3, 0x0, 0xe6, 0xe0, 0xf5,
0x85, 0x56, 0x10, 0x4, 0x85, 0x56, 0x10, 0x4,
/* U+F0C0 "" */
0x0, 0xf8, 0xaf, 0xfa, 0x0, 0x3f, 0xf8, 0x43,
0x88, 0x2, 0xf2, 0x1, 0xf9, 0x3b, 0x88, 0x12,
0x20, 0x19, 0x84, 0x13, 0xb8, 0x80, 0xa, 0x11,
0x50, 0x18, 0x7, 0x94, 0x28, 0x45, 0x40, 0x1f,
0x18, 0x7, 0x94, 0x3, 0xea, 0x11, 0x50, 0x48,
0x80, 0x66, 0x10, 0xa1, 0x15, 0x0, 0x13, 0xb8,
0x80, 0x38, 0x80, 0x2f, 0x20, 0x4, 0xee, 0x20,
0x27, 0x7f, 0xb8, 0x40, 0xaf, 0xfa, 0x0, 0x5,
0xdf, 0xee, 0x4a, 0x10, 0x5, 0x26, 0xfd, 0xc4,
0xef, 0xe3, 0x58, 0x0, 0x68, 0x40, 0xc, 0x14,
0x40, 0x8e, 0xc4, 0x7, 0x62, 0xc0, 0x1, 0x30,
0x5, 0x40, 0x7, 0xfa, 0x28, 0x0, 0x79, 0xfe,
0x33, 0x0, 0x7f, 0x8c, 0xdf, 0xec, 0x0, 0xff,
0xe6, 0x38, 0x7, 0xf9, 0xc0, 0x30,
/* U+F0C4 "" */ /* U+F0C4 "" */
0x8, 0xee, 0x40, 0x7, 0x99, 0x82, 0x10, 0xe2, 0x8, 0xee, 0x40, 0x7, 0x99, 0x82, 0x10, 0xe2,
0x27, 0x80, 0x8, 0xb6, 0x65, 0x80, 0xc1, 0x98, 0x27, 0x80, 0x8, 0xb6, 0x65, 0x80, 0xc1, 0x98,
@@ -8898,6 +8914,16 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xed, 0xf8, 0x7, 0xff, 0x24, 0x80, 0x1f, 0xff, 0xed, 0xf8, 0x7, 0xff, 0x24, 0x80, 0x1f, 0xff,
0xfc, 0x30, 0x6, 0xb8, 0x7, 0xff, 0x15, 0xc0, 0xfc, 0x30, 0x6, 0xb8, 0x7, 0xff, 0x15, 0xc0,
/* U+F279 "" */
0x0, 0xff, 0xe4, 0xb6, 0x6, 0xb9, 0x0, 0x78,
0xa7, 0x8a, 0x7e, 0x4c, 0xa, 0x37, 0x4e, 0x40,
0x75, 0xac, 0x38, 0xc0, 0x1f, 0x14, 0x68, 0x62,
0x80, 0x42, 0x1, 0xff, 0xff, 0x0, 0xff, 0xff,
0x80, 0x7f, 0x84, 0x2, 0x5c, 0xd, 0x72, 0x0,
0xf9, 0xb0, 0x9b, 0x68, 0xc0, 0xa3, 0x74, 0xe4,
0x7, 0x3f, 0x25, 0xb2, 0x40, 0x1e, 0x28, 0xd0,
0xc6, 0x0, 0x80,
/* U+F287 "" */ /* U+F287 "" */
0x0, 0xff, 0x84, 0x3, 0xff, 0x8e, 0xfd, 0xa2, 0x0, 0xff, 0x84, 0x3, 0xff, 0x8e, 0xfd, 0xa2,
0x1, 0xff, 0xc2, 0x1c, 0xf8, 0x2, 0x40, 0xf, 0x1, 0xff, 0xc2, 0x1c, 0xf8, 0x2, 0x40, 0xf,
@@ -10045,30 +10071,32 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 48861, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 48861, .adv_w = 256, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 48929, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 48929, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49023, .adv_w = 248, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 49023, .adv_w = 248, .box_w = 16, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49131, .adv_w = 224, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 49131, .adv_w = 320, .box_w = 20, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49222, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 49241, .adv_w = 224, .box_w = 15, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49269, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 49332, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49321, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 49379, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49376, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 49431, .adv_w = 224, .box_w = 14, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49435, .adv_w = 160, .box_w = 12, .box_h = 16, .ofs_x = -1, .ofs_y = -2}, {.bitmap_index = 49486, .adv_w = 256, .box_w = 16, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49503, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 49545, .adv_w = 160, .box_w = 12, .box_h = 16, .ofs_x = -1, .ofs_y = -2},
{.bitmap_index = 49564, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 49613, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49634, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 49674, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49700, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3}, {.bitmap_index = 49744, .adv_w = 288, .box_w = 18, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 49789, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 49810, .adv_w = 256, .box_w = 18, .box_h = 18, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 49822, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 49899, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 49935, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 49932, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 49980, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 50045, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50024, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 50090, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50068, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 50134, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50114, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 50178, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50154, .adv_w = 320, .box_w = 21, .box_h = 14, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 50224, .adv_w = 320, .box_w = 20, .box_h = 10, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 50255, .adv_w = 224, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -2}, {.bitmap_index = 50264, .adv_w = 288, .box_w = 18, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50351, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 50323, .adv_w = 320, .box_w = 21, .box_h = 14, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 50406, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -3}, {.bitmap_index = 50424, .adv_w = 224, .box_w = 12, .box_h = 16, .ofs_x = 1, .ofs_y = -2},
{.bitmap_index = 50500, .adv_w = 320, .box_w = 20, .box_h = 12, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 50520, .adv_w = 224, .box_w = 14, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50589, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 50575, .adv_w = 256, .box_w = 17, .box_h = 17, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 50628, .adv_w = 258, .box_w = 17, .box_h = 11, .ofs_x = 0, .ofs_y = 1} {.bitmap_index = 50669, .adv_w = 320, .box_w = 20, .box_h = 12, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 50758, .adv_w = 192, .box_w = 12, .box_h = 16, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 50797, .adv_w = 258, .box_w = 17, .box_h = 11, .ofs_x = 0, .ofs_y = 1}
}; };
/*--------------------- /*---------------------
@@ -10097,9 +10125,10 @@ static const uint16_t unicode_list_12[] = {
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
}; };
/*Collect the unicode lists and glyph_id offsets*/ /*Collect the unicode lists and glyph_id offsets*/
@@ -10155,7 +10184,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
}, },
{ {
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017, .range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY .unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
} }
}; };
@@ -10302,7 +10331,7 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Map glyph_ids to kern right classes*/ /*Map glyph_ids to kern right classes*/
@@ -10443,7 +10472,7 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Kern values between classes*/ /*Kern values between classes*/
@@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
* Size: 20 px * Size: 20 px
* Bpp: 4 * Bpp: 4
* Opts: --bpp 4 --size 20 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_20 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_20.c * Opts: --bpp 4 --size 20 --force-fast-kern-format --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/NotoSans-Medium.ttf -r 0x20-0x7E,0xA0-0x24F,0x370-0x3FF,0x400-0x52F,0x1E80-0x1E85,0x1E9E,0x1EF2-0x1EF3,0x2010-0x2027,0x2030,0x2039-0x203A,0x20A4-0x20BF,0x2116,0x2122 --font /var/folders/1d/dgkmygls6x5gsb16pbn2mwlr0000gn/T//meshcore-ui-fonts/fa.woff -r 61612,61445,62073,61632,61441,61448,61451,61452,61453,61457,61459,61461,61465,61468,61473,61478,61479,61480,61502,61507,61512,61515,61516,61517,61521,61522,61523,61524,61543,61544,61550,61552,61553,61556,61559,61560,61561,61563,61587,61589,61636,61637,61639,61641,61664,61671,61674,61683,61724,61732,61787,61931,62016,62017,62018,62019,62020,62087,62099,62212,62189,62810,63426,63650 --format lvgl --lv-include lvgl.h --lv-font-name ui_font_20 -o /Users/jakub/Documents/MeshCore/MeshCore/examples/companion_radio/ui-lvgl/fonts/ui_font_20.c
******************************************************************************/ ******************************************************************************/
#ifdef LV_LVGL_H_INCLUDE_SIMPLE #ifdef LV_LVGL_H_INCLUDE_SIMPLE
@@ -10980,6 +10980,30 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0xf9, 0xf8, 0x12, 0x0, 0xec, 0xbb, 0x8, 0x3, 0xf9, 0xf8, 0x12, 0x0, 0xec, 0xbb, 0x8, 0x3,
0xf8, 0x50, 0x1f, 0xf0, 0x48, 0xc0, 0x3c, 0xf8, 0x50, 0x1f, 0xf0, 0x48, 0xc0, 0x3c,
/* U+F0C0 "" */
0x0, 0xff, 0xe0, 0x8, 0x7, 0xff, 0x24, 0x6b,
0xfb, 0xe4, 0x3, 0xff, 0x8f, 0x8a, 0x1, 0x36,
0x0, 0x7f, 0xc9, 0x2e, 0x1, 0x41, 0x0, 0x71,
0xb8, 0x4, 0xf2, 0x60, 0x12, 0x5b, 0x45, 0x80,
0x10, 0x3, 0xeb, 0x0, 0x64, 0x36, 0x20, 0x3,
0xc0, 0x24, 0x20, 0x10, 0xf, 0x84, 0x8, 0xc0,
0x2f, 0x0, 0x8, 0x6, 0x30, 0x60, 0xf, 0x9c,
0xc, 0x3, 0x8, 0x2, 0x4, 0x0, 0xe2, 0x10,
0x1, 0xf4, 0x80, 0xb8, 0x0, 0x60, 0x0, 0x5f,
0x7b, 0x0, 0x3, 0x90, 0xe, 0xa3, 0x0, 0x46,
0x5f, 0x90, 0x0, 0x90, 0x91, 0x0, 0x13, 0x6c,
0x34, 0x72, 0x80, 0x4a, 0xa2, 0x42, 0x6, 0xda,
0x99, 0x5a, 0x82, 0xb0, 0xbc, 0xb9, 0x3a, 0x82,
0x54, 0xca, 0xb5, 0x60, 0x3, 0x58, 0x75, 0x4e,
0x7e, 0xfe, 0x45, 0x70, 0x60, 0x6, 0xb1, 0x0,
0xa1, 0xe8, 0x40, 0x31, 0x0, 0x61, 0xb0, 0x90,
0x8, 0x48, 0x2, 0x78, 0x0, 0xff, 0xe0, 0xc3,
0x0, 0x45, 0x97, 0x70, 0x20, 0x7, 0xff, 0x5,
0x2, 0xee, 0xc1, 0x44, 0x80, 0x3f, 0xf8, 0xa8,
0x90, 0x80, 0x7f, 0xf4, 0xa4, 0x3, 0xff, 0x83,
0x20, 0x1f, 0xe6, 0xff, 0xff, 0xe0, 0xb0, 0x7,
0x0,
/* U+F0C4 "" */ /* U+F0C4 "" */
0x0, 0x12, 0xa0, 0x7, 0xff, 0xa, 0x76, 0xaf, 0x0, 0x12, 0xa0, 0x7, 0xff, 0xa, 0x76, 0xaf,
0x8c, 0x3, 0xc9, 0x54, 0x50, 0x76, 0x0, 0x87, 0x8c, 0x3, 0xc9, 0x54, 0x50, 0x76, 0x0, 0x87,
@@ -11215,6 +11239,19 @@ static LV_ATTRIBUTE_LARGE_CONST const uint8_t glyph_bitmap[] = {
0x1b, 0xbf, 0xff, 0x18, 0x41, 0xd2, 0x84, 0x3, 0x1b, 0xbf, 0xff, 0x18, 0x41, 0xd2, 0x84, 0x3,
0xff, 0x90, 0x70, 0x0, 0xff, 0x90, 0x70, 0x0,
/* U+F279 "" */
0x0, 0xf8, 0x40, 0x3f, 0xf8, 0x24, 0x1, 0x8a,
0x39, 0x27, 0xa4, 0xc0, 0x3f, 0x2e, 0x6a, 0x2,
0xde, 0xb8, 0x80, 0x5, 0xb3, 0xa4, 0xc0, 0x26,
0xda, 0x30, 0x1c, 0xa4, 0x0, 0xfc, 0x2d, 0x9c,
0x1b, 0x24, 0x1, 0x8c, 0x3, 0xff, 0x82, 0x20,
0x60, 0x1f, 0xff, 0xf0, 0xf, 0xff, 0xf8, 0x7,
0xff, 0xfc, 0x3, 0xff, 0x8a, 0xfc, 0x1, 0xff,
0xc2, 0x70, 0xc, 0xfc, 0x6d, 0xd2, 0x60, 0x1f,
0x8a, 0x38, 0x80, 0x63, 0xa0, 0x40, 0x2, 0xd9,
0xd2, 0x60, 0x12, 0xde, 0xb8, 0x86, 0xf3, 0x88,
0x7, 0xc2, 0xd9, 0xc1, 0xf4, 0x80, 0x18,
/* U+F287 "" */ /* U+F287 "" */
0x0, 0xff, 0xe1, 0x8, 0x7, 0xff, 0x33, 0x3b, 0x0, 0xff, 0xe1, 0x8, 0x7, 0xff, 0x33, 0x3b,
0x84, 0x1, 0xff, 0xc7, 0x7c, 0xd3, 0x1, 0xb0, 0x84, 0x1, 0xff, 0xc7, 0x7c, 0xd3, 0x1, 0xb0,
@@ -12390,30 +12427,32 @@ static const lv_font_fmt_txt_glyph_dsc_t glyph_dsc[] = {
{.bitmap_index = 66527, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 66527, .adv_w = 320, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66637, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3}, {.bitmap_index = 66637, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 66775, .adv_w = 310, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 66775, .adv_w = 310, .box_w = 20, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 66934, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 66934, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67082, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 67103, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67155, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 67251, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67248, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 67324, .adv_w = 280, .box_w = 18, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 67308, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 67417, .adv_w = 280, .box_w = 18, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 67398, .adv_w = 200, .box_w = 14, .box_h = 21, .ofs_x = -1, .ofs_y = -3}, {.bitmap_index = 67477, .adv_w = 320, .box_w = 20, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67491, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 67567, .adv_w = 200, .box_w = 14, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67579, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 67660, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67690, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 67748, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 67816, .adv_w = 320, .box_w = 22, .box_h = 21, .ofs_x = -1, .ofs_y = -3}, {.bitmap_index = 67859, .adv_w = 360, .box_w = 23, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 67937, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 67985, .adv_w = 320, .box_w = 22, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 67985, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2}, {.bitmap_index = 68106, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 68149, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 68154, .adv_w = 400, .box_w = 25, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68216, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 68318, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68284, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 68385, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68351, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 68453, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68418, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1}, {.bitmap_index = 68520, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68478, .adv_w = 400, .box_w = 26, .box_h = 17, .ofs_x = 0, .ofs_y = -1}, {.bitmap_index = 68587, .adv_w = 400, .box_w = 25, .box_h = 13, .ofs_x = 0, .ofs_y = 1},
{.bitmap_index = 68612, .adv_w = 280, .box_w = 16, .box_h = 21, .ofs_x = 1, .ofs_y = -3}, {.bitmap_index = 68647, .adv_w = 360, .box_w = 23, .box_h = 19, .ofs_x = 0, .ofs_y = -2},
{.bitmap_index = 68759, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 68734, .adv_w = 400, .box_w = 26, .box_h = 17, .ofs_x = 0, .ofs_y = -1},
{.bitmap_index = 68846, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3}, {.bitmap_index = 68868, .adv_w = 280, .box_w = 16, .box_h = 21, .ofs_x = 1, .ofs_y = -3},
{.bitmap_index = 68986, .adv_w = 400, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 0}, {.bitmap_index = 69015, .adv_w = 280, .box_w = 18, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69106, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3}, {.bitmap_index = 69102, .adv_w = 320, .box_w = 21, .box_h = 21, .ofs_x = -1, .ofs_y = -3},
{.bitmap_index = 69165, .adv_w = 322, .box_w = 21, .box_h = 13, .ofs_x = 0, .ofs_y = 1} {.bitmap_index = 69242, .adv_w = 400, .box_w = 25, .box_h = 15, .ofs_x = 0, .ofs_y = 0},
{.bitmap_index = 69362, .adv_w = 240, .box_w = 15, .box_h = 21, .ofs_x = 0, .ofs_y = -3},
{.bitmap_index = 69421, .adv_w = 322, .box_w = 21, .box_h = 13, .ofs_x = 0, .ofs_y = 1}
}; };
/*--------------------- /*---------------------
@@ -12442,9 +12481,10 @@ static const uint16_t unicode_list_12[] = {
0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b, 0xcf12, 0xcf28, 0xcf2d, 0xcf32, 0xcf35, 0xcf36, 0xcf37, 0xcf3b,
0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b, 0xcf3c, 0xcf3d, 0xcf3e, 0xcf51, 0xcf52, 0xcf58, 0xcf5a, 0xcf5b,
0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96, 0xcf5e, 0xcf61, 0xcf62, 0xcf63, 0xcf65, 0xcf7d, 0xcf7f, 0xcf96,
0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4, 0xcfdd, 0xcfaa, 0xcfae, 0xcfaf, 0xcfb1, 0xcfb3, 0xcfca, 0xcfd1, 0xcfd4,
0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c, 0xd12d, 0xcfdd, 0xd006, 0xd00e, 0xd045, 0xd0d5, 0xd12a, 0xd12b, 0xd12c,
0xd12e, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444, 0xd6ac, 0xd78c 0xd12d, 0xd12e, 0xd163, 0xd171, 0xd17d, 0xd1d7, 0xd1ee, 0xd444,
0xd6ac, 0xd78c
}; };
/*Collect the unicode lists and glyph_id offsets*/ /*Collect the unicode lists and glyph_id offsets*/
@@ -12500,7 +12540,7 @@ static const lv_font_fmt_txt_cmap_t cmaps[] =
}, },
{ {
.range_start = 8470, .range_length = 55181, .glyph_id_start = 1017, .range_start = 8470, .range_length = 55181, .glyph_id_start = 1017,
.unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 64, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY .unicode_list = unicode_list_12, .glyph_id_ofs_list = NULL, .list_length = 66, .type = LV_FONT_FMT_TXT_CMAP_SPARSE_TINY
} }
}; };
@@ -12647,7 +12687,7 @@ static const uint8_t kern_left_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Map glyph_ids to kern right classes*/ /*Map glyph_ids to kern right classes*/
@@ -12788,7 +12828,7 @@ static const uint8_t kern_right_class_mapping[] =
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0 0, 0, 0
}; };
/*Kern values between classes*/ /*Kern values between classes*/
+4 -1
View File
@@ -11,7 +11,7 @@
#define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN #define LV_USE_STDLIB_MALLOC LV_STDLIB_BUILTIN
#define LV_USE_STDLIB_STRING LV_STDLIB_CLIB #define LV_USE_STDLIB_STRING LV_STDLIB_CLIB
#define LV_USE_STDLIB_SPRINTF LV_STDLIB_CLIB #define LV_USE_STDLIB_SPRINTF LV_STDLIB_CLIB
#define LV_MEM_SIZE (1024U * 1024U) #define LV_MEM_SIZE (2048U * 1024U) // map tiles decode through it (~0.5 MB peak per PNG)
#if defined(ESP32) #if defined(ESP32)
#define LV_MEM_ADR 0 #define LV_MEM_ADR 0
#define LV_MEM_POOL_INCLUDE "lv_psram_pool.h" #define LV_MEM_POOL_INCLUDE "lv_psram_pool.h"
@@ -35,6 +35,9 @@
#define LV_FONT_DEFAULT &ui_font_14 #define LV_FONT_DEFAULT &ui_font_14
#define LV_USE_FONT_COMPRESSED 1 // generated fonts are RLE-compressed (without this every glyph draws as a box) #define LV_USE_FONT_COMPRESSED 1 // generated fonts are RLE-compressed (without this every glyph draws as a box)
// PNG decoder for raster map tiles (map/TileProvider.h calls lodepng directly).
#define LV_USE_LODEPNG 1
#define LV_USE_THEME_DEFAULT 1 #define LV_USE_THEME_DEFAULT 1
#define LV_THEME_DEFAULT_DARK 1 #define LV_THEME_DEFAULT_DARK 1
@@ -0,0 +1,71 @@
#pragma once
// Decoded map tiles (RGB565, 128 KB each) kept for reuse while panning / zooming
// back, least-recently-used evicted. Buffers live in PSRAM on the ESP32 and are
// allocated on first use, then kept for the session.
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp only.
#if defined(ESP32)
#include <esp_heap_caps.h>
#endif
extern "C" void lv_image_cache_drop(const void* src); // not exported through lvgl.h here
namespace mapview {
class TileCache {
public:
static const int SLOTS = 12; // 6 on screen at 320x218 + a ring of neighbours
struct Slot {
int16_t z = -1;
int32_t x = 0, y = 0;
bool present = false; // false: looked up, there is no tile (don't retry)
uint32_t used = 0;
uint16_t* px = nullptr;
lv_image_dsc_t dsc;
};
// The slot holding z/x/y if it was loaded (present or known missing), else nullptr.
Slot* find(int z, int x, int y) {
for (Slot& s : _slots)
if (s.z == z && s.x == x && s.y == y) { s.used = ++_tick; return &s; }
return nullptr;
}
// Render z/x/y through `src` into the least recently used slot.
Slot* load(TileProvider& src, int z, int x, int y) {
Slot* s = &_slots[0];
for (Slot& c : _slots) if (c.used < s->used) s = &c;
if (!s->px) {
#if defined(ESP32)
s->px = (uint16_t*)heap_caps_malloc(TILE_PX * TILE_PX * 2, MALLOC_CAP_SPIRAM);
#else
s->px = (uint16_t*)malloc(TILE_PX * TILE_PX * 2);
#endif
if (!s->px) return nullptr;
}
lv_image_cache_drop(&s->dsc); // same descriptor, new pixels
s->z = z; s->x = x; s->y = y;
s->used = ++_tick;
s->present = src.renderTile(z, x, y, s->px);
memset(&s->dsc, 0, sizeof(s->dsc));
s->dsc.header.magic = LV_IMAGE_HEADER_MAGIC;
s->dsc.header.cf = LV_COLOR_FORMAT_RGB565;
s->dsc.header.w = TILE_PX;
s->dsc.header.h = TILE_PX;
s->dsc.header.stride = TILE_PX * 2;
s->dsc.data_size = TILE_PX * TILE_PX * 2;
s->dsc.data = (const uint8_t*)s->px;
return s;
}
// Forget everything (e.g. the card was swapped); keeps the buffers.
void invalidate() { for (Slot& s : _slots) { s.z = -1; s.used = 0; } }
private:
Slot _slots[SLOTS];
uint32_t _tick = 0;
};
} // namespace mapview
@@ -0,0 +1,130 @@
#pragma once
// Map tile sources. The map screen, its tile cache and the overlays only ever
// call renderTile(): 256x256 RGB565 pixels for Web-Mercator tile z/x/y. The
// raster provider decodes PNG files from the card; a vector renderer would be
// another TileProvider drawing into the same buffer, with nothing else changing.
//
// Single-TU fragment: included by ui-lvgl/UITask.cpp only.
#include <stdio.h>
#include <sys/stat.h>
// LVGL's copy of lodepng (LV_USE_LODEPNG), compiled as C. It is patched for
// LVGL: the decode "output" is an lv_draw_buf_t* (32-bit RGBA pixels in ->data),
// not a plain pixel array, and is freed with lv_draw_buf_destroy().
#define LODEPNG_NO_COMPILE_CPP
extern "C" {
#include "src/libs/lodepng/lodepng.h"
}
namespace mapview {
static const int TILE_PX = 256;
class TileProvider {
public:
virtual ~TileProvider() {}
virtual bool available() = 0; // anything to draw at all
virtual bool renderTile(int z, int x, int y, uint16_t* out) = 0; // false: no tile there
virtual const char* attribution() const = 0;
};
// Raster tiles under <root>/{z}/{x}/{y}.png -- the Meshtastic MUI layout, so
// cards prepared for either work for both (tools/maps/fetch_tiles.py writes
// it) -- or packed per column in <root>/{z}/{x}.pak ('TPK1': y_min, y_max,
// offsets[n+1], PNG blobs; the format of upstream PR #3381's tile_packer.py),
// which copies onto a FAT card far faster than millions of loose files.
class RasterTileProvider : public TileProvider {
public:
explicit RasterTileProvider(const char* root) : _root(root) {}
// Also reads <root>/attribution.txt (written by tools/maps/fetch_tiles.py and
// the on-device downloader): the tile set's credit line, shown on the map.
bool available() override {
struct stat st;
if (stat(_root, &st) != 0 || !S_ISDIR(st.st_mode)) return false;
char path[64];
snprintf(path, sizeof(path), "%s/attribution.txt", _root);
_attr[0] = '\0';
if (FILE* f = fopen(path, "r")) {
size_t n = fread(_attr, 1, sizeof(_attr) - 1, f);
fclose(f);
while (n > 0 && (_attr[n - 1] == '\n' || _attr[n - 1] == '\r')) n--;
_attr[n] = '\0';
}
return true;
}
bool renderTile(int z, int x, int y, uint16_t* out) override {
uint32_t len = 0;
uint8_t* png = readLoose(z, x, y, len);
if (!png) png = readPacked(z, x, y, len);
if (!png) return false;
unsigned char* res = nullptr;
unsigned w = 0, h = 0;
unsigned err = lodepng_decode32(&res, &w, &h, png, len);
lv_free(png);
lv_draw_buf_t* db = (lv_draw_buf_t*)res;
if (err || !db) { if (db) lv_draw_buf_destroy(db); return false; }
if (w != TILE_PX || h != TILE_PX) { lv_draw_buf_destroy(db); return false; }
for (int row = 0; row < TILE_PX; row++) {
const uint8_t* p = db->data + row * db->header.stride; // R, G, B, A
uint16_t* o = out + row * TILE_PX;
for (int i = 0; i < TILE_PX; i++, p += 4)
o[i] = (uint16_t)(((p[0] & 0xF8) << 8) | ((p[1] & 0xFC) << 3) | (p[2] >> 3));
}
lv_draw_buf_destroy(db);
return true;
}
const char* attribution() const override { return _attr[0] ? _attr : "\xC2\xA9 OpenStreetMap contributors"; }
private:
const char* _root;
char _attr[96] = "";
static uint8_t* readRange(FILE* f, long off, uint32_t len) {
if (len == 0 || len > 512 * 1024) return nullptr;
uint8_t* buf = (uint8_t*)lv_malloc(len);
if (!buf) return nullptr;
if (fseek(f, off, SEEK_SET) != 0 || fread(buf, 1, len, f) != len) { lv_free(buf); return nullptr; }
return buf;
}
uint8_t* readLoose(int z, int x, int y, uint32_t& len) {
char path[64];
snprintf(path, sizeof(path), "%s/%d/%d/%d.png", _root, z, x, y);
FILE* f = fopen(path, "rb");
if (!f) return nullptr;
fseek(f, 0, SEEK_END);
long sz = ftell(f);
uint8_t* buf = sz > 0 ? readRange(f, 0, (uint32_t)sz) : nullptr;
fclose(f);
if (buf) len = (uint32_t)sz;
return buf;
}
uint8_t* readPacked(int z, int x, int y, uint32_t& len) {
char path[64];
snprintf(path, sizeof(path), "%s/%d/%d.pak", _root, z, x);
FILE* f = fopen(path, "rb");
if (!f) return nullptr;
uint8_t hdr[12];
uint32_t y0, y1, off[2];
uint8_t* buf = nullptr;
if (fread(hdr, 1, 12, f) == 12 && memcmp(hdr, "TPK1", 4) == 0) {
memcpy(&y0, hdr + 4, 4);
memcpy(&y1, hdr + 8, 4);
if (y >= (int)y0 && y <= (int)y1 &&
fseek(f, 12 + 4 * (y - (int)y0), SEEK_SET) == 0 && fread(off, 4, 2, f) == 2 && off[1] > off[0]) {
len = off[1] - off[0];
buf = readRange(f, off[0], len);
}
}
fclose(f);
return buf;
}
};
} // namespace mapview
+2
View File
@@ -65,3 +65,5 @@ lib_deps =
${Wio_Tracker_L2.lib_deps} ${Wio_Tracker_L2.lib_deps}
densaugeo/base64 @ ~1.4.0 densaugeo/base64 @ ~1.4.0
lvgl/lvgl @ 9.2.2 ; pinned: bump deliberately, re-test on hardware lvgl/lvgl @ 9.2.2 ; pinned: bump deliberately, re-test on hardware
SD_MMC ; map tiles on the microSD (framework library)
FS
+152
View File
@@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""Offline raster map tiles for the ui-lvgl map screen (Wio Tracker L2 SD card).
Writes <out>/maps/{z}/{x}/{y}.png -- the same layout Meshtastic MUI uses, so a
card prepared for either works for both. Copy the `maps` folder to the root of
the SD card. The device can also download an area itself over WiFi; this is
the offline / bulk option.
tools/maps/fetch_tiles.py --preset krakow --url URL --out /Volumes/SDCARD
tools/maps/fetch_tiles.py --bbox 19.90,50.04,19.98,50.08 --zoom 10-16 --url URL --out ./card
tools/maps/fetch_tiles.py --preset krakow --dry-run # count + size estimate
Tile source: --url (or TILE_URL) with {z}/{x}/{y}. There is deliberately no
default. Check the provider's terms before downloading an area:
- tile.openstreetmap.org forbids offline use and answers with an
"access blocked" image;
- OpenTopoMap (https://a.tile.opentopomap.org/{z}/{x}/{y}.png) tolerates
moderate use -- a town or a trip, not a country;
- keyed providers (MapTiler, Thunderforest, Stadia, ...) put the key in the
URL; their plans say whether offline caching is allowed;
- your own render server (e.g. from an OSM extract) has no limits.
--attribution is written to maps/attribution.txt; the device shows it on the
map, as the data licences require.
The tool stops if the server answers every tile with the same image (a
"blocked" or "API key required" placeholder).
Resumable (existing files are skipped), single connection, throttled.
--manifest also writes maps/manifest.json (the list of tiles), which the
browser simulator (variants/sim/web/lvgl.html) uses to preload them.
"""
import argparse
import json
import math
import os
import sys
import time
import urllib.error
import urllib.request
PRESETS = {
# name: (lon_min, lat_min, lon_max, lat_max, z_min, z_max)
"krakow": (19.90, 50.04, 19.98, 50.08, 10, 16), # Old Town + Kazimierz; test set
"krakow-wide": (19.79, 49.97, 20.22, 50.13, 10, 15),
"tatry": (19.75, 49.15, 20.25, 49.32, 10, 15),
"warszawa": (20.85, 52.10, 21.20, 52.35, 10, 15),
"poland": (14.07, 49.00, 24.15, 54.84, 5, 9), # overview zooms only
}
USER_AGENT = "MeshCore-Solo-map-prep/1.0 (offline tiles for a LoRa handheld; throttled)"
def tile_xy(lon, lat, z):
n = 1 << z
x = int((lon + 180.0) / 360.0 * n)
lat_r = math.radians(max(min(lat, 85.0511), -85.0511))
y = int((1.0 - math.asinh(math.tan(lat_r)) / math.pi) / 2.0 * n)
return min(max(x, 0), n - 1), min(max(y, 0), n - 1)
def tiles_for(bbox, z_min, z_max):
lon_min, lat_min, lon_max, lat_max = bbox
for z in range(z_min, z_max + 1):
x0, y0 = tile_xy(lon_min, lat_max, z) # north-west corner
x1, y1 = tile_xy(lon_max, lat_min, z) # south-east corner
for x in range(x0, x1 + 1):
for y in range(y0, y1 + 1):
yield z, x, y
def main():
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
ap.add_argument("--preset", choices=sorted(PRESETS))
ap.add_argument("--bbox", help="lon_min,lat_min,lon_max,lat_max")
ap.add_argument("--zoom", help="z_min-z_max (default from preset, else 10-15)")
ap.add_argument("--out", default=".", help="folder that gets maps/ (e.g. the SD card root)")
ap.add_argument("--url", default=os.environ.get("TILE_URL"), help="tile URL template with {z} {x} {y}")
ap.add_argument("--attribution", default="\u00a9 OpenStreetMap contributors",
help="credit line shown on the device's map")
ap.add_argument("--delay", type=float, default=1.0, help="seconds between requests (default 1)")
ap.add_argument("--manifest", action="store_true", help="also write maps/manifest.json")
ap.add_argument("--dry-run", action="store_true")
a = ap.parse_args()
if a.preset:
*bbox, z_min, z_max = PRESETS[a.preset]
elif a.bbox:
bbox = [float(v) for v in a.bbox.split(",")]
z_min, z_max = 10, 15
else:
ap.error("give --preset or --bbox")
if a.zoom:
z_min, z_max = (int(v) for v in a.zoom.split("-"))
tiles = list(tiles_for(bbox, z_min, z_max))
print(f"{len(tiles)} tiles, z{z_min}-{z_max}, ~{len(tiles) * 25 // 1024} MB (at ~25 KB/tile)")
if a.dry_run:
return
if not a.url:
ap.error("give --url (see the provider notes in --help)")
root = os.path.join(a.out, "maps")
os.makedirs(root, exist_ok=True)
with open(os.path.join(root, "attribution.txt"), "w", encoding="utf-8") as f:
f.write(a.attribution + "\n")
got = skipped = failed = 0
seen = {} # payload -> count, over the first downloads: a placeholder repeats
for i, (z, x, y) in enumerate(tiles, 1):
path = os.path.join(root, str(z), str(x), f"{y}.png")
if os.path.exists(path) and os.path.getsize(path) > 0:
skipped += 1
continue
os.makedirs(os.path.dirname(path), exist_ok=True)
url = a.url.format(z=z, x=x, y=y)
for attempt in range(3):
try:
req = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(req, timeout=20) as r:
data = r.read()
if got < 8:
seen[data] = seen.get(data, 0) + 1
if seen[data] >= 4:
sys.exit("stopped: the server returns the same image for different tiles "
"(blocked or API key required?) -- check --url; delete what was written")
with open(path + ".tmp", "wb") as f:
f.write(data)
os.replace(path + ".tmp", path)
got += 1
break
except (urllib.error.URLError, TimeoutError) as e:
if attempt == 2:
failed += 1
print(f" failed {z}/{x}/{y}: {e}", file=sys.stderr)
time.sleep(2 * (attempt + 1))
if i % 50 == 0:
print(f" {i}/{len(tiles)}")
time.sleep(a.delay)
if a.manifest:
listed = []
for dirpath, _, files in os.walk(root):
for fn in files:
if fn.endswith(".png") or fn == "attribution.txt":
listed.append(os.path.relpath(os.path.join(dirpath, fn), root).replace(os.sep, "/"))
with open(os.path.join(root, "manifest.json"), "w") as f:
json.dump(sorted(listed), f)
print(f"done: {got} downloaded, {skipped} already there, {failed} failed -> {root}")
if __name__ == "__main__":
main()
+24 -1
View File
@@ -12,6 +12,10 @@
cd variants/sim/web && python3 -m http.server 8080 cd variants/sim/web && python3 -m http.server 8080
and open http://localhost:8080/lvgl.html . and open http://localhost:8080/lvgl.html .
Map: tiles in web/maps (tools/maps/fetch_tiles.py --preset krakow --out
variants/sim/web --manifest) are copied into the L2's /sdcard/maps at boot,
and its GPS is put on Kraków's Old Town.
The mouse is the finger: press / drag / release on the LCD. Holding a key The mouse is the finger: press / drag / release on the LCD. Holding a key
on the on-screen keyboard opens its accent popup, as on the device. on the on-screen keyboard opens its accent popup, as on the device.
@@ -144,12 +148,31 @@
inst.call('sim_test_disable_screen_timeout', 'number'); inst.call('sim_test_disable_screen_timeout', 'number');
} }
// Map tiles listed in maps/manifest.json -> the instance's /sdcard/maps.
async function loadMaps(inst) {
let list;
try { list = await (await fetch('maps/manifest.json')).json(); }
catch (e) { log('no maps/manifest.json (map will be empty)'); return; }
const FS = inst.mod.FS;
await Promise.all(list.map(async (rel) => {
const buf = new Uint8Array(await (await fetch('maps/' + rel)).arrayBuffer());
const path = '/sdcard/maps/' + rel;
FS.mkdirTree(path.substring(0, path.lastIndexOf('/')));
FS.writeFile(path, buf);
}));
log('map: ' + list.length + ' tiles loaded');
}
async function bootL() { async function bootL() {
document.getElementById('status-L').textContent = 'booting…'; document.getElementById('status-L').textContent = 'booting…';
if (L) L.call('sim_stop_main_loop', null); if (L) L.call('sim_stop_main_loop', null);
L = wrap(await MeshCoreSimLvgl({ simInstanceTag: 'L' }), 'L'); L = wrap(await MeshCoreSimLvgl({ simInstanceTag: 'L' }), 'L');
const ok = await waitReady(L); const ok = await waitReady(L);
if (ok) prepare(L); if (ok) {
prepare(L);
L.call('sim_location_set', null, ['number', 'number', 'number'], [50.0614, 19.9372, 220]);
await loadMaps(L);
}
document.getElementById('status-L').textContent = ok ? 'ready' : 'boot timed out'; document.getElementById('status-L').textContent = ok ? 'ready' : 'boot timed out';
return ok; return ok;
} }