-
Notifications
You must be signed in to change notification settings - Fork 0
/
GPS_tracker_433.ino
279 lines (232 loc) · 12.7 KB
/
GPS_tracker_433.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
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/*******************************************************************************
* The Things Network - ABP Feather M0 RFM96
*
* This uses ABP (Activation by Personalization), where session keys for
* communication would be assigned/generated by TTN and hard-coded on the device.
*
* Copyright (c) 2015 Thomas Telkamp and Matthijs Kooijman
* Copyright (c) 2018 Terry Moore, MCCI
* Copyright (c) 2018 Brent Rubell, Adafruit Industries
*
* Permission is hereby granted, free of charge, to anyone
* obtaining a copy of this document and accompanying files,
* to do whatever they want with them without any restriction,
* including, but not limited to, copying, modification and redistribution.
* NO WARRANTY OF ANY KIND IS PROVIDED.
*
* Reference: https://github.com/mcci-catena/arduino-lmic
https://github.com/OndrejKnebl/TTNv3-LoRaVSB/blob/main/GPS_Tracker/GPS_Tracker/GPS_Tracker.ino
*
* Modified for GPS Tracker Adafruit Feather M0 RFM96 LoRa Radio - 433MHz, 30. 1. 2024
*******************************************************************************/
// Include necessary libraries for LoRa communication, GPS handling, RTC, and SPI communication
#include <lmic.h>
#include <hal/hal.h>
#include <SPI.h>
#include <TinyGPSPlus.h> // For parsing GPS data
#include <RTCZero.h> // Real Time Clock Library
// Initialize GPS and RTC objects
TinyGPSPlus gps;
RTCZero rtc;
// Define pin numbers for tracking/charging switches and LEDs
int switch_charging = 14;
int switch_tracking = 15;
int green_LED = 18;
int red_LED = 19;
// LED blink
const unsigned BLINK_INTERVAL = 1; // 1 second
// Variables to keep track of LED states
bool green_led_on = false;
bool red_led_on = false;
//--------------------------------------- Here change your keys -------------------------------------------------
static const PROGMEM u1_t NWKSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // LoRaWAN NwkSKey, network session key, MSB
static const u1_t PROGMEM APPSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // LoRaWAN AppSKey, application session key, MSB
static const u4_t DEVADDR = 0x00000000; // LoRaWAN end-device address (DevAddr), MSB
//---------------------------------------------------------------------------------------------------------------
// Buffer for transmitting GPS data
uint8_t txBuffer[9];
bool GPS_Error = false; // Flag to indicate GPS error
// Scheduling transmissions
static osjob_t sendjob;
// Pin mapping for the Adafruit Feather M0
const lmic_pinmap lmic_pins = {
.nss = 8,
.rxtx = LMIC_UNUSED_PIN,
.rst = 4,
.dio = { 3, 6, LMIC_UNUSED_PIN },
.rxtx_rx_active = 0,
.rssi_cal = 8,
.spi_freq = 8000000,
};
// Function to put the device into sleep mode for a specified duration
void goSleep(byte hours, byte minutes, byte seconds) {
rtc.setTime(0, 0, 0);
rtc.setAlarmTime(hours, minutes, seconds);
rtc.standbyMode(); // Set time and alarm for sleep duration, then enter standby mode
}
void onEvent(ev_t ev) {
if (ev == EV_TXCOMPLETE) { // If message was sent
goSleep(0, 0, 8); // Put device to sleep for 8 seconds
os_setCallback(&sendjob, do_send); // Schedule next send after wake-up.
}
}
void turnOffLEDs() {
digitalWrite(red_LED, LOW); // Turn the red LED off by making the voltage LOW
digitalWrite(green_LED, LOW); // Turn the green LED off by making the voltage LOW
green_led_on = false; // Update state to reflect that the green LED is now off
}
void blinkLEDTracking() {
digitalWrite(red_LED, LOW); // Turn the red LED off by making the voltage LOW
if (green_led_on) {
digitalWrite(green_LED, LOW); // Turn the green LED off by making the voltage LOW
} else {
digitalWrite(green_LED, HIGH); // Turn the green LED on by making the voltage HIGH
}
green_led_on = !green_led_on; // Toggle the green LED's state for the next call
Serial.println(F("Tracking and charging")); // Print status message to serial
}
void blinkLEDDoingNothing() {
digitalWrite(green_LED, LOW); // Turn the green LED off by making the voltage LOW
digitalWrite(red_LED, HIGH); // Turn the red LED on by making the voltage HIGH
if (green_led_on) {
digitalWrite(green_LED, LOW); // Turn the green LED off by making the voltage LOW
digitalWrite(red_LED, HIGH); // Turn the red LED on by making the voltage HIGH
} else {
digitalWrite(green_LED, HIGH); // Turn the green LED on by making the voltage HIGH
digitalWrite(red_LED, LOW); // Turn the red LED off by making the voltage LOW
}
green_led_on = !green_led_on; // Toggle the green LED's state for the next call
Serial.println(F("Not charging and not tracking")); // Print status message to serial
}
void blinkLEDCharging() {
digitalWrite(green_LED, LOW); // Turn the green LED off by making the voltage LOW
if (red_led_on) {
digitalWrite(red_LED, HIGH); // Turn the red LED on by making the voltage HIGH
} else {
digitalWrite(red_LED, LOW); // Turn the red LED off by making the voltage LOW
}
red_led_on = !red_led_on; // Toggle the red LED's state for the next call
Serial.println(F("Charging only")); // Print status message to serial
}
void get_coords() {
bool newData = false;
// Read new data from the GPS module for up to 1 second.
for (unsigned long start = millis(); millis() - start < 1000;) {
while (Serial1.available()) {
// Try to parse GPS data. If successful, set newData to true.
if (gps.encode(Serial1.read())) {
newData = true;
}
}
}
// Check if new valid GPS data was obtained within the time frame.
if (newData && gps.location.isValid() && gps.altitude.isValid() && gps.hdop.isValid() && (gps.location.age() < 1000)) {
GPS_Error = false; // Indicate no error with the GPS data
build_packet(); // Proceed to build the packet with the GPS data.
} else {
// If valid GPS data was not obtained, print status message to serial an error message and set the GPS_Error flag.
Serial.println("No GPS or data aren't valid!");
GPS_Error = true;
}
}
void build_packet() {
// Latitude, Longitude, altitude and hdop are multiplied to preserve precision without using floats.
uint32_t latitude = gps.location.lat() * 10000;
uint32_t longitude = gps.location.lng() * 10000;
uint16_t altitude = gps.altitude.meters() * 10;
uint8_t hdop = gps.hdop.value() * 10;
// Pack the scaled GPS data into the transmission buffer
txBuffer[0] = latitude >> 16;
txBuffer[1] = latitude >> 8;
txBuffer[2] = latitude;
txBuffer[3] = longitude >> 16;
txBuffer[4] = longitude >> 8;
txBuffer[5] = longitude;
txBuffer[6] = altitude >> 8;
txBuffer[7] = altitude;
txBuffer[8] = hdop;
}
void do_send(osjob_t* j) {
if (digitalRead(switch_tracking) == LOW) { //If tracking and charging
turnOffLEDs(); // Turn off any LEDs before starting the process
blinkLEDTracking(); // Indicate tracking status with the green LED
goSleep(0, 0, 1); // Short sleep before send data
blinkLEDTracking(); // Blink again
if (!(LMIC.opmode & OP_TXRXPEND)) { // Check if there is not a current TX/RX job running
get_coords(); // Attempt to get fresh GPS coordinates
// If GPS data is valid and ready to be sent
if (!GPS_Error) {
turnOffLEDs();
LMIC_setTxData2(1, (uint8_t*)txBuffer, sizeof(txBuffer) + 1, 0); // Queue the packet for transmission with LMIC library
Serial.println(F("Packet queued.")); // Indicate packet is ready to send
} else {
// If GPS data is invalid, reschedule the send operation
os_setCallback(&sendjob, do_send);
}
} else {
// If a transmission is in progress, log and reschedule
Serial.println(F("OP_TXRXPEND, not sending"));
os_setCallback(&sendjob, do_send);
}
} else if (digitalRead(switch_charging) == LOW) { // Charging only mode
blinkLEDCharging(); // Indicate charging status with the red LED
goSleep(0, 0, 1); // sleep intervals between LED blinks
blinkLEDCharging(); // Blink again for visual indication
goSleep(0, 0, 4); // Longer sleep to conserve power while charging
os_setCallback(&sendjob, do_send); // Reschedule the job for continuity
} else if ((digitalRead(switch_tracking) == HIGH) && (digitalRead(switch_charging) == HIGH)) { // Not charging and not tracking mode
blinkLEDDoingNothing(); // Use LEDs to indicate the device is idle
os_setTimedCallback(&sendjob, os_getTime() + sec2osticks(BLINK_INTERVAL), do_send);
} else { // Default action if other conditions are not met
os_setCallback(&sendjob, do_send);
}
}
void setup() {
// Start serial communication with the PC
Serial.begin(9600);
Serial.println(F("Starting"));
// Start serial communication with GPS module.
Serial1.begin(9600);
// Set up LED pins as outputs and turn them off.
pinMode(green_LED, OUTPUT);
pinMode(red_LED, OUTPUT);
digitalWrite(green_LED, LOW);
digitalWrite(red_LED, LOW);
// Set up switch pins as inputs with internal pull-up resistors enabled.
// This configuration helps in detecting when the switches are pressed.
pinMode(switch_charging, INPUT);
pinMode(switch_tracking, INPUT);
digitalWrite(switch_charging, HIGH); // Activate Adafruit internal pull up
digitalWrite(switch_tracking, HIGH); // Activate Adafruit internal pull up
// Initialize the LoRaWAN stack.
os_init();
LMIC_reset();
// Prepare the session keys for LoRaWAN communication.
uint8_t appskey[sizeof(APPSKEY)];
uint8_t nwkskey[sizeof(NWKSKEY)];
// Copy the keys from program memory into RAM and preparing the device for communication.
memcpy_P(appskey, APPSKEY, sizeof(APPSKEY));
memcpy_P(nwkskey, NWKSKEY, sizeof(NWKSKEY));
LMIC_setSession(0x13, DEVADDR, nwkskey, appskey);
// Configures LoRaWAN channels for the EU433 frequency plan.
LMIC_setupChannel(0, 433175000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 0 to use a frequency of 433.175 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(1, 433375000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 1 to use a frequency of 433.375 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(2, 433575000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 2 to use a frequency of 433.575 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(3, 433775000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 3 to use a frequency of 433.775 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(4, 433975000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 4 to use a frequency of 433.975 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(5, 434175000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 5 to use a frequency of 434.175 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(6, 434375000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 6 to use a frequency of 434.375 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setupChannel(7, 434575000, DR_RANGE_MAP(DR_SF12, DR_SF7), BAND_CENTI); // Set up channel 7 to use a frequency of 434.575 MHz, set spreading factor from SF12 to SF7, set 1% duty cycle.
LMIC_setLinkCheckMode(0); // Disable link check validation
LMIC.dn2Dr = DR_SF9; // TTN uses SF9 for its RX2 window.
LMIC_setDrTxpow(DR_SF7, 17); // Set the spreading factor and transmit power for the uplink of an antenna with a gain of -7 dBd to ensure the ERP does not exceed limit of 10 dBm.
LMIC_setAdrMode(1); // Enable Adaptive Data Rate (ADR)
LMIC_setClockError(MAX_CLOCK_ERROR * 1 / 100); // Set the clock error rate, which help in adjusting inaccuracies in the device's clock. Here it's set to 1% of the maximum clock error.
rtc.begin(); // Initialize the Real Time Clock (RTC) library
rtc.enableAlarm(rtc.MATCH_MMSS); //Enable the alarm function of the RTC, which can wake the device from sleep.
do_send(&sendjob); // Start send job
}
// Main program loop, which runs continuously after the setup.
void loop() {
os_runloop_once();
}