Issue trying to use NeoPixel Stick

I got the motor to work but I had to do it without using PWM, when i would first start the proffieboard and i press the button to turn on the saber, the motor would not run until after 2-5 mins using PWM, but once the motor ran for the first time, then after that it would work correctly when powering off and on the saber but if i unplugged the battery and plugged it back in, that same issue would occur. So to fix it I’m just sending HIGH and LOW to the motor and that works fine!

That’s strange.
Maybe if I saw the full code I could figure out why.

it’s not a big deal on my end, I need full speed for the motor anyways.

But going back to my led stick, it’s working great and displays the battery life. But is there a way to have it turn off when the saber is not in use. I guess im wondering if there is a standby function i can tie into because i would like to have the stick turn off but i also want to code in a few things that i would like to have happen when entering the standby mode. For example, i would like to have my motor return back to it original position. Is something like that possible?

Anything and everything is possible.
For styles, you just need to remove the InOut layer and it will stay on all the time.
For your motor, you can either poll SaberBase::IsOn(), or you can inherit from SaberBase and react to SB_Effect(EFFECT_ON,…) and SB_Off() calls.

Im only using one button for everything. so, what i need to do is, when you first turn on the proffieboard and press the button for the first time, i want the dc motor function to be called without any of the saber ignition stuff. I’ll have a flag set to something like saberReady and ill set it to true after the motor has completed its cycle. Then the next button press i can have the regular saber ignition. But right now im struggling to find where to add that code. Which file is all the button presses handled?
I found this section of code

bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {

in the props/saber.h file but when I added Serial.println inside that function nothing printed when i pressed the button.
What function and file should i look for in order to achieve what i need?

You are using the fett263 prop, try props/saber_fett263_buttons.h.
Event2 in the fett263 prop does not call Event2 in the parent class (which is saber)

Also, you probably don’t need printouts, because ProffieOS already prints out the events received by the prop here:

Thanks!

So heres what im trying to achieve. When the board is first turned on and the button is pressed for the first time, instead of turning the saber ignition on, i want the motor to run
for a few rotations. Once the motor has stopped i will set the saberReady var to true. Then i would like the rest of the saber operations to run like normal on the next button press. I know this is out of the norm, but im working on a unique saber.

Here is the code i currently have in the proffieOS.ino loop

if (SaberBase::IsOn()) {
  if(!saberReady) {  
    // Proffieboard is ON, run the motor only if it's not already running
    if (!motor_running && rotation_count < target_rotations) {
        runMotorForRotations(false);  // Start motor forward
        motor_running = true;
    }
    
    if (motor_running && rotation_count >= target_rotations) {
        motor_running = false;  // Motor has stopped
        stopMotor();  // Stop the motor
        saberReady = true;
    } 
  }
  
  if(saberReady) {    
    // Add random flicker effect
    unsigned long currentMillis = millis();
  
    // Check if it's time to toggle the flicker state
    if (currentMillis - lastFlickerTime >= flickerInterval) {
      lastFlickerTime = currentMillis;  // Reset the timer
      flickerInterval = random(3, 10);  // Randomize the next interval
  
      // Toggle between HIGH and LOW to create flicker
      if (isFlickerOn) {
        digitalWrite(MOSFET_CONTROL_PIN, LOW);   // Turn off MOSFET (LED off)
        isFlickerOn = false;
      } else {
        digitalWrite(MOSFET_CONTROL_PIN, HIGH);  // Turn on MOSFET (LED on)
        isFlickerOn = true;
      }
    }
    
    // Saber is ON, run the motor only if it's not already running
    if (!motor_running && rotation_count < target_rotations) {
        runMotorForRotations(true);  // Start motor forward
        motor_running = true;
    }
    
    if (motor_running && rotation_count >= target_rotations) {
        motor_running = false;  // Motor has stopped
        stopMotor();  // Stop the motor
    }
    
    // Power the NeoPixel stick only if saber is on
    int batteryLevel = battery_monitor.battery_percent(); // Returns value from 0 to 100
   
    // Calculate how many LEDs should be on
    int ledsOn = (batteryLevel * 8) / 100;  // Number of LEDs to turn on based on percentage
  
  }

This does not execute the way i need it to. With that code, the button press doesnt work at all. Im thinking that the SaberBase::IsOn() is true when the button is pressed to turn the saber on. So thats where im struggling to figure out where i should place this code

if(!saberReady) {  
    //Proffieboard is ON, run the motor only if it's not already running
    if (!motor_running && rotation_count < target_rotations) {
        runMotorForRotations(false);  // Start motor forward
        motor_running = true;
    }
    
    if (motor_running && rotation_count >= target_rotations) {
        motor_running = false;  // Motor has stopped
        stopMotor();  // Stop the motor
        saberReady = true;
    } 
  }

because it needs to be triggered by a button press. Can you give an example of how i can listen for button presses directly in the loop? Thank for your time!

Button presses are generally not polled, so you wouldn’t normally put the code that detects button presses in the loop. Instead you put it in Event2. It could 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_CLICK_SHORT, MODE_OFF):
            if (!saberReady && !motorRunning) {
                 startMotor();
                  return true;
            }
            On();
            return true;
    }
}

