Skip to content

Architecture

CrossPad is one portable C++ core and one shared LVGL UI, built into two platforms: the ESP-IDF firmware for the ESP32-S3 board and a desktop simulator. Apps plug in on top, an STM32 co-processor handles the pads and power, and host tools drive both platforms. The reasons behind this split are in the Manifesto.

CrossPad's layers: your app on crosspad-gui and crosspad-core, which run on the firmware (platform-idf) or the simulator (crosspad-pc). Five rows, top to bottom. Your app, from the crosspad-apps registry or your own repo, uses core interfaces and gui widgets. Under it sit the two shared layers: crosspad-gui, the shared UI (LVGL widgets, AppBase, the launcher, the settings screens, and its own IGuiPlatform and IFileSystem interfaces), and crosspad-core, the portable logic (event bus, pads, MIDI, settings, sequencer, kits, audio graph, and the I*.hpp interfaces). Their interfaces are implemented by one of two platforms: platform-idf, the ESP-IDF firmware for the ESP32-S3 board with the board support package and the Esp* adapters, or crosspad-pc, the SDL2 desktop simulator with the Pc* adapters. Under the firmware is the STM32 co-processor, which scans the pads and runs the power button and the charger, linked to the ESP32-S3 by UART (MIDI and SysEx) and, on v2 boards, I2C. Under the simulator are your keyboard and mouse and the stm32_emu window, which stands in for the STM32. Host tools on your computer drive both platforms from outside: crosspad-hil runs tests on a real board over USB, and crosspad-mcp gives AI assistants build, flash, simulator and trace tools. Your app from the crosspad-apps registry, or your own repo uses core interfaces and gui widgets Shared: one copy, built into both platforms crosspad-gui shared UI LVGL widgets, AppBase, launcher, settings screens its own IGuiPlatform, IFileSystem crosspad-core portable logic event bus, pads, MIDI, settings, sequencer, kits, audio graph; the I*.hpp interfaces each platform implements the core and gui interfaces platform-idf firmware ESP-IDF build for the ESP32-S3 board board support package, Esp* adapters crosspad-pc simulator SDL2 window on your computer Pc* adapters UART (MIDI and SysEx), I2C on v2 boards STM32 co-processor pads, power button, charger Keyboard and mouse a stm32_emu window stands in for the STM32 Host tools on your computer, outside both platforms crosspad-hil: tests on a real board over USB · crosspad-mcp: build, flash, simulator and trace tools for AI assistants

The layers from top to bottom. Blue is your code; orange is hardware, or what stands in for it on the simulator.

Read the diagram from the top down. Your app sits on the two shared layers. The shared layers sit on a platform. Each platform turns the shared interfaces into real hardware calls or desktop calls. The rest of this page walks through each box.

CrossPad/crosspad-core holds the logic that does not care which chip it runs on. That is the event bus, pad handling, MIDI, settings, the sequencer, kits and the audio graph. You write it in portable C++, and both platforms compile the same files. A change here reaches the board and the simulator at once.

Core is portable C++. Chip-specific drivers such as EspEventBus, FreeRtosEventBus, the DRV2605 haptic driver and the esp-dsp FFT sit behind platform guards, and the simulator’s build leaves them out.

Core also defines the interfaces that platforms implement. Both platforms implement IClock, ILedStrip, IKeyValueStore, IMidiOutput and IBleMidi. Only the firmware implements IStm32Transport, IImu and IVibration, because the simulator has no co-processor and no sensors. Platform parity lists the full set of differences.

The interface list grows with each release. Read it from the code, or ask the crosspad_list_interfaces MCP tool, rather than trusting an old list. This page names only the interfaces that explain the split.

CrossPad/crosspad-gui holds everything you see on the screen. It has the LVGL widgets, AppBase, the launcher and the settings screens. Every platform uses the same copy, so a screen you build looks and behaves the same on the LCD and in the simulator window.

The UI needs a few things from its host, such as files and platform details. For these, gui has its own small interfaces: IGuiPlatform and IFileSystem. Each platform implements them the same way it implements the core interfaces. The UI never calls SDL2, and its one ESP-IDF call (a PSRAM allocation in the scope view) sits behind an ESP_PLATFORM guard.

A platform repo turns the shared layers into a program for one target. It keeps only what cannot be shared: drivers, HAL bindings and build glue. Where code belongs gives the rule for deciding what that is.

CrossPad/platform-idf is the ESP-IDF firmware. It pulls core, gui and the bundled apps in as git submodules under components/, and builds each one as an ESP-IDF component. The board support package (BSP) and the adapters live here. Adapters are named Esp*: EspClock, EspLedStrip, EspBleMidi and more.

CrossPad/crosspad-pc is the SDL2 desktop simulator. Core and gui are submodules under lib/, and the simulator’s CMake compiles their sources with a recursive glob. Its adapters are named Pc*: PcClock, PcLedStrip, PcMidi and more. The simulator draws the whole device body, LCD, pads and encoder in one window.

Both platforms pin the core and gui versions they were written against. A major version mismatch stops the build, and a minor or patch drift prints a warning on every build. This keeps the two platforms honest when one of them moves to a newer core.

An older Arduino variant also exists. It does not follow this layout; see Arduino platform (platform-arduino).

Each app is its own repo and is listed in the app registry. A platform adds it as a submodule: under components/ in platform-idf, under src/apps/ in crosspad-pc. The two platforms do not ship the same set of apps; Platform parity lists both.

An app registers itself with REGISTER_APP, or REGISTER_APP_PL for an app with pad logic. At build time each platform scans the app sources for these macros and generates its app registry. You never add your app to a list by hand. See App manager.

Write your app against core interfaces and gui widgets only; do not add #ifdef ESP_PLATFORM blocks or FreeRTOS includes to it.

An STM32G0B1RBT6 sits next to the ESP32-S3. Its firmware lives in CrossPad/CrossPad_STM32_r2. It scans the pads, runs the charger and the power button, and talks to the ESP over a UART that carries MIDI messages (the pads: a note for a hit, aftertouch for pressure, a control change for the finger’s position) and SysEx (commands). On v2 boards (PCB r20) there is also an I2C link.

The LEDs are split between the two chips. The ESP drives the 16 pad RGB LEDs through EspLedStrip. The STM32 drives the LCD backlight and two status LEDs: LED1 shows power and charging, LED2 blinks on MIDI activity.

The board has two USB ports. The STM32’s port is the STM32 bridge port (0483:5740): it carries the ESP console and UART flashing. The ESP has its own ESP USB port for USB MIDI and USB audio. OTA and commands need its CDC profile, which shows up as 303A:3456 (on the board: Settings → USB → Mode → MIDI + Serial). The default MIDI + Audio profile has no CDC.

The STM32 firmware is bundled into the ESP image. At boot the ESP offers it as an update, so you never flash the STM32 by hand as a new user. See STM32 in-field update.

Two tools run on your computer and drive the platforms from outside. Neither is part of the firmware or the simulator.

CrossPad/crosspad-hil drives a board over USB for automated hardware tests. See Test on hardware (HIL).

CrossPad/crosspad-mcp is an MCP server for AI coding assistants. It gives them build, flash, simulator and trace tools across all the repos. See the MCP tools reference.