MAX7219 with ESP32 - flickering

hi guys,

just switched my project to MEGA2560 to ESP32, what a difference, i love ESP32! :slight_smile:

however, i've noticed one issue. I have a max7219 7 segment displays, While i was using MEGA2560 to run it everything worked ok but now with ESP32 i have a strange issue, if i run something that should write to 7 segment displays in a main loop (full speed) random display characters flicker, they don't turn off/on but it looks as if they are just changing in brightness. there is no pattern, just randomly a digit would get brighter for a fraction of a moment, then 2 seconds nothing then another one....

now, if i run led display code in one second intervals, everything works great, but as soon as i run a part of the code at full speed, i get random flickering.
I need to run it fast to change the display values based on encoder interrupt.
I can not reproduce the issue on MEGA board.

I've figured, that the speed of ESP32 is writing to display too fast and it gets out of sync, could this be the issue?

Thanks!
Alek

i think the problem is in this part of the code:

if (sleepWakeCounter == 0) {
    MaximCC.setChar(DisplayDate,7,tensofhours,false);
    MaximCC.setChar(DisplayDate,6,singlehours,true); 
  } else {
    MaximCC.setChar(DisplayDate,6,singlehours,false); 
  }

  if (sleepWakeCounter == 1) {
    MaximCC.setChar(DisplayDate,5,tensofminutes,false);
    MaximCC.setChar(DisplayDate,4,singleminutes,true); 
  } else {
    MaximCC.setChar(DisplayDate,4,singleminutes,false); 
  }

  if (sleepWakeCounter == 2) {
    MaximCC.setChar(DisplayDate,3,tensofhours1,false);
    MaximCC.setChar(DisplayDate,2,singlehours1,true); 
  } else {
    MaximCC.setChar(DisplayDate,2,singlehours1,false); 
  }

  if (sleepWakeCounter == 3) {
    MaximCC.setChar(DisplayDate,1,tensofminutes1,false);
    MaximCC.setChar(DisplayDate,0,singleminutes1,true); 
  } else {
    MaximCC.setChar(DisplayDate,0,singleminutes1,false); 
  }

As i turn the encoder, decimal point of every second digit marks the group of 2 digits i have selected to change. The problem is, whatever the "sleepWakeCounter" we are at, its "else" statement constantly re-writes the decimal point for other 3.

is there a way to do this in any other way so the code is not constantly writing to led display every loop?

many thanks,
Alek

As a general rule, post the entire code. Well done using code tags!
Check the library You use, if it's made for this controller. I'm an UNO guy and don't know the ESP family.

Thanks Railroader.

This is the second time someone is commenting on my code tags, am i not doing something right?
To me it looks like the code is indeed in proper code tags.
LedControl library works with ESP, as i said everything always worked fine on both MEGA and ESP32 until i made the "setup menu" for setting up the alarm.
What my code does is constantly writing "else" statement for 3 out of 4 "sleepWakeCounter", and i am doing this just to get the decimal point.

i am thinking of maybe using led matrix code (lc.setRow(0,2,B10110000); ) instead so i just turn off the decimal point instead of writing the digit as well every cycle.

Many thanks,
Alek

Yes, in code tags, but only a snippet.

Someone here has a standard reply: "The problem is in the part of the code you did not post."

full sketch bellow:

//----------------------------------------------------------------------------------------------------------
// Libraries
//----------------------------------------------------------------------------------------------------------
#include <EEPROM.h>
#include <ezButton.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
#include <Wire.h>
#include <LedControl.h>
#include <SPI.h>

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

#define MAXIMCCLD 16         // output - CS/LOAD - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays
#define MAXIMCCCLK 5        // output - clock   - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays
#define MAXIMCCDATA 17       // output - DATA    - pseudo SPI connection to the Maxim 7219 chip - 7 segment displays
#define MOSFET_Signal 4

