MLX90640 Sensor Fails After 3-5 Minutes When Using ArduinoBLE Library

Hello everyone, I am currently working on a project using an Arduino equipped with an MLX90640 sensor to transmit temperature images via Bluetooth Low Energy (BLE). When I use only the MLX90640 library from Adafruit, the program functions correctly and displays the temperature image as expected. However, when I include the ArduinoBLE library, the sensor operates for just 3 to 5 minutes before it fails. Additionally, I've noticed that the dynamic memory usage spikes to 70% when both libraries are included. Here is the code I'm working with Best regards,
Solution:
Hey @aymen ammari first signs are pointing towards memory fragmentation degrading system performance. When memory is not managed well, your Arduino might fail in a variety of ways. Sometimes, this can be as obvious as the code not uploading to your board like shown above. In other cases, everything might appear to run fine for awhile, only for the microcontroller to stop responding later on – that’s much more tricky! Looking at your code you have many opportunities for optimizing dynamic memory usage. Mostly by moving things on flash, you will take some hit on performance. You can rewrite the sendLargeDataOverBLE function to cap memory usage. Here are some improvements you can make: ...
Jump to solution
4 Replies
aymen ammari
aymen ammariOP4mo ago
#include <Adafruit_MLX90640.h>
#include <ArduinoBLE.h>
Adafruit_MLX90640 mlx;
float frame[32*24]; // buffer for full frame of temperatures
uint8_t frame_ble[32*24];

// Définir les UUIDs des services et caractéristiques
const char * deviceServiceUuid = "1523";
const char * deviceServiceRequestCharacteristicUuid = "1526";
const char * deviceServiceResponseCharacteristicUuid = "1527";

BLEService ArduinoMLXService(deviceServiceUuid);
BLECharacteristic ArduinoMLXRequestCharacteristic(deviceServiceRequestCharacteristicUuid, BLEWrite, 4);
BLECharacteristic ArduinoMLXResponseCharacteristic(deviceServiceResponseCharacteristicUuid, BLENotify, 768);

