With the outstanding modular design, excellent user experience, and innovative integration of hardware and software ecosystems, the M5Stack Series was honored with the 2025 Good Design Award!
The Good Design Award, founded in Japan in 1957, celebrates products that combine aesthetic design with social impact. This year, the M5Stack Series stood out among thousands of entries for its modular system, open ecosystem, and design that empowers creativity across all levels.
Designed for Innovation
M5Stack is a modular prototyping tool for turning ideas into real products fast. It combines a durable enclosure, standardized expansion modules, and support for multiple programming languages. Its GUI environment, UIFlow, lowers the barrier for beginners while serving professionals, making hardware development simpler and more efficient.
Since its launch in 2017, M5Stack has built a vibrant global community of developers and educators. The series has become a common sight not only in schools, research institutions, and maker activities but also across industrial and IoT innovation projects. In Japan alone, dozens of introductory books have been published on IoT and robotics development using M5Stack.
Today, the M5Stack Series encompasses more than 400 products, widely used in education, industry, and embedded systems, significantly enhancing rapid development and fostering innovation.
Evaluation Comments from Jury members
The Good Design Award 2025 judges commended M5Stack :
M5Stack is a development platform featuring a modular design that allows users to freely combine a diverse lineup of modules. With its highly refined enclosure and attention to detail, it offers a level of aesthetic appeal and usability unmatched by conventional microcontroller boards—reminding users of the joy of creating tools born from their own imagination.
Supported by an active global community, M5Stack has found widespread use across various fields—from education and hobbyist projects to industrial and embedded product applications. Its standardized expansion system and GUI-based tools make it accessible for both beginners and advanced developers alike, offering a broad entry point into creative technology development.
As programming becomes more approachable in the age of AI, M5Stack is expected to play a key role in unlocking creativity across diverse groups of people, opening up new possibilities for innovation and making.
The official announcement: Good Design Award 2025 — M5Stack Seires
About GOOD DESIGN AWARD
GOOD DESIGN AWARD is a social movement to make people's lives, industries, and society more well-off through design. Since its start in 1957, it has gained widespread support along with its logo G Mark. GOOD DESIGN AWARD is for products, architecture, software, systems, and services that are relevant to people. Whether visible or invisible, anything that is constructed for some ideal or purpose is considered as design, and its quality is evaluated and honored.
The GOOD DESIGN AWARD helps people to discover the possibilities of design and expand the fields where design can be used through screening and a wide range of promotion, and is dedicated to a well-off society where everyone can enjoy creative life.
The M5Stack Series has been officially included in the GOOD DESIGN AWARD Gallery, which archives and showcases all award-winning works by year since its establishment in 1957.
About M5Stack
Founded in 2017, M5Stack creates modular, open-source hardware development tools designed to streamline prototyping and productization. Based in Shenzhen, China, M5Stack has become a globally recognized developer platform for IoT, AIoT, and robotics solutions.
Learn more about M5Stack and explore our products: https://shop.m5stack.com/
If you've been wanting to use your devices remotely for a while, today you'll learn how to do it by integrating LoRa into Home Assistant.
Index
l What is LoRa and how does it work?
l Prerequisites
l Mounting and configuring the LoRa Gateway
l LoRa node configuration
l Check the communication
l Sending information
l Customize your Gateway
l Acknowledgments
What is LoRa and how does it work?
LoRa (an acronym for "Long Range") is a radio frequency communication protocol that enables long-distance data transmission (up to 10 km in open fields) with very low power consumption. Therefore, it offers two major advantages:
However, it also has its limitations, as its data transfer speed is slow. In practice, this means it's perfect for sending simple data (commands to our devices, numbers, or text strings), but it's not a suitable protocol for transmitting photos or videos.
Additionally, you should consider the frequency LoRa uses in your geographic area. For example, in Europe it's 868 MHz, while in the US and most of Latin America it's 915 MHz.
Prerequisites
To integrate LoRa into Home Assistant, you will need the following components:
Mounting and configuring the LoRa Gateway
We are going to be using the M5Stack Core S3 SE along with the LoRa868 V1.1 module (now the LoRa868 v1.2 module is available). This is a modular device that's very easy to expand by simply assembling the components.
Something important to keep in mind is that the LoRa module has some small switches on the back ('DIP switches') that modify the pin assignment, and logically, it must match what we indicate in the ESPHome code.
To do this, make sure the switches are in the following position (2, 6 and 7 on and the rest off).
From here, the process will be similar for any compatible motherboard (adapting the steps and connection diagram to the specifications of your device). The steps we followed are as follows:
1. In Home Assistant, go to your ESPHome plugin , tap “New device” and “Continue.”
2. Give your device a name (for example, “LoRa Gateway” ) and click “Next”.
3. Select “ESP32-S3” as the device type . You'll notice that a new block has been created for your device in the background.
4. Click “Skip” and click “Edit” above your device block.
5. Add the following lines to the end of your code (which come directly from the ESPHome SX127x component, adapted to our device).
captive_portal:
spi:
clk_pin: GPIO36
mosi_pin: GPIO37
miso_pin: GPIO35
sx127x:
cs_pin: GPIO6
rst_pin: GPIO7
dio0_pin: GPIO10
pa_pin: BOOST
pa_power: 14
bandwidth: 125_0kHz
crc_enable: true
frequency: 868920000
modulation: LORA
rx_start: true
sync_value: 0x12
spreading_factor: 7
coding_rate: CR_4_5
preamble_size: 8
on_packet:
then:
- lambda: |-
ESP_LOGD("lambda", "packet %s", format_hex(x).c_str());
ESP_LOGD("lambda", "rssi %.2f", rssi);
ESP_LOGD("lambda", "snr %.2f", snr);
button:
- platform: template
name: "Transmit Packet"
on_press:
then:
- sx127x.send_packet:
data: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C]
6. Click “Save” and “Install.” Select “Manual download” and wait for the code to compile.
7. When finished, select the “Modern format” option to download the corresponding '.bin' file.
8. Connect the M5Stack Core S3 SE to your computer using the USB-C data cable via the port on the side.
9. Now go to the ESPHome page and click "Connect." In the pop-up window, select your board and click "Connect."
10. Now click on “Install” and select the '.bin' file obtained in step 7. Again, click on “Install”.
11. Return to Home Assistant and go to Settings > Devices & Services. Your device should have been discovered and appear at the top, waiting for you to press the "Configure" button. Otherwise, click the "Add integration" button, search for "ESPHome," and enter your board's IP address in the "Host" field. As always, we recommend assigning a static IP address to your router to avoid future issues if it changes.
LoRa node configuration
Now that we have our LoRa gateway, let's configure a node to send information to it. To do this, we'll follow steps very similar to those in the previous section:
1. In Home Assistant, go to your ESPHome plugin , tap “New device” and “Continue.”
2. Give your device a name (for example, “LoRa Node” ) and click “Next”.
3. Select “ESP32” as the device type . You'll notice a new block has been created for your device in the background.
4. Click “Skip” and click “Edit” above your device block.
5. Add the following lines to the end of your code (which in this case match the example of the SX127x component of ESPHome).
captive_portal:
spi:
clk_pin: GPIO5
mosi_pin: GPIO27
miso_pin: GPIO19
# Example configuration entry
sx127x:
cs_pin: GPIO18
rst_pin: GPIO23
dio0_pin: GPIO26
pa_pin: BOOST
pa_power: 14
bandwidth: 125_0kHz
crc_enable: true
frequency: 868920000
modulation: LORA
rx_start: true
sync_value: 0x12
spreading_factor: 7
coding_rate: CR_4_5
preamble_size: 8
on_packet:
then:
- lambda: |-
ESP_LOGD("lambda", "packet %s", format_hex(x).c_str());
ESP_LOGD("lambda", "rssi %.2f", rssi);
ESP_LOGD("lambda", "snr %.2f", snr);
button:
- platform: template
name: "Transmit Packet"
on_press:
then:
- sx127x.send_packet:
data: [0xC5, 0x51, 0x78, 0x82, 0xB7, 0xF9, 0x9C, 0x5C]
6. Note that the pin assignment is different (because we're using a different device than the one we used for the Gateway), but the rest of the configuration is exactly the same as in the previous section. This is important so they can communicate with each other.
7. Click “Save” and “Install.” Select “Manual download” and wait for the code to compile.
8. When finished, select the “Modern format” option to download the corresponding '.bin' file.
9. Connect the board to your computer using the Micro USB data cable through the port on the side.
10. Now go to the ESPHome page and click "Connect." In the pop-up window, select your board and click "Connect."
11. Now click on “Install” and select the '.bin' file obtained in step 8. Again, click on “Install”.
12. Return to Home Assistant and go to Settings > Devices & Services . Your device should have been discovered and appear at the top, waiting for you to press the "Configure" button . Otherwise, click the "Add integration" button, search for "ESPHome," and enter your board's IP address in the "Host" field. As always, we recommend assigning a static IP address to your router to avoid future issues if it changes.
Check the communication
Let's do a little test to check that both devices are communicating correctly (the Gateway and the node) .
1. Make sure both devices are turned on and are online in Home Assistant and ESPHome.
2. From Home Assistant, go to Settings > Devices & Services > ESPHome and access one of them (for example, the Gateway).
3. Open a new window (without closing the previous one), enter the ESPHome plugin and access the logs of the other device (in this case, the node).
4. In the Home Assistant window, click the "Transmit Packet" button. You'll immediately see the logs from the second device recording the incoming packet.
You can perform a reverse test to verify that communication is working both ways. If everything went well, your devices are now communicating.
Sending information
Logically, the point of integrating LoRa into Home Assistant is to send some kind of useful information (such as the value of a sensor connected to the node).
1. The first step is to add the sensor you're interested in to the node board. For example, we're going to add a PIR sensor to get the long-awaited motion sensor on the mailbox. To do this, we've added the following code snippet:
binary_sensor:
- platform: gpio
pin: GPIO34
name: "PIR Sensor"
device_class: motion
id: pir_sensor
2. If you look at the code above, it's the same one we would use in any "normal" scenario. However, to send the information to our LoRa Gateway, we need to add something extra using the 'Packet Transport' component.
packet_transport:
platform: sx127x
update_interval: 5s
encryption: "password"
rolling_code_enable: true
binary_sensors:
- pir_sensor
3. Analyze the code above and observe the following:
· In the 'encryption' attribute, you have to indicate the encryption key for your message (whatever you want), so that it cannot be received by anyone on the same frequency.
· We've identified the ID of the sensor we want to send (in this case, the binary sensor with the ID "pir_sensor") . You can add any other sensors you're interested in here.
4. Now we are going to add the following to the code of our LoRa Gateway, so that it receives the information.
packet_transport:
platform: sx127x
update_interval: 5s
providers:
- name: lora-node
encryption: "password"
binary_sensor:
- platform: packet_transport
id: pir_sensor
provider: lora-node
- platform: template
name: "Buzón"
device_class: motion
lambda: return id(pir_sensor).state;
5. Now we’re going to add the following to the code of our LoRa Gateway, so it can receive the information.
Again, analyze the code and note the following:
l We have specified the ESPHome device name of our LoRa node as the data provider.
l In the ‘encryption’ attribute, we have indicated exactly the same key as in the node.
l To transform the information received into a gateway sensor, we used the “packet_transport” platform. We assigned it an “id” (you can choose any) and again indicated the LoRa node name as the provider. This is an internal ESPHome sensor.
l To display this information in Home Assistant, we created a template sensor of the same type, assigning it the value of the internal sensor created in the previous step.
6. And that's it! If you now check your gateway device in Home Assistant, you’ll see that it already shows the information from the node.
Customize your Gateway
Since we used the M5Stack Core S3 SE to integrate LoRa into Home Assistant, we can take advantage of its other features to customize it! Below, we're leaving you the full code to create a screen that notifies you when you receive letters in your mailbox!
esphome:
name: lora-gateway
friendly_name: LoRa Gateway
libraries:
- m5stack/M5GFX@^0.1.11
- m5stack/M5Unified@^0.1.11
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
psram:
mode: octal
speed: 80MHz
external_components:
- source:
type: git
url: https://github.com/m5stack/M5CoreS3-Esphome
components: [ m5cores3_display ]
refresh: 0s
# Enable logging
logger:
# Enable Home Assistant API
api:
encryption:
key: "1QrsXUgryxlF6OGsIwLj7eijyy/OMhSobQQHYWPvpb0="
ota:
- platform: esphome
password: "4844c4205ab6ab665c2d1a4be82deb57"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Lora-Gateway Fallback Hotspot"
password: "6BRaaV17Iebb"
captive_portal:
spi:
clk_pin: GPIO36
mosi_pin: GPIO37
miso_pin: GPIO35
sx127x:
cs_pin: GPIO6
rst_pin: GPIO7
dio0_pin: GPIO10
pa_pin: BOOST
pa_power: 14
bandwidth: 125_0kHz
crc_enable: true
frequency: 868920000
modulation: LORA
rx_start: true
sync_value: 0x12
spreading_factor: 7
coding_rate: CR_4_5
preamble_size: 8
on_packet:
then:
- lambda: |-
ESP_LOGD("lambda", "packet %s", format_hex(x).c_str());
ESP_LOGD("lambda", "rssi %.2f", rssi);
ESP_LOGD("lambda", "snr %.2f", snr);
packet_transport:
platform: sx127x
update_interval: 5s
providers:
- name: lora-node
encryption: "password"
binary_sensor:
- platform: packet_transport
id: pir_sensor
provider: lora-node
- platform: template
name: "Buzón"
device_class: motion
lambda: return id(pir_sensor).state;
color:
- id: green
hex: 'bfea11'
- id: red
hex: 'ff0000'
font:
- file: "gfonts://Roboto"
id: font_title
size: 18
- file: "gfonts://Roboto"
id: font_text
size: 16
image:
- file: mdi:mailbox
id: buzon_off
resize: 100x100
type: grayscale
transparency: alpha_channel
- file: mdi:email-alert
id: buzon_on
resize: 100x100
type: grayscale
transparency: alpha_channel
display:
- platform: m5cores3_display
model: ILI9342
dc_pin: 15
update_interval: 1s
id: m5cores3_lcd
lambda: |-
// Obtener dimensiones de la pantalla
int screen_width = it.get_width();
int screen_height = it.get_height();
// Título en la parte superior con margen de 20px
it.print(screen_width/2, 20, id(font_title), id(green), TextAlign::TOP_CENTER, "LoRa Gateway by Aguacatec");
// Obtener estado del sensor del buzón
bool mailbox_open = id(pir_sensor).state;
if (mailbox_open) {
// Buzón abierto - icono rojo
it.image(110, 70, id(buzon_on), id(red));
it.print(screen_width/2, 200, id(font_text), id(red), TextAlign::CENTER, "Carta recibida!!");
} else {
// Buzón cerrado - icono verde
it.image(110, 70, id(buzon_off), id(green));
it.print(screen_width/2, 200, id(font_text), id(green), TextAlign::CENTER, "No hay correspondencia");
}
Acknowledgements
To prepare this post, this video by our friend Miguel Ángel (from La Choza Digital) was extremely helpful!
Source: AguacaTEC
Author: TitoTB
Amsterdam, Netherlands – September 25, 2025 — M5Stack showcased its latest breakthroughs in modular IoT, edge computing, and smart automation at The Things Conference 2025, held at De Kromhouthal, Amsterdam — the world’s leading LoRaWAN and IoT innovation event.
Highlights from the M5Stack Booth
At the exhibition, M5Stack showcased its signature 5x5cm ESP32-based stackable controllers alongside a versatile lineup of expansion units and modules for plug-and-play development. Live demos revealed how our solutions power rapid, scalable IoT applications across smart homes, classrooms, and industrial environments.
Alongside best-selling devices, visitors enjoyed a first look at new and upcoming innovations:
· Cardputer-Adv — The next-generation upgrade to our signature Cardputer, this powerful, credit card-sized computer is built for rapid prototyping, industrial control, and home automation.
· Tab5 — A sleek, intuitive hub for IoT data visualization and management, demonstrated with its forthcoming keyboard accessory.
· StamPLC — A compact, IoT-enabled programmable logic controller for industrial automation and remote monitoring.
· Switch C6 — A single-live-wire smart switch controller featuring an ESP32-C6-MINI-1 wireless SoC and latching relay for IoT-enabled appliances.
· PowerHub — A programmable multi-channel power management controller with ESP32-S3 and precision monitoring, designed for efficient power control in industrial and smart home applications.
Innovative LoRa Solutions
M5Stack also debuted its new LoRa product family, ranging from enterprise-ready LoRaWAN gateways to off-grid Meshtastic devices built for makers, outdoor enthusiasts, and developers. Engineered for low-power, long-range communication, these solutions target applications such as environmental monitoring, smart agriculture, and industrial IoT.
Showcased products included:
· SolarMesh — A waterproof, solar-powered Meshtastic gateway integrating Wi-Fi (ESP32-S3), LoRa (SX1262), and GPS for versatile, off-grid connectivity.
· UnitC6L — An edge-computing LoRa module combining ESP32-C6 MCU with SX1262, Wi-Fi 6, and Bluetooth 5 (LE) for flexible deployments.
· Atom DTU Terminal — A compact, reliable terminal unit for robust data transmission in distributed networks.
Building the Future Together
Inspiring conversations and hands-on workshops with developers and industry experts highlighted M5Stack’s role in shaping the next wave of IoT solutions. By expanding our modular ecosystem, we aim to empower innovators worldwide to transform ideas into impactful, real-world applications.
About M5Stack
M5Stack is a leading provider of modular, open-source IoT development solutions. Our stackable hardware and intuitive programming platform empower developers and businesses to accelerate innovation and rapidly prototype solutions for IIoT, home automation, smart retail, and STEM education.
September 5, 2025 – M5Stack, a global leader in modular IoT and embedded development platforms, today announced the launch of Cardputer‑Adv, the upgraded model of its popular card‑sized computer series.
Designed for engineers, developers, and tech enthusiasts, Cardputer‑Adv delivers enhanced performance, richer interfaces, and extended battery life — all while keeping the portable elegance of the original Cardputer.
Whether you’re prototyping IoT gadgets, building portable dashboards, testing sensor networks, or creating unique interactive experiences, Cardputer‑Adv is the ultimate pocket‑sized development companion.
Highlights of Cardputer‑Adv
Comparison with Previous Models
A Growing Developer Ecosystem
Cardputer isn’t just hardware — it’s a platform. Alongside the built‑in mini‑programs, users can explore a growing library of community‑developed firmware via M5Burner, enabling instant prototyping for applications like retro gaming, remote control, UI dashboards, cybersecurity tools, and more.
Popular projects such as M5Launcher, Bruce, NEMO, Evil‑Cardputer, Marauder, and Ultimate Remote have each surpassed 10,000+ downloads, inspiring creativity across the Cardputer community.
Beyond M5Stack’s official channels, an active user‑driven community has formed around Cardputer, where enthusiasts share projects, tips, and firmware.
Join the discussions and see what’s possible:
Availability
Cardputer‑Adv is now available via the M5Stack Official Store and authorized global distributors. Order today and start building your next project.
For full details, technical documentation, and project ideas, visit www.m5stack.com.
In today's fast-evolving IoT and smart hardware landscape, a smooth and responsive user interface (UI) has become just as important as core functionality.
M5Stack, continuing to refine its visual programming platform UIFlow2, now officially integrates the powerful LVGL (Light and Versatile Graphics Library) — giving makers and developers the best of both worlds: the speed of visual programming and the freedom of a professional embedded GUI framework.
UIFlow2, built on MicroPython, is designed to lower the barrier for hardware programming. Earlier versions included basic controls and drawing functions — useful for simple projects but less suited for complex UI needs.
LVGL changes the game. It's an open-source, lightweight, cross-platform embedded GUI library with:
With UIFlow2's LVGL integration, developers can start by dragging and dropping blocks, then fine-tune behavior via Python code — moving seamlessly from beginner-friendly to pro-level control.
What's Available Now
UIFlow2 already supports LVGL in its first integration phase:
Whether you're a first-time maker or a seasoned embedded engineer, you can design and deploy interactive UIs faster than ever.
Getting Started in 4 Steps
1. Launch UIFlow2 Web IDE
Power on your M5 device and connect to the UIFLow2 online editor.
2. Enable LVGL Support
Select M5UI in settings to enable related blocks.
3. Add Widgets or Write Code
Block Mode: Drag and drop "Button", "Label" and other controls directly.
Code Mode Example:
import m5ui
import lvgl as lv
import M5
M5.begin()
m5ui.init()
page0 = m5ui.M5Page(bg_c=0xffffff)
button0 = m5ui.M5Button(
text="click me",
x=115,
y=153,
bg_c=0x2196f3,
text_c=0xffffff,
font=lv.font_montserrat_14,
parent=page0
)
label0 = m5ui.M5Label(
"Hello M5!!!",
x=123,
y=82,
text_c=0x000000,
bg_c=0xffffff,
bg_opa=0,
font=lv.font_montserrat_14,
parent=page0
)
page0.screen_load()
After downloading the program to the device, controls will immediately display on the screen and support real-time interaction.
What's Coming Next
We're pushing towards full LVGL integration and a more intuitive design experience:
Ultimately, UIFLow2+LVGL bridges the gap between quick, beginner-friendly prototyping and precise, professional-grade UI development — giving every creator the speed to start and the depth to go further.
If you’re just getting started with Home Assistant, one of the easiest and most fun projects to try is smart lighting. This guide walks through how to set up the M5Atom Lite—a compact ESP32-based module—as a smart RGB light controller, fully integrated with Home Assistant using ESPHome.
What You’ll Need
Before we begin, make sure you have Home Assistant installed. You can follow the official documentation for your preferred platform.
Once Home Assistant is up and running:
2. Adding the Device
1. Open the ESPHome sidebar and click NEW DEVICE in the lower right corner.
2. Click CONTINUE when the setup screen appears.
3. Name your device (e.g., Atom-Lite), then proceed.
4. On the device type screen:
o Uncheck Use recommended settings
o Select ESP32 → choose M5Stack-ATOM
5. Click NEXT and copy the encryption key that appears.
6. Choose Manual download to begin compiling the firmware.
Back on the ESPHome dashboard, you’ll now see your new Atom-Lite device listed.
1. Click EDIT to open the YAML configuration editor
2. Replace the content with the following configuration (update your Wi-Fi credentials!):
esphome: name: atom-lite friendly_name: Atom-Lite esp32: board: m5stack-atom framework: type: arduino logger: api: encryption: key: "*********" ota: - platform: esphome password: "*****************" wifi: ssid: "*********" password: "***********" ap: ssid: "Atom-Lite Fallback Hotspot" password: "jFsIc2XGuKRe" captive_portal: binary_sensor: - platform: gpio pin: number: GPIO39 mode: INPUT inverted: true name: "Atom Button" id: atom_button filters: - delayed_on: 50ms - delayed_off: 50ms on_multi_click: - timing: - ON for at most 0.8s - OFF for at most 0.5s - ON for at most 0.8s - OFF for at least 0.2s then: - logger.log: "Double Clicked" - light.turn_on: id: atom_light red: 100% blue: 50% green: 20% brightness: 50% - timing: - ON for at least 0.8s then: - logger.log: "Single Long Clicked" - light.turn_on: id: atom_light green: 100% blue: 50% red: 30% brightness: 100% light: - platform: neopixelbus type: GRB pin: GPIO27 num_leds: 1 variant: sk6812 name: "Atom RGB Light" id: atom_light restore_mode: RESTORE_DEFAULT_OFF effects: - random: name: "Random" transition_length: 1s update_interval: 1s
3. Click SAVE, then INSTALL → Manual download to compile
Note: The first compilation may take several minutes, depending on your setup and network.
After compiling the firmware:
1. Click DOWNLOAD, and choose Factory format
2. Connect the M5Atom Lite to your computer using a USB-C data cable
3. Back in ESPHome, select INSTALL → Plug into this computer
4. Click Open ESPHome Web
5. Press CONNECT, then select the detected serial port
6. Click INSTALL and wait for the firmware installation to complete.
Once flashing is complete, the device will restart and attempt to connect to your Wi-Fi.
Once Atom Lite is online:
1. Open Settings → Integrations in Home Assistant
2. Under Discovered, click ADD and follow the prompts to integrate it
6. Create an Automation
You can now set up a basic automation using the button to control the light:
1. In the Home Assistant page, go to Settings → Device& Service
2. Locate ESPHome → hit Atom-Lite → click Automations
3. Select Create new automation → ADD TRIGGER → Entity → State → Atom Button
4. In the When section, change the status from Off to On
5. In the Then do section, select ADD ACTION → Light → Toggle → + Choose entity → Atom-Lite RGB Light → Save
This simple setup turns the button into a light switch for the RGB LED.
To control the RGB light from the Home Assistant interface:
1. Go to Overview → Edit
2. In the By card page, input Light on the search cards Select the Light card type
3. Choose the Atom RGB Light entity
4. Save the changes
The light can now be toggled and color-adjusted directly from the dashboard.
8. Demo & Behavior
Here’s how your new Atom-Lite smart RGB light behaves:
Conclusion
Home Assistant makes smart home control simple, with M5Stack Atom-Lite and ESPHome, setting up RGB lighting is just the start. With the same process, you can go further by adding mode device like a human presence sensor to detect movement, turn on lights automatically, or turn on the AC and set it to the optimal temperature when someone enters the room.
In this article, we’ll integrate the M5Stack Dial into Home Assistant (HA) — a multifunctional system with many interesting features to control our setup.
Index
M5Stack Dial
M5Stack is already a well-known brand to us, with creations like the M5Stack CoreS3SE and the classic Atom Echo. Today, we are going to integrate the M5Stack Dial into Home Assistant — a device that includes the following components:
All these features packed into a single, ready-to-use device which make the M5Dial a truly compelling gadget. And since it's powered by an ESP32-S3, we can easily integrate it into Home Assistant using ESPHome.
Mr. Avocado
As usual, we wanted to make the most of these features by building a fun and practical project. This time, it's something special — a device co-designed with our Patreon community.
We named it Mr. Avocado, a playful nod to the iconic “Mr. Potato.” The goal was to create a multifunctional device with the following capabilities:
Prerequisites
To integrate the M5Dial into Home Assistant, you’ll need:
🥑 If you’re just getting started with ESPHome, I highly recommend checking out the academy workshop — it’s a great way to get the most out of it!
Follow these steps to integrate the M5Stack Dial into Home Assistant:
1. In Home Assistant, open the ESPHome add-on, click “New Device”, then “Continue.”
2. Give your device a name (for example, “M5Stack Dial”) and click “Next.”
3. For the device type, select “ESP32-S3.” You’ll see that a new tile has been created for your device.
4. Click “Skip”, then “Edit” on your device’s tile. Copy the default code that appears and save it—you’ll need parts of it later.
5. Now, copy the code below and use it to replace the default code in ESPHome.
substitutions: # Device customization # Personalización del dispositivo name: m5stack-dial friendly_name: M5Stack Dial background_color: 'fab02b' background_image: https://aguacatec.es/wp-content/uploads/2025/02/mravocado_background_white.jpg background_image_saver: https://aguacatec.es/wp-content/uploads/2025/02/mravocado_bg_off.jpg background_image_device: https://aguacatec.es/wp-content/uploads/2025/02/mravocado_bg_device.jpeg # Icons # Iconos icon_1: mdi:led-strip-variant icon_2: mdi:thermostat icon_3: mdi:robot-vacuum icon_4: mdi:printer icon_5: mdi:printer-3d-nozzle icon_6: mdi:fan icon_7: mdi:air-humidifier icon_8: mdi:ceiling-light # Sounds # Sonidos menu_sound: 'beep:d=64,o=5,b=255:c7' alarm_sound: 'xmen:d=4,o=6,b=200:16f#5,16g5,16b5,16d,c#,8b5,8f#5,p,16f#5,16g5,16b5,16d,c#,8b5,8g5,p,16f#5,16g5,16b5,16d,c#,8b5,8d,2p,8c#,8b5,2p' # Example of Lights # Ejemplo de Luces desk_led: light.tira_led_escritorio lamp: light.lampara # Example of Thermostat # Ejemplo de Termostatos climate: climate.salon aircon: climate.aircon # Example of Vacuum # Ejemplo de Aspirador vacuum: vacuum.robot_aspirador # Example of Switches # Ejemplo de Enchufes printer: switch.regleta_l3 printer3d: switch.regleta_l4 # Example of dehumidifier # Ejemplo de Deshumidificador dehumidifier: humidifier.deshumidificador # NFC/RFID Tags # Etiquetas NFC/RFID # tag1: C3-DB-4F-28 # tag2: 03-55-E5-13 # Other settings # Otros ajustes allowed_characters: " ¿?¡!#%'()+,-./:°0123456789ABCDEFGHIJKLMNOPQRSTUVWYZabcdefghijklmnopqrstuvwxyzáéíóú" ################################################################################################################ esphome: name: ${name} friendly_name: ${friendly_name} on_boot: then: - pcf8563.read_time: - display.page.show: home platformio_options: board_build.flash_mode: dio esp32: board: esp32-s3-devkitc-1 flash_size: 8MB framework: type: esp-idf wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "M5Stack-Dial Fallback Hotspot" password: "Aosad564JQR" api: encryption: key: "QYmasdasdsd71H8/dlyD1BI5cU10X234234fhg=" services: - service: play_sound variables: song: string volume: int then: - lambda: "id(script_rtttl_play).execute(song, volume);" script: - id: script_rtttl_play parameters: song: string volume: int mode: single then: - lambda: |- float volume_f = (volume>0) ? ((float)clamp(volume, 0, 100))/100.0f : 1.0f; id(buzzer).set_max_power(volume_f); - rtttl.play: rtttl: !lambda 'return (song.find('':'') == std::string::npos) ? ("song:d=16,o=5,b=100:" + song).c_str() : song.c_str();' ota: - platform: esphome password: "0935e9dsasdfgdb3d8934c" logger: captive_portal: binary_sensor: - platform: gpio name: "Front Button" id: front_button pin: number: GPIO42 inverted: true internal: true on_press: then: - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - if: condition: light.is_on: backlight then: - if: condition: display.is_displaying_page: device_control then: - if: condition: lambda: |- return id(device) == 1; then: - homeassistant.action: service: light.toggle data: entity_id: ${desk_led} - if: condition: lambda: |- return id(device) == 2; then: - homeassistant.action: service: climate.toggle data: entity_id: ${climate} - if: condition: lambda: |- return id(device) == 3; then: - if: condition: lambda: 'return id(device_vacuum).state == "cleaning";' then: - homeassistant.action: service: vacuum.pause data: entity_id: ${vacuum} else: - homeassistant.action: service: vacuum.start data: entity_id: ${vacuum} - if: condition: lambda: |- return id(device) == 4; then: - homeassistant.action: service: switch.toggle data: entity_id: ${printer} - if: condition: lambda: |- return id(device) == 5; then: - homeassistant.action: service: switch.toggle data: entity_id: ${printer3d} - if: condition: lambda: |- return id(device) == 6; then: - homeassistant.action: service: climate.toggle data: entity_id: ${aircon} - if: condition: lambda: |- return id(device) == 7; then: - homeassistant.action: service: humidifier.toggle data: entity_id: ${dehumidifier} - if: condition: lambda: |- return id(device) == 8; then: - homeassistant.action: service: light.toggle data: entity_id: ${lamp} - if: condition: display.is_displaying_page: locked_screen then: - switch.turn_on: mravocado_display - light.turn_on: id: backlight brightness: 100% - display.page.show: home - if: condition: display.is_displaying_page: home then: - if: condition: lambda: |- return id(device) > 0; then: - light.turn_on: id: backlight brightness: 100% - display.page.show: device_control else: - switch.turn_on: mravocado_display - light.turn_on: id: backlight brightness: 100% - display.page.show: home - lambda: |- id(inactivity_time) = 0; - platform: gpio name: Hold Button pin: GPIO46 internal: True - platform: touchscreen name: "Home Button" internal: true x_min: 0 x_max: 240 y_min: 0 y_max: 80 page_id: device_control on_press: - display.page.show: home - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - lambda: |- id(inactivity_time) = 0; - platform: touchscreen name: "Device Button" internal: true x_min: 81 x_max: 160 y_min: 80 y_max: 240 page_id: device_control on_press: - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - if: condition: lambda: |- return id(device) == 1; then: - homeassistant.action: service: light.toggle data: entity_id: ${desk_led} - if: condition: lambda: |- return id(device) == 2; then: - homeassistant.action: service: climate.toggle data: entity_id: ${climate} - if: condition: lambda: |- return id(device) == 3; then: - if: condition: lambda: 'return id(device_vacuum).state == "cleaning";' then: - homeassistant.action: service: vacuum.pause data: entity_id: ${vacuum} else: - homeassistant.action: service: vacuum.start data: entity_id: ${vacuum} - if: condition: lambda: |- return id(device) == 4; then: - homeassistant.action: service: switch.toggle data: entity_id: ${printer} - if: condition: lambda: |- return id(device) == 5; then: - homeassistant.action: service: switch.toggle data: entity_id: ${printer3d} - if: condition: lambda: |- return id(device) == 6; then: - homeassistant.action: service: climate.toggle data: entity_id: ${aircon} - if: condition: lambda: |- return id(device) == 7; then: - homeassistant.action: service: humidifier.toggle data: entity_id: ${dehumidifier} - if: condition: lambda: |- return id(device) == 8; then: - homeassistant.action: service: light.toggle data: entity_id: ${lamp} - lambda: |- id(inactivity_time) = 0; - platform: touchscreen name: "Minus Button" internal: true x_min: 0 x_max: 80 y_min: 80 y_max: 240 page_id: device_control on_press: - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - if: condition: display.is_displaying_page: device_control then: - if: condition: lambda: |- return id(device) == 1; then: - homeassistant.action: service: light.turn_on data: entity_id: ${desk_led} brightness_step_pct: '-10' - if: condition: lambda: |- return id(device) == 2; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${climate} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(thermostat_temperature).state - 1.0; - if: condition: lambda: |- return id(device) == 6; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${aircon} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(aircon_temperature).state - 1.0; - if: condition: lambda: |- return id(device) == 7; then: - homeassistant.action: service: humidifier.set_humidity data: entity_id: ${dehumidifier} data_template: humidity: '{{ my_variable | float }}' variables: my_variable: |- return id(dehumidifier_humidity).state - 5.0; - if: condition: lambda: |- return id(device) == 8; then: - homeassistant.action: service: light.turn_on data: entity_id: ${lamp} brightness_step_pct: '-10' - lambda: |- id(inactivity_time) = 0; - platform: touchscreen name: "Plus Button" internal: true x_min: 161 x_max: 240 y_min: 80 y_max: 240 page_id: device_control on_press: - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - if: condition: display.is_displaying_page: device_control then: - if: condition: lambda: |- return id(device) == 1; then: - homeassistant.action: service: light.turn_on data: entity_id: ${desk_led} brightness_step_pct: '10' - if: condition: lambda: |- return id(device) == 2; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${climate} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(thermostat_temperature).state + 1.0; - if: condition: lambda: |- return id(device) == 3; then: - homeassistant.action: service: vacuum.return_to_base data: entity_id: ${vacuum} - if: condition: lambda: |- return id(device) == 6; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${aircon} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(aircon_temperature).state + 1.0; - if: condition: lambda: |- return id(device) == 7; then: - homeassistant.action: service: humidifier.set_humidity data: entity_id: ${dehumidifier} data_template: humidity: '{{ my_variable | float }}' variables: my_variable: |- return id(dehumidifier_humidity).state + 5.0; - if: condition: lambda: |- return id(device) == 8; then: - homeassistant.action: service: light.turn_on data: entity_id: ${lamp} brightness_step_pct: '10' - lambda: |- id(inactivity_time) = 0; # - platform: rc522 # uid: ${tag1} # name: "NFC Tag" # on_press: # - homeassistant.action: # service: light.toggle # data: # entity_id: ${desk_led} button: - platform: template name: "Alarm" id: alarm_sound icon: "mdi:bell-ring" on_press: - rtttl.play: ${alarm_sound} - switch.turn_on: screen_saver - lambda: |- id(inactivity_time) = 0; color: - id: background_color hex: ${background_color} - id: icon_on hex: 'f28800' - id: icon_off hex: 'e7aa77' - id: icon_big_on hex: 'ffebbf' - id: icon_big_off hex: 'f78f1d' - id: dark_orange hex: 'd2750b' - id: light_orange hex: 'f9c699' font: - file: "gfonts://Space Grotesk" id: clock_time size: 40 glyphs: ${allowed_characters} - file: "gfonts://Space Grotesk" id: secondary size: 18 glyphs: ${allowed_characters} globals: - id: inactivity_time type: int restore_value: no initial_value: '0' - id: device type: int restore_value: no initial_value: '0' i2c: - id: internal_i2c sda: GPIO11 scl: GPIO12 scan: False image: - file: ${background_image} id: background_image resize: 245x245 type: RGB transparency: alpha_channel - file: ${background_image_saver} id: background_image_saver resize: 245x245 type: RGB transparency: alpha_channel - file: ${background_image_device} id: background_image_device resize: 245x245 type: RGB transparency: alpha_channel - file: mdi:home id: icon_home resize: 40x40 type: BINARY transparency: chroma_key - file: mdi:plus-thick id: plus resize: 30x30 type: BINARY transparency: chroma_key - file: mdi:minus-thick id: minus resize: 30x30 type: BINARY transparency: chroma_key - file: mdi:home-map-marker id: vacuum_dock resize: 30x30 type: BINARY transparency: chroma_key - file: mdi:play-box id: play_icon resize: 30x30 type: BINARY transparency: chroma_key - file: mdi:pause-box id: pause_icon resize: 30x30 type: BINARY transparency: chroma_key - file: ${icon_1} id: icon_1 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_1} id: icon_1_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_2} id: icon_2 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_2} id: icon_2_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_3} id: icon_3 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_3} id: icon_3_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_4} id: icon_4 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_4} id: icon_4_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_5} id: icon_5 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_5} id: icon_5_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_6} id: icon_6 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_6} id: icon_6_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_7} id: icon_7 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_7} id: icon_7_big resize: 100x100 type: BINARY transparency: chroma_key - file: ${icon_8} id: icon_8 resize: 33x33 type: BINARY transparency: chroma_key - file: ${icon_8} id: icon_8_big resize: 100x100 type: BINARY transparency: chroma_key interval: - interval: 1s then: - lambda: |- id(inactivity_time) += 1; if (id(auto_lock).state) { if (id(inactivity_time) > id(screen_saver_time).state && id(inactivity_time) < id(auto_lock_time_out).state ) { id(screen_saver).turn_on(); } if (id(inactivity_time) > id(auto_lock_time_out).state) { id(backlight_pwm).turn_off(); id(mravocado_display).turn_off(); id(screen_saver).turn_off(); } } else { if (id(inactivity_time) > id(screen_saver_time).state) { id(screen_saver).turn_on(); } } light: - platform: monochromatic name: "Backlight" output: backlight_pwm id: backlight default_transition_length: 0s restore_mode: ALWAYS_ON internal: True number: - platform: template name: "Auto Lock" id: auto_lock_time_out icon: "mdi:timer-sand" optimistic: true min_value: 20 max_value: 300 step: 10 unit_of_measurement: "s" restore_value: true - platform: template name: "Screen Saver" id: screen_saver_time icon: "mdi:screen-rotation-lock" optimistic: true min_value: 10 max_value: 300 step: 10 unit_of_measurement: "s" restore_value: true output: - platform: ledc pin: GPIO3 id: buzzer - platform: ledc pin: GPIO9 id: backlight_pwm #rc522_i2c: # - i2c_id: internal_i2c # id: tag_reader # address: 0x28 # on_tag: # then: # - rtttl.play: "success:d=24,o=5,b=100:c,g,b" # - homeassistant.tag_scanned: !lambda 'return x;' rtttl: output: buzzer sensor: - platform: rotary_encoder id: encoder pin_a: GPIO40 pin_b: GPIO41 on_clockwise: then: - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - if: condition: display.is_displaying_page: home then: - lambda: |- if (id(device) == 8) { id(device) = 1; } else { id(device) += 1; } - if: condition: display.is_displaying_page: device_control then: - if: condition: lambda: |- return id(device) == 1; then: - homeassistant.action: service: light.turn_on data: entity_id: ${desk_led} brightness_step_pct: '10' - if: condition: lambda: |- return id(device) == 2; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${climate} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(thermostat_temperature).state + 1.0; - if: condition: lambda: |- return id(device) == 3; then: - homeassistant.action: service: vacuum.return_to_base data: entity_id: ${vacuum} - if: condition: lambda: |- return id(device) == 6; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${aircon} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(aircon_temperature).state + 1.0; - if: condition: lambda: |- return id(device) == 7; then: - homeassistant.action: service: humidifier.set_humidity data: entity_id: ${dehumidifier} data_template: humidity: '{{ my_variable | float }}' variables: my_variable: |- return id(dehumidifier_humidity).state + 5.0; - if: condition: lambda: |- return id(device) == 8; then: - homeassistant.action: service: light.turn_on data: entity_id: ${lamp} brightness_step_pct: '10' - lambda: |- id(inactivity_time) = 0; on_anticlockwise: then: - if: condition: switch.is_on: menu_sounds then: - rtttl.play: ${menu_sound} - if: condition: display.is_displaying_page: home then: - lambda: |- if (id(device) == 1) { id(device) = 8; } if (id(device) == 0) { id(device) = 8; } else { id(device) -= 1; } - if: condition: display.is_displaying_page: device_control then: - if: condition: lambda: |- return id(device) == 1; then: - homeassistant.action: service: light.turn_on data: entity_id: ${desk_led} brightness_step_pct: '-10' - if: condition: lambda: |- return id(device) == 2; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${climate} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(thermostat_temperature).state - 1.0; - if: condition: lambda: |- return id(device) == 3; then: - if: condition: lambda: 'return id(device_vacuum).state == "cleaning";' then: - homeassistant.action: service: vacuum.pause data: entity_id: ${vacuum} else: - homeassistant.action: service: vacuum.start data: entity_id: ${vacuum} - if: condition: lambda: |- return id(device) == 6; then: - homeassistant.action: service: climate.set_temperature data: entity_id: ${aircon} data_template: temperature: '{{ my_variable | float }}' variables: my_variable: |- return id(aircon_temperature).state - 1.0; - if: condition: lambda: |- return id(device) == 7; then: - homeassistant.action: service: humidifier.set_humidity data: entity_id: ${dehumidifier} data_template: humidity: '{{ my_variable | float }}' variables: my_variable: |- return id(dehumidifier_humidity).state - 5.0; - if: condition: lambda: |- return id(device) == 8; then: - homeassistant.action: service: light.turn_on data: entity_id: ${lamp} brightness_step_pct: '-10' - lambda: |- id(inactivity_time) = 0; - platform: homeassistant id: desk_led_brightness entity_id: ${desk_led} attribute: brightness internal: true filters: - lambda: |- if (isnan(x)) { return 0; } else { return x; } - platform: homeassistant id: thermostat_temperature entity_id: ${climate} attribute: temperature internal: true - platform: homeassistant id: aircon_temperature entity_id: ${aircon} attribute: temperature internal: true - platform: homeassistant id: dehumidifier_humidity entity_id: ${dehumidifier} attribute: humidity internal: true - platform: homeassistant id: lamp_brightness entity_id: ${lamp} attribute: brightness internal: true filters: - lambda: |- if (isnan(x)) { return 0; } else { return x; } spi: id: spi_bus mosi_pin: GPIO5 clk_pin: GPIO6 switch: - platform: template name: "Auto Lock" id: auto_lock icon: "mdi:lock-clock" optimistic: true restore_mode: 'restore_default_off' - platform: template name: "Display" id: mravocado_display icon: "mdi:fit-to-screen" optimistic: true restore_mode: 'always_on' on_turn_on: - light.turn_on: id: backlight brightness: 100% - lambda: |- id(inactivity_time) = 0; on_turn_off: - light.turn_off: backlight - display.page.show: home - lambda: |- id(device) = 0; - platform: template name: "Screen Saver" id: screen_saver icon: "mdi:screen-rotation-lock" optimistic: true restore_mode: 'always_off' internal: true on_turn_on: - light.turn_on: id: backlight brightness: 50% - display.page.show: locked_screen - lambda: |- id(device) = 0; - platform: template name: "Menu Sounds" id: menu_sounds icon: "mdi:playlist-music" optimistic: true restore_mode: 'restore_default_on' text_sensor: - platform: homeassistant id: device_desk_led entity_id: ${desk_led} internal: true - platform: homeassistant id: device_thermostat entity_id: ${climate} internal: true - platform: homeassistant id: device_vacuum entity_id: ${vacuum} internal: true - platform: homeassistant id: device_printer entity_id: ${printer} internal: true - platform: homeassistant id: device_printer3d entity_id: ${printer3d} internal: true - platform: homeassistant id: device_dehumidifier entity_id: ${dehumidifier} internal: true - platform: homeassistant id: device_aircon entity_id: ${aircon} internal: true - platform: homeassistant id: device_lamp entity_id: ${lamp} internal: true time: # RTC - platform: pcf8563 id: rtctime i2c_id: internal_i2c address: 0x51 update_interval: never - platform: homeassistant id: esptime on_time_sync: then: - pcf8563.write_time: touchscreen: - platform: ft5x06 id: touchscreen_mravocado i2c_id: internal_i2c address: 0x38 display: - platform: ili9xxx id: round_display model: GC9A01A cs_pin: GPIO7 reset_pin: GPIO8 update_interval: 0.05s dc_pin: GPIO4 invert_colors: true pages: - id: locked_screen lambda: |- it.fill(id(background_color)); it.image(0, 0, id(background_image_saver)); it.strftime(120, 40, id(clock_time), TextAlign::CENTER, "%H:%M", id(esptime).now()); it.strftime(120, 200, id(secondary), TextAlign::CENTER, "%d/%m/%y", id(esptime).now()); - id: home lambda: |- it.fill(id(background_color)); it.image(0, 0, id(background_image)); if (id(device) == 1) { it.image(103, 4, id(icon_1), id(icon_on)); } else { it.image(103, 4, id(icon_1), id(icon_off)); } if (id(device) == 2) { it.image(175, 35, id(icon_2), id(icon_on)); } else { it.image(175, 35, id(icon_2), id(icon_off)); } if (id(device) == 3) { it.image(205, 105, id(icon_3), id(icon_on)); } else { it.image(205, 105, id(icon_3), id(icon_off)); } if (id(device) == 4) { it.image(175, 175, id(icon_4), id(icon_on)); } else { it.image(175, 175, id(icon_4), id(icon_off)); } if (id(device) == 5) { it.image(103, 205, id(icon_5), id(icon_on)); } else { it.image(103, 205, id(icon_5), id(icon_off)); } if (id(device) == 6) { it.image(30, 175, id(icon_6), id(icon_on)); } else { it.image(30, 175, id(icon_6), id(icon_off)); } if (id(device) == 7) { it.image(5, 105, id(icon_7), id(icon_on)); } else { it.image(5, 105, id(icon_7), id(icon_off)); } if (id(device) == 8) { it.image(30, 35, id(icon_8), id(icon_on)); } else { it.image(30, 35, id(icon_8), id(icon_off)); } - id: device_control lambda: |- it.fill(id(background_color)); it.image(0, 0, id(background_image_device)); it.image(98, 10, id(icon_home), id(light_orange)); if (id(device) == 1) { if (id(device_desk_led).state == "on") { it.image(70, 80, id(icon_1_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_1_big), id(icon_big_off)); } it.image(25, 115, id(minus), id(light_orange)); it.printf(120, 210, id(secondary), TextAlign::CENTER, "%.0f %%", ((id(desk_led_brightness).state / 255) * 100)); it.image(185, 115, id(plus), id(light_orange)); } if (id(device) == 2) { if (id(device_thermostat).state == "heat") { it.image(70, 80, id(icon_2_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_2_big), id(icon_big_off)); } it.image(25, 115, id(minus), id(light_orange)); it.printf(120, 210, id(secondary), TextAlign::CENTER, "%.1f°C", id(thermostat_temperature).state); it.image(185, 115, id(plus), id(light_orange)); } if (id(device) == 3) { if (id(device_vacuum).state == "cleaning") { it.image(70, 80, id(icon_3_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_3_big), id(icon_big_off)); } if (id(device_vacuum).state == "cleaning") { it.image(25, 115, id(pause_icon), id(light_orange)); } else { it.image(25, 115, id(play_icon), id(light_orange)); } it.printf(120, 210, id(secondary), TextAlign::CENTER, "%s", id(device_vacuum).state.c_str()); it.image(185, 115, id(vacuum_dock), id(light_orange)); } if (id(device) == 4) { if (id(device_printer).state == "on") { it.image(70, 80, id(icon_4_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_4_big), id(icon_big_off)); } it.printf(120, 210, id(secondary), TextAlign::CENTER, "%s", id(device_printer).state.c_str()); } if (id(device) == 5) { if (id(device_printer3d).state == "on") { it.image(70, 80, id(icon_5_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_5_big), id(icon_big_off)); } it.printf(120, 210, id(secondary), TextAlign::CENTER, "%s", id(device_printer3d).state.c_str()); } if (id(device) == 6) { if (id(device_aircon).state == "cool") { it.image(70, 80, id(icon_6_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_6_big), id(icon_big_off)); } it.image(25, 115, id(minus), id(light_orange)); it.printf(120, 210, id(secondary), TextAlign::CENTER, "%.1f°C", id(aircon_temperature).state); it.image(185, 115, id(plus), id(light_orange)); } if (id(device) == 7) { if (id(device_dehumidifier).state == "on") { it.image(70, 80, id(icon_7_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_7_big), id(icon_big_off)); } it.image(25, 115, id(minus), id(light_orange)); it.printf(120, 210, id(secondary), TextAlign::CENTER, "%.1f°C", id(dehumidifier_humidity).state); it.image(185, 115, id(plus), id(light_orange)); } if (id(device) == 8) { if (id(device_lamp).state == "on") { it.image(70, 80, id(icon_8_big), id(icon_big_on)); } else { it.image(70, 80, id(icon_8_big), id(icon_big_off)); } it.image(25, 115, id(minus), id(light_orange)); it.printf(120, 210, id(secondary), TextAlign::CENTER, "%.0f %%", ((id(lamp_brightness).state / 255) * 100)); it.image(185, 115, id(plus), id(light_orange)); }
⚠️ While we’ve managed to implement all these features for Mr. Avocado, my recommendation is to comment out (or remove) any parts of the code you’re not planning to use. This will help improve the device’s performance and prevent it from freezing.
6. This code does not include the credentials needed for your device to connect to your Wi-Fi network and your Home Assistant instance. You’ll need to add them manually.
Specifically, I’m referring to the following lines from the code you copied in Step 4.
# Enable Home Assistant API api: encryption: key: "bg6hash6sjdjsdjk02hh0qnQeYVwm123vdfKE8BP5" ota: - platform: esphome password: "asddasda27aab65a48484502b332f" wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Assist Fallback Hotspot" password: "ZsasdasdHGP2234"
7. What you need to do is to find the corresponding lines in the code (they’re at the top) and add your Wi-Fi and Home Assistant credentials there.
8. Now, click “Save” and then “Install.” Choose “Manual download” and wait for the code to compile.
9. Once the compilation is complete, select the “Modern format” option to download the corresponding .bin file.
10. Connect the M5Stack Dial to your computer using a USB-C data cable via the port on the bottom of the device.
11. Next, go to the ESPHome web page and click “Connect.” In the popup window, select your board and click “Connect.”
12. Then click “Install” again and choose the .bin file you downloaded in step 9. Click “Install” once more to flash the firmware.
13. Return to Home Assistant and go to Settings > Devices & Services.
In most cases, your device should be automatically discovered and appear at the top, waiting for you to click “Configure.”
If not, click “Add Integration,” search for “ESPHome,” and enter your board’s IP address in the Host field. As always, it’s a good idea to assign a static IP to your device in your router settings to avoid connection issues later on.
14. To finish, go to Settings > Devices & Services > ESPHome, click the “Configure” link next to your device, and in the popup window, check the box that says, “Allow this device to make Home Assistant API calls,” then click “Submit.”
This will allow us to control devices directly from the screen.
Alright, you’ve successfully integrated the M5Stack Dial into Home Assistant as Mr. Avocado. Now let’s go over how to take full advantage of its features.
To protect the screen, we’ve added a customizable screensaver function. You can easily tweak it by accessing the entities exposed by the device in Home Assistant. Pay special attention to these three controls.
After a few seconds of inactivity (you can set the duration using the “Screen Saver” slider), the menu automatically hides, showing the clock and date with reduced screen brightness.
Additionally, if you enable “Auto Lock,” the screen will turn off completely a few seconds later (adjustable using the “Auto Lock” slider).
Of course, you can use our default Mr. Avocado background images — or replace them with your own.
Just update the image references in the first few lines of the code to point to your preferred files.
substitutions: # Device customization # Personalización del dispositivo background_image: https://aguacatec.es/wp-content/uploads/2025/02/mravocado_background_white.jpg background_image_saver: https://aguacatec.es/wp-content/uploads/2025/02/mravocado_bg_off.jpg background_image_device: https://aguacatec.es/wp-content/uploads/2025/02/mravocado_bg_device.jpeg
You can also customize which devices you want to control and the icons that represent them. Throughout the code, you’ll find examples for controlling different types of entities—lights, climate, vacuum, switches, humidifiers, and more. But if you understand the logic, you can control any Home Assistant device!
The example menu is optimized for 8 devices, but you can add more icons or even paginate the menu. Plus, if you press on any device, you’ll see advanced controls using the rotary encoder.
Since Mr. Avocado has a built-in buzzer, you can use it as an alarm or timer, making it sound whenever you want. Just use the “Alarm” button entity exposed by the device. You can also enable or disable a beep sound when navigating through the menu.
By the way, you can customize the alarm tone, as I explained in this article.
substitutions: # Sounds # Sonidos menu_sound: 'beep:d=64,o=5,b=255:c7' alarm_sound: 'xmen:d=4,o=6,b=200:16f#5,16g5,16b5,16d,c#,8b5,8f#5,p,16f#5,16g5,16b5,16d,c#,8b5,8g5,p,16f#5,16g5,16b5,16d,c#,8b5,8d,2p,8c#,8b5,2p'
Mr. Avocado also includes an NFC/RFID reader, although it’s not its strongest feature — I find the tag detection to be somewhat imprecise.
Still, if you want to use it, be sure to check out this article where I explain in detail how to create automations for each specific tag.
By the way, if you like it, you can also get our custom Mr. Avocado stand!
Thanks to the threaded mount built into the M5Stack Dial, attaching it is very easy. Just screw the device onto the stand’s base and route the USB-C power cable through the rear opening.
I recommend using a 90º angled USB-C cable to make positioning easier (although you can also rotate the screen to your liking).
Also, keep in mind the case has space at the top and an opening at the bottom—both intentionally designed so you can access the two expansion ports (I2C and GPIO) to add your favorite sensors.
⭐ If you have a 3D printer, you can download this stand I designed from our Patreon profile. If not, you can also purchase it from La R3D and have it shipped to your home!
Source: AguacaTEC
Author: TitoTB
Launched in October 2023 by M5Stack, the StickC Plus2 has quickly become a popular choice among makers, educators, and embedded system developers. It’s widely used in IoT projects, embedded systems, and cybersecurity applications. Many users even compare it with Flipper Zero as affordable alternatives for their similar roles in wireless testing and cybersecurity tools.
In this article, we’ll walk you through the StickC Plus2’s key features, programming options, and practical project ideas.
The M5StickC Plus2 is a compact, all-in-one ESP32-S3 development board designed for rapid prototyping and embedded applications. It integrates a 1.14” TFT display, IMU, microphone, infrared transmitter, and rechargeable battery — all within an ultra-portable form factor.
With support for Arduino, UIFlow2, and MicroPython, the StickC Plus2 is well-suited for a wide range of applications, from IoT devices and wearable interfaces to educational tools and lightweight cybersecurity projects.
M5StickC Plus2 builds on the StickC Plus with a new ESP32-PICO-V3-02 chip, increased battery capacity, improved Wi-Fi and infrared performance, and an upgraded CH9102 UART for more stable USB communication. However, the original StickC Plus has a built-in battery management IC that Plus2 lacks.
M5StickC Plus2 is a compact ESP32-S3 board ideal for developers looking to build custom IoT or cybersecurity tools. With third-party firmware (Like Bruce), it can emulate some Flipper Zero features like RF, IR, and USB HID.
Flipper Zero, on the other hand, is a ready-made hacking device with built-in wireless protocols, perfect for users who want an all-in-one security testing tool without programming.
Designed for the ESP32 microcontroller platform, Bruce merges Wi-Fi, RF (Radio Frequency), BLE (Bluetooth Low Energy), IR, and USB capabilities into a single, modular offensive toolkit tailored for red team activities and security research.
The easiest way to install Bruce on your M5Stack device is with M5Burner, M5Stack’s official firmware flashing tool.
If you haven’t downloaded it yet, click here to get started.
Open M5Burner > Select STICKC > Scroll down and you’ll see Bruce for StickC plus2 (or simply type “Bruce” into the search bar to locate it quickly.)
Click Download > Connect your device via a USB cable > Click Burn, select the corresponding USB port and default baud rate 1500000 > Hit Start to begin flashing
Note: Bruce is an open-source, community-developed firmware project not officially affiliated with M5Stack. A warning may pop up when you download the firmware.
Once you see the message "Burn successfully, click here to return", click it to finish, and press the reset button on your device once to let it start. Now you can begin exploring its features!
Note: If any issues occur during flashing, you may need to install a USB driver on your computer, or long-press the reset button on the main controller to enter download mode. For details, refer to the documentation page of your specific main controller device.
⚠️ Disclaimer: All use must comply with local laws. Use only for educational or testing purposes on your own devices.
1. Wi-Fi Attacks: Beacon flooding, deauthentication, EvilPortal phishing pages, EAPOL handshake capture, ARP spoofing, and wardriving GPS mapping.
The captive portal is one of Bruce’s most well-known features, commonly used for Wi-Fi phishing attacks. To launch it, go to Wi-Fi > Evil Portal, enter the target Wi-Fi name, and specify an IP address (or use the default). The phishing portal will begin broadcasting immediately.
On the screen, you'll see two URLs, these are used to view captured credentials and spoofed SSIDs. When a user attempts to log in through the fake portal, their credentials are captured by the device.
Public Wi-Fi networks present inherent security risks. It is advisable to avoid using such networks whenever possible. If usage is necessary, refrain from logging into personal accounts or transmitting sensitive information to minimize potential exposure.
2. Infrared & BadUSB: Offers IR transmission and reception, along with USB HID keyboard / mouse simulation on supported boards.
For example, navigate to the main menu, select the BadUSB section, choose a script, then run it to start USB keystroke injection. You can also watch this quick video by Pirata to see it in action: https://www.youtube.com/shorts/F_7QlGVx-XU
3. Sub-GHz & RF Hacking: Integrates modules like CC1101 and NRF24 for jamming, scanning, and replaying RF signals.
4. RFID / NFC Tools: Uses the PN532 chip to support Mifare Ultralight tag emulation, reading, writing, and P2P communication.
5. Bluetooth LE: Scanning, beacon broadcasting, and early BLE payload experiments were implemented as early as version 1.3.
6. Web Interface & Scripting: Features a full web UI for module control, LittleFS/SD card file management, and an integrated JavaScript scripting interface for automation and extension.
Bruce is licensed under the AGPL-3.0 for its firmware, emphasizing true software freedom. It supports a variety of ESP32 and ESP32-S3 development boards, except for the M5StickC Plus2, it's also suitable for: Cardputer, Core1 (Basic), Core2, CoreS3 / SE, StickC-Plus. It lowers the barrier for both beginners and experts looking to dive into embedded offensive tooling.
Meshtastic is an open-source, off-grid communication project that uses LoRa (Long Range) radio technology to enable secure, long-distance messaging without the internet or cellular networks. It leverages point-to-point(P2P) and mesh communication in areas with no internet or cellular access by forming a self-sufficient network of devices. Utilizing ultra-low power hardware and license-free frequency bands, it enables long-range message transmission, making it ideal for outdoor adventures, emergency rescue, rural connectivity, and low-power IoT applications.
In this guide, we'll walk you through building your very own Meshtastic LoRa node using Module LoRa868 and ESP32-based M5Stack Core series controllers.
What You’ll Need
The easiest way to install Meshtastic firmware on your M5Stack device is with M5Burner, a simple and free intuitive firmware flashing tool developed by M5Stack.
1. Locate the Meshtastic Firmware
Open M5Burner > Select ALL from the list in the left column > Input "Meshtastic" into the search bar and choose the firmware that match your device > Click Download.
If you haven’t installed M5Burner, click here and follow the installation steps to download it to your computer.
2. Connect Your Device and Start Flashing
– Connect the main controller to your computer via USB data cable > Click Burn > Select the correct USB port and set baud rate to 1500000 > Click Start to begin flashing.
– Wait for the message "Burn successfully" > Click "Click here to return" > When the Meshtastic logo "//\" appears on the screen, disconnect the device from your computer.
Note: if flashing fails, try installing the USB driver on your computer or long-press the reset button on the main controller to enter download mode. For more information, refer to your device’s documentation page.
After the firmware successfully downloaded on the device:
– Check M5Burner firmware for pin info and set DIP switches by following Module LoRa868 v1.2 DIP Switch Guide (for Core1/Core2: long pins 2,5,7 and short pin 1 ON).
– Remove red dust cap > Install antenna > Connect the module to the device.
– Power on the device, then the Meshtastic logo "//\" will appear.
Safety Warning
Do NOT connect or power on the device without installing the antenna, as this may cause permanent hardware damage!
1. Install the Meshtastic app on Your Phone
Download the Meshtastic app from the Google Play or Download APK from GitHub(for Android) or App Store(for IOS).
2. Pair the Device
Open the Meshtastic app and follow the on-screen instructions to pair your device via Bluetooth—nearby devices will be detected automatically. The iOS and Android Meshtastic apps offer similar features but have different interfaces, so setup steps and screenshots are shown separately for each platform.
3. Configure Device Settings
After pairing, you could set the Lora region, select the appropriate region (e.g., EU 868MHz), username in the app.
When running Meshtastic, the ESP32 can't use Wi-Fi and Bluetooth simultaneously. Bluetooth is enabled by default. If you turn on Wi-Fi, Bluetooth will be disabled. To re-enable it, connect the device to your computer via USB and use the Meshtastic Web Client in Chrome to disable Wi-Fi.
Step 4: Add GPS To Your Meshtastic Node
Core1/Core2 with LoRa868 v1.2 doesn’t have built-in GPS, but you can share your smartphone’s GPS location with the device. It's useful for team members to track each other during outdoor activities.
Step 5: Send and Receive Message
With other nodes show up in the list, you're connected to the mesh and can start messaging via the Meshtastic app.
Is Meshtastic Legal?
Yes, it’s legal. Meshtastic operates on license-free frequency bands such as 433 / 470 / 868 / 915 MHz which is in full compliance with FCC regulations.
How far does Meshtastic work?
The estimated range of this Meshtastic setup is around 4 km (2.49 miles). But the range between two Meshtastic nodes varies based on antenna setup, and environmental conditions. You may try moving the device around to test the range, check the signal to ensure stable connectivity.
For those exploring what hardware ChatGPT runs on, the traditional answer involves large-scale cloud infrastructure. However, with the OpenAI API and lightweight microcontrollers like the M5Stack ESP32-based AtomS3R, it’s now possible to build a compact, connected ChatGPT AI device. Paired with the Atomic Echo Base for audio I/O, this setup enables a tiny AI voice assistant capable of real-time voice interaction via Wi-Fi.
In this article, we’ll walk you through how to build your own AI-powered voice assistant using OpenAI—no coding required.
M5Stack AtomS3R
The M5Stack AtomS3R is a compact microcontroller powered by the ESP32-S3 chip, measuring just 24 × 24 mm. It supports Wi-Fi, Bluetooth, and offline voice wake-up, making it ideal for building portable AI voice assistant and IoT applications.
Required Hardware
M5Burner is a tool that enables creators to upload firmware and allows users to flash it onto M5Stack devices. If you haven’t downloaded it before, please select the version compatible with your operating system to proceed.
Software Version | Download Link |
---|---|
M5Burner_Windows | Download |
M5Burner_MacOS | Download |
M5Burner_Linux | Download |
1. Download the OpenAI Firmware
Double click M5Burner > Locate the OpenAI Voice Assistant for AtomS3R Firmware > Click Download.
2. Get Your OpenAI API Key
An API key is required after clicking Download. Visit OpenAI's platform > Complete registration and login > Review pricing for Realtime API and select the package > Navigate to the API Keys section and create a new key
3. Firmware Flashing
I. Input your Wi-Fi connection information and OpenAI API keys in the pop-up window > Hit Next
II. Connect your AtomS3R via USB-C > Press and hold the Reset button for ~2 seconds until the green LED turns on, then release to enter the download mode.
III. Select the correct COM port and click “Start” to start flashing.
Once completed, your device will reboot and connect to OpenAI for real-time voice interaction. You could speak directly to your assistant and receive instant responses.
If you prefer a more customizable approach to integrate the OpenAI into your project instead of downloading the prebuilt firmware from M5Burner, you could visit GitHub for the original source code.
May 9, 2025 – M5Stack, a leader in modular IoT and embedded development platforms, unveils Tab5, a next-generation 5-inch smart touch terminal powered by the advanced 400MHz ESP32-P4 dual-core RISC-V processor. Designed for industrial control, smart home hubs, edge intelligence, and IoT applications, Tab5 offers high performance, versatile interfaces, and seamless connectivity through Wi-Fi 6 and Bluetooth 5.2 in one compact tablet. Combining multimedia capabilities with modular expandability, Tab5 empowers developers to create flexible, scalable solutions for a wide range of industries.
At the heart of Tab5 is the ESP32-P4 dual-core RISC-V processor, running at 400MHz and backed by 32MB of PSRAM and 16MB Flash, delivering robust performance for embedded applications and edge computing. While the ESP32-P4 handles processing tasks, wireless connectivity is provided by the onboard ESP32-C6-MINI-1U module, which supports Wi-Fi 6 and Bluetooth 5.2. Equipped with 3D internal antennas, this module ensures high-throughput, low-latency communication across a wide range of IoT scenarios.
Tab5 features a 5-inch IPS touchscreen with a 1280×720 resolution and a GT911 capacitive multi-touch panel, providing responsive touch interaction and high-resolution visuals for an intuitive user experience. Built-in 2MP camera (1600×1200 resolution) and dual microphones with a speaker enable intelligent interactions such as facial recognition, image processing, and voice commands. The camera interface uses MIPI-CSI to enable HD video capture and edge AI tasks such as object tracking.
Additional onboard features including RESET/BOOT buttons, a BMI270 six-axis IMU for motion sensing, and a Micro SD card slot provide enhanced control, motion tracking, and local data storage—empowering developers to create multi-modal edge applications.
Tab5 is designed for flexibility and scalability with a comprehensive suite of I/O interfaces:
These interfaces ensure seamless integration with the M5Stack hardware ecosystem, enabling plug-and-play expansion through a wide range of functional modules.
Tab5 is available in two variants:
Both models support external NP-F550 lithium batteries, offering flexible deployment options for fixed installations or mobile applications.
Tab5 is engineered to address diverse application scenarios across industries. In industrial settings, it functions as an effective HMI for control panels and data visualization. Its multi-protocol wireless support makes it ideal for smart home hubs and gateways. In education and maker spaces, it supports UIFlow 2.0, Arduino IDE, ESP-IDF and PlatformIO, enabling flexibility for both beginners and advanced developers. With a built-in camera and dual microphones, it also enables AI vision and voice interaction for use cases like smart kiosks and voice-controlled terminals.
“Designed for seamless development, the Tab5 delivers innovation and usability, empowering creators to turn ideas into reality,” said Jimmy Lai, Founder and CEO of M5Stack.
Built to Expand
To further enhance Tab5’s utility, a dedicated keyboard accessory is in development — stay tuned for the full reveal. Designed for seamless integration with the M5Stack ecosystem, Tab5 supports a broad range of functional modules, offering developers the flexibility to tailor solutions across diverse application needs.
Tab5 is now available through M5Stack’s official store and global distributors. For more details, please visit www.m5stack.com.
Today, we’re checking out another great device for running our assistant with excellent performance, and learning how to activate Assist using the M5Stack CoreS3SE.
Index
Well, let's start with the basics. M5Stack is the brand behind well-known devices like the Atom Echo. As I mentioned before, this was the first external device used to interact with Home Assistant. I’m convinced that if you're getting into the world of local assistants, you’re already familiar with it.
But Atom Echo isn’t the only option. M5Stack also makes a variety of ESP-based devices that are easy to integrate with Home Assistant. Think of them like custom ESPHome builds, but without the hassle of soldering, wiring, or configuring components from scratch.
Now that the introduction is done, today I want to introduce you to the M5Stack CoreS3SE, a device that’ll definitely remind you of the ESP32-S3-BOX-3 we looked at recently, as we can also activate Assist with the M5Stack CoreS3SE.
Since the goal of this guide is to activate Assist using the M5Stack CoreS3SE, I’ll go over the differences and similarities I’ve found between the two devices.
Prerequisites
To activate Assist on the M5Stack CoreS3SE, you’ll need:
🥑 If you're setting up Assist, I highly recommend checking out the workshop from the academy to get the most out of it!
Follow these preparation steps to get your M5Stack CoreS3SE up and running:
1. In Home Assistant, go to your ESPHome add-on, click on “New Device”, then “Continue”.
2. Give your device a name (e.g., “Assist”) and click “Next”.
3. For the device type, select “ESP32-S3”. You’ll see a new block for your device appear in the background.
4. Click “Skip”, then click “Edit” on your device’s card. Copy the code that appears and keep it handy — you’ll need part of it later.
5. Head over to the GitHub page linked in the guide, copy the provided code, and replace the original ESPHome code with it.
6. Important: This new code doesn’t include your Wi-Fi or Home Assistant credentials, so you’ll need to manually add them. Specifically, look for the lines from the original code that you copied in step 4 and insert them into the new code.
# Enable Home Assistant API api: encryption: key: "bg6hash6sjdjsdjk02hh0qnQeYVwm123vdfKE8BP5" ota: - platform: esphome password: "asddasda27aab65a48484502b332f" wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "Assist Fallback Hotspot" password: "ZsasdasdHGP2234"
7. What you need to do is find the corresponding lines in the code (it's at the beginning) and add the corresponding information . This code snippet would look like this:
# Enable Home Assistant API api: encryption: key: "1fPr5BBxCfGiLLPgu/OEILB1T4XUdXN4Sh2pic4mgQk=" on_client_connected: - script.execute: draw_display on_client_disconnected: - script.execute: draw_display ota: - platform: esphome password: "a048862eecd273b682fde5d1a93acc36" wifi: ssid: !secret wifi_ssid password: !secret wifi_password # Enable fallback hotspot (captive portal) in case wifi connection fails ap: ssid: "M5Stack-Cores3Se" password: "uCh6BjJ34Tnl" on_connect: - script.execute: draw_display - delay: 5s # Gives time for improv results to be transmitted on_disconnect: - script.execute: draw_display
8. Now click “Save” and then “Install.” Select “Manual download” and wait for the code to compile. It might take a while, so feel free to do something else in the meantime.
9. Once it’s finished, choose the “Modern format” option to download the corresponding .bin file.
10. Connect the M5Stack CoreS3SE to your computer using a USB-C data cable, plugging it into the port on the left side of the device.
11. Go to the ESPHome page and click “Connect.” In the pop-up window, select your device and click “Connect” again.
12. Click “Install” and select the .bin file you downloaded in step 9. Then click “Install” again to upload it to the device.
13. You may see a message saying “HA not found.” Don’t worry — this is normal. In Home Assistant, go to Settings > Devices & Services, where the device should appear as discovered. Click “Configure” and then “Submit.”
14. That’s it! You can now activate Assist with the M5Stack CoreS3SE. By default, just say “Ok, Nabu,” and it’ll respond using your preferred assistant settings.
Personalized support
As I mentioned in the comparison, the M5Stack CoreS3SE doesn't come with a standard stand, which gives us the opportunity to create one to our liking. For example, this time I wanted to create a simple and elegant stand, taking advantage of the black frame.
⭐ If you have a 3D printer, you can download this stand I designed for FREE from our Patreon page.