Module threads

Serial Modem Host Applications

tags
Serial Modem Host Applications

Each module's thread follows the same pattern of waiting for new messages and handling them by executing a state machine. For example, a simplified version of the Network module's thread looks like this:

static void network_module(void)
{
    int err;
    int task_wdt_id;
    const uint32_t wdt_timeout_ms =
        (CONFIG_APP_NETWORK_WATCHDOG_TIMEOUT_SECONDS * MSEC_PER_SEC);
    const uint32_t execution_time_ms =
        (CONFIG_APP_NETWORK_MSG_PROCESSING_TIMEOUT_SECONDS * MSEC_PER_SEC);
    const k_timeout_t zbus_wait = K_MSEC(wdt_timeout_ms - execution_time_ms);

    task_wdt_id = task_wdt_add(wdt_timeout_ms, network_wdt_callback, (void *)k_current_get());

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

    while (true) {
        /* Feed the watchdog before blocking on the next message */
        err = task_wdt_feed(task_wdt_id);

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

        /* 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);
            FATAL_ERROR();
        }
    }
}

In this pattern:

  1. The thread feeds its task watchdog, then waits for a message using zbus_sub_wait_msg().
  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.

The wait is bounded rather than K_FOREVER so that the watchdog is fed even when no messages arrive. The timeout is the watchdog timeout minus the time budgeted for processing a single message, which leaves room for the longest possible message handling before the watchdog expires. A timeout returns -ENOMSG, which simply restarts the loop.