nimphea/boards/daisy_petal

Search:
Group by:
Source   Edit  

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
Source   Edit  

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
Source   Edit  
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
Source   Edit  
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
Source   Edit  
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 pressed(enc: var Encoder): bool {.importcpp: "#.Pressed()",
                                       header: "hid/encoder.h", ...raises: [],
                                       tags: [], forbids: [].}

Check if encoder button is currently pressed

Returns: True if button is pressed, false otherwise

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