Calling ‎Bsec2::allocateMemory blocks execution if using RadioHead library

Hardware

Context

I am trying to write an Arduino sketch with two distinct functionalities

  • Some environmental data is measured by a BME688 Devkit and is further processed to obtain classification probabilities for two classes.

  • Once I got these class probabilities, I would like to send this data using LoRa communication technology, using the RadioHead library.

I largely reused code from the examples of the bsec2 library (basic_config_state.ino)

Code (bsec2 v1.8.2610, BME68x v1.2.40408)

sketch.ino

// Bosch sensor libraries
#include <bsec2.h>
#include <EEPROM.h>
#include <commMux\commMux.h>
#include "config.h"

// LoRa libraries
#include <SPI.h>
#include <RH_RF95.h>

#define COMPLETED       1

// bsec2 library state saving period 
#define STATE_SAVE_PERIOD   UINT32_C(360 * 60 * 1000) /* 360 minutes - 4 times a day */

// Number of sensors to operate
#define NUM_BME68X_UNITS    8

#define PANIC_LED	LED_BUILTIN
#define ERROR_DUR   1000

// RF95 module pins for Feather RP2040 RFM:
#define RFM95_CS   16
#define RFM95_INT  21
#define RFM95_RST  17

// EU LoRa central frequency 
#define RF95_FREQ 868.0



/*
 * Devkit forward declarations 
 */

void setupDevkit();
void checkNewDevKitData();
// Update bsec2 state
void updateBsecState(Bsec2 bsec);

// Function called each time new data is available
void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec);

// Check status of bsec2 library
void checkBsecStatus(Bsec2 bsec);

// Load state from non-volatile memory
bool loadState(Bsec2 bsec);

// Load state from non-volatile memory
bool saveState(Bsec2 bsec);

void errLeds(void);

/* 
 * LoRa forward declarations
 */

void lora_setup();
void lora_send_data(uint8_t data[], uint8_t len);

/* 
 * Variables declarations
 */

Bsec2 envSensor[NUM_BME68X_UNITS];

// Handles communication with multiple sensors
comm_mux commConfig[NUM_BME68X_UNITS];

uint8_t bsecMemBlock[NUM_BME68X_UNITS][BSEC_INSTANCE_SIZE];
uint8_t sensor = 0;
static uint8_t bsecState[BSEC_MAX_STATE_BLOB_SIZE];


// Singleton instance of the radio driver
RH_RF95 rf95(RFM95_CS, RFM95_INT);

void errLeds(void)
{
    while(1)
    {
        digitalWrite(PANIC_LED, HIGH);
        delay(ERROR_DUR);
        digitalWrite(PANIC_LED, LOW);
        delay(ERROR_DUR);
    }
}

void checkBsecStatus(Bsec2 bsec)
{
    if (bsec.status < BSEC_OK)
    {
        Serial.println("BSEC error code : " + String(bsec.status));
        errLeds();  
    } else if (bsec.status > BSEC_OK)
    {
        Serial.println("BSEC warning code : " + String(bsec.status));
    }

    if (bsec.sensor.status < BME68X_OK)
    {
        Serial.println("BME68X error code : " + String(bsec.sensor.status));
        errLeds(); 
    } else if (bsec.sensor.status > BME68X_OK)
    {
        Serial.println("BME68X warning code : " + String(bsec.sensor.status));
    }
}

void updateBsecState(Bsec2 bsec)
{
    static uint16_t stateUpdateCounter = 0;
    bool update = false;

    if (!stateUpdateCounter || (stateUpdateCounter * STATE_SAVE_PERIOD) < millis())
    {
        
        update = true;
        stateUpdateCounter++;
    }

    if (update && !saveState(bsec))
        checkBsecStatus(bsec);
}

bool loadState(Bsec2 bsec)
{

    if (EEPROM.read(0) == BSEC_MAX_STATE_BLOB_SIZE)
    {
        
        Serial.println("Reading state from EEPROM");
        Serial.print("State file: ");
        for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++)
        {
            bsecState[i] = EEPROM.read(i + 1);
            Serial.print(String(bsecState[i], HEX) + ", ");
        }
        Serial.println();

        if (!bsec.setState(bsecState))
            return false;
    } else
    {
        
        Serial.println("Erasing EEPROM");

        for (uint8_t i = 0; i <= BSEC_MAX_STATE_BLOB_SIZE; i++)
            EEPROM.write(i, 0);

        EEPROM.commit();
    }
    return true;
}

