M5Atom S3 Exception LoadProhibited if dead code is present
-
I am experiencing a odd behavior with my code, which I am not able to solve by myself. I'm using platformio to implement and upload code to my Atom S3.
I have following code which first waits for a BLE connection and then sends/receives information.
#include <M5AtomS3.h> #include <Preferences.h> #include <M5Unified.h> #include <NimBLEDevice.h> #include "Buttons.cpp" #define SERVICE_UUID *HIDDEN* #define CHARACTERISTIC_UUID *HIDDEN* #define DEBUG // Debugging helper for Serial connection #ifdef DEBUG #define PRINTF(text, ...) Serial.printf(text, __VA_ARGS__) #define PRINTLN(text) Serial.println(text) #define PRINT(text) Serial.print(text) #else #define PRINTF(text, ...) #define PRINTLN(text) #define PRINT(text) #endif using namespace std; NimBLEServer *pServer; NimBLECharacteristic *pCharacteristic; M5Canvas canvas(&AtomS3.Display); LGFX_Button button; Preferences preferences; // Parameters auto *font = &fonts::DejaVu18; unsigned int counter; int offset_from_top = 5; const char *games_counter_key = "games"; // BLE Commands const string recording_prefix = "recording:"; const string ready_prefix = "ready:"; const string cmd_rec = "REC"; const string cmd_stop = "STOP"; // Placeholder for recording timestamp string time_label = ""; // State machine enum pi_state { disconnected = 1, connected = 2, recording = 3, }; pi_state p_s = disconnected; volatile bool update = false; // Button Bitmaps extern const unsigned short rec_inactive[]; extern const unsigned short rec_unpressed[]; extern const unsigned short rec_pressed[]; extern const unsigned short stop_unpressed[]; extern const unsigned short stop_pressed[]; // BLE Callbacks class MyServerCallbacks : public NimBLEServerCallbacks { void onConnect(NimBLEServer *pServer) { // p_s = connected; PRINT("Client connected!\n"); } void onDisconnect(NimBLEServer *pServer) { p_s = disconnected; PRINT("Client disconnected.\n"); } }; class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic *pCharacteristic) { update = true; } }; void put_asleep() { M5.Display.sleep(); M5.Display.waitDisplay(); gpio_get_level(GPIO_NUM_41); gpio_wakeup_enable((GPIO_NUM_41),GPIO_INTR_LOW_LEVEL); esp_sleep_enable_gpio_wakeup(); NimBLEDevice::stopAdvertising(); Serial.print("Sleepy\n"); esp_light_sleep_start(); // Start from the beginning... will return excp in Serial esp_restart(); } void setup() { auto cfg = M5.config(); #if defined(DEBUG) cfg.serial_baudrate = 115200; #endif AtomS3.begin(cfg); preferences.begin( "my-app", false); counter = preferences.getUInt( games_counter_key, 0); PRINTF("Counter: %d\n", counter); if (AtomS3.Display.isEPD()) { AtomS3.Display.setEpdMode(epd_mode_t::epd_fastest); } canvas.setColorDepth(16); canvas.createSprite(AtomS3.Display.width(), AtomS3.Display.height()); canvas.setTextColor(WHITE); canvas.setTextDatum(middle_centre); canvas.setFont(font); canvas.setTextSize(0.9f); offset_from_top = (canvas.fontHeight(font) / 2) + 2; int edge_length = canvas.height() - (canvas.fontHeight(font) * 2) - 15; button.initButton(&canvas, canvas.width() / 2, canvas.height() / 2 + (canvas.fontHeight(font)) + 4, edge_length, edge_length, TFT_LIGHTGREY, TFT_DARKGREY, TFT_BLACK, ""); NimBLEDevice::init("Kicker_GATT_Server"); pServer = NimBLEDevice::createServer(); pServer->setCallbacks(new MyServerCallbacks()); NimBLEService *pService = pServer->createService(SERVICE_UUID); pCharacteristic = pService->createCharacteristic( CHARACTERISTIC_UUID, NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); pCharacteristic->setValue("Hello GATT!"); pService->start(); NimBLEDevice::startAdvertising(); canvas.drawString("Stats", canvas.width() / 2, offset_from_top); canvas.drawString("Games played:", canvas.width() / 2, canvas.height() / 2 - (canvas.fontHeight(font) / 2) - 2); canvas.drawNumber(counter, canvas.width() / 2, canvas.height() / 2 + (canvas.fontHeight(font) / 2) + 2, &fonts::DejaVu24); canvas.pushSprite(0, 0); delay(3000); PRINTLN("Setup done"); } void loop() { if(update){ std::string value = pCharacteristic->getValue().c_str(); PRINTF("Received data: \"%s\"\n", value.c_str()); if (value.compare(0, recording_prefix.length(), recording_prefix) == 0) { if (p_s == connected) { counter++; preferences.putUInt( games_counter_key, counter); } canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)stop_unpressed); p_s = recording; if (value.length() > 10) time_label = value.substr(recording_prefix.length()); } else if (value.compare(0, ready_prefix.length(), ready_prefix) == 0) { canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)rec_unpressed); p_s = connected; time_label = ""; } update = false; } AtomS3.update(); switch (p_s) { case connected: canvas.fillRect(0, 0, canvas.width(), offset_from_top + canvas.fontHeight(font) * 2 + 2, TFT_BLACK); canvas.drawString("Press to", canvas.width() / 2, offset_from_top); canvas.drawString("record", canvas.width() / 2, offset_from_top + canvas.fontHeight(font) + 2); if (AtomS3.BtnA.isHolding() || AtomS3.BtnA.wasPressed()) { canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)rec_pressed); PRINTLN("Pressed"); } if (AtomS3.BtnA.wasReleased()) { canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)rec_unpressed); pCharacteristic->setValue(cmd_rec); pCharacteristic->notify(); PRINTLN("Released"); } break; case recording: canvas.fillRect(0, 0, canvas.width(), offset_from_top + canvas.fontHeight(font) * 2 + 2, TFT_BLACK); canvas.drawString("Recording", canvas.width() / 2, offset_from_top); canvas.drawString(time_label.c_str(), canvas.width() / 2, offset_from_top + canvas.fontHeight(font) + 2); if (AtomS3.BtnA.isHolding() || AtomS3.BtnA.wasPressed()) { canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)stop_pressed); PRINTLN("Pressed"); } if (AtomS3.BtnA.wasReleased()) { canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)stop_unpressed); pCharacteristic->setValue(cmd_stop); pCharacteristic->notify(); PRINTLN("Released"); } break; default: canvas.fillRect(0, 0, canvas.width(), offset_from_top + canvas.fontHeight(font) * 2 + 2, TFT_BLACK); canvas.drawString("Disconnected", canvas.width() / 2, offset_from_top); canvas.drawString("Waiting for Pi", canvas.width() / 2, offset_from_top + canvas.fontHeight(font) + 2); canvas.fillRect(0, offset_from_top + canvas.fontHeight(font) * 2 + 2, canvas.width(), canvas.height(), TFT_BLACK); canvas.pushImage(canvas.width() / 2 - 40, canvas.height() / 2 + (canvas.fontHeight(font)) + 4 - 40, 80, 80, (uint16_t *)rec_inactive); break; } canvas.pushSprite(0, 0); }
Buttons.cpp
only contains images in binary format.put_asleep()
is dead code but will become relevant in a few seconds. It's just some random code I've used to test other things and features, which led me to the issue in the first place.If I run this code, I will get the following exception once a BLE connection is established and first informations should be displayed (line 196 is the line starting with
canvas.drawString("record"...
in the first switch case block.Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled. Core 1 register dump: PC : 0x42003e62 PS : 0x00060630 A0 : 0x8201d918 A1 : 0x3fcebe40 A2 : 0x3fc9d4b4 A3 : 0x3fc98730 A4 : 0x3fc9872c A5 : 0x00000040 A6 : 0x00000000 A7 : 0x00000080 A8 : 0x82003e60 A9 : 0x3fcebe20 A10 : 0x00000036 A11 : 0x3c07029e A12 : 0x00000040 A13 : 0x0000001c A14 : 0x0000002c A15 : 0x00000000 SAR : 0x00000010 EXCCAUSE: 0x0000001c EXCVADDR: 0x00000018 LBEG : 0x400570a4 LEND : 0x400570a9 LCOUNT : 0x00000000 Backtrace: 0x42003e5f:0x3fcebe40 0x4201d915:0x3fcebeb0 #0 0x42003e5f in loop() at src/main.cpp:196 #1 0x4201d915 in loopTask(void*) at /Users/xyz/.platformio/packages/framework-arduinoespressif32/cores/esp32/main.cpp:50 ELF file SHA256: c74994da80137adc Rebooting... ESP-ROM:esp32s3-20210327 Build:Mar 27 2021 rst:0xc (RTC_SW_CPU_RST),boot:0x28 (SPI_FAST_FLASH_BOOT) Saved PC:0x420663d6 #0 0x420663d6 in esp_pm_impl_waiti at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/esp_pm/pm_impl.c:855
I am already aware that this indicates some sort of null pointer problem. However, the corresponding line does not have a null pointer (at least from my understanding).
canvas
is used in the line before, without any issue. The problem still persists if I remove the font height getter. Also, the stack trace will simply point to the next line, if I comment out line 196.
The weirdest part here is, that ifput_asleep()
is removed or commented out, the exception does not occur. The issue persists regardless of its content. Especially this aspect makes me wonder what the underlying issue is. I've changed the overall code a lot, while trying to solve it, but I'm out of ideas now.Does somehow have any clue what the problem/error here is?
-
Hello @nelll
it looks like the crash is happening when
AtomS3.BtnA.isHolding()
is called.Now the strange part. When I modify below two lines in
put_asleep()
the crash goes away.// M5.Display.sleep(); // M5.Display.waitDisplay(); AtomS3.Display.sleep(); AtomS3.Display.waitDisplay();
I can only guess that while
put_asleep()
isn't called anywhere it still confuses somehow the namespaces - but that is really just a wild guess.Thanks
Felix -
Hi @felmue
thank you for your help!
This solves the issue in the mentioned example.
However, I took a look at an earlier version of my code and this issue persists if I addAtomS3.Power.deepSleep(0,false);
orM5.Power.deepSleep(0,false);
somewhere. This was the reason for the messy implementation ofput_asleep
as a workaround in the first place. I've checked it with the code above and if you replace the content ofput_asleep
with just thedeepSleep
call, it still runs into the exception. Same applies tolightSleep
.I assume the reason behind this, is the fact, that the AtomS3 library contains calls to the Unified library (
M5.(...)
)?!Nonetheless, from my understanding, it has to be some mistake I must have made, because otherwise this problem would have happened to other devs as well, as this is pretty significant. However, I'm out of ideas with my limited knowledge about C++ and ESP overall.
P.S.: I've removed the redundant
#include <M5Unified.h>
. Still the same issues... -
Hello @nelll
have you tried to not use M5AtomS3 library and only use M5Unified library and replace all
AtomS3.
withM5.
? Note: not tested myself - just an idea.Thanks
Felix -
Hey @felmue,
thanks, this did solve all the issues! Didn't know that I can solely rely on the Unified library without any further changes.
Do you know if this is intended, and if so, why does the Board-specific library exist?Thank you so much for your help!