Multiple LEDs blinking at different intervals plus servo sweep

Hello! I am very out of practice with programming. I am trying to prototype a robot Halloween costume for my almost 3 yr old. I have adapted the sketch by mikalhart found here:
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1225593807
I have adapted the number of LEDs and the rates of flash (but I am pretty much using his code) I love the idea that I can alter the blink of each in the array, but I am unsure of where to go next.

If nobody cares to click, I will post the sample code below.

I would also love to have a servo sweep simultaneously (like an antenna or some other robot type device), but I am clueless as to how to implement this.

I have tried to simply cut and paste the servo sweep example into this sample code, and as many of you could imagine, the introduction of delay means the LEDs blink THEN the servo sweeps.

My original thought was to implement a ping sensor to "turn on" the costume (or off) at a certain distance, but I am lost and will graciously accept any help. I have played with some other examples including the ping sensor (like parking assistants) that turn on LEDs, but blinky and servo are more important to the wife than proximity sensors.

I thank you for reading! I am quite lost, so be gentle. :slight_smile:

Thanks!
-e

Mikal's code:

/* Blink Multiple LEDs without Delay
*
* Turns on and off several 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.
*/
int led1 = 13;                // LED connected to digital pin 13
int led2 = 12;
int value1 = LOW;                // previous value of the LED
int value2 = LOW;                // previous value of the LED
long time1 = millis();
long time2 = millis();

long interval1 = 1000;           // interval at which to blink (milliseconds)
long interval2 = 500;

void setup()
{
 pinMode(led1, OUTPUT);      // sets the digital pin as output
 pinMode(led2, OUTPUT);
}

void loop()
{
 unsigned long m = millis();

 if (m - time1 > interval1){
   time1 = m;

   if (value1 == LOW)
     value1 = HIGH;
   else
     value1 = LOW;

   digitalWrite(led1, value1);
 }

 if (m - time2 > interval2){
   time2 = m;

   if (value2 == LOW)
     value2 = HIGH;
   else
     value2 = LOW;

   digitalWrite(led2, value2);
 }
}

I just realized that I posted an earlier version of Mikal’s code. This is the one that I originally used FWIW:

/* Blink Multiple LEDs without Delay
*
* Turns on and off several 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.
*/
const int NUMLEDS = 18;
byte pin[NUMLEDS] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
byte state[NUMLEDS] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
unsigned long interval[NUMLEDS] = {100, 500, 1000, 750, 750, 750, 750, 750, 5000, 250, 1000, 1000, 1000, 50, 7500, 500, 400, 250};
unsigned long time[NUMLEDS];

void setup()
{
 for (int i=0; i<NUMLEDS; ++i)
   pinMode(pin[i], OUTPUT);
}

void loop()
{
 unsigned long m = millis();

 for (int i=0; i<NUMLEDS; ++i)
   if (m - time[i] > interval[i])
   {
     time[i] = m;
     state[i] = state[i] == LOW ? HIGH : LOW;
     digitalWrite(pin[i], state[i]);
   }
}

Thanks~
-e

I would also love to have a servo sweep simultaneously (like an antenna or some other robot type device), but I am clueless as to how to implement this.

I have tried to simply cut and paste the servo sweep example into this sample code, and as many of you could imagine, the introduction of delay means the LEDs blink THEN the servo sweeps.

So, don't use delay(). Look at the code you posted. Understand how it determines if it is time to alter the display state of the LEDs.

Use that same approach to determine if it is time to move the servo to the next position.

My original thought was to implement a ping sensor to "turn on" the costume (or off) at a certain distance, but I am lost and will graciously accept any help.

Do you have a ping sensor? Do you know how to read the distance from it? Do you know how to use an if statement?

Making all the stuff that the costume does only happen when the distance is greater than some value and less than some other value is trivial.

Thank you for the reply!! I will look at that and do some experiments this evening.

I guess I should start by trying to adapt the servo sweep example to work without delay. If I can do this, I can surely figure the rest out. I am just picking up bits of info as I stumble around these forums. I am trying to learn. I need to read the knowledge base stuff and study up a bit.

I appreciate coaxing it out of me, instead of coding it for me. I don't want to give up on this, I just don't know what I am doing and have little time to "play".

Thanks!
-e

