Hi everyone, I am facing an issue where my project completely locks up after several minutes after turning it on.
Brief overview of my device:
- SAM D21 mcu
- SPBTLE-RF BLE chip (SPI)
- 32KB FRAM (I2C)
- MAX30105 Heart rate sensor (I2C)
- MPU-9250 Accelerometer (I2C)
- SSD1306 OLED Display (I2C)
The code is currently all working fine. It just breaks after a couple minutes. This problem does not happen when I do not call the functions: countSteps and calculateHR() in my main loop.
To fix this problem, I could simply implement a watchdog timer which will reset the mcu but I rather not and wish to solve the root of the problem.
EDIT: The device seems to stop alot at time: 4:27 (almost 4 minutes after startup)
Code:
#include <U8g2lib.h>
#include <SparkFunMPU9250-DMP.h>
#include <RTCZero.h>
#include "MAX30105.h"
#include "heartRate.h"
#include <SparkFunBLEMate2.h>
#include "Adafruit_FRAM_I2C.h"
#include "avdweb_SAMDtimer.h"
#include <SPI.h>
#include <STBLE.h>
#include "esperto.h"
extern "C" char *sbrk(int i);
// Define display I2C bus
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);
// Define FRAM
Adafruit_FRAM_I2C FRAM = Adafruit_FRAM_I2C();
// Bluetooth and Clock Variables
char dateBT[15]; // date info MM/DD/YYYY
char timeBT[15]; // time info HH:MM:SS MM
char callBT[20]; // caller number info
char textBT[20]; // text number info
RTCZero rtc; // instance of RTCZero class
bool isRTCInit; // determines if RTC has been initialized
uint8_t ble_rx_buffer[21];
uint8_t ble_rx_buffer_len = 0;
uint8_t ble_connection_state = false;
int connected = FALSE;
volatile uint8_t set_connectable = 1;
uint16_t connection_handle = 0;
uint16_t UARTServHandle, UARTTXCharHandle, UARTRXCharHandle;
uint8_t sendBuffer[21];
uint8_t sendLength = 0;
byte lastSecondDisplayUpdated; // last second which display was updated (only update display every second)
// MPU9250
MPU9250_DMP imu;
float iir_Av = 0; // IIR filter average
const int stepDiffMinThreshold = 20; // minimum difference between step max and min for considered step
int stepCount = 0; // total number of steps taken (combination of dmp and gyro step counts)
int dmpSteps = 0; // total number of high frequency steps calculated by DMP
int gyroSteps = 0; // total number of low frequency steps calculated by gyroscope
double stepMax = 0; // peak of gyration data
double stepMin = 0; // trough of gyration data
double gyroData[3]; // array storing recent gyration readings
// Heart Rate Variables
MAX30105 heartRateSensor; // instance of MAX30105 class
const byte arraySizeHR = 5; // size of array containing latest HR values
byte heartRates[arraySizeHR]; // array containing latest HR values
byte heartRateIndex = 0; // index latest value was inputted into array
long prevHeartBeat = 0; // time at which the last heart beat occurred
float heartRate; // current heart rate
int heartRateAvg = 0; // average heart rate which will we be displayed
long timeLastHRBeat = 0;
// FRAM Variables
uint16_t countFRAM = 0; // stores the address which is going to be used by the FRAM
volatile uint8_t FRAM_ISR_CTR = 0; // counter used in addition to timer to determine when memory is stored
// FRAM Timer ISR -- write to FRAM every minute when not connected to device
void ISR_timer3(struct tc_module *const module_inst)
{
FRAM_ISR_CTR = FRAM_ISR_CTR + 1;
if(FRAM_ISR_CTR >= 60)
{
writeFRAM();
FRAM_ISR_CTR = 0;
}
}
SAMDtimer timer3_1Hz = SAMDtimer(3, ISR_timer3, 1e6); // FRAM timer interrupt
// Battery Variables
const uint8_t chargePin = 0; // TODO: needs to be implemented
const uint8_t batteryPin = 1; // analog 1: used to determine when battery is low/high
const float referenceVolts = 5.0; // the default reference on a 5-volt board
// Bootloader
void setup()
{
Serial1.begin(9600);
SerialUSB.begin(9600);
Wire.begin();
// Define displays I2C address and display boot screen
u8g2.begin();
u8g2.firstPage();
do {
u8g2.drawXBMP(32, 0, 64, 64, boot);
} while ( u8g2.nextPage() );
// setup MPU9250
init_MPU9250();
// initialize Real Time Clock
rtc.begin();
// Turn on and setup heart rate sensor
heartRateSensor.begin(Wire, I2C_SPEED_STANDARD); // 100 KHz
heartRateSensor.setup(); // configure sensor with default settings
heartRateSensor.setPulseAmplitudeRed(0x0A); // red LED to low to indicate sensor is running
heartRateSensor.setPulseAmplitudeGreen(0); // turn off Green LED
// Initialize FRAM and associated timer interrupt
timer3_1Hz.attachInterrupt(ISR_timer3);
FRAM.begin();
// Initialize STPBTLE-RF
BLEsetup();
}
// Function to update the display with latest information
void updateDisplay()
{
// if there is an incoming phone call
if (strlen(callBT) >= 10 && ble_connection_state == true)
{
u8g2.setFont(u8g2_font_profont11_tf);
// print time top left corner
u8g2.drawStr(0, 10, timeBT);
u8g2.setFont(u8g2_font_profont22_tf);
// display call text and phone number
u8g2.setCursor(40, 38);
u8g2.print("Call");
u8g2.drawStr(0, 58, callBT);
}
// if there is an incoming text
else if (strlen(textBT) >= 10 && ble_connection_state == true)
{
u8g2.setFont(u8g2_font_profont11_tf);
// print time top left corner
u8g2.drawStr(0, 10, timeBT);
u8g2.setFont(u8g2_font_profont22_tf);
// display text text and phone number
u8g2.setCursor(40, 38);
u8g2.print("Text");
u8g2.drawStr(0, 58, textBT);
}
// if no incoming call or text
else
{
u8g2.setFont(u8g2_font_profont11_tf);
// display date
if(ble_connection_state == true)
u8g2.drawStr(0, 10, dateBT);
else
{
u8g2.setCursor(0, 10);
u8g2.print(String(rtc.getMonth()) + "/" + String(rtc.getDay()) + "/20" + String(rtc.getYear()));
}
// display heart rate
u8g2.setCursor(14, 62);
u8g2.print(String(heartRateAvg) + " bpm");
u8g2.drawXBMP(0, 54, 10, 10, heart);
// display steps
u8g2.setCursor(78, 62);
u8g2.print(String(stepCount*2) + " stp");
u8g2.drawXBMP(64, 54, 10, 10, mountain);
// display time
u8g2.setFont(u8g2_font_profont22_tf);
// if connected to Bluetooth
if(ble_connection_state == true)
{
// center time on display
if (strlen(timeBT) == 7)
u8g2.drawStr(20, 38, timeBT);
else if (strlen(timeBT) == 8)
u8g2.drawStr(13, 38, timeBT);
}
// if not connected to Bluetooth
else
{
u8g2.setCursor(13, 38);
// time does not come with leading 0's, add to display string if needed
if(rtc.getMinutes() < 10 && rtc.getSeconds() < 10)
u8g2.print(String(rtc.getHours()) + ":0" + String(rtc.getMinutes()) + ":0" + String(rtc.getSeconds()));
else if(rtc.getMinutes() < 10)
u8g2.print(String(rtc.getHours()) + ":0" + String(rtc.getMinutes()) + ":" + String(rtc.getSeconds()));
else if(rtc.getSeconds() < 10)
u8g2.print(String(rtc.getHours()) + ":" + String(rtc.getMinutes()) + ":0" + String(rtc.getSeconds()));
else
u8g2.print(String(rtc.getHours()) + ":" + String(rtc.getMinutes()) + ":" + String(rtc.getSeconds()));
}
}
// draw BT logo only if connected
if(ble_connection_state == true)
u8g2.drawXBMP(118, 0, 10, 10, BT);
// obtain and display battery status
int battVoltRaw = analogRead(batteryPin);
float battVolt = (battVoltRaw / 1023.0) * referenceVolts;
// display full battery
if (battVolt >= 3.6)
u8g2.drawXBMP(105, 0, 10, 10, battHigh);
// display low battery
if (battVolt < 3.6)
u8g2.drawXBMP(105, 0, 10, 10, battLow);
}