nimphea_pod
Nim wrapper for Electro-Smith Daisy Pod development board.
The Daisy Pod is a desktop synth/effect platform featuring:
- Rotary encoder with integrated button
- 2 potentiometers (knobs)
- 2 tactile buttons
- 2 RGB LEDs
- MIDI I/O (5-pin DIN)
- Audio I/O (line level, 24-bit/48kHz)
- USB for programming/power
Hardware Overview:
- Based on Daisy Seed (STM32H750, 480MHz ARM Cortex-M7)
- Compact desktop enclosure with panel-mount controls
- Perfect for tabletop synthesizers and effect processors
Example - Simple LED control:
import nimphea/boards/daisy_pod var pod: DaisyPod pod.init() while true: pod.processAllControls() let knob1 = pod.getKnobValue(KNOB_1) let knob2 = pod.getKnobValue(KNOB_2) pod.led1.setColor(knob1, 0.0, 1.0 - knob1) pod.led2.setColor(0.0, knob2, 1.0 - knob2) pod.updateLeds() pod.seed.delay(10)
Example - With audio processing:
var pod: DaisyPod var gain: float32 = 0.5 proc audioCallback(input, output: ptr ptr cfloat, size: csize_t) {.cdecl.} = for i in 0..<size: output[0][i] = input[0][i] * gain output[1][i] = input[1][i] * gain proc main() = pod.init() pod.startAdc() pod.startAudio(audioCallback) while true: pod.processAllControls() gain = pod.getKnobValue(KNOB_1) pod.seed.delay(1)
Types
AnalogControl {.importcpp: "daisy::AnalogControl", header: "hid/ctrl.h".} = object
- Analog control (knob/CV input) wrapper Source Edit
DaisyPod {.importcpp: "daisy::DaisyPod".} = object seed*: DaisySeed ## Underlying Seed board encoder*: Encoder ## Rotary encoder with integrated button knob1*: AnalogControl ## Left knob (pot) knob2*: AnalogControl ## Right knob (pot) knobs*: array[2, ptr AnalogControl] ## Array of pointers to knobs button1*: Switch ## Left tactile button button2*: Switch ## Right tactile button buttons*: array[2, ptr Switch] ## Array of pointers to buttons led1*: RgbLed ## Left RGB LED led2*: RgbLed ## Right RGB LED midi*: MidiUartHandler ## MIDI UART handler (5-pin DIN)
-
Daisy Pod board handle
Contains all hardware peripherals pre-configured for the Pod platform. Access individual components as public members.
Source Edit MidiUartHandler {.importcpp: "daisy::MidiHandler<daisy::MidiUartTransport>", header: "hid/midi.h".} = object
- MIDI UART handler Source Edit
MidiUartTransport {.importcpp: "daisy::MidiUartTransport", header: "hid/midi.h".} = object
- MIDI UART transport Source Edit
Procs
proc audioBlockSize(this: var DaisyPod): csize_t {. importcpp: "#.AudioBlockSize()", ...raises: [], tags: [], forbids: [].}
-
Get current audio block size
Returns: Samples per channel per callback
Source Edit proc audioCallbackRate(this: var DaisyPod): cfloat {. importcpp: "#.AudioCallbackRate()", ...raises: [], tags: [], forbids: [].}
-
Get audio callback rate
Returns: Callbacks per second (Hz)
Formula: sample_rate / block_size Example: 48000 / 48 = 1000 Hz (1ms per callback)
Source Edit proc audioSampleRate(this: var DaisyPod): cfloat {. importcpp: "#.AudioSampleRate()", ...raises: [], tags: [], forbids: [].}
-
Get current audio sample rate
Returns: Sample rate in Hz (e.g., 48000.0)
Source Edit proc changeAudioCallback(pod: var DaisyPod; callback: AudioCallback) {. ...raises: [], tags: [], forbids: [].}
- Change the audio callback while audio is running Source Edit
proc changeAudioCallback(pod: var DaisyPod; callback: InterleavingAudioCallback) {. ...raises: [], tags: [], forbids: [].}
- Change the interleaved audio callback while audio is running Source Edit
proc changeAudioCallback(this: var DaisyPod; cb: AudioCallbackC) {. importcpp: "#.ChangeAudioCallback(#)", ...raises: [], tags: [], forbids: [].}
-
Switch to a different multi-channel audio callback
Can be called while audio is running.
Parameters:
- cb - New audio callback function
proc changeAudioCallback(this: var DaisyPod; cb: InterleavingAudioCallbackC) {. importcpp: "#.ChangeAudioCallback(#)", ...raises: [], tags: [], forbids: [].}
-
Switch to a different interleaved audio callback
Can be called while audio is running.
Parameters:
- cb - New audio callback function
proc delayMs(this: var DaisyPod; del: csize_t) {.importcpp: "#.DelayMs(#)", ...raises: [], tags: [], forbids: [].}
-
Delay execution for specified milliseconds
Parameters:
- del - Delay time in milliseconds
Note: Blocking delay. For audio applications, prefer timing based on audio callback rate or use timers.
Source Edit proc fallingEdge(enc: var Encoder): bool {.importcpp: "#.FallingEdge()", header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
- Check if encoder button has falling edge Source Edit
proc fallingEdge(sw: var Switch): bool {.importcpp: "#.FallingEdge()", header: "hid/switch.h", ...raises: [], tags: [], forbids: [].}
- Check if switch has a falling edge (just released) Source Edit
proc getKnobValue(this: var DaisyPod; k: PodKnob): cfloat {. importcpp: "#.GetKnobValue(#)", ...raises: [], tags: [], forbids: [].}
-
Read knob value
Parameters:
- k - Knob identifier (KNOB_1 or KNOB_2)
Returns: Normalized value from 0.0 to 1.0
Note: Call processAnalogControls() regularly for accurate readings.
Source Edit proc init(this: var DaisyPod; boost: bool = false) {.importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
-
Initialize the Daisy Pod board
Configures all hardware peripherals:
- Audio codec (AK4556, 24-bit/48kHz)
- ADC for knobs
- Encoder with button
- Switches
- RGB LEDs
- MIDI UART
Parameters:
- boost - Enable CPU boost mode (480MHz, default is 400MHz)
Note: Audio and ADC must be started separately with startAudio() and startAdc().
Source Edit proc processAllControls(this: var DaisyPod) {. importcpp: "#.ProcessAllControls()", ...raises: [], tags: [], forbids: [].}
-
Update all control inputs (analog + digital)
Convenience method. Equivalent to:
pod.processAnalogControls() pod.processDigitalControls()
Call regularly in main loop (e.g., every 1ms).
Source Edit proc processAnalogControls(this: var DaisyPod) {. importcpp: "#.ProcessAnalogControls()", ...raises: [], tags: [], forbids: [].}
-
Update analog control values (knobs)
Call regularly in main loop (e.g., every 1-10ms) for smooth readings. Applies filtering to ADC values for stability.
Source Edit proc processDigitalControls(this: var DaisyPod) {. importcpp: "#.ProcessDigitalControls()", ...raises: [], tags: [], forbids: [].}
-
Update digital control states (encoder, buttons)
Call regularly in main loop (e.g., every 1ms) for responsive input. Handles debouncing and edge detection.
Source Edit proc risingEdge(enc: var Encoder): bool {.importcpp: "#.RisingEdge()", header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
- Check if encoder button has rising edge Source Edit
proc risingEdge(sw: var Switch): bool {.importcpp: "#.RisingEdge()", header: "hid/switch.h", ...raises: [], tags: [], forbids: [].}
- Check if switch has a rising edge (just pressed) Source Edit
proc setAudioBlockSize(this: var DaisyPod; blocksize: csize_t) {. importcpp: "#.SetAudioBlockSize(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio block size (samples per callback)
Must be called before startAudio().
Parameters:
- blocksize - Number of samples per channel (default: 48)
Smaller blocks = lower latency, higher CPU load. Larger blocks = higher latency, lower CPU load.
Source Edit proc setAudioSampleRate(this: var DaisyPod; samplerate: SampleRate) {. importcpp: "#.SetAudioSampleRate(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio sample rate
Must be called before startAudio().
Parameters:
- samplerate - Target sample rate (e.g., SAI_48KHZ, SAI_96KHZ)
Supported rates: 8kHz, 16kHz, 32kHz, 48kHz, 96kHz
Source Edit proc startAudio(pod: var DaisyPod; callback: AudioCallback) {....raises: [], tags: [], forbids: [].}
-
Start audio processing with multi-channel (non-interleaved) callback
The callback receives separate channels as arrays of float samples.
Example:
proc audioCallback(input, output: AudioBuffer, size: int) {.cdecl.} = for i in 0..<size: output[0][i] = input[0][i] * 0.5 # Left channel output[1][i] = input[1][i] * 0.5 # Right channel pod.startAudio(audioCallback)
Source Edit proc startAudio(pod: var DaisyPod; callback: InterleavingAudioCallback) {. ...raises: [], tags: [], forbids: [].}
-
Start audio processing with interleaved callback
The callback receives interleaved samples (L, R, L, R, ...)
Example:
proc audioCallback(input, output: InterleavedAudioBuffer, size: int) {.cdecl.} = for i in 0..<size: output[i * 2] = input[i * 2] * 0.5 # Left output[i * 2 + 1] = input[i * 2 + 1] * 0.5 # Right pod.startAudio(audioCallback)
Source Edit proc startAudio(this: var DaisyPod; cb: AudioCallbackC) {. importcpp: "#.StartAudio(#)", ...raises: [], tags: [], forbids: [].}
-
Start audio processing with multi-channel callback
Parameters:
- cb - Audio callback function with separate channels
Callback signature:
proc audioCallback(input, output: ptr ptr cfloat, size: csize_t) {.cdecl.}
Audio format: input0 = left, input1 = right
Source Edit proc timeHeldMs(enc: var Encoder): cfloat {.importcpp: "#.TimeHeldMs()", header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
- Get time encoder button has been held Source Edit
proc timeHeldMs(sw: var Switch): cfloat {.importcpp: "#.TimeHeldMs()", header: "hid/switch.h", ...raises: [], tags: [], forbids: [].}
- Get time switch has been held in milliseconds Source Edit
proc updateLeds(this: var DaisyPod) {.importcpp: "#.UpdateLeds()", ...raises: [], tags: [], forbids: [].}
-
Apply LED color changes to hardware
Must be called after setting LED colors with:
- pod.led1.setColor(r, g, b)
- pod.led2.setColor(r, g, b)
Example:
pod.led1.setColor(1.0, 0.0, 0.0) # Red pod.led2.setColor(0.0, 1.0, 0.0) # Green pod.updateLeds() # Apply to hardware
Source Edit