Module threads

Asset Tracker Template

tags
Asset Tracker Template

Most modules in the Asset Tracker Template have dedicated threads. If a module uses blocking calls while processing messages, a dedicated thread is required. For example, the Network module may react to a message by sending an AT command to the modem, which might be blocked until signaling with the network completes and a response is received. Separate threads also help to keep the required stack size for each module more predictable.

Each module's thread follows a similar pattern of waiting for new messages and handling them by executing a state machine, illustrated as follows:

Module thread, SMF, and zbus relationship

For example, a simplified version of the network module's thread looks like this:

static void network_module_thread(void)
{
        int err;
        static struct network_state_object network_state;

        /* Initialize the state machine to the initial state */
        smf_set_initial(SMF_CTX(&network_state), &states[STATE_RUNNING]);

        while (true) {
                /* Wait for a message on any subscribed channel */
                err = zbus_sub_wait_msg(&network, &network_state.chan,
                                        network_state.msg_buf, K_FOREVER);
                if (err == -ENOMSG) {
                        continue;
                } else if (err) {
                        LOG_ERR("zbus_sub_wait_msg, error: %d", err);
                        return;
                }

                /* Run the state machine with the received message */
                err = smf_run_state(SMF_CTX(&network_state));
                if (err) {
                        LOG_ERR("smf_run_state(), error: %d", err);
                        return;
                }
        }
}

In this pattern:

  1. The thread waits for a message using zbus_sub_wait_msg(), which blocks until a message is received.
  2. When a message arrives, it is stored in the state object's buffer along with the channel it was received on.
  3. The state machine is then executed with smf_run_state(), which calls the appropriate handler for the current state.
  4. The handler processes the message based on its type and the current state, potentially triggering state transitions.
  5. The loop continues, waiting for the next message.