Mulitple flashing LED with different sequences

Hi, I am fairy new to programming and electronics, but have rewired my defender fine so not a complete novice.

I am trying to create a wall piece, that is a navigation chart and includes all the navigational lights flashing the correct sequences. Something like - www.lightedseacharts.com. Example of sequences are, (6 quick, then off - every 15 seconds)(2 quick flashes ever 10 seconds)(3 quick one long) etc, I think I have sorted out continuous flashes.

I just wanted to know if one Arduino Nano/Uno board would be able to host multiple sequences, and then split each sequence between the LEDs through a breadboard? Would it be powerful enough from one? Any additional advice will be greatly appreciated. Is there a similar code that I can learn from, looked at a Morse code forum.

Almost without a doubt. Read these fora for awhile and see amazing things done with these small machines.

How many are the LEDs that each could show a pattern?

Can you post any code you've written, or are working with that we might see what kind of track you are on?

a7

1 Like

Yes it is possible. Try this code, it is the Base code for many of my projects. I just updated some of the comments a few days ago. Good luck.

char Gil[] = "\n\tVersion: 1.1A 230617";     // This is printed in the hello message at setup
/**************************************[ FYI ]***********************************************
**                                                                                         **
**  This Code was compiled with IDE 1.8.13 on AMD processor running Linux Mint 18.3 Sylvia **
**  This core module uses a state sequencer (threads) to control the processes and there   **
**  pseudo priority and processing rate. this structure is such as the higher the sequence **
**  number the higher pseudo priority of the task(higher occurs first). Also contained in  **
**  this Base Module is Gil_Timer() Great Industrious Little _Timer. Although not super    **
**  accurate it will work for a lot of applications and keep the mills portion simple, it  **
**  is now based on mills rather then interrupts. This Great Industrious Little timer      **
**  uses 16 bit integers in place of unsigned 32 bit numbers. This can save a lot of RAM   **
**  Depending on the number of timers implemented. The time base can be changed at will by **
**  changing Tick. This can be done in the program by changing to int Tick = #;            **
**                                                                                         **
**     I have used this on Arduino (Nano, UNO, Mega, ESP8266 ) OEM and China versions.     **
**  This code is written in the way I like it. I have a few years programming in various   **
**  various assemblers, I am now in the process of learning the higher level languages.    **
**  If you do not like the format either change it or do not use it! The original of this  **
**  was written in about 1981 in assembler so it is creaky old.                            **
**   Gil Shultz 200614  -  June 14, 2020                                                   **
*********************************************************************************************/

// Set this up at the beginning of the program before setup() or loop()
/***************************[ Great Industrious Little Timer ]************************************
 *                                                                                               *
 *   This defines the offset (Gil Timer Number) into the timing array. Each of these timers is   *
 *   counted down x times a second, when zero is reached the timer is no longer decremented.     *
 *   To use the timer simply place the delay time you want (16 bit unsigned integer) in the      *
 *   appropriate Array location and check for zero which indicates the time has expired. The     *
 *   only limit is the amount of Ram available and the amount of time available for servicing    *
 *   the pseudo interrupt. The following constructs are offsets into the timer array Gil_Timers  *
 *   "num_Gil_Timers" determines the number of timers that will be serviced. You can increase or *
 *   decrease the number This code has been modified to use the millis() function in militime()  *
 *   Currently we are set up to count 0.01 seconds so 500 = 5 seconds, 100 = 1 second.           *
 *      140212                                                                                   *
 *****************[ This has been modified from an Arduino interrupt ISR ]***********************/