bool saveState(Bsec2 bsec)
{
    if (!bsec.getState(bsecState))
        return false;

    Serial.println("Writing state to EEPROM");
    Serial.print("State file: ");

    for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++)
    {
        EEPROM.write(i + 1, bsecState[i]);
        Serial.print(String(bsecState[i], HEX) + ", ");
    }
    Serial.println();

    EEPROM.write(0, BSEC_MAX_STATE_BLOB_SIZE);
    EEPROM.commit();

    return true;
}

void newDataCallback(const bme68xData data, const bsecOutputs outputs, Bsec2 bsec)
{
    if (!outputs.nOutputs)
        return;

    Serial.println("BSEC outputs:\n\tSensor num = " + String(sensor));
    Serial.println("\tTime stamp = " + String((int) (outputs.output[0].time_stamp / INT64_C(1000000))));

	  int index = 0;

    for (uint8_t i = 0; i < outputs.nOutputs; i++)
    {
        const bsecData output  = outputs.output[i];
        switch (output.sensor_id)
        {
            case BSEC_OUTPUT_RAW_TEMPERATURE:
                Serial.println("\tTemperature = " + String(output.signal));
                break;
            case BSEC_OUTPUT_RAW_PRESSURE:
                Serial.println("\tPressure = " + String(output.signal));
                break;
            case BSEC_OUTPUT_RAW_HUMIDITY:
                Serial.println("\tHumidity = " + String(output.signal));
                break;
            case BSEC_OUTPUT_RAW_GAS:
                Serial.println("\tGas resistance = " + String(output.signal));
                break;
            case BSEC_OUTPUT_RAW_GAS_INDEX:
                Serial.println("\tGas index = " + String(output.signal));
                break;
            case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE:
                Serial.println("\tCompensated temperature = " + String(output.signal));
                break;
            case BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY:
                Serial.println("\tCompensated humidity = " + String(output.signal));
                break;
            case BSEC_OUTPUT_GAS_ESTIMATE_1:
            case BSEC_OUTPUT_GAS_ESTIMATE_2:
            case BSEC_OUTPUT_GAS_ESTIMATE_3:
            case BSEC_OUTPUT_GAS_ESTIMATE_4:
                index = (output.sensor_id - BSEC_OUTPUT_GAS_ESTIMATE_1);
                if (index == 0) // The four classes are updated from BSEC with same accuracy, thus printing is done just once.
                {
                  Serial.println("\tAccuracy = " + String((int) output.accuracy));
                }
                Serial.println(("\tClass " + String(index + 1) + " probability = ") + String(output.signal * 100) + "%");
                break;
            case BSEC_OUTPUT_REGRESSION_ESTIMATE_1:
            case BSEC_OUTPUT_REGRESSION_ESTIMATE_2:
            case BSEC_OUTPUT_REGRESSION_ESTIMATE_3:
            case BSEC_OUTPUT_REGRESSION_ESTIMATE_4:                
                index = (output.sensor_id - BSEC_OUTPUT_REGRESSION_ESTIMATE_1);
                if (index == 0) // The four targets are updated from BSEC with same accuracy, thus printing is done just once.
                {
                  Serial.println("\tAccuracy = " + String(output.accuracy));
                }
                Serial.println("\tTarget " + String(index + 1) + " = " + String(output.signal * 100));
                break;
            default:
                break;
        }
    }

}

