Hi All,
I’m trying to troubleshoot an issue with my Electrons in which the onboard LED breathes cyan as if everything was working fine, but the code doesn’t seem to run. I put a println
in the setup()
but when this problem occurs (which is intermittent) it doesn’t come out on the console. The Electron has attached to it: two I2C sensors, an SPI SD card reader, and 2 RGB 1050 LEDs.
My guess is that SYSTEM_THREAD(ENABLED);
might be causing this, but am unsure of how to confirm this and how to get around it.
Suggestions or similar experiences are very much appreciated.
Thanks!
EDIT: posted code below
// ENABLE MULTI THRADING
SYSTEM_THREAD(ENABLED);
#include "KaiMMA8452Q/KaiMMA8452Q.h"
#include "math.h"
#include "SdFat/SdFat.h"
int16_t AcX,AcY,AcZ;
MMA8452Q accel; // Default MMA8452Q object create. (Address = 0x1D)
FuelGauge fuel; // object that gets battery data
int I2C_ADDRESS = 0x77; // bmp180 sensor address
const unsigned char oversampling_setting = 2; //oversampling for measurement
const unsigned char pressure_conversiontime[4] = {
5, 8, 14, 26
}; // delays for oversampling settings 0, 1, 2 and 3
// sensor registers from the BOSCH BMP085 datasheet
int ac1;
int ac2;
int ac3;
unsigned int ac4;
unsigned int ac5;
unsigned int ac6;
int b1;
int b2;
int mb;
int mc;
int md;
// variables to keep the values
int temperature = 0;
long pressure = 0;
// led pins
int greenLED1 = B0;
int redLED1 = B1;
int blueLED1 = B2;
int greenLED2 = B3;
int redLED2 = D2;
int blueLED2 = D3;
// sd card vars
const int chipSelect = A2;
SdFat sd;
File root;
File dataFile;
// read temperature and pressure from sensor
void readSensor() {
int ut= readUT();
long up = readUP();
long x1, x2, x3, b3, b5, b6, p;
unsigned long b4, b7;
//calculate true temperature
x1 = ((long)ut - ac6) * ac5 >> 15;
x2 = ((long) mc << 11) / (x1 + md);
b5 = x1 + x2;
temperature = (b5 + 8) >> 4;
// calculate true pressure
b6 = b5 - 4000;
x1 = (b2 * (b6 * b6 >> 12)) >> 11;
x2 = ac2 * b6 >> 11;
x3 = x1 + x2;
b3 = (((int32_t) ac1 * 4 + x3)<<oversampling_setting + 2) >> 2;
x1 = ac3 * b6 >> 13;
x2 = (b1 * (b6 * b6 >> 12)) >> 16;
x3 = ((x1 + x2) + 2) >> 2;
b4 = (ac4 * (uint32_t) (x3 + 32768)) >> 15;
b7 = ((uint32_t) up - b3) * (50000 >> oversampling_setting);
p = b7 < 0x80000000 ? (b7 * 2) / b4 : (b7 / b4) * 2;
x1 = (p >> 8) * (p >> 8);
x1 = (x1 * 3038) >> 16;
x2 = (-7357 * p) >> 16;
pressure = p + ((x1 + x2 + 3791) >> 4);
}
// read uncompensated temperature value
unsigned int readUT() {
writeRegister(0xf4,0x2e);
delay(5); // the datasheet suggests 4.5 ms
return readIntRegister(0xf6);
}
// read uncompensated pressure value
long readUP() {
writeRegister(0xf4,0x34+(oversampling_setting<<6));
delay(pressure_conversiontime[oversampling_setting]);
unsigned char msb, lsb, xlsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(0xf6); // register to read
Wire.endTransmission();
delay(2);
Wire.requestFrom(I2C_ADDRESS, 3); // request three bytes
while(!Wire.available()) {
Particle.process();
delay(2); // wait until data available
}
msb = Wire.read();
while(!Wire.available()) {
Particle.process();
delay(2); // wait until data available
}
lsb |= Wire.read();
while(!Wire.available()) {
Particle.process();
delay(2); // wait until data available
}
xlsb |= Wire.read();
return (((long)msb<<16) | ((long)lsb<<8) | ((long)xlsb)) >>(8-oversampling_setting);
}
void getCalibrationData() {
ac1 = readIntRegister(0xAA);
ac2 = readIntRegister(0xAC);
ac3 = readIntRegister(0xAE);
ac4 = readIntRegister(0xB0);
ac5 = readIntRegister(0xB2);
ac6 = readIntRegister(0xB4);
b1 = readIntRegister(0xB6);
b2 = readIntRegister(0xB8);
mb = readIntRegister(0xBA);
mc = readIntRegister(0xBC);
md = readIntRegister(0xBE);
}
void writeRegister(unsigned char r, unsigned char v)
{
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(r);
Wire.write(v);
Wire.endTransmission();
}
// read a 16 bit register
int readIntRegister(unsigned char r)
{
unsigned char msb, lsb;
Wire.beginTransmission(I2C_ADDRESS);
Wire.write(r); // register to read
Wire.endTransmission();
delay(2);
Wire.requestFrom(I2C_ADDRESS, 2); // request two bytes
while(!Wire.available()) {
Particle.process();
delay(2);
} // wait until data available
msb = Wire.read();
while(!Wire.available()) {
Particle.process();
delay(2);
} // wait until data available
lsb = Wire.read();
return (((int)msb<<8) | ((int)lsb));
}
const int maxAvgCount = 4;
float AcXAvg = 0; float AcYAvg = 0; float AcZAvg = 0; long pAvg = 0; float vectorSumAvg = 0;
float adjAcXAvg = 0; float adjAcYAvg = 0; float adjAcZAvg = 0; long adjPAvg = 0; // float adjVectorSum = 0;
float AcXArray[maxAvgCount]; float AcYArray[maxAvgCount]; float AcZArray[maxAvgCount];
long pArray[maxAvgCount];
float vectorSumArray[maxAvgCount];
float adjAcXArray[maxAvgCount]; float adjAcYArray[maxAvgCount]; float adjAcZArray[maxAvgCount];
long adjPArray[maxAvgCount];
int avgCount = 0;
bool firstAvg = false;
void smoothData(float ax, float ay, float az) { // , float gx, float gy, float gz) {
if (avgCount < maxAvgCount && firstAvg == false) {
// add the first values to arrays
if (avgCount < maxAvgCount) {
AcXArray[avgCount] = ax;
AcYArray[avgCount] = ay;
AcZArray[avgCount] = az;
pArray[avgCount] = pressure;
vectorSumArray[avgCount] = sqrt(pow(ax, 2) + pow(ay, 2) + pow(az, 2));
}
avgCount++;
}
if (avgCount == maxAvgCount && firstAvg == false) { // calculate rolling average
for (int i=0; i < avgCount; i++) {
if (i == 0) {
AcXAvg = AcXArray[i];
AcYAvg = AcYArray[i];
AcZAvg = AcZArray[i];
pAvg = pArray[i];
vectorSumAvg = vectorSumArray[i];
} else {
AcXAvg = AcXAvg + AcXArray[i];
AcYAvg = AcYAvg + AcYArray[i];
AcZAvg = AcZAvg + AcZArray[i];
pAvg = pAvg + pArray[i];
vectorSumAvg = vectorSumAvg + vectorSumArray[i];
}
}
// not checking for division by zero
AcXAvg = AcXAvg/avgCount;
AcYAvg = AcYAvg/avgCount;
AcZAvg = AcZAvg/avgCount;
pAvg = pAvg/avgCount;
vectorSumAvg = vectorSumAvg/avgCount;
firstAvg = true;
}
else if (avgCount == maxAvgCount && firstAvg == true) {
// shift values in original arrays and add newest to end
float tempAcXArray[avgCount]; float tempAcYArray[avgCount]; float tempAcZArray[avgCount];
float tempPArray[avgCount];
float tempVectorSumArray[avgCount];
for (int i = 0; i < avgCount; i++) {
if (i < maxAvgCount - 1) {
tempAcXArray[i] = AcXArray[i+1];
tempAcYArray[i] = AcYArray[i+1];
tempAcZArray[i] = AcZArray[i+1];
tempPArray[i] = pArray[i+1];
tempVectorSumArray[i] = vectorSumArray[i+1];
} else {
tempAcXArray[i] = ax;
tempAcYArray[i] = ay;
tempAcZArray[i] = az;
tempPArray[i] = pressure;
tempVectorSumArray[i] = sqrt(pow(ax, 2) + pow(ay, 2) + pow(az, 2));
}
}
for (int i = 0; i < avgCount; i++) {
AcXArray[i] = tempAcXArray[i];
AcYArray[i] = tempAcYArray[i];
AcZArray[i] = tempAcZArray[i];
pArray[i] = tempPArray[i];
vectorSumArray[i] = tempVectorSumArray[i];
}
// recalculate averages
// sum values in temp arrays
for (int i = 0; i < avgCount; i++) {
if (i == 0) {
AcXAvg = AcXArray[i];
AcYAvg = AcYArray[i];
AcZAvg = AcZArray[i];
pAvg = pArray[i];
vectorSumAvg = vectorSumArray[i];
} else {
AcXAvg = AcXAvg + AcXArray[i];
AcYAvg = AcYAvg + AcYArray[i];
AcZAvg = AcZAvg + AcZArray[i];
pAvg = pAvg + pArray[i];
vectorSumAvg = vectorSumAvg + vectorSumArray[i];
}
}
// not checking for division by zero
AcXAvg = AcXAvg/avgCount;
AcYAvg = AcYAvg/avgCount;
AcZAvg = AcZAvg/avgCount;
pAvg = pAvg/avgCount;
vectorSumAvg = vectorSumAvg/avgCount;
// calculate adjusted values and store in adjArrays
for (int i = 0; i < avgCount; i++) {
// check if addition or subtraction is needed
if (AcXArray[i] >= 0 && AcXAvg >= 0) {
adjAcXArray[i] = abs(AcXArray[i] - AcXAvg);
} else {
adjAcXArray[i] = AcXArray[i] - AcXAvg;
}
if (AcYArray[i] >= 0 && AcYAvg >= 0) {
adjAcYArray[i] = abs(AcYArray[i] - AcYAvg);
} else {
adjAcYArray[i] = AcYArray[i] - AcYAvg;
}
if (AcZArray[i] >= 0 && AcZAvg >= 0) {
adjAcZArray[i] = abs(AcZArray[i] - AcZAvg);
} else {
adjAcZArray[i] = AcZArray[i] - AcZAvg;
}
if (pArray[i] >= 0 && pAvg >= 0) {
adjPArray[i] = abs(pArray[i] - pAvg);
} else {
adjPArray[i] = pArray[i] - pAvg;
}
}
// calculate adjusted averages
for (int i=0; i < avgCount; i++) {
if (i == 0) {
adjAcXAvg = adjAcXArray[i];
adjAcYAvg = adjAcYArray[i];
adjAcZAvg = adjAcZArray[i];
adjPAvg = adjPArray[i];
} else {
adjAcXAvg = adjAcXAvg + adjAcXArray[i];
adjAcYAvg = adjAcYAvg + adjAcYArray[i];
adjAcZAvg = adjAcZAvg + adjAcZArray[i];
adjPAvg = adjPAvg + adjPArray[i];
}
}
adjAcXAvg = adjAcXAvg/avgCount;
adjAcYAvg = adjAcYAvg/avgCount;
adjAcZAvg = adjAcZAvg/avgCount;
adjPAvg = adjPAvg/avgCount;
detectWave(adjAcXAvg, adjAcYAvg, adjAcZAvg);
}
}
int waveHappened = 0;
int eventsRequired = 6;
int waveCountX = 0;
int waveCountY = 0;
int waveCountZ = 0;
bool firstEventDetected = false;
bool lastEventOverThresh = false;
unsigned long lastEventTime;
long minEventTime = 200;
long maxEventTime = 6000;
long maxWaveTime = 7000;
int aThresh = 220;
void detectWave(float axAvg, float ayAvg, float azAvg) {
// check for wave event
squeezeDetect(adjPAvg);
}
void logWave(){
sdLogEvent(String("0," + Time.timeStr()));
pubLiveData(String("0," + Time.timeStr()));
Serial.print("Wave: "); Serial.println(Time.timeStr());
}
// set timer to block squeezeEvents from happening too quick of succession
unsigned long lastSqueezeEventTime;
long nextSqueezeAllowedIn = 4000;
int squeezeThresh = 250;
long squeezeStartedAt;
int squeezeEventDetected = 0;
void squeezeDetect(long p) {
// look for squeeze event
tapDetect();
}
void logSqueeze(){
sdLogEvent(String("1," + Time.timeStr()));
pubLiveData(String("1," + Time.timeStr()));
Serial.print("Squeeze: "); Serial.println(Time.timeStr());
}
int tap = 0;
void tapDetect() {
tap = accel.readTap();
if (tap > 0 && waveCountX < eventsRequired && waveCountY < eventsRequired && waveCountZ < eventsRequired) {
logTap();
tapOutput(1);
waveCountX = 0;
waveCountY = 0;
waveCountZ = 0;
}
}
void logTap(){
sdLogEvent(String("2," + Time.timeStr()));
pubLiveData(String("2," + Time.timeStr()));
Serial.print("Tap: "); Serial.println(Time.timeStr());
}
void tapOutput(int c) {
int cnt = 0;
while(cnt < c) {
for (int i = 0; i <= 200; i++) {
analogWrite(blueLED1, i);
analogWrite(blueLED2, i);
delay(7);
}
for (int i = 200; i >= 0; i--) {
analogWrite(blueLED1, i);
analogWrite(blueLED2, i);
delay(7);
}
cnt++;
}
}
void squeezeOutput(int c) {
int cnt = 0;
while(cnt < c) {
for (int i = 0; i <= 200; i++) {
analogWrite(redLED1, i);
analogWrite(redLED2, i);
delay(7);
}
for (int i = 200; i >= 0; i--) {
analogWrite(redLED1, i);
analogWrite(redLED2, i);
delay(7);
}
cnt++;
}
}
void waveOutput (int c) {
int cnt = 0;
while(cnt < c) {
for (int i = 0; i <= 200; i++) {
// analogWrite(redLED, i);
analogWrite(greenLED1, i);
analogWrite(greenLED2, i);
delay(7);
}
for (int i = 200; i >= 0; i--) {
analogWrite(greenLED1, i);
analogWrite(greenLED2, i);
delay(7);
}
cnt++;
}
}
char msgType;
char msgID;
void getAlertFromOtherBlink(const char *event, const char *data) {
sdLogEvent(data);
char* saveptr; // pointer that will store strtok_r place in the string it's parsing LOCALLY instead of globally like strtok
char* copy = strndup(data, strlen(data)); // strndup instead of strdup because it's safe to know exactly how much data to allocate
int message = atoi(strtok_r(copy, ",", &saveptr)); // strtok_r instead of strtok because it uses a local pointer instead of a global
char* id = strtok_r(NULL, ",", &saveptr); // strtok_r instead of strtok because it uses a local pointer instead of a global
char cpyoftnow[25]; // standard length of a ISO timestamp from Time.timeStr();
snprintf(cpyoftnow, sizeof(cpyoftnow), "%s", (const char*)Time.timeStr()); //snprintf instead of strcpy because it knows exactly how many characters to copy over and includes Null termination
char buffer[strlen(id) + strlen(cpyoftnow) + 2]; // create a buffer that's the length of the id, timestamp, and 2 more spaces for a comma and NULL terminator
snprintf(buffer, sizeof(buffer), "%s,%s", id, cpyoftnow); //snprintf instead of strcpy because it knows exactly how many characters to copy over and includes Null termination
Particle.publish("topicname", buffer, 60, PRIVATE);
free(copy);
if (message == 0) {
waveOutput(3);
} else if (message == 1) {
squeezeOutput(3);
} else if (message == 2) {
tapOutput(3);
}
}
int fallHappened = 0;
int impactHappened = 0;
const int fallThreshold = 350;
const int vSumThresh = 2000;
void detectFall(float ax, float ay, float az) {
float vectorSum = sqrt(pow(ax, 2) + pow(ay, 2) + pow(az, 2));
if (vectorSum < fallThreshold) {
fallHappened = 1;
impactHappened = 0;
logFall();
} else if (fallHappened == 1 && vectorSum > vSumThresh) {
impactHappened = 1;
fallHappened = 0;
logImpact();
} else {
fallHappened = 0;
impactHappened = 0;
}
}
void logFall(){
sdLogEvent(String("3," + Time.timeStr()));
Serial.print("Fall: "); Serial.println(Time.timeStr());
}
void logImpact(){
sdLogEvent(String("4," + Time.timeStr()));
Serial.print("Impact: "); Serial.println(Time.timeStr());
}
unsigned int inMotion = 0;
unsigned int motionThreshold = 1080;
unsigned long motionStartMillis = 0;
unsigned long motionLatencyMillis = 1000;
void detectMotion(float ax, float ay, float az){
long millisDiff = millis() - motionStartMillis;
if (inMotion == 0 && vectorSumAvg > motionThreshold && motionStartMillis == 0 && millisDiff > motionLatencyMillis) {
motionStartMillis = millis();
logMotion("5,");
inMotion = 1;
} else if (inMotion == 1 && vectorSumAvg < motionThreshold && motionStartMillis > 0 && millisDiff > motionLatencyMillis) {
motionStartMillis = 0;
logMotion("6,");
inMotion = 0;
} else if (inMotion == 1 && vectorSumAvg > motionThreshold && millisDiff < motionLatencyMillis) {
motionStartMillis = millis();
}
}
void logMotion(String m){
sdLogEvent(String(m + Time.timeStr()));
Serial.print(m); Serial.println(Time.timeStr());
}
int lastOrientation = 0;
bool firstOrientationLogged = false;
void logOrientation() {
// is the current orientation the same as the last
int currentOrientation = accel.readPL();
if (firstOrientationLogged == false) {
lastOrientation = currentOrientation;
firstOrientationLogged = true;
} else if (firstOrientationLogged == true && lastOrientation != currentOrientation) {
sdLogEvent(String("7," + String(currentOrientation) + "," + Time.timeStr()));
Serial.print("Orientation changed to "); Serial.print(currentOrientation); Serial.print(": "); Serial.println(Time.timeStr());
lastOrientation = currentOrientation;
}
}
const float maxCharge = 77.0;
const float minCharge = 10.0;
float lastBatteryPercentage = 0.0;
bool battLow = false;
bool battFull = false;
bool chargingBatt = false;
void checkBattery() {
float batteryPercentage = fuel.getSoC();
if (batteryPercentage >= lastBatteryPercentage) {
chargingBatt = true;
} else if (batteryPercentage < lastBatteryPercentage) {
chargingBatt = false;
}
if (batteryPercentage > maxCharge) {
battFull = true;
battLow = false;
} else if (batteryPercentage <= minCharge) {
battLow = true;
battFull = false;
} else if (batteryPercentage < maxCharge && batteryPercentage > minCharge) {
battLow = false;
battFull = false;
}
lastBatteryPercentage = batteryPercentage;
}
void logBattery(){
float batteryPercentage = fuel.getSoC();
float voltage = fuel.getVCell();
String ds = String(String(batteryPercentage) + "," + String(voltage) + "," + Time.timeStr());
sdLogEvent(ds);
pubBattData(ds);
Serial.print(batteryPercentage); Serial.print("%,"); Serial.print(voltage); Serial.print(","); Serial.println(Time.timeStr());
}
int lastNetStatus = 0;
void logNetStatus(){
int netStatus = Cellular.ready();
if (lastNetStatus == 1 && netStatus == 0) {
// if it was connected but isn't now
lastNetStatus = 0;
sdLogEvent(String("8," + Time.timeStr()));
Serial.print("Network Disconnected: "); Serial.println(Time.timeStr());
} else if (lastNetStatus == 0 && netStatus == 1) {
// if it was NOT connected but is now
lastNetStatus = 1;
sdLogEvent(String("9," + Time.timeStr()));
}
}
// id the last file in dir and return it, or open a new one if its size is over the limit
unsigned long maxFileSize = 1000000; // in bytes 1000000 = 1mb
void readDirectory() {
root.rewindDirectory();
String fExt = ".txt";
int fileCounter = 0;
unsigned long lastFileSize = 0;
String lastFileName;
while (true) {
File f = root.openNextFile();
if (!f && fileCounter == 0) { // there are no files present at all
f.close();
String fileIndex = String(fileCounter);
String fileName = String(fileIndex + fExt);
dataFile = sd.open(fileName, FILE_WRITE);
// Serial.print("no files present. new file name: "); dataFile.printName(); Serial.println();
break;
} else if (!f && fileCounter > 0 && lastFileSize > maxFileSize) { // no more files, the last file is too big to open
f.close();
String fileIndex = String(fileCounter);
String fileName = String(fileIndex + fExt);
dataFile = sd.open(fileName, FILE_WRITE);
// Serial.print("no more files to count and last file too big to open. new file name: "); dataFile.printName(); Serial.println();
break;
} else if (!f && fileCounter > 0 && lastFileSize < maxFileSize) { // no more files, the last file is small enough to open
String fniString = String(fileCounter - 1);
String fileName = String(fniString + fExt);
f.close();
dataFile = sd.open(fileName, FILE_WRITE);
// Serial.print("lastFileSize: "); Serial.println(lastFileSize);
// Serial.print("last file was small enough to use again. file name: "); Serial.println(fileName);
break;
} else {
fileCounter++;
lastFileSize = f.size();
}
}
}
bool battMessageSent = false;
bool receivedBattNotification = false;
void pubBattData (String dataString) {
Particle.publish("topicname", dataString, PRIVATE);
Serial.println("publishing battdata...");
}
bool eventMessageSent = false;
bool receivedEventNotification = false;
void pubLiveData(String dataString) {
Particle.publish("topicname", dataString, PRIVATE);
Serial.println("publishing live event data...");
}
void sdLogEvent(String dataString) {
// if the file is available, write to it:
if (dataFile) {
dataFile.println(dataString);
dataFile.flush();
} else {
Serial.print("error opening "); Serial.println(dataFile.printName());
}
}
void setup() {
Serial.println("test");
pinMode(greenLED1, OUTPUT);
pinMode(redLED1, OUTPUT);
pinMode(blueLED1, OUTPUT);
pinMode(greenLED2, OUTPUT);
pinMode(redLED2, OUTPUT);
pinMode(blueLED2, OUTPUT);
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}
accel.begin(SCALE_2G, ODR_800); // Init and customize the FSR and ODR
accel.setupTap(0x2F, 0x2F, 0x2F, 0x1E, 0x32, 0xC8);
accel.setupPL();
getCalibrationData();
lastSqueezeEventTime = millis();
String sysId = System.deviceID();
String topicName = String("topicname/" + sysId);
Particle.subscribe(topicName, getAlertFromOtherBlink, MY_DEVICES); // subscribes to the M2X server response from other Blink
Particle.publish("topicname", "restart", PRIVATE);
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!sd.begin(chipSelect)) {
Serial.println("Card failed, or not present");
return;
}
Serial.println("card initialized.");
root = sd.open("/");
readDirectory();
}
bool msgToSend = 0;
bool battLowLedOn = false;
unsigned long previousMillis = 0;
const long interval = 300000;
void loop() {
if (accel.available()) {
accel.read(); // Update acceleromter data
AcX = accel.x;
AcY = accel.y;
AcZ = accel.z;
}
if (dataFile.size() > maxFileSize) {
readDirectory();
}
readSensor();
smoothData(AcX, AcY, AcZ);
detectFall(AcX, AcY, AcZ);
detectMotion(AcX, AcY, AcZ);
logOrientation();
logNetStatus();
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
logBattery();
previousMillis = currentMillis;
}
checkBattery();
delay(25);
}