control multiple pins (8 relays) irregular intervals (array?)

Hello everyone

I am making a series of lights that read a set of musical notation. For each light there are on times and off times (on 150ms, off 1000 ms, on 2000 ms, off 10000) that go on for 6 minutes. I have seven lights each with a relatively Long score. I was thinking about a state machine program and really want to be able to group times in an array, like the heartbeat sketch code, or something like that which will permit me to easily change and organize the times. I have been trying to modify the heartbeatsketch code, but don't know enough about arrays to do so. for example how would i have multiple less active on the heartbeat code and a list of times that is 6 minutes long... is that possible
I realize this is a super n00b question. But any help would be appreciated.

/*  Heartbeat
*   Paul Badger 2009
*   Demonstates how to use an array with millis to achieve irregular rhythms
*   This function still uses one delay but it never leaves the LEDpin / LED in 
*   the ON state.
*/


long heartBeatArray[] = {
   50, 100, 15, 1200};
int hbeatIndex = 1;   // this initialization is important or it starts on the "wrong foot"
long prevMillis;

int LEDpin = 13;

void setup()                    
{
   Serial.begin(9600);
   pinMode(LEDpin, OUTPUT);
}

void loop()                    
{
   heartBeat(1.0);   // try changing the parameter
}

void heartBeat(float tempo){
   if ((millis() - prevMillis) > (long)(heartBeatArray[hbeatIndex] * tempo)){
       hbeatIndex++;
       if (hbeatIndex > 3) hbeatIndex = 0;

       if ((hbeatIndex % 2) == 0){ 
           digitalWrite(LEDpin, HIGH); 
           delay((int)heartBeatArray[hbeatIndex]) ;   
           digitalWrite(LEDpin, LOW); 
       }
       hbeatIndex++;
       // Serial.println(hbeatIndex);
       prevMillis = millis();

   }
}

Please modify your post and use the code button </> so your code looks like this and is easy to copy to a text editor. See How to use the Forum

The demo Several Things at a Time controls 3 LEDs and could easily be extended.

If you are using severa LEDs it would make sense to store their timing values in an array to shorten the code.

...R

Hi Robin.
Thanks for your reply. How would I phrase the timing to a phase state on off? How could I use a sequence of timings in the code you provided ? Many thanks

EDIT :

I am actually trying to control 8 different relays, so its basically synchronously switching pins on and off at different times. One way to explain it would be like a drum machine with lights or switching on and off light like one might play a piano.

Silicon_Salut:
Hi Robin.
Thanks for your reply. How would I phrase the timing to a phase state on off? How could I use a sequence of timings in the code you provided ? Many thanks

I presume you meant "phase".

I don't know how to answer that without writing a 2-page essay which may completely miss the point that you are having trouble with.

I think the best thing is for you to try writing a program that works with 2 LEDs (or relays) and if you can't figure it out then post your best attempt. That way we can directly address the problems you are having.

The first thing you should do is write down step-by-step (in English, not code) exactly what you want to happen. If you do that carefully it should then be easy to write a program to follow those steps.

Have look at Planning and Implementing a Program

...R

Hey Robin and Arduino Forum Community!

Thanks for the replies. So i found this as a good example of a state machine that makes the LEDS blink. I would like to add different timings in an array like on for 250 ms off 750 ms on 1000ms etc. turn the interval from a constant to a variable, like i mentioned above. i am reading through the if then functions in my new code below and the heartbeat code, i am trying to integrate the array part into my code but can't really seem to do it.

the code i am using

// constants to set pin numbers
const int greenLedPin =  8; // the number of the green LED pin
const int redLedPin = 13;    // the number of the green LED pin

// Variables will change
int greenLedState = HIGH; //ledState for green LED
int redLedState = HIGH;   //ledState for red LED
long previousMillisRed = 0;    //last time Red LED was updated
long previousMillisGreen = 0;  //last time Green LED was updated

// must be long to prevent overflow
long greenLedInterval = 2500; //interval to blink greed LED (milliseconds)
long redLedInterval = 500;    //interval to blink red LED (milliseconds)
unsigned long currentMillis = 0;

