Problem with i2c_t3/IntervalTimer/RingBuffer and Serial output

Status
Not open for further replies.

linuxgeek

Well-known member
I'm working with a couple of BME280 sensors using i2c_t3. I'm sampling them using IntervalTimer and putting their values in a RingBuffer (from the ADC library). I'm outputting the available samples in the RingBuffer over serial in the loop.

If I don't do any actual i2c sampling in the sampling function, the serial output is fine. Although I'm sampling from the sensors, I'm only storing/outputting an incremented counter until I can figure out this problem.

I'm using the teensy-LC, and using Wire and Wire1 to access both sensors simultaneously. Is there something about using i2c and interrupts that I should be doing to allow this to work?

Here's the output I usually see over serial, and the problem seems to start to crop up when the string gets longer when the digits get longer with 1000.
Thanks for any insights into what is the issue.

Code:
line: 977:982
977,978,979,980,981,
line: 982:987
982,983,984,985,986,
line: 987:992
987,988,989,990,991,
line: 992:997
992,993,994,995,996,
line: 997:1002
997,998,999,1000,1001,
line: 1002:1007
1,0,1004,1005,1006,
line: 1007:1012
1007,536870880,1074163715,536870880,1011,
line: 1012:1017
0,222121527,253,9845,1016,
line: 1017:1022
9845,590,4294967281,536870880,1021,
line: 1022:1027
1000005,8331,1024,1025,1026,
line: 1027:1032
1027,1028,1029,1030,1031,
line: 1032:1037
1032,1033,1034,1035,1036,

Here's the code. If I comment out the relevant portion for i2c sampling then it works.
Code:
  rawTemp1 =   readBME280TemperatureWire();
  rawPress1 =  readBME280PressureWire();
  rawHumidity1 = readBME280HumidityWire();
  rawTemp2 =   readBME280TemperatureWire1();
  rawPress2 =  readBME280PressureWire1();
  rawHumidity2 = readBME280HumidityWire1();


Here's the full code

Code:
#include <i2c_t3.h>
#include <RingBuffer.h>
#include <IntervalTimer.h>

// BME280 registers
#define BME280_HUM_LSB    0xFE
#define BME280_HUM_MSB    0xFD
#define BME280_TEMP_XLSB  0xFC
#define BME280_TEMP_LSB   0xFB
#define BME280_TEMP_MSB   0xFA
#define BME280_PRESS_XLSB 0xF9
#define BME280_PRESS_LSB  0xF8
#define BME280_PRESS_MSB  0xF7
#define BME280_CONFIG     0xF5
#define BME280_CTRL_MEAS  0xF4
#define BME280_STATUS     0xF3
#define BME280_CTRL_HUM   0xF2
#define BME280_RESET      0xE0
#define BME280_ID         0xD0  // should be 0x60
#define BME280_CALIB00    0x88
#define BME280_CALIB26    0xE1

// Device address
#define BME280_ADDRESS             0x76 // Address of BME280 pressure/temperature sensor


enum Posr {
  P_OSR_00 = 0,  // no op
  P_OSR_01,
  P_OSR_02,
  P_OSR_04,
  P_OSR_08,
  P_OSR_16
};

enum Hosr {
  H_OSR_00 = 0,  // no op
  H_OSR_01,
  H_OSR_02,
  H_OSR_04,
  H_OSR_08,
  H_OSR_16
};

enum Tosr {
  T_OSR_00 = 0,  // no op
  T_OSR_01,
  T_OSR_02,
  T_OSR_04,
  T_OSR_08,
  T_OSR_16
};

enum IIRFilter {
  full = 0,  // bandwidth at full sample rate
  BW0_223ODR,
  BW0_092ODR,
  BW0_042ODR,
  BW0_021ODR // bandwidth at 0.021 x sample rate
};

enum Mode {
  BME280Sleep = 0,
  forced,
  forced2,
  normal
};

enum SBy {
  t_00_5ms = 0,
  t_62_5ms,
  t_125ms,
  t_250ms,
  t_500ms,
  t_1000ms,
  t_10ms,
  t_20ms,
};

uint32_t rawPress1, rawTemp1;   // pressure and temperature raw count output for BME280
uint16_t rawHumidity1;
uint32_t rawPress2, rawTemp2;   // pressure and temperature raw count output for BME280
uint16_t rawHumidity2; 