const  int     num_Timers          =      10; // Number of Timers Zero used for Debug
int Gil_Timer[num_Timers]          =     {0}; // Array defining the storage location for the timers.
const  int        BugTimer         =       0; // This timer is reserved for Debugging, do not use
const  uint16_t   BugTime          =     500; // Temporary time to be changed as needed
const  int        Alrm_Timer       =       1; // offset into array for alarm timer
const  uint16_t   Alrm_Time        =     100; // Alarm blink Delay 100 = 1 second or 0.01
const  int        Case_Timer       =       2; // offset in array for the Case Timer
const  uint16_t   Case_Time        =     150; // Delay time for the case timer
const  int        A_test_timer     =       3;
const  uint16_t   A_test_time      =     100; // These are samples that do nothing but print a number
const  int        B_test_timer     =       4; // on the console. It tries to demonstrate that the
const  uint16_t   B_test_time      =      90; // timers although have the same time base operate
const  int        C_test_timer     =       5; // independently from each other.
const  uint16_t   C_test_time      =      80;
const  int        D_test_timer     =       6;
const  uint16_t   D_test_time      =     70;
const  int        E_test_timer     =       7;
const  uint16_t   E_test_time      =     150;
const  int        F_test_timer     =       8;
const  uint16_t   F_test_time      =     150;
const  int        G_test_timer     =       9;
const  uint16_t   G_test_time      =     150;
// Fill in with additional Timers!

/***********************************************************************************************
 *                                                                                             *
 *  Tick Timers count up from 1 to 0x0 (overflow to zero). If the value is zero the timer is   *
 *  idle. Nice for elapsed timing, debouncing etc. This uses the same time base and Gil Timer  *
 *    230615                                                                                   *
 ***********************************************************************************************/
const  int     num_Tickers         =       4; // These are used to measure elapsed time they start at
int Gil_Ticker[num_Tickers]        =     {0}; // This is the number of timers, it should match below
const  uint8_t     A_TicTimer      =       0; // Timer offset into array
const  uint16_t     A_TicTime      =       0; // If zero it is ignored
const  uint8_t     B_TicTimer      =       0; // Timer offset into array
const  uint16_t     B_TicTime      =       0; // If zero it is ignored
const  uint8_t    C_TicTimer       =       0; // Timer offset into array
const  uint16_t     C_TicTime      =       0; // If zero it is ignored
const  uint8_t     D_TicTimer      =       0; // Timer offset into array
const  uint16_t     D_TicTime      =       0; // If zero it is ignored

// Change to: int Tick;  for your code to dynamically change the time base.
const  int     Tick                =      10; // Time base for Gil_Timers 1=1ms 10=10ms 100=100ms
unsigned long  Last_Time           =       0; // This is part of the time base for Gil_Timers

// State equates
const int Max_State                =      50; // Maximum number of states (Threads)
int stateM                         =       0; // Our state machine counter

// Hardware Equates    I always use a test pin as it makes debugging easer
const   int      TestPin           =       7; // This is pinned as D2 on my Arduino D1 R2 Board

//***********[ Location and ID information ] ************************************
char  Revision_Date[] =  "Revision\0\0";  // This is a dummy for the revision Number
char  TmpBuffer[16] {0};                  // Used for string calculations

//***************[ These are used for the console Hello Message ]****************************
const char  Source_file[]          =    __FILE__;  // Main File Name
const char  compile_date[]         = __DATE__ " " __TIME__; // Time and Date from Complier

// Put this with your Functions

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Functions]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/******************************[ Functions Installed ]****************************************
*                                                                                            *
*   militime()             + used to replace interrupt timer base.                           *
*   Mark_It()              + Generates a pulse on the Test Pin, used for timing etc.         *
*   logname(1,2,3)         + Configure year (4 dig) month day as a string. Use compiler info *
*   LastLine()             + Used as part of the serial number must be at end                *
*                                                                                            *
*   ISR(TIMER1_COMPA_vect) + This is the Gtimer interrupt replaced with millies              *
*                                                                                            *
****************************[ Listed in program order ]***************************************/



