Skip to content

Architecture

How the Flux V1 HAL is put together. The short version: your code talks to one object, Flux; Flux holds one manager per board function; managers own hf-core handlers; handlers wrap chip drivers; drivers reach the hardware only through the interface classes of the internal interface wrap. The objects behind those interfaces are created by a platform binding, one per MCU family, which is the only code that includes ESP-IDF.

Layer stack of the Flux V1 HAL: application, the Flux facade, 7 MCU-free managers, 5 hf-core handlers, 6 chip drivers, the interface classes, and the platform binding that creates their ESP32 implementations on ESP-IDF and the ESP32-S3.Layer stack of the Flux V1 HAL: application, the Flux facade, 7 MCU-free managers, 5 hf-core handlers, 6 chip drivers, the interface classes, and the platform binding that creates their ESP32 implementations on ESP-IDF and the ESP32-S3.
Flux V1 HAL, layer by layer
Figure description and sources

From top to bottom, each layer only calls the one under it. Your application asks the facade singleton for the managers. The managers (one per board function) own the hf-core handlers, which wrap one chip driver each. The managers are MCU-free: the platform binding of the MCU family (bsp_bind/esp32s3 on this board) creates the objects behind the interface classes, the ESP32 implementations that sit on ESP-IDF, and is the only HAL code that includes ESP-IDF. Drivers talk to hardware only through the interface classes. Every name in a band is read from the code: the managers from the facade's member references, the handlers, drivers and interface classes from the managers', handlers' and binding's #include lines.

Drawn from: lib/api/Flux.h Flux (manager references); lib/managers #includes; lib/core/handlers #includes; lib/managers/bsp_bind #includes; figure module docs/diagrams/figures/hal-layers.py.

The layers

Layer Where What it does You use it when
Application your project, examples/esp32 Tasks, control loops, protocols Always
Facade lib/api/Flux.h One singleton; brings the managers up in order; system diagnostics Always: Flux::GetInstance()
Managers lib/managers One per board function (valves, ADC, temperature, ...); know the board's wiring from the pin map; MCU-free Almost always: flux.valves, flux.adc, ...
Platform binding lib/managers/bsp_bind Turns the managers' plain-data specs into Base* objects; esp32s3/ is the only ESP-IDF code in the HAL When porting to another MCU (Platform bindings)
hf-core handlers lib/core/handlers Wrap one chip driver each and adapt it to the interface classes Rarely; for chip features a manager does not expose
Chip drivers lib/core/hf-core-drivers/external Register-level drivers, one repository per chip Almost never from application code
Interface wrap lib/core/hf-core-drivers/internal/hf-internal-interface-wrap BaseSpi, BaseGpio, BaseAdc, ... and their ESP32 implementations (EspSpi, EspGpio, ...), which only the binding names When porting to another MCU

The rule that holds the design together: a layer only calls the layer below it. The managers are the only code that knows Flux V1 exists; handlers and drivers are shared with other HardFOC boards. Why the line is drawn there is recorded in ADR 0001; why the MCU objects are created in a binding and not in the managers is ADR 0004, and a lint in CI keeps it that way.

Read on

  • Platform bindings: what the binding creates, the families, and the boundary lint.
  • Initialisation: the order the managers come up and what happens when one fails.
  • Ownership: which manager creates which handler, and how memory is allocated.
  • Threading: which calls are safe from several tasks.
  • Errors and diagnostics: error enums, GetLastError(), health snapshots.