-
Notifications
You must be signed in to change notification settings - Fork 0
/
Sender5.ino
235 lines (195 loc) · 7.22 KB
/
Sender5.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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
/*
* Project: Sender5 (Ding15)
* Description:
* Sensor for a mailbox. When the lid of the slot is opened, a magnetic reed switch
* triggers and sends a LoRa signal to the receiver. Additionally once per day the
* current battery voltage and the magnetic reed switch state will also be sent to the receiver.
*
* License: 2-Clause BSD License
* Copyright (c) 2023 codingABI
* For details see: License.txt
*
* created by codingABI https://github.com/codingABI/SenderReceiver
*
* External code:
* getBandgap() from https://forum.arduino.cc/t/measuring-battery-voltage-conditionally/319327/5
* For details see externalCode.ino
*
* Used external libraries from Arduino IDE Library Manager
* - LoRa (by Sandeep Mistry)
*
* Hardware:
* - Microcontroller ATmega328P (without crystal, in 8 MHz-RC mode. Board manager: "ATmega328 on a breadboard (8 MHz internal clock)" )
* - HT7333 voltage regulator
* - LoRa SX1278 Ra-02 (433 MHz)
* - 18650 Battery with integrated protection against deep discharge
* - Magnetic reed-switch "normally closed" with a pullup resistor
* - Control LED (Switched on a) 50ms every 8 seconds, b) for a short time,
* during sending the LoRa signal and c) for 16 seconds when LoRa init fails)
* which can be enabled/disabled on demand with physical jumper JP2
*
* Current consumption (measured on JP1 while JP2 was opened):
* - 28uA in deep sleep (5uA is HT7333 idle current)
* - 5mA after wake up
* - 120mA while sending LoRa signal
*
* History:
* 20221226, Initial version
* 20230105, Simplify pinMode for LED_PIN
*/
#define DEBUG false // true for Serial.print
#define SERIALDEBUG if (DEBUG) Serial
#include <SPI.h>
#include <LoRa.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#define LED_PIN 5 // Control LED (Switched on a) 50ms every 8 seconds, b) for a short time, during sending the LoRa signal and c) for 16 seconds when LoRa init fails)
#define SENSW_PIN 3 // Magnetic reed-switch "normally closed" with external pullup resistor
#define NSS_PIN 10 // LoRa NSS
#define RST_PIN 9 // LoRa RST
#define DIO0_PIN 2 // LoRa IRQ
#define ID 5 // ID/Nbr for the individual sender
#define RCSIGNATURE 0b00111000000000000000000000000000UL // Signature for signals (only the first 5 bits are the signature)
/* Signal (32-bit):
* 5 bit: Signature
* 3 bit: ID
* 1 bit: Low battery
* 6 bit: Vcc (0-63)
* 1 bit: Mail box sensor switch
* 1 bit: Pin change interrupt (1 if signal was triggered by the mailbox slot; 0 if signal is the daily status signal)
* 15 bit: unused
*/
// -------- Global variables --------
volatile bool v_pinChangeInterrupt = false;
// Sleep for X seconds
void sleep(int seconds) {
for(int i=0; i < seconds/8; i++) { // Repeat 8 second intervals from WatchdogTimer to reach the needed seconds
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Set sleep mode to "Power-down"
cli();
sleep_enable(); // Set SE bit in SMCR register (Required for sleep_mode)
sei();
sleep_cpu(); // Goto sleep mode
sleep_disable(); // Disable SE bit from SMCR register
enableWatchdogTimer(); // Reenable Watchdog (without the ISR will be startet once and the device resets)
// Flash control LED
pinMode(LED_PIN,OUTPUT);
digitalWrite(LED_PIN,HIGH);
delay(50);
digitalWrite(LED_PIN,LOW);
pinMode(LED_PIN,INPUT);
if (v_pinChangeInterrupt) break; // End loop when IRQ was triggered by switch
}
}
// Enable WatchdogTimer
void enableWatchdogTimer() {
noInterrupts();
// Set bit 3+4 (WDE+WDCE bits)
// From Atmel datasheet: "...Within the next four clock cycles, write the WDE and
// watchdog prescaler bits (WDP) as desired, but with the WDCE bit cleared.
// This must be done in one operation..."
WDTCSR = WDTCSR | B00011000;
// Set Watchdog-Timer duration to 8 seconds
WDTCSR = B00100001;
// Enable Watchdog interrupt by WDIE bit and enable device reset via 1 in WDE bit.
// From Atmel datasheet: "...The third mode, Interrupt and system reset mode, combines the other two modes by first giving an interrupt and then switch to system reset mode. This mode will for instance allow a safe shutdown by saving critical parameters before a system reset..."
WDTCSR = WDTCSR | B01001000;
interrupts();
}
// Interrupt service routine for the Watchdog-Timer
ISR(WDT_vect) {
}
// Handle pin change interrupt for D0 to D7 here
ISR(PCINT2_vect) {
v_pinChangeInterrupt = true;
}
void pciSetup(byte pin) {
*digitalPinToPCMSK(pin) |= bit (digitalPinToPCMSKbit(pin)); // Enable pin
PCIFR |= bit(digitalPinToPCICRbit(pin)); // Clear pending interrupts
PCICR |= bit(digitalPinToPCICRbit(pin)); // Enable interrupt for group
}
void setup() {
SERIALDEBUG.begin(115200);
SERIALDEBUG.print("Init Sender ID ");
SERIALDEBUG.println(ID);
enableWatchdogTimer(); // Watchdog timer
// Pin mode and Pin change interrupt for the switch
pinMode(SENSW_PIN,INPUT);
pciSetup(SENSW_PIN);
// Init LoRa
LoRa.setPins(NSS_PIN,RST_PIN,DIO0_PIN);
SERIALDEBUG.println("Init LoRa Sender");
if (!LoRa.begin(433E6)) { // 433 MHz
// Enable control LED
pinMode(LED_PIN,OUTPUT);
digitalWrite(LED_PIN,HIGH);
SERIALDEBUG.println("Starting LoRa failed");
// Starting LoRa failed => wait for watchdog reset
while (1);
}
LoRa.setSyncWord(0xA5); // ranges from 0-0xFF, default 0x34, see API docs
delay(1000);
}
void loop() {
int id = 5;
int Switch;
int Vcc;
int lowBattery;
static int msgCount = 0;
byte oldADCSRA;
unsigned long sendData;
if (v_pinChangeInterrupt) { // Wait after PinChangeInterrupt to get switch state more stable
SERIALDEBUG.println("Delay after PinChangeInterrupt");
delay(200);
wdt_reset();
}
Switch = digitalRead(SENSW_PIN); // Read switch state
SERIALDEBUG.print("Switch state ");
SERIALDEBUG.println(Switch);
Vcc = getBandgap(); // Vcc in 10mV units
/* Enable low battery warning, when voltage is below threshold 300 = 3.0V
* We measure the voltage after the voltage regulator and not the real battery voltage,
* but this should be enough to detect, when the battery is too low
*/
if (Vcc < 300 ) lowBattery=1; else lowBattery=0;
SERIALDEBUG.print("Battery ");
SERIALDEBUG.println(Vcc);
wdt_reset();
// Enable control LED while sending
pinMode(LED_PIN,OUTPUT);
digitalWrite(LED_PIN,HIGH);
// Send data
sendData = RCSIGNATURE +
(((unsigned long) id & 7) <<24) +
((unsigned long) lowBattery << 23) +
(((((unsigned long) Vcc+5)/10) & 63) << 17) +
((unsigned long) Switch << 16) +
((unsigned long) v_pinChangeInterrupt << 15);
SERIALDEBUG.print("Data to send ");
SERIALDEBUG.println(sendData);
// Send data
SERIALDEBUG.println("Start LoRa sending");
LoRa.beginPacket();
LoRa.print(sendData);
LoRa.endPacket();
SERIALDEBUG.println("End LoRa sending");
// Disable control LED
digitalWrite(LED_PIN,LOW);
pinMode(LED_PIN,INPUT);
// Goto to sleep
SERIALDEBUG.println("Going to sleep");
SERIALDEBUG.flush();
LoRa.sleep();
wdt_reset();
// Save ADC status
oldADCSRA = ADCSRA;
// Disable ADC
ADCSRA = 0;
// Disable TWI (Two Wire Interface = I2C)
TWCR = 0;
v_pinChangeInterrupt = false;
// Sleep mode
sleep(3600*24); // Sleep for 24 hours
// Restore ADC
ADCSRA = oldADCSRA;
SERIALDEBUG.println("Wake up");
}