Retractable Saber from Japan

Hello, everyone. I’m writing from Japan.

We’ve built a mechanism for an extendable lightsaber that operates reliably.

Currently, we’re using a separate Arduino Nano to send signals that mimic the actions of the control buttons to the Proffie board to make it work.

We’d like to achieve this using only the Proffie board, so we’ve decided to ask for your advice.

Currently, we are using two photoreflector sensors to detect the extension and retraction limits of the blade,

as well as two signal lines to control a separate small motor driver

that manages the blade’s retraction and extension.

In other words, we need four signal lines.

Specifically, when the saber is turned on, I need to monitor the extension-side sensor while sending a signal to the motor driver to rotate in the extension direction.

When the saber is turned off, I need to monitor the retraction-side sensor while sending a signal to the motor driver to rotate in the retraction direction.

Which files should I modify, and what kind of code should I add?

Thank you very much !

Here is a sample video showing the current structure.

So on a V3 proffieboard, there are lots of pins available that can be used:

  • Free 1, Free 2, Free 3
  • Data 2, Data 3, Data 4
  • Button 1, Button 2, Button 3
  • Rx, Tx

The three Free pins are meant for these sort of things, and there is a timer available if you need some sort of PWM functionality, so I would recommend using those. Then add Data 4 as the fourth wire. (Or one of the other ones if that makes things easier for some reason.)

Coding-wise, there are a lot of ways to do this, but because this is relatively simple and doesn’t seem to require a lot of configuration, we can just stick with the simplest option, which is to put a piece of code directly in your configuration file for this.

Basically, inside CONFIG_PRESET, you would put something like this:

class BladeExtender : public Looper {
public:
  const int extendSensorPin = blade4Pin;
  const int retractSensorPin = blade5Pin;  // Free1
  const int directionPin = blade5Pin;  // Free2
  const int motorEnablePin = blade6Pin // Free3

  const char* name() override { return "BladeExtender"; }

  void Setup() override {
     // Setup code, like pinMode() calls go here.
     pinMode(extendSensorPin, INPUT);
     pinMode(retractSensorPin, INPUT);
     pinMode(directionPin, OUTPUT);
     pinMode(motorEnablePin, OUTPUT);
  }

  void Loop() override {
    // This gets called over and over again, just like arduino.
    // You can do anything here, but do not use delay() calls as that will
    // slow down everything else.
    if (Saberbase::IsOn()) {
       digitalWrite(directionPin, 1);
       digitailWrite(motorEnablePin, !digitalRead(extendSensorPin));
    } else {
       digitalWrite(directionPin, 0);
       digitailWrite(motorEnablePin, !digitalRead(retractSensorPin));
    }
  }
private:
  // Any variables you need go here.
};

// None of the above does anything until you instantiate the class like this:
BladeExtender blade_extender;

This example is untested, and may need some tweaking to work, but at least it should show you one way of doing what you want to do.

2 Likes

Thank you so much for your quick help!

First, I’ll take a close look at the code you sent. Thank you so much, profezzorn-san!

First, I ordered the Version 3 board. I’ll connect the four terminals to sensors and motor drivers to test the code.

After that, I’ll have some more questions for you.

Thank you for your continued support!!!

I took a look at the code, and here’s what we want to do.

When the blade’s on button is pressed, the LED lights up accompanied by a sound, right?

Immediately after that, we enter a while loop to check the status of Sensor A,

and during that time, we send signals (two are needed) to the motor.

The moment the sensor clears, we want to turn off the motor signals

and return control to the main loop.

The same applies when turning the blade off: when the button is held down (we’ve selected a single button), the blade-off sound starts,

and we enter a while loop to check the status of Sensor B,

sending signals to the motor during that time.

The moment the sensor clears, we want to turn off the motor signals,

turn off the LED, and return control to the main loop.

This doesn’t work well in ProffieOS.
You can’t just run a while loop, because then blade animations stop, no new sounds are started, motion events stop, etc.

Luckly, you don’t need a while loop to do what you want. Instead you use the Loop() function, which gets called very often (usually thousands of times per second) to check what the current state is, make changes, and then return. It’s really very similar to your while loop.