// Specify BME280 configuration
uint8_t Posr = P_OSR_16, Hosr = H_OSR_16, Tosr = T_OSR_02, Mode = normal, IIRFilter = BW0_042ODR, SBy = t_62_5ms;     // set pressure amd temperature output data rate
// t_fine carries fine temperature as global value for BME280
int32_t t_fine;

// BME280 compensation parameters
uint8_t dig_H1A, dig_H3A, dig_H6A;
uint16_t dig_T1A, dig_P1A, dig_H4A, dig_H5A;
int16_t  dig_T2A, dig_T3A, dig_P2A, dig_P3A, dig_P4A, dig_P5A, dig_P6A, dig_P7A, dig_P8A, dig_P9A, dig_H2A;

// BME280 compensation parameters
uint8_t dig_H1B, dig_H3B, dig_H6B;
uint16_t dig_T1B, dig_P1B, dig_H4B, dig_H5B;
int16_t  dig_T2B, dig_T3B, dig_P2B, dig_P3B, dig_P4B, dig_P5B, dig_P6B, dig_P7B, dig_P8B, dig_P9B, dig_H2B;

RingBuffer *buffer = new RingBuffer();
int baseSampleRate = 5, nStatus = -1;
IntervalTimer samplingTimer;
volatile uint32_t sampleCount = 0, sampleCountSnapshot = 0, currReadSnapshot = 0, rtCount = 0, currReadCount = 0;
char command = ' ';

void setup() {

  Wire.begin(I2C_MASTER, 0x00, I2C_PINS_16_17, I2C_PULLUP_EXT, I2C_RATE_400);
  Wire1.begin(I2C_MASTER, 0x00, I2C_PINS_22_23, I2C_PULLUP_EXT, I2C_RATE_400);
  delay(5000);
  Serial.begin(38400);

  while ((command=waitForCommand()) != 'i') {
    Serial.print("command was: ");
    Serial.println(command);
  }

  while ((command=waitForCommand()) != 'r') {
    Serial.print("command was: ");
    Serial.println(command);
  }

  pinMode(13, OUTPUT);
  digitalWrite(13, HIGH);
}

void loop() {
  delay(1000);
  sampleCountSnapshot = sampleCount;
  currReadSnapshot = currReadCount;
  Serial.print(currReadSnapshot);
  Serial.print(":");
  Serial.println(sampleCountSnapshot);
  Serial.flush();

  for (rtCount=currReadSnapshot;rtCount<sampleCountSnapshot;rtCount++) {
    currReadCount = buffer->read();
    Serial.print(currReadCount);
    Serial.print(",");
  }
  currReadCount=rtCount;
  Serial.flush();
}


char waitForCommand() {
  char c = ' ';
  Serial.println("i = initialize");
  Serial.println("s = status");
  Serial.println("r = run");
  Serial.println("waiting for command...");
  Serial.flush();

  while (c == ' ') {
    if (Serial.available() > 0) {
      c=Serial.read();
      switch(c) {
      case 'i':
        initialize();
        nStatus = 0;
        break;
      case 'r':
        startSampling();
        nStatus = 1;
        break;
        //output status, this is not a complete status, only the status at setup(), stub now
      case 's':
        Serial.print("status");
      default:
      c=' ';
    }
  }
  delay(500);
}
  return c;
}

void startSampling() {
  sampleCount=0;
  samplingTimer.begin(sample, 1000000/baseSampleRate); 
}

void sample() {
  //read the sensors
  // comment out this section and the serial output doesn't get corrupted
  rawTemp1 =   readBME280TemperatureWire();
  rawPress1 =  readBME280PressureWire();
  rawHumidity1 = readBME280HumidityWire();
  rawTemp2 =   readBME280TemperatureWire1();
  rawPress2 =  readBME280PressureWire1();
  rawHumidity2 = readBME280HumidityWire1();

  buffer->write(sampleCount);
  //count after, so that reading knows that it's available
  sampleCount++;
}

void writeByteWire(uint8_t address, uint8_t subAddress, uint8_t data) {
  Wire.beginTransmission(address);  // Initialize the Tx buffer
  Wire.write(subAddress);           // Put slave register address in Tx buffer
  Wire.write(data);                 // Put data in Tx buffer
  Wire.endTransmission();           // Send the Tx buffer
}

