Changing this code to work in milliseconds instead of seconds

I am making a diorama with blinking led lights that have to resemble little eyes in the dark. I have an Arduino Nano to attach the leds to, and have been trying to find a code that lets me have the leds turn on for like 200 milliseconds, turn of for 500, turn on for 300, off for 200, on for 500, and then off for 15000, for instance.

I have found code that does that, but it is in seconds. Me, being a biologist first, and an artist second, do not know much about coding, and allthough I can expand the code to use all 12 leds I have, and understand what to change to make them blink longer or shorter, I have no idea what to change to make the code work in milliseconds instead of seconds. A second is too long a blink time, so I need to make it work witj millliseconds. Below is the code I have borrowed from chucktodd , expanded to use 12 ports for 12 leds, but nor working in milliseconds. Can someone tell me what to change?

I do like the whole coding thing, and for the next diorama, I want to write my own, but there is a competition coming up in 3 weeks and that leaves me with not enough time to learn all this...

/*
*  Pin Sequencer V0.1.0
*  Chuck Todd <ctodd@cableone.net>
*/

#define LED1 8
#define LED2 9
#define LED3 10
#define LED4 13
#define LED5 6
#define LEDCount 5 // count of LEDs, Arrays 

// #define DEBUG  // uncomment this line to enable Debug display on Serial Monitor

/* each entry in this list must have Arduino Pin Number in first position,
*       at least one positive or negative value in the sequence, 
*       ending with a zero '0' to mark the end of the sequence
* these PROGMEM arrays exist in Program memory, they do not take up any DATA memory
* they are a little tough to work with, but worth it. because of the RAM savings.
*/
const int16_t L1[] PROGMEM ={LED1,-40,2,-20,2,-36,0}; // negative means Low, count in seconds
const int16_t L2[] PROGMEM ={LED2,-20,2,-20,2,-56,0}; // pattern for each LED
const int16_t L3[] PROGMEM ={LED3,-10,2,-15,2,-71,0}; // zero is end/ repeat again marker
const int16_t L4[] PROGMEM ={LED4,10,-10,5,-2,0};        
const int16_t L5[] PROGMEM ={LED5,1000,-10,0};        // this led will be on for 1,000 second, off for 10

const int16_t* const LTable[] PROGMEM ={L1,L2,L3,L4,L5}; // for each LED create a Ln[] and add it here

/* Each LED pin only consumes 4 bytes of ram, irrespective of how many sequence events is requires
*/
uint16_t L_index[LEDCount][2]; // two elements for each LED
                        // [0] is index for current element
                       // [1] is next trip point in seconds                       

void initIndex(){ // setup the Arduio pins for output, High or Low as controlled by the Ln[] arrays.
#ifdef DEBUG
char ch[50];
#endif
for(uint8_t i = 0;i<LEDCount;i++){
  L_index[i][0] = 1;                              // first entry in sequence
  uint16_t base = pgm_read_word_near(&LTable[i]); // base address for sequence table for this LED
  int16_t pin =pgm_read_word_near(base);          // Actual pin to change
  pinMode(pin,OUTPUT);
  int16_t n = pgm_read_word_near(base+2);         // time in seconds for the first state
                                                  // added 2 because each element is 2 bytes 
  L_index[i][1]=abs(n);                           // duration in seconds for this state  
  if(n<0) digitalWrite(pin,LOW);                  // negative for low
  if(n>0) digitalWrite(pin,HIGH);                 // positive for high
#ifdef DEBUG
  sprintf(ch,"base=%04X LED%d =%d LI[%d][0]=%d LI[%d][1]=%d\n",base,i,pin,i,L_index[i][0],i,L_index[i][1]);
  Serial.print(ch);
#endif
  }
}

uint16_t findNext(){ // returns next expiration second
uint8_t i=0;
uint16_t ns=L_index[i][1];  // pick first expiration
while(i<LEDCount){
  if(ns>L_index[i][1]) ns = L_index[i][1]; // is this one shorter?
  i++;
  }
#ifdef DEBUG
char ch[50];
sprintf(ch," next second =%d\n",ns);
Serial.print(ch);
#endif
return ns;
}

