Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ESPHome configuration templates and projects.
- Version 2.4:
- Added M5Stack CoreS3 support, the first touchscreen template, with an LVGL example device.
- Added an M5Stack CoreS3 camera overlay for the GC0308 camera.
- Added M5Stack Dial support, with an LVGL example device that changes pages with the encoder.
- Version 2.3:
- Changed OTA to encryption only, using the API key, so only the upgrade bridge build still needs the `ota_password` secret.
- Fixed the compile warnings the templates and the EasyStart component can address.
Expand Down
13 changes: 13 additions & 0 deletions OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,18 @@ Do not `!remove` blocks from Apollo's package without checking what depends on t
- **The FT6336 drops quick taps once it falls into its monitor scan.** It slows its scan a few seconds after the last touch. Waking a dark screen then took several taps, and the touchscreen component logged one of three. ESPHome's `ft63x6` driver sets the active scan rate in register 0x88 and leaves the power mode in register 0x86 at its default. The board template writes 0 to register 0x86 through the touchscreen's own I2C device. It does so in an `on_boot` at priority -100, since the driver's setup resets the controller. A single tap then wakes the screen.
- **The example device swipes between tabs only from the screen edge.** LVGL's tabview swipe is a plain scroll that also starts on the slider and the drawing pad, so the example turns it off. A drag starting within 24 pixels of the left or right edge and moving 60 pixels inward changes tab instead.

### M5Stack Dial

[`templates/m5stack-dial.yaml`][m5stack-dial-template] is a board template, with every part on the StampS3 GPIO pins rather than behind an expander or a PMU.

- **GPIO46 is the power hold, an output.** On battery the front button or the RTC alarm powers the board on, and it stays on only while the firmware drives GPIO46 high. ESPHome's device page configures it as a "Hold Button" input instead, so a board on battery turns off as soon as the button is released. On USB or the DC input the pin does nothing, so the bench unit could not test it.
- **A restart releases the power hold until the switch sets up again.** The reset returns GPIO46 to an input with its pulldown. Whether a board on battery stays powered through an OTA reboot or a restart is untested.
- **GPIO8 resets both the LCD and the WS1850S RFID reader.** The display owns the pin, so the reader takes no `reset_pin`. On the bench the reader set up without an error after the display's reset pulse.
- **The FT3267 touch controller runs on the `ft5x06` driver.** The boot I2C scan lists only 0x28 and 0x51, but the driver then reads vendor ID 0x11 at 0x38, and touch works. Do not chase the missing address.
- **The touch mapping matches the display with no calibration.** Taps on rings 30 pixels in from each edge and at the center landed on them. The reported coordinates sit one pixel below the raw ones.
- **`resolution: 1` gives one encoder step per detent.** M5Stack's example uses 4, which counts four steps per click.
- **The RTC is a BM8563, which the docs also call an RTC8563 and M5Stack's example drives as a PCF8563.** The template uses the `bm8563` platform, as the CoreS3 template does.

### RGB LED Status

