LED Heartbeat without delay on ESP8266, how shoud i do it?

Since ive got my ESP8266 i would like to add a "heartbeat" pulse for the LED to know wether its on or not. Ive used delay() for my test codes, but that doesnt really work while running other stuff.
Ive googled, but every other code without delay() doesnt really do a heartbeat.
How can I change my code to get this effect?
Any help is appreciated :wink:

Code:

#define LED D0

void setup2() {
  pinMode(LED, OUTPUT);       //initialize LED
  delay(200);
  digitalWrite(LED, LOW);     //tes LED
  delay(200);
  digitalWrite(LED, HIGH);
  delay(200);                 //delay() is fine here
 }

void heartbeatstart() {       //this is being started from another tab
  while(1) {                  //looping heartbeat() forever
    heartbeat();
  }
}

void heartbeat() {            //the actual heartbeat code
  digitalWrite(LED, LOW);
  delay(100);                 //<--this should go away
  digitalWrite(LED, HIGH);
  delay(150);
  digitalWrite(LED, LOW);
  delay(100);
  digitalWrite(LED, HIGH);
  delay(1000);
}

Start with "blink without delay" that is part of the IDE examples

then read Several Things at a Time for more

I saw this and i did understand it, however for my heartbeat ill need different intervals.
I´ve tried compiling some code with millis():

#define LED D0

unsigned long currtime;                     //current time
unsigned long timewantedbeat1on;
unsigned long timewantedbeat1off;
unsigned long timewantedbeat2on;
unsigned long timewantedbeat2off;
unsigned long timewantedbreak;

void loop() {
  Serial.println("doing some task");
  unsigned long currtime = millis();
  heartbeat();
}


void setup() {
  Serial.begin(115200);
  pinMode(LED, OUTPUT);       //initialize LED
  delay(200);
  digitalWrite(LED, LOW);     //test LED
  delay(200);
  digitalWrite(LED, HIGH);
  delay(200);                 //delay() is fine here
 }

void heartbeat() {       //this is being started from another tab
  heartbeat1on();
}

void heartbeat1on() {
  digitalWrite(LED, LOW);
  unsigned long currtime = millis();
  timewantedbeat1off = currtime + 100;
  heartbeat1off();
}
void heartbeat1off() {
  unsigned long currtime = millis();
  if(timewantedbeat1off >= currtime) {
    digitalWrite(LED, HIGH);
    unsigned long currtime = millis();
    timewantedbeat2on = currtime + 150;
    heartbeat2on();
  } else {
    loop();
  }
}
void heartbeat2on() {
  unsigned long currtime = millis();
  if(timewantedbeat2on >= currtime) {
    digitalWrite(LED, LOW);
    unsigned long currtime = millis();
    timewantedbeat2off = currtime +100;
    heartbeat2off();
  } else {
    loop();
  }
}
void heartbeat2off() {
  unsigned long currtime = millis();
  if(timewantedbeat2off >= currtime) {
    digitalWrite(LED, HIGH);
    unsigned long currtime = millis();
  timewantedbreak = currtime + 1000;
    heartbeatbreak();
  } else {
    loop();
  }
}
void heartbeatbreak() {
  unsigned long currtime = millis();
  if(timewantedbreak >= currtime) {
    heartbeat1on();
  } else {
    loop();
  }
}





void ledon() {
  digitalWrite(LED, LOW);
}



//  digitalWrite(LED, LOW);
//  delay(100);
//  digitalWrite(LED, HIGH);
//  delay(150);
//  digitalWrite(LED, LOW);
//  delay(100);
//  digitalWrite(LED, HIGH);
//  delay(1000);

But that doesnt seem to be working, since i get a loop of reset cause 4, which means that a watchdog triggered a reset

doing some task

 ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1264, room 16 
tail 0
chksum 0x42
csum 0x42
~ld
doing some task

I cant find my error :confused: probably a very simple one but i cant see it

Make an array with the different on and off interval lengths. Every time the interval times out, toggle the LED, load the next wait time, then move to the next index in the array. When you reach the end, wrap around to 0.

here is an old example I had written with an array of durations between "tics"

you can run it with the console at 115200 bauds and it will print info at after pausing for a time as defined in the ticDuration array (in ms)

const unsigned int ticDuration[] = {1000, 1500, 2000, 200};
const byte nbTics = sizeof(ticDuration) / sizeof(ticDuration[0]);
byte tic;
unsigned long chrono;

void setup() {
  Serial.begin(115200);
  tic = 0;
  chrono = 0;
}

void loop() {
  unsigned long int currentMillis = millis();

  if (currentMillis - chrono > ticDuration[tic]) {
    Serial.print(tic);
    Serial.print("\t(");
    Serial.print(ticDuration[tic]);
    Serial.print(")\t");
    Serial.println(millis());
    chrono = currentMillis;
    tic = (tic + 1) % nbTics; // next duration and rollover
  }
}

