ESP32, Program runs sometimes then misbehaves !

I am trying to dim 3 string lights using an ESP32 board.
after unable to find a timer interrupt library, i found a readymade library for AC Dimming with ZeroCross.

Library: GitHub - fabianoriccardi/dimmable-light: AVR, ESP8266, ESP32, ESP32-S2, SAMD compatible library to manage dimmers in Arduino framework

The Problem:
Everything works fine sometimes and then start to stay completely ON or OFF or Flickers.
(so it's not a hardware problem or pin issue (i've tried multiple different pins)

The Code: The Problem must be here

/**
 * In this example you can see a number of effects on different indipendent 
 * dimmable lights. To switch among the available effects, (un)comment the
 * proper line in the setup() function.
 *
 * NOTE: compiles only for ESP8266 and ESP32 because the Ticker.h dependency
 */
 
#include <Ticker.h>
#include <dimmable_light.h>

const int syncPin = 2;
DimmableLight l1(27);
DimmableLight l2(17);
DimmableLight l3(26);

Ticker dim;

void setup() {
  DimmableLight::setSyncPin(syncPin);
  DimmableLight::begin();
  doCircularSwipe();
}

/**
 * Turn on the light with (255/nLights) steps offset between consecutive lights
 */
void doCircularSwipe(void){
  static const double period = 0.01;    // The library accepts as it is its in miliseconds maybe

  static double brightnessStep = 0;

  // Circular swipe effect sin funcrion used for easy phase shifting and triangular value generation
  l1.setBrightness(140 + (sin(brightnessStep - 1.04)*116));   //140 so that Leds don't completly turrn off
  l2.setBrightness(140 + (sin(brightnessStep)*116));
  l3.setBrightness(140 + (sin(brightnessStep + 1.04)*116));


  brightnessStep+= 0.03;
  if(brightnessStep >= 30000.00){       //continue for some time without resetting
    brightnessStep= 0;
  }
  dim.once(period,doCircularSwipe);
}

void loop() {

}

The circuit is perfectly fine with zerocrossing, Leds are controlled with SCR, AC current is Rectified before sending in, so it's a fluctuating DC basically.

