Water usage monitor

Hey all, just wanted to quickly share this project i spent today working on…

This standard Aussie water meter has a little port where you can slip in a reed switch and measure water in 5L increments. So i decided I would use my Photon and a [sparkfun battery shield][1], a cheap solar panel and a handfull of little components to do the job.

The circuit is pretty simple,

This is the back of the battery shield and where all the components are mounted. There is a battery connected to the… you guessed it… the battery connection, the solar panel is connected to the two pads for the barrel connector… then it starts getting a tad complicated.

I chose to use a 47k pull down resistor to the WKP pin (because thats what i found in my box of junk) and also a 0.1uf ceramic cap from WKP to 3.3v. This acts as a hardware de-bounce for the reed switch. During testing I had some issues with my code so I jumpered the WKP and A4 pins, so not sure if its needed, but its there now.

You will also notice a reed switch at the top edge of the board between D3 and GND. This is to force the photon to stay on for re-flashing over the air, i just place a magnet on the outside of the box and the next time the photon switches on from the water meter it will stay on until the magnet is removed.

The pickup from the water meter is a simple reed switch connected to a cable with a connector on the end to make things easier in the field. the two wires are connected to WKP and 3.3v. I uses some heat shrink to protect the glass of the reed switch and bulk it up to a snug fit in the meter.

Now for the Firmware, its a bit hacked together but works well for the minute, till i work out what im going to do with the data. Also i need to add in the scaling so it reports liters used rather than just the pulses. I tried to make use of some of the awesome new features like threading for the connectivity stuff, and Backup RAM to keep the total while in deep sleep mode.

EDIT: Updated code
Bugfixes:

  • correct an issue where the time wake-up added to the total
  • wasn’t counting in stay-awake mode
    Features Added:
  • days water usage
  • Total usage
  • Scaled pulses to read in litres (5L per pulse)
  • added a function to set the actual meter reading
  • tidied up time formatting in Sleep Info
  • Added RSSI to status to allow better antenna adjustment
  • easier to read code
#include "application.h"
#include "SparkFunMAX17043.h" // Include the SparkFun MAX17043 library

SYSTEM_THREAD(ENABLED);
STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
STARTUP(WiFi.selectAntenna(ANT_AUTO));

// Pins
int stayAwake = D3;
int reedSwitch = WKP;

// Settings
#define debounceTime 500    // milliseconds
#define updatePeriod 1000   // milliseconds
#define sleepAfter 30       // Seconds
#define sleepTime 120        // Minutes
#define lowBattery 10       // %

// Data to retain in deep sleep
retained int timeSleep = 0;
retained volatile int pulseCount = 0;
retained volatile int pulseToday = 0;

// Other variables
int prevCount = pulseCount;
long lastPulse;
long lastUpdate;
bool runOnce = TRUE;
int timeStartup;
String data = ""; //used for all publish statements

int setMeter(String command);

void setup() {
    Time.zone(+8); //Perth time 
    
    lastPulse = (millis() - debounceTime); //Initialise timer + allow for another pulse straight away
    lastUpdate = (millis() - updatePeriod); //Initialise timer + allow update straight away 
    
    attachInterrupt(reedSwitch, pulse, RISING);
    pinMode(stayAwake, INPUT_PULLUP);
    
    timeStartup = Time.now();
    
    if (Time.day(timeStartup) != Time.day(timeSleep)){ //check if its still the same day
        pulseToday = 0;
    }
    
    if (((sleepTime * 60) - (timeStartup - timeSleep)) > 2){ // calculate if woke on time or wkp pin 
        pulseCount += 5; 
        pulseToday += 5;
    }
    
    Particle.function("SetMeter", setMeter);
}