void setupDevkit() 
{
  // Desired subscription list of BSEC2 Classification outputs 
  bsecSensor sensorList[] = {
          BSEC_OUTPUT_RAW_TEMPERATURE,
          BSEC_OUTPUT_RAW_PRESSURE,
          BSEC_OUTPUT_RAW_HUMIDITY,
          BSEC_OUTPUT_RAW_GAS,
          BSEC_OUTPUT_RAW_GAS_INDEX,
          BSEC_OUTPUT_GAS_ESTIMATE_1,
          BSEC_OUTPUT_GAS_ESTIMATE_2,
          BSEC_OUTPUT_GAS_ESTIMATE_3,
          BSEC_OUTPUT_GAS_ESTIMATE_4
  };

  EEPROM.begin(BSEC_MAX_STATE_BLOB_SIZE + 1);
  comm_mux_begin(Wire, SPI);
  pinMode(PANIC_LED, OUTPUT);
	delay(100);

  uint8_t state_write_otp = 0;

  for (uint8_t i = 0; i < NUM_BME68X_UNITS; i++)
  {
    // Sets the Communication interface for the given sensor 
    commConfig[i] = comm_mux_set_config(Wire, SPI, i, commConfig[i]);

    // Assigning a chunk of memory block to the bsecInstance 
    envSensor[i].allocateMemory(bsecMemBlock[i]);
  
    // Initialize the library and interfaces /
    if (!envSensor[i].begin(BME68X_SPI_INTF, comm_mux_read, comm_mux_write, comm_mux_delay, &commConfig[i]))
    {
        checkBsecStatus (envSensor[i]);
    }

    
    if (!envSensor[i].setConfig(bsec_config))
    {
      checkBsecStatus (envSensor[i]);
    }

    // Copy state from the EEPROM to the algorithm 
    if (state_write_otp == 0) 
    {
      if (!loadState(envSensor[i]))
      {
          checkBsecStatus (envSensor[i]);
      }
      state_write_otp = COMPLETED;
    }

    // Subscribe for the desired BSEC2 outputs 
    if (!envSensor[i].updateSubscription(sensorList, ARRAY_LEN(sensorList), BSEC_SAMPLE_RATE_SCAN))
    {
        checkBsecStatus (envSensor[i]);
    }

    // Whenever new data is available call the newDataCallback function 
    envSensor[i].attachCallback(newDataCallback);

    updateBsecState(envSensor[i]);
  }

  Serial.println("\nBSEC library version " + \
          String(envSensor[0].version.major) + "." \
          + String(envSensor[0].version.minor) + "." \
          + String(envSensor[0].version.major_bugfix) + "." \
          + String(envSensor[0].version.minor_bugfix));  
}

void checkNewDevKitData() {

    for (sensor = 0; sensor < NUM_BME68X_UNITS; sensor++)
    {
        if (!envSensor[sensor].run())
        {
         checkBsecStatus(envSensor[sensor]);
        }
    }
}


 void lora_setup() {

  if (!rf95.init()) { // if initialization fails, block execution
    Serial.println("init failed");
    while(1);
  }

  // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM

  // Set the right frequency for transmission, if it fails, stop execution
  if (!rf95.setFrequency(RF95_FREQ)) { 
    Serial.println("setFrequency failed");
    while (1);
  }

} 

void lora_send_data(uint8_t data[], uint8_t len) {

  Serial.println("Sending to rf95_server");

  // Send a message to rf95_server
  rf95.send(data, len);
  
  rf95.waitPacketSent();

} 

void setup(void)
{
  Serial.begin(9600);
  while(!Serial);
  lora_setup(); 
  Serial.println("lora_setup(): Success");
  setupDevkit();
  Serial.println("setupDevkit(): Success");
}

void loop(void)
{
  checkNewDevKitData();
  
}

config.h

