Hi,
I wonder if anybody could help me? What is the best way to close down the nrf9161 so that it reconnects quickly after powering back up again?
I'm running Zephyr 2.6 on a custom board we are developing.
I've seen a lot of chatter about the modem saving settings (e.g. by calling nrf_modem_lib_shutdown) - but it doesn't seem to work. If I shut the system down "mid-connection" it seems to reconnect quickly - although it does then seem to suffer from the , However, if I let it shut down "gracefully", it can take an age to reconnect.
Here is my "main" loop - the system connects, works for a while and then, when finished, powers down the modem and sleeps forever so that it can be powered off.
#include "main.h" LOG_MODULE_REGISTER(nrf_whisper1, LOG_LEVEL_INF); // Device details static wcp_display_settings disp_settings; int main(void) { int err; bool modem_connected = false; LOG_INF("Stating nrf_whisper, version: %s", CONFIG_APP_VERSION); : nrf_modem_lib_dfu_handler(); // Get hardware settings off modem update_imei (&disp_settings); set_default_coap_addresses (&disp_settings); LOG_INF("Configure Modem"); err = modem_connect(); if (err) { LOG_ERR("Failed to configure the modem"); return 0; } LOG_INF("Resolve Server"); int res = 1; while (res != 0) { res = server_resolve(); if (res != 0) { LOG_INF("Failed to resolve server name - retry in 1 second"); k_msleep(1000); } } // If we get here - LTE is already connected, so don't need to reconnect in toop. modem_connected = true; init_display (); disp_settings.poll_counter = 1; bool init_cmd_run = false; while (disp_settings.poll_counter != 0) { if (disp_settings.poll_counter > 0) { disp_settings.poll_counter--; } if (disp_settings.input_type != UART_INPUT_TYPE) { // If LTE isn't already connected - connect up if (!modem_connected) { LOG_INF("Connect to Modem"); err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_NORMAL); if (err != 0){ LOG_ERR("Failed to activate LTE"); break; } LOG_INF("Waiting for LTE connection"); // Wait until the modem is connected to the network until continuing wait_for_modem_connection (); modem_connected = true; } } // Update the current time update_time (&disp_settings); if ((disp_settings.input_type != UART_INPUT_TYPE) && (client_init(false)) != 0) { LOG_INF("Failed to initialize client"); } else { if (!init_cmd_run) { run_template_from_file (&disp_settings, "init_cmd.tmp", 1); init_cmd_run = true; } handle_messages(&disp_settings); send_messages (&disp_settings); disconnect_coap_session (&disp_settings); update_pic (); } if (disp_settings.poll_counter != 0) { err = lte_lc_func_mode_set(LTE_LC_FUNC_MODE_DEACTIVATE_LTE); if (err != 0){ LOG_ERR("Failed to decativate LTE and enable GNSS functional mode"); break; } modem_connected = false; LOG_INF("Pause for %d seconds", disp_settings.polling_sleep_time); k_sleep(K_SECONDS(disp_settings.polling_sleep_time)); } } if (disp_settings.poll_counter == 0) { LOG_INF("Polling disabled"); } LOG_INF("Shutting down modem"); lte_lc_offline(); lte_lc_power_off(); nrf_modem_lib_shutdown(); while (true) { LOG_INF("Sleeping"); k_sleep(K_FOREVER); } return 0; }
I have various other things going on - but this about covers the functionality I'm talking about.
Here is the "COAP" functions I use:
/* * Copyright (c) 2024: Infotec Limited * * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ #include "main.h" #include <nrf_socket.h> #include <zephyr/device.h> #include <zephyr/drivers/uart.h> LOG_MODULE_REGISTER(wcp_coap, LOG_LEVEL_INF); /* Include the header file for the CoAP library */ #include <zephyr/net/coap.h> #include <zephyr/net/tls_credentials.h> /* Define the macros for the CoAP version and message length */ #define APP_COAP_VERSION 1 #define APP_COAP_MAX_MSG_LEN 4000 #define APP_COAP_MAX_PACKET_SIZE 150 #define STILL_RECEIVING_COAP 1 /* Declare the buffer coap_buf to receive the response. */ static uint8_t coap_io_buff[APP_COAP_MAX_MSG_LEN]; static uint8_t coap_packet_buff[APP_COAP_MAX_PACKET_SIZE]; static int coap_io_offset=0; #define COAP_SERVER_DEFAUT_BASE "asset" #define COAP_SERVER_DEFAUT_COMMAND "command" #define COAP_SERVER_DEFAUT_STATUS_COMMAND "status" static char coap_command_uri[100]; static char coap_status_uri[100]; /* Define the CoAP message token next_token */ static uint16_t next_token; static int sock; static struct sockaddr_storage server; static struct coap_block_context blk_ctx; static int bulk_payload = false; K_SEM_DEFINE(wcp_lte_connected, 0, 1); // <<<< UART INPUT FOR HANDLING #define UART_DEVICE_NODE DT_CHOSEN(zephyr_shell_uart) static const struct device *const uart_dev = DEVICE_DT_GET(UART_DEVICE_NODE); static int rx_buf_pos = 0; // >>>> static void coap_lte_handler(const struct lte_lc_evt *const evt) { switch (evt->type) { case LTE_LC_EVT_NW_REG_STATUS: LOG_INF("Network registration status: %d", evt->nw_reg_status); if ((evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_HOME) && (evt->nw_reg_status != LTE_LC_NW_REG_REGISTERED_ROAMING)) { break; } LOG_INF("Network registration status: %s", evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "Connected - home network" : "Connected - roaming"); k_sem_give(&wcp_lte_connected); break; case LTE_LC_EVT_PSM_UPDATE: LOG_INF("PSM parameter update: TAU: %d, Active time: %d", evt->psm_cfg.tau, evt->psm_cfg.active_time); break; case LTE_LC_EVT_EDRX_UPDATE: { char log_buf[60]; ssize_t len; len = snprintf(log_buf, sizeof(log_buf), "eDRX parameter update: eDRX: %f, PTW: %f", evt->edrx_cfg.edrx, evt->edrx_cfg.ptw); if (len > 0) { LOG_INF("%s", log_buf); } break; } case LTE_LC_EVT_RRC_UPDATE: LOG_INF("RRC mode: %s", evt->rrc_mode == LTE_LC_RRC_MODE_CONNECTED ? "Connected" : "Idle"); break; case LTE_LC_EVT_CELL_UPDATE: LOG_INF("LTE cell changed: Cell ID: %d, Tracking area: %d", evt->cell.id, evt->cell.tac); break; default: break; } } static int client_get_send(wcp_display_settings *disp_settings, bool initialise_get, bool get_bulk_payload) { /* Create the CoAP message*/ struct coap_packet request; if (initialise_get) { bulk_payload = get_bulk_payload; if (bulk_payload) { coap_block_transfer_init(&blk_ctx, COAP_BLOCK_64, 0); } } next_token = sys_rand32_get(); int err = coap_packet_init(&request, coap_packet_buff, sizeof(coap_packet_buff), APP_COAP_VERSION, COAP_TYPE_CON, sizeof(next_token), (uint8_t *)&next_token, COAP_METHOD_GET, coap_next_id()); if (err < 0) { LOG_ERR("Failed to create CoAP request, %d", err); return err; } /* Add an option specifying the resource path */ err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, (uint8_t *)coap_command_uri, strlen(coap_command_uri)); if (err < 0) { LOG_ERR("Failed to encode CoAP option, %d", err); return err; } if (bulk_payload) { err = coap_append_block2_option(&request, &blk_ctx); if (err < 0) { LOG_ERR("Unable to add block2 option."); return err; } } /* Send the configured CoAP packet */ err = send(sock, request.data, request.offset, 0); if (err < 0) { LOG_ERR("Failed to send CoAP request, %d", errno); return -errno; } LOG_DBG("CoAP GET request sent: Token 0x%04x", next_token); return 0; } static int send_client_status_update(wcp_display_settings *disp_settings) { int err,ret; struct coap_packet request; int messageId = coap_next_id(); next_token++; err = coap_packet_init(&request, coap_packet_buff, sizeof(coap_packet_buff), APP_COAP_VERSION, COAP_TYPE_NON_CON, sizeof(next_token), (uint8_t *)&next_token, COAP_METHOD_PUT, messageId); if (err < 0) { LOG_ERR("Failed to create CoAP request, %d", err); return err; } err = coap_packet_append_option(&request, COAP_OPTION_URI_PATH, (uint8_t *)coap_status_uri, strlen(coap_status_uri)); if (err < 0) { LOG_ERR("Failed to encode CoAP option, %d", err); return err; } err = coap_append_option_int(&request, COAP_OPTION_CONTENT_FORMAT, COAP_CONTENT_FORMAT_TEXT_PLAIN); if (err < 0) { LOG_ERR("Failed to encode CoAP CONTENT_FORMAT option, %d", err); return err; } err = coap_packet_append_payload_marker(&request); if (err < 0) { LOG_ERR("Failed to append payload marker, %d", err); return err; } get_time(disp_settings); time_t uptime = k_uptime_get () / 1000; ret = snprintf (coap_io_buff, sizeof(coap_io_buff), "I,%d\ni,%s\nT,%s\nU,%d", messageId,disp_settings->imei,disp_settings->current_time,(int)uptime); if (err < 0) { LOG_ERR("snprintf failed to format string, %d", err); return err; } err = coap_packet_append_payload(&request, (uint8_t *)coap_io_buff, ret); if (err < 0) { LOG_ERR("Failed to append payload, %d", err); return err; } err = send(sock, request.data, request.offset, 0); if (err < 0) { LOG_ERR("Failed to send CoAP request, %d", errno); return -errno; } LOG_INF("CoAP status message sent: token 0x%04x", next_token); return 0; } /**@brief Handles responses from the remote CoAP server. */ static int client_handle_response(wcp_display_settings *disp_settings, uint8_t *buf, int received) { struct coap_packet reply; uint8_t token[8]; uint16_t token_len; const uint8_t *payload; uint16_t payload_len; uint8_t temp_buf[512]; int ret = 0; /* Parse the received CoAP packet */ int err = coap_packet_parse(&reply, buf, received, NULL, 0); if (err < 0) { LOG_ERR("Malformed response received: %d", err); return err; } /* Confirm the token in the response matches the token sent */ token_len = coap_header_get_token(&reply, token); if ((token_len != sizeof(next_token)) || (memcmp(&next_token, token, sizeof(next_token)) != 0)) { LOG_ERR("Invalid token received: 0x%02x%02x", token[1], token[0]); return 0; } /* Retrieve the payload and confirm it's nonzero */ payload = coap_packet_get_payload(&reply, &payload_len); if (payload_len > 0) { snprintf(temp_buf, MIN(payload_len+1, sizeof(temp_buf)), "%s", payload); uint16_t copy_len = payload_len; if (payload_len >= APP_COAP_MAX_MSG_LEN - coap_io_offset) { // Overflowing boffer - so handle it and then carry on copy_len = APP_COAP_MAX_MSG_LEN - coap_io_offset - 1; } memcpy(coap_io_buff + coap_io_offset, payload, copy_len); coap_io_offset += copy_len; coap_io_buff[coap_io_offset] = '\0'; if (payload_len > copy_len) { uint16_t partial_line_br = 0; for (int i = 1; i <= coap_io_offset; i++) { if (coap_io_buff[coap_io_offset - i] == '\n') { coap_io_buff[coap_io_offset - i] = '\0'; partial_line_br = i-1; break; } } LOG_INF ("OVERFLOWING BUFFER - Handle partial Commands (%d bytes loaded, %d bytes to process)", coap_io_offset, coap_io_offset - partial_line_br); handle_cmd_block (coap_io_buff, 0, disp_settings); LOG_INF ("OVERFLOWING BUFFER - Load up unused portion (%d + %d bytes) and carry on", partial_line_br, payload_len - copy_len); // Shift the remaining characters to the beginning of the buffer for (int i = 1; i <= partial_line_br; i++) { coap_io_buff[partial_line_br - i] = coap_io_buff[coap_io_offset - i]; } // Copy over the rest of the block and then carry on memcpy(coap_io_buff + partial_line_br, payload + copy_len, payload_len - copy_len); coap_io_offset = partial_line_br + payload_len - copy_len; coap_io_buff[coap_io_offset] = '\0'; } } else { strcpy(temp_buf, "EMPTY"); } if (bulk_payload) { err = coap_update_from_block(&reply, &blk_ctx); if (err < 0) { LOG_ERR("Bulk load Error: %d", ret); return err; } else { if (!coap_next_block(&reply, &blk_ctx)) { LOG_DBG("Bulk load complete"); } else { client_get_send(disp_settings, false, true); ret = STILL_RECEIVING_COAP; } } } /* Log the header code, token and payload of the response */ LOG_DBG("CoAP response: Code 0x%x, Token 0x%02x%02x, Payload: %s", coap_header_get_code(&reply), token[1], token[0], (char *)temp_buf); return ret; } static int handle_CoAP_response (wcp_display_settings *disp_settings) { int received; int ret = 1; coap_io_offset = 0; coap_io_buff[0] = '\0'; while (ret > 0) { /* Receive response from the CoAP server */ received = recv(sock, coap_packet_buff, sizeof(coap_packet_buff), 0); if (received < 0) { LOG_ERR("Socket error: %d, exit", errno); ret = errno; } else if (received == 0) { LOG_INF("Empty datagram"); ret = 0; } else { /* Parse the received CoAP packet */ ret = client_handle_response(disp_settings, coap_packet_buff, received); if (ret < 0) { LOG_ERR("Invalid response: %d, exit", ret); } } } return ret; } char *set_coap_uri_default (char *command, char *default_command) { if ((command == NULL) || (*command == '\0') || (*command == '#')) { return default_command; } else if (*command == '-') { return ""; } else { return command; } } #define LEVEL_DIVIDER(x) (x[0] == '\0' ? "" : "/") static void __set_coap_uri (char *uri_name, char *uri, char *l1, char *l2, char *l3, char *default_l3) { l3 = set_coap_uri_default (l3, default_l3); sprintf (uri, "%s%s%s%s%s", l1, LEVEL_DIVIDER(l2), l2, LEVEL_DIVIDER(l3), l3); LOG_INF("%s URI set to: %s", uri_name, uri); } void set_coap_uri (wcp_display_settings *disp_settings, char command_type, char *base, char *assetId, char *command) { base = set_coap_uri_default (base, COAP_SERVER_DEFAUT_BASE); assetId = set_coap_uri_default (assetId, disp_settings->displayId); if (command_type == 'S') { __set_coap_uri ("Status", coap_status_uri, base, assetId, command, COAP_SERVER_DEFAUT_STATUS_COMMAND); } else { __set_coap_uri ("Command", coap_command_uri, base, assetId, command, COAP_SERVER_DEFAUT_COMMAND); } } int server_resolve(void) { int err; struct addrinfo *result; struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_DGRAM }; char ipv4_addr[NET_IPV4_ADDR_LEN]; err = getaddrinfo(CONFIG_COAP_SERVER_HOSTNAME, NULL, &hints, &result); if (err != 0) { LOG_ERR("ERROR: getaddrinfo failed %d", err); return -EIO; } if (result == NULL) { LOG_ERR("ERROR: Address not found"); return -ENOENT; } /* IPv4 Address. */ struct sockaddr_in *server4 = ((struct sockaddr_in *)&server); server4->sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr; server4->sin_family = AF_INET; server4->sin_port = htons(CONFIG_COAP_SERVER_PORT); inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr, sizeof(ipv4_addr)); LOG_INF("IPv4 Address found %s", ipv4_addr); /* Free the address. */ freeaddrinfo(result); return 0; } /**@brief Initialize the CoAP client */ int client_init(bool useTLS) { int err; if (useTLS) { sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2); } else { sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); } if (sock < 0) { LOG_ERR("Failed to create CoAP socket: %d.", errno); return -errno; } if (useTLS) { int verify; sec_tag_t sec_tag_list[] = { 12 }; enum { NONE = 0, OPTIONAL = 1, REQUIRED = 2, }; verify = REQUIRED; err = setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify)); if (err) { LOG_ERR("Failed to setup peer verification, errno %d", errno); return -errno; } err = setsockopt(sock, SOL_TLS, TLS_HOSTNAME, CONFIG_COAP_SERVER_HOSTNAME, strlen(CONFIG_COAP_SERVER_HOSTNAME)); if (err) { LOG_ERR("Failed to setup TLS hostname (%s), errno %d", CONFIG_COAP_SERVER_HOSTNAME, errno); return -errno; } err = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list, sizeof(sec_tag_t) * ARRAY_SIZE(sec_tag_list)); if (err) { LOG_ERR("Failed to setup socket security tag, errno %d", errno); return -errno; } } err = connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)); if (err < 0) { LOG_ERR("Connect failed : %d", errno); return -errno; } /* Randomize token. */ next_token = sys_rand32_get(); return 0; } int modem_connect(void) { int err; LOG_INF("Set DNS server to Google"); struct nrf_in_addr dns; dns.s_addr = 134744072; // Google DNS, 8.8.8.8 err = nrf_setdnsaddr(NRF_AF_INET, &dns, sizeof(dns)); if (err) { LOG_INF("DNS set error: %d", err); } else { LOG_INF("DNS set to Google"); LOG_INF("Connect to LTE network"); err = lte_lc_connect_async(coap_lte_handler); LOG_INF("lte_lc_connect_async - returned: %d", err); if (err) { LOG_ERR("Error in lte_lc_connect_async, error: %d", err); return err; } } return 0; } void wait_for_modem_connection () { // Wait until the modem is connected to the network until continuing k_sem_take(&wcp_lte_connected, K_FOREVER); } // <<<< UART INPUT FOR HANDLING /* * Read characters from UART until line end is detected. Afterwards push the * data to the message queue. */ static void serial_cb(const struct device *dev, void *user_data) { uint8_t c; if (!uart_irq_update(uart_dev)) { return; } if (!uart_irq_rx_ready(uart_dev)) { return; } /* read until FIFO empty */ while (uart_fifo_read(uart_dev, &c, 1) == 1) { uart_poll_out(uart_dev, c); if ((rx_buf_pos < 0) || (rx_buf_pos == 0 && c == '\n')) { // Ignore leading newlines and any input coming in when we haven't handles the previous commmand continue; } if ((c == '\n' || c == '\r') && rx_buf_pos > 0) { uart_poll_out(uart_dev, '\n'); /* terminate string */ coap_io_buff[rx_buf_pos] = '\0'; // Set rx_buf_pos to negative so that any more messages are ignored until this one has been processed rx_buf_pos = -1; } else if (rx_buf_pos < (APP_COAP_MAX_MSG_LEN - 1)) { coap_io_buff[rx_buf_pos++] = c; } /* else: characters beyond buffer size are dropped */ } } static int handle_uart_input (wcp_display_settings *disp_settings) { if (!device_is_ready(uart_dev)) { LOG_ERR("UART device not found!"); return 0; } /* configure interrupt and callback to receive data */ int ret = uart_irq_callback_user_data_set(uart_dev, serial_cb, NULL); if (ret < 0) { if (ret == -ENOTSUP) { LOG_ERR("Interrupt-driven UART API support not enabled\n"); } else if (ret == -ENOSYS) { LOG_ERR("UART device does not support interrupt-driven API\n"); } else { LOG_ERR("Error setting UART callback: %d\n", ret); } return 0; } rx_buf_pos = -1; uart_irq_rx_enable(uart_dev); bool getting_UART_data = true; /* indefinitely wait for input from the user */ while (getting_UART_data) { LOG_INF("UART >>>>"); rx_buf_pos = 0; while (rx_buf_pos >= 0) { k_sleep(K_MSEC(100)); } if (strcmp(coap_io_buff, "#END") == 0) { getting_UART_data = false; } else { LOG_INF("UART 1"); handle_cmd_block (coap_io_buff, 0, disp_settings); LOG_INF("UART 2"); } } return 0; } int handle_messages (wcp_display_settings *disp_settings) { if (disp_settings->input_type == UART_INPUT_TYPE) { return handle_uart_input (disp_settings); } else { int err = 0; LOG_INF("Get COAP Messages"); // Get a bulk message from the server err = client_get_send(disp_settings, true, true); if (err) { LOG_ERR("Failed to send CoAP GET request, %d", err); } if (err == 0) { err = handle_CoAP_response (disp_settings); } if (err == 0) { /** Successfully connected to COAP Server - so mark boot image as * working to avoid reverting to the former image upon reboot. * Possible that we wil no-longer be able to connect to the AWS server * but as we don't want to do that very often this will have to do! */ boot_write_img_confirmed(); // Process the command block that has been loaded handle_cmd_block (coap_io_buff, 0, disp_settings); return 0; } else { return -1; } } } int send_messages (wcp_display_settings *disp_settings) { int err; // Update Device status here LOG_INF("Send COAP Messages"); if (disp_settings->input_type == UART_INPUT_TYPE) { LOG_INF("send_messages not suported for UART"); return 1; } else { /* Send POST request */ err = send_client_status_update(disp_settings); if (err) { LOG_ERR("Failed to send CoAP POST request, %d", err); return err; } return handle_CoAP_response (disp_settings); } } void disconnect_coap_session (wcp_display_settings *disp_settings) { if (disp_settings->input_type != UART_INPUT_TYPE) { // Disconnect from LTE close(sock); } } // >>>>
Here is the PRJ file:0358.prj.conf
Anybody got any ideas?
Many thanks,
Ed.