General device info:
name/channel: spark/device/last_reset
msg: panic, assert_failed
SOS blink pattern: 10 blinks between SOS -> Assertion Failure
Frequency of occurrence: approximately every 1-2 minutes
Device OS version(s): v0.7.0 and v0.8.0-rc11
Devices: Photon
Does anyone have any suggestions for what might cause an assertion failure? Or where to begin debugging? The message is rather cryptic as to what 'failed to assert'?
I can flash another program that achieves the same objective and not have this issue, so the board(s) seem to be O.K.
The BaseComponents.hpp/cpp is the same as with the prior version. The main difference is that this version makes extensive use of std::queue, std::vector, std::string, and std::array. I was hoping to simplify the code by relying on the standard libraries, but that may not be the case? Although, on a cortex-m3, I feel like my code shouldn't be to complicated for it to execute every couple seconds?
Here is the main code body (AlphaMonitor.ino):
/*
* Project AlphaMonitor
* Description:
* Author:
* Date:
*/
#include <queue> // Using for queues
#include <string> // Using for std::string
#include <array> // Using for easy array management
#include <vector>
#include "BaseComponents.hpp"
char HW_VERSION[] = "v0.0.1";
char USE_CLASS[] = "Heater_Monitor";
char MSG_PROTOCOL[] = "v0.2";
unsigned long lastTime = 0UL;
char UPTIME[12];
unsigned long lastTimeThermocouple = 0UL;
unsigned long lastTimeRTD = 0UL;
unsigned long lastTimeLevel = 0UL;
unsigned long lastTimeModbus = 0UL;
unsigned long lastTimeGPIO = 0UL;
std::array<TemperatureSensor, 2> tempArr;
std::array<LevelSensor, 2> levelArr;
std::queue<std::string> statusQueue;
std::queue<std::string> alarmQueue; // Not used in program yet
// reset the system after 10 minutes if the application is unresponsive
ApplicationWatchdog wd(600000, System.reset, 512);
// Verify if device has a cloud connection, uses data each time called
void checkConnection() {
// reset device after this many seconds pass without a connection
static const int reconnectTimeSecs = 120;
static long lastFailTime = 0;
long currentTime = millis();
bool connected = Particle.connected();
if(connected) {
// connected, all is well
lastFailTime = 0;
}
else if(!connected && lastFailTime == 0) {
// connection has just gone down, note the time
lastFailTime = currentTime;
}
else if(!connected && ((currentTime-lastFailTime) >= (reconnectTimeSecs*1000))) {
// connection has been down for too long
System.reset();
}
}
// Obtains device name (human readable) from the Particle Cloud
// *********************************WARNING*********************************
// **********************************NOTES**********************************
// Particle.subscribe("particle/device/name", setName)
// Particle.publish("particle/device/name");
char myName[255];
void setName(const char *topic, const char *name){ // Ignoring topic
strcpy(myName, name);
}
// setup() runs once, when the device is first turned on.
void setup() {
bool success = false;
for(uint8_t i = 0; i < tempArr.size(); i++){
tempArr[i].setHysteresis(5);
}
for(uint8_t i = 0; i < tempArr.size(); i++){
levelArr[i].setHysteresis(5);
}
Particle.subscribe("particle/device/name", setName);
Particle.publish("particle/device/name"); // Asking to have name sent
delay(1000);
}
void loop() {
unsigned long now = millis();
if (now-lastTime>4000UL) {
lastTime = now;
// now is in milliseconds
unsigned nowSec = now/1000UL;
unsigned sec = nowSec%60;
unsigned min = (nowSec%3600)/60;
unsigned hours = (nowSec%86400)/3600;
sprintf(UPTIME,"%u:%u:%u",hours,min,sec);
}
// Update all sensor readings every 3 seconds (simulated)
if(now-lastTimeGPIO > (3*1000UL)){
for(uint8_t i = 0; i < tempArr.size(); i++){
tempArr[i].setValue(rand() % 500 + 1);
}
for(uint8_t i = 0; i < levelArr.size(); i++){
levelArr[i].setValue(rand() % 375 + 1);
}
}
// Determine what messages need to be sent
// We want to send a thermocouple reading at least every 5 min.
// This section is not currently implemented
if(now-lastTimeThermocouple>(5*60*1000UL)){
// Queue all
lastTimeThermocouple = now;
}
// We want to send a RTD reading at least every 5 min.
if(now-lastTimeRTD>(5*60*1000UL)){
// Queue all
lastTimeRTD = now;
}
// We want to send a level reading at least every 5 min.
if(now-lastTimeLevel>(5*60*1000UL)){
// Queue all
lastTimeLevel = now;
}
// We want to send a modbus reading at least every 10 min.
if(now-lastTimeModbus>(10*60*1000UL)){
// Queue all
lastTimeModbus = now;
}
delay(100);
// Check if sensors have new data that needs to be sent
std::queue<std::string> tempMSGs;
for(uint8_t i = 0; i < tempArr.size(); i++){
char buf[15];
if(tempArr[i].newData()){
// Queue for publish
std::string msg;
msg += "{\"i\":";
snprintf(buf, 15, "%d", i);
msg += buf;
msg += ",\"v\":";
snprintf(buf, 15, "%.1f", tempArr[i].getReading());
msg += buf;
msg += "}";
tempMSGs.push(msg);
}
}
std::string tempBranch;
if(tempMSGs.size() > 0){
tempBranch += "\"THERMOCOUPLE\":[";
for(uint8_t i = 0; i < tempArr.size(); i++){
tempBranch += tempMSGs.front();
tempMSGs.pop();
if(tempMSGs.size() > 0){
tempBranch += ",";
}
}
tempBranch += "]";
}
delay(100);
//Particle.publish("STATUS", tempBranch.c_str(), PRIVATE);
std::queue<std::string> levelMSGs;
for(uint8_t i = 0; i < levelArr.size(); i++){
char buf[15];
if(levelArr[i].newData()){
// Queue for publish
std::string msg;
msg += "{\"i\":";
snprintf(buf, 15, "%d", i);
msg += buf;
msg += ",\"v\":";
snprintf(buf, 15, "%.1f", levelArr[i].getReading());
msg += buf;
msg += "}";
levelMSGs.push(msg);
}
}
std::string levelBranch;
if(levelMSGs.size() > 0){
levelBranch += "\"LEVEL\":[";
for(uint8_t i = 0; i < levelArr.size(); i++){
levelBranch += levelMSGs.front();
levelMSGs.pop();
if(levelMSGs.size() > 0){
levelBranch += ",";
}
}
levelBranch += "]";
}
// Aggregate MSGs
if((tempBranch.size() + levelBranch.size()) > 5){
std::string outboundPacket;
outboundPacket += "{";
outboundPacket += tempBranch;
outboundPacket += ",";
outboundPacket += levelBranch;
outboundPacket += "}";
Particle.publish("STATUS", outboundPacket.c_str(), PRIVATE);
}
delay(500);
}
BaseComponents.hpp
#ifndef BASECOMPONENTS_HPP
#define BASECOMPONENTS_HPP
/*
class BaseSensor {
public:
virtual bool newData(void){};
virtual bool updateSensor(void){};
virtual float getReading(void){};
virtual void setHysteresis(float hysteresis){};
virtual float getHysteresis(void){};
};
*/
class TemperatureSensor {
//enum sensorCatType {J, K, T, N, E, B, R, S, RTD, Transmitter, unused};
enum sensorCatType {J = 'J', K = 'K', T = 'T', N = 'N', E = 'E', B = 'B', R = 'R', S = 'S', unused = 'u'}; // Currently does not support using a RTD or Transmitter
private:
sensorCatType _sensorType;
float _temperatureValue_C;
bool _newData;
//float _hysteresisLow; // Low side hysteresis
//float _hysteresisHigh; // High side hysteresis
float _hysteresis;
//float _highAlarm;
//float _lowAlarm;
//String _deviceName // Used to set what the device is used for
protected:
void updateData(float data){
if(data > (_temperatureValue_C + _hysteresis) || data < (_temperatureValue_C - _hysteresis)){
_newData = true;
_temperatureValue_C = data;
}
else{
// Ignoring write b/c of hysteresis
}
};
public:
//TemperatureSensor()
void setSensorType(char type); // Sets the type of sensor used
char getSensorType(void){return(_sensorType);}; // Returns the type of sensor used
bool newData(void){return(_newData);}; // Returns true if new data is available
bool updateSensor(void); // Reads the physical sensor, and if outside of previous value with hysteresis, records
float getReading(void); // Returns the stored reading
//void setHysteresisLow(float hLow){_hysteresisLow = hLow;};
//void setHysteresisHigh(float hHigh){_hysteresisHigh = hHigh;};
//void setHysteresis(float hysteresis){_hysteresisLow = hysteresis; _hysteresisHigh = hysteresis;};
void setHysteresis(float hysteresis){_hysteresis = hysteresis;};
float getHysteresis(void){return(_hysteresis);};
// Testing/Debug functions, remove once physical hardware available
void setValue(float val){updateData(val);};
};
class LevelSensor {
private:
float _level_m;
float _hysteresis_m;
bool _newData;
protected:
void updateData(float data){
if(data > (_level_m + _hysteresis_m) || data < (_level_m - _hysteresis_m)){
_newData = true;
_level_m = data;
}
else{
// Ignoring write b/c of hysteresis
}
};
public:
bool newData(void){return(_newData);};
bool updateSensor(void);
float getReading(void);
void setHysteresis(float hysteresis){_hysteresis_m = hysteresis;};
float getHysteresis(void){return(_hysteresis_m);};
// Testing/Debug functions, remove once physical hardware available
void setValue(float val){updateData(val);};
};
#endif /* BASECOMPONENTS_H */
BaseComponents.cpp
#include "BaseComponents.hpp"
void TemperatureSensor::setSensorType(char type){
switch(type) {
case 'J': {
_sensorType = J;
// Program thermocouple sensor to use type J
break;
}
case 'K': {
_sensorType = K;
// Program thermocouple sensor to use type K
break;
}
case 'T': {
_sensorType = T;
// Program thermocouple sensor to use type T
break;
}
case 'N': {
_sensorType = N;
// Program thermocouple sensor to use type N
break;
}
case 'E': {
_sensorType = E;
// Program thermocouple sensor to use type E
break;
}
case 'B': {
_sensorType = B;
// Program thermocouple sensor to use type B
break;
}
case 'R': {
_sensorType = R;
// Program thermocouple sensor to use type R
break;
}
case 'S': {
_sensorType = S;
// Program thermocouple sensor to use type S
break;
}
/*
case '1': {
_sensorType = RTD;
// Configure system to read/use a RTD
break;
}
case '2': {
_sensorType = Transmitter;
// Configure system to read/use a Transmitter
break;
}
*/
default: {
_sensorType = unused;
break;
}
}
}
float TemperatureSensor::getReading(void){
_newData = false;
return(_temperatureValue_C);
}
float LevelSensor::getReading(void){
_newData = false;
return(_level_m);
}