forked from disk91/wisblock-soil-moisture-solar
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlora_handler.cpp
341 lines (298 loc) · 9.57 KB
/
lora_handler.cpp
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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
/**
* @file lora_handler.cpp
* @author Bernd Giesecke ([email protected])
* @brief Initialization, event handlers and task for LoRaWan
* @version 0.1
* @date 2020-08-15
*
* @copyright Copyright (c) 2020
*
*/
#include "main.h"
#include <CayenneLPP.h>
CayenneLPP lpp(51);
/** DIO1 GPIO pin for RAK4631 */
#define PIN_LORA_DIO_1 47
/** Max size of the data to be transmitted. */
#define LORAWAN_APP_DATA_BUFF_SIZE 64
/** Number of trials for the join request. */
#define JOINREQ_NBTRIALS 8
/** Lora application data buffer. */
static uint8_t m_lora_app_data_buffer[LORAWAN_APP_DATA_BUFF_SIZE];
/** Lora application data structure. */
static lmh_app_data_t m_lora_app_data = {m_lora_app_data_buffer, 0, 0, 0, 0};
// LoRaWan event handlers
/** LoRaWan callback when join network finished */
static void lorawan_has_joined_handler(void);
/** LoRaWan callback when data arrived */
static void lorawan_rx_handler(lmh_app_data_t *app_data);
/** LoRaWan callback after class change request finished */
static void lorawan_confirm_class_handler(DeviceClass_t Class);
/** LoRaWan Function to send a package */
bool sendLoRaFrame(void);
/** Semaphore used by SX126x IRQ handler to wake up LoRaWan task */
SemaphoreHandle_t loraEvent = NULL;
/**@brief Structure containing LoRaWan parameters, needed for lmh_init()
*
* Set structure members to
* LORAWAN_ADR_ON or LORAWAN_ADR_OFF to enable or disable adaptive data rate
* LORAWAN_DEFAULT_DATARATE OR DR_0 ... DR_5 for default data rate or specific data rate selection
* LORAWAN_PUBLIC_NETWORK or LORAWAN_PRIVATE_NETWORK to select the use of a public or private network
* JOINREQ_NBTRIALS or a specific number to set the number of trials to join the network
* LORAWAN_DEFAULT_TX_POWER or a specific number to set the TX power used
* LORAWAN_DUTYCYCLE_ON or LORAWAN_DUTYCYCLE_OFF to enable or disable duty cycles
* Please note that ETSI mandates duty cycled transmissions.
*/
static lmh_param_t lora_param_init = {LORAWAN_ADR_OFF, DR_3, LORAWAN_PUBLIC_NETWORK, JOINREQ_NBTRIALS, LORAWAN_DEFAULT_TX_POWER, LORAWAN_DUTYCYCLE_ON};
/** Structure containing LoRaWan callback functions, needed for lmh_init() */
static lmh_callback_t lora_callbacks = {BoardGetBatteryLevel, BoardGetUniqueId, BoardGetRandomSeed,
lorawan_rx_handler, lorawan_has_joined_handler, lorawan_confirm_class_handler};
/** Device EUI required for OTAA network join */
uint8_t nodeDeviceEUI[8] = {0x03, 0x6D, 0x77, 0x81, 0x2D, 0x7F, 0x30, 0x63};
/** Application EUI required for network join */
uint8_t nodeAppEUI[8] = {0x70, 0x13, 0xB4, 0xA9, 0x58, 0x63, 0x4B, 0x80};
/** Application key required for network join */
uint8_t nodeAppKey[16] = {0xF7, 0xD0, 0xAD, 0x2C, 0xB2, 0x61, 0x80, 0x9E, 0x78, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
/** Device address required for ABP network join */
uint32_t nodeDevAddr = 0x26021FB6;
/** Network session key required for ABP network join */
uint8_t nodeNwsKey[16] = {0x32, 0x3D, 0x15, 0x5A, 0x00, 0x0D, 0xF3, 0x35, 0x30, 0x7A, 0x16, 0xDA, 0x0C, 0x9D, 0xF5, 0x3F};
/** Application session key required for ABP network join */
uint8_t nodeAppsKey[16] = {0x3F, 0x6A, 0x66, 0x45, 0x9D, 0x5E, 0xDC, 0xA6, 0x3C, 0xBC, 0x46, 0x19, 0xCD, 0x61, 0xA1, 0x1E};
/** Flag whether to use OTAA or ABP network join method */
bool doOTAA = true;
/** LoRa task handle */
TaskHandle_t loraTaskHandle;
/** GPS reading task */
void loraTask(void *pvParameters);
/**
* @brief SX126x interrupt handler
* Called when DIO1 is set by SX126x
* Gives loraEvent semaphore to wake up LoRaWan handler task
*
*/
void loraIntHandler(void)
{
// SX126x set IRQ
if (loraEvent != NULL)
{
// Wake up LoRa task
xSemaphoreGive(loraEvent);
}
}
/**
* @brief Initialize LoRa HW and LoRaWan MAC layer
*
* @return int8_t result
* 0 => OK
* -1 => SX126x HW init failure
* -2 => LoRaWan MAC initialization failure
* -3 => Subband selection failure
* -4 => LoRaWan handler task start failure
*/
int8_t initLoRaWan(void)
{
// Create the LoRaWan event semaphore
loraEvent = xSemaphoreCreateBinary();
// Initialize semaphore
xSemaphoreGive(loraEvent);
// Initialize LoRa chip.
if (lora_rak4630_init() != 0)
{
return -1;
}
// Setup the EUIs and Keys
lmh_setDevEui(nodeDeviceEUI);
lmh_setAppEui(nodeAppEUI);
lmh_setAppKey(nodeAppKey);
lmh_setNwkSKey(nodeNwsKey);
lmh_setAppSKey(nodeAppsKey);
lmh_setDevAddr(nodeDevAddr);
// Initialize LoRaWan
if (lmh_init(&lora_callbacks, lora_param_init, doOTAA) != 0)
{
return -2;
}
// For some regions we might need to define the sub band the gateway is listening to
// This must be called AFTER lmh_init()
if (!lmh_setSubBandChannels(1))
{
return -3;
}
// In deep sleep we need to hijack the SX126x IRQ to trigger a wakeup of the nRF52
attachInterrupt(PIN_LORA_DIO_1, loraIntHandler, RISING);
// Start the task that will handle the LoRaWan events
#ifndef MAX_SAVE
Serial.println("Starting LoRaWan task");
#endif
if (!xTaskCreate(loraTask, "LORA", 2048, NULL, TASK_PRIO_LOW, &loraTaskHandle))
{
return -4;
}
// Start Join procedure
#ifndef MAX_SAVE
Serial.println("Start network join request");
#endif
lmh_join();
return 0;
}
/**
* @brief Independent task to handle LoRa events
*
* @param pvParameters Unused
*/
void loraTask(void *pvParameters)
{
while (1)
{
if (lmh_join_status_get() == LMH_SET)
{ // Switch off the indicator lights
digitalWrite(LED_CONN, LOW);
}
// Only if semaphore is available we need to handle LoRa events.
// Otherwise we sleep here until an event occurs
if (xSemaphoreTake(loraEvent, portMAX_DELAY) == pdTRUE)
{
// Switch off the indicator lights
digitalWrite(LED_CONN, HIGH);
// Handle Radio events with special process command!!!!
Radio.IrqProcessAfterDeepSleep();
}
}
}
/**
* @brief LoRa function for handling HasJoined event.
*/
static void lorawan_has_joined_handler(void)
{
if (doOTAA)
{
uint32_t otaaDevAddr = lmh_getDevAddr();
#ifndef MAX_SAVE
Serial.printf("OTAA joined and got dev address %08X\n", otaaDevAddr);
#endif
}
else
{
#ifndef MAX_SAVE
Serial.println("ABP joined");
#endif
}
// Default is Class A, where the SX1262 transceiver is in sleep mode unless a package is sent
// If switched to Class C the power consumption is higher because the SX1262 chip remains in RX mode
// lmh_class_request(CLASS_C);
// Now we are connected, start the timer that will wakeup the loop frequently
taskWakeupTimer.begin(SLEEP_TIME, periodicWakeup);
taskWakeupTimer.start();
}
/**
* @brief Function for handling LoRaWan received data from Gateway
*
* @param app_data Pointer to rx data
*/
static void lorawan_rx_handler(lmh_app_data_t *app_data)
{
#ifndef MAX_SAVE
Serial.printf("LoRa Packet received on port %d, size:%d, rssi:%d, snr:%d\n",
app_data->port, app_data->buffsize, app_data->rssi, app_data->snr);
#endif
switch (app_data->port)
{
case 3:
// Port 3 switches the class
if (app_data->buffsize == 1)
{
switch (app_data->buffer[0])
{
case 0:
lmh_class_request(CLASS_A);
#ifndef MAX_SAVE
Serial.println("Request to switch to class A");
#endif
break;
case 1:
lmh_class_request(CLASS_B);
#ifndef MAX_SAVE
Serial.println("Request to switch to class B");
#endif
break;
case 2:
lmh_class_request(CLASS_C);
#ifndef MAX_SAVE
Serial.println("Request to switch to class C");
#endif
break;
default:
break;
}
}
// Send LoRaWan handler back to sleep
xSemaphoreTake(loraEvent, 10);
break;
case LORAWAN_APP_PORT:
// Copy the data into loop data buffer
memcpy(rcvdLoRaData, app_data->buffer, app_data->buffsize);
rcvdDataLen = app_data->buffsize;
eventType = 0;
// Notify task about the event
if (taskEvent != NULL)
{
#ifndef MAX_SAVE
Serial.println("Waking up loop task");
#endif
xSemaphoreGive(taskEvent);
}
// Send LoRa handler back to sleep
xSemaphoreTake(loraEvent, 10);
}
}
/**
* @brief Callback for class switch confirmation
*
* @param Class The new class
*/
static void lorawan_confirm_class_handler(DeviceClass_t Class)
{
#ifndef MAX_SAVE
Serial.printf("switch to class %c done\n", "ABC"[Class]);
#endif
// Informs the server that switch has occurred ASAP
m_lora_app_data.buffsize = 0;
m_lora_app_data.port = LORAWAN_APP_PORT;
lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG);
// Send LoRa handler back to sleep
xSemaphoreTake(loraEvent, 10);
}
/**
* @brief Send a LoRaWan package
*
* @return result of send request
*/
bool sendLoRaFrame(float temp, float humidity, float pressure, float soil)
{
if (lmh_join_status_get() != LMH_SET)
{
//Not joined, try again later
#ifndef MAX_SAVE
Serial.println("Did not join network, skip sending frame");
#endif
// Send LoRa handler back to sleep
xSemaphoreTake(loraEvent, 10);
return false;
}
m_lora_app_data.port = LORAWAN_APP_PORT;
//******************************************************************
/// \todo here some more usefull data should be put into the package
//******************************************************************
lpp.reset();
lpp.addTemperature(1,temp);
lpp.addRelativeHumidity(2,humidity);
lpp.addRelativeHumidity(3,soil);
lpp.addBarometricPressure(4,pressure);
memcpy(m_lora_app_data_buffer,lpp.getBuffer(),lpp.getSize());
m_lora_app_data.buffsize = lpp.getSize();
lmh_error_status error = lmh_send(&m_lora_app_data, LMH_UNCONFIRMED_MSG);
// Send LoRa handler back to sleep
xSemaphoreTake(loraEvent, 10);
return (error == 0);
}