void writeByteWire1(uint8_t address, uint8_t subAddress, uint8_t data) {
  Wire1.beginTransmission(address);  // Initialize the Tx buffer
  Wire1.write(subAddress);           // Put slave register address in Tx buffer
  Wire1.write(data);                 // Put data in Tx buffer
  Wire1.endTransmission();           // Send the Tx buffer
}

uint8_t readByteWire(uint8_t address, uint8_t subAddress) {
  uint8_t data; // `data` will store the register data   
  Wire.beginTransmission(address);         // Initialize the Tx buffer
  Wire.write(subAddress);                  // Put slave register address in Tx buffer
  Wire.endTransmission(I2C_NOSTOP);        // Send the Tx buffer, but send a restart to keep connection alive
//  Wire.endTransmission(false);             // Send the Tx buffer, but send a restart to keep connection alive
//  Wire.requestFrom(address, 1);  // Read one byte from slave register address 
  Wire.requestFrom(address, (size_t) 1);   // Read one byte from slave register address 
  data = Wire.read();                      // Fill Rx buffer with result
  return data;                             // Return data read from slave register
}

uint8_t readByteWire1(uint8_t address, uint8_t subAddress) {
  uint8_t data; // `data` will store the register data   
  Wire1.beginTransmission(address);         // Initialize the Tx buffer
  Wire1.write(subAddress);                  // Put slave register address in Tx buffer
  Wire1.endTransmission(I2C_NOSTOP);        // Send the Tx buffer, but send a restart to keep connection alive
//  Wire1.endTransmission(false);             // Send the Tx buffer, but send a restart to keep connection alive
//  Wire1.requestFrom(address, 1);  // Read one byte from slave register address 
  Wire1.requestFrom(address, (size_t) 1);   // Read one byte from slave register address 
  data = Wire1.read();                      // Fill Rx buffer with result
  return data;                             // Return data read from slave register
}

void readBytesWire(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest) {  
  Wire.beginTransmission(address);   // Initialize the Tx buffer
  Wire.write(subAddress);            // Put slave register address in Tx buffer
  Wire.endTransmission(I2C_NOSTOP);  // Send the Tx buffer, but send a restart to keep connection alive
//  Wire.endTransmission(false);       // Send the Tx buffer, but send a restart to keep connection alive
  uint8_t i = 0;
//        Wire.requestFrom(address, count);  // Read bytes from slave register address 
        Wire.requestFrom(address, (size_t) count);  // Read bytes from slave register address 
  while (Wire.available()) {
        dest[i++] = Wire.read(); }         // Put read results in the Rx buffer
}


void readBytesWire1(uint8_t address, uint8_t subAddress, uint8_t count, uint8_t * dest) {  
  Wire1.beginTransmission(address);   // Initialize the Tx buffer
  Wire1.write(subAddress);            // Put slave register address in Tx buffer
  Wire1.endTransmission(I2C_NOSTOP);  // Send the Tx buffer, but send a restart to keep connection alive
//  Wire1.endTransmission(false);       // Send the Tx buffer, but send a restart to keep connection alive
  uint8_t i = 0;
//        Wire1.requestFrom(address, count);  // Read bytes from slave register address 
        Wire1.requestFrom(address, (size_t) count);  // Read bytes from slave register address 
  while (Wire1.available()) {
        dest[i++] = Wire1.read(); }         // Put read results in the Rx buffer
}

uint32_t readBME280TemperatureWire() {
  uint8_t rawData[3];  // 20-bit pressure register data stored here
  readBytesWire(BME280_ADDRESS, BME280_TEMP_MSB, 3, &rawData[0]);  
  return (uint32_t) (((uint32_t) rawData[0] << 16 | (uint32_t) rawData[1] << 8 | rawData[2]) >> 4);
}

uint32_t readBME280TemperatureWire1() {
  uint8_t rawData[3];  // 20-bit pressure register data stored here
  readBytesWire1(BME280_ADDRESS, BME280_TEMP_MSB, 3, &rawData[0]);  
  return (uint32_t) (((uint32_t) rawData[0] << 16 | (uint32_t) rawData[1] << 8 | rawData[2]) >> 4);
}

uint32_t readBME280PressureWire() {
  uint8_t rawData[3];  // 20-bit pressure register data stored here
  readBytesWire(BME280_ADDRESS, BME280_PRESS_MSB, 3, &rawData[0]);  
  return (uint32_t) (((uint32_t) rawData[0] << 16 | (uint32_t) rawData[1] << 8 | rawData[2]) >> 4);
}

