Overriding Serial Commands and Doing Two Things at Once

Hi all

I am currently working on an RC Car project, which receives commands over serial. I would like to be able to override commands, rather than having them queue up. For example, if the car is driving forward, and I enter the right turn command into the Serial Monitor, I want it to immediately stop running forward and turn right. I’m pretty sure that it shouldn’t require too much changing.

/*
 RC Car Project Mark II

 
 */

#include <AFMotor.h>
#include <math.h>

String readString, function, mspeed, duration;

AF_DCMotor motor1(1);
AF_DCMotor motor2(2);
AF_DCMotor motor3(3);
AF_DCMotor motor4(4);

const int rightLEDs = 9;
const int leftLEDs = 10;
const int buttonPin = 2;
const int buzzerPin = A0;
const int thermistorPin = A1;

int buttonState = 0;
float vcc = 4.91;
float pad = 9850;
float thermr = 10000;

float Thermistor(int RawADC) 
{
  long Resistance;  
  float Temp;
  Resistance = ((1024 * thermr / RawADC) - pad); 
  Temp = log(Resistance);
  Temp = 1 / (0.001129148 + (0.000234125 * Temp) + (0.0000000876741 * Temp * Temp * Temp));
  Temp = Temp - 273.15;
  Temp = (Temp * 9.0)/ 5.0 + 32.0;
  return Temp;
}

void motorSet(char motor_direction, word motor_speed, int motor_duration)
{
  motor1.setSpeed(motor_speed);
  motor2.setSpeed(motor_speed);
  motor3.setSpeed(motor_speed);
  motor4.setSpeed(motor_speed);

  if(motor_direction == 'f')
  {
    if(motor_speed < 255)
    {
      Serial.print("Running forward at ");
      Serial.print(motor_speed);
      Serial.print(" / 255 speed for ");
      Serial.print(motor_duration / 1000);
      Serial.println(" seconds.");
    }

    if(motor_speed == 255)
    {
      Serial.print("Running forward at full speed for ");
      Serial.print(motor_duration / 1000);
      Serial.println(" seconds.");
    } 

    motor1.run(FORWARD);
    motor2.run(FORWARD);
    motor3.run(FORWARD);
    motor4.run(FORWARD);
  }

  if(motor_direction == 'r')
  {
    if(motor_speed < 255)
    {
      Serial.print("Running backward at ");
      Serial.print(motor_speed);
      Serial.print(" / 255 speed for ");
      Serial.print(motor_duration / 1000);
      Serial.println(" seconds.");
    }

    if(motor_speed == 255)
    {
      Serial.print("Running backward at full speed for ");
      Serial.print(motor_duration / 1000);
      Serial.println(" seconds.");
    }

    motor1.run(BACKWARD);
    motor2.run(BACKWARD);
    motor3.run(BACKWARD);
    motor4.run(BACKWARD);
  }

  delay(motor_duration);

  motor1.run(RELEASE);
  motor2.run(RELEASE);
  motor3.run(RELEASE);
  motor4.run(RELEASE);
}


void setup() 
{
  pinMode(rightLEDs, OUTPUT);
  pinMode(leftLEDs, OUTPUT);
  pinMode(buttonPin, INPUT);
  pinMode(buzzerPin, OUTPUT);
  pinMode(thermistorPin, INPUT);
  digitalWrite(rightLEDs, HIGH);
  digitalWrite(leftLEDs, HIGH);
  Serial.begin(9600);
  Serial.println("RC Car Project Mark II connected."); 
}

