1 #include <twr_cmwx1zzabz.h> 15 #define TWR_CMWX1ZZABZ_DELAY_RUN 100 16 #define TWR_CMWX1ZZABZ_DELAY_INITIALIZATION_RESET_H 100 17 #define TWR_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_COMMAND 100 // ! when using longer AT responses 18 #define TWR_CMWX1ZZABZ_DELAY_CONFIG_SAVE 100 19 #define TWR_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_RESPONSE 100 20 #define TWR_CMWX1ZZABZ_DELAY_SEND_MESSAGE_RESPONSE 1500 21 #define TWR_CMWX1ZZABZ_DELAY_JOIN_RESPONSE 500 //8000 22 #define TWR_CMWX1ZZABZ_DELAY_LINK_CHECK_RESPONSE 4000 23 #define TWR_CMWX1ZZABZ_DELAY_CUSTOM_COMMAND_RESPONSE 100 25 #define TWR_CMWX1ZZABZ_TIMEOUT_CUSTOM_COMMAND_RESPONSE 500 26 #define TWR_CMWX1ZZABZ_TIMEOUT_LNCHECK 20000 27 #define TWR_CMWX1ZZABZ_TIMEOUT_JOIN 20000 30 const char *_init_commands[] =
57 static void _twr_cmwx1zzabz_task(
void *param);
61 static void _twr_cmwx1zzabz_save_config(
twr_cmwx1zzabz_t *
self, twr_cmwx1zzabz_config_index_t config_index);
67 memset(
self, 0,
sizeof(*
self));
69 self->_uart_channel = uart_channel;
72 twr_fifo_init(&self->_tx_fifo, self->_tx_fifo_buffer,
sizeof(self->_tx_fifo_buffer));
73 twr_fifo_init(&self->_rx_fifo, self->_rx_fifo_buffer,
sizeof(self->_rx_fifo_buffer));
81 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
92 self->_state = TWR_CMWX1ZZABZ_STATE_RECEIVE;
98 self->_event_handler = event_handler;
99 self->_event_param = event_param;
104 if (self->_state == TWR_CMWX1ZZABZ_STATE_IDLE)
119 self->_message_length = length;
121 memcpy(self->_message_buffer, buffer, self->_message_length);
123 self->_state = TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND;
137 self->_message_length = length;
139 memcpy(self->_message_buffer, buffer, self->_message_length);
141 self->_state = TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND;
150 self->_debug = debug;
153 static size_t _twr_cmwx1zzabz_async_write(
twr_cmwx1zzabz_t *
self,
const void *buffer,
size_t length)
165 static size_t _twr_cmwx1zzabz_write(
twr_cmwx1zzabz_t *
self,
const void *buffer,
size_t length)
177 static void _twr_cmwx1zzabz_task(
void *param)
183 switch (self->_state)
185 case TWR_CMWX1ZZABZ_STATE_READY:
187 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
189 if (self->_event_handler != NULL)
196 case TWR_CMWX1ZZABZ_STATE_IDLE:
198 if (self->_save_config_mask != 0)
200 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
204 if(self->_join_command)
206 self->_state = TWR_CMWX1ZZABZ_STATE_JOIN_SEND;
210 if(self->_custom_command)
212 self->_state = TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_SEND;
216 if(self->_link_check_command)
218 self->_state = TWR_CMWX1ZZABZ_STATE_LINK_CHECK_SEND;
224 case TWR_CMWX1ZZABZ_STATE_RECEIVE:
226 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
228 while (_twr_cmwx1zzabz_read_response(
self))
230 if (memcmp(self->_response,
"+RECV=", 5) == 0)
232 self->_message_port = atoi(&self->_response[6]);
234 char *comma_search = strchr(self->_response,
',');
241 self->_message_length = atoi(++comma_search);
252 if (self->_message_length >
sizeof(self->_message_buffer))
258 bytes =
twr_uart_async_read(self->_uart_channel, self->_message_buffer, self->_message_length);
259 if (bytes != self->_message_length)
264 if (self->_event_handler != NULL)
269 else if (memcmp(self->_response,
"+ACK", 4) == 0)
271 if (self->_event_handler != NULL)
276 else if (memcmp(self->_response,
"+NOACK", 4) == 0)
278 if (self->_event_handler != NULL)
283 else if (memcmp(self->_response,
"+EVENT=2,2", 10) == 0)
285 if (self->_event_handler != NULL)
293 else if (memcmp(self->_response,
"+EVENT=0,1", 10) == 0 || memcmp(self->_response,
"+EVENT=0,0", 10) == 0)
295 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
296 self->_save_config_mask = 0;
298 if (self->_event_handler != NULL)
300 self->_event_handler(
self, TWR_CMWX1ZZABZ_EVENT_MODEM_FACTORY_RESET, self->_event_param);
307 case TWR_CMWX1ZZABZ_STATE_ERROR:
309 self->_save_config_mask = 0;
311 if (self->_event_handler != NULL)
316 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
320 case TWR_CMWX1ZZABZ_STATE_INITIALIZE:
322 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
323 self->_init_command_index = 0;
326 char cmd_at[] =
"AT\r";
327 size_t length = strlen(cmd_at);
329 _twr_cmwx1zzabz_write(
self, cmd_at, length);
338 if (_twr_cmwx1zzabz_async_write(
self, cmd_at, length) != length)
343 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_AT_RESPONSE;
349 case TWR_CMWX1ZZABZ_STATE_INITIALIZE_AT_RESPONSE:
351 _twr_cmwx1zzabz_read_response(
self);
353 if (strcmp(self->_response,
"+OK\r") == 0)
360 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
369 self->_state = TWR_CMWX1ZZABZ_STATE_RECOVER_BAUDRATE_UART;
372 case TWR_CMWX1ZZABZ_STATE_RECOVER_BAUDRATE_UART:
374 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
387 char cmd_at[] =
"AT\r";
388 _twr_cmwx1zzabz_write(
self, cmd_at, strlen(cmd_at));
394 char cmd_at_baud[] =
"AT+UART=9600\r";
395 _twr_cmwx1zzabz_write(
self, cmd_at_baud, strlen(cmd_at_baud));
401 char cmd_at_reboot[] =
"AT+REBOOT\r";
402 _twr_cmwx1zzabz_write(
self, cmd_at_reboot, strlen(cmd_at_reboot));
412 twr_fifo_init(&self->_tx_fifo, self->_tx_fifo_buffer,
sizeof(self->_tx_fifo_buffer));
413 twr_fifo_init(&self->_rx_fifo, self->_rx_fifo_buffer,
sizeof(self->_rx_fifo_buffer));
419 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
425 case TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND:
427 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
432 strcpy(self->_command, _init_commands[self->_init_command_index]);
433 size_t length = strlen(self->_command);
435 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
440 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_RESPONSE;
446 case TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_RESPONSE:
448 if (!_twr_cmwx1zzabz_read_response(
self))
451 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_CUSTOM_COMMAND_RESPONSE))
453 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
458 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
459 uint8_t response_handled = 0;
462 uint32_t response_valid = (memcmp(self->_response,
"+OK=", 4) == 0);
464 const char *last_command = _init_commands[
self->_init_command_index];
466 char *response_string_value = &
self->_response[4];
468 if (strcmp(last_command,
"AT+DEVADDR?\r") == 0 && response_valid)
471 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR) == 0)
473 memcpy(self->_config.devaddr, response_string_value, 8);
474 self->_config.devaddr[8] =
'\0';
476 response_handled = 1;
478 else if (strcmp(last_command,
"AT+DEVEUI?\r") == 0 && response_valid)
480 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI) == 0)
482 memcpy(self->_config.deveui, response_string_value, 16);
483 self->_config.deveui[16] =
'\0';
485 response_handled = 1;
487 else if (strcmp(last_command,
"AT+APPEUI?\r") == 0 && response_valid)
489 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_APPEUI) == 0)
491 memcpy(self->_config.appeui, response_string_value, 16);
492 self->_config.appeui[16] =
'\0';
494 response_handled = 1;
496 else if (strcmp(last_command,
"AT+NWKSKEY?\r") == 0 && response_valid)
498 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY) == 0)
500 memcpy(self->_config.nwkskey, response_string_value, 32);
501 self->_config.nwkskey[32] =
'\0';
503 response_handled = 1;
505 else if (strcmp(last_command,
"AT+APPSKEY?\r") == 0 && response_valid)
507 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY) == 0)
509 memcpy(self->_config.appskey, response_string_value, 32);
510 self->_config.appskey[32] =
'\0';
512 response_handled = 1;
514 else if (strcmp(last_command,
"AT+APPKEY?\r") == 0 && response_valid)
516 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_APPKEY) == 0)
518 memcpy(self->_config.appkey, response_string_value, 32);
519 self->_config.appkey[32] =
'\0';
521 response_handled = 1;
523 else if (strcmp(last_command,
"AT+BAND?\r") == 0 && response_valid)
525 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_BAND) == 0)
527 self->_config.band = response_string_value[0] -
'0';
529 response_handled = 1;
531 else if (strcmp(last_command,
"AT+MODE?\r") == 0 && response_valid)
533 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_MODE) == 0)
535 self->_config.mode = response_string_value[0] -
'0';
537 response_handled = 1;
539 else if (strcmp(last_command,
"AT+CLASS?\r") == 0 && response_valid)
541 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_CLASS) == 0)
543 self->_config.class = response_string_value[0] -
'0';
545 response_handled = 1;
547 else if (strcmp(last_command,
"AT+RX2?\r") == 0 && response_valid)
549 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_RX2) == 0)
551 self->_config.rx2_frequency = atoi(response_string_value);
553 char *comma_search = strchr(response_string_value,
',');
559 self->_config.rx2_datarate = atoi(++comma_search);
561 response_handled = 1;
563 else if (strcmp(last_command,
"AT+NWK?\r") == 0 && response_valid)
565 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_NWK) == 0)
567 self->_config.nwk_public = response_string_value[0] -
'0';
569 response_handled = 1;
571 else if (strcmp(last_command,
"AT+ADR?\r") == 0 && response_valid)
573 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_ADAPTIVE_DATARATE) == 0)
575 self->_config.adaptive_datarate = response_string_value[0] ==
'1';
577 response_handled = 1;
579 else if (strcmp(last_command,
"AT+DR?\r") == 0 && response_valid)
581 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_DATARATE) == 0)
583 self->_config.datarate = atoi(response_string_value);
585 response_handled = 1;
587 else if (strcmp(last_command,
"AT+REP?\r") == 0 && response_valid)
589 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_REP) == 0)
591 self->_config.repetition_unconfirmed = atoi(response_string_value);
593 response_handled = 1;
595 else if (strcmp(last_command,
"AT+RTYNUM?\r") == 0 && response_valid)
597 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_RTYNUM) == 0)
599 self->_config.repetition_confirmed = atoi(response_string_value);
601 response_handled = 1;
603 else if (strcmp(last_command,
"AT+DUTYCYCLE=0\r") == 0 && strcmp(self->_response,
"+ERR=-17\r") == 0)
606 response_handled = 1;
608 else if (strcmp(last_command,
"AT+DWELL=0,0\r") == 0 && strcmp(self->_response,
"+ERR=-17\r") == 0)
611 response_handled = 1;
613 else if (strcmp(last_command,
"AT+JOINDC=0\r") == 0 && (strcmp(self->_response,
"+ERR=-1\r") == 0 || strcmp(self->_response,
"+ERR=-14\r") == 0 || strcmp(self->_response,
"+ERR=-17\r") == 0))
619 response_handled = 1;
621 else if (strcmp(last_command,
"AT+VER?\r") == 0 && memcmp(self->_response,
"+OK=", 4) == 0)
625 char *comma = strchr(self->_response,
',');
628 char *first_character = &
self->_response[4];
629 memset(self->_fw_version,
'\0',
sizeof(self->_fw_version));
630 int len = comma - first_character;
631 memcpy(self->_fw_version, first_character, len);
633 response_handled = 1;
635 else if ( strcmp(last_command,
"AT\r") == 0 &&
636 strcmp(self->_response,
"+OK\r") == 0
639 response_handled = 1;
642 else if (memcmp(self->_response,
"+OK", 3) == 0)
644 response_handled = 1;
647 if (!response_handled)
652 self->_init_command_index++;
654 if (_init_commands[self->_init_command_index] == NULL)
657 if (self->_save_config_mask)
659 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
660 self->_save_command_index = 0;
664 self->_state = TWR_CMWX1ZZABZ_STATE_READY;
669 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
674 case TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND:
675 case TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND:
677 if (self->_state == TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND)
679 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+PCTX %d,%d\r", self->_tx_port, self->_message_length);
683 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+PUTX %d,%d\r", self->_tx_port, self->_message_length);
686 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
688 uint8_t command_length = strlen(self->_command);
690 for (
size_t i = 0; i <
self->_message_length; i++)
693 self->_command[command_length + i] =
self->_message_buffer[i];
696 self->_command[command_length +
self->_message_length] =
'\r';
698 size_t length = command_length +
self->_message_length + 1;
700 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
705 self->_state = TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE;
707 if (self->_event_handler != NULL)
716 case TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE:
718 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
720 if (!_twr_cmwx1zzabz_read_response(
self))
725 if (strcmp(self->_response,
"+OK\r") != 0)
730 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
732 if (self->_event_handler != NULL)
739 case TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND:
741 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
744 if (self->_save_config_mask == 0)
746 if (self->_event_handler != NULL)
751 self->_state = TWR_CMWX1ZZABZ_STATE_READY;
756 for (uint8_t i = 0; i < TWR_CMWX1ZZABZ_CONFIG_INDEX_LAST_ITEM; i++)
758 if (self->_save_config_mask & 1 << i)
760 self->_save_command_index = i;
768 switch (self->_save_command_index)
770 case TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR:
772 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+DEVADDR=%s\r", self->_config.devaddr);
775 case TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI:
777 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+DEVEUI=%s\r", self->_config.deveui);
780 case TWR_CMWX1ZZABZ_CONFIG_INDEX_APPEUI:
782 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+APPEUI=%s\r", self->_config.appeui);
785 case TWR_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY:
787 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+NWKSKEY=%s\r", self->_config.nwkskey);
790 case TWR_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY:
792 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+APPSKEY=%s\r", self->_config.appskey);
795 case TWR_CMWX1ZZABZ_CONFIG_INDEX_APPKEY:
797 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+APPKEY=%s\r", self->_config.appkey);
800 case TWR_CMWX1ZZABZ_CONFIG_INDEX_BAND:
802 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+BAND=%d\r", self->_config.band);
805 case TWR_CMWX1ZZABZ_CONFIG_INDEX_MODE:
807 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+MODE=%d\r", self->_config.mode);
810 case TWR_CMWX1ZZABZ_CONFIG_INDEX_CLASS:
812 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+CLASS=%d\r", self->_config.class);
815 case TWR_CMWX1ZZABZ_CONFIG_INDEX_RX2:
817 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+RX2=%d,%d\r", (
int) self->_config.rx2_frequency, self->_config.rx2_datarate);
820 case TWR_CMWX1ZZABZ_CONFIG_INDEX_NWK:
822 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+NWK=%d\r", (
int) self->_config.nwk_public);
825 case TWR_CMWX1ZZABZ_CONFIG_INDEX_ADAPTIVE_DATARATE:
827 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+ADR=%d\r", self->_config.adaptive_datarate ? 1 : 0);
830 case TWR_CMWX1ZZABZ_CONFIG_INDEX_DATARATE:
832 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+DR=%d\r", (
int) self->_config.datarate);
835 case TWR_CMWX1ZZABZ_CONFIG_INDEX_REP:
837 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+REP=%d\r", (
int) self->_config.repetition_unconfirmed);
840 case TWR_CMWX1ZZABZ_CONFIG_INDEX_RTYNUM:
842 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+RTYNUM=%d\r", (
int) self->_config.repetition_confirmed);
852 size_t length = strlen(self->_command);
854 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
859 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE;
864 case TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE:
866 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
868 if (!_twr_cmwx1zzabz_read_response(
self))
874 if (memcmp(self->_response,
"+OK", 3) != 0)
880 self->_save_config_mask &= ~(1 <<
self->_save_command_index);
882 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
887 case TWR_CMWX1ZZABZ_STATE_JOIN_SEND:
889 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
894 strcpy(self->_command,
"AT+JOIN\r");
896 size_t length = strlen(self->_command);
897 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
903 self->_join_command =
false;
905 self->_state = TWR_CMWX1ZZABZ_STATE_JOIN_RESPONSE;
911 case TWR_CMWX1ZZABZ_STATE_JOIN_RESPONSE:
913 bool join_successful =
false;
915 if (!_twr_cmwx1zzabz_read_response(
self))
918 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_JOIN))
920 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
925 if (memcmp(self->_response,
"+OK", 3) == 0)
932 if (memcmp(self->_response,
"+EVENT=1,1", 10) == 0)
934 join_successful =
true;
939 if (self->_event_handler != NULL)
946 if (self->_event_handler != NULL)
952 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
956 case TWR_CMWX1ZZABZ_STATE_LINK_CHECK_SEND:
958 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
963 strcpy(self->_command,
"AT+LNCHECK\r");
965 size_t length = strlen(self->_command);
966 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
971 self->_link_check_command =
false;
973 self->_state = TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE;
979 case TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE:
981 if (!_twr_cmwx1zzabz_read_response(
self))
984 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_LNCHECK))
986 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
991 if (memcmp(self->_response,
"+OK", 3) == 0)
998 if (strstr(self->_response,
"+EVENT=2,") > 0)
1001 char *event_str = strchr(self->_response,
',');
1004 uint8_t link_check_event = atoi(event_str);
1007 self->_cmd_link_check_gwcnt = 0;
1008 self->_cmd_link_check_margin = 0;
1010 if (link_check_event == 1)
1012 self->_state = TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE_ANS;
1020 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1024 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1026 if (self->_event_handler != NULL)
1028 self->_event_handler(
self, TWR_CMWX1ZZABZ_EVENT_LINK_CHECK_NOK, self->_event_param);
1035 case TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE_ANS:
1038 if (_twr_cmwx1zzabz_read_response(
self))
1041 char *ans = strstr(self->_response,
"+ANS=2,");
1046 self->_cmd_link_check_margin = atoi(ans);
1049 char *gwcnt_str = strchr(ans,
',');
1052 self->_cmd_link_check_gwcnt = atoi(gwcnt_str);
1056 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1058 if (self->_event_handler != NULL)
1060 self->_event_handler(
self, TWR_CMWX1ZZABZ_EVENT_LINK_CHECK_OK, self->_event_param);
1067 case TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_SEND:
1069 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1074 strcpy(self->_command, self->_custom_command_buf);
1076 size_t length = strlen(self->_command);
1077 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
1082 self->_state = TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_RESPONSE;
1088 case TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_RESPONSE:
1090 if (!_twr_cmwx1zzabz_read_response(
self))
1093 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_CUSTOM_COMMAND_RESPONSE))
1095 self->_custom_command =
false;
1096 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1098 if (strcmp(self->_command,
"AT+FACNEW\r") == 0)
1101 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
1107 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1108 self->_custom_command =
false;
1112 if (memcmp(self->_response,
"+ERR=-1", 7) == 0)
1114 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1115 self->_cmd_frmcnt_uplink = 0;
1116 self->_cmd_frmcnt_downlink = 0;
1120 if (memcmp(self->_response,
"+OK=", 4) == 0)
1126 if (strcmp(self->_custom_command_buf,
"AT+RFQ?\r") == 0)
1129 char *rssi_str = strchr(self->_response,
'=');
1132 char *snr_str = strchr(self->_response,
',');
1135 self->_cmd_rfq_rssi = atoi(rssi_str);
1136 self->_cmd_rfq_snr = atoi(snr_str);
1140 if (strcmp(self->_custom_command_buf,
"AT+FRMCNT?\r") == 0)
1143 char *uplink_str = strchr(self->_response,
'=');
1146 char *downlink_str = strchr(self->_response,
',');
1149 self->_cmd_frmcnt_uplink = atoi(uplink_str);
1150 self->_cmd_frmcnt_downlink = atoi(downlink_str);
1154 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1156 if (self->_event_handler != NULL)
1158 self->_event_handler(
self, event, self->_event_param);
1176 self->_join_command =
true;
1182 self->_tx_port = port;
1187 return self->_tx_port;
1192 strncpy(self->_config.devaddr, devaddr, 8+1);
1194 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR);
1199 strncpy(devaddr, self->_config.devaddr, 8+1);
1204 strncpy(self->_config.deveui, deveui, 16+1);
1206 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI);
1211 strncpy(deveui, self->_config.deveui, 16+1);
1216 strncpy(self->_config.appeui, appeui, 16+1);
1218 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_APPEUI);
1223 strncpy(appeui, self->_config.appeui, 16+1);
1228 strncpy(self->_config.nwkskey, nwkskey, 32);
1230 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY);
1235 strncpy(nwkskey, self->_config.nwkskey, 32+1);
1240 strncpy(self->_config.appskey, appskey, 32);
1242 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY);
1247 strncpy(appskey, self->_config.appskey, 32+1);
1252 strncpy(self->_config.appkey, appkey, 32+1);
1254 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_APPKEY);
1259 strncpy(appkey, self->_config.appkey, 32+1);
1264 self->_config.band = band;
1266 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_BAND);
1271 return self->_config.band;
1276 self->_config.mode = mode;
1278 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_MODE);
1283 return self->_config.mode;
1288 self->_config.class =
class;
1290 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_CLASS);
1295 return self->_config.class;
1300 return self->_message_port;
1305 return self->_message_length;
1310 if (self->_message_length > buffer_size)
1315 memcpy(buffer, self->_message_buffer, self->_message_length);
1317 return self->_message_length;
1322 self->_config.rx2_frequency = frequency;
1324 self->_config.rx2_datarate = datarate;
1326 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_RX2);
1331 *frequency =
self->_config.rx2_frequency;
1332 *datarate =
self->_config.rx2_datarate;
1337 self->_config.nwk_public =
public;
1339 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_NWK);
1344 return self->_config.nwk_public;
1349 self->_config.adaptive_datarate = enable;
1351 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_ADAPTIVE_DATARATE);
1356 return self->_config.adaptive_datarate;
1361 self->_config.datarate = datarate;
1363 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_DATARATE);
1368 return self->_config.datarate;
1373 self->_config.repetition_unconfirmed = repeat;
1375 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_REP);
1380 return self->_config.repetition_unconfirmed;
1385 self->_config.repetition_confirmed = repeat;
1387 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_RTYNUM);
1392 return self->_config.repetition_confirmed;
1397 return self->_command;
1402 return self->_response;
1407 if (self->_custom_command)
1412 self->_custom_command =
true;
1413 strcpy(self->_custom_command_buf,
"AT+RFQ?\r");
1422 *rssi =
self->_cmd_rfq_rssi;
1423 *snr =
self->_cmd_rfq_snr;
1430 if (self->_custom_command)
1435 self->_custom_command =
true;
1436 strcpy(self->_custom_command_buf,
"AT+FRMCNT?\r");
1445 *uplink =
self->_cmd_frmcnt_uplink;
1446 *downlink =
self->_cmd_frmcnt_downlink;
1453 if (self->_custom_command)
1458 self->_custom_command =
true;
1459 strcpy(self->_custom_command_buf,
"AT+FACNEW\r");
1468 return self->_fw_version;
1473 if (self->_state != TWR_CMWX1ZZABZ_STATE_IDLE)
1478 self->_link_check_command =
true;
1487 *margin =
self->_cmd_link_check_margin;
1488 *gateway_count =
self->_cmd_link_check_gwcnt;
1504 if (rx_character ==
'\n')
1509 self->_response[
self->_response_length++] = rx_character;
1511 if (rx_character ==
'\r')
1513 if (self->_response_length == 1)
1515 self->_response_length = 0;
1520 self->_response[
self->_response_length] =
'\0';
1525 if (self->_response_length ==
sizeof(self->_response) - 1)
1536 self->_response_length = 0;
1541 static void _twr_cmwx1zzabz_save_config(
twr_cmwx1zzabz_t *
self, twr_cmwx1zzabz_config_index_t config_index)
1543 self->_save_config_mask |= 1 << config_index;
1545 if (self->_state == TWR_CMWX1ZZABZ_STATE_IDLE)
uint8_t twr_cmwx1zzabz_get_repeat_confirmed(twr_cmwx1zzabz_t *self)
Get number of transmissions of confirmed message.
Sent message not confirmed.
uint8_t twr_cmwx1zzabz_get_nwk_public(twr_cmwx1zzabz_t *self)
Get the configuration if public networks are enabled.
void twr_scheduler_plan_relative(twr_scheduler_task_id_t task_id, twr_tick_t tick)
Schedule specified task to tick relative from current spin.
void twr_cmwx1zzabz_set_mode(twr_cmwx1zzabz_t *self, twr_cmwx1zzabz_config_mode_t mode)
Set ABP/OTAA mode.
void twr_cmwx1zzabz_set_repeat_unconfirmed(twr_cmwx1zzabz_t *self, uint8_t repeat)
Set number of transmissions of unconfirmed message.
void twr_cmwx1zzabz_set_port(twr_cmwx1zzabz_t *self, uint8_t port)
Set the port for the transmission of the messages.
bool twr_cmwx1zzabz_get_link_check(twr_cmwx1zzabz_t *self, uint8_t *margin, uint8_t *gateway_count)
Get link check values.
twr_cmwx1zzabz_event_t
Callback events.
void twr_cmwx1zzabz_get_devaddr(twr_cmwx1zzabz_t *self, char *devaddr)
Get DEVADDR.
uint8_t twr_cmwx1zzabz_get_repeat_unconfirmed(twr_cmwx1zzabz_t *self)
Get number of transmissions of unconfirmed message.
bool twr_cmwx1zzabz_rfq(twr_cmwx1zzabz_t *self)
Request RF quality of the last received packet (JOIN, LNPARAM, confirmed message) ...
twr_cmwx1zzabz_config_class_t
LoRa device class A or C.
bool twr_uart_async_read_start(twr_uart_channel_t channel, twr_tick_t timeout)
Start async reading.
void twr_cmwx1zzabz_set_band(twr_cmwx1zzabz_t *self, twr_cmwx1zzabz_config_band_t band)
Set BAND.
twr_scheduler_task_id_t twr_scheduler_register(void(*task)(void *), void *param, twr_tick_t tick)
Register task in scheduler.
void twr_cmwx1zzabz_init(twr_cmwx1zzabz_t *self, twr_uart_channel_t uart_channel)
Initialize CMWX1ZZABZ.
void twr_cmwx1zzabz_set_event_handler(twr_cmwx1zzabz_t *self, void(*event_handler)(twr_cmwx1zzabz_t *, twr_cmwx1zzabz_event_t, void *), void *event_param)
Set callback function.
void void twr_log_debug(const char *format,...) __attribute__((format(printf
Log DEBUG message (annotated in log as <D>)
bool twr_cmwx1zzabz_link_check(twr_cmwx1zzabz_t *self)
Request send of link check packet.
void twr_cmwx1zzabz_set_datarate(twr_cmwx1zzabz_t *self, uint8_t datarate)
Set the configuration of datarate.
void twr_cmwx1zzabz_set_appkey(twr_cmwx1zzabz_t *self, char *appkey)
Set APPKEY.
RF frame transmission finished event.
twr_cmwx1zzabz_config_band_t twr_cmwx1zzabz_get_band(twr_cmwx1zzabz_t *self)
Get BAND.
void twr_uart_init(twr_uart_channel_t channel, twr_uart_baudrate_t baudrate, twr_uart_setting_t setting)
Initialize UART channel.
void twr_cmwx1zzabz_get_rx2(twr_cmwx1zzabz_t *self, uint32_t *frequency, uint8_t *datarate)
Get the frequency and datarate for RX2 receive window.
twr_cmwx1zzabz_config_class_t twr_cmwx1zzabz_get_class(twr_cmwx1zzabz_t *self)
Get device class.
void twr_cmwx1zzabz_set_deveui(twr_cmwx1zzabz_t *self, char *deveui)
Set DEVEUI.
Retransmission of the confirmed message.
void twr_cmwx1zzabz_get_deveui(twr_cmwx1zzabz_t *self, char *deveui)
Get DEVEUI.
uint32_t twr_cmwx1zzabz_get_received_message_data(twr_cmwx1zzabz_t *self, uint8_t *buffer, uint32_t buffer_size)
Get received message data.
void twr_cmwx1zzabz_get_appkey(twr_cmwx1zzabz_t *self, char *appkey)
Get APPKEY.
void twr_timer_stop(void)
Stop timer.
void twr_fifo_init(twr_fifo_t *fifo, void *buffer, size_t size)
Initialize FIFO buffer.
void twr_cmwx1zzabz_set_rx2(twr_cmwx1zzabz_t *self, uint32_t frequency, uint8_t datarate)
Set the frequency and datarate for RX2 receive window.
void twr_cmwx1zzabz_set_devaddr(twr_cmwx1zzabz_t *self, char *devaddr)
Set DEVADDR.
char * twr_cmwx1zzabz_get_error_response(twr_cmwx1zzabz_t *self)
Get pointer to string containg response on last sent command.
size_t twr_uart_async_read(twr_uart_channel_t channel, void *buffer, size_t length)
Get data that has been received in async mode.
void twr_cmwx1zzabz_set_appeui(twr_cmwx1zzabz_t *self, char *appeui)
Set APPEUI.
void twr_scheduler_plan_current_from_now(twr_tick_t tick)
Schedule current task to tick relative from now.
void twr_cmwx1zzabz_set_class(twr_cmwx1zzabz_t *self, twr_cmwx1zzabz_config_class_t class)
Set device class.
void twr_cmwx1zzabz_set_appskey(twr_cmwx1zzabz_t *self, char *appskey)
Set APPSKEY.
uint8_t twr_cmwx1zzabz_get_received_message_port(twr_cmwx1zzabz_t *self)
Get port of the received message.
bool twr_cmwx1zzabz_is_ready(twr_cmwx1zzabz_t *self)
Check if modem is ready for commands.
void twr_scheduler_plan_current_now(void)
Schedule current task for immediate execution.
RF frame transmission started event.
struct twr_cmwx1zzabz_t twr_cmwx1zzabz_t
CMWX1ZZABZ instance.
twr_cmwx1zzabz_config_mode_t
LoRa mode ABP/OTAA.
void twr_cmwx1zzabz_get_appskey(twr_cmwx1zzabz_t *self, char *appskey)
Get APPSKEY.
void twr_cmwx1zzabz_set_nwk_public(twr_cmwx1zzabz_t *self, uint8_t public)
Set the configuration enabling public networks.
bool twr_cmwx1zzabz_get_frame_counter(twr_cmwx1zzabz_t *self, uint32_t *uplink, uint32_t *downlink)
Get frame counter value.
void twr_cmwx1zzabz_set_debug(twr_cmwx1zzabz_t *self, bool debug)
Set debugging flag which prints modem communication to twr_log.
void twr_cmwx1zzabz_get_nwkskey(twr_cmwx1zzabz_t *self, char *nwkskey)
Set NWKSKEY.
bool twr_cmwx1zzabz_factory_reset(twr_cmwx1zzabz_t *self)
Send factory reset command to LoRa Module.
twr_cmwx1zzabz_config_mode_t twr_cmwx1zzabz_get_mode(twr_cmwx1zzabz_t *self)
Get ABP/OTAA mode.
void twr_timer_init(void)
Initialize timer.
char * twr_cmwx1zzabz_get_error_command(twr_cmwx1zzabz_t *self)
Get pointer to string containg last sent command.
void twr_uart_set_event_handler(twr_uart_channel_t channel, void(*event_handler)(twr_uart_channel_t, twr_uart_event_t, void *), void *event_param)
Set callback function.
bool twr_cmwx1zzabz_get_adaptive_datarate(twr_cmwx1zzabz_t *self)
Get the configuration if adaptive data rate are enabled.
twr_tick_t twr_tick_get(void)
Get absolute timestamp since start of program.
void twr_cmwx1zzabz_join(twr_cmwx1zzabz_t *self)
Start LoRa OTAA join procedure.
uint8_t twr_cmwx1zzabz_get_port(twr_cmwx1zzabz_t *self)
Get the port for the transmission of the messages.
twr_uart_event_t
Callback events.
uint8_t twr_cmwx1zzabz_get_datarate(twr_cmwx1zzabz_t *self)
Get the configuration of datarate.
void twr_fifo_purge(twr_fifo_t *fifo)
Purge FIFO buffer.
bool twr_cmwx1zzabz_send_message_confirmed(twr_cmwx1zzabz_t *self, const void *buffer, size_t length)
Send LoRa confirmed message.
void twr_uart_deinit(twr_uart_channel_t channel)
Deinitialize UART channel.
twr_uart_channel_t
UART channels.
size_t twr_uart_write(twr_uart_channel_t channel, const void *buffer, size_t length)
Write data to UART channel (blocking call)
char * twr_cmwx1zzabz_get_fw_version(twr_cmwx1zzabz_t *self)
Get firmware version string.
bool twr_cmwx1zzabz_send_message(twr_cmwx1zzabz_t *self, const void *buffer, size_t length)
Send LoRa message.
void twr_cmwx1zzabz_set_nwkskey(twr_cmwx1zzabz_t *self, char *nwkskey)
Set NWKSKEY.
#define TWR_TICK_INFINITY
Maximum timestamp value.
8N1: 8 data bits, none parity bit, 1 stop bit
bool twr_cmwx1zzabz_get_rfq(twr_cmwx1zzabz_t *self, int32_t *rssi, int32_t *snr)
Get RF quality values in callback.
bool twr_cmwx1zzabz_frame_counter(twr_cmwx1zzabz_t *self)
Request frame counter value.
void twr_uart_set_async_fifo(twr_uart_channel_t channel, twr_fifo_t *write_fifo, twr_fifo_t *read_fifo)
Set buffers for async transfers.
void twr_timer_delay(uint16_t microseconds)
Relative delay.
void twr_scheduler_plan_now(twr_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
uint32_t twr_cmwx1zzabz_get_received_message_length(twr_cmwx1zzabz_t *self)
Get length of the received message.
void twr_timer_start(void)
Start timer.
twr_cmwx1zzabz_config_band_t
Frequency modes and standards.
void twr_cmwx1zzabz_set_adaptive_datarate(twr_cmwx1zzabz_t *self, bool enable)
Set the configuration adaptive data rate.
void twr_cmwx1zzabz_get_appeui(twr_cmwx1zzabz_t *self, char *appeui)
Get APPEUI.
void twr_cmwx1zzabz_set_repeat_confirmed(twr_cmwx1zzabz_t *self, uint8_t repeat)
Set number of transmissions of confirmed message.
size_t twr_uart_async_write(twr_uart_channel_t channel, const void *buffer, size_t length)
Add data to be transmited in async mode.