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.
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-core: portable logic
Section titled “crosspad-core: portable logic”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-gui: shared UI
Section titled “crosspad-gui: shared UI”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.
Platforms
Section titled “Platforms”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.
STM32 co-processor
Section titled “STM32 co-processor”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.
Host tools
Section titled “Host tools”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.
Read next
Section titled “Read next”- Where code belongs: which layer your new code goes in.
- Platform parity: what differs between the board and the simulator.
- Manifesto: the principles behind the layers.