Counting Problems

Thank you all!

I found threading was really useful with my project, no more worrying about what the connection was doing, and my code could continue to count pulses.

This little snippet may help you with formatting the string for you. it converts the unix time (saved as an int in timeStartup) into HH:MM.SSPM
eg 08:45.22PM

String woke = Time.format(timeStartup, "%I:%M.%S%p");

Im just about to go digging through the firmware code to find the letters as im confused as to why the hours is “%I” and not “%H”

Thanks, @Hootie81. Let me know if you come across a list of Firmware letters since I could not find one.

Look here. It is the Time functions (from docs.particle.io reference is below the time functions)

https://sourceware.org/newlib/libc.html#Timefns

All the options are outlined. Looking forward to using some of the new time stuff for my own project!

1 Like

That is exactly what i was looking for! Maybe we should add it to our docs near the Time.format() section
even maybe the link directly to the section

https://sourceware.org/newlib/libc.html#strftime

1 Like

I second that!

I’m afraid I talked too soon. After seeing the counter arriving up to 4000 (it used to reset after about 200 counts max) I thought the problem was fixed, but I am back to the initial problem. If wifi is switched off, the counter resets.

Here is the latest code:

#include "HttpClient/HttpClient.h"
#include "application.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));

//Settings
String DeviceID = "XXXXXX";
String TargetHostName = "yyy.yyyy.com";


int val = 0;
int inPin  = D0;
int outPin = D7;
int PreviousMinute = -1;
int ThisMinute = -1;
bool SensorState = false;
bool PreviousSensorState = false;
retained uint32_t PulseCount = 0;
retained uint32_t TotalPulseCount = 0;
String PacketData = "";
String DeviceTime = "";
String PreviousResponseBody = "";
int ResponseStatus = 0;
bool success;
unsigned long PreviousTime = millis();


uint8_t retry_count = 0;
unsigned long old_time = millis();
unsigned long PulsePeriod = 0;

double CurrentPower = 0;
double incPower = 0;
double AveragePower = 0;
int TimeStamp;


HttpClient http;
http_header_t headers[] = {
    { "Accept" , "*/*"},
    { NULL, NULL }
};
http_request_t request;
http_response_t response;

void setup() {
    pinMode(outPin, OUTPUT);
    pinMode(inPin,  INPUT);
    Time.zone(2);
    WiFi.on();
}

void loop() {
    if(millis() - old_time >= 2000){        
        if(retry_count < 10){
            if(!WiFi.ready()){
                WiFi.connect();
                retry_count++;

            }
            else if (!Particle.connected()){
                Particle.connect();
                retry_count++;
            }
        }
        else{
            WiFi.off();
            retry_count = 0;
            WiFi.on();
        }
        old_time = millis();
    }    
    
    val = digitalRead(inPin);
    SensorState = !val;
    digitalWrite(outPin, SensorState); //show sensor state on little blue led
    
    //Count Pulses
    if(SensorState != PreviousSensorState) {
        //Detect Leading Edge
        if(SensorState==true) {
            PulseCount += 1;
            TotalPulseCount += 1;
            if(TotalPulseCount==10000) {
               TotalPulseCount=0; 
            }
            PulsePeriod = millis() - PreviousTime;
            CurrentPower = (3600000 / (double)PulsePeriod);
            incPower += CurrentPower;
            PreviousTime = millis();
        }
        PreviousSensorState = SensorState;
    }

    
    //Transmit Pulsecount at the turn of the minute//
    ThisMinute = Time.minute();
    if(ThisMinute != PreviousMinute) {
        TimeStamp = Time.now();
        DeviceTime = URLEncode(Time.format(TimeStamp, "%y%m%d%H%M%S"));
        AveragePower = (incPower / PulseCount);

        PacketData = DeviceID + ";" + String(PulseCount) + ";" + DeviceTime + ";" + String(TotalPulseCount) + ";" + String(AveragePower);
        
        //***Using HTTP Client***
        request.hostname = TargetHostName;
        request.port = 80;
        request.path = "/spark_receiver.ashx?PD=" + PacketData;
        http.get(request, response, headers);
        ResponseStatus = response.status;


        if((response.body != PreviousResponseBody) && (response.body.substring(0,2) == "OK") && (ResponseStatus == 200)) {
            //only reset if connection was successful
            PulseCount = 0;
            incPower = 0;
            PreviousResponseBody = response.body;
            success = Particle.publish("TotalPulseCount", String(TotalPulseCount) , 60, PRIVATE);
            success = Particle.publish("CurrentPower", String(CurrentPower) , 60, PRIVATE);
            success = Particle.publish("AveragePower", String(AveragePower) , 60, PRIVATE);
        }
        else {
            success = Particle.publish("Upload Failed");
        }
        PreviousMinute = ThisMinute;
    }
}