void loop() {
    
    // Run once after cloud connected
    if (Particle.connected() && runOnce){
        lipo.begin(); 
        lipo.wake();
        lipo.quickStart();
        lipo.setThreshold(lowBattery); 
        
        if (lipo.getAlert()) Particle.publish("Low Battery", NULL, 60, PRIVATE);    

        Particle.publish("Status", data.format("Battery %1.2fV %2.1f%% Signal: %ddB" , lipo.getVoltage(), lipo.getSOC(), WiFi.RSSI()), 60, PRIVATE);

        String slept = Time.format(timeSleep, "%I:%M.%S%p");
        String woke = Time.format(timeStartup, "%I:%M.%S%p");
        String diff = String((sleepTime * 60) - (timeStartup - timeSleep));
        Particle.publish("SleepInfo", String("Slept at " + slept + ", Woke at " + woke + ", Short by " + diff + "seconds"), 60, PRIVATE);
        
        runOnce = FALSE;
    }
    
    // Only publish if cloud connected
    if (Particle.connected()){
        if (((millis() - lastUpdate) > updatePeriod) && (pulseCount != prevCount)){
            lastUpdate = millis();
            prevCount = pulseCount;
            Particle.publish("Water", data.format("Todays usage = %dL Total = %dL", pulseToday, pulseCount), 60, PRIVATE);
        }
    }
    
    //sleep to save battery
    if (((millis() - lastPulse) >  (sleepAfter * 1000)) && (digitalRead(stayAwake) == HIGH)){
        lipo.sleep();
        timeSleep = Time.now();
        System.sleep(SLEEP_MODE_DEEP, (sleepTime * 60));
    }
    
}

void pulse(){
    if ((millis() - lastPulse) > debounceTime){
        pulseCount += 5;
        pulseToday += 5;
    }
    lastPulse = millis();
}

int setMeter(String command){
    pulseCount = command.toInt();
    return 1;
}

Now watch your dashboard and see the numbers rolling in… I just hope the tiny solar panel I chose will keep the battery up… cant complain for a $25 garden light that i ripped apart for the panel and the battery

EDIT (part 2)
Oh and the call to set the Total reading
curl https://api.particle.io/v1/devices/***DeviceID***/SetMeter -d access_token=***AccessToken*** -d "args=12345"
[1]: https://www.sparkfun.com/products/13626

15 Likes

Im having some issues with Deep Sleep, it works most of the time but once a day or so it goes to sleep and doesn’t wake up.

it should wake after 2 hours or when the WKP is pulled high by the reed switch, but neither seem to have woken the unit. So tonight i bought it inside and due to the way it is mounted in the enclosure i cheated a little and connected RST to gnd to reset the photon, just briefly like how long you would press the reset button for. the photon woke up and went straight to breathing cyan, but nothing showed in my dashboard. so i unscrewed the boards so i could get to the photon and pressed the reset button briefly, same thing, back to breathing cyan, nothing showing up in my dashboard, then i pressed reset again holding it longer this time about 3 or 4 seconds, the photon reset and did its normal startup sequence.

this is the third time its happened now… and i don’t know where to begin looking. code is in the post above, but i think this may be more than something I’m doing. Maybe @mdma might know why??

Now when the photon did restart properly and connect, the first publish gives me info about when the unit went to sleep (retained variable), and it was the exact time the photon last went offline (9PM last night). so it over slept by 75000ish seconds.

Seems to happen every day at the moment, so could probably test some things if anyone has any idea’s

Is the battery dying after the sun goes down? A long reset would maybe reduce the load enough to allow the battery to self charge a bit for another burst of energy.

@Bdub it stays fully charged same as if the unit was sleeping the whole time…

Any chance you can run this test with a powered unit, as a subset of your water meter code? If it is a problem with the RTC it should show up with just the parts that sleep for 2 hours. We can dive in and troubleshoot this but would love some help narrowing in on the root of the issue since before doing that if possible. Thanks!!

2 steps ahead of you, i have stripped out the code and just left the sleep stuff. its been running all night and is still going. the water monitor has stopped again. so i will let this run for another 4 hours and start testing a few other idea’s

1 Like

I ran into this as a result of a counter wrap in my counter arithmetic once. Could that be your problem?