uint32_t readBME280PressureWire1() {
  uint8_t rawData[3];  // 20-bit pressure register data stored here
  readBytesWire1(BME280_ADDRESS, BME280_PRESS_MSB, 3, &rawData[0]);  
  return (uint32_t) (((uint32_t) rawData[0] << 16 | (uint32_t) rawData[1] << 8 | rawData[2]) >> 4);
}

uint16_t readBME280HumidityWire() {
  uint8_t rawData[3];  // 20-bit pressure register data stored here
  readBytesWire(BME280_ADDRESS, BME280_HUM_MSB, 2, &rawData[0]);  
  return (uint16_t) (((uint16_t) rawData[0] << 8 | rawData[1]) );
}

uint16_t readBME280HumidityWire1() {
  uint8_t rawData[3];  // 20-bit pressure register data stored here
  readBytesWire1(BME280_ADDRESS, BME280_HUM_MSB, 2, &rawData[0]);  
  return (uint16_t) (((uint16_t) rawData[0] << 8 | rawData[1]) );
}

void BME280InitWire() {
  // Configure the BME280
  // Set H oversampling rate
  writeByteWire(BME280_ADDRESS, BME280_CTRL_HUM, 0x07 & Hosr);
  // Set T and P oversampling rates and sensor mode
  writeByteWire(BME280_ADDRESS, BME280_CTRL_MEAS, Tosr << 5 | Posr << 2 | Mode);
  // Set standby time interval in normal mode and bandwidth
  writeByteWire(BME280_ADDRESS, BME280_CONFIG, SBy << 5 | IIRFilter << 2);
  // Read and store calibration data
  uint8_t calib[26];
  readBytesWire(BME280_ADDRESS, BME280_CALIB00, 26, &calib[0]);
  dig_T1A = (uint16_t)(((uint16_t) calib[1] << 8) | calib[0]);
  dig_T2A = ( int16_t)((( int16_t) calib[3] << 8) | calib[2]);
  dig_T3A = ( int16_t)((( int16_t) calib[5] << 8) | calib[4]);
  dig_P1A = (uint16_t)(((uint16_t) calib[7] << 8) | calib[6]);
  dig_P2A = ( int16_t)((( int16_t) calib[9] << 8) | calib[8]);
  dig_P3A = ( int16_t)((( int16_t) calib[11] << 8) | calib[10]);
  dig_P4A = ( int16_t)((( int16_t) calib[13] << 8) | calib[12]);
  dig_P5A = ( int16_t)((( int16_t) calib[15] << 8) | calib[14]);
  dig_P6A = ( int16_t)((( int16_t) calib[17] << 8) | calib[16]);
  dig_P7A = ( int16_t)((( int16_t) calib[19] << 8) | calib[18]);
  dig_P8A = ( int16_t)((( int16_t) calib[21] << 8) | calib[20]);
  dig_P9A = ( int16_t)((( int16_t) calib[23] << 8) | calib[22]);
  dig_H1A = calib[25];
  readBytesWire(BME280_ADDRESS, BME280_CALIB26, 7, &calib[0]);
  dig_H2A = ( int16_t)((( int16_t) calib[1] << 8) | calib[0]);
  dig_H3A = calib[2];
  dig_H4A = ( int16_t)(((( int16_t) calib[3] << 8) | (0x0F & calib[4]) << 4) >> 4);
  dig_H5A = ( int16_t)(((( int16_t) calib[5] << 8) | (0xF0 & calib[4]) ) >> 4 );
  dig_H6A = calib[6];
}

