Board Support:
- Fri3d 2026 badge: B/Y buttons now also reach apps as ord("B")/ord("Y") KEY events (dual delivery), in addition to their NEXT/PREV focus-navigation role. Previously LVGL consumed NEXT/PREV for tabbing so apps never saw B/Y (e.g. Quasi Bird's FPS toggle and high-score reset did nothing). Tab navigation is unchanged; matches the fri3d 2024 badge and desktop SDL.
Frameworks:
- qrdecode: faster QR scanning with bit-identical results. quirc rounds perspective-mapped points without a rintf() library call where there is no rounding instruction (ESP32) and scans for finder patterns run by run; the decoder (about 400 KB at 640x640) is kept between frames instead of being allocated and freed for every frame, and the camera frame is read in place instead of copied. New qrdecode.release() frees the decoder; the Camera activity calls it when QR scanning stops or the activity is paused
- qrdecode: exceptions from qrdecode_rgb565() are now caught by the caller's except clause; they used to skip it (and could leave the caller's frame in a broken state) because the error path popped the caller's exception handler as well as its own
- qrdecode: the decoder kept while QR scanning is on comes entirely from PSRAM. quirc's flood-fill stack (about 7 KB at 640x640) used to come from internal RAM, which the display and camera DMA buffers also need. quirc's ps_malloc() passed its capability flags where heap_caps_malloc_prefer() expects their count, and it now prefers PSRAM on every ESP32 target, not only on Xtensa
- qrdecode: a frame size whose pixel count overflows a 32-bit int raises ValueError. On the ESP32 such a size used to pass the buffer size check, and quirc then scanned rows beyond its buffer
- Camera: stopping the camera twice, for example when the activity is paused again, no longer deletes the capture timer twice
- qrdecode: reads QR codes whose finder patterns have rounded corners, as some apps draw them (#313). quirc takes the extreme pixels of a finder pattern's ring as its corners, and on a rounded ring those lie on the arcs. When no grid decodes and there are three or more finder patterns, the new quirc_retry_rounded() fits the corners to the rings' straight edges and groups them again, so frames that decode today do the same work as before. qrdecode also tries every grid quirc finds instead of only the first, each one also mirrored
- qrdecode: quirc's alignment pattern search never looks at more points than the frame has pixels. A bad finder pattern grouping could make it run for millions of steps (dlbeer/quirc#162)
- MposKeyboard: set_textarea() hooks a textarea's click once, so an activity that moves the keyboard between several fields no longer stacks a show-keyboard handler (and animation) per switch
- secp256k1: Schnorr signature checks work in builds whose frozen modules are compiled with mpy-cross -O3 (macOS desktop, web). PublicKey computed its x-only key inside an assert, which -O3 removes, so with the frozen copy every good signature failed to verify and Nostr dropped every relay event
- SensorManager: IMU reads no longer stall on the port's default I2C transaction timeout on every call when the bus is held up. The boards that serve the IMU now construct their sensor bus with a 20 ms timeout (IMU_I2C_TIMEOUT_US), the four-chip IMU probe is retried at most once per 5 s instead of on every sensor access once it has failed to find a chip (IMU_PROBE_RETRY_MS), and a failed read no longer prints a traceback. Reported on the Fri3d 2026 badge (#294)
- WifiService: when the scan straight after a radio restart finds nothing, it scans once more 5 s later (#309). On an ESP32-P4 whose ESP32-C6 still ran its original firmware, that first scan found nothing although networks were in range, so auto_connect() could miss its network after a drop and switch the radio off again; a scan 5 s later found them. The wait only happens when that scan is empty, never while connected, and also applies to the Wi-Fi app's scan
Builtin Apps:
- Nostr Chat: private group chats (NIP-17 chat rooms). "New chat" gets a "Group" mode: up to 9 other members (NIP-17 rooms hold at most 10 people, you included; npubs, hex keys or names from the profile cache, comma or newline separated; your own key is dropped and duplicates merged) plus an optional subject. The subject is sent with every message, the newest incoming subject renames the chat, and group rows and headers show the subject or, failing that, the members' names. Rooms of 4 or more members are stored under a hashed file name (the room id alone is longer than a file name may be). Not yet done: wraps still go to your own relays rather than each member's NIP-17 inbox relays (kind 10050), and relays that require NIP-42 AUTH are not supported
- Nostr Chat: every NIP-17 message (DMs too) is now also gift-wrapped to yourself, as NIP-17 requires, so your other Nostr clients see what you sent from the device; the local copy is stored under that wrap before anything is published, so its relay echo (which can arrive while a group send is still encrypting the other wraps) doesn't duplicate, also for messages flushed from the offline outbox. The NIP-17 subscription now looks back 4 days (wraps are dated up to 2 days before their message) and fetches up to 100 wraps, since your own copies share that budget. With the matching micropython-nostr update the rumor carries the
idNIP-17 requires and seal/wrap timestamps are backdated independently (NIP-59); the app computes the id itself on the older library, and always dates seals and wraps from the Nostr clock itself (the library defaults read time.time(), which counts from 2000 on the ESP32) - Nostr Chat: a group send yields between recipients instead of starving the relay connections, the Send button shows "Encrypting 2/5" meanwhile, and a gift wrap that fails to publish is logged instead of aborting the rest. Leaving the chat during a send no longer touches the deleted screen. Offline-queued group messages keep their subject, are flushed from a background task (one wrap per member costs seconds on an ESP32) and leave the outbox one by one, so a failure later in the queue doesn't re-send the earlier ones
- Nostr Chat: a chat opened from "New chat" (DM, group or channel) stays open. The new-chat screen finished itself after starting the chat, which popped the chat off the activity stack and left an empty screen
- Nostr Chat: decrypted private-message content is no longer written to the log at INFO level
- Nostr Chat: a NIP-17 gift wrap that is already stored is no longer unwrapped again. Each unwrap costs two ECDH, two decryptions and the seal's signature check (well over 0.1 s on an ESP32), and every app start re-fetches up to 100 wraps from the last 4 days, while every message you send comes back from the relay as your own, already stored, wrap
Testing:
- unittest: an exception raised in setUp, tearDown, a cleanup, setUpClass or tearDownClass, including self.skipTest() or raise unittest.SkipTest, is now reported as a skip or an error instead of aborting the whole test file, which was then reported as failed with its later tests and classes never run. As in CPython, a failed setUp skips the test body and tearDown, a failed setUpClass skips the class's tests and tearDownClass, and cleanups always run, each one even if an earlier one raised
OS:
- Displays (ESP32): tell LVGL a flush is done directly from C in the DMA-done interrupt (new lcd_bus.FlushReadyCallback) instead of running a Python callback inside that interrupt after every flush. Python callbacks that still run there, such as Breakout's, get a zeroed thread state and a stack limit taken from the real interrupt stack, now 2560 bytes, so running out of stack raises RecursionError instead of silently overwriting memory
- ESP32: fix hard resets (stack overflow panics) when an app is started with AppManager.start_app() from the REPL, mpremote, an asyncio task or auto_start_app at boot, seen with BlockTV on the Waveshare ESP32-S3-Touch-LCD-3.5. The LVGL task handler runs as a scheduled callback on top of whatever Python code is executing, so a pass that started while an app built its UI deep in onCreate/onResume overflowed the 16 KB mp_task stack (a redraw needs up to 7.3 KB measured). A pass is now skipped, and retried on the next tick, when less than 9 KB of stack is left (mpos.ui.render_guard)
- binascii/base64: lib/binascii.py no longer shadows the C ubinascii b2a_base64/a2b_base64 with the pure-Python copies from micropython-lib, which were O(n²) and frozen into every firmware. A 3000-byte base64 round-trip takes 0.026 ms instead of 13.6 ms on the desktop build, and everything that base64-encodes benefits: NIP-44/NIP-17 DMs, NWC, aiohttp websockets, webrepl. Incorrect padding now raises ValueError instead of a bare Exception, and str input is accepted
- base64: b64decode(validate=True), b32decode() and b16decode() raise ValueError on invalid input, as documented. They raised AttributeError, because lib/base64.py raises binascii.Error and lib/binascii.py (a shim over ubinascii) never defined it; binascii.Error is now an alias of ValueError, as in CPython, where it is a ValueError subclass. urlsafe_b64decode() is implemented instead of raising NotImplementedError: MicroPython has no bytes.translate(), so it decodes through b64decode(altchars=b"-_"), the counterpart of the already working urlsafe_b64encode()
- base64: the decoders raise ValueError("string argument should contain only ASCII characters") for a str with a non-ASCII character, as in CPython, instead of silently skipping it: urlsafe_b64decode(chr(233)) returned b"" and b64decode("YWJj" + chr(233)) returned b"abc". MicroPython's str.encode("ascii") ignores the codec and returns UTF-8, so the old check never fired. This covers b64decode(), urlsafe_b64decode(), b32decode(), b16decode() and their altchars/map01 arguments
Testing:
- test_graphical_notification_bar: the check that the clock, temperature, WiFi and battery icons are not hidden ran its assertFalse inside
try: ... except Exception, which also caught the AssertionError, so a hidden icon could never fail the test. It is now a real assertion (waiting up to 5 s for the periodic icon timers); the WiFi icon is only required to be visible while WiFi is connected, as it is hidden by design otherwise (e.g. --ondevice without a network) - test_osupdate: five fetch_update_info error tests (HTTP 404, timeout, connection refused, empty response, HTTP 500) called self.fail() inside
try: ... except Exception, which also caught the AssertionError, so they could never fail. They now use assertRaises(ValueError) and check the 'Invalid JSON' message
Board Support:
- Add Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3: first esp32p4 target, MIPI-DSI display, touch, speaker
- Add Waveshare ESP32-S3-Touch-LCD-1.54: 1.54' 240x240 ST7789, CST816S touch, ES8311 speaker + ES7210 microphone, TF card slot
- Waveshare ESP32-S3-Touch-LCD-3.5: keep the ES8311 speaker output warm for 30 s after a clip (warm_ms) so rapid back-to-back clips play without a click
- Fri3d 2024/2026: increase display SPI frequency from 40Mhz to 80Mhz (like Waveshare ESP32-S3-Touch-LCD-3.5) for higher framerate
- ESP32-S3-Touch-LCD-2: increase display SPI frequency from 40Mhz to 80Mhz (like Waveshare ESP32-S3-Touch-LCD-3.5) for higher framerate
Frameworks:
- AudioManager (desktop): RTTTL playback renders one pass of the tune and lets WAVStream repeat it, so a .rtttl file opened in the Music Player (Repeat is on by default) plays straight away instead of silently pre-rendering 1,000,000 repeats into /tmp at ~10 MB/s; set_repeat() now reaches the playing stream
- AudioManager/WAVStream: opt-in warm output (Output(warm_ms=N)) keeps the I2S clocks running and the codec unmuted between clips so back-to-back playback doesn't click on codecs such as the ES8311; released after N ms idle, by WAVStream.release_warm(), or before I2S recording
- InputActivity: optional
selected_callback(value)setting key for radiobuttons/dropdown, fired on every pick before Save (re-tapping the active radio fires again) so pickers can preview a choice live, e.g. play a sound effect.changed_callbacksemantics unchanged - SettingsActivity: fix a TypeError ('can't convert 'int' object to str implicitly', surfaced as the 'app threw an exception' dialog) when a setting's stored value is a number instead of a string
- CameraManager: apply the Manual Exposure, Manual Gain and WB Mode camera settings when their Auto switch is turned off. They were never applied, because the Auto switches always have a value (defaults), and the manual gain check was also inverted
Builtin Apps:
- Optimize PNGs to reduce build size by 7KiB
- Settings: the Notification sound picker plays each option as you tap it (via InputActivity selected_callback), so you can hear a sound before saving
OS:
- uaiowebsocket: stop trying to send a second pong on incoming pings (aiohttp already replies; the call raised and logged 'Failed to send pong' on every relay ping, #299)
Testing:
- mpos_controller: on macOS, only kill orphaned MPOS desktop processes (parent gone or reparented to launchd) when a test run starts, instead of
killall -9on every lvgl_micropy_macOS, which crashed concurrent test runs and open desktop apps - mpos_controller: on Linux, really kill orphaned lvgl_micropy_unix processes when a test run starts. The kernel cuts /proc//comm to 15 characters ('lvgl_micropy_un'), so the exact name match never hit; a truncated comm is now confirmed against argv[0] from /proc//cmdline
Board Support:
- ESP32-S3: add setting to (de)activate USB host mode at runtime using USB On-The-Go to allow for USB HUBs, mice, keyboards and display adapters instead of the default USB-CDC (serial console) mode
- ESP32-S3: add USB hub and hub-behind-hub support (these do count as 1 or 2 USB devices, taking away from the maximum of 3 on ESP32-S3)
- ESP32-S3: add USB display adapter support (DisplayLink DL-1xx, e.g. DL-165/DL-195 fully validated; T6/MS91xx protocol code vendored for ESP32-P4 but untested)
- ESP32-S3: add USB Human Interface Device (HID) keyboard support
- ESP32-S3: add USB Human HID mice support with theme-aware cursor
Frameworks:
- SettingsActivity:
dont_persistentries with adefault_valuenow show '(defaults to X)' in the list instead of '(not persisted)' - topmenu: fix swipe-up-to-close never firing — the close gesture only listened for SCROLL events (which require drawer content taller than the viewport) while real drags deliver PRESSED/PRESSING/RELEASED; the drawer now tracks the press drag and closes once it moves upward past the notification-bar height, with per-event debug logging of position and target
- topmenu: drawer now extends from below the notification bar all the way to the bottom screen edge instead of a fixed 90% height, closing the tappable strip of the underlying app that stayed visible beneath it
OS:
- ESP32-S3: cap ESP-IDF log strings at the ERROR default (LOG_MAXIMUM_EQUALS_DEFAULT), saving ~42 KB of app flash (micropython.bin 3,666,864 → 3,624,784 B); MicroPython logging is unaffected, C logs can no longer be raised above ERROR at runtime
- ESP32-S3: disable Ethernet (ETH_ENABLED=n plus the SPI-Ethernet drivers, which would otherwise force-select it back on), saving ~24 KB of app flash (micropython.bin 3,624,784 → 3,600,688 B); no supported S3 board has an Ethernet PHY
Builtin Apps:
- AppStore: speedup loading from 18 to 6 seconds
- AppStore: load list icons lazily for visible rows only (250ms viewport loader, capped per-tick work) instead of rendering every icon upfront
Frameworks:
- InfiniteList: dynamic initial list sizing instead of hard-coded 18 items
Board Support:
- Add Waveshare ESP32-S3-Touch-LCD-3.5 (3.5' 320x480 ST7796 over SPI with LCD reset/CS on a PCA9554 IO expander, FT6336 touch, QMI8658 IMU, AXP2101 battery management); vendors the st7796 display driver and adds a PCA9554 expander driver
- UNIHIKER K10: improve two-button navigation, correct camera preview orientation, and restore button input after camera operations
- unix/macOS/web: compiler fallback to bytecode on architectures without a native emitter (e.g. macOS on Apple Silicon) instead of runtime-loaded apps using @micropython.native/@micropython.viper failing to import with SyntaxError 'invalid micropython decorator'
- Waveshare ESP32-S3-Touch-LCD-3.5: ES8311 audio support (speaker output + microphone input over I2S, DAC volume default tuned by ear on hardware)
Builtin Apps:
- AppStore and OSUpdate: defer boot service import and start by 120s and 90s respectively via delay_s in manifest intent_filter, moving their module imports out of the boot path
- AppStore and OSUpdate: fix cooldown blocking the first update check for 60s after boot on ESP32 (ticks_ms counts from boot)
- AppStore: add 'Scan QR' button that opens an app's detail screen from a scanned app link (https://badgehub.eu/page/project/APP_ID, micropythonos://app/APP_ID or mpos://app/APP_ID)
- AppStore: fix insert_app_list_item not hiding items that don't match the selected category filter, causing remote-only apps to leak into the 'Installed' view
- AppStore: guard against segfault when the activity leaves the foreground during an async download (e.g. back_screen() while the index is downloading)
- AppStore: open a QR deep link's app detail screen immediately when the app is already known locally, instead of waiting for the index download
Frameworks:
- AppManager: apps can declare URL handlers via 'urlPattern' in manifest intent_filters; patterns matching the official store host, mpos:// or micropythonos:// are reserved and rejected; multiple matching handlers open the chooser
- AppManager: boot services can declare delay_s in their intent_filter; services with delay_s > 0 are imported and started asynchronously after the delay, keeping non-critical module imports out of the boot path
- AudioManager: WAVStream on desktop and web now honors set_repeat()/repeat_count (was ESP32-only), so the MusicPlayer Repeat checkbox works across all platforms
- AudioManager: WAVStream on ESP32 drains remaining I2S audio by wall-clock instead of a fixed sleep, fixing on_complete delays of up to 2s for low-sample-rate clips
- Camera: after decoding a QR code in free-scan mode, show an 'Open in App Store' / 'Open link' chip when the code is an app link the OS can open
- Camera: gracefully handle boards with no camera hardware (e.g. fri3d_2026) — show a 'No camera found' status instead of crashing on get_cameras()[0]
- DeepLink: new mpos.content.deeplink module with strict app-link parsing (exact host allowlist, identity-only links) and URL dispatch
- DNS (async_dns): single-flight lookups per name with a synchronous fallback when no worker thread can be spawned (e.g. boot-time thread pressure), so concurrent websocket/download connections no longer fail with 'can't create thread'
- FileExplorer: pick mode with a path_pattern now lists only matching files (directories stay listed) — non-matching files could never be selected and taps on them were silently ignored, so listing them was pure clutter; browse mode still lists everything
- focus_direction: skip stale widgets from inactive screens during navigation
- FontManager: cache emoji codepoints for keyboard input by @fdb
- FontManager: stop leaking an app's TTF and emoji fonts after the app closes by @fdb
- fs_driver: widen callback exception handling from OSError to Exception, supporting non-seekable streams like DeflateIO
- Nostr: surface NIP-47 (NWC) error replies through the error callback instead of silently discarding them. An UNAUTHORIZED reply (e.g. a retired wallet connection) previously left apps stuck on their connecting state forever, indistinguishable from a dead relay; identical repeated errors are forwarded once, and an error reply now also resets the relay silence watchdog since the wallet service is demonstrably answering
- Screenshot: move the BMP encoder out of the web server into mpos.ui.testing.encode_bmp(), which both now share, and add save_screenshot_bmp() to capture the screen straight into a file
- SharedPreferences: get_dict_item() and get_list_item_dict() now return copies, fixing silent write loss when mutating returned items
- TaskManager: add create_supervised_task(restart_on_return=True) and use it for the aiorepl console, so it is restarted when it exits
- TaskManager: create_task returns None when the task manager is disabled, preventing C-level crashes from asyncio.create_task on ESP32 when called from a disabled test runner context
- topmenu: close_drawer() now accepts an animate parameter, matching the existing close_bar() API
- topmenu: replace group.remove_all_objs() + rebuild with per-object lv.group_remove_obj() in _remove_focusables_from_group, avoiding DEFOCUSED event dispatch mid-cleanup and eliminating repeated linked-list node allocations that fragmented the LVGL heap
- View: clear the shared default focus group before the screen is deleted to prevent dangling LVGL pointer accumulation across activity transitions
OS:
- aiorepl: stop Ctrl-D in the non-raw REPL loop from shutting asyncio down; it now ends the line like Ctrl-C
- aiorepl: survive stdin EOF (host disconnect) by polling for reconnection instead of exiting, so the serial console no longer dies permanently after mpremote/raw-REPL connection attempts
- boot: disable the Ctrl-C interrupt character while mpos.main boots (restored on the REPL fallback paths), so hosts connecting over serial mid-boot (e.g. mpremote entering raw REPL) can no longer silently abort the boot scripts and leave the OS half-started at the REPL
- builtin: compress the frozen /builtin filesystem archive (freezefs --compress), saving ~39 KB of firmware flash, and strip development junk (pycache, .DS_Store, backup files) from it during the build (#268)
- lvgl_micropython: add general-punctuation glyphs to Montserrat fonts
- lvgl_micropython: add native-decorator bytecode fallback for builds without a native emitter like the unix port on aarch64 (Apple Silicon macOS) and the wasm/web port
- lvgl_micropython: compress Montserrat 10-18 fonts to save ~19 KB of flash space
- main.py: skip the lib/ override when lib/mpos is from a different release than the frozen firmware, instead of letting a stale lib/ (as flashed by the web installer) shadow the new frozen modules after an OTA update and crash the launcher at boot (#239)
- nostr: improve connection management, watchdog, error management
- sdl_keyboard: CTRL-SHIFT-S (CMD-SHIFT-S on macOS) saves a timestamped BMP screenshot in the current directory, at the screen's own pixel size instead of the scaled SDL window
Testing:
- howto_app: clear auto_start_app_early SharedPreferences in setUp so the 'Don't show again' checkbox reliably starts unchecked
- mpos.ui.testing: simulate_click() now pumps simulated-indev reads during the hold, so LONG_PRESSED fires even when the scheduler is blocked
- mpos_controller: click_button() now clicks the innermost clickable widget containing the text, instead of the outermost ancestor that sent clicks to the screen center
- mpos_controller: write paste-mode payloads in chunks, draining echoed input between chunks to avoid PTY-buffer deadlock
- on-device: increase timeouts for about_app (10→15s), launcher_splash dialog (8→15s), and fs_driver image loading (5→15s with upfront render wait) to account for device latency
- on-device: relax timing thresholds for infinite_list (scroll drags, render waits, set_data) and navigation_leaks (32→2048 bytes/round) on ESP32 where the slower CPU exceeds desktop expectations
- on-device: skip websocket (requires internet), nostr_local_relay (WebSocket on localhost unreliable), and scan_bluetooth simulation-mode detection (BLE-dependent)
- test_battery_voltage: skip ADC/caching/voltage classes on boards that override BatteryManager to read the io_expander (no battery ADC, e.g. fri3d_2026)
- test_calibration_check_bug: use the real IMU (auto-detected) instead of hardware mocks; save/restore calibration
- test_runner/mpos_controller: self-check that unittest assertions aren't no-ops from an -O3 build; auto-repair from lib/unittest when possible, and fail loudly with a diagnostic when not — previously stripped assertions reported vacuously green
- test_runner: catch subprocess timeout when a device freezes mid-test so the runner can retry (with --reset) instead of crashing
- test_runner: disable notification sound on macOS/darwin in the settings because it causes a hang on headless CI runners
- test_runner: save/restore asyncio.core globals around nested event loops, preventing segfaults when tests run inside the TaskManager's paste-mode event loop
- test_runner: USB device unbind/rebind and automatic recovery when relay reset fails to bring the device back; also CDC PID polling (0x4001/0x1001) for ESP32-S3 USB enumeration
Board Support:
- WebAssembly: expose WebExpander (= simulated Fri3d expander) through mpos.io_expander like on fri3d_2026 board by @DrSkunk
Builtin Apps:
- AppStore: apply dropdown filter when adding to list so apps don't show up unnecessarily (less glitchy)
Frameworks:
- View: fix two LVGL memory leaks in activity navigation by @fdb
Builtin Apps:
- AppStore: fix 'Update N Apps' button and 'Updates (0)' dropdown behavior when opened before the background update check has run
Builtin Apps:
- AppStore: fix 'Update' button in app detail activity
- AppStore: improve 'Update N Apps' button behavior
Frameworks:
- View: fix crash when navigating back from an app launched via notification drawer
Board Support:
- Fri3d 2026: update CH32 firmware to 2.0.3 which fixes issue with 2 consecutive i2c register writes (fixes #224)
- Adapt freenove_esp32s3_display, fri3d_2024, fri3d_2026, lilygo_t4, lilygo_t_hmi, matouch_esp32_s3_spi_ips_2_8_with_camera_ov3660 and squixl to SDCardManager API changes
Builtin Apps:
- AppStore: add new 'Installed' category and show it by default (or 'All' if there are no apps installed)
- AppStore: fix double update check (when network established + some time after boot)
- OSUpdate: fix double update check (when network established + some time after boot)
- OSUpdate: randomly select between update mirrors (updates.micropythonos.com and updates.micropythonos.org) for redundancy
- OSUpdate: if wifi is connected but update check fails (due to server error, for example) don't go into 'Waiting for WiFi' state
Frameworks:
- SDCardManager integrates the old 'sdcard' functionality, which has been removed
- AudioManager: fix buzzer buzzing along when PWM instance is created and fix clicking sound on PWM init (#233) by @cheops
- DownloadManager: resumption offset after connection loss that could prevent over-the-air update (ESP_ERR_OTA_VALIDATE_FAILED) if wifi was lost during update download
- InputManager: add back screen and drawer menu disable/enable APIs so apps receive those keys as regular lv.KEY.ESC / lv.KEY.HOME press
- InfiniteList: add new
mpos.ui.InfiniteListvirtual-scrolling list widget that only renders visible items, keeping memory and render cost bounded even with thousands of entries - SDCardManager: integrate 'sdcard' module functionality and cleanup legacy module
- Focus: add_focus_highlight with mode='bg' now defaults to 100% background opacity, was 30%
- Focus: bg-mode focus handler now scrolls the focused widget into view (matching the border-mode handler)
OS:
- Optimize status bar clock to reduce allocations from ~4.5 KB/s to ~224 B/s (#244) by @fdb
- SPI: expose Device.lock()/unlock() for shared-bus arbitration between multiple drivers
- SPI: fix MicroPython's ESP32 SPI driver DMA failures due to incorrect txdata/rxdata flags
- WiFi: set default country code to Japan instead of World to support channel 12, 13 and 14 (including active scan, meaning hidden SSIDs)
Board Support:
- Linux and WebAssembly: initialize mock IMU sensor if no real IMU sensor is present
- Fri3d 2026: increase LoRa SPI frequency from 500 kHz to 16 Mhz to speed up transfers and reduce bus collisions
Builtin Apps:
- AppStore: add special 'Updates' category, opened by default when update notification is pressed
- AppStore: add app ratings support
- AppStore: make app version and description focusable so they can be scrolled using arrow keys
- OSUpdate: improve update progress UI
OS:
- Add simple way to force DeviceInfo.hardware_id, skip board detection, and customize hardware board initialization #215
- App class: support multiple categories in MANIFEST.JSON (
'categories'array), normalize to title-caseself.categorieslist withcategoryproperty for backward compatibility - Fix WiFi network switch not actually changing network — disconnect from current network before connecting to new one #220
- Focus restoration fix: navigating back now correctly restores the previously focused widget
- sdl_keyboard: fix CTRL-C and CTRL-V on textarea
- aiowebsocket: reduce reconnect frequency to reduce performance impact
- Tweak TaskHandler's period from 1ms to 2ms to improve asyncio performance at the cost of slightly increased callback latency
- micropython-nostr: log background relay connection failures at INFO instead of ERROR to avoid REPL pollution breaking file transfers
Development:
- mpos_controller: give --serial-port to mpremote, so file transfers, screenshots and widget trees use the selected device instead of the first device that mpremote finds
- Add Python-level line coverage via sys.settrace (mpcov build variant)
- Add
--coverageflag to test_runner.py for collecting per-file line coverage - Add
make build-mpos-unix-coveragetarget - Add HTML coverage report with expandable inline source (scripts/coverage_report.py)
- Add scripts/coverage.sh for automated coverage runs with clustered/partial test support
- Add scripts/cyclonatic_complexity.sh for per-function cyclomatic complexity reports
- build_mpos.sh: restore @micropython.native/@micropython.viper decorators after desktop/web builds (via EXIT trap) so local builds no longer leave the working tree modified
- WAVStream: refactor play() to reduce cyclomatic complexity (69→42) by extracting read_decode_chunk() and play_desktop()
Builtin Apps:
- AppStore: add category filter
- AppStore: add 'Work in Progress' filter
- AppStore: hide work_in_progress apps by default (toggle with setting)
- AppStore: ignore punctuation when alphabetically sorting apps
- AppStore: report app install to BadgeHub report/install API for statistics
- AppStore: optimize icon display and add blurhash support
- AppStore: show 'Loading details...' instead of 'Unknown' while loading app details
- OSUpdate: make long changelog scrollable with arrow keys
Frameworks:
- App: don't search for local icons if app isn't installed locally to reduce storage access time
- AppManager: cleanup /prefs/appfullname and /cache/appfullname when uninstalling app
- DownloadManager: add post_url() for HTTP POST requests (needed for BadgeHub report/install API call)
- DownloadManager: reduce log level of missing Content-Length from warning to info to avoid polluting REPL
OS:
- aiowebsocket: add exponential reconnect backoff so unreachable relays stop exhausting the thread pool (#191) by @jnuyens
- async_dns: resolve .local domains synchronously on desktop because mDNS is not thread safe
- LightsManager: simplify API to 'from mpos import LightsManager'
Board Support:
- WebAssembly: fix mobile touch input by properly converting SDL's normalized touch coordinates to actual window pixel coordinates by @DrSkunk
- WebAssembly: Add fake machine.Pin and machine peripherals for WebAssembly build by @DrSkunk
Builtin Apps:
- About: show correct netmask (thanks @lebeno !)
- About, HowTo, Launcher, WiFi Settings: migrate to add_focus_highlight()
Frameworks:
- Focus: rename add_focus_border() to add_focus_highlight(), add mode='bg' so widgets with borders can show focus via background tint instead; keep old name as compatibility wrapper
- Focus: don't clear borders when focus-nav was never active
- App: change 'no icon' log level from warning to info
- AudioManager: add find_input_by_kind() and find_output_by_kind()
- AudioManager: add find_input_by_name() and find_input_by_kind()
- InputManager: add has_haptic_feedback() class method
- Lights: re-export mpos.lights from the top-level mpos package by @tjorim
- WifiService: fix get_ipv4_netmask to get the netmask (not the address) by @lebeno
OS:
- Settings: add 'Startup sound' to mute bootup buzzer by @DrSkunk
- Settings: gate 'Startup sound' by AudioManager having a 'buzzer' output
- Settings: gate 'Haptic feedback' by InputManager having haptic feedback support
Builtin Apps:
- AppStore: use one backend list call to check for updates instead of per-app detail fetches
Frameworks:
- NotificationManager: support emojis in notifications
- Topmenu: close notification bar when launching app from notification drawer
Builtin Apps:
- AppStore: improve responsiveness by skipping download of icons in list view (generate image based on SHA1 hash)
- AppStore: use new version field in BadgeHub.eu's /project-summaries
- New MPOS-AppLogos icons by @QuasiKili
- Appstore, OSUpdate, Settings: remove redundant assets/ folder
Frameworks:
- MposKeyboard: don't show NEWLINE button when the linked textarea is set_one_line(True)
- Code cleanups
OS:
- Reduce max concurrent DNS resolving threads from 4 to 2 to be on the safe side
Board Support:
- DFRobot UniHiker K10: add board support (ST7789 display, XL9535 GPIO expander, GC2145 camera, on-board sensors, RGB LED) by @woodhead-tech
- Fri3d 2026: repeat buttons when pressed
- Add new 'web' target by @DrSkunk to run in a webbrowser using WebAssembly!
Builtin Apps:
- About: fix logo not showing
- AppStore: download the correct .mpk from BadgeHub.eu if there are multiple versions
- File Manager: fix browsing SD card (FAT32) folders and files not showing
- OSUpdate: show raw ESP error and a user-friendly message when the update fails to activate
Frameworks:
- AppManager: add support for apps as Python packages
- AudioManager: support for desktop audio by @jnuyens
- DownloadManager and aiohttp: various connection handling improvements by @jnuyens
- File Explorer: add support for deleting directories
- Focus:
add_focus_border()highlights stay hidden until the user navigates by direction - FontManager: fallback to included lips for various variations
- NotificationManager: add buzzer sound notifications with setting
- SDCard: tolerate trailing slashes when listing SD card contents
- StreamingUnzip: tolerate trailing slashes when creating directories
- FontManager: tolerate trailing slashes when listing directories
- WifiService: increase connection timeout from 10 to 13 seconds, reduce log levels to avoid polluting REPL shell
OS:
- Increase size of esp32-small and lilygo_t4 partitions to 3800000 to accomodate esp32 builds
- shutil: tolerate trailing slashes in
rmtree(),copytree(), andmove() - os.path: fix
exists(),isdir(), andisfile()for directories ending with/on FAT32 - micropython-nostr: add NIP-17 and NIP-44 support
- DNS lookups in separate thread to prevent UI hang on esp32 by @jnuyens
- Strip trailing slashes from directory paths before filesystem calls to support both LittleFS and FAT32 (SD card)
Builtin Apps:
- Add File Manager to builtin apps, with support for dispatching an implicit 'view' intent to open files
- AppStore: make BadgeHub.eu the default backend, with Apps.MicroPythonOS.com second, cleanups
- AppStore: prioritize display of known icons, show installed apps first (no network needed)
- OSUpdate: show 'OS Update' instead of 'OTA Update' during progress
Board Support:
- Fri3d 2024/2026: correct IR TX pin for SAO IO13/IO21 (not IO10 badge link) so Mini Blaster / Noisy Cricket works with IR Remote app
- Fri3d 2026: add support for headset-only audio output (without simulteneous communicator output)
Drivers:
- ST7789 display controller: tune up VCOMS, VRHS for improved contrast; raise frame rate to 90 Hz to prevent tearing effect; update positive and negative gamma curves for better colors
Frameworks:
- Add generic
InputActivityfor reusable single-value input UI;SettingActivityis now a thin wrapper that launchesInputActivityand persists the result - FontManager: add a few more emojis and have
getEmojiStrings()return complete emoji sequences (e.g. flag pairs) for visual emoji lists - Add new
add_focus_borderutility and migrate all focus/defocus border handlers (ImageView, MusicPlayer, ShowFonts, About, HowTo, Launcher, WiFi Settings, Connect4, SettingsActivity, topmenu) to use it - Add new builtin File Explorer Activity with picker
- AppManager: add support to install/update mpk/zip packages with 0-byte files (like IR Remote)
- AppManager: move to new 'flat' mpk structure to reduce directory overhead by LittleFS
- AppManager: add file-type association support (mimeType/pathPattern intent filters) and an 'Open With' chooser
- AudioManager: add support for compressed 4-bit ADPCM IMA WAV format
- ActivityNavigator: dispatch implicit file intents to installed apps with proper app context and status-bar handling
- Fix ChooserActivity, ViewActivity, and ShareActivity for LVGL 9.x and remove undefined references
- ViewActivity fallback now displays the filename plus the first 512 bytes of the file's contents
- Activity: add
onBackPressed()hook so an activity can intercept the back/close gesture and stay foreground until it decides to close - View: split the explicit
finish_current_activity()path fromback_screen()soActivity.finish()no longer re-triggersonBackPressed() - SharedPreferences: migrate and store preferences to /prefs/ instead of /
OS:
- Add BMP bitmap image support (with fix for LVGL 9.4 bmp scaling)
- Fix tjpgd JPEG scaling in LVGL
- Move MicroPythonOS-logo-white-long-w296.png to /builtin/res/
Board Support:
- lilygo_t_display_s3.py: remove old LVGL 9.2 workaround for button repeat handling
- m5stack_fire.py: remove old LVGL 9.2 workaround for button repeat handling
- esp32-small and lilygo_t4: reduce app partition size from 3.9M to 3.7M
- Fix import logging error in 9 boards
Builtin Apps:
- AppStore: fix focus border around action button
- AppStore: wait at least 2 minutes before checking for app updates
- HowTo: fix checkbox being persisted unnecessarily
- Launcher: speed up by removing icon image hasing
- OSUpdate: improve layout, focus handling, UI
- OSUpdate: wait at least 2 minutes before checking for app updates
Frameworks:
- AppManager: look for apps with both absolute and relative paths
OS:
- Increase LV_IMAGE_HEADER_CACHE_DEF_CNT from 24 to 64 to fit enough app icons
- fs_driver.py: fix one failed M:/ load from breaking all future M: loads
- Logging: add timestamp (milliseconds since startup) for improved performance debugging
- Mount LittleFS2 internal filesystem progsize=256 to match the ESP32 SPI flash hardware page, avoiding read-modify-write cycles.
- Mount LittleFS2 internal filesystem with readsize=256 to align flash reads for better efficiency.
- Move Nostr userlist from internal_filesystem/lib/userlist.py micropython-nostr/userlist.py
- Print warning about limited aiorepl and how to enable full REPL shell
- macOS/unix builds: auto import main (like on ESP32) so it works without any internal_filesystem directory
Board Support:
- Fri3d 2024: cleanup key repeat workaround for old LVGL 9.2
- Fri3d 2026: add button repeat on long press
- Fri3d 2026: switch audio to final pin assigments (keep IO0/START pressed to use old prototype pin assigments)
Builtin Apps:
- Launcher: fix splash screen remaining visible when app fails to start
- Use logger/logging with
__debug__guard for smaller build size
Frameworks:
- CameraActivity: use QR symbol for QR decoding
- DownloadManager: print stack trace in case of download error
- DownloadManager: include User-Agent HTTP header
- FontManager: optimize emoji PNG sizes with optipng and zopflipng to reduce build size by ~14KiB
- FontManager: add popular 'Smiling Face with Three Hearts' emoji that was missing from the frequency table
- WidgetAnimator: fix animations with large values (like a large on-chain balance in LightningPiggy) not being animated
OS:
- aiohttp: add support for relative HTTP redirects (in addition to absolute HTTP redirects)
- Drawer menu: close when home button is clicked
- Optional override of builtin boot splash image by /data/images/boot_splash.png
- Compile Python code with -O3 to optimize for speed and size
- Use logger/logging with
__debug__guard for smaller build size
Board Support:
- New board: LilyGo T4
- New board: SQUiXL device by Unexpected Maker - https://squixl.io
Builtin Apps:
- About: use shutil.disk_usage()
- About: add LVGL version info
- AppStore: add 'Update All' functionality, redesign with split between UI and Service logic
- Launcher: show splash screen (app icon centered) while starting an app
- OSUpdate: redesign with split between UI and Service logic
- Settings: new 'Haptic feedback' enable/disable setting
Frameworks:
- Add Services with intent filter boot_completed
- AppManager: buffer .mpk/.zip in RAM instead of ROM before extracting (less space required, less flash wear, faster)
- AppManager: check sufficient free storage space before installing app package
- FontManager: switch openmoji to noto-emoji
- FontManager: add 10 additional emojis
- FontManager: switch from 20x20 to 32x32 emojis to improve quality (same filesize!)
- FontManager: delegate emoji image scaling to LVGL (native C)
- FontManager: speed up and simplify emoji scaling
- NotificationManager: new framework to notify the user
- SettingActivity: add new optional 'note' field for inline informational text about a setting
- WebServer: add fast one-layer screenshot view in addition to slower all-layer mode
OS:
- Gesture navgation: use lv.SYMBOL.NEW_LINE for the swipe from the left edge to go back
- build_mpos.sh: suppress Clang 21's -Wunterminated-string-initialization so the macOS build doesn't fail compiling secp256k1
- Create WifiBoot-, WebServerBoot- and AIOReplService
- Restyle drawer menu to accomodate notifications
- shutil: add move, copyfile, and copytree() functions
- Focus direction: rewrite algorithm for speed and widget reachability
- Show warning if an app throws an exception (with optional details)
- Require top-level directory in .mpk files that matches the app name
Known issues:
- Animations with large values (like a large on-chain balance in LightningPiggy) are not animated
Frameworks:
- Fix SharedPreferences no-op guard silently discarding writes (affects all settings, not just WiFi)
- Switch Fri3d Communicator Add-On 2026 Keyboard to polling mode to work around spotty IRQ
Builtin Apps:
- About: cleanup unnecessary code
Board Support:
- Fri3d 2024: register i2c with DeviceManager for add-ons
- Fri3d 2024 and 2026: detect and initialize Fri3d Communicator 2024 or 2026 Keyboard
- Fri3d Communicator Add-On: improve UART handling
- Fri3d Communicator Add-On Keyboard: new input device (indev) for LVGL with IRQ mode, polling mode, key repeat, escape and arrows for navigation
Frameworks:
- AudioManager: tweak WAV input buffer to reduce glitches
- FontManager: default to non-emoji font unless emoji=True to improve performance
- FontManager: add a few more emojis
Builtin Apps:
- About: show system uptime, use FontManager
- Compile builtin apps to bytecode .mpy files to reduce build size by 60KiB and improve startup performance
- HowTo app: make import lvgl as lv explicit
Frameworks:
- AppManager: try .mpy after .py and use import instead of explicit compile
- AppManager: require explicit import lvgl for clarity
- FontManager: new framework that provides font listing, TTF support, emoji support
- InputManager: mark emulate_focus_obj(group,o) as deprecated in favor of lv.group_focus_obj(o)
- MposKeyboard: add support for typing a few basic emojis
- SharedPreferences: avoid writing default-only configs at boot and prune empty config dirs/files
OS:
- Add os.path functionality like in CPython
- c_mpos/quirc: fix compilation warnings
- Disable unused OS facilities (FreeRTOS internals, tracing, INFO logging, broken GIF, Pinyin IME, LVGL window, BMP) to reduce build size by 109KiB
- Simplify focusgroup handling
- Re-enable UART REPL again BUT add esp.uart_repl(False) functionality to disable/enable it at runtime
- Format internal storage if mounting fails instead of hanging (even if not empty)
Board Support:
- Freenove's ESP32-S3 display board by @Rohansi
OS:
- Synchronize with upstream https://github.com/lvgl-micropython/lvgl_micropython 14ad6ce to bring ESP-IDF: 5.4 to 5.5.1 and MicroPython: 1.25 to 1.27
- Split lvgl_micropython customizations into topic branches for easier rebasing: topic/fonts, topic/lv-conf, topic/platform, topic/error-handling
Builtin Apps:
- About: show correct next update partition instead of always using get_next_update()
- OSUpdate: restrict OTA update flip-flop target to ota_0/ota_1 instead of all ota_N partitions (via shared partition helper)
Frameworks:
- Add mpos.partitions.get_next_update_partition() helper that alternates between ota_0 and ota_1 only
- DownloadManager: add 'redact' argument to replace sensitive info with REDACTED in log output
- SettingActivity: support slider UI for integer settings
- SettingActivity: show human-readable value instead of internally used option
Board Support:
- Fri3d 2026: access expander.analog as property instead of function
OS:
- Disable the repl on hardware uart for esp32s3 targets (USB serial still works)
- Remove big, rarely used font Montserrat 34, 40 and 48 to reduce build size by 218KiB, while apps can still upscale or load fonts at runtime
Builtin Apps:
- Optimize PNGs to reduce build size by 8KiB
Frameworks:
- WebServer: tweak webREPL UI and serve gzipped HTML to reduce total build size by 60KiB
Board Support:
- Fri3d 2026: update CH32 firmware to 1.2.2 release
- Fri3d 2026: remove workarounds for CH32 firmware 1.2.1
Board Support:
- Fri3d 2026: add CH32 LCD backlight setting
- Fri3d 2026: fix virgin CH32 coprocessor firmware installation
OS:
- Patch esp-idf for to workaround sporadic SD card slowness (espressif/esp-idf/issues/16909)
Builtin Apps:
- AppStore: fallback to .zip file if no .mpk file found in filelist
- AppStore: fetch new long_description from BadgeHub details API
- Settings - Wi-Fi: don't print password on serial port
Frameworks:
- Add new GPSManager framework
- Add new IRManager framework
- Add new LoRaManager framework
- Add new DeviceManager framework
- Add mpos.ui.change_task_handler() function for improving IR timing accuracy
- AppearanceManager: fix set_light_mode() and set_primary_color()
- AppManager: support .mpk/.zip files with compression and a redundant top-level directory
- AppManager: export 'mpos' global to apps for convenience
- Camera activity: use QR symbol for QR decoding, tweak fonts
- LightsManager: allow changing number of LEDs after initialization
- SettingActivity: add
allow_deselectoption (default False) to radiobuttons - SharedPreferences: don't print potentially sensitive values on serial port
- WebServer: add basic 'View Screen' functionality to view the device's display remotely
OS:
- aioREPL: use >>> prompt (for ViperIDE)
- Drawer menu: reload apps when Launch(er) is (re)started
- Export 'lv' and 'mpos' globals to aioREPL and apps for convenience
- Compress largest fonts to reduce build size by ~208KiB
- Rename font_montserrat_28_compressed to font_montserrat_28 for uniformity
- LilyGo T-Watch S3 Plus: add support for IR Remote app TX
- LilyGo T-Watch S3 Plus: add support for UART GPS
- Fri3d 2024: add support for IR remote app (RX only)
- Fri3d 2026: add CH32 coprocessor firmware handling (credit @bertouttier)
- Fri3d 2026: add CH32 indev driver (credit @bertouttier)
- Fri3d 2026: add calibrated battery voltage measurements using CH32
Builtin Apps:
- Settings: new Audio subsection to choose default output and input device, for boards with multiple audio devices
Frameworks:
- Activity: add appFullName property
- AudioManager: load and apply configured default_output and default_input devices
- AudioManager: fix final 1-2 seconds of WAV files not being played
- AudioManager: add support for PDM microphones
- AudioManager: fix 24 and 32 bits per sample WAV support
- SensorManager: add BMA423 IMU support
- TimeZone: set Real Time Clock if present
OS:
- Fix lvgl_micropython UI hang when lv.event_handler() throws exception from timers or callbacks
- Fix notification bar hiding after swipe up in Launcher apps
- Increase default heapsize from 8MB to 16MB on desktop to fix sporadic segfault
- Fri3d 2026: don't provide unnecessary SCLK/BCLK to CJC4334 DAC
- LilyGo T-Watch S3 Plus: fix power button sporadically becoming unresponsive
- LilyGo T-Watch S3 Plus: add battery charge level support
- LilyGo T-Watch S3 Plus: add IMU accelerometer support so IMU app works
- LilyGo T-Watch S3 Plus: enable audio input (PDM microphone) and output (I2S speaker)
- LilyGo T-Watch S3 Plus: enable Real Time Clock to keep time when powered off
- LilyGo T-Watch S3 Plus: power down/up display and touch screen upon power button press
Builtin Apps:
- AppStore: use BadgeHub.eu filter mpos_api_0 instead of device-specific hardware ID
- HowTo: add padding
- Settings: add Number Format setting
Frameworks:
- Add new NumberFormat framework for decimal and thousands separators
- DownloadManager: add connection timeout to DownloadManager session.get()
OS:
- New board support: LilyGo T-HMI
- New board support: M5Stack Core2
- LilyGo T-Watch S3 Plus: initialize Power Management Unit at startup
- LilyGo T-Watch S3 Plus: power button short press for display backlight on/off, long press for power down
- Add driver for LoRa SX1262 with lvgl_micropython-style (= split Bus/Device) hardware SPI
- Add drivers for LoRa SX126X with SoftSPI (and default MicroPython hardware SPI)
- Add esp32-component-rvswd and MicroPython bindings to flash WCH's CH32 microcontrollers
- Add glyphs to fonts: diacritics 0x7F-0xFF, Bitcoin symbol ₿ 0x20BF, italic satoshi symbol 丯 0x4E2F and regular satoshi symbol 丰 0x4E30
- Add LVGL symbols to fonts: 0xf002,0xf004,0xf005,0xf00e,0xf010,0xf029,0xf030 for search, heart, star, search-plus, search-minus, qrcode, camera
- Add LVGL symbols to fonts: 0xf15a,0xf164,0xf165,0xf1e0 for btc (without circle), thumbs-up, thumbs-down, share-alt
- Add LVGL symbols to fonts: 0xf2ea,0xf379,0xf58f for undo-alt, bitcoin (in circle), headphones-alt
- Improve handling of 'mpos.main' errors
- Fix empty black window issue on macOS desktop
- Fix macOS/unix desktop build with newer Clang (17+)
Builtin Apps:
- AppStore: update BadgeHub.eu URL
- About: show netmask separately, make labels focusable
- HowTo: new onboarding app with auto-start handling to explain controls
- Settings: add sub-groups of setings as separate apps, including WiFi app
- Settings: add Hotspot sub-group (SSID, password, security)
- Settings: add WebServer sub-group (autostart, port, password)
- Launcher: ignore launchers and MPOS settings (except WiFi)
Frameworks:
- Audio streams: WAV playback/recording improvements (duration/progress, hardware volume control)
- AudioManager: registry/session model, multi-speaker/mic routing, ADC-based mic (adc_mic)
- DownloadManager: explicit certificate handling
- InputManager: pointer detection helpers and board registrations
- SensorManager: refactor to IMU drivers with magnetometer support and desktop IIO fallback
- SharedPreferences: fix None handling
- WebServer: new framework with Linux/macOS fixes and no background thread
- WifiService: hotspot support, IP address helpers, simplified connect/auto-connect
- Websocket library: renamed to uaiowebsocket to avoid conflicts
OS:
- ESP32 boards: bundle WebREPL (not started by default) to offer remote MicroPython shell over the network, accessible through webbrowser
- New board support: LilyGo T-Display-S3 (physical and emulated by QEMU)
- New board support: LilyGo T-Watch S3 Plus
- New board support: M5Stack Fire
- New board support: ODroid Go
- New board support: unPhone 9
- Fri3d 2024/2026 updates: display reset support using CH32 microcontroller, communicator/expander drivers
- ADC microphone C module and tests
- Build system: switch to static builds for desktop systems to bundle LIBC and fix LIBC version issue
- Build system: add linux-arm64 and macos-intel GitHub workflows to support more precompiled binaries
- Add FreeRTOS module for low-level ESP32 functions
Builtin Apps:
- About: use logger framework
- AppStore: mark BadgeHub backend as 'beta'
- Launcher: improve layout on different screen width sizes
- OSUpdate: remove 'force update' checkbox not in favor of varying button labels
Frameworks:
- SDCard: add support for SDIO/SD/MMC mode
- CameraManager and CameraActivity: work fully camera-agnostic
OS:
- Add board support: Makerfabs MaTouch ESP32-S3 SPI IPS 2.8' with Camera OV3660
- Scale MicroPythonOS boot logo down if necessary
- Don't show battery icon if battery is not supported
- Move logging.py to subdirectory
Builtin Apps:
- Update icons for AppStore, Settings, and Wifi apps
Frameworks:
- Fix issue with multiple DownloadManager.download_url's on ESP32 due to SSL session sharing/corruption
Builtin Apps:
- Redesign all app icons from scratch for a more consistent style
- About app: show MicroPythonOS logo at the top
- AppStore app: fix BadgeHub backend handling
- OSUpdate app: eliminate requests library
- Settings app: make 'Cancel' button more 'ghost-y' to discourage accidental misclicks
Frameworks:
- Harmonize frameworks to use same coding patterns
- Rename AudioFlinger to AudioManager framework
- Rename PackageManager to AppManager framework
- Add new AppearanceManager framework
- Add new BatteryManager framework
- Add new DeviceInfo framework
- Add new DisplayMetrics framework
- Add new InputManager framework
- Add new TimeZone framework
- Add new VersionInfo framework
- ActivityNavigator: support pre-instantiated activities so an activity can close a child activity
- SensorManager: add support for LSM6DSO
OS:
- Show new MicroPythonOS logo at boot
- Replace all compiled binary .mpy files by source copies for transparency (they get compiled during the build, so performance won't suffer)
- Remove dependency on micropython-esp32-ota library
- Remove dependency on traceback library
- Additional board support: Fri3d Camp 2026 (untested)
- About app: make more beautiful
- AppStore app: add Settings screen to choose backend
- Camera app and QR scanning: fix aspect ratio for higher resolutions
- WiFi app: check 'hidden' in EditNetwork
- Wifi app: add support for scanning wifi QR codes to 'Add Network'
- Create new SettingsActivity and SettingActivity framework so apps can easily add settings screens with just a few lines of code
- Create CameraManager framework so apps can easily check whether there is a camera available etc.
- Simplify and unify most frameworks to make developing apps easier
- Improve robustness by catching unhandled app exceptions
- Improve robustness with custom exception that does not deinit() the TaskHandler
- Improve robustness by removing TaskHandler callback that throws an uncaught exception
- Don't rate-limit update_ui_threadsafe_if_foreground
- Make 'Power Off' button on desktop exit completely
- Fri3d Camp 2024 Board: add I2S microphone as found on the communicator add-on
- API: add TaskManager that wraps asyncio
- API: add DownloadManager that uses TaskManager
- API: use aiorepl to eliminate another thread
- AudioFlinger API: add support for I2S microphone recording to WAV
- AudioFlinger API: optimize WAV volume scaling for speed and immediately set volume
- Rearrange automated testing facilities
- About app: add mpy format info
- AppStore app: eliminate all threads by using TaskManager
- AppStore app: add experimental support for BadgeHub backend (not enabled)
- MusicPlayer app: faster volume slider action
- OSUpdate app: show download speed
- SoundRecorder app: created to test AudioFlinger's new recording feature!
- WiFi app: new 'Add network' functionality for out-of-range networks
- WiFi app: add support for hidden networks
- WiFi app: add 'Forget' button to delete networks
- Fri3d Camp 2024 Board: add startup light and sound
- Fri3d Camp 2024 Board: workaround ADC2+WiFi conflict by temporarily disable WiFi to measure battery level
- Fri3d Camp 2024 Board: improve battery monitor calibration to fix 0.1V delta
- Fri3d Camp 2024 Board: add WSEN-ISDS 6-Axis Inertial Measurement Unit (IMU) support (including temperature)
- API: improve and cleanup animations
- API: SharedPreferences: add erase_all() function
- API: add defaults handling to SharedPreferences and only save non-defaults
- API: restore sys.path after starting app
- API: add AudioFlinger for audio playback (i2s DAC and buzzer)
- API: add LightsManager for multicolor LEDs
- API: add SensorManager for generic handling of IMUs and temperature sensors
- UI: back swipe gesture closes topmenu when open (thanks, @Mark19000 !)
- About app: add free, used and total storage space info
- AppStore app: remove unnecessary scrollbar over publisher's name
- Camera app: massive overhaul!
- Lots of settings (basic, advanced, expert)
- Enable decoding of high density QR codes (like Nostr Wallet Connect) from small sizes (like mobile phone screens)
- Even dotted, logo-ridden and scratched pictures of QR codes are now decoded properly!
- ImageView app: add delete functionality
- ImageView app: add support for grayscale images
- OSUpdate app: pause download when wifi is lost, resume when reconnected
- Settings app: fix un-checking of radio button
- Settings app: add IMU calibration
- Wifi app: simplify on-screen keyboard handling, fix cancel button handling
- ESP32: one build to rule them all; instead of 2 builds per supported board, there is now one single build that identifies and initializes the board at runtime!
- MposKeyboard: fix q, Q, 1 and ~ button unclickable bug
- MposKeyboard: increase font size from 16 to 20
- MposKeyboard: use checkbox instead of newline symbol for 'OK, Ready'
- MposKeyboard: bigger space bar
- OSUpdate app: simplify by using ConnectivityManager
- OSUpdate app: adapt to new device IDs
- ImageView app: improve error handling
- Settings app: tweak font size
- Settings app: add 'format internal data partition' option
- Settings app: fix checkbox handling with buttons
- UI: pass clicks on invisible 'gesture swipe start' are to underlying widget
- UI: only show back and down gesture icons on swipe, not on tap
- UI: double size of back and down swipe gesture starting areas for easier gestures
- UI: increase navigation gesture sensitivity
- UI: prevent visual glitches in animations
- API: add facilities for instrumentation (screengrabs, mouse clicks)
- API: move WifiService to mpos.net
- API: remove fonts to reduce size
- API: replace font_montserrat_28 with font_montserrat_28_compressed to reduce size
- API: improve SD card error handling
- WifiService: connect to strongest networks first
- Add custom MposKeyboard with more than 50% bigger buttons, great for tiny touch screens!
- Apply theme changes (dark mode, color) immediately after saving
- About app: add a bit more info
- Camera app: fix one-in-two 'camera image stays blank' issue
- OSUpdate app: enable scrolling with joystick/arrow keys
- OSUpdate app: Major rework with improved reliability and user experience
- add WiFi monitoring - shows 'Waiting for WiFi...' instead of error when no connection
- add automatic pause/resume on WiFi loss during downloads using HTTP Range headers
- add user-friendly error messages with specific guidance for each error type
- add 'Check Again' button for easy retry after errors
- add state machine for better app state management
- add comprehensive test coverage (42 tests: 31 unit tests + 11 graphical tests)
- refactor code into testable components (NetworkMonitor, UpdateChecker, UpdateDownloader)
- improve download error recovery with progress preservation
- improve timeout handling (5-minute wait for WiFi with clear messaging)
- Tests: add test infrastructure with mock classes for network, HTTP, and partition operations
- Tests: add graphical test helper utilities for UI verification and screenshot capture
- API: change 'display' to mpos.ui.main_display
- API: change mpos.ui.th to mpos.ui.task_handler
- waveshare-esp32-s3-touch-lcd-2: power off camera at boot to conserve power
- waveshare-esp32-s3-touch-lcd-2: increase touch screen input clock frequency from 100kHz to 400kHz
- Settings app: add 'Auto Start App' setting
- Tweak gesture navigation to trigger back and top menu more easily
- Rollback OTA update if launcher fails to start
- Rename 'Home' to 'Launch' in top menu drawer
- Fri3d-2024 Badge: use same SPI freq as Waveshare 2 inch for uniformity
- ESP32: reduce drawing frequency by increasing task_handler duration from 1ms to 5ms
- Rework MicroPython WebSocketApp websocket-client library using uasyncio
- Rework MicroPython python-nostr library using uasyncio
- Update aiohttp_ws library to 0.0.6
- Add fragmentation support for aiohttp_ws library
Known issues:
- Fri3d-2024 Badge: joystick arrow up ticks a radio button (workaround: un-tick the radio button)
- OSUpdate app: fix typo that prevented update rollback from being cancelled
- Fix 'Home' button in top menu not stopping all apps
- Update micropython-nostr library to fix epoch time on ESP32 and NWC event kind
- OSUpdate app: now gracefully handles the user closing the app mid-update instead of freezing
- Launcher app: much faster thanks to PackageManager + UI only rebuilt when apps actually change
- AppStore app: improved stability + icons for already-installed apps are shown instantly (no download needed)
- API: Add SDCardManager for SD Card support
- API: add PackageManager to (un)install MPK packages
- API: split mpos.ui into logical components
- Remove 'long press IO0 button' to activate bootloader mode; either use the Settings app (very convenient) or keep it pressed while plugging in the USB cable (or briefly pressing the reset button)
- Increase framerate on ESP32 by lowering task_handler duration from 5ms to 1ms
- Throttle per-frame async_call() to prevent apps from overflowing memory
- Overhaul build system and docs: much simplier (single clone and script run), add MacOS support, build with GitHub Workflow, automatic tests, etc.
- Settings app: fix stray /cat in Europe/Brussels timezone
- Launcher app: fix handling of empty filesystem without apps
- Fix KeyPad focus handling for devices without touch screen like the Fri3d Camp 2024 Badge
- Use direction arrows for more intuitive navigation instead of Y/A or pageup/pagedown for previous/next
- About app: enable scrolling using arrow keys so off-screen info can be viewed
- About app: add info about freezefs compiled-in filesystem
- AppStore app: don't update UI after the user has closed the app
- Launcher app: improve error handling
- Wifi app: cleanup and improve keyboard and focus handling
- Wifi app: improve different screensize handling
- Update to MicroPython 1.25.0 and LVGL 9.3.0
- About app: add info about over-the-air partitions
- OSUpdate app: check update depending on current hardware identifier, add 'force update' option, improve user feedback
- AppStore, Camera, Launcher, Settings: adjust for compatibility with LVGL 9.3.0
- Merge official Fri3d Camp 2024 Badge support
- About app: add machine.freq, unique_id, wake_reason and reset_cause
- Reduce timezones from 400 to 150 to reduce scrolling
- Experimental Fri3d Camp 2024 Badge support
- UI: add visual cues during back/top swipe gestures
- UI: prevent menu drawer button clicks while swiping
- Settings: add Timezone configuration
- Draw: new app for simple drawing on a canvas
- IMU: new app for showing data from the Intertial Measurement Unit ('Accellerometer')
- Camera: speed up QR decoding 4x - thanks @kdmukai!
- Move wifi icon to the right-hand side
- Power off camera after boot and before deepsleep to conserve power
- Settings: add 20 common theme colors in dropdown list
- Update battery icon every 5 seconds depending on VBAT/BAT_ADC
- Add 'Power' off button in menu drawer
- Scale button size in drawer for bigger screens
- Show 'Brightness' text in drawer
- Add builtin 'Settings' app with settings for Light/Dark Theme, Theme Color, Restart to Bootloader
- Add 'Settings' button to drawer that opens settings app
- Save and restore 'Brightness' setting
- AppStore: speed up app installs
- Camera: scale camera image to fit screen on bigger displays
- Camera: show decoded result on-display if QR decoded
- Improve focus group handling while in deskop keyboard mode
- Add filesystem driver for LVGL
- Implement CTRL-V to paste on desktop
- Implement Escape key for back button on desktop
- WiFi: increase size of on-screen keyboard for easier password entry
- WiFi: prevent concurrent operation of auto-connect and Wifi app
- Add left edge swipe gesture for back screen action
- Add animations
- Add support for QR decoding by porting quirc
- Add support for Nostr by porting python-nostr
- Add support for Websockets by porting websocket-client's WebSocketApp
- Add support for secp256k1 with ecdh by porting and extending secp256k1-embedded
- Change theme from dark to light
- Improve display refresh rate
- Fix aiohttp_ws bug that caused partial websocket data reception
- Add support for on Linux desktop
- Add support for VideoForLinux2 devices (webcams etc) on Linux
- Improve builtin apps: Launcher, WiFi, AppStore and OSUpdate
- appstore: add 'update' button if a new version of an app is available
- appstore: add 'restore' button to restore updated built-in apps to their original built-in version
- launcher: don't show launcher apps and sort alphabetically
- osupdate: show info about update and 'Start OS Update' before updating
- wificonf: scan and connect to wifi in background thread so app stays responsive
- introduce MANIFEST.JSON format for apps
- improve notification bar behavior
- Handle IO0 'BOOT button' so long-press starts bootloader mode for updating firmware over USB
- Initial release