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  self->_config.datarate = atoi(response_string_value);
391  response_handled = 1;
392  }
393  else if (strcmp(last_command, "AT+DUTYCYCLE=0\r") == 0 && strcmp(self->_response, "+ERR=-17\r") == 0)
394  {
395  // DUTYCYLE is unusable in some band configuration, ignore this err response
396  response_handled = 1;
397  }
398  else if ( strcmp(last_command, "AT\r") == 0 &&
399  strcmp(self->_response, "+OK\r") == 0
400  )
401  {
402  response_handled = 1;
403  }
404  // Generic OK response to other commands
405  else if (memcmp(self->_response, "+OK", 3) == 0)
406  {
407  response_handled = 1;
408  }
409 
410  if (!response_handled)
411  {
412  continue;
413  }
414 
415  self->_init_command_index++;
416 
417  if (_init_commands[self->_init_command_index] == NULL)
418  {
419  // If configuration was changed and flag set, save them
420  if (self->_save_config_mask)
421  {
422  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
423  self->_save_command_index = 0;
424  }
425  else
426  {
427  self->_state = BC_CMWX1ZZABZ_STATE_READY;
428  }
429  }
430  else
431  {
432  self->_state = BC_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
433  }
434 
435  continue;
436  }
437  case BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND:
438  case BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND:
439  {
440  if (self->_state == BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND)
441  {
442  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+PCTX %d,%d\r", self->_tx_port, self->_message_length);
443  }
444  else
445  {
446  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+PUTX %d,%d\r", self->_tx_port, self->_message_length);
447  }
448 
449  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
450 
451  uint8_t command_length = strlen(self->_command);
452 
453  for (size_t i = 0; i < self->_message_length; i++)
454  {
455  // put binary data directly to the "string" buffer
456  self->_command[command_length + i] = self->_message_buffer[i];
457  }
458 
459  self->_command[command_length + self->_message_length] = '\r';
460 
461  size_t length = command_length + self->_message_length + 1; // 1 for \n
462 
463  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
464  {
465  continue;
466  }
467 
468  self->_state = BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE;
469 
470  if (self->_event_handler != NULL)
471  {
472  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_SEND_MESSAGE_START, self->_event_param);
473  }
474 
475  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_SEND_MESSAGE_RESPONSE);
476 
477  return;
478  }
479  case BC_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE:
480  {
481  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
482 
483  if (!_bc_cmwx1zzabz_read_response(self))
484  {
485  continue;
486  }
487 
488  if (strcmp(self->_response, "+OK\r") != 0)
489  {
490  continue;
491  }
492 
493  self->_state = BC_CMWX1ZZABZ_STATE_READY;
494 
495  if (self->_event_handler != NULL)
496  {
497  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_SEND_MESSAGE_DONE, self->_event_param);
498  }
499 
500  continue;
501  }
502  case BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND:
503  {
504  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
505 
506  // There are no more config items to send
507  if (self->_save_config_mask == 0)
508  {
509  if (self->_event_handler != NULL)
510  {
511  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_CONFIG_SAVE_DONE, self->_event_param);
512  }
513 
514  self->_state = BC_CMWX1ZZABZ_STATE_READY;
515  continue;
516  }
517 
518  // Find config item that has been changed
519  for (uint8_t i = 0; i < BC_CMWX1ZZABZ_CONFIG_INDEX_LAST_ITEM; i++)
520  {
521  if (self->_save_config_mask & 1 << i)
522  {
523  self->_save_command_index = i;
524  break;
525  }
526  }
527 
528  // Purge RX FIFO
529  char rx_character;
530  while (bc_uart_async_read(self->_uart_channel, &rx_character, 1) != 0)
531  {
532  }
533 
534  switch (self->_save_command_index)
535  {
536  case BC_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR:
537  {
538  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+DEVADDR=%s\r", self->_config.devaddr);
539  break;
540  }
541  case BC_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI:
542  {
543  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+DEVEUI=%s\r", self->_config.deveui);
544  break;
545  }
546  case BC_CMWX1ZZABZ_CONFIG_INDEX_APPEUI:
547  {
548  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+APPEUI=%s\r", self->_config.appeui);
549  break;
550  }
551  case BC_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY:
552  {
553  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+NWKSKEY=%s\r", self->_config.nwkskey);
554  break;
555  }
556  case BC_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY:
557  {
558  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+APPSKEY=%s\r", self->_config.appskey);
559  break;
560  }
561  case BC_CMWX1ZZABZ_CONFIG_INDEX_APPKEY:
562  {
563  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+APPKEY=%s\r", self->_config.appkey);
564  break;
565  }
566  case BC_CMWX1ZZABZ_CONFIG_INDEX_BAND:
567  {
568  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+BAND=%d\r", self->_config.band);
569  break;
570  }
571  case BC_CMWX1ZZABZ_CONFIG_INDEX_MODE:
572  {
573  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+MODE=%d\r", self->_config.mode);
574  break;
575  }
576  case BC_CMWX1ZZABZ_CONFIG_INDEX_CLASS:
577  {
578  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+CLASS=%d\r", self->_config.class);
579  break;
580  }
581  case BC_CMWX1ZZABZ_CONFIG_INDEX_RX2:
582  {
583  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+RX2=%d,%d\r", (int) self->_config.rx2_frequency, self->_config.rx2_datarate);
584  break;
585  }
586  case BC_CMWX1ZZABZ_CONFIG_INDEX_NWK:
587  {
588  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+NWK=%d\r", (int) self->_config.nwk_public);
589  break;
590  }
591  case BC_CMWX1ZZABZ_CONFIG_INDEX_DATARATE:
592  {
593  snprintf(self->_command, BC_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE, "AT+DR=%d\r", (int) self->_config.datarate);
594  break;
595  }
596  default:
597  {
598  break;
599  }
600  }
601 
602  size_t length = strlen(self->_command);
603 
604  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
605  {
606  continue;
607  }
608 
609  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE;
610  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_COMMAND);
611  return;
612  }
613 
614  case BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE:
615  {
616  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
617 
618  if (!_bc_cmwx1zzabz_read_response(self))
619  {
620  continue;
621  }
622 
623  // Jump to error state when response is not OK
624  if (memcmp(self->_response, "+OK", 3) != 0)
625  {
626  continue;
627  }
628 
629  // Clean bit mask
630  self->_save_config_mask &= ~(1 << self->_save_command_index);
631 
632  self->_state = BC_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
633  continue;
634 
635  }
636 
637 
638  case BC_CMWX1ZZABZ_STATE_JOIN_SEND:
639  {
640  self->_state = BC_CMWX1ZZABZ_STATE_ERROR;
641 
642  // Purge RX FIFO
643  char rx_character;
644  while (bc_uart_async_read(self->_uart_channel, &rx_character, 1) != 0)
645  {
646  }
647 
648  strcpy(self->_command, "AT+JOIN\r");
649 
650  size_t length = strlen(self->_command);
651  if (bc_uart_async_write(self->_uart_channel, self->_command, length) != length)
652  {
653  continue;
654  }
655 
656  self->_state = BC_CMWX1ZZABZ_STATE_JOIN_RESPONSE;
657  bc_scheduler_plan_current_from_now(BC_CMWX1ZZABZ_DELAY_JOIN_RESPONSE);
658  return;
659  }
660 
661  case BC_CMWX1ZZABZ_STATE_JOIN_RESPONSE:
662  {
663  bool join_successful = false;
664  // Clear join command flag
665  self->_join_command = false;
666 
667  while (true)
668  {
669  if (!_bc_cmwx1zzabz_read_response(self))
670  {
671  break;
672  }
673 
674  // Response EVENT=1,1 means JOIN was successful
675  if (memcmp(self->_response, "+EVENT=1,1", 10) == 0)
676  {
677  join_successful = true;
678  break;
679  }
680  }
681 
682  if (join_successful)
683  {
684  if (self->_event_handler != NULL)
685  {
686  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_JOIN_SUCCESS, self->_event_param);
687  }
688  }
689  else
690  {
691  if (self->_event_handler != NULL)
692  {
693  self->_event_handler(self, BC_CMWX1ZZABZ_EVENT_JOIN_ERROR, self->_event_param);
694  }
695  }
696 
697  self->_state = BC_CMWX1ZZABZ_STATE_IDLE;
698  continue;
699  }
700 
701  default:
702  {
703  break;
704  }
705  }
706  }
707 }
708 
710 {
711  self->_join_command = true;
712  bc_scheduler_plan_now(self->_task_id);
713 }
714 
715 void bc_cmwx1zzabz_set_port(bc_cmwx1zzabz_t *self, uint8_t port)
716 {
717  self->_tx_port = port;
718 }
719 
721 {
722  return self->_tx_port;
723 }
724 
725 void bc_cmwx1zzabz_set_devaddr(bc_cmwx1zzabz_t *self, char *devaddr)
726 {
727  strncpy(self->_config.devaddr, devaddr, 8+1);
728 
729  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR);
730 }
731 
732 void bc_cmwx1zzabz_get_devaddr(bc_cmwx1zzabz_t *self, char *devaddr)
733 {
734  strncpy(devaddr, self->_config.devaddr, 8+1);
735 }
736 
737 void bc_cmwx1zzabz_set_deveui(bc_cmwx1zzabz_t *self, char *deveui)
738 {
739  strncpy(self->_config.deveui, deveui, 16+1);
740 
741  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI);
742 }
743 
744 void bc_cmwx1zzabz_get_deveui(bc_cmwx1zzabz_t *self, char *deveui)
745 {
746  strncpy(deveui, self->_config.deveui, 16+1);
747 }
748 
749 void bc_cmwx1zzabz_set_appeui(bc_cmwx1zzabz_t *self, char *appeui)
750 {
751  strncpy(self->_config.appeui, appeui, 16+1);
752 
753  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_APPEUI);
754 }
755 
756 void bc_cmwx1zzabz_get_appeui(bc_cmwx1zzabz_t *self, char *appeui)
757 {
758  strncpy(appeui, self->_config.appeui, 16+1);
759 }
760 
761 void bc_cmwx1zzabz_set_nwkskey(bc_cmwx1zzabz_t *self, char *nwkskey)
762 {
763  strncpy(self->_config.nwkskey, nwkskey, 32);
764 
765  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY);
766 }
767 
768 void bc_cmwx1zzabz_get_nwkskey(bc_cmwx1zzabz_t *self, char *nwkskey)
769 {
770  strncpy(nwkskey, self->_config.nwkskey, 32+1);
771 }
772 
773 void bc_cmwx1zzabz_set_appskey(bc_cmwx1zzabz_t *self, char *appskey)
774 {
775  strncpy(self->_config.appskey, appskey, 32);
776 
777  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY);
778 }
779 
780 void bc_cmwx1zzabz_get_appskey(bc_cmwx1zzabz_t *self, char *appskey)
781 {
782  strncpy(appskey, self->_config.appskey, 32+1);
783 }
784 
785 void bc_cmwx1zzabz_set_appkey(bc_cmwx1zzabz_t *self, char *appkey)
786 {
787  strncpy(self->_config.appkey, appkey, 32+1);
788 
789  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_APPKEY);
790 }
791 
792 void bc_cmwx1zzabz_get_appkey(bc_cmwx1zzabz_t *self, char *appkey)
793 {
794  strncpy(appkey, self->_config.appkey, 32+1);
795 }
796 
798 {
799  self->_config.band = band;
800 
801  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_BAND);
802 }
803 
805 {
806  return self->_config.band;
807 }
808 
810 {
811  self->_config.mode = mode;
812 
813  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_MODE);
814 }
815 
817 {
818  return self->_config.mode;
819 }
820 
822 {
823  self->_config.class = class;
824 
825  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_CLASS);
826 }
827 
829 {
830  return self->_config.class;
831 }
832 
834 {
835  return self->_message_port;
836 }
837 
839 {
840  return self->_message_length;
841 }
842 
843 uint32_t bc_cmwx1zzabz_get_received_message_data(bc_cmwx1zzabz_t *self, uint8_t *buffer, uint32_t buffer_size)
844 {
845  if (self->_message_length > buffer_size)
846  {
847  return 0;
848  }
849 
850  memcpy(buffer, self->_message_buffer, self->_message_length);
851 
852  return self->_message_length;
853 }
854 
855 void bc_cmwx1zzabz_set_rx2(bc_cmwx1zzabz_t *self, uint32_t frequency, uint8_t datarate)
856 {
857  self->_config.rx2_frequency = frequency;
858 
859  self->_config.rx2_datarate = datarate;
860 
861  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_RX2);
862 }
863 
864 void bc_cmwx1zzabz_get_rx2(bc_cmwx1zzabz_t *self, uint32_t *frequency, uint8_t *datarate)
865 {
866  *frequency = self->_config.rx2_frequency;
867  *datarate = self->_config.rx2_datarate;
868 }
869 
871 {
872  self->_config.nwk_public = public;
873 
874  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_NWK);
875 }
876 
878 {
879  return self->_config.nwk_public;
880 }
881 
882 void bc_cmwx1zzabz_set_datarate(bc_cmwx1zzabz_t *self, uint8_t datarate)
883 {
884  self->_config.datarate = datarate;
885 
886  _bc_cmwx1zzabz_save_config(self, BC_CMWX1ZZABZ_CONFIG_INDEX_DATARATE);
887 }
888 
890 {
891  return self->_config.datarate;
892 }
893 
894 static bool _bc_cmwx1zzabz_read_response(bc_cmwx1zzabz_t *self)
895 {
896  size_t length = 0;
897 
898  while (true)
899  {
900  char rx_character;
901 
902  if (bc_uart_async_read(self->_uart_channel, &rx_character, 1) == 0)
903  {
904  return false;
905  }
906 
907  if (rx_character == '\n')
908  {
909  continue;
910  }
911 
912  self->_response[length++] = rx_character;
913 
914  if (rx_character == '\r')
915  {
916  if (length == 1)
917  {
918  length = 0;
919 
920  continue;
921  }
922 
923  self->_response[length] = '\0';
924 
925  break;
926  }
927 
928  if (length == sizeof(self->_response) - 1)
929  {
930  return false;
931  }
932  }
933 
934  return true;
935 }
936 
937 static void _bc_cmwx1zzabz_save_config(bc_cmwx1zzabz_t *self, bc_cmwx1zzabz_config_index_t config_index)
938 {
939  self->_save_config_mask |= 1 << config_index;
940 
941  if (self->_state == BC_CMWX1ZZABZ_STATE_IDLE)
942  {
943  bc_scheduler_plan_now(self->_task_id);
944  }
945 }
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