Hello there everyone,
I have a trained model for audio classification, which works woderfully by itself.
And then I have an I2C set-up between the Nicla Sense ME(master) and Arduino Nano 33 Ble Sense(or Seeed Xiao Sense, tried with both, as slave). Which also works alone.
Here is the code for the I2C working between the 2 devices, a very simple i2c communication:
Master:
#include <Arduino.h>
#include "Nicla_System.h"
#include <Wire.h>
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600);
}
void loop() {
while (Serial.available() > 0) {
char incomingCharacter = Serial.read();
if (incomingCharacter == '1') {
Wire.beginTransmission(8); // transmit to device #8
Wire.write(5); // sends one byte
Wire.endTransmission(); // stop transmitting
delay(100);
}
}
}
And here for the slave:
#include <Wire.h>
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
}
void loop() {
}
// function that executes whenever data is received from master
void receiveEvent(int howMany) {
while (Wire.available()){
int x = Wire.read(); // receive byte as an integer
if (x == 5){
Serial.println("Hello World!");
}
}
}
Again, all of this code works.
Then my trained model had the void loop() edited so that it starts the recording and outputs the highest accuracy label and its accuracy, only after I send a 1 in the serial monitor (I am running record+classify, not continous):
while (Serial.available() > 0) {
char incomingCharacter = Serial.read();
if (incomingCharacter == '1') {
ei_printf("Starting...\n");
delay(350);
ei_printf("Recording...\n");
bool m = microphone_inference_record();
if (!m) {
ei_printf("ERR: Failed to record audio...\n");
return;
}
ei_printf("Recording done\n");
signal_t signal;
signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT;
signal.get_data = µphone_audio_signal_get_data;
ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn);
if (r != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", r);
return;
}
// print the predictions
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
if (result.classification[ix].value > 0.5) {
Serial.println("Highest Prediction Classifier is: " + String(result.classification[ix].label) + "\nAccuracy is: " + String(result.classification[ix].value, 2));
}
}
}
}
}
This code works wonderfully as well. Nothing else, with the exception of the shown void loop has been changed.
Then if I try to move to I2C, I have the master the same, but the slave with 2 different methods, like this:
void setup()
{
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent); // register event
Serial.begin(115200); // start serial for output
}
void loop()
{
}
void receiveEvent(int howMany) {
while (Wire.available()) {
int x = Wire.read(); // receive byte as an integer
Serial.println(x);
if (x == 5) {
ei_printf("Starting...\n");
delay(350);
ei_printf("Recording...\n");
bool m = microphone_inference_record();
if (!m) {
ei_printf("ERR: Failed to record audio...\n");
return;
}
ei_printf("Recording done\n");
signal_t signal;
signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT;
signal.get_data = µphone_audio_signal_get_data;
ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn);
if (r != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", r);
return;
}
// print the predictions
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
if (result.classification[ix].value > 0.5) {
Serial.println("Highest Prediction Classifier is: " + String(result.classification[ix].label) + "\nAccuracy is: " + String(result.classification[ix].value, 2));
//Wire.write(int(ix));
}
}
}
}
}
void requestEvent() {
Wire.write(label); // respond with message of 1 byte
}
And also with the following method:
void setup()
{
Wire.begin(8); // join i2c bus with address #8
Serial.begin(115200); // start serial for output
}
void loop()
{
while (Wire.available()) {
int x = Wire.read(); // receive byte as an integer
Serial.println(x);
if (x == 5) {
ei_printf("Starting...\n");
delay(350);
ei_printf("Recording...\n");
bool m = microphone_inference_record();
if (!m) {
ei_printf("ERR: Failed to record audio...\n");
return;
}
ei_printf("Recording done\n");
signal_t signal;
signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT;
signal.get_data = µphone_audio_signal_get_data;
ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn);
if (r != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", r);
return;
}
// print the predictions
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
if (result.classification[ix].value > 0.5) {
Serial.println("Highest Prediction Classifier is: " + String(result.classification[ix].label) + "\nAccuracy is: " + String(result.classification[ix].value, 2));
Wire.write(int(ix));
}
}
}
}
}
Neither work, it receives the byte, prints it out, prints that it starts recording and it seems to stop when it needs to start the recording:
Here is the full code:
#define EIDSP_QUANTIZE_FILTERBANK 0
/* Includes ---------------------------------------------------------------- */
#include <PDM.h>
#include <Wire.h>
#include <a1210_samples_cnn_voice_rec_inferencing.h>
/** Audio buffers, pointers and selectors */
typedef struct {
int16_t *buffer;
uint8_t buf_ready;
uint32_t buf_count;
uint32_t n_samples;
} inference_t;
static inference_t inference;
static signed short sampleBuffer[2048];
static bool debug_nn = false; // Set this to true to see e.g. features generated from the raw signal
int label = 0;
/**
@brief Arduino setup function
*/
void setup()
{
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent); // register event
Serial.begin(115200); // start serial for output
}
/**
@brief Arduino main function. Runs the inferencing loop.
*/
void loop()
{
}
void receiveEvent(int howMany) {
while (Wire.available()) {
int inByte = Wire.read(); // receive byte as an integer
Serial.println(inByte);
if (inByte == 5) {
ei_printf("Starting...\n");
delay(350);
ei_printf("Recording...\n");
bool m = microphone_inference_record();
if (!m) {
ei_printf("ERR: Failed to record audio...\n");
return;
}
ei_printf("Recording done\n");
signal_t signal;
signal.total_length = EI_CLASSIFIER_RAW_SAMPLE_COUNT;
signal.get_data = µphone_audio_signal_get_data;
ei_impulse_result_t result = { 0 };
EI_IMPULSE_ERROR r = run_classifier(&signal, &result, debug_nn);
if (r != EI_IMPULSE_OK) {
ei_printf("ERR: Failed to run classifier (%d)\n", r);
return;
}
// print the predictions
for (size_t ix = 0; ix < EI_CLASSIFIER_LABEL_COUNT; ix++) {
if (result.classification[ix].value > 0.5) {
Serial.println("Highest Prediction Classifier is: " + String(result.classification[ix].label) + "\nAccuracy is: " + String(result.classification[ix].value, 2));
label = int(ix);
}
}
}
}
}
void requestEvent() {
Wire.write(label);
}
/**
@brief PDM buffer full callback
Get data and call audio thread callback
*/
static void pdm_data_ready_inference_callback(void)
{
int bytesAvailable = PDM.available();
// read into the sample buffer
int bytesRead = PDM.read((char *)&sampleBuffer[0], bytesAvailable);
if (inference.buf_ready == 0) {
for (int i = 0; i < bytesRead >> 1; i++) {
inference.buffer[inference.buf_count++] = sampleBuffer[i];
if (inference.buf_count >= inference.n_samples) {
inference.buf_count = 0;
inference.buf_ready = 1;
break;
}
}
}
}
/**
@brief Init inferencing struct and setup/start PDM
@param[in] n_samples The n samples
@return { description_of_the_return_value }
*/
static bool microphone_inference_start(uint32_t n_samples)
{
inference.buffer = (int16_t *)malloc(n_samples * sizeof(int16_t));
if (inference.buffer == NULL) {
return false;
}
inference.buf_count = 0;
inference.n_samples = n_samples;
inference.buf_ready = 0;
// configure the data receive callback
PDM.onReceive(&pdm_data_ready_inference_callback);
PDM.setBufferSize(4096);
// initialize PDM with:
// - one channel (mono mode)
// - a 16 kHz sample rate
if (!PDM.begin(1, EI_CLASSIFIER_FREQUENCY)) {
ei_printf("Failed to start PDM!");
microphone_inference_end();
return false;
}
// set the gain, defaults to 20
PDM.setGain(127);
return true;
}
/**
@brief Wait on new data
@return True when finished
*/
static bool microphone_inference_record(void)
{
inference.buf_ready = 0;
inference.buf_count = 0;
while (inference.buf_ready == 0) {
delay(10);
}
return true;
}
/**
Get raw audio signal data
*/
static int microphone_audio_signal_get_data(size_t offset, size_t length, float *out_ptr)
{
numpy::int16_to_float(&inference.buffer[offset], out_ptr, length);
return 0;
}
/**
@brief Stop PDM and release buffers
*/
static void microphone_inference_end(void)
{
PDM.end();
free(inference.buffer);
}
What makes it to be stuck is the while loop in the following function, more specifically the while loop:
static bool microphone_inference_record(void)
{
inference.buf_ready = 0;
inference.buf_count = 0;
while (inference.buf_ready == 0) {
delay(10);
}
return true;
}
I have written on the Edge Impulse forums as well, and I was pointed towards interrupting the I2C communication before the while happens, or using different mbed threads. As well as asking here as it seems to be an I2C issue.
I have tried using the Wire.end() and Wire.begin(8); with different methods (with end before the while loop and begin after). but nothing seems to work.
Meanwhile in regards to multiple Mbed threads, I have 0 experience on how I can do that.
I have tried 2 Arduino Nano 33 BLE Sense, 3 Seeed Xiao Sense, all have the same behaviour.
I also tried adding 4.7k pull-up resistors on the SDA/SCL pins (Although there are internal pull-up resistors already and the code without the voice recognition does work).
I would really appreaciate any help or ideas.
