FSM manipulation in ISR

I am using a FSM in my alarm clock project (when an alarm is to be triggered, it goes into the alarm state). I am trying to decide how to implement the snooze functionality. My question is, can I transition my state machine in an ISR or will I have to get more creative? I am using the Wiring FSM Library. I’m just not really sure of the dos and don’ts of ISRs other than keep 'em short.

@tjp, you can use a switch()/case statement to build an FSM which will be simpler IMO. Whatever you use, the idea in your ISR is to change the NEXT state of the FMS and not actually execute the FMS. You can run the FSM in loop and set a flag in the ISR that, in the correct state (alarm is triggered), changes the state to “snooze”. Make sure that flag is declared as volatile to avoid problems. :smile:

1 Like

I know there are simpler ways to implement a FSM, I’m using this library because I like it’s features. Honestly, while I’m using a button today (because I have buttons on hand) my intention is to use the Sparkfun Gesture Sensor because I need ambient light detection and touchless snooze/dismiss would be kind of cool. I’m not quite ready to make that purchase yet, so for now, to test other aspects of the alarm clock, I’d like to use a single button to snooze/dismiss the alarm. I’m thinking I could use quick tap for snooze and long press for dismiss. I’m having trouble with exactly how I want to implement that, though. Everything I keep coming up with requires some amount of polling, in which case an interrupt seems wasted. On the other hand, I feel like polling isn’t very elegant. Any additional thoughts?

@tjp, take a look at the ClickButton library in the IDE. It is a polling library that you call in loop() but I really like using it since it works very well. :smile:

2 Likes

Will do, thanks!

I’m having issues with long click. Here’s how I have it setup:

button.debounceTime   = 20;
button.multiclickTime = 20; // Only allow 1 click
button.longClickTime  = 2000;

My loop:

void loop() {
  uint32_t start = millis();

  if(!Particle.connected()) {
    Particle.connect();
  }

  if(stateMachine.isInState(Alarm) || stateMachine.isInState(Snuze)) {
    button.Update();
  }

  stateMachine.update();

  loopTime = millis() - start;
}

I catch the short clicks but not the long clicks. Am I doing it wrong?

@jtp, you should be calling button.update() unconditionally in loop() and put the conditions in your button event handler instead. :smile:

  if(!Particle.connected()) {
    Particle.connect();
  }

I guess you also want to add a condition to this if() to prevent subsequent Particle.connect() attempts while one attempt is already running, since this would interfere with the original attempt delaying the actual process.
You could do this with a “one-shot” flag, which you reset once the connection is established, or a timout (via millis()-msConnecting > conTimeout), or you just waitFor(Particle.connected, conTimeout).

BTW: Does your loop actually take long enough to produce a “considerable” loopTime?

Can’t use waitFor, I need my alarm clock to be an alarm clock with or without a WiFi connection. lol I thought I read somewhere on here that calling Particle.connect() consecutively was safe because it set a flag or something. I’ll see if I can find that post again.

As for loopTime, it reports 0. That was just debugging code.

I’ve called it conditionally and unconditionally with the same results. Button event handler?

It is possible, that system behaviour has been improved that way, but I do remember a time where this definetly was an issue, and ever since I do the above and I'll rather stick with defensive programming than have to rely on "hear-say" and I'll also keep encouraging others to do the same :wink:

1 Like

Here’s what I got to work:

uint32_t clicks = 0;

void loop() {
  if(!Particle.connected()) {
    Particle.connect();
  }

  button.Update();
  clicks = button.clicks;

  stateMachine.update();
}

then I just use clicks when I need it

@tjp, too many libraries, too many functions!! There is no event handler for clickButton :flushed: The way you handle it now it exactly what I had in mind :wink:

1 Like