conn_mgr and LTE-M PSM

Hi, 

I really like the new Zephyr conn_mgr, it is really easy to use, almost UNIX like. I am wondering what is now the best way to control PSM. Will CONFIG_LTE_LINK_CONTROL and lte_lc_connect_async interfere with the conn_mgr? Should I only use one of the options or can they exist next to each other? 

will CONN_MGR_IF_NO_AUTO_CONNECT and then connection using lte_lc work reliable?

I am searching for the "right" way to do. 

Thanks!

Parents
  • Hello,

    I will have to look more into conn_mgr, but I am not sure if our LTE code is well-integrated with that feature.

    To be safe I would recommend using our LTE link control library.

    Best regards,

    Michal

  • Looks like conn_mgr and LTE Link control can (or even needs to) work next to each other. I can image someone else is also struggling wit it so this is how I got it to work, it is actually quite easy.

    Add this to prj.conf:

    ##Custom
    CONFIG_LTE_LINK_CONTROL=y
    ## PSM
    CONFIG_LTE_PSM_REQ_RPTAU="01010100"
    CONFIG_LTE_PSM_REQ_RAT="00000001"

    Then copy and edit the LTE_Handler from the example just above the main function. I changed to LOG_INF instead of printk and added te PSM part.

    static void lte_handler(const struct lte_lc_evt *const evt)
    {
            switch (evt->type) {
            case LTE_LC_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("Connected to: %s network\n",
                           evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "home" : "roaming");
    
                    break;
    
    		case LTE_LC_EVT_PSM_UPDATE:
    			LOG_INF("PSM parameter update: TAU: %d, Active time: %d\n", evt->psm_cfg.tau,
    				evt->psm_cfg.active_time);
    			//activeTime = evt->psm_cfg.active_time;
    			//tauTime = evt->psm_cfg.tau;
    			break;
            case LTE_LC_EVT_EDRX_UPDATE:
            case LTE_LC_EVT_RRC_UPDATE:
            case LTE_LC_EVT_CELL_UPDATE:
            case LTE_LC_EVT_LTE_MODE_UPDATE:
            case LTE_LC_EVT_TAU_PRE_WARNING:
            case LTE_LC_EVT_NEIGHBOR_CELL_MEAS:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT_PRE_WARNING:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT:
            case LTE_LC_EVT_MODEM_SLEEP_ENTER:
            case LTE_LC_EVT_MODEM_EVENT:
                    /* Handle LTE events */
                    break;
    
            default:
                    break;
            }
    }

    Then in main add this before connecting to the network.

    	lte_lc_register_handler(lte_handler);

    And add this after connection is established.

    	err = lte_lc_psm_req(true);
    	if (err) {
    		LOG_ERR("Requesting PSM failed, error: %d\n", err);
    	}  	

    If PSM request went alright you will see this in the serial log:

    :03.150,970] <inf> aws_iot_sample: Network connectivity established
    [00:00:03.151,550] <inf> aws_iot_sample: PSM parameter update: TAU: 756000, Active time: 16

    And you can clearly see the 16s active time using the PPK, After 16 the power consumption will go down. I have't optimized it in this case. 

    Complete AWS example main.c

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/reboot.h>
    #include <zephyr/dfu/mcuboot.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/logging/log_ctrl.h>
    #include <zephyr/net/conn_mgr_connectivity.h>
    #include <zephyr/net/conn_mgr_monitor.h>
    #include <net/aws_iot.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <hw_id.h>
    #include <modem/modem_info.h>
    #include <modem/lte_lc.h>
    
    
    #include "json_payload.h"
    
    /* Register log module */
    LOG_MODULE_REGISTER(aws_iot_sample, CONFIG_AWS_IOT_SAMPLE_LOG_LEVEL);
    
    /* Macros used to subscribe to specific Zephyr NET management events. */
    #define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
    #define CONN_LAYER_EVENT_MASK (NET_EVENT_CONN_IF_FATAL_ERROR)
    
    #define MODEM_FIRMWARE_VERSION_SIZE_MAX 50
    
    /* Macro called upon a fatal error, reboots the device. */
    #define FATAL_ERROR()					\
    	LOG_ERR("Fatal error! Rebooting the device.");	\
    	LOG_PANIC();					\
    	IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)))
    
    /* Zephyr NET management event callback structures. */
    static struct net_mgmt_event_callback l4_cb;
    static struct net_mgmt_event_callback conn_cb;
    
    /* Forward declarations. */
    static void shadow_update_work_fn(struct k_work *work);
    static void connect_work_fn(struct k_work *work);
    static void aws_iot_event_handler(const struct aws_iot_evt *const evt);
    
    /* Work items used to control some aspects of the sample. */
    static K_WORK_DELAYABLE_DEFINE(shadow_update_work, shadow_update_work_fn);
    static K_WORK_DELAYABLE_DEFINE(connect_work, connect_work_fn);
    
    /* Static functions */
    
    static int app_topics_subscribe(void)
    {
    	int err;
    	static const char custom_topic[] = "my-custom-topic/example";
    
    	const struct aws_iot_topic_data topics_list[CONFIG_AWS_IOT_APP_SUBSCRIPTION_LIST_COUNT] = {
    		[0].str = custom_topic,
    		[0].len = strlen(custom_topic),
    
    	};
    
    	err = aws_iot_subscription_topics_add(topics_list, ARRAY_SIZE(topics_list));
    	if (err) {
    		LOG_ERR("aws_iot_subscription_topics_add, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	return 0;
    }
    
    static int aws_iot_client_init(void)
    {
    	int err;
    	struct aws_iot_config config = { 0 };
    
    #if defined(CONFIG_AWS_IOT_SAMPLE_DEVICE_ID_USE_HW_ID)
    	char device_id[HW_ID_LEN] = { 0 };
    
    	/* Get unique hardware ID, can be used as AWS IoT MQTT broker device/client ID. */
    	err = hw_id_get(device_id, ARRAY_SIZE(device_id));
    	if (err) {
    		LOG_ERR("Failed to retrieve device ID, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	/* To use HW ID as MQTT device/client ID set the CONFIG_AWS_IOT_CLIENT_ID_APP option.
    	 * Otherwise the ID set by CONFIG_AWS_IOT_CLIENT_ID_STATIC is used.
    	 */
    	config.client_id = device_id;
    	config.client_id_len = strlen(device_id);
    
    	LOG_INF("Hardware ID: %s", device_id);
    #endif /* CONFIG_AWS_IOT_SAMPLE_DEVICE_ID_USE_HW_ID */
    
    	err = aws_iot_init(&config, aws_iot_event_handler);
    	if (err) {
    		LOG_ERR("AWS IoT library could not be initialized, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	/* Add application specific non-shadow topics to the AWS IoT library.
    	 * These topics will be subscribed to when connecting to the broker.
    	 */
    	err = app_topics_subscribe();
    	if (err) {
    		LOG_ERR("Adding application specific topics failed, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	return 0;
    }
    
    /* System Workqueue handlers. */
    
    static void shadow_update_work_fn(struct k_work *work)
    {
    	int err;
    	char message[CONFIG_AWS_IOT_SAMPLE_JSON_MESSAGE_SIZE_MAX] = { 0 };
    	struct payload payload = {
    		.state.reported.uptime = k_uptime_get(),
    		.state.reported.app_version = CONFIG_AWS_IOT_SAMPLE_APP_VERSION,
    	};
    	struct aws_iot_data tx_data = {
    		.qos = MQTT_QOS_0_AT_MOST_ONCE,
    		.topic.type = AWS_IOT_SHADOW_TOPIC_UPDATE,
    	};
    
    	if (IS_ENABLED(CONFIG_MODEM_INFO)) {
    		char modem_version_temp[MODEM_FIRMWARE_VERSION_SIZE_MAX];
    
    		err = modem_info_get_fw_version(modem_version_temp,
    						ARRAY_SIZE(modem_version_temp));
    		if (err) {
    			LOG_ERR("modem_info_get_fw_version, error: %d", err);
    			FATAL_ERROR();
    			return;
    		}
    
    		payload.state.reported.modem_version = modem_version_temp;
    	}
    
    	err = json_payload_construct(message, sizeof(message), &payload);
    	if (err) {
    		LOG_ERR("json_payload_construct, error: %d", err);
    		FATAL_ERROR();
    		return;
    	}
    
    	tx_data.ptr = message;
    	tx_data.len = strlen(message);
    
    	LOG_INF("Publishing message: %s to AWS IoT shadow", message);
    
    	err = aws_iot_send(&tx_data);
    	if (err) {
    		LOG_ERR("aws_iot_send, error: %d", err);
    		FATAL_ERROR();
    		return;
    	}
    
    	(void)k_work_reschedule(&shadow_update_work,
    				K_SECONDS(120));
    }
    
    static void connect_work_fn(struct k_work *work)
    {
    	int err;
    
    	LOG_INF("Connecting to AWS IoT");
    
    	err = aws_iot_connect(NULL);
    	if (err) {
    		LOG_ERR("aws_iot_connect, error: %d", err);
    	}
    
    	LOG_INF("Next connection retry in %d seconds",
    		CONFIG_AWS_IOT_SAMPLE_CONNECTION_RETRY_TIMEOUT_SECONDS);
    
    	(void)k_work_reschedule(&connect_work,
    				K_SECONDS(CONFIG_AWS_IOT_SAMPLE_CONNECTION_RETRY_TIMEOUT_SECONDS));
    }
    
    /* Functions that are executed on specific connection-related events. */
    
    static void on_aws_iot_evt_connected(const struct aws_iot_evt *const evt)
    {
    	(void)k_work_cancel_delayable(&connect_work);
    
    	/* If persistent session is enabled, the AWS IoT library will not subscribe to any topics.
    	 * Topics from the last session will be used.
    	 */
    	if (evt->data.persistent_session) {
    		LOG_WRN("Persistent session is enabled, using subscriptions "
    			"from the previous session");
    	}
    
    	/* Mark image as working to avoid reverting to the former image after a reboot. */
    #if defined(CONFIG_BOOTLOADER_MCUBOOT)
    	boot_write_img_confirmed();
    #endif
    
    	/* Start sequential updates to AWS IoT. */
    	(void)k_work_reschedule(&shadow_update_work, K_NO_WAIT);
    }
    
    static void on_aws_iot_evt_disconnected(void)
    {
    	(void)k_work_cancel_delayable(&shadow_update_work);
    	(void)k_work_reschedule(&connect_work, K_SECONDS(5));
    }
    
    static void on_aws_iot_evt_fota_done(const struct aws_iot_evt *const evt)
    {
    	int err;
    
    	/* Tear down MQTT connection. */
    	(void)aws_iot_disconnect();
    	(void)k_work_cancel_delayable(&connect_work);
    
    	/* If modem FOTA has been carried out, the modem needs to be reinitialized.
    	 * This is carried out by bringing the network interface down/up.
    	 */
    	if (evt->data.image & DFU_TARGET_IMAGE_TYPE_ANY_MODEM) {
    		LOG_INF("Modem FOTA done, reinitializing the modem");
    
    		err = conn_mgr_all_if_down(true);
    		if (err) {
    			LOG_ERR("conn_mgr_all_if_down, error: %d", err);
    			FATAL_ERROR();
    			return;
    		}
    
    		err = conn_mgr_all_if_up(true);
    		if (err) {
    			LOG_ERR("conn_mgr_all_if_up, error: %d", err);
    			FATAL_ERROR();
    			return;
    		}
    
    	} else if (evt->data.image & DFU_TARGET_IMAGE_TYPE_ANY_APPLICATION) {
    		LOG_INF("Application FOTA done, rebooting");
    		IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)));
    	} else {
    		LOG_WRN("Unexpected FOTA image type");
    	}
    }
    
    static void on_net_event_l4_connected(void)
    {
    	(void)k_work_reschedule(&connect_work, K_SECONDS(5));
    }
    
    static void on_net_event_l4_disconnected(void)
    {
    	(void)aws_iot_disconnect();
    	(void)k_work_cancel_delayable(&connect_work);
    	(void)k_work_cancel_delayable(&shadow_update_work);
    }
    
    /* Event handlers */
    
    static void aws_iot_event_handler(const struct aws_iot_evt *const evt)
    {
    	switch (evt->type) {
    	case AWS_IOT_EVT_CONNECTING:
    		LOG_INF("AWS_IOT_EVT_CONNECTING");
    		break;
    	case AWS_IOT_EVT_CONNECTED:
    		LOG_INF("AWS_IOT_EVT_CONNECTED");
    		on_aws_iot_evt_connected(evt);
    		break;
    	case AWS_IOT_EVT_READY:
    		LOG_INF("AWS_IOT_EVT_READY");
    		break;
    	case AWS_IOT_EVT_DISCONNECTED:
    		LOG_INF("AWS_IOT_EVT_DISCONNECTED");
    		on_aws_iot_evt_disconnected();
    		break;
    	case AWS_IOT_EVT_DATA_RECEIVED:
    		LOG_INF("AWS_IOT_EVT_DATA_RECEIVED");
    
    		LOG_INF("Received message: \"%.*s\" on topic: \"%.*s\"", evt->data.msg.len,
    									 evt->data.msg.ptr,
    									 evt->data.msg.topic.len,
    									 evt->data.msg.topic.str);
    		break;
    	case AWS_IOT_EVT_PUBACK:
    		LOG_INF("AWS_IOT_EVT_PUBACK, message ID: %d", evt->data.message_id);
    		break;
    	case AWS_IOT_EVT_PINGRESP:
    		LOG_INF("AWS_IOT_EVT_PINGRESP");
    		break;
    	case AWS_IOT_EVT_FOTA_START:
    		LOG_INF("AWS_IOT_EVT_FOTA_START");
    		break;
    	case AWS_IOT_EVT_FOTA_ERASE_PENDING:
    		LOG_INF("AWS_IOT_EVT_FOTA_ERASE_PENDING");
    		break;
    	case AWS_IOT_EVT_FOTA_ERASE_DONE:
    		LOG_INF("AWS_FOTA_EVT_ERASE_DONE");
    		break;
    	case AWS_IOT_EVT_FOTA_DONE:
    		LOG_INF("AWS_IOT_EVT_FOTA_DONE");
    		on_aws_iot_evt_fota_done(evt);
    		break;
    	case AWS_IOT_EVT_FOTA_DL_PROGRESS:
    		LOG_INF("AWS_IOT_EVT_FOTA_DL_PROGRESS, (%d%%)", evt->data.fota_progress);
    		break;
    	case AWS_IOT_EVT_ERROR:
    		LOG_INF("AWS_IOT_EVT_ERROR, %d", evt->data.err);
    		break;
    	case AWS_IOT_EVT_FOTA_ERROR:
    		LOG_INF("AWS_IOT_EVT_FOTA_ERROR");
    		break;
    	default:
    		LOG_WRN("Unknown AWS IoT event type: %d", evt->type);
    		break;
    	}
    }
    
    static void l4_event_handler(struct net_mgmt_event_callback *cb,
    			     uint32_t event,
    			     struct net_if *iface)
    {
    	switch (event) {
    	case NET_EVENT_L4_CONNECTED:
    		LOG_INF("Network connectivity established");
    		on_net_event_l4_connected();
    		break;
    	case NET_EVENT_L4_DISCONNECTED:
    		LOG_INF("Network connectivity lost");
    		on_net_event_l4_disconnected();
    		break;
    	default:
    		/* Don't care */
    		return;
    	}
    }
    
    static void connectivity_event_handler(struct net_mgmt_event_callback *cb,
    				       uint32_t event,
    				       struct net_if *iface)
    {
    	if (event == NET_EVENT_CONN_IF_FATAL_ERROR) {
    		LOG_ERR("NET_EVENT_CONN_IF_FATAL_ERROR");
    		FATAL_ERROR();
    		return;
    	}
    }
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
            switch (evt->type) {
            case LTE_LC_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("Connected to: %s network\n",
                           evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "home" : "roaming");
    
                    break;
    
    		case LTE_LC_EVT_PSM_UPDATE:
    			LOG_INF("PSM parameter update: TAU: %d, Active time: %d\n", evt->psm_cfg.tau,
    				evt->psm_cfg.active_time);
    			//activeTime = evt->psm_cfg.active_time;
    			//tauTime = evt->psm_cfg.tau;
    			break;
            case LTE_LC_EVT_EDRX_UPDATE:
            case LTE_LC_EVT_RRC_UPDATE:
            case LTE_LC_EVT_CELL_UPDATE:
            case LTE_LC_EVT_LTE_MODE_UPDATE:
            case LTE_LC_EVT_TAU_PRE_WARNING:
            case LTE_LC_EVT_NEIGHBOR_CELL_MEAS:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT_PRE_WARNING:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT:
            case LTE_LC_EVT_MODEM_SLEEP_ENTER:
            case LTE_LC_EVT_MODEM_EVENT:
                    /* Handle LTE events */
                    break;
    
            default:
                    break;
            }
    }
    
    int main(void)
    {
    	LOG_INF("The AWS IoT sample started, version: %s", CONFIG_AWS_IOT_SAMPLE_APP_VERSION);
    
    	int err;
    
    	/* Setup handler for Zephyr NET Connection Manager events. */
    	net_mgmt_init_event_callback(&l4_cb, l4_event_handler, L4_EVENT_MASK);
    	net_mgmt_add_event_callback(&l4_cb);
    
    	/* Setup handler for Zephyr NET Connection Manager Connectivity layer. */
    	net_mgmt_init_event_callback(&conn_cb, connectivity_event_handler, CONN_LAYER_EVENT_MASK);
    	net_mgmt_add_event_callback(&conn_cb);
    
    	lte_lc_register_handler(lte_handler);
    
    	/* Connecting to the configured connectivity layer.
    	 * Wi-Fi or LTE depending on the board that the sample was built for.
    	 */
    	LOG_INF("Bringing network interface up and connecting to the network");
    
    	err = conn_mgr_all_if_up(true);
    	if (err) {
    		LOG_ERR("conn_mgr_all_if_up, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	err = lte_lc_psm_req(true);
    	if (err) {
    		LOG_ERR("Requesting PSM failed, error: %d\n", err);
    	}  	
    
    	err = aws_iot_client_init();
    	if (err) {
    		LOG_ERR("aws_iot_client_init, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	err = lte_lc_psm_req(true);
    	if (err) {
    		LOG_ERR("Requesting PSM failed, error: %d\n", err);
    	}  
    
    	return 0;
    }
    

Reply
  • Looks like conn_mgr and LTE Link control can (or even needs to) work next to each other. I can image someone else is also struggling wit it so this is how I got it to work, it is actually quite easy.

    Add this to prj.conf:

    ##Custom
    CONFIG_LTE_LINK_CONTROL=y
    ## PSM
    CONFIG_LTE_PSM_REQ_RPTAU="01010100"
    CONFIG_LTE_PSM_REQ_RAT="00000001"

    Then copy and edit the LTE_Handler from the example just above the main function. I changed to LOG_INF instead of printk and added te PSM part.

    static void lte_handler(const struct lte_lc_evt *const evt)
    {
            switch (evt->type) {
            case LTE_LC_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("Connected to: %s network\n",
                           evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "home" : "roaming");
    
                    break;
    
    		case LTE_LC_EVT_PSM_UPDATE:
    			LOG_INF("PSM parameter update: TAU: %d, Active time: %d\n", evt->psm_cfg.tau,
    				evt->psm_cfg.active_time);
    			//activeTime = evt->psm_cfg.active_time;
    			//tauTime = evt->psm_cfg.tau;
    			break;
            case LTE_LC_EVT_EDRX_UPDATE:
            case LTE_LC_EVT_RRC_UPDATE:
            case LTE_LC_EVT_CELL_UPDATE:
            case LTE_LC_EVT_LTE_MODE_UPDATE:
            case LTE_LC_EVT_TAU_PRE_WARNING:
            case LTE_LC_EVT_NEIGHBOR_CELL_MEAS:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT_PRE_WARNING:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT:
            case LTE_LC_EVT_MODEM_SLEEP_ENTER:
            case LTE_LC_EVT_MODEM_EVENT:
                    /* Handle LTE events */
                    break;
    
            default:
                    break;
            }
    }

    Then in main add this before connecting to the network.

    	lte_lc_register_handler(lte_handler);

    And add this after connection is established.

    	err = lte_lc_psm_req(true);
    	if (err) {
    		LOG_ERR("Requesting PSM failed, error: %d\n", err);
    	}  	

    If PSM request went alright you will see this in the serial log:

    :03.150,970] <inf> aws_iot_sample: Network connectivity established
    [00:00:03.151,550] <inf> aws_iot_sample: PSM parameter update: TAU: 756000, Active time: 16

    And you can clearly see the 16s active time using the PPK, After 16 the power consumption will go down. I have't optimized it in this case. 

    Complete AWS example main.c

    /*
     * Copyright (c) 2020 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
     */
    
    #include <zephyr/kernel.h>
    #include <zephyr/sys/reboot.h>
    #include <zephyr/dfu/mcuboot.h>
    #include <zephyr/logging/log.h>
    #include <zephyr/logging/log_ctrl.h>
    #include <zephyr/net/conn_mgr_connectivity.h>
    #include <zephyr/net/conn_mgr_monitor.h>
    #include <net/aws_iot.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <hw_id.h>
    #include <modem/modem_info.h>
    #include <modem/lte_lc.h>
    
    
    #include "json_payload.h"
    
    /* Register log module */
    LOG_MODULE_REGISTER(aws_iot_sample, CONFIG_AWS_IOT_SAMPLE_LOG_LEVEL);
    
    /* Macros used to subscribe to specific Zephyr NET management events. */
    #define L4_EVENT_MASK (NET_EVENT_L4_CONNECTED | NET_EVENT_L4_DISCONNECTED)
    #define CONN_LAYER_EVENT_MASK (NET_EVENT_CONN_IF_FATAL_ERROR)
    
    #define MODEM_FIRMWARE_VERSION_SIZE_MAX 50
    
    /* Macro called upon a fatal error, reboots the device. */
    #define FATAL_ERROR()					\
    	LOG_ERR("Fatal error! Rebooting the device.");	\
    	LOG_PANIC();					\
    	IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)))
    
    /* Zephyr NET management event callback structures. */
    static struct net_mgmt_event_callback l4_cb;
    static struct net_mgmt_event_callback conn_cb;
    
    /* Forward declarations. */
    static void shadow_update_work_fn(struct k_work *work);
    static void connect_work_fn(struct k_work *work);
    static void aws_iot_event_handler(const struct aws_iot_evt *const evt);
    
    /* Work items used to control some aspects of the sample. */
    static K_WORK_DELAYABLE_DEFINE(shadow_update_work, shadow_update_work_fn);
    static K_WORK_DELAYABLE_DEFINE(connect_work, connect_work_fn);
    
    /* Static functions */
    
    static int app_topics_subscribe(void)
    {
    	int err;
    	static const char custom_topic[] = "my-custom-topic/example";
    
    	const struct aws_iot_topic_data topics_list[CONFIG_AWS_IOT_APP_SUBSCRIPTION_LIST_COUNT] = {
    		[0].str = custom_topic,
    		[0].len = strlen(custom_topic),
    
    	};
    
    	err = aws_iot_subscription_topics_add(topics_list, ARRAY_SIZE(topics_list));
    	if (err) {
    		LOG_ERR("aws_iot_subscription_topics_add, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	return 0;
    }
    
    static int aws_iot_client_init(void)
    {
    	int err;
    	struct aws_iot_config config = { 0 };
    
    #if defined(CONFIG_AWS_IOT_SAMPLE_DEVICE_ID_USE_HW_ID)
    	char device_id[HW_ID_LEN] = { 0 };
    
    	/* Get unique hardware ID, can be used as AWS IoT MQTT broker device/client ID. */
    	err = hw_id_get(device_id, ARRAY_SIZE(device_id));
    	if (err) {
    		LOG_ERR("Failed to retrieve device ID, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	/* To use HW ID as MQTT device/client ID set the CONFIG_AWS_IOT_CLIENT_ID_APP option.
    	 * Otherwise the ID set by CONFIG_AWS_IOT_CLIENT_ID_STATIC is used.
    	 */
    	config.client_id = device_id;
    	config.client_id_len = strlen(device_id);
    
    	LOG_INF("Hardware ID: %s", device_id);
    #endif /* CONFIG_AWS_IOT_SAMPLE_DEVICE_ID_USE_HW_ID */
    
    	err = aws_iot_init(&config, aws_iot_event_handler);
    	if (err) {
    		LOG_ERR("AWS IoT library could not be initialized, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	/* Add application specific non-shadow topics to the AWS IoT library.
    	 * These topics will be subscribed to when connecting to the broker.
    	 */
    	err = app_topics_subscribe();
    	if (err) {
    		LOG_ERR("Adding application specific topics failed, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	return 0;
    }
    
    /* System Workqueue handlers. */
    
    static void shadow_update_work_fn(struct k_work *work)
    {
    	int err;
    	char message[CONFIG_AWS_IOT_SAMPLE_JSON_MESSAGE_SIZE_MAX] = { 0 };
    	struct payload payload = {
    		.state.reported.uptime = k_uptime_get(),
    		.state.reported.app_version = CONFIG_AWS_IOT_SAMPLE_APP_VERSION,
    	};
    	struct aws_iot_data tx_data = {
    		.qos = MQTT_QOS_0_AT_MOST_ONCE,
    		.topic.type = AWS_IOT_SHADOW_TOPIC_UPDATE,
    	};
    
    	if (IS_ENABLED(CONFIG_MODEM_INFO)) {
    		char modem_version_temp[MODEM_FIRMWARE_VERSION_SIZE_MAX];
    
    		err = modem_info_get_fw_version(modem_version_temp,
    						ARRAY_SIZE(modem_version_temp));
    		if (err) {
    			LOG_ERR("modem_info_get_fw_version, error: %d", err);
    			FATAL_ERROR();
    			return;
    		}
    
    		payload.state.reported.modem_version = modem_version_temp;
    	}
    
    	err = json_payload_construct(message, sizeof(message), &payload);
    	if (err) {
    		LOG_ERR("json_payload_construct, error: %d", err);
    		FATAL_ERROR();
    		return;
    	}
    
    	tx_data.ptr = message;
    	tx_data.len = strlen(message);
    
    	LOG_INF("Publishing message: %s to AWS IoT shadow", message);
    
    	err = aws_iot_send(&tx_data);
    	if (err) {
    		LOG_ERR("aws_iot_send, error: %d", err);
    		FATAL_ERROR();
    		return;
    	}
    
    	(void)k_work_reschedule(&shadow_update_work,
    				K_SECONDS(120));
    }
    
    static void connect_work_fn(struct k_work *work)
    {
    	int err;
    
    	LOG_INF("Connecting to AWS IoT");
    
    	err = aws_iot_connect(NULL);
    	if (err) {
    		LOG_ERR("aws_iot_connect, error: %d", err);
    	}
    
    	LOG_INF("Next connection retry in %d seconds",
    		CONFIG_AWS_IOT_SAMPLE_CONNECTION_RETRY_TIMEOUT_SECONDS);
    
    	(void)k_work_reschedule(&connect_work,
    				K_SECONDS(CONFIG_AWS_IOT_SAMPLE_CONNECTION_RETRY_TIMEOUT_SECONDS));
    }
    
    /* Functions that are executed on specific connection-related events. */
    
    static void on_aws_iot_evt_connected(const struct aws_iot_evt *const evt)
    {
    	(void)k_work_cancel_delayable(&connect_work);
    
    	/* If persistent session is enabled, the AWS IoT library will not subscribe to any topics.
    	 * Topics from the last session will be used.
    	 */
    	if (evt->data.persistent_session) {
    		LOG_WRN("Persistent session is enabled, using subscriptions "
    			"from the previous session");
    	}
    
    	/* Mark image as working to avoid reverting to the former image after a reboot. */
    #if defined(CONFIG_BOOTLOADER_MCUBOOT)
    	boot_write_img_confirmed();
    #endif
    
    	/* Start sequential updates to AWS IoT. */
    	(void)k_work_reschedule(&shadow_update_work, K_NO_WAIT);
    }
    
    static void on_aws_iot_evt_disconnected(void)
    {
    	(void)k_work_cancel_delayable(&shadow_update_work);
    	(void)k_work_reschedule(&connect_work, K_SECONDS(5));
    }
    
    static void on_aws_iot_evt_fota_done(const struct aws_iot_evt *const evt)
    {
    	int err;
    
    	/* Tear down MQTT connection. */
    	(void)aws_iot_disconnect();
    	(void)k_work_cancel_delayable(&connect_work);
    
    	/* If modem FOTA has been carried out, the modem needs to be reinitialized.
    	 * This is carried out by bringing the network interface down/up.
    	 */
    	if (evt->data.image & DFU_TARGET_IMAGE_TYPE_ANY_MODEM) {
    		LOG_INF("Modem FOTA done, reinitializing the modem");
    
    		err = conn_mgr_all_if_down(true);
    		if (err) {
    			LOG_ERR("conn_mgr_all_if_down, error: %d", err);
    			FATAL_ERROR();
    			return;
    		}
    
    		err = conn_mgr_all_if_up(true);
    		if (err) {
    			LOG_ERR("conn_mgr_all_if_up, error: %d", err);
    			FATAL_ERROR();
    			return;
    		}
    
    	} else if (evt->data.image & DFU_TARGET_IMAGE_TYPE_ANY_APPLICATION) {
    		LOG_INF("Application FOTA done, rebooting");
    		IF_ENABLED(CONFIG_REBOOT, (sys_reboot(0)));
    	} else {
    		LOG_WRN("Unexpected FOTA image type");
    	}
    }
    
    static void on_net_event_l4_connected(void)
    {
    	(void)k_work_reschedule(&connect_work, K_SECONDS(5));
    }
    
    static void on_net_event_l4_disconnected(void)
    {
    	(void)aws_iot_disconnect();
    	(void)k_work_cancel_delayable(&connect_work);
    	(void)k_work_cancel_delayable(&shadow_update_work);
    }
    
    /* Event handlers */
    
    static void aws_iot_event_handler(const struct aws_iot_evt *const evt)
    {
    	switch (evt->type) {
    	case AWS_IOT_EVT_CONNECTING:
    		LOG_INF("AWS_IOT_EVT_CONNECTING");
    		break;
    	case AWS_IOT_EVT_CONNECTED:
    		LOG_INF("AWS_IOT_EVT_CONNECTED");
    		on_aws_iot_evt_connected(evt);
    		break;
    	case AWS_IOT_EVT_READY:
    		LOG_INF("AWS_IOT_EVT_READY");
    		break;
    	case AWS_IOT_EVT_DISCONNECTED:
    		LOG_INF("AWS_IOT_EVT_DISCONNECTED");
    		on_aws_iot_evt_disconnected();
    		break;
    	case AWS_IOT_EVT_DATA_RECEIVED:
    		LOG_INF("AWS_IOT_EVT_DATA_RECEIVED");
    
    		LOG_INF("Received message: \"%.*s\" on topic: \"%.*s\"", evt->data.msg.len,
    									 evt->data.msg.ptr,
    									 evt->data.msg.topic.len,
    									 evt->data.msg.topic.str);
    		break;
    	case AWS_IOT_EVT_PUBACK:
    		LOG_INF("AWS_IOT_EVT_PUBACK, message ID: %d", evt->data.message_id);
    		break;
    	case AWS_IOT_EVT_PINGRESP:
    		LOG_INF("AWS_IOT_EVT_PINGRESP");
    		break;
    	case AWS_IOT_EVT_FOTA_START:
    		LOG_INF("AWS_IOT_EVT_FOTA_START");
    		break;
    	case AWS_IOT_EVT_FOTA_ERASE_PENDING:
    		LOG_INF("AWS_IOT_EVT_FOTA_ERASE_PENDING");
    		break;
    	case AWS_IOT_EVT_FOTA_ERASE_DONE:
    		LOG_INF("AWS_FOTA_EVT_ERASE_DONE");
    		break;
    	case AWS_IOT_EVT_FOTA_DONE:
    		LOG_INF("AWS_IOT_EVT_FOTA_DONE");
    		on_aws_iot_evt_fota_done(evt);
    		break;
    	case AWS_IOT_EVT_FOTA_DL_PROGRESS:
    		LOG_INF("AWS_IOT_EVT_FOTA_DL_PROGRESS, (%d%%)", evt->data.fota_progress);
    		break;
    	case AWS_IOT_EVT_ERROR:
    		LOG_INF("AWS_IOT_EVT_ERROR, %d", evt->data.err);
    		break;
    	case AWS_IOT_EVT_FOTA_ERROR:
    		LOG_INF("AWS_IOT_EVT_FOTA_ERROR");
    		break;
    	default:
    		LOG_WRN("Unknown AWS IoT event type: %d", evt->type);
    		break;
    	}
    }
    
    static void l4_event_handler(struct net_mgmt_event_callback *cb,
    			     uint32_t event,
    			     struct net_if *iface)
    {
    	switch (event) {
    	case NET_EVENT_L4_CONNECTED:
    		LOG_INF("Network connectivity established");
    		on_net_event_l4_connected();
    		break;
    	case NET_EVENT_L4_DISCONNECTED:
    		LOG_INF("Network connectivity lost");
    		on_net_event_l4_disconnected();
    		break;
    	default:
    		/* Don't care */
    		return;
    	}
    }
    
    static void connectivity_event_handler(struct net_mgmt_event_callback *cb,
    				       uint32_t event,
    				       struct net_if *iface)
    {
    	if (event == NET_EVENT_CONN_IF_FATAL_ERROR) {
    		LOG_ERR("NET_EVENT_CONN_IF_FATAL_ERROR");
    		FATAL_ERROR();
    		return;
    	}
    }
    
    static void lte_handler(const struct lte_lc_evt *const evt)
    {
            switch (evt->type) {
            case LTE_LC_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("Connected to: %s network\n",
                           evt->nw_reg_status == LTE_LC_NW_REG_REGISTERED_HOME ? "home" : "roaming");
    
                    break;
    
    		case LTE_LC_EVT_PSM_UPDATE:
    			LOG_INF("PSM parameter update: TAU: %d, Active time: %d\n", evt->psm_cfg.tau,
    				evt->psm_cfg.active_time);
    			//activeTime = evt->psm_cfg.active_time;
    			//tauTime = evt->psm_cfg.tau;
    			break;
            case LTE_LC_EVT_EDRX_UPDATE:
            case LTE_LC_EVT_RRC_UPDATE:
            case LTE_LC_EVT_CELL_UPDATE:
            case LTE_LC_EVT_LTE_MODE_UPDATE:
            case LTE_LC_EVT_TAU_PRE_WARNING:
            case LTE_LC_EVT_NEIGHBOR_CELL_MEAS:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT_PRE_WARNING:
            case LTE_LC_EVT_MODEM_SLEEP_EXIT:
            case LTE_LC_EVT_MODEM_SLEEP_ENTER:
            case LTE_LC_EVT_MODEM_EVENT:
                    /* Handle LTE events */
                    break;
    
            default:
                    break;
            }
    }
    
    int main(void)
    {
    	LOG_INF("The AWS IoT sample started, version: %s", CONFIG_AWS_IOT_SAMPLE_APP_VERSION);
    
    	int err;
    
    	/* Setup handler for Zephyr NET Connection Manager events. */
    	net_mgmt_init_event_callback(&l4_cb, l4_event_handler, L4_EVENT_MASK);
    	net_mgmt_add_event_callback(&l4_cb);
    
    	/* Setup handler for Zephyr NET Connection Manager Connectivity layer. */
    	net_mgmt_init_event_callback(&conn_cb, connectivity_event_handler, CONN_LAYER_EVENT_MASK);
    	net_mgmt_add_event_callback(&conn_cb);
    
    	lte_lc_register_handler(lte_handler);
    
    	/* Connecting to the configured connectivity layer.
    	 * Wi-Fi or LTE depending on the board that the sample was built for.
    	 */
    	LOG_INF("Bringing network interface up and connecting to the network");
    
    	err = conn_mgr_all_if_up(true);
    	if (err) {
    		LOG_ERR("conn_mgr_all_if_up, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	err = lte_lc_psm_req(true);
    	if (err) {
    		LOG_ERR("Requesting PSM failed, error: %d\n", err);
    	}  	
    
    	err = aws_iot_client_init();
    	if (err) {
    		LOG_ERR("aws_iot_client_init, error: %d", err);
    		FATAL_ERROR();
    		return err;
    	}
    
    	err = lte_lc_psm_req(true);
    	if (err) {
    		LOG_ERR("Requesting PSM failed, error: %d\n", err);
    	}  
    
    	return 0;
    }
    

Children
Related