GPIO & boards¶
This page tells you which ESP32-S3 pins AWTRIX NG can use, how to enter your own pin map, and what happens when a pin is not allowed.
There is one firmware for every ESP32-S3 board. Only the pin map is different, and you set it in
the web UI or through the API. The firmware comes in an -s3-octal- and an -s3-quad- variant.
Which file you need is explained in
Install AWTRIX NG.
Changes apply after a reboot
When you save a new pin map, AWTRIX stores it and answers 200. It keeps using the old map
until you restart it.
Set the pin map¶
- Open the web UI and go to System → GPIO.
- Pick a pin for each part you have wired. Choose not connected for parts you do not have.
- Save.
- Restart AWTRIX.
The dropdowns only offer pins that work for that part on your chip. See The web UI's pin dropdowns.
To do the same through the API, see Reading and writing the map.
Rules come from the chip¶
The ESP32-S3 has these pin rules:
| ESP32-S3 | |
|---|---|
| GPIO numbers | 0–48, but 22–25 do not exist |
| Input-only | none – every pin can drive an output |
| ADC1 (analog input) | 1–10 |
| Reserved | 19–20 (USB-JTAG), 26–37 (flash + octal PSRAM), 43–44 (UART0) |
| LED matrix data | 13, 14, 15, 16, 17, 18, 21, 38, 39, 40, 41, 42, 47 |
| Strapping | 0, 3, 45, 46 |
| Wake from deep sleep | 0–21 |
GPIO 26–37 are reserved even on a board without PSRAM.
Strapping pins are read by the chip at power-on to decide how it starts. If you connect something that pulls such a pin high or low, the board may not start. AWTRIX accepts these pins, so the choice is yours.
Wake pins matter only for the select button (pinBtnSelect). If the select button is on a
wake pin, pressing it ends a POST /api/v1/device/sleep early.
On any other pin the button works normally while AWTRIX is awake, but it cannot wake it up. The
sleep then runs for its full durationMs. The left and right buttons never wake AWTRIX.
AWTRIX reports the rules for its own chip under gpio in
GET /api/v1/capabilities, and the chip type as soc in
device state. Use those values in your own tools instead of copying this table.
The pin map¶
Each field holds a GPIO number – not a label printed on the board and not a Dx pin name.
-1 means "not connected".
The defaults are a generic DIY layout with the analog inputs on ADC1 and the usual ESP32-S3 I²C pins.
| Key | Type | Default | -1 allowed |
Meaning |
|---|---|---|---|---|
pinMatrix |
int | 21 |
no | LED matrix data line. Must be on the LED matrix list. |
pinBtnLeft |
int | 11 |
yes | Left button. Wire it to ground; pressed = LOW. AWTRIX turns on the internal pull-up. |
pinBtnSelect |
int | 12 |
yes | Select (middle) button, wired like the left one. Also wakes AWTRIX from deep sleep – see the wake row above. |
pinBtnRight |
int | 13 |
yes | Right button, wired like the left one. |
pinBattery |
int | 1 |
yes | Battery voltage divider tap. Must be ADC1. |
pinLdr |
int | 2 |
yes | Light sensor (LDR) tap. Must be ADC1. |
pinBuzzer |
int | 7 |
yes | Passive buzzer. |
pinI2cSda |
int | 8 |
yes | I²C data line for the temperature/humidity sensor. |
pinI2cScl |
int | 9 |
yes | I²C clock line. |
pinDfRx |
int | 17 |
yes | DFPlayer Mini serial RX. Used only while dfplayer is true, but always checked when set. |
pinDfTx |
int | 18 |
yes | DFPlayer Mini serial TX. Used only while dfplayer is true, but always checked when set. |
pinI2sBclk |
int | 5 |
yes | I²S bit clock to an external amplifier/DAC such as the MAX98357A. |
pinI2sLrclk |
int | 6 |
yes | I²S word-select (left/right) clock. |
pinI2sDout |
int | 4 |
yes | I²S data out. |
pinI2sMclk |
int | -1 |
yes | Master clock, for DACs that need one. |
pinAmpEnable |
int | -1 |
yes | Amplifier enable. Goes high at startup and stays high. |
The three I²S lines (pinI2sBclk, pinI2sLrclk, pinI2sDout) work as a set: set all three, or
set all three to -1. If you set only some of them, AWTRIX answers 422 validationFailed and
names the missing one:
{"error":{"code":"validationFailed","message":"set all three I2S pins or none","field":"pinI2sDout"}}
pinI2sMclk and pinAmpEnable are optional. You can set each one alone, but only when the three
I²S lines are set. Otherwise you get the same 422.
One more field belongs to the pin map:
| Key | Type | Default | Meaning |
|---|---|---|---|
dfplayer |
bool | false |
Switches the DFPlayer Mini on. With true and both pinDfRx and pinDfTx set, AWTRIX plays numbered tracks on the DFPlayer Mini. The passive buzzer on pinBuzzer keeps working either way. The DF pins are checked the same way whether this is on or off. |
What -1 does¶
| Field | What happens with -1 |
|---|---|
pinMatrix |
Rejected. You cannot switch off the matrix. |
pinBtnLeft / pinBtnSelect / pinBtnRight |
The button always reads as not pressed. |
pinBattery |
No battery support: batteryPercent, batteryVoltage, batteryPinMillivolts and lowBattery are left out of device state, the battery entities disappear from Home Assistant, and the Battery app leaves the rotation. |
pinLdr |
No light sensor: lightLevel and ldrRaw are left out of device state, and the Light level and Brightness mode entities disappear from Home Assistant. autoBrightness has no effect – the panel uses brightness – and the web UI hides the auto-brightness switch. |
pinBuzzer |
No buzzer. Melodies and RTTTL tunes make no sound. |
pinI2cSda / pinI2cScl |
No sensor. Temperature and humidity stay empty. |
pinDfRx / pinDfTx |
No DFPlayer. The buzzer is not affected. |
pinI2sBclk / pinI2sLrclk / pinI2sDout |
No I²S output. MP3s and internet radio are not available; their API answers 503 unavailable. |
pinI2sMclk |
No master clock. |
pinAmpEnable |
The amplifier is never switched on. Amplifiers with an enable input stay silent. |
Validation rules¶
AWTRIX checks every pin map before it stores anything. A rejected request changes nothing – not even the other fields in the same request.
The exact values for your chip are under gpio in
GET /api/v1/capabilities.
AWTRIX combines the fields you send with the stored map and checks the result in this order:
-
Each value on its own. Every
pin*value must be an integer:-1or a GPIO from0to48. If not, you get422 validationFailedwith the field name: -
The I²S set. A partial I²S set gets
422 validationFailed, as shown above. -
The rules 1 to 6 below. The first failure is returned as
400 invalidPinConfig:
Rule 1 is checked first. Rules 2 to 4 are then checked pin by pin in the order of the table
above, starting with pinMatrix. Rules 5 and 6 come last. So you always see only one problem at
a time; fix it and send again.
1. Matrix pin whitelist¶
pinMatrix must be on the LED matrix data list in the chip table.
Any other value, including -1, is rejected, and the message lists the allowed pins:
2. Valid GPIO range¶
Each pin that is not -1 must exist on the chip. GPIO 22–25 do not exist.
3. Reserved pins¶
The chip uses these pins itself. The message says what for:
| Range | Reserved for |
|---|---|
19–20 |
the USB-JTAG interface |
26–37 |
the SPI flash and PSRAM |
43–44 |
the UART0 console |
4. Input-only pins¶
The ESP32-S3 has no input-only pins, so this rule never applies.
5. ADC1 requirement¶
pinBattery and pinLdr must be ADC1 pins: GPIO 1–10. ADC2 pins
cannot be read while Wi-Fi is on.
6. No duplicates¶
Each pin that is not -1 may be used only once.
If one of the two fields is pinMatrix, the message also tells you how to fix it:
Both changes have to be in the same request, because either one on its own still collides.
Any number of fields can be -1 at the same time. A pin that is set is always checked, even when
the part behind it is switched off. For example, pinBuzzer: 17 collides with the default
pinDfRx, even with dfplayer: false. If you have no DFPlayer, set both DF pins to -1.
Reading and writing the map¶
The pin fields are normal keys of the system configuration.
Read the current map¶
Write a complete map¶
Send the whole map in one request. Then every rule is checked against the values you want, not a mix of new and stored values.
curl -X PUT http://<awtrix-ip>/api/v1/system \
-H "Content-Type: application/json" \
-d '{
"pinMatrix": 21,
"pinBtnLeft": 11,
"pinBtnSelect": 12,
"pinBtnRight": 13,
"pinBattery": -1,
"pinLdr": 2,
"pinBuzzer": 7,
"pinI2cSda": 8,
"pinI2cScl": 9,
"pinDfRx": -1,
"pinDfTx": -1,
"pinI2sBclk": 5,
"pinI2sLrclk": 6,
"pinI2sDout": 4,
"pinI2sMclk": -1,
"pinAmpEnable": -1,
"dfplayer": false
}'
On success AWTRIX answers 200 with the new configuration. Then restart it:
Always send Content-Type: application/json. Without it, curl -d marks the body as a form, and
AWTRIX refuses the PUT with 415 (Content-Type).
A rejected write¶
curl -X PUT http://<awtrix-ip>/api/v1/system \
-H "Content-Type: application/json" \
-d '{"pinBattery": 25}'
Nothing was stored. AWTRIX stays exactly as it was, including any other fields in the same request.
All messages a rejected pin map can return, and the other answers of this route, are listed in Errors – GPIO validation.
Recovery from a bad map¶
A pin map cannot make AWTRIX unusable.
AWTRIX checks the stored map again at every start. If it is not valid, for example a map saved on a board with a different chip, AWTRIX starts with the default pins. The web UI is reachable and you can fix the map. The stored map stays as it is, so AWTRIX keeps using the defaults until you save a valid map.
A map can be valid but wrong for your hardware. Then AWTRIX starts, but the panel or the buttons do nothing. If you cannot reach the web UI, a factory reset restores the default pins together with all other settings.
Panel layout¶
A panel you built yourself also needs a description: the width of one panel, how many panels the cable runs through, the corner where the data enters, whether the LED strip runs along rows or columns, and whether every second row runs back the other way.
Every panel is 8 pixels high and panelWidth × panels is 32–128. The default is 32×8.
All panel keys, their ranges and the values for common builds are in
Panel and orientation. They are set with PUT /api/v1/system.
A new total width needs a restart; wiring and orientation changes apply at once.
Wiring your own board¶
Work through this list in order:
- Read your chip's rules: run
curl http://<awtrix-ip>/api/v1/capabilitiesand look atgpio. - Pick the matrix pin first, from the LED matrix list. It limits your layout more than any other pin.
- Battery and LDR need ADC1 (
1–10). If you have only one ADC1 pin free, use it for the LDR: without it, auto-brightness is not available. Without a battery pin, only the battery display is missing. - Buttons need a pull-up. Wire each button between the pin and ground. AWTRIX turns on the internal pull-up; LOW means pressed. Any existing pin works. If the select button should wake AWTRIX from deep sleep, pick a pin from the wake row of the chip table.
- Avoid reserved pins:
26–37(flash and PSRAM),19–20(USB) and43–44(console). - Set parts you do not have to
-1instead of leaving a pin that looks right. - Send the whole map at once and restart.
- Calibrate the analog inputs. The pin map only says where to read. What the readings
mean is set separately:
batteryDividerRatiofor the battery divider, andldrFactor,ldrGammaandldrOnGroundfor the light sensor. The defaults are general values and may be wrong for your divider. See Brightness & sensors and Power & battery.
If your panel is mounted differently, you may also need rotate or mirror under
Panel and orientation, and swapButtons under
Buttons.
Sensor bus¶
AWTRIX finds I²C sensors by itself at startup. You only set pinI2cSda and pinI2cScl.
AWTRIX looks for these sensors in this order: BME280 (0x76, then 0x77), BMP280 (same
two addresses), HTU21DF (its fixed address), SHT31 (0x44). It uses the first one that
answers. If a BME280 and an SHT31 share the bus, the SHT31 is ignored.
| Sensor | Temperature | Humidity | Air pressure |
|---|---|---|---|
| BME280 | yes | yes | yes |
| BMP280 | yes | – | yes |
| HTU21DF | yes | yes | – |
| SHT31 | yes | yes | – |
Values the sensor does not measure are left out of device state. With no sensor, all of them
are left out, and tempOffset / humOffset have no effect.
The web UI's pin dropdowns¶
Each pin field in the web UI is a dropdown. It lists only the pins your chip can use for that part:
- analog pins for the battery and light sensor,
- output pins for the buttons, buzzer, I²C and the DFPlayer TX line,
- the LED matrix list for the matrix,
- not connected wherever
-1is allowed.
Pins the chip needs for flash, PSRAM, USB or its console are never offered. If the stored map contains a pin the list cannot offer – for example a map saved on a different board – that pin is shown as its own entry marked as the stored value, so it is not overwritten by accident.
The API checks the same rules, so a direct API call cannot get around them.
Related¶
- System configuration – Wi-Fi, MQTT, time, calibration and every other key of
PUT /api/v1/system. - DIY build – parts, power and wiring for your own clock.
- Errors – GPIO validation