I need help with a loop

Hello everyone,

Me and two friends are working on a little project. We want to modify an RC Car so that it can be controlled by a smartphone (with Bluetooth). We are almost entering the final stage of our project, which is connecting the Arduino board to the RC Car.

However, first we've got to make sure the Arduino knows what to do with the data the application sends. We're using a couple of LEDs to do/test that (see image). Everything works, except for the hazard warning lights. For some reason, we can't figure out how to make the front- and backlights blink repeatedly when receiving the right signal.

The smartphone app sends a 'K' when the user presses the hazard warning lights button (on), and a 'k' when the user presses it again (off). What happens, though, is that when I press the hazard warning lights button, they turn on, but as soon as I press the 'drive forward' button, they turn off again.

What we want the case 'K' to do, is to loop the blink code over and over again, until it receives a 'k' to turn it off again.

Thanks in advance!

int ledf = 9; // Drive forward
int ledb = 6; // Drive backward
int ledl = 13; // Turn left
int ledr = 2; // Turn right
int ledfront = 11; // The frontlights
int ledback = 4; // The backlights
//int ledunder = 6; // Underglow 
char incomingByte = '0'; // Data the application sends

void setup() {
  pinMode(ledf, OUTPUT);
  pinMode(ledb, OUTPUT);
  pinMode(ledl, OUTPUT);
  pinMode(ledr, OUTPUT);
  pinMode(ledfront, OUTPUT);
  pinMode(ledback, OUTPUT);
  //pinMode(ledunder, OUTPUT);
  pinMode(12, OUTPUT); // GND ledfront
  pinMode(10, OUTPUT); //GND ledf
  pinMode(7, OUTPUT); // GND ledb
  pinMode(5, OUTPUT); // GND ledback
  pinMode(3, OUTPUT); // GND ledr
  Serial.begin(9600); // baud rate
}

void loop() {
  digitalWrite(12, LOW);
  digitalWrite(10, LOW);
  digitalWrite(7, LOW);
  digitalWrite(5, LOW);
  digitalWrite(3, LOW);
  
  if (Serial.available() > 0) { 
    incomingByte = Serial.read(); // read the incoming data
    
  switch (incomingByte) {
    case 'a':                   
     digitalWrite(ledf, LOW);  // Forward off
     digitalWrite(ledb, LOW);
     break;
   case 'A':
     analogWrite(ledf, 45); // Forward low power
     digitalWrite(ledb, LOW);
     break;
   case 'B':                   
     analogWrite(ledf, 150); // Forward medium power
     digitalWrite(ledb, LOW);
     break;
   case 'C':                   
     analogWrite(ledf, 255);  // Forward high power 
     digitalWrite(ledb, LOW);
     break;
   case 'd':
     digitalWrite(ledb, LOW); // Backward off
     digitalWrite(ledr, LOW);
     break;
   case 'D':
     analogWrite(ledb, 45);  // Backward low power
     digitalWrite(ledf, LOW);
     break;
   case 'E':
     analogWrite(ledb, 150); // Backward medium power
     digitalWrite(ledf, LOW);
     break;
   case 'F':
     analogWrite(ledb, 255);  // Backward high power
     digitalWrite(ledf, LOW);
     break;
   case 'G':
     digitalWrite(ledl, HIGH); // Left on
     digitalWrite(ledr, LOW);
     break;
   case 'g':
     digitalWrite(ledl, LOW); // Left off  
     digitalWrite(ledr, LOW);
     break;
   case 'H':
     digitalWrite(ledr, HIGH); // Right on
     digitalWrite(ledl, LOW); 
     break;
   case 'h':
     digitalWrite(ledr, LOW); // Right off
     digitalWrite(ledl, LOW);
     break;
   case 'I':
     digitalWrite(ledfront, HIGH); // Frontlights on
     break;
   case 'i':
     digitalWrite(ledfront, LOW); // Frontlights off
     break;
   case 'J':
     digitalWrite(ledback, HIGH); // Backlights on
     break;
   case 'j':
     digitalWrite(ledback, LOW); // Backlights off
     break;
   case 'K':
     do {
       digitalWrite(ledfront, HIGH);
       digitalWrite(ledback, HIGH);
       delay(500);
       digitalWrite(ledfront, LOW);
       digitalWrite(ledback, LOW);
       delay(500);
     }while(incomingByte != 'k');   
     break;
   case 'k':
     digitalWrite(ledfront, LOW);
     digitalWrite(ledback, LOW);
     break;
   /*case 'L':
     digitalWrite(ledunder, HIGH);
     break;
   case 'l':
     digitalWrite(ledunder, LOW);
     break;*/ 
  }
}
}

