I read through a couple of posts regarding similar issues, but could not find one addressing my problem directly, even though it seems very much the same;
I have a Photon publishing a string value ever 1 second. It seems that it stops publishing to Particle Cloud after any given time (anything form 10mins to 28 mins in 5 tests) Here is my code, still work in progress;
STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); //Set to use external antenna//
STARTUP(WiFi.setListenTimeout(120)); //Set listening mode timeout n seconds//
STARTUP(RGB.mirrorTo(D3, D2, D1)); //Mirror Photon onboard LED
unsigned long old_time = millis(); // set Timeout to enter Listening Mode
#define CURRENT_SENSOR A5 // Define Analog input pin that sensor is attached to
#include <cmath>
int redPin = D3;
int greenPin = D2;
int bluePin = D1;
float amplitude_current; // Float amplitude current
float effective_value; // Float effective current
void setup()
{
Serial.begin(9600);
pins_init();
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}
void pins_init()
{
pinMode(CURRENT_SENSOR, INPUT);
}
// The below constants are specific to the ACS722 as used in the FireFli design
// 12 bits of resolution on the Particle hardware
// We make a correction because the ACS722 output is 80% of the full scale available.
const int Resolution = 3277; //4095
// The value of 'FullScaleAmps' is 10 because it measures from -5 A to +5 A.
const float FullScaleAmps = 10.0;
// We assume the ACS722 is zero-biased
const int ZeroValue = 2047; //2047 //2100
const float MilliVoltsPerAmp = float(Resolution) / FullScaleAmps;
// Function: Take 1000 samples and calculate the RMS value of the current.
// The input values are squared, and then the area under the curve is
// calculated using the trapezoidal rule. We take the average of the power
// and then the square root to get the RMS value.
float getRms()
{
const int numSamples = 1000;
// The integer value (between 0 and 4095) that we read from the sensor
int sensorValue;
// The timestamp of the "previous" sample
int ts_prev = 0;
// The difference between the "present" and "previous" timestamps
int ts_delta = 0;
// We keep the value of the first timestamp to use in the final
// averaging step. Alternatively we could simply sum up all the
// deltas from the start to the end.
int ts_first = 0;
// The integaer value of the "prevous" sample.
int value_prev = 0;
// Total elapsed time for the 1000 samples.
int elapsedTime = 0;
// The timestamp as read from the sensor.
uint32_t timeStamp = 0;
// The measured values (integers) are converted to floats for the calculation.
float val_now = 0.0;
float val_prev = 0.0;
float accum = 0.0;
float avgValue = 0.0;
float rms = 0.0;
for (int i=0; i<numSamples; i++) {
sensorValue = analogRead(CURRENT_SENSOR);
timeStamp = micros();
if (ts_prev > 0) {
ts_delta = timeStamp - ts_prev;
if (ts_delta < 0) {
// If we detect an overflow we return an invalid RMS current value immediately
return -1;
} else {
val_now = (float(sensorValue) - float(ZeroValue)) / MilliVoltsPerAmp;
val_prev = (float(value_prev) - float(ZeroValue)) / MilliVoltsPerAmp;
accum += 0.5 * ts_delta * (val_now*val_now + val_prev*val_prev);
}
} else {
// This is only executed the first time round the loop
ts_first = timeStamp;
}
ts_prev = timeStamp;
value_prev = sensorValue;
}
elapsedTime = timeStamp - ts_first;
avgValue = accum / float(elapsedTime);
rms = sqrt(avgValue);
return rms;
}
void loop()
{
// // == ENTER lstening mode if no current Wifi credentials are not valid == //
if(millis() - old_time >= 20000 && millis()) {
if(!WiFi.ready()){
WiFi.listen();
}
}
float sensor_value = getRms();
// Still need to add a test here to detect invalid values
if (sensor_value >= 0) {
sensor_value = round(sensor_value*1000)/1000; //Rounding to 3 decimals
Particle.publish("Amp", String(sensor_value));
delay(1000);
} else {
// Still to determine what to do if there was an error like overflow.
}
}
I am still able to get response form the device when I send a Ping, just no recent events in Event monitor.
Are you sure you mean to use && millis()? Do you not rather mean && old_time?
Also, since you are not using SYSTEM_THREAD(ENABLED) nor a non-AUTOMATIC SYSTEM_MODE the whole body of that conditional doesn't make a lot of sense as this code will stop running as soon WiFi.read() would turn false (actually as soon Particle.connected() becomes false).
It's best to avoid String and rather use snprintf() to prepare a C string (aka char array) and you should not publish your events PUBLIC (default) unless expressly needed.
Rather do this
For testing, try to increase your delay to 1100ms (or more) to ensure you are not running into the rate limit due to unprecise timing of the delay (this used to be an issue in some ancient device OS version - BTW, which are you using?)
Had some help with some of the code, but let me see whether I can address some of the points.
STARTUP function is something I only learned of recently. I had to get those three things done which I included, but all examples I could find of getting it done, was to add them to STARTUP. Having said this, no one added all three. I will spend more time on learning this function and correct it accordingly.
Again code I had some help with in one of the topics on the forum. Would you suggest then:
if(millis() - old_time >= 20000 && old_time)
I tried running SYSTEM THREAD (ENABLED) but it did not have the desired effect. My challenge has always been shipping a product to a client and getting the product onto their Wifi without opening up the device. What I managed to get done here (probably by chance) is that when the WiFi fails (maybe client swops out router or changes credentials), the device will go into listening mode. Due to the fact that the Wifi disruption might also be temporary, it will will then also exit Listening mode after specified time attempting to connect with stored credentials.
With SYSTEM THREAD (ENABLED) the indicator LED's I had in the code presented as if everything was ok even though the device was not connected to Wifi.
EDIT: DONE!! Whoohoo... I am a professional code Editor Thanks a million, works like a charm!
My first question would be, do you know what this should do at all?
Since old_time is not set anywhere but on initialisation I'm not entirely sure what it should achieve and hence can't really tell how to do it right
The comment above that block doesn't provide much useful info about that either, IMO.
The way how I read the original construct is that you don't check the connection for the initial 20 seconds of your code running and from then on permanently.
Is this what you want?
However, my assessment still stands that - if this is your entire code - your code would stop as soon the cloud connection gets lost.
I'm not sure why your code behaves the way you described - IMO, it shouldn't
I think to avoid everything from flying way over my head, let me take one thing at a time. I received the following code below from another elite member in the forum who wrote some code on how to better manage WiFi connections:
unsigned long old_time = millis();
void loop(){
if(millis() - old_time >= 60000 && millis()) {
if(!WiFi.ready()){
WiFi.listen();
}
}
// Insert user code here
}
As the thread was quite dated, I did not want to revive it as I was once āreprimandedā for unknowing doing so. I sent him a direct message on how to achieve the following:
If my device is unable to detect the Wifi with stored credentials for a period of time, it should automatically enter listening mode. This way client will be able to reconnect the device with Particle App to new WiFi should that be the case.
In the case where it was a temporary network failure, I then added the STARTUP(WiFi.setListenTimeout(120); allowing the device to exit listening mode every 120s
My apologies for the trivial questions, doing my best to learn as quickly as possible, time just spread thin as I also need to learn Fusion360 and electronics 101
ps: Implemented the new publish codeā¦ works great thanks.
Looking at that code, this does make sense
But he has not got the && millis() part in his condition. Having it there would only make a difference whenmillis() is 0 which will only happen once every 49.7 days after you started your code.
Also he is settingold_time at the end of his conditional block.
So with the original code the conditional block will be executed every only every two seconds.
With your code the block will not be executed for the first 20 seconds and after that with each iteration of loop().
I noticed the difference between his code on Github and the code he sent me, I just assumed the code he sent would be more applicable to my case. Thanks for the advice, I will look into more and see whether I can get ti to work properly
Not so much and issue, more of an undesired outcome, hehe. I have two RGB LED's (referenced in the mirrorTo() ) function. They are connected to D1, D2 and D3. Initially the idea was to use these as some sort of Status indicator.. i.e
Green == System OK
Blue == Publishing
Red == Network Failure
Then running SYSTEM THREAD (ENABLED) The status LED remained green (flashing blue whilst publishing) regardless of the status of the WiFi. I presume this is due to the fact the setup() and loop() runs regardless of the state of the WiFi connection correct?
I later though it might be better to simply mirror the onboard LED even though it won't allow custom status indicators.
Sadly, even with all ātime codeā removed only a single Delay(1100) when publishing, the code still fails after Ā±45minutes
Most current code:
//STARTUP(WiFi.selectAntenna(ANT_EXTERNAL)); //Set to use external antenna//
//STARTUP(WiFi.setListenTimeout(120)); //Set listening mode timeout n seconds//
STARTUP(RGB.mirrorTo(D3, D2, D1)); //Mirror Photon onboard LED
#define CURRENT_SENSOR A5 // Define Analog input pin that sensor is attached to
//#define CURRENT_SENSOR A4 // In case of External Sensor
#include <cmath>
int redPin = D3;
int greenPin = D2;
int bluePin = D1;
float amplitude_current; // Float amplitude current
float effective_value; // Float effective current
void setup()
{
Serial.begin(9600);
pins_init();
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}
void pins_init()
{
pinMode(CURRENT_SENSOR, INPUT);
}
// The below constants are specific to the ACS722 as used in the FireFli design
// 12 bits of resolution on the Particle hardware
// We make a correction because the ACS722 output is 80% of the full scale available.
const int Resolution = 3277; //4095
// The value of 'FullScaleAmps' is 10 because it measures from -5 A to +5 A. Adjust accordingly based on Sensor Range (5A, 10A, 20A, 30A, 40A)
const float FullScaleAmps = 10.0;
// We assume the ACS722 is zero-biased
const int ZeroValue = 2047; //2047 //2100
const float MilliVoltsPerAmp = float(Resolution) / FullScaleAmps;
// Function: Take 1000 samples and calculate the RMS value of the current.
// The input values are squared, and then the area under the curve is
// calculated using the trapezoidal rule. We take the average of the power
// and then the square root to get the RMS value.
float getRms()
{
const int numSamples = 1000;
// The integer value (between 0 and 4095) that we read from the sensor
int sensorValue;
// The timestamp of the "previous" sample
int ts_prev = 0;
// The difference between the "present" and "previous" timestamps
int ts_delta = 0;
// We keep the value of the first timestamp to use in the final
// averaging step. Alternatively we could simply sum up all the
// deltas from the start to the end.
int ts_first = 0;
// The integaer value of the "prevous" sample.
int value_prev = 0;
// Total elapsed time for the 1000 samples.
int elapsedTime = 0;
// The timestamp as read from the sensor.
uint32_t timeStamp = 0;
// The measured values (integers) are converted to floats for the calculation.
float val_now = 0.0;
float val_prev = 0.0;
float accum = 0.0;
float avgValue = 0.0;
float rms = 0.0;
for (int i=0; i<numSamples; i++) {
sensorValue = analogRead(CURRENT_SENSOR);
timeStamp = micros();
if (ts_prev > 0) {
ts_delta = timeStamp - ts_prev;
if (ts_delta < 0) {
// If we detect an overflow we return an invalid RMS current value immediately
return -1;
} else {
val_now = (float(sensorValue) - float(ZeroValue)) / MilliVoltsPerAmp;
val_prev = (float(value_prev) - float(ZeroValue)) / MilliVoltsPerAmp;
accum += 0.5 * ts_delta * (val_now*val_now + val_prev*val_prev);
}
} else {
// This is only executed the first time round the loop
ts_first = timeStamp;
}
ts_prev = timeStamp;
value_prev = sensorValue;
}
elapsedTime = timeStamp - ts_first;
avgValue = accum / float(elapsedTime);
rms = sqrt(avgValue);
return rms;
}
void loop()
{
float sensor_value = getRms();
// We need to add a test here to detect invalid values
if (sensor_value >= 0) {
char data[16];
snprintf(data, sizeof(data), "%.3f", sensor_value);
Particle.publish("Amp", data, PRIVATE);
delay(1100);
} else {
// We should figure out what to do if there was an error like overflow.
}
}
You could add some Serial.print(sensor_value) statement to your else case.
If you should happen to get a bad reading from your getRms() function you wouldnāt know in anyway as you have now status reports of any kind for that case.
When seeing undesired behaviour the first thing to locate where this might be caused by adding debug outputs at strategically important places.
You need to know whatās going on in your code, what are your variables doing where, how and why.
One line of investigation would also be your use of micros() in conjunction with int ts_prev and int ts_delta.
You correctly have uint32_t for timeStamp but all your other variables are signed integers and that can cause troubles when mixing signed and unsigned types as the rollover will not be handled consistently. uint32_t will rollover after 71.6 minutes but int will do that 36 minutes earlier and give you negative values.
I will start debugging. your help is much appreciated!! I will open up and connect via USB to get serial monitor. Suppose no chance of getting Serial.print done via WiFi is there
Not sure whether this was suppose to work, but implemented a System.reset(); as per below and like magicā¦ it works.
void loop()
{
float sensor_value = getRms();
// We need to add a test here to detect invalid values
if (sensor_value >= 0) {
char data[16];
snprintf(data, sizeof(data), "%.3f", sensor_value);
Particle.publish("Amp", data, PRIVATE);
delay(1100);
} else {
// We should figure out what to do if there was an error like overflow.
System.reset();
}
}
The Photon still fails to publish, from the Even manager it seems the system resets and continues to function as normal. Now to find out what is going wrong.
This is just a workaround at best - not a solution and it does not address the underlying problem.
If my suspicion from above was correct a reset would of course also circumvent the long term consequences of the error but it doesnāt prevent it from happening at least once causing the reset in the first place.
Just in case it seemed otherwise, I agree 100%. I would never send the device to the client knowing error's occur. Working relentlessly on finding the problem now.
Just thought I give you an update on this. I followed your advice to look into this section quoted; I inserted a the following in the else section of my IF statement:
float sensor_value = getRms();
// Still need to add a test here to detect invalid values
if (sensor_value >= 0) {
char data[16];
snprintf(data, sizeof(data), "%.3f", sensor_value);
Particle.publish("Amp", data);
// Ubidots publish code start //
ubidots.add("Amp", sensor_value);
bool bufferSent = false;
bufferSent = ubidots.send(WEBHOOK_NAME, PUBLIC); // Use particle webhook to send data
// Ubidots publish code end //
delay(10000);
} else {
Particle.publish("I stopped publishing");
delay(1000);
}
From this I saw the device "got stuck" in the else section of the statement and never got out. I first tried to force a Particle.disconnect() and Particle.connect() but to no avail, it remains stuck. So, I made the following changes, not sure if it is correct, but so far so good
Prior to these changes, I could not get more than 45 minutes without the Photon stop publishing to Particle Cloud. With the changes in place, so far 3 hours without interruption.
This seemed to do the job until I implemented some control functions from Ubidots into the Setup() sections of the code. I realised the reset() does not "restart" the Photon which led me to assume the setup() would not be run, rendering the control function useless.
... until your 16 bit variable overflows every 65 seconds and will role back to zero while millis() will still carry on counting up for another 49+ days.
Really appreciate the help. Was using another forum recently trying out a new dashboard service, does not come close to this forum. Thanks for having patience with us guys just starting out
EDIT: Implemented non-bloking delay, seems to working fine. Can work on the rest of the code now
thanks for letting me know. I have been wanting to try out non-blocking delays for a while. Just one question;
The delay(10000); is only used while testing to avoid unnecessary consumption of ādotsā on my Ubidots account This is usefully set to delay(1000); and the main reason for that is adhere to the 1 publish per second (or more accurate 4 per 4 seconds) in Particle Cloud. Will using a non blocking delay not cause problems here?
I also assume the delay(1000); in the }else{ section will only be called should the Photon fail to publish correct?