#ifndef CONFIG_H
#define CONFIG_H
inline constexpr uint8_t bsec_config[] = {
0,1,6,2,189,1,0,0,0,0,0,0,127,7,0,0,56,0,1,0,0,168,19,73,64,49,119,76,0,0,97,69,0,0,97,69,10,0,3,0,0,0,96,64,23,183,209,56,43,24,149,60,140,74,106,188,43,24,149,60,216,129,243,190,151,255,80,190,216,129,243,190,8,0,2,0,0,0,72,66,16,0,3,0,10,215,163,60,10,215,35,59,10,215,35,59,13,0,5,0,0,0,0,0,100,254,131,137,87,88,0,9,0,7,240,150,61,0,0,0,0,0,0,0,0,28,124,225,61,52,128,215,63,0,0,160,64,0,0,0,0,0,0,0,0,205,204,12,62,103,213,39,62,230,63,76,192,0,0,0,0,0,0,0,0,145,237,60,191,251,58,64,63,177,80,131,64,0,0,0,0,0,0,0,0,93,254,227,62,54,60,133,191,0,0,64,64,12,0,10,0,0,0,0,0,0,0,0,0,45,5,11,0,0,0,2,230,28,3,63,132,158,57,62,174,81,14,61,85,190,100,63,120,204,139,61,230,199,160,189,142,162,12,190,31,30,133,190,176,197,186,62,180,203,149,190,249,97,177,62,155,0,177,189,32,243,58,189,80,194,79,189,81,251,184,62,15,42,44,190,11,223,33,190,121,52,214,62,122,115,33,62,86,147,135,61,59,165,132,190,59,165,132,62,0,0,0,0,0,0,0,0,39,206,135,190,10,103,50,63,2,109,43,191,167,233,163,190,98,102,145,63,38,115,8,63,246,180,198,190,221,91,68,62,108,60,51,191,107,67,243,61,31,28,164,189,29,132,137,190,115,54,189,188,110,72,69,61,15,173,167,61,142,161,23,190,14,127,41,191,63,70,181,62,67,44,197,188,55,86,56,62,47,88,87,190,76,187,24,61,35,227,62,62,62,60,235,62,198,136,129,190,68,111,175,190,142,110,84,190,48,186,107,62,117,139,153,189,94,162,25,63,124,191,153,190,167,92,239,190,42,76,146,62,145,66,47,62,219,57,36,191,171,47,195,190,204,61,94,62,189,69,155,190,247,130,91,63,179,224,44,191,171,226,221,190,195,35,60,189,177,194,146,190,121,224,136,62,83,55,120,190,77,49,181,62,141,156,157,62,234,143,181,190,186,105,138,61,143,22,21,190,196,148,108,190,155,243,42,190,186,204,183,190,116,115,70,190,79,180,215,62,66,157,28,191,139,135,47,190,6,63,68,191,180,100,40,62,111,27,230,190,193,93,14,191,230,253,177,190,48,15,230,62,251,120,101,61,13,216,198,189,250,181,184,62,234,62,3,63,229,36,75,61,86,114,149,62,214,183,163,190,239,159,87,189,217,253,199,62,144,243,156,62,1,39,11,189,248,18,87,63,237,208,167,189,100,0,211,189,84,180,164,62,124,153,35,191,144,249,166,62,64,151,195,190,170,96,27,62,138,122,213,62,221,44,205,62,98,93,52,191,126,21,26,190,128,101,26,62,150,115,196,189,198,135,232,189,24,219,53,62,6,126,239,189,198,184,171,188,72,60,82,63,192,112,131,62,177,192,147,190,36,18,157,190,134,82,135,63,204,214,155,190,0,217,162,62,175,32,22,191,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,87,1,159,62,246,61,5,61,222,219,165,62,86,238,169,190,123,129,187,62,41,245,244,61,88,33,166,60,240,167,65,62,14,0,145,61,117,196,211,60,119,63,251,191,172,173,248,190,137,171,234,63,109,240,64,190,178,59,230,191,134,22,84,62,230,174,24,64,47,106,0,192,73,176,225,63,164,244,254,63,53,39,0,63,49,120,167,187,197,85,146,190,102,61,45,62,216,161,17,63,137,55,168,61,244,153,192,189,46,81,211,62,190,255,35,190,234,223,133,59,25,217,9,63,124,232,2,191,166,186,173,62,126,226,187,190,80,214,59,62,64,157,20,191,61,247,161,189,166,19,106,63,118,1,205,61,24,102,242,61,57,220,15,191,72,22,0,190,112,116,124,63,96,119,202,190,174,163,85,191,127,46,3,62,12,209,62,63,27,110,46,191,6,158,160,63,197,3,89,63,39,110,168,189,198,255,171,61,13,141,227,62,167,61,169,188,89,35,77,191,204,168,254,61,54,229,206,62,10,27,201,190,246,149,89,62,69,126,32,63,100,49,20,62,73,213,98,189,218,125,37,191,188,136,55,62,143,182,0,63,63,157,124,190,212,158,48,191,116,112,60,63,167,142,67,191,87,6,68,191,212,130,226,190,182,206,61,61,170,22,27,63,43,220,14,63,56,54,59,191,243,121,145,62,236,178,12,63,177,61,183,190,139,206,192,62,200,34,72,63,184,199,176,63,115,1,186,190,58,128,163,191,12,58,248,189,197,106,201,63,57,83,122,62,221,174,249,190,83,201,38,63,224,174,185,191,72,75,162,191,176,43,223,190,36,97,8,191,232,38,13,62,104,21,136,190,180,51,1,191,186,92,227,189,47,184,149,61,132,218,220,190,234,229,57,62,132,15,48,63,180,20,40,191,186,132,129,63,0,0,0,0,0,0,0,0,24,56,88,62,182,62,6,190,0,0,0,0,0,0,0,0,137,130,129,63,40,4,114,191,0,0,0,0,0,0,0,0,223,217,54,63,77,112,139,190,0,0,0,0,0,0,0,0,221,161,182,191,97,118,116,63,0,0,0,0,0,0,0,0,219,244,47,189,112,253,146,189,0,0,0,0,0,0,0,0,71,170,183,63,178,1,68,191,0,0,0,0,0,0,0,0,1,143,170,191,28,249,122,63,0,0,0,0,0,0,0,0,1,198,2,64,138,166,225,191,0,0,0,0,0,0,0,0,80,229,171,63,73,198,138,191,0,0,0,0,0,0,0,0,10,10,2,234,72,106,71,54,106,158,74,34,20,125,74,33,42,53,74,82,132,71,72,91,3,50,72,96,67,35,72,183,51,65,71,44,54,111,71,200,148,132,71,0,0,0,0,0,0,0,0,0,0,0,0,211,244,214,70,51,235,69,74,142,64,31,74,173,179,231,73,227,141,213,71,86,209,186,71,93,226,168,71,95,65,133,70,139,31,151,70,54,80,158,70,0,0,128,63,0,0,128,63,0,0,128,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,145,1,254,0,2,1,5,48,117,100,0,44,1,112,23,151,7,132,3,197,0,92,4,144,1,64,1,64,1,144,1,48,117,48,117,48,117,48,117,100,0,100,0,100,0,48,117,48,117,48,117,100,0,100,0,48,117,48,117,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,8,7,100,0,100,0,100,0,100,0,48,117,48,117,48,117,100,0,100,0,100,0,48,117,48,117,100,0,100,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,112,23,112,23,112,23,112,23,8,7,8,7,8,7,8,7,112,23,112,23,112,23,112,23,112,23,112,23,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,112,23,112,23,112,23,112,23,255,255,255,255,220,5,220,5,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,220,5,220,5,220,5,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,48,117,0,5,10,5,0,2,0,10,0,30,0,5,0,5,0,5,0,5,0,5,0,5,0,64,1,100,0,100,0,100,0,200,0,200,0,200,0,64,1,64,1,64,1,10,1,0,0,0,0,0,9,166,0,0
};
/*
 * Names of the configuration classes
 */
inline const String gasName[] = {"Incenso", "Aria"};
#endif

Problem

If I use only the code for the devkit tasks, everything goes smooth and the board outputs periodically fetched data from the devkit on the Serial Monitor, but if I include also the code for the LoRa transmission (uncommenting it), the execution stops while executing the devkit setup function. After further debugging, I discovered that the execution stops when executing the following statement

    // Assigning a chunk of memory block to the bsecInstance 
    envSensor[i].allocateMemory(bsecMemBlock[i]);

If I remove this statement, the sketch works correctly (I have yet to implement and try the LoRa transmission). Can someone understand what is going on and why this statement blocks the execution (only when I keep the LoRa code!)? Can I just ignore it and keep going as it seems that everything is just fine after I remove it?

When the execution blocks, the Serial Monitor does not give any output (not even Serial prints after the blocking statement). Moreover, I can no longer upload any code to my board without pushing the BOOT button manually after this problem occurs. To recover normal upload behavior I upload a simple Blink sketch while pushing BOOT, so that the IDE is again able to upload any sketch without the need of manually press BOOT on the board.

Huge thaks to anyone that will try to help me.

4 replies