Ok. I have made minute progress. Here is the current code:

const int pingPin = 1; 
const int NUMLEDS = 18;
byte pin[NUMLEDS] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
byte state[NUMLEDS] = {LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
unsigned long interval[NUMLEDS] = {100, 500, 1000, 722, 750, 325, 40, 800, 5000, 250, 1000, 1000, 1000, 50, 7500, 500, 400, 250};
unsigned long time[NUMLEDS];



#include <Servo.h> 
 
Servo myservo;  
 
int pos = 0;   
 
void setup() 

{
 for (int i=0; i<NUMLEDS; ++i)
   pinMode(pin[i], OUTPUT);

  myservo.attach(21);  // attaches the servo on pin 9 to the servo object 
} 
 
 
void loop() 
{ 
  
  int sensorReading = digitalRead(pingPin);
  long duration;
   pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
 
  
  unsigned long m = millis();
  for(pos = 0; pos < 160; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    
  } 
  for(pos = 160; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    
  } 
  
  {
 
if (duration <= 6600)
 for (int i=0; i<NUMLEDS; ++i)
   if (m - time[i] > interval[i])
   {
     time[i] = m;
     state[i] = state[i] == LOW ? HIGH : LOW;
     digitalWrite(pin[i], state[i]);
   }
if(duration >=6599)
myservo.write (0);
   
} }

First, the 6600 is arbitrary, but puts the distance pretty close to a kid knocking on a door and saying “trick or treat”

This does most of what I was hoping to accomplish. I am able to flash many LEDs at different rates while the servo sweeps and it works with proximity. I do have two unexpected results. The servo continues to move (rapidly) back and forth while out of range (this is why I tried the if(duration >2200) myservo.write (0), but it does not seem to change this), and the servo sweeps a bit erratic while in range (I am only going based on trial and error and have had some luck). This is not a deal breaker, as I feel I have been moderately successful.

If someone could help me understand what I am doing wrong, and if there is any way to shorten/ revise my code that would be awesome. I pretty much have the functionality I wanted, but I would like a more polished result. I would also rather not burn up a servo too if there is a way to make it run properly.

So I am in need of a tiny bit of guidance on making the servo sweep to take a bit longer, and stopping it when out of range.

Thanks!
-e

I suspect with the number of lights you have, at some point you may not have enough power for the servo. The traditional advice is to wire the servo separately. Here is a tutorial somebody else wrote and posted, when I asked a servo question in the past: http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html.

As a style guide, I would suggest naming the pin array something else, perhaps led_pin or even just leds, since there are other pin assignments like the servo pin.

When you attach the servo, you have the comment to use pin 9, but you are actually using pin 21.

You probably want some sort of delay after moving the servo. Unfortunately to do it right, you probably need to reorganize the program not to use delay, but you need a state machine for each of the lights and servo position, that has when each thing moves. This is a hard concept to teach, particularly if you are new to programming. There was a posting about the QP framework which allows creation of state machines, but it still may be a tough nut to crack. Arduino Playground - HomePage.

Thank you! I am in fact using pin 21 for the servo, I just didn't change the comment. I will look into your suggestion. I am very in experienced with this stuff, but i do want to learn.

Thanks!
-e

  long duration;
   pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);

This should be in a function that returns a value - the distance. Don’t clutter up loop() with code like this.

  for(pos = 0; pos < 160; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    
  } 
  for(pos = 160; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 
    
  }

Telling the servo to go to a position is one thing. Waiting for it to get there before telling it to move again is another. The servo will not have had time to move even a few degrees before it is told to move back to 0.

The whole point with the blink with delay example is that you periodically see if it is time to do something. In the case of the servo, you want to see if it is time to move to the next position. If so, you then need to decide what the next position is to be. That is NOT done with a for loop or even a pair of them.

When you use for loops or if statements, there should always be curly braces surrounding the body of the statement. In circumstances where there is only one statement in the body, the braces are optional, but I still prefer to see them used.

Putting each { and } on its own line, and using Tools + Auto Format will make your code much more readable. The lined up { and } make it very easy to see what a block of code looks like, especially the extents of that block.

PaulS:
Putting each { and } on its own line, and using Tools + Auto Format will make your code much more readable. The lined up { and } make it very easy to see what a block of code looks like, especially the extents of that block.

