2014-10-17 17:32:03 +01:00
// Sample RFM69 sender/node sketch for mailbox motion sensor
2014-09-26 15:00:28 +01:00
// http://www.lowpowerlab.com/mailbox
2014-09-26 14:58:48 +01:00
// PIR motion sensor connected to D3 (INT1)
// When RISE happens on D3, the sketch transmits a "MOTION" msg to receiver Moteino and goes back to sleep
2014-10-17 17:32:03 +01:00
// It then wakes up every 32 seconds and sends a message indicating when the last
// motion event happened (days, hours, minutes, seconds ago) and the battery level
2014-09-26 14:58:48 +01:00
// In sleep mode, Moteino + PIR motion sensor use about ~78uA
2014-10-17 17:32:03 +01:00
// Make sure you adjust the settings in the configuration section below !!!
2014-09-26 14:58:48 +01:00
// **********************************************************************************
2016-11-18 03:51:22 +00:00
// Copyright Felix Rusu 2016, http://www.LowPowerLab.com/contact
2014-09-26 14:58:48 +01:00
// **********************************************************************************
// 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
2014-10-17 17:32:03 +01:00
// Foundation; either version 3 of the License, or
2014-09-26 14:58:48 +01:00
// (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
2014-10-17 17:32:03 +01:00
// PARTICULAR PURPOSE. See the GNU General Public
2014-09-26 14:58:48 +01:00
// License for more details.
//
// Licence can be viewed at
2014-10-17 17:32:03 +01:00
// http://www.gnu.org/licenses/gpl-3.0.txt
2014-09-26 14:58:48 +01:00
//
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
// **********************************************************************************
# include <RFM69.h> //get it here: https://github.com/LowPowerLab/RFM69
2016-11-18 03:51:22 +00:00
# include <RFM69_ATC.h>//get it here: https://github.com/lowpowerlab/RFM69
# include <SPIFlash.h> //get it here: https://github.com/lowpowerlab/spiflash
# include <SPI.h> //included with Arduino IDE (www.arduino.cc)
2014-09-26 14:58:48 +01:00
# include <LowPower.h> //get library from: https://github.com/LowPowerLab/LowPower
//writeup here: http://www.rocketscream.com/blog/2011/07/04/lightweight-low-power-arduino-library/
2016-11-18 03:51:22 +00:00
//****************************************************************************************************************
//**** IMPORTANT RADIO SETTINGS - YOU MUST CHANGE/CONFIGURE TO MATCH YOUR HARDWARE TRANSCEIVER CONFIGURATION! ****
//****************************************************************************************************************
# define NODEID 55 //unique for each node on same network
# define NETWORKID 100 //the same on all nodes that talk to each other
# define GATEWAYID 1
2014-09-26 14:58:48 +01:00
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
2016-11-18 03:51:22 +00:00
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
# define FREQUENCY RF69_915MHZ
//#define FREQUENCY_EXACT 917000000
# define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W!
# define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes!
# define SENDEVERYXLOOPS 8 //each loop sleeps 8 seconds, so send status message every this many sleep cycles (default "4" = 32 seconds)
//*********************************************************************************************
# define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL
# define ATC_RSSI -75
2014-10-17 17:32:03 +01:00
//*********************************************************************************************
2014-09-26 14:58:48 +01:00
2016-11-18 03:51:22 +00:00
# define MOTION_PIN 3 // D3
# define MOTION_IRQ 1 // hardware interrupt 1 (D3) - where motion sensors OUTput is connected, this will generate an interrupt every time there is MOTION
# define BATT_MONITOR A7 //through 1Meg+470Kohm and 0.1uF cap from battery VCC - this ratio divides the voltage to bring it below 3.3V where it is scaled to a readable range
# define BATT_CYCLES 50 // read and report battery voltage every this many sleep cycles (ex 30cycles * 8sec sleep = 240sec/4min). For 450 cyclesyou would get ~1 hour intervals
# define BATT_FORMULA(reading) reading * 0.00322 * 1.47
# define LED 5 // Moteinos have LEDs on D9
# define DUPLICATE_INTERVAL 55000 //avoid duplicates in 55second intervals (ie mailman sometimes spends 30+ seconds at mailbox)
2014-10-17 17:32:03 +01:00
//#define BLINK_EN //uncomment to make LED flash when messages are sent, leave out if you want low power
2014-09-26 14:58:48 +01:00
2014-10-17 17:32:03 +01:00
//#define SERIAL_EN //uncomment this line to enable serial IO debug messages, leave out if you want low power
2014-09-26 14:58:48 +01:00
# ifdef SERIAL_EN
2016-11-18 03:51:22 +00:00
# define SERIAL_BAUD 115200
2014-09-26 14:58:48 +01:00
# define DEBUG(input) {Serial.print(input); delay(1);}
# define DEBUGln(input) {Serial.println(input); delay(1);}
# else
# define DEBUG(input);
# define DEBUGln(input);
# endif
2016-11-18 03:51:22 +00:00
# ifdef ENABLE_ATC
RFM69_ATC radio ;
# else
RFM69 radio ;
# endif
2014-09-26 14:58:48 +01:00
volatile boolean motionDetected = false ;
2016-11-18 03:51:22 +00:00
char sendBuf [ 61 ] ;
byte sendLen ;
byte sendLoops = 0 ;
unsigned long MLO = 0 ; //MailLastOpen (ago, in ms)
unsigned long now = 0 , time = 0 , lastSend = 0 , temp = 0 ;
float batteryVolts = 5 ;
char BATstr [ 20 ] ;
char MLOstr [ 20 ] ;
2014-09-26 14:58:48 +01:00
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 ) ;
2016-11-18 03:51:22 +00:00
# ifdef FREQUENCY_EXACT
radio . setFrequency ( FREQUENCY_EXACT ) ; //set frequency to some custom frequency
# endif
radio . setPowerLevel ( 29 ) ;
# ifdef ENABLE_ATC
radio . enableAutoPower ( ATC_RSSI ) ;
# endif
radio . sendWithRetry ( GATEWAYID , " START " , 6 ) ;
radio . sleep ( ) ;
pinMode ( MOTION_PIN , INPUT ) ;
pinMode ( BATT_MONITOR , INPUT ) ;
attachInterrupt ( MOTION_IRQ , motionIRQ , RISING ) ;
2014-09-26 14:58:48 +01:00
char buff [ 50 ] ;
sprintf ( buff , " \n Transmitting at %d Mhz... " , FREQUENCY = = RF69_433MHZ ? 433 : FREQUENCY = = RF69_868MHZ ? 868 : 915 ) ;
DEBUGln ( buff ) ;
2016-11-18 03:51:22 +00:00
checkBattery ( ) ;
2014-09-26 14:58:48 +01:00
}
void motionIRQ ( )
{
motionDetected = true ;
2016-11-18 03:51:22 +00:00
DEBUGln ( " IRQ " ) ;
2014-09-26 14:58:48 +01:00
}
void loop ( ) {
now = millis ( ) ;
2016-11-18 03:51:22 +00:00
checkBattery ( ) ;
if ( motionDetected & & ( time - MLO > DUPLICATE_INTERVAL ) )
2014-09-26 14:58:48 +01:00
{
2016-11-18 03:51:22 +00:00
DEBUG ( " MOTION " ) ;
2014-09-26 14:58:48 +01:00
MLO = time ; //save timestamp of event
2016-11-18 03:51:22 +00:00
sprintf ( sendBuf , " MOTION LO:0s BAT:%sv " , BATstr ) ;
2014-09-26 14:58:48 +01:00
sendLen = strlen ( sendBuf ) ;
if ( radio . sendWithRetry ( GATEWAYID , sendBuf , sendLen ) )
{
2016-11-18 03:51:22 +00:00
DEBUGln ( " ..OK " ) ;
2014-09-26 14:58:48 +01:00
# ifdef BLINK_EN
Blink ( LED , 3 ) ;
# endif
}
2016-11-18 03:51:22 +00:00
else DEBUGln ( " ..NOK " ) ;
2014-09-26 14:58:48 +01:00
radio . sleep ( ) ;
}
else sendLoops + + ;
2016-11-18 03:51:22 +00:00
2014-09-26 14:58:48 +01:00
//send readings every SENDEVERYXLOOPS
if ( sendLoops > = SENDEVERYXLOOPS )
{
sendLoops = 0 ;
char periodO = ' X ' , periodC = ' X ' ;
unsigned long lastOpened = ( time - MLO ) / 1000 ; //get seconds
unsigned long LO = lastOpened ;
if ( lastOpened < = 59 ) periodO = ' s ' ; //1-59 seconds
else if ( lastOpened < = 3599 ) { periodO = ' m ' ; lastOpened / = 60 ; } //1-59 minutes
else if ( lastOpened < = 259199 ) { periodO = ' h ' ; lastOpened / = 3600 ; } // 1-71 hours
else if ( lastOpened > = 259200 ) { periodO = ' d ' ; lastOpened / = 86400 ; } // >=3 days
if ( periodO = = ' d ' )
sprintf ( MLOstr , " LO:%ldd%ldh " , lastOpened , ( LO % 86400 ) / 3600 ) ;
else if ( periodO = = ' h ' )
sprintf ( MLOstr , " LO:%ldh%ldm " , lastOpened , ( LO % 3600 ) / 60 ) ;
else sprintf ( MLOstr , " LO:%ld%c " , lastOpened , periodO ) ;
2016-11-18 03:51:22 +00:00
sprintf ( sendBuf , " %s BAT:%sv " , MLOstr , BATstr ) ;
2014-09-26 14:58:48 +01:00
sendLen = strlen ( sendBuf ) ;
radio . send ( GATEWAYID , sendBuf , sendLen ) ;
radio . sleep ( ) ;
DEBUG ( sendBuf ) ; DEBUG ( " ( " ) ; DEBUG ( sendLen ) ; DEBUGln ( " ) " ) ;
lastSend = time ;
# ifdef BLINK_EN
Blink ( LED , 5 ) ;
# endif
}
2016-11-18 03:51:22 +00:00
motionDetected = false ; //do NOT move this after the SLEEP line below or motion will never be detected
2014-09-26 14:58:48 +01:00
time = time + 8000 + millis ( ) - now + 480 ; //correct millis() resonator drift, may need to be tweaked to be accurate
LowPower . powerDown ( SLEEP_8S , ADC_OFF , BOD_OFF ) ;
2016-11-18 03:51:22 +00:00
DEBUGln ( " WAKEUP " ) ;
2014-09-26 14:58:48 +01:00
}
void Blink ( byte PIN , int DELAY_MS )
{
pinMode ( PIN , OUTPUT ) ;
digitalWrite ( PIN , HIGH ) ;
delay ( DELAY_MS ) ;
digitalWrite ( PIN , LOW ) ;
2016-11-18 03:51:22 +00:00
}
byte cycleCount = BATT_CYCLES ;
void checkBattery ( )
{
if ( cycleCount + + = = BATT_CYCLES ) //only read battery every BATT_CYCLES sleep cycles
{
unsigned int reading = 0 ;
//enable battery monitor
pinMode ( A3 , OUTPUT ) ;
digitalWrite ( A3 , LOW ) ;
for ( byte i = 0 ; i < 10 ; i + + )
reading + = analogRead ( BATT_MONITOR ) ;
//disable battery monitor
pinMode ( A3 , INPUT ) ; //highZ mode will allow p-mosfet to be pulled high and disconnect the voltage divider on the weather shield
batteryVolts = BATT_FORMULA ( reading / 10 ) ;
DEBUG ( " reading: " ) ; DEBUGln ( reading ) ;
dtostrf ( batteryVolts , 3 , 2 , BATstr ) ;
cycleCount = 0 ;
}
2014-10-17 17:32:03 +01:00
}