Documentation / Hardware

Peripherals and I/O

Buses, devices, expanders, analog and digital pins - and the poll loop that reads them.

Beyond the panel and the touch controller, a board has sensors, expanders, analog inputs and plain digital pins. The Buses, Devices and I/O tabs describe them, and the generator writes hardware.h and hardware.cpp to bring them up and read them on a schedule. Nothing here ever draws: the readings go into caches that variables read.

Buses

The Buses tab declares I2C and SPI buses beyond the display's. An I2C bus has SDA, SCL, a clock in kHz and an internal pull-up option (weak - fit 4.7 kΩ externals for anything but the shortest run). An SPI bus has SCK, MOSI, an optional MISO, a clock in MHz and the SPI mode. The first bus of a kind becomes Wire or SPI in the generated code, the next Wire1 or SPI1, and so on up to what the MCU has. A device attached to a bus that shares pins with the display's bus is legitimate and not flagged.

Devices

The Devices tab adds peripherals. Each has a Type, a Bus, an I2C address or a CS pin depending on the bus, and a Poll interval - how often hw_poll() reads it, independently of everything else, so a slow sensor never holds up a fast one or the display. Below those comes the device's own configuration: every register the driver library exposes, applied once in hw_init() after the device reports present.

The Devices tab with a BME280 on the first I2C bus: bus, address, poll interval, then its sampling configuration.
The Devices tab with a BME280 on the first I2C bus: bus, address, poll interval, then its sampling configuration.
GroupDeviceBusChannelsLibraryConfiguration
EnvironmentBME280I2CTemperature (C), Humidity (%), Pressure (hPa), Altitude (m)adafruit/Adafruit BME280 Librarymode, osTemp, osPress, osHum, filter, standby, seaLevel
EnvironmentBMP280I2CTemperature (C), Pressure (hPa)adafruit/Adafruit BMP280 Librarymode, osTemp, osPress, filter, standby
EnvironmentSHT31 / SHT35I2CTemperature (C), Humidity (%)adafruit/Adafruit SHT31 Libraryheater
EnvironmentAHT10 / AHT20I2CTemperature (C), Humidity (%)adafruit/Adafruit AHTX0-
EnvironmentSCD40 / SCD41 (CO2)I2CCO2 (ppm), Temperature (C), Humidity (%)sparkfun/SparkFun SCD4x Arduino LibrarytempOffset, altitude, asc
EnvironmentBH1750 (light)I2CIlluminance (lx)claws/BH1750mode, mtreg
EnvironmentMCP9808 (temperature)I2CTemperature (C)adafruit/Adafruit MCP9808 Libraryresolution, wake
ADCADS1115 (16-bit ADC)I2CChannel 0 (V), Channel 1 (V), Channel 2 (V), Channel 3 (V), Differential 0-1 (V)adafruit/Adafruit ADS1X15gain, rate
ADCINA219 (current)I2CBus voltage (V), Current (mA), Power (mW), Shunt voltage (mV)adafruit/Adafruit INA219calibration
ADCMAX17048 (battery gauge)I2CCell voltage (V), State of charge (%)adafruit/Adafruit MAX1704XalertMin, alertMax
ADCMCP3008 (8ch 10-bit ADC)SPIChannel 0 (counts), Channel 1 (counts), Channel 2 (counts), Channel 3 (counts), Channel 4 (counts), Channel 5 (counts), Channel 6 (counts), Channel 7 (counts)adafruit/Adafruit MCP3008vref
MotionMPU6050 (IMU)I2CAccel X (m/s2), Accel Y (m/s2), Accel Z (m/s2), Gyro X (rad/s), Gyro Y (rad/s), Gyro Z (rad/s), Temperature (C)adafruit/Adafruit MPU6050accelRange, gyroRange, bandwidth, cycleMode
MotionADXL345 (accelerometer)I2CAccel X (m/s2), Accel Y (m/s2), Accel Z (m/s2)adafruit/Adafruit ADXL345range, dataRate
MotionVL53L0X (distance)I2CDistance (mm)adafruit/Adafruit_VL53L0Xsense
TemperatureMAX6675 (thermocouple)SPITemperature (C)adafruit/MAX6675 librarysettleMs
TemperatureMAX31855 (thermocouple)SPITemperature (C), Cold junction (C)adafruit/Adafruit MAX31855 librarysettleMs
TimeDS3231 (RTC)I2CHour, Minute, Second, Day, Month, Year, Unix time (s), Die temperature (C)adafruit/RTClibsqw, enable32k, setOnBoot
StorageSD cardSPICard size (MB), Used (MB)SD (bundled with the core)freqMhz
OutputPCA9685 (16ch PWM)I2C-adafruit/Adafruit PWM Servo Driver LibrarypwmFreq, oscFreq, totemPole
OutputNeoPixel string (WS2812 / SK6812)GPIOPixel countadafruit/Adafruit NeoPixelpixels, order, speed, brightness

