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

Best way to organize firmware to periodically update AWS IoT Server with nRF9160

Hello, guys.

In our current project, we need to periodically update AWS IoT Server with the data from local sensors. So far, thanks to the cloud_client example and your documentation from here, we were able to connect to AWS IoT server and transfer a message. Now, unlike cloud_client example which is constantly connected to the cloud thanks to the following code structure within the main loop:

connect:
	err = cloud_connect(cloud_backend);
	if (err) {
		printk("cloud_connect, error: %d\n", err);
	}

	struct pollfd fds[] = {
		{
			.fd = cloud_backend->config->socket,
			.events = POLLIN
		}
	};

	while (true) {
		/* The timeout is set to (keepalive / 3), so that the worst case
		 * time between two messages from device to broker is
		 * ((4 / 3) * keepalive + connection overhead), which is within
		 * MQTT specification of (1.5 * keepalive) before the broker
		 * must close the connection.
		 */
		err = poll(fds, ARRAY_SIZE(fds),
			K_SECONDS(CONFIG_MQTT_KEEPALIVE / 3));

		if (err < 0) {
			printk("poll() returned an error: %d\n", err);
			continue;
		}

		if (err == 0) {
			cloud_ping(cloud_backend);
			continue;
		}

		if ((fds[0].revents & POLLIN) == POLLIN) {
			cloud_input(cloud_backend);
		}

		if ((fds[0].revents & POLLNVAL) == POLLNVAL) {
			printk("Socket error: POLLNVAL\n");
			printk("The cloud socket was unexpectedly closed.\n");
			return;
		}

		if ((fds[0].revents & POLLHUP) == POLLHUP) {
			printk("Socket error: POLLHUP\n");
			printk("Connection was closed by the cloud.\n");
			return;
		}

		if ((fds[0].revents & POLLERR) == POLLERR) {
			printk("Socket error: POLLERR\n");
			printk("Cloud connection was unexpectedly closed.\n");
			return;
		}
	}
	cloud_disconnect(cloud_backend);
	goto connect;

we would like our device to be in sleep mode, to wake up periodically, connect to the cloud and receive/transmit messages. We used timers to implement non-trivial task execution on periodic basis. The approach is taken from here. Since the required work cannot be done at interrupt level, the timer’s expiry function submits a work item to the system workqueue, whose thread performs the work:

void periodic_timer_handler(struct k_timer *dummy)
{
    k_delayed_work_submit(&bintech_work, K_NO_WAIT);
}

Is this good approach? Should we use another periodic timer to keep connection alive as long as we need it (i.e. to simulate

while (true)
) loop from above?

Do you see any better way to implement this periodic Server update activities?

What we noticed so far is that we have a problem to execute configure_modem() function if we move it to the system workqueue with k_delayed_work_submit(). If we run a function from the main(), however, it executes successfully. Is there anything we are missing here?

Thanks for your time and efforts!

Sincerely,