/****************************[ Process the software timers here ]**************************
*                                                                                         *
*          Timers[?] is processed Here and called from the main loop                      *
*                                                                                         *
*   This updates the (Gil_Timers) every 0.1 second called in the main loop by utilizing   *
*   the Mills function. This then decrements each timer every time period until they      *
*   reach zero. This function yielding almost an unlimited number of timers only          *
*   constrained by the amount of memory available.                                        *
*                                                                                         *
*   Set timers[?] to the delay time wanted see Tick for period. The value at timers[?]    *
*   has the time remaining until time out (tick). The timer will will stop at zero when   *
*   timed out. It is the responsibility  of the application to check  the  timers[?] for  *
*   zero. Note there is some jitter because of interrupts etc. This Unlike the "delay()"  *
*   function is non blocking. This was derived from my ISR with the same name.            *
*                                                                                         *
*   The Tickers timers will count up unsigned from 1 through 0, the overflow causes zero  *
*                                                                                0.14 us  *
*******************[ Note these Gil_Timers are NOT for critical timing]********************/
void militime() {
  unsigned long currentTime = millis();       //  Updates frequently
  if (currentTime - Last_Time >= Tick) {      // This is the time base for Gil_Timers
    int i;  int x;                            // Temporary save RAM
    for (i = 0; i < num_Timers; i++) {        // Initialize our loop, i is our timer pointer
      x = Gil_Timer[i];                       // Get the Timer we are pointing at
      if (x > 00) {                           // If zero we are finished, if not decrement it by 1
        x--;                                  // if not subtract a second
        (Gil_Timer[i]) = x;                   // and update the timer location
      }           // End of X > zero
    }          // End of for i loop
    Last_Time = currentTime;                  // Update the timing for the next time around
    // ******  Tickers
    for (uint8_t i = 0; i < num_Tickers; i++) { // Initialize our loop, i is our timer pointer
      uint16_t x = Gil_Ticker[i];             // Get the Timer we are pointing at
      if (x > 00) {                           // It will roll over and stop at zero
        x++;                                  // if not add a tick
        (Gil_Ticker[i]) = x;                  // and update the timer location
      }           // End of X > zero
    }          // End of for i loop
  }      // end of event time test
}     // End of militime


/*******************************************************
 *                                                     *
 *   Send Marker to trigger Logic Analyzer or Scope.   *
 *                                                     *
 *******************************************************/
void Mark_It() {
  //  if (Gil_Timer[Alrm_Timer] == 0) {       // Loop Delay
  //    Gil_Timer[Alrm_Timer] = Alrm_Time;    // You can increase the time when using LEDs
  digitalWrite(TestPin, HIGH);                // For me this is normally pin 7, no reason for that
  // } else {
  digitalWrite(TestPin, LOW);                 // That completes the pulse/
  //  }    // End of Else
}  // End of Mark_It

/*******************************************************************
*                                                                  *
*  Configure year (4 dig) month day as a string. Use compiler info *
*  Print with Buffer +2 to eliminate the century.                  *
*                                                                  *
********************************************************************/
void logname(char const *date, char *buff) {
  int month, day, year;
  static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec";
  sscanf(date, "%s %d %d", buff, &day, &year);
  month = (strstr(month_names, buff) - month_names) / 3 + 1;
  sprintf(buff, "%d%02d%02d.txt", year, month, day);
}

//@@@@@@@@@@@@@@@@@@@@@@@@@[ Code Setup ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/***********************[ Our code starts here }************************
 **                                                                   **
 **    This is where we define our starting point This is entered     **
 **    only once when the processor starts Example, Startup or reset  **
 **    button or from shield This way we start with everything in a   **
 **    known state                                                    **
 **                                                                   **
 **                put your setup code here, to run once.             **
 **                                                                   **
 ***********************************************************************/
void setup() {                                // put your setup code here, to run once:
  Serial.begin(115200);                       // This is for Debug
  pinMode(TestPin, OUTPUT);                   // Set up our TestPin as an output

  //************  Set up Revision Date Buffer  (Part of Setup) ***************
  // This contains the Date as YYMMDD and the last line number LLN so we get YYMMDDLLN
  logname(__DATE__, TmpBuffer);               // Get date string
  for (int i = 0; i < 8; i++) {               // Initialize our loop, to move yyyymmdd (8 Characters)
    Revision_Date[i] = TmpBuffer[i];
    Revision_Date[i + 1] = 0;                 // Add tag for end of string
  }    // End of for i loop buffer parsed
  Serial.print(F("\n\n\n\n\n\n\t\t Hello Gil!"));// Clean up the screen a bit
  Serial.print(F("\n\tThis Program is filed as:\n"));// Make room to tell who we are including the path
  Serial.print(Source_file);                  // Display the source file name
  Serial.print(Gil);                          // Located at the begining of the file.
  Serial.print(F("\n\tRevision: "));          // Show the rest of the info such as compiled date
  Serial.print(Revision_Date + 2);            // Skip past the century (YYYYMMDD)
  Serial.print(LastLine());                   // Show the current revision date inserted manually
  Serial.print(F("\n\tcompiled: "));          // Show date of compilation
  Serial.print(compile_date);                 // Get it from the compiler data
  Serial.print(F("\n\n"));                    // Show date of compilation
  delay(5000);                                // To prevent serial freeze in uno
  Gil_Ticker[A_TicTimer] = 0x0001;            // Start our up ticker and let it wrap around to zero
}

