How to get Electron to publish value if value has change by more than X, and then enter deep sleep?

So what I am hoping (and believe) is that someone else has already done this, or done a code that is similar enough so I can use it for what I am doing.
Long story short, I have been messing around with SEMI_AUTOMATIC mode and manually turning on and of the cellular modem based on if I have something to publish or not, and to make the story short, I am not having much of a success, have tried all kinds of waitFor, Wait until etc. to wait until the electron is online and ready to publish data. But usually I end up with green blinking LED for ever, not resulting in any timeout even though I put in a timeout in the waitFor statement. I an not sure if I should use SYSTEM_THREAD or not.

the goal with this project is to measure a waterlevel using a pressure transducer and then if the waterlevel has changed by more than a given number (5cm in this code) then the electron should turn on the cellular modem, go online and publish the data using particle.publish, after that the waterlevel should be stored in EEPROM for later use/comparison and the electron should turn of the cellular and go to deep sleep for a fixed time and keep the cellular off when it wakes up and only turn it on and publish if the waterlevel has changed by more than 5cm, else it should go back to deep sleep.

in the code below I need help with the “void publishData()” part, I intentionally deleted all the waitFor or waitUntill connected so I would get a comment on where and how I should implement that part for best results.

Hope someone is willing to help me. :relaxed:

  SYSTEM_MODE(SEMI_AUTOMATIC);
    //SYSTEM_THREAD(ENABLED);
    char publishStr[20];
    int sleepInterval = 1;
    int maxChange = 5;  //the ammount in centimeters that the waterlevel has to change so it will be published
    
    uint16_t waterlevel;
    uint16_t oldWaterlevel;
    uint32_t start;
    
    void publishData() {
    
      Cellular.on();
      Cellular.connect();
      Particle.connect();
      sprintf(publishStr, "%i cm", waterlevel);
      Particle.publish("Waterlevel", publishStr, PRIVATE, NO_ACK);
      Cellular.off();
    }//void publishData()
    
    void getWaterlevel(){
      //has not been implemented yet
      //read waterlevel sensor and put the outcome in the waterlevel integer
      waterlevel=100;   //for test purposes
      oldWaterlevel=94; //for test purposes so that the differnce between new and old waterlevel is more than maxChange, resulting in particle.publish 
    }
    
    void setup() {
      //nothing to setup
    }//setup()
    
    void loop() {
    
      EEPROM.get(0, oldWaterlevel);               //get the last published waterlevel to be able to compare
      getWaterlevel();                                        //get new waterlevel
      if (abs(waterlevel-oldWaterlevel)>maxChange){           //publish data if waterlevel has changed by more than maxChange
        publishData();
        oldWaterlevel=waterlevel;
        EEPROM.put(0, waterlevel);                            //safe Waterlevel in EEProm because deep sleep will reset the MCU
        }
      System.sleep(SLEEP_MODE_DEEP,60 * sleepInterval);       //go to Deep sleep for "sleepInterval" minutes
    }//loop

Yes, you should, in order to have waitFor() work "reliably", but after a complete disconnect from the cell towers a reconnect on wake will consume about 6KB of data and take up to 5 minutes, no matter how you've set the timeout in waitFor() (that is a known issue).

I'd recommend using System.sleep(WKP, RISING, 60 * sleepInterval, SLEEP_NETWORK_STANDBY) and keep the sleeping periode less than 23 minutes. I've found that this consums less battery and less data than System.sleep(SLEEP_MODE_DEEP) due to the reduced reconnect time.

Also this sleep will continue in loop() after wake up and your variables will still be valid.
On the other hand, if you insist on using DEEP_SLEEP, I'd rather go for retained variables than for EEPROM.

So I'd have your function just that simple

void publishData() 
{
  if (!Particle.connected())
  { // this is only required once on first start due to SLEEP_NETWORK_STANDBY
    // in SYSTEM_MODE(AUTOMATIC) you can get rid of that whole block too
    Cellular.on();      // just in case
    Particle.connect(); // implicitly performs a Cellular.connect()
    waitUntil(Particle.connected);
  }
  sprintf(publishStr, "%i cm", waterlevel);
  Particle.publish("Waterlevel", publishStr, PRIVATE, NO_ACK);
  // don't switch the modem off!
  // but since you are going to sleep after a publish, hang in there to let the publish finish
  for(uint32_t ms = millis(); millis() - ms < 2000; Particle.process);
}

You'd need to correlate the estimated rate of change in water level with the impact on power and data consumption of DEEP_SLEEP vs. Stop Mode sleep to find the correct solution.
With a higher rate of change Stop Mode will win, with a really slow change DEEP_SLEEP would, but you still need to wait for the publish to finish before you go to sleep.
Or you could even use both and a dynamic sleep time depending on the trend of the change rate.

2 Likes

Thanks alot for your suggestions and detailed answer @ScruffR , yes I have been evaluating if I should go for deep sleep or not, but I think I will go with deep sleep (or maybe now SLEEP_MODE_SOFTPOWEROFF based on a suggestion from BDub) as I am aiming for ultra low power and long sleeping periods (counted in hours), and even longer periods between publishing (even down to once or twice per day), I will do some tests tonight and maybe post my results here.