#ifdef DEBUG
void printTables(){
char ch[50];
for(uint8_t i = 0;i<LEDCount; i++){
  uint16_t base = pgm_read_word_near(&LTable[i]);
  sprintf(ch,"base%d=0x%04X ",i,base);
  Serial.print(ch);
  int16_t j;
  uint8_t k=0;
  do{
    j=pgm_read_word_near(base+k*2);
    Serial.print(j,DEC);Serial.print(' ');
    k++;
  }while(j!=0);
  Serial.println();
  }
}
#endif

void lightEmUp(uint16_t tick){
#ifdef DEBUG
char ch[50];
#endif
for(uint8_t i = 0;i<LEDCount;i++){
  uint16_t base =pgm_read_word_near(&LTable[i]);
  while(tick>=L_index[i][1]){
    L_index[i][0] += 1;                                      // next sequence value
    int16_t n = pgm_read_word_near(base+(L_index[i][0]*2));  // time,polarity or stop
                                                             // multipled by 2 because each element is 2 bytes. 
#ifdef DEBUG
    sprintf(ch,"\nbase=%04X LI[%d][0]=%d n=%d",base,i,L_index[i][0],n);
    Serial.print(ch);
#endif
    if(n==0){
      L_index[i][0]=1; // back to start of timing array, skip pin 
      n = pgm_read_word_near(base+(L_index[i][0]*2));
      }
    L_index[i][1] += abs(n);                    // tick value this new status is value for
    int16_t pin = pgm_read_word_near(base);     // actual Arduino pin to change
#ifdef DEBUG
    sprintf(ch," LI[%d][1]=%d n=%d pin=%d ",i,L_index[i][1],n,pin);
    Serial.print(ch);
#endif
    if(n<0){
      digitalWrite(pin,LOW);
#ifdef DEBUG
      Serial.print("L ");
#endif
      }
    if(n>0){
      digitalWrite(pin,HIGH);
#ifdef DEBUG
      Serial.print("H ");
#endif
      }
    }
  if(tick<L_index[i][1])
    L_index[i][1] -= tick;
  }
}

static uint16_t nextSec;           // how many seconds until the next change event
static unsigned long startTime=0;  // millisecond counter, continually adjusted after each event.

void setup(){
Serial.begin(19200);  //debug console
#ifdef DEBUG
printTables();    // just to verify I can access the PROGMEM correctly
#endif
startTime=millis();
initIndex();            // Set up the pins, Set their initial state, duration
nextSec = findNext();   // when the next change happens
}

void loop(){
unsigned long t=millis();
if((t-startTime)>=((unsigned long)nextSec*1000L)){ // is it time yet?
  uint16_t tick = (t-startTime)/1000;  //number of seconds since last event.
#ifdef DEBUG
  Serial.print("\n+");Serial.print(tick,DEC);Serial.print(' ');
#endif
  startTime += (unsigned long)tick*1000; // adjust startime to last event 
  lightEmUp(tick);                       // actuall change the pins, net the next event countdown timers
  nextSec=findNext();                    // when is the next event scheduled?
  }
}
  • Always show us a good schematic of your proposed circuit.
    Show us good images of your ‘actual’ wiring.
    Give links to components.

  • In the Arduino IDE, use Ctrl T or CMD T to format your code then copy the complete sketch.
    Use the < CODE / > icon from the ‘posting menu’ to attach the copied sketch.




  • Have you considered PWM for dimming the LEDs from OFF to ON to OFF etc ?
1 Like

What do you mean by seconds, the most common timing unit is milliseconds as in delay(500) <<< don't use those though.

1 Like

I'm an idiot :slight_smile: I just forgot to paste it....it's there now

The code is especially converted to use seconds instead of millis. It does it somewhere with the *1000 and /1000 converting the millis to seconds but I am not sure what to delete from that bit of code

That is the weirdest LED code I have ever seen. I don;t have time, but maybe someone else will decode the time elements for you.

2 Likes