The string lights Leds share the same ground as ESP32 (it's obvious)

Please Help.
It floped my festival cause it was working fine yesterday for some time

All your code is placed in setup.
The function setup runs down exactly one time.

After that your ESP32 startes executing the function loop().

The name is "program" function lopp() loops.
As there are no commands to execute inside the function loop
it is just looping empty = does not

  • switch any IO-pin,
  • change the value of a vraiable
  • nor doing something else.

If you want further help you should provide a link to the documentation of the libraries you are using.

best regards Stefan

StefanL38:
All your code is placed in setup.
The function setup runs down exactly one time.

After that your ESP32 startes executing the function loop().

The name is "program" function lopp() loops.
As there are no commands to execute inside the function loop
it is just looping empty = does not

If you want further help you should provide a link to the documentation of the libraries you are using.

I have already linked the library's repository.
Please check the examples there.

As for the loop function, there is no need for that because the

dim.once(period,doCircularSwipe);

Function calls itself everytime, it's implemented in the library itself.

Even putting it inside the loop function doesn't help.

Please check the example codes of the library

The Problem:
Everything works fine sometimes and then start to stay completely ON or OFF or Flickers.
(so it's not a hardware problem or pin issue (i've tried multiple different pins)

That is not proof that your hardware is working, just trying different pins. There must be a lot more to your circuit that can fail. I'm wondering why you think that is conclusive.

You mention library examples, do any of those work on your hardware?

making a function call itself is a bad idea as everytime it gets called some additional RAM must be allocated for storing local variables. If you do this infinetely it will use all available RAM and maybe even start overwriting already used RAM

best regards Stefan

It floped my festival cause it was working fine yesterday for some time

Trust me, a single day of testing for a project like that is not enough.

The circuit is perfectly fine with zerocrossing, Leds are controlled with SCR, AC current is Rectified before sending in, so it's a fluctuating DC basically.

The string lights Leds share the same ground as ESP32 (it's obvious)

It is not that obvious, why don't you post a complete circuit schematic. Including the TRIAC & DIAC and the opto-coupler, or a link to the breakout board you use.
I have had experience with a project where dimming a single light worked, (in fact all individual channels worked) and connecting more than one (filament LED) bulb cause flickering, probably due to the capacitor in the bulbs drawing power from the DIAC, which would cause it and the TRIAC to close. To be honest that is just a suspicion, in the end it converted the whole system to 240v DC and controlled it with MOSFET's and that worked.

StefanL38 is totally correct.

You've written yourself an endless rabbit hole. There's no end to your recursion except when the processor runs out of RAM and goes rogue.

-jim lee

StefanL38:
making a function call itself is a bad idea

Not always, but in a limited memory situation, yes. It can be an elegant solution to some problems, but it has to have protection to limit the number of recursive calls it can make, also a guarantee that during some call, it will not call itself again. Obviously this makes exception handling difficult. Somewhere I saw a guide to converting recursive to non-recursive form - it stated that theoretically, there is no recursive solution that can't be made non-recursive.

An example of that is a recursive function to calculate the factorial of an integer, 'x!'. Each call calls the number that it was passed, reduced by one. It's easy to see that it eventually has to reach zero and return cleanly. It's good to keep the criteria simple like that, or it's too hard to prove.

jimLee:
StefanL38 is totally correct.

You've written yourself an endless rabbit hole. There's no end to your recursion except when the processor runs out of RAM and goes rogue.

-jim lee

I hope you can see that the variables and constants inside the recursive function is defined as "static" .
That means the aren't initialised everytime the function is called.

Considering how much powerful is an ESP32 and the amount of ram, I don't think it'll run out of it, also there is no dynamic allocation of data, the lengths are pre defined and can't exceed the value.

aarg:
That is not proof that your hardware is working, just trying different pins. There must be a lot more to your circuit that can fail. I'm wondering why you think that is conclusive.

You mention library examples, do any of those work on your hardware?

As I have this problem and I want a solution, why would I lie about anything !

As I mentioned, turning it on and off like 10 times make it work perfectly once.
That is once out of 10 times it works perfectly !

Deva_Rishi:
Trust me, a single day of testing for a project like that is not enough.

To be honest that is just a suspicion, in the end it converted the whole system to 240v DC and controlled it with MOSFET's and that worked.

Deva_rishi hope you understand, I've made other lights work (on AC 220V) by use of triacs and optocouplers, but this is led lights and I did just rectified into DC, and it's controlled with an SCR now.

I had the program that worked perfectly fine with Arduino nano
Reference : Arduino Cloud

But the timerIsr doesn't work on ESP32 so I had to code something else.

If possible, can you share your code that you used to control multiple lights ?

Also, there are no capacitive loads.

I'm not too deep into the internals of how a c++ compiler and the compiled code works. But as calling a function needs a "jump back" adress to the calling position storing this adress needs memory too.

So to really know if nested calling eat up memory you could code a testcode that reports the free RAM or the stack-size

best regards Stefan

but this is led lights

Of what kind ? 12v ? 240v ?

But as calling a function needs a "jump back" adress to the calling position storing this adress needs memory too.

again you are totally correct ! and when passing an argument to a function (i think you do that, though your code is rather un-transparent), this counts as a declared variable. Your point about the ESP32 is correct, but recursion with care and never infinitely.

If possible, can you share your code that you used to control multiple lights ?

Here it is provided 'as is' without much support. This runs on a pro-mini, which receives it's data over Serial from another pro-mini that receives DMX and controls an LCD screen with menu options. Freqstep is actually a bit low at 16us, putting a bit more stress on the CPU than it should. Just came out like an easy equation, but should probably be 24us (some other variables would have to change for that as well though)
The concept is simple.

  • poll for data.
  • sort data,
  • wait for zero-X
  • copy data into active buffer.

and in the ISR

  • fire the pins one by one, every time just checking the next dimming level, starting at the longest first.
    It uses TimerOne, AVR's ticker equevalent
#include <TimerOne.h>

#define ZERO_X 2            // zero-cross detection pin
#define LED_PIN 13
#define NR_LIGHTS 12
#define BAUD 9600
#define M_DELAY 1200

const uint8_t seq[NR_LIGHTS] = {A2, A1, A0, 6, 12, 11, 10, 9, 8, 7, 5, 4};  // pin sequence got a little disturbed

uint16_t dim[NR_LIGHTS], minBright[NR_LIGHTS], maxBright[NR_LIGHTS];
uint16_t sdim[NR_LIGHTS], cdim[NR_LIGHTS];

uint8_t sseq[NR_LIGHTS], cseq[NR_LIGHTS];
uint8_t freqstep = 16, hz = 50;

volatile uint16_t i = 0;
volatile uint8_t pinon;
volatile uint8_t state = 0;

void setup() {
  Serial.begin(BAUD);
  for (uint8_t i = 0; i < NR_LIGHTS; i++)  {
    pinMode(seq[i], OUTPUT);
    dim[i] = 300;
    minBright[i] = 600;
    maxBright[i] = 90;
  }
  delay(20);
  pinMode(ZERO_X, INPUT);
  pinMode(LED_PIN, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(ZERO_X), ZeroCrossed, RISING);

  Timer1.initialize(freqstep);
  Timer1.attachInterrupt(DimCheck, freqstep);
  digitalWrite(LED_PIN, LOW);
}

void loop()  {
  if (state == 0) DoRequest();
  if (state == 1) Sort();
  if (state == 3) CopySort();
}

void Sort() {
  memcpy(sdim, dim, 2 * NR_LIGHTS);
  memcpy(sseq, seq, NR_LIGHTS);
  for (uint8_t c = 0; c < NR_LIGHTS - 1; c++) { // do a bubble sort
    for (uint8_t d = 0; d < NR_LIGHTS - c - 1; d++) {
      if (sdim[d] > sdim[d + 1]) { // compare
        uint8_t tempseq = sseq[d];
        sseq[d] = sseq[d + 1];
        sseq[d + 1] = tempseq;
        uint16_t tempdim = sdim[d];
        sdim[d] = sdim[d + 1];
        sdim[d + 1] = tempdim;
      }
    }
  }
  state = 2; // ready to copy
}

void CopySort() {
  memcpy(cdim, sdim, 2 * NR_LIGHTS);
  memcpy(cseq, sseq, NR_LIGHTS);
  state = 0;
}


void ZeroCrossed() {
  if (state = 2) state = 3; // ready to copy, start copy !!
  pinon = 0;   // reset array pointers

  PORTD = PORTD | B11110000;  // Turn all the pins HIGH
  PORTB = PORTB | B11111;
  PORTC = PORTC | B111;
  i = 0; // reset timer counter
}


void DimCheck() {
  i++;   // up the counter
  while ((pinon < NR_LIGHTS) && (i >= cdim[pinon])) { // compare the next pin
    digitalWrite(cseq[pinon], LOW);          // turn on light
    pinon++;
  }
}

//-----------------------------------------------------------------------------------------

void DoRequest() {
  const uint32_t MSG[] = {0x53455453, 0x444D5852 };
  uint8_t sbuffer[30];
  if (ClearSerial(M_DELAY, 40));
  Serial.flush();
  Serial.print("DMXR");
  uint32_t moment = millis();
  while (Serial.available() < 4) {
    if (millis() - moment > 200) {
      if (!Serial.available()) BlinkLed(3, 150);
      state = 1;
      return;
    }
  }
  for (uint8_t i = 0; i < 4; i++)     sbuffer[i] = Serial.read();
  uint32_t RecMSG;
  for (uint8_t m = 0; m < 4; m++) {
    RecMSG = RecMSG << 8;
    RecMSG = RecMSG | sbuffer[m];
  }
  if (RecMSG == MSG[1]) { // DMXR
    while (Serial.available() < NR_LIGHTS) {
      if (millis() - moment > 200) { // timeout not enough data
        BlinkLed(4, 100);
        state = 1;
        return;
      }
    }
    for (uint8_t i = 0; i < NR_LIGHTS; i++)   sbuffer[i] = Serial.read();
    if (Serial.available()) {
      BlinkLed(5, 70); // too much data
      state = 1;
      return;
    }
    for (uint8_t i = 0; i < NR_LIGHTS; i++) {
      dim[i] = map(sbuffer[i], 0, 255, minBright[i], maxBright[i]);
    }
    state = 1;
    return;
  }  // end DMXR
  if (RecMSG == MSG[0]) { // SETS
    while (Serial.available() < (NR_LIGHTS * 2)) {
      if (millis() - moment > 200) { // timeout not enough data
        BlinkLed(7, 50);
        state = 1;
        return;
      }
    }
    for (uint8_t i = 0; i < NR_LIGHTS * 2; i++)   sbuffer[i] = Serial.read();
    if (Serial.available()) {
      BlinkLed(5, 50); // too much data
      state = 1;
      return;
    }
    for (uint8_t i = 0; i < NR_LIGHTS; i++) {
      minBright[i] = (uint16_t) sbuffer[i * 2] * 3 + 30;
      maxBright[i] = (uint16_t) sbuffer[(i * 2) + 1] * 3 + 30;
    }
    state = 1;
    return;
  }
  BlinkLed(3, 300);
  state = 1;
  return;
}


inline void Meditate(uint32_t us) {
  uint32_t moment = micros();
  while (micros() - moment < us) ;
}


inline bool WaitForSerial(uint32_t timeout) {
  uint32_t moment = millis();
  while (!Serial.available()) {
    if (millis() - moment > timeout ) return false;
  }
  return true;
}

inline bool ClearSerial(uint16_t waittime, uint8_t maxbytes) {
  uint8_t byt = 0;
  while ((byt < maxbytes) && (Serial.available())) {
    char c = Serial.read();
    byt++;
    if (!Serial.available()) Meditate(M_DELAY);  // it is not actually required to wait every time around
  }
  if (byt == maxbytes) return false;
  return true;
}

inline void BlinkLed(uint8_t times, uint16_t del) {
  delay(400);
  for (uint8_t j = 0; j < times; j++) {
    digitalWrite(LED_PIN, HIGH);
    delay(del);
    digitalWrite(LED_PIN, LOW);
    delay(del);
  }
}

Deva_Rishi:
Here it is provided 'as is' without much support. This runs on a pro-mini, ......Freqstep is actually a bit low at 16us, putting a bit more stress on the CPU than it should. Just came out like an easy equation, but should probably be 24us (some other variables would have to change for that as well though)
The concept is simple.

  • poll for data.
  • sort data,
  • wait for zero-X
  • copy data into active buffer.

and in the ISR

  • fire the pins one by one, every time just checking the next dimming level, starting at the longest first.
    It uses TimerOne, AVR's ticker equevalent

I had the same approach for my Arduino nano, and it works nice.
The problem here is that timerone (or two or three

The problem here is that timerone (or two or three

But that's what we have ticker.h for.