Complex OOP cpp code sets bool at 179 then crashes

This code is for an automatic chicken coop powered by an ESP32. As said in the title I have a class with a bool variable that is set to 179( I don't know how) then it crashes.

The name of the variable is dotIsBlinking, and the class that has the bool is named CustomDisplayBehavior, which lets me blink the segments of a TM1637 display, and other stuff, this variable enables me to track if the two dots on the display are blinking or not.

I will not paste the code here because it spans multiple files and classes. so here is the GitHub

I also added some debug print statements. Here is the output in the terminal:

 ELF file SHA256: aabfb4a7174c6382
    
    Rebooting...
    ESP-ROM:esp32s3-20210327
    Build:Mar 27 2021
    rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
    Saved PC:0x42025b9a
    SPIWP:0xee
    mode:DIO, clock div:1
    load:0x3fce3808,len:0x44c
    load:0x403c9700,len:0xbec
    load:0x403cc700,len:0x2920
    entry 0x403c98d8
    Starting
    MoveTimes uint8_t Constructor
    ChickenDoorInterface
    DisplayUiConfig
    ChickenDoorInterface def Constructor
    StateCounter created
    StateCounter created
    StateCounter created
    StateCounter created
    FourDigitTime init
    StateCounter created
    StateCounter created
    CustomDisplayBehavior constuct
    registering callback
    Button init
    registering callback
    Button init
    registering callback
    DisplayUi constructor
    ChickenDoor constructor
    async check
    dot blinkCheck
    dot is binking: 0
    blinkCheck
    dot is binking: 0
    dot is binking: 0
    check
    check
    async check
    dot blinkCheck
    dot is binking: 179
    dot blinkCheck 1
    dot blinkCheck 1.1
    dot blinkCheck 1.2
    dot blinkCheck 1.3
    dot blinkCheck 1.4
    
    Stack smashing protect failure!
    
    
    abort() was called at PC 0x4200c80b on core 1
    
    
    Backtrace: 0x403776c2:0x3fcec010 0x4037a665:0x3fcec030 0x4037fe45:0x3fcec050 0x4200c80b:0x3fcec0d0 0x42002b31:0x3fcec0f0 0x42002b4e:0x3fcec130 0x42002b61:0xffffffff |<-CORRUPTED
    
      #0  0x403776c2:0x3fcec010 in ?? ??:0
      #1  0x4037a665:0x3fcec030 in ?? ??:0
      #2  0x4037fe45:0x3fcec050 in ?? ??:0
      #3  0x4200c80b:0x3fcec0d0 in ?? ??:0
      #4  0x42002b31:0x3fcec0f0 in ?? ??:0
      #5  0x42002b4e:0x3fcec130 in ?? ??:0
      #6  0x42002b61:0xffffffff in ?? ??:0

I would suspect that something is overwriting the dotIsBlinking variable.
Check for anything that might be writing outside the bounds of an array.

if you run the IDE 1.8.x you can use the Arduino ESP8266/ESP32 Exception Stack Trace Decoder to get more information

(side note - a bool in C++ is either true or false, it's up to the compiler to decide how to represent that)

I used ctr+f to see all the places where the variable could be mutated and I couldn't find any out of the ordinary

used the platformio to decode directly in the serial monitor, this is already decoded

That is not what you are looking for.

Suppose that you have an array declared as holding 10 bytes and a separate boolean variable. The array will be in memory somewhere and may be followed immediately in memory by the boolean variable.

Now, suppose that your sketch wrote to the array with an index of 10. Where do you suppose that the value would be written in memory ?

you'd get more useful information with the Arduino IDE, it looks like this

I'd say one possible suspect is the memcpy

    memcpy(segments, currentSegments, segmentsLength);

can you print the value of segmentsLength ?

also are you running low in terms of stack ?

thanks, here is with the exeption decoder:

Decoding stack results
0x403776c2: panic_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/panic.c line 402
0x4037a665: esp_system_abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/esp_system.c line 128
0x4037fe45: abort at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/newlib/abort.c line 46
0x4200c80b: __stack_chk_fail at /Users/ficeto/Desktop/ESP32/ESP32S2/esp-idf-public/components/esp_system/stack_check.c line 36
0x42002b31: CustomDisplayBehavior::dotBlinkCheck() at src/classes/physical_interface/custom_display_behavior.cpp line 95
0x42002b4e: CustomDisplayBehavior::check() at src/classes/physical_interface/custom_display_behavior.cpp line 213
0x42002b61: std::_Function_handler   >::_M_invoke(const std::_Any_data &) at src/classes/physical_interface/custom_display_behavior.cpp line 12

line 95 is the ending bracket of CustomDisplayBehavior::dotBlinkCheck()

yeah, the segmentsLength is the culprit:

Rebooting...
ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0xc (RTC_SW_CPU_RST),boot:0x8 (SPI_FAST_FLASH_BOOT)
Saved PC:0x42025c36
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fce3808,len:0x44c
load:0x403c9700,len:0xbec
load:0x403cc700,len:0x2920
entry 0x403c98d8
Starting
MoveTimes uint8_t Constructor
ChickenDoorInterface
DisplayUiConfig
ChickenDoorInterface def Constructor
StateCounter created
StateCounter created
StateCounter created
StateCounter created
FourDigitTime init
StateCounter created
StateCounter created
CustomDisplayBehavior constuct
registering callback
Button init
registering callback
Button init
registering callback
DisplayUi constructor
ChickenDoor constructor
async check
dot blinkCheck
dot is binking: 0
segments length: 209
segments length: 209
blinkCheck
dot is binking: 0
dot is binking: 0
check
check
async check
dot blinkCheck
dot is binking: 179
segments length: 209
segments length: 209
blinkCheck
dot is binking: 179
blinkCheck 1
segments length: 209
dot is binking: 179
Guru Meditation Error: Core  1 panic'ed (Double exception).

Core  1 register dump:
PC      : 0x4037a872  PS      : 0x00040c36  A0      : 0x82002bc0  A1      : 0x3fcec100  
A2      : 0x3fcec31c  A3      : 0x3fc954f8  A4      : 0x3c030202  A5      : 0x82005276
A6      : 0x00ff0000  A7      : 0xff000000  A8      : 0x820029cd  A9      : 0x3fcec0e0  
A10     : 0x00000005  A11     : 0x00000003  A12     : 0x0000000a  A13     : 0x00000000
A14     : 0x00000000  A15     : 0x3fcec238  SAR     : 0x0000000a  EXCCAUSE: 0x00000002  
EXCVADDR: 0xfffffff0  LBEG    : 0x400556d5  LEND    : 0x400556e5  LCOUNT  : 0xfffffffe


Backtrace: 0x4037a86f:0x3fcec100 0x42002bbd:0x00000000 |<-CORRUPTED

  #0  0x4037a86f:0x3fcec100 in ?? ??:0
  #1  0x42002bbd:0x00000000 in ?? ??:0

here is the decoded exception

PC: 0x4037a872
EXCVADDR: 0xfffffff0

Decoding stack results
0x42002bbd: std::_Function_handler   >::_M_invoke(const std::_Any_data &) at src/classes/physical_interface/custom_display_behavior.cpp line 12

added an initial value to segmetsLength, and the it dose not crash, but dotIsBlinking is still 179

async check
dot blinkCheck
dot is binking: 179
segments length: 4
dot blinkCheck 1
dot blinkCheck 1.1
dot blinkCheck 1.2
dot blinkCheck 1.3
dot blinkCheck 1.4
segments length: 4
blinkCheck
dot is binking: 0
blinkCheck 1
segments length: 4
dot is binking: 0
check
check
m_longPress
async check
dot blinkCheck
dot is binking: 179
segments length: 4
dot blinkCheck 1
dot blinkCheck 1.1
dot blinkCheck 1.2
dot blinkCheck 1.3
dot blinkCheck 1.4
segments length: 4
blinkCheck
dot is binking: 0
blinkCheck 1
segments length: 4
dot is binking: 0
check
check
m_longPress

printing the value of a bool does not make much sense in C++. code should be

Serial.println(dotIsBlinking ? "true" : "false");

You have some memcpy() in different parts of the code, you should double check the length parameter everywhere

there are other weird stuff like in setup

so if there is something pending un the Serial buffer, you are stuck ?

or in loop

if you want to instantiate the door only once, why don't you have that in setup() and this is a transient object as the ChickenDoor door; will get deleted as soon as you leave the if... (create a destructor in the ChickenDoor class and you'll see it's called)

OK, changed the main.cpp file to look like this:


void setup() {
  Serial.begin(115200);
  
  Serial.println("Starting");
  delay(500);
  ChickenDoor *door= new ChickenDoor();
  
}

void loop() {
 
Async.check();
}

and now it works fine.
the output is:

async check
dot blinkCheck
dot is binking: 0
segments length: 4
segments length: 4
blinkCheck
dot is binking: 0
dot is binking: 0
check
check
async check
dot blinkCheck
dot is binking: 0
segments length: 4
segments length: 4
blinkCheck
dot is binking: 0
dot is binking: 0
check
check
async check
dot blinkCheck
dot is binking: 0
segments length: 4
segments length: 4
blinkCheck
dot is binking: 0
dot is binking: 0
check
check

so seems your issue was that the ChickenDoor instance was killed after being instantiated in the loop

typically we tend to avoid using new. you would instantiate the ChickenDoor as a global variable and add a begin() function to set up things that need to be setup at run time (as you don't control when the constructor is called)

ChickenDoor door;

void setup() {
  Serial.begin(115200);
  Serial.println(F("\nStarting"));
  door.begin();
  Serial.println(F("Ready"));
}

void loop() {
  Async.check();
}

So I would need to add a begin method to every class ?

also code only runs on an esp-32-s3, on a normal esp32, it restarts without a guru meditation error after all the constructors are done, it gives me this text:

rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13104
load:0x40080400,len:3036
entry 0x400805e4

Do you have a custom board?
Do you have enough voltage and current?

It also might be linked to constructors doing stuff before the board is ready….

I have experienced some weird stuff with these normal esp32 boards, when I enable wifi they say brown out detector activated, but when I connect my S3 to the same power source and enable wifi it works great, people say online that the problem is with a capacitor in-between the the power and ground lines. They are standard dev boards

I will put a delay before the calls delegation, to see if that's the problem

it still restarts

rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:2
load:0x3fff0030,len:1184
load:0x40078000,len:13104
load:0x40080400,len:3036
entry 0x400805e4
Starting
MoveTimes uint8_t Constructor
ChickenDoorInterface
DisplayUiConfig
ChickenDoorInterface def Constructor
StateCounter created
StateCounter created
StateCounter created
StateCounter created
FourDigitTime init
StateCounter created
StateCounter created
CustomDisplayBehavior constuct
registering callback
Button init
ets Jul 29 2019 12:21:46

The delay won’t solve it

The thing is that you don’t control in which order constructors are called for your global instances and this happens all before you hit your setup() function. So if there are weird dependencies then you might end up in a bad place on one architecture and because things might be different it can work on another one.

I’m reading this from my iPhone so hard to check details esp as your code is pretty "rich" in terms of classes, pointers to functions etc… but that’s where I would dig.