Photon stops publishing but remains connected

photon
Tags: #<Tag:0x00007fe222df6ce8>

#1

Hi -

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.

Kind Regards,
Friedl.


#2

Maybe not actually addressing your issue, but I’ve noticed some points in your code

This does not align with this note in the docs
https://docs.particle.io/reference/device-os/firmware/photon/#startup-

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

  char data[16];
  snprintf(data, sizeof(data), "%.3f", sensor_value);
  Particle.publish("Amp", data, PRIVATE);

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?)


#3

Hi @ScruffR

Couple of mistakes here, sorry… still learning :laughing:

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 :rofl::rofl::rofl: Thanks a million, works like a charm!

1.4.0

Hope I answered the questions :slight_smile:


#4

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 :wink:
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


#5

@ScruffR -

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 :see_no_evil:

ps: Implemented the new publish code… works great thanks.


#6

Looking at that code, this does make sense :wink:
But he has not got the && millis() part in his condition. Having it there would only make a difference when millis() is 0 which will only happen once every 49.7 days after you started your code.
Also he is setting old_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().

He is also using SYSTEM_MODE(SEMI_AUTOMATIC).

When you said this

What exactly was the issue?


#7

Hi @ScruffR -

Are you referring to the code on GitHub.

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 :slight_smile:

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.

ps: I set Delay(1100)… so far so good.


#8

@ScruffR

–UPDATE–

Sadly, even with all ‘time code’ removed only a single Delay(1100) when publishing, the code still fails after ±45minutes :pensive:

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.
  }
    
}

Regards,


#9

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.


#10

Thanks @ScruffR

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 :joy:

Regards,


#11

– UPDATE –

@ScruffR

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.

Regards
Friedl.


#12

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.


#13

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.