ProffieOS Button Code for Three-State Lightsaber Control

Hi again, I’m trying to code a custom prop file that uses the power button input to cycle between 3 states: off, on, and retracting. I want to override the normal power button inputs so that the sound and lights trigger only as I define it in my custom prop file - would the implementation below in my custom prop file under the loop() section be the right way to do it?

Alternatively, if I don’t want my button presses to activate the saber when I don’t want it to activate, could I just redefine the button in my config file to something like aux2?

    bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {
	if (EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_OFF) || EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_ON)) {
    // State machine for saber control
    switch (power_state_) {
      case OFF:
        if (((EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_OFF) || EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_ON)) && !is_on_ && millis() > activation_buffer_) {
          ActivateSaber();
          power_state_ = ON;
	  activation_buffer_ = millis() + 8000;
        }
        break;
        
      case ON:
        if (((EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_OFF) || EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_ON)) && millis() > activation_buffer_) {
          BeginRetraction();
          power_state_ = RETRACTING;
	  activation_buffer_ = millis() + 2000;
        }
        break;

      case RETRACTING:
        if (((EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_OFF) || EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_ON)) && millis() > activation_buffer_) {
          DeactivateSaber();
          power_state_ = OFF;
	  activation_buffer_ = millis() + 20000;
        }
        break;
        
    }
   }
  }

No, that would not be how to do it.

I suggest reading this:

This may also apply:
https://crucible.hubbe.net/faq#aicontent

I think your problem is not that hard, you want to go from OFF to ON to RETRACTED to OFF, correct?
If yes, you can just use one bool variable, lets call it retracted_

  • OFF state, when you’re OFF, just turn it ON and set retracted_ to false
  • ON state, if retracted_ is false then you want to retract your blade and set retracted_ to true
  • ON state, if retracted_ is true then you want to turn OFF

It should look something like this:

bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {
  switch (EVENTID(button, event, modifiers)) {
    case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF):
      retracted_ = false
      On();
      // insert here whatever code you have to extract your blade
      // (don't trust AI, it will give you some BS code with a pretty name for a function that doesn't exist!)
      return true;
    case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON):
      if (retracted_) {
        Off();
      } else {
        retracted_ = true;
        // insert here whatever code you have to retract your blade (again don't trust AI!)
      }
      return true;

... insert here the rest of your case EVENTID
  return false;
  }
}

// then add to the bottom:
private:
  bool retracted_ = false;
};
#endif

Then if you want to do OFF to ON to RETRACTED back to ON to RETRACTED to OFF, you can replace OFF(); above with

if (fusor.angle1() < -M_PI / 9) {
  // Pointing towards DOWN (below -20° from the horizon)
  Off();
} else {
  // Pointing above -20°, horizontal or up
  retracted_ = false;
  // insert here whatever code you have to extract your blade
}

Meaning if your saber is pointing down, it will turn OFF, else it will extract again.

1 Like

I appreciate the responses, thank you! Will try Olivier’s code, thanks so much Olivier! What you said makes sense.

I did have another question, for full context our lightsaber inner chassis is rotating at around 700rpm, and we’re using a reed switch as the “button”. A magnet on the outside of the hilt moving in/out of the range of the reed switch is how we “press” the button. Is there a minimum time the button needs to be pressed to send an EVENT_PRESSED, or as long as the button is pressed even for an instant then EVENT_PRESSED is sent?

I don’t know but it seems wrong, will you not get EVENT_PRESSED many times at that speed? EVENT_PRESSED gets released immediately.
Lets take 600 rpm (it’s easier for mental math), that will be 10 rotations per seconds, even if you are very quick to release, say in 200 milli sec, that would be 2 rotations and EVENT_PRESSED would be released twice (I don’t know how your button works though)

Edit: and then, assuming I am correct, you will also immediately get 2 EVENT_RELEASED as well

1 Like

The code for debouncing buttons is here:

And here is an example of how to override that code with something else:

I suspect that for your purpose, you’ll to set up an interrupt for the button. The interrupt simply does something like: last_pressed_ = millis();

Then you can update DeboucedRead to return millis() - last_pressed < 50 or something like that.

This button code can show you how to set up an interrupt:

2 Likes

Okay, I think I’ve got the code working now. Will try it out and let y’all know how it goes, thanks so much both for your help!

Next version of the saber with this code is done! Would love to hear what y’all think :slight_smile: https://youtu.be/0QArAMtBGx8

3 Likes

Great work! Your persistence is admirable the responses from Comicon were spectacular.

Looking forward to a stable model so you can hand it to other people and let them lose their minds :slight_smile:

1 Like

Thank you!!! Yeah the next version should be stable and we’re gonna duel with it :slight_smile:

2 Likes