The code I posted is written this way. When Loop() is called, is checks if the saber is ignited or not. (SaberBase::IsOn() returns true immedately when ignition begins, and returns false as soon as retraction begins.) When the saber is ignited, it checks the sensor and runs the motor in one direction until the sensor is triggered. Once the sensor is triggered, it keeps checking it, but it doesn’t do anything. (Assuming the sensor stays triggered.) if the saber is off (meaning that the user has pressed the “retract” button, whatever that is.) then it runs the motor the other way, if, and only if, the retraction sensor hasn’t triggered yet.

Doesn’t this do pretty much exactly what you need?
(Without the while loop.)

1 Like

If you don’t need pwm signals, then you can also use a V2 board.
You could any of these pins:

Data2, Data3, Data4, RX, TX, Button1, Button2, Button3.

My recommendation would probably be Data3, Data4, RX and TX.

1 Like

I see what you mean!

Thank you so much for explaining that to me.

That brings us to the following issue.

Currently, there is a slight variation in the timing when the motor stops, so

we have been waiting a very short time (a few milliseconds) after the sensor clears

before turning the motor off.

Also, the motor doesn’t just set the signal line to LOW;

in our current sample, we send a HIGH signal to both signal lines for about 50 milliseconds

to apply the brake, and then set them to LOW.

Is there a way to achieve the same effect without using a delay?

Genereally speaking, what you need is state machine. There is lots of ways to write a state machine, and I can provide an example tomorrow. (Getting late here)

Basically, it comes down to set a variable to the current time, then check how many millis (or micros) has passed, repeatedly, and when enough time has passed, you go to the next state.

What kind of signals? Do you need to send a PWM signal at a particular frequency to make the motor go?

I want to set the global variables bool extending_ and bool retracting_ to true within functions that are called exactly once each when the blade extends and retracts.

Is there an appropriate time to do this?

void Loop() override {
         if (SaberBase::IsOn()) {
            if (extending_) {
                if (analogRead(extendSensorPin) <= 1000) {
                    Serial.print("Extending");
                    analogWrite(motorAPin, 255);
                    analogWrite(motorBPin, 0);
                } else if (! braked_) {
                     Serial.println(analogRead(extendSensorPin));
                     analogWrite(motorAPin, 255);
                     analogWrite(motorBPin, 255);
                     braked_ = true;
                     last_brake_time_millis_ = millis();
                } else if (millis() - last_brake_time_millis_ > 50) {
                    analogWrite(motorAPin, 0);
                    analogWrite(motorBPin, 0);
                    extending_ = false;
                    braked_=false;
                } else {
                     // do nothing
                }

         } else {
            if (retracting_) {
                if (analogRead(retractSensorPin) <= 1005) {
                     Serial.print("Retracting");
                     analogWrite(motorAPin, 0);
                     analogWrite(motorBPin, 255);
                } else if (! braked_) {
                     Serial.println(analogRead(retractSensorPin));
                     analogWrite(motorAPin, 255);
                     analogWrite(motorBPin, 255);
                     braked_ = true;
                     last_brake_time_millis_ = millis();
                } else if (millis() - last_brake_time_millis_ > 50) {
                    analogWrite(motorAPin, 0);
                    analogWrite(motorBPin, 0);
                    retracting_ = false;
                    braked_=false;
                } else {
                     // do nothing
                }
            }
         }
      }
   }

Why exactly once?
If it’s a bool, nobody will know if you set it to true more than once.

If you put:

extending_ = SaberBase::IsOn();
retracting_ = !SaberBase::IsOn();

in loop, it will set it every iteration, but the result is the same.

It’s just a matter of personal preference, but even after the blade has fully extended or retracted, I was concerned about the strain of constantly monitoring the sensor, so I personally wanted to avoid it.

Well, you can do something like this:

if (on_ != SaberBase::IsOn()) {
  on_ = SaberBase::IsOn();
  if (on_) {
    extending_ = true;
  } else {
    retracting_ = true;
  }
}

Thank you so much for the advice!

It looks like the method you suggested will work.

Now, the sensors and motor driver require 5V to operate, but 5V is only supplied when the sound is playing, right?

Is there a way to get around this and explicitly supply power to the sensor when I need it?