Though my experience is that the code blocks in this forum sometimes undoes things like carefully lining up comments in columns.

Thanks for the help!! I had a friend of mine help with my code. I have a couple of questions that he couldn’t answer.

Now I have the ping sensor, servo, and LEDs mostly working. However, while the servo is connected (and powered externally with grounds connected), the range on the ping sensor is inconsistent. We verified this with serial monitor too. I got most of what I wanted out of this sketch, but if anybody can suggest a “slicker” way to do any of this, or possibly help to explain why having the servo in the circuit causes anomalies, this would be awesome.

I’m not sure if this matters, but I am using the Mega 2560, as I quickly ran out of pins on my others. If switching boards might help minimize conflict this is not a deal breaker. Could another sensor be a better choice?

Thanks!
-e

Revised code:

const int pingPin = 20; 
const int NUMLEDS = 18;
byte pin[NUMLEDS] = {
  2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19};
byte state[NUMLEDS] = {
  LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW, LOW};
unsigned long interval[NUMLEDS] = {
  400, 500, 1000, 722, 750, 325, 200, 800, 5000, 250, 1000, 300, 775 , 1000, 300, 500, 400, 250};
unsigned long time[NUMLEDS];
unsigned long servotime;

unsigned long degree_interval = 20;


#include <Servo.h> 

Servo myservo;  

int servo_degree = 0; // variable to store the servo position
boolean forward = true;
long duration;

void setup() 
{

  // initialize serial communication:
  //Serial.begin(9600);
  
  for (int i=0; i<NUMLEDS; ++i){
    pinMode(pin[i], OUTPUT);
  }

  myservo.attach(21);  // attaches the servo on pin 21 to the servo object 
} 

long getDuration(){
 
  int sensorReading = digitalRead(pingPin);
  pinMode(pingPin, OUTPUT);
  digitalWrite(pingPin, LOW);
  delayMicroseconds(2);
  digitalWrite(pingPin, HIGH);
  delayMicroseconds(5);
  digitalWrite(pingPin, LOW);
  pinMode(pingPin, INPUT);
  duration = pulseIn(pingPin, HIGH);
  return duration;
}

void loop() 
{ 
  //Serial.println();
  duration = getDuration();


  unsigned long m = millis();
  
  /*
  for(pos = 0; pos < 160; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 

  } 
  for(pos = 160; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 

  } 
  */



    if (duration <= 6600){
      
      
      for (int i=0; i<NUMLEDS; ++i){
        if (m - time[i] > interval[i])
        {
          time[i] = m;
          state[i] = state[i] == LOW ? HIGH : LOW;
          digitalWrite(pin[i], state[i]);
        }
      }
      
      
      if( m - servotime >= degree_interval){
        myservo.attach(21);
        myservo.write(servo_degree);
        
        if(servo_degree >= 180){
          forward = false;
        }
        if(servo_degree <= 0){
          forward = true;
        }
        
        if(forward){
          servo_degree++;
        }
        else{
          servo_degree--;
        }
        
        servotime = m;
     
      }      
      
    }
    else{
      myservo.detach();
    }



}

Why are you attaching and detaching the servo?

Why is Serial.begin() commented out?

However, while the servo is connected (and powered externally with grounds connected), the range on the ping sensor is inconsistent. We verified this with serial monitor too.

With Serial,begin() commented out?

  /*
  for(pos = 0; pos < 160; pos += 1)  // goes from 0 degrees to 180 degrees 
  {                                  // in steps of 1 degree 
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 

  } 
  for(pos = 160; pos>=1; pos-=1)     // goes from 180 degrees to 0 degrees 
  {                                
    myservo.write(pos);              // tell servo to go to position in variable 'pos' 

  } 
  */

Did we really need to see this?

I'm sorry. I am not trying to waste anybody's time. I should have removed the commented part, It was in there from a previous revision. I am not trying to offend anyone. I am unaware if this is bad forum etiquette.

The servo would pulse and get hot while at the edge of the range. I put in the detach to stop the servo's jitter while the ping is out of range. Is there a better way to do this?

The serial.begin is commented because the servo AND the LED behavior was erratic with it un commented. Is this normal? No I did not read serial output with this commented.