void loop() 
{
  while (Serial.available()) 
  {
    delay(1);  
    if (Serial.available() > 0) 
    {
      char c = Serial.read();
      readString += c;
    } 
  }

  if(readString.length() > 0) 
  {
    function = readString.substring(0, 1); 
    mspeed = readString.substring(1, 4);     
    duration = readString.substring(4, 6);


    if(function == 'f') function = "FORWARD";
    if(function == 'r') function = "REVERSE";
    if(function == 'a') function = "RIGHT_TURN";
    if(function == 'b') function = "LEFT_TURN";
    if(function == 'h' && mspeed == "onn") function = "HEADLIGHTS_ON";
    if(function == 'h' && mspeed == "off") function = "HEADLIGHTS_OFF";
    if(function == 'h' && mspeed == "orn") function = "SHORT_HORN";
    if(function == 'l' && mspeed == "kho" && duration == "rn") function = "HORN_LOCK_ON";
    if(function == 'n' && mspeed == "oho" && duration == "rn") function = "HORN_LOCK_OFF";
    if(function == 'c' && mspeed == "tem" && duration == "pf") function = "CURRENT_TEMP";

    word newSpeed;
    long newDuration;

    char carray1[9]; 
    mspeed.toCharArray(carray1, sizeof(carray1));
    newSpeed = atoi(carray1);

    char carray2[9];
    duration.toCharArray(carray2, sizeof(carray2));
    newDuration = atoi(carray2);
    newDuration = newDuration * 1000; 

    buttonState = digitalRead(buttonPin);

    if(buttonState == LOW)
    {
      if(function == "FORWARD")
      {
        motorSet('f', newSpeed, newDuration);
      }

      if(function == "RIGHT_TURN")
      {    
        Serial.println("Turning right.");

        motor1.setSpeed(100);
        motor2.setSpeed(100);
        motor3.setSpeed(100);
        motor4.setSpeed(100);

        motor1.run(BACKWARD);
        motor2.run(BACKWARD);
        motor3.run(FORWARD);
        motor4.run(FORWARD);

        delay(1500);

        motor1.run(RELEASE);
        motor2.run(RELEASE);
        motor3.run(RELEASE);
        motor4.run(RELEASE); 
      }

      if(function == "LEFT_TURN")
      {
        Serial.println("Turning left.");

        motor1.setSpeed(100);
        motor2.setSpeed(100);
        motor3.setSpeed(100);
        motor4.setSpeed(100);

        motor1.run(FORWARD);
        motor2.run(FORWARD);
        motor3.run(BACKWARD);
        motor4.run(BACKWARD);

        delay(1500);

        motor1.run(RELEASE);
        motor2.run(RELEASE);
        motor3.run(RELEASE);
        motor4.run(RELEASE); 
      }
    }

    if(function == "HEADLIGHTS_OFF")
    {
      Serial.println("Headlights off.");
      digitalWrite(rightLEDs, LOW);
      digitalWrite(leftLEDs, LOW);
    }

    if(function == "HEADLIGHTS_ON")
    {
      Serial.println("Headlights on.");
      digitalWrite(rightLEDs, HIGH);
      digitalWrite(leftLEDs, HIGH);
    }

    if(function == "SHORT_HORN")
    {
      Serial.println("Horn sounded.");
      analogWrite(buzzerPin, 255);
      delay(2000);
      analogWrite(buzzerPin, 0);
    }

    if(function == "HORN_LOCK_ON")
    {
      Serial.println("Horn lock on.");
      analogWrite(buzzerPin, 255);
    }

    if(function == "HORN_LOCK_OFF")
    {
      Serial.println("Horn lock off.");
      analogWrite(buzzerPin, 0);
    }

    if(function == "CURRENT_TEMP")
    {
      float temp;
      temp = Thermistor(analogRead(thermistorPin));
      temp = (temp * 9.0) / 5.0 + 32.0;
      Serial.print("The current temperature is "); 
      Serial.print(temp,1);
      Serial.println(" degrees Fahrenheit.");
    }        

    if(function == "REVERSE")
    {      
      analogWrite(buzzerPin, 255);
      delay(500);
      analogWrite(buzzerPin, 0);

      motorSet('r', newSpeed, newDuration);
    }

    if(buttonState == HIGH && function != "REVERSE" && function != "HEADLIGHTS_OFF"
      && function != "HEADLIGHTS_ON" && function != "SHORT_HORN" && function != "HORN_LOCK_ON"
      && function != "HORN_LOCK_OFF" && function != "CURRENT_TEMP")
    {
      Serial.println("Obstruction in way. Command canceled.");
      readString = "";
    }

    readString = "";
  }
}

Second, I am having trouble setting up a blinking loop which will act as a turn signal while the car turns. I can’t seem to get it to loop while the car is turning. It only runs once. Maybe I have to have a while loop?

void turnSignal(char side, boolean state)
{
  boolean newLEDState = !ledState;
  if(state == true)
  {
    if(side == 'r')
    {
      digitalWrite(rightLEDs, newLEDState);
      digitalWrite(rightLEDs, newLEDState);
      delay(250);
      digitalWrite(rightLEDs, ledState);
      digitalWrite(rightLEDs, ledState);
      delay(250);
    }

    if(side == 'l')
    {
      digitalWrite(leftLEDs, newLEDState);
      digitalWrite(leftLEDs, newLEDState);
      delay(250);
      digitalWrite(leftLEDs, ledState);
      digitalWrite(leftLEDs, ledState);
      delay(250);
    }

    if(state == false)
    {
      digitalWrite(rightLEDs, ledState);
      digitalWrite(leftLEDs, ledState);
    }
  }
}

