// Sample RFM69 sketch for PulseMote - reading an EE-SY310 based water/pulse meter // Example: https://lowpowerlab.com/blog/2013/02/02/meet-the-watermote-moteino-based-water-meter-reader-ee-sy310/ // Copyright (c) 2015 Felix Rusu (felix@lowpowerlab.com). All rights reserved. // ********************************************************************************** // License // ********************************************************************************** // This program is free software; you can redistribute it // and/or modify it under the terms of the GNU General // Public License as published by the Free Software // Foundation; either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will // be useful, but WITHOUT ANY WARRANTY; without even the // implied warranty of MERCHANTABILITY or FITNESS FOR A // PARTICULAR PURPOSE. See the GNU General Public // License for more details. // // You should have received a copy of the GNU General // Public License along with this program. // If not, see . // // Licence can be viewed at // http://www.gnu.org/licenses/gpl-3.0.txt // // Please maintain this license information along with authorship // and copyright notices in any redistribution of this code // *************************************************************************************************************************** #include //get it here: http://github.com/lowpowerlab/rfm69 #include //get it here: http://github.com/lowpowerlab/spiflash #include //get it here: https://github.com/LowPowerLab/WirelessProgramming #include #include #include //********************************************************************************************* //************ IMPORTANT SETTINGS - YOU MUST CHANGE/ONFIGURE TO FIT YOUR HARDWARE ************* //********************************************************************************************* #define NODEID 5 #define GATEWAYID 1 #define NETWORKID 250 #define FREQUENCY RF69_915MHZ //Match this with the version of your Moteino! (others: RF69_433MHZ, RF69_868MHZ) #define ENCRYPTKEY "sampleEncryptKey" //has to be same 16 characters/bytes on all nodes, not more not less! //#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W! #define PULSESPERGALLON 45 //how many pulses from sensor equal 1 gallon #define GPMTHRESHOLD 8000 // GPM will reset after this many MS if no pulses are registered #define XMITPERIOD 5000 // GPMthreshold should be less than 2*XMITPERIOD //********************************************************************************************* #ifdef __AVR_ATmega1284P__ #define LED 15 // Moteino MEGAs have LEDs on D15 #define INTERRUPTPIN 1 //INT1 = digital pin 11 (must be a hardware interrupt pin!) #else #define LED 9 // Moteinos have LEDs on D9 #define INTERRUPTPIN 1 //INT1 = digital pin 3 (must be a hardware interrupt pin!) #endif //********************************************************************************************* #define SERIAL_EN //uncomment this line to enable serial IO (when you debug Moteino and need serial output) #define SERIAL_BAUD 115200 #ifdef SERIAL_EN #define DEBUG(input) {Serial.print(input);} #define DEBUGln(input) {Serial.println(input);} #else #define DEBUG(input); #define DEBUGln(input); #endif RFM69 radio; SPIFlash flash(8, 0xEF30); //WINDBOND 4MBIT flash chip on CS pin D8 (default for Moteino) volatile byte ledState = LOW; volatile unsigned long PulseCounterVolatile = 0; // use volatile for shared variables unsigned long NOW = 0; unsigned long PulseCounter = 0; unsigned long LASTMINUTEMARK = 0; unsigned long PULSECOUNTLASTMINUTEMARK = 0; //keeps pulse count at the last minute mark byte COUNTEREEPROMSLOTS = 10; unsigned long COUNTERADDRBASE = 8; //address in EEPROM that points to the first possible slot for a counter unsigned long COUNTERADDR = 0; //address in EEPROM that points to the latest Counter in EEPROM byte secondCounter = 0; unsigned long TIMESTAMP_pulse_prev = 0; unsigned long TIMESTAMP_pulse_curr = 0; int pulseAVGInterval = 0; int pulsesPerXMITperiod = 0; float GPM=0, GLM=0, GAL=0, GALlast=0, GPMlast=0, GLMlast=0; byte sendLen; char buff[80]; char* GALstr="99999999999999.99"; //longest expected GAL message char* GPMstr="99999.99"; //longest expected GPM message char* GLMstr="9999999.99"; //longest expected GLM message boolean WPReady = false; void setup() { #ifdef SERIAL_EN Serial.begin(SERIAL_BAUD); #endif radio.initialize(FREQUENCY,NODEID,NETWORKID); #ifdef IS_RFM69HW radio.setHighPower(); //uncomment only for RFM69HW! #endif radio.encrypt(ENCRYPTKEY); pinMode(LED, OUTPUT); //initialize counter from EEPROM unsigned long savedCounter = EEPROM_Read_Counter(); if (savedCounter <=0) savedCounter = 1; //avoid division by 0 PulseCounterVolatile = PulseCounter = PULSECOUNTLASTMINUTEMARK = savedCounter; attachInterrupt(INTERRUPTPIN, pulseCounterInterrupt, RISING); Timer1.initialize(XMITPERIOD * 1000L); Timer1.attachInterrupt(XMIT); sprintf(buff, "\nTransmitting at %d Mhz, id:%d nid:%d gid:%d", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915, NODEID, NETWORKID, GATEWAYID); DEBUG(buff); for (byte i=0;i GPMTHRESHOLD) //more than 'GPMthreshold' seconds passed since last pulse... resetting GPM pulsesPerXMITperiod=pulseAVGInterval=0; else { pulsesPerXMITperiod++; pulseAVGInterval += TIMESTAMP_pulse_curr - TIMESTAMP_pulse_prev; } interrupts(); } void XMIT() { noInterrupts(); PulseCounter = PulseCounterVolatile; interrupts(); if (millis() - TIMESTAMP_pulse_curr >= 5000) { ledState = !ledState; digitalWrite(LED, ledState); } //calculate Gallons counter GAL = ((float)PulseCounter)/PULSESPERGALLON; DEBUG("PulseCounter:");DEBUG(PulseCounter);DEBUG(", GAL: "); DEBUGln(GAL); //calculate & output GPM GPM = pulseAVGInterval > 0 ? 60.0 * 1000 * (1.0/PULSESPERGALLON)/(pulseAVGInterval/pulsesPerXMITperiod) : 0; dtostrf(GAL,3,2, GALstr); dtostrf(GPM,3,2, GPMstr); pulsesPerXMITperiod = 0; pulseAVGInterval = 0; secondCounter += XMITPERIOD/1000; //once per minute, output a GallonsLastMinute count if (secondCounter>=60) { //DEBUG("60sec mark ... "); secondCounter=0; GLM = ((float)(PulseCounter - PULSECOUNTLASTMINUTEMARK))/PULSESPERGALLON; PULSECOUNTLASTMINUTEMARK = PulseCounter; EEPROM_Write_Counter(PulseCounter); dtostrf(GLM,3,2, GLMstr); sprintf(buff, "GAL:%s GPM:%s GLM:%s", GALstr, GPMstr, GLMstr); //DEBUGln("done"); } else { sprintf(buff, "GAL:%s GPM:%s", GALstr, GPMstr); } if (GPM!=GPMlast || GAL!=GALlast || GLM!=GLMlast) { sendLen = strlen(buff); radio.sendWithRetry(GATEWAYID, buff, sendLen); GALlast = GAL; GPMlast = GPM; GLMlast = GLM; } DEBUGln(buff); } unsigned long EEPROM_Read_Counter() { return EEPROM_Read_ULong(EEPROM_Read_ULong(COUNTERADDR)); } void EEPROM_Write_Counter(unsigned long counterNow) { if (counterNow == EEPROM_Read_Counter()) { DEBUG("{EEPROM-SKIP(no changes)}"); return; //skip if nothing changed } DEBUG("{EEPROM-SAVE("); DEBUG(EEPROM_Read_ULong(COUNTERADDR)); DEBUG(")="); DEBUG(PulseCounter); DEBUG("}"); unsigned long CounterAddr = EEPROM_Read_ULong(COUNTERADDR); if (CounterAddr == COUNTERADDRBASE+8*(COUNTEREEPROMSLOTS-1)) CounterAddr = COUNTERADDRBASE; else CounterAddr += 8; EEPROM_Write_ULong(CounterAddr, counterNow); EEPROM_Write_ULong(COUNTERADDR, CounterAddr); } unsigned long EEPROM_Read_ULong(int address) { unsigned long temp; for (byte i=0; i<8; i++) temp = (temp << 8) + EEPROM.read(address++); return temp; } void EEPROM_Write_ULong(int address, unsigned long data) { for (byte i=0; i<8; i++) { EEPROM.write(address+7-i, data); data = data >> 8; } }