-
Notifications
You must be signed in to change notification settings - Fork 381
/
MailboxNotifier.ino
216 lines (194 loc) · 8.63 KB
/
MailboxNotifier.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// Sample RFM69 sender/node sketch for mailbox motion sensor
// http://www.lowpowerlab.com/mailbox
// 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
// 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
// In sleep mode, Moteino + PIR motion sensor use about ~78uA
// Make sure you adjust the settings in the configuration section below !!!
// **********************************************************************************
// Copyright Felix Rusu 2016, http://www.LowPowerLab.com/contact
// **********************************************************************************
// 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.
//
// 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 <RFM69.h> //get it here: https://github.com/LowPowerLab/RFM69
#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)
#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/
//****************************************************************************************************************
//**** 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
//Match frequency to the hardware version of the radio on your Moteino (uncomment one):
//#define FREQUENCY RF69_433MHZ
//#define FREQUENCY RF69_868MHZ
#define FREQUENCY RF69_915MHZ
//#define FREQUENCY_EXACT 917000000
#define IS_RFM69HW_HCW //uncomment only for RFM69HW/HCW! Leave out if you have RFM69W/CW!
#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
//*********************************************************************************************
#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)
//#define BLINK_EN //uncomment to make LED flash when messages are sent, leave out if you want low power
//#define SERIAL_EN //uncomment this line to enable serial IO debug messages, leave out if you want low power
#ifdef SERIAL_EN
#define SERIAL_BAUD 115200
#define DEBUG(input) {Serial.print(input); delay(1);}
#define DEBUGln(input) {Serial.println(input); delay(1);}
#else
#define DEBUG(input);
#define DEBUGln(input);
#endif
#ifdef ENABLE_ATC
RFM69_ATC radio;
#else
RFM69 radio;
#endif
volatile boolean motionDetected=false;
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];
void setup() {
#ifdef SERIAL_EN
Serial.begin(SERIAL_BAUD);
#endif
radio.initialize(FREQUENCY,NODEID,NETWORKID);
#ifdef IS_RFM69HW_HCW
radio.setHighPower(); //must include this only for RFM69HW/HCW!
#endif
radio.encrypt(ENCRYPTKEY);
#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);
char buff[50];
sprintf(buff, "\nTransmitting at %d Mhz...", FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915);
DEBUGln(buff);
checkBattery();
}
void motionIRQ()
{
motionDetected=true;
DEBUGln("IRQ");
}
void loop() {
now = millis();
checkBattery();
if (motionDetected && (time-MLO > DUPLICATE_INTERVAL))
{
DEBUG("MOTION");
MLO = time; //save timestamp of event
sprintf(sendBuf, "MOTION LO:0s BAT:%sv", BATstr);
sendLen = strlen(sendBuf);
if (radio.sendWithRetry(GATEWAYID, sendBuf, sendLen))
{
DEBUGln("..OK");
#ifdef BLINK_EN
Blink(LED,3);
#endif
}
else DEBUGln("..NOK");
radio.sleep();
}
else sendLoops++;
//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);
sprintf(sendBuf, "%s BAT:%sv", MLOstr, BATstr);
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
}
motionDetected=false; //do NOT move this after the SLEEP line below or motion will never be detected
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);
DEBUGln("WAKEUP");
}
void Blink(byte PIN, int DELAY_MS)
{
pinMode(PIN, OUTPUT);
digitalWrite(PIN,HIGH);
delay(DELAY_MS);
digitalWrite(PIN,LOW);
}
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;
}
}