Hi everyone. I want to make a project with a few leds. The thing is that i want to multitask with my board and im not sure at all how millis work for over one task. I want to make leds light one after other and the period of switching has to be adjustable with a potentiometer. I know how to blink the first one, by using as interval potentiometer maped value. I have a problem at second one. Any ideas how i should move from this point? Thanks in advance
What is the sequence that you need?
Led 1 on
Led 2 on
Led 3 on
...
...
Led 1 off
Led 2 off
Led 3 off
...
...
Repeat
Or
Led 1 blink
Led 1 off
Led 2 blink
Led 2 off
Led 3 blink
Led 3 off
...
...
Repeat?
The same way you keep track of the day.
I go to work and start the timer for break time and lunch time and 2nd break and time to leave. That's several timers running from one clock source. Each event just occurs at a different time, millis(), peorid.
The Uno does not, really, have enough RAM to run a task switcher or RTOS in my not so humble opinion. I know the Due, STM32,, and ESP32 will do multitasking; the ESP32 does multiprocessing, with a 2nd core and a 2nd processor; ULP.
You can load freeRTOS for the Uno, it's a memory hog but if the main program is small.
You might read about doing the state machine thing, just search the site.
I don't see anything that requires multitasking.. This would be a good newbie project to get familiar with millis.
Millis is simply a timestamp. When you turn on an LED, remember the millis at the time you turned it on. Then every time in loop, see if the time to leave the LED on has expired:
if ( (millis() - ledOnMillis) > ledOnDelay) {
//Turn the LED off
}
Commented example of millis. Compiles, not tested. YMMV.
#define NUM_LEDS 5 //number of LEDs in your sequence
#define MIN_TIME 50ul //minimum time an LED is on (in mS)
#define MAX_TIME 1000ul //maximum time an LED is on (in mS)
#define TIME_READ_POT 50ul //time between potentiometer reads (in mS)
//LED pins
const uint8_t grLEDs[NUM_LEDS] = { 2, 3, 4, 5, 6 }; //change to whatever pins you use
//potentiometer pin
const uint8_t pinPOT = A0;
uint8_t
ledIndex; //points to the currently active LED
uint32_t
timeNow, //holds the current value of millis
timePOT, //times each potentiometer read
timeLEDOnTime = 0xfffful, //holds the time each LED is on
timeLED; //with timeLEDOnTime, times the LED active period
void setup( void )
{
pinMode( pinPOT, INPUT ); //not strictly needed; added for clarity
//set LED pins as outputs
for( uint8_t i=0; i<NUM_LEDS; i++ )
pinMode( grLEDs[i], OUTPUT );
//we start at LED 0
ledIndex = 0;
}//setup
void loop( void )
{
//get the value of the millis counter
timeNow = millis();
//is it time for a potentiometer read?
if( (timeNow - timePOT) >= TIME_READ_POT )
{
//yes; save this time for the next read
timePOT = timeNow;
//read the POT and map it to our min and max limits
//timeLEDOnTime holds the time each LED is on in the blinking sequence
timeLEDOnTime = (uint32_t)map( analogRead( pinPOT ), 0, 1023, MIN_TIME, MAX_TIME );
}//if
//is it time to update the LED?
if( (timeNow - timeLED) >= timeLEDOnTime )
{
//yes; save the time for the next LED
timeLED = timeNow;
//turn off the current LED
digitalWrite( grLEDs[ledIndex], LOW );
//bump to the next LED
ledIndex++;
if( ledIndex == NUM_LEDS )
ledIndex = 0;
//and turn it on
digitalWrite( grLEDs[ledIndex], HIGH );
}//if
//rinse and repeat indefinitely
}//loop
I want to use millis because im gonna get an external signal. I need to light 4 leds one after other. The potentiometer should change the blinking frequency for all leds the same amount. The thing is that i get how a single led and millis should be, but i dont know how to do it for multiple leds at once .All tutorials show a single light and the other show the delay technique that i cant use for my application.
giannhs_mich:
I want to use millis because im gonna get an external signal. I need to light 4 leds one after other. The potentiometer should change the blinking frequency for all leds the same amount. The thing is that i get how a single led and millis should be, but i dont know how to do it for multiple leds at once .All tutorials show a single light and the other show the delay technique that i cant use for my application.
You're not going to find an exact match glossing over example code found during a google search.
You need to take the logic behind what's going on in those examples and adapt it to your own code.
millis() timing is not hard. Once you understand the example for blinking a single LED it's not rocket science to adapt the method to just about any timing requirement you might have.
Well, the "multiblink" code here just might give you some ideas. ![]()
// Blink without "delay()" - multi!
const int led1Pin = 13; // LED pin number
const int led2Pin = 2;
const int led3Pin = 5;
const int button1 = 10;
int led1State = LOW; // initialise the LED
int led2State = LOW;
int led3State = LOW;
char bstate1 = 0;
char togl1 = LOW;
unsigned long count1 = 0; // will store last time LED was updated
unsigned long count2 = 0;
unsigned long count3 = 0;
unsigned long bcount1 = 0; // button debounce timer. Replicate as necessary.
// Have we completed the specified interval since last confirmed event?
// "marker" chooses which counter to check
boolean timeout(unsigned long *marker, unsigned long interval) {
if (millis() - *marker >= interval) {
*marker += interval; // move on ready for next interval
return true;
}
else return false;
}
// Deal with a button read; true if button pressed and debounced is a new event
// Uses reading of button input, debounce store, state store and debounce interval.
// Routines by Paul__B of Arduino Forum
boolean butndown(char button, unsigned long *marker, char *butnstate, unsigned long interval) {
switch (*butnstate) { // Odd states if was pressed, >= 2 if debounce in progress
case 0: // Button up so far,
if (button == HIGH) return false; // Nothing happening!
else {
*butnstate = 2; // record that is now pressed
*marker = millis(); // note when was pressed
return false; // and move on
}
case 1: // Button down so far,
if (button == LOW) return false; // Nothing happening!
else {
*butnstate = 3; // record that is now released
*marker = millis(); // note when was released
return false; // and move on
}
case 2: // Button was up, now down.
if (button == HIGH) {
*butnstate = 0; // no, not debounced; revert the state
return false; // False alarm!
}
else {
if (millis() - *marker >= interval) {
*butnstate = 1; // jackpot! update the state
return true; // because we have the desired event!
}
else
return false; // not done yet; just move on
}
case 3: // Button was down, now up.
if (button == LOW) {
*butnstate = 1; // no, not debounced; revert the state
return false; // False alarm!
}
else {
if (millis() - *marker >= interval) {
*butnstate = 0; // Debounced; update the state
return false; // but it is not the event we want
}
else
return false; // not done yet; just move on
}
default: // Error; recover anyway
{
*butnstate = 0;
return false; // Definitely false!
}
}
}
void setup() {
pinMode(led1Pin, OUTPUT);
pinMode(led2Pin, OUTPUT);
pinMode(led3Pin, OUTPUT);
pinMode(button1, INPUT);
digitalWrite(button1,HIGH); // internal pullup all versions
}
void loop() {
// Toggle switch if button debounced
if (butndown(digitalRead(button1), &bcount1, &bstate1, 10UL )) {
if (togl1 == LOW) {
togl1 = HIGH;
}
else {
togl1 = LOW;
}
}
// flash if toggle is on
if (togl1 == HIGH) {
// Act if the latter time (ms) has now passed on this particular counter,
if (timeout(&count1, 500UL )) {
if (led1State == LOW) {
led1State = HIGH;
}
else {
led1State = LOW;
}
digitalWrite(led1Pin, led1State);
}
if (timeout(&count2, 300UL )) {
if (led2State == LOW) {
led2State = HIGH;
}
else {
led2State = LOW;
}
digitalWrite(led2Pin, led2State);
}
if (timeout(&count3, 77UL )) {
if (led3State == LOW) {
led3State = HIGH;
}
else
led3State = LOW;
digitalWrite(led3Pin, led3State);
}
}
else {
led1State = LOW;
led2State = LOW;
led3State = LOW;
digitalWrite(led1Pin, led1State);
digitalWrite(led2Pin, led2State);
digitalWrite(led3Pin, led3State);
}
}
... or not. ![]()
Think of it this way - Blink without Delay has timing code that drives an action - toggling a LED. In your case, you also have a single periodic action. It's just more complex. But you can simply substitute some code to point to the next LED and update the LEDs, in the same place where the toggle took place before. You can practically cut and paste from the IDE example, except for your custom code.
All it has to do, really, is increment a variable to select one of the LEDs, and increment it every time, then update all the LEDs. Easy peasy and no convoluted timing logic at all. As long as you don't have overlapping multiple timing events, you don't need additional timing variables such as one for each LED.
I wrote a "pseudorandom" blink sketch this way. Since the blinks still occur at periodic intervals, it only needs the basic BWD framework, with one copy of currentMillis. There is one code block to calculate the next pseudorandom number and update the LEDs.
int ed2 = 3;
int ed3 = 5;
int ed4 = 6;
int ed5 = 9;
int ed6 = 10;
int pot=A0;
unsigned long previousMillis=0;
long x=0;
long y=0;
long y1=0;
long y2=0;
long y3=0;
long y4=0;
int button=A1;
int x3=0;
int x4=0;
int x5=0;
int x6=0;
int z=A0;
int z1=0;
void setup() {
pinMode(z,INPUT);
pinMode (ed2, OUTPUT);
pinMode (ed3, OUTPUT);
pinMode (ed4, OUTPUT);
pinMode(ed5, OUTPUT);
pinMode(ed6, OUTPUT);
pinMode(pot, INPUT);
digitalWrite(ed6,HIGH);
pinMode(button,INPUT);
}
void loop() {
if(analogRead(button)<=1000){
x=analogRead(pot);
y=map(x,0,1023,500,1000);
y1=y;
y2= 2*y;
y3= 3*y;
y4= 4*y;
digitalWrite(ed6,HIGH);
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > y1 && currentMillis - previousMillis < y2){
digitalWrite (ed2,HIGH);digitalWrite (ed3,LOW);digitalWrite (ed4,LOW); digitalWrite(ed5,LOW);}
if (currentMillis - previousMillis >y2 && currentMillis - previousMillis < y3){
digitalWrite (ed2,LOW);digitalWrite (ed3, HIGH);digitalWrite (ed4,LOW); digitalWrite(ed5,LOW);}
if (currentMillis - previousMillis > y3 && currentMillis-previousMillis<y4){
digitalWrite (ed2,LOW);digitalWrite (ed3,LOW);digitalWrite (ed4,HIGH); digitalWrite(ed5,LOW);}
if (currentMillis-previousMillis>y4){
digitalWrite (ed2,LOW);digitalWrite (ed3,LOW);digitalWrite (ed4,LOW); digitalWrite(ed5,HIGH);}
if (currentMillis - previousMillis > y4){previousMillis = currentMillis;}
}
if(analogRead(button)>1000){
x3=y;
x4=2*x3;
x5=3*x3;
x6=4*x3;
z=analogRead(A0);
z1=map(z,0,1023,51,255);
analogWrite(ed6,z1);
unsigned long currentMillis = millis();
if (currentMillis - previousMillis > x3 && currentMillis - previousMillis < x4){
digitalWrite (ed2,HIGH);digitalWrite (ed3,LOW);digitalWrite (ed4,LOW); digitalWrite(ed5,LOW);}
if (currentMillis - previousMillis >x4 && currentMillis - previousMillis < x5){
digitalWrite (ed2,LOW);digitalWrite (ed3, HIGH);digitalWrite (ed4,LOW); digitalWrite(ed5,LOW);}
if (currentMillis - previousMillis > x5 && currentMillis-previousMillis<x6){
digitalWrite (ed2,LOW);digitalWrite (ed3,LOW);digitalWrite (ed4,HIGH); digitalWrite(ed5,LOW);}
if (currentMillis-previousMillis>x6){
digitalWrite (ed2,LOW);digitalWrite (ed3,LOW);digitalWrite (ed4,LOW); digitalWrite(ed5,HIGH);}
if (currentMillis - previousMillis > y4){previousMillis = currentMillis;}
}
}
I solve my problem. Thanks everyone for your response.
When button is low the potentiometer can adjust the chasing leds period of 4 leds while 5th led is just constantly high. When button is high potentiometer adjust the brightness of fifth led without changing the period of blink pattern.