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