I didn't quite understand your need, but I made a change to your code.
I created a variable named "myTime" and replaced the values ​​1000 with this variable.
Then you can vary the value of this variable to meet your needs.
This variable cannot have the value 0.

Here is part of your code that I modified.
Test it and see if it worked.

unsigned long myTime = 100L;
void loop(){
unsigned long t=millis();
if((t-startTime)>=((unsigned long)nextSec*myTime)){ // is it time yet?
  uint16_t tick = (t-startTime)/myTime;  //number of seconds since last event.
#ifdef DEBUG
  Serial.print("\n+");Serial.print(tick,DEC);Serial.print(' ');
#endif
  startTime += (unsigned long)tick*myTime; // adjust startime to last event 
  lightEmUp(tick);                       // actuall change the pins, net the next event countdown timers
  nextSec=findNext();                    // when is the next event scheduled?
  }
}
2 Likes

What is your shortest interval (in ms) and what will be your longest interval (in seconds)?
based on your figures the range is 200ms to 15s.

You need to change the "/1000" here to "/100". That way, each tick is 0.1s instead of 1s.

uint16_t tick = (t-startTime)/1000;  //number of seconds since last event.
1 Like

Thanks all, I'll try out the different suggestions and see what works.

The code does look rather intimidating, I have no idea what half of it does, and I didn't think it would be so difficult to have 12 LEDs act like little eyes blinking in different orders and speeds.

I am making a diorama with little tree hollows, crevices and caves, in which I have placed the ends of some glass fibre. These are attached to 12 LEDs, and to make it look a little bit more realistic I want the lights to be dark, blink on for a tiny moment 2 or 3 times, then on for a little longer, then off for a while. I want all the LEDs to have a different sequence so they don't turn on all at the same time.

Questions: 1:If the sequence I write starts with dark and ends with dark, could I just add those up and put them at the front of the loop? 2: can the different lights have different lengths of sequences?

Well that's why I asked you what is your minimum - maximum interval.

With slightly adopted intervals, I can offer you a sketch which is hopefully more readable for you.

You just define the pins and the rhythm you need:

/*
 different blink rhythm for LEDs

 https://forum.arduino.cc/t/changing-this-code-to-work-in-milliseconds-instead-of-seconds/1305833
*/
#include <Noiasca_led.h>               // download library from http://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm
#include <utility/Noiasca_discrete.h>  // use the discrete pins on the Arduino/Microcontroller

RhythmPin rhythmLed[] {8, 9, 10, 13, 6}; // create several LED objects, each has an individuel discrete Arduino pin

void setup() {
  for (auto &r : rhythmLed) r.begin();                   // you have to call the .begin() method for the LED
  //                       on   off    on    off    on    off
  rhythmLed[0].setInterval(0, 40000, 2000, 20000, 2000, 36000);         // this LED starts with a OFF phase,  hence the first ON phase is 0 ms
  rhythmLed[1].setInterval(0, 20000, 2000, 20000, 2000, 56000);
  rhythmLed[2].setInterval(0, 10000, 2000, 15000, 2000, 65535);         // * max value is 65535
  rhythmLed[3].setInterval(10000, 10000, 5000, 2000);
  rhythmLed[4].setInterval(65535, 10000);                               // * max value is 65535
}

void loop() {
  for (auto &r : rhythmLed) r.update();   // you have to call update() for each LED, as the objects are in array you can loop through this array with a "auto range based for loop" 
}
//

You need to download the library and install it with the .zip import.

You can find the library here:
https://werner.rothschopf.net/microcontroller/202202_tools_led_en.htm

"rhythm" is just one effect, there are several others available.
The library comes with lot of examples.

2 Likes

RhythmPin rhythmLed[] {8, 9, 10, 13, 6};

Am I correct in thinking the order of the pins here is used in assigning the type of rhythm in the void setup? so the pin third in the row gets assigned rhythmLed[3]?

array fields start counting at 0.

rhythmLed[2] will get the pin 10.
rhythmLed[3] will get the pin 13.

//                     0  1  2   3   4 index position
RhythmPin rhythmLed[] {8, 9, 10, 13, 6};
1 Like

Thanks, got it! is there a place to test this online?

(and is there a way to upload photo's/a video to this forum? I'd love to share the end result. Or do I have to upload it somewhere else first and then link to it?)

The trouble with that borrowed code is that you'll not learn anything using it and it will be impossible for you to maintain, edit or build off of some time later. You'll forget what that little fix was maybe because as @sonofcy says:

and I echo his message overall. It seems very complicated to blink some LEDs, certainly more complicated than you need to make some blinking eyes in your diorama.

Read through this excellent tutorial courtesy of Adafruit.

Note that you can call instances of the class Flasher in this tutorial that use the same pin assignment with different on/off timing and call them as needed throughout your main code loop.

1 Like

I totally agree with you, and normally I would indeed take the time to learn what I am doing. But in three weeks there is a competition where I would like to enter my diorama, and I don't have time to learn the ins and outs of coding the arduino. Next project I will reserve more time for it, as I do want to learn it.

For now, I have used the library as suggested above, and that seems to do the trick with a lot less code needed. I understand how to change the on and off times for the leds, there is no delay code ending the loop, and I think this will work.

Thanks for taking the time to help me with finding a better way to do this!

Wokwi is a nice online simulator. You could use the files locally.
You will need to adopt the includes.

some blinky:

You can upload pictures to the forum.
You can link videos on youtube with your project.

With all respect to others work:
The code in #1 is quite efficient. It can be tweaked to even use less Flash and SRAM by just moving the Serial.begin into the first #ifdef DEBUG.

Only thing is you don't get any money back for unused flash/SRAM and the Uno has enough to "blink" LEDs on all available pins.

btw ... it's not just a simple blink of several LEDs.
:wink:

My understanding was that blinking some LEDs was what OP was trying to do.

Who went on to say:

which is great, as I thought that's what Arduino the project was aimed at - making electronics accessible to students, artists and designers who aren't programmers or experts in electronics.
Furthermore, OP said:

and I had to wonder if project two will wind its way in time to a similar circumstance, namely

Thus, my post was informed by these factors, and I think that, over the long run, the Adafruit tutorial will benefit @allis_diorama more than the code that OP was asking for help with in the first place.
I think the Adafruit tutorial is more accessible and is certainly more stylistically consistent with the Arduino IDE examples than is the code they were working off that almost looks like an alien language by comparison to those uneducated in C/C++.

I believe in the spirit of Arduino and I will stand by the coding style as presented in the Arduino IDE. After all, it has been enormously successful in drawing thousands of people (myself included) to the maker community using microcontrollers.

Hopefully, OP learns from both the code they decided to go with, as well as the Adafruit link, as it explains a number of key concepts quite well (millis timing and state machines to name but two).

I think that the Adafruit tutorial provides more learning "bang for your buck" than deciphering one's way through

for(uint8_t i = 0;i<LEDCount;i++){
  L_index[i][0] = 1;                              // first entry in sequence
  uint16_t base = pgm_read_word_near(&LTable[i]); // base address for sequence table for this LED
  int16_t pin =pgm_read_word_near(base);          // Actual pin to change
  pinMode(pin,OUTPUT);
  int16_t n = pgm_read_word_near(base+2);         // time in seconds for the first state
                                                  // added 2 because each element is 2 bytes 
  L_index[i][1]=abs(n);                           // duration in seconds for this state  
  if(n<0) digitalWrite(pin,LOW);                  // negative for low
  if(n>0) digitalWrite(pin,HIGH);                 // positive for high
#ifdef DEBUG
  sprintf(ch,"base=%04X LED%d =%d LI[%d][0]=%d LI[%d][1]=%d\n",base,i,pin,i,L_index[i][0],i,L_index[i][1]);
  Serial.print(ch);
#endif
  }

if you are new to coding, effective and elegant code though it may be.

That's just my $0.02, and thanks for taking the time to comment.

Please post photos when you have this working, I love seeing working creative projects on here.

Thank you :blush: