Write firmware for Daisy hardware (DaisySeed, DaisyPetal, DaisyPod) using libDaisy and DaisySP. Use when the user mentions Daisy, DaisySeed, DaisyPetal, DaisyPod, libDaisy, DaisySP, embedded audio, bare-metal synth, or hardware DSP. Also use for projects like 909Pod, SLICERPOD, SAMPLEPOD, EMBRYO_FIRMWARE, DRUMPOD, or any project in DaisyWorkspace. Covers: AudioCallback pattern, volatile parameter passing, SoftPickup knob takeover, hardware init, DaisySP oscillators and effects, Makefile structure, and real-time safety rules.
You are helping write firmware for Daisy hardware using libDaisy and DaisySP.
The user's projects (909Pod, SLICERPOD, SAMPLEPOD) follow a strict workspace standard
defined in C:\DaisyWorkspace\DAISY_PROJECT_STANDARDS.md. Follow these patterns exactly.
The AudioCallback runs at 48kHz in a high-priority interrupt. Violating these rules causes audio dropouts, crashes, or undefined behaviour:
SAFE in AudioCallback NEVER in AudioCallback
-------------------------- --------------------------
Read volatile globals hw.ProcessAllControls()
DaisySP .Process() calls printf() / logging
Math, DSP, signal routing SD card access (f_read)
Write to output buffers Memory allocation (new/malloc)
Blocking delays (DelayMs)
I2C/SPI direct writes
Use volatile globals as the shared memory between the two contexts:
// Global declarations (top of main.cpp)
volatile float g_cutoff = 0.5f;
volatile float g_resonance = 0.5f;
volatile float g_drive = 0.0f;
volatile bool g_gate = false;
// Main loop — runs at ~1kHz, handles controls
void MainLoop(DaisyPod& hw, SoftPickup* pickups)
{
hw.ProcessAllControls(); // Safe here — never in AudioCallback
g_cutoff = pickups[0].Process(hw.knob1.Process());
g_resonance = pickups[1].Process(hw.knob2.Process());
if (hw.button1.RisingEdge()) g_gate = true;
if (hw.button1.FallingEdge()) g_gate = false;
}
// AudioCallback — reads volatiles, never calls hw.Process()
void AudioCallback(AudioHandle::InputBuffer in,
AudioHandle::OutputBuffer out,
size_t size)
{
float cutoff = g_cutoff; // local copy for consistency
float resonance = g_resonance;
filter.SetFreq(cutoff * 8000.f + 80.f);
filter.SetRes(resonance);
for (size_t i = 0; i < size; i++)
{
float sig = osc.Process() * env.Process(g_gate);
out[0][i] = filter.Process(sig);
out[1][i] = out[0][i];
}
}
Prevents parameter jumps when switching modes or layers. Used in every project.
// SoftPickup.h — copy this into new projects
class SoftPickup {