you'll see it's not accurate to the ms due to all the printing and extra code and possibly millis() rounding error but "good enough" at respecting the pause in between 2 consecutive ticks (it print the value of millis() and expected pause so you can see it does wait the right amount of time

</sub> <sub>tic duration millis() 0 ([color=blue]1000[/color]) 1001 --> duration [color=blue]1000[/color], triggered at millis = 1001 1 ([color=blue]1500[/color]) 2502 --> duration 1500, triggered at millis = 2502, so ~[color=blue]1500[/color] ms delay 2 ([color=blue]2000[/color]) 4503 --> duration 2000, triggered at millis = 4503, so ~[color=blue]2000[/color] ms delay 3 ([color=blue]200[/color]) 4704 --> duration 200, triggered at millis = 4704, so ~[color=blue]200[/color] ms delay 0 (1000) 5705  etc.. 1 (1500) 7206 2 (2000) 9207 3 (200) 9408</sub> <sub>

Instead of

chrono = currentMillis;

Do this

chrono += ticDuration[tic];

Well Both options are valid while not driving the same behavior in case the rest of the code at some point has a big hiccup and your loop does not come back quickly enough to test the timing

The first one (i proposed) is "forgetful" and focused on maintaining the next duration as close as possible to the delay that is being asked at that point; so it focuses on the duration of the pause regardless to what happened in the past. It has no memory.

Jiggy's suggestion is trying to focus on expected absolute timing of the tic times and playing catchup by having a shorter that expected delay if the previous tics have not been aligned to the expected absolute time.

This is not without consequence - In my example, say something rare and bad happens in one waiting period that drives missing the next 10 absolute tics times, then this tic is obviously totally wrong but the next one will go back to expected duration whereas with jiggy's suggestion this tic is going to be wrong AND the next 10 tics 'pause' will be way off too as they will happen instantly (at the speed of the loop) to play catch up.

So choose the option you want based on your specifics. If you need a precise number of triggers in a given time even if delays in between tics are not aligned to expectations then option 2 is what you need, if you don't care and want to focus more on the general behavior of sticking to the right duration in between triggers, then go for the first one.

Ideally your loop compute time will be always be way shorter that any of the tic duration so that both version will end up doing globally the same thing

J-M-L:
here is an old example I had written with an array of durations between "tics"

you can run it with the console at 115200 bauds and it will print info at after pausing for a time as defined in the ticDuration array (in ms)

const unsigned int ticDuration[] = {1000, 1500, 2000, 200};

const byte nbTics = sizeof(ticDuration) / sizeof(ticDuration[0]);
byte tic;
unsigned long chrono;

void setup() {
 Serial.begin(115200);
 tic = 0;
 chrono = 0;
}

void loop() {
 unsigned long int currentMillis = millis();

if (currentMillis - chrono > ticDuration[tic]) {
   Serial.print(tic);
   Serial.print("\t(");
   Serial.print(ticDuration[tic]);
   Serial.print(")\t");
   Serial.println(millis());
   chrono = currentMillis;
   tic = (tic + 1) % nbTics; // next duration and rollover
 }
}





you'll see it's not accurate to the ms due to all the printing and extra code and possibly millis() rounding error but "good enough" at respecting the pause in between 2 consecutive ticks (it print the value of millis() and expected pause so you can see it does wait the right amount of time


<sub>```</sub>
<sub>tic duration millis()

0 (1000) 1001 --> duration 1000, triggered at millis = 1001
1 (1500) 2502 --> duration 1500, triggered at millis = 2502, so ~1500 ms delay
2 (2000) 4503 --> duration 2000, triggered at millis = 4503, so ~2000 ms delay
3 (200) 4704 --> duration 200, triggered at millis = 4704, so ~200 ms delay
0 (1000) 5705  etc..
1 (1500) 7206
2 (2000) 9207
3 (200) 9408

```

Thanks to both of you for replying and helping me :slight_smile:
Ive edited the code a little bit to fit my needs:

#define LED D0                                                              //the led is on pin D0/16

const unsigned int ticDuration[] = {100, 150, 100, 1000};                   //added my own delays
const byte nbTics = sizeof(ticDuration) / sizeof(ticDuration[0]);
byte tic;
unsigned long chrono;
unsigned long lastPass = 0;
int state = 0;

void setup() { 
  pinMode(LED, OUTPUT);
  Serial.begin(115200);
  tic = 0;
  chrono = 0;
  digitalWrite(LED, HIGH);                                                  //turn off led after initializing 
}

void loop() {
  unsigned long int currentMillis = millis();

  if( currentMillis - chrono > ticDuration[tic]) {
    ledtoggle();
    //Serial.print(tic);                                                    // <-- avoid serial console spam
    //Serial.print("\t(");
    //Serial.print(ticDuration[tic]);
    //Serial.print(")\t");
    //Serial.println(millis());
    chrono = currentMillis;
    tic = (tic + 1) % nbTics;                                               //next duration and roll over 
  }
}

void ledtoggle() {                                                          //toggle led
  digitalWrite(LED, (!state) ? HIGH : LOW);                                 //!state because otherwise the led would be inverted
  state = !state;                                                           //toggle the state function to switch the led state
}

Works just fine, now I need to implement it into my other project but that should work just fine as well.

Just a quick note on this:

  digitalWrite(LED, (!state) ? HIGH : LOW); //!state because otherwise the led would be inverted

usually when you meet this, you would just write it that way:

  digitalWrite(LED, state ? LOW: HIGH); //

--> just inverse what happens :slight_smile:

J-M-L:
Just a quick note on this:

  digitalWrite(LED, (!state) ? HIGH : LOW); //!state because otherwise the led would be inverted

usually when you meet this, you would just write it that way:

  digitalWrite(LED, state ? LOW: HIGH); //

--> just inverse what happens :slight_smile:

It's commented: the led is on as soon as the pin is low. It somehow then runs current to gnd but all i know its inverted. So i inverted the code to get it right

tstech:
It's commented: the led is on as soon as the pin is low. It somehow then runs current to gnd but all i know its inverted. So i inverted the code to get it right

What JML is saying is that instead of inverting the state variable, invert the positions of HIGH and LOW.