Hello!
I have made an app that controls LED dimming via PWM frequency, after the hours of the sun. It also measures power usage, + temperature of the casing where it is sitting. PWM frequency (light strength) can be controlled via MQTT from Ubidots.
I currently have it running on two devices, one argon and one boron. On separate locations, and very steady power sources. The Boron also has a battery connected. They are both on 0.9.0 but it also happened on earlier firmware.
Everything works fine, but i am experiencing very random reboots. Sometimes it runs fine for days (3-4-5 days) with no reboots, and sometimes it has 1-3 reboots a day. It sends an MQTT message to ubidots in the setup function (including the reset reason), thats how i know when it last reboot.
It happens on both units, but not at the same times.
I have enable “Reset Reason” - but it gives me “USER” as reason. What can trigger a reset with USER as reason? Any idea where i should start looking?
// This #include statement was automatically added by the Particle IDE.
#include <UbidotsMQTT.h>.
#include <Sunrise.h>
// -----------------------------------------------------------------
// Sign Control v0.1 - Hardware 0.1
// -----------------------------------------------------------------
/****************************************
* Enable reset info
****************************************/
STARTUP(System.enableFeature(FEATURE_RESET_INFO));
char resetreason[20];
/****************************************
* Instances for MQTT
****************************************/
#ifndef TOKEN
#define TOKEN "###############" // Add here your Ubidots TOKEN
#endif
UbidotsMQTT client(TOKEN, callback);
/****************************************
* Different timed routines
****************************************/
// Initiate value for daily updates routine
#define ONE_DAY_MILLIS (24 * 60 * 60 * 1000)
unsigned long lastSync_oneday = millis();
// Initiate value for minute updates routines
#define ONE_MINUTE_MILLIS (60 * 1000)
unsigned long lastSync_minute = millis();
// minimum time between publish
#define PUBLISH_MINIMUM (1000)
unsigned long lastpublish = millis();
// minimum time before reset initiated from cloud
//This ensures that the device can report back to the cloud
#define RESET_WAIT (15 * 1000)
unsigned long resetinit = millis();
bool resetFlag = false;
#define CLOUD_WAIT (600 * 1000)
unsigned long cloudinit = millis();
int cloudinit_faultcount = 0;
/****************************************
* GLOBAL VARIABLES
****************************************/
// We're going to start by declaring which pins everything is plugged into.
int led = D2; // PWM Output pin for LED
int volt = A5; // Input pin from voltage divider
int amp = A4; // Input pin from power measure
int temp = A3; // Input pin from temp measure
// Voltage divider settings
float resistor1 = 220000.00; // R1 Resistor value
float resistor2 = 33000.00; // R2 Resistor value
// Setting variables for pwm duty cycle
int lightvalue_old = 0;
int lightvalue;
// Default variables in case cloud is unavailable
int value_min = 45;
int value_mid1 = 100;
int value_mid2 = 160;
int value_max = 220;
int value;
int hertz = 500;
// Variables for power consumption
float amp_readout;
char amp_readout_string[20];
int amp_calibration = 0;
float volt_readout;
char volt_readout_string[20];
float watt_readout;
char watt_readout_string[20];
float temp_readout;
char temp_readout_string[20];
// Variables for sunset /sunrise
int sunrisetime;
int sunsettime;
int timeaftermidnight;
char publishString[40];
// Wifi/cellular global
int signalstrength = -1;
/****************************************
* Get device ID
****************************************/
char* deviceid = string2char(System.deviceID());
/****************************************
* Now for sunrise.h - we create an object for summer and wintertime
****************************************/
Sunrise winterSunrise(63.367948,10.371597, +1); // Trondheim - Tiller - Latitude/Longitude and UTC offset
Sunrise summerSunrise(63.367948,10.371597,+2); // Trondheim - Tiller - Latitude/Longitude and UTC offset
/****************************************
* Software Watchdog
****************************************/
ApplicationWatchdog wd(60000, System.reset);
// Next we go into the setup function.
void setup()
{
// This is here to allow for debugging using the USB serial port
// Serial.begin();
// First, declare all of our pins.
pinMode(led, OUTPUT); // Our LED pin is output (lighting up the LED)
pinMode(volt, INPUT); // Voltage read input
pinMode(amp, INPUT); // Ampere read input
/****************************************
* Wifi or cellular variables
****************************************/
#if Wiring_WiFi
waitUntil(WiFi.ready);
delay(1000);
char network[64];
strcpy(network, WiFi.SSID());
IPAddress local_ip = WiFi.localIP();
#endif
#if Wiring_Cellular
waitUntil(Cellular.ready);
delay(1000);
// SYNTAX
CellularSignal sig = Cellular.RSSI();
char network[18];
strcpy(network, "UNKNOWN");
switch( sig.getAccessTechnology() )
{
case NET_ACCESS_TECHNOLOGY_GSM: strcpy(network, "2G RAT"); break;
case NET_ACCESS_TECHNOLOGY_EDGE: strcpy(network, "2G RAT with EDGE"); break;
case NET_ACCESS_TECHNOLOGY_UMTS: strcpy(network, "UMTS RAT"); break;
case NET_ACCESS_TECHNOLOGY_LTE: strcpy(network, "LTE"); break;
}
IPAddress local_ip = Cellular.localIP();
#endif
//MQTT
client.ubidotsSetBroker("industrial.api.ubidots.com");
client.initialize();
if(client.isConnected())
{
client.ubidotsSubscribe(deviceid, "max_duty_cycle");
client.ubidotsSubscribe(deviceid, "min_duty_cycle");
}
// Get Reset Reason
switch( System.resetReason() )
{
case RESET_REASON_PIN_RESET: strcpy(resetreason, "PIN_RESET"); break;
case RESET_REASON_POWER_MANAGEMENT: strcpy(resetreason, "POWER_MANAGEMENT"); break;
case RESET_REASON_POWER_DOWN: strcpy(resetreason, "POWER_DOWN"); break;
case RESET_REASON_POWER_BROWNOUT: strcpy(resetreason, "POWER_BROWNOUT"); break;
case RESET_REASON_WATCHDOG: strcpy(resetreason, "WATCHDOG"); break;
case RESET_REASON_UPDATE: strcpy(resetreason, "UPDATE"); break;
case RESET_REASON_UPDATE_TIMEOUT: strcpy(resetreason, "UPDATE_TIMEOUT"); break;
case RESET_REASON_FACTORY_RESET: strcpy(resetreason, "FACTORY_RESET"); break;
case RESET_REASON_DFU_MODE: strcpy(resetreason, "DFU_MODE"); break;
case RESET_REASON_PANIC: strcpy(resetreason, "PANIC"); break;
case RESET_REASON_USER: strcpy(resetreason, "USER"); break;
case RESET_REASON_UNKNOWN: strcpy(resetreason, "UNKNOWN"); break;
case RESET_REASON_NONE: strcpy(resetreason, "NONE"); break;
default: strcpy(resetreason, "NOT_DEFINED");
}
// Cloud variables
Particle.variable("Duty_cycle", &lightvalue_old, INT);
Particle.variable("Signalstr", &signalstrength, INT);
Particle.variable("Sunrisetime", &sunrisetime, INT);
Particle.variable("Sunsettime", &sunsettime, INT);
Particle.variable("Volt", volt_readout_string, STRING);
Particle.variable("Amp", amp_readout_string, STRING);
Particle.variable("AmpCal", &_calibration, INT);
Particle.variable("Watt", watt_readout_string, STRING);
Particle.variable("Temp", temp_readout_string, STRING);
Particle.variable("ResetReason", resetreason, STRING);
Particle.function("Restart", cloudResetFunction);
// Sunset - define sunset/rise
winterSunrise.Actual(); //Actual, Civil, Nautical, Astronomical
summerSunrise.Actual();
//Set timezone depending on dst
if (IsDST())
Time.zone(+2);
else
Time.zone(+1);
// Set sunset/Sunrise variables
sunsetrise_set();
// Send an EVENT about startup
char message[160];
sprintf(message,"\"oppstart\":\"%s\", \"nettverk\":\"%s\", \"ip\":\"%s\", \"resetreason\":\"%s\"", string2char(Time.timeStr()), string2char(network), string2char(local_ip), resetreason);
publish_event ("oppstart", message);
// calibrate amp (While LED is still off)
updatewatt();
// Allow things to settle before resuming, and recieve some values from the sky before starting loop
// This allows us to recieve last value from MQTT before starting loop function
int i = 10;
while (i > 0)
{
i--;
client.loop();
delay(500);
}
}
// Next is the loop function...
void loop()
{
// Try to make a failsafe - if unit not is connected to cloud
if (Particle.connected()) {
cloudinit = millis();
cloudinit_faultcount = 0;
}
else
{
if (millis() - cloudinit > CLOUD_WAIT)
{
Particle.connect();
cloudinit = millis();
publish_event ("log", "\"message\":\"Trying to reconnect to cloud after CLOUD_WAIT.\"");
cloudinit_faultcount++;
}
else if (cloudinit_faultcount <= 12)
{
cloudinit = millis();
resetFlag = true;
resetinit=millis();
publish_event ("log", "\"message\":\"Reset initiated from Particle.connect failsafe.\"");
}
}
// This part checks if reset function has been initiated, and lets us delay reset
if (resetFlag && (millis() - resetinit > RESET_WAIT))
{
System.reset();
}
//MQTT
if(!client.isConnected())
{
client.reconnect();
client.ubidotsSubscribe(deviceid, "max_duty_cycle");
client.ubidotsSubscribe(deviceid, "min_duty_cycle");
publish_event ("log", "\"message\":\"MQTT Disconnected - reconnecting\"");
}
//Check for MQTT subscriptions
client.loop();
// Dailyroutine .. sync time once a day and check if DST status has changed
if (millis() - lastSync_oneday > ONE_DAY_MILLIS)
{
if (IsDST())
Time.zone(+2);
else
Time.zone(+1);
sunsetrise_set();
// Request time synchronization from the Particle Device Cloud
Particle.syncTime();
lastSync_oneday = millis();
}
// Minute routine .. update variables for cloud
if (millis() - lastSync_minute > ONE_MINUTE_MILLIS)
{
updatewatt();
temperature();
lastSync_minute = millis();
}
#if Wiring_WiFi
//Wifi Signal strength
WiFiSignal sig = WiFi.RSSI();
signalstrength = sig.getStrength();
#endif
#if Wiring_Cellular
CellularSignal sig = Cellular.RSSI();
signalstrength = sig.getStrength();
#endif
/*### OK! Down to business - this is the main function ###*/
timeaftermidnight = (Time.hour() * 60) + Time.minute();
//if time is after sunset...
if (timeaftermidnight >= sunsettime)
{
//Serial.printlnf("Value_mid1: %d Value_mid2: %d", value_mid1, value_mid2);
if (timeaftermidnight > (sunsettime + 60))
fade(value_min);
else if (timeaftermidnight > (sunsettime + 30))
fade(value_mid1);
else
fade(value_mid2);
}
// if time is before sunrise
else if (timeaftermidnight <= sunrisetime)
{
//Serial.printlnf("Value_mid1: %d Value_mid2: %d", value_mid1, value_mid2);
if (timeaftermidnight < (sunrisetime - 60))
fade(value_min);
else if (timeaftermidnight > (sunsettime - 30))
fade(value_mid1);
else
fade(value_mid2);
}
// We reach here if it is daytime
else
{
fade(value_max);
}
}
void callback(char* topic, byte* payload, unsigned int length) {
int len = strlen(topic) + 1;
char p[len];
memcpy(p, topic, len);
p[len] = NULL;
String message(p);
if (message.indexOf("max_duty_cycle") > 0)
{
payload[length] = '\0'; // Make payload a string by NULL terminating it.
value_max = atoi((char *)payload);
value_mid1 = ((value_max - value_min) / 3) + value_min;
value_mid2 = (((value_max - value_min) / 3) * 2) + value_min;
}
else if (message.indexOf("min_duty_cycle") > 0)
{
payload[length] = '\0'; // Make payload a string by NULL terminating it.
value_min = atoi((char *)payload);
value_mid1 = ((value_max - value_min) / 3) + value_min;
value_mid2 = (((value_max - value_min) / 3) * 2) + value_min;
}
}
int cloudResetFunction(String command)
{
resetFlag = true;
resetinit=millis();
publish_event ("log", "\"message\":\"Reset initiated from cloud function.\"");
return 1;
}
void updatewatt()
{
// Get voltage
volt_readout = voltage_calculation();
sprintf(volt_readout_string, "%.2f", volt_readout);
// Get amps
amp_readout = amp_calculation();
sprintf(amp_readout_string, "%.2f", amp_readout);
// Get watt
watt_readout = amp_readout * volt_readout;
sprintf(watt_readout_string, "%.2f", watt_readout);
}
//Funtion that fades light to the desired value
int fade(int lightvalue)
{
if (lightvalue != lightvalue_old)
{
// Start fading light to new value
while (lightvalue != lightvalue_old)
{
if (lightvalue > lightvalue_old) {
analogWrite(led, lightvalue_old, hertz);
lightvalue_old++;
}
else if (lightvalue < lightvalue_old) {
analogWrite(led, lightvalue_old, hertz);
lightvalue_old--;
}
else {
return -1;
}
//This delay controls how fast the fading happens
delay(20);
}
// Trigger watt publish
updatewatt();
client.add("duty_cycle", lightvalue); // Insert as first parameter your variable label
client.add("daily_watt", calculate_daily_watt());
client.add("watt", watt_readout); // Insert as first parameter your variable label
client.ubidotsPublish(deviceid); // Insert your device label where the values will be stored in Ubidots
}
return 1;
}
// Function to check if DST is active (Europe version)
bool IsDST()
{ // (Central) European Summer Timer calculation (last Sunday in March/October)
int dayOfMonth = Time.day();
int month = Time.month();
int dayOfWeek = Time.weekday() - 1; // make Sunday 0 .. Saturday 6
if (month >= 4 && month <= 9)
{ // April to September definetly DST
return true;
}
else if (month < 3 || month > 10)
{ // before March or after October is definetly standard time
return false;
}
// March and October need deeper examination
boolean lastSundayOrAfter = (dayOfMonth - dayOfWeek > 24);
if (!lastSundayOrAfter)
{ // before switching Sunday
return (month == 10); // October DST will be true, March not
}
if (dayOfWeek)
{ // AFTER the switching Sunday
return (month == 3); // for March DST is true, for October not
}
int secSinceMidnightUTC = Time.now() % 86400;
boolean dayStartedAs = (month == 10); // DST in October, in March not
// on switching Sunday we need to consider the time
if (secSinceMidnightUTC >= 1*3600)
{ // 1:00 UTC (=1:00 GMT/2:00 BST or 2:00 CET/3:00 CEST)
return !dayStartedAs;
}
return dayStartedAs;
}
// Convert string to char
char* string2char(String command)
{
if(command.length()!=0){
char *q = const_cast<char*>(command.c_str());
return q;
}
}
void sunsetrise_set()
{
bool daylightSavings = IsDST();
// time in minutes after midnight
timeaftermidnight = (Time.hour() * 60) + Time.minute();
int hsr,msr,hss,mss;
// minutes past midnight of sunrise/sunset
if (daylightSavings)
{
sunrisetime = summerSunrise.Rise(Time.month(),Time.day());
hsr=summerSunrise.sun_Hour();
msr=summerSunrise.sun_Minute();
sunsettime = summerSunrise.Set(Time.month(),Time.day());
hss=summerSunrise.sun_Hour();
mss=summerSunrise.sun_Minute();
}
else
{
sunrisetime = winterSunrise.Rise(Time.month(),Time.day());
hsr=winterSunrise.sun_Hour();
msr=winterSunrise.sun_Minute();
sunsettime = winterSunrise.Set(Time.month(),Time.day());
hss=winterSunrise.sun_Hour();
mss=winterSunrise.sun_Minute();
}
// Send an EVENT about sunset
char message[100];
char msr_v[4];
if (msr<10) sprintf(msr_v,"0%d", msr);
else sprintf(msr_v,"%d", msr);
char mss_v[4];
if (mss<10) sprintf(mss_v,"0%d", mss);
else sprintf(mss_v,"%d", mss);
#if Wiring_WiFi
//Wifi Signal strength
WiFiSignal sig = WiFi.RSSI();
signalstrength = sig.getStrength();
#endif
#if Wiring_Cellular
CellularSignal sig = Cellular.RSSI();
signalstrength = sig.getStrength();
#endif
sprintf(message,"\"Localtime\":\"%s\", \"Sunset\":\"%d:%s\", \"Sunrise\":\"%d:%s\", \"wifisignal\":\"%i\"", string2char(Time.timeStr()), hss, string2char(mss_v), hsr, string2char(msr_v), signalstrength);
publish_event ("update", message);
}
void publish_event (char* topic, char* message)
{
while(millis() - lastpublish < PUBLISH_MINIMUM) delay (100);
client.add(topic, 1, message); // Insert as first parameter your variable label
client.ubidotsPublish(deviceid); // Insert your device label where the values will be stored in Ubidots
lastpublish = millis();
}
float voltage_calculation ()
{
float denominator = resistor2 / (resistor1 + resistor2);
int volt_average = 0;
for (int i = 0; i <= 200; i++) {
volt_average = volt_average + analogRead(volt);
}
volt_average = volt_average / 201;
//Convert to actual voltage (0 - 3.3 Vdc)
float result = (((float)volt_average / 4096) * 3.3) / denominator;
return result;
}
float amp_calculation ()
{
// Read amp at a sampling rate
// First, find sampling frequency
float freq = 7 * (((float)1000000 / (float)hertz) / (float)256);
int analogstatetotal = 0;
int count = 0;
unsigned long micros_start = micros();
while (count <= 1792)
{
float totaltime = freq * count;
if (micros() >= (micros_start + totaltime))
{
analogstatetotal = analogstatetotal + analogRead(amp);
count++;
}
}
int analogstate = analogstatetotal / count;
if (lightvalue_old == 0)
{
amp_calibration = analogstate - 2048;
}
// Calibration
analogstate = analogstate - amp_calibration;
float vout = (((float)analogstate / (float)4096) * 3.3);
float i = 73.3 * ((float)vout / 3.3) - 36.65;
return i;
}
int compare (const void * a, const void * b) {
float fa = *(const float*) a;
float fb = *(const float*) b;
return (fa > fb) - (fa < fb);
}
float calculate_daily_watt ()
{
// Estimate watt usage
float watt_pr_ds = (float)watt_readout / lightvalue_old;
float value_max_calc = (float)value_max * watt_pr_ds;
float value_min_calc = (float)value_min * watt_pr_ds;
float value_mid1_calc = (float)value_mid1 * watt_pr_ds;
float value_mid2_calc = (float)value_mid2 * watt_pr_ds;
//hours of daylight
float value_max_hours = (sunsettime - sunrisetime) / (float)60;
float value_min_hours = (float)0;
float value_mid1_hours = (float)0;
float value_mid2_hours = (float)0;
if (value_max_hours > 22)
{
if (value_max_hours > 23)
{
value_mid2_hours = ((float)60 / 60) - (24 - value_max_hours);
}
else
{
value_mid1_hours = ((float)60 / 60) - (23 - value_max_hours);
value_mid2_hours = (float)60 / 60;
}
}
else
{
value_min_hours = (((22 * 60) - sunsettime) + sunrisetime) / (float)60;
value_mid1_hours = (float)60 / 60;
value_mid2_hours = (float)60 / 60;
}
float value_max_watt = value_max_hours * (float)value_max_calc;
float value_min_watt = value_min_hours * (float)value_min_calc;
float value_mid1_watt = value_mid1_hours * (float)value_mid1_calc;
float value_mid2_watt = value_mid2_hours * (float)value_mid2_calc;
float sum = value_max_watt + value_min_watt + value_mid1_watt + value_mid2_watt;
return sum;
}
void temperature()
{
int temp_average = 0;
for (int i = 0; i <= 200; i++) {
temp_average = temp_average + analogRead(temp);
}
temp_average = temp_average / 201;
temp_readout = temperature_calculation(temp_average);
sprintf(temp_readout_string, "%.1f", temp_readout);
}
float temperature_calculation (int voltage)
{
//Convert to actual voltage (0 - 3.3 Vdc) and subtract base voltage, and divide on 10mV as per the datasheet
float temperature = ((((float)voltage / 4096) * 3.3) - 0.5) / 0.01;
return temperature;
}