A device that fails to answer at boot is marked absent, its variables keep their initial values, and the sketch says so on the serial monitor rather than hanging. Each device's library is listed in LIBRARIES.md with the Library Manager search string.

Configuration worth knowing about

I/O extenders

Port expanders that add digital pins over I2C or SPI. Add one on the I/O tab with its bus and address (or CS pin), and digital I/O entries can then be placed on its pins instead of the MCU's. Each pin's direction is set by the entry that uses it, and the generated ext_<name>_write() and _read() helpers are guarded by the expander's presence.

ExtenderBusPinsLibrary
PCF8574I2C8xreef/PCF8574 library
PCF8575I2C16xreef/PCF8575 library
MCP23017I2C16adafruit/Adafruit MCP23017 Arduino Library
MCP23S17 (SPI)SPI16adafruit/Adafruit MCP23017 Arduino Library
CH422GI2C12built in (no library)
TCA9554 / PCA9554I2C8built in (no library)
PCA9555I2C16built in (no library)

The validator errors on an extender pin outside the part's width, on two entries claiming the same pin, and on an entry pointing at an extender that has been deleted. The same rules cover a display reset, touch reset or backlight routed through an extender (see Boards and displays).

The CH422G, TCA9554 and PCA9555 need no library: each is a few I2C register writes, and the generator writes a small driver class into hardware.cpp for them. The PCF and MCP parts use their Arduino libraries, listed in LIBRARIES.md as usual.

Analog inputs

An Analog input is an MCU ADC pin read and scaled. Fields:

FieldMeaning
PinAn ADC-capable GPIO on the chosen MCU.
AttenuationESP32 only: 11 dB (0-3.3 V), 6 dB (0-2.2 V), 2.5 dB (0-1.5 V) or 0 dB (0-1.1 V).
SamplesHow many conversions are averaged per read.
Poll (ms)How often it is read.
SmoothingAn exponential filter: 0 is raw, 0.2 is a good default, closer to 1 reacts faster.
ScalingA linear map from a raw range (e.g. 0-4095) to a value range (e.g. 0-100).
FormulaOptional; overrides the scaling. raw is the averaged ADC count.
UnitA label carried through to bindings.

The ADC pins per MCU come from the catalogue - the ESP32 has 16, the S3 20, the C3 6, the RP2040 4 - and a pin that is not one of them is an error. The STM32 and ESP32-P4 entries currently have no ADC pins listed, so analog inputs are not offered on them.

Digital I/O

A Digital input or output on an MCU pin or an extender pin:

FieldMeaning
SourceMCU pin or I/O extender.
Pin / Extender pinThe GPIO, or the extender and its pin number.
Active lowActive when the pin reads LOW. Everything downstream uses "active", not the raw level.
Pull-upEnable the internal pull-up on an input.
Debounce (ms)How long a level must hold before it counts.
Initial stateFor an output: start active or not.

Inputs are debounced in hw_poll() and exposed to variables as a Boolean; the Digital input changes and Digital input held automation triggers watch them. Outputs are driven by the Set digital output action. "Active" respects the active-low setting throughout, so a relay module that switches on LOW is described once and reads correctly everywhere.

What the generated code does

hardware.cpp declares one driver object per device and the bus objects they sit on. hw_init() begins each bus, probes each device, applies its configuration and records whether it answered. hw_poll(), called from app_poll() every loop, checks each device's own timer and reads it when due into a cache struct. Reads are cheap to ask for and never block on a slow part - a BME280 polled every two seconds costs nothing to a label refreshed ten times a second. The Refresh inputs now action forces every timer due at once.