For more images: Imgur: The magic of the Internet

Start by moving default states into setup(), that is

  digitalWrite(12, LOW);
  digitalWrite(10, LOW);
  digitalWrite(7, LOW);
  digitalWrite(5, LOW);
  digitalWrite(3, LOW);

should NOT be in the loop()

This

   case 'K':
     do {
       digitalWrite(ledfront, HIGH);
       digitalWrite(ledback, HIGH);
       delay(500);
       digitalWrite(ledfront, LOW);
       digitalWrite(ledback, LOW);
       delay(500);
     }while(incomingByte != 'k');   
     break;

is very bad not considered good programming practice. You need to move to a "state machine" model where a variable is interrogated during every loop() and then write a function to perform the HIGH/LOW based upon a timer variable from millis(). When you write a section of code like in the example, the entire loop() is frozen for 1000mS ... a very long time to the microcontroller.

State control is used in the Arduino Example; Blink without Delay

Ray

Ray

Thanks a bunch for pointing that out. You see, none of us three has any experience in programming C++, so we were actually really glad most of it worked.

Anyway, I've made some changes to the code.

int ledf = 9; // Drive forward
int ledb = 6; // Drive backward
int ledl = 13; // Turn left
int ledr = 2; // Turn right
int ledfront = 11; // The frontlights
int ledback = 4; // The backlights
//int ledunder = 6; // Underglow 
char incomingByte = '0'; // Data the application sends

int ledState = LOW;
long previousMillis = 0;
long interval = 500;

void setup() {
  pinMode(ledf, OUTPUT);
  pinMode(ledb, OUTPUT);
  pinMode(ledl, OUTPUT);
  pinMode(ledr, OUTPUT);
  pinMode(ledfront, OUTPUT);
  pinMode(ledback, OUTPUT);
  //pinMode(ledunder, OUTPUT);
  pinMode(12, OUTPUT); // GND ledfront
  pinMode(10, OUTPUT); //GND ledf
  pinMode(7, OUTPUT); // GND ledb
  pinMode(5, OUTPUT); // GND ledback
  pinMode(3, OUTPUT); // GND ledr
  
  digitalWrite(12, LOW);
  digitalWrite(10, LOW);
  digitalWrite(7, LOW);
  digitalWrite(5, LOW);
  digitalWrite(3, LOW);
  
  Serial.begin(9600); // baud rate
}

void loop() {
  unsigned long currentMillis = millis();
  
  if (Serial.available() > 0) { 
    incomingByte = Serial.read(); // read the incoming data
    
  switch (incomingByte) {
    case 'a':                   
     digitalWrite(ledf, LOW);  // Forward off
     digitalWrite(ledb, LOW);
     break;
   case 'A':
     analogWrite(ledf, 45); // Forward low power
     digitalWrite(ledb, LOW);
     break;
   case 'B':                   
     analogWrite(ledf, 150); // Forward medium power
     digitalWrite(ledb, LOW);
     break;
   case 'C':                   
     analogWrite(ledf, 255);  // Forward high power 
     digitalWrite(ledb, LOW);
     break;
   case 'd':
     digitalWrite(ledb, LOW); // Backward off
     digitalWrite(ledr, LOW);
     break;
   case 'D':
     analogWrite(ledb, 45);  // Backward low power
     digitalWrite(ledf, LOW);
     break;
   case 'E':
     analogWrite(ledb, 150); // Backward medium power
     digitalWrite(ledf, LOW);
     break;
   case 'F':
     analogWrite(ledb, 255);  // Backward high power
     digitalWrite(ledf, LOW);
     break;
   case 'G':
     digitalWrite(ledl, HIGH); // Left on
     digitalWrite(ledr, LOW);
     break;
   case 'g':
     digitalWrite(ledl, LOW); // Left off  
     digitalWrite(ledr, LOW);
     break;
   case 'H':
     digitalWrite(ledr, HIGH); // Right on
     digitalWrite(ledl, LOW); 
     break;
   case 'h':
     digitalWrite(ledr, LOW); // Right off
     digitalWrite(ledl, LOW);
     break;
   case 'I':
     digitalWrite(ledfront, HIGH); // Frontlights on
     break;
   case 'i':
     digitalWrite(ledfront, LOW); // Frontlights off
     break;
   case 'J':
     digitalWrite(ledback, HIGH); // Backlights on
     break;
   case 'j':
     digitalWrite(ledback, LOW); // Backlights off
     break;
   case 'K':
     do{
       if(currentMillis - previousMillis > interval) {
       previousMillis = currentMillis;
   
       if(ledState == LOW) {
         ledState = HIGH;
       }
       else{
         ledState = LOW;
       }
     
       digitalWrite(ledfront, ledState);
       digitalWrite(ledback, ledState);
       }
       
       incomingByte = Serial.read();
     }while(incomingByte != 'k');
     break;
   case 'k':
     digitalWrite(ledfront, LOW);
     digitalWrite(ledback, LOW);
     break;
   /*case 'L':
     digitalWrite(ledunder, HIGH);
     break;
   case 'l':
     digitalWrite(ledunder, LOW);
     break;*/ 
  }
}
}

However, now the lights don't blink and now I cannot send other commands while the button is activated.

You must understand this example. Then duplicate the logic for every LED you want to control. You will be able to have a blink rate in milliseconds and you will use millis() plus the blink rate and then test to see where you are in the countdown. The magic is in this math; if(currentMillis - previousMillis > interval)
after which you can change the On/Off state of the LED in question (blink it.)

/* Blink without Delay
 
 Turns on and off a light emitting diode(LED) connected to a digital  
 pin, without using the delay() function.  This means that other code
 can run at the same time without being interrupted by the LED code.
 
 The circuit:
 * LED attached from pin 13 to ground.
 * Note: on most Arduinos, there is already an LED on the board
 that's attached to pin 13, so no hardware is needed for this example.
 
 
 created 2005
 by David A. Mellis
 modified 8 Feb 2010
 by Paul Stoffregen
 
 This example code is in the public domain.

 
 http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
 */

// constants won't change. Used here to 
// set pin numbers:
const int ledPin =  13;      // the number of the LED pin

// Variables will change:
int ledState = LOW;             // ledState used to set the LED
long previousMillis = 0;        // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000;           // interval at which to blink (milliseconds)

void setup() {
  // set the digital pin as output:
  pinMode(ledPin, OUTPUT);      
}

void loop()
{
  // here is where you'd put code that needs to be running all the time.

  // check to see if it's time to blink the LED; that is, if the 
  // difference between the current time and last time you blinked 
  // the LED is bigger than the interval at which you want to 
  // blink the LED.
  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   

    // if the LED is off turn it on and vice-versa:
    if (ledState == LOW)
      ledState = HIGH;
    else
      ledState = LOW;

    // set the LED with the ledState of the variable:
    digitalWrite(ledPin, ledState);
  }
}

Well, I have added that code to the case 'K', but it have not found a way to loop that case yet. The app sends a 'K' when I press the button. If I press the 'forward' button after that, the incomingByte is changed to 'A', meaning the case 'K' is not looped.