void BME280InitWire1() {
  // Configure the BME280
  // Set H oversampling rate
  writeByteWire1(BME280_ADDRESS, BME280_CTRL_HUM, 0x07 & Hosr);
  // Set T and P oversampling rates and sensor mode
  writeByteWire1(BME280_ADDRESS, BME280_CTRL_MEAS, Tosr << 5 | Posr << 2 | Mode);
  // Set standby time interval in normal mode and bandwidth
  writeByteWire1(BME280_ADDRESS, BME280_CONFIG, SBy << 5 | IIRFilter << 2);
  // Read and store calibration data
  uint8_t calib[26];
  readBytesWire1(BME280_ADDRESS, BME280_CALIB00, 26, &calib[0]);
  dig_T1B = (uint16_t)(((uint16_t) calib[1] << 8) | calib[0]);
  dig_T2B = ( int16_t)((( int16_t) calib[3] << 8) | calib[2]);
  dig_T3B = ( int16_t)((( int16_t) calib[5] << 8) | calib[4]);
  dig_P1B = (uint16_t)(((uint16_t) calib[7] << 8) | calib[6]);
  dig_P2B = ( int16_t)((( int16_t) calib[9] << 8) | calib[8]);
  dig_P3B = ( int16_t)((( int16_t) calib[11] << 8) | calib[10]);
  dig_P4B = ( int16_t)((( int16_t) calib[13] << 8) | calib[12]);
  dig_P5B = ( int16_t)((( int16_t) calib[15] << 8) | calib[14]);
  dig_P6B = ( int16_t)((( int16_t) calib[17] << 8) | calib[16]);
  dig_P7B = ( int16_t)((( int16_t) calib[19] << 8) | calib[18]);
  dig_P8B = ( int16_t)((( int16_t) calib[21] << 8) | calib[20]);
  dig_P9B = ( int16_t)((( int16_t) calib[23] << 8) | calib[22]);
  dig_H1B = calib[25];
  readBytesWire1(BME280_ADDRESS, BME280_CALIB26, 7, &calib[0]);
  dig_H2B = ( int16_t)((( int16_t) calib[1] << 8) | calib[0]);
  dig_H3B = calib[2];
  dig_H4B = ( int16_t)(((( int16_t) calib[3] << 8) | (0x0F & calib[4]) << 4) >> 4);
  dig_H5B = ( int16_t)(((( int16_t) calib[5] << 8) | (0xF0 & calib[4]) ) >> 4 );
  dig_H6B = calib[6];
}


// simple function to scan for I2C devices on the bus
void i2cScanWire1() {
    // scan for i2c devices
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire1.beginTransmission(address);
    error = Wire1.endTransmission();
    //sensor2Status = error;

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
      nDevices++;
    } else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else
    Serial.println("done\n");
}


