#pragma once #include #include // ── Scalable mini-icons ────────────────────────────────────────────────────── // Small procedural glyphs (delivery markers, etc.) authored on a 1× pixel grid // and scaled to the current font, so they stay legible on large-font layouts // (e.g. landscape e-ink renders text at 2×). Distinct from the XBM page icons // further down, which are fixed-size full bitmaps drawn via drawXbm(). // // To add a mini-icon: // 1. Draw it as ASCII-art rows, one string per row (width ≤ 8): any char // other than ' ' or '.' is a filled pixel. packRow() packs each string // into one byte at compile time, so the strings never reach flash — the // binary holds only the packed bytes, identical to hand-written hex. // 2. Wrap the rows in a MINI_ICON(name, width, ...) — width and row count are // bundled with the data, so the draw site carries no magic numbers. // 3. Draw it with miniIconDraw(display, x, topY, name). // Scaling is automatic — no per-display handling needed. // // packRow() is written as a single-expression recursion so it stays a valid // constant expression on any C++ standard the firmware targets (≥ C++11), not // just the relaxed C++14 constexpr with loops. It packs up to the string's NUL // (capped at 8 cols), so width lives once in MINI_ICON, not in every row. constexpr uint8_t packBit(const char* s, int x) { return (s[x] && s[x] != ' ' && s[x] != '.') ? (uint8_t)(1u << x) : 0; } constexpr uint8_t packRow(const char* s, int x = 0) { return (!s[x] || x >= 8) ? 0 : (uint8_t)(packBit(s, x) | packRow(s, x + 1)); } // A mini-icon bundles its pixel data with its dimensions, so call sites can't // pass a stale width/height. Built via the MINI_ICON macro below. struct MiniIcon { uint8_t w, h; const uint8_t* rows; }; // Define a mini-icon: the row count (height) is derived from the initializer, // the width is stated once. Emits a packed byte array plus a MiniIcon view. #define MINI_ICON(name, width, ...) \ static constexpr uint8_t name##_rows[] = { __VA_ARGS__ }; \ static constexpr MiniIcon name = { (uint8_t)(width), \ (uint8_t)sizeof(name##_rows), name##_rows } // Pixel scale from the font: 1× on an 8px OLED line, 2× on a 16px landscape // e-ink line, etc. Bitmaps are authored on the 1× grid. inline int miniIconScale(DisplayDriver& d) { int s = d.getLineHeight() / 8; return s < 1 ? 1 : s; } // Draw a w×h (w ≤ 8) bitmap with the current ink colour, scaled by the font and // vertically centred in the text line that starts at top_y. inline void miniIconDraw(DisplayDriver& d, int x, int top_y, const uint8_t* rows, int w, int h) { const int s = miniIconScale(d); int y = top_y + (d.getLineHeight() - h * s) / 2; if (y < top_y) y = top_y; for (int r = 0; r < h; r++) for (int c = 0; c < w; c++) if (rows[r] & (1 << c)) d.fillRect(x + c * s, y + r * s, s, s); } // Preferred overload: dimensions travel with the icon — no magic numbers. inline void miniIconDraw(DisplayDriver& d, int x, int top_y, const MiniIcon& ic) { miniIconDraw(d, x, top_y, ic.rows, ic.w, ic.h); } // Draw a mini-icon at an exact top-left (no vertical centring). Used where the // caller controls placement, e.g. flush to the top/bottom of a scroll track. inline void miniIconDrawTop(DisplayDriver& d, int x, int y, const MiniIcon& ic) { const int s = miniIconScale(d); for (int r = 0; r < ic.h; r++) for (int c = 0; c < ic.w; c++) if (ic.rows[r] & (1 << c)) d.fillRect(x + c * s, y + r * s, s, s); } // Draw a mini-icon centred on a point (scaled) — used for map markers, which // are positioned by their centre rather than a text-line top. inline void miniIconDrawCentered(DisplayDriver& d, int cx, int cy, const MiniIcon& ic) { const int s = miniIconScale(d); miniIconDrawTop(d, cx - (ic.w * s) / 2, cy - (ic.h * s) / 2, ic); } // Centre a mini-icon inside the slot [x, 0, box_w, box_h] using the current ink // colour (no background). Centres on the box itself, not the text line, so the // glyph sits dead-centre regardless of font line height. inline void drawSlotIcon(DisplayDriver& d, int x, int box_w, int box_h, const MiniIcon& ic) { const int s = miniIconScale(d); int ix = x + (box_w - ic.w * s) / 2; int iy = (box_h - ic.h * s) / 2; if (ix < x) ix = x; if (iy < 0) iy = 0; miniIconDrawTop(d, ix, iy, ic); } // Inverted status indicator: fill the box [x, 0, box_w, box_h] LIGHT, draw the // glyph DARK and centred, then restore ink to LIGHT. inline void drawBoxedIcon(DisplayDriver& d, int x, int box_w, int box_h, const MiniIcon& ic) { d.setColor(DisplayDriver::LIGHT); d.fillRect(x, 0, box_w, box_h); d.setColor(DisplayDriver::DARK); drawSlotIcon(d, x, box_w, box_h, ic); d.setColor(DisplayDriver::LIGHT); } // Horizontal row of `count` square dots (scaled, vertically centred). Used by // the "awaiting ACK" marker, where the dot count = number of send attempts. inline void miniIconDotRow(DisplayDriver& d, int x, int top_y, int count) { const int s = miniIconScale(d); const int dot = 2 * s, pitch = 3 * s; // 2px dot + 1px gap, scaled int y = top_y + (d.getLineHeight() - dot) / 2; if (y < top_y) y = top_y; for (int i = 0; i < count; i++) d.fillRect(x + i * pitch, y, dot, dot); } // Mini-icon bitmaps (authored on the 1× grid as ASCII-art; see packRow above). MINI_ICON(ICON_CHECK, 5, // ✓ packRow("....#"), packRow("...#."), packRow("#.#.."), packRow(".#...")); MINI_ICON(ICON_CROSS, 4, // ✗ packRow("#..#"), packRow(".##."), packRow(".##."), packRow("#..#")); // Top-bar status glyphs (replace the single-letter M / B / A indicators). MINI_ICON(ICON_MUTE, 6, // speaker + cross (sound off) packRow("..#..."), packRow(".##..."), packRow("####.#"), packRow("###.#."), packRow("####.#"), packRow(".##..."), packRow("..#...")); MINI_ICON(ICON_BLUETOOTH, 5, // ᛒ bluetooth rune packRow("..#.."), packRow("..##."), packRow("#.#.#"), packRow(".###."), packRow("#.#.#"), packRow("..##."), packRow("..#..")); MINI_ICON(ICON_ADVERT, 6, // broadcast mast + radiating waves (auto-advert) packRow("#....#"), packRow(".#..#."), packRow("..##.."), packRow("..##.."), packRow(".####."), packRow(".####.")); MINI_ICON(ICON_ALARM, 5, // bell — an alarm is armed packRow("..#.."), packRow(".###."), packRow(".###."), packRow(".###."), packRow("#####"), packRow("..#..")); MINI_ICON(ICON_TRAIL, 6, // map pin / location marker (GPS trail logging) packRow(".####."), packRow("######"), packRow("##..##"), packRow("######"), packRow(".####."), packRow("..##..")); MINI_ICON(ICON_REPEATER, 6, // » double chevron — relaying/forwarding (repeater active) packRow("#..#.."), packRow(".#..#."), packRow("..#..#"), packRow(".#..#."), packRow("#..#..")); MINI_ICON(ICON_GPS, 5, // reticle — GPS fix status (boxed = fix, plain = searching) packRow(".###."), packRow("#...#"), packRow("#.#.#"), packRow("#...#"), packRow(".###.")); // Tools-menu glyphs (auto-reply bot, ringtone editor, diagnostics, system). MINI_ICON(ICON_BOT, 5, // robot head: antenna + eyes + grille (auto-reply bot) packRow("..#.."), packRow("#####"), packRow("#.#.#"), packRow("#####"), packRow("#.#.#"), packRow("#####")); MINI_ICON(ICON_NOTE, 5, // ♪ quaver — ringtone editor packRow("...##"), packRow("...##"), packRow("...#."), packRow("...#."), packRow("...#."), packRow("####."), packRow("####.")); MINI_ICON(ICON_CHART, 5, // ascending bars — diagnostics / stats packRow("....#"), packRow("..#.#"), packRow("..#.#"), packRow("#.#.#"), packRow("#.#.#"), packRow("#####")); MINI_ICON(ICON_GEAR, 7, // ⚙ cog with hub hole — system packRow("..#.#.."), packRow(".#####."), packRow("#######"), packRow(".##.##."), packRow("#######"), packRow(".#####."), packRow("..#.#..")); // Home-carousel page glyphs — a uniform 5x5 set, deliberately smaller than the // menu/status icons above, used in place of the page-indicator dots. One per // HomePage; see UITask HomeScreen::pageIcon(). MINI_ICON(ICON_PG_CLOCK, 5, // clock face + hands packRow(".###."), packRow("#.#.#"), packRow("#.###"), packRow("#...#"), packRow(".###.")); MINI_ICON(ICON_PG_STAR, 5, // favourites packRow("..#.."), packRow("#####"), packRow(".###."), packRow("##.##"), packRow("#...#")); MINI_ICON(ICON_PG_RECENT, 5, // stacked lines — recent list packRow("#####"), packRow("....."), packRow("#####"), packRow("....."), packRow("#####")); MINI_ICON(ICON_PG_RADIO, 5, // antenna tower — radio params packRow("..#.."), packRow(".###."), packRow("..#.."), packRow(".#.#."), packRow("#...#")); MINI_ICON(ICON_PG_BT, 5, // bluetooth (compact) packRow("..#.."), packRow("#.##."), packRow(".###."), packRow("#.##."), packRow("..#..")); MINI_ICON(ICON_PG_ADVERT, 5, // mast + radiating waves — advert packRow("#...#"), packRow(".#.#."), packRow("..#.."), packRow("..#.."), packRow("..#..")); MINI_ICON(ICON_PG_GPS, 5, // location pin — GPS packRow(".###."), packRow("#.#.#"), packRow("#.#.#"), packRow(".#.#."), packRow("..#..")); MINI_ICON(ICON_PG_SENSORS, 5, // thermometer/gauge — sensors packRow("..#.."), packRow(".#.#."), packRow(".#.#."), packRow(".###."), packRow(".###.")); MINI_ICON(ICON_PG_SETTINGS, 5, // small cog — settings packRow(".#.#."), packRow("#####"), packRow("##.##"), packRow("#####"), packRow(".#.#.")); MINI_ICON(ICON_PG_MAP, 5, // folded map — map page packRow("#####"), packRow("#.#.#"), packRow("#.#.#"), packRow("#.#.#"), packRow("#####")); MINI_ICON(ICON_PG_TOOLS, 5, // wrench (open jaw + handle) — tools packRow(".#.#."), packRow(".###."), packRow("..#.."), packRow("..#.."), packRow("..#..")); MINI_ICON(ICON_PG_MSG, 5, // speech bubble — quick messages packRow("#####"), packRow("#...#"), packRow("#...#"), packRow("#####"), packRow(".#...")); MINI_ICON(ICON_PG_POWER, 5, // power symbol — shutdown packRow("..#.."), packRow("#.#.#"), packRow("#...#"), packRow("#...#"), packRow(".###.")); // Trail-map markers — centred on a point (see miniIconDrawCentered) rather // than anchored to a text line. MINI_ICON(ICON_MAP_DOT, 3, // ● filled trail point packRow("###"), packRow("###"), packRow("###")); MINI_ICON(ICON_MAP_RING, 3, // ○ hollow ring — marks a new trail segment start packRow("###"), packRow("#.#"), packRow("###")); MINI_ICON(ICON_MAP_WAYPOINT, 5, // ◇ hollow diamond — saved waypoint packRow("..#.."), packRow(".#.#."), packRow("#...#"), packRow(".#.#."), packRow("..#..")); MINI_ICON(ICON_MAP_START, 5, // + trail start marker packRow("..#.."), packRow("..#.."), packRow("#####"), packRow("..#.."), packRow("..#..")); MINI_ICON(ICON_MAP_CURRENT, 5, // ✕ live position / last trail point packRow("#...#"), packRow(".#.#."), packRow("..#.."), packRow(".#.#."), packRow("#...#")); MINI_ICON(ICON_MAP_CONTACT, 5, // ◆ filled diamond — a live-tracked contact ([LOC] share) packRow("..#.."), packRow(".###."), packRow("#####"), packRow(".###."), packRow("..#..")); MINI_ICON(ICON_MAP_NORTH, 5, // "N" with a peaked roof — compass north marker packRow("..#.."), packRow(".###."), packRow("#...#"), packRow("##..#"), packRow("#.#.#"), packRow("#..##"), packRow("#...#")); MINI_ICON(ICON_MAP_ARROW, 5, // → distance-to-nearest-tracked-contact indicator packRow("..#.."), packRow("...#."), packRow("#####"), packRow("...#."), packRow("..#..")); MINI_ICON(ICON_MAP_TARGET, 5, // ⚑ flag on a pole — the active Locator/Nav target packRow("####."), packRow("#..#."), packRow("####."), packRow("#...."), packRow("#....")); // Keyboard special-key glyphs. MINI_ICON(ICON_SHIFT, 7, // ⇧ caps packRow("...#..."), packRow("..###.."), packRow(".#####."), packRow("#######"), packRow("..###.."), packRow("..###.."), packRow("..###..")); MINI_ICON(ICON_BACKSPACE, 8, // ⌫ delete-left (× knocked out of the arrow body) packRow("...#####"), packRow("..######"), packRow(".###.#.#"), packRow("#####.##"), packRow(".###.#.#"), packRow("..######"), packRow("...#####")); // Space ⎵ is wider than the 8-px mini-icon limit, so it is two halves drawn // side by side (offset by ICON_SPACE_L.w * scale): ticks at both far ends + bar. MINI_ICON(ICON_SPACE_L, 8, packRow("#......."), packRow("#......."), packRow("#......."), packRow("########")); MINI_ICON(ICON_SPACE_R, 8, packRow(".......#"), packRow(".......#"), packRow(".......#"), packRow("########")); // Scroll-indicator caps — small up/down triangles (authored on the 1× grid). MINI_ICON(ICON_SCROLL_UP, 5, // ▲ packRow("..#.."), packRow(".###."), packRow("#####")); MINI_ICON(ICON_SCROLL_DOWN, 5, // ▼ packRow("#####"), packRow(".###."), packRow("..#..")); // Width of the right-edge column drawScrollIndicator occupies, or 0 when the // list fits and no indicator is drawn. Subtract from a row's content width so // text never runs under the scrollbar. Mirrors the column math below (5*scale // triangle + 1px gap). inline int scrollIndicatorColWidth(DisplayDriver& d) { return 5 * miniIconScale(d) + 1; // triangle (5*scale) + 1px gap } inline int scrollIndicatorReserve(DisplayDriver& d, int total, int visible) { return (total > visible) ? scrollIndicatorColWidth(d) : 0; } // Right-edge scroll indicator: a proportional track + thumb topped/tailed with // up/down triangle mini-icon caps. Drop-in replacement for // DisplayDriver::drawScrollArrows that also shows how much of the list is on // screen and where you are within it. Everything lives in a ~5px column at the // right edge and scales with the font (mini-icon scale). // top_y : y of the first visible row (top of the viewport) // track_h : pixel height of the viewport (e.g. visible_rows * row_pitch) // // Core takes the proportions in arbitrary units. For uniform-height lists pass // item counts (see the item wrapper below). For variable-height lists (e.g. the // portrait DM history, where each message box wraps to a different height) pass // pixel sums — total_px = Σ all box heights, view_px = pixels currently on // screen, scroll_px = pixels above the first visible box — so the thumb size and // position track real content extent instead of jumping as box counts fluctuate. // total_px : total extent of the whole list (items or pixels) // view_px : extent currently visible // scroll_px : extent scrolled past (offset of the first visible item) // right_x : column origin's right edge, in screen pixels (the indicator // occupies [right_x - col, right_x)). Full-screen lists pass // d.width(); a narrower container (e.g. a centred popup) passes its // own right edge so the indicator lands inside the box, not the screen. inline void drawScrollIndicatorPx(DisplayDriver& d, int right_x, int top_y, int track_h, long total_px, long view_px, long scroll_px) { if (total_px <= view_px || track_h <= 0) return; // whole list fits — no indicator const long span = total_px - view_px; // > 0 here if (scroll_px < 0) scroll_px = 0; if (scroll_px > span) scroll_px = span; const int s = miniIconScale(d); const int col = 5 * s; // triangle / column width const int th = 3 * s; // triangle height const int x = right_x - col; // indicator column origin // Caps are static end-markers, always drawn flush at the very top / bottom of // the track; the thumb travels in the fixed band between them. (They mark the // track ends, not "more above/below", so they never vanish at the extremes.) const int cap = th + 2; // triangle height + 2px gap const int band_t = top_y + cap; const int band_b = top_y + track_h - cap; int band = band_b - band_t; if (band < s) band = s; const int bar_w = 3 * s; // odd width → centres exactly in the 5*s column const int bar_x = x + (col - bar_w) / 2; int thumb_h = (int)((long)band * view_px / total_px); if (thumb_h < th) thumb_h = th; if (thumb_h > band) thumb_h = band; const int thumb_y = band_t + (int)((long)(band - thumb_h) * scroll_px / span); // No halo: every caller already keeps its selection bar clear of this column // (reserve/_reserve), so the markers never need to fight a LIGHT background // for contrast — and a halo on the bottom arrow had no symmetric clip like // the top one, so it bled 1px into the container's bottom border. d.setColor(DisplayDriver::LIGHT); d.fillRect(bar_x, thumb_y, bar_w, thumb_h); miniIconDrawTop(d, x, top_y, ICON_SCROLL_UP); miniIconDrawTop(d, x, top_y + track_h - th, ICON_SCROLL_DOWN); } // Convenience overload anchored to the screen's right edge — the common case // for full-width lists. inline void drawScrollIndicatorPx(DisplayDriver& d, int top_y, int track_h, long total_px, long view_px, long scroll_px) { drawScrollIndicatorPx(d, d.width(), top_y, track_h, total_px, view_px, scroll_px); } // Uniform-height wrapper: one item = one unit. Drop-in replacement for // DisplayDriver::drawScrollArrows. // total : total number of items // visible : items shown at once // first : index of the first visible item (scroll offset) inline void drawScrollIndicator(DisplayDriver& d, int top_y, int track_h, int total, int visible, int first) { drawScrollIndicatorPx(d, top_y, track_h, total, visible, first); } // right_x variant — see drawScrollIndicatorPx's right_x for why a narrower // container (e.g. a popup) needs this instead of the screen-edge default. inline void drawScrollIndicator(DisplayDriver& d, int right_x, int top_y, int track_h, int total, int visible, int first) { drawScrollIndicatorPx(d, right_x, top_y, track_h, total, visible, first); } // Scrollable item-list skeleton shared by the list screens. Computes the visible // window from the font metrics, keeps `sel` in view, reserves the scroll-column, // draws each visible row through `row`, then the indicator. The callback // `row(idx, y, sel, reserve)` draws one item — including its own selection bar — // using `reserve` to keep right-aligned content clear of the indicator. Returns // the visible row count (callers cache it for input handling). template inline int drawList(DisplayDriver& d, int total, int sel, int& scroll, RenderRow row) { const int item_h = d.lineStep(); const int start_y = d.listStart(); int visible = d.listVisible(item_h); if (visible < 1) visible = 1; if (sel < scroll) scroll = sel; if (sel >= scroll + visible) scroll = sel - visible + 1; if (scroll < 0) scroll = 0; const int reserve = scrollIndicatorReserve(d, total, visible); for (int i = 0; i < visible && (scroll + i) < total; i++) row(scroll + i, start_y + i * item_h, scroll + i == sel, reserve); drawScrollIndicator(d, start_y, visible * item_h, total, visible, scroll); return visible; } // Canonical selection bar for a drawList() row: spans the row width minus the // scroll-indicator `reserve`, one pixel short of the row height, anchored one // pixel above `y` (the row's text baseline-top). Call as the first line of a // row callback, then draw content over it. Captures the geometry every list row // repeated by hand; rows that intentionally differ (full-width, custom height) // still call display.drawSelectionRow() directly. inline void drawRowSelection(DisplayDriver& d, int y, bool sel, int reserve) { d.drawSelectionRow(0, y - 1, d.width() - reserve, d.lineStep() - 1, sel); } // ── Big ASCII-art icons (skeleton, not yet used) ───────────────────────────── // Same authoring idea as the mini-icons but for full page glyphs up to 32 px // wide: one uint32_t per row. The existing XBM bitmaps below (logo/bluetooth/…) // stay hand-encoded; reach for this when adding a *new* big icon so it's // readable in source. Drawn at 1× (page icons aren't font-scaled). // // To add one: // BIG_ICON(MY_ICON, 16, // packRow32("......####......"), // packRow32(".....######....."), // ...); // bigIconDraw(display, x, y, MY_ICON); constexpr uint32_t packBit32(const char* s, int x) { return (s[x] && s[x] != ' ' && s[x] != '.') ? (1u << x) : 0u; } constexpr uint32_t packRow32(const char* s, int x = 0) { return (!s[x] || x >= 32) ? 0u : (packBit32(s, x) | packRow32(s, x + 1)); } struct BigIcon { uint8_t w, h; const uint32_t* rows; }; #define BIG_ICON(name, width, ...) \ static constexpr uint32_t name##_rows[] = { __VA_ARGS__ }; \ static constexpr BigIcon name = { \ (uint8_t)(width), \ (uint8_t)(sizeof(name##_rows) / sizeof(uint32_t)), name##_rows } // Draw a packed big icon at 1× with the current ink colour, top-left at (x, y). inline void bigIconDraw(DisplayDriver& d, int x, int y, const BigIcon& ic) { for (int r = 0; r < ic.h; r++) for (int c = 0; c < ic.w; c++) if (ic.rows[r] & (1u << c)) d.fillRect(x + c, y + r, 1, 1); } // 'meshcore', 128x13px static const uint8_t meshcore_logo [] = { 0x3c, 0x01, 0xe3, 0xff, 0xc7, 0xff, 0x8f, 0x03, 0x87, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x1f, 0xfe, 0x3c, 0x03, 0xe3, 0xff, 0xc7, 0xff, 0x8e, 0x03, 0x8f, 0xfe, 0x3f, 0xfe, 0x1f, 0xff, 0x1f, 0xfe, 0x3e, 0x03, 0xc3, 0xff, 0x8f, 0xff, 0x0e, 0x07, 0x8f, 0xfe, 0x7f, 0xfe, 0x1f, 0xff, 0x1f, 0xfc, 0x3e, 0x07, 0xc7, 0x80, 0x0e, 0x00, 0x0e, 0x07, 0x9e, 0x00, 0x78, 0x0e, 0x3c, 0x0f, 0x1c, 0x00, 0x3e, 0x0f, 0xc7, 0x80, 0x1e, 0x00, 0x0e, 0x07, 0x1e, 0x00, 0x70, 0x0e, 0x38, 0x0f, 0x3c, 0x00, 0x7f, 0x0f, 0xc7, 0xfe, 0x1f, 0xfc, 0x1f, 0xff, 0x1c, 0x00, 0x70, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, 0x7f, 0x1f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x0e, 0x38, 0x0e, 0x3f, 0xf8, 0x7f, 0x3f, 0xc7, 0xfe, 0x0f, 0xff, 0x1f, 0xff, 0x1c, 0x00, 0xf0, 0x1e, 0x3f, 0xfe, 0x3f, 0xf0, 0x77, 0x3b, 0x87, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xfc, 0x38, 0x00, 0x77, 0xfb, 0x8f, 0x00, 0x00, 0x07, 0x1c, 0x0f, 0x3c, 0x00, 0xe0, 0x1c, 0x7f, 0xf8, 0x38, 0x00, 0x73, 0xf3, 0x8f, 0xff, 0x0f, 0xff, 0x1c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x78, 0x7f, 0xf8, 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfe, 0x3c, 0x0e, 0x3f, 0xf8, 0xff, 0xfc, 0x70, 0x3c, 0x7f, 0xf8, 0xe3, 0xe3, 0x8f, 0xff, 0x1f, 0xfc, 0x3c, 0x0e, 0x1f, 0xf8, 0xff, 0xf8, 0x70, 0x3c, 0x7f, 0xf8, }; static const uint8_t bluetooth_on[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x3B, 0xE0, 0x00, 0x30, 0x38, 0xF8, 0x00, 0x3C, 0x38, 0x7C, 0x00, 0x3E, 0x38, 0x7C, 0x00, 0x1F, 0xB8, 0xF8, 0x70, 0x07, 0xF9, 0xF0, 0x78, 0x03, 0xFF, 0xC0, 0x78, 0x00, 0xFF, 0x80, 0x3C, 0x00, 0x7F, 0x07, 0x1C, 0x00, 0x7E, 0x07, 0x1C, 0x03, 0xFF, 0x82, 0x1C, 0x03, 0xFF, 0xC0, 0x78, 0x07, 0xFB, 0xE0, 0x78, 0x0F, 0xB8, 0xF8, 0x70, 0x3E, 0x38, 0x7C, 0x00, 0x3C, 0x38, 0x7C, 0x00, 0x38, 0x38, 0xF8, 0x00, 0x00, 0x39, 0xF0, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const uint8_t bluetooth_off[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x38, 0x03, 0xF8, 0x00, 0x3C, 0x03, 0xFC, 0x00, 0x3E, 0x03, 0xBF, 0x00, 0x0F, 0x83, 0x8F, 0x80, 0x07, 0xC3, 0x87, 0xC0, 0x03, 0xF0, 0x03, 0xC0, 0x00, 0xF8, 0x0F, 0x80, 0x00, 0x7C, 0x0F, 0x00, 0x00, 0x1F, 0x0E, 0x00, 0x00, 0x0F, 0x80, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x0F, 0xF8, 0x00, 0x00, 0x3F, 0xBE, 0x00, 0x00, 0x7F, 0x9F, 0x00, 0x00, 0xFB, 0x8F, 0xC0, 0x03, 0xE3, 0x83, 0xE0, 0x03, 0xC3, 0x87, 0xF0, 0x03, 0x83, 0x8F, 0xFC, 0x00, 0x03, 0xBF, 0x3C, 0x00, 0x03, 0xFC, 0x1C, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; static const uint8_t power_icon[] = { 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x03, 0xC0, 0x00, 0x00, 0x33, 0xCC, 0x00, 0x00, 0xF3, 0xCF, 0x00, 0x01, 0xF3, 0xCF, 0x80, 0x03, 0xF3, 0xCF, 0xC0, 0x07, 0xF3, 0xCF, 0xE0, 0x0F, 0xE3, 0xC7, 0xF0, 0x1F, 0xC3, 0xC3, 0xF8, 0x1F, 0x83, 0xC1, 0xF8, 0x3F, 0x03, 0xC0, 0xFC, 0x3E, 0x03, 0xC0, 0x7C, 0x3E, 0x03, 0xC0, 0x7C, 0x7E, 0x01, 0x80, 0x7E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x7C, 0x00, 0x00, 0x3E, 0x3E, 0x00, 0x00, 0x7C, 0x3E, 0x00, 0x00, 0x7C, 0x3F, 0x00, 0x00, 0xFC, 0x1F, 0x80, 0x01, 0xF8, 0x1F, 0xC0, 0x03, 0xF8, 0x0F, 0xE0, 0x07, 0xF0, 0x0F, 0xF8, 0x1F, 0xF0, 0x07, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xC0, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x0F, 0xF0, 0x00, }; static const uint8_t advert_icon[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x30, 0x1C, 0x00, 0x00, 0x38, 0x18, 0x00, 0x00, 0x18, 0x30, 0x00, 0x00, 0x0C, 0x30, 0x60, 0x06, 0x0C, 0x60, 0xE0, 0x07, 0x06, 0x61, 0xC0, 0x03, 0x86, 0xE1, 0x81, 0x81, 0x87, 0xC3, 0x07, 0xE0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x0F, 0xF0, 0xC3, 0xC3, 0x07, 0xE0, 0xC3, 0xC1, 0x83, 0xC1, 0x83, 0x61, 0x80, 0x01, 0x86, 0x60, 0xC0, 0x03, 0x06, 0x70, 0xE0, 0x07, 0x0E, 0x30, 0x40, 0x02, 0x0C, 0x38, 0x00, 0x00, 0x1C, 0x18, 0x00, 0x00, 0x18, 0x0C, 0x00, 0x00, 0x30, 0x04, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, };