This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Using RTC to control the real timestamp

Hi Dev Team,

I had a task to work on that required a timsetamp to schedule a certain event, like for example I wanted my modem to start off every morning at 10:00:00 and switch off at 10:05:00.

What could be the best way to achieve this kind of functionality ?

Regards,

Adeel.

Parents
  • Hi Adeel

    You might want to have a look at the calendar example on our Github account:
    https://github.com/NordicPlayground/nrf5-calendar-example

    It basically uses the standard time.h library in C to decode rolling second counter into seconds, minutes, hours, days etc, and uses the RTC timer in the nRF52 to update the time every second. 

    Please be aware that if you need to keep track of time over long periods you should use an external 32k crystal for the best accuracy.

    Best regards
    Torbjørn

  • Hi Torbjorn,

    Is  there any possibility of an example for nRF9160DK. I am using NCS v1.3.0 nRF Connect for desktop and running the asset tracker application. 

    I tried using the date_time.h library to try and get the time either using NTP or through MODEM. I can use the AT+CCLK command to fix the time manually and then read it but I would like the network to get the current time for me.   

    Regards,

    Adeel.

  • Hi Adeel

    After turning the modem back on it will take some time for it to reconnect to the network. 

    If you wait for the CEREG notification informing you that the modem is connected is the date_time library still reporting errors?

    Best regards
    Torbjørn

  • Hi Tobjorn,

    Unfortunately yes, its still reporting the same error. The CEREG value says the modem is connected but the error still remains. Is there any way or other example where this could have been implemented for a reference ?

    Regards,

    Adeel. 

  • This is the output coming up on the screen. I even tried different NTP servers to try and get the error out.

    Regards,

    Adeel.

  • Hi Adeel

    One of my colleagues made an example for NTP that he made available online, but I am not sure the date_time library is used, so I don't know how relevant it is for you:
    https://github.com/Rallare/fw-nrfconnect-nrf/tree/nrf9160_samples/samples/nrf9160/ntp

    Are you able to share your code with me?
    Then I will give it a go and see if I can get it to work on my end. 

    Best regards
    Torbjørn

  • Hi Torbjorn,

    I am attaching my main file for you to have a look. I have added the date_time_update() ; time_modem_get() and date_time_now() functions in the main loop part of the code. I am just trying to build up on the asset tracker application using the date_time library. 

    You can try it out and let me know if it works or you can modify it as well :).

    Regards,

    Adeel.

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
     */
    
    #include <drivers/gpio.h>
    #include <nrf9160.h>
    #include <zephyr.h>
    #include <kernel_structs.h>
    #include <stdio.h>
    #include <string.h>
    #include <device.h>
    #include <date_time.h>
    #include <time.h>
    //#include <sntp.h>
    #include <kernel.h>
    #include <drivers/gps.h>
    #include <drivers/sensor.h>
    #include <console/console.h>
    #include <power/reboot.h>
    #include <logging/log_ctrl.h>
    #if defined(CONFIG_BSD_LIBRARY)
    #include <modem/bsdlib.h>
    #include <bsd.h>
    #include <modem/lte_lc.h>
    #include <modem/at_cmd.h>
    #include <modem/modem_info.h>
    #endif /* CONFIG_BSD_LIBRARY */
    #include <net/cloud.h>
    #include <net/socket.h>
    #include <net/nrf_cloud.h>
    #if defined(CONFIG_NRF_CLOUD_AGPS)
    #include <net/nrf_cloud_agps.h>
    #endif
    
    #if defined(CONFIG_LWM2M_CARRIER)
    #include <lwm2m_carrier.h>
    #endif
    
    #if defined(CONFIG_BOOTLOADER_MCUBOOT)
    #include <dfu/mcuboot.h>
    #endif
    
    #include "cloud_codec.h"
    #include "env_sensors.h"
    #include "motion.h"
    #include "ui.h"
    #include "service_info.h"
    #include <modem/at_cmd.h>
    #include "watchdog.h"
    #include "gps_controller.h"
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(asset_tracker, CONFIG_ASSET_TRACKER_LOG_LEVEL);
    
    #define CALIBRATION_PRESS_DURATION  K_SECONDS(5)
    #define CLOUD_CONNACK_WAIT_DURATION (CONFIG_CLOUD_WAIT_DURATION * MSEC_PER_SEC)
    
    #ifdef CONFIG_ACCEL_USE_SIM
    #define FLIP_INPUT			CONFIG_FLIP_INPUT
    #define CALIBRATION_INPUT		-1
    #else
    #define FLIP_INPUT			-1
    #ifdef CONFIG_ACCEL_CALIBRATE
    #define CALIBRATION_INPUT		CONFIG_CALIBRATION_INPUT
    #else
    #define CALIBRATION_INPUT		-1
    #endif /* CONFIG_ACCEL_CALIBRATE */
    #endif /* CONFIG_ACCEL_USE_SIM */
    
    #if defined(CONFIG_BSD_LIBRARY) && \
    !defined(CONFIG_LTE_LINK_CONTROL)
    #error "Missing CONFIG_LTE_LINK_CONTROL"
    #endif
    
    #if defined(CONFIG_BSD_LIBRARY) && \
    defined(CONFIG_LTE_AUTO_INIT_AND_CONNECT) && \
    defined(CONFIG_NRF_CLOUD_PROVISION_CERTIFICATES)
    #error "PROVISION_CERTIFICATES \
    	requires CONFIG_LTE_AUTO_INIT_AND_CONNECT to be disabled!"
    #endif
    
    
    #define M1 0
    #define NB1 1
    
    #define CAT M1 //M1 or NB1
    
    #if CAT == M1
    #define AT_EDRX_REQ         "AT+CEDRXS=2,4,\"0000\""
    #define AT_PTW_REQ          "AT\%XPTW=4,\"0000\""
    #define AT_XSYSTEMMODE		"AT\%XSYSTEMMODE=1,0,0,0"
    #elif CAT == NB1
    #define AT_EDRX_REQ         "AT+CEDRXS=2,5,\"0010\""
    #define AT_PTW_REQ          "AT\%XPTW=5,\"0000\""
    #define AT_XSYSTEMMODE		"AT\%XSYSTEMMODE=0,1,0,0"
    #endif
    
    #define AT_SUBSCRIBE		"AT+CEREG=5"
    #define AT_CEREG	    	"AT+CEREG?"
    #define AT_CCLK                 "AT+CCLK=\"20/08/24,00:00:00+08\""
    //#define AT_CCLK1                 "AT+CCLK=\"20/08/24,00:05:00+08\""
    #define AT_TIME                 "AT\%XTIME: \"08\",\"81109251714208\",\"01\""   
    #define AT_NETTIME              "AT%XNETTIME=1"         
    #define AT_ASK                  "AT+CCLK?"
    #define AT_PSM_REQ			"AT+CPSMS=1,\"\",\"\",\"00010100\",\"00000000\""
    #define AT_PSM_OFF			"AT+CPSMS=0"
    #define AT_EDRX_READ        "AT+CEDRXRDP"
    #define AT_XMONITOR	    	"AT\%XMONITOR"
    #define AT_CFUN1        	"AT+CFUN=1"
    #define AT_CFUN0        	"AT+CFUN=0"
    #define AT_POWER            "AT\%XDATAPRFL=0"
    
    #define AT_CMD_MODEM_DATE_TIME			"AT+CCLK?"
    #define AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN	500
    
    #define MODEM_TIME_DEFAULT 115
    
    #define TIMER_INTERVAL_SEC 20
    #define TIMER_DURATION 25
    
    struct k_timer my_timer;
    struct device *dev;
    uint8_t toggle = 0;
    
    struct k_timer single_shot_timer;
    //struct k_timer multi_shot_timer;
    
    
    //K_SEM_DEFINE(time_fetch_sem, 0, 1);
    
    //static struct k_delayed_work time_work;
    
    bool connected = false;
    enum at_cmd_state at_state;
    
    static void at_cmd_notification_handler(const char* response){
        printk("AT notify: \t%s",response);
        if (!memcmp("+CEREG: 1", response, 9) || !memcmp("+CEREG: 5", response, 9)){
            connected = true;
        }
    }
    
    static void at_cmd_handler(const char* response){
        if(strlen(response) > 0){
            printk("AT recv:\t%s",response);
        }
    }
    
    static void at_send_command(const char *cmd)
    {
        printk("AT send: \t%s\n", cmd);
        at_cmd_write_with_callback(cmd, &at_cmd_handler, &at_state);
        if(at_state){
            printk("AT ret: \t%u\n",at_state);
        }
        else{
            printk("AT ret: \tOK\n");
        }
    }
    
    
    void my_expiry_function(struct k_timer *timer_id){
            ++toggle;
            gpio_pin_write(dev, 16, toggle % 2);
            printk("interrupt!\r\n");
    }
    
    
    static void my_expiry_function_1(struct k_timer *timer)
    {
    	ARG_UNUSED(timer);
    	printk("single_shot_timer expired\n");
    
    }
    
    /*static void my_expiry_function_2(struct k_timer *timer)
    {
    	ARG_UNUSED(timer);
    	printk("multi_shot_timer expired\n");
    
    }*/
    
    
    #define CLOUD_LED_ON_STR "{\"led\":\"on\"}"
    #define CLOUD_LED_OFF_STR "{\"led\":\"off\"}"
    #define CLOUD_LED_MSK UI_LED_1
    
    /* Interval in milliseconds after which the device will reboot
     * if the disconnect event has not been handled.
     */
    #define REBOOT_AFTER_DISCONNECT_WAIT_MS     (15 * MSEC_PER_SEC)
    
    /* Interval in milliseconds after which the device will
     * disconnect and reconnect if association was not completed.
     */
    #define CONN_CYCLE_AFTER_ASSOCIATION_REQ_MS K_MINUTES(5)
    
    struct rsrp_data {
    	u16_t value;
    	u16_t offset;
    };
    
    #if CONFIG_MODEM_INFO
    static struct rsrp_data rsrp = {
    	.value = 0,
    	.offset = MODEM_INFO_RSRP_OFFSET_VAL,
    };
    #endif /* CONFIG_MODEM_INFO */
    
    /* Stack definition for application workqueue */
    K_THREAD_STACK_DEFINE(application_stack_area,
    		      CONFIG_APPLICATION_WORKQUEUE_STACK_SIZE);
    static struct k_work_q application_work_q;
    static struct cloud_backend *cloud_backend;
    
    /* Sensor data */
    static struct gps_nmea gps_data;
    static struct cloud_channel_data gps_cloud_data = {
    	.type = CLOUD_CHANNEL_GPS,
    	.tag = 0x1
    };
    static struct cloud_channel_data button_cloud_data;
    
    static struct time_aux {
    	s64_t date_time_utc;
    	int last_date_time_update;
    } time_aux;
    
    
    #if CONFIG_MODEM_INFO
    static struct modem_param_info modem_param;
    static struct cloud_channel_data signal_strength_cloud_data;
    #endif /* CONFIG_MODEM_INFO */
    
    static s64_t gps_last_active_time;
    static atomic_t carrier_requested_disconnect;
    static atomic_t cloud_connect_attempts;
    
    /* Flag used for flip detection */
    static bool flip_mode_enabled = true;
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    /* Current state of activity monitor */
    static motion_activity_state_t last_activity_state = MOTION_ACTIVITY_NOT_KNOWN;
    #endif
    
    /* Variable to keep track of nRF cloud association state. */
    enum cloud_association_state {
    	CLOUD_ASSOCIATION_STATE_INIT,
    	CLOUD_ASSOCIATION_STATE_REQUESTED,
    	CLOUD_ASSOCIATION_STATE_PAIRED,
    	CLOUD_ASSOCIATION_STATE_RECONNECT,
    	CLOUD_ASSOCIATION_STATE_READY,
    };
    static atomic_val_t cloud_association =
    	ATOMIC_INIT(CLOUD_ASSOCIATION_STATE_INIT);
    
    /* Structures for work */
    static struct k_work send_gps_data_work;
    static struct k_work send_button_data_work;
    static struct k_work send_modem_at_cmd_work;
    static struct k_delayed_work long_press_button_work;
    static struct k_delayed_work cloud_reboot_work;
    static struct k_delayed_work cycle_cloud_connection_work;
    static struct k_delayed_work device_config_work;
    static struct k_delayed_work cloud_connect_work;
    static struct k_work device_status_work;
    static struct k_delayed_work send_agps_request_work;
    
    #if defined(CONFIG_AT_CMD)
    #define MODEM_AT_CMD_BUFFER_LEN (CONFIG_AT_CMD_RESPONSE_MAX_LEN + 1)
    #else
    #define MODEM_AT_CMD_NOT_ENABLED_STR "Error: AT Command driver is not enabled"
    #define MODEM_AT_CMD_BUFFER_LEN (sizeof(MODEM_AT_CMD_NOT_ENABLED_STR))
    #endif
    #define MODEM_AT_CMD_RESP_TOO_BIG_STR "Error: AT Command response is too large to be sent"
    #define MODEM_AT_CMD_MAX_RESPONSE_LEN (2000)
    static char modem_at_cmd_buff[MODEM_AT_CMD_BUFFER_LEN];
    static K_SEM_DEFINE(modem_at_cmd_sem, 1, 1);
    static K_SEM_DEFINE(cloud_disconnected, 0, 1);
    #if defined(CONFIG_LWM2M_CARRIER)
    static void app_disconnect(void);
    static K_SEM_DEFINE(bsdlib_initialized, 0, 1);
    static K_SEM_DEFINE(lte_connected, 0, 1);
    static K_SEM_DEFINE(cloud_ready_to_connect, 0, 1);
    #endif
    
    #if CONFIG_MODEM_INFO
    static struct k_delayed_work rsrp_work;
    #endif /* CONFIG_MODEM_INFO */
    
    enum error_type {
    	ERROR_CLOUD,
    	ERROR_BSD_RECOVERABLE,
    	ERROR_LTE_LC,
    	ERROR_SYSTEM_FAULT
    };
    
    /* Forward declaration of functions */
    static void motion_handler(motion_data_t  motion_data);
    static void env_data_send(void);
    #if CONFIG_LIGHT_SENSOR
    static void light_sensor_data_send(void);
    #endif /* CONFIG_LIGHT_SENSOR */
    static void sensors_init(void);
    static void work_init(void);
    static void sensor_data_send(struct cloud_channel_data *data);
    static void device_status_send(struct k_work *work);
    static void cycle_cloud_connection(struct k_work *work);
    static void set_gps_enable(const bool enable);
    static bool data_send_enabled(void);
    static void connection_evt_handler(const struct cloud_event *const evt);
    static void date_time_handler(struct k_work *work);
    
    static void shutdown_modem(void)
    {
    #if defined(CONFIG_LTE_LINK_CONTROL)
    	/* Turn off and shutdown modem */
    	LOG_ERR("LTE link disconnect");
    	int err = lte_lc_power_off();
    
    	if (err) {
    		LOG_ERR("lte_lc_power_off failed: %d", err);
    	}
    #endif /* CONFIG_LTE_LINK_CONTROL */
    #if defined(CONFIG_BSD_LIBRARY)
    	LOG_ERR("Shutdown modem");
    	bsdlib_shutdown();
    #endif
    }
    
    
    
    
    
    #if defined(CONFIG_LWM2M_CARRIER)
    int lwm2m_carrier_event_handler(const lwm2m_carrier_event_t *event)
    {
    	switch (event->type) {
    	case LWM2M_CARRIER_EVENT_BSDLIB_INIT:
    		LOG_INF("LWM2M_CARRIER_EVENT_BSDLIB_INIT");
    		k_sem_give(&bsdlib_initialized);
    		break;
    	case LWM2M_CARRIER_EVENT_CONNECTING:
    		LOG_INF("LWM2M_CARRIER_EVENT_CONNECTING\n");
    		break;
    	case LWM2M_CARRIER_EVENT_CONNECTED:
    		LOG_INF("LWM2M_CARRIER_EVENT_CONNECTED");
    		k_sem_give(&lte_connected);
    		break;
    	case LWM2M_CARRIER_EVENT_DISCONNECTING:
    		LOG_INF("LWM2M_CARRIER_EVENT_DISCONNECTING");
    		break;
    	case LWM2M_CARRIER_EVENT_BOOTSTRAPPED:
    		LOG_INF("LWM2M_CARRIER_EVENT_BOOTSTRAPPED");
    		break;
    	case LWM2M_CARRIER_EVENT_DISCONNECTED:
    		LOG_INF("LWM2M_CARRIER_EVENT_DISCONNECTED");
    		break;
    	case LWM2M_CARRIER_EVENT_READY:
    		LOG_INF("LWM2M_CARRIER_EVENT_READY");
    		k_sem_give(&cloud_ready_to_connect);
    		break;
    	case LWM2M_CARRIER_EVENT_FOTA_START:
    		LOG_INF("LWM2M_CARRIER_EVENT_FOTA_START");
    		/* Due to limitations in the number of secure sockets,
    		 * the cloud socket has to be closed when the carrier
    		 * library initiates firmware upgrade download.
    		 */
    		atomic_set(&carrier_requested_disconnect, 1);
    		app_disconnect();
    		break;
    	case LWM2M_CARRIER_EVENT_REBOOT:
    		LOG_INF("LWM2M_CARRIER_EVENT_REBOOT");
    		break;
    	case LWM2M_CARRIER_EVENT_ERROR:
    		LOG_ERR("LWM2M_CARRIER_EVENT_ERROR: code %d, value %d",
    			((lwm2m_carrier_event_error_t *)event->data)->code,
    			((lwm2m_carrier_event_error_t *)event->data)->value);
    		break;
    	}
    
    	return 0;
    }
    
    /**@brief Disconnects from cloud. First it tries using the cloud backend's
     *        disconnect() implementation. If that fails, it falls back to close the
     *        socket directly, using close().
     */
    static void app_disconnect(void)
    {
    	int err;
    
    	atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_INIT);
    	LOG_INF("Disconnecting from cloud.");
    
    	err = cloud_disconnect(cloud_backend);
    	if (err == 0) {
    		/* Ensure that the socket is indeed closed before returning. */
    		if (k_sem_take(&cloud_disconnected, K_MINUTES(1)) == 0) {
    			LOG_INF("Disconnected from cloud.");
    			return;
    		}
    	} else if (err == -ENOTCONN) {
    		LOG_INF("Cloud connection was not established.");
    		return;
    	} else {
    		LOG_ERR("Could not disconnect from cloud, err: %d", err);
    	}
    
    	LOG_INF("Closing the cloud socket directly.");
    
    	err = close(cloud_backend->config->socket);
    	if (err) {
    		LOG_ERR("Failed to close socket, error: %d", err);
    		return;
    	}
    
    	LOG_INF("Socket was closed successfully.");
    	return;
    }
    #endif /* defined(CONFIG_LWM2M_CARRIER) */
    
    /**@brief nRF Cloud error handler. */
    void error_handler(enum error_type err_type, int err_code)
    {
    	atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_INIT);
    
    	if (err_type == ERROR_CLOUD) {
    		shutdown_modem();
    	}
    
    #if !defined(CONFIG_DEBUG) && defined(CONFIG_REBOOT)
    	LOG_PANIC();
    	sys_reboot(0);
    #else
    	switch (err_type) {
    	case ERROR_CLOUD:
    		/* Blinking all LEDs ON/OFF in pairs (1 and 4, 2 and 3)
    		 * if there is an application error.
    		 */
    		ui_led_set_pattern(UI_LED_ERROR_CLOUD);
    		LOG_ERR("Error of type ERROR_CLOUD: %d", err_code);
    	break;
    	case ERROR_BSD_RECOVERABLE:
    		/* Blinking all LEDs ON/OFF in pairs (1 and 3, 2 and 4)
    		 * if there is a recoverable error.
    		 */
    		ui_led_set_pattern(UI_LED_ERROR_BSD_REC);
    		LOG_ERR("Error of type ERROR_BSD_RECOVERABLE: %d", err_code);
    	break;
    	default:
    		/* Blinking all LEDs ON/OFF in pairs (1 and 2, 3 and 4)
    		 * undefined error.
    		 */
    		ui_led_set_pattern(UI_LED_ERROR_UNKNOWN);
    		LOG_ERR("Unknown error type: %d, code: %d",
    			err_type, err_code);
    	break;
    	}
    
    	while (true) {
    		k_cpu_idle();
    	}
    #endif /* CONFIG_DEBUG */
    }
    
    void k_sys_fatal_error_handler(unsigned int reason,
    			       const z_arch_esf_t *esf)
    {
    	ARG_UNUSED(esf);
    
    	LOG_PANIC();
    	LOG_ERR("Running main.c error handler");
    	error_handler(ERROR_SYSTEM_FAULT, reason);
    	CODE_UNREACHABLE;
    }
    
    void cloud_error_handler(int err)
    {
    	error_handler(ERROR_CLOUD, err);
    }
    
    static void send_agps_request(struct k_work *work)
    {
    	ARG_UNUSED(work);
    
    #if defined(CONFIG_NRF_CLOUD_AGPS)
    	int err;
    	static s64_t last_request_timestamp;
    
    /* Request A-GPS data no more often than every hour (time in milliseconds). */
    #define AGPS_UPDATE_PERIOD (60 * 60 * 1000)
    
    	if ((last_request_timestamp != 0) &&
    	    (k_uptime_get() - last_request_timestamp) < AGPS_UPDATE_PERIOD) {
    		LOG_WRN("A-GPS request was sent less than 1 hour ago");
    		return;
    	}
    
    	LOG_INF("Sending A-GPS request");
    
    	err = nrf_cloud_agps_request_all();
    	if (err) {
    		LOG_ERR("A-GPS request failed, error: %d", err);
    		return;
    	}
    
    	last_request_timestamp = k_uptime_get();
    
    	LOG_INF("A-GPS request sent");
    #endif /* defined(CONFIG_NRF_CLOUD_AGPS) */
    }
    
    void cloud_connect_error_handler(enum cloud_connect_result err)
    {
    	bool reboot = true;
    	char *backend_name = "invalid";
    
    	if (err == CLOUD_CONNECT_RES_SUCCESS) {
    		return;
    	}
    
    	LOG_ERR("Failed to connect to cloud, error %d", err);
    
    	switch (err) {
    	case CLOUD_CONNECT_RES_ERR_NOT_INITD: {
    		LOG_ERR("Cloud back-end has not been initialized");
    		/* no need to reboot, program error */
    		reboot = false;
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_NETWORK: {
    		LOG_ERR("Network error, check cloud configuration");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_BACKEND: {
    		if (cloud_backend && cloud_backend->config &&
    		    cloud_backend->config->name) {
    			backend_name = cloud_backend->config->name;
    		}
    		LOG_ERR("An error occurred specific to the cloud back-end: %s",
    			backend_name);
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_PRV_KEY: {
    		LOG_ERR("Ensure device has a valid private key");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_CERT: {
    		LOG_ERR("Ensure device has a valid CA and client certificate");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_CERT_MISC: {
    		LOG_ERR("A certificate/authorization error has occurred");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_TIMEOUT_NO_DATA: {
    		LOG_ERR("Connect timeout. SIM card may be out of data");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_ALREADY_CONNECTED: {
    		LOG_ERR("Connection already exists.");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_MISC: {
    		break;
    	}
    	default: {
    		LOG_ERR("Unhandled connect error");
    		break;
    	}
    	}
    
    	if (reboot) {
    		LOG_ERR("Device will reboot in %d seconds",
    				CONFIG_CLOUD_CONNECT_ERR_REBOOT_S);
    		k_delayed_work_submit_to_queue(
    			&application_work_q, &cloud_reboot_work,
    			K_SECONDS(CONFIG_CLOUD_CONNECT_ERR_REBOOT_S));
    	}
    
    	ui_led_set_pattern(UI_LED_ERROR_CLOUD);
    	shutdown_modem();
    	k_thread_suspend(k_current_get());
    }
    
    /**@brief Recoverable BSD library error. */
    void bsd_recoverable_error_handler(uint32_t err)
    {
    	error_handler(ERROR_BSD_RECOVERABLE, (int)err);
    }
    
    static void send_gps_data_work_fn(struct k_work *work)
    {
    	sensor_data_send(&gps_cloud_data);
    }
    
    static void send_button_data_work_fn(struct k_work *work)
    {
    	sensor_data_send(&button_cloud_data);
    }
    
    void connect_to_cloud(const s32_t connect_delay_s)
    {
    	static bool initial_connect = true;
    
    	/* Ensure no data can be sent to cloud before connection is established.
    	 */
    	atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_INIT);
    
    	if (atomic_get(&carrier_requested_disconnect)) {
    		/* A disconnect was requested to free up the TLS socket
    		 * used by the cloud.  If enabled, the carrier library
    		 * (CONFIG_LWM2M_CARRIER) will perform FOTA updates in
    		 * the background and reboot the device when complete.
    		 */
    		return;
    	}
    
    	atomic_inc(&cloud_connect_attempts);
    
    	/* Check if max cloud connect retry count is exceeded. */
    	if (atomic_get(&cloud_connect_attempts) >
    	    CONFIG_CLOUD_CONNECT_COUNT_MAX) {
    		LOG_ERR("The max cloud connection attempt count exceeded.");
    		cloud_error_handler(-ETIMEDOUT);
    	}
    
    	if (!initial_connect) {
    		LOG_INF("Attempting reconnect in %d seconds...",
    			connect_delay_s);
    		k_delayed_work_cancel(&cloud_reboot_work);
    	} else {
    		initial_connect = false;
    	}
    
    	k_delayed_work_submit_to_queue(&application_work_q,
    				       &cloud_connect_work,
    				       K_SECONDS(connect_delay_s));
    }
    
    static void cloud_connect_work_fn(struct k_work *work)
    {
    	int ret;
    
    	LOG_INF("Connecting to cloud, attempt %d of %d",
    	       atomic_get(&cloud_connect_attempts),
    		   CONFIG_CLOUD_CONNECT_COUNT_MAX);
    
    	k_delayed_work_submit_to_queue(&application_work_q,
    			&cloud_reboot_work,
    			K_MSEC(CLOUD_CONNACK_WAIT_DURATION));
    
    	ui_led_set_pattern(UI_CLOUD_CONNECTING);
    
    	/* Attempt cloud connection */
    	ret = cloud_connect(cloud_backend);
    	if (ret != CLOUD_CONNECT_RES_SUCCESS) {
    		k_delayed_work_cancel(&cloud_reboot_work);
    		/* Will not return from this function.
    		 * If the connect fails here, it is likely
    		 * that user intervention is required.
    		 */
    		cloud_connect_error_handler(ret);
    	} else {
    		LOG_INF("Cloud connection request sent.");
    		LOG_INF("Connection response timeout is set to %d seconds.",
    		       CLOUD_CONNACK_WAIT_DURATION / MSEC_PER_SEC);
    		k_delayed_work_submit_to_queue(&application_work_q,
    					&cloud_reboot_work,
    					K_MSEC(CLOUD_CONNACK_WAIT_DURATION));
    	}
    }
    
    static void send_modem_at_cmd_work_fn(struct k_work *work)
    {
    	enum at_cmd_state state;
    	int err;
    	struct cloud_channel_data modem_data = {
    		.type = CLOUD_CHANNEL_MODEM
    	};
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_MSG
    	};
    	size_t len = strlen(modem_at_cmd_buff);
    
    	if (len == 0) {
    		err = -ENOBUFS;
    		state = AT_CMD_ERROR;
    	} else {
    #if defined(CONFIG_AT_CMD)
    		err = at_cmd_write(modem_at_cmd_buff, modem_at_cmd_buff,
    				sizeof(modem_at_cmd_buff), &state);
    #else
    		/* Proper error msg has already been copied to buffer */
    		err = 0;
    #endif
    	}
    
    	/* Get response length; same buffer was used for the response. */
    	len = strlen(modem_at_cmd_buff);
    
    	if (err) {
    		len = snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    				"AT CMD error: %d, state %d", err, state);
    	} else if (len == 0) {
    		len = snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    				"OK\r\n");
    	} else if (len > MODEM_AT_CMD_MAX_RESPONSE_LEN) {
    		len = snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    				MODEM_AT_CMD_RESP_TOO_BIG_STR);
    	}
    
    	modem_data.data.buf = modem_at_cmd_buff;
    	modem_data.data.len = len;
    
    	err = cloud_encode_data(&modem_data, CLOUD_CMD_GROUP_COMMAND, &msg);
    	if (err) {
    		LOG_ERR("[%s:%d] cloud_encode_data failed with error %d",
    			__func__, __LINE__, err);
    	} else {
    		err = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    		if (err) {
    			LOG_ERR("[%s:%d] cloud_send failed with error %d",
    				__func__, __LINE__, err);
    		}
    	}
    
    	k_sem_give(&modem_at_cmd_sem);
    }
    
    
    
    
    static void gps_handler(struct device *dev, struct gps_event *evt)
    {
    	gps_last_active_time = k_uptime_get();
    
    	switch (evt->type) {
    	case GPS_EVT_SEARCH_STARTED:
    		LOG_INF("GPS_EVT_SEARCH_STARTED");
    		gps_control_set_active(true);
    		ui_led_set_pattern(UI_LED_GPS_SEARCHING);
    		break;
    	case GPS_EVT_SEARCH_STOPPED:
    		LOG_INF("GPS_EVT_SEARCH_STOPPED");
    		gps_control_set_active(false);
    		ui_led_set_pattern(UI_CLOUD_CONNECTED);
    		break;
    	case GPS_EVT_SEARCH_TIMEOUT:
    		LOG_INF("GPS_EVT_SEARCH_TIMEOUT");
    		gps_control_set_active(false);
    		LOG_INF("GPS will be attempted again in %d seconds",
    			gps_control_get_gps_reporting_interval());
    		break;
    	case GPS_EVT_PVT:
    		/* Don't spam logs */
    		break;
    	case GPS_EVT_PVT_FIX:
    		LOG_INF("GPS_EVT_PVT_FIX");
    		break;
    	case GPS_EVT_NMEA:
    		/* Don't spam logs */
    		break;
    	case GPS_EVT_NMEA_FIX:
    		LOG_INF("Position fix with NMEA data");
    
    		memcpy(gps_data.buf, evt->nmea.buf, evt->nmea.len);
    		gps_data.len = evt->nmea.len;
    		gps_cloud_data.data.buf = gps_data.buf;
    		gps_cloud_data.data.len = gps_data.len;
    		gps_cloud_data.tag += 1;
    
    		if (gps_cloud_data.tag == 0) {
    			gps_cloud_data.tag = 0x1;
    		}
    
    		ui_led_set_pattern(UI_LED_GPS_FIX);
    		gps_control_set_active(false);
    		LOG_INF("GPS will be started in %d seconds",
    			gps_control_get_gps_reporting_interval());
    
    		k_work_submit_to_queue(&application_work_q,
    				       &send_gps_data_work);
    		env_sensors_poll();
    		break;
    	case GPS_EVT_OPERATION_BLOCKED:
    		LOG_INF("GPS_EVT_OPERATION_BLOCKED");
    		ui_led_set_pattern(UI_LED_GPS_BLOCKED);
    		break;
    	case GPS_EVT_OPERATION_UNBLOCKED:
    		LOG_INF("GPS_EVT_OPERATION_UNBLOCKED");
    		ui_led_set_pattern(UI_LED_GPS_SEARCHING);
    		break;
    	case GPS_EVT_AGPS_DATA_NEEDED:
    		LOG_INF("GPS_EVT_AGPS_DATA_NEEDED");
    		/* Send A-GPS request with short delay to avoid LTE network-
    		 * dependent corner-case where the request would not be sent.
    		 */
    		k_delayed_work_submit_to_queue(&application_work_q,
    					       &send_agps_request_work,
    					       K_SECONDS(1));
    		break;
    	case GPS_EVT_ERROR:
    		LOG_INF("GPS_EVT_ERROR\n");
    		break;
    	default:
    		break;
    	}
    }
    
    #if defined(CONFIG_USE_UI_MODULE)
    /**@brief Send button presses to cloud */
    static void button_send(bool pressed)
    {
    	static char data[] = "1";
    
    	if (!data_send_enabled()) {
    		return;
    	}
    
    	if (pressed) {
    		data[0] = '1';
    	} else {
    		data[0] = '0';
    	}
    
    	button_cloud_data.data.buf = data;
    	button_cloud_data.data.len = strlen(data);
    	button_cloud_data.tag += 1;
    
    	if (button_cloud_data.tag == 0) {
    		button_cloud_data.tag = 0x1;
    	}
    
    	k_work_submit_to_queue(&application_work_q, &send_button_data_work);
    }
    #endif
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    static bool motion_activity_is_active(void)
    {
    	return (last_activity_state == MOTION_ACTIVITY_ACTIVE);
    }
    
    static void motion_trigger_gps(motion_data_t  motion_data)
    {
    	static bool initial_run = true;
    
    	/* The handler is triggered once on startup, regardless of motion,
    	 * in order to get into a known state. This should be ignored.
    	 */
    	if (initial_run) {
    		initial_run = false;
    		return;
    	}
    
    	if (motion_activity_is_active() && gps_control_is_enabled()) {
    		static s64_t next_active_time;
    		s64_t last_active_time = gps_last_active_time / 1000;
    		s64_t now = k_uptime_get() / 1000;
    		s64_t time_since_fix_attempt = now - last_active_time;
    		s64_t time_until_next_attempt = next_active_time - now;
    
    		LOG_DBG("Last at %lld s, now %lld s, next %lld s; "
    			"%lld secs since last, %lld secs until next",
    			last_active_time, now, next_active_time,
    			time_since_fix_attempt, time_until_next_attempt);
    
    		if (time_until_next_attempt >= 0) {
    			LOG_DBG("keeping original schedule.");
    			return;
    		}
    
    		time_since_fix_attempt = MAX(0, (MIN(time_since_fix_attempt,
    			CONFIG_GPS_CONTROL_FIX_CHECK_INTERVAL)));
    		s64_t time_to_start_next_fix = 1 +
    			CONFIG_GPS_CONTROL_FIX_CHECK_INTERVAL -
    			time_since_fix_attempt;
    
    		next_active_time = now + time_to_start_next_fix;
    
    		char buf[100];
    
    		/* due to a known design issue in Zephyr, we need to use
    		 * snprintf to output floats; see:
    		 * https://github.com/zephyrproject-rtos/zephyr/issues/18351
    		 * https://github.com/zephyrproject-rtos/zephyr/pull/18921
    		 */
    		snprintf(buf, sizeof(buf),
    			"Motion triggering GPS; accel, %.1f, %.1f, %.1f",
    			motion_data.acceleration.x,
    			motion_data.acceleration.y,
    			motion_data.acceleration.z);
    		LOG_INF("%s", log_strdup(buf));
    
    		LOG_INF("starting GPS in %lld seconds", time_to_start_next_fix);
    		gps_control_start(time_to_start_next_fix * MSEC_PER_SEC);
    	}
    }
    #endif
    
    
    // Can be used or interrupt handling
    
    /**@brief Callback from the motion module. Sends motion data to cloud. */
    static void motion_handler(motion_data_t  motion_data)
    {
    	static motion_orientation_state_t last_orientation_state =
    		MOTION_ORIENTATION_NOT_KNOWN;
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    	/* toggle state since the accelerometer does not yet report
    	 * which state occurred
    	 */
    	last_activity_state = (last_activity_state != MOTION_ACTIVITY_ACTIVE) ?
    			      MOTION_ACTIVITY_ACTIVE : MOTION_ACTIVITY_INACTIVE;
    #endif
    
    	if (!flip_mode_enabled || !data_send_enabled() ||
    	    gps_control_is_active()) {
    		return;
    	}
    
    	if (motion_data.orientation != last_orientation_state) {
    
    		if (flip_mode_enabled && data_send_enabled()) {
    
    			struct cloud_msg msg = {
    				.qos = CLOUD_QOS_AT_MOST_ONCE,
    				.endpoint.type = CLOUD_EP_TOPIC_MSG
    			};
    
    			int err = 0;
    
    			if (cloud_encode_motion_data(&motion_data, &msg) == 0) {
    				err = cloud_send(cloud_backend, &msg);
    				cloud_release_data(&msg);
    				if (err) {
    					LOG_ERR("Transmisison of "
    						"motion data failed: %d", err);
    					cloud_error_handler(err);
    				}
    			}
    
    			if (!err) {
    				last_orientation_state =
    					motion_data.orientation;
    			}
    		}
    	}
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    	motion_trigger_gps(motion_data);
    #endif
    }
    
    static void cloud_cmd_handle_modem_at_cmd(const char *const at_cmd)
    {
    	if (!at_cmd) {
    		return;
    	}
    
    	if (k_sem_take(&modem_at_cmd_sem, K_MSEC(20)) != 0) {
    		LOG_ERR("[%s:%d] Modem AT cmd in progress.", __func__,
    		       __LINE__);
    		return;
    	}
    
    #if defined(CONFIG_AT_CMD)
    	const size_t max_cmd_len = sizeof(modem_at_cmd_buff);
    
    	if (strnlen(at_cmd, max_cmd_len) == max_cmd_len) {
    		LOG_ERR("[%s:%d] AT cmd is too long, max length is %zu",
    		       __func__, __LINE__, max_cmd_len - 1);
    		/* Empty string will be handled as an error */
    		modem_at_cmd_buff[0] = '\0';
    	} else {
    		strcpy(modem_at_cmd_buff, at_cmd);
    	}
    #else
    	snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    			MODEM_AT_CMD_NOT_ENABLED_STR);
    #endif
    
    	k_work_submit_to_queue(&application_work_q, &send_modem_at_cmd_work);
    }
    
    static void cloud_cmd_handler(struct cloud_command *cmd)
    {
    	if ((cmd->channel == CLOUD_CHANNEL_GPS) &&
    	    (cmd->group == CLOUD_CMD_GROUP_CFG_SET) &&
    	    (cmd->type == CLOUD_CMD_ENABLE)) {
    		set_gps_enable(cmd->data.sv.state == CLOUD_CMD_STATE_TRUE);
    	} else if ((cmd->channel == CLOUD_CHANNEL_MODEM) &&
    		   (cmd->group == CLOUD_CMD_GROUP_COMMAND) &&
    		   (cmd->type == CLOUD_CMD_DATA_STRING)) {
    		cloud_cmd_handle_modem_at_cmd(cmd->data.data_string);
    	} else if ((cmd->channel == CLOUD_CHANNEL_RGB_LED) &&
    		   (cmd->group == CLOUD_CMD_GROUP_CFG_SET) &&
    		   (cmd->type == CLOUD_CMD_COLOR)) {
    		ui_led_set_color(((u32_t)cmd->data.sv.value >> 16) & 0xFF,
    				 ((u32_t)cmd->data.sv.value >> 8) & 0xFF,
    				 ((u32_t)cmd->data.sv.value) & 0xFF);
    	} else if ((cmd->channel == CLOUD_CHANNEL_DEVICE_INFO) &&
    		   (cmd->group == CLOUD_CMD_GROUP_GET) &&
    		   (cmd->type == CLOUD_CMD_EMPTY)) {
    		k_work_submit_to_queue(&application_work_q,
    				       &device_status_work);
    	} else if ((cmd->channel == CLOUD_CHANNEL_LTE_LINK_RSRP) &&
    		   (cmd->group == CLOUD_CMD_GROUP_GET) &&
    		   (cmd->type == CLOUD_CMD_EMPTY)) {
    #if CONFIG_MODEM_INFO
    		k_delayed_work_submit_to_queue(&application_work_q, &rsrp_work,
    					       K_NO_WAIT);
    #endif
    	} else if ((cmd->group == CLOUD_CMD_GROUP_CFG_SET) &&
    			   (cmd->type == CLOUD_CMD_INTERVAL)) {
    		if (cmd->channel == CLOUD_CHANNEL_LIGHT_SENSOR) {
    #if defined(CONFIG_LIGHT_SENSOR)
    			light_sensor_set_send_interval(
    				(u32_t)cmd->data.sv.value);
    #endif
    		} else if (cmd->channel == CLOUD_CHANNEL_ENVIRONMENT) {
    			env_sensors_set_send_interval(
    				(u32_t)cmd->data.sv.value);
    		} else if (cmd->channel == CLOUD_CHANNEL_GPS) {
    			/* TODO: update GPS controller to handle send */
    			/* interval */
    		} else {
    			LOG_ERR("Interval command not valid for channel %d",
    				cmd->channel);
    		}
    	}
    }
    
    #if CONFIG_MODEM_INFO
    /**@brief Callback handler for LTE RSRP data. */
    static void modem_rsrp_handler(char rsrp_value)
    {
    	/* RSRP raw values that represent actual signal strength are
    	 * 0 through 97 (per "nRF91 AT Commands" v1.1). If the received value
    	 * falls outside this range, we should not send the value.
    	 */
    	if (rsrp_value > 97) {
    		return;
    	}
    
    	rsrp.value = rsrp_value;
    
    	/* Only send the RSRP if transmission is not already scheduled.
    	 * Checking CONFIG_HOLD_TIME_RSRP gives the compiler a shortcut.
    	 */
    	if (CONFIG_HOLD_TIME_RSRP == 0 ||
    	    k_delayed_work_remaining_get(&rsrp_work) == 0) {
    		k_delayed_work_submit_to_queue(&application_work_q, &rsrp_work,
    					       K_NO_WAIT);
    	}
    }
    
    /**@brief Publish RSRP data to the cloud. */
    static void modem_rsrp_data_send(struct k_work *work)
    {
    	char buf[CONFIG_MODEM_INFO_BUFFER_SIZE] = {0};
    	static s32_t rsrp_prev; /* RSRP value last sent to cloud */
    	s32_t rsrp_current;
    	size_t len;
    
    	if (!data_send_enabled()) {
    		return;
    	}
    
    	/* The RSRP value is copied locally to avoid any race */
    	rsrp_current = rsrp.value - rsrp.offset;
    
    	if (rsrp_current == rsrp_prev) {
    		return;
    	}
    
    	len = snprintf(buf, CONFIG_MODEM_INFO_BUFFER_SIZE,
    		       "%d", rsrp_current);
    
    	signal_strength_cloud_data.data.buf = buf;
    	signal_strength_cloud_data.data.len = len;
    	signal_strength_cloud_data.tag += 1;
    
    	if (signal_strength_cloud_data.tag == 0) {
    		signal_strength_cloud_data.tag = 0x1;
    	}
    
    	sensor_data_send(&signal_strength_cloud_data);
    	rsrp_prev = rsrp_current;
    
    	if (CONFIG_HOLD_TIME_RSRP > 0) {
    		k_delayed_work_submit_to_queue(&application_work_q, &rsrp_work,
    					      K_SECONDS(CONFIG_HOLD_TIME_RSRP));
    	}
    }
    #endif /* CONFIG_MODEM_INFO */
    
    /**@brief Poll device info and send data to the cloud. */
    static void device_status_send(struct k_work *work)
    {
    	struct modem_param_info *modem_ptr = NULL;
    	int ret;
    
    	if (!data_send_enabled() || gps_control_is_active()) {
    		return;
    	}
    
    #ifdef CONFIG_MODEM_INFO
    	ret = modem_info_params_get(&modem_param);
    	if (ret < 0) {
    		LOG_ERR("Unable to obtain modem parameters: %d", ret);
    	} else {
    		modem_ptr = &modem_param;
    	}
    #endif /* CONFIG_MODEM_INFO */
    
    	const char *const ui[] = {
    		CLOUD_CHANNEL_STR_GPS,
    		CLOUD_CHANNEL_STR_FLIP,
    		CLOUD_CHANNEL_STR_TEMP,
    		CLOUD_CHANNEL_STR_HUMID,
    		CLOUD_CHANNEL_STR_AIR_PRESS,
    #if IS_ENABLED(CONFIG_CLOUD_BUTTON)
    		CLOUD_CHANNEL_STR_BUTTON,
    #endif
    #if IS_ENABLED(CONFIG_LIGHT_SENSOR)
    		CLOUD_CHANNEL_STR_LIGHT_SENSOR,
    #endif
    #if IS_ENABLED(CONFIG_MODEM_INFO)
    		CLOUD_CHANNEL_STR_LTE_LINK_RSRP,
    #endif
    	};
    
    	const char *const fota[] = {
    #if defined(CONFIG_CLOUD_FOTA_APP)
    		SERVICE_INFO_FOTA_STR_APP,
    #endif
    #if defined(CONFIG_CLOUD_FOTA_MODEM)
    		SERVICE_INFO_FOTA_STR_MODEM
    #endif
    	};
    
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_STATE
    	};
    
    	ret = cloud_encode_device_status_data(modem_ptr,
    					      ui, ARRAY_SIZE(ui),
    					      fota, ARRAY_SIZE(fota),
    					      SERVICE_INFO_FOTA_VER_CURRENT,
    					      &msg);
    	if (ret) {
    		LOG_ERR("Unable to encode cloud data: %d", ret);
    	} else {
    		/* Transmits the data to the cloud. */
    		ret = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    		if (ret) {
    			LOG_ERR("sensor_data_send failed: %d", ret);
    			cloud_error_handler(ret);
    		}
    	}
    }
    
    /**@brief Send device config to the cloud. */
    static void device_config_send(struct k_work *work)
    {
    	int ret;
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_STATE
    	};
    	enum cloud_cmd_state gps_cfg_state =
    		cloud_get_channel_enable_state(CLOUD_CHANNEL_GPS);
    
    	if (gps_cfg_state == CLOUD_CMD_STATE_UNDEFINED) {
    		return;
    	}
    
    	if (gps_control_is_active() && gps_cfg_state == CLOUD_CMD_STATE_FALSE) {
    		/* GPS hasn't been stopped yet, reschedule this work */
    		k_delayed_work_submit_to_queue(&application_work_q,
    			&device_config_work, K_SECONDS(5));
    		return;
    	}
    
    	ret = cloud_encode_config_data(&msg);
    	if (ret) {
    		LOG_ERR("Unable to encode cloud data: %d", ret);
    	} else if (msg.len && msg.buf) {
    		ret = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    
    		if (ret) {
    			LOG_ERR("%s failed: %d", __func__, ret);
    			cloud_error_handler(ret);
    		}
    	}
    
    	if (gps_cfg_state == CLOUD_CMD_STATE_TRUE) {
    		gps_control_start(0);
    	}
    }
    
    /**@brief Get environment data from sensors and send to cloud. */
    static void env_data_send(void)
    {
    	int err;
    	env_sensor_data_t env_data;
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_MSG
    	};
    
    	if (!data_send_enabled()) {
    		return;
    	}
    
    	if (gps_control_is_active()) {
    		env_sensors_set_backoff_enable(true);
    		return;
    	}
    
    	env_sensors_set_backoff_enable(false);
    
    	if (env_sensors_get_temperature(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_TEMP, env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	if (env_sensors_get_humidity(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_HUMID,
    					  env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	if (env_sensors_get_pressure(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_AIR_PRESS,
    					  env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	if (env_sensors_get_air_quality(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_AIR_QUAL,
    					  env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	return;
    error:
    	LOG_ERR("sensor_data_send failed: %d", err);
    	cloud_error_handler(err);
    }
    
    #if defined(CONFIG_LIGHT_SENSOR)
    void light_sensor_data_send(void)
    {
    	int err;
    	struct light_sensor_data light_data;
    	struct cloud_msg msg = { .qos = CLOUD_QOS_AT_MOST_ONCE,
    				 .endpoint.type = CLOUD_EP_TOPIC_MSG };
    
    	if (!data_send_enabled() || gps_control_is_active()) {
    		return;
    	}
    
    	err = light_sensor_get_data(&light_data);
    	if (err) {
    		LOG_ERR("Failed to get light sensor data, error %d", err);
    		return;
    	}
    
    	if (!cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_RED, light_data.red) &&
    	    !cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_GREEN,
    				   light_data.green) &&
    	    !cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_BLUE, light_data.blue) &&
    	    !cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_IR, light_data.ir)) {
    		LOG_WRN("Light values not sent due to config settings");
    		return;
    	}
    
    	err = cloud_encode_light_sensor_data(&light_data, &msg);
    	if (err) {
    		LOG_ERR("Failed to encode light sensor data, error %d", err);
    		return;
    	}
    
    	err = cloud_send(cloud_backend, &msg);
    	cloud_release_data(&msg);
    
    	if (err) {
    		LOG_ERR("Failed to send light sensor data to cloud, error: %d",
    		       err);
    		cloud_error_handler(err);
    	}
    }
    #endif /* CONFIG_LIGHT_SENSOR */
    
    /**@brief Send sensor data to nRF Cloud. **/
    static void sensor_data_send(struct cloud_channel_data *data)
    {
    	int err = 0;
    	struct cloud_msg msg = {
    			.qos = CLOUD_QOS_AT_MOST_ONCE,
    			.endpoint.type = CLOUD_EP_TOPIC_MSG
    		};
    
    	if (!data_send_enabled() || gps_control_is_active()) {
    		return;
    	}
    
    	err = cloud_encode_data(data, CLOUD_CMD_GROUP_DATA, &msg);
    	if (err) {
    		LOG_ERR("Unable to encode cloud data: %d", err);
    	} else {
    		err = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    		if (err) {
    			LOG_ERR("%s failed, data was not sent: %d", __func__,
    			err);
    		}
    	}
    }
    
    /**@brief Reboot the device if CONNACK has not arrived. */
    static void cloud_reboot_handler(struct k_work *work)
    {
    	error_handler(ERROR_CLOUD, -ETIMEDOUT);
    }
    
    static bool data_send_enabled(void)
    {
    	return (atomic_get(&cloud_association) ==
    		   CLOUD_ASSOCIATION_STATE_READY);
    }
    
    /**@brief Callback for sensor attached event from nRF Cloud. */
    void sensors_start(void)
    {
    	static bool started;
    	bool start_gps = IS_ENABLED(CONFIG_GPS_START_AFTER_CLOUD_EVT_READY);
    
    	if (!started) {
    		sensors_init();
    
    		/* Value from cloud has precedence */
    		switch (cloud_get_channel_enable_state(CLOUD_CHANNEL_GPS)) {
    		case CLOUD_CMD_STATE_FALSE:
    			start_gps = false;
    			break;
    		case CLOUD_CMD_STATE_TRUE:
    			start_gps = true;
    			break;
    		case CLOUD_CMD_STATE_UNDEFINED:
    		default:
    			break;
    		}
    		set_gps_enable(start_gps);
    		started = true;
    	}
    }
    
    /**@brief nRF Cloud specific callback for cloud association event. */
    static void on_user_pairing_req(const struct cloud_event *evt)
    {
    	if (atomic_get(&cloud_association) !=
    		CLOUD_ASSOCIATION_STATE_REQUESTED) {
    		atomic_set(&cloud_association,
    				   CLOUD_ASSOCIATION_STATE_REQUESTED);
    		ui_led_set_pattern(UI_CLOUD_PAIRING);
    		LOG_INF("Add device to cloud account.");
    		LOG_INF("Waiting for cloud association...");
    
    		/* If the association is not done soon enough (< ~5 min?)
    		 * a connection cycle is needed... TBD why.
    		 */
    		k_delayed_work_submit_to_queue(&application_work_q,
    					       &cycle_cloud_connection_work,
    					       CONN_CYCLE_AFTER_ASSOCIATION_REQ_MS);
    	}
    }
    
    static void cycle_cloud_connection(struct k_work *work)
    {
    	s32_t reboot_wait_ms = REBOOT_AFTER_DISCONNECT_WAIT_MS;
    
    	LOG_INF("Disconnecting from cloud...");
    
    	if (cloud_disconnect(cloud_backend) != 0) {
    		reboot_wait_ms = 5 * MSEC_PER_SEC;
    		LOG_INF("Disconnect failed. Device will reboot in %d seconds",
    			(reboot_wait_ms / MSEC_PER_SEC));
    	}
    
    	/* Reboot fail-safe on disconnect */
    	k_delayed_work_submit_to_queue(&application_work_q, &cloud_reboot_work,
    				       K_MSEC(reboot_wait_ms));
    }
    
    /** @brief Handle procedures after successful association with nRF Cloud. */
    void on_pairing_done(void)
    {
    	if (atomic_get(&cloud_association) ==
    			CLOUD_ASSOCIATION_STATE_REQUESTED) {
    		k_delayed_work_cancel(&cycle_cloud_connection_work);
    
    		/* After successful association, the device must
    		 * reconnect to the cloud.
    		 */
    		LOG_INF("Device associated with cloud.");
    		LOG_INF("Reconnecting for cloud policy to take effect.");
    		atomic_set(&cloud_association,
    				   CLOUD_ASSOCIATION_STATE_RECONNECT);
    		k_delayed_work_submit_to_queue(&application_work_q,
    					       &cycle_cloud_connection_work,
    					       K_NO_WAIT);
    	} else {
    		atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_PAIRED);
    	}
    }
    
    void cloud_event_handler(const struct cloud_backend *const backend,
    			 const struct cloud_event *const evt,
    			 void *user_data)
    {
    	ARG_UNUSED(user_data);
    
    	switch (evt->type) {
    	case CLOUD_EVT_CONNECTED:
    	case CLOUD_EVT_CONNECTING:
    	case CLOUD_EVT_DISCONNECTED:
    		connection_evt_handler(evt);
    		break;
    	case CLOUD_EVT_READY:
    		LOG_INF("CLOUD_EVT_READY");
    		ui_led_set_pattern(UI_CLOUD_CONNECTED);
    
    #if defined(CONFIG_BOOTLOADER_MCUBOOT)
    		/* Mark image as good to avoid rolling back after update */
    		boot_write_img_confirmed();
    #endif
    		atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_READY);
    		sensors_start();
    		break;
    	case CLOUD_EVT_ERROR:
    		LOG_INF("CLOUD_EVT_ERROR");
    		break;
    	case CLOUD_EVT_DATA_SENT:
    		LOG_INF("CLOUD_EVT_DATA_SENT");
    		break;
    	case CLOUD_EVT_DATA_RECEIVED: {
    		int err;
    
    		LOG_INF("CLOUD_EVT_DATA_RECEIVED");
    		err = cloud_decode_command(evt->data.msg.buf);
    		if (err == 0) {
    			/* Cloud decoder has handled the data */
    			return;
    		}
    
    #if defined(CONFIG_NRF_CLOUD_AGPS)
    		/* The decoder didn't handle the data, check if it's A-GPS data
    		 */
    		err = nrf_cloud_agps_process(evt->data.msg.buf,
    					     evt->data.msg.len,
    					     NULL);
    		if (err) {
    			LOG_WRN("Data was not valid A-GPS data, err: %d", err);
    			break;
    		}
    
    		LOG_INF("A-GPS data processed");
    #endif /* defined(CONFIG_GPS_USE_AGPS) */
    		break;
    	}
    	case CLOUD_EVT_PAIR_REQUEST:
    		LOG_INF("CLOUD_EVT_PAIR_REQUEST");
    		on_user_pairing_req(evt);
    		break;
    	case CLOUD_EVT_PAIR_DONE:
    		LOG_INF("CLOUD_EVT_PAIR_DONE");
    		on_pairing_done();
    		break;
    	case CLOUD_EVT_FOTA_DONE:
    		LOG_INF("CLOUD_EVT_FOTA_DONE");
    #if defined(CONFIG_LTE_LINK_CONTROL)
    		lte_lc_power_off();
    #endif
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	default:
    		LOG_WRN("Unknown cloud event type: %d", evt->type);
    		break;
    	}
    }
    
    void connection_evt_handler(const struct cloud_event *const evt)
    {
    	if (evt->type == CLOUD_EVT_CONNECTING) {
    		LOG_INF("CLOUD_EVT_CONNECTING");
    		ui_led_set_pattern(UI_CLOUD_CONNECTING);
    		k_delayed_work_cancel(&cloud_reboot_work);
    
    		if (evt->data.err != CLOUD_CONNECT_RES_SUCCESS) {
    			cloud_connect_error_handler(evt->data.err);
    		}
    		return;
    	} else if (evt->type == CLOUD_EVT_CONNECTED) {
    		LOG_INF("CLOUD_EVT_CONNECTED");
    		k_delayed_work_cancel(&cloud_reboot_work);
    		k_sem_take(&cloud_disconnected, K_NO_WAIT);
    		atomic_set(&cloud_connect_attempts, 0);
    #if defined(CONFIG_CLOUD_PERSISTENT_SESSIONS)
    		LOG_INF("Persistent Sessions = %u",
    			evt->data.persistent_session);
    #endif
    	} else if (evt->type == CLOUD_EVT_DISCONNECTED) {
    		s32_t connect_wait_s = CONFIG_CLOUD_CONNECT_RETRY_DELAY;
    
    		LOG_INF("CLOUD_EVT_DISCONNECTED: %d", evt->data.err);
    		ui_led_set_pattern(UI_LTE_CONNECTED);
    
    		switch (evt->data.err) {
    		case CLOUD_DISCONNECT_INVALID_REQUEST:
    			LOG_INF("Cloud connection closed.");
    			if ((atomic_get(&cloud_connect_attempts) == 1) &&
    			    (atomic_get(&cloud_association) ==
    			     CLOUD_ASSOCIATION_STATE_INIT)) {
    				LOG_INF("This can occur during initial nRF Cloud provisioning.");
    #if defined(CONFIG_LWM2M_CARRIER)
    #if !defined(CONFIG_DEBUG) && defined(CONFIG_REBOOT)
    				/* Reconnect does not work with carrier library */
    				LOG_INF("Rebooting in 10 seconds...");
    				k_sleep(K_SECONDS(10));
    #endif
    				error_handler(ERROR_CLOUD, -EIO);
    				break;
    #endif
    				connect_wait_s = 10;
    			} else {
    				LOG_INF("This can occur if the device has the wrong nRF Cloud certificates.");
    			}
    			break;
    		case CLOUD_DISCONNECT_USER_REQUEST:
    			if (atomic_get(&cloud_association) ==
    			    CLOUD_ASSOCIATION_STATE_RECONNECT ||
    			    atomic_get(&cloud_association) ==
    			    CLOUD_ASSOCIATION_STATE_REQUESTED ||
    			    (atomic_get(&carrier_requested_disconnect))) {
    				connect_wait_s = 10;
    			}
    			break;
    		case CLOUD_DISCONNECT_CLOSED_BY_REMOTE:
    			LOG_INF("Disconnected by the cloud.");
    			break;
    		case CLOUD_DISCONNECT_MISC:
    		default:
    			break;
    		}
    		k_sem_give(&cloud_disconnected);
    		connect_to_cloud(connect_wait_s);
    	}
    }
    
    static void set_gps_enable(const bool enable)
    {
    	s32_t delay_ms = 0;
    	bool changing = (enable != gps_control_is_enabled());
    
    	/* Exit early if the link is not ready or if the cloud
    	 * state is defined and the local state is not changing.
    	 */
    	if (!data_send_enabled() ||
    	    ((cloud_get_channel_enable_state(CLOUD_CHANNEL_GPS) !=
    	    CLOUD_CMD_STATE_UNDEFINED) && !changing)) {
    		return;
    	}
    
    	cloud_set_channel_enable_state(CLOUD_CHANNEL_GPS,
    		enable ? CLOUD_CMD_STATE_TRUE : CLOUD_CMD_STATE_FALSE);
    
    	if (changing) {
    		if (enable) {
    			LOG_INF("Starting GPS");
    			/* GPS will be started from the device config work
    			 * handler AFTER the config has been sent to the cloud
    			 */
    		} else {
    			LOG_INF("Stopping GPS");
    			gps_control_stop(0);
    			/* Allow time for the gps to be stopped before
    			 * attemping to send the config update
    			 */
    			delay_ms = 5 * MSEC_PER_SEC;
    		}
    	}
    
    	/* Update config state in cloud */
    	k_delayed_work_submit_to_queue(&application_work_q,
    			&device_config_work, K_MSEC(delay_ms));
    }
    
    static void long_press_handler(struct k_work *work)
    {
    	ARG_UNUSED(work);
    
    	if (!data_send_enabled()) {
    		LOG_INF("Link not ready, long press disregarded");
    		return;
    	}
    
    	set_gps_enable(!gps_control_is_enabled());
    }
    
    /**@brief Initializes and submits delayed work. */
    static void work_init(void)
    {
    	k_work_init(&send_gps_data_work, send_gps_data_work_fn);
    	k_work_init(&send_button_data_work, send_button_data_work_fn);
    	k_work_init(&send_modem_at_cmd_work, send_modem_at_cmd_work_fn);
    	k_delayed_work_init(&send_agps_request_work, send_agps_request);
    	k_delayed_work_init(&long_press_button_work, long_press_handler);
    	k_delayed_work_init(&cloud_reboot_work, cloud_reboot_handler);
    	k_delayed_work_init(&cycle_cloud_connection_work,
    			    cycle_cloud_connection);
    	k_delayed_work_init(&device_config_work, device_config_send);
    	k_delayed_work_init(&cloud_connect_work, cloud_connect_work_fn);
    	k_work_init(&device_status_work, device_status_send);
    #if CONFIG_MODEM_INFO
    	k_delayed_work_init(&rsrp_work, modem_rsrp_data_send);
    #endif /* CONFIG_MODEM_INFO */
    }
    
    static void cloud_api_init(void)
    {
    	int ret;
    
    	cloud_backend = cloud_get_binding("NRF_CLOUD");
    	__ASSERT(cloud_backend != NULL, "nRF Cloud backend not found");
    
    	ret = cloud_init(cloud_backend, cloud_event_handler);
    	if (ret) {
    		LOG_ERR("Cloud backend could not be initialized, error: %d",
    			ret);
    		cloud_error_handler(ret);
    	}
    
    	ret = cloud_decode_init(cloud_cmd_handler);
    	if (ret) {
    		LOG_ERR("Cloud command decoder could not be initialized, error: %d",
    			ret);
    		cloud_error_handler(ret);
    	}
    }
    
    /**@brief Configures modem to provide LTE link. Blocks until link is
     * successfully established.
     */
    static int modem_configure(void)
    {
    #if defined(CONFIG_BSD_LIBRARY)
    	if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) {
    		/* Do nothing, modem is already turned on */
    		/* and connected */
    		goto connected;
    	}
    
    	ui_led_set_pattern(UI_LTE_CONNECTING);
    	LOG_INF("Connecting to LTE network.");
    	LOG_INF("This may take several minutes.");
    
    #if defined(CONFIG_LWM2M_CARRIER)
    	/* Wait for the LWM2M carrier library to configure the */
    	/* modem and set up the LTE connection. */
    	k_sem_take(&lte_connected, K_FOREVER);
    #else /* defined(CONFIG_LWM2M_CARRIER) */
    	int err = lte_lc_init_and_connect();
    	if (err) {
    		LOG_ERR("LTE link could not be established.");
    		return err;
    	}
    #endif /* defined(CONFIG_LWM2M_CARRIER) */
    
    connected:
    	LOG_INF("Connected to LTE network.");
    	ui_led_set_pattern(UI_LTE_CONNECTED);
    
    #endif /* defined(CONFIG_BSD_LIBRARY) */
    	return 0;
    }
    
    static void button_sensor_init(void)
    {
    	button_cloud_data.type = CLOUD_CHANNEL_BUTTON;
    	button_cloud_data.tag = 0x1;
    }
    
    #if CONFIG_MODEM_INFO
    /**brief Initialize LTE status containers. */
    static void modem_data_init(void)
    {
    	int err;
    	err = modem_info_init();
    	if (err) {
    		LOG_ERR("Modem info could not be established: %d", err);
    		return;
    	}
    
    	modem_info_params_init(&modem_param);
    
    	signal_strength_cloud_data.type = CLOUD_CHANNEL_LTE_LINK_RSRP;
    	signal_strength_cloud_data.tag = 0x1;
    
    	modem_info_rsrp_register(modem_rsrp_handler);
    }
    #endif /* CONFIG_MODEM_INFO */
    
    /**@brief Initializes the sensors that are used by the application. */
    static void sensors_init(void)
    {
    	int err;
    
    	err = motion_init_and_start(&application_work_q, motion_handler);
    	if (err) {
    		LOG_ERR("motion module init failed, error: %d", err);
    	}
    
    	err = env_sensors_init_and_start(&application_work_q, env_data_send);
    	if (err) {
    		LOG_ERR("Environmental sensors init failed, error: %d", err);
    	}
    #if CONFIG_LIGHT_SENSOR
    	err = light_sensor_init_and_start(&application_work_q,
    					  light_sensor_data_send);
    	if (err) {
    		LOG_ERR("Light sensor init failed, error: %d", err);
    	}
    #endif /* CONFIG_LIGHT_SENSOR */
    #if CONFIG_MODEM_INFO
    	modem_data_init();
    #endif /* CONFIG_MODEM_INFO */
    
    	k_work_submit_to_queue(&application_work_q, &device_status_work);
    
    	if (IS_ENABLED(CONFIG_CLOUD_BUTTON)) {
    		button_sensor_init();
    	}
    
    	err = gps_control_init(&application_work_q, gps_handler);
    	if (err) {
    		LOG_ERR("GPS could not be initialized");
    		return;
    	}
    }
    
    #if defined(CONFIG_USE_UI_MODULE)
    /**@brief User interface event handler. */
    static void ui_evt_handler(struct ui_evt evt)
    {
    	if (IS_ENABLED(CONFIG_CLOUD_BUTTON) &&
    	   (evt.button == CONFIG_CLOUD_BUTTON_INPUT)) {
    		button_send(evt.type == UI_EVT_BUTTON_ACTIVE ? 1 : 0);
    	}
    
    	if (IS_ENABLED(CONFIG_ACCEL_USE_SIM) && (evt.button == FLIP_INPUT) &&
    	    data_send_enabled()) {
    		motion_simulate_trigger();
    	}
    
    	if (IS_ENABLED(CONFIG_GPS_CONTROL_ON_LONG_PRESS) &&
    	   (evt.button == UI_BUTTON_1)) {
    		if (evt.type == UI_EVT_BUTTON_ACTIVE) {
    			k_delayed_work_submit_to_queue(&application_work_q,
    						       &long_press_button_work,
    						       K_SECONDS(5));
    		} else {
    			k_delayed_work_cancel(&long_press_button_work);
    		}
    	}
    
    
    
    
    /*Added to check the PSM mode */
    
    #if defined(CONFIG_LTE_LINK_CONTROL)
    	if ((evt.button == UI_BUTTON_1)  &&
    	    IS_ENABLED(CONFIG_GPS_CONTROL_PSM_ENABLE_ON_START)) {
                    int err;
                    if (evt.type == UI_EVT_BUTTON_ACTIVE) {
                    LOG_INF("Enabling PSM for sensor tracking");
    		err = lte_lc_psm_req(true);
                            if (err) {
                                    LOG_ERR("PSM mode could not be enabled");
                                    LOG_ERR(" or was already enabled.");
    		}} else {
    			LOG_INF("PSM is enabled");
    		}
    	}
    #endif 
    
    
    
    
    #if defined(CONFIG_LTE_LINK_CONTROL)
    	if ((evt.button == UI_BUTTON_1) &&
    	    IS_ENABLED(CONFIG_POWER_OPTIMIZATION_ENABLE)) {
    		int err;
    		if (evt.type == UI_EVT_BUTTON_ACTIVE) {
    			err = lte_lc_edrx_req(false);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    			err = lte_lc_psm_req(true);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    		} else {
    			err = lte_lc_psm_req(false);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    			err = lte_lc_edrx_req(true);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    		}
    	}
    #endif /* defined(CONFIG_LTE_LINK_CONTROL) */
    }
    #endif /* defined(CONFIG_USE_UI_MODULE) */
    
    void handle_bsdlib_init_ret(void)
    {
    	#if defined(CONFIG_BSD_LIBRARY)
    	int ret = bsdlib_get_init_ret();
    
    	/* Handle return values relating to modem firmware update */
    	switch (ret) {
    	case MODEM_DFU_RESULT_OK:
    		LOG_INF("MODEM UPDATE OK. Will run new firmware");
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	case MODEM_DFU_RESULT_UUID_ERROR:
    	case MODEM_DFU_RESULT_AUTH_ERROR:
    		LOG_ERR("MODEM UPDATE ERROR %d. Will run old firmware", ret);
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	case MODEM_DFU_RESULT_HARDWARE_ERROR:
    	case MODEM_DFU_RESULT_INTERNAL_ERROR:
    		LOG_ERR("MODEM UPDATE FATAL ERROR %d. Modem failiure", ret);
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	default:
    		break;
    	}
    	#endif /* CONFIG_BSD_LIBRARY */
    }
    
    
    
    #if defined(CONFIG_DATE_TIME_MODEM)
    static int time_modem_get(void)
    {
    	int err;
    	char buf[AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN + 1];
    	struct tm date_time;
            LOG_INF("Testing time update");
            
            //at_send_command(AT_CCLK);
    	err = at_cmd_write(AT_CMD_MODEM_DATE_TIME, buf, sizeof(buf), NULL);
    	if (err) {
    		LOG_DBG("Could not get cellular network time, error: %d", err);
    		return err;
    	}
    	/* This line zero indexes the buffer at the desired length
    	 * This ensures clean printing of the modem response.
    	 */
    	buf[AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN - 4] = '\0';
    
    	/* Example of modem time response:
    	 * "+CCLK: \"20/02/25,17:15:02+04\"\r\n\000{"
    	 */
    	LOG_DBG("Response from modem: %s", log_strdup(buf));
    
    	/* Replace '/' ',' and ':' with whitespace for easier parsing by strtol.
    	 * strtol skips over whitespace.
    	 */
    	for (int i = 0; i < AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN; i++) {
    		if (buf[i] == '/' || buf[i] == ',' || buf[i] == ':') {
    			buf[i] = ' ';
    		}
    	}
    
    	/* The year starts at index 8. */
    	char *ptr_index = &buf[8];
    	int base = 10;
    
    	date_time.tm_year = strtol(ptr_index, &ptr_index, base) + 2000 - 1900;
    	date_time.tm_mon = strtol(ptr_index, &ptr_index, base) - 1;
    	date_time.tm_mday = strtol(ptr_index, &ptr_index, base);
    	date_time.tm_hour = strtol(ptr_index, &ptr_index, base);
    	date_time.tm_min = strtol(ptr_index, &ptr_index, base);
    	date_time.tm_sec = strtol(ptr_index, &ptr_index, base);
    
    	if (date_time.tm_year == MODEM_TIME_DEFAULT) {
    		LOG_DBG("Modem time never set");
    		return -ENODATA;
    	}
    
    	time_aux.date_time_utc = (s64_t)timeutil_timegm64(&date_time) * 1000;
    	time_aux.last_date_time_update = k_uptime_get();
            //printk(time_aux.last_date_time_update);
    
    	return 0;
    }
    #endif
    
    
    
    
    void main(void)
    {
    	LOG_INF("Asset tracker started");
    	k_work_q_start(&application_work_q, application_stack_area,
    		       K_THREAD_STACK_SIZEOF(application_stack_area),
    		       CONFIG_APPLICATION_WORKQUEUE_PRIORITY);
    	if (IS_ENABLED(CONFIG_WATCHDOG)) {
    		watchdog_init_and_start(&application_work_q);
    	}
    
    #if defined(CONFIG_LWM2M_CARRIER)
    	k_sem_take(&bsdlib_initialized, K_FOREVER);
    #else
    	handle_bsdlib_init_ret();
    #endif
    	
           
    	cloud_api_init();
    
    #if defined(CONFIG_USE_UI_MODULE)
    	ui_init(ui_evt_handler);
    #endif
    	work_init();
    
    
    
    
    
            // shutdown_modem();
    
    	while (modem_configure() != 0) {
    		LOG_WRN("Failed to establish LTE connection.");
    		LOG_WRN("Will retry in %d seconds.",
    				CONFIG_CLOUD_CONNECT_RETRY_DELAY);
    		k_sleep(K_SECONDS(CONFIG_CLOUD_CONNECT_RETRY_DELAY));
    	}
    
    
    
    #if defined(CONFIG_LWM2M_CARRIER)
    	LOG_INF("Waiting for LWM2M carrier to complete initialization...");
    	k_sem_take(&cloud_ready_to_connect, K_FOREVER);
    #endif
            connect_to_cloud(0);
    
    
            s64_t time_stamp;
            s64_t milliseconds_spent;
    
    /* capture initial time stamp */
            time_stamp = k_uptime_get();
            LOG_INF("Timestamp is :%d", time_stamp);
            //return k_uptime_get();
    
            k_uptime_get();
    
    /* do work for some (extended) period of time */
    
            k_sleep(K_SECONDS(20));
    
    /* compute how long the work took (also updates the time stamp) */
            milliseconds_spent = k_uptime_delta(&time_stamp);
            LOG_INF("Timestamp is updating :%d", milliseconds_spent);
    
    
    
            u32_t start_time;
            u32_t stop_time;
            u32_t cycles_spent;
            u32_t nanoseconds_spent;
    
    /* capture initial time stamp */
            start_time = k_cycle_get_32();
    
    /* do work for some (short) period of time */
    
            k_sleep(K_SECONDS(20));
    
    /* capture final time stamp */
            stop_time = k_cycle_get_32();
    
    /* compute how long the work took (assumes no counter rollover) */
            cycles_spent = stop_time - start_time;
            nanoseconds_spent = SYS_CLOCK_HW_CYCLES_TO_NS(cycles_spent);
            LOG_INF("Time spent on the timer is :%d", nanoseconds_spent);
    
    
    
            LOG_INF("Scheduler starting at :%d", k_uptime_get());
            at_send_command(AT_CCLK);
    
             static s64_t st;
    
            static s64_t date;
    
            st = k_uptime_get();
    
            date_time_update();
    
            date_time_now(&time_aux.last_date_time_update);
            
            LOG_INF("The date time is :%d", date);
    
            date_time_uptime_to_unix_time_ms(&st);
            
    
            //printk("Scheduler started at", AT_CCLK);
    
            /*lte_lc_init_and_connect();
            k_sleep(K_MSEC(1000));
            lte_lc_offline();
            lte_lc_power_off();*/
            
    
    // Scheduler initiate and control
    
    at_cmd_set_notification_handler(&at_cmd_notification_handler);
    
    	// start modem
    	at_send_command(AT_POWER);
    	at_send_command(AT_SUBSCRIBE);
    	at_send_command(AT_XSYSTEMMODE);
    
    	at_send_command(AT_CFUN1);
        printk("connecting...\n");
        while(connected == true){k_sleep(K_MSEC(1000));}
        printk("connected\n");
        at_send_command(AT_SUBSCRIBE);
        at_send_command(AT_CEREG);
    
    
    
    
        // wait 30 sec and enter PSM
        k_sleep(K_MSEC(10000));
        at_send_command(AT_PSM_REQ);
               
    
        while(true){
    
          
          //printk(st);
          //k_sleep(K_SECONDS(30));
          //at_send_command(AT_TIME);
          at_send_command(AT_NETTIME);
          at_send_command(AT_ASK);
          time_modem_get();
          
          k_sleep(K_MSEC(10000));
    
          static s64_t st;
    
          st = k_uptime_get();
    
          LOG_INF("Scheduler started at:%d", st);
    
          //time_modem_get();
    
           date_time_update();
    
           date_time_uptime_to_unix_time_ms(&st);
    
    
         // date_time_now(&time_aux.last_date_time_update);
    
    
          //at_send_command(AT_CFUN0);
          //k_sleep(K_SECONDS(10));
          at_send_command(AT_CFUN1);
          k_sleep(K_SECONDS(30));
          //connect_to_cloud(40);
          //at_send_command(AT_CCLK1);
          date_time_now(&st);
    
          
    
            printk("The kernel timer sample started\n"); // Doc: https://docs.zephyrproject.org/latest/reference/kernel/timing/timers.html
    
    	/* start one single shot timer that expires after 5000 ms */
    	/* start one multi shot timer that expires after 5000ms, and then every 5000 ms */
            k_timer_start(&single_shot_timer, K_SECONDS(100),             0);
    	//k_timer_start(&multi_shot_timer,  K_MSEC(5000), K_MSEC(5000) );
           // at_send_command(AT_CCLK1);
    
            //current_time_check();
    
            //new_date_time_get();
    
            
      }
        /*while(true){
            at_send_command(AT_CFUN0);
            k_sleep(K_SECONDS(60));
            at_send_command(AT_CFUN1);
            k_sleep(K_SECONDS(40));
            
            
            while (modem_configure() != 0) {
    		LOG_WRN("Failed to establish LTE connection.");
    		LOG_WRN("Will retry in %d seconds.",
    				CONFIG_CLOUD_CONNECT_RETRY_DELAY);
    		k_sleep(K_SECONDS(CONFIG_CLOUD_CONNECT_RETRY_DELAY));
    	}
    
            connect_to_cloud(0);
            env_data_send();
            //k_sleep(K_SECONDS(10));
    
        }
    
        while(true){
            k_sleep(K_MSEC(10000));
        }*/
    
    
    
    }
    K_TIMER_DEFINE(single_shot_timer, my_expiry_function_1, NULL);
    //K_TIMER_DEFINE(multi_shot_timer,  my_expiry_function_2, NULL);

