nimphea_petal
Nim wrapper for Electro-Smith Daisy Petal development board.
The Daisy Petal is a guitar pedal platform featuring:
- 6 potentiometers (knobs)
- 1 expression pedal input
- 7 switches (4 footswitches + 3 toggles)
- Rotary encoder with button
- 8 RGB ring LEDs
- 4 footswitch LEDs
- Audio I/O (stereo, instrument level)
- USB for programming/power
Hardware Overview:
- Based on Daisy Seed (STM32H750, 480MHz ARM Cortex-M7)
- Guitar pedal form factor (Hammond 1590BB enclosure)
- Perfect for guitar effects, loopers, synth pedals
Example - Simple effect:
import nimphea/boards/daisy_petal var petal: DaisyPetal petal.init() proc audioCallback(input, output: AudioBuffer, size: int) = let gain = petal.getKnobValue(KNOB_1.cint) for i in 0..<size: output[0][i] = input[0][i] * gain # Left output[1][i] = input[1][i] * gain # Right petal.startAudio(audioCallback) while true: petal.processAllControls() # Knobs control RGB ring LED brightness let brightness = petal.getKnobValue(KNOB_1.cint) petal.setRingLed(RING_LED_1.cint, brightness, 0.0, 0.0) petal.updateLeds() petal.seed.delay(10)
Example - Footswitch control:
while true: petal.processDigitalControls() if petal.switches[SW_1.int].risingEdge(): # Footswitch 1 pressed petal.setFootswitchLed(FOOTSWITCH_LED_1.cint, 1.0) if petal.switches[SW_1.int].fallingEdge(): # Footswitch 1 released petal.setFootswitchLed(FOOTSWITCH_LED_1.cint, 0.0) petal.updateLeds()
Types
AnalogControl {.importcpp: "daisy::AnalogControl", header: "hid/ctrl.h".} = object
- Analog control (knob/expression pedal) wrapper Source Edit
DaisyPetal {.importcpp: "daisy::DaisyPetal".} = object seed*: DaisySeed ## Underlying Seed board encoder*: Encoder ## Rotary encoder with button knob*: array[6, AnalogControl] ## 6 potentiometers expression*: AnalogControl ## Expression pedal input switches*: array[7, switch.Switch] ## 7 switches (4 footswitches + 3 toggles) ring_led*: array[8, RgbLed] ## 8 RGB ring LEDs footswitch_led*: array[4, Led] ## 4 footswitch LEDs
-
Daisy Petal board handle
Contains all hardware peripherals pre-configured for the Petal platform.
Source Edit Encoder {.importcpp: "daisy::Encoder", header: "hid/encoder.h".} = object
- Rotary encoder with integrated button Source Edit
PetalFootswitchLed {.importcpp: "daisy::DaisyPetal::FootswitchLed", size: 4.} = enum FOOTSWITCH_LED_1 = 0, ## Footswitch 1 LED FOOTSWITCH_LED_2 = 1, ## Footswitch 2 LED FOOTSWITCH_LED_3 = 2, ## Footswitch 3 LED FOOTSWITCH_LED_4 = 3 ## Footswitch 4 LED
-
Footswitch LED identifiers
One LED per footswitch (white LED)
Source Edit PetalKnob {.importcpp: "daisy::DaisyPetal::Knob", size: 4.} = enum KNOB_1 = 0, ## Top-left knob KNOB_2 = 1, ## Top-center-left knob KNOB_3 = 2, ## Top-center-right knob KNOB_4 = 3, ## Top-right knob KNOB_5 = 4, ## Bottom-left knob KNOB_6 = 5 ## Bottom-right knob
-
Knob (potentiometer) identifiers
Layout: Top row left to right, then bottom row left to right
Source Edit PetalRingLed {.importcpp: "daisy::DaisyPetal::RingLed", size: 4.} = enum RING_LED_1 = 0, ## Ring LED 1 (12 o'clock position) RING_LED_2 = 1, ## Ring LED 2 (1:30 position) RING_LED_3 = 2, ## Ring LED 3 (3 o'clock position) RING_LED_4 = 3, ## Ring LED 4 (4:30 position) RING_LED_5 = 4, ## Ring LED 5 (6 o'clock position) RING_LED_6 = 5, ## Ring LED 6 (7:30 position) RING_LED_7 = 6, ## Ring LED 7 (9 o'clock position) RING_LED_8 = 7 ## Ring LED 8 (10:30 position)
-
RGB ring LED identifiers
8 RGB LEDs arranged in a ring around the encoder
Source Edit PetalSwitch {.importcpp: "daisy::DaisyPetal::Sw", size: 4.} = enum SW_1 = 0, ## Footswitch 1 (bottom-left) SW_2 = 1, ## Footswitch 2 (bottom-right) SW_3 = 2, ## Footswitch 3 (top-left) SW_4 = 3, ## Footswitch 4 (top-right) SW_5 = 4, ## Toggle switch 1 (left panel) SW_6 = 5, ## Toggle switch 2 (center panel) SW_7 = 6 ## Toggle switch 3 (right panel)
-
Switch identifiers
Footswitches (momentary):
- SW_1 through SW_4: Four stomp switches
Toggle switches:
- SW_5 through SW_7: Three SPDT toggle switches
Procs
proc audioBlockSize(this: var DaisyPetal): csize_t {. importcpp: "#.AudioBlockSize()", ...raises: [], tags: [], forbids: [].}
-
Get current audio block size
Returns: Samples per channel per callback
Source Edit proc audioCallbackRate(this: var DaisyPetal): cfloat {. importcpp: "#.AudioCallbackRate()", ...raises: [], tags: [], forbids: [].}
-
Get audio callback rate in Hz
Returns: Callback frequency (sampleRate / blockSize)
Source Edit proc audioSampleRate(this: var DaisyPetal): cfloat {. importcpp: "#.AudioSampleRate()", ...raises: [], tags: [], forbids: [].}
-
Get current audio sample rate in Hz
Returns: Sample rate as floating point (e.g., 48000.0)
Source Edit proc changeAudioCallback(this: var DaisyPetal; callback: AudioCallback) {. ...raises: [], tags: [], forbids: [].}
-
Change audio callback function while audio is running
Parameters:
- callback - New callback function
proc changeAudioCallback(this: var DaisyPetal; callback: InterleavingAudioCallback) {....raises: [], tags: [], forbids: [].}
-
Change audio callback function while audio is running (interleaved version)
Parameters:
- callback - New interleaved callback function
proc clearLeds(this: var DaisyPetal) {.importcpp: "#.ClearLeds()", ...raises: [], tags: [], forbids: [].}
-
Turn all LEDs off (ring LEDs and footswitch LEDs)
Note: Must call updateLeds() to apply changes.
Example:
petal.clearLeds() petal.updateLeds() # Apply the changes
Source Edit proc delayMs(this: var DaisyPetal; del: csize_t) {.importcpp: "#.DelayMs(#)", ...raises: [], tags: [], forbids: [].}
-
Delay execution for specified milliseconds
Parameters:
- del - Delay time in milliseconds
proc fallingEdge(enc: var Encoder): bool {.importcpp: "#.FallingEdge()", header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
-
Check if encoder button has falling edge (just released)
Returns: True on the first call after button release
Source Edit proc getExpression(this: var DaisyPetal): cfloat {. importcpp: "#.GetExpression()", ...raises: [], tags: [], forbids: [].}
-
Get expression pedal value as normalized float
Returns: Normalized value from 0.0 to 1.0
Example:
let volume = petal.getExpression() output = input * volume # Expression pedal controls volume
Note: Call processAnalogControls() regularly for accurate readings.
Source Edit proc getKnobValue(this: var DaisyPetal; k: cint): cfloat {. importcpp: "#.GetKnobValue(#)", ...raises: [], tags: [], forbids: [].}
-
Get knob value as normalized float
Parameters:
- k - Knob index (cast PetalKnob to cint: KNOB_1.cint)
Returns: Normalized value from 0.0 to 1.0
Example:
let gain = petal.getKnobValue(KNOB_1.cint) let freq = petal.getKnobValue(KNOB_2.cint) * 1000.0 # 0-1000 Hz
Note: Call processAnalogControls() regularly for accurate readings.
Source Edit proc increment(enc: var Encoder): int32 {.importcpp: "#.Increment()", header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
-
Get encoder increment since last call
Returns: Number of clicks (positive = clockwise, negative = counter-clockwise)
Example:
let delta = petal.encoder.increment() value += delta # Update value based on encoder rotation
Source Edit proc init(this: var DaisyPetal; boost: bool = false) {.importcpp: "#.Init(#)", ...raises: [], tags: [], forbids: [].}
-
Initialize the Daisy Petal board
Configures all hardware peripherals:
- Audio codec (AK4556, 24-bit/48kHz)
- ADC for knobs and expression pedal
- Encoder (rotary with button)
- Switches (footswitches and toggles)
- LED drivers (PCA9685, for ring and footswitch LEDs)
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 DaisyPetal) {. importcpp: "#.ProcessAllControls()", ...raises: [], tags: [], forbids: [].}
-
Process all control inputs (analog and digital)
Convenience method. Equivalent to:
petal.processAnalogControls() petal.processDigitalControls()
Call regularly in main loop (e.g., every 1ms).
Source Edit proc processAnalogControls(this: var DaisyPetal) {. importcpp: "#.ProcessAnalogControls()", ...raises: [], tags: [], forbids: [].}
-
Process analog control inputs (knobs and expression pedal)
Updates internal state of all knobs and expression pedal. Call regularly in main loop (e.g., every 1ms) for stable readings.
Example:
while true: petal.processAnalogControls() let knob1 = petal.getKnobValue(KNOB_1.cint) petal.delayMs(1)
Source Edit proc processDigitalControls(this: var DaisyPetal) {. importcpp: "#.ProcessDigitalControls()", ...raises: [], tags: [], forbids: [].}
-
Process digital control inputs (switches and encoder)
Updates internal state of all switches and encoder. Call regularly in main loop for edge detection.
Example:
while true: petal.processDigitalControls() if petal.switches[SW_1.int].risingEdge(): echo "Footswitch 1 pressed!" petal.delayMs(1)
Source Edit proc risingEdge(enc: var Encoder): bool {.importcpp: "#.RisingEdge()", header: "hid/encoder.h", ...raises: [], tags: [], forbids: [].}
-
Check if encoder button has rising edge (just pressed)
Returns: True on the first call after button press
Example:
if petal.encoder.risingEdge(): echo "Encoder button pressed!"
Source Edit proc setAudioBlockSize(this: var DaisyPetal; size: csize_t) {. importcpp: "#.SetAudioBlockSize(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio block size (samples per channel per callback)
Parameters:
- size - Block size in samples (default: 48)
Note: Audio must be stopped before changing block size.
Source Edit proc setAudioSampleRate(this: var DaisyPetal; samplerate: SampleRate) {. importcpp: "#.SetAudioSampleRate(#)", ...raises: [], tags: [], forbids: [].}
-
Set audio sample rate
Parameters:
- samplerate - Desired sample rate (e.g., SAI_48KHZ, SAI_96KHZ)
Note: Audio must be stopped before changing sample rate.
Source Edit proc setFootswitchLed(this: var DaisyPetal; idx: cint; bright: cfloat) {. importcpp: "#.SetFootswitchLed(#, #)", ...raises: [], tags: [], forbids: [].}
-
Set footswitch LED brightness
Parameters:
- idx - LED index (cast PetalFootswitchLed to cint: FOOTSWITCH_LED_1.cint)
- bright - Brightness (0.0 = off, 1.0 = full brightness)
Note: Must call updateLeds() to apply changes.
Example:
# Turn on footswitch LED 1 at full brightness petal.setFootswitchLed(FOOTSWITCH_LED_1.cint, 1.0) # Set footswitch LED 2 to 25% brightness petal.setFootswitchLed(FOOTSWITCH_LED_2.cint, 0.25) petal.updateLeds() # Apply changes
Source Edit proc setRingLed(this: var DaisyPetal; idx: cint; r: cfloat; g: cfloat; b: cfloat) {. importcpp: "#.SetRingLed(#, #, #, #)", ...raises: [], tags: [], forbids: [].}
-
Set RGB ring LED color
Parameters:
- idx - LED index (cast PetalRingLed to cint: RING_LED_1.cint)
- r - Red component (0.0 to 1.0)
- g - Green component (0.0 to 1.0)
- b - Blue component (0.0 to 1.0)
Note: Must call updateLeds() to apply changes.
Example:
# Set ring LED 1 to purple petal.setRingLed(RING_LED_1.cint, 1.0, 0.0, 1.0) # Set ring LED 2 to white at 50% brightness petal.setRingLed(RING_LED_2.cint, 0.5, 0.5, 0.5) petal.updateLeds() # Apply changes
Source Edit proc startAdc(this: var DaisyPetal) {.importcpp: "#.StartAdc()", ...raises: [], tags: [], forbids: [].}
-
Start analog-to-digital conversion for knobs and expression pedal
Note: Must be called before reading knob/expression values.
Source Edit proc startAudio(this: var DaisyPetal; callback: AudioCallback) {....raises: [], tags: [], forbids: [].}
-
Start audio processing with multichannel callback
Parameters:
- callback - Function called at audio rate for processing
Callback signature:
proc(input, output: AudioBuffer, size: int)
Example:
proc myCallback(input, output: AudioBuffer, size: int) = 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 petal.startAudio(myCallback)
Source Edit proc startAudio(this: var DaisyPetal; callback: InterleavingAudioCallback) {. ...raises: [], tags: [], forbids: [].}
-
Start audio processing with interleaved callback
Parameters:
- callback - Function called at audio rate for processing
Callback signature:
proc(input, output: InterleavedAudioBuffer, size: int)
Note: Size is total samples (stereo = size/2 frames)
Source Edit proc stopAdc(this: var DaisyPetal) {.importcpp: "#.StopAdc()", ...raises: [], tags: [], forbids: [].}
- Stop analog-to-digital conversion Source Edit
proc stopAudio(this: var DaisyPetal) {.importcpp: "#.StopAudio()", ...raises: [], tags: [], forbids: [].}
-
Stop audio processing
Stops the audio callback from being called.
Source Edit proc updateLeds(this: var DaisyPetal) {.importcpp: "#.UpdateLeds()", ...raises: [], tags: [], forbids: [].}
-
Update LED hardware to reflect current LED states
Must be called after setRingLed() or setFootswitchLed() to actually change the physical LEDs.
Example:
petal.setRingLed(RING_LED_1.cint, 1.0, 0.0, 0.0) # Set to red petal.setFootswitchLed(FOOTSWITCH_LED_1.cint, 1.0) # Turn on petal.updateLeds() # Apply all changes at once
Source Edit
Exports
-
pressed, Init, init, FallingEdge, SwitchType, GpioPull, Debounce, rawState, Switch, RawState, Init, debounce, RisingEdge, risingEdge, Pressed, timeHeldMs, init, TimeHeldMs, SwitchPolarity, fallingEdge, setAllToRaw, clearAllLeds, init, continueTransmission, PCA9685_PRESCALE, getDriverForLed, initializeDrivers, initializeBuffers, PCA9685_I2C_BASE_ADDRESS, setAllTo, getNumLeds, PCA9685_MODE2, delayMs, setLedRaw, setLed, getDriverChannelForLed, setLed, swapBuffersAndTransmit, LedDriverPca9685, GAMMA_TABLE, PCA9685_LED0, delayTicks, Pca9685TransmitBuffer, LedDriverConfig, LedDriverDmaBuffer, setAllTo, delayUs, setLed, swapBuffersAndTransmit, FieldLedDriver, getStartCycleForLed, PCA9685_MODE1