Code Review for working Photon Solar Powered Wifi Pool Thermometer

What I want to accomplish:
Hardware - Photon, Solar Panel, 10K Thermistor, Photon Power Shield
Desired behaviour:
Wake up ever 15 minutes, publish pool temperature (and other stuff to IFTTT), write temperature to Google sheets. Charge LiPO battery - Rinse and repeat

I’ve cobbled code and hardware from a few different information sources and have a working system (new to IOT). It reads temperature publishes and charges.

Problems:

  1. After I started using deep sleep (to conserve battery overnight) the particle inconsistently connects to the cloud and publishes data. Some hours I’ll get 4 readings - sometimes 1. I think this is a blocking issue, but not sure where to start.
  2. I have difficulty publish new code since the particle does it thing and then goes back to sleep. I tried a delay, but that seemed to be part of the blocking problem.
  3. I’m a coding hack new to IOT. I’d like my code to be as clean, efficient and fault tolerant as possible.
// This #include statement was automatically added by the Particle IDE.
#include <PowerShield.h>

//**************************************************************************************
// Author: Gustavo Gonnet
// Contact: gusgonnet@gmail.com
// Project: https://www.hackster.io/gusgonnet/pool-temperature-monitor-5331f2
// License: Apache-2.0
//**************************************************************************************

// IO mapping
// A0 : pool_THERMISTOR

int _version = 1.2;

#include <math.h>
#include "application.h"
// Include the Powershield library
#include "PowerShield/PowerShield.h"
PowerShield batteryMonitor;

// this is the thermistor used
// https://amzn.to/2KmOIT4
// resistance at 25 degrees C
#define THERMISTORNOMINAL 10000
// temp. for nominal resistance (almost always 25 C)
#define TEMPERATURENOMINAL 25
// how many samples to take and average, more takes longer
// but measurement is 'smoother'
#define NUMSAMPLES 10
// The beta coefficient of the thermistor (usually 3000-4000)
#define BCOEFFICIENT 3950
// the value of the 'other' resistor
#define SERIESRESISTOR 10000

//measure the temperature every POOL_READ_INTERVAL msec
#define POOL_READ_INTERVAL 900000

unsigned long pool_interval = 0;
int samples[NUMSAMPLES];
int pool_THERMISTOR = A0;
//this is coming from http://www.instructables.com/id/Datalogging-with-Spark-Core-Google-Drive/?ALLSTEPS
char pool_temperature_str[64]; //String to store the sensor data
char pool_temperature_ifttt[64];

//by default, we'll display the temperature in degrees celsius, but if you prefer farenheit please set this to true
bool useFahrenheit = true;
//Get wifi signal strength
int rssi = 0;

void setup() {

    Particle.publish("device starting", "Version: " + String(_version), 60, PRIVATE);

    pool_interval = 0;
    Particle.function("status", status);
    pinMode(pool_THERMISTOR, INPUT);
    //google sheets will get this variable
    //the name of this varriable CANNOT be longer than 12 characters
    //https://docs.particle.io/reference/firmware/photon/#particle-variable-
    Particle.variable("pool_tmp", pool_temperature_str, STRING);
    Particle.variable("pool_temp", pool_temperature_str, STRING);
    Particle.variable("RSSI", &rssi, INT);
    //Include the setp for power shield
    // This essentially starts the I2C bus
    batteryMonitor.begin();
    // This sets up the fuel gauge
    batteryMonitor.quickStart();
    // Wait for it to settle down
    delay(1000); //delay 1.0 second
}

void loop() {
    //measure the temperature right away after a start and every POOL_READ_INTERVAL msec after that
//    if( (millis() - pool_interval >= POOL_READ_INTERVAL) or (pool_interval==0) ) {
        pool_temp();
        pool_interval = millis();
//    }
    rssi = WiFi.RSSI();  // Read wifi signal strength
    Particle.publish("rssi", String(rssi), 60, PRIVATE); //Publish wifi signal to the cloud
    
    float cellVoltage = batteryMonitor.getVCell(); // Read the volatge of the LiPo Battery
    Particle.publish("ps-voltage", String(cellVoltage), 60, PRIVATE); // Publish Cell Voltage to cloud
    
    float stateOfCharge = batteryMonitor.getSoC(); // Read the State of Charge of the LiPo
    Particle.publish("ps-soc", String(stateOfCharge), 60, PRIVATE); // Publish State of Charge to Cloud


//    delay(6000);  // added to keep Particle awake to receive new code flashed over wi-fi
//    System.sleep(SLEEP_MODE_DEEP, 840); // sleep time with above to get 15 minute intervals
    System.sleep(SLEEP_MODE_DEEP, 900);
}