Please suggest improvements. I am trying to learn how to do this better, but don't know how to ask the right questions.
thanks
-e

The servo would pulse and get hot while at the edge of the range. I put in the detach to stop the servo's jitter while the ping is out of range. Is there a better way to do this?

Of course there is. Don't try to drive the servo to a non-valid position. You are assuming that the servo's range of motion is from 0 to 180 degrees. Have you verified, independently, that the servo will actually move to 180 degrees with overheating?

  int sensorReading = digitalRead(pingPin);

What is this, in getDuration(), doing?

E3po:
The servo would pulse and get hot while at the edge of the range. I put in the detach to stop the servo's jitter while the ping is out of range. Is there a better way to do this?

Probably you're asking the servo to move past the end of its travel. You need to put constraints in your code so that you don't ask the servo to go to an impossible position.

Thanks for the replies. The range I was referring to is the limits of the ping (6600ms) not the range of the servo. The servo has 180 degrees of motion and I don’t think my code goes beyond this, correct? I tried to sweep only 160 degrees in an earlier revision and that is what the commented code reflects, but this is in fact a 180 degree servo. By verified, independently, do the package labels and product manuals count? Other than that, the servo sweep example works fine. The jitter/ pulse/ heating up I have issue with id when the motor does not completely STOP when the constraint of duration (6600) is reached.

The servo was pulsing (not completely off) when the sensor was reading around 6600 for duration. While the sensor is consistently returning a value of less than 6600, the servo moves fluidly 180 degrees.

As far as what is :

  int sensorReading = digitalRead(pingPin);

What is this, in getDuration(), doing?

My friend that helped me clarify the code was working on a “buffer” where it looks for multiple readings longer than the expected duration. The thought was to reduce the jitter, it was a variable to be called on in another idea.

Basically another line I should have deleted before asking for advice.

My current goals are:
More accurate scaling/ reading of the ping sensor
----The readings bounce all over the place while the servo is connected. Should I employ a greater than but less than range for this?

If I could find out why the ping readings are inconsistent while the servo is connected I think the duration of 6600 would accurately “turn on and off” the device. There is erratic behavior with the LEDs and the servo when it is “about to turn off”.

Is it possible that the LEDs (or even just the amount of them) is causing the PING sensor to not get consistent voltage from the 5v pin on the arduino?

Thanks for reading.
-e

The readings bounce all over the place while the servo is connected.

Are they consistent and reasonable when the servo is not connected?

The pulseIn() function returns a value in microSeconds. You should be able to convert that to inches or centimeters and determine whether the distance being determined is reasonable.

There is erratic behavior with the LEDs and the servo when it is "about to turn off".

Is it possible that the LEDs (or even just the amount of them) is causing the PING sensor to not get consistent voltage from the 5v pin on the arduino?

It is more likely that the servo is the culprit, not the LEDs. How are you powering the servo? Not from the Arduino, I hope.

E3po:
If I could find out why the ping readings are inconsistent while the servo is connected I think the duration of 6600 would accurately "turn on and off" the device. There is erratic behavior with the LEDs and the servo when it is "about to turn off".

Is it possible that the LEDs (or even just the amount of them) is causing the PING sensor to not get consistent voltage from the 5v pin on the arduino?

I couldn't see anything wrong with your code. There are several places where you put the opening brace '{' on the same line as a condition rather than on a line on its own, which is not very good practice imo. There's some legacy commented-out code too that would be better removed. Neither of these affect the logic. However, I couldn't guess what your code looked like when you took out the detach() so I can't guess why the servo was heating up.

Is your ping sensor sensitive to the supply voltage? If you're powering the LEDs directly from the Arduino them each one will drag the voltage down slightly when you turn it on. Since you're turning them on and off at various intervals, that could be enough to cause jitter in the ping sensor if it is voltage-sensitive. If you disconnect the LEDs, does that improve the consistency of the ping results?

Thanks for the replies! I haven't removed the LEDs yet, but the PING example returns very consistent readings. I will try that next. I am using a 4-pin (HC-SR04) sensor.

I have tried commenting out everything having to do with the LEDs and the serial info is still all over the place. I am at a loss right now. I will keep plugging away.

Thanks!
--e

I have also tried another Arduino (Duemilanove) with less LEDs and I get the same results.