L14-Click 1.0
STM32WLE5CC LoRaWAN Sensor Platform
Loading...
Searching...
No Matches
mylora_process.c
Go to the documentation of this file.
1/*
2 * mylora_process.c
3 *
4 * Created on: 18. 1. 2026
5 * Author: Milan
6 */
7
8#include "mylora_process.h"
9#include "main.h"
10#include "rtc.h"
11#include "mysensors.h"
12#include "mysensors_base.h"
13#include "sys_app.h"
14#include "stm32_seq.h"
15#include "lora_app.h"
16#include "mymems.h"
17#include "CayenneLpp.h"
18#include "LoRaMac.h"
19#include <stdio.h>
20
21
22// LoRaWAN data formatting constants
23
24static struct tm _localTime = {};
25static uint32_t _myloraWanAppBit = 0; // Sequencer bits
26static uint8_t _connectedCount = 0; // the connection counter for repeating reconnect after timeout duty cycle
27static uint8_t _isDataSending = 0; // data is in process of sending
28static uint8_t _isConnected = 0; // the connect has been done
29static uint8_t _currentSendingDataBuffer[256]; // buffer for sending data - current - copy
30static uint8_t _currentSendingDataSz = 0; // the data size in buffer
31static uint8_t _fromEndInx = 0; // the index from end to send (.SendInOnePacket)
32
33/**
34 * @brief Get the maximum application payload size for the current data rate.
35 * Returns 0 if the query fails or channel is not available.
36 */
37static uint8_t myLoRaWAN_GetMaxPayloadSize(void)
38{
39 LoRaMacTxInfo_t txInfo;
40 LoRaMacStatus_t status = LoRaMacQueryTxPossible(0, &txInfo);
41
42 // Even if status != LORAMAC_STATUS_OK (e.g. duty cycle),
43 // txInfo.MaxPossibleApplicationDataSize is still filled in.
44 //writeLog("MaxPossibleAppDataSize: %d, CurrentPayloadSize: %d",
45 // txInfo.MaxPossibleApplicationDataSize,
46 // txInfo.CurrentPayloadSize);
47
48 return (status == LORAMAC_STATUS_OK) ? txInfo.MaxPossibleApplicationDataSize : 51; // 51max
49}
50
52{
53 _isConnected = 0;
54 if (LORAMAC_HANDLER_SUCCESS != LmHandlerStop())
55 {
56 writeLog("myLoraWan_Reconnect Stop on going ...");
57 }
58 else
59 {
60 writeLog("myLoraWan_Reconnect Start join");
61 LmHandlerJoin(LORAWAN_DEFAULT_ACTIVATION_TYPE, true);
62 }
63}
64
66{
67 writeLog("myLoraWan_StartReconnect - count:%d", (int) _connectedCount);
69 {
70 UTIL_SEQ_SetTask((1 << (_myloraWanAppBit + 1)), CFG_SEQ_Prio_0); // myLoraWan_Reconnect
72 }
73}
74
75/**
76 * @brief Send confirmed data to LoRaWAN network
77 *
78 * This function sends data that requires acknowledgment from the server.
79 * The server must respond with a downlink acknowledgment. This is useful
80 * for critical data that must be verified as received.
81 *
82 * @param data Pointer to the data buffer to send
83 * @param dataSize Size of the data in bytes (max 242 bytes)
84 * @param port LoRaWAN application port (typically 2)
85 * @return LmHandlerErrorStatus_t Status of the send operation
86 * - LORAMAC_HANDLER_SUCCESS: Data queued for transmission with confirmation
87 * - LORAMAC_HANDLER_ERROR: Failed to queue data
88 * - LORAMAC_HANDLER_BUSY_ERROR: MAC layer is busy
89 * - LORAMAC_HANDLER_NO_NETWORK_JOINED: Not connected to network
90 */
91static LmHandlerErrorStatus_t LoRaWAN_SendConfirmedData(uint8_t *data, uint8_t dataSize, uint8_t port)
92{
93 LmHandlerAppData_t appData;
94 LmHandlerErrorStatus_t status;
95
96 if (data == NULL || dataSize == 0 || dataSize > 242)
97 {
98 writeLog("Invalid data parameters");
99 return LORAMAC_HANDLER_ERROR;
100 }
101
102 appData.Buffer = data;
103 appData.BufferSize = dataSize;
104 appData.Port = port;
105
106 writeLog("Sending confirmed data to LoRaWAN: port=%d, size=%d bytes", port, dataSize);
107
108 // Send as confirmed message (requires ACK from server)
109 status = LmHandlerSend(&appData, LORAMAC_HANDLER_CONFIRMED_MSG, false);
110
111 switch (status)
112 {
113 case LORAMAC_HANDLER_SUCCESS:
114 writeLog("Confirmed data queued for transmission");
115 break;
116
117 case LORAMAC_HANDLER_NO_NETWORK_JOINED:
118 writeLog("Cannot send: Not joined to network");
119 break;
120
121 case LORAMAC_HANDLER_BUSY_ERROR:
122 writeLog("Cannot send: MAC layer busy");
123 break;
124
125 case LORAMAC_HANDLER_DUTYCYCLE_RESTRICTED:
126 myLoraWan_StartReconnect(); // try reconnect
127 break;
128
129 default:
130 writeLog("Send failed with status: %d", status);
131 break;
132 }
133
134 return status;
135}
136
137/**
138 * @brief the preparing _currentSendingDataBuffer/_currentSendingDataSz from CayennelLpp to send data to LoRaWan gateway
139 * @param data - memory data block, sensors measured values
140 * @param reset - the sending buffer is reset or not(adding for _systemParams.SendInOnePacket == 1)
141 * @param maxSize - the max size of payload for current datarate, 0 - no buffer is checked
142 */
143static uint8_t myloraWan_PrepareSendingBuffer(const mems_DataBlock_t *data, uint8_t reset, uint8_t maxSize)
144{
145 uint8_t *ptr, sz, isOK = 1;
146
147 if (reset)
149
152 isOK = (!maxSize || _currentSendingDataSz + sz <= maxSize); // buffer cannot be longer than max, maxSize - 0, is not check
153 if (ptr != NULL && isOK)
154 {
157 }
158 return isOK;
159}
160
161/**
162 * @brief the measure has been finished, sensor data save to memory or local buffer, if no memory is exist or is not storing set
163 * _currentSendingDataBuffer - contains data
164 * _currentSendingDataSz - size of data in _currentSendingDataBuffer
165 * after measure is started myLoraWan_LastDataSend
166 */
168{
169 mems_DataBlock_t *memsData;
170
171 writeLog("myloraWan_MeasureFinish - begin, dataCount:%d", (int) _memsMainBlock.Data_CountCurrent);
172 //UTIL_SEQ_WaitEvt(_myloraWanAppBit); // waiting for event - fired from mysensors.c -> tasksensors_Work
173 //UTIL_SEQ_ClrEvt(_myloraWanAppBit);
174 //UTIL_SEQ_SetTask((1 << _myloraWanAppBit), CFG_SEQ_Prio_0); // pokracujem
175
176 memsData = mems_InicBuffer(MEMS_DATATYPE_SENSOR); // prepare working memory buffer
177 if (memsData != NULL)
178 {
179 sensors_CopyToBck(); // copy to backup
180 sensors_WriteFromBckToDataBlock(memsData); // write sensors to memory block from backup
181 if (!_systemParams.MemsStoreSensorData || mems_AddData(NULL, 0, 0) != HAL_OK)
182 myloraWan_PrepareSendingBuffer(memsData, 1, 0); // reset & no max buffer check
183
184 /*
185 // test only - add next
186 if (_memsMainBlock.Data_CountCurrent <= 1 && _systemParams.StoreSensorData)
187 {
188 int i, m = 10;
189 for (i = 0; i < m && mems_AddData(NULL, 0, 0) == HAL_OK; i++)
190 ;
191
192 writeLog("myloraWan_MeasureFinish - written %dx data, max:%d", i, m);
193 } // */
194 }
195
196 UTIL_SEQ_SetTask(1 << (_myloraWanAppBit + 2), CFG_SEQ_Prio_0); // myLoraWan_LastDataSend
197 writeLog("myloraWan_MeasureFinish - end");
198}
199
200/**
201 * @brief take data from LIFO - last data and send. Cannot be nested
202 * only if is LoRa connected
203 */
205{
206 if (_isConnected)
207 {
208 writeLog("myLoraWan_LastDataSend - begin");
209
210 if (_isDataSending == 0)
211 {
212 mems_DataBlock_t *buf = NULL;
213 HAL_StatusTypeDef memsStatus;
214 const uint8_t maxPayLoad = MIN(CayenneLppMaxBuffer(), myLoRaWAN_GetMaxPayloadSize()); // 5 - ensure... -5 bytes
215
216
217 /*
218 * the reading one(_systemParams.SendInOnePacket == 0) or more(_systemParams.SendInOnePacket == 1)
219 * data from memory a prepare CayenneLppBuffer max to 242 bytes from end to begin of memory
220 * _fromEndInx
221 * _fromEndInx+1
222 * _fromEndInx+2
223 * .
224 * .
225 * .
226 * myloraWan_PrepareSendingBuffer prepare buffer from memory
227 * _systemParams.SendInOnePacket = 0 - one data, = 1 more, until are data or max
228 * _currentSendingDataSz + firstDataSize * 2 < CayenneLppMaxBuffer() - must be 2timer more free space before continue
229 *
230 *
231 Max payload per DR (EU868 region):
232 Data Rate Spreading Factor Max App Payload
233 DR0 SF12 / 125kHz 51 bytes
234 DR1 SF11 / 125kHz 51 bytes
235 DR2 SF10 / 125kHz 51 bytes
236 DR3 SF9 / 125kHz 115 bytes
237 DR4 SF8 / 125kHz 222 bytes
238 DR5 SF7 / 125kHz 222 bytes
239 */
240 if (_currentSendingDataSz == 0) // data ready in buffer, must be send
241 {
242 _fromEndInx = 0;
243 do
244 {
245 memsStatus = mems_GetLastData(&buf, _fromEndInx);
246 switch (memsStatus)
247 {
248 case HAL_OK: // data available
249 if (myloraWan_PrepareSendingBuffer(buf, _fromEndInx == 0, maxPayLoad)) // reset buffer, if 1st is going & max check
250 {
251 writeLog("myLoraWan_LastDataSend - mems sent data, still:%d", (int) _memsMainBlock.Data_CountCurrent - _fromEndInx);
252 }
253 else
254 {
255 writeLog("myLoraWan_LastDataSend - maximum data payload reached:%d", (int)maxPayLoad);
256 if (_fromEndInx)
257 --_fromEndInx;
258 memsStatus = HAL_BUSY;
259 }
260 break;
261 case HAL_BUSY: // no more data
262 writeLog("myLoraWan_LastDataSend - mems no more data");
263 break;
264 default: // error
265 writeLog("myLoraWan_LastDataSend - mems error");
266 }
267 } while(_systemParams.SensSendInOnePacket && memsStatus == HAL_OK && ++_fromEndInx);
268 }
269 // data ready to send
270 if (_currentSendingDataSz > 0)
271 {
272 _isDataSending = 1; // in OnLoRaWANTxData is reset
274 writeLog("myLoraWan_LastDataSend - sendConfirmedData:%d", (int) _currentSendingDataSz);
275 }
276 else
277 {
278 writeLog("myLoraWan_LastDataSend - sendConfirmedData: no data");
279 }
280 }
281 else
282 writeLog("myLoraWan_LastDataSend - busy");
283 writeLog("myLoraWan_LastDataSend - end");
284 }
285 else
286 writeLog("myLoraWan_LastDataSend - not connected");
287}
288
289////////////////////////////////// public /////////////////////////////
290
292{
293 SysTime_t currentSysTime = SysTimeGet();
294 SysTimeLocalTime(currentSysTime.Seconds, &_localTime);
295}
296
297/**
298 * @brief Called after LoRaWAN successfully connects to the server
299 *
300 * This function is called when the LoRaWAN join process completes successfully.
301 * It performs time synchronization with the LoRaWAN server by requesting the
302 * device time, which updates the RTC with the server's time.
303 */
305{
306 writeLog("LoRaWAN connected successfully!");
307
308 /* Request time synchronization from the LoRaWAN server */
309 /* This will trigger the DeviceTimeReq MAC command */
310 /* The response will automatically update the system time via OnSysTimeUpdate callback */
311 LmHandlerErrorStatus_t status = LmHandlerDeviceTimeReq();
312
313 if (status == LORAMAC_HANDLER_SUCCESS)
314 {
315 writeLog("Time synchronization request sent");
316 }
317 else
318 {
319 writeLog("Failed to send time synchronization request");
320 }
321 _connectedCount = _systemParams.LoRaRepeatTryConnect;
322 _isConnected = 1;
323 UTIL_SEQ_SetTask((1 << (_myloraWanAppBit + 2)), CFG_SEQ_Prio_0); // myLoraWan_LastDataSend, can be send from buffer
324 writeLog("Measure done, start myLoraWan_LastDataSend");
325}
326
327/**
328 * @brief Called after date/time has been synchronized from LoRaWAN
329 *
330 * This function is called when the device receives a time synchronization
331 * response (DeviceTimeAns) from the LoRaWAN server. The RTC has already
332 * been updated with the server's time when this function is called.
333 *
334 * Add your custom code here to perform actions after time synchronization,
335 * such as updating time-dependent schedules, logging the sync event, or
336 * triggering time-based operations.
337 */
339{
340 GetTimeDate();
341 writeLog("Time synchronized: %04d-%02d-%02d %02d:%02d:%02d", _localTime.tm_year + 1900, _localTime.tm_mon + 1, _localTime.tm_mday, _localTime.tm_hour, _localTime.tm_min,
342 _localTime.tm_sec);
343}
344
345/**
346 * @brief Handle received downlink data from LoRaWAN server
347 *
348 * This callback function is triggered when the device receives data from the
349 * LoRaWAN server. This can be a response to a confirmed uplink or an
350 * application-level downlink message.
351 *
352 * @param appData Received application data including buffer and port
353 * @param params Receive parameters including RSSI, SNR, and downlink counter
354 */
355void OnLoRaWANRxData(LmHandlerAppData_t *appData, LmHandlerRxParams_t *params)
356{
357 if (appData == NULL || params == NULL)
358 {
359 writeLog("Invalid RX data parameters");
360 return;
361 }
362
363 writeLog("Received data from server:");
364 writeLog(" Port: %d", appData->Port);
365 writeLog(" Size: %d bytes", appData->BufferSize);
366 writeLog(" RSSI: %d dBm", params->Rssi);
367 writeLog(" SNR: %d dB", params->Snr);
368 writeLog(" Downlink Counter: %lu", params->DownlinkCounter);
369
370 // Process received data based on port
371 if (appData->BufferSize > 0)
372 {
373 // Handle specific commands from server
374 switch (appData->Port)
375 {
376 case 2: // Application data port
377 // Process application-specific commands
378 if (appData->BufferSize >= 1)
379 {
380 uint8_t command = appData->Buffer[0];
381 writeLog(" Command received: 0x%02X", command);
382
383 // Example: handle different commands
384 switch (command)
385 {
386 case 0x01:
387 writeLog(" -> Command: Request immediate sensor reading");
388 // Trigger immediate sensor read
389 break;
390 case 0x02:
391 writeLog(" -> Command: Change reporting interval");
392 if (appData->BufferSize >= 3)
393 {
394 uint16_t interval = (appData->Buffer[1] << 8) | appData->Buffer[2];
395 writeLog(" -> New interval: %d seconds", interval);
396 }
397 break;
398 default:
399 writeLog(" -> Unknown command");
400 break;
401 }
402 }
403 break;
404
405 case 3: // Configuration port
406 writeLog(" Configuration data received");
407 break;
408
409 default:
410 writeLog(" Data on unknown port");
411 break;
412 }
413 }
414
415 // Check if this was an ACK for a confirmed uplink
416 // IsMcpsIndication == 0 means this is a MAC layer response (ACK), not an application data indication
417 if (params->IsMcpsIndication == 0)
418 {
419 writeLog(" This is an ACK for confirmed uplink");
420 }
421}
422
423/**
424 * @brief Handle transmission event callback
425 *
426 * This callback is triggered after a transmission attempt, whether successful or not.
427 * It provides information about whether an ACK was received for confirmed messages.
428 *
429 * @param params Transmission parameters including status and ACK received flag
430 */
431void OnLoRaWANTxData(LmHandlerTxParams_t *params)
432{
433 if (_currentSendingDataSz > 0 && params != NULL)
434 {
435 writeLog("Transmission completed:");
436 writeLog(" Uplink Counter: %lu", params->UplinkCounter);
437 writeLog(" Datarate: DR%d", params->Datarate);
438 writeLog(" TX Power: %d dBm", params->TxPower);
439 writeLog(" Channel: %d", params->Channel);
440
441 if (params->MsgType == LORAMAC_HANDLER_CONFIRMED_MSG)
442 {
443 if (params->AckReceived)
444 {
445
446 writeLog(" Status: ACK received from server");
447 // last data send OK
450 _fromEndInx = 0;
451 _isConnected = 1; // if ACK - the connetion must be
452 UTIL_SEQ_SetTask(1 << (_myloraWanAppBit + 2), CFG_SEQ_Prio_0); // myLoraWan_LastDataSend
453 writeLog(" Status: next data is going to send");
454 }
455 else
456 {
457 writeLog(" Status: No ACK received (will retry)");
459 }
460 }
461 else
462 {
463 writeLog(" Status: Unconfirmed message sent");
464 }
465
466 if (params->Status != LORAMAC_EVENT_INFO_STATUS_OK)
467 {
468 writeLog(" TX Error: %d", params->Status);
469 }
470 }
471 _isDataSending = 0; // sending has been finished
472}
473
474void myloraWan_Init(uint32_t myloraWanAppBit)
475{
476 _myloraWanAppBit = myloraWanAppBit;
477 UTIL_SEQ_RegTask((1 << _myloraWanAppBit), UTIL_SEQ_RFU, myloraWan_MeasureFinish);
478 UTIL_SEQ_RegTask((1 << (_myloraWanAppBit + 1)), UTIL_SEQ_RFU, myLoraWan_Reconnect);
479 UTIL_SEQ_RegTask((1 << (_myloraWanAppBit + 2)), UTIL_SEQ_RFU, myLoraWan_LastDataSend);
480}
uint8_t CayenneLppMaxBuffer()
Definition CayenneLpp.c:407
Implements the Cayenne Low Power Protocol.
Header of application of the LRWAN Middleware.
#define LORAWAN_DEFAULT_ACTIVATION_TYPE
Definition lora_app.h:100
: Header for main.c file. This file contains the common defines of the application.
void writeLog(const char *format,...)
Format and send a log message over UART (printf-style). Available only when WRITELOG is defined; comp...
Definition main.c:97
static void myloraWan_MeasureFinish()
the measure has been finished, sensor data save to memory or local buffer, if no memory is exist or i...
static uint8_t _isDataSending
static void myLoraWan_LastDataSend()
take data from LIFO - last data and send. Cannot be nested only if is LoRa connected
void GetTimeDate()
Filled up _currentTime & _currentDate.
void OnTimeSynchronized(void)
Called after date/time has been synchronized from LoRaWAN.
static uint8_t myloraWan_PrepareSendingBuffer(const mems_DataBlock_t *data, uint8_t reset, uint8_t maxSize)
the preparing _currentSendingDataBuffer/_currentSendingDataSz from CayennelLpp to send data to LoRaWa...
static uint8_t myLoRaWAN_GetMaxPayloadSize(void)
Get the maximum application payload size for the current data rate. Returns 0 if the query fails or c...
void OnLoRaWANRxData(LmHandlerAppData_t *appData, LmHandlerRxParams_t *params)
Handle received downlink data from LoRaWAN server.
void myloraWan_Init(uint32_t myloraWanAppBit)
initialization of myloraWan process, sequencer
static void myLoraWan_Reconnect()
static uint8_t _currentSendingDataSz
static uint32_t _myloraWanAppBit
void OnLoRaWanConnected(void)
Called after LoRaWAN successfully connects to the server.
static void myLoraWan_StartReconnect()
static uint8_t _fromEndInx
void OnLoRaWANTxData(LmHandlerTxParams_t *params)
Handle transmission event callback.
static LmHandlerErrorStatus_t LoRaWAN_SendConfirmedData(uint8_t *data, uint8_t dataSize, uint8_t port)
Send confirmed data to LoRaWAN network.
static struct tm _localTime
static uint8_t _connectedCount
static uint8_t _currentSendingDataBuffer[256]
static uint8_t _isConnected
void mems_RemoveLastData(uint8_t fromEndTo)
remove last data
Definition mymems.c:242
HAL_StatusTypeDef mems_GetLastData(mems_DataBlock_t **memBlock, uint8_t inxFromEnd)
Get the last sensor data if exists. The data and mem block is stored in work buffer,...
Definition mymems.c:194
mems_MainBlock_t _memsMainBlock
Global instance of the main flash control block; loaded by mems_ReadMainBlock().
Definition mymems.c:27
HAL_StatusTypeDef mems_AddData(const void *data, mems_DataType_t type, uint8_t size)
write new data of sensor at the end of queue, if is no space, the most older data start to rewrites
Definition mymems.c:168
mems_DataBlock_t * mems_InicBuffer(mems_DataType_t type)
Initialize the work buffer - _memsDataBlock.
Definition mymems.c:160
@ MEMS_DATATYPE_SENSOR
Definition mymems.h:67
void sensors_WriteFromBckToDataBlock(mems_DataBlock_t *data)
filling the data block from sensor data - store to memory the methods works with _bck_XXXXX senzor,...
Definition mysensors.c:530
uint8_t * sensors_CayennelFromBckData(uint8_t *sizeOut)
collecting sensor data into CayenneLppGetBuffer buffer for sending them via LoRa the methods works wi...
Definition mysensors.c:469
void sensors_CopyToBck()
The copying data from current sensors buffer to backUp - _bck_XXXXX.
Definition mysensors.c:447
uint8_t sensors_ReadToBckFromDataBlock(const mems_DataBlock_t *data)
reading from memory block and filling specified sensor from memory - ready for sensors_CayennelData t...
Definition mysensors.c:575
systemParams_t _systemParams
This file contains all the function prototypes for the rtc.c file.
Sensor data record stored as one element in the flash circular queue.
Definition mymems.h:78
Function prototypes for sys_app.c file.
@ CFG_SEQ_Prio_0