String URLEncode(const char* msg)
{
    const char *hex = "0123456789abcdef";
    String encodedMsg = "";

    while (*msg!='\0'){
        if( ('a' <= *msg && *msg <= 'z')
                || ('A' <= *msg && *msg <= 'Z')
                || ('0' <= *msg && *msg <= '9') ) {
            encodedMsg += *msg;
        } else {
            encodedMsg += '%';
            encodedMsg += hex[*msg >> 4];
            encodedMsg += hex[*msg & 15];
        }
        msg++;
    }
    return encodedMsg;
}

While it would be nice to have it all in the Particle docs, where should it end?

Maybe it should suffice to have a note (which already is there) in the docs that these format place-holders go in line with the standard C/C++ ones (as do the printf(), scanf(), ... ones) which can be found all over the web.

http://www.cplusplus.com/reference/ctime/strftime/
http://www.cplusplus.com/reference/cstdio/printf/
http://www.cplusplus.com/reference/cstdio/scanf/

2 Likes

If you are using SYSTEM_THREAD(ENABLED); do you think you also need to use SYSTEM_MODE(SEMI_AUTOMATIC);?

you could try waitUntil(WiFi.ready); instead of constantly toggling wifi on and off.

https://docs.particle.io/reference/firmware/photon/#system-thread

also another side note, your use of httpClient (its blocking nature) is likely to cause you missed pulses in your state-change-detection method of reading inPin. Since you have blocking code, you may want to think about putting the counting into an interrupt function.

I am troubled with your persistent vars using retained not holding, but noticed this thread earlier, and I have not played with that "EEPROM-like" feature yet.

2 Likes

The problem is that my program is mixture of suggestions by different users and I do not fully understand what each line does. For example, I don't know about the relationship between the two lines you mentioned.

I got this code:

if(millis() - old_time >= 2000){        
    if(retry_count < 10){
        if(!WiFi.ready()){
            WiFi.connect();
            retry_count++;

        }
        else if (!Particle.connected()){
            Particle.connect();
            retry_count++;
        }
    }
    else{
        WiFi.off();
        retry_count = 0;
        WiFi.on();
    }
    old_time = millis();
}

from this thread: Starting Spark Core without wifi [SOLVED] - #3 by kennethlimcp

Again I am not sure how it works. My aim is to have the device keep counting even when there is no wifi connection.

Is there a httpclient that works asynchronously and that would keep the counting working even while it is attempting to send an http request?

I will look up interrupts and see if it solves my problem. In the meantime, thanks for pointing me in the right direction.

Just a brief and rough description:

  • SYSTEM_MODE(SEMI_AUTOMATIC) sets your system mode so that your program runs off without any WiFi and cloud connection, but you can choose to connect to WiFi only or WiFi and cloud at any time and from then on you don’t need to bother too much about maintaining the cloud connection (default AUTOMATIC always wants/requires cloud connection, MANUAL requires you to bother more when using the cloud)

  • SYSTEM_THREAD(ENABLED) is a beta feature that pushes the system functionality into a seperate (free running) thread while your own code runs in its own thread. So if your code blocks for some reason the system will still maintain the cloud connection, but not respond to Particle.function() calls or Particle.subscribe() triggers. And, since it’s a beta stage feature, might have some side effects or its behaviour might change to a certain degree till it gets to stable stage.

While for some use cases the two seem to have similar effects they do different things and do complement eachother.

1 Like

Going back to the original question - I do not think it is either desirable or expected that a photon/core should restart because of external wifi/internet availability.

I’m not saying this isn’t the case, just that I believe that is undesirable; and if it is happening it would be good to understand the root cause(s) and address them.

Persistent variables can be useful when you need to ride through resets due to power problems, but my view is that they are the wrong way to address the unit resetting when it should not be doing so.

2 Likes

Could it be a heat problem? Is there some way to measure the device temperature and temporarily switch off wifi (assuming that is the cause of the heating)? Since I am uploading only once a minute, would it make sense to switch off wifi after uploading and turning it back on before uploading again? How long does the photon take to connect?

Sometimes its as quick as 5 seconds, sometimes it takes 15 seconds to connect to wifi and the cloud.

Photons are designed to run 24x7. There should be no weird heating problems.

I’d suggest focusing on addressing/understanding root causes, not trying to paper over symptoms.

1 Like

It does raise the specter (it is getting close to halloween) of a power issue, though.

How are you powering your rig @osprey?

1 Like

I'm using a a USB charger similar to this one.

@osprey, what is the current rating on the adapter. It should say on the sticker 5V, xxxmA somewhere.

1 Like

It says 5V 750mA. I have now replaced it with one that reads 5V 2A. Will let you know if it resets in the next 24 hours.

Another thing you might need to look out for - apart from current rating - is the DC filter quality.
For charging purely rectified unfiltered DC will do but might not do for running a Photon and even less for analog sensors.

3 Likes