Question: Polling switches and LED's using state machine

Edit: updated with simple proof of concept

I’m experimenting with a state machine interface design on the POD (libDaisy) and running into some weird behavior. Maybe I’m missing something obvious and/or just need to dig more into the docs. A second pair of eyes, with experience, may help. I have tried the Switch, LED, and, Encoder sample programs without issue. Maybe I’m running into a timing issue?

The purpose of this program:

  1. At startup, state is INIT and led should be light white.
  2. On button press, LED to light red, state is PRESSED
    3a. If button released, wait for timeout, then reset LED to light white, state to INIT
    3b. If button held longer than 2 seconds, LED to bright red, state to HELD
  3. If button released, wait for timeout, then reset LED to light white, state to INIT

The current code is at https://github.com/angrycloud/Switch-States/blob/main/Tape.cpp

What I’m seeing during testing:

  1. led lit to light white after delay, expected
  2. short press clears led, back to white after delay
  3. after held press, led resets to white again, subsequent press is bright red.

I’ve tried processing the HID controls in the audio callback as well as the main loop but I can’t get anything consistent. Any suggestion or examples?

This line:

if (state == S_INIT && pod.seed.system.GetNow() - released >= LED_TIMEOUT) {

I think >= has higher precedence than &&.

I would write it like this

if ((state == S_INIT) && (pod.seed.system.GetNow() - released >= LED_TIMEOUT)) {

Does this change the behaviour?

I’d do the same on this line:

		} else if (pod.button1.Pressed() && pod.button1.TimeHeldMs() > LONG_HOLD) {

to

		} else if (pod.button1.Pressed() && (pod.button1.TimeHeldMs() > LONG_HOLD)) {

Does this help?

Same behavior either way.

The POD init is setting the update freq of the switches to the audio callback rate, which is used to keep track of hold times. I get the same behavior calling HID fromthe callback as well.

I will admit that Pressed() && TimeHeldMs() is superfluous since TimeHeldMs() internally calls Pressed()

Sorry, I don’t have a Pod so I can’t test it.

What happens if you change the update freq of the button to something (a lot) less? This is how I set up my button. I’ve put all the UI code in the main() loop.

button1.Init(hardware.GetPin(28), 10);

I started mapping out exactly what the POD init is actually doing. I’m going to be building a custom board for the resulting project so I might as well do my own hardware init. I’ll see if changing the update freq helps. I’ve got some day job work first so that’ll have to wait until later today.

It’s got to be timing related but I’m evaluating the buttons after every debounce so even a fast callback shouldn’t miss events. It doesn’t need to be 48kHz though. A good part of this is that I don’t fully know what’s going on in the background (setup and handled by the POD init)

Thank you for the suggestions.

1 Like

Ok, System:Delay is blocking, says it clearly in the doc. I just didn’t think it would foul up the PWM for the RGB LED’s on the POD. The code below blinks depending on the delay.

int main(void) {
    pod.Init();

    while(1) {
		pod.button1.Debounce();

		if (pod.button1.TimeHeldMs() > 2000) {
			pod.led1.Set(0.75, 0, 0);
		} else if (pod.button1.Pressed()) {
			pod.led1.Set(0.3, 0, 0);
		} else {
			pod.led1.Set(0, 0.3, 0);
		}
		
		pod.UpdateLeds();

		System::Delay(1);
	}
}

After deleting the delay, the test code above works (in main while loop) with the exception of TimeHeldMs. Since debounce just adds a fixed value (1 / updateFreq) on every call, calling debounce more or less often wil affect the value reported by TimeHeldMs.

Since DaisyPOD init is setting update freq to audioCallback rate, calling debounce from main instead of the callback renders TimeHeldMs useless.

Possible solutions (bear in mind I’m still relearning all this)

  1. Change TimeHeldMs to use time call difference to actually calculate time held independent of debounce rate. (EDIT: using System::GetNow() to calculate in Switch works)
  2. Use a separate HID callback at a fixed rate.
1 Like

Thanks for sharing your findings!

I also re-read the headers for rgb_led and realized I was making another incorrect assumption (part of the learning process)

The update method has to be called regularly for PWM to work.

/** Updates the PWM of the LED based on the current values.
    Should be called at a regular interval. (i.e. 1kHz/1ms)
    */
    void Update();

It just occurred to me that the last arduino board I was playing with had a peripheral LED driver that you only needed to update if the value changed. That also explains why using a blocking delay was blanking the LED’s

At least this little learning exercise forced me to actually look at the libDaisy code.

1 Like

Thanks!

I’ve also realized, as you seem to, that an Arduino background (and in my case, many hours with the Mozzi lib), can be helpful, but also misleading.

I’ve realised that the Arduino ecosystem hides a lot of stuff, that you now have to know.

I like that.

1 Like