Firmware SDK
 All Data Structures Functions Variables Typedefs Enumerations Enumerator Groups Pages
bc_cmwx1zzabz.c
1 #include <bc_cmwx1zzabz.h>
2 
3 #define BC_CMWX1ZZABZ_DELAY_RUN 100
4 #define BC_CMWX1ZZABZ_DELAY_INITIALIZATION_RESET_H 100
5 #define BC_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_COMMAND 100 // ! when using longer AT responses
6 #define BC_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_RESPONSE 100
7 #define BC_CMWX1ZZABZ_DELAY_SEND_MESSAGE_RESPONSE 3000
8 #define BC_CMWX1ZZABZ_DELAY_JOIN_RESPONSE 8000
9 
10 // Apply changes to the factory configuration
11 const char *_init_commands[] =
12 {
13  "\rAT\r",
14  "AT+DUTYCYCLE=0\r",
15  "AT+DEVADDR?\r",
16  "AT+DEVEUI?\r",
17  "AT+APPEUI?\r",
18  "AT+NWKSKEY?\r",
19  "AT+APPSKEY?\r",
20  "AT+APPKEY?\r",
21  "AT+BAND?\r",
22  "AT+MODE?\r",
23  "AT+CLASS?\r",
24  "AT+RX2?\r",
25  "AT+NWK?\r",
26  "AT+DR?\r",
27  NULL
28 };
29 
30 static void _bc_cmwx1zzabz_task(void *param);
31 
32 static bool _bc_cmwx1zzabz_read_response(bc_cmwx1zzabz_t *self);
33 
34 static void _bc_cmwx1zzabz_save_config(bc_cmwx1zzabz_t *self, bc_cmwx1zzabz_config_index_t config_index);
35 
36 static void _uart_event_handler(bc_uart_channel_t channel, bc_uart_event_t event, void *param);
37 
39 {
40  memset(self, 0, sizeof(*self));
41 
42  self->_uart_channel = uart_channel;
43  self->_tx_port = 2;
44 
45  bc_fifo_init(&self->_tx_fifo, self->_tx_fifo_buffer, sizeof(self->_tx_fifo_buffer));
46  bc_fifo_init(&self->_rx_fifo, self->_rx_fifo_buffer, sizeof(self->_rx_fifo_buffer));
47 
49  bc_uart_set_async_fifo(self->_uart_channel, &self->_tx_fifo, &self->_rx_fifo);
50  bc_uart_async_read_start(self->_uart_channel, BC_TICK_INFINITY);
51  bc_uart_set_event_handler(self->_uart_channel, _uart_event_handler, self);
52 
53  self->_task_id = bc_scheduler_register(_bc_cmwx1zzabz_task, self, BC_CMWX1ZZABZ_DELAY_RUN);
54  self->_state = BC_CMWX1ZZABZ_STATE_INITIALIZE;
55 }
56 
57 static void _uart_event_handler(bc_uart_channel_t channel, bc_uart_event_t event, void *param)
58 {
59  (void) channel;
60  bc_cmwx1zzabz_t *self = (bc_cmwx1zzabz_t*)param;
61 
62  if (event == BC_UART_EVENT_ASYNC_READ_DATA && self->_state == BC_CMWX1ZZABZ_STATE_IDLE)
63  {
64  bc_scheduler_plan_relative(self->_task_id, 100);
65  self->_state = BC_CMWX1ZZABZ_STATE_RECEIVE;
66  }
67 }
68 
69 void bc_cmwx1zzabz_set_event_handler(bc_cmwx1zzabz_t *self, void (*event_handler)(bc_cmwx1zzabz_t *, bc_cmwx1zzabz_event_t, void *), void *event_param)
70 {
71  self->_event_handler = event_handler;
72  self->_event_param = event_param;
73 }
74 
76 {
77  if (self->_state == BC_CMWX1ZZABZ_STATE_IDLE)
78  {
79  return true;
80  }
81 
82  return false;
83 }
84 
85 bool bc_cmwx1zzabz_send_message(bc_cmwx1zzabz_t *self, const void *buffer, size_t length)
86 {
87  if (!bc_cmwx1zzabz_is_ready(self) || length == 0 || length > BC_CMWX1ZZABZ_TX_MAX_PACKET_SIZE)
88  {
89  return false;
90  }
91 
92  self->_message_length = length;
93 
94  memcpy(self->_message_buffer, buffer, self->_message_length);
95 
96  self->_state = BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND;
97 
98  bc_scheduler_plan_now(self->_task_id);
99 
100  return true;
101 }
102 
103 bool bc_cmwx1zzabz_send_message_confirmed(bc_cmwx1zzabz_t *self, const void *buffer, size_t length)
104 {
105  if (!bc_cmwx1zzabz_is_ready(self) || length == 0 || length > BC_CMWX1ZZABZ_TX_MAX_PACKET_SIZE)
106  {
107  return false;
108  }
109 
110  self->_message_length = length;
111 
112  memcpy(self->_message_buffer, buffer, self->_message_length);
113 
114  self->_state = BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND;
115 
116  bc_scheduler_plan_now(self->_task_id);
117 
118  return true;
119 }
120 
121 static void _bc_cmwx1zzabz_task(void *param)
122 {
123  bc_cmwx1zzabz_t *self = param;
124 
125  while (true)
126  {
127  switch (self->_state)
128  {
129  case BC_CMWX1ZZABZ_STATE_READY:
130  {
131  self->_state = BC_CMWX1ZZABZ_STATE_IDLE;
132 
133  if (self->_event_handler != NULL)
134  {
135  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_READY, self->_event_param);
136  }
137 
138  continue;
139  }
140  case BC_CMWX1ZZABZ_STATE_IDLE:
141  {
142  if (self->_save_config_mask != 0)
143  {
144  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
145  continue;
146  }
147 
148  if(self->_join_command)
149  {
150  self->_state = BC_CMWX1ZZABZ_STATE_JOIN_SEND;
151  continue;
152  }
153 
154  return;
155  }
156  case BC_CMWX1ZZABZ_STATE_RECEIVE:
157  {
158  self->_state = BC_CMWX1ZZABZ_STATE_IDLE;
159 
160  while (_bc_cmwx1zzabz_read_response(self))
161  {
162  if (memcmp(self->_response, "+RECV=", 5) == 0)
163  {
164  self->_message_port = atoi(&self->_response[6]);
165 
166  char *comma_search = strchr(self->_response, ',');
167  if (!comma_search)
168  {
169  continue;
170  }
171 
172  // Parse from the next character
173  self->_message_length = atoi(++comma_search);
174 
175  // Dummy read three \r\n\r characters
176  char dummy[3];
177  uint32_t bytes = bc_uart_async_read(self->_uart_channel, &dummy, 3);
178  if (bytes != 3)
179  {
180  continue;
181  }
182 
183  // Received data is bigger than library message buffer
184  if (self->_message_length > sizeof(self->_message_buffer))
185  {
186  continue;
187  }
188 
189  // Read the received message
190  bytes = bc_uart_async_read(self->_uart_channel, self->_message_buffer, self->_message_length);
191  if (bytes != self->_message_length)
192  {
193  continue;
194  }
195 
196  if (self->_event_handler != NULL)
197  {
198  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_MESSAGE_RECEIVED, self->_event_param);
199  }
200  }
201  else if (memcmp(self->_response, "+ACK", 4) == 0)
202  {
203  if (self->_event_handler != NULL)
204  {
205  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_MESSAGE_CONFIRMED, self->_event_param);
206  }
207  }
208  else if (memcmp(self->_response, "+NOACK", 4) == 0)
209  {
210  if (self->_event_handler != NULL)
211  {
212  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_MESSAGE_NOT_CONFIRMED, self->_event_param);
213  }
214  }
215  else if (memcmp(self->_response, "+EVENT=2,2", 10) == 0)
216  {
217  if (self->_event_handler != NULL)
218  {
219  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_MESSAGE_RETRANSMISSION, self->_event_param);
220  }
221  }
222  }
223 
224  return;
225  }
226  case BC_CMWX1ZZABZ_STATE_ERROR:
227  {
228  if (self->_event_handler != NULL)
229  {
230  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_ERROR, self->_event_param);
231  }
232 
233  self->_state = BC_CMWX1ZZABZ_STATE_INITIALIZE;
234 
235  continue;
236  }
237  case BC_CMWX1ZZABZ_STATE_INITIALIZE:
238  {
239  self->_init_command_index = 0;
240  self->_state = BC_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
241 
242  continue;
243  }
244 
245  case BC_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND:
246  {
247  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
248 
249  // Purge RX FIFO
250  char rx_character;
251  while (bc_uart_async_read(self->_uart_channel, &rx_character, 1) != 0)
252  {
253  }
254 
255  strcpy(self->_command, _init_commands[self->_init_command_index]);
256  size_t length = strlen(self->_command);
257 
258  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
259  {
260  continue;
261  }
262 
263  self->_state = BC_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_RESPONSE;
264  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_COMMAND);
265 
266  return;
267  }
268  case BC_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_RESPONSE:
269  {
270  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
271  uint8_t response_handled = 0;
272 
273  if (!_bc_cmwx1zzabz_read_response(self))
274  {
275  continue;
276  }
277 
278  // Compare first 4 cahracters from response
279  uint32_t response_valid = (memcmp(self->_response, "+OK=", 4) == 0);
280  // Pointer to the last send command to know the context of the answer
281  const char *last_command = _init_commands[self->_init_command_index];
282  // Pointer to the first character of response value after +OK=
283  char *response_string_value = &self->_response[4];
284 
285  if (strcmp(last_command, "AT+DEVADDR?\r") == 0 && response_valid)
286  {
287  // Check if user did not filled this structure to save configuration, oterwise it would be overwritten
288  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR) == 0)
289  {
290  memcpy(self->_config.devaddr, response_string_value, 8);
291  self->_config.devaddr[8] = '\0';
292  }
293  response_handled = 1;
294  }
295  else if (strcmp(last_command, "AT+DEVEUI?\r") == 0 && response_valid)
296  {
297  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI) == 0)
298  {
299  memcpy(self->_config.deveui, response_string_value, 16);
300  self->_config.deveui[16] = '\0';
301  }
302  response_handled = 1;
303  }
304  else if (strcmp(last_command, "AT+APPEUI?\r") == 0 && response_valid)
305  {
306  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_APPEUI) == 0)
307  {
308  memcpy(self->_config.appeui, response_string_value, 16);
309  self->_config.appeui[16] = '\0';
310  }
311  response_handled = 1;
312  }
313  else if (strcmp(last_command, "AT+NWKSKEY?\r") == 0 && response_valid)
314  {
315  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY) == 0)
316  {
317  memcpy(self->_config.nwkskey, response_string_value, 32);
318  self->_config.nwkskey[32] = '\0';
319  }
320  response_handled = 1;
321  }
322  else if (strcmp(last_command, "AT+APPSKEY?\r") == 0 && response_valid)
323  {
324  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY) == 0)
325  {
326  memcpy(self->_config.appskey, response_string_value, 32);
327  self->_config.appskey[32] = '\0';
328  }
329  response_handled = 1;
330  }
331  else if (strcmp(last_command, "AT+APPKEY?\r") == 0 && response_valid)
332  {
333  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_APPKEY) == 0)
334  {
335  memcpy(self->_config.appkey, response_string_value, 32);
336  self->_config.appkey[32] = '\0';
337  }
338  response_handled = 1;
339  }
340  else if (strcmp(last_command, "AT+BAND?\r") == 0 && response_valid)
341  {
342  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_BAND) == 0)
343  {
344  self->_config.band = response_string_value[0] - '0';
345  }
346  response_handled = 1;
347  }
348  else if (strcmp(last_command, "AT+MODE?\r") == 0 && response_valid)
349  {
350  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_MODE) == 0)
351  {
352  self->_config.mode = response_string_value[0] - '0';
353  }
354  response_handled = 1;
355  }
356  else if (strcmp(last_command, "AT+CLASS?\r") == 0 && response_valid)
357  {
358  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_CLASS) == 0)
359  {
360  self->_config.class = response_string_value[0] - '0';
361  }
362  response_handled = 1;
363  }
364  else if (strcmp(last_command, "AT+RX2?\r") == 0 && response_valid)
365  {
366  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_RX2) == 0)
367  {
368  self->_config.rx2_frequency = atoi(response_string_value);
369 
370  char *comma_search = strchr(response_string_value, ',');
371  if (!comma_search)
372  {
373  continue;
374  }
375 
376  self->_config.rx2_datarate = atoi(++comma_search);
377  }
378  response_handled = 1;
379  }
380  else if (strcmp(last_command, "AT+NWK?\r") == 0 && response_valid)
381  {
382  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_NWK) == 0)
383  {
384  self->_config.nwk_public = response_string_value[0] - '0';
385  }
386  response_handled = 1;
387  }
388  else if (strcmp(last_command, "AT+DR?\r") == 0 && response_valid)
389  {
390  if ((self->_save_config_mask & 1 << BC_CMWX1ZZABZ_CONFIG_INDEX_DATARATE) == 0)
391  {
392  self->_config.datarate = atoi(response_string_value);
393  }
394  response_handled = 1;
395  }
396  else if (strcmp(last_command, "AT+DUTYCYCLE=0\r") == 0 && strcmp(self->_response, "+ERR=-17\r") == 0)
397  {
398  // DUTYCYLE is unusable in some band configuration, ignore this err response
399  response_handled = 1;
400  }
401  else if ( strcmp(last_command, "AT\r") == 0 &&
402  strcmp(self->_response, "+OK\r") == 0
403  )
404  {
405  response_handled = 1;
406  }
407  // Generic OK response to other commands
408  else if (memcmp(self->_response, "+OK", 3) == 0)
409  {
410  response_handled = 1;
411  }
412 
413  if (!response_handled)
414  {
415  continue;
416  }
417 
418  self->_init_command_index++;
419 
420  if (_init_commands[self->_init_command_index] == NULL)
421  {
422  // If configuration was changed and flag set, save them
423  if (self->_save_config_mask)
424  {
425  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
426  self->_save_command_index = 0;
427  }
428  else
429  {
430  self->_state = BC_CMWX1ZZABZ_STATE_READY;
431  }
432  }
433  else
434  {
435  self->_state = BC_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
436  }
437 
438  continue;
439  }
440  case BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND:
441  case BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND:
442  {
443  if (self->_state == BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND)
444  {
445  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+PCTX %d,%d\r", self->_tx_port, self->_message_length);
446  }
447  else
448  {
449  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+PUTX %d,%d\r", self->_tx_port, self->_message_length);
450  }
451 
452  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
453 
454  uint8_t command_length = strlen(self->_command);
455 
456  for (size_t i = 0; i < self->_message_length; i++)
457  {
458  // put binary data directly to the "string" buffer
459  self->_command[command_length + i] = self->_message_buffer[i];
460  }
461 
462  self->_command[command_length + self->_message_length] = '\r';
463 
464  size_t length = command_length + self->_message_length + 1; // 1 for \n
465 
466  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
467  {
468  continue;
469  }
470 
471  self->_state = BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE;
472 
473  if (self->_event_handler != NULL)
474  {
475  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_SEND_MESSAGE_START, self->_event_param);
476  }
477 
478  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_SEND_MESSAGE_RESPONSE);
479 
480  return;
481  }
482  case BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE:
483  {
484  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
485 
486  if (!_bc_cmwx1zzabz_read_response(self))
487  {
488  continue;
489  }
490 
491  if (strcmp(self->_response, "+OK\r") != 0)
492  {
493  continue;
494  }
495 
496  self->_state = BC_CMWX1ZZABZ_STATE_READY;
497 
498  if (self->_event_handler != NULL)
499  {
500  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_SEND_MESSAGE_DONE, self->_event_param);
501  }
502 
503  continue;
504  }
505  case BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND:
506  {
507  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
508 
509  // There are no more config items to send
510  if (self->_save_config_mask == 0)
511  {
512  if (self->_event_handler != NULL)
513  {
514  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_CONFIG_SAVE_DONE, self->_event_param);
515  }
516 
517  self->_state = BC_CMWX1ZZABZ_STATE_READY;
518  continue;
519  }
520 
521  // Find config item that has been changed
522  for (uint8_t i = 0; i < BC_CMWX1ZZABZ_CONFIG_INDEX_LAST_ITEM; i++)
523  {
524  if (self->_save_config_mask & 1 << i)
525  {
526  self->_save_command_index = i;
527  break;
528  }
529  }
530 
531  // Purge RX FIFO
532  char rx_character;
533  while (bc_uart_async_read(self->_uart_channel, &rx_character, 1) != 0)
534  {
535  }
536 
537  switch (self->_save_command_index)
538  {
539  case BC_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR:
540  {
541  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+DEVADDR=%s\r", self->_config.devaddr);
542  break;
543  }
544  case BC_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI:
545  {
546  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+DEVEUI=%s\r", self->_config.deveui);
547  break;
548  }
549  case BC_CMWX1ZZABZ_CONFIG_INDEX_APPEUI:
550  {
551  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+APPEUI=%s\r", self->_config.appeui);
552  break;
553  }
554  case BC_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY:
555  {
556  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+NWKSKEY=%s\r", self->_config.nwkskey);
557  break;
558  }
559  case BC_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY:
560  {
561  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+APPSKEY=%s\r", self->_config.appskey);
562  break;
563  }
564  case BC_CMWX1ZZABZ_CONFIG_INDEX_APPKEY:
565  {
566  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+APPKEY=%s\r", self->_config.appkey);
567  break;
568  }
569  case BC_CMWX1ZZABZ_CONFIG_INDEX_BAND:
570  {
571  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+BAND=%d\r", self->_config.band);
572  break;
573  }
574  case BC_CMWX1ZZABZ_CONFIG_INDEX_MODE:
575  {
576  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+MODE=%d\r", self->_config.mode);
577  break;
578  }
579  case BC_CMWX1ZZABZ_CONFIG_INDEX_CLASS:
580  {
581  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+CLASS=%d\r", self->_config.class);
582  break;
583  }
584  case BC_CMWX1ZZABZ_CONFIG_INDEX_RX2:
585  {
586  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+RX2=%d,%d\r", (int) self->_config.rx2_frequency, self->_config.rx2_datarate);
587  break;
588  }
589  case BC_CMWX1ZZABZ_CONFIG_INDEX_NWK:
590  {
591  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+NWK=%d\r", (int) self->_config.nwk_public);
592  break;
593  }
594  case BC_CMWX1ZZABZ_CONFIG_INDEX_DATARATE:
595  {
596  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+DR=%d\r", (int) self->_config.datarate);
597  break;
598  }
599  default:
600  {
601  break;
602  }
603  }
604 
605  size_t length = strlen(self->_command);
606 
607  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
608  {
609  continue;
610  }
611 
612  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE;
613  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_COMMAND);
614  return;
615  }
616 
617  case BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE:
618  {
619  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
620 
621  if (!_bc_cmwx1zzabz_read_response(self))
622  {
623  continue;
624  }
625 
626  // Jump to error state when response is not OK
627  if (memcmp(self->_response, "+OK", 3) != 0)
628  {
629  continue;
630  }
631 
632  // Clean bit mask
633  self->_save_config_mask &= ~(1 << self->_save_command_index);
634 
635  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
636  continue;
637 
638  }
639 
640 
641  case BC_CMWX1ZZABZ_STATE_JOIN_SEND:
642  {
643  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
644 
645  // Purge RX FIFO
646  char rx_character;
647  while (bc_uart_async_read(self->_uart_channel, &rx_character, 1) != 0)
648  {
649  }
650 
651  strcpy(self->_command, "AT+JOIN\r");
652 
653  size_t length = strlen(self->_command);
654  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
655  {
656  continue;
657  }
658 
659  self->_state = BC_CMWX1ZZABZ_STATE_JOIN_RESPONSE;
660  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_JOIN_RESPONSE);
661  return;
662  }
663 
664  case BC_CMWX1ZZABZ_STATE_JOIN_RESPONSE:
665  {
666  bool join_successful = false;
667  // Clear join command flag
668  self->_join_command = false;
669 
670  while (true)
671  {
672  if (!_bc_cmwx1zzabz_read_response(self))
673  {
674  break;
675  }
676 
677  // Response EVENT=1,1 means JOIN was successful
678  if (memcmp(self->_response, "+EVENT=1,1", 10) == 0)
679  {
680  join_successful = true;
681  break;
682  }
683  }
684 
685  if (join_successful)
686  {
687  if (self->_event_handler != NULL)
688  {
689  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_JOIN_SUCCESS, self->_event_param);
690  }
691  }
692  else
693  {
694  if (self->_event_handler != NULL)
695  {
696  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_JOIN_ERROR, self->_event_param);
697  }
698  }
699 
700  self->_state = BC_CMWX1ZZABZ_STATE_IDLE;
701  continue;
702  }
703 
704  default:
705  {
706  break;
707  }
708  }
709  }
710 }
711 
713 {
714  self->_join_command = true;
715  bc_scheduler_plan_now(self->_task_id);
716 }
717 
718 void bc_cmwx1zzabz_set_port(bc_cmwx1zzabz_t *self, uint8_t port)
719 {
720  self->_tx_port = port;
721 }
722 
724 {
725  return self->_tx_port;
726 }
727 
728 void bc_cmwx1zzabz_set_devaddr(bc_cmwx1zzabz_t *self, char *devaddr)
729 {
730  strncpy(self->_config.devaddr, devaddr, 8+1);
731 
732  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR);
733 }
734 
735 void bc_cmwx1zzabz_get_devaddr(bc_cmwx1zzabz_t *self, char *devaddr)
736 {
737  strncpy(devaddr, self->_config.devaddr, 8+1);
738 }
739 
740 void bc_cmwx1zzabz_set_deveui(bc_cmwx1zzabz_t *self, char *deveui)
741 {
742  strncpy(self->_config.deveui, deveui, 16+1);
743 
744  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI);
745 }
746 
747 void bc_cmwx1zzabz_get_deveui(bc_cmwx1zzabz_t *self, char *deveui)
748 {
749  strncpy(deveui, self->_config.deveui, 16+1);
750 }
751 
752 void bc_cmwx1zzabz_set_appeui(bc_cmwx1zzabz_t *self, char *appeui)
753 {
754  strncpy(self->_config.appeui, appeui, 16+1);
755 
756  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_APPEUI);
757 }
758 
759 void bc_cmwx1zzabz_get_appeui(bc_cmwx1zzabz_t *self, char *appeui)
760 {
761  strncpy(appeui, self->_config.appeui, 16+1);
762 }
763 
764 void bc_cmwx1zzabz_set_nwkskey(bc_cmwx1zzabz_t *self, char *nwkskey)
765 {
766  strncpy(self->_config.nwkskey, nwkskey, 32);
767 
768  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY);
769 }
770 
771 void bc_cmwx1zzabz_get_nwkskey(bc_cmwx1zzabz_t *self, char *nwkskey)
772 {
773  strncpy(nwkskey, self->_config.nwkskey, 32+1);
774 }
775 
776 void bc_cmwx1zzabz_set_appskey(bc_cmwx1zzabz_t *self, char *appskey)
777 {
778  strncpy(self->_config.appskey, appskey, 32);
779 
780  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY);
781 }
782 
783 void bc_cmwx1zzabz_get_appskey(bc_cmwx1zzabz_t *self, char *appskey)
784 {
785  strncpy(appskey, self->_config.appskey, 32+1);
786 }
787 
788 void bc_cmwx1zzabz_set_appkey(bc_cmwx1zzabz_t *self, char *appkey)
789 {
790  strncpy(self->_config.appkey, appkey, 32+1);
791 
792  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_APPKEY);
793 }
794 
795 void bc_cmwx1zzabz_get_appkey(bc_cmwx1zzabz_t *self, char *appkey)
796 {
797  strncpy(appkey, self->_config.appkey, 32+1);
798 }
799 
801 {
802  self->_config.band = band;
803 
804  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_BAND);
805 }
806 
808 {
809  return self->_config.band;
810 }
811 
813 {
814  self->_config.mode = mode;
815 
816  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_MODE);
817 }
818 
820 {
821  return self->_config.mode;
822 }
823 
825 {
826  self->_config.class = class;
827 
828  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_CLASS);
829 }
830 
832 {
833  return self->_config.class;
834 }
835 
837 {
838  return self->_message_port;
839 }
840 
842 {
843  return self->_message_length;
844 }
845 
846 uint32_t bc_cmwx1zzabz_get_received_message_data(bc_cmwx1zzabz_t *self, uint8_t *buffer, uint32_t buffer_size)
847 {
848  if (self->_message_length > buffer_size)
849  {
850  return 0;
851  }
852 
853  memcpy(buffer, self->_message_buffer, self->_message_length);
854 
855  return self->_message_length;
856 }
857 
858 void bc_cmwx1zzabz_set_rx2(bc_cmwx1zzabz_t *self, uint32_t frequency, uint8_t datarate)
859 {
860  self->_config.rx2_frequency = frequency;
861 
862  self->_config.rx2_datarate = datarate;
863 
864  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_RX2);
865 }
866 
867 void bc_cmwx1zzabz_get_rx2(bc_cmwx1zzabz_t *self, uint32_t *frequency, uint8_t *datarate)
868 {
869  *frequency = self->_config.rx2_frequency;
870  *datarate = self->_config.rx2_datarate;
871 }
872 
874 {
875  self->_config.nwk_public = public;
876 
877  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_NWK);
878 }
879 
881 {
882  return self->_config.nwk_public;
883 }
884 
885 void bc_cmwx1zzabz_set_datarate(bc_cmwx1zzabz_t *self, uint8_t datarate)
886 {
887  self->_config.datarate = datarate;
888 
889  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_DATARATE);
890 }
891 
893 {
894  return self->_config.datarate;
895 }
896 
897 static bool _bc_cmwx1zzabz_read_response(bc_cmwx1zzabz_t *self)
898 {
899  size_t length = 0;
900 
901  while (true)
902  {
903  char rx_character;
904 
905  if (bc_uart_async_read(self->_uart_channel, &rx_character, 1) == 0)
906  {
907  return false;
908  }
909 
910  if (rx_character == '\n')
911  {
912  continue;
913  }
914 
915  self->_response[length++] = rx_character;
916 
917  if (rx_character == '\r')
918  {
919  if (length == 1)
920  {
921  length = 0;
922 
923  continue;
924  }
925 
926  self->_response[length] = '\0';
927 
928  break;
929  }
930 
931  if (length == sizeof(self->_response) - 1)
932  {
933  return false;
934  }
935  }
936 
937  return true;
938 }
939 
940 static void _bc_cmwx1zzabz_save_config(bc_cmwx1zzabz_t *self, bc_cmwx1zzabz_config_index_t config_index)
941 {
942  self->_save_config_mask |= 1 << config_index;
943 
944  if (self->_state == BC_CMWX1ZZABZ_STATE_IDLE)
945  {
946  bc_scheduler_plan_now(self->_task_id);
947  }
948 }
void bc_cmwx1zzabz_set_deveui(bc_cmwx1zzabz_t *self, char *deveui)
Set DEVEUI.
void bc_cmwx1zzabz_set_datarate(bc_cmwx1zzabz_t *self, uint8_t datarate)
Set the configuration of datarate.
bc_uart_channel_t
UART channels.
Definition: bc_uart.h:13
void bc_cmwx1zzabz_get_appeui(bc_cmwx1zzabz_t *self, char *appeui)
Get APPEUI.
Configuration save done.
Definition: bc_cmwx1zzabz.h:38
void bc_cmwx1zzabz_get_devaddr(bc_cmwx1zzabz_t *self, char *devaddr)
Get DEVADDR.
void bc_cmwx1zzabz_set_event_handler(bc_cmwx1zzabz_t *self, void(*event_handler)(bc_cmwx1zzabz_t *, bc_cmwx1zzabz_event_t, void *), void *event_param)
Set callback function.
Definition: bc_cmwx1zzabz.c:69
void bc_cmwx1zzabz_set_mode(bc_cmwx1zzabz_t *self, bc_cmwx1zzabz_config_mode_t mode)
Set ABP/OTAA mode.
bc_cmwx1zzabz_config_mode_t
LoRa mode ABP/OTAA.
Definition: bc_cmwx1zzabz.h:66
void bc_cmwx1zzabz_set_devaddr(bc_cmwx1zzabz_t *self, char *devaddr)
Set DEVADDR.
void bc_cmwx1zzabz_set_band(bc_cmwx1zzabz_t *self, bc_cmwx1zzabz_config_band_t band)
Set BAND.
void bc_cmwx1zzabz_init(bc_cmwx1zzabz_t *self, bc_uart_channel_t uart_channel)
Initialize CMWX1ZZABZ.
Definition: bc_cmwx1zzabz.c:38
bool bc_uart_async_read_start(bc_uart_channel_t channel, bc_tick_t timeout)
Start async reading.
Definition: bc_uart.c:475
Event is reading done.
Definition: bc_uart.h:133
void bc_cmwx1zzabz_get_deveui(bc_cmwx1zzabz_t *self, char *deveui)
Get DEVEUI.
bc_scheduler_task_id_t bc_scheduler_register(void(*task)(void *), void *param, bc_tick_t tick)
Register task in scheduler.
Definition: bc_scheduler.c:56
8N1: 8 data bits, none parity bit, 1 stop bit
Definition: bc_uart.h:70
struct bc_cmwx1zzabz_t bc_cmwx1zzabz_t
CMWX1ZZABZ instance.
Definition: bc_cmwx1zzabz.h:62
Retransmission of the confirmed message.
Definition: bc_cmwx1zzabz.h:50
void bc_scheduler_plan_now(bc_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
Definition: bc_scheduler.c:119
bool bc_cmwx1zzabz_send_message(bc_cmwx1zzabz_t *self, const void *buffer, size_t length)
Send LoRa message.
Definition: bc_cmwx1zzabz.c:85
void bc_cmwx1zzabz_get_nwkskey(bc_cmwx1zzabz_t *self, char *nwkskey)
Set NWKSKEY.
Sent message not confirmed.
Definition: bc_cmwx1zzabz.h:56
void bc_cmwx1zzabz_set_appkey(bc_cmwx1zzabz_t *self, char *appkey)
Set APPKEY.
uint32_t bc_cmwx1zzabz_get_received_message_length(bc_cmwx1zzabz_t *self)
Get length of the received message.
uint8_t bc_cmwx1zzabz_get_nwk_public(bc_cmwx1zzabz_t *self)
Get the configuration if public networks are enabled.
uint32_t bc_cmwx1zzabz_get_received_message_data(bc_cmwx1zzabz_t *self, uint8_t *buffer, uint32_t buffer_size)
Get received message data.
void bc_uart_init(bc_uart_channel_t channel, bc_uart_baudrate_t baudrate, bc_uart_setting_t setting)
Initialize UART channel.
Definition: bc_uart.c:53
bool bc_cmwx1zzabz_is_ready(bc_cmwx1zzabz_t *self)
Check if modem is ready for commands.
Definition: bc_cmwx1zzabz.c:75
bc_uart_event_t
Callback events.
Definition: bc_uart.h:127
void bc_cmwx1zzabz_join(bc_cmwx1zzabz_t *self)
Start LoRa OTAA join procedure.
bc_cmwx1zzabz_config_mode_t bc_cmwx1zzabz_get_mode(bc_cmwx1zzabz_t *self)
Get ABP/OTAA mode.
void bc_fifo_init(bc_fifo_t *fifo, void *buffer, size_t size)
Initialize FIFO buffer.
Definition: bc_fifo.c:4
void bc_cmwx1zzabz_get_appkey(bc_cmwx1zzabz_t *self, char *appkey)
Get APPKEY.
void bc_scheduler_plan_relative(bc_scheduler_task_id_t task_id, bc_tick_t tick)
Schedule specified task to tick relative from current spin.
Definition: bc_scheduler.c:129
void bc_cmwx1zzabz_set_appskey(bc_cmwx1zzabz_t *self, char *appskey)
Set APPSKEY.
bc_cmwx1zzabz_config_class_t bc_cmwx1zzabz_get_class(bc_cmwx1zzabz_t *self)
Get device class.
void bc_cmwx1zzabz_set_nwk_public(bc_cmwx1zzabz_t *self, uint8_t public)
Set the configuration enabling public networks.
bool bc_cmwx1zzabz_send_message_confirmed(bc_cmwx1zzabz_t *self, const void *buffer, size_t length)
Send LoRa confirmed message.
uint8_t bc_cmwx1zzabz_get_datarate(bc_cmwx1zzabz_t *self)
Get the configuration of datarate.
void bc_cmwx1zzabz_set_nwkskey(bc_cmwx1zzabz_t *self, char *nwkskey)
Set NWKSKEY.
bc_cmwx1zzabz_event_t
Callback events.
Definition: bc_cmwx1zzabz.h:23
void bc_cmwx1zzabz_get_rx2(bc_cmwx1zzabz_t *self, uint32_t *frequency, uint8_t *datarate)
Get the frequency and datarate for RX2 receive window.
void bc_cmwx1zzabz_set_rx2(bc_cmwx1zzabz_t *self, uint32_t frequency, uint8_t datarate)
Set the frequency and datarate for RX2 receive window.
void bc_scheduler_plan_current_from_now(bc_tick_t tick)
Schedule current task to tick relative from now.
Definition: bc_scheduler.c:154
void bc_cmwx1zzabz_set_class(bc_cmwx1zzabz_t *self, bc_cmwx1zzabz_config_class_t class)
Set device class.
size_t bc_uart_async_write(bc_uart_channel_t channel, const void *buffer, size_t length)
Add data to be transmited in async mode.
Definition: bc_uart.c:428
void bc_uart_set_async_fifo(bc_uart_channel_t channel, bc_fifo_t *write_fifo, bc_fifo_t *read_fifo)
Set buffers for async transfers.
Definition: bc_uart.c:422
void bc_uart_set_event_handler(bc_uart_channel_t channel, void(*event_handler)(bc_uart_channel_t, bc_uart_event_t, void *), void *event_param)
Set callback function.
Definition: bc_uart.c:416
uint8_t bc_cmwx1zzabz_get_port(bc_cmwx1zzabz_t *self)
Get the port for the transmission of the messages.
#define BC_TICK_INFINITY
Maximum timestamp value.
Definition: bc_tick.h:12
void bc_cmwx1zzabz_set_appeui(bc_cmwx1zzabz_t *self, char *appeui)
Set APPEUI.
UART baudrat 9600 bps.
Definition: bc_uart.h:31
size_t bc_uart_async_read(bc_uart_channel_t channel, void *buffer, size_t length)
Get data that has been received in async mode.
Definition: bc_uart.c:571
void bc_cmwx1zzabz_get_appskey(bc_cmwx1zzabz_t *self, char *appskey)
Get APPSKEY.
bc_cmwx1zzabz_config_class_t
LoRa device class A or C.
Definition: bc_cmwx1zzabz.h:88
uint8_t bc_cmwx1zzabz_get_received_message_port(bc_cmwx1zzabz_t *self)
Get port of the received message.
bc_cmwx1zzabz_config_band_t bc_cmwx1zzabz_get_band(bc_cmwx1zzabz_t *self)
Get BAND.
void bc_cmwx1zzabz_set_port(bc_cmwx1zzabz_t *self, uint8_t port)
Set the port for the transmission of the messages.
RF frame transmission started event.
Definition: bc_cmwx1zzabz.h:32
RF frame transmission finished event.
Definition: bc_cmwx1zzabz.h:35
bc_cmwx1zzabz_config_band_t
Frequency modes and standards.
Definition: bc_cmwx1zzabz.h:75