Concepts

These sections explain some concepts of Drogue Device.

Actor System

An actor system is a framework that allows for isolating state within narrow contexts, making it easier to reason about system. Within a actor system, the primary component is an Actor, which represents the boundary of state usage. Each actor has exclusive access to its own state and only communicates with other actors through message-passing.

Actor model example

Drogue Actor Model

Drogue Device Actor Model

Async

Each actor is ostensibly single-threaded, able to process a single message at a time, allowing for lock-free processing of each event. As embedded processors are ostensibly globally single-threaded, supporting multiple actors requires the usage of async and .await within the Rust ecosystem. Each actor can therefore process each message either synchronously if its logic is non-blocking or using an async block if complex processing is required.

Each event is fully processed, in the order in which it is received, before the next event is considered.

While processing an event, an actor may send a message to another actor, which itself is an asynchronous action, allowing the system to continue to make progress with actors that are able to.

Messages

All messages are sent using async channels attached to each actor. The channel depth is configurable based on generic-array and heapless. Once const generics is used by heapless, we will make the move as well.

Addresses

Each actor within the system has its own unique Address which is used to communicate with the actor (through it’s FIFO). There is an async send(msg) method on each address to send a message asynchronously to the actor, which may only be used from another async context, as the sender must .await the response.

Specifically, the Address for a given actor may expose additional async methods to facility fluent APIs for communicating with the underlying actor. For instance, the Address<SimpleLED<…​>> instance has a turn_on() and turn_off() pair of methods for manipulating the underlying LED.

State

Each actor is wrapped in a state object which is executed by the embassy runtime. When each state is mount(…​)`ed into the system, its `Address<…​> is made available.

Each Actor in the system defines the configuration it expects to get handed in its mount() implementation.

Bootstrap & Mounting

A top-level Device struct created by the application writer, maintain members for each actor. A static instance of DeviceContext is created to hold a static reference to the Device struct.

The application entry point is specified using a function marked with #[embassy::main], and it will be passed a Spawner that is used when mounting actors and packages.

Packages

In some cases, it may be desirable to have two or more actors involve in a single semantic component or package. The Package trait may be implemented for any type.