Skip to content

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.yaml or board_esp32s3.yaml) to that path.
  • A factory reset (hold the button) renames the current file to board_invalid.yaml and 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 / adcChannels field 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)
button:
  gpio: 0

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
pilot:
  gpio: 27
  adcChannel: 0
  levels: [2405, 2099, 1792, 1484, 728]

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.
acRelay:
  gpios: [26]

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
leds:
  charging:
    gpio: 19
  error:
    gpio: 18
  wifi:
    gpio: 17

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
proximity:
  adcChannel: 3
  levels: [1650, 820, 430]

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
socketLock:
  gpios: [20, 19]
  detectionGpio: 34
  detectionDelay: 1000
  minBreakTime: 1000

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
rcm:
  gpio: 41
  testGpio: 26

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
onewire:
  gpio: 16
  temperatureSensor: true

Complete examples~

Check out the source repository for actual examples.

See also~