← Back to portfolio Français

Puara framework

Ecosystem architecture overview — projects: puara-module, puara-module-templates, puara-arduino, puara-gestures

Developed by the Société des Arts Technologiques (SAT) and the Input Devices and Music Interaction Laboratory (IDMIL)

Purpose

Puara is a framework for building networked musical controllers and interactive installations on ESP32 boards. Rather than one repository, it's an ecosystem of four repos with distinct, complementary roles: a core device library, a sensor-to-gesture library, and two IDE-specific packagings of ready-to-flash example firmware built on top of both.

Ecosystem overview

How the repos fit together

Puara framework ecosystem overview The core puara-module and puara-gestures libraries are packaged into two synchronized sets of ready-to-flash templates, one for PlatformIO and one for the Arduino IDE, which together produce a flashed ESP32 device. Core libraries puara-module + puara-gestures puara-module-templates PlatformIO + VS Code puara-arduino Arduino 2.0 IDE Kept synchronized: same templates, two IDEs Flashed ESP32 device Firmware + filesystem uploaded Firmware and filesystem (config.json, settings.json, web pages) are built and uploaded as two separate steps
Two core libraries, two synchronized IDE packagings, one device

puara-module and puara-gestures are the two core libraries — independent of each other, each usable on its own. The two template repos both consume them to produce ready-to-flash example projects, one packaging for each supported toolchain; the projects are kept equivalent across both so a person can follow either the PlatformIO or the Arduino path and get the same behavior. Either path ends the same way: build and upload firmware, then separately build and upload the filesystem image (LittleFS by default, SPIFFS optionally), onto a physical ESP32 board.

On-device runtime, shared by every template

