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 if put_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?