// lc is for the Maxim displays
LedControl MaximCC=LedControl(MAXIMCCDATA, MAXIMCCCLK, MAXIMCCLD, 1); // Define pins for Maxim 72xx and how many 72xx we use
ezButton button(34);  // create ezButton object that attach to pin 9 - in this case encoder button;

int hundredsof_pressure, tensofdegrees, singledegrees, millidegrees, tensofpercents, singlepercents, millipercents, tensofcounts, singlecounts, singleHH_powerSave, tensofHH_powerSave,MM_powerSave,singleMM_powerSave,tensofMM_powerSave;

//  These variables are for encoder counters. These need to be always declared as VOLATILE
volatile int counter_hour_start;
volatile int counter_minute_start;
volatile int counter_hour_end;
volatile int counter_minute_end;
volatile int sleepWakeCounter;
volatile int counter;

int previousSecond = 0;

// usually the rotary encoders three pins have the ground pin in the middle
enum PinAssignments {
  encoderPinA = 18,   // right DT
  encoderPinB = 23,   // left CLK
};

// interrupt service routine vars
bool A_set = false;
bool B_set = false;

  // a counter for the dial
signed int lastReportedPos_hour_start = 1;   // change management
signed int lastReportedPos_minute_start = 1;   // change management
signed int lastReportedPos_hour_end = 1;   // change management
signed int lastReportedPos_minute_end = 1;   // change management
signed int lastReportedPos_brightness_count = 1;   // change management
signed int lastReportedPos_setupMenuCounter = 1;
signed int setupMenuCounter = 1; // counter goes from 1 to 3 or whatever the number of menu items is. Counter cycles so after 3 we get 1 or if we go to less than 1 we get 3
signed int lastReportedPos_sleepWakeCounter = 1;

int tensofhours, singlehours, tensofminutes, singleminutes,tensofhours1, singlehours1, tensofminutes1, singleminutes1;

const int SHORT_PRESS_TIME = 1000; // 1000 milliseconds
const int LONG_PRESS_TIME  = 1000; // 2000 milliseconds

unsigned long pressedTime  = 0;
unsigned long releasedTime = 0;
bool isPressing = false;
bool isLongDetected = false;

// we need to set count0 as true to start with, we will later toggle it if we enter the setup menu.
bool count0 = true;

//Non-Blocking delay code using millis, for encoder - source: https://dzone.com/articles/arduino-using-millis-instead-of-delay
int period = 25;
unsigned long time_now = 0;

int oldEncoderButtonState = LOW;
int encoderButtonState = LOW;

bool selectVar = false;
bool setSleepWakeDisplay = false;

//==============================================================================
// Interrupt Service Routine - ISR Encoder
//==============================================================================

void IRAM_ATTR isr(){
  
  if ( digitalRead(encoderPinA) != A_set ) { // debounce once more
    A_set = !A_set;
    // adjust counter + if A leads B
    if ( A_set && !B_set )
    if (count0 == true)
        counter +=1;
      else if (selectVar == true)
        sleepWakeCounter +=1;
      else if ((sleepWakeCounter == 0) && (selectVar == false))
        counter_hour_start +=1; 
      else if ((sleepWakeCounter == 1) && (selectVar == false))
        counter_minute_start +=1;
      else if ((sleepWakeCounter == 2) && (selectVar == false))
        counter_hour_end +=1;
      else if ((sleepWakeCounter == 3) && (selectVar == false))
        counter_minute_end +=1;
    }
    
  if ( digitalRead(encoderPinB) != B_set ) {
    B_set = !B_set;
    //  adjust counter - 1 if B leads A
    if ( B_set && !A_set )
      if (count0 == true)
        counter -=1;
      else if (selectVar == true)
        sleepWakeCounter -=1;
      else if ((sleepWakeCounter == 0) && (selectVar == false))
        counter_hour_start -=1;
      else if ((sleepWakeCounter == 1) && (selectVar == false))
        counter_minute_start -=1;
      else if ((sleepWakeCounter == 2) && (selectVar == false))
        counter_hour_end -=1;
      else if ((sleepWakeCounter == 3) && (selectVar == false))
        counter_minute_end -=1;
  }
}