Thank you! I’ll give that a try!

That helped me out a lot! Thank you! Im now able to get the motor to run on the first button press before the saber is turned on.

How would i add my own custom button press, like, I’m thinking a 5 second or 10 second press.

The button base class already has a variety of long-press events you might be able to use. If not, that’s probably where you would add yours:

The length required for different events can also be customized with defines.

I was trying to add some code in the button loop function within the button_base.h

I just need to set a bool to true if the button was pressed for 5 seconds. I tried adding it like this

void Loop() override {
    STATE_MACHINE_BEGIN();
    while (true) {
      while (!DebouncedRead()) {
	if (saved_event_ &&
	    millis() - push_millis_ > BUTTON_DOUBLE_CLICK_TIMEOUT) {
	  Send(saved_event_);
	  saved_event_ = 0;;
	}
	YIELD();
      }
      saved_event_ = 0;
      if (millis() - push_millis_ < BUTTON_DOUBLE_CLICK_TIMEOUT) {
	if (press_count_ < 4) press_count_++;
      } else {
	press_count_ = 1;
      }
      Send(EVENT_PRESSED);
      push_millis_ = millis();
      current_modifiers |= button_;
      while (DebouncedRead() && (current_modifiers & button_)) {
		if (millis() - push_millis_ > BUTTON_HELD_LONG_TURNOFF) {
           longButtonPressed = true; //set the flag here
       }
        if (millis() - push_millis_ > BUTTON_HELD_TIMEOUT) {
          Send(EVENT_HELD);
          while (DebouncedRead() && (current_modifiers & button_)) {
            if (millis() - push_millis_ > BUTTON_HELD_MEDIUM_TIMEOUT){
              Send(EVENT_HELD_MEDIUM);
	      while (DebouncedRead() && (current_modifiers & button_)) {
                if (millis() - push_millis_ > BUTTON_HELD_LONG_TIMEOUT) {
                  Send(EVENT_HELD_LONG);
		  
		  while (DebouncedRead() && (current_modifiers & button_)) YIELD();
                }
                YIELD();
              }
            }
            YIELD();
          }
        }
        YIELD();
      }
      while (DebouncedRead()) YIELD();
      Send(EVENT_RELEASED);
      if (current_modifiers & button_) {
        current_modifiers &=~ button_;
        if (millis() - push_millis_ < BUTTON_SHORT_CLICK_TIMEOUT) {
          SendClick(EVENT_CLICK_SHORT);
        } else if (millis() - push_millis_ < 2500) {
	  // Long clicks cannot be "saved", so just emit immediately.
          Send(EVENT_CLICK_LONG);
        }
      } else {
        // someone ate our clicks
        push_millis_ = millis() - 10000; // disable double click
      }
    }
    STATE_MACHINE_END();
  }

Is this the place to add it? Sorry for all the questions, the buttons events are bit confusing to me.

I don’t know what you changed in that code.

If all you need is to set a bool, I still recommend doing it in Event2, something like:

bool power_pressed_ = false;
uint32_t power_press_millis_;

bool pressed_for_five_seconds() {
  if (!power_pressed_) return false;
  return millis() - power_press_millis_ > 5000;
}

bool Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override {
    switch (EVENTID(button, event, modifiers)) {
        case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_ANY):
          power_pressed_ = true;
          power_press_millis_ = millis();
          return false;
        case EVENTID(BUTTON_POWER, EVENT_RELEASED, MODE_ANY):
          power_pressed_ = false;
          return false;
    }
}

This creates a bool function rather than a variable, but you can then poll that function from loop if you like.

Thanks for the code, that work!.
Im facing another issue. If i first turn on the saber and i press the button a few times in short bursts, then hold it down for the 5 seconds, it doesn’t run the code i have for a 5 second button press. however, if i turn the saber on for the first time and press the button for 5 seconds without doing any other button presses, it works fine. So, does the millis() get reset once a button is released? Or doesn’t it continue to count?

millis() does not get reset

1 Like

Do you happen to know what the PWM frequency is for the v3.9 board? Right now if i run analogWrite(MOTOR_PWM_PIN, 255); i just here a high pitch humming noise.

However, the motor runs fine if i use digitalWrite(MOTOR_PWM_PIN, HIGH);

Im using free 3 pad which i have which i have pin 9 set in code.

The default for analogWrite is 488Hz.
However, I think analogWrite has some bugs on Profieboards. The timer is re-initialized every time you call analogWrite(), so if you call analogWrite a lot, the output signal becomes very glitchy.

I haven’t bothered fixing it because ProffieOS doesn’t use analogWrite, instead I wrote some functions for PWM which separates the setup from setting the pwm level. If you want to use those instead, you would just do:

    // Once
   LSanalogWriteSetup(MOTOR_PWM_PIN);

   // Many times
   LSanalogWrite(MOTOR_PWM_PIN, value /* 0-32768 */ );

LSanlogWrite ues 813Hz PWM

Thanks, i’ll give that try!