Reply
  • Hi Torbjorn,

    I am attaching my main file for you to have a look. I have added the date_time_update() ; time_modem_get() and date_time_now() functions in the main loop part of the code. I am just trying to build up on the asset tracker application using the date_time library. 

    You can try it out and let me know if it works or you can modify it as well :).

    Regards,

    Adeel.

    /*
     * Copyright (c) 2018 Nordic Semiconductor ASA
     *
     * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic
     */
    
    #include <drivers/gpio.h>
    #include <nrf9160.h>
    #include <zephyr.h>
    #include <kernel_structs.h>
    #include <stdio.h>
    #include <string.h>
    #include <device.h>
    #include <date_time.h>
    #include <time.h>
    //#include <sntp.h>
    #include <kernel.h>
    #include <drivers/gps.h>
    #include <drivers/sensor.h>
    #include <console/console.h>
    #include <power/reboot.h>
    #include <logging/log_ctrl.h>
    #if defined(CONFIG_BSD_LIBRARY)
    #include <modem/bsdlib.h>
    #include <bsd.h>
    #include <modem/lte_lc.h>
    #include <modem/at_cmd.h>
    #include <modem/modem_info.h>
    #endif /* CONFIG_BSD_LIBRARY */
    #include <net/cloud.h>
    #include <net/socket.h>
    #include <net/nrf_cloud.h>
    #if defined(CONFIG_NRF_CLOUD_AGPS)
    #include <net/nrf_cloud_agps.h>
    #endif
    
    #if defined(CONFIG_LWM2M_CARRIER)
    #include <lwm2m_carrier.h>
    #endif
    
    #if defined(CONFIG_BOOTLOADER_MCUBOOT)
    #include <dfu/mcuboot.h>
    #endif
    
    #include "cloud_codec.h"
    #include "env_sensors.h"
    #include "motion.h"
    #include "ui.h"
    #include "service_info.h"
    #include <modem/at_cmd.h>
    #include "watchdog.h"
    #include "gps_controller.h"
    
    #include <logging/log.h>
    LOG_MODULE_REGISTER(asset_tracker, CONFIG_ASSET_TRACKER_LOG_LEVEL);
    
    #define CALIBRATION_PRESS_DURATION  K_SECONDS(5)
    #define CLOUD_CONNACK_WAIT_DURATION (CONFIG_CLOUD_WAIT_DURATION * MSEC_PER_SEC)
    
    #ifdef CONFIG_ACCEL_USE_SIM
    #define FLIP_INPUT			CONFIG_FLIP_INPUT
    #define CALIBRATION_INPUT		-1
    #else
    #define FLIP_INPUT			-1
    #ifdef CONFIG_ACCEL_CALIBRATE
    #define CALIBRATION_INPUT		CONFIG_CALIBRATION_INPUT
    #else
    #define CALIBRATION_INPUT		-1
    #endif /* CONFIG_ACCEL_CALIBRATE */
    #endif /* CONFIG_ACCEL_USE_SIM */
    
    #if defined(CONFIG_BSD_LIBRARY) && \
    !defined(CONFIG_LTE_LINK_CONTROL)
    #error "Missing CONFIG_LTE_LINK_CONTROL"
    #endif
    
    #if defined(CONFIG_BSD_LIBRARY) && \
    defined(CONFIG_LTE_AUTO_INIT_AND_CONNECT) && \
    defined(CONFIG_NRF_CLOUD_PROVISION_CERTIFICATES)
    #error "PROVISION_CERTIFICATES \
    	requires CONFIG_LTE_AUTO_INIT_AND_CONNECT to be disabled!"
    #endif
    
    
    #define M1 0
    #define NB1 1
    
    #define CAT M1 //M1 or NB1
    
    #if CAT == M1
    #define AT_EDRX_REQ         "AT+CEDRXS=2,4,\"0000\""
    #define AT_PTW_REQ          "AT\%XPTW=4,\"0000\""
    #define AT_XSYSTEMMODE		"AT\%XSYSTEMMODE=1,0,0,0"
    #elif CAT == NB1
    #define AT_EDRX_REQ         "AT+CEDRXS=2,5,\"0010\""
    #define AT_PTW_REQ          "AT\%XPTW=5,\"0000\""
    #define AT_XSYSTEMMODE		"AT\%XSYSTEMMODE=0,1,0,0"
    #endif
    
    #define AT_SUBSCRIBE		"AT+CEREG=5"
    #define AT_CEREG	    	"AT+CEREG?"
    #define AT_CCLK                 "AT+CCLK=\"20/08/24,00:00:00+08\""
    //#define AT_CCLK1                 "AT+CCLK=\"20/08/24,00:05:00+08\""
    #define AT_TIME                 "AT\%XTIME: \"08\",\"81109251714208\",\"01\""   
    #define AT_NETTIME              "AT%XNETTIME=1"         
    #define AT_ASK                  "AT+CCLK?"
    #define AT_PSM_REQ			"AT+CPSMS=1,\"\",\"\",\"00010100\",\"00000000\""
    #define AT_PSM_OFF			"AT+CPSMS=0"
    #define AT_EDRX_READ        "AT+CEDRXRDP"
    #define AT_XMONITOR	    	"AT\%XMONITOR"
    #define AT_CFUN1        	"AT+CFUN=1"
    #define AT_CFUN0        	"AT+CFUN=0"
    #define AT_POWER            "AT\%XDATAPRFL=0"
    
    #define AT_CMD_MODEM_DATE_TIME			"AT+CCLK?"
    #define AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN	500
    
    #define MODEM_TIME_DEFAULT 115
    
    #define TIMER_INTERVAL_SEC 20
    #define TIMER_DURATION 25
    
    struct k_timer my_timer;
    struct device *dev;
    uint8_t toggle = 0;
    
    struct k_timer single_shot_timer;
    //struct k_timer multi_shot_timer;
    
    
    //K_SEM_DEFINE(time_fetch_sem, 0, 1);
    
    //static struct k_delayed_work time_work;
    
    bool connected = false;
    enum at_cmd_state at_state;
    
    static void at_cmd_notification_handler(const char* response){
        printk("AT notify: \t%s",response);
        if (!memcmp("+CEREG: 1", response, 9) || !memcmp("+CEREG: 5", response, 9)){
            connected = true;
        }
    }
    
    static void at_cmd_handler(const char* response){
        if(strlen(response) > 0){
            printk("AT recv:\t%s",response);
        }
    }
    
    static void at_send_command(const char *cmd)
    {
        printk("AT send: \t%s\n", cmd);
        at_cmd_write_with_callback(cmd, &at_cmd_handler, &at_state);
        if(at_state){
            printk("AT ret: \t%u\n",at_state);
        }
        else{
            printk("AT ret: \tOK\n");
        }
    }
    
    
    void my_expiry_function(struct k_timer *timer_id){
            ++toggle;
            gpio_pin_write(dev, 16, toggle % 2);
            printk("interrupt!\r\n");
    }
    
    
    static void my_expiry_function_1(struct k_timer *timer)
    {
    	ARG_UNUSED(timer);
    	printk("single_shot_timer expired\n");
    
    }
    
    /*static void my_expiry_function_2(struct k_timer *timer)
    {
    	ARG_UNUSED(timer);
    	printk("multi_shot_timer expired\n");
    
    }*/
    
    
    #define CLOUD_LED_ON_STR "{\"led\":\"on\"}"
    #define CLOUD_LED_OFF_STR "{\"led\":\"off\"}"
    #define CLOUD_LED_MSK UI_LED_1
    
    /* Interval in milliseconds after which the device will reboot
     * if the disconnect event has not been handled.
     */
    #define REBOOT_AFTER_DISCONNECT_WAIT_MS     (15 * MSEC_PER_SEC)
    
    /* Interval in milliseconds after which the device will
     * disconnect and reconnect if association was not completed.
     */
    #define CONN_CYCLE_AFTER_ASSOCIATION_REQ_MS K_MINUTES(5)
    
    struct rsrp_data {
    	u16_t value;
    	u16_t offset;
    };
    
    #if CONFIG_MODEM_INFO
    static struct rsrp_data rsrp = {
    	.value = 0,
    	.offset = MODEM_INFO_RSRP_OFFSET_VAL,
    };
    #endif /* CONFIG_MODEM_INFO */
    
    /* Stack definition for application workqueue */
    K_THREAD_STACK_DEFINE(application_stack_area,
    		      CONFIG_APPLICATION_WORKQUEUE_STACK_SIZE);
    static struct k_work_q application_work_q;
    static struct cloud_backend *cloud_backend;
    
    /* Sensor data */
    static struct gps_nmea gps_data;
    static struct cloud_channel_data gps_cloud_data = {
    	.type = CLOUD_CHANNEL_GPS,
    	.tag = 0x1
    };
    static struct cloud_channel_data button_cloud_data;
    
    static struct time_aux {
    	s64_t date_time_utc;
    	int last_date_time_update;
    } time_aux;
    
    
    #if CONFIG_MODEM_INFO
    static struct modem_param_info modem_param;
    static struct cloud_channel_data signal_strength_cloud_data;
    #endif /* CONFIG_MODEM_INFO */
    
    static s64_t gps_last_active_time;
    static atomic_t carrier_requested_disconnect;
    static atomic_t cloud_connect_attempts;
    
    /* Flag used for flip detection */
    static bool flip_mode_enabled = true;
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    /* Current state of activity monitor */
    static motion_activity_state_t last_activity_state = MOTION_ACTIVITY_NOT_KNOWN;
    #endif
    
    /* Variable to keep track of nRF cloud association state. */
    enum cloud_association_state {
    	CLOUD_ASSOCIATION_STATE_INIT,
    	CLOUD_ASSOCIATION_STATE_REQUESTED,
    	CLOUD_ASSOCIATION_STATE_PAIRED,
    	CLOUD_ASSOCIATION_STATE_RECONNECT,
    	CLOUD_ASSOCIATION_STATE_READY,
    };
    static atomic_val_t cloud_association =
    	ATOMIC_INIT(CLOUD_ASSOCIATION_STATE_INIT);
    
    /* Structures for work */
    static struct k_work send_gps_data_work;
    static struct k_work send_button_data_work;
    static struct k_work send_modem_at_cmd_work;
    static struct k_delayed_work long_press_button_work;
    static struct k_delayed_work cloud_reboot_work;
    static struct k_delayed_work cycle_cloud_connection_work;
    static struct k_delayed_work device_config_work;
    static struct k_delayed_work cloud_connect_work;
    static struct k_work device_status_work;
    static struct k_delayed_work send_agps_request_work;
    
    #if defined(CONFIG_AT_CMD)
    #define MODEM_AT_CMD_BUFFER_LEN (CONFIG_AT_CMD_RESPONSE_MAX_LEN + 1)
    #else
    #define MODEM_AT_CMD_NOT_ENABLED_STR "Error: AT Command driver is not enabled"
    #define MODEM_AT_CMD_BUFFER_LEN (sizeof(MODEM_AT_CMD_NOT_ENABLED_STR))
    #endif
    #define MODEM_AT_CMD_RESP_TOO_BIG_STR "Error: AT Command response is too large to be sent"
    #define MODEM_AT_CMD_MAX_RESPONSE_LEN (2000)
    static char modem_at_cmd_buff[MODEM_AT_CMD_BUFFER_LEN];
    static K_SEM_DEFINE(modem_at_cmd_sem, 1, 1);
    static K_SEM_DEFINE(cloud_disconnected, 0, 1);
    #if defined(CONFIG_LWM2M_CARRIER)
    static void app_disconnect(void);
    static K_SEM_DEFINE(bsdlib_initialized, 0, 1);
    static K_SEM_DEFINE(lte_connected, 0, 1);
    static K_SEM_DEFINE(cloud_ready_to_connect, 0, 1);
    #endif
    
    #if CONFIG_MODEM_INFO
    static struct k_delayed_work rsrp_work;
    #endif /* CONFIG_MODEM_INFO */
    
    enum error_type {
    	ERROR_CLOUD,
    	ERROR_BSD_RECOVERABLE,
    	ERROR_LTE_LC,
    	ERROR_SYSTEM_FAULT
    };
    
    /* Forward declaration of functions */
    static void motion_handler(motion_data_t  motion_data);
    static void env_data_send(void);
    #if CONFIG_LIGHT_SENSOR
    static void light_sensor_data_send(void);
    #endif /* CONFIG_LIGHT_SENSOR */
    static void sensors_init(void);
    static void work_init(void);
    static void sensor_data_send(struct cloud_channel_data *data);
    static void device_status_send(struct k_work *work);
    static void cycle_cloud_connection(struct k_work *work);
    static void set_gps_enable(const bool enable);
    static bool data_send_enabled(void);
    static void connection_evt_handler(const struct cloud_event *const evt);
    static void date_time_handler(struct k_work *work);
    
    static void shutdown_modem(void)
    {
    #if defined(CONFIG_LTE_LINK_CONTROL)
    	/* Turn off and shutdown modem */
    	LOG_ERR("LTE link disconnect");
    	int err = lte_lc_power_off();
    
    	if (err) {
    		LOG_ERR("lte_lc_power_off failed: %d", err);
    	}
    #endif /* CONFIG_LTE_LINK_CONTROL */
    #if defined(CONFIG_BSD_LIBRARY)
    	LOG_ERR("Shutdown modem");
    	bsdlib_shutdown();
    #endif
    }
    
    
    
    
    
    #if defined(CONFIG_LWM2M_CARRIER)
    int lwm2m_carrier_event_handler(const lwm2m_carrier_event_t *event)
    {
    	switch (event->type) {
    	case LWM2M_CARRIER_EVENT_BSDLIB_INIT:
    		LOG_INF("LWM2M_CARRIER_EVENT_BSDLIB_INIT");
    		k_sem_give(&bsdlib_initialized);
    		break;
    	case LWM2M_CARRIER_EVENT_CONNECTING:
    		LOG_INF("LWM2M_CARRIER_EVENT_CONNECTING\n");
    		break;
    	case LWM2M_CARRIER_EVENT_CONNECTED:
    		LOG_INF("LWM2M_CARRIER_EVENT_CONNECTED");
    		k_sem_give(&lte_connected);
    		break;
    	case LWM2M_CARRIER_EVENT_DISCONNECTING:
    		LOG_INF("LWM2M_CARRIER_EVENT_DISCONNECTING");
    		break;
    	case LWM2M_CARRIER_EVENT_BOOTSTRAPPED:
    		LOG_INF("LWM2M_CARRIER_EVENT_BOOTSTRAPPED");
    		break;
    	case LWM2M_CARRIER_EVENT_DISCONNECTED:
    		LOG_INF("LWM2M_CARRIER_EVENT_DISCONNECTED");
    		break;
    	case LWM2M_CARRIER_EVENT_READY:
    		LOG_INF("LWM2M_CARRIER_EVENT_READY");
    		k_sem_give(&cloud_ready_to_connect);
    		break;
    	case LWM2M_CARRIER_EVENT_FOTA_START:
    		LOG_INF("LWM2M_CARRIER_EVENT_FOTA_START");
    		/* Due to limitations in the number of secure sockets,
    		 * the cloud socket has to be closed when the carrier
    		 * library initiates firmware upgrade download.
    		 */
    		atomic_set(&carrier_requested_disconnect, 1);
    		app_disconnect();
    		break;
    	case LWM2M_CARRIER_EVENT_REBOOT:
    		LOG_INF("LWM2M_CARRIER_EVENT_REBOOT");
    		break;
    	case LWM2M_CARRIER_EVENT_ERROR:
    		LOG_ERR("LWM2M_CARRIER_EVENT_ERROR: code %d, value %d",
    			((lwm2m_carrier_event_error_t *)event->data)->code,
    			((lwm2m_carrier_event_error_t *)event->data)->value);
    		break;
    	}
    
    	return 0;
    }
    
    /**@brief Disconnects from cloud. First it tries using the cloud backend's
     *        disconnect() implementation. If that fails, it falls back to close the
     *        socket directly, using close().
     */
    static void app_disconnect(void)
    {
    	int err;
    
    	atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_INIT);
    	LOG_INF("Disconnecting from cloud.");
    
    	err = cloud_disconnect(cloud_backend);
    	if (err == 0) {
    		/* Ensure that the socket is indeed closed before returning. */
    		if (k_sem_take(&cloud_disconnected, K_MINUTES(1)) == 0) {
    			LOG_INF("Disconnected from cloud.");
    			return;
    		}
    	} else if (err == -ENOTCONN) {
    		LOG_INF("Cloud connection was not established.");
    		return;
    	} else {
    		LOG_ERR("Could not disconnect from cloud, err: %d", err);
    	}
    
    	LOG_INF("Closing the cloud socket directly.");
    
    	err = close(cloud_backend->config->socket);
    	if (err) {
    		LOG_ERR("Failed to close socket, error: %d", err);
    		return;
    	}
    
    	LOG_INF("Socket was closed successfully.");
    	return;
    }
    #endif /* defined(CONFIG_LWM2M_CARRIER) */
    
    /**@brief nRF Cloud error handler. */
    void error_handler(enum error_type err_type, int err_code)
    {
    	atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_INIT);
    
    	if (err_type == ERROR_CLOUD) {
    		shutdown_modem();
    	}
    
    #if !defined(CONFIG_DEBUG) && defined(CONFIG_REBOOT)
    	LOG_PANIC();
    	sys_reboot(0);
    #else
    	switch (err_type) {
    	case ERROR_CLOUD:
    		/* Blinking all LEDs ON/OFF in pairs (1 and 4, 2 and 3)
    		 * if there is an application error.
    		 */
    		ui_led_set_pattern(UI_LED_ERROR_CLOUD);
    		LOG_ERR("Error of type ERROR_CLOUD: %d", err_code);
    	break;
    	case ERROR_BSD_RECOVERABLE:
    		/* Blinking all LEDs ON/OFF in pairs (1 and 3, 2 and 4)
    		 * if there is a recoverable error.
    		 */
    		ui_led_set_pattern(UI_LED_ERROR_BSD_REC);
    		LOG_ERR("Error of type ERROR_BSD_RECOVERABLE: %d", err_code);
    	break;
    	default:
    		/* Blinking all LEDs ON/OFF in pairs (1 and 2, 3 and 4)
    		 * undefined error.
    		 */
    		ui_led_set_pattern(UI_LED_ERROR_UNKNOWN);
    		LOG_ERR("Unknown error type: %d, code: %d",
    			err_type, err_code);
    	break;
    	}
    
    	while (true) {
    		k_cpu_idle();
    	}
    #endif /* CONFIG_DEBUG */
    }
    
    void k_sys_fatal_error_handler(unsigned int reason,
    			       const z_arch_esf_t *esf)
    {
    	ARG_UNUSED(esf);
    
    	LOG_PANIC();
    	LOG_ERR("Running main.c error handler");
    	error_handler(ERROR_SYSTEM_FAULT, reason);
    	CODE_UNREACHABLE;
    }
    
    void cloud_error_handler(int err)
    {
    	error_handler(ERROR_CLOUD, err);
    }
    
    static void send_agps_request(struct k_work *work)
    {
    	ARG_UNUSED(work);
    
    #if defined(CONFIG_NRF_CLOUD_AGPS)
    	int err;
    	static s64_t last_request_timestamp;
    
    /* Request A-GPS data no more often than every hour (time in milliseconds). */
    #define AGPS_UPDATE_PERIOD (60 * 60 * 1000)
    
    	if ((last_request_timestamp != 0) &&
    	    (k_uptime_get() - last_request_timestamp) < AGPS_UPDATE_PERIOD) {
    		LOG_WRN("A-GPS request was sent less than 1 hour ago");
    		return;
    	}
    
    	LOG_INF("Sending A-GPS request");
    
    	err = nrf_cloud_agps_request_all();
    	if (err) {
    		LOG_ERR("A-GPS request failed, error: %d", err);
    		return;
    	}
    
    	last_request_timestamp = k_uptime_get();
    
    	LOG_INF("A-GPS request sent");
    #endif /* defined(CONFIG_NRF_CLOUD_AGPS) */
    }
    
    void cloud_connect_error_handler(enum cloud_connect_result err)
    {
    	bool reboot = true;
    	char *backend_name = "invalid";
    
    	if (err == CLOUD_CONNECT_RES_SUCCESS) {
    		return;
    	}
    
    	LOG_ERR("Failed to connect to cloud, error %d", err);
    
    	switch (err) {
    	case CLOUD_CONNECT_RES_ERR_NOT_INITD: {
    		LOG_ERR("Cloud back-end has not been initialized");
    		/* no need to reboot, program error */
    		reboot = false;
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_NETWORK: {
    		LOG_ERR("Network error, check cloud configuration");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_BACKEND: {
    		if (cloud_backend && cloud_backend->config &&
    		    cloud_backend->config->name) {
    			backend_name = cloud_backend->config->name;
    		}
    		LOG_ERR("An error occurred specific to the cloud back-end: %s",
    			backend_name);
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_PRV_KEY: {
    		LOG_ERR("Ensure device has a valid private key");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_CERT: {
    		LOG_ERR("Ensure device has a valid CA and client certificate");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_CERT_MISC: {
    		LOG_ERR("A certificate/authorization error has occurred");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_TIMEOUT_NO_DATA: {
    		LOG_ERR("Connect timeout. SIM card may be out of data");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_ALREADY_CONNECTED: {
    		LOG_ERR("Connection already exists.");
    		break;
    	}
    	case CLOUD_CONNECT_RES_ERR_MISC: {
    		break;
    	}
    	default: {
    		LOG_ERR("Unhandled connect error");
    		break;
    	}
    	}
    
    	if (reboot) {
    		LOG_ERR("Device will reboot in %d seconds",
    				CONFIG_CLOUD_CONNECT_ERR_REBOOT_S);
    		k_delayed_work_submit_to_queue(
    			&application_work_q, &cloud_reboot_work,
    			K_SECONDS(CONFIG_CLOUD_CONNECT_ERR_REBOOT_S));
    	}
    
    	ui_led_set_pattern(UI_LED_ERROR_CLOUD);
    	shutdown_modem();
    	k_thread_suspend(k_current_get());
    }
    
    /**@brief Recoverable BSD library error. */
    void bsd_recoverable_error_handler(uint32_t err)
    {
    	error_handler(ERROR_BSD_RECOVERABLE, (int)err);
    }
    
    static void send_gps_data_work_fn(struct k_work *work)
    {
    	sensor_data_send(&gps_cloud_data);
    }
    
    static void send_button_data_work_fn(struct k_work *work)
    {
    	sensor_data_send(&button_cloud_data);
    }
    
    void connect_to_cloud(const s32_t connect_delay_s)
    {
    	static bool initial_connect = true;
    
    	/* Ensure no data can be sent to cloud before connection is established.
    	 */
    	atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_INIT);
    
    	if (atomic_get(&carrier_requested_disconnect)) {
    		/* A disconnect was requested to free up the TLS socket
    		 * used by the cloud.  If enabled, the carrier library
    		 * (CONFIG_LWM2M_CARRIER) will perform FOTA updates in
    		 * the background and reboot the device when complete.
    		 */
    		return;
    	}
    
    	atomic_inc(&cloud_connect_attempts);
    
    	/* Check if max cloud connect retry count is exceeded. */
    	if (atomic_get(&cloud_connect_attempts) >
    	    CONFIG_CLOUD_CONNECT_COUNT_MAX) {
    		LOG_ERR("The max cloud connection attempt count exceeded.");
    		cloud_error_handler(-ETIMEDOUT);
    	}
    
    	if (!initial_connect) {
    		LOG_INF("Attempting reconnect in %d seconds...",
    			connect_delay_s);
    		k_delayed_work_cancel(&cloud_reboot_work);
    	} else {
    		initial_connect = false;
    	}
    
    	k_delayed_work_submit_to_queue(&application_work_q,
    				       &cloud_connect_work,
    				       K_SECONDS(connect_delay_s));
    }
    
    static void cloud_connect_work_fn(struct k_work *work)
    {
    	int ret;
    
    	LOG_INF("Connecting to cloud, attempt %d of %d",
    	       atomic_get(&cloud_connect_attempts),
    		   CONFIG_CLOUD_CONNECT_COUNT_MAX);
    
    	k_delayed_work_submit_to_queue(&application_work_q,
    			&cloud_reboot_work,
    			K_MSEC(CLOUD_CONNACK_WAIT_DURATION));
    
    	ui_led_set_pattern(UI_CLOUD_CONNECTING);
    
    	/* Attempt cloud connection */
    	ret = cloud_connect(cloud_backend);
    	if (ret != CLOUD_CONNECT_RES_SUCCESS) {
    		k_delayed_work_cancel(&cloud_reboot_work);
    		/* Will not return from this function.
    		 * If the connect fails here, it is likely
    		 * that user intervention is required.
    		 */
    		cloud_connect_error_handler(ret);
    	} else {
    		LOG_INF("Cloud connection request sent.");
    		LOG_INF("Connection response timeout is set to %d seconds.",
    		       CLOUD_CONNACK_WAIT_DURATION / MSEC_PER_SEC);
    		k_delayed_work_submit_to_queue(&application_work_q,
    					&cloud_reboot_work,
    					K_MSEC(CLOUD_CONNACK_WAIT_DURATION));
    	}
    }
    
    static void send_modem_at_cmd_work_fn(struct k_work *work)
    {
    	enum at_cmd_state state;
    	int err;
    	struct cloud_channel_data modem_data = {
    		.type = CLOUD_CHANNEL_MODEM
    	};
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_MSG
    	};
    	size_t len = strlen(modem_at_cmd_buff);
    
    	if (len == 0) {
    		err = -ENOBUFS;
    		state = AT_CMD_ERROR;
    	} else {
    #if defined(CONFIG_AT_CMD)
    		err = at_cmd_write(modem_at_cmd_buff, modem_at_cmd_buff,
    				sizeof(modem_at_cmd_buff), &state);
    #else
    		/* Proper error msg has already been copied to buffer */
    		err = 0;
    #endif
    	}
    
    	/* Get response length; same buffer was used for the response. */
    	len = strlen(modem_at_cmd_buff);
    
    	if (err) {
    		len = snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    				"AT CMD error: %d, state %d", err, state);
    	} else if (len == 0) {
    		len = snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    				"OK\r\n");
    	} else if (len > MODEM_AT_CMD_MAX_RESPONSE_LEN) {
    		len = snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    				MODEM_AT_CMD_RESP_TOO_BIG_STR);
    	}
    
    	modem_data.data.buf = modem_at_cmd_buff;
    	modem_data.data.len = len;
    
    	err = cloud_encode_data(&modem_data, CLOUD_CMD_GROUP_COMMAND, &msg);
    	if (err) {
    		LOG_ERR("[%s:%d] cloud_encode_data failed with error %d",
    			__func__, __LINE__, err);
    	} else {
    		err = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    		if (err) {
    			LOG_ERR("[%s:%d] cloud_send failed with error %d",
    				__func__, __LINE__, err);
    		}
    	}
    
    	k_sem_give(&modem_at_cmd_sem);
    }
    
    
    
    
    static void gps_handler(struct device *dev, struct gps_event *evt)
    {
    	gps_last_active_time = k_uptime_get();
    
    	switch (evt->type) {
    	case GPS_EVT_SEARCH_STARTED:
    		LOG_INF("GPS_EVT_SEARCH_STARTED");
    		gps_control_set_active(true);
    		ui_led_set_pattern(UI_LED_GPS_SEARCHING);
    		break;
    	case GPS_EVT_SEARCH_STOPPED:
    		LOG_INF("GPS_EVT_SEARCH_STOPPED");
    		gps_control_set_active(false);
    		ui_led_set_pattern(UI_CLOUD_CONNECTED);
    		break;
    	case GPS_EVT_SEARCH_TIMEOUT:
    		LOG_INF("GPS_EVT_SEARCH_TIMEOUT");
    		gps_control_set_active(false);
    		LOG_INF("GPS will be attempted again in %d seconds",
    			gps_control_get_gps_reporting_interval());
    		break;
    	case GPS_EVT_PVT:
    		/* Don't spam logs */
    		break;
    	case GPS_EVT_PVT_FIX:
    		LOG_INF("GPS_EVT_PVT_FIX");
    		break;
    	case GPS_EVT_NMEA:
    		/* Don't spam logs */
    		break;
    	case GPS_EVT_NMEA_FIX:
    		LOG_INF("Position fix with NMEA data");
    
    		memcpy(gps_data.buf, evt->nmea.buf, evt->nmea.len);
    		gps_data.len = evt->nmea.len;
    		gps_cloud_data.data.buf = gps_data.buf;
    		gps_cloud_data.data.len = gps_data.len;
    		gps_cloud_data.tag += 1;
    
    		if (gps_cloud_data.tag == 0) {
    			gps_cloud_data.tag = 0x1;
    		}
    
    		ui_led_set_pattern(UI_LED_GPS_FIX);
    		gps_control_set_active(false);
    		LOG_INF("GPS will be started in %d seconds",
    			gps_control_get_gps_reporting_interval());
    
    		k_work_submit_to_queue(&application_work_q,
    				       &send_gps_data_work);
    		env_sensors_poll();
    		break;
    	case GPS_EVT_OPERATION_BLOCKED:
    		LOG_INF("GPS_EVT_OPERATION_BLOCKED");
    		ui_led_set_pattern(UI_LED_GPS_BLOCKED);
    		break;
    	case GPS_EVT_OPERATION_UNBLOCKED:
    		LOG_INF("GPS_EVT_OPERATION_UNBLOCKED");
    		ui_led_set_pattern(UI_LED_GPS_SEARCHING);
    		break;
    	case GPS_EVT_AGPS_DATA_NEEDED:
    		LOG_INF("GPS_EVT_AGPS_DATA_NEEDED");
    		/* Send A-GPS request with short delay to avoid LTE network-
    		 * dependent corner-case where the request would not be sent.
    		 */
    		k_delayed_work_submit_to_queue(&application_work_q,
    					       &send_agps_request_work,
    					       K_SECONDS(1));
    		break;
    	case GPS_EVT_ERROR:
    		LOG_INF("GPS_EVT_ERROR\n");
    		break;
    	default:
    		break;
    	}
    }
    
    #if defined(CONFIG_USE_UI_MODULE)
    /**@brief Send button presses to cloud */
    static void button_send(bool pressed)
    {
    	static char data[] = "1";
    
    	if (!data_send_enabled()) {
    		return;
    	}
    
    	if (pressed) {
    		data[0] = '1';
    	} else {
    		data[0] = '0';
    	}
    
    	button_cloud_data.data.buf = data;
    	button_cloud_data.data.len = strlen(data);
    	button_cloud_data.tag += 1;
    
    	if (button_cloud_data.tag == 0) {
    		button_cloud_data.tag = 0x1;
    	}
    
    	k_work_submit_to_queue(&application_work_q, &send_button_data_work);
    }
    #endif
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    static bool motion_activity_is_active(void)
    {
    	return (last_activity_state == MOTION_ACTIVITY_ACTIVE);
    }
    
    static void motion_trigger_gps(motion_data_t  motion_data)
    {
    	static bool initial_run = true;
    
    	/* The handler is triggered once on startup, regardless of motion,
    	 * in order to get into a known state. This should be ignored.
    	 */
    	if (initial_run) {
    		initial_run = false;
    		return;
    	}
    
    	if (motion_activity_is_active() && gps_control_is_enabled()) {
    		static s64_t next_active_time;
    		s64_t last_active_time = gps_last_active_time / 1000;
    		s64_t now = k_uptime_get() / 1000;
    		s64_t time_since_fix_attempt = now - last_active_time;
    		s64_t time_until_next_attempt = next_active_time - now;
    
    		LOG_DBG("Last at %lld s, now %lld s, next %lld s; "
    			"%lld secs since last, %lld secs until next",
    			last_active_time, now, next_active_time,
    			time_since_fix_attempt, time_until_next_attempt);
    
    		if (time_until_next_attempt >= 0) {
    			LOG_DBG("keeping original schedule.");
    			return;
    		}
    
    		time_since_fix_attempt = MAX(0, (MIN(time_since_fix_attempt,
    			CONFIG_GPS_CONTROL_FIX_CHECK_INTERVAL)));
    		s64_t time_to_start_next_fix = 1 +
    			CONFIG_GPS_CONTROL_FIX_CHECK_INTERVAL -
    			time_since_fix_attempt;
    
    		next_active_time = now + time_to_start_next_fix;
    
    		char buf[100];
    
    		/* due to a known design issue in Zephyr, we need to use
    		 * snprintf to output floats; see:
    		 * https://github.com/zephyrproject-rtos/zephyr/issues/18351
    		 * https://github.com/zephyrproject-rtos/zephyr/pull/18921
    		 */
    		snprintf(buf, sizeof(buf),
    			"Motion triggering GPS; accel, %.1f, %.1f, %.1f",
    			motion_data.acceleration.x,
    			motion_data.acceleration.y,
    			motion_data.acceleration.z);
    		LOG_INF("%s", log_strdup(buf));
    
    		LOG_INF("starting GPS in %lld seconds", time_to_start_next_fix);
    		gps_control_start(time_to_start_next_fix * MSEC_PER_SEC);
    	}
    }
    #endif
    
    
    // Can be used or interrupt handling
    
    /**@brief Callback from the motion module. Sends motion data to cloud. */
    static void motion_handler(motion_data_t  motion_data)
    {
    	static motion_orientation_state_t last_orientation_state =
    		MOTION_ORIENTATION_NOT_KNOWN;
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    	/* toggle state since the accelerometer does not yet report
    	 * which state occurred
    	 */
    	last_activity_state = (last_activity_state != MOTION_ACTIVITY_ACTIVE) ?
    			      MOTION_ACTIVITY_ACTIVE : MOTION_ACTIVITY_INACTIVE;
    #endif
    
    	if (!flip_mode_enabled || !data_send_enabled() ||
    	    gps_control_is_active()) {
    		return;
    	}
    
    	if (motion_data.orientation != last_orientation_state) {
    
    		if (flip_mode_enabled && data_send_enabled()) {
    
    			struct cloud_msg msg = {
    				.qos = CLOUD_QOS_AT_MOST_ONCE,
    				.endpoint.type = CLOUD_EP_TOPIC_MSG
    			};
    
    			int err = 0;
    
    			if (cloud_encode_motion_data(&motion_data, &msg) == 0) {
    				err = cloud_send(cloud_backend, &msg);
    				cloud_release_data(&msg);
    				if (err) {
    					LOG_ERR("Transmisison of "
    						"motion data failed: %d", err);
    					cloud_error_handler(err);
    				}
    			}
    
    			if (!err) {
    				last_orientation_state =
    					motion_data.orientation;
    			}
    		}
    	}
    
    #if IS_ENABLED(CONFIG_GPS_START_ON_MOTION)
    	motion_trigger_gps(motion_data);
    #endif
    }
    
    static void cloud_cmd_handle_modem_at_cmd(const char *const at_cmd)
    {
    	if (!at_cmd) {
    		return;
    	}
    
    	if (k_sem_take(&modem_at_cmd_sem, K_MSEC(20)) != 0) {
    		LOG_ERR("[%s:%d] Modem AT cmd in progress.", __func__,
    		       __LINE__);
    		return;
    	}
    
    #if defined(CONFIG_AT_CMD)
    	const size_t max_cmd_len = sizeof(modem_at_cmd_buff);
    
    	if (strnlen(at_cmd, max_cmd_len) == max_cmd_len) {
    		LOG_ERR("[%s:%d] AT cmd is too long, max length is %zu",
    		       __func__, __LINE__, max_cmd_len - 1);
    		/* Empty string will be handled as an error */
    		modem_at_cmd_buff[0] = '\0';
    	} else {
    		strcpy(modem_at_cmd_buff, at_cmd);
    	}
    #else
    	snprintf(modem_at_cmd_buff, sizeof(modem_at_cmd_buff),
    			MODEM_AT_CMD_NOT_ENABLED_STR);
    #endif
    
    	k_work_submit_to_queue(&application_work_q, &send_modem_at_cmd_work);
    }
    
    static void cloud_cmd_handler(struct cloud_command *cmd)
    {
    	if ((cmd->channel == CLOUD_CHANNEL_GPS) &&
    	    (cmd->group == CLOUD_CMD_GROUP_CFG_SET) &&
    	    (cmd->type == CLOUD_CMD_ENABLE)) {
    		set_gps_enable(cmd->data.sv.state == CLOUD_CMD_STATE_TRUE);
    	} else if ((cmd->channel == CLOUD_CHANNEL_MODEM) &&
    		   (cmd->group == CLOUD_CMD_GROUP_COMMAND) &&
    		   (cmd->type == CLOUD_CMD_DATA_STRING)) {
    		cloud_cmd_handle_modem_at_cmd(cmd->data.data_string);
    	} else if ((cmd->channel == CLOUD_CHANNEL_RGB_LED) &&
    		   (cmd->group == CLOUD_CMD_GROUP_CFG_SET) &&
    		   (cmd->type == CLOUD_CMD_COLOR)) {
    		ui_led_set_color(((u32_t)cmd->data.sv.value >> 16) & 0xFF,
    				 ((u32_t)cmd->data.sv.value >> 8) & 0xFF,
    				 ((u32_t)cmd->data.sv.value) & 0xFF);
    	} else if ((cmd->channel == CLOUD_CHANNEL_DEVICE_INFO) &&
    		   (cmd->group == CLOUD_CMD_GROUP_GET) &&
    		   (cmd->type == CLOUD_CMD_EMPTY)) {
    		k_work_submit_to_queue(&application_work_q,
    				       &device_status_work);
    	} else if ((cmd->channel == CLOUD_CHANNEL_LTE_LINK_RSRP) &&
    		   (cmd->group == CLOUD_CMD_GROUP_GET) &&
    		   (cmd->type == CLOUD_CMD_EMPTY)) {
    #if CONFIG_MODEM_INFO
    		k_delayed_work_submit_to_queue(&application_work_q, &rsrp_work,
    					       K_NO_WAIT);
    #endif
    	} else if ((cmd->group == CLOUD_CMD_GROUP_CFG_SET) &&
    			   (cmd->type == CLOUD_CMD_INTERVAL)) {
    		if (cmd->channel == CLOUD_CHANNEL_LIGHT_SENSOR) {
    #if defined(CONFIG_LIGHT_SENSOR)
    			light_sensor_set_send_interval(
    				(u32_t)cmd->data.sv.value);
    #endif
    		} else if (cmd->channel == CLOUD_CHANNEL_ENVIRONMENT) {
    			env_sensors_set_send_interval(
    				(u32_t)cmd->data.sv.value);
    		} else if (cmd->channel == CLOUD_CHANNEL_GPS) {
    			/* TODO: update GPS controller to handle send */
    			/* interval */
    		} else {
    			LOG_ERR("Interval command not valid for channel %d",
    				cmd->channel);
    		}
    	}
    }
    
    #if CONFIG_MODEM_INFO
    /**@brief Callback handler for LTE RSRP data. */
    static void modem_rsrp_handler(char rsrp_value)
    {
    	/* RSRP raw values that represent actual signal strength are
    	 * 0 through 97 (per "nRF91 AT Commands" v1.1). If the received value
    	 * falls outside this range, we should not send the value.
    	 */
    	if (rsrp_value > 97) {
    		return;
    	}
    
    	rsrp.value = rsrp_value;
    
    	/* Only send the RSRP if transmission is not already scheduled.
    	 * Checking CONFIG_HOLD_TIME_RSRP gives the compiler a shortcut.
    	 */
    	if (CONFIG_HOLD_TIME_RSRP == 0 ||
    	    k_delayed_work_remaining_get(&rsrp_work) == 0) {
    		k_delayed_work_submit_to_queue(&application_work_q, &rsrp_work,
    					       K_NO_WAIT);
    	}
    }
    
    /**@brief Publish RSRP data to the cloud. */
    static void modem_rsrp_data_send(struct k_work *work)
    {
    	char buf[CONFIG_MODEM_INFO_BUFFER_SIZE] = {0};
    	static s32_t rsrp_prev; /* RSRP value last sent to cloud */
    	s32_t rsrp_current;
    	size_t len;
    
    	if (!data_send_enabled()) {
    		return;
    	}
    
    	/* The RSRP value is copied locally to avoid any race */
    	rsrp_current = rsrp.value - rsrp.offset;
    
    	if (rsrp_current == rsrp_prev) {
    		return;
    	}
    
    	len = snprintf(buf, CONFIG_MODEM_INFO_BUFFER_SIZE,
    		       "%d", rsrp_current);
    
    	signal_strength_cloud_data.data.buf = buf;
    	signal_strength_cloud_data.data.len = len;
    	signal_strength_cloud_data.tag += 1;
    
    	if (signal_strength_cloud_data.tag == 0) {
    		signal_strength_cloud_data.tag = 0x1;
    	}
    
    	sensor_data_send(&signal_strength_cloud_data);
    	rsrp_prev = rsrp_current;
    
    	if (CONFIG_HOLD_TIME_RSRP > 0) {
    		k_delayed_work_submit_to_queue(&application_work_q, &rsrp_work,
    					      K_SECONDS(CONFIG_HOLD_TIME_RSRP));
    	}
    }
    #endif /* CONFIG_MODEM_INFO */
    
    /**@brief Poll device info and send data to the cloud. */
    static void device_status_send(struct k_work *work)
    {
    	struct modem_param_info *modem_ptr = NULL;
    	int ret;
    
    	if (!data_send_enabled() || gps_control_is_active()) {
    		return;
    	}
    
    #ifdef CONFIG_MODEM_INFO
    	ret = modem_info_params_get(&modem_param);
    	if (ret < 0) {
    		LOG_ERR("Unable to obtain modem parameters: %d", ret);
    	} else {
    		modem_ptr = &modem_param;
    	}
    #endif /* CONFIG_MODEM_INFO */
    
    	const char *const ui[] = {
    		CLOUD_CHANNEL_STR_GPS,
    		CLOUD_CHANNEL_STR_FLIP,
    		CLOUD_CHANNEL_STR_TEMP,
    		CLOUD_CHANNEL_STR_HUMID,
    		CLOUD_CHANNEL_STR_AIR_PRESS,
    #if IS_ENABLED(CONFIG_CLOUD_BUTTON)
    		CLOUD_CHANNEL_STR_BUTTON,
    #endif
    #if IS_ENABLED(CONFIG_LIGHT_SENSOR)
    		CLOUD_CHANNEL_STR_LIGHT_SENSOR,
    #endif
    #if IS_ENABLED(CONFIG_MODEM_INFO)
    		CLOUD_CHANNEL_STR_LTE_LINK_RSRP,
    #endif
    	};
    
    	const char *const fota[] = {
    #if defined(CONFIG_CLOUD_FOTA_APP)
    		SERVICE_INFO_FOTA_STR_APP,
    #endif
    #if defined(CONFIG_CLOUD_FOTA_MODEM)
    		SERVICE_INFO_FOTA_STR_MODEM
    #endif
    	};
    
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_STATE
    	};
    
    	ret = cloud_encode_device_status_data(modem_ptr,
    					      ui, ARRAY_SIZE(ui),
    					      fota, ARRAY_SIZE(fota),
    					      SERVICE_INFO_FOTA_VER_CURRENT,
    					      &msg);
    	if (ret) {
    		LOG_ERR("Unable to encode cloud data: %d", ret);
    	} else {
    		/* Transmits the data to the cloud. */
    		ret = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    		if (ret) {
    			LOG_ERR("sensor_data_send failed: %d", ret);
    			cloud_error_handler(ret);
    		}
    	}
    }
    
    /**@brief Send device config to the cloud. */
    static void device_config_send(struct k_work *work)
    {
    	int ret;
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_STATE
    	};
    	enum cloud_cmd_state gps_cfg_state =
    		cloud_get_channel_enable_state(CLOUD_CHANNEL_GPS);
    
    	if (gps_cfg_state == CLOUD_CMD_STATE_UNDEFINED) {
    		return;
    	}
    
    	if (gps_control_is_active() && gps_cfg_state == CLOUD_CMD_STATE_FALSE) {
    		/* GPS hasn't been stopped yet, reschedule this work */
    		k_delayed_work_submit_to_queue(&application_work_q,
    			&device_config_work, K_SECONDS(5));
    		return;
    	}
    
    	ret = cloud_encode_config_data(&msg);
    	if (ret) {
    		LOG_ERR("Unable to encode cloud data: %d", ret);
    	} else if (msg.len && msg.buf) {
    		ret = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    
    		if (ret) {
    			LOG_ERR("%s failed: %d", __func__, ret);
    			cloud_error_handler(ret);
    		}
    	}
    
    	if (gps_cfg_state == CLOUD_CMD_STATE_TRUE) {
    		gps_control_start(0);
    	}
    }
    
    /**@brief Get environment data from sensors and send to cloud. */
    static void env_data_send(void)
    {
    	int err;
    	env_sensor_data_t env_data;
    	struct cloud_msg msg = {
    		.qos = CLOUD_QOS_AT_MOST_ONCE,
    		.endpoint.type = CLOUD_EP_TOPIC_MSG
    	};
    
    	if (!data_send_enabled()) {
    		return;
    	}
    
    	if (gps_control_is_active()) {
    		env_sensors_set_backoff_enable(true);
    		return;
    	}
    
    	env_sensors_set_backoff_enable(false);
    
    	if (env_sensors_get_temperature(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_TEMP, env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	if (env_sensors_get_humidity(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_HUMID,
    					  env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	if (env_sensors_get_pressure(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_AIR_PRESS,
    					  env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	if (env_sensors_get_air_quality(&env_data) == 0) {
    		if (cloud_is_send_allowed(CLOUD_CHANNEL_AIR_QUAL,
    					  env_data.value) &&
    		    cloud_encode_env_sensors_data(&env_data, &msg) == 0) {
    			err = cloud_send(cloud_backend, &msg);
    			cloud_release_data(&msg);
    			if (err) {
    				goto error;
    			}
    		}
    	}
    
    	return;
    error:
    	LOG_ERR("sensor_data_send failed: %d", err);
    	cloud_error_handler(err);
    }
    
    #if defined(CONFIG_LIGHT_SENSOR)
    void light_sensor_data_send(void)
    {
    	int err;
    	struct light_sensor_data light_data;
    	struct cloud_msg msg = { .qos = CLOUD_QOS_AT_MOST_ONCE,
    				 .endpoint.type = CLOUD_EP_TOPIC_MSG };
    
    	if (!data_send_enabled() || gps_control_is_active()) {
    		return;
    	}
    
    	err = light_sensor_get_data(&light_data);
    	if (err) {
    		LOG_ERR("Failed to get light sensor data, error %d", err);
    		return;
    	}
    
    	if (!cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_RED, light_data.red) &&
    	    !cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_GREEN,
    				   light_data.green) &&
    	    !cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_BLUE, light_data.blue) &&
    	    !cloud_is_send_allowed(CLOUD_CHANNEL_LIGHT_IR, light_data.ir)) {
    		LOG_WRN("Light values not sent due to config settings");
    		return;
    	}
    
    	err = cloud_encode_light_sensor_data(&light_data, &msg);
    	if (err) {
    		LOG_ERR("Failed to encode light sensor data, error %d", err);
    		return;
    	}
    
    	err = cloud_send(cloud_backend, &msg);
    	cloud_release_data(&msg);
    
    	if (err) {
    		LOG_ERR("Failed to send light sensor data to cloud, error: %d",
    		       err);
    		cloud_error_handler(err);
    	}
    }
    #endif /* CONFIG_LIGHT_SENSOR */
    
    /**@brief Send sensor data to nRF Cloud. **/
    static void sensor_data_send(struct cloud_channel_data *data)
    {
    	int err = 0;
    	struct cloud_msg msg = {
    			.qos = CLOUD_QOS_AT_MOST_ONCE,
    			.endpoint.type = CLOUD_EP_TOPIC_MSG
    		};
    
    	if (!data_send_enabled() || gps_control_is_active()) {
    		return;
    	}
    
    	err = cloud_encode_data(data, CLOUD_CMD_GROUP_DATA, &msg);
    	if (err) {
    		LOG_ERR("Unable to encode cloud data: %d", err);
    	} else {
    		err = cloud_send(cloud_backend, &msg);
    		cloud_release_data(&msg);
    		if (err) {
    			LOG_ERR("%s failed, data was not sent: %d", __func__,
    			err);
    		}
    	}
    }
    
    /**@brief Reboot the device if CONNACK has not arrived. */
    static void cloud_reboot_handler(struct k_work *work)
    {
    	error_handler(ERROR_CLOUD, -ETIMEDOUT);
    }
    
    static bool data_send_enabled(void)
    {
    	return (atomic_get(&cloud_association) ==
    		   CLOUD_ASSOCIATION_STATE_READY);
    }
    
    /**@brief Callback for sensor attached event from nRF Cloud. */
    void sensors_start(void)
    {
    	static bool started;
    	bool start_gps = IS_ENABLED(CONFIG_GPS_START_AFTER_CLOUD_EVT_READY);
    
    	if (!started) {
    		sensors_init();
    
    		/* Value from cloud has precedence */
    		switch (cloud_get_channel_enable_state(CLOUD_CHANNEL_GPS)) {
    		case CLOUD_CMD_STATE_FALSE:
    			start_gps = false;
    			break;
    		case CLOUD_CMD_STATE_TRUE:
    			start_gps = true;
    			break;
    		case CLOUD_CMD_STATE_UNDEFINED:
    		default:
    			break;
    		}
    		set_gps_enable(start_gps);
    		started = true;
    	}
    }
    
    /**@brief nRF Cloud specific callback for cloud association event. */
    static void on_user_pairing_req(const struct cloud_event *evt)
    {
    	if (atomic_get(&cloud_association) !=
    		CLOUD_ASSOCIATION_STATE_REQUESTED) {
    		atomic_set(&cloud_association,
    				   CLOUD_ASSOCIATION_STATE_REQUESTED);
    		ui_led_set_pattern(UI_CLOUD_PAIRING);
    		LOG_INF("Add device to cloud account.");
    		LOG_INF("Waiting for cloud association...");
    
    		/* If the association is not done soon enough (< ~5 min?)
    		 * a connection cycle is needed... TBD why.
    		 */
    		k_delayed_work_submit_to_queue(&application_work_q,
    					       &cycle_cloud_connection_work,
    					       CONN_CYCLE_AFTER_ASSOCIATION_REQ_MS);
    	}
    }
    
    static void cycle_cloud_connection(struct k_work *work)
    {
    	s32_t reboot_wait_ms = REBOOT_AFTER_DISCONNECT_WAIT_MS;
    
    	LOG_INF("Disconnecting from cloud...");
    
    	if (cloud_disconnect(cloud_backend) != 0) {
    		reboot_wait_ms = 5 * MSEC_PER_SEC;
    		LOG_INF("Disconnect failed. Device will reboot in %d seconds",
    			(reboot_wait_ms / MSEC_PER_SEC));
    	}
    
    	/* Reboot fail-safe on disconnect */
    	k_delayed_work_submit_to_queue(&application_work_q, &cloud_reboot_work,
    				       K_MSEC(reboot_wait_ms));
    }
    
    /** @brief Handle procedures after successful association with nRF Cloud. */
    void on_pairing_done(void)
    {
    	if (atomic_get(&cloud_association) ==
    			CLOUD_ASSOCIATION_STATE_REQUESTED) {
    		k_delayed_work_cancel(&cycle_cloud_connection_work);
    
    		/* After successful association, the device must
    		 * reconnect to the cloud.
    		 */
    		LOG_INF("Device associated with cloud.");
    		LOG_INF("Reconnecting for cloud policy to take effect.");
    		atomic_set(&cloud_association,
    				   CLOUD_ASSOCIATION_STATE_RECONNECT);
    		k_delayed_work_submit_to_queue(&application_work_q,
    					       &cycle_cloud_connection_work,
    					       K_NO_WAIT);
    	} else {
    		atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_PAIRED);
    	}
    }
    
    void cloud_event_handler(const struct cloud_backend *const backend,
    			 const struct cloud_event *const evt,
    			 void *user_data)
    {
    	ARG_UNUSED(user_data);
    
    	switch (evt->type) {
    	case CLOUD_EVT_CONNECTED:
    	case CLOUD_EVT_CONNECTING:
    	case CLOUD_EVT_DISCONNECTED:
    		connection_evt_handler(evt);
    		break;
    	case CLOUD_EVT_READY:
    		LOG_INF("CLOUD_EVT_READY");
    		ui_led_set_pattern(UI_CLOUD_CONNECTED);
    
    #if defined(CONFIG_BOOTLOADER_MCUBOOT)
    		/* Mark image as good to avoid rolling back after update */
    		boot_write_img_confirmed();
    #endif
    		atomic_set(&cloud_association, CLOUD_ASSOCIATION_STATE_READY);
    		sensors_start();
    		break;
    	case CLOUD_EVT_ERROR:
    		LOG_INF("CLOUD_EVT_ERROR");
    		break;
    	case CLOUD_EVT_DATA_SENT:
    		LOG_INF("CLOUD_EVT_DATA_SENT");
    		break;
    	case CLOUD_EVT_DATA_RECEIVED: {
    		int err;
    
    		LOG_INF("CLOUD_EVT_DATA_RECEIVED");
    		err = cloud_decode_command(evt->data.msg.buf);
    		if (err == 0) {
    			/* Cloud decoder has handled the data */
    			return;
    		}
    
    #if defined(CONFIG_NRF_CLOUD_AGPS)
    		/* The decoder didn't handle the data, check if it's A-GPS data
    		 */
    		err = nrf_cloud_agps_process(evt->data.msg.buf,
    					     evt->data.msg.len,
    					     NULL);
    		if (err) {
    			LOG_WRN("Data was not valid A-GPS data, err: %d", err);
    			break;
    		}
    
    		LOG_INF("A-GPS data processed");
    #endif /* defined(CONFIG_GPS_USE_AGPS) */
    		break;
    	}
    	case CLOUD_EVT_PAIR_REQUEST:
    		LOG_INF("CLOUD_EVT_PAIR_REQUEST");
    		on_user_pairing_req(evt);
    		break;
    	case CLOUD_EVT_PAIR_DONE:
    		LOG_INF("CLOUD_EVT_PAIR_DONE");
    		on_pairing_done();
    		break;
    	case CLOUD_EVT_FOTA_DONE:
    		LOG_INF("CLOUD_EVT_FOTA_DONE");
    #if defined(CONFIG_LTE_LINK_CONTROL)
    		lte_lc_power_off();
    #endif
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	default:
    		LOG_WRN("Unknown cloud event type: %d", evt->type);
    		break;
    	}
    }
    
    void connection_evt_handler(const struct cloud_event *const evt)
    {
    	if (evt->type == CLOUD_EVT_CONNECTING) {
    		LOG_INF("CLOUD_EVT_CONNECTING");
    		ui_led_set_pattern(UI_CLOUD_CONNECTING);
    		k_delayed_work_cancel(&cloud_reboot_work);
    
    		if (evt->data.err != CLOUD_CONNECT_RES_SUCCESS) {
    			cloud_connect_error_handler(evt->data.err);
    		}
    		return;
    	} else if (evt->type == CLOUD_EVT_CONNECTED) {
    		LOG_INF("CLOUD_EVT_CONNECTED");
    		k_delayed_work_cancel(&cloud_reboot_work);
    		k_sem_take(&cloud_disconnected, K_NO_WAIT);
    		atomic_set(&cloud_connect_attempts, 0);
    #if defined(CONFIG_CLOUD_PERSISTENT_SESSIONS)
    		LOG_INF("Persistent Sessions = %u",
    			evt->data.persistent_session);
    #endif
    	} else if (evt->type == CLOUD_EVT_DISCONNECTED) {
    		s32_t connect_wait_s = CONFIG_CLOUD_CONNECT_RETRY_DELAY;
    
    		LOG_INF("CLOUD_EVT_DISCONNECTED: %d", evt->data.err);
    		ui_led_set_pattern(UI_LTE_CONNECTED);
    
    		switch (evt->data.err) {
    		case CLOUD_DISCONNECT_INVALID_REQUEST:
    			LOG_INF("Cloud connection closed.");
    			if ((atomic_get(&cloud_connect_attempts) == 1) &&
    			    (atomic_get(&cloud_association) ==
    			     CLOUD_ASSOCIATION_STATE_INIT)) {
    				LOG_INF("This can occur during initial nRF Cloud provisioning.");
    #if defined(CONFIG_LWM2M_CARRIER)
    #if !defined(CONFIG_DEBUG) && defined(CONFIG_REBOOT)
    				/* Reconnect does not work with carrier library */
    				LOG_INF("Rebooting in 10 seconds...");
    				k_sleep(K_SECONDS(10));
    #endif
    				error_handler(ERROR_CLOUD, -EIO);
    				break;
    #endif
    				connect_wait_s = 10;
    			} else {
    				LOG_INF("This can occur if the device has the wrong nRF Cloud certificates.");
    			}
    			break;
    		case CLOUD_DISCONNECT_USER_REQUEST:
    			if (atomic_get(&cloud_association) ==
    			    CLOUD_ASSOCIATION_STATE_RECONNECT ||
    			    atomic_get(&cloud_association) ==
    			    CLOUD_ASSOCIATION_STATE_REQUESTED ||
    			    (atomic_get(&carrier_requested_disconnect))) {
    				connect_wait_s = 10;
    			}
    			break;
    		case CLOUD_DISCONNECT_CLOSED_BY_REMOTE:
    			LOG_INF("Disconnected by the cloud.");
    			break;
    		case CLOUD_DISCONNECT_MISC:
    		default:
    			break;
    		}
    		k_sem_give(&cloud_disconnected);
    		connect_to_cloud(connect_wait_s);
    	}
    }
    
    static void set_gps_enable(const bool enable)
    {
    	s32_t delay_ms = 0;
    	bool changing = (enable != gps_control_is_enabled());
    
    	/* Exit early if the link is not ready or if the cloud
    	 * state is defined and the local state is not changing.
    	 */
    	if (!data_send_enabled() ||
    	    ((cloud_get_channel_enable_state(CLOUD_CHANNEL_GPS) !=
    	    CLOUD_CMD_STATE_UNDEFINED) && !changing)) {
    		return;
    	}
    
    	cloud_set_channel_enable_state(CLOUD_CHANNEL_GPS,
    		enable ? CLOUD_CMD_STATE_TRUE : CLOUD_CMD_STATE_FALSE);
    
    	if (changing) {
    		if (enable) {
    			LOG_INF("Starting GPS");
    			/* GPS will be started from the device config work
    			 * handler AFTER the config has been sent to the cloud
    			 */
    		} else {
    			LOG_INF("Stopping GPS");
    			gps_control_stop(0);
    			/* Allow time for the gps to be stopped before
    			 * attemping to send the config update
    			 */
    			delay_ms = 5 * MSEC_PER_SEC;
    		}
    	}
    
    	/* Update config state in cloud */
    	k_delayed_work_submit_to_queue(&application_work_q,
    			&device_config_work, K_MSEC(delay_ms));
    }
    
    static void long_press_handler(struct k_work *work)
    {
    	ARG_UNUSED(work);
    
    	if (!data_send_enabled()) {
    		LOG_INF("Link not ready, long press disregarded");
    		return;
    	}
    
    	set_gps_enable(!gps_control_is_enabled());
    }
    
    /**@brief Initializes and submits delayed work. */
    static void work_init(void)
    {
    	k_work_init(&send_gps_data_work, send_gps_data_work_fn);
    	k_work_init(&send_button_data_work, send_button_data_work_fn);
    	k_work_init(&send_modem_at_cmd_work, send_modem_at_cmd_work_fn);
    	k_delayed_work_init(&send_agps_request_work, send_agps_request);
    	k_delayed_work_init(&long_press_button_work, long_press_handler);
    	k_delayed_work_init(&cloud_reboot_work, cloud_reboot_handler);
    	k_delayed_work_init(&cycle_cloud_connection_work,
    			    cycle_cloud_connection);
    	k_delayed_work_init(&device_config_work, device_config_send);
    	k_delayed_work_init(&cloud_connect_work, cloud_connect_work_fn);
    	k_work_init(&device_status_work, device_status_send);
    #if CONFIG_MODEM_INFO
    	k_delayed_work_init(&rsrp_work, modem_rsrp_data_send);
    #endif /* CONFIG_MODEM_INFO */
    }
    
    static void cloud_api_init(void)
    {
    	int ret;
    
    	cloud_backend = cloud_get_binding("NRF_CLOUD");
    	__ASSERT(cloud_backend != NULL, "nRF Cloud backend not found");
    
    	ret = cloud_init(cloud_backend, cloud_event_handler);
    	if (ret) {
    		LOG_ERR("Cloud backend could not be initialized, error: %d",
    			ret);
    		cloud_error_handler(ret);
    	}
    
    	ret = cloud_decode_init(cloud_cmd_handler);
    	if (ret) {
    		LOG_ERR("Cloud command decoder could not be initialized, error: %d",
    			ret);
    		cloud_error_handler(ret);
    	}
    }
    
    /**@brief Configures modem to provide LTE link. Blocks until link is
     * successfully established.
     */
    static int modem_configure(void)
    {
    #if defined(CONFIG_BSD_LIBRARY)
    	if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) {
    		/* Do nothing, modem is already turned on */
    		/* and connected */
    		goto connected;
    	}
    
    	ui_led_set_pattern(UI_LTE_CONNECTING);
    	LOG_INF("Connecting to LTE network.");
    	LOG_INF("This may take several minutes.");
    
    #if defined(CONFIG_LWM2M_CARRIER)
    	/* Wait for the LWM2M carrier library to configure the */
    	/* modem and set up the LTE connection. */
    	k_sem_take(&lte_connected, K_FOREVER);
    #else /* defined(CONFIG_LWM2M_CARRIER) */
    	int err = lte_lc_init_and_connect();
    	if (err) {
    		LOG_ERR("LTE link could not be established.");
    		return err;
    	}
    #endif /* defined(CONFIG_LWM2M_CARRIER) */
    
    connected:
    	LOG_INF("Connected to LTE network.");
    	ui_led_set_pattern(UI_LTE_CONNECTED);
    
    #endif /* defined(CONFIG_BSD_LIBRARY) */
    	return 0;
    }
    
    static void button_sensor_init(void)
    {
    	button_cloud_data.type = CLOUD_CHANNEL_BUTTON;
    	button_cloud_data.tag = 0x1;
    }
    
    #if CONFIG_MODEM_INFO
    /**brief Initialize LTE status containers. */
    static void modem_data_init(void)
    {
    	int err;
    	err = modem_info_init();
    	if (err) {
    		LOG_ERR("Modem info could not be established: %d", err);
    		return;
    	}
    
    	modem_info_params_init(&modem_param);
    
    	signal_strength_cloud_data.type = CLOUD_CHANNEL_LTE_LINK_RSRP;
    	signal_strength_cloud_data.tag = 0x1;
    
    	modem_info_rsrp_register(modem_rsrp_handler);
    }
    #endif /* CONFIG_MODEM_INFO */
    
    /**@brief Initializes the sensors that are used by the application. */
    static void sensors_init(void)
    {
    	int err;
    
    	err = motion_init_and_start(&application_work_q, motion_handler);
    	if (err) {
    		LOG_ERR("motion module init failed, error: %d", err);
    	}
    
    	err = env_sensors_init_and_start(&application_work_q, env_data_send);
    	if (err) {
    		LOG_ERR("Environmental sensors init failed, error: %d", err);
    	}
    #if CONFIG_LIGHT_SENSOR
    	err = light_sensor_init_and_start(&application_work_q,
    					  light_sensor_data_send);
    	if (err) {
    		LOG_ERR("Light sensor init failed, error: %d", err);
    	}
    #endif /* CONFIG_LIGHT_SENSOR */
    #if CONFIG_MODEM_INFO
    	modem_data_init();
    #endif /* CONFIG_MODEM_INFO */
    
    	k_work_submit_to_queue(&application_work_q, &device_status_work);
    
    	if (IS_ENABLED(CONFIG_CLOUD_BUTTON)) {
    		button_sensor_init();
    	}
    
    	err = gps_control_init(&application_work_q, gps_handler);
    	if (err) {
    		LOG_ERR("GPS could not be initialized");
    		return;
    	}
    }
    
    #if defined(CONFIG_USE_UI_MODULE)
    /**@brief User interface event handler. */
    static void ui_evt_handler(struct ui_evt evt)
    {
    	if (IS_ENABLED(CONFIG_CLOUD_BUTTON) &&
    	   (evt.button == CONFIG_CLOUD_BUTTON_INPUT)) {
    		button_send(evt.type == UI_EVT_BUTTON_ACTIVE ? 1 : 0);
    	}
    
    	if (IS_ENABLED(CONFIG_ACCEL_USE_SIM) && (evt.button == FLIP_INPUT) &&
    	    data_send_enabled()) {
    		motion_simulate_trigger();
    	}
    
    	if (IS_ENABLED(CONFIG_GPS_CONTROL_ON_LONG_PRESS) &&
    	   (evt.button == UI_BUTTON_1)) {
    		if (evt.type == UI_EVT_BUTTON_ACTIVE) {
    			k_delayed_work_submit_to_queue(&application_work_q,
    						       &long_press_button_work,
    						       K_SECONDS(5));
    		} else {
    			k_delayed_work_cancel(&long_press_button_work);
    		}
    	}
    
    
    
    
    /*Added to check the PSM mode */
    
    #if defined(CONFIG_LTE_LINK_CONTROL)
    	if ((evt.button == UI_BUTTON_1)  &&
    	    IS_ENABLED(CONFIG_GPS_CONTROL_PSM_ENABLE_ON_START)) {
                    int err;
                    if (evt.type == UI_EVT_BUTTON_ACTIVE) {
                    LOG_INF("Enabling PSM for sensor tracking");
    		err = lte_lc_psm_req(true);
                            if (err) {
                                    LOG_ERR("PSM mode could not be enabled");
                                    LOG_ERR(" or was already enabled.");
    		}} else {
    			LOG_INF("PSM is enabled");
    		}
    	}
    #endif 
    
    
    
    
    #if defined(CONFIG_LTE_LINK_CONTROL)
    	if ((evt.button == UI_BUTTON_1) &&
    	    IS_ENABLED(CONFIG_POWER_OPTIMIZATION_ENABLE)) {
    		int err;
    		if (evt.type == UI_EVT_BUTTON_ACTIVE) {
    			err = lte_lc_edrx_req(false);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    			err = lte_lc_psm_req(true);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    		} else {
    			err = lte_lc_psm_req(false);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    			err = lte_lc_edrx_req(true);
    			if (err) {
    				error_handler(ERROR_LTE_LC, err);
    			}
    		}
    	}
    #endif /* defined(CONFIG_LTE_LINK_CONTROL) */
    }
    #endif /* defined(CONFIG_USE_UI_MODULE) */
    
    void handle_bsdlib_init_ret(void)
    {
    	#if defined(CONFIG_BSD_LIBRARY)
    	int ret = bsdlib_get_init_ret();
    
    	/* Handle return values relating to modem firmware update */
    	switch (ret) {
    	case MODEM_DFU_RESULT_OK:
    		LOG_INF("MODEM UPDATE OK. Will run new firmware");
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	case MODEM_DFU_RESULT_UUID_ERROR:
    	case MODEM_DFU_RESULT_AUTH_ERROR:
    		LOG_ERR("MODEM UPDATE ERROR %d. Will run old firmware", ret);
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	case MODEM_DFU_RESULT_HARDWARE_ERROR:
    	case MODEM_DFU_RESULT_INTERNAL_ERROR:
    		LOG_ERR("MODEM UPDATE FATAL ERROR %d. Modem failiure", ret);
    		sys_reboot(SYS_REBOOT_COLD);
    		break;
    	default:
    		break;
    	}
    	#endif /* CONFIG_BSD_LIBRARY */
    }
    
    
    
    #if defined(CONFIG_DATE_TIME_MODEM)
    static int time_modem_get(void)
    {
    	int err;
    	char buf[AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN + 1];
    	struct tm date_time;
            LOG_INF("Testing time update");
            
            //at_send_command(AT_CCLK);
    	err = at_cmd_write(AT_CMD_MODEM_DATE_TIME, buf, sizeof(buf), NULL);
    	if (err) {
    		LOG_DBG("Could not get cellular network time, error: %d", err);
    		return err;
    	}
    	/* This line zero indexes the buffer at the desired length
    	 * This ensures clean printing of the modem response.
    	 */
    	buf[AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN - 4] = '\0';
    
    	/* Example of modem time response:
    	 * "+CCLK: \"20/02/25,17:15:02+04\"\r\n\000{"
    	 */
    	LOG_DBG("Response from modem: %s", log_strdup(buf));
    
    	/* Replace '/' ',' and ':' with whitespace for easier parsing by strtol.
    	 * strtol skips over whitespace.
    	 */
    	for (int i = 0; i < AT_CMD_MODEM_DATE_TIME_RESPONSE_LEN; i++) {
    		if (buf[i] == '/' || buf[i] == ',' || buf[i] == ':') {
    			buf[i] = ' ';
    		}
    	}
    
    	/* The year starts at index 8. */
    	char *ptr_index = &buf[8];
    	int base = 10;
    
    	date_time.tm_year = strtol(ptr_index, &ptr_index, base) + 2000 - 1900;
    	date_time.tm_mon = strtol(ptr_index, &ptr_index, base) - 1;
    	date_time.tm_mday = strtol(ptr_index, &ptr_index, base);
    	date_time.tm_hour = strtol(ptr_index, &ptr_index, base);
    	date_time.tm_min = strtol(ptr_index, &ptr_index, base);
    	date_time.tm_sec = strtol(ptr_index, &ptr_index, base);
    
    	if (date_time.tm_year == MODEM_TIME_DEFAULT) {
    		LOG_DBG("Modem time never set");
    		return -ENODATA;
    	}
    
    	time_aux.date_time_utc = (s64_t)timeutil_timegm64(&date_time) * 1000;
    	time_aux.last_date_time_update = k_uptime_get();
            //printk(time_aux.last_date_time_update);
    
    	return 0;
    }
    #endif
    
    
    
    
    void main(void)
    {
    	LOG_INF("Asset tracker started");
    	k_work_q_start(&application_work_q, application_stack_area,
    		       K_THREAD_STACK_SIZEOF(application_stack_area),
    		       CONFIG_APPLICATION_WORKQUEUE_PRIORITY);
    	if (IS_ENABLED(CONFIG_WATCHDOG)) {
    		watchdog_init_and_start(&application_work_q);
    	}
    
    #if defined(CONFIG_LWM2M_CARRIER)
    	k_sem_take(&bsdlib_initialized, K_FOREVER);
    #else
    	handle_bsdlib_init_ret();
    #endif
    	
           
    	cloud_api_init();
    
    #if defined(CONFIG_USE_UI_MODULE)
    	ui_init(ui_evt_handler);
    #endif
    	work_init();
    
    
    
    
    
            // shutdown_modem();
    
    	while (modem_configure() != 0) {
    		LOG_WRN("Failed to establish LTE connection.");
    		LOG_WRN("Will retry in %d seconds.",
    				CONFIG_CLOUD_CONNECT_RETRY_DELAY);
    		k_sleep(K_SECONDS(CONFIG_CLOUD_CONNECT_RETRY_DELAY));
    	}
    
    
    
    #if defined(CONFIG_LWM2M_CARRIER)
    	LOG_INF("Waiting for LWM2M carrier to complete initialization...");
    	k_sem_take(&cloud_ready_to_connect, K_FOREVER);
    #endif
            connect_to_cloud(0);
    
    
            s64_t time_stamp;
            s64_t milliseconds_spent;
    
    /* capture initial time stamp */
            time_stamp = k_uptime_get();
            LOG_INF("Timestamp is :%d", time_stamp);
            //return k_uptime_get();
    
            k_uptime_get();
    
    /* do work for some (extended) period of time */
    
            k_sleep(K_SECONDS(20));
    
    /* compute how long the work took (also updates the time stamp) */
            milliseconds_spent = k_uptime_delta(&time_stamp);
            LOG_INF("Timestamp is updating :%d", milliseconds_spent);
    
    
    
            u32_t start_time;
            u32_t stop_time;
            u32_t cycles_spent;
            u32_t nanoseconds_spent;
    
    /* capture initial time stamp */
            start_time = k_cycle_get_32();
    
    /* do work for some (short) period of time */
    
            k_sleep(K_SECONDS(20));
    
    /* capture final time stamp */
            stop_time = k_cycle_get_32();
    
    /* compute how long the work took (assumes no counter rollover) */
            cycles_spent = stop_time - start_time;
            nanoseconds_spent = SYS_CLOCK_HW_CYCLES_TO_NS(cycles_spent);
            LOG_INF("Time spent on the timer is :%d", nanoseconds_spent);
    
    
    
            LOG_INF("Scheduler starting at :%d", k_uptime_get());
            at_send_command(AT_CCLK);
    
             static s64_t st;
    
            static s64_t date;
    
            st = k_uptime_get();
    
            date_time_update();
    
            date_time_now(&time_aux.last_date_time_update);
            
            LOG_INF("The date time is :%d", date);
    
            date_time_uptime_to_unix_time_ms(&st);
            
    
            //printk("Scheduler started at", AT_CCLK);
    
            /*lte_lc_init_and_connect();
            k_sleep(K_MSEC(1000));
            lte_lc_offline();
            lte_lc_power_off();*/
            
    
    // Scheduler initiate and control
    
    at_cmd_set_notification_handler(&at_cmd_notification_handler);
    
    	// start modem
    	at_send_command(AT_POWER);
    	at_send_command(AT_SUBSCRIBE);
    	at_send_command(AT_XSYSTEMMODE);
    
    	at_send_command(AT_CFUN1);
        printk("connecting...\n");
        while(connected == true){k_sleep(K_MSEC(1000));}
        printk("connected\n");
        at_send_command(AT_SUBSCRIBE);
        at_send_command(AT_CEREG);
    
    
    
    
        // wait 30 sec and enter PSM
        k_sleep(K_MSEC(10000));
        at_send_command(AT_PSM_REQ);
               
    
        while(true){
    
          
          //printk(st);
          //k_sleep(K_SECONDS(30));
          //at_send_command(AT_TIME);
          at_send_command(AT_NETTIME);
          at_send_command(AT_ASK);
          time_modem_get();
          
          k_sleep(K_MSEC(10000));
    
          static s64_t st;
    
          st = k_uptime_get();
    
          LOG_INF("Scheduler started at:%d", st);
    
          //time_modem_get();
    
           date_time_update();
    
           date_time_uptime_to_unix_time_ms(&st);
    
    
         // date_time_now(&time_aux.last_date_time_update);
    
    
          //at_send_command(AT_CFUN0);
          //k_sleep(K_SECONDS(10));
          at_send_command(AT_CFUN1);
          k_sleep(K_SECONDS(30));
          //connect_to_cloud(40);
          //at_send_command(AT_CCLK1);
          date_time_now(&st);
    
          
    
            printk("The kernel timer sample started\n"); // Doc: https://docs.zephyrproject.org/latest/reference/kernel/timing/timers.html
    
    	/* start one single shot timer that expires after 5000 ms */
    	/* start one multi shot timer that expires after 5000ms, and then every 5000 ms */
            k_timer_start(&single_shot_timer, K_SECONDS(100),             0);
    	//k_timer_start(&multi_shot_timer,  K_MSEC(5000), K_MSEC(5000) );
           // at_send_command(AT_CCLK1);
    
            //current_time_check();
    
            //new_date_time_get();
    
            
      }
        /*while(true){
            at_send_command(AT_CFUN0);
            k_sleep(K_SECONDS(60));
            at_send_command(AT_CFUN1);
            k_sleep(K_SECONDS(40));
            
            
            while (modem_configure() != 0) {
    		LOG_WRN("Failed to establish LTE connection.");
    		LOG_WRN("Will retry in %d seconds.",
    				CONFIG_CLOUD_CONNECT_RETRY_DELAY);
    		k_sleep(K_SECONDS(CONFIG_CLOUD_CONNECT_RETRY_DELAY));
    	}
    
            connect_to_cloud(0);
            env_data_send();
            //k_sleep(K_SECONDS(10));
    
        }
    
        while(true){
            k_sleep(K_MSEC(10000));
        }*/
    
    
    
    }
    K_TIMER_DEFINE(single_shot_timer, my_expiry_function_1, NULL);
    //K_TIMER_DEFINE(multi_shot_timer,  my_expiry_function_2, NULL);

Children
  • Hi Adeel 

    Sorry for the slow response. To be honest I haven't handled much nRF91 support up until now, and have spent some time just to get the tools and basic examples running. 

    I will do my best to integrate your changes and get back to you by tomorrow. 

    Best regards
    Torbjørn

  • Hi Tobjorn,

    I appreciate your support. You can get back to me tomorrow, no issues :).

  • Hi Adeel

    I messed around with it today, and finally got it to work with some help from the developer. 

    You can find my example here:
    https://github.com/too1/mqtt_w_date_time

    It is based on the mqtt_simple example, but I moved all the mqtt functions into a separate file for clarity, and added the date_time calls into main. 

    Some things worth noting: 

    1) To make date_time.c build I had to make a small change to the code. I explain this in the readme of the repository. 

    According to the developer this issue is fixed in the master branch, but was not properly tested in the v1.3.0 tag.

    2) In order to get the time information properly he suggested I set CONFIG_DATE_TIME_MODEM=n in the project configuration (prj.conf). This prevents the library from trying to get the time from the modem, and instead accesses the NTP server. 

    3) He also suggested adding some delay between the first call to date_time_update() and date_time_now(), as you can see in my example. 

    With these things in place I am able to see the current time printed after the program has run for a bit: 

    Best regards
    Torbjørn

  • Hi Tobjorn,

    Thanks, I was able to run the example file you sent and it works. Thanks for the effort.

    I just wanted to know one last thing. How would you suggest to go about and convert the time I am getting into a date_time format as we use normally. Any such available library or struct I could use.

    Really appreciate the help on this Slight smile.

  • Hi Adeel

    You could do something like this, using the time.h library similar to how it's done in the calendar example:

    // Convert to string using time.h
    time_t time = unix_time_ms / 1000;
    struct tm local_time;
    local_time = *localtime(&time);
    char time_string[80];
    strftime(time_string, 80, "%x - %H:%M:%S", &local_time);
    printk("Local time from time.h: %s\n", time_string);

    The main difference is that you need to divide the timestamp by 1000, since the time.h library expects a second based time value. 

    In order to use time.h you also need to enable one of the standard C libraries, which can be done by adding the following line to prj.conf:

    CONFIG_NEWLIB_LIBC=y

    Best regards
    Torbjørn

Related