//==============================================================================
// SETUP
//==============================================================================
void setup()
{
  // initialize Serial communication - We have to have SERIAL
  Serial.begin(115200);
  button.setDebounceTime(50); // set debounce time to 50 milliseconds
  button.setCountMode(COUNT_FALLING);

  pinMode(encoderPinA, INPUT);
  pinMode(encoderPinB, INPUT);

  EEPROM.begin(64);
  counter_hour_start = EEPROM.read(0);
  counter_minute_start = EEPROM.read(2);
  counter_hour_end = EEPROM.read(4);
  counter_minute_end = EEPROM.read(6);

  attachInterrupt(encoderPinA, isr, CHANGE);
  attachInterrupt(encoderPinB, isr, CHANGE);

  // Initialize RTC and set as SyncProvider.
  // Later RTC will be synced with DCF time
  setSyncProvider(RTC.get); // the function to get the time from the RTC
  // check if RTC has set the system time
  if (timeStatus() != timeSet)
  { // Unable to sync with the RTC - activate RTCError LED
  }

  tensofhours = counter_hour_start / 10;
  singlehours = counter_hour_start % 10;
  tensofminutes = counter_minute_start / 10;
  singleminutes = counter_minute_start % 10;
  tensofhours1 = counter_hour_end / 10;
  singlehours1 = counter_hour_end % 10;
  tensofminutes1 = counter_minute_end / 10;
  singleminutes1 = counter_minute_end % 10;
}

//==============================================================================
// LOOP
//==============================================================================
void loop()
{
  tasksEverySecond();

  // check if there was a short or a long press of a bottom // 
  button.loop(); // MUST call the loop() function first

  if(button.isPressed()){
    pressedTime = millis();
    isPressing = true;
    isLongDetected = false;
  }

  if(button.isReleased()) {
    isPressing = false;
    releasedTime = millis();

    long pressDuration = releasedTime - pressedTime;
  }

  if(isPressing == true && isLongDetected == false) {
    long pressDuration = millis() - pressedTime;
    if( pressDuration > LONG_PRESS_TIME ) {
      isLongDetected = true;
    }
  }

  if (count0 == true){ 
    if(millis() > time_now + period){
        time_now = millis();
    encoder_brightness();
    } 
  } else {
    if(millis() > time_now + period){
        time_now = millis();
    encoder_alarm();
    }
  }
  
  if (isLongDetected == true){
    button_press();
  }
}


//================================================================================================================
//
// Function name : tasksEverySecond
// called from   : <loop>
//
// Purpose       : perform tasks that must happen once every SECOND
// Parameters    : none
// Return value  : none
//
//================================================================================================================
void tasksEverySecond()
{
  // check if time is changed
  if (second() != previousSecond)
  {
    // 'reset' variable state
    previousSecond = second();

    if (count0 == true) {
    displayRtcTime();
    }

  }
}

//================================================================================================================
//
// Function name : displayRtcTime
// called from   : <tasksEverySecond>
//
// Purpose       : display the Real Time clock time on the RTC display
// Parameters    : none
// Return value  : none
//
//================================================================================================================

void displayRtcTime()
{

  MaximCC.setChar(0, 0, (hour() % 10), true); 
  MaximCC.setChar(0, 1, (hour() % 10), true);

  
  MaximCC.setChar(0, 2, (minute() / 10), true);
  MaximCC.setChar(0, 3, (minute() % 10), false);

  MaximCC.setChar(0, 7, (second() / 10), false);
  MaximCC.setChar(0, 6, (second() % 10), false);

}

//==============================================================================
// button_press
//==============================================================================
// this loop has a task to convert button.getCount(); to boolean "count0" so we can
// use it in other loops without calling button.getCount(); every time
void button_press (){

  unsigned long count = button.getCount();
  if (count == 0) {
    count0 = true;
    } else {
      count0 = false;
    }  
}

//==============================================================================
// ENCODER ALARM
//==============================================================================