/*******************************************************************************
 * Function Name  : status
 * Description    : this function gets called for the sake of pushing the temperature to your phone
 * Return         : 0
 *******************************************************************************/
int status(String args)
{
 //this triggers a recipe in IFTTT
 Particle.publish("pool_temp", pool_temperature_ifttt, 60, PRIVATE);
 return 0;
}

/*******************************************************************************
 * Function Name  : pool_temp
 * Description    : read the value of the thermistor, convert it to degrees and store it in pool_temperature_str
 * Return         : 0
 *******************************************************************************/
int pool_temp()
{
    uint8_t i;
    float average;

    // take N samples in a row, with a slight delay
    for (i=0; i< NUMSAMPLES; i++) {
        samples[i] = analogRead(pool_THERMISTOR);
        delay(10);
    }

    // average all the samples out
    average = 0;
    for (i=0; i< NUMSAMPLES; i++) {
        average += samples[i];
    }
    average /= NUMSAMPLES;

    // convert the value to resistance
    average = (4095 / average)  - 1;
    average = SERIESRESISTOR / average;


    float steinhart;
    steinhart = average / THERMISTORNOMINAL;     // (R/Ro)
    steinhart = log(steinhart);                  // ln(R/Ro)
    steinhart /= BCOEFFICIENT;                   // 1/B * ln(R/Ro)
    steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
    steinhart = 1.0 / steinhart;                 // Invert
    steinhart -= 273.15;                         // convert to C
    
  // Convert Celsius to Fahrenheit - EXPERIMENTAL, so let me know if it works please - Gustavo.
  // source: http://playground.arduino.cc/ComponentLib/Thermistor2#TheSimpleCode
  // project source to let me know if this works: https://www.hackster.io/gusgonnet/pool-temperature-monitor-5331f2
  if (useFahrenheit) {
    steinhart = (steinhart * 9.0)/ 5.0 + 32.0;
  }

    char ascii[32];
    int steinhart1 = (steinhart - (int)steinhart) * 100;

    // for negative temperatures
    steinhart1 = abs(steinhart1);

    sprintf(ascii,"%0d.%d", (int)steinhart, steinhart1);
    Particle.publish("pool_temp", ascii, 60, PRIVATE);

    char tempInChar[32];
    sprintf(tempInChar,"%0d.%d", (int)steinhart, steinhart1);

    //Write temperature to string, google sheets will get this variable
    sprintf(pool_temperature_str, "{\"t\":%s}", tempInChar);

    //this variable will be published by function status()
    sprintf(pool_temperature_ifttt, "%s", tempInChar);

    return 0;
}

Have you got access to the device?
If so Safe Mode would help.

I can get to it - I just time my updates. The browser based build tool waits waits for like 30 seconds before it times out. Would the best thing to add a delay function at the end of the loop (say for 1 or 5 minutes)? Would the device really be sitting there using low power? Or is the timer and the connection to the cloud still using power?

This syntax suggests that you are targeting an old system version.
I’d update that.

Adding a unconditional delay is a way to do it, but not the best.
Currently there are no easy ways to solve that, but Particle is working on solutions to make delivering firmware to mostly sleeping devices a lot easier.

Thanks I updated my variable definitions (usually being more specific is better), But I’ll change my to the current standard without the “type” definition (ex. STRING and INT), since these can now be inferred. Thanks

Good to hear something is coming down the pike for mostly sleeping devices.

How is your project going? I did something with a free kit from Losant, but one of the kids destroyed the sensors, so i am looking to redesign. My current design uses about 25’ of cat 3 cable to run the sensors outside. I like your idea of a floating self powered unit.

I didn’t do mine as floating sensor. It has a thermistor sensor in the pool -either in the skimmer or pool. It has a a battery back up solar panel mounted some distance away. No AC power required. It’s working well and just got a waterproof enclosure this weekend.