change variable for 3 seconds then revert

Hi, new to the forum and to Arduino projects!

Im working on an RC project. I have connected a receiver to my Arduino Nano 33 BLE and I have successfully used pulseIn to monitor and view PWM.

I have an RC switch (A7) to temporarily change the value of a variable (Throttlevalue). I would like this change to last for 3 seconds, and then revert to the pulseIn value.

I attempted to use a millis() IF statement to do this, but it resulted in only running my if statement every 3 seconds.

Eventually I plan to output the modified variable to a servo.

Here's my code:

#include <ArduinoBLE.h> 
double channel[4];
const int Threshold = 1500;


void setup() {
pinMode(A7, INPUT );
pinMode(A6, INPUT );
pinMode(A5, INPUT );
pinMode(A4, INPUT );
pinMode(A3, INPUT );
Serial.begin(9600);
}

void loop() {
 channel[0] = pulseIn(A7, HIGH);
 channel[1] = pulseIn(A6, HIGH);
 channel[2] = pulseIn(A5, HIGH);
 channel[3] = pulseIn(A4, HIGH);

 int Switchvalue = pulseIn(A3, HIGH);

 if(Switchvalue < Threshold){
 Serial.println(" On ");
 int Throttlevalue = 900;
  Serial.print(" Throttle:");
 Serial.print(Throttlevalue);
 }
 else{
  Serial.println(" Off ");
  int Throttlevalue = pulseIn(A7, HIGH);
   Serial.print(" Throttle:");
 Serial.print(Throttlevalue);
 }

Check if this works

#include <ArduinoBLE.h>

double channel[4];
const int Threshold = 1500;

bool waiting3Sec = false;
unsigned long startWaitingTime;

void setup()
{
  pinMode(A7, INPUT );
  pinMode(A6, INPUT );
  pinMode(A5, INPUT );
  pinMode(A4, INPUT );
  pinMode(A3, INPUT );

  Serial.begin(9600);
}

void loop()
{
  channel[0] = pulseIn(A7, HIGH);
  channel[1] = pulseIn(A6, HIGH);
  channel[2] = pulseIn(A5, HIGH);
  channel[3] = pulseIn(A4, HIGH);

  if (waiting3Sec)
  {
    if (millis() - startWaitingTime >= 3000)
    {
      // time elapsed, next loop() will use SwitchValue
      Serial.println("End 3 sec. waiting");
      waiting3Sec = false;
    }
  }

  else
  {
    int Switchvalue = pulseIn(A3, HIGH);

    if (Switchvalue < Threshold)
    {
      Serial.println(" On ");
      int Throttlevalue = 900;
      Serial.print(" Throttle:");
      Serial.print(Throttlevalue);

      Serial.println("Start 3 sec. waiting");
      waiting3Sec = true;
      startWaitingTime = millis();
    }

    else
    {
      Serial.println(" Off ");
      int Throttlevalue = pulseIn(A7, HIGH);
      Serial.print(" Throttle:");
      Serial.print(Throttlevalue);
    }
  }
}

I have an RC switch (A7) to temporarily change the value of a variable (Throttlevalue). I would like this change to last for 3 seconds, and then revert to the pulseIn value.

What do you want if they press the switch again during those three seconds?
What do you want if the switch is held down for less than 3 seconds?
What do you want if the switch is held down for more than 3 seconds?
Do you want to time the 3 seconds from the start of the press-down, or from the time of release of the switch?
Is the switch a little noisy? Do you want to reject very short press-downs as probably spurious?

In any case: think of the 'loop' statement as like a goldfish with a very, very short memory. Each time it executes, it only "knows" about what is happenning right now, and so it has to leave notes to its future self by setting the values of variables outside the loop.

Here's a simple implementation:

#include <ArduinoBLE.h>
double channel[4];
const int Threshold = 1500;
const long throttleOnMs = 3000; // three seconds

boolean throttleOn = false;
long throttleStartMs;
int throttleValue;

void setup() {
  pinMode(A7, INPUT );
  pinMode(A6, INPUT );
  pinMode(A5, INPUT );
  pinMode(A4, INPUT );
  pinMode(A3, INPUT );
  Serial.begin(9600);
}

void loop() {
  channel[0] = pulseIn(A7, HIGH);
  channel[1] = pulseIn(A6, HIGH);
  channel[2] = pulseIn(A5, HIGH);
  channel[3] = pulseIn(A4, HIGH);

  int switchValue= pulseIn(A3, HIGH);

  if(switchValue < Threshold){
    if(!throttleOn) {
      Serial.println(" On ");
      throttleValue= 900;
      throttleOn = true;
      throttleStartMs = millis();
    }
    else {
      Serial.println(" Still holding that switch down! ");
    }
  }
  else{
    if(throttleOn) {
      if(millis()-throttleStartMs >= throttleOnMs ) {
        Serial.println(" Off ");
        int throttleValue = pulseIn(A7, HIGH);
        throttleOn = false;
      }
      else {
        Serial.println(" Timeout not elapsed ");
      } 
  }

  Serial.print(" Throttle:");
  Serial.print(throttleValue);
  Serial.println();

  if(throttleOn) {
    Serial.print(" TimeRemaining:");
    Serial.print(throttleOnMs - (millis()-throttleStartMs) );
    Serial.println();
    
  }
}

Now,

  1. how will this sketch behave if you press-and hold, re-press during the 3 seconds, etc etc?
  2. is that what you want?
  3. how would you modify it to do what you do want?

-- edit --

Oh, I forgot: when the switch is not down, do you want the throttle to follow the pulsein value, or do you want it to be set to the pulsein value at the time the throttle was released?

vlc0617,

Thanks for your reply, that worked as I had hoped! thank you.

PaulMurrayCbr, I'll explain more of what I am trying to achieve. You ask some very valid questions and that will help me develop the idea further.

As i am very much learning as I go here, i have been working on understanding a small bit of functionality at a time and then look to re-code the whole project as one. If this is misguided I'd glady follow more experienced hands!

- This will be a RC combat system using IR.
- Each vehicle will have an IR diode (front) and one or more IR sensors (rear, side etc)
- The Arduino will passthrough PWM RC signal until such a time as it receives an IR "hit" from another vehicle.
- Once "hit" the PWM passthrough will be modified in various ways, starting with reduced throttle for specified
period, 3 seconds in this case.
- Eventually I hope to utilise BLE and a mobile app to change game settings for things like number of shots allowed (ammo), hit actions etc.

With that in mind, to answer you questions:

What do you want if they press the switch again during those three seconds?

  • This would be a momentary switch on the transmitter, it could be held down for a max duration after which it would need to be released (think empty a magazine of ammo)

What do you want if the switch is held down for less than 3 seconds?

  • Once the switch (trigger) is released it could be pressed again immediately - the limit will be on a single press duration. Possibility of a maximum total pressed state time?

What do you want if the switch is held down for more than 3 seconds?

  • It should timeout and revert to the live pulseIn.

Do you want to time the 3 seconds from the start of the press-down, or from the time of release of the switch?
Is the switch a little noisy? Do you want to reject very short press-downs as probably spurious?

  • In this case the switch would be an IR "hit". I dont yet know if this would be noisy, but in the real world I would likely want to make it easier to hit the opponent and so sensitivity should be high.

when the switch is not down, do you want the throttle to follow the pulsein value, or do you want it to be set to the pulsein value at the time the throttle was released?

  • Once the "hit" action has elapsed (3s throttle reduction), it should revert to pulseIn value.

I appreciate this is a complicated project, especially for a beginner! I am willing to give it the time I need to and I do very much appreciate your help :slight_smile:

Thanks,
Rob

vlc0617:
Check if this works

#include <ArduinoBLE.h>

double channel[4];
const int Threshold = 1500;

bool waiting3Sec = false;
unsigned long startWaitingTime;

void setup()
{
 pinMode(A7, INPUT );
 pinMode(A6, INPUT );
 pinMode(A5, INPUT );
 pinMode(A4, INPUT );
 pinMode(A3, INPUT );

Serial.begin(9600);
}

void loop()
{
 channel[0] = pulseIn(A7, HIGH);
 channel[1] = pulseIn(A6, HIGH);
 channel[2] = pulseIn(A5, HIGH);
 channel[3] = pulseIn(A4, HIGH);

if (waiting3Sec)
 {
   if (millis() - startWaitingTime >= 3000)
   {
     // time elapsed, next loop() will use SwitchValue
     Serial.println("End 3 sec. waiting");
     waiting3Sec = false;
   }
 }

else
 {
   int Switchvalue = pulseIn(A3, HIGH);

if (Switchvalue < Threshold)
   {
     Serial.println(" On ");
     int Throttlevalue = 900;
     Serial.print(" Throttle:");
     Serial.print(Throttlevalue);

Serial.println("Start 3 sec. waiting");
     waiting3Sec = true;
     startWaitingTime = millis();
   }

else
   {
     Serial.println(" Off ");
     int Throttlevalue = pulseIn(A7, HIGH);
     Serial.print(" Throttle:");
     Serial.print(Throttlevalue);
   }
 }
}

Another way is to use "startWaitingTime != 0" in place of a separate "waiting3Sec" flag.

#include <ArduinoBLE.h>

double channel[4];
const int Threshold = 1500;

unsigned long startWaitingTime = 0;

void setup()
{
  pinMode(A7, INPUT );
  pinMode(A6, INPUT );
  pinMode(A5, INPUT );
  pinMode(A4, INPUT );
  pinMode(A3, INPUT );

  Serial.begin(9600);
}

void loop()
{
  channel[0] = pulseIn(A7, HIGH);
  channel[1] = pulseIn(A6, HIGH);
  channel[2] = pulseIn(A5, HIGH);
  channel[3] = pulseIn(A4, HIGH);

  if (startWaitingTime != 0)
  {
    if (millis() - startWaitingTime >= 3000)
    {
      // time elapsed, next loop() will use SwitchValue
      Serial.println("End 3 sec. waiting");
      startWaitingTime = 0;
    }
  }
  else
  {
    int Switchvalue = pulseIn(A3, HIGH);

    if (Switchvalue < Threshold)
    {
      Serial.println(" On ");
      int Throttlevalue = 900;
      Serial.print(" Throttle:");
      Serial.print(Throttlevalue);

      Serial.println("Start 3 sec. waiting");
      startWaitingTime = millis();
    }
    else
    {
      Serial.println(" Off ");
      int Throttlevalue = pulseIn(A7, HIGH);
      Serial.print(" Throttle:");
      Serial.print(Throttlevalue);
    }
  }
}

I've tried to clean up the code a little and I have now used map() to convert the pulseIn to servo write and output it to the servo.

So now the trottle on the transmitter is passed through to the servo, unless the switch it activated in which case it is set to an idle value.

This is simulates the basic functionality of an IR "hit", cutting the throttle.

Will this wait function stop all other processes like the delay would?

Is there a better way to do this when other channels are being used at the same time, i.e a trigger switch and steering?

#include <Servo.h>
#include <ArduinoBLE.h>

const int Threshold = 1500;

bool waiting3Sec = false;
unsigned long startWaitingTime;

Servo ThServo;

void setup() {
  
pinMode(A7, INPUT);
pinMode(A3, INPUT);
ThServo.attach(A2);

Serial.begin(9600);
}

void loop() 
{
  int Switchvalue = pulseIn(A3, HIGH);            //READ TX SWITCH POSITION AND SET TO SWITCHVALUE
  int ThValue = pulseIn(A7, HIGH);                //READ THROTTLE POSITION AND SET TO THVALUE

  if (waiting3Sec)
    {
     if(millis() - startWaitingTime >=3000)
      {
       waiting3Sec = false;
      }  
    }
    else
    {
      if (Switchvalue < Threshold)
    {
      int LiveTh = (50);                             //CHANGE LIVETH TO FIXED POSITION
      ThServo.write(LiveTh);                          //SEND LIVETH TO SERVO
      waiting3Sec = true;
      startWaitingTime = millis();
    }
    else
    {
     int LiveTh = map(ThValue, 970, 2050, 0, 255);   //MAP THROTTLE POSITION AND SET TO LIVETH
     ThServo.write(LiveTh);                          //SEND LIVETH TO SERVO
    }
  }
}