[`templates/rgb-led-status.yaml`][rgb-led-status-template] derives from [Flo-R1der's package][flo-r1der-link] but does not import it, because that version binds its state to `wifi:` `on_connect` triggers and so cannot run on an Ethernet board.
Expand Down Expand Up @@ -693,6 +705,7 @@ Sharp edges in the tooling around this repository, each one learned by tripping
[heltec-template]: ./templates/heltec-wifi-lora32-v4-r8.yaml
[m5stack-cores3-camera-template]: ./templates/m5stack-cores3-camera.yaml
[m5stack-cores3-template]: ./templates/m5stack-cores3.yaml
[m5stack-dial-template]: ./templates/m5stack-dial.yaml
[max17048-template]: ./templates/max17048.yaml
[min-version-template]: ./templates/min-version.yaml
[office-bluetooth-proxy]: ./office-bluetooth-proxy.yaml
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ ESPHome configuration templates and projects.

- Added M5Stack CoreS3 support, the first touchscreen template, with an LVGL example device.
- Added an M5Stack CoreS3 camera overlay for the GC0308 camera.
- Added M5Stack Dial support, with an LVGL example device that changes pages with the encoder.

See [HISTORY.md][history] for the release notes.

Expand Down Expand Up @@ -252,6 +253,23 @@ Shared building-block includes, composed via `packages:` by the device templates
- `camera_clock_pin`: `GPIO10` by default, a spare M5-Bus pin for the clock ESPHome requires although the camera clocks itself. Move it where a module uses `GPIO10`.
- Note: the camera's pixel clock is GPIO45, the pin the ESP32-S3 reads at reset to pick its flash voltage. A reset taken while the camera streams can leave the board looping in its bootloader until it is power cycled. The overlay holds the camera in reset on every orderly shutdown, which covers OTA, restart, and API reboots. Turn the `Camera Enable` switch off before flashing over USB, since the reset after a USB flash is not an orderly one. Turning it back on resets the sensor's registers, so restart afterwards.

#### M5Stack Dial

- [Template][m5stack-dial] for the [M5Stack Dial][m5stack-shop-dial-link], an ESP32-S3 StampS3 with 8MB Quad Flash and no PSRAM. It carries a 240x240 round capacitive touch display, a rotary encoder, an RFID reader, and a passive buzzer. The [Dial docs][m5stack-docs-dial-link] carry the pin map the template header repeats.
- Includes the display, the touchscreen, and the backlight as a dimmable light.
- Includes the encoder as a step count, the front button that pressing the screen down operates, and the StampS3 BOOT button.
- Includes the RTC written through from Home Assistant time.
- Includes the RFID reader, which fires a Home Assistant tag event on each scan and holds the UID as text while the tag is present.
- Includes the buzzer as a PWM output, and the StampS3 RGB LED through the [RGB LED status][rgb-led-status] package.
- Includes a Power Off button, which releases the power hold pin and turns off a board running on its battery.
- Optional substitutions:
- `backlight_brightness`: `80%` by default, the level the backlight turns on at during boot.
- `rgb_led_pin`: `GPIO21` by default, the StampS3 RGB LED.
- Flash over USB-C. The port is the chip's native USB with no UART bridge, so no adapter is needed.
- The display carries no lambda, so a device draws on it with [LVGL][esphome-components-lvgl-link], as the [example device][test-m5stack-dial] does. There the encoder changes page and the front button plays a beep.
- Note: a V1 unit confirms USB and OTA flashing, the display, touch, the backlight, the encoder, the front button, the buzzer, and the RTC write. Reading an RFID tag, the power hold on battery, the BOOT button, and the RGB LED are untested. So is a V1.1 on the StampS3A.
- Note: the battery level is not sensed, because no ADC pin reaches the battery.

## Devices

My deployed devices use the templates and helpers, and are documented in [DEVICES.md][devices].
Expand Down Expand Up @@ -350,6 +368,7 @@ Licensed under the MIT License. See [LICENSE][license] for details.
[logger]: ./templates/logger.yaml
[m5stack-cores3]: ./templates/m5stack-cores3.yaml
[m5stack-cores3-camera]: ./templates/m5stack-cores3-camera.yaml
[m5stack-dial]: ./templates/m5stack-dial.yaml
[max17048]: ./templates/max17048.yaml
[min-version]: ./templates/min-version.yaml
[norvi-enet-ae06-r]: ./templates/norvi-enet-ae06-r.yaml
Expand All @@ -368,6 +387,7 @@ Licensed under the MIT License. See [LICENSE][license] for details.
[test]: ./test/
[test-heltec-wifi-lora32-v4-r8]: ./test/heltec-wifi-lora32-v4-r8.yaml
[test-m5stack-cores3]: ./test/m5stack-cores3.yaml
[test-m5stack-dial]: ./test/m5stack-dial.yaml
[test-norvi-enet-ae06-r]: ./test/norvi-enet-ae06-r.yaml
[time]: ./templates/time.yaml
[unexpectedmaker-pros3d]: ./templates/unexpectedmaker-pros3d.yaml
Expand Down Expand Up @@ -424,7 +444,9 @@ Licensed under the MIT License. See [LICENSE][license] for details.
[kincony-esp32-sd-card-rtc-ds3231-link]: https://www.kincony.com/esp32-sd-card-rtc-ds3231.html
[konnected-products-smart-garage-door-opener-blaq-myq-alternative-link]: https://konnected.io/products/smart-garage-door-opener-blaq-myq-alternative
[m5stack-docs-cores3-link]: https://docs.m5stack.com/en/core/CoreS3
[m5stack-docs-dial-link]: https://docs.m5stack.com/en/core/Dial
[m5stack-shop-cores3-link]: https://shop.m5stack.com/products/m5stack-cores3-esp32s3-iotdevelopment-kit
[m5stack-shop-dial-link]: https://shop.m5stack.com/products/m5stack-dial-v1-1
[markdown-all-in-one-link]: https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one
[markdownlint-cli2-link]: https://github.com/DavidAnson/markdownlint-cli2
[microair-link]: https://www.microair.net
Expand Down
1 change: 1 addition & 0 deletions cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@
"reflash",
"regen",
"resharper",
"RFID",
"rhysd",
"Rigol",
"RMII",
Expand Down
270 changes: 270 additions & 0 deletions templates/m5stack-dial.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
# External usage:
# packages:
# device: github://ptr727/ESPHome-Config/templates/m5stack-dial.yaml@main
# - Secrets resolve from a secrets.yaml beside the including config, see secrets._yaml for the names.

# Required substitutions:
# - device_name - the ESPHome node name.
# - device_friendly_name - the Home Assistant display name.

# Optional substitutions:
# - backlight_brightness - 80%, the level the backlight turns on at during boot.
# - rgb_led_pin - GPIO21, the StampS3 module's RGB LED, used by rgb-led-status.yaml.

# M5Stack Dial ESP32-S3 Smart Rotary Knob, V1 on a StampS3, V1.1 on a StampS3A
# ESP32-S3FN8, 8MB Quad Flash, no PSRAM
# https://shop.m5stack.com/products/m5stack-dial-v1-1
# https://docs.m5stack.com/en/core/Dial

# Board tested: 2026-10-24, a V1 on a StampS3, USB and OTA flash, display, touch, backlight, encoder, front button, buzzer, RTC write.
# - The RFID reader answers on the bus, and reading a tag is untested.
# - The power hold and the Power Off button are untested, since the bench unit ran on USB.
# - The BOOT button and the RGB LED sit inside the case, so neither is tested.
# - A V1.1 on a StampS3A is untested.

# The display, the touchscreen and the backlight are configured here.
# - The display carries no lambda, so a device draws with LVGL, see test/m5stack-dial.yaml.
# - A lambda device extends board_display with lambda, update_interval and auto_clear_enabled true instead.

# GPIO19, GPIO20 : native USB, so flashing needs no adapter and no UART bridge
# GPIO0 : BOOT button on the StampS3, held at power on for download mode
# GPIO3 : passive buzzer
# GPIO21 : RGB LED on the StampS3, inside the case
# GPIO40, GPIO41 : rotary encoder A and B
# GPIO42 : front button, pressing the whole screen down
# GPIO46 : power hold, see below

# Power hold
# - On battery the front button or the RTC alarm powers the board on, and the board stays on only while GPIO46 is high.
# - The Power Off button drives it low, which turns a board on battery off and does nothing on USB or the DC input.
# - A restart releases the pin until the switch sets up again, and whether a board on battery survives that is untested.
# - Battery level is not sensed, since no ADC pin reaches the battery.

# Internal I2C bus
# GPIO11 : sda
# GPIO12 : scl
# - 0x38 FT3267 touch, 0x51 BM8563 RTC, 0x28 WS1850S RFID reader.

# SPI bus, the LCD only
# GPIO6 : sclk
# GPIO5 : mosi
# GPIO4 : LCD dc
# GPIO7 : LCD cs
# GPIO8 : LCD reset, also wired to the WS1850S reset
# GPIO9 : LCD backlight, PWM

# GC9A01 240x240 round IPS, with an FT3267 touch panel
# GPIO14 : touch interrupt
# GPIO10 : WS1850S interrupt, unclaimed here

# Grove ports, left to the device
# Port A : GPIO13 sda, GPIO15 scl
# Port B : GPIO1, GPIO2

# https://esphome.io/components/substitutions
substitutions:
backlight_brightness: 80%
# Used by rgb-led-status.yaml
rgb_led_pin: GPIO21

# https://esphome.io/components/esphome
esphome:
name: ${device_name}
friendly_name: ${device_friendly_name}

# https://esphome.io/components/esphome#esphome-on_boot
on_boot:
- priority: 600
then:
- light.turn_on:
id: lcd_backlight
brightness: ${backlight_brightness}

# The RTC sets the clock once at boot, so it never overwrites a network time source later.
- bm8563.read_time: rtc_time
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# https://esphome.io/components/esp32
esp32:
# PlatformIO carries no Dial or StampS3 board definition, and the DevKitC-1 profile matches the module.
# https://docs.platformio.org/en/latest/boards/espressif32/esp32-s3-devkitc-1.html
board: esp32-s3-devkitc-1

# The board definition does not set this, ESPHome defaults to 4MB.
flash_size: 8MB

# https://esphome.io/components/esp32#esp-idf-framework
framework:
type: esp-idf

# https://esphome.io/components/packages#local-packages
packages:
common: !include common.yaml
temperature: !include temperature.yaml
rgbledstatus: !include rgb-led-status.yaml

# USB is the chip's own port, with no UART bridge on this board.
# https://esphome.io/components/logger
logger:
hardware_uart: USB_SERIAL_JTAG # UART0, UART1, USB_CDC, USB_SERIAL_JTAG

# A list entry rather than the mapping shorthand, so a device's own bus merges instead of replacing this.
# https://esphome.io/components/i2c
i2c:
- id: board_i2c
sda: GPIO11
scl: GPIO12
scan: true
frequency: 400kHz

# https://esphome.io/components/spi
spi:
- id: board_spi
clk_pin: GPIO6
mosi_pin: GPIO5

# https://esphome.io/components/output
output:

# https://esphome.io/components/output/ledc
- platform: ledc
id: lcd_backlight_output
pin: GPIO9

# Channel 7 is on the last LEDC timer, so a buzzer note never retunes the backlight or a device's own ledc output.
# https://esphome.io/components/output/ledc
- platform: ledc
id: buzzer_output
channel: 7
pin:
number: GPIO3 # Strapping pin, fixed board wiring, so the warning is acknowledged.
ignore_strapping_warning: true

# https://esphome.io/components/light/monochromatic
light:
- platform: monochromatic
name: "Backlight"
id: lcd_backlight
output: lcd_backlight_output
default_transition_length: 250ms
restore_mode: ALWAYS_OFF

# https://esphome.io/components/switch/gpio
switch:
- platform: gpio
id: power_hold
pin:
number: GPIO46 # Strapping pin, fixed board wiring, so the warning is acknowledged.
ignore_strapping_warning: true
restore_mode: ALWAYS_ON
internal: true

# https://esphome.io/components/button/template
button:
- platform: template
name: "Power Off"
id: power_off
entity_category: config
on_press:
- switch.turn_off: power_hold

# https://esphome.io/components/binary_sensor/gpio
binary_sensor:
- platform: gpio
name: "Button"
id: front_button
pin:
number: GPIO42
inverted: true
mode:
input: true
pullup: true

- platform: gpio
name: "Boot Button"
id: boot_button
pin:
number: GPIO0 # Strapping pin, fixed board wiring, so the warning is acknowledged.
ignore_strapping_warning: true
inverted: true
mode:
input: true
pullup: true
entity_category: diagnostic

# https://esphome.io/components/sensor/rotary_encoder
sensor:
- platform: rotary_encoder
name: "Encoder"
id: board_encoder
pin_a:
number: GPIO40
mode:
input: true
pullup: true
pin_b:
number: GPIO41
mode:
input: true
pullup: true
resolution: 1
publish_initial_value: true

# https://esphome.io/components/display/mipi_spi
display:
- platform: mipi_spi
id: board_display
spi_id: board_spi
model: GC9A01A
dc_pin: GPIO4
cs_pin: GPIO7
reset_pin: GPIO8
invert_colors: true
data_rate: 40MHz
auto_clear_enabled: false
update_interval: never

# https://esphome.io/components/touchscreen/ft5x06
touchscreen:
- platform: ft5x06
id: board_touch
i2c_id: board_i2c
address: 0x38
display: board_display
interrupt_pin: GPIO14

# https://esphome.io/components/time
time:

# Home Assistant time writes through to the RTC, so the clock survives a boot with no Home Assistant.
- id: !extend homeassistant_time
on_time_sync:
- bm8563.write_time: rtc_time

# https://esphome.io/components/time/bm8563
- platform: bm8563
id: rtc_time
i2c_id: board_i2c
update_interval: never

# The WS1850S answers as an MFRC522.
# - Each scan fires a Home Assistant tag event, and the text sensor holds the UID until the tag leaves.
# https://esphome.io/components/binary_sensor/rc522
rc522_i2c:
- id: board_rfid
i2c_id: board_i2c
address: 0x28
on_tag:
- homeassistant.tag_scanned: !lambda return x;
- text_sensor.template.publish:
id: rfid_tag
state: !lambda return x;
on_tag_removed:
- text_sensor.template.publish:
id: rfid_tag
state: ""

# https://esphome.io/components/text_sensor/template
text_sensor:
- platform: template
name: "RFID Tag"
id: rfid_tag
Loading
Loading