Hi all,
I have 2 ESP32 communicating with eachother.
The first uses 80% of its memory but it seems that is working fine.
The second uses 80% or more and I think this is causing problems.
The second ESP32 connects to a remote BLE server and characteristic to read the WIFI credentials and after this, it runs a camera web server for video streaming.
I'm trying to change between type of board, partition (I will need OTA), and so, and I have different issues:
sometimes it says:
E (5105) httpd: httpd_create: Failed to allocate memory for HTTP session data
assert failed: vQueueDelete queue.c:2140 (pxQueue)
other times:
Guru Meditation Error: Core 1 panic'ed (LoadProhibited). Exception was unhandled.
and other times it doesn't detect the camera.
The board works with other programs so I guess it's a problem with memory (maybe coming from the video stream buffer).
One option is changing to I2C to communicate with the first ESP32 as the BLEDevice library is too big, or changing to an ESP32 with 8MB, but I want to see if I can solve the issue first.
The board:
ESP32-WROOM-32E 4MB flash (ESP32-D0WD-V3 (revision 3))
No PSRAM
partition: minimal SPIFFS (only for the size of the program)
Sketch uses 1566605 bytes (79%) of program storage space. Maximum is 1966080 bytes.
Global variables use 68352 bytes (20%) of dynamic memory, leaving 259328 bytes for local variables. Maximum is 327680 bytes.
The camera:
OV2640
The code:
#include "BLEDevice.h"
#include <WiFi.h>
#include "EEPROM.h"
#include "esp_camera.h"
#define LENGTH(x) (strlen(x) + 1) // length of char string
#define EEPROM_SIZE 200 // EEPROM size
#define CAMERA_MODEL_AI_THINKER
//#define CAMERA_MODEL_ESP32_CAM_BOARD
#include "camera_pins.h"
void writeStringToFlash(const char* toStore, int startAddr) {
int i = 0;
for (; i < LENGTH(toStore); i++) {
EEPROM.write(startAddr + i, toStore[i]);
}
EEPROM.write(startAddr + i, '\0');
EEPROM.commit();
}
String readStringFromFlash(int startAddr) {
char in[128]; // char array of size 128 for reading the stored data
int i = 0;
for (; i < 128; i++) {
in[i] = EEPROM.read(startAddr + i);
}
return String(in);
}
bool special_char = false; // detection of the end of array (0x00, "~"...)
int i = 0; // credentials[i]
char credentials_received[256] = ""; // final array sent to ESP32 (camera board)
String ssid;
String pass;
char* credential[2];
// The remote service we wish to connect to.
static BLEUUID serviceUUID("0000FFA0-0000-1000-8000-00805F9B34FB");
// The characteristic of the remote service we are interested in.
static BLEUUID charUUID("FAC1");
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static BLERemoteCharacteristic* pRemoteCharacteristic;
static BLEAdvertisedDevice* myDevice;
class MyClientCallback : public BLEClientCallbacks {
void onConnect(BLEClient* pclient) {
}
void onDisconnect(BLEClient* pclient) {
connected = false;
Serial.println("onDisconnect");
}
};
bool connectToServer() {
Serial.print("Forming a connection to ");
Serial.println(myDevice->getAddress().toString().c_str());
BLEClient* pClient = BLEDevice::createClient();
Serial.println(" - Created client");
pClient->setClientCallbacks(new MyClientCallback());
// Connect to the remove BLE Server.
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
Serial.println(" - Connected to server");
pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)
// Obtain a reference to the service we are after in the remote BLE server.
BLERemoteService* pRemoteService = pClient->getService(serviceUUID);
if (pRemoteService == nullptr) {
Serial.print("Failed to find our service UUID: ");
Serial.println(serviceUUID.toString().c_str());
pClient->disconnect();
return false;
}
Serial.println(" - Found our service");
// Obtain a reference to the characteristic in the service of the remote BLE server.
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
if (pRemoteCharacteristic == nullptr) {
Serial.print("Failed to find our characteristic UUID: ");
Serial.println(charUUID.toString().c_str());
pClient->disconnect();
return false;
}
connected = true;
return true;
}
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("BLE Advertised Device found: ");
Serial.println(advertisedDevice.toString().c_str());
// We have found a device, let us now see if it contains the service we are looking for.
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
BLEDevice::getScan()->stop();
myDevice = new BLEAdvertisedDevice(advertisedDevice);
doConnect = true;
doScan = true;
} // Found our server
} // onResult
}; // MyAdvertisedDeviceCallbacks
void startCameraServer();
void cameraConfig();
void setup() {
Serial.begin(115200);
Serial.setDebugOutput(true);
BLEDevice::init("");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
pBLEScan->setInterval(1349);
pBLEScan->setWindow(449);
pBLEScan->setActiveScan(true);
pBLEScan->start(5, false);
cameraConfig();
wifiConnect();
startCameraServer();
}
void loop() {
if (doConnect == true) {
if (connectToServer()) {
Serial.println("We are now connected to the BLE Server.");
} else {
Serial.println("We have failed to connect to the server; there is nothin more we will do.");
}
doConnect = false;
}
if (connected) {
if (WiFi.status() != WL_CONNECTED) {
wifiConnect();
}
} else if (doScan) {
BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
}
delay(500); // Delay a second between loops.
}
void credentials() {
std::string valueCredentials;
valueCredentials = pRemoteCharacteristic->readValue();
if (!special_char) {
for (int j = 0; j < valueCredentials.length(); j++) {
credentials_received[i] = valueCredentials[j]; // the second package will be stored starting from i = valueLength().
Serial.print(credentials_received[i]);
i++;
// Check if theres a special character in the data received
if (valueCredentials[j] == 0x00) {
special_char = true;
}
}
}
int credentials_length = i - 1; // i - 1 so it doesn't send the special character
for (int j = 0; j < credentials_length; j++) {
credentials_received[j] = credentials_received[j];
}
char* token = strtok(credentials_received, "$"); //split msg in parts(tokens) with strtok
int k = 0;
while (token != NULL) { //until null character is detected
credential[k++] = token; //credentials[0] = first token
token = strtok(NULL, "$"); //split if it detects "#"
}
ssid = credential[0];
pass = credential[1];
Serial.println();
Serial.print("SSID: ");
Serial.print(ssid);
Serial.print(" PSW: ");
Serial.println(pass);
}
void wifiConnect() {
if (!EEPROM.begin(EEPROM_SIZE)) { //Init EEPROM
Serial.println("failed to init EEPROM");
delay(1000);
} else {
ssid = readStringFromFlash(0); // Read SSID stored at address 0
Serial.println();
Serial.print("Read SSID = ");
Serial.println(ssid);
pass = readStringFromFlash(40); // Read Password stored at address 40
Serial.print("Read PSS = ");
Serial.println(pass);
}
Serial.println("+++++++++++++++++++++++++++ CONNECTING ++++++++++++++++++++++++++++++++++++++++++");
Serial.print(ssid);
Serial.print("/");
Serial.print(pass);
WiFi.begin(ssid.c_str(), pass.c_str());
delay(4000);
Serial.println("########################################################################################");
if (WiFi.status() != WL_CONNECTED) // if WiFi is not connected
{
//credentials();
Serial.print("Storing SSID:");
Serial.println(ssid);
Serial.print("Storing PSS:");
Serial.println(pass);
writeStringToFlash(ssid.c_str(), 0); // storing ssid at address 0
writeStringToFlash(pass.c_str(), 40); // storing pss at address 40
WiFi.begin(ssid.c_str(), pass.c_str());
special_char = false;
} else {
Serial.println("WiFi Connected");
Serial.println(WiFi.macAddress());
Serial.print("Camera Ready! Use 'http://");
Serial.print(WiFi.localIP());
Serial.println("' to connect");
special_char = false;
}
}
void cameraConfig() {
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.frame_size = FRAMESIZE_QVGA;
config.pixel_format = PIXFORMAT_JPEG; // for streaming
//config.pixel_format = PIXFORMAT_RGB565; // for face detection/recognition
//init with high specs to pre-allocate larger buffers
if (psramFound()) {
Serial.print("--------------------------------------------------------------------------PSRAM--------------------------------------------------------------------------------------------");
config.jpeg_quality = 15; //higher number will give a more fluent stream with less quality.
config.fb_count = 2;
} else {
Serial.println("----------------------------------------------------------------------------NO PSRAM------------------------------------------------------------------------------------------");
config.jpeg_quality = 20;
config.fb_count = 1;
config.fb_location = CAMERA_FB_IN_DRAM; // add 04/05/2022
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
return;
}
sensor_t* s = esp_camera_sensor_get();
//initial sensors are flipped vertically and colors are a bit saturated
if (s->id.PID == OV3660_PID) {
s->set_vflip(s, 1); //flip it back
s->set_brightness(s, 1); //up the blightness just a bit
s->set_saturation(s, -2); //lower the saturation
}
//drop down frame size for higher initial frame rate
s->set_framesize(s, FRAMESIZE_QVGA);
}