Question/Issue:
Hi, I am getting the following error when I try to deploy to the ESP32. My project is on audio classification of security-critical sounds. Please do get back as soon as possible as my project is due soon. Thanks!
Project ID:
1053549
Context/Use case:
[Provide context or use case where the issue is encountered]
Steps Taken:
- Downloaded model zip from Edge
- Revised the mic_continuous code for my project
- Attempted to upload/troubleshoot
Environment:
- Platform: ESP32-WROOM-32D
- Build Environment Details: Arduino IDE 2.3.10
- OS Version: Windows 11
- Custom Blocks / Impulse Configuration: MFE Impulse
Code:
#include <ArduinoJson.h>
#include <ArduinoJson.hpp>
// ββββββββββββββββ
// ESP32 Firmware β Network + WebSocket Server + Continuous Inference
// ββββββββββββββββ
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <AsyncTCP.h>
#include βtime.hβ
#include <ESPmDNS.h>
#include <esp_task_wdt.h>
#include <LittleFS.h>
#include βmbedtls/md.hβ
#include
#include <WiFiMulti.h>
#include βwifi_credentials.hβ
#include <ArduinoJson.h>
// ββ Edge Impulse Model + I2S ββββββββββββββββββββββββββββββββββββββββββββββββ
#define EIDSP_QUANTIZE_FILTERBANK 0
#define EI_CLASSIFIER_ALLOCATION_STATIC 1
#define EIDSP_USE_HW_FFT 0
#include <Audio_Classification_for_Security-Critical_Sounds_FYP__inferencing.h>
#include βfreertos/FreeRTOS.hβ
#include βfreertos/task.hβ
#include βdriver/i2s.hβ
// βββββββββββββ
// NETWORK / SERVER CONFIGURATION
// βββββββββββββββββ
static const char* const api_key = βMY_SECURE_KEY_123β;
const char* ntpServer = βpool.ntp.orgβ;
const long gmtOffset_sec = 28800; //Malaysia Time (UTC+8)
const int daylightOffset_sec = 0;
WiFiMulti wifiMulti;
AsyncWebServer server(80);
AsyncWebSocket ws(β/wsβ);
#define MAX_EVENTS 150
#define MAX_LOG_SIZE 51200
#define EVENT_COOLDOWN_MS 2000
#define FLUSH_INTERVAL_MS 15000
#define WIFI_CHECK_MS 10000
#define HEAP_MIN_BYTES 20000
#define WDT_TIMEOUT_MS 30000
#define REBOOT_COOLDOWN_MS 30000
char eventHistory[MAX_EVENTS][256];
int head = 0;
int lastSavedIndex = 0;
bool isFull = false;
char lastLabel[64] = ββ;
unsigned long lastEventTime = 0;
unsigned long lastWiFiCheck = 0;
unsigned long lastFlush = 0;
unsigned long lastReboot = 0;
std::set<uint32_t> authenticatedClients;
// ββββββββββββββ
// TIME HELPERS
// βββββββββββββββ
bool isTimeSynced(){
struct tm t;
return getLocalTime(&t) && t.tm_year > 120;
}
String getISOTime(){
struct tm t;
if (!getLocalTime(&t)) return β0000-00-00T00:00:00β;
char buf[25];
strftime(buf, sizeof(buf), β%Y-%m-%dT%H:%M:%Sβ, &t);
return String(buf);
}
// βββββββββββββββββ
// AUTH
// βββββββββββββββββββ
bool validateToken(const char* token){
if (!token) return false;
size_t len = strlen(api_key);
if (strlen(token) != len) return false;
uint8_t diff = 0;
for (size_t i = 0; i < len; i++) diff |= token[i] ^ api_key[i];
return diff == 0;
}
// ββββββββββββββββββββ
// EVENT LOGGING (LittleFS + circular buffer)
// βββββββββββββββββββββ
void checkLogSize(){
if (!LittleFS.exists(β/events.txtβ)) return;
File file = LittleFS.open(β/events.txtβ, βrβ);
size_t s = file.size();
file.close();
if (s > MAX_LOG_SIZE){
LittleFS.remove(β/events.txtβ);
Serial.println(βLog file exceeded limit. Cleared for rotation.β);
}
}
void flushPendingEvents(){
if (head == lastSavedIndex) return;
checkLogSize();
File file = LittleFS.open(β/events.txtβ, FILE_APPEND);
if (!file){
Serial.println(βFailed to open log for writingβ);
return;
}
while (lastSavedIndex != head){
file.println(eventHistory[lastSavedIndex]);
lastSavedIndex = (lastSavedIndex + 1) % MAX_EVENTS;
}
file.flush();
file.close();
Serial.println(βDisk sync complete.β);
}
void broadcastEvent(const char* label, float confidence){
if (!label || label[0] == β\0β) return;
unsigned long now = millis();
bool sameLabel = strcmp(label, lastLabel) == 0;
bool inCooldown = (now - lastEventTime) < EVENT_COOLDOWN_MS;
if (sameLabel && inCooldown) return;
strncpy(lastLabel, label, sizeof(lastLabel) - 1);
lastLabel[sizeof(lastLabel) - 1] = β\0β;
lastEventTime = now;
StaticJsonDocument<256> doc;
doc[βlabelβ] = label;
doc[βconfidenceβ] = serialized(String(confidence, 2));
doc[βtimestampβ] = getISOTime();
doc[βsyncedβ] = isTimeSynced();
char json[256];
if (serializeJson(doc, json, sizeof(json)) == 0){
Serial.println(βJSON serialize failed!β);
return;
}
for (auto clientId: authenticatedClients){
AsyncWebSocketClient* client = ws.client(clientId);
if (client && client->canSend()) client->text(json);
}
strncpy(eventHistory[head],json, sizeof(eventHistory[head])-1);
eventHistory[head][sizeof(eventHistory[head])-1] = β\0β;
int nextHead = (head + 1) % MAX_EVENTS;
if (nextHead == lastSavedIndex){
flushPendingEvents();
}
head = nextHead;
if (head == 0) isFull = true;
}
// βββββββββββββββββββββ
// WEBSOCKET EVENT HANDLER
// ββββββββββββββββββββββ
void onWsEvent(AsyncWebSocket* srv, AsyncWebSocketClient* client, AwsEventType type, void* arg, uint8_t* data, size_t len){
if (type == WS_EVT_CONNECT){
Serial.printf(β[WS] Client connected: %u\nβ, client->id());
client->text(β{"status":"auth_required"}β);
} else if (type == WS_EVT_DISCONNECT){
authenticatedClients.erase(client->id());
Serial.printf(β[WS] Client disconnected: %u\nβ, client->id());
} else if (type == WS_EVT_DATA){
AwsFrameInfo* info = (AwsFrameInfo*)arg;
if (info->opcode == WS_TEXT){
data[len] = β\0β;
StaticJsonDocument<128> msg;
if (deserializeJson(msg, data) == DeserializationError::Ok){
if (msg.containsKey("auth")){
if (validateToken(msg["auth"])){
authenticatedClients.insert(client->id());
client->text("{\"status\":\"authenticated\"}");
Serial.printf("[WS] Client %u authenticated\n", client->id());
} else {
client->text("{\"status\":\"unauthorized\"}");
client->close(4001, "Invalid Token");
}
}
if (msg.containsKey("ping")){
client->text("{\"pong\":true}");
}
}
}
}
}
// βββββββββββββββββββββ
// WIFI
// βββββββββββββββββββββ
void setupWiFiMulti() {
wifiMulti.addAP(HOME_WIFI_SSID, HOME_WIFI_PASSWORD);
wifiMulti.addAP(UNI_WIFI_SSID, UNI_WIFI_PASSWORD);
}
void connectWiFi(){
if (WiFi.status() == WL_CONNECTED) return;
Serial.println(β[WiFi] Connectingβ¦β);
int attempts = 0;
while (wifiMulti.run() != WL_CONNECTED && attempts++ < 20) {
delay(500);
Serial.print(β.β);
}
if (WiFi.status() == WL_CONNECTED){
Serial.println(β\n[WiFi] Connected: " + WiFi.localIP().toString());
} else {
Serial.println(β\n[WiFi] Failed β will retry in loop");
}
}
// ββββββββββββββββββββ
// AUDIO INFERENCE β Edge Impulse Continuous Classification
// βββββββββββββββββββββ
typedef struct {
signed short *buffers[2];
unsigned char buf_select;
unsigned char buf_ready;
unsigned int buf_count;
unsigned int n_samples;
} inference_t;
static inference_t inference;
static const uint32_t sample_buffer_size = 1024;
static signed short sampleBuffer[sample_buffer_size];
static bool debug_nn = false;
static int print_results = -(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW);
static bool record_status = true;
// Confidence threshold
#define INFERENCE_CONFIDENCE_THRESHOLD 0.52f
static void audio_inference_callback(uint32_t n_bytes)
{
for(int i = 0; i < n_bytes>>1; i++) {
inference.buffers[inference.buf_select][inference.buf_count++] = sampleBuffer[i];
if(inference.buf_count >= inference.n_samples) {
inference.buf_select ^= 1;
inference.buf_count = 0;
inference.buf_ready = 1;
}
}
}
static void capture_samples(void* arg) {
const int32_t i2s_bytes_to_read = (uint32_t)arg;
size_t bytes_read = i2s_bytes_to_read;
while (record_status) {
i2s_read((i2s_port_t)1, (void*)sampleBuffer, i2s_bytes_to_read, &bytes_read, 100);
if (bytes_read <= 0) {
ei_printf("Error in I2S read : %d", bytes_read);
}
else {
if (bytes_read < i2s_bytes_to_read) {
ei_printf("Partial I2S read");
}
// Scale the data (otherwise the sound is too quiet)
for (int x = 0; x < i2s_bytes_to_read/2; x++) {
sampleBuffer[x] = (int16_t)(sampleBuffer[x]) * 8;
}
if (record_status) {
audio_inference_callback(i2s_bytes_to_read);
}
else {
break;
}
}
}
vTaskDelete(NULL);
}
static int i2s_init(uint32_t sampling_rate) {
i2s_config_t i2s_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_TX),
.sample_rate = sampling_rate,
.bits_per_sample = (i2s_bits_per_sample_t)16,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S,
.intr_alloc_flags = 0,
.dma_buf_count = 8,
.dma_buf_len = 512,
.use_apll = false,
.tx_desc_auto_clear = false,
.fixed_mclk = -1,
};
i2s_pin_config_t pin_config = {
.bck_io_num = 26, // IIS_SCLK
.ws_io_num = 22, // IIS_LCLK
.data_out_num = -1, // IIS_DSIN
.data_in_num = 21, // IIS_DOUT
};
esp_err_t ret = 0;
ret = i2s_driver_install((i2s_port_t)1, &i2s_config, 0, NULL);
if (ret != ESP_OK) ei_printf(βError in i2s_driver_installβ);
ret = i2s_set_pin((i2s_port_t)1, &pin_config);
if (ret != ESP_OK) ei_printf(βError in i2s_set_pinβ);
ret = i2s_zero_dma_buffer((i2s_port_t)1);
if (ret != ESP_OK) ei_printf(βError in initializing dma buffer with 0β);
return int(ret);
}
static int i2s_deinit(void) {
i2s_driver_uninstall((i2s_port_t)1);
return 0;
}
static bool microphone_inference_start(uint32_t n_samples)
{
inference.buffers[0] = (signed short *)malloc(n_samples * sizeof(signed short));
if (inference.buffers[0] == NULL) return false;
inference.buffers[1] = (signed short *)malloc(n_samples * sizeof(signed short));
if (inference.buffers[1] == NULL) {
ei_free(inference.buffers[0]);
return false;
}
inference.buf_select = 0;
inference.buf_count = 0;
inference.n_samples = n_samples;
inference.buf_ready = 0;
if (i2s_init(EI_CLASSIFIER_FREQUENCY)) {
ei_printf("Failed to start I2S!");
}
ei_sleep(100);
record_status = true;
xTaskCreate(capture_samples, "CaptureSamples", 1024 * 32, (void*)sample_buffer_size, 10, NULL);
return true;
}
static bool microphone_inference_record(void)
{
bool ret = true;
if (inference.buf_ready == 1) {
ei_printf(
"Error sample buffer overrun. Decrease the number of slices per model window "
"(EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)\n");
ret = false;
}
while (inference.buf_ready == 0) {
delay(1);
}
inference.buf_ready = 0;
return true;
}
static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr)
{
numpy::int16_to_float(&inference.buffers[inference.buf_select ^ 1][offset], out_ptr, length);
return 0;
}
static void microphone_inference_end(void)
{
i2s_deinit();
ei_free(inference.buffers[0]);
ei_free(inference.buffers[1]);
}
// Runs one continuous-inference slice and, if a confident prediction is found,
// feeds it into the existing network/logging pipeline via broadcastEvent().
void runInferenceSlice()
{
bool m = microphone_inference_record();
if (!m) {
ei_printf(βERR: Failed to record audioβ¦\nβ);
return;
}
signal_t signal;
signal.total_length = EI_CLASSIFIER_SLICE_SIZE;
signal.get_data = µphone_audio_signal_get_data;
ei_impulse_result_t result = {0};
EI_IMPULSE_ERROR r = run_classifier_continuous(&signal, &result, debug_nn);
if (r != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", r);
return;
}
if (++print_results >= (EI_CLASSIFIER_SLICES_PER_MODEL_WINDOW)) {
// Find the top predicted label for this completed window
float maxVal = 0;
const char* topLabel = "";
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
if (result.classification[ix].value > maxVal) {
maxVal = result.classification[ix].value;
topLabel = result.classification[ix].label;
}
}
ei_printf("Predictions (DSP: %d ms., Classification: %d ms.): \n",
result.timing.dsp, result.timing.classification);
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
ei_printf(" %s: ", result.classification[ix].label);
ei_printf_float(result.classification[ix].value);
ei_printf("\n");
}
// Feed into existing network/logging pipeline
if (maxVal >= INFERENCE_CONFIDENCE_THRESHOLD) {
broadcastEvent(topLabel, maxVal);
}
print_results = 0;
}
}
// ββββββββββββββββββββββ
// SETUP
// ββββββββββββββββββββ
void setup() {
Serial.begin(115200);
// ββ Edge Impulse Inference Init (FIRST β before any network allocations) ββ
Serial.printf(βFree heap at boot: %d bytes\nβ, ESP.getFreeHeap());
ei_printf(βInferencing settings:\nβ);
ei_printf(β\tInterval: β);
ei_printf_float((float)EI_CLASSIFIER_INTERVAL_MS);
ei_printf(β ms.\nβ);
ei_printf(β\tFrame size: %d\nβ, EI_CLASSIFIER_DSP_INPUT_FRAME_SIZE);
ei_printf(β\tSample length: %d ms.\nβ, EI_CLASSIFIER_RAW_SAMPLE_COUNT / 16);
run_classifier_init();
Serial.printf(βFree heap after classifier init: %d bytes\nβ, ESP.getFreeHeap());
ei_printf(β\nStarting continuous inference in 2 secondsβ¦\nβ);
ei_sleep(2000);
if (microphone_inference_start(EI_CLASSIFIER_SLICE_SIZE) == false) {
ei_printf(βERR: Could not allocate audio buffer (size %d)\r\nβ, EI_CLASSIFIER_RAW_SAMPLE_COUNT);
// Donβt return here β let networking still come up so you can debug remotely
}
Serial.printf(βFree heap after mic start: %d bytes\nβ, ESP.getFreeHeap());
ei_printf(βRecordingβ¦\nβ);
// ββ Storage
if (!LittleFS.begin(true)) Serial.println(βLittleFS mount failedβ);
// ββ WiFi βββββββββββββ
WiFi.mode(WIFI_STA);
WiFi.setAutoReconnect(true);
setupWiFiMulti();
connectWiFi();
Serial.printf(βFree heap after WiFi: %d bytes\nβ, ESP.getFreeHeap());
// ββ Time Sync βββββββββββ
if (WiFi.status() == WL_CONNECTED){
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
struct tm t;
int attempts = 0;
Serial.print(βSyncing NTPβ);
while (!getLocalTime(&t) && attempts++ < 20){
delay(500);
Serial.print(β.β);
}
Serial.println(isTimeSynced() ? "\n[NTP] Synced: " + getISOTime() : β\n[NTP] Timeout - using internal clockβ);
}
if(MDNS.begin(βesp32β)) Serial.println(βmDNS esp32.local activeβ);
// ββ WebSocket + HTTP Server βββ
ws.onEvent(onWsEvent);
server.addHandler(&ws);
server.on(β/healthβ, HTTP_GET, [](AsyncWebServerRequest * request){
StaticJsonDocument<128> healthDoc;
healthDoc[βstatusβ] = βokβ;
healthDoc[βuptime_sβ] = millis() / 1000;
healthDoc[βfree_heapβ] = ESP.getFreeHeap();
healthDoc[βwifi_rssiβ] = WiFi.RSSI();
healthDoc[βntp_syncedβ] = isTimeSynced();
String r;
serializeJson(healthDoc, r);
request->send(200, βapplication/jsonβ, r);
});
server.on(β/eventsβ, HTTP_GET, [](AsyncWebServerRequest * request){
if (!request->hasHeader(βX-API-KEYβ) || request->getHeader(βX-API-KEYβ)->value() != api_key){
request->send(403, βapplication/jsonβ, β{"error":"unauthorized"}β);
return;
}
AsyncResponseStream * response = request->beginResponseStream(βapplication/jsonβ);
response->print(β[β);
int start = isFull ? head : 0;
int count = isFull ? MAX_EVENTS : head;
for(int i = 0; i < count; i++){
response->print(eventHistory[(start + i) % MAX_EVENTS]);
if (i < count - 1) response->print(β,β);
if(i % 15 == 0) yield();
}
response->print(β]β);
request->send(response);
});
server.on(β/rebootβ, HTTP_POST, [](AsyncWebServerRequest* request) {
if (!request->hasHeader(βX-API-KEYβ) ||
request->getHeader(βX-API-KEYβ)->value() != api_key) {
request->send(403, βapplication/jsonβ, β{"error":"unauthorized"}β);
return;
}
if (millis() - lastReboot < REBOOT_COOLDOWN_MS) {
request->send(429, βapplication/jsonβ, β{"error":"rate_limited"}β);
return;
}
lastReboot = millis();
request->send(200, βapplication/jsonβ, β{"status":"rebooting"}β);
delay(500);
ESP.restart();
});
server.begin();
Serial.println(β[HTTP] Server startedβ);
Serial.printf(βFree heap after server start: %d bytes\nβ, ESP.getFreeHeap());
// ββ Watchdog ββββββββββββββββββ
esp_task_wdt_config_t twdt_config = {
.timeout_ms = WDT_TIMEOUT_MS,
.idle_core_mask = 0,
.trigger_panic = true
};
esp_task_wdt_reconfigure(&twdt_config);
esp_task_wdt_add(NULL);
}
// ββββββββββββββββββββ
// LOOP
// βββββββββββββββββββββ
void loop() {
esp_task_wdt_reset();
ws.cleanupClients();
if (millis() - lastWiFiCheck > WIFI_CHECK_MS) {
lastWiFiCheck = millis();
if (WiFi.status() != WL_CONNECTED) {
Serial.println(β[WiFi] Lost β reconnectingβ);
wifiMulti.run();
}
}
if (millis() - lastFlush > FLUSH_INTERVAL_MS) {
flushPendingEvents();
lastFlush = millis();
}
if (ESP.getFreeHeap() < HEAP_MIN_BYTES) {
Serial.println(β[SYS] Critical heap β flushing and restartingβ);
flushPendingEvents();
delay(500);
ESP.restart();
}
// ββ Run one inference slice per loop iteration ββββββββββββββββββββββββββ
runInferenceSlice();
}
#if !defined(EI_CLASSIFIER_SENSOR) || EI_CLASSIFIER_SENSOR != EI_CLASSIFIER_SENSOR_MICROPHONE
#error βInvalid model for current sensor.β
#endif
