Board configuration schema~
The firmware ships as one binary per ESP32 family and contains no GPIO
numbers, ADC channels or board-specific wiring. Everything that describes a
particular board lives in a single YAML file, board.yaml, read at boot. The
same firmware therefore runs on any board built around a given chip — you only
write a config file. See Architecture for the bigger picture.
This page documents the current schema, board-config-schema-1.json
(in the repo).
Where the file lives~
board.yaml is stored on a dedicated LittleFS partition labelled usr
(320 KB), mounted at /usr, so the full path on the device is
/usr/board.yaml.
- On first boot, or whenever the file is missing, the firmware writes a built-in
default for the target chip (
board_esp32.yaml,board_esp32s2.yamlorboard_esp32s3.yaml) to that path. - A factory reset (hold the button) renames the current file to
board_invalid.yamland regenerates the default on the next boot. - The same partition is exposed over WebDAV,
so you can edit the file in place at
dav://<device-ip>/dav/usr/board.yaml. It also holds the Lua scripts under/usr/lua/.
Warning
The config is parsed with a hard assertion: if board.yaml is malformed
or fails validation, the device aborts and reboots rather than starting
with a bad configuration. Validate before you save — keep a known-good copy,
and use the schema in your editor (below) to catch mistakes early.
Editor autocompletion~
Both example files start with a language-server directive so editors with the YAML extension give you completion and validation against the schema:
# yaml-language-server: $schema=https://github.com/dzurikmiroslav/esp32-evse/raw/refs/heads/master/board-config/board-config-schema-1.json
GPIO and ADC conventions~
- All pin fields are raw GPIO numbers for the chip, not board silk-screen labels. Pick pins valid for the function on your chip (e.g. input-only pins cannot drive a relay).
- Every
adcChannel/adcChannelsfield is an ADC1 channel index, not a GPIO number. Only ADC1 is usable while Wi-Fi is active. Map the channel to its pin from the Espressif datasheet for your chip. The control pilot and energy meter sensing must be on ADC1.
Top-level structure~
| Key | Type | Required | Purpose |
|---|---|---|---|
deviceName |
string (≤ 32) | ✅ | Human-readable device name |
button |
object | ✅ | The multifunction button |
pilot |
object | ✅ | Control pilot (PWM + sensing) |
acRelay |
object | ✅ | Mains contactor control |
ota |
object | ✅ | OTA update channels |
leds |
object | — | Status LEDs |
proximity |
object | — | Proximity pilot sensing |
socketLock |
object | — | Socket lock actuator |
rcm |
object | — | Residual current monitor |
energyMeter |
object | — | Energy metering |
aux |
object | — | Auxiliary GPIO / ADC exposed to scripting |
serials |
array | — | Serial ports (UART / RS-485) |
onewire |
object | — | 1-Wire bus (temperature sensor) |
No properties beyond those listed in each object are allowed
(additionalProperties: false throughout) — a typo'd key is a validation error.
A peripheral that is simply omitted is treated as absent, and its driver does
nothing.
Required sections~
deviceName~
A string up to 32 characters, shown in the UI and via
GET /board-config.
button~
The single multifunction button (short press / long press / factory reset).
| Field | Type | Required | Notes |
|---|---|---|---|
gpio |
integer | ✅ | Button GPIO (active by board wiring) |
pilot~
Control pilot: a PWM output plus an ADC1 input that reads the CP voltage to detect vehicle state. See Control pilot and CP calibration.
| Field | Type | Required | Notes |
|---|---|---|---|
gpio |
integer | ✅ | PWM generation GPIO |
adcChannel |
integer | ✅ | ADC1 channel that measures CP |
levels |
integer[5] | ✅ | ADC thresholds in mV for the +12, +9, +6, +3 and −12 V CP levels, in that order |
The five levels are board-specific (they depend on the CP divider/clamp
network) and are what the CP calibration
procedure produces.
acRelay~
Controls the mains contactor.
| Field | Type | Required | Notes |
|---|---|---|---|
gpios |
integer[1..2] | ✅ | Contactor control GPIOs |
The array length selects single- vs. split-phase switching:
- One GPIO — drives all phases together (L1–L3).
- Two GPIOs — the first drives L1, the second drives L2–L3, allowing the EVSE to switch between single-phase and three-phase charging.
ota~
Over-the-air update sources. Each channel points at a JSON descriptor that the firmware update endpoints consult.
| Field | Type | Required | Notes |
|---|---|---|---|
channels |
array[1..3] | ✅ | One to three OTA channels |
Each channel:
| Field | Type | Required | Notes |
|---|---|---|---|
name |
string (≤ 16) | ✅ | Channel label shown in the UI |
path |
string | ✅ | URL of the channel's JSON descriptor |
ota:
channels:
- name: stable
path: https://dzurikmiroslav.github.io/esp32-evse/ota/stable/esp32.json
# - name: testing
# path: https://dzurikmiroslav.github.io/esp32-evse/ota/testing/esp32.json
Optional sections~
leds~
Up to three status LEDs; each is an object with a single gpio. Any of the
three may be omitted.
| Sub-key | Meaning |
|---|---|
charging |
Charging-state LED |
error |
Error LED |
wifi |
Wi-Fi-state LED |
proximity~
Proximity pilot sensing — reads the cable's current rating resistor. Used in socket-outlet mode. See Proximity pilot.
| Field | Type | Required | Notes |
|---|---|---|---|
adcChannel |
integer | ✅ | ADC1 channel measuring PP |
levels |
integer[3] | ✅ | ADC thresholds in mV for 13 A, 20 A and 32 A cables, in that order |
socketLock~
Motorized socket lock actuator (Type-2 socket outlets). See Socket lock.
| Field | Type | Required | Notes |
|---|---|---|---|
gpios |
integer[2] | ✅ | Drive GPIOs for lock coil A and B (direction) |
detectionGpio |
integer | ✅ | Lock-state feedback input |
detectionDelay |
integer | ✅ | Delay after actuating before reading state, in ms |
minBreakTime |
integer | ✅ | Minimum pause between repeated lock/unlock actions, in ms |
The operating time, retry count and detection polarity are runtime settings
(/config/evse), not part of board.yaml.
rcm~
Residual current monitor (6 mA DC / AC fault detection with self-test). See Residual current monitor.
| Field | Type | Required | Notes |
|---|---|---|---|
gpio |
integer | ✅ | Fault-sense input GPIO |
testGpio |
integer | ✅ | Self-test trigger output GPIO |
energyMeter~
Integrated energy meter. current sensing is required; voltage sensing is
optional — without it the meter uses the configured nominal AC voltage. The
number of adcChannels (1 or 3) determines single- vs. three-phase measurement.
See Energy metering.
| Field | Type | Required | Notes |
|---|---|---|---|
current |
object | ✅ | Current sensing |
voltage |
object | — | Voltage sensing |
Each of current / voltage:
| Field | Type | Required | Notes |
|---|---|---|---|
adcChannels |
integer[1..3] | ✅ | ADC1 channels for L1 (L2, L3). One item = single phase |
scale |
number | ✅ | Multiplier converting the raw measurement to A or V |
energyMeter:
current:
adcChannels: [4, 5, 6]
scale: 0.090909091
voltage:
adcChannels: [7, 8, 9]
scale: 0.47
The active meter mode (dummy / cur / cur_vlt) and three-phase flag are
chosen at runtime via /config/evse, but are
constrained by what the hardware here declares.
aux~
Auxiliary IO surfaced to Lua scripting and reported by
GET /board-config. Each entry has a short
name (≤ 8 characters) used to reference it from scripts.
| Sub-key | Type | Max entries | Entry fields |
|---|---|---|---|
inputs |
array | 4 | name, gpio |
outputs |
array | 4 | name, gpio |
analogInputs |
array | 2 | name, adcChannel (ADC1) |
aux:
inputs:
- name: IN1
gpio: 11
outputs:
- name: OUT1
gpio: 17
analogInputs:
- name: IN3
adcChannel: 0
Note
The schema text expresses these caps with maxLength, which JSON Schema
only applies to strings, so a generic validator will not reject an
over-long array. The firmware enforces the real limits (4 inputs, 4
outputs, 2 analog inputs); extra entries are ignored or rejected at load.
serials~
Serial ports, in order. Each port can be plain UART or RS-485, and its
function (log, Modbus, Nextion, AT, script) is selected at runtime via
/config/serial. Shared by
Modbus RTU, Nextion and AT commands.
| Field | Type | Required | Notes |
|---|---|---|---|
type |
string | ✅ | uart or rs485 |
name |
string (≤ 16) | ✅ | Label shown in the UI |
rxdGpio |
integer | ✅ | RX GPIO |
txdGpio |
integer | ✅ | TX GPIO |
rtsGpio |
integer | rs485 only | Flow-control / direction GPIO — required when type: rs485 |
serials:
- type: uart
name: UART
rxdGpio: 12
txdGpio: 13
- type: rs485
name: RS-485
rxdGpio: 40
txdGpio: 38
rtsGpio: 39
Note
The schema allows up to 3 entries, but the real ceiling is the chip's UART
count (SOC_UART_NUM): 3 on ESP32 / ESP32-S3, 2 on ESP32-S2.
Additionally, the UART used for the system console is reserved and forced to
"no port" at boot, so one slot is effectively consumed by the console on a
standard build.
onewire~
A 1-Wire bus, currently used for temperature sensors.
| Field | Type | Required | Notes |
|---|---|---|---|
gpio |
integer | ✅ | 1-Wire data GPIO |
temperatureSensor |
boolean | ✅ | Whether temperature sensors are present on the bus |
Complete examples~
Check out the source repository for actual examples.
See also~
- Build examples — reference hardware designs
- Architecture — how the config drives the firmware
- REST API:
GET /board-config— read the parsed result at runtime