I’m going to try a method where the power for the reflect sensor’s LED is drawn from a separate battery used to run the motor, and the sensor itself is powered by 3.3V from the Proffie.

Yes, call EnableBooster(); from your code.
Doing this will make sure the 5v booster is on, and it will keep it on for a few seconds. Just keep calling EnableBooster(); in loop() as long as retracting_ or extending_ is true. There is no function for turning the booster off, it just turns off automatically when no code has called EnableBooster() in a while.

Thank you very much!

I’ve been trying this for dozens of hours, but the sensor (LBR-127HLD) isn’t working properly.
The sensor is powered from the 5V pin, with a 220-ohm resistor on the LED side and a 1-kOhm resistor on the sensor side.
When I run the code, it only returns zeros. Previously, when I ran the same setup on an Arduino, the reading was in the 900s when there was a reflective object present, and it exceeded 1,000 when the object was removed.

class BladeExtender : public Looper {

public:

const int extendSensorPin = blade4Pin;    // Data4

const int motorAPin = rxPin;

const int retractSensorPin = blade3Pin;  // Data3

const int motorBPin = txPin;

const char\* name() override { return "BladeExtender"; }

void Setup() override {

    Serial.begin(9600);

    // Setup code, like pinMode() calls go here.

    pinMode(extendSensorPin, INPUT);

    pinMode(retractSensorPin, INPUT);

    pinMode(motorAPin, OUTPUT);

    pinMode(motorBPin, OUTPUT);

}

void Loop() override {

     if (on\_ != SaberBase::IsOn()) {

           on\_ = SaberBase::IsOn();

           if (on\_) {

                 EnableBooster();

                 Serial.println(analogRead(extendSensorPin));

                 extending\_ = true;

                 retracting\_ = false;

           } else {

                 EnableBooster();

                 Serial.println(analogRead(retractSensorPin));

                 retracting\_ = true;

                 extending\_ = false;

           }

     }

     if (SaberBase::IsOn()) {

        if (extending\_) {

            EnableBooster();

            //Serial.println(analogRead(extendSensorPin));

            if (analogRead(retractSensorPin) <= 1000) {

                Serial.print("#####Extending#####");

                analogWrite(motorAPin, 255);

                analogWrite(motorBPin, 0);

            } else if (! braked\_) {

                 Serial.println("#####Extend Stop#####");

                 analogWrite(motorAPin, 255);

                 analogWrite(motorBPin, 255);

                 braked\_ = true;

                 last_brake_time_millis\_ = millis();

            } else if (millis() - last_brake_time_millis\_ > 50) {

                analogWrite(motorAPin, 0);

                analogWrite(motorBPin, 0);

                extending\_ = false;

                braked\_=false;

            } else {

                 // do nothing

            }

        } else if (retracting\_) {

            EnableBooster();

            //Serial.println(analogRead(retractSensorPin));

            if (analogRead(retractSensorPin) <= 1005) {

                 //Serial.print("#####Retracting#####");

                 analogWrite(motorAPin, 0);

                 analogWrite(motorBPin, 255);

            } else if (! braked\_) {

                 Serial.println("#####Retract Stop#####");

                 analogWrite(motorAPin, 255);

                 analogWrite(motorBPin, 255);

                 braked\_ = true;

                 last_brake_time_millis\_ = millis();

            } else if (millis() - last_brake_time_millis\_ > 50) {

                analogWrite(motorAPin, 0);

                analogWrite(motorBPin, 0);

                retracting\_ = false;

                braked\_=false;

            } else {

                 // do nothing

            }

        }

  }

}

private:

// Any variables you need go here.

bool extending\_ = false;

bool retracting\_ = false;

bool on\_ = false;

bool braked\_ = false;

uint32_t last_brake_time_millis\_;

};

// None of the above does anything until you instantiate the class like this:

BladeExtender blade_extender;

I don’t know if this is helpful to you @Magus1340 but @herotech has already made an extendable/retractable saber. Here are some of his threads:

Thank you very much!

Of course I know about HeroTech!

It’s a great idea, but the way our saber works is different from theirs.

Have you seen the video of our saber?

Our saber has been running stably for months now, but our goal is to remove the Arduino and run it using only Proffie, so we’d like to ask for your advice.

The current issue is that the reflect sensor isn’t working properly.