void encoder_alarm(){

  // toggle boolean state to choose between cycling the setup positions and changing the values
  encoderButtonState = digitalRead(34);
  if (( encoderButtonState != oldEncoderButtonState ) && (encoderButtonState == HIGH)) {
     selectVar = !selectVar;
    }
    oldEncoderButtonState = encoderButtonState;
    

  //--------------------- sleepWakeCounter ------------------------//
    if (lastReportedPos_sleepWakeCounter != sleepWakeCounter) {
      if (sleepWakeCounter>4) {(sleepWakeCounter = 0);}
      if (sleepWakeCounter<0) {(sleepWakeCounter = 4);}
    
       lastReportedPos_sleepWakeCounter = sleepWakeCounter;
    }

  //--------------------- HOURS alarm STARTS at ------------------------//
  if (lastReportedPos_hour_start != counter_hour_start) {
      if (counter_hour_start>23) {(counter_hour_start = 0);}
      if (counter_hour_start<0) {(counter_hour_start = 23);}

      tensofhours = counter_hour_start / 10;
      singlehours = counter_hour_start % 10; 

      lastReportedPos_hour_start = counter_hour_start;
  }
   

  //--------------------- MINUTES alarm STARTS at ------------------------//

    if (lastReportedPos_minute_start != counter_minute_start) {
      if (counter_minute_start>59) {(counter_minute_start = 0);}
      if (counter_minute_start<0) {(counter_minute_start = 59);}

       tensofminutes = counter_minute_start / 10;
       singleminutes = counter_minute_start % 10; 

      lastReportedPos_minute_start = counter_minute_start;     
  } 
  
  //--------------------- HOURS alarm ENDS at ------------------------// 

    if (lastReportedPos_hour_end != counter_hour_end) {
      if (counter_hour_end>23) {(counter_hour_end = 0);}
      if (counter_hour_end<0) {(counter_hour_end = 23);}

      tensofhours1 = counter_hour_end / 10;
      singlehours1 = counter_hour_end % 10; 

      lastReportedPos_hour_end = counter_hour_end;
  }
  
  //--------------------- MINUTES alarm ENDS at ------------------------//
  
    if (lastReportedPos_minute_end != counter_minute_end) {
      if (counter_minute_end>59) {(counter_minute_end = 0);}
      if (counter_minute_end<0) {(counter_minute_end = 59);}

       tensofminutes1 = counter_minute_end / 10;
       singleminutes1 = counter_minute_end % 10; 

      lastReportedPos_minute_end = counter_minute_end;     
  } 

  //--------------------- 7 Segment display  ------------------------//

  // this runs only once
  if (setSleepWakeDisplay == false)
  {
    // display current alarm times
    MaximCC.setChar(0,7,tensofhours,false);
    MaximCC.setChar(0,6,singlehours,false);
    MaximCC.setChar(0,5,tensofminutes,false);
    MaximCC.setChar(0,4,singleminutes,false);
    MaximCC.setChar(0,3,tensofhours1,false);
    MaximCC.setChar(0,2,singlehours1,false);
    MaximCC.setChar(0,1,tensofminutes1,false);
    MaximCC.setChar(0,0,singleminutes1,false);
    setSleepWakeDisplay = true;  
  }

   if (sleepWakeCounter == 0) {
    MaximCC.setChar(0,7,tensofhours,false);
    MaximCC.setChar(0,6,singlehours,true); 
  } else {
    MaximCC.setChar(0,6,singlehours,false); 
  }

  if (sleepWakeCounter == 1) {
    MaximCC.setChar(0,5,tensofminutes,false);
    MaximCC.setChar(0,4,singleminutes,true); 
  } else {
    MaximCC.setChar(0,4,singleminutes,false); 
  }

  if (sleepWakeCounter == 2) {
    MaximCC.setChar(0,3,tensofhours1,false);
    MaximCC.setChar(0,2,singlehours1,true); 
  } else {
    MaximCC.setChar(0,2,singlehours1,false); 
  }

  if (sleepWakeCounter == 3) {
    MaximCC.setChar(0,1,tensofminutes1,false);
    MaximCC.setChar(0,0,singleminutes1,true); 
  } else {
    MaximCC.setChar(0,0,singleminutes1,false); 
  }
  
  if (sleepWakeCounter == 4) {
      
      // once we press the button from digit selection boolean togles to "false", this then executes everything below
      if(selectVar == false) {
        // only write to EEPROM once we are exiting the setup menu.
        EEPROM.write(0,counter_hour_start);
        EEPROM.commit();
        EEPROM.write(2,counter_minute_start);
        EEPROM.commit();
        EEPROM.write(4,counter_hour_end);
        EEPROM.commit();
        EEPROM.write(6,counter_minute_end);
        EEPROM.commit();
        
        sleepWakeCounter = 0;         // set sleepWakeCounter to 0 so that when we are back in the setup menu our selection is at position 0 - counter_hour_start
        setSleepWakeDisplay = false;  // set setSleepWakeDisplay to "false" so that when we are back in the setup menu last alarm setup is displayed on the display
        count0 = true;                // once we set count0 to "true" we are exiting "encoder_alarm" loop
        } 
      }
}



