Public Access
Split the README into docs/ pages, add web UI screenshots
- README is the overview; web UI, pages, Home Assistant, how it works, wiring, Nano over WiFi and the recovery app have their own page in docs/. - docs/web-ui.md has a screenshot of every tab, made with headless Edge by tools/make_webui_screenshots.py. - The web UI opens a page in the editor with #pages/<name>. Version 2.7.2. - The reply wire is fitted (flashing the Nano over WiFi works). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e991210ce6
commit
58a7cb03c2
23 files changed
+532
-273
No files matched your search
@@ -0,0 +1,49 @@
|
||||
# Home Assistant
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
The ESP32 connects to Home Assistant in two ways:
|
||||
|
||||
- **MQTT** (with discovery): the display's own switches, dropdowns, sensors and buttons.
|
||||
Broker, user and password are in the web UI, Home Assistant tab (an empty broker uses
|
||||
`secrets.h`). Changing them restarts the ESP32.
|
||||
- **REST API**: values of any entity for the dynamic pages and the trigger rules. Set the
|
||||
URL (empty: `http://<broker>:8123`, plain http only, use the IP address) and a
|
||||
long-lived access token (Home Assistant, your profile, Security tab) in the same tab.
|
||||
|
||||
## Entities
|
||||
|
||||
- `FlipDot Display Power` switch: off clears the display and cuts power through the relay.
|
||||
- `FlipDot Display Mode` dropdown: what is shown during the day, a dynamic page ("Clock"
|
||||
first) or one of the bitmaps (the same choice as in the web UI).
|
||||
- `FlipDot Display Off image` dropdown: what is left on the dots when the display
|
||||
switches off (at night or with the Power switch): blank or one of the bitmaps.
|
||||
- `FlipDot Display IP` sensor: the ESP32's IP address.
|
||||
- `FlipDot Display Stopwatch` switch and `FlipDot Display Stopwatch reset` button: the
|
||||
stopwatch of the dynamic pages.
|
||||
- `FlipDot Display Shown page` sensor: what the display shows now (schedule, rule or
|
||||
chosen page). `FlipDot Display Back to schedule` button: ends a page chosen by hand.
|
||||
- `FlipDot Display Firmware` sensor: version and health, e.g. `2.7.2, ok, 0 crashes, recovery ok`.
|
||||
- `FlipDot Display Recovery` button: restarts into the [recovery app](recovery.md) for 10
|
||||
minutes.
|
||||
|
||||
The unique ids of these entities never change, so automations and dashboards keep working
|
||||
after firmware updates.
|
||||
|
||||
## Values on dynamic pages
|
||||
|
||||
A Home Assistant value element on a [page](pages.md) shows an entity's state or one of
|
||||
its attributes, as a number (multiplier, decimals, auto unit, without minus sign), a
|
||||
duration, a time from a timestamp, or with own texts per state (see the
|
||||
[web UI](web-ui.md#pages)).
|
||||
|
||||
**Home Assistant values** on the shown page are read every minute (one entity per loop,
|
||||
at most about 3 s). An unknown or unreachable value shows `--`. The display only changes
|
||||
when the shown text changes, so round values with the decimals setting.
|
||||
|
||||
## Rules
|
||||
|
||||
The [schedule](web-ui.md#schedule) can bring a page to the front from a Home Assistant
|
||||
state, e.g. the 3D printer page while `sensor.mainsailos_current_print_state` is
|
||||
`printing`. Each rule is read at its own interval; when Home Assistant can't be reached it
|
||||
is read once a minute at most, so the display keeps running.
|
||||
@@ -0,0 +1,26 @@
|
||||
# How it works
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
1. **ESP32 boot.** The ESP32 connects to WiFi and starts ArduinoOTA. After a crash it
|
||||
runs only OTA for 15 seconds, and after repeated crashes it goes to safe mode. Bad OTA
|
||||
firmware is rolled back automatically, see
|
||||
[Keeping OTA alive](../src/esp32/README.md#keeping-ota-alive). It then switches the display relay on, waits for
|
||||
the Nano's `READY` message (at most 3 s), shows "Connecting...", starts NTP and
|
||||
connects to MQTT.
|
||||
2. **Clock.** Once the time is synced, the ESP32 sends a new frame whenever the screen
|
||||
changes, mostly once a minute:
|
||||
- **08:00–22:00:** the page: the chosen page (by default the built-in Clock page,
|
||||
Pikachu with a thought bubble showing `HH:MM`, yawning from 17:00), or what the
|
||||
[schedule and rules](web-ui.md#schedule) pick.
|
||||
- **22:00–23:30:** the clock is only shown during the minutes :00, :15, :30 and :45.
|
||||
30 seconds after each of those minutes, the relay turns the display off.
|
||||
- **23:31–08:00:** the screen is cleared (or shows the off image chosen in the web UI)
|
||||
and power is cut after 30 seconds.
|
||||
3. **Home Assistant**: switches, dropdowns and sensors over MQTT, see
|
||||
[Home Assistant](home-assistant.md).
|
||||
4. **Nano.** The Nano reads commands from the serial line and updates its frame buffer.
|
||||
On `D` it flips only the dots that changed. Every 2 minutes it redraws the whole screen
|
||||
to fix dots that did not flip.
|
||||
|
||||
The serial protocol is described in [src/nano/README.md](../src/nano/README.md).
|
||||
@@ -0,0 +1,74 @@
|
||||
# Flashing the Nano over WiFi
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
The ESP32 can flash the Nano for you, so you don't need a USB cable at the display:
|
||||
|
||||
```sh
|
||||
pio run -e nano_ota -t upload
|
||||
```
|
||||
|
||||
What happens:
|
||||
|
||||
1. The uploader ([tools/nano_net_upload.py](../tools/nano_net_upload.py)) connects to the
|
||||
ESP32 on TCP port **2323** and tells it the bootloader's baud rate.
|
||||
2. The ESP32 pauses the clock and switches the relay off for 1 second, then on again. If
|
||||
the reset wire is fitted, it also pulses the Nano's RESET pin. This restarts the Nano,
|
||||
and its bootloader starts.
|
||||
3. The ESP32 switches its link to the bootloader's baud rate and passes bytes between
|
||||
WiFi and the Nano.
|
||||
4. The uploader writes the new firmware and reads it back to verify it.
|
||||
5. When the uploader disconnects, the ESP32 switches the relay off. The clock powers the
|
||||
Nano on again with the new firmware and continues.
|
||||
|
||||
The display goes dark for the duration of the upload, which takes a few seconds.
|
||||
|
||||
**Requirements**
|
||||
|
||||
- The reply wire must be connected: Nano D1 (TX) → voltage divider → ESP32 GPIO 16. The
|
||||
uploader needs the bootloader's answers.
|
||||
- The ESP32 must be running firmware with the flash bridge (this version or newer).
|
||||
|
||||
## Bootloaders
|
||||
|
||||
Flashing the Nano's firmware never changes its bootloader. Both common Nano bootloaders
|
||||
are supported:
|
||||
|
||||
| Bootloader | Baud | Starts after a power cycle | Reset wire |
|
||||
|------------|------|----------------------------|------------|
|
||||
| Old bootloader (ATmegaBOOT), the one this Nano has now | 57600 | Yes | Not needed |
|
||||
| Optiboot ("new bootloader") | 115200 | No, only after a reset-pin reset | **Required** |
|
||||
|
||||
By default (`--baud=auto` in `upload_flags`) the uploader tries 57600 first and then
|
||||
115200. Set `--baud=57600` or `--baud=115200` to skip the other attempt.
|
||||
|
||||
If you ever replace the bootloader with Optiboot:
|
||||
|
||||
- Fit the reset wire: ESP32 GPIO → 1 kΩ → base of an NPN transistor (e.g. 2N2222 or
|
||||
BC547). Connect the collector to Nano RESET and the emitter to GND. Set `NANO_RESET_PIN` in
|
||||
`src/esp32/config.h` to that GPIO, for example `4`. The transistor keeps the Nano's 5 V
|
||||
reset line away from the 3.3 V ESP32 pin.
|
||||
- For uploads over USB, change `board` in the `nano` environment to `nanoatmega328new`.
|
||||
|
||||
**Settings**
|
||||
|
||||
- ESP32 IP address: `upload_port` in the `nano_ota` environment in `platformio.ini`. You
|
||||
can add a port number: `10.1.1.198:2323`. For a one-time different address, use
|
||||
`pio run -e nano_ota -t upload --upload-port 10.1.1.50`.
|
||||
- Port, reset pin and timings: `NANO_FLASH_PORT`, `NANO_RESET_PIN`, `NANO_POWER_OFF_TIME`
|
||||
and `NANO_RESET_DELAY` in `src/esp32/config.h`.
|
||||
|
||||
**If it fails**
|
||||
|
||||
| Message | Cause |
|
||||
|---------|-------|
|
||||
| `can't connect to ...` | Wrong IP address, or the ESP32 is offline |
|
||||
| `the ESP32 flash bridge didn't accept the request` | The ESP32 runs older firmware without the flash bridge. Flash it first (`esp32` or `esp32_usb`). |
|
||||
| `no answer from the Nano bootloader` | Reply wire missing, or the Nano has Optiboot and the reset wire is missing |
|
||||
| `bad reply to command ...` / `verify failed` | Connection problem. Try again. A failed upload can leave the Nano without working firmware, but the bootloader itself is never overwritten, so flashing again (over WiFi or USB) always fixes it. |
|
||||
|
||||
The ESP32 ends the session after 10 seconds without data, so the clock always resumes,
|
||||
even if the uploader is interrupted.
|
||||
|
||||
> **Security:** anyone on your network can connect to port 2323 and flash the Nano, just
|
||||
> like the ESP32's own OTA has no password. Keep the display on a trusted network.
|
||||
@@ -0,0 +1,22 @@
|
||||
# Pages
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
The dynamic pages on the display, drawn by the ESP32 itself (`python
|
||||
tools/make_page_screenshots.py` makes these pictures again). The Clock page is built in;
|
||||
the others are the example pages in [examples/home-pages.json](../examples/home-pages.json)
|
||||
(import them in the web UI, System tab; they use this home's Home Assistant entities).
|
||||
|
||||
| Page | |
|
||||
|---|---|
|
||||
| **Clock** (built in), day and from 17:00 |   |
|
||||
| **Weer**: clock, weather icon by state, humidity, temperature |  |
|
||||
| **Dag en nacht**: sun from 07:00, moon and stars from 19:00, date |   |
|
||||
| **Energie**: net power now, solar today, clock |  |
|
||||
| **3D printer**: progress, time left, status message (scrolls), nozzle temperature |  |
|
||||
| **Binnen**: room and outside temperature, CO2, air quality |  |
|
||||
| **Verbruik**: water and energy used today, solar today |  |
|
||||
| **Grote klok**: big clock, inverted from 22:00 |   |
|
||||
| **Datum**: weekday and date |  |
|
||||
| **Aftellen**: countdown to Christmas |  |
|
||||
| **Stopwatch** |  |
|
||||
@@ -0,0 +1,82 @@
|
||||
# Recovery app
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
The ESP32 has a small second app, the **recovery app** ([src/recovery](../src/recovery)). It
|
||||
has its own place on the flash and is never changed by OTA updates. It contains only WiFi,
|
||||
a setup web page and OTA, so the ESP32 can always be reached, even when the clock firmware
|
||||
is broken. It shows what is going on as a short message on the display, and the built-in
|
||||
LED double-blinks.
|
||||
|
||||
```
|
||||
flash (4 MB): bootloader | nvs | otadata | factory: recovery | ota_0: clock | ota_1: clock | bitmaps
|
||||
```
|
||||
|
||||
**When it starts**
|
||||
|
||||
| Trigger | Back to the clock |
|
||||
|---------|-------------------|
|
||||
| The clock firmware can't connect to WiFi (30 s at boot, or 10 min later on) | As soon as the WiFi works again (after at least 1 minute) |
|
||||
| The clock firmware crashed 3 times in a row | After 10 minutes |
|
||||
| **FlipDot Display Recovery** button in Home Assistant | After 10 minutes |
|
||||
| Switching the power on and off 3 times, each time within 10 s | After 10 minutes |
|
||||
| There is no working clock firmware (the bootloader falls back to it) | Never by itself |
|
||||
|
||||
The setup page can also start the clock right away, or keep the recovery app running
|
||||
("Stay in recovery").
|
||||
|
||||
**What the display shows**
|
||||
|
||||
| Message | Meaning |
|
||||
|---------|---------|
|
||||
| `Connecting` / `to WiFi...` | The clock firmware is starting |
|
||||
| `Getting` / `the time...` | Connected, waiting for the time from the internet (NTP) |
|
||||
| `No WiFi` / `12:34` | The clock firmware lost WiFi for more than a minute; the time keeps running |
|
||||
| `Crashed!` / `OTA open 15s` | The clock firmware crashed and waits 15 s for an OTA update before it starts again |
|
||||
| `Crashed 3x` / `OTA only` | Repeated crashes without the recovery app installed: only OTA for 10 minutes |
|
||||
| `Recovery` / reason | The recovery app started (`no WiFi`, `crashed`, `requested`, `power cycled`, `no firmware`) |
|
||||
| `WiFi setup:` / `FlipDot-Setup` | Connect to this WiFi to choose another network, see below |
|
||||
| `Recovery` / IP address | The recovery app is connected; open this address for the setup page |
|
||||
|
||||
The recovery app switches the display off after each message; the dots keep showing it
|
||||
without power.
|
||||
|
||||
**WiFi setup.** The recovery app keeps trying the stored WiFi. If that fails for 20 s, it
|
||||
also opens its own WiFi: **FlipDot-Setup**, with the password `SETUP_AP_PASSWORD` from
|
||||
`src/esp32/secrets.h`. Connect a phone to it; the setup page opens by itself (otherwise go
|
||||
to <http://192.168.4.1>). Pick a network, enter the password and save. The new WiFi is
|
||||
stored in the ESP32's NVS and used by both apps from then on (instead of `WIFI_SSID` from
|
||||
`secrets.h`). The setup page is also reachable on your normal WiFi, at the ESP32's IP
|
||||
address.
|
||||
|
||||
**Updating the clock firmware from the recovery app.** Upload as usual,
|
||||
`pio run -e esp32 -t upload`. On the setup WiFi, add `--upload-port 192.168.4.1`. The
|
||||
recovery app writes the firmware to `ota_0` and starts it.
|
||||
|
||||
**Updating the recovery app itself** needs USB (`pio run -e recovery -t upload`). It
|
||||
should rarely be necessary.
|
||||
|
||||
## One-time installation (USB)
|
||||
|
||||
This changes the flash layout, so it can only be done over USB. It is the last USB flash
|
||||
the ESP32 needs. Best done together with the wiring TODOs, while the display is open
|
||||
anyway.
|
||||
|
||||
1. Connect the ESP32 over USB (`COM11`, see `platformio.ini`).
|
||||
2. Erase the flash and install the recovery app:
|
||||
```sh
|
||||
pio run -e recovery -t erase
|
||||
pio run -e recovery -t upload
|
||||
```
|
||||
The recovery app starts (LED double-blinks, the display shows `Recovery` /
|
||||
`no firmware`), connects to the WiFi from `secrets.h` and shows its IP address. It waits
|
||||
there, because there is no clock firmware yet.
|
||||
3. Install the clock firmware **over WiFi**. This also tests that the recovery app works:
|
||||
```sh
|
||||
pio run -e esp32 -t upload
|
||||
```
|
||||
4. Check Home Assistant: the **FlipDot Display Firmware** sensor should show
|
||||
`2.1.0, testing, ...`, and after a minute `2.1.0, ok, 0 crashes, recovery ok`.
|
||||
|
||||
Erasing the flash also clears the NVS: WiFi settings made on the setup page are reset to
|
||||
the ones in `secrets.h`.
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
# Web UI
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
Open `http://<ESP32 IP>/` (e.g. <http://10.1.1.198/>) in a browser. At the top is a live
|
||||
view of the display: it shows what the dots show right now, updated every 1.5 s. Each tab
|
||||
has its own address, e.g. `http://10.1.1.198/#schedule`; `#pages/<page name>` opens a page
|
||||
in the editor.
|
||||
|
||||
The screenshots are made with `python tools/make_webui_screenshots.py` (Edge or Chrome in
|
||||
headless mode).
|
||||
|
||||
## Show
|
||||
|
||||
Choose what the display shows during the day: a dynamic page (e.g. the built-in Clock:
|
||||
Pikachu with the time) or any bitmap, and what is left on the dots when the display
|
||||
switches off. With a [schedule](#schedule), a page chosen here (or with the Home Assistant
|
||||
"Mode" dropdown) is shown until the next block starts; "Back to schedule" ends it earlier.
|
||||
Bitmaps that pages or rules use are in their own subsection.
|
||||
|
||||

|
||||
|
||||
## Pages
|
||||
|
||||
Dynamic pages: a background bitmap per time of day (e.g. Pikachu from 00:00, yawning
|
||||
Pikachu from 17:00) with up to 8 elements on top. The preview is drawn by the ESP32, so it
|
||||
is exactly what the display shows; drag the elements on it, or select one and use the
|
||||
arrow keys. Built-in pages can be copied, not changed. See the [pictures of the pages](pages.md).
|
||||
|
||||
| Element | Options |
|
||||
|---------|---------|
|
||||
| Clock | `08:05`, `8:05`, optionally with seconds |
|
||||
| Date | 7 formats, Dutch or English (`do 15 okt`, `15 oktober`, `donderdag`, ...) |
|
||||
| Home Assistant value | An entity or attribute, text before and after. Shown as a number (multiplier, decimals, auto unit `1257 W` → `1.3 kW`, without minus sign), a duration (`2h 21m`, `2:21:00`, `2:21`, `2 uur`), a time from an HA timestamp (`23:07`, `do 23:07`, `15-10 23:07`, `over 34 min`) or with own texts per state (`printing=Bezig, standby=Klaar`) |
|
||||
| Text | Up to 32 characters |
|
||||
| Countdown | To a date and time: smart (`12 dagen`, `5 uur`, `34 min`, `0:45`), days, `12d 04:05`, with seconds; a text when it has passed |
|
||||
| Stopwatch | `4:05`, `0:04`, `0:04:05`; started in the [Stopwatch](#stopwatch) tab or Home Assistant |
|
||||
| Image | A bitmap at a position, fixed or chosen by a Home Assistant state (`weer ` + `cloudy` = the bitmap `weer cloudy`) |
|
||||
|
||||
Every element has a position, alignment, font and overlay (on top, box that clears the
|
||||
dots behind it, inverted). Texts can scroll in a window at a chosen speed and can contain
|
||||
the degree sign. Fonts: Small (3x5), Normal (5x7), Large (5x7 doubled), Full height (19 dot
|
||||
digits), and your own: upload a TTF/OTF, choose the height in dots, the browser converts
|
||||
it.
|
||||
|
||||

|
||||
|
||||
## Schedule
|
||||
|
||||
What is shown when, every day: blocks from a start time until the next block, each with up
|
||||
to 6 pages or bitmaps that rotate (each for its own time, 5 s to 1 hour), switching with
|
||||
the refresh effect or instantly.
|
||||
|
||||
Rules bring a page to the front from a Home Assistant state (an entity or attribute, `=`,
|
||||
`is not`, `above`, `below`): they show it while the state is true, add it to the rotation
|
||||
while it is true, or show it for a while when it becomes true. Each rule is read at its own
|
||||
interval (2-600 s) and can be limited to some of the blocks. The first rule that shows a
|
||||
page wins. The display schedule ([Settings](#settings)) still decides when the display is on.
|
||||
|
||||

|
||||
|
||||
## Stopwatch
|
||||
|
||||
Start, stop and reset the stopwatch that pages can show (also in Home Assistant).
|
||||
|
||||

|
||||
|
||||
## Bitmaps
|
||||
|
||||
All bitmaps with a preview: show one as the page, send it live, open it in the editor,
|
||||
export or delete it. The built-in ones (Nintendo, Pikachu, Pikachu yawning) can't be
|
||||
deleted, nor can a bitmap that a page, the schedule or a rule uses.
|
||||
|
||||

|
||||
|
||||
## Draw / Live
|
||||
|
||||
Draw on an 84 x 19 canvas (click or drag), or load an image: it is converted in the
|
||||
browser (threshold, invert, dithering, fit / fill / stretch). "Show on display now" sends
|
||||
it to the display right away (*live mode*); with "live while drawing" every change appears
|
||||
on the dots. Save it as a bitmap with a name.
|
||||
|
||||

|
||||
|
||||
## Settings
|
||||
|
||||
The display schedule (on/off, when the display is on, the late-evening quarter-hour mode,
|
||||
when it switches off); the "No WiFi" screen; the flip time, the pause after each pulse,
|
||||
pulses per dot and the refresh effect (instant, sparkle, wipe, rain, from the middle out,
|
||||
diagonal, spiral, flip all then reveal, push, random; optionally only for small changes
|
||||
like the clock's minutes); test patterns; the status LED.
|
||||
|
||||

|
||||
|
||||
## Home Assistant
|
||||
|
||||
MQTT: on/off, broker, port, user, password (changing these restarts the ESP32; an empty
|
||||
broker uses `secrets.h`). The URL and long-lived access token for the values on dynamic
|
||||
pages and the rules (REST API, plain http; empty URL: `http://<broker>:8123`). See
|
||||
[Home Assistant](home-assistant.md).
|
||||
|
||||

|
||||
|
||||
## System
|
||||
|
||||
Firmware status, uptime, WiFi, storage; restart; start the [recovery app](recovery.md).
|
||||
|
||||
**Backup:** export everything (settings, the schedule and rules, your bitmaps, pages and
|
||||
fonts; the MQTT password and HA token only when ticked) to one JSON file, and import a
|
||||
file: choose which items and which parts of the settings. Imported items are added (a
|
||||
taken name gets a number) unless you give them an existing name. Pages and bitmaps also
|
||||
have their own Export button, e.g. to share one page (with the bitmaps and fonts it uses).
|
||||
|
||||

|
||||
|
||||
## Good to know
|
||||
|
||||
**Live mode** shows the image at once, also at night or before the time is known. It
|
||||
ends with "Back to normal", or 30 minutes after the last image. The Home Assistant Power
|
||||
switch still wins: when it is off, nothing is shown.
|
||||
|
||||
**Bitmaps, pages and fonts** are stored on the ESP32 (LittleFS, 320 KB, room for well over
|
||||
a thousand bitmaps, 50 pages, 20 fonts). Pages and bitmaps share their names. They need
|
||||
the new flash layout from the [one-time installation](recovery.md#one-time-installation-usb);
|
||||
before that, only the built-in ones are available.
|
||||
|
||||
**Moving pages** (scrolling text, seconds, a running stopwatch) send a frame whenever the
|
||||
content moves, without the refresh effect (`D1`, needs the Nano firmware from version
|
||||
2.5.0), and only the dots that changed, not faster than the Nano can flip them: with 3
|
||||
pulses and a 300 µs gap a 1-dot scroll step of a line of text takes about 0.1-0.2 s.
|
||||
|
||||
**Settings** are stored in the ESP32's NVS. The defaults are the original behaviour. There
|
||||
is no login: anyone on your WiFi can use the page, as with OTA.
|
||||
@@ -0,0 +1,14 @@
|
||||
# Wiring between ESP32 and Nano
|
||||
|
||||
[← Back to the README](../README.md)
|
||||
|
||||
The ESP32 talks to the Nano on **UART2** (`Serial2`, 115200 baud). The ESP32's own
|
||||
`Serial` (UART0, USB) is free for debug output.
|
||||
|
||||
| ESP32 | Nano | Notes |
|
||||
|-------|------|-------|
|
||||
| GPIO 17 (TX2) | D0 (RX) | Display commands |
|
||||
| GPIO 16 (RX2) | D1 (TX) | `READY` and `ERR` replies, and bootloader answers when flashing over WiFi. Use a 5 V → 3.3 V voltage divider (e.g. 1 kΩ / 2 kΩ), because the ESP32 is not 5 V tolerant. Without it, the ESP waits a fixed 3 s after power-on, and `nano_ota` doesn't work. |
|
||||
| GPIO set in `NANO_RESET_PIN` | RESET (through an NPN transistor) | Optional, only needed to flash a Nano with Optiboot over WiFi. See [Bootloaders](nano-ota.md#bootloaders). |
|
||||
| GND | GND | Common ground |
|
||||
| GPIO 13 | Relay input | Relay powers the panels and the Nano (`LOW` = on) |
|
||||
Reference in new issue
Block a user