//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@[ Main Code ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
/***************************************************************************************************
*                                                                                                  *
*         The Main Program loop is here. It. dispatches to all Sub Processes!                      *
*   NOTE: Loop delays will Kill militime which needs to be cycled each pass through your loop.     *
*  On My Arduino D1 R3 (ESP8266)) This loop cycles 65,800 times a second however this will change  *
*  if the code is changed. Adding or removing process will change the time of the main loop.       *
*  This is not prememtptive so blocking code will stall the loop. This works nice when combining   *
*  several different codes.                                                                        *
*     or 0.000015198 of a second per pass. This changes with clocks and processors.                *
*                                                                                                  *
*                                                                                                  *
****************************************************************************************************/
void loop() {
  //  wdt_reset();                            // Pet the dog so it does not bite!
  //  militime() is our pseudo timer for software delays. This must run every pass of the loop
  militime();                                 // Cycle our program timer for events
  Mark_It();                                  // Send pulse to Logic analyzer for timing

  /*************************[ Program States ]*************************************
   **                                                                            **
   **      The is the main program loop, it controls all main functions          **
   **       Implementing a pseudo state machine.  Utilizing states               **
   **       accessed by using this statement: [if (stateM == ?) {}               **
   **  State:                                                                    **
   **     3.  Test Timer 3      Just for Demo and in no order                    **
   **     5.  Test Timer 5      Just for Demo and in no order                    **
   **    13.  Test Timer 13     Just for Demo and in no order                    **
   **    22.  Test Timer 22     Just for Demo and in no order                    **
   **    44.  Test Timer G      Just for Demo and in no order                    **
   **    43.  Test Timer I      Just for Demo and in no order                    **
   **    42   Test Timer L      Just for Demo and in no order                    **
   **    25.  Test Timer Case   Just and gives us a clean line.                  **
   **        50. ----------------------------                                    **
   **   stateCount  -1 update PWM channels (Currently 50 states)                 **
   **   NOTE: States can be in any order in the source code but will be executed **
   **  in numerical sequence. To disable a state simply change its state number  **
   **  to something > Max_States.You can call a function as many times as wanted **
   **  it will process OK. Careful of Variables and redefining them.             **
   **  When combing programs you can place them as a pseudo thread and it will   **
   **  be processed each time through the loop. They cannot have the name loop.  **
   **  The code must be non blocking ie. NO delay()statements as they will block **
   **  your code and nothing else will run. Waiting for something is forbidden   **
   **  as that will block. Test and if not true exit and test next time through. **
   **                                                                            **
   ********************************************************************************/

  // Example of a disabled state, There are only Max_State states and we do not use 0 - or Max_State
  if (stateM == 61) {
    digitalWrite(TestPin, LOW);               // Debug;
  }

  //****[ Sample of a thread with independent timer ]****
  if (stateM == 3) {                          // Test for state, If not valid this is skipped.
    //  Mark_It();                            // Used to time function and when active
    if (Gil_Timer[A_test_timer] == 0) {       // Check if it has timed out.
      Gil_Timer[A_test_timer] = A_test_time;  // Yes, just reset it to the correct delay
      Serial.print(F("#3 "));                 // Our code controlled by processor and a timer
    }
    //  Mark_It();                            // For accurate measure from the falling of first to rising here
  }    //  End of stateM == 3

  //***********
  if (stateM == 5) {
    if (Gil_Timer[B_test_timer] == 0) {
      Gil_Timer[B_test_timer] = A_test_time;
      Serial.print(F("#5 "));
    }
  }//  End of stateM == 5

  //***********
  if (stateM == 13) {
    if (Gil_Timer[C_test_timer] == 0) {
      Gil_Timer[C_test_timer] = A_test_time;
      Serial.print(F("#13 "));
    }
  }//  End of stateM == 13

  //***********
  if (stateM == 22) {
    if (Gil_Timer[D_test_timer] == 0) {
      Gil_Timer[D_test_timer] = A_test_time;
      Serial.print(F("#22 "));
    }
  }//  End of stateM == 22

  //***********
  if (stateM == 44) {
    if (Gil_Timer[E_test_timer] == 0) {
      Gil_Timer[E_test_timer] = A_test_time;
      Serial.print(F("#G "));
    }
  }//  End of stateM == 44

  //***********
  if (stateM == 43) {
    if (Gil_Timer[F_test_timer] == 0) {
      Gil_Timer[F_test_timer] = A_test_time;
      Serial.print(F("#I "));
    }
  }//  End of stateM == 43

  //***********
  if (stateM == 42) {
    if (Gil_Timer[G_test_timer] == 0) {
      Gil_Timer[G_test_timer] = A_test_time;
      Serial.print(F("#L --> "));
      uint16_t g = Gil_Ticker[A_TicTimer];    // Force it as unsigned.
      Serial.print(g , HEX);
      Serial.print(F(" "));
    }
  }  //  End of stateM == 43

//***********
// Example of a disabled state
  if (stateM == 625) {                        // Number is greater then Max_State
    digitalWrite(TestPin, HIGH);              // A single thread is taking about 8uS
  }

  /**************[ Display Case Temperature  ]**************
  *       State: #25                                       *
  *    Update the displays on a regular basis.             *
  *   200215                                               *
  **********************************************************/
  if (stateM == 25) {                         // Check our thread - Remove if you are not using threads
    if (Gil_Timer[Case_Timer] == 0) {         // See if we timed out
      Gil_Timer[Case_Timer] = Case_Time;      // Yes reset timer and process it
      Serial.print(F("\nCase: "));
      Mark_It();                              // Function code removed
    }  // End of if timers
  }   // End of stateM == 2                   // Remove if you are not using threads

  /*@@@@@@@@@@@@@@@@@@@@@@@@@@@[ State Machine ]@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
    @@                                                                      @@
    @@   This is the state count controller for the pseudo state machine.   @@
    @@    The state machine is counted down.  This must be at the end of    @@
    @@    the main loop!                                                    @@
    @@                                                                      @@
    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/
  if (stateM > 0)  {                          // Check current state
    (stateM = (stateM - 1));                  // If greater then 0 subtract 1
  } else {
    (stateM = (Max_State))  ;                 // Reset to start loop over
  }   // End of State Update                  // If zero update count
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

}   // End of Main Loop


/**********************[ Used as part of the Revision number]************************
 *                                                                                  *
 *  This allows us to use the last line number to be used as part of the revision   *
 *  number. It is used only once during setup. Revision = Last Line/YY/MM/DD        *
 *                                                                         --us     *
 ********[ THIS MUST BE AT THE END OF THE OF THE PROGRAM! to operate properly]*******/
int LastLine() {
  int x = __LINE__;                           // This returns the current line number
  return x + 2;                               // Got to get it correct
}                                             // END OF THE PROGRAM!

1 Like

Just written some basic lighting frequencies for different lights patterns. My next issue is having then running alongside each other continuiously, which I think I can not do on the Uno board, is there another board that could do all at once? I am probably looking at having 50 micro LEDs or so.


 digitalWrite(7, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, LOW);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, LOW);
  delay(four); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, LOW);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(7, LOW);
  delay(four); // Wait for animationSpeed millisecond(s)
  
  digitalWrite(8, HIGH);
  delay(two); // Wait for animationSpeed millisecond(s)
  digitalWrite(8, LOW);
  delay(three); // Wait for animationSpeed millisecond(s)
  digitalWrite(8, HIGH);
  delay(two); // Wait for animationSpeed millisecond(s)
  digitalWrite(8, LOW);
  delay(three); // Wait for animationSpeed millisecond(s)
  
  digitalWrite(9, HIGH);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(9, LOW);
  delay(two); // Wait for animationSpeed millisecond(s)
  digitalWrite(9, HIGH);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(9, LOW);
  delay(two); // Wait for animationSpeed millisecond(s)
  
  digitalWrite(10, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, LOW);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, LOW);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, LOW);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(10, LOW);
  delay(half); // Wait for animationSpeed millisecond(s)
  
 
  digitalWrite(1, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(1, LOW);
  delay(threehalf); // Wait for animationSpeed millisecond(s)
  digitalWrite(1, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(1, LOW);
  delay(threehalf); // Wait for animationSpeed millisecond(s)
  
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(11, LOW);
  delay(three); // Wait for animationSpeed millisecond(s)
  
 
  digitalWrite(12, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, HIGH);
  delay(three); // Wait for animationSpeed millisecond(s)
  digitalWrite(12, LOW);
  delay(four); // Wait for animationSpeed millisecond(s)
  
  digitalWrite(13, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(13, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(13, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(13, LOW);
  delay(one); // Wait for animationSpeed millisecond(s)
  digitalWrite(13, HIGH);
  delay(half); // Wait for animationSpeed millisecond(s)
  digitalWrite(13, LOW);
  delay(seven); // Wait for animationSpeed millisecond(s)

You are probably correct, that is to say you could not do it on an UNO board. Yet. :wink:

So 50 LEDs. How many distinct patterns of flashing? And how many steps might there be in each? What makes a sequence start or stop? Is there an overall script or tell us more about that part.

Here's where you need to stop progress on the project and take some time out to learn about how to do this with limited resources.

An Arduino with more I/O pins might make things easier. On the other hand, LEDs can be placed in a matrix arrangement so fewer pins could be used. And there are so called smart LEDs, Neopixel is one brand, which can all be controlled from one pin.

Then you need software. Google and poke around to see if any of these searches lands you in territory that isn't too unfamiliar and start appreciating the power of a distinctly different approach:

 arduino blink without delay

and

 arduino two things at once

and

 arduino finite state machine

and

 arduino finite state machine traffics lights

You'll need to use the techniques shown to avoid blocking code. You will make no use of delay() s this is what makes you think you can only do one sequence at a time… delay() makes your code go dumb. Nothing but the delay gets done during.

HTH and ask questions

a7

1 Like

Even with an Arduino Uno. or Arduino Nano with Neo-Pixel LEDs or I2C-IO-expanders you can have 50 or even 100 LEDs with each blinking in it its own pattern and each at a different frequency

This is done by
Using a coding-technique that is pretty simple once you have understood the basic principle.

This coding technique is called non-blocking timing.
As you are a newcomer I want to recommend you to not use this

famous notorious bad explained standard example blink without delay()

This standard example does not include an introductory explanation of the difference between delay() and non-blocking timing.
The code used makes it unnecessarily difficult to understand the principle.
Users who have already understood the basic principle are almost all blind to the difficulties that a real beginner has with it if he only has the code available without a supplementary explanation.

That's why I recommend this tutorial because it contains exactly that:

  • clarify the fundamental difference between delay() and millis()
  • the principle explained using an everyday example

best regards Stefan

1 Like

Hello kitch014

Did you already read and study the light layout of buoys specified by the International Hydrographic Organization?
Each Ligth Character has ist own name.
Eg
'F' for fixed
'Oc' for single-occulting
'Oc(2)' for group-occulting
.
etc.
Take a view into this specification.

Per Light Character you can design an array containing the timing information. The BlinkWithOutDelay example of the IDE shall be able to handle such arrays for different Light Buoys at the same time at the project begining.

Later this design could be transfered to a OOP.

Have a nice day and enjoy coding in C++.

1 Like

The example has about 8 different sequences, you could easily add 50 or more. To add more, you currently have 48 states available, you simply add more states (Max_States) I think. Follow the example, just about everything is being timed, and is non blocking. The timing does not take much time and you simply test for zero to determine if it is finished. If it is not zero you exit, if it is zero reset the time and do the function. Each states has 1 test to determine if that is the state that needs to be executed. the other tests are for timers that function uses. You could add external port expanders and add hundreds.

Hi all, Thank you for all of your help and posts/links. It was definitely more work than I had assumed, but found a similar code to the ones you recommended. I think I am more or less there, but if you see any issues/developments please shout. I have it set to a 15second repeat.

int ledPin9 = 9;
int ledPin8 = 8;
int ledPin7 = 7;
int ledPin6 = 6;
int ledPin5 = 5;
int ledPin4 = 4;
int ledPin3 = 3;
int ledPin2 = 2;
int ledPin10 = 10;
int ledPin11 = 11;
int ledPin12 = 12;
int ledPin13 = 13;



/// 1  East Cardinal Mark
/// 2  South Cardinal Mark
/// 3  West Cardinal Mark
/// 4  North Cardinal Mark (cont)
/// 5  QF continuous
/// 6
/// 7  Port/Stbd FL.R/G.3s
/// 8
/// 9  L.FL.R/G
/// 10
/// 11 FL.(2).R/G
/// 12 Racing Mark FL(4)

byte ledSequ [][12] {
  
  
///1  2  3  4  5  6  7  8  9  10 11 12
  {1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
  {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1},
  {1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
  {1, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1},
  {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1},
  {1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0},
  {0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1},
  {1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1},
  {1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0},
  {0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
  {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1},
  {0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1},
  {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0},
  {0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0},
  {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0},
  {0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0},
  {0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0},
  {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0},
  {0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0},
  {0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0},
  {0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0},
  {0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
  {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
  
 
};
// new 
byte leds[] {ledPin13, ledPin12, ledPin11, ledPin10, ledPin9, ledPin8, ledPin7, ledPin6, ledPin5, ledPin4, ledPin3, ledPin2};
void setup()
{
  Serial.begin(9600);
  pinMode(ledPin13, OUTPUT);
  pinMode(ledPin12, OUTPUT);
  pinMode(ledPin11, OUTPUT);
  pinMode(ledPin10, OUTPUT);
  pinMode(ledPin9, OUTPUT);
  pinMode(ledPin8, OUTPUT);
  pinMode(ledPin7, OUTPUT);
  pinMode(ledPin6, OUTPUT);
  pinMode(ledPin5, OUTPUT);
  pinMode(ledPin4, OUTPUT);
  pinMode(ledPin3, OUTPUT);
  pinMode(ledPin2, OUTPUT);
  
}
void loop() {
  const unsigned long blink = 250;
  static unsigned long blinkMillis;
  if (millis() - blinkMillis >= blink ) {
    static int nummer;
    blinkMillis = millis();
    Serial.println("blink");
    for (unsigned int n=0; n<sizeof(leds); n++) digitalWrite(leds[n],ledSequ[nummer][n]);
    nummer++;
    nummer = nummer % (sizeof(ledSequ)/sizeof(leds)); 
  }
}

i measure that the body of the timer condition takes 80 usec to execute which is certainly much smaller that the 250 msec timer period.

but that time/led will certainly be longer if you try to expand the # of LEDs using shift registers, I/O expander or possibly Charlieplexing

Nice.

You don't really need the pin names any more.

byte leds[] = {
  13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2,
};

Then in setup(), use a for loop, like you do later:

    for (unsigned int  n = 0; n < sizeof(leds); n++)
      pinMode(leds[n], OUTPUT);

When you expand this to more LEDs, the same programming pattern can be used and instead of writing directly to an individual LED on an output pin you will use whatever expansion device or smart pixels or matrix code to specific which LED you talking about.

Is there going to be any need to turn on and off an individual sequencing LED?

a7

That doesn't seem particularly useful

const byte leds[] {13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2};
const byte numLeds = sizeof(leds) / sizeof(leds[0]);

void setup()
{
  Serial.begin(9600);
  for( int i = 0; i < numLeds; ++i ) {
    pinMode(i, OUTPUT);
  }
}

Those sizeof operators only work because your variables are 1 byte. If you ever change them in the future, it will bite you :slight_smile:

Much better to always use sizeof(array) / sizeof(array[0]) so it doesn't matter what type of array you are using.

Late to the party but, you could take a look at How can i fade LEDs concurrently and at different rates on a PCA9685 chain ? - #8 by noiasca

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.