void setup() {
while (!Serial) delay(10);
Serial.begin(115200);
delay(100);

Serial.println("Adafruit MLX90640 Simple Test");
if (! mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
Serial.println("MLX90640 not found!");
while (1) delay(10);
}
Serial.println("Found Adafruit MLX90640");

Serial.print("Serial number: ");
Serial.print(mlx.serialNumber[0], HEX);
Serial.print(mlx.serialNumber[1], HEX);
Serial.println(mlx.serialNumber[2], HEX);

//mlx.setMode(MLX90640_INTERLEAVED);
mlx.setMode(MLX90640_CHESS);
Serial.print("Current mode: ");
if (mlx.getMode() == MLX90640_CHESS) {
Serial.println("Chess");
} else {
Serial.println("Interleave");
}

mlx.setResolution(MLX90640_ADC_18BIT);
Serial.print("Current resolution: ");
mlx90640_resolution_t res = mlx.getResolution();
#include <Adafruit_MLX90640.h>
#include <ArduinoBLE.h>
Adafruit_MLX90640 mlx;
float frame[32*24]; // buffer for full frame of temperatures
uint8_t frame_ble[32*24];

// Définir les UUIDs des services et caractéristiques
const char * deviceServiceUuid = "1523";
const char * deviceServiceRequestCharacteristicUuid = "1526";
const char * deviceServiceResponseCharacteristicUuid = "1527";

BLEService ArduinoMLXService(deviceServiceUuid);
BLECharacteristic ArduinoMLXRequestCharacteristic(deviceServiceRequestCharacteristicUuid, BLEWrite, 4);
BLECharacteristic ArduinoMLXResponseCharacteristic(deviceServiceResponseCharacteristicUuid, BLENotify, 768);

void setup() {
while (!Serial) delay(10);
Serial.begin(115200);
delay(100);

Serial.println("Adafruit MLX90640 Simple Test");
if (! mlx.begin(MLX90640_I2CADDR_DEFAULT, &Wire)) {
Serial.println("MLX90640 not found!");
while (1) delay(10);
}
Serial.println("Found Adafruit MLX90640");

Serial.print("Serial number: ");
Serial.print(mlx.serialNumber[0], HEX);
Serial.print(mlx.serialNumber[1], HEX);
Serial.println(mlx.serialNumber[2], HEX);

//mlx.setMode(MLX90640_INTERLEAVED);
mlx.setMode(MLX90640_CHESS);
Serial.print("Current mode: ");
if (mlx.getMode() == MLX90640_CHESS) {
Serial.println("Chess");
} else {
Serial.println("Interleave");
}

mlx.setResolution(MLX90640_ADC_18BIT);
Serial.print("Current resolution: ");
mlx90640_resolution_t res = mlx.getResolution();
`
switch (res) {
case MLX90640_ADC_16BIT: Serial.println("16 bit"); break;
case MLX90640_ADC_17BIT: Serial.println("17 bit"); break;
case MLX90640_ADC_18BIT: Serial.println("18 bit"); break;
case MLX90640_ADC_19BIT: Serial.println("19 bit"); break;
}

mlx.setRefreshRate(MLX90640_2_HZ);
Serial.print("Current frame rate: ");
mlx90640_refreshrate_t rate = mlx.getRefreshRate();
switch (rate) {
case MLX90640_0_5_HZ: Serial.println("0.5 Hz"); break;
case MLX90640_1_HZ: Serial.println("1 Hz"); break;
case MLX90640_2_HZ: Serial.println("2 Hz"); break;
case MLX90640_4_HZ: Serial.println("4 Hz"); break;
case MLX90640_8_HZ: Serial.println("8 Hz"); break;
case MLX90640_16_HZ: Serial.println("16 Hz"); break;
case MLX90640_32_HZ: Serial.println("32 Hz"); break;
case MLX90640_64_HZ: Serial.println("64 Hz"); break;
}

/* Initialiser BLE */
BLE.setDeviceName("ArduinoMLX");
BLE.setLocalName("ArduinoMLX");

if (!BLE.begin()) {
Serial.println(F("- Starting Bluetooth® Low Energy module failed!"));
while (1);
}

BLE.setAdvertisedService(ArduinoMLXService);
ArduinoMLXService.addCharacteristic(ArduinoMLXRequestCharacteristic);
ArduinoMLXService.addCharacteristic(ArduinoMLXResponseCharacteristic);
BLE.addService(ArduinoMLXService);
ArduinoMLXResponseCharacteristic.writeValue((uint8_t*)"0", 1);

BLE.advertise();

Serial.println(F("Arduino R4 WiFi BLE (Peripheral Device)"));
Serial.println(" ");

}


switch (res) {
case MLX90640_ADC_16BIT: Serial.println("16 bit"); break;
case MLX90640_ADC_17BIT: Serial.println("17 bit"); break;
case MLX90640_ADC_18BIT: Serial.println("18 bit"); break;
case MLX90640_ADC_19BIT: Serial.println("19 bit"); break;
}

mlx.setRefreshRate(MLX90640_2_HZ);
Serial.print("Current frame rate: ");
mlx90640_refreshrate_t rate = mlx.getRefreshRate();
switch (rate) {
case MLX90640_0_5_HZ: Serial.println("0.5 Hz"); break;
case MLX90640_1_HZ: Serial.println("1 Hz"); break;
case MLX90640_2_HZ: Serial.println("2 Hz"); break;
case MLX90640_4_HZ: Serial.println("4 Hz"); break;
case MLX90640_8_HZ: Serial.println("8 Hz"); break;
case MLX90640_16_HZ: Serial.println("16 Hz"); break;
case MLX90640_32_HZ: Serial.println("32 Hz"); break;
case MLX90640_64_HZ: Serial.println("64 Hz"); break;
}

/* Initialiser BLE */
BLE.setDeviceName("ArduinoMLX");
BLE.setLocalName("ArduinoMLX");

if (!BLE.begin()) {
Serial.println(F("- Starting Bluetooth® Low Energy module failed!"));
while (1);
}

BLE.setAdvertisedService(ArduinoMLXService);
ArduinoMLXService.addCharacteristic(ArduinoMLXRequestCharacteristic);
ArduinoMLXService.addCharacteristic(ArduinoMLXResponseCharacteristic);
BLE.addService(ArduinoMLXService);
ArduinoMLXResponseCharacteristic.writeValue((uint8_t*)"0", 1);

BLE.advertise();

Serial.println(F("Arduino R4 WiFi BLE (Peripheral Device)"));
Serial.println(" ");

}


`
aymen ammari
aymen ammari4mo ago
void loop() {
BLEDevice central = BLE.central();
Serial.println("- Discovering central device...");

if (central) {
Serial.println("* Connected to central device!");
Serial.print("* Device MAC address: ");
Serial.println(central.address());
Serial.println(" ");

while (central.connected()) {
delay(500);
if (mlx.getFrame(frame) != 0) {
Serial.println("Failed");
return;
}
Serial.println();
for (uint8_t h=0; h<24; h++) {
for (uint8_t w=0; w<32; w++) {
// float t = frame[h*32 + w];
frame_ble[h*32 + w] = frame[h*32 + w];
Serial.print(frame_ble[h*32 + w]);
Serial.print(", ");
}
Serial.println();
}
sendLargeDataOverBLE();
// delay(1000);
}
Serial.println("* Disconnected to central device!");
}

}
void sendLargeDataOverBLE() {
for (int i = 0; i < sizeof(frame_ble); i += 20) {
// Create a buffer for the chunk
uint8_t chunk[20];
int chunkSize = min(20, sizeof(frame_ble) - i);

// Copy the chunk from the large data array
memcpy(chunk, frame_ble + i, chunkSize);

// Send the chunk
ArduinoMLXResponseCharacteristic.writeValue(chunk, chunkSize);

// Small delay to ensure the BLE stack can handle the next packet
delay(10);
}
}
void loop() {
BLEDevice central = BLE.central();
Serial.println("- Discovering central device...");

if (central) {
Serial.println("* Connected to central device!");
Serial.print("* Device MAC address: ");
Serial.println(central.address());
Serial.println(" ");

while (central.connected()) {
delay(500);
if (mlx.getFrame(frame) != 0) {
Serial.println("Failed");
return;
}
Serial.println();
for (uint8_t h=0; h<24; h++) {
for (uint8_t w=0; w<32; w++) {
// float t = frame[h*32 + w];
frame_ble[h*32 + w] = frame[h*32 + w];
Serial.print(frame_ble[h*32 + w]);
Serial.print(", ");
}
Serial.println();
}
sendLargeDataOverBLE();
// delay(1000);
}
Serial.println("* Disconnected to central device!");
}

}
void sendLargeDataOverBLE() {
for (int i = 0; i < sizeof(frame_ble); i += 20) {
// Create a buffer for the chunk
uint8_t chunk[20];
int chunkSize = min(20, sizeof(frame_ble) - i);

// Copy the chunk from the large data array
memcpy(chunk, frame_ble + i, chunkSize);

// Send the chunk
ArduinoMLXResponseCharacteristic.writeValue(chunk, chunkSize);

// Small delay to ensure the BLE stack can handle the next packet
delay(10);
}
}
`
Solution
32bitSaviour
32bitSaviour4mo ago
Hey @aymen ammari first signs are pointing towards memory fragmentation degrading system performance. When memory is not managed well, your Arduino might fail in a variety of ways. Sometimes, this can be as obvious as the code not uploading to your board like shown above. In other cases, everything might appear to run fine for awhile, only for the microcontroller to stop responding later on – that’s much more tricky! Looking at your code you have many opportunities for optimizing dynamic memory usage. Mostly by moving things on flash, you will take some hit on performance. You can rewrite the sendLargeDataOverBLE function to cap memory usage. Here are some improvements you can make: - First avoid declaration of arrays in a loop whenever you can. - You can pass pass chunks of your frame_ble without copying into declaring an array and copying into it. Since you already have the data in memory, you can using some pointer additions to pass the right offset into the array and its size.
32bitSaviour
32bitSaviour4mo ago
Other concerns that you need to pay attention to are: - Serial.print() and Serial.println functions use SRAM, for string literals can be used if F() string wrapper. The wrapper will move those strings to flash memory. - Your global variables also take up some amount of SRAM. The global contants can be pushed to flash. If you are on an avr-based arduino board, you can use PROGMEM. For example you can declare your global constants as:
const PROGMEM deviceServiceUuid[] = {"1523"};
Remember once a variable is declared using PROGMEM it cannot be read as a regular variable you will have to read it using specific functions defined inside pgmspace.h. It stores data on flash like the string wrapper F() that we saw earlier. However you need to use this judiciously since read speed to from the flash are horrible compared to RAM. So your code design needs to reflect which variables are crucial and those that aren’t. On ARM-based arduino boards the same can be achieved using static const
static const int Variable = Data;
You can also use the following functions to monitor your RAM usage: For ARM-based arduino
extern "C" char* sbrk(int incr);

void display_freeram(){
Serial.print(F("- SRAM left: "));
Serial.println(freeRam());
}

int freeRam() {
char top;
return &top - reinterpret_cast<char*>(sbrk(0));
}
extern "C" char* sbrk(int incr);

void display_freeram(){
Serial.print(F("- SRAM left: "));
Serial.println(freeRam());
}

int freeRam() {
char top;
return &top - reinterpret_cast<char*>(sbrk(0));
}
For AVR-based arduino
void display_freeram() {
Serial.print(F("- SRAM left: "));
Serial.println(freeRam());
}

int freeRam() {
extern int __heap_start,*__brkval;
int v;
return (int)&v - (__brkval == 0
? (int)&__heap_start : (int) __brkval);
}
void display_freeram() {
Serial.print(F("- SRAM left: "));
Serial.println(freeRam());
}

int freeRam() {
extern int __heap_start,*__brkval;
int v;
return (int)&v - (__brkval == 0
? (int)&__heap_start : (int) __brkval);
}
Want results from more Discord servers?
Add your server