// …………………………………… //

 if(function == "RIGHT_TURN")
      {    
        Serial.println("Turning right.");

        turnSignal('r', true);

        motor1.setSpeed(100);
        motor2.setSpeed(100);
        motor3.setSpeed(100);
        motor4.setSpeed(100);

        motor1.run(BACKWARD);
        motor2.run(BACKWARD);
        motor3.run(FORWARD);
        motor4.run(FORWARD);

        delay(1500);

        motor1.run(RELEASE);
        motor2.run(RELEASE);
        motor3.run(RELEASE);
        motor4.run(RELEASE);

        turnSignal('r', false);
      }

      if(function == "LEFT_TURN")
      {
        Serial.println("Turning left.");

        turnSignal('l', true);

        motor1.setSpeed(100);
        motor2.setSpeed(100);
        motor3.setSpeed(100);
        motor4.setSpeed(100);

        motor1.run(FORWARD);
        motor2.run(FORWARD);
        motor3.run(BACKWARD);
        motor4.run(BACKWARD);

        delay(1500);

        motor1.run(RELEASE);
        motor2.run(RELEASE);
        motor3.run(RELEASE);
        motor4.run(RELEASE); 

        turnSignal('l', false);
      }

Well, this might not be what you want to hear but I think you would get much better results as far as responsiveness in control and having several things happen at once like if you read and understand the blink without delay example, http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay, and implement a state machine. Not what you want to hear cause it means scrapping a lot of what you have and pretty much starting over.

The problem you have currently is that you are using delay(), and nothing happens during that time. You can see from the blink without delay example how to keep track of time. Create variables to represent different aspects of the output for example,

boolean LeftBlinkerIsActive, RightBlinkerIsActive, LeftMotorIsTurning, RightMotorIsTurning;

Then in your loop you check to see if the various variables are set and act accordingly. Its more complicated to set up but it means your processor will never be just waiting, unable to change motor speeds for example, until a delay() is finished.

How many more threads asking for threads will we see???

How many more threads asking for threads will we see???

The requests to be able to have multiple threads, each with their own delay() running, will continue until the damned delay() function is removed.

Hey, OP. Explain this please:

  while (Serial.available()) 
  {
    delay(1);  
    if (Serial.available() > 0) 
    {
      char c = Serial.read();

You check that there is at least one byte of serial data to read, with the while statement.
Then, you delay. Why?
After waiting, which can not possibly cause a byte of data in the serial buffer to go away, you check again to see if it is still there? Where could it possibly have gone?

PaulS: until the damned delay() function is removed.

I'll sign that petition.

I did remove the delay. The lights do absolutely nothing. No change in state whatsoever.

void turnSignal(char side, boolean state)
{
  int ledState = LOW;
  long previousMillis = 0;
  long interval = 250;

  if(state == true)
  {
    if(side = 'r')
    {
      unsigned long currentMillis = millis();

      if(currentMillis - previousMillis > interval) 
      {
        previousMillis = currentMillis;   

        if (ledState == LOW)
          ledState = HIGH;

        else
          ledState = LOW;

        digitalWrite(9, ledState);
      }

      if(side = 'l')
      {
        unsigned long currentMillis = millis();

        if(currentMillis - previousMillis > interval) 
        {
          previousMillis = currentMillis;   

          if (ledState == LOW)
            ledState = HIGH;

          else
            ledState = LOW;

          digitalWrite(10, ledState);
        }
      }
    }

    if(state == false)
    {
      digitalWrite(rightLEDs, HIGH);
      digitalWrite(leftLEDs, HIGH);
    }
  }
}

Going to need to see that function in context, that function will need to be called very rapidly.

The first bug I can see is that in your test's for which side you are on you have this: if (side = 'r'), which is going to assign r to side and return true.

you want:

if (side == 'r'), to actually test for equality.

Same for the test for side l.

I think you will also want to move your ledState variable outside your function definition. You want to keep track of that value in between function calls.

Your also doing a couple of weird things here like checking to see if state is false inside a block of code you will only be in when state is true with no way for state to change. Also, since you are setting previousMillis to 0 every time you enter the function and don't assign a good time value to it until after checking the time this just isn't right yet. previousMillis should also be declared outside this function so you can keep track of it.

As far as I can tell all that function will do as written is set 9 and 10 to high when you run it.

No change in state whatsoever.

Well, it's obvious why. There is a lot of code missing.