Skip to content

Architecture Overview

This document provides an overview of the PolyBoard firmware architecture, including component relationships and data flow.

PolyBoard is custom firmware for the Novation Launchpad Pro. It transforms the device into an isomorphic MIDI controller with configurable scales, intervals, and pages.

┌─────────────────────────────────────────────────────────────┐
│ Launchpad Pro Hardware │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ 64 Pads │ │ Controls │ │ RGB LEDs (100) │ │
│ │ (pressure) │ │ (buttons) │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────▲──────────┘ │
└─────────┼────────────────┼─────────────────────┼────────────┘
│ │ │
▼ ▼ │
┌─────────────────────────────────────────────────────────────┐
│ SDK (launchpad_pro.a) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ HAL Functions: hal_plot_led, hal_send_midi, etc. │ │
│ └──────────────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Callbacks: app_surface_event, app_timer_event, etc. │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ PolyBoard Firmware │
│ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────────────┐ │
│ │ app.c │───▶│ app_state │◀───│ layout.c │ │
│ │ (entry) │ │ .c │ │ (LED rendering) │ │
│ └────┬─────┘ └─────┬─────┘ └──────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────────────┐ │
│ │ page.c │◀──▶│ note.c │ │ flash.c │ │
│ │ (pages) │ │ (scales) │ │ (persistence) │ │
│ └──────────┘ └───────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
User presses pad
┌──────────────────┐
│ app_surface_event│ SDK callback with pad index and velocity
└────────┬─────────┘
┌──────────────────┐
│ handle_play_mode │ Route to appropriate handler
└────────┬─────────┘
┌──────────────────┐
│ calculate_note │ Convert pad position to MIDI note
│ │ (considers root, scale, interval, transpose)
└────────┬─────────┘
┌──────────────────┐
│ send_note_on │ Send MIDI to configured channels
└────────┬─────────┘
┌──────────────────┐
│ set_note_active │ Track active notes for visual feedback
└────────┬─────────┘
┌──────────────────┐
│ update_pad_color │ Update LED to show active state
└──────────────────┘
// 1. Get pad position
u8 row = index / 10 - 1; // 0-7
u8 col = index % 10 - 1; // 0-7
// 2. Calculate semitone offset using interval
int semitone_offset = row * interval + col;
// 3. Apply scale-relative transposition (if any)
if (transpose != 0) {
semitone_offset = transpose_semitone_by_degree(
semitone_offset, scale_type, transpose);
}
// 4. Add root note and octave
int note = root + semitone_offset + octave * 12;
// 5. Clamp to MIDI range [0, 127]
note = clamp(note, 0, 127);

Volatile state that exists only during the current session:

typedef struct AppState {
AppMode mode; // Current UI mode
unsigned char active_notes[16]; // 128-bit bitmask
unsigned char dirty; // Flash save pending
unsigned char held_page; // Page button held
unsigned char held_shift; // Shift modifier
unsigned char scale_lock; // Global scale lock
unsigned char in_setup_mode;
unsigned char pad_notes[100]; // Note-to-pad mapping
} AppState;

Configuration that can be saved to flash:

typedef struct Page {
unsigned char root; // 0-11 (C through B)
unsigned char scale_type; // 0-9 (10 scales)
unsigned char octave; // 0-10
unsigned char interval_index; // 0-3 (m3, M3, P4, P5)
unsigned short midi_channels; // 16-bit bitmask
unsigned char aftertouch_mode; // 0=channel, 1=poly
unsigned char velocity_curve; // 0-3
signed char transpose; // -7 to +7
} Page;
typedef struct PageManager {
Page pages[4];
unsigned char current_page;
} PageManager;
┌──────────────┐
┌─────────▶│ MODE_PLAY │◀─────────┐
│ └──────┬───────┘ │
│ │ │
Circle released Circle pressed Double released
│ │ │
│ ▼ │
│ ┌──────────────┐ │
└──────────│MODE_ROOT_SEL │ │
└──────────────┘ │
┌──────────────┐ │
│MODE_SCALE_SEL│──────────┘
└──────────────┘
Double pressed
┌──────┴───────┐
│ MODE_PLAY │
└──────────────┘
ComponentResponsibility
app.cEvent routing, MIDI I/O, initialization
app_state.cRuntime state management
page.cPage configuration and management
note.cScale definitions, note calculations
layout.cLED rendering, display updates
flash.cPersistence, save/load configuration
surface.cButton/pad type detection
Flash (128KB)
├── Bootloader (fixed)
├── SDK Library (launchpad_pro.a)
├── PolyBoard Firmware (~24KB)
└── User Area (1KB)
└── FlashData structure
RAM (20KB)
├── Stack (~512 bytes)
├── .data (initialized globals)
│ └── led_cache[100] (400 bytes)
├── .bss (zero-initialized)
│ ├── AppState
│ ├── PageManager
│ └── render_pending
└── SDK internal buffers