void setup() {

   Serial.begin(9600);
   
  // set the pins to output mode
  pinMode(greenLedPin, OUTPUT);
  pinMode(redLedPin, OUTPUT);
  digitalWrite(redLedPin, redLedState);
  digitalWrite(greenLedPin, greenLedState);
  
  currentMillis = millis();
}

void loop()
{
  // capture the current time
  currentMillis = millis();
  manageRedLed();
  manageGreenLed();
}

void manageRedLed() {  //check if it's time to change the Red LED yet
  if(currentMillis - previousMillisRed > redLedInterval) {
    //store the time of this change
    previousMillisRed = currentMillis;
    redLedState = (redLedState == HIGH) ? LOW : HIGH;
    digitalWrite(redLedPin, redLedState);
  }
}

void manageGreenLed() {
  //check if it's time to change the green LED yet 
  if(currentMillis - previousMillisGreen > greenLedInterval) {
    //store the time of this change
    previousMillisGreen = currentMillis;
    greenLedState = (greenLedState == HIGH) ? LOW : HIGH;
    digitalWrite(greenLedPin, greenLedState);
  }
}

specifically in this code the function that phase switches the LEDS

void manageRedLed() {  //check if it's time to change the Red LED yet
  if(currentMillis - previousMillisRed > redLedInterval) {
    //store the time of this change
    previousMillisRed = currentMillis;
    redLedState = (redLedState == HIGH) ? LOW : HIGH;
    digitalWrite(redLedPin, redLedState);
  }

whereas the heartbeat code that i posted above...

void heartBeat(float tempo){
    if ((millis() - prevMillis) > (long)(heartBeatArray[hbeatIndex] * tempo)){
        hbeatIndex++;
        if (hbeatIndex > 3) hbeatIndex = 0;

        if ((hbeatIndex % 2) == 0){ 
            digitalWrite(LEDpin, HIGH); 
            delay((int)heartBeatArray[hbeatIndex]) ;   
            digitalWrite(LEDpin, LOW); 
        }
        hbeatIndex++;
        // Serial.println(hbeatIndex);
        prevMillis = millis();

    }

there is a bunch of extra junk in the heartbeat code about tempo etc but i am wondering how i could go about incorporating an array in the code i posted.

i hope this is clear. thanks in advance for your attention.

:disappointed_relieved:

I found this code by aarg

unsigned long intervals[] = {100, 100, 100, 100, 100, 100, 50, 2000, 50, 1000, 500};
const byte NUM_OF_INTERVALS = sizeof(intervals) / sizeof(unsigned long);
byte currentInterval = 0;

unsigned long previousMillis = 0;
void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop()
{
  unsigned long currentMillis = millis();          //get current value of millisecond counter
  if (currentMillis - previousMillis >= intervals[currentInterval])  //if the time interval has elapsed
  {
    currentInterval = currentInterval + 1;     // select the next interval in the list
    if (currentInterval >= NUM_OF_INTERVALS)
      currentInterval = 0;
    digitalWrite(LED_BUILTIN, not digitalRead(LED_BUILTIN));    //change state of the LED
    previousMillis = currentMillis;                //save the time of change
  }
}

i am just now trying to write it for multiple pins (less, relays, etc) to run a series synchronously. will this work?

Silicon_Salut:
i am trying to integrate the array part into my code but can't really seem to do it.

instead of a series of variables like

int greenLedState = HIGH; //ledState for green LED
int redLedState = HIGH;   //ledState for red LED
long previousMillisRed = 0;    //last time Red LED was updated
long previousMillisGreen = 0;

if you use arrays it would look like this

byte ledStates[] = {0,0};
unsigned long previousLedMillis[] = {0,0}
byte redLed = 0;
byte greenLed = 1;

then you could refer specifically to the red led like this

ledStates[redLed] = 1;

or you could upldate all the leds with

for (byte n = 0; n < 2; n++) {
   ledStates[n] = 1;
}

...R

Thanks Robin

I took aargs code as a starting point and now I am here

// yet another blink sketch by aarg, Arduino forum
// constants to set pin numbers
const int neon1 =  8; // the number of the green LED pin
const int neon2 = 13;    // the number of the green LED pin
//
// circular list of intervals, alternating off and on
unsigned long intervals1[] = {100, 100, 100, 100, 100, 100, 50, 2000, 50, 1000, 500};
unsigned long intervals2[] = {250, 1000, 100, 100, 100, 100, 50, 2000, 50, 1000, 500};



const byte NUM_OF_INTERVALS1 = sizeof(intervals1) / sizeof(unsigned long);
const byte NUM_OF_INTERVALS2 = sizeof(intervals2) / sizeof(unsigned long);
byte currentInterval = 0;

unsigned long previousMillis = 0;

void setup()
{
  pinMode(neon1, OUTPUT);
  pinMode(neon2, OUTPUT);
}

void loop()
{
  unsigned long currentMillis = millis();          //get current value of millisecond counter
  manageNeon1();
  manageNeon2();

}

void manangeNeon1(); {
  if (currentMillis - previousMillis >= intervals1[currentInterval])  //if the time interval has elapsed
  {
    currentInterval = currentInterval + 1;     // select the next interval in the list
    if (currentInterval >= NUM_OF_INTERVALS1)
      currentInterval = 0;
    digitalWrite(neon1, not digitalRead(neon1));    //change state of the LED
    previousMillis = currentMillis;                //save the time of change
  }
}

void manangeNeon2(); {
  if (currentMillis - previousMillis >= intervals2[currentInterval])  //if the time interval has elapsed
  {
    currentInterval = currentInterval + 1;     // select the next interval in the list
    if (currentInterval >= NUM_OF_INTERVALS2)
      currentInterval = 0;
    digitalWrite(neon2, not digitalRead(neon2));    //change state of the LED
    previousMillis = currentMillis;                //save the time of change
  }
}

but as it happens with debutant coders i am getting compilation errors

Arduino: 1.6.7 (Mac OS X), Board: "Arduino/Genuino Uno"
/Users/baker/Documents/Arduino/leds_sequence/leds_sequence.ino: In function 'void loop()':
leds_sequence:24: error: 'manageNeon1' was not declared in this scope

  • manageNeon1();*
  • ^*
    leds_sequence:25: error: 'manageNeon2' was not declared in this scope
  • manageNeon2();*
  • ^*
    /Users/baker/Documents/Arduino/leds_sequence/leds_sequence.ino: At global scope:
    leds_sequence:29: error: expected unqualified-id before '{' token
    void manangeNeon1(); {
  • ^*
    exit status 1
    'manageNeon1' was not declared in this scope
  • This report would have more information with*
  • "Show verbose output during compilation"*
  • enabled in File > Preferences.*

I will try integrating your code as well Robin THANKS!

void manangeNeon1()
{
}

and in loop()

manageNeon1();

Those names don't match :wink:

And

void manangeNeon1(); {

}

The semi-colon does not belong there.

Silicon_Salut:
I will try integrating your code as well Robin THANKS!

You may (or may not) learn something from my code but do yourself a favour and stick to one program. @aarg's seems to be the third program you have tried in this Thread. He usually has good advice so stick with it.

If there is something in @aarg's code that you don't understand tell us and ask questions about it. But don't go looking for another program.

...R

thanks sterretje and robin for your advice. i am sticking to aargs code.

I do have some questions.

I would now like to control two pins with two different arrays.

To do that i think i need to do the following:

I need to declare the pins to control
create different arrays of variables for each pin
Have a separate function that includes these unique variables for each pin

In preparation of that, I am trying to isolate the if then function in aargs code to duplicate it for each pin but i am getting a current millis not declared error.

//SETS PINS FOR OUTPUT

const int Neon1Pin =  8; // the number of the pin corresponding to the neon relay
const int Neon2Pin = 13;

unsigned long intervals[] = {100, 100, 100, 100, 100, 100, 50, 2000, 50, 1000, 500};
const byte NUM_OF_INTERVALS = sizeof(intervals) / sizeof(unsigned long);
byte currentInterval = 0;
unsigned long previousMillis = 0;


void setup()
{
  pinMode(Neon1Pin, OUTPUT);
}


void loop()
{
  unsigned long currentMillis = millis();          //get current value of millisecond counter
  manageNeon1();
}

void manageNeon1() {
  if (currentMillis - previousMillis >= intervals[currentInterval])  //if the time interval has elapsed
  {
    currentInterval = currentInterval + 1;     // select the next interval in the list
    if (currentInterval >= NUM_OF_INTERVALS)
      currentInterval = 0;
    digitalWrite(Neon1Pin, not digitalRead(Neon1Pin));    //change state of the LED
    previousMillis = currentMillis;                //save the time of change
  }
}

i am learning a lot thanks so much

it may be a bit above your understanding, but I wrote they for another post.

that op wanted to create an arduino controlled chorus of singers that are opening and closing their mouths along wit an array of times.

the 'song' is an array of time and OPEN or CLOSE (easily changed). It seems complex with the use of the class, but put in some times and see if it gets you most of the way there:

#define ULONG_MAX  4294967295UL

enum LoopState {
  LOOP_SONG,
  DO_NOT_LOOP
};


class Singer {
  public:
    Singer();
    ~Singer();
    void begin(int pin, int randomOffset);
    void setSong( int* song, size_t arraySize, unsigned long startMillis);
    void setSong( int* song, size_t arraySize);
    void sing();
    void test(LoopState state);

  private:
    int _pin;
    unsigned int _fuzzy;
    byte _randomOffset;
    bool _mouthOpen;
    unsigned long _startMillis;
    int* _currentSong;
    size_t _numIntervals;
    int _nextInterval;
    int _index = 0;
    LoopState _loopState = LOOP_SONG;
    int _noSong[1] = {NULL};
};

Singer::Singer() {}
Singer::~Singer() {}

void Singer::begin(int pin, int randomOffset)
{
  _pin = pin;
  _randomOffset = randomOffset;
  pinMode(_pin, OUTPUT);
  digitalWrite(_pin, LOW);
  _mouthOpen = false;
  _index = 0;
}

void Singer::setSong(int* song, size_t arraySize, unsigned long startMillis)
{
  _currentSong = song;
  _numIntervals = arraySize;
  byte lowestValue = 0; 
  for (int i = 0; i < _numIntervals; i++)
  {
    lowestValue = constrain(min(_randomOffset, _currentSong[i]), 0, 256);
  }
  _randomOffset = lowestValue;
  Serial.print("Random Offset = ");
  Serial.println(_randomOffset);
  _nextInterval = _currentSong[0];
  _fuzzy = _currentSong[0] + random(-_randomOffset, _randomOffset+1);
  _startMillis = startMillis;
  _index = 0;
}

void Singer::setSong(int* song, size_t arraySize)
{
  this->setSong(song, arraySize, millis());
}

void Singer::sing()
{
  long MilliDiff;
  if (_startMillis <= millis())
    MilliDiff = millis() - _startMillis;
  else
    MilliDiff = ULONG_MAX - _startMillis + millis();
  if (MilliDiff >= _fuzzy)
    {
      _mouthOpen = !_mouthOpen;
      digitalWrite(_pin, _mouthOpen);
      _startMillis += _currentSong[_index];
      _index++;
      if (_index >= _numIntervals)
      {
        _index = 0;
        if(_loopState != LOOP_SONG)
        this->setSong(_noSong, 0);
      }
      _fuzzy = _currentSong[_index] + random(-75, 76);
    }
}

int mySong[] = {1000, 1000, 300, 1000, 1000, 1000, 2000, 1000, 1000, 1250};
int staccato[] = {100, 250, 150, 300, 190, 175, 190, 400, 450, 100, 250, 150, 300, 190, 175, 190, 400, 450, 100, 250, 150, 300, 190, 175, 190, 400, 450, 100, 250, 150, 300, 190, 175, 190, 400, 450, };
int noSong[] = {NULL};

Singer lead;
Singer background1;
Singer background2;

void setup()
{
  Serial.begin(9600);
  randomSeed(analogRead(A4));
  lead.begin(13, 50);         // set the pin and the max random timing that singer is "off timing"
  background1.begin(8, 150); // background singers are a little more apt to be off
  background2.begin(9, 150); 
  unsigned long justNow = millis();
  lead.setSong(mySong, sizeof(mySong) / sizeof(mySong[0]), justNow);
  background1.setSong(mySong, sizeof(mySong) / sizeof(mySong[0]), justNow); // synch them
  background2.setSong(mySong, sizeof(mySong) / sizeof(mySong[0]), justNow); // synch them
}

void loop()
{
  lead.sing();
  background1.sing();
  background2.sing();
}

you can try it if you feel comfortable... just edit mySong[] or change the song to staccato to see how it works blinking LEDs.

THANKS!

i guess you could have different songs (timings) and different singers (leds, motors, whatever) in the way you coded it?

I have it working for two using aargs code. I think the code is butt ugly and I am a bit out of my league but it works

//SETS PINS FOR OUTPUT

const int Neon1Pin = 8; // the number of the pin corresponding to the neon relay
const int Neon2Pin = 13;

int Neon1State = HIGH; //On or Off Start Position
int Neon2State = HIGH;

unsigned long Neon1intervals[] = {1000, 500, 500, 1000, 5000};
unsigned long Neon2intervals[] = {1000, 500, 500, 1000, 5000};
const byte NUM_OF_1_INTERVALS = sizeof(Neon1intervals) / sizeof(unsigned long);
const byte NUM_OF_2_INTERVALS = sizeof(Neon2intervals) / sizeof(unsigned long);
byte currentInterval1 = 0;
byte currentInterval2 = 0;

long previousMillis1 = 0;
long previousMillis2 = 0;
unsigned long currentMillis = 0;


void setup()
{
  pinMode(Neon1Pin, OUTPUT);
  pinMode(Neon2Pin, OUTPUT);
  digitalWrite(Neon1Pin, Neon1State);
  digitalWrite(Neon2Pin, Neon2State);
  currentMillis = millis();
}


void loop()
{
  currentMillis = millis();          //get current value of millisecond counter
  manageNeon1();
  manageNeon2();
}

void manageNeon1() {
  if (currentMillis - previousMillis1 >= Neon1intervals[currentInterval1])  //if the time interval has elapsed
  {
    currentInterval1 = currentInterval1 + 1;     // select the next interval in the list
    if (currentInterval1 >= NUM_OF_1_INTERVALS)
      currentInterval1 = 0;
      Neon1State = (Neon1State == HIGH) ? LOW : HIGH;
    digitalWrite(Neon1Pin, Neon1State);    //change state of the LED
    previousMillis1 = currentMillis;                //save the time of change
  }
}


void manageNeon2() {
  if (currentMillis - previousMillis2 >= Neon2intervals[currentInterval2])  //if the time interval has elapsed
  {
    currentInterval2 = currentInterval2 + 1;     // select the next interval in the list
    if (currentInterval2 >= NUM_OF_2_INTERVALS)
      currentInterval2 = 0;
      Neon2State = (Neon2State == HIGH) ? LOW : HIGH;
    digitalWrite(Neon2Pin, Neon2State);    //change state of the LED
    previousMillis2 = currentMillis;                //save the time of change
  }
}

EDIT : Which ways could i condense this code instead of repeating certain parts of it 7 times for the seven lights? im really into ELEGANT CODE

Which ways could i condense this code instead of repeating certain parts of it 7 times for the seven lights?

Make the code that manages the neons into a function and pass a parameter indicating which neon is to be managed.

Make the code that manages the neons into a function and pass a parameter indicating which neon is to be managed.

yes i know its possible, could you give an example even a simple one or maybe a link to a tutorial that deals with this kind of swap? thanks for responding.

:o

If you want to write neat code, you can start by not using global variables if they don't need to be global :wink:

Your previousMillis1 and previousMillis2 are specific to manageNeon1() and manageNeon2(). So they need to be declared in those functions. The same applies to currentInterval1 and currentInterval2

void manageNeon1() {
static unsigned long previousMillis = 0;
static byte currentInterval = 0;

  if (currentMillis - previousMillis >= Neon1intervals[currentInterval])  //if the time interval has elapsed
  {
    currentInterval = currentInterval + 1;     // select the next interval in the list
    if (currentInterval >= NUM_OF_1_INTERVALS)
      currentInterval = 0;
    Neon1State = (Neon1State == HIGH) ? LOW : HIGH;
    digitalWrite(Neon1Pin, Neon1State);    //change state of the LED
    previousMillis = currentMillis;                //save the time of change
  }
}

OK, so much for that. I hope you're up for the below :smiley:

Next you need to start thinking a little object oriented. You started this thread with 'on' times and 'off' times. You wanted to switch a led ON for a certain duration and next switch it OFF for a certain duration. I'm not much of a C++ programmer so will not use a class for this but will stick to a so-called structures.

Instead of using an array of 'on' times and an array with 'off' times, combined them in a structure. The structure allows you to combine multiple variables into one variable.

/*
  structure to hold timing information (ontime and offtime)
*/
struct TIMING
{
  // time that we want the LED to be ON
  unsigned long ontime;
  // time that we want the LED to be OFF
  unsigned long offtime;
};

Next you can define arrays for the timings for each led. I've used a RGB led and so there are only three sets of timings.

/*
  LED timing
*/
TIMING timingRed[] = {{1800, 1800}, {1000, 1000}};  // red led: on 1800 / off 1800, on 1000 / off 1000
TIMING timingGreen[] = {{1200, 1800}, {750, 1750}, {1000, 1000}};
TIMING timingBlue[] = {{2500, 1500}};

If you have the same timing for all leds, you can simply define one array.

Next you can think about an individual led. How does it compose? A pin that it is connected to and it has one or more timings. You also need to keep track which timing is currently selected. That brings the following struct.

/*
structure to hold information for a LED
 */
struct LED
{
  // pin number of LED
  int pin;
  // timings for the LED
  TIMING *timings;
  // number of timings
  int numtimings;
  // current timing
  int currenttiming;
  // start time when LED is switched ON or OFF
  unsigned long starttime;
};

There are a few other variables; one to keep track of the number of timings and one to keep track of the start time of a delay.

You can now declare an array of LEDs using the above struct. Again. as there are three leds, the array contains three elements.

/*
  LED declaration
*/
LED leds[] = {
  // RED: pin 9, timings, number of timings, current timing 0, start time 0
  { 9, timingRed, sizeof(timingRed) / sizeof(TIMING), 0, 0},
  // GREEN: pin 10, timings, number of timings, current timing 0, start time 0
  { 10, timingGreen, sizeof(timingGreen) / sizeof(TIMING), 0, 0},
  // BLUE: pin 11, timings, number of timings, current timing 0, start time 0
  { 11, timingBlue, sizeof(timingBlue) / sizeof(TIMING), 0, 0},
};

Last thing is to write a function that does the processing of one of the leds using an index; the below is a simple demo to show how to access the variables inside the struct.

void manageLed(int ledIndex)
{
  Serial.print(F("ledIndex: "));
  Serial.println(ledIndex);
  Serial.print(F("pin: "));
  Serial.println(leds[ledIndex].pin);
  Serial.print(F("number of defined timings: "));
  Serial.println(leds[ledIndex].numtimings);
  for (int cnt = 0; cnt < leds[ledIndex].numtimings; cnt++)
  {
    Serial.print(F("\ttiming #: "));
    Serial.println(cnt);
    Serial.print(F("\tontime: "));
    Serial.println(leds[ledIndex].timings[cnt].ontime);
    Serial.print(F("\tofftime: "));
    Serial.println(leds[ledIndex].timings[cnt].offtime);
  }
  Serial.print(F("currently selected timing: "));
  Serial.println(leds[ledIndex].currenttiming);
  Serial.print(F("starttime: "));
  Serial.println(leds[ledIndex].starttime);
}

And the last piece is how to loop through the LEDs and call the function; I've placed it in setup (as it's just a demo).

// sensible names for ON and OFF; swap around if LOW defines ON
#define ON HIGH
#define OFF LOW

void setup()
{
  // serial for debugging
  Serial.begin(9600);
  
  //loop through leds
  for (int cnt = 0; cnt < sizeof(leds) / sizeof(LED); cnt++)
  {
    // set LED pins as output
    pinMode(leds[cnt].pin, OUTPUT);
    // switch leds ON
    digitalWrite(leds[cnt].pin, ON);

    // display
    manageLed(cnt);
  }
}