jandelgado/jled

Various ESP32 pins do not work properly

3dluvr opened this issue · 11 comments

Hi,

I have spent past two days trying to get various ESP32 pins to work, and I can't find rhyme to reason what is going on. A simple blinking test should work regardless, yet...

In my application, I use a number of pins for various LEDs (White and RGB), as well pins for proximity input, RTC (via I2C pin 21/22).

Using this https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ as a reference on "free" pins, I can say that only a small sub-set of pins actually work with this library.

For example 16, 17, 26, 27 won't, but 2, 4, 18, 25 do. Others are hit-and-miss for me as well.

Hi, I just did a quick test with my ESP32 WROOM Devkit (https://circuits4you.com/2018/12/31/esp32-devkit-esp32-wroom-gpio-pinout/). The GPIOs 16,17,26 and 27 work fine for me, also all in parallel.

Please note that you must pass the GPIO-Number to the JLed Constructor, not the physical pin number. Example (WROOM 32 Devkit): Physical pin 36 = GPIO23. So you have to construct the JLed instance with JLed(23).

  • Are you sure you did not mix up physical pin numbers and GPIO numbers?
  • Which version of JLed are you using?

Thanks for your quick reply.

I am passing GPIO pins, and I use ESP32 DevKitC board for my project, and I'm using your master branch.

This is the part of the code I use to set the values:

`constexpr auto LED1_PIN = 2; // W-Top
constexpr auto LED2_PIN = 4; // W-Right
constexpr auto LED3_PIN = 18; // W-Left
constexpr auto LED4_PIN = 16; // R
constexpr auto LED5_PIN = 17; // G
constexpr auto LED6_PIN = 26; // B

auto Led1 = JLed(LED1_PIN).Breathe(5000).Repeat(1);
auto Led2 = JLed(LED2_PIN).Breathe(5000).Repeat(1);
auto Led3 = JLed(LED3_PIN).Breathe(5000).Repeat(1);
auto Led4 = JLed(LED4_PIN).Blink(500, 500).Forever;
auto Led5 = JLed(LED5_PIN).Blink(750, 250).Forever();
auto Led6 = JLed(LED6_PIN).Breathe(1000).Forever();

JLed resting1[] = {
Led3, Led6
};
JLed resting2[] = {
Led2, Led6
};
JLed resting3[] = {
Led1, Led6
};

JLedSequence resting1_sequence(JLedSequence::eMode::PARALLEL, resting1);
JLedSequence resting2_sequence(JLedSequence::eMode::PARALLEL, resting2);
JLedSequence resting3_sequence(JLedSequence::eMode::PARALLEL, resting3);`

What happens is that depending on the pin I used above, the LED ends up being either HIGH or LOW but no effect is being applied to it.

This is really a deal breaker for me, especially after spending 2 days thinking I'm doing something wrong. And I'm on a deadline to finish by Friday on top of it all. :(

I still need to tie sequences to some motion detection so that they are triggered by it and run or stop.

Hi, could you please post a compilable sketch so I can try to reproduce the problem?

This for example works on the ESP32:

#include <jled.h>

auto led1 = JLed(2).Blink(500,750).Forever();
auto led2 = JLed(16).Breathe(2000).Forever().DelayAfter(500);
auto led3 = JLed(17).Breathe(1000).Forever();
auto led4 = JLed(23).Breathe(500).Forever();

JLed leds[] = {led1, led2, led3, led4};

JLedSequence sequence(JLedSequence::eMode::PARALLEL, leds);

void setup() {
}

void loop() {
    sequence.Update();
}

Thanks !!

Please see below, it's one variant from earlier today, just butchered. :)

#include <jled.h>

constexpr auto LED1_PIN = 2; // W-Top
constexpr auto LED2_PIN = 4; // W-Right
constexpr auto LED3_PIN = 16; // W-Left
constexpr auto LED4_PIN = 17; // R
constexpr auto LED5_PIN = 18; // G
constexpr auto LED6_PIN = 19; // B

constexpr auto MWSENSOR_PIN = 35; // Microwave detector input pin

auto Led1 = JLed(LED1_PIN).Breathe(5000).Repeat(1);
auto Led2 = JLed(LED2_PIN).Breathe(5000).Repeat(1);
auto Led3 = JLed(LED3_PIN).Breathe(5000).Repeat(1);
auto Led4 = JLed(LED4_PIN).Blink(500, 500).Forever();
auto Led5 = JLed(LED5_PIN).Blink(750, 250).Forever();
auto Led6 = JLed(LED6_PIN).Breathe(1000).Forever();

JLed resting1[] = {
    Led3, Led6
};
JLed resting2[] = {
    Led2, Led6
};
JLed resting3[] = {
    Led1, Led6
};

JLedSequence resting1_sequence(JLedSequence::eMode::PARALLEL, resting1);
JLedSequence resting2_sequence(JLedSequence::eMode::PARALLEL, resting2);
JLedSequence resting3_sequence(JLedSequence::eMode::PARALLEL, resting3);

auto running1 = 0;
auto running2 = 0;
auto running3 = 0;

bool state;
bool lastState;

unsigned long currentMillis, previousMillis, stepMillis;

void checkMWSensor() {
    state = digitalRead(MWSENSOR_PIN);
    if (state != lastState)
    {
        // remember this state as the last
        lastState = state;
    }
    //Serial.println("polled - " + String(state ? "ACTIVE" : "IDLE"));
}

void setup() { 
    Serial.begin(115200);
    Serial.println("Initializing console");

    // initialize the states...
    state = false;
    lastState = false;
    
    // set up the pin to read the sensor state
    pinMode(MWSENSOR_PIN, INPUT);

    // initialize the timing counters
    currentMillis, previousMillis, stepMillis = millis();
}

void loop() {
    checkMWSensor();
    if (state) {
        Led4.Off();
        Led4.Reset();
        //resting1_sequence.Reset();
        //running2 = resting2_sequence.Update();
        Led6.Update();
    } else {
      Led6.Off();
      Led6.Reset(); 
      Led4.Update();
    }
    
    //running1 = resting1_sequence.Update();
    Serial.println("polled - " + String(state ? "ACTIVE" : "IDLE"));
}

Hi, could you please post a compilable sketch so I can try to reproduce the problem?

This for example works on the ESP32:

#include <jled.h>

auto led1 = JLed(2).Blink(500,750).Forever();
auto led2 = JLed(16).Breathe(2000).Forever().DelayAfter(500);
auto led3 = JLed(17).Breathe(1000).Forever();
auto led4 = JLed(23).Breathe(500).Forever();

JLed leds[] = {led1, led2, led3, led4};

JLedSequence sequence(JLedSequence::eMode::PARALLEL, leds);

void setup() {
}

void loop() {
    sequence.Update();
}

With your example, only LED on GPIO2 blinks, all the others are not lit up.

Update.

With the code below, GPIO16 does not work, so I had to switch to GPIO15.

Problem is, I need to be able to turn sequences on and off, and it appears that's not doable. What happens is that when states switch, the previous state sequence/LEDs remain on. :-(

#include <jled.h>

auto LED1_PIN = 2; // Wh-Top
auto LED2_PIN = 4; // Wh-Right
auto LED3_PIN = 15; // Wh-Left
auto LED4_PIN = 17; // R
auto LED5_PIN = 18; // G
auto LED6_PIN = 19; // B

auto MWSENSOR_PIN = 35; // Microwave detector input pin

#define CONTACT_PERIOD 4000 // in ms how long should the continous CONTACT last
                            // or allowing for gap in detection while person is standing still

#define DETECT_PERIOD 7000 // in ms how long is the detection interval
                           // that we consider for switching from 
                           // CONTACT to INTERACTION state

#define COUNT(x)   (sizeof(x) / sizeof(x[0]))

byte state;
byte lastState;
int detect;

String states[] = { "RESTING", "CONTACT", "AGITATED", "JEALOUS", "INTERACTION", "SLEEP" };

const byte RESTING = 0;
const byte CONTACT = 1;
const byte AGITATED = 2;
const byte JEALOUS = 3;
const byte INTERACTION = 4;
const byte SLEEP = 5;

unsigned long currentMillis, detectMillis, contactMillis;
/*
JLed Led1 = JLed(LED1_PIN).Breathe(5000).Repeat(1);
JLed Led2 = JLed(LED2_PIN).Breathe(5000).Repeat(1);
JLed Led3 = JLed(LED3_PIN).Breathe(5000).Repeat(1);
JLed Led4 = JLed(LED4_PIN).Blink(500, 500).Forever();
JLed Led5 = JLed(LED5_PIN).Blink(750, 250).Forever();
JLed Led6 = JLed(LED6_PIN).Breathe(1000).Forever();
*/

JLed jledLEDtest = JLed(LED4_PIN).Off();

JLed resting_leds[] = {
    JLed(LED1_PIN).Breathe(3000).Forever(),
    JLed(LED2_PIN).Breathe(3000).Forever(),
    JLed(LED3_PIN).Breathe(3000).Forever(),
};

JLedSequence resting_sequence(JLedSequence::eMode::PARALLEL, resting_leds);
//JLedSequence resting2_sequence(JLedSequence::eMode::PARALLEL, resting2);
//JLedSequence resting3_sequence(JLedSequence::eMode::PARALLEL, resting3);

auto running1 = 0;
auto running2 = 0;
auto running3 = 0;

void checkMWSensor();
void tick();

//=======================================================================
//                    Power on setup
//=======================================================================
void setup() {
  Serial.begin(115200);
  Serial.println("Initializing console");

  // initialize the states...
  state = RESTING;
  lastState = RESTING;
    
  // set up the pin to read the sensor state
  pinMode(MWSENSOR_PIN, INPUT);

  // initialize the timing counters
  currentMillis = millis();
  detectMillis = millis();
  contactMillis = millis();
  
  checkMWSensor();

}
 
//=======================================================================
//                    Main Program Loop
//=======================================================================
void loop() {
  checkMWSensor();
  switch(state) {
    case CONTACT:
      if (state != lastState) {
        resting_sequence.Reset();
        jledLEDtest = JLed(LED4_PIN).Blink(500, 250).Forever();
      }
      jledLEDtest.Update();
      break;
    case INTERACTION:
      if (state != lastState) {
        resting_sequence.Reset();
        jledLEDtest = JLed(LED5_PIN).Breathe(500).Forever();
      }
      jledLEDtest.Update();
      break;
    case RESTING:
      if (state != lastState) {
        jledLEDtest.Off();
        //jledLEDtest = JLed(LED1_PIN).Breathe(4000).Forever();
      }
      resting_sequence.Update();
      break;
  } 
}

// ********************************************************************* //

void checkMWSensor() {
  detect = digitalRead(MWSENSOR_PIN);
  
  //Serial.println(String(millis()) + " - " + String(detectMillis));
  //Serial.println("Detection: " + String(detect ? "TRUE" : "FALSE") + " (" + states[state] + " - " + states[lastState] + ")");
  
  if (detect)
  {
    if (millis() - detectMillis < DETECT_PERIOD) // are we within the set period since the last detection
    {
      if (state == CONTACT)
      {
        if (millis() - contactMillis > CONTACT_PERIOD) // are we over the set period of initial contact
        {
          lastState = state;
          state = INTERACTION; // bump us to interaction
        } else {
          lastState = state;
        }
        detectMillis = millis();
      } 
      else if (state == INTERACTION)
      {
        lastState = state; 
        detectMillis = millis(); 
      }
    } else {
      // we must be initiating contact after a while
      lastState = state;
      state = CONTACT;
      detectMillis = millis();
      contactMillis = millis();
    }
  } else {
    if (millis() - detectMillis > DETECT_PERIOD)
    {
      if (state != RESTING)
      {
        state = RESTING;
      } else {
        lastState = state;
      }
    }
  }
}

I'm not sure if I fully understood what you trying to do, but anyhow, some hints:

  • In general, If certain pins do not work as expected, build a simple setup to reproduce and validate this single problem (e.g. by using digitalWrite to turn an LED on and off, then proceeding with JLed)
  • Same goes for your transition function in checkMwSensor(). Test it independently to make sure it works as expected. Also some comments on the state machine states and the transitions would be great ;)
  • In your loop() you rather might call Stop() instead of Reset() since Stop stops the effect and turns the Led off permanently. Further calls to Update will have no effect, unless you restart the effect with a call to Reset.

Hope that helps, good luck.

Thanks for those suggestions, I will update my code. I tried the code on two different ESP32 dev kits just to make sure the one I was using is not damaged in any way, and saw no difference in behaviour. They both experienced the same issue of certain pins not working/stoping to work after too many sequences were assigned.

My understanding of .Stop() from reading the docs was that once you call that, you can't do .Reset() anymore, as the effect is cleared? Is that a correct assumption?

Late last night I ended up re-making the sequences as individual LEDs and that appears to work for the most part.

The problem of pins stopping to work is still there but not as prominent when using individually created JLEDs. It appears that using sequences causes something to happen: pin mapping relocation, memory relocation, I can't really say? But the more sequences I add less pins I have available and I notice I keep getting pushed up the to higher pin numbers, although there's a gap around 25 as well.

Could this be related to ESP32 HAL in jled, and the way LEDs are assigned to channels?

If I just use digitalWrite I have no issues, and I wrote a version of my code yesterday that did that, but it was blocking and not as nice and feature-full as your library.

So, I'm back to using jled and individual LEDs grouped into quazi-sequences, as I'm nearly out of time. Still have to work on creating more effects, fixing the logic and reducing the detection range on the microwave detector.

Stop and Reset
From the README:

Reset
A call to Reset() brings the JLed object to its initial state. Use it when you want to start-over an effect.

Immediate Stop
Call Stop() to immediately turn the LED off and stop any running effects. Further calls to Update() will > have no effect unless the Led is reset (using Reset()) or a new effect activated.

Channels usage
The ESP32 has a limitation of at most 16 so called LEDC channels that are used by JLed to mimic the analogWrite behaviour of the Arduino framework. JLed will start to cycle through the LEDC channels if more than 16 different pins are used. This results in at most 16 Led being controllable on an ESP32.

Perhaps this is what you observed, or is any other lib also using LEDC channels?

(see eps32_hal.h and https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/peripherals/ledc.html)

To be honest now I feel crazy, I must have been tired when I read and thought that .Stop() destroys the effect.

I am only using 6 pins for PWM. The other pins are for I2C/RTC and one digital input for motion detector. No other libs are being used but jled.

My latest code is below. I think it needs more cleanup as those .Stop() and .Update() calls could be all bundled into a procedure. I'd also like to add more random effects for the INTERACTION state as well as during RESTING state...

I'm going to be adding ESP-NOW connectivity next, and RTC with DS3231 via I2C. Fingers crossed I can do all that, troubleshoot/test in the next few hours as that's all the time I have left, and still have some electronics tasks to complete, too.

#include <jled.h>

auto LED1_PIN = 2; // Wh-Top
auto LED2_PIN = 4; // Wh-Right
auto LED3_PIN = 15; // Wh-Left
auto LED4_PIN = 17; // R
auto LED5_PIN = 18; // G
auto LED6_PIN = 19; // B

auto MWSENSOR_PIN = 35; // Microwave detector input pin

#define CONTACT_PERIOD 5000 // in ms how long should the continous CONTACT last
                            // or allowing for gap in detection while person is standing still

#define DETECT_PERIOD 8000 // in ms how long is the detection interval
                           // that we consider for switching from 
                           // CONTACT to INTERACTION state

#define SLEEP_PERIOD 180000 // (180sec) in ms how long before we go to sleep

#define COUNT(x)   (sizeof(x) / sizeof(x[0]))

byte state;
byte lastState;
bool detect;

// initial state is RESTING, unless we are SLEEPING due to long inactivity
// once we make CONTACT if it continues after set period, we elevate into INTERACTION
// if our partner has been detected but we haven't been, be AGITATED or JEALOUS
String states[] = { "RESTING", "CONTACT", "AGITATED", "JEALOUS", "INTERACTION", "SLEEPING" };

const byte RESTING = 0;
const byte CONTACT = 1;
const byte AGITATED = 2;
const byte JEALOUS = 3;
const byte INTERACTION = 4;
const byte SLEEPING = 5;

unsigned long currentMillis, detectMillis, contactMillis;

JLed rest1 = JLed(LED1_PIN).Breathe(3000).Forever();
JLed rest2 = JLed(LED2_PIN).DelayBefore(3000).Breathe(3000).Forever();
JLed rest3 = JLed(LED3_PIN).DelayBefore(6000).Breathe(3000).Forever();

JLed contact = JLed(LED4_PIN).Candle(6, 60).Forever();

JLed interaction1 = JLed(LED5_PIN).Blink(750, 250).Forever();
JLed interaction2 = JLed(LED6_PIN).DelayBefore(200).FadeOn(750).DelayAfter(250).Forever();

JLed sleeping = JLed(LED6_PIN).Breathe(5000).Forever();

void checkMWSensor();

//=======================================================================
//                    Power on setup
//=======================================================================
void setup() {
  Serial.begin(115200);
  Serial.println("Initializing console");

  // initialize the states...
  state = RESTING;
  lastState = RESTING;
    
  // set up the pin to read the sensor state
  pinMode(MWSENSOR_PIN, INPUT);

  // initialize the timing counters
  currentMillis = millis();
  detectMillis = millis();
  contactMillis = millis();

  sleeping.Off();
  contact.Off();
  interaction1.Off();
  interaction2.Off();
  
  checkMWSensor();

}
 
//=======================================================================
//                    Main Program Loop
//=======================================================================
void loop() {
  checkMWSensor(); // are we detecting any movement arond us?
  switch(state) {
    case SLEEPING:
      if (state != lastState) {
        rest1.Stop();
        rest2.Stop();
        rest3.Stop();
        contact.Stop();
        interaction1.Stop();
        interaction2.Stop();
        sleeping = JLed(LED6_PIN).Breathe(6000).Forever();
      }
      break;
    case CONTACT:
      if (state != lastState) {
        sleeping.Stop();
        rest1.Stop();
        rest2.Stop();
        rest3.Stop();
        interaction1.Stop();
        interaction2.Stop();
        contact = JLed(LED4_PIN).Candle(6, 60).Forever();
      }
      break;
    case INTERACTION:
      if (state != lastState) {
        sleeping.Stop();
        rest1.Stop();
        rest2.Stop();
        rest3.Stop();
        contact.Stop();
        interaction1 = JLed(LED5_PIN).Blink(750, 250).Forever();
        interaction2 = JLed(LED6_PIN).DelayBefore(200).FadeOn(750).DelayAfter(250).Forever();
      }
      break;
    case RESTING:
      if (state != lastState) {
        sleeping.Stop();
        contact.Stop();
        interaction1.Stop();
        interaction2.Stop();
        rest1 = JLed(LED1_PIN).Breathe(3000).Forever();
        rest2 = JLed(LED2_PIN).DelayBefore(3000).Breathe(3000).Forever();
        rest3 = JLed(LED3_PIN).DelayBefore(6000).Breathe(3000).Forever();
      }
      break;
  } 
  sleeping.Update();
  rest1.Update();
  rest2.Update();
  rest3.Update();
  contact.Update();
  interaction1.Update();
  interaction2.Update();
}

// ********************************************************************* //

void checkMWSensor() {
  detect = digitalRead(MWSENSOR_PIN);
  
  //Serial.println(String(millis()) + " - " + String(detectMillis));
  //Serial.println("Detection: " + String(detect ? "TRUE" : "FALSE") + " (" + states[state] + " - " + states[lastState] + ")");
  
  if (detect)
  {
    if (millis() - detectMillis < DETECT_PERIOD) // are we within the set period since the last detection
    {
      if (state == CONTACT)
      {
        if (millis() - contactMillis > CONTACT_PERIOD) // are we over the set period of initial contact
        {
          lastState = state;
          state = INTERACTION; // bump us to interaction
        } else {
          lastState = state;
        }
        detectMillis = millis();
      } 
      else if (state == INTERACTION) // we are already interacting
      {
        lastState = state; // update the last state and
        detectMillis = millis(); // detection accordingly
      }
    } else {
      // we must be initiating contact after a while (more than our detection periods)
      lastState = state;
      state = CONTACT;
      detectMillis = millis();
      contactMillis = millis();
    }
  } else {
    // nothing detected in a while 
    // are we ready to drop back to RESTING or SLEEP state?
    if (millis() - detectMillis > DETECT_PERIOD)
    {
      if (millis() - detectMillis < SLEEP_PERIOD) // has it been long enough to go to sleep
      {
        if (state != RESTING)
        {
          state = RESTING;
        } else {
          lastState = state;
        }
      } else {
        lastState = state;
        state = SLEEPING;
      }
    }
  }
}