Issue with null key/value's when accessing a map/vector from bluetooth_callback
Posted: Wed Nov 11, 2020 9:09 pm
I am trying to fire a function every time an ESP32 node receives a certain char. I can receive the data and parse the char out of a packet structure I have made. I register all my actions in a map of type <char,callback_function>, where
The idea being that I link all the chars to their respective functions, and when I parse a char out I can find the pointer to the function I want to fire easily. The only downside is I have to keep a reference to a global object to access this map from within the bluetooth_callback. I've been testing this for almost 2 days now, and I've written some example code without the espressif APIs. On its own, my code seems to work fine and it runs on the Arduino too. But if I let the bluetooth_callback get called after registering it with the espressif API, the behavior is undefined. The size of the map is still correct with the number of pairs I have added to it, but all of the values and keys are null or undefined, and if I try to access them I get a Guru Meditation Error. I have been trying to solve this problem a lot, and the behaviour is very strange to me. If I call the callback manually then everything seems to work, but if I let it get called by a bluetooth event, I get undefined behaviour and crashes. I'm not sure if I am doing something wrong c++ wise or if I this is an issue specific to the esp32. I could be making a dumb mistake. I have attached my arduino script, the header and cpp files of the library I was working on, and a sample application that should show what I am trying to do. Thank you for your help.
### Hardware:
Board: ESP32 Dev Module (Sparkfun ESP32 thing)
Core Installation version: 1.0.3
IDE name: Arduino IDE
Flash Frequency: 40Mhz
PSRAM enabled: no
Upload Speed: 115200
Computer OS: Windows 10
Backtrace.
Decoded.
PC: 0x400014e8
EXCVADDR: 0x000000fc
NodeESP.h
And here is NodeESP.cpp
Here is an example program that shows what I want to happen and that it works correctly.
Code: Select all
typedef void (*callback_function)(void);
### Hardware:
Board: ESP32 Dev Module (Sparkfun ESP32 thing)
Core Installation version: 1.0.3
IDE name: Arduino IDE
Flash Frequency: 40Mhz
PSRAM enabled: no
Upload Speed: 115200
Computer OS: Windows 10
Backtrace.
Code: Select all
Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400014e8 PS : 0x00060430 A0 : 0x800d2410 A1 : 0x3ffcf1a0
A2 : 0x000000fe A3 : 0x000000fc A4 : 0x000000ff A5 : 0x0000ff00
A6 : 0x00ff0000 A7 : 0xff000000 A8 : 0x800d2228 A9 : 0x3ffcf170
A10 : 0x3ffbed90 A11 : 0x3f40148b A12 : 0x3f40148b A13 : 0x0000ff00
A14 : 0x00ff0000 A15 : 0xff000000 SAR : 0x00000010 EXCCAUSE: 0x0000001c
EXCVADDR: 0x000000fc LBEG : 0x400014fd LEND : 0x4000150d LCOUNT : 0xfffffffb
Backtrace: 0x400014e8:0x3ffcf1a0 0x400d240d:0x3ffcf1b0 0x400d242d:0x3ffcf1d0 0x400d14a2:0x3ffcf1f0 0x400d1501:0x3ffcf210 0x400df881:0x3ffcf230 0x400d8362:0x3ffcf280 0x4008d83d:0x3ffcf2b0
PC: 0x400014e8
EXCVADDR: 0x000000fc
Code: Select all
Decoding stack results
0x400d240d: Print::write(char const*) at C:\Users\Jake Booth\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\cores\esp32/Print.h line 66
0x400d242d: Print::print(char const*) at C:\Users\Jake Booth\Documents\ArduinoData\packages\esp32\hardware\esp32\1.0.3\cores\esp32\Print.cpp line 89
0x400d14a2: NodeESP::act_message(char*) at C:\Users\Jake Booth\Documents\Arduino\libraries\NodeESP\NodeESP.cpp line 336
0x400d1501: NodeESP::bluetooth_callback(esp_spp_cb_event_t, esp_spp_cb_param_t*) at C:\Users\Jake Booth\Documents\Arduino\libraries\NodeESP\NodeESP.cpp line 176
0x400df881: btc_spp_cb_handler at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/bt/bluedroid/btc/profile/std/spp/btc_spp.c line 152
0x400d8362: btc_task at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/bt/bluedroid/btc/core/btc_task.c line 110
0x4008d83d: vPortTaskWrapper at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/port.c line 143
[code]
I've highlighed line 336 below to show you easily which one it is.
The arduino sketch.
[code]
#include "NodeESP.h"
#define LED_PIN 5
#define TOGGLE_LED_CHAR 'l'
#define TURN_ON_LED_CHAR 'p'
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(LED_PIN,OUTPUT);
char l = TOGGLE_LED_CHAR;
char p = TURN_ON_LED_CHAR;
esp_spp_cb_param_t ex_param;
ex_param.data_ind.data = (uint8_t*)l;
ex_param.data_ind.len = 1;
//ex_param_ptr = *ex_param;
NodeESP node_esp;
node_esp.add_func(&p, toggle_LED);
node_esp.add_func(&l, turn_on_LED);
node_esp.begin("TEST_1");
//Just calling it manually works, but when the espressif API calls it I get undefined behaviour.
// NodeESP::bluetooth_callback(ESP_SPP_DATA_IND_EVT,&ex_param);
}
void toggle_LED()
{
Serial.println("toggle_LED()");
}
void turn_on_LED()
{
Serial.println("turn_on_LED()");
}
void loop() {
// put your main code here, to run repeatedly:
while(1)
{
vTaskDelay(100 / portTICK_PERIOD_MS);
}
}
Code: Select all
/*
NodeESP.cpp - Library for automatically connecting to and using esp32 microcontrollers
Created by Jacob Booth 11/9/2020
*/
#ifndef NodeESP_h
#define NodeESP_h
#include "Arduino.h"
#include <ESPmDNS.h>
#include <WiFi.h>
#include <map>
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "esp_gap_bt_api.h"
//---Define buffer constants--------------------------
#define RCV_BUF_SIZE 128
#define TX_BUF_SIZE 256
//---Define communication constants-------------------
#define RFCOMM_CHANNEL 1
#define RFCOMM_MAX_FRAME_SIZE 0xffff
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! please run 'make menuconfig' to enable it
#endif
class NodeESP
{
public:
//Constructor
NodeESP();
//Begin
bool begin(const char *_name);
//Typedef
typedef void (*callback_function)(void);
//Variables
uint32_t bt_handle;
const char *_name;
std::map<char*, callback_function> funcs;
char rx_buf[RCV_BUF_SIZE];
char tx_buf[RCV_BUF_SIZE];
WiFiServer server;
WiFiClient client;
//Generic
void add_func(char* command, callback_function func);
void act_message(char* trigger);
//Bluetooth
void bluetooth_write(char *buf, int len);
bool bluetooth_spp_init();
bool bluetooth_init(const char *deviceName);
static char parse_data(uint8_t *rcvd, uint16_t len);
static void bluetooth_callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param);
String toString(const IPAddress & address);
private:
};
#endif
Code: Select all
/*
NodeESP.cpp - Library for automatically connecting to and using esp32 microcontrollers
Created by Jacob Booth 11/9/2020
*/
#include "Arduino.h"
#include <ESPmDNS.h>
#include <WiFi.h>
#include <map>
#include "esp_bt_main.h"
#include "esp_bt_device.h"
#include "esp_spp_api.h"
#include "esp_gap_bt_api.h"
#include "NodeESP.h"
using namespace std;
//---State Machine Variables---
//State enum that helps the state machine track its way through
//connecting to wifi.
enum state_t {
UNDEFINED,
DISCONNECTED,
WIFI_CON,
SERVER_CON
};
enum comm_t {
s,
NONE,
WIFI,
BLUETOOTH,
q
};
//The current communication protocol we are using
comm_t cur_comm = s;
//The current state
state_t cur_state = UNDEFINED;
char tx_buf[TX_BUF_SIZE];
char rx_buf[RCV_BUF_SIZE];
//---Wifi
WiFiServer server(0);
WiFiClient client;
//---Bluetooth
uint32_t bt_handle;
void* example_obj;
//---Main Init--------------------------------------------------------
NodeESP::NodeESP()
{
this->_name = "default";
example_obj = (void*)this;
}
bool NodeESP::begin(const char *_name)
{
this->_name = _name;
//For testing set the current communication method to bluetooth
cur_comm = BLUETOOTH;
switch (cur_comm)
{
case UNDEFINED:
return false;
break;
case NONE:
return false;
break;
case WIFI:
return true;
break;
case BLUETOOTH:
if (!this->bluetooth_init(_name))
{
return false;
}
break;
}
return true;
}
//---Bluetooth Functions--------------------------------
bool NodeESP::bluetooth_init(const char *deviceName)
{
//Start Bluetooth
if (!btStart())
{
return false;
}
//Start BlueDroid (bluetooth stack)
if (esp_bluedroid_init()!= ESP_OK)
{
return false;
}
//Enable BlueDroid
if (esp_bluedroid_enable()!= ESP_OK)
{
return false;
}
//Set device name
esp_bt_dev_set_device_name(deviceName);
//Set sannable and discoverable
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
//Init spp server
if (!this->bluetooth_spp_init())
{
return false;
}
//Set send bytes to the correct callback
//send_bytes = write_bluetooth;
return true;
}
bool NodeESP::bluetooth_spp_init()
{
if (esp_spp_register_callback(&bluetooth_callback) != ESP_OK)
{
return false;
}
if (esp_spp_init(ESP_SPP_MODE_CB) != ESP_OK)
{
return false;
}
if (esp_spp_start_srv(ESP_SPP_SEC_NONE, ESP_SPP_ROLE_SLAVE, 1, "") != ESP_OK)
{
return false;
}
return true;
}
void NodeESP::bluetooth_write(char *buf, int len)
{
esp_spp_write(bt_handle, len, (uint8_t*)buf);
}
void NodeESP::bluetooth_callback(esp_spp_cb_event_t event, esp_spp_cb_param_t *param)
{
switch (event)
{
case ESP_SPP_SRV_OPEN_EVT:
{
NodeESP* self = (NodeESP*) example_obj;
self->bt_handle = param->srv_open.handle;
break;
}
case ESP_SPP_CL_INIT_EVT:
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_NONE);
break;
case ESP_SPP_OPEN_EVT:
break;
case ESP_SPP_DATA_IND_EVT:
{
NodeESP* self = (NodeESP*) example_obj;
//char trig = self->parse_data(param->data_ind.data, param->data_ind.len);
self->act_message((char*)param->data_ind.data);
break;
}
case ESP_SPP_CLOSE_EVT:
esp_bt_gap_set_scan_mode(ESP_BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE);
case ESP_SPP_WRITE_EVT:
break;
case ESP_SPP_CONG_EVT:
break;
}
return;
}
//---Parsing functions-------------------------------
//TODO: Add circular buffer
char NodeESP::parse_data(uint8_t *rcvd, uint16_t len)
{
char body_buf[RCV_BUF_SIZE];
uint8_t len_buf[2];
uint8_t rcvd_bytes = 0;
uint8_t recive_stage = 0;
uint16_t calc_len = 0;
//Loop through each char recieved
for (uint16_t i = 0; i < len; i++)
{
uint8_t cur_char = rcvd[i];
rcvd_bytes++;
//If we haven't recieved the length or body
//And we recieve a start delim
if (recive_stage == 0 && cur_char == 0x7E)
{
//We are ready to read the length of the message (next 2 bytes)
recive_stage = 1;
rcvd_bytes = 0;
}
else if (recive_stage == 1)//If we are recieving the length
{
//Next two bytes get stored in the length buffer
len_buf[rcvd_bytes - 1] = cur_char;
//If we have recieved the 2 length bytes
if (rcvd_bytes == 2)
{
//Decode the length
calc_len = ((uint16_t)len_buf[0]) << 8 | (uint16_t)len_buf[1];
//Ready to recieve the body
recive_stage = 2;
rcvd_bytes = 0;
}
}
else if (recive_stage == 2)//If we are recieving the body
{
//Store the recieved bytes
body_buf[rcvd_bytes - 1] = cur_char;
if (rcvd_bytes == calc_len)
{
//Reset
rcvd_bytes = 0;
recive_stage = 0;
return(cur_char);
}
}
}
return '/';
}
void NodeESP::add_func(char* command, callback_function func)
{
Serial.println("Added new callback...");
std::pair<char*, callback_function>* new_pair = new std::pair<char*, callback_function>(command,func);
this->funcs.insert(*new_pair);
}
void NodeESP::act_message(char* trigger)
{
Serial.println("[act message]");
Serial.print("funcs.size() = ");
Serial.print(this->funcs.size()); //Always returns the correct size all of the time
Serial.println("");
for (std::map<char*, callback_function>::iterator it = this->funcs.begin();
it != this->funcs.end();
++it)
{
Serial.print("funcs[i].first = ");
Serial.print(it->first); //Line 336, accessing the first element of the iterator
Serial.println(""); //This works properly when I don't call the function through the callback.
if (trigger == it->first)
{
(it->second)();
}
}
}
String NodeESP::toString(const IPAddress & address)
{
return String(address[0]) + "." + address[1] + "." + address[2] + "." + address[3];
}
Code: Select all
// Example program
#include <iostream>
#include <string>
#include <map>
//Global object to access the instance from within static function
using namespace std;
class example
{
public:
example();
void begin();
typedef void (*callback_function)(void);
std::map<char, callback_function> funcs;
void add_func(char command, callback_function func);
void do_something(char trigger);
static void example_callback(char cb);
};
void* example_obj;
example::example()
{
example_obj = (void*)this;
}
void example::begin()
{
//Set up everything to do with external library, peripherals, ect...
//Register the callback with the library
//register_callback(&example_callback);
}
//Static func that does something
void example::example_callback(char cb)
{
//Have to do this because the args need to match the library's requirements
example* self = (example*) example_obj;
//Do some processing...
self->do_something(cb);
}
//Add a function pointer and its command char to the vector
void example::add_func(char command, callback_function func)
{
std::pair<char, callback_function>* new_pair = new std::pair<char, callback_function>(command,func);
this->funcs.insert(*new_pair);
}
//This function is called from the callback
void example::do_something(char trigger)
{
cout << "funcs.size() = "<< (this->funcs.size())<<"\n"; //Always returns the correct size all of the time
for (std::map<char, callback_function>::iterator it = this->funcs.begin();
it != this->funcs.end();
++it)
{
cout << "funcs[i].first = "<< (it->first)<<"\n";
if (trigger == it->first)
{
(it->second)();
}
}
}
void callback()
{
cout << "Callback Called!";
}
int main()
{
cout << "Starting program...\n";
cout << "Creating example object...\n";
example ex;
cout << "Adding some callbacks...\n";
ex.add_func('x',callback);
ex.add_func('y',callback);
ex.add_func('z',callback);
cout << "Firing callback...\n";
example::example_callback('z');
}