It's quite hard to explain, so I hope you understand what I mean.

The app sends a 'K' when I press the button. If I press the 'forward' button after that, the incomingByte is changed to 'A', meaning the case 'K' is not looped.

So, I find the easiest way to handle such things is to start a new sketch and just build the code to control one LED and only one received value. You'll need variables for that one LED, the LED blink time, etc. You need to initialize the LED port direction, set the LED off and capture the current time using millis() and then generate the blink-state time by adding the current time and the blink change time together. At this point, we are ready for the loop()

In the loop() you will check to see if the LED blink_state time is lessthan (<) the current time. If so, you will execute the true part of the IF which will change the On or Off condition like this: LEDstate = !LEDstate and then you will write that out to the LED port as digitalWrite(LEDpin, LEDstate); this works because true/false are synonymous to 1/0. Then you need to calculate the next time you want to change that LED state by adding the current time and the blink change together. The close the if with }.

When you get one LED right, all the others are simply copy & paste with a few changes to the LED and pin numbers.

Ray

Thanks a lot! I'm out of time for now, but I'll definitely try that!

Sublimity:

Is this really how the LEDs are connected? It uses twice as many pins as necessary, introduces the possibility that you might drive the LEDs backwards by mistake, doubles the electrical load on the Arduino I/O components and doesn't show any current limiting resistor.

This should give you a general idea of ONE WAY this can be accomplished. Look at the 'K' and 'k' selections to see how the blink state is turned on/off. The test before the loop repeats will control blinking.

If compiles but I have not tested beyond that and I've only had 1 cup of coffee, so it will give the general idea but I will not promise it is entirely correct.

int ledfront = 11; // The frontlights
int ledback = 4; // The backlights
boolean TurnSignalBlink;
boolean TurnSignalState;
unsigned long BlinkTimeMs = 500;
unsigned long BlinkChangeMs;
unsigned long TimeRightNow;
char incomingByte = '0'; // Data the application sends

void setup() {

  pinMode(ledfront, OUTPUT);
  pinMode(ledback, OUTPUT);
  TurnSignalBlink = false;

  Serial.begin(9600); // baud rate
}

void loop() {
  
  if (Serial.available() > 0) { 
    incomingByte = Serial.read(); // read the incoming data
    
  switch (incomingByte) {
     
   case 'K':
   TurnSignalBlink = true;
   BlinkChangeMs = BlinkTimeMs + millis();
     
   case 'k':
   TurnSignalBlink = false;
     digitalWrite(ledfront, LOW);
     digitalWrite(ledback, LOW);
     break;
   

  }
}
TimeRightNow = millis();

if (TurnSignalBlink) {                    // blink until told otherwise
  if (BlinkChangeMs < TimeRightNow) {
       digitalWrite(ledfront, HIGH);
       digitalWrite(ledback, HIGH);
  }
  else {
     digitalWrite(ledfront, LOW);
     digitalWrite(ledback, LOW);
     BlinkChangeMs = BlinkTimeMs + millis(); // new time to change LED state
  }
}
}

@PeterH:
Yes, I know it's horrible, but it's all we have at the moment. We have been working on this for the past three days and we didn't have everything we needed, so this was the only solution we could come up with.

@mrburnette:
I'm working on implementing your code. It's not working yet, but I'll keep working on it. I do think I understand the method, though, using an 'independent' boolean.

Sublimity:
Yes, I know it's horrible, but it's all we have at the moment.

But you do have current-limiting resistors, right?

Yes we do, but we left them at school.

Sublimity:
Yes we do, but we left them at school.

In that case I suggest you disconnect the LEDs until you are able to connect them up properly with the necessary current-limiting resistors, otherwise you're liable to damage the Arduino.

There is another way! Without the resistors, write the sketch so that the LED anodes are put (and kept) in input mode with internal pull-ups enabled, and control the cathodes only.

With modern efficient LEDs, they will show at half a milliamp.