Bojan.

  • Hey, guys...

    I decided to try with the threads (control_thread_id and cloud_thread_id).

    I will have one thread (control_thread_id) that will be in charge of periodically taking sensor data and transferring them to the second thread (cloud_thread_id) by the help of FIFO object used to pass the data from thread to thread (link). Second thread will be responsible for keeping MQTT connection with the cloud alive until necessary.

    By following basic thread example I defined two threads:

    void cloud_control(void){
        int err;
        InterThreadData_t* inter_thread_data;
    
        printk("Cloud control thread has started\n");
    
        cloud_backend = cloud_get_binding(CONFIG_CLOUD_BACKEND);
        __ASSERT(cloud_backend != NULL, "%s backend not found",
                 CONFIG_CLOUD_BACKEND);
    
        err = cloud_init(cloud_backend, cloud_event_handler);
        if (err) {
                printk("Cloud backend could not be initialized, error: %d\n",
                        err);
        }
    
        while (1){
            inter_thread_data = k_fifo_get(&control_fifo, K_FOREVER);
            if (inter_thread_data->cloud_control_flag == 1){
                printk("Connect to cloud\n");
            }
            k_free(inter_thread_data);
        }
    }
    
    void bintech_control(void){
        int err;
    
        printk("Bintech control thread has started\n");
    
        k_delayed_work_init(&bintech_work, bintech_work_fn);
        init_hx711(&_hx711_data);
    
        /* start periodic timer that expires once every second */
        k_timer_start(&periodic_timer, K_SECONDS(DEFAULT_WAKEUP_PERIOD), K_SECONDS(DEFAULT_WAKEUP_PERIOD));
        printk("Periodic timer started\n");
    
        for (;;) {
    
            /* wait for ISR to trigger work to perform */
    //        if (k_sem_take(&my_sem, K_NO_WAIT) == 0) {
    
                /* ... do processing */
    
    //        }
    
        //TODO: replace k_cpu_idle() with k_cpu_atomic_idle()
            /* put CPU to sleep to save power */
            k_cpu_idle();
        }
    }
    
    
    void control_thread(void){
        bintech_control();
    }
    
    void cloud_thread(void){
        cloud_control();
    }
    
    
    K_THREAD_DEFINE(control_thread_id, STACKSIZE, control_thread, NULL, NULL, NULL, 6, 0, K_NO_WAIT);
    K_THREAD_DEFINE(cloud_thread_id, STACKSIZE, cloud_thread, NULL, NULL, NULL, PRIORITY, 0, K_NO_WAIT);

    Within the control_thread_id thread I defined periodic timer with the handler function that submits a work item to the system workqueue

    void periodic_timer_handler(struct k_timer *dummy)
    {
        k_delayed_work_submit(&bintech_work, K_NO_WAIT);
    }

    Within work item handler function I try to pass the data to the second thread:

    void bintech_work_fn(struct k_work *work)
    {
        printk("Periodic work to do here!\n");
        InterThreadData_t inter_thread_data;
        inter_thread_data.cloud_control_flag = 1;
    
        size_t size = sizeof(InterThreadData_t);
        char *mem_ptr = k_malloc(size);
        __ASSERT_NO_MSG(mem_ptr != 0);
    
        memcpy(mem_ptr, &inter_thread_data, size);
    
        k_fifo_put(&control_fifo, mem_ptr);
        
    }

    Item handler function defined above is properly called (periodically). printk() message "Periodic work to do here" is successfully sent to the LTE Monitor App but the FIFO data are not received by the second thread.

    Oh the other hand, when I try to send the data not from the system workque but from within infinite for(; ;) loop of the  bintech_control() thread function:

        for (;;) {
    
        InterThreadData_t inter_thread_data;
        inter_thread_data.cloud_control_flag = 1;
    
        size_t size = sizeof(InterThreadData_t);
        char *mem_ptr = k_malloc(size);
        __ASSERT_NO_MSG(mem_ptr != 0);
    
        memcpy(mem_ptr, &inter_thread_data, size);
    
        k_fifo_put(&control_fifo, mem_ptr);
    
        
        k_sleep(5000);
    
            /* put CPU to sleep to save power */
            k_cpu_idle();
        }

    Data are successfully transferred to the second thread. I again have the similar problem with executing work items from the system workqueue. What I am missing here?

    To the best of my understanding workqueue item is properly initialized within bintech_control() function with:

    k_delayed_work_init(&bintech_work, bintech_work_fn);

    Is it the issue in the fact that work item variable is defined outside threads with:

    static struct k_delayed_work bintech_work;

    Sincerely,

    Bojan.

  • It seems that the issue was around thread priorities.

    When I set the priority of the cloud_thread_id to be higher compared to the control_thread_id the FIFO message is successfully passed from control_thread_id to cloud_thread_id thread.

    Now, when I receive FIFO message to connect to the cloud, I call configure_modem() function. However, that function seems to freeze when called from within cloud_thread_id :

    Connect to cloud
    Connecting to LTE network. This may take several minutes.

    What we are missing here, why this function is unable to properly run when called from the thread?

    It seems I have the same issue like the guys from this thread.

    Sincerely,

    Bojan.

  • Hello,

     

    What we noticed so far is that we have a problem to execute configure_modem() function if we move it to the system workqueue with k_delayed_work_submit(). If we run a function from the main(), however, it executes successfully. Is there anything we are missing here?

     Have a look at the post here. In short, you should not run configure_modem() from the system work queue since it uses the CFUN command that blocks for several seconds.

  • Hello .

    Thanks for your reply. If it is impossible to call configure_modem() function from the system workqueue, can we call it from the thread or from the workqueue we specify? I experience the same freezing behaviour when calling the function from the thread in multi-thread example.

    Regards,

    Bojan.

  • Okay then. It might be priority issue. Could you share the code so I can debug for my self?

Related