//==============================================================================
// ENCODER BRIGHTNESS - MANUAL
//==============================================================================

void encoder_brightness(){
  
    if (lastReportedPos_brightness_count != counter) {
      if (counter>15) {(counter = 15);}
       if (counter<0) {(counter = 0);}
       tensofcounts = counter / 10;
       singlecounts = counter % 10; 
       lastReportedPos_brightness_count = counter;
       

      for (int i = 0; i < 8; i++) {
        MaximCC.setIntensity(i, counter);
      }
       
       MaximCC.setChar(0,0,tensofcounts,false);
       MaximCC.setChar(0,1,singlecounts,false);
       }
}

many thanks,
Alek

Hi,
What if you write some basic code to drive the displays to see if your problem is software or hardware?

You must have some display only code from when you wrote your code in stages for the Mega.

Tom...... :grinning: :+1: :coffee: :australia:

thanks Tom!

Yes, if you look at the sketch, i have a function "taskEverySecond" that runs once a second and displays the time, no issues whatsoever, only when i enter "encoder_alarm" issues start. Then i did another test, i placed the function "displayRtcTime();" directly in main loop and the issue is there, so i figured that ESP32 is writing to LED display way too fast and thats why some digits flicker. Again, they do not go ON/OFF but just flicker in brightness.

Then, another test, if i change "int period = 25;" which is a millis delay for "encoder_brightness" flickering reduces significantly and it does no happen few times per second any more but maybe 2-3 times every 10 seconds.
If i increase that delay too much then my encoder does not update the display as in "real time".

So the issue has something to do with constantly writing to the display in this part of the code:

if (sleepWakeCounter == 0) {
    MaximCC.setChar(0,7,tensofhours,false);
    MaximCC.setChar(0,6,singlehours,true); 
  } else {
    MaximCC.setChar(0,6,singlehours,false); 
  }

  if (sleepWakeCounter == 1) {
    MaximCC.setChar(0,5,tensofminutes,false);
    MaximCC.setChar(0,4,singleminutes,true); 
  } else {
    MaximCC.setChar(0,4,singleminutes,false); 
  }

  if (sleepWakeCounter == 2) {
    MaximCC.setChar(0,3,tensofhours1,false);
    MaximCC.setChar(0,2,singlehours1,true); 
  } else {
    MaximCC.setChar(0,2,singlehours1,false); 
  }

  if (sleepWakeCounter == 3) {
    MaximCC.setChar(0,1,tensofminutes1,false);
    MaximCC.setChar(0,0,singleminutes1,true); 
  } else {
    MaximCC.setChar(0,0,singleminutes1,false); 
  }

Once i remove "else" statement i did not noticed any flickering. But do not know how to re-write that code to still get the decimal point dot ON/OFF based on digit group selection

Many thanks,
Alek