On-device runtime shared by every Puara template Every Puara template follows the same runtime shape: bring up WiFi in station, access point, or both, serve a browser-based config and settings web server backed by the filesystem, run the user program loop reading sensors and optionally computing gesture descriptors, and send or receive OSC or BLE, with settings changes applied live without reflashing. WiFi bring-up STA-AP, AP-only, or STA-only Web server (LittleFS) Config, scan, and settings pages User program loop Sensors, optional puara-gestures OSC / BLE output CNMAT OSC send/receive or BLE advertising onSettingsChanged() applies new settings live, no rebuild or reflash needed
The shared runtime shape across every Puara template
  1. WiFi bring-up — on boot, the module manager reads config.json from the filesystem and attempts one of three modes: Station-Access Point (STA-AP), the default, where the device both joins an existing network and creates its own; Access Point (AP) only, where it only creates its own network; or Station (STA) only (persistent_AP=0), where it only joins an existing network and never advertises its own, useful for reducing WiFi pollution and improving security.
  2. Web server — a browser-accessible server exposes Config, Scan, and Settings pages, reachable either by IP address (e.g. http://192.168.4.1 in AP mode, or the address from puara.staIP() in STA/STA-AP mode) or, if mDNS is enabled (default), by hostname (e.g. http://puara_001.local). Through this UI, values defined in /data/settings.json can be edited and persist across reboots, and are read in code with puara.getVarText("name") / puara.getVarNumber("name").
  3. User program loop — the sketch's own logic runs here: reading sensors, optionally running them through puara-gestures descriptors, and reacting to events.
  4. OSC / BLE output — depending on the template, this stage sends sensor or gesture data out as OSC messages (via CNMAT's OSC library), receives incoming OSC to control outputs, or broadcasts data connectionlessly over BLE advertising.

A registered onSettingsChanged() callback re-reads updated settings whenever the user saves changes through the web UI, so parameters like OSC IP/port take effect immediately without a rebuild.

Firmware and filesystem are two separate uploads. The firmware is the compiled program logic; the filesystem (LittleFS/SPIFFS) holds config.json, settings.json, and the web UI's HTML/CSS — both must be built and uploaded (PlatformIO: Upload Filesystem Image + Upload; Arduino IDE: the Arduino-LittleFS-Upload plugin's Upload LittleFS to Pico/ESP8266/ESP32 command, plus the normal sketch upload).

Available templates / examples

Both puara-module-templates (PlatformIO) and puara-arduino (Arduino IDE) ship the same underlying examples; the table below notes which are currently available in each.

TemplateWhat it demonstratesIn templatesIn arduino
BasicCore module functionality: reads settings, prints dummy sensor data to serial✅✅
OSC-SendSends sensor data as OSC to a configurable IP/port✅✅
OSC-ReceiveReceives OSC (e.g. /led/brightness f 0.34) and drives an output✅✅
OSC-DuplexCombines OSC-Send and OSC-Receive in one sketch✅✅
BLE advertisingBroadcasts CBOR-encoded sensor data via BLE advertising, no connection needed✅✅
Basic GesturesExtends Basic with puara-gestures gesture recognition on a pseudo-IMU✅—
Button OSCButton tap/double-tap/hold detection via puara-gestures, sent as OSC✅—
Libmapper OSCRegisters signals with libmapper alongside OSC messaging✅—

Puara Gestures: descriptors and utilities

puara-gestures is intentionally decoupled from networking or hardware specifics — it just turns numeric sensor streams into gesture-shaped signals:

DescriptorPurpose
Jab, Jab2D, Jab3DSimple motion-burst detectors across 1, 2, or 3 axes
Shake, Shake2D, Shake3DSmooth motion-energy tracking for vibration/shaking, with a threshold and decay
Tilt, RollOrientation signals from full 9DoF IMU data
Tilt_RollFast roll/tilt from accelerometer data alone (no gyro/magnetometer needed)
TouchArrayGestureDetectorBrush/rub and swipe-style features for touch sensor arrays
ButtonTap, double-tap, hold, and press-count tracking from a digital input

Supporting utils/ helpers: rollingminmax (sliding min/max), leakyintegrator (smooth decay / energy tracking), maprange (range scaling), smooth (moving average), threshold (clamping), wrap (angle wrapping), discretizer (change detection), circularbuffer (fixed-size history).

puara-gestures builds standalone via CMake, or as a PlatformIO lib_deps entry (https://github.com/Puara/puara-gestures.git#v0.2.0), optionally paired with https://github.com/malloch/IMU_Sensor_Fusion.git for full IMU sensor fusion support.

Example data flow: BLE advertising → OSC

The BLE advertising template is the one example that explicitly crosses into a second, off-repo tool:

BLE advertising to OSC bridge The ble-advertising example broadcasts CBOR-encoded sensor data as BLE advertising packets without any connection, which a separate Python script running on a host computer decodes and forwards as OSC messages. ESP32 (ble-advertising) CBOR-encoded sensor payload BLE advertising No connection, ~50 Hz broadcast ble-cbor-to-osc.py Separate host-side Python script OSC out 127.0.0.1:9001 (configurable)
Connectionless BLE broadcast, decoded and forwarded as OSC
  1. The ESP32 broadcasts sensor data as CBOR payloads inside BLE manufacturer data, at a configurable frequency (default 50 Hz) — no BLE connection is established, so a single receiver can pick up data from many devices at once (tested at roughly 120 devices over a 0–150 m range, updating every 500 ms).
  2. A separate Python script, ble-cbor-to-osc.py (its own GitLab repo, run in its own virtual environment), listens for these BLE advertisements, decodes the CBOR payload, and forwards the values as OSC messages to 127.0.0.1:9001 by default (configurable).

This pattern — connectionless BLE broadcast in, OSC out on the receiving computer — is suited to distributed sensor networks and installations with many simultaneous low-data-rate devices, where per-device BLE connections wouldn't scale.

Getting started

PlatformIO (puara-module-templates)

  1. Install VS Code + the PlatformIO extension.
  2. git clone https://github.com/Puara/puara-module-templates.git
  3. In PlatformIO, "Pick a folder" and open one of the template subfolders (e.g. basic/).
  4. Set the board field in platformio.ini to match your hardware.
  5. Edit the template, then Build/Upload the firmware and Build/Upload Filesystem Image separately.

Arduino IDE (puara-arduino)

  1. Install the Arduino 2.0 IDE, the ESP32 board package (Boards Manager → search "esp32" → install Espressif Systems), and the Arduino-LittleFS-Upload plugin.
  2. Install the puara-module library via the Library Manager.
  3. File > Examples > puara-module → pick a template.
  4. Set board/port, and under Tools > Partition Scheme pick a minimal-SPIFFS-style option to leave enough room for the program.
  5. Edit data/config.json (WiFi SSID/PSK) and data/settings.json (custom variables) via Sketch > Show Sketch Folder.
  6. Upload the sketch normally, then use the LittleFS-Upload plugin's Upload LittleFS to Pico/ESP8266/ESP32 command for the filesystem.

Tested boards

BoardStatusPlatformIO IDArduino IDE board
M5StickC✅m5stick-cM5StickC
TinyPICO✅tinypicoUM TinyPico
ESP32-C3-WROOM-02 / DevKitC-02✅esp32-c3-devkitc-02ESP32C3 Dev Module
Adafruit ESP32-S3 Feather⚠️ manual boot-mode entry, slow serialadafruit_feather_esp32s3Adafruit Feather ESP32-S3 2MB PSRAM
Adafruit ESP32-S3 TFT Feather⚠️ same caveatsadafruit_feather_esp32s3_tftAdafruit Feather ESP32-S3 TFT
Seeed XIAO S3⚠️ same caveatsseeed_xiao_esp32s3XIAO_ESP32S3
DOIT ESP32 DevKit V1⚠️ no minimal partition option in Arduino IDE (works via PlatformIO)esp32doit-devkit-v1DOIT ESP32 DEVKIT V1

Licensing

All four repositories are MIT-licensed, unless a specific file states otherwise.