Skip to content

V7 architecture: structured initialization and configuration layering #1067

Description

@terrillmoore

Summary

Capture design thinking for a future major refactor of the initialization and configuration architecture.

Current state

Configuration is spread across multiple mechanisms without clean separation:

  1. OS/compile environment adaptation (platform-specific types, timing)
  2. LMIC feature selection (Class C, ADR, region/bandplan -- region will become runtime-selectable)
  3. Radio driver configuration (oscillator trim, RF switch, DCDC, TCXO -- currently SX126x- and SX127x-specific queries live on the shared HalConfiguration_t base class)
  4. SPI/communication channel (pin assignments, SPI frequency -- bundled with radio config in HalPinmap_t)
  5. Secure element configuration (driver selection, key storage -- has its own driver abstraction via LMIC_CFG_SecureElement_DRIVER)
  6. Debug/logging (affects timing; most users don't know how to use it safely)

The radio driver and HAL are pure C (lmic.c, radio_sx126x.c, radio_sx127x.c). hal.cpp is C++ and provides extern "C" bridge functions. This works but means radio-specific configuration queries (e.g., SX126x crystal trim) end up on the shared HalConfiguration_t class where all board files see them.

Desired architecture

Abstract initialization order:

  1. Create OS platform
  2. Create low-level drivers with handles (SPI, I2C, GPIO)
  3. Create radio driver, passing SPI handle, GPIO handles, OS platform
  4. Create SE driver, passing I2C/SPI handle as appropriate
  5. Create LMIC, passing radio and SE driver handles

Key principles:

  • Radio-specific configuration should live on a radio-specific configuration object, not the shared HAL base class
  • Low-level operations (GPIO toggles, DIOx queries) should be passed as explicit config vars in a config structure, or as handles to low-level drivers (e.g., "antenna switch" driver)
  • The SE driver abstraction (LMIC_SecureElement_DECLARE_DRIVER_FNS / LMIC_SecureElement_METHOD pattern) is a reasonable model for driver dispatch
  • AES implementation selection is a configuration of the default SE driver, not independent
  • For Arduino, the C++ approach with extern "C" bridges is preferred for end-user convenience (less typing than pure C structs)

Radio driver interface

The radio driver interface (os_radio(), radio_init(), radio_config(), etc.) is currently internal and undocumented as a contract. This needs to be specified as a stable interface to support:

  • External radio drivers (Add option to use externally defined radio #1014 -- ESPHome wants to proxy radio operations through its native LoRa drivers)
  • Non-PHY transports (LoRaWAN-over-Meshtastic / LWoM -- replaces the LoRa PHY entirely with Meshtastic packet transport, using soft RX timing and runtime region selection)

Both use cases require a well-defined radio driver contract that survives LMIC internal refactoring (v6.0.0 broke external assumptions by adding os_radio_v2() and changing radio state management).

doc/RadioDriver.md exists and is a starting point, but needs to be promoted to a formal interface specification.

Why this matters

Each new SX126x feature (crystal trim, DIO2/DIO3 config, DCDC) adds radio-specific methods to the shared base class. SX127x board files see SX126x methods and vice versa. As more radio families and non-PHY transports are supported, this becomes increasingly messy.

Scope

This is a major refactor, likely V7. For now, accept SX126x-specific queries on HalConfiguration_t but avoid namespace pollution (no unnecessary header includes).

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions