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
18 #define TWR_CMWX1ZZABZ_DELAY_CONFIG_SAVE 100
19 #define TWR_CMWX1ZZABZ_DELAY_INITIALIZATION_REBOOT 500
20 #define TWR_CMWX1ZZABZ_DELAY_INITIALIZATION_AT_RESPONSE 100
21 #define TWR_CMWX1ZZABZ_DELAY_SEND_MESSAGE_RESPONSE 1500
22 #define TWR_CMWX1ZZABZ_DELAY_JOIN_RESPONSE 500
23 #define TWR_CMWX1ZZABZ_DELAY_LINK_CHECK_RESPONSE 4000
24 #define TWR_CMWX1ZZABZ_DELAY_CUSTOM_COMMAND_RESPONSE 100
26 #define TWR_CMWX1ZZABZ_TIMEOUT_CUSTOM_COMMAND_RESPONSE 500
27 #define TWR_CMWX1ZZABZ_TIMEOUT_LNCHECK 20000
28 #define TWR_CMWX1ZZABZ_TIMEOUT_JOIN 120000
31 const char *_init_commands[] =
59 static void _twr_cmwx1zzabz_task(
void *param);
63 static void _twr_cmwx1zzabz_save_config(
twr_cmwx1zzabz_t *
self, twr_cmwx1zzabz_config_index_t config_index);
69 memset(
self, 0,
sizeof(*
self));
71 self->_uart_channel = uart_channel;
74 twr_fifo_init(&self->_tx_fifo, self->_tx_fifo_buffer,
sizeof(self->_tx_fifo_buffer));
75 twr_fifo_init(&self->_rx_fifo, self->_rx_fifo_buffer,
sizeof(self->_rx_fifo_buffer));
83 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
99 self->_custom_command =
false;
100 self->_link_check_command =
false;
101 self->_join_command =
false;
104 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
116 self->_state = TWR_CMWX1ZZABZ_STATE_RECEIVE;
122 self->_event_handler = event_handler;
123 self->_event_param = event_param;
128 if (self->_state == TWR_CMWX1ZZABZ_STATE_IDLE)
143 self->_message_length = length;
145 memcpy(self->_message_buffer, buffer, self->_message_length);
147 self->_state = TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND;
161 self->_message_length = length;
163 memcpy(self->_message_buffer, buffer, self->_message_length);
165 self->_state = TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND;
174 self->_debug = debug;
177 static size_t _twr_cmwx1zzabz_async_write(
twr_cmwx1zzabz_t *
self,
const void *buffer,
size_t length)
189 static size_t _twr_cmwx1zzabz_write(
twr_cmwx1zzabz_t *
self,
const void *buffer,
size_t length)
201 static void _twr_cmwx1zzabz_task(
void *param)
207 switch (self->_state)
209 case TWR_CMWX1ZZABZ_STATE_READY:
211 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
213 if (self->_event_handler != NULL)
220 case TWR_CMWX1ZZABZ_STATE_IDLE:
222 if (self->_save_config_mask != 0)
224 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
228 if(self->_join_command)
230 self->_state = TWR_CMWX1ZZABZ_STATE_JOIN_SEND;
234 if(self->_custom_command)
236 self->_state = TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_SEND;
240 if(self->_link_check_command)
242 self->_state = TWR_CMWX1ZZABZ_STATE_LINK_CHECK_SEND;
248 case TWR_CMWX1ZZABZ_STATE_RECEIVE:
250 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
252 while (_twr_cmwx1zzabz_read_response(
self))
254 if (memcmp(self->_response,
"+RECV=", 5) == 0)
256 self->_message_port = atoi(&self->_response[6]);
258 char *comma_search = strchr(self->_response,
',');
265 self->_message_length = atoi(++comma_search);
276 if (self->_message_length >
sizeof(self->_message_buffer))
282 bytes =
twr_uart_async_read(self->_uart_channel, self->_message_buffer, self->_message_length);
283 if (bytes != self->_message_length)
288 if (self->_event_handler != NULL)
293 else if (memcmp(self->_response,
"+ACK", 4) == 0)
295 if (self->_event_handler != NULL)
300 else if (memcmp(self->_response,
"+NOACK", 4) == 0)
302 if (self->_event_handler != NULL)
307 else if (memcmp(self->_response,
"+EVENT=2,2", 10) == 0)
309 if (self->_event_handler != NULL)
317 else if (memcmp(self->_response,
"+EVENT=0,1", 10) == 0 || memcmp(self->_response,
"+EVENT=0,0", 10) == 0)
319 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
320 self->_save_config_mask = 0;
322 if (self->_event_handler != NULL)
324 self->_event_handler(
self, TWR_CMWX1ZZABZ_EVENT_MODEM_FACTORY_RESET, self->_event_param);
331 case TWR_CMWX1ZZABZ_STATE_ERROR:
333 self->_save_config_mask = 0;
335 if (self->_event_handler != NULL)
340 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
344 case TWR_CMWX1ZZABZ_STATE_INITIALIZE:
346 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
347 self->_init_command_index = 0;
353 char cmd_at[] =
"AT\r";
354 size_t length = strlen(cmd_at);
356 _twr_cmwx1zzabz_write(
self, cmd_at, length);
365 if (_twr_cmwx1zzabz_async_write(
self, cmd_at, length) != length)
370 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_AT_RESPONSE;
376 case TWR_CMWX1ZZABZ_STATE_INITIALIZE_AT_RESPONSE:
378 _twr_cmwx1zzabz_read_response(
self);
380 if (strcmp(self->_response,
"+OK\r") == 0)
387 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
396 self->_state = TWR_CMWX1ZZABZ_STATE_RECOVER_BAUDRATE_UART;
400 case TWR_CMWX1ZZABZ_STATE_RECOVER_BAUDRATE_UART:
402 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
415 char cmd_at[] =
"AT\r";
416 _twr_cmwx1zzabz_write(
self, cmd_at, strlen(cmd_at));
422 char cmd_at_baud[] =
"AT+UART=9600\r";
423 _twr_cmwx1zzabz_write(
self, cmd_at_baud, strlen(cmd_at_baud));
429 char cmd_at_reboot[] =
"AT+REBOOT\r";
430 _twr_cmwx1zzabz_write(
self, cmd_at_reboot, strlen(cmd_at_reboot));
440 twr_fifo_init(&self->_tx_fifo, self->_tx_fifo_buffer,
sizeof(self->_tx_fifo_buffer));
441 twr_fifo_init(&self->_rx_fifo, self->_rx_fifo_buffer,
sizeof(self->_rx_fifo_buffer));
447 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
453 case TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND:
455 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
460 strcpy(self->_command, _init_commands[self->_init_command_index]);
461 size_t length = strlen(self->_command);
463 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
468 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_RESPONSE;
471 if(strcmp(self->_command,
"AT+REBOOT\r") == 0)
483 case TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_RESPONSE:
485 if (!_twr_cmwx1zzabz_read_response(
self))
488 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_CUSTOM_COMMAND_RESPONSE))
490 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
495 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
496 uint8_t response_handled = 0;
499 uint32_t response_valid = (memcmp(self->_response,
"+OK=", 4) == 0);
501 const char *last_command = _init_commands[
self->_init_command_index];
503 char *response_string_value = &
self->_response[4];
505 if (strcmp(last_command,
"AT+DEVADDR?\r") == 0 && response_valid)
508 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR) == 0)
510 memcpy(self->_config.devaddr, response_string_value, 8);
511 self->_config.devaddr[8] =
'\0';
513 response_handled = 1;
515 else if (strcmp(last_command,
"AT+DEVEUI?\r") == 0 && response_valid)
517 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI) == 0)
519 memcpy(self->_config.deveui, response_string_value, 16);
520 self->_config.deveui[16] =
'\0';
522 response_handled = 1;
524 else if (strcmp(last_command,
"AT+APPEUI?\r") == 0 && response_valid)
526 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_APPEUI) == 0)
528 memcpy(self->_config.appeui, response_string_value, 16);
529 self->_config.appeui[16] =
'\0';
531 response_handled = 1;
533 else if (strcmp(last_command,
"AT+NWKSKEY?\r") == 0 && response_valid)
535 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY) == 0)
537 memcpy(self->_config.nwkskey, response_string_value, 32);
538 self->_config.nwkskey[32] =
'\0';
540 response_handled = 1;
542 else if (strcmp(last_command,
"AT+APPSKEY?\r") == 0 && response_valid)
544 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY) == 0)
546 memcpy(self->_config.appskey, response_string_value, 32);
547 self->_config.appskey[32] =
'\0';
549 response_handled = 1;
551 else if (strcmp(last_command,
"AT+APPKEY?\r") == 0 && response_valid)
553 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_APPKEY) == 0)
555 memcpy(self->_config.appkey, response_string_value, 32);
556 self->_config.appkey[32] =
'\0';
558 response_handled = 1;
560 else if (strcmp(last_command,
"AT+BAND?\r") == 0 && response_valid)
562 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_BAND) == 0)
564 self->_config.band = response_string_value[0] -
'0';
566 response_handled = 1;
568 else if (strcmp(last_command,
"AT+MODE?\r") == 0 && response_valid)
570 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_MODE) == 0)
572 self->_config.mode = response_string_value[0] -
'0';
574 response_handled = 1;
576 else if (strcmp(last_command,
"AT+CLASS?\r") == 0 && response_valid)
578 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_CLASS) == 0)
580 self->_config.class = response_string_value[0] -
'0';
582 response_handled = 1;
584 else if (strcmp(last_command,
"AT+RX2?\r") == 0 && response_valid)
586 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_RX2) == 0)
588 self->_config.rx2_frequency = atoi(response_string_value);
590 char *comma_search = strchr(response_string_value,
',');
596 self->_config.rx2_datarate = atoi(++comma_search);
598 response_handled = 1;
600 else if (strcmp(last_command,
"AT+NWK?\r") == 0 && response_valid)
602 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_NWK) == 0)
604 self->_config.nwk_public = response_string_value[0] -
'0';
606 response_handled = 1;
608 else if (strcmp(last_command,
"AT+ADR?\r") == 0 && response_valid)
610 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_ADAPTIVE_DATARATE) == 0)
612 self->_config.adaptive_datarate = response_string_value[0] ==
'1';
614 response_handled = 1;
616 else if (strcmp(last_command,
"AT+DR?\r") == 0 && response_valid)
618 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_DATARATE) == 0)
620 self->_config.datarate = atoi(response_string_value);
622 response_handled = 1;
624 else if (strcmp(last_command,
"AT+REP?\r") == 0 && response_valid)
626 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_REP) == 0)
628 self->_config.repetition_unconfirmed = atoi(response_string_value);
630 response_handled = 1;
632 else if (strcmp(last_command,
"AT+RTYNUM?\r") == 0 && response_valid)
634 if ((self->_save_config_mask & 1 << TWR_CMWX1ZZABZ_CONFIG_INDEX_RTYNUM) == 0)
636 self->_config.repetition_confirmed = atoi(response_string_value);
638 response_handled = 1;
640 else if (strcmp(last_command,
"AT+DUTYCYCLE=0\r") == 0 && strcmp(self->_response,
"+ERR=-17\r") == 0)
643 response_handled = 1;
645 else if (strcmp(last_command,
"AT+DWELL=0,0\r") == 0 && strcmp(self->_response,
"+ERR=-17\r") == 0)
648 response_handled = 1;
650 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))
656 response_handled = 1;
658 else if (strcmp(last_command,
"AT+VER?\r") == 0 && memcmp(self->_response,
"+OK=", 4) == 0)
662 char *comma = strchr(self->_response,
',');
665 char *first_character = &
self->_response[4];
666 memset(self->_fw_version,
'\0',
sizeof(self->_fw_version));
667 int len = comma - first_character;
668 memcpy(self->_fw_version, first_character, len);
670 response_handled = 1;
672 else if ( strcmp(last_command,
"AT\r") == 0 &&
673 strcmp(self->_response,
"+OK\r") == 0
676 response_handled = 1;
679 else if (memcmp(self->_response,
"+OK", 3) == 0)
681 response_handled = 1;
684 if (!response_handled)
689 self->_init_command_index++;
691 if (_init_commands[self->_init_command_index] == NULL)
694 if (self->_save_config_mask)
696 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
697 self->_save_command_index = 0;
701 self->_state = TWR_CMWX1ZZABZ_STATE_READY;
706 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE_COMMAND_SEND;
711 case TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_COMMAND:
712 case TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND:
714 if (self->_state == TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_CONFIRMED_COMMAND)
716 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+PCTX %d,%d\r", self->_tx_port, self->_message_length);
720 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+PUTX %d,%d\r", self->_tx_port, self->_message_length);
723 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
725 uint8_t command_length = strlen(self->_command);
727 for (
size_t i = 0; i <
self->_message_length; i++)
730 self->_command[command_length + i] =
self->_message_buffer[i];
733 self->_command[command_length +
self->_message_length] =
'\r';
735 size_t length = command_length +
self->_message_length + 1;
737 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
742 self->_state = TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE;
744 if (self->_event_handler != NULL)
753 case TWR_CMWX1ZZABZ_STATE_SEND_MESSAGE_RESPONSE:
755 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
757 if (!_twr_cmwx1zzabz_read_response(
self))
762 if (strcmp(self->_response,
"+OK\r") != 0)
767 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
769 if (self->_event_handler != NULL)
776 case TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND:
778 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
781 if (self->_save_config_mask == 0)
783 if (self->_event_handler != NULL)
788 self->_state = TWR_CMWX1ZZABZ_STATE_READY;
793 for (uint8_t i = 0; i < TWR_CMWX1ZZABZ_CONFIG_INDEX_LAST_ITEM; i++)
795 if (self->_save_config_mask & 1 << i)
797 self->_save_command_index = i;
805 switch (self->_save_command_index)
807 case TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR:
809 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+DEVADDR=%s\r", self->_config.devaddr);
812 case TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI:
814 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+DEVEUI=%s\r", self->_config.deveui);
817 case TWR_CMWX1ZZABZ_CONFIG_INDEX_APPEUI:
819 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+APPEUI=%s\r", self->_config.appeui);
822 case TWR_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY:
824 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+NWKSKEY=%s\r", self->_config.nwkskey);
827 case TWR_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY:
829 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+APPSKEY=%s\r", self->_config.appskey);
832 case TWR_CMWX1ZZABZ_CONFIG_INDEX_APPKEY:
834 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+APPKEY=%s\r", self->_config.appkey);
837 case TWR_CMWX1ZZABZ_CONFIG_INDEX_BAND:
839 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+BAND=%d\r", self->_config.band);
842 case TWR_CMWX1ZZABZ_CONFIG_INDEX_MODE:
844 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+MODE=%d\r", self->_config.mode);
847 case TWR_CMWX1ZZABZ_CONFIG_INDEX_CLASS:
849 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+CLASS=%d\r", self->_config.class);
852 case TWR_CMWX1ZZABZ_CONFIG_INDEX_RX2:
854 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+RX2=%d,%d\r", (
int) self->_config.rx2_frequency, self->_config.rx2_datarate);
857 case TWR_CMWX1ZZABZ_CONFIG_INDEX_NWK:
859 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+NWK=%d\r", (
int) self->_config.nwk_public);
862 case TWR_CMWX1ZZABZ_CONFIG_INDEX_ADAPTIVE_DATARATE:
864 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+ADR=%d\r", self->_config.adaptive_datarate ? 1 : 0);
867 case TWR_CMWX1ZZABZ_CONFIG_INDEX_DATARATE:
869 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+DR=%d\r", (
int) self->_config.datarate);
872 case TWR_CMWX1ZZABZ_CONFIG_INDEX_REP:
874 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+REP=%d\r", (
int) self->_config.repetition_unconfirmed);
877 case TWR_CMWX1ZZABZ_CONFIG_INDEX_RTYNUM:
879 snprintf(self->_command, TWR_CMWX1ZZABZ_TX_FIFO_BUFFER_SIZE,
"AT+RTYNUM=%d\r", (
int) self->_config.repetition_confirmed);
889 size_t length = strlen(self->_command);
891 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
896 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE;
901 case TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_RESPONSE:
903 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
905 if (!_twr_cmwx1zzabz_read_response(
self))
911 if (memcmp(self->_response,
"+OK", 3) != 0)
917 self->_save_config_mask &= ~(1 <<
self->_save_command_index);
919 self->_state = TWR_CMWX1ZZABZ_STATE_CONFIG_SAVE_SEND;
924 case TWR_CMWX1ZZABZ_STATE_JOIN_SEND:
926 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
931 strcpy(self->_command,
"AT+JOIN\r");
933 size_t length = strlen(self->_command);
934 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
940 self->_join_command =
false;
942 self->_state = TWR_CMWX1ZZABZ_STATE_JOIN_RESPONSE;
948 case TWR_CMWX1ZZABZ_STATE_JOIN_RESPONSE:
950 bool join_successful =
false;
952 if (!_twr_cmwx1zzabz_read_response(
self))
955 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_JOIN))
957 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
962 if (memcmp(self->_response,
"+OK", 3) == 0)
969 if (memcmp(self->_response,
"+ERR=-7", 7) == 0)
971 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
976 if (memcmp(self->_response,
"+EVENT=1,1", 10) == 0)
978 join_successful =
true;
983 if (self->_event_handler != NULL)
990 if (self->_event_handler != NULL)
996 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1000 case TWR_CMWX1ZZABZ_STATE_LINK_CHECK_SEND:
1002 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1007 strcpy(self->_command,
"AT+LNCHECK\r");
1009 size_t length = strlen(self->_command);
1010 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
1015 self->_link_check_command =
false;
1017 self->_state = TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE;
1023 case TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE:
1025 if (!_twr_cmwx1zzabz_read_response(
self))
1028 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_LNCHECK))
1030 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1035 if (memcmp(self->_response,
"+OK", 3) == 0)
1042 if (strstr(self->_response,
"+EVENT=2,") > (
char *)0)
1045 char *event_str = strchr(self->_response,
',');
1048 uint8_t link_check_event = atoi(event_str);
1051 self->_cmd_link_check_gwcnt = 0;
1052 self->_cmd_link_check_margin = 0;
1054 if (link_check_event == 1)
1056 self->_state = TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE_ANS;
1064 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1068 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1070 if (self->_event_handler != NULL)
1072 self->_event_handler(
self, TWR_CMWX1ZZABZ_EVENT_LINK_CHECK_NOK, self->_event_param);
1079 case TWR_CMWX1ZZABZ_STATE_LINK_CHECK_RESPONSE_ANS:
1082 if (_twr_cmwx1zzabz_read_response(
self))
1085 char *ans = strstr(self->_response,
"+ANS=2,");
1090 self->_cmd_link_check_margin = atoi(ans);
1093 char *gwcnt_str = strchr(ans,
',');
1096 self->_cmd_link_check_gwcnt = atoi(gwcnt_str);
1100 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1102 if (self->_event_handler != NULL)
1104 self->_event_handler(
self, TWR_CMWX1ZZABZ_EVENT_LINK_CHECK_OK, self->_event_param);
1111 case TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_SEND:
1113 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1118 strcpy(self->_command, self->_custom_command_buf);
1120 size_t length = strlen(self->_command);
1121 if (_twr_cmwx1zzabz_async_write(
self, self->_command, length) != length)
1126 self->_state = TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_RESPONSE;
1132 case TWR_CMWX1ZZABZ_STATE_CUSTOM_COMMAND_RESPONSE:
1134 if (!_twr_cmwx1zzabz_read_response(
self))
1137 if (
twr_tick_get() > (self->_timeout + TWR_CMWX1ZZABZ_TIMEOUT_CUSTOM_COMMAND_RESPONSE))
1139 self->_custom_command =
false;
1140 self->_state = TWR_CMWX1ZZABZ_STATE_ERROR;
1142 if (strcmp(self->_command,
"AT+FACNEW\r") == 0)
1145 self->_state = TWR_CMWX1ZZABZ_STATE_INITIALIZE;
1152 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1153 self->_custom_command =
false;
1157 if (memcmp(self->_response,
"+ERR=-1", 7) == 0)
1159 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1160 self->_cmd_frmcnt_uplink = 0;
1161 self->_cmd_frmcnt_downlink = 0;
1165 if (memcmp(self->_response,
"+OK=", 4) == 0)
1172 if (strcmp(self->_custom_command_buf,
"AT+RFQ?\r") == 0)
1175 char *rssi_str = strchr(self->_response,
'=');
1178 char *snr_str = strchr(self->_response,
',');
1181 self->_cmd_rfq_rssi = atoi(rssi_str);
1182 self->_cmd_rfq_snr = atoi(snr_str);
1186 if (strcmp(self->_custom_command_buf,
"AT+FRMCNT?\r") == 0)
1189 char *uplink_str = strchr(self->_response,
'=');
1192 char *downlink_str = strchr(self->_response,
',');
1195 self->_cmd_frmcnt_uplink = atoi(uplink_str);
1196 self->_cmd_frmcnt_downlink = atoi(downlink_str);
1200 self->_state = TWR_CMWX1ZZABZ_STATE_IDLE;
1202 if (self->_event_handler != NULL)
1204 self->_event_handler(
self, event, self->_event_param);
1212 case TWR_CMWX1ZZABZ_STATE_RECOVER_BAUDRATE_REBOOT:
1228 self->_join_command =
true;
1234 self->_tx_port = port;
1239 return self->_tx_port;
1244 strncpy(self->_config.devaddr, devaddr, 8+1);
1246 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVADDR);
1251 strncpy(devaddr, self->_config.devaddr, 8+1);
1256 strncpy(self->_config.deveui, deveui, 16+1);
1258 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_DEVEUI);
1263 strncpy(deveui, self->_config.deveui, 16+1);
1268 strncpy(self->_config.appeui, appeui, 16+1);
1270 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_APPEUI);
1275 strncpy(appeui, self->_config.appeui, 16+1);
1280 strncpy(self->_config.nwkskey, nwkskey, 32);
1282 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_NWKSKEY);
1287 strncpy(nwkskey, self->_config.nwkskey, 32+1);
1292 strncpy(self->_config.appskey, appskey, 32);
1294 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_APPSKEY);
1299 strncpy(appskey, self->_config.appskey, 32+1);
1304 strncpy(self->_config.appkey, appkey, 32+1);
1306 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_APPKEY);
1311 strncpy(appkey, self->_config.appkey, 32+1);
1316 self->_config.band = band;
1318 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_BAND);
1323 return self->_config.band;
1328 self->_config.mode = mode;
1330 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_MODE);
1335 return self->_config.mode;
1340 self->_config.class =
class;
1342 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_CLASS);
1347 return self->_config.class;
1352 return self->_message_port;
1357 return self->_message_length;
1362 if (self->_message_length > buffer_size)
1367 memcpy(buffer, self->_message_buffer, self->_message_length);
1369 return self->_message_length;
1374 self->_config.rx2_frequency = frequency;
1376 self->_config.rx2_datarate = datarate;
1378 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_RX2);
1383 *frequency =
self->_config.rx2_frequency;
1384 *datarate =
self->_config.rx2_datarate;
1389 self->_config.nwk_public =
public;
1391 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_NWK);
1396 return self->_config.nwk_public;
1401 self->_config.adaptive_datarate = enable;
1403 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_ADAPTIVE_DATARATE);
1408 return self->_config.adaptive_datarate;
1413 self->_config.datarate = datarate;
1415 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_DATARATE);
1420 return self->_config.datarate;
1425 self->_config.repetition_unconfirmed = repeat;
1427 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_REP);
1432 return self->_config.repetition_unconfirmed;
1437 self->_config.repetition_confirmed = repeat;
1439 _twr_cmwx1zzabz_save_config(
self, TWR_CMWX1ZZABZ_CONFIG_INDEX_RTYNUM);
1444 return self->_config.repetition_confirmed;
1449 return self->_command;
1454 return self->_response;
1459 if (self->_custom_command)
1464 self->_custom_command =
true;
1465 snprintf(self->_custom_command_buf,
sizeof(self->_custom_command_buf),
"%s\r", at_command);
1474 if (self->_custom_command)
1479 self->_custom_command =
true;
1480 strcpy(self->_custom_command_buf,
"AT+RFQ?\r");
1489 *rssi =
self->_cmd_rfq_rssi;
1490 *snr =
self->_cmd_rfq_snr;
1497 if (self->_custom_command)
1502 self->_custom_command =
true;
1503 strcpy(self->_custom_command_buf,
"AT+FRMCNT?\r");
1512 *uplink =
self->_cmd_frmcnt_uplink;
1513 *downlink =
self->_cmd_frmcnt_downlink;
1520 if (self->_custom_command)
1525 self->_custom_command =
true;
1526 strcpy(self->_custom_command_buf,
"AT+FACNEW\r");
1535 return self->_fw_version;
1540 if (self->_state != TWR_CMWX1ZZABZ_STATE_IDLE)
1545 self->_link_check_command =
true;
1554 *margin =
self->_cmd_link_check_margin;
1555 *gateway_count =
self->_cmd_link_check_gwcnt;
1571 if (rx_character ==
'\n')
1576 self->_response[
self->_response_length++] = rx_character;
1578 if (rx_character ==
'\r')
1580 if (self->_response_length == 1)
1582 self->_response_length = 0;
1587 self->_response[
self->_response_length] =
'\0';
1592 if (self->_response_length ==
sizeof(self->_response) - 1)
1603 self->_response_length = 0;
1608 static void _twr_cmwx1zzabz_save_config(
twr_cmwx1zzabz_t *
self, twr_cmwx1zzabz_config_index_t config_index)
1610 self->_save_config_mask |= 1 << config_index;
1612 if (self->_state == TWR_CMWX1ZZABZ_STATE_IDLE)
void twr_cmwx1zzabz_set_adaptive_datarate(twr_cmwx1zzabz_t *self, bool enable)
Set the configuration adaptive data rate.
bool twr_cmwx1zzabz_link_check(twr_cmwx1zzabz_t *self)
Request send of link check packet.
void twr_cmwx1zzabz_set_appeui(twr_cmwx1zzabz_t *self, char *appeui)
Set APPEUI.
void twr_cmwx1zzabz_get_deveui(twr_cmwx1zzabz_t *self, char *deveui)
Get DEVEUI.
void twr_cmwx1zzabz_init(twr_cmwx1zzabz_t *self, twr_uart_channel_t uart_channel)
Initialize CMWX1ZZABZ.
char * twr_cmwx1zzabz_get_error_command(twr_cmwx1zzabz_t *self)
Get pointer to string containg last sent command.
uint8_t twr_cmwx1zzabz_get_nwk_public(twr_cmwx1zzabz_t *self)
Get the configuration if public networks are enabled.
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_nwkskey(twr_cmwx1zzabz_t *self, char *nwkskey)
Set NWKSKEY.
void twr_cmwx1zzabz_get_appkey(twr_cmwx1zzabz_t *self, char *appkey)
Get APPKEY.
uint8_t twr_cmwx1zzabz_get_datarate(twr_cmwx1zzabz_t *self)
Get the configuration of datarate.
bool twr_cmwx1zzabz_send_message(twr_cmwx1zzabz_t *self, const void *buffer, size_t length)
Send LoRa message.
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.
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.
bool twr_cmwx1zzabz_is_ready(twr_cmwx1zzabz_t *self)
Check if modem is ready for commands.
bool twr_cmwx1zzabz_get_rfq(twr_cmwx1zzabz_t *self, int32_t *rssi, int32_t *snr)
Get RF quality values in callback.
void twr_cmwx1zzabz_set_band(twr_cmwx1zzabz_t *self, twr_cmwx1zzabz_config_band_t band)
Set BAND.
void twr_cmwx1zzabz_set_repeat_unconfirmed(twr_cmwx1zzabz_t *self, uint8_t repeat)
Set number of transmissions of unconfirmed message.
bool twr_cmwx1zzabz_frame_counter(twr_cmwx1zzabz_t *self)
Request frame counter value.
twr_cmwx1zzabz_config_mode_t
LoRa mode ABP/OTAA.
bool twr_cmwx1zzabz_get_link_check(twr_cmwx1zzabz_t *self, uint8_t *margin, uint8_t *gateway_count)
Get link check values.
void twr_cmwx1zzabz_set_devaddr(twr_cmwx1zzabz_t *self, char *devaddr)
Set DEVADDR.
void twr_cmwx1zzabz_join(twr_cmwx1zzabz_t *self)
Start LoRa OTAA join procedure.
char * twr_cmwx1zzabz_get_fw_version(twr_cmwx1zzabz_t *self)
Get firmware version string.
twr_cmwx1zzabz_config_band_t twr_cmwx1zzabz_get_band(twr_cmwx1zzabz_t *self)
Get BAND.
void twr_cmwx1zzabz_set_port(twr_cmwx1zzabz_t *self, uint8_t port)
Set the port for the transmission of the messages.
void twr_cmwx1zzabz_get_devaddr(twr_cmwx1zzabz_t *self, char *devaddr)
Get DEVADDR.
bool twr_cmwx1zzabz_rfq(twr_cmwx1zzabz_t *self)
Request RF quality of the last received packet (JOIN, LNPARAM, confirmed message)
void twr_cmwx1zzabz_set_class(twr_cmwx1zzabz_t *self, twr_cmwx1zzabz_config_class_t class)
Set device class.
uint32_t twr_cmwx1zzabz_get_received_message_data(twr_cmwx1zzabz_t *self, uint8_t *buffer, uint32_t buffer_size)
Get received message data.
twr_cmwx1zzabz_config_class_t
LoRa device class A or C.
void twr_cmwx1zzabz_reboot(twr_cmwx1zzabz_t *self)
Reboot and initialize CMWX1ZZABZ.
void twr_cmwx1zzabz_get_appskey(twr_cmwx1zzabz_t *self, char *appskey)
Get APPSKEY.
uint32_t twr_cmwx1zzabz_get_received_message_length(twr_cmwx1zzabz_t *self)
Get length of the received message.
void twr_cmwx1zzabz_set_nwk_public(twr_cmwx1zzabz_t *self, uint8_t public)
Set the configuration enabling public networks.
void twr_cmwx1zzabz_set_deveui(twr_cmwx1zzabz_t *self, char *deveui)
Set DEVEUI.
twr_cmwx1zzabz_config_band_t
Frequency modes and standards.
uint8_t twr_cmwx1zzabz_get_port(twr_cmwx1zzabz_t *self)
Get the port for the transmission of the messages.
uint8_t twr_cmwx1zzabz_get_repeat_confirmed(twr_cmwx1zzabz_t *self)
Get number of transmissions of confirmed message.
bool twr_cmwx1zzabz_custom_at(twr_cmwx1zzabz_t *self, char *at_command)
Send custom AT command to LoRa Module.
void twr_cmwx1zzabz_deinit(twr_cmwx1zzabz_t *self)
Deinitialize CMWX1ZZABZ.
uint8_t twr_cmwx1zzabz_get_repeat_unconfirmed(twr_cmwx1zzabz_t *self)
Get number of transmissions of unconfirmed message.
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.
void twr_cmwx1zzabz_set_appskey(twr_cmwx1zzabz_t *self, char *appskey)
Set APPSKEY.
twr_cmwx1zzabz_config_mode_t twr_cmwx1zzabz_get_mode(twr_cmwx1zzabz_t *self)
Get ABP/OTAA mode.
void twr_cmwx1zzabz_get_appeui(twr_cmwx1zzabz_t *self, char *appeui)
Get APPEUI.
bool twr_cmwx1zzabz_get_adaptive_datarate(twr_cmwx1zzabz_t *self)
Get the configuration if adaptive data rate are enabled.
twr_cmwx1zzabz_config_class_t twr_cmwx1zzabz_get_class(twr_cmwx1zzabz_t *self)
Get device class.
bool twr_cmwx1zzabz_factory_reset(twr_cmwx1zzabz_t *self)
Send factory reset command to LoRa Module.
struct twr_cmwx1zzabz_t twr_cmwx1zzabz_t
CMWX1ZZABZ instance.
bool twr_cmwx1zzabz_get_frame_counter(twr_cmwx1zzabz_t *self, uint32_t *uplink, uint32_t *downlink)
Get frame counter value.
void twr_cmwx1zzabz_get_rx2(twr_cmwx1zzabz_t *self, uint32_t *frequency, uint8_t *datarate)
Get the frequency and datarate for RX2 receive window.
bool twr_cmwx1zzabz_send_message_confirmed(twr_cmwx1zzabz_t *self, const void *buffer, size_t length)
Send LoRa confirmed message.
void twr_cmwx1zzabz_set_mode(twr_cmwx1zzabz_t *self, twr_cmwx1zzabz_config_mode_t mode)
Set ABP/OTAA mode.
char * twr_cmwx1zzabz_get_error_response(twr_cmwx1zzabz_t *self)
Get pointer to string containg response on last sent command.
uint8_t twr_cmwx1zzabz_get_received_message_port(twr_cmwx1zzabz_t *self)
Get port of the received message.
twr_cmwx1zzabz_event_t
Callback events.
void twr_cmwx1zzabz_set_repeat_confirmed(twr_cmwx1zzabz_t *self, uint8_t repeat)
Set number of transmissions of confirmed message.
@ TWR_CMWX1ZZABZ_EVENT_ERROR
Error event.
@ TWR_CMWX1ZZABZ_EVENT_CONFIG_SAVE_DONE
Configuration save done.
@ TWR_CMWX1ZZABZ_EVENT_MESSAGE_RECEIVED
Received message.
@ TWR_CMWX1ZZABZ_EVENT_FRAME_COUNTER
Frame counter response.
@ TWR_CMWX1ZZABZ_EVENT_SEND_MESSAGE_DONE
RF frame transmission finished event.
@ TWR_CMWX1ZZABZ_EVENT_READY
Ready event.
@ TWR_CMWX1ZZABZ_EVENT_MESSAGE_NOT_CONFIRMED
Sent message not confirmed.
@ TWR_CMWX1ZZABZ_EVENT_MESSAGE_CONFIRMED
Sent message confirmed.
@ TWR_CMWX1ZZABZ_EVENT_JOIN_ERROR
OTAA join error.
@ TWR_CMWX1ZZABZ_EVENT_SEND_MESSAGE_START
RF frame transmission started event.
@ TWR_CMWX1ZZABZ_EVENT_JOIN_SUCCESS
OTAA join success.
@ TWR_CMWX1ZZABZ_EVENT_RFQ
RF quality response.
@ TWR_CMWX1ZZABZ_EVENT_MESSAGE_RETRANSMISSION
Retransmission of the confirmed message.
void twr_fifo_purge(twr_fifo_t *fifo)
Purge FIFO buffer.
void twr_fifo_init(twr_fifo_t *fifo, void *buffer, size_t size)
Initialize FIFO buffer.
void void twr_log_debug(const char *format,...) __attribute__((format(printf
Log DEBUG message (annotated in log as <D>)
void twr_scheduler_plan_current_from_now(twr_tick_t tick)
Schedule current task to tick relative from now.
void twr_scheduler_unregister(twr_scheduler_task_id_t task_id)
Unregister specified task.
void twr_scheduler_plan_now(twr_scheduler_task_id_t task_id)
Schedule specified task for immediate execution.
void twr_scheduler_plan_current_now(void)
Schedule current task for immediate execution.
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.
twr_scheduler_task_id_t twr_scheduler_register(void(*task)(void *), void *param, twr_tick_t tick)
Register task in scheduler.
#define TWR_TICK_INFINITY
Maximum timestamp value.
twr_tick_t twr_tick_get(void)
Get absolute timestamp since start of program.
void twr_timer_init(void)
Initialize timer.
void twr_timer_delay(uint16_t microseconds)
Relative delay.
void twr_timer_stop(void)
Stop timer.
void twr_timer_start(void)
Start timer.
twr_uart_channel_t
UART channels.
void twr_uart_init(twr_uart_channel_t channel, twr_uart_baudrate_t baudrate, twr_uart_setting_t setting)
Initialize UART channel.
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.
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.
size_t twr_uart_write(twr_uart_channel_t channel, const void *buffer, size_t length)
Write data to UART channel (blocking call)
bool twr_uart_async_read_start(twr_uart_channel_t channel, twr_tick_t timeout)
Start async reading.
twr_uart_event_t
Callback events.
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.
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_uart_deinit(twr_uart_channel_t channel)
Deinitialize UART channel.
@ TWR_UART_EVENT_ASYNC_READ_DATA
Event is reading done.
@ TWR_UART_BAUDRATE_19200
UART baudrat 19200 bps.
@ TWR_UART_BAUDRATE_9600
UART baudrat 9600 bps.
@ TWR_UART_SETTING_8N1
8N1: 8 data bits, none parity bit, 1 stop bit