Skip to content

File espos_flow.h

FileList > espos_flow > include > espos_flow.h

Go to the source code of this file

  • #include <stdbool.h>
  • #include <stddef.h>
  • #include <stdint.h>
  • #include "esp_err.h"

Classes

Type Name
struct espos_flow_stats_t

Public Types

Type Name
typedef void(* espos_flow_cb_t
typedef uint32_t espos_flow_timer_t

Public Functions

Type Name
esp_err_t espos_flow_adopt_loop (void)
Adopt the calling task as the loop for as long as the loop is NOT running, so emit() is permitted from it.
esp_err_t espos_flow_after (uint32_t delay_ms, espos_flow_cb_t cb, void * arg, espos_flow_timer_t * out)
Call cb(arg) once,delay_ms from now.
esp_err_t espos_flow_cancel (espos_flow_timer_t h)
Cancel a timer.
esp_err_t espos_flow_every (uint32_t period_ms, espos_flow_cb_t cb, void * arg, espos_flow_timer_t * out)
Call cb(arg) everyperiod_ms , starting one period from now.
bool espos_flow_is_running (void)
True between a successful start and a stop.
void espos_flow_note_edges_used (uint32_t n)
uint32_t espos_flow_now_ms (void)
Milliseconds since the loop's epoch — a monotonic counter that never jumps when the wall clock is learned.
bool espos_flow_on_loop_task (void)
True when called on the flow task.
esp_err_t espos_flow_post (espos_flow_cb_t cb, void * arg)
Run cb(arg) on the flow task, from any task.
esp_err_t espos_flow_post_from_isr (espos_flow_cb_t cb, void * arg, bool * hp_task_woken)
espos_flow_post() from an interrupt.
void espos_flow_release_loop (void)
Undo espos_flow_adopt_loop() .
bool espos_flow_run_until_idle (uint32_t timeout_ms)
Run timers and mailbox work until neither has anything left to do, or until timeout_ms has passed; return true if the loop actually reached idle.
esp_err_t espos_flow_start (void)
Start the flow loop: creates the task (CONFIG_ESPOS_FLOW_TASK_STACK, CONFIG_ESPOS_FLOW_TASK_PRIO) and its mailbox.
void espos_flow_stats (espos_flow_stats_t * out)
Snapshot the counters.
esp_err_t espos_flow_stop (void)
Stop the loop and delete its task.

Macros

Type Name
define ESPOS_FLOW_TIMER_NONE (([**espos\_flow\_timer\_t**](espos__flow_8h.md#typedef-espos_flow_timer_t))0)

Public Types Documentation

typedef espos_flow_cb_t

typedef void(* espos_flow_cb_t) (void *arg);

typedef espos_flow_timer_t

typedef uint32_t espos_flow_timer_t;

Public Functions Documentation

function espos_flow_adopt_loop

Adopt the calling task as the loop for as long as the loop is NOT running, so emit() is permitted from it.

esp_err_t espos_flow_adopt_loop (
    void
) 

Returns ESP_ERR_INVALID_STATE once espos_flow_start() has created the real loop — the check exists precisely to stop a second task emitting alongside it, and this may not be used to subvert that.

Two legitimate uses, both single-threaded by construction:

  • Wiring that emits during start-up — a Constant priming a chain, an initial reading pushed before the loop exists.
  • A host test that drives the graph by hand between espos_flow_run_until_idle() calls.

espos_flow_run_until_idle() does this for the duration of the call by itself; this is the same borrow held open across several statements. espos_flow_release_loop() gives it back.


function espos_flow_after

Call cb(arg) once,delay_ms from now.

esp_err_t espos_flow_after (
    uint32_t delay_ms,
    espos_flow_cb_t cb,
    void * arg,
    espos_flow_timer_t * out
) 

delay_ms 0 runs it on the loop's next pass, which is the cheapest way to move work off the current task onto the flow task with a delay of "as soon as convenient".

The handle is spent once the callback has run; cancelling it afterwards is ESP_ERR_NOT_FOUND, never a cancellation of some unrelated later timer.


function espos_flow_cancel

Cancel a timer.

esp_err_t espos_flow_cancel (
    espos_flow_timer_t h
) 

Safe from inside the callback of the timer being cancelled. ESP_ERR_NOT_FOUND when the handle is stale (already fired one-shot, already cancelled, never existed) — which is the answer, not a failure.


function espos_flow_every

Call cb(arg) everyperiod_ms , starting one period from now.

esp_err_t espos_flow_every (
    uint32_t period_ms,
    espos_flow_cb_t cb,
    void * arg,
    espos_flow_timer_t * out
) 

The deadline is advanced from the deadline just met, so the period does not drift with the callback's own duration; periods missed because the loop was busy are skipped, not fired back to back.

Returns:

ESP_ERR_NO_MEM when CONFIG_ESPOS_FLOW_MAX_TIMERS is exhausted, ESP_ERR_INVALID_ARG for a NULL callback, a period of 0 or a period over 24.8 days.


function espos_flow_is_running

True between a successful start and a stop.

bool espos_flow_is_running (
    void
) 


function espos_flow_note_edges_used

void espos_flow_note_edges_used (
    uint32_t n
) 

function espos_flow_now_ms

Milliseconds since the loop's epoch — a monotonic counter that never jumps when the wall clock is learned.

uint32_t espos_flow_now_ms (
    void
) 

This is NOT espos_time: espos_time answers "what time is it" and reads 0 until something syncs it; this answers "how long since" and is always usable. A node that needs to stamp a value for SignalK uses espos_time; a node that needs to know whether two inputs are within 200 ms of each other uses this.

uint32_t, so it wraps every 49.7 days. Every comparison in espos_flow is modular (see espos_sched.h) and yours must be too: subtract, never compare.


function espos_flow_on_loop_task

True when called on the flow task.

bool espos_flow_on_loop_task (
    void
) 

The C++ layer's emit() checks this under CONFIG_ESPOS_FLOW_CHECK_TASK; an application writing its own node in C can use it for the same assertion.


function espos_flow_post

Run cb(arg) on the flow task, from any task.

esp_err_t espos_flow_post (
    espos_flow_cb_t cb,
    void * arg
) 

This is the seam every external value crosses: a driver callback, a task that just finished a blocking read, another component's subscription callback.

Ordering is FIFO: two posts from the same task run in that order. Posts from different tasks interleave however the scheduler decided, which is the only nondeterminism in the whole model.

Never blocks. When the mailbox is full the post is dropped, the drop is counted, and health condition "flowMailbox" is raised WARN once — a full mailbox means the loop is behind or a producer is too fast, and neither is fixed by blocking the producer (that would just push the stall upstream, possibly into an ISR).

Returns:

ESP_ERR_NO_MEM when the mailbox is full, ESP_ERR_INVALID_STATE when the loop is not running, ESP_ERR_INVALID_ARG for a NULL callback.


function espos_flow_post_from_isr

espos_flow_post() from an interrupt.

esp_err_t espos_flow_post_from_isr (
    espos_flow_cb_t cb,
    void * arg,
    bool * hp_task_woken
) 

Same contract, ISR-safe primitives.

hp_task_woken may be NULL; when it is not, it is set to true if the post woke a task of higher priority than the interrupted one, and the ISR should then yield (portYIELD_FROM_ISR). The callback still runs on the flow task — an ISR posts the work, it does not do it.


function espos_flow_release_loop

Undo espos_flow_adopt_loop() .

void espos_flow_release_loop (
    void
) 

Harmless when nothing was adopted.


function espos_flow_run_until_idle

Run timers and mailbox work until neither has anything left to do, or until timeout_ms has passed; return true if the loop actually reached idle.

bool espos_flow_run_until_idle (
    uint32_t timeout_ms
) 

Two uses. A test drives a graph deterministically: post, run until idle, assert. And a device about to enter deep sleep drains what is pending before the clock stops, so a queued publish is not lost to the nap.

Callable only when the loop is NOT running (ESP_ERR_INVALID_STATE otherwise, reported as false): it does the loop's job, so the two would fight over the same tables. A firmware that wants both starts the loop after start-up and stops it before sleeping.


function espos_flow_start

Start the flow loop: creates the task (CONFIG_ESPOS_FLOW_TASK_STACK, CONFIG_ESPOS_FLOW_TASK_PRIO) and its mailbox.

esp_err_t espos_flow_start (
    void
) 

Idempotent — a second call with the loop already running returns ESP_OK, so a library and its application may both call it without arranging who goes first.

Timers may be added before this: they are held and start counting from the moment the loop starts, which is what lets a device struct arm its poll in its own constructor, long before app_main() decides to run the graph.

Returns:

ESP_ERR_NO_MEM when the task or the queue cannot be created.


function espos_flow_stats

Snapshot the counters.

void espos_flow_stats (
    espos_flow_stats_t * out
) 

Safe from any task. Cheap enough to put behind a REST endpoint or publish as a SignalK path; dropped growing is the one number that means something is wrong.


function espos_flow_stop

Stop the loop and delete its task.

esp_err_t espos_flow_stop (
    void
) 

Anything still in the mailbox is dropped (counted in dropped); timers stay armed, so a later espos_flow_start() or espos_flow_run_until_idle() fires whatever is due. To keep work that was already posted, post a marker and wait for it to run before stopping: the mailbox is first in, first out. Mostly for tests and for a device going into deep sleep; a normal firmware starts the loop and leaves it running.

Must not be called from the flow task itself (ESP_ERR_INVALID_STATE): a task cannot wait for its own exit.


Macro Definition Documentation

define ESPOS_FLOW_TIMER_NONE

#define ESPOS_FLOW_TIMER_NONE `(( espos_flow_timer_t )0)`


The documentation for this class was generated from the following file espos_flow/include/espos_flow.h