waveshare-watch-rs

repository·main·Indexed 43 days ago

https://github.com/infinition/waveshare-watch-rs

A high-performance, 100% Rust no_std firmware for the Waveshare ESP32-S3-Touch-AMOLED-2.06 smartwatch. It features an async-driven architecture using embassy-executor, extreme power efficiency with an Always-On Display (AOD) mode, and support for a 410×502 QSPI AMOLED display, I2S audio, and various sensors. The project includes a launcher with mini-games, a T9 keyboard, and an MP3 player, providing a complete rewrite of the original C/C++ project into a single-binary Rust codebase.

Tokens
14.2K
Snippets
52
Records
73
Agent score
53%

What's inside waveshare-watch-rs

  1. Overview of waveshare-watch-rs

    main

    waveshare-watch-rs is a 100% Rust no_std smartwatch firmware designed specifically for the Waveshare ESP32-S3-Touch-AMOLED-2.06. It is a complete rewrite of the original C/C++ project (ESP-IDF + Arduino GFX + LVGL) into a single-binary Rust codebase.

    Key features include:

    • Display: 410×502 QSPI AMOLED running at 80 MHz via DMA.
    • Audio: I2S audio codec support.
    • Connectivity: 2.4 GHz WiFi with NTP synchronization.
    • Sensors: Capacitive touch, gyroscope/accelerometer (IMU), and hardware RTC.
    • Power Management: Advanced sleep/wake modes including an Apple Watch-style Always-On Display (AOD) and an event-driven main loop that keeps the CPU asleep >99% of the time on the watchface.
    • Applications: Includes a launcher with mini-games (Snake, 2048, Tetris, Flappy, Maze), a T9 keyboard, an MP3 player UI, and a Smart Home app (HTTP-based).
  2. How power management and screen states work

    main

    The firmware uses an event-driven model to maximize battery life, keeping the CPU parked most of the time. The Embassy executor only wakes on GPIO interrupts (Touch or Button) or periodic timers.

    Screen States

    StateBrightnessIdle triggerBehavior
    30xD0—Normal interactive, full bright
    20x4020 sDimming (transition), still interactive
    10x1840 sAOD: minimal HH:MM, pure black background (AMOLED pixels OFF), 1 update/min
    0DISPOFF10 min in AODSLPIN panel, QSPI idle, only GPIO IRQ for wake

    Transition Logic

    • Wake: A touch or button GPIO IRQ triggers an immediate return to State 3. If WiFi was disconnected due to inactivity, it will reconnect following the wake.
    • Idle Sequence:
      1. Interaction $\rightarrow$ State 3
      2. +20s idle $\rightarrow$ State 2 (Dim)
      3. +40s idle $\rightarrow$ State 1 (AOD) or State 0 (if not on Clock page)
      4. +300s idle $\rightarrow$ WiFi disconnect
      5. +600s idle $\rightarrow$ State 0 (Full OFF)
  3. How the display pipeline works

    main

    The display pipeline uses a 410×502 u16 RGB565 PSRAM framebuffer (402 KB) and supports different flushing strategies depending on the use case:

    • swap_and_flush(): Uses double buffering, ideal for games (e.g., Flappy Bird) to ensure zero tearing.
    • flush_vsync(te_pin): Uses a single buffer and waits for a Tearing Effect (TE) pulse on GPIO13 before sending pixels.
    • flush_region(x, y, w, h): Performs partial updates, used for watchface partial updates to save power/time.

    Data Flow: embedded-graphics calls $\rightarrow$ PSRAM Framebuffer $\rightarrow$ Co5300Display::set_addr_window $\rightarrow$ QspiBus::write_pixels $\rightarrow$ esp-hal SPI2 half-duplex quad-mode write $\rightarrow$ DMA transfer to GPIO SIO0..SIO3 at 80 MHz.

  4. Install the Xtensa ESP Rust toolchain

    main

    To build this project, you must install the Xtensa ESP Rust toolchain using espup. The project uses a rust-toolchain.toml file that pins the channel to esp.

    # Install espup and the toolchain
    cargo install espup
    espup install
    cargo install espup
    espup install
  5. Configure PSRAM for the Framebuffer

    main

    To use the PSRAM for the framebuffer, you must enable the psram feature in esp-hal and use the esp_alloc::psram_allocator! macro after calling esp_hal::init(). Note that the CO5300 display controller requires even widths for partial writes; the project handles this via even-rounding logic in flush_region.

    // Ensure features = ["psram"] is enabled in esp-hal
    esp_hal::init();
    esp_alloc::psram_allocator!();
  6. Correct Display Power-On Sequence

    main

    To properly wake the display and avoid initialization errors, follow the standard MIPI DCS transition order. The correct sequence is:

    1. SLPOUT (wait ~120 ms)
    2. DISPON (wait ~20 ms)

    Reversing this order (performing DISPON while the panel is still in SLPIN) will result in failure.

  7. Configure MSVC linker for Windows

    main

    If you are developing on Windows, you need the MSVC linker for host build scripts. Install "Desktop development with C++" via the Visual Studio Installer. Ensure link.exe is in your PATH.

    Example path configuration:

    export PATH="/c/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/MSVC/14.50.35717/bin/Hostx64/x64:$PATH"
  8. Configure the adaptive main loop tick budgets

    main

    The firmware adjusts its sleep duration (tick) based on the active application and screen state to balance responsiveness and power savings. When a user is actively touching the screen or holding the boot button, the tick rate is forced to ~60Hz (16ms) for smooth interaction.

    Typical tick budgets include:

    • Screen Off: 30s (deep idle)
    • AOD (Always-On Display): 10s (to check for minute changes)
    • Watchface (Clock, Gyro Off): 1s
    • Watchface (Clock, Gyro On): 33ms
    • Sensors Page: 100ms (10Hz IMU updates)
    • Games (Snake, 2048, etc.): 33ms (~30Hz)
    • Launcher/Settings/MP3: 100ms
  9. Configure WiFi credentials at compile-time

    main

    WiFi SSID and password are read at compile-time using the env!() macro. You must set these environment variables before running the build command.

    Linux / macOS / Git Bash (Windows):

    export WIFI_SSID="MyNetwork"
    export WIFI_PASS="MyPassword"

    PowerShell:

    $env:WIFI_SSID = "MyNetwork"
    $env:WIFI_PASS = "MyPassword"
    ```bash
    # Linux / macOS / Git Bash (Windows)
    export WIFI_SSID="MyNetwork"
    export WIFI_PASS="MyPassword"
    ``` mut