// simple function to scan for I2C devices on the bus
void i2cScanWire() {
    // scan for i2c devices
  byte error, address;
  int nDevices;

  Serial.println("Scanning...");

  nDevices = 0;
  for(address = 1; address < 127; address++ ) {
    // The i2c_scanner uses the return value of
    // the Write.endTransmisstion to see if
    // a device did acknowledge to the address.
    Wire.beginTransmission(address);
    error = Wire.endTransmission();
    //sensor1Status=error;

    if (error == 0) {
      Serial.print("I2C device found at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.print(address,HEX);
      Serial.println("  !");
      nDevices++;
    } else if (error==4) {
      Serial.print("Unknow error at address 0x");
      if (address<16) 
        Serial.print("0");
      Serial.println(address,HEX);
    }    
  }
  if (nDevices == 0)
    Serial.println("No I2C devices found\n");
  else {
    Serial.println("done\n");
  }
}



void initialize() {
  i2cScanWire();  // Wire Check for I2C devices on the I2C bus
  i2cScanWire1(); // Wire1
  
  delay(1000);

  // Read the WHO_AM_I register of the BME280 this is a good test of communication
  byte f = readByteWire(BME280_ADDRESS, BME280_ID);  // Read WHO_AM_I register for BME280
  Serial.print("BME280 "); 
  Serial.print("I AM "); 
  Serial.print(f, HEX); 
  Serial.print(" I should be "); 
  Serial.println(0x60, HEX);
  Serial.println(" ");

  delay(1000); 

 if(f == 0x60) {
  writeByteWire(BME280_ADDRESS, BME280_RESET, 0xB6); // reset BME280 before initilization
  delay(100);
  BME280InitWire(); // Initialize BME280 altimeter

  Serial.println("Calibration coeficients:");
  Serial.print("dig_T1A ="); 
  Serial.println(dig_T1A);
  Serial.print("dig_T2A ="); 
  Serial.println(dig_T2A);
  Serial.print("dig_T3A ="); 
  Serial.println(dig_T3A);
  Serial.print("dig_P1A ="); 
  Serial.println(dig_P1A);
  Serial.print("dig_P2A ="); 
  Serial.println(dig_P2A);
  Serial.print("dig_P3A ="); 
  Serial.println(dig_P3A);
  Serial.print("dig_P4A ="); 
  Serial.println(dig_P4A);
  Serial.print("dig_P5A ="); 
  Serial.println(dig_P5A);
  Serial.print("dig_P6A ="); 
  Serial.println(dig_P6A);
  Serial.print("dig_P7A ="); 
  Serial.println(dig_P7A);
  Serial.print("dig_P8A ="); 
  Serial.println(dig_P8A);
  Serial.print("dig_P9A ="); 
  Serial.println(dig_P9A);
  Serial.print("dig_H1A ="); 
  Serial.println(dig_H1A);
  Serial.print("dig_H2A ="); 
  Serial.println(dig_H2A);
  Serial.print("dig_H3A ="); 
  Serial.println(dig_H3A);
  Serial.print("dig_H4A ="); 
  Serial.println(dig_H4A);
  Serial.print("dig_H5A ="); 
  Serial.println(dig_H5A);
  Serial.print("dig_H6A ="); 
  Serial.println(dig_H6A);
 }
  else Serial.println(" BME280 not functioning!");

  // Read the WHO_AM_I register of the BME280 this is a good test of communication
  byte f2 = readByteWire1(BME280_ADDRESS, BME280_ID);  // Read WHO_AM_I register for BME280
  Serial.print("BME280 "); 
  Serial.print("I AM "); 
  Serial.print(f, HEX); 
  Serial.print(" I should be "); 
  Serial.println(0x60, HEX);
  Serial.println(" ");
  delay(1000); 

 if(f2 == 0x60) { 
  writeByteWire1(BME280_ADDRESS, BME280_RESET, 0xB6); // reset BME280 before initilization
  delay(100);
  
  BME280InitWire1(); // Initialize BME280 altimeter
  Serial.println("Calibration coeficients:");
  Serial.print("dig_T1B ="); 
  Serial.println(dig_T1B);
  Serial.print("dig_T2B ="); 
  Serial.println(dig_T2B);
  Serial.print("dig_T3B ="); 
  Serial.println(dig_T3B);
  Serial.print("dig_P1B ="); 
  Serial.println(dig_P1B);
  Serial.print("dig_P2B ="); 
  Serial.println(dig_P2B);
  Serial.print("dig_P3B ="); 
  Serial.println(dig_P3B);
  Serial.print("dig_P4B ="); 
  Serial.println(dig_P4B);
  Serial.print("dig_P5B ="); 
  Serial.println(dig_P5B);
  Serial.print("dig_P6B ="); 
  Serial.println(dig_P6B);
  Serial.print("dig_P7B ="); 
  Serial.println(dig_P7B);
  Serial.print("dig_P8B ="); 
  Serial.println(dig_P8B);
  Serial.print("dig_P9B ="); 
  Serial.println(dig_P9B);
  Serial.print("dig_H1B ="); 
  Serial.println(dig_H1B);
  Serial.print("dig_H2B ="); 
  Serial.println(dig_H2B);
  Serial.print("dig_H3B ="); 
  Serial.println(dig_H3B);
  Serial.print("dig_H4B ="); 
  Serial.println(dig_H4B);
  Serial.print("dig_H5B ="); 
  Serial.println(dig_H5B);
  Serial.print("dig_H6B ="); 
  Serial.println(dig_H6B);
  }
}
 

Attachments

  • ringBufferInterrupt.ino
    18.4 KB · Views: 90
Also it doesn't seem to be an issue with using Wire & Wire1 together. I commented all the Wire1 access, and just used Wire. Still have the same problem.

It does seem to have the problem predictably about every 1000 samples. Hmmm.

Ok, I did change the Ring buffer size to 1024 from 8, so maybe it has something to do with that.

< #define RING_BUFFER_DEFAULT_BUFFER_SIZE 1024
---
> #define RING_BUFFER_DEFAULT_BUFFER_SIZE 8
 
Last edited:
Well, I changed it back to 8 from 1024, and now I don't get the problem. But I'll need to use a bigger buffer.

From RingBuffer.h in ADC library.

// THE SIZE MUST BE A POWER OF 2!!
#define RING_BUFFER_DEFAULT_BUFFER_SIZE 8

I'm confused at why 8 works, but 1024 doesn't. And only in the case of doing i2c sampling in an interrupt.

512 buffer size doesn't seem to produce the problem. Could I be using too much memory and doing a buffer overrun? How would I know if I'm using too much memory in creating the buffer?
 
Last edited:
I hope it's not annoying that I keep posting follow-ups to my own question, but as I narrow it down maybe someone has some insight.

I notice that it doesn't matter how big I make the ring buffer, the amount of memory doesn't reflect where I would expect to see it. I always get the same for dynamic memory. This is with 2048 buffer size (# of elements):
Code:
Sketch uses 20,992 bytes (33%) of program storage space. Maximum is 63,488 bytes.
Global variables use 3,704 bytes (45%) of dynamic memory, leaving 4,488 bytes for local variables. Maximum is 8,192 bytes.

Here's with 512 (this looks identical when the buffer is at it's default of 8):
Code:
Sketch uses 20,984 bytes (33%) of program storage space. Maximum is 63,488 bytes.
Global variables use 3,704 bytes (45%) of dynamic memory, leaving 4,488 bytes for local variables. Maximum is 8,192 bytes.


Even though I changed it 2048, which should be 8k. I was assuming the buffer would be in RAM, but I don't see it reflected anywhere.

I also re-launched arduino, since I think maybe it doesn't try and recompile the libraries, except at startup.

Also, I notice that the arduino IDE shows "RingBuffer" in bold for the header as such:
arduinoIDEbold.png

Does that bold highlighting mean something?
 
Last edited:
Clearly I don't understand something about tracking memory usage.

I think my problem is that the Arduino IDE doesn't include memory allocation unless the memory is initialized to some known value.

This sketch only shows about 1k extra usage than an empty sketch if I do the intialization for loop in setup.

Code:
byte bytePtr[1024];

void setup() {
for (int i=0;i<1024;i++) bytePtr[i] = 0;
}

void loop() {
}

Code:
Sketch uses 11,420 bytes (17%) of program storage space. Maximum is 63,488 bytes.
Global variables use 2,208 bytes (26%) of dynamic memory, leaving 5,984 bytes for local variables. Maximum is 8,192 bytes.

//if I comment out for loop
Sketch uses 11,444 bytes (18%) of program storage space. Maximum is 63,488 bytes.
Global variables use 3,232 bytes (39%) of dynamic memory, leaving 4,960 bytes for local variables. Maximum is 8,192 bytes.

This must be a common problem but I didn't come across it searching the forum.
 
In case anyone else may be helped by this.

I tried creating a constructor and a specific function to initialize the array in RingBuffer, but it didn't help in the arduino IDE reporting memory usage.

But I went ahead and created the same size array before setup(), and initialized it in setup(). And when I set the array to 1024 elements and compile it, I get a warning "Low memory available, stability problems may occur."

Apparently libraries don't get the same memory usage measuring as what's in the sketch, which probably isn't usually a big deal unless the library holds a considerable buffer. Live and learn.
 
In case anyone else may be helped by this.

I tried creating a constructor and a specific function to initialize the array in RingBuffer, but it didn't help in the arduino IDE reporting memory usage.

But I went ahead and created the same size array before setup(), and initialized it in setup(). And when I set the array to 1024 elements and compile it, I get a warning "Low memory available, stability problems may occur."Beware using nynamic (run time) object creation and deletion (like New or malloc()). Better to use static objects if at all possible, instead of using the heap. The String class can be rough for a small micro, due to heap and coales

Apparently libraries don't get the same memory usage measuring as what's in the sketch, which probably isn't usually a big deal unless the library holds a considerable buffer. Live and learn.

Beware using dynamic (run time) object creation and deletion (like New or malloc()). Better to use static objects if at all possible, instead of using the heap. The String class can be rough for a small micro, due to heap and coalescing. Or if the object is short-lived, do it as a local variable off the stack (assuming the stack is large enough).
 
Beware using dynamic (run time) object creation and deletion (like New or malloc()). Better to use static objects if at all possible, instead of using the heap. The String class can be rough for a small micro, due to heap and coalescing. Or if the object is short-lived, do it as a local variable off the stack (assuming the stack is large enough).

Yeah I was trying to avoid that by creating everything at the start, and not deleting anything. I wanted a buffer just for holding data while it streams it. And yes avoiding strings all together.

The thing that got me, was that since the compiler should know that this is all created at the beginning, I thought the memory would be accounted for in usage. It is only accounted for, if it's in the sketch AND if the memory is initialized. But if it's in a library, in a .h or .cpp file, then it's not accounted for. I tried using "static" but it didn't make any difference. Oddly, malloc() or new was not accounted for either. I assume it is accounted for if the memory is initialized in setup(), but I didn't try that.
 
Does something like this end up creating dynamic allocation with strings?

Code:
for (int i=0;i<1024;i++) {
    val = buffer->read();
    Serial.print(val);
    Serial.print(",");
 }
Serial.println();
Serial.flush();
 
Compiler / linker knows how large the heap is, as that is given in the linker script file.
How much of the heap is in use, and how fragmented it is, requires use of software functions that are widely used to determine and print those numbers, and more. Coincidentally, I did just that yesterday - to get a printout of heap high/low watermarks as they vary in time. I needed to do this because of a purchased library using the heap (grr). This was on an ARM M4 but not a Teensy.

C++ programmers coming from GByte big-iron have to learn how to be frugal with RAM in the embedded world. Also speed consequences.

So a static class instance is well advised and hopefully those classes don't create objects using the heap, unless your app knows and copes.
 
This sketch only shows about 1k extra usage than an empty sketch if I do the intialization for loop in setup.

Code:
byte bytePtr[1024];

void setup() {
for (int i=0;i<1024;i++) bytePtr[i] = 0;
}

void loop() {
}

Code:
Sketch uses 11,420 bytes (17%) of program storage space. Maximum is 63,488 bytes.
Global variables use 2,208 bytes (26%) of dynamic memory, leaving 5,984 bytes for local variables. Maximum is 8,192 bytes.

//if I comment out for loop
Sketch uses 11,444 bytes (18%) of program storage space. Maximum is 63,488 bytes.
Global variables use 3,232 bytes (39%) of dynamic memory, leaving 4,960 bytes for local variables. Maximum is 8,192 bytes.

Well, that doesn't make any sense, that it would use *more* memory when you comment out the loop!

I tried it just now (on Arduino 1.6.9 with Teensyduino 1.29-beta1), and I got 2208 bytes without the for loop, and 3232 bytes with it.

Indeed the compiler is smart about detecting and automatically removing things that are never used. The bytePtr[1024] array isn't actually compiled into your program if no code actually accesses it.
 
Well, that doesn't make any sense, that it would use *more* memory when you comment out the loop!

I tried it just now (on Arduino 1.6.9 with Teensyduino 1.29-beta1), and I got 2208 bytes without the for loop, and 3232 bytes with it.

Indeed the compiler is smart about detecting and automatically removing things that are never used. The bytePtr[1024] array isn't actually compiled into your program if no code actually accesses it.

Yes, sorry I meant "if I removed the comment". What you described is what I was trying to say.

The compiler is pretty smart (now I know), but its smarts don't get applied to any libraries. With so many good libraries that I'm sure are used, that seems less than ideal. I'd have to copy/paste all the accessed library code into the sketch to get a good idea of memory usage.
 
Yes, sorry I meant "if I removed the comment". What you described is what I was trying to say.

The compiler is pretty smart (now I know), but its smarts don't get applied to any libraries. With so many good libraries that I'm sure are used, that seems less than ideal. I'd have to copy/paste all the accessed library code into the sketch to get a good idea of memory usage.

The Arduino libraries are compiled with the -ffunction-sections and -fdata-sections options passed to the compiler, and --gc-sections to the linker. This means if there is any function or global data object in the library which is not used, it will not be linked into the final image.
 
The Arduino libraries are compiled with the -ffunction-sections and -fdata-sections options passed to the compiler, and --gc-sections to the linker. This means if there is any function or global data object in the library which is not used, it will not be linked into the final image.

But the compiler doesn't report ANY memory usage in a library. The data was accessed in the library, and I also specifically added a constructor and a separate function to initialize the large array. But still it's not accounted for in the memory usage. I'll go ahead and create a library to demonstrate.
 
I created a library to demonstrate and apparently it's how you call the library. I'm sure it's apparent to someone here, but I don't know. I created a library, StealthMemory (least useful library ever). To start off, I followed the example of access that was in RingBuffer example code. I attached the library I created, which comes with the example.

View attachment StealthMemory.zip


Code:
#include <StealthMemory.h>

//StealthMemory sm;
StealthMemory *sm;

void setup() {
/*this faithfully tracks memory usage*/
//sm = StealthMemory();
//sm.initialize();

/*this does not track memory usage*/
sm = new StealthMemory();
sm->initialize();
}

void loop() {

}

edit: BTW, this was with arduino 1.6.5 and 1.6.8/teensyduino1.28
 
Last edited:
Status
Not open for further replies.
Back
Top