News
M5Stack News.

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, CardputerAdv is the ultimate pocketsized development companion.

Highlights of CardputerAdv

  • Upgraded Core – Stamp‑S3A module, offering robust computing power with integrated Wi‑Fi + BLE connectivity.
  • Enhanced Audio – ES8311 audio codec, high‑SNR MEMS microphone, NS4150B amplifier, 1W speaker, plus 3.5mm headphone jack for external audio devices.
  • Rich Interaction –56key compact keyboard with improved tactile feel (key actuation force reduced from 260gf to 160gf) and 1.14 TFT LCD for clear display and comfortable input.
  • Smart Sensing – Built‑in BMI270 6‑axis IMU and IR transmitter enable motion tracking and remote control functions.
  • Connectivity & Expansion – Grove‑compatible HY2.0‑4P port, EXT 2.54mm 14‑pin header, microSD card slot, plus compatibility with the optional Cap LoRa868 accessory for Meshtastic communication.
  • Power & Portability – 1750mAh rechargeable lithium‑ion battery, magnetic base for metal surfaces, LEGO‑hole‑compatible mounting design.
  • Multi‑Platform Ready – Supports UIFlow2, Arduino IDE, ESP‑IDF, and PlatformIO for flexible development workflows.

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 userdriven 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.

    2025-09-05

    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.

     

    Why LVGL?

    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:

    • Rich widget library: Buttons, sliders, progress bars, charts, and more
    • High-performance rendering: Adaptable to different display memory sizes and refresh rates
    • Flexible extensibility: Animations, themes, event callbacks, and advanced custom styling

    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:

    • Controller support: Core2, Tough, Dial, CoreS3, Tab5, and more ready to go
    • Standard widgets: Buttons, labels, images available instantly in the visual editor
    • Drag, drop, adjust: Direct manipulation of LVGL elements with live attribute tweaks
    • Font optimization: One-click custom font generation & download for better localization
    • Full Python API access: Call LVGL functions directly in "Code Mode" to build advanced logic

    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()
    

     

    4. Run and Debug

    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:

    • More widget support: Charts, animations, themes, advanced style management
    • Improved visual designer: What You See Is What You Get layout editing (WYSIWYG)
    • Wider hardware coverage: More display modules and input peripherals like Module Display and Unit CardKB

    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.

    2025-09-03

    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

    • Home Assistant installed and running
    • A stable Wi-Fi connection
    • A USB data cable to connect with computer

    1. Environment Setup

    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:

    • Go to SettingsAdd-onsAdd-on Store
    • Search for and install the ESPHome add-on
    • After installation, enable Show in sidebar for quick access

    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.

    3. Firmware Setup & Compilation

    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.

    4. Flash the Firmware to M5Atom Lite

    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.

    5. Add the Device to Home Assistant

    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 ESPHomehit 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.

      7. Add Atom RGB Light to Your Dashboard

      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:

        • Single Click → RGB light toggles pink
        • Long Press (2+ seconds) → RGB light switches to green
        • Light state updates in real-time and can also be controlled via the Home Assistant dashboard

        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.

        2025-08-06

        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
        • Mr. Avocado
        • Prerequisites
        • Configuration in ESPHome
        • Device Customization
        • Screensaver Control
        • Customize the Wallpaper
        • Menu Configuration
        • Sounds and Alarm Clock
        • NFC Reader
        • Mr. Avocado Support

        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:

        • M5StampS3 board, a microcontroller based on the ESP32-S3.
        • 1.28-inch circular TFT touchscreen display.
        • Rotary encoder surrounding the screen, with a push button at the bottom.
        • RFID and card reader (13.56MHz), although I find it to be not very precise.
        • Buzzer for sound output.
        • Battery connection port with built-in charging circuitry.
        • Two expansion ports for I2C and GPIO.
          m5stack-core-dial

        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:

        • Lock screen that shows the current date and time after a few seconds of inactivity
        • Automatic screen-off to save power
        • A streamlined interface to control up to 8 devices
        • Alarm clock with a custom ringtone, configurable via Home Assistant
        • NFC/RFID reader for tags and cards
        • And thanks to the ESP32-S3, it can also act as a Bluetooth Proxy to serve as a presence sensor with Bermuda.

        Prerequisites

        To integrate the M5Dial into Home Assistant, you’ll need:

        • Obviously, an M5Dial
        • ESPHome installed and set up in Home Assistant
        • A USB-C data cable to power and program the board (charging-only cables won’t work for installing the software)
        • Optionally, the custom stand we designed to turn it into Mr. Avocado

        🥑 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!

        ESPHome Setup

        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.
         

        Device Customization

        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.

        Screensaver Control

        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.

        m5stack-home-assistant-screensaver-setting

        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).

        Customize the Wallpaper

        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
        
            

        Menu Configuration

        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.

        Sounds and Alarm Clock

        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.

        m5stack-home-assistant-sound-setting

        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'
        
            

        NFC Reader

        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.

        Mr. Avocado Stand

        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!

        m5stack-dial-home-assistant-integrations

        Source: AguacaTEC

        Author: TitoTB

        2025-07-16

        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.
        product image of M5StickC Plus2

        In this article, we’ll walk you through the StickC Plus2’s key features, programming options, and practical project ideas.

        What’s StickC Plus2?

        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 Plu2 vs StickC Plus

        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.

        The differences between M5StickC Plus and Plus 2
        Additionally, the two devices differ slightly in their power-on and power-off behavior.

        M5StickC vs Flipper Zero

        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.

        Getting Started with Bruce Firmware on M5StickC Plus2

        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.

        bruce firmware for M5Stack

        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.

        1.    Find the Bruce Firmware on M5Burner

        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.)

        Search result of Bruce in M5Burner

        2.  Download the Bruce Firmware to M5StickC Plus2

        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 flashinghow to burn bruce to stickC plus2

        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.

        3.  Exploring the Bruce Firmware Functions on the device

        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!image shown bruce is burnt successfully on stickC Plus2

        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.

        steps to launch captive portal on StickC Plus2

        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.

        StickC Plus2 screen showing URLs

        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

        BadUSB running on StickC Plus2

        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.

        Related Topic: Is Bruce Firmware Legal?

        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.

        2025-07-04

        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.

        the topology of meshtastic network

        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

        Step 1: Flashing the Meshtastic Firmware

        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.

        Find meshtastic firmware in M5Burner

        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.

          burn meshtastic firmware from M5Burner

               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.

          Step 2: Configure the Module LoRa868 v1.2 and Connect Antenna

          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).

          checking pin info and set DIP switch on M5 Core

               Remove red dust cap > Install antenna > Connect the module to the device.

               Power on the device, then the Meshtastic logo "//\" will appear.

          meshtastic logo showing on M5 core screen

          Safety Warning

          Do NOT connect or power on the device without installing the antenna, as this may cause permanent hardware damage!

          Step 3: Install the Meshtastic App on your iOS or Android device

          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.

          iOS Setup

             

          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.

          iOS Setup

           

          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. 

          iOS Chat

           

          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.

          2025-06-09

          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.

          a ChatGPT Voice Assistant built on M5 AtomS3R and Atomic Echo Base

          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

          Download OpenAI Voice Assistant for AtomS3R Firmware from M5Burner

          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.

          Find the "OpenAI Voice Assistant for AtomS3R" Firmware in M5Burner

          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

          get OpenAI key on OpenAI platform

          3.   Firmware Flashing

          I.              Input your Wi-Fi connection information and OpenAI API keys in the pop-up window > Hit Next

          input Wi-Fi password and OpenAI key to burn

          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.

          start flashing
          image showing burn successfully

          Talking to Your AI Assistant

          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.

          the atom voice assistant in connecting and ready status

          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.

          2025-05-22

          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.

          High-Performance Core for Embedded Intelligence

          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.

          Immersive Display and Interactive Design

          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.

          Rich Interfaces and Modular Expandability

          Tab5 is designed for flexibility and scalability with a comprehensive suite of I/O interfaces:

          • RS485, Grove, M5Bus, GPIO.EXT
          • USB-A (Host) and USB-C (USB 2.0 OTG)
          • 3.5mm audio jack
          • Support for UART, I2C, and ADC protocols
          • STAMP solder pads for easy integration of communication modules like Cat.M, NB-IoT, or LoRaWAN

          These interfaces ensure seamless integration with the M5Stack hardware ecosystem, enabling plug-and-play expansion through a wide range of functional modules.

          Battery Support & Deployment Options

          Tab5 is available in two variants:

          • Tab5 Kit – Includes a removable 2S NP-F550 7.4V 2000mAh lithium-ion battery
          • Tab5 – Battery not included

          Both models support external NP-F550 lithium batteries, offering flexible deployment options for fixed installations or mobile applications.

          Versatile Applications Across Industries

          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.

          2025-05-09

          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

          • M5Stack CoreS3SE
          • M5Stack CoreS3SE vs ESP32-S3-BOX-3
          • Prerequisites
          • Configuring the M5Stack CoreS3SE
          • Custom support

          M5Stack CoreS3SE

          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.

          M5Stack CoreS3SE vs ESP32-S3-BOX-3

          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.

          • Both run on the ESP32-S3 chip and are fully compatible with ESPHome.
          • Both come with two microphones for better voice recognition.
          • Both have a built-in speaker and a touchscreen display.
          • The setup process is nearly identical for both.
          • The CoreS3SE is about €10 cheaper than the BOX-3B.
          • The CoreS3SE has a black frame, while the BOX-3B comes in white.
          • The CoreS3SE is a bit smaller and more square in shape compared to the BOX-3B.
          • The M5Stack CoreS3SE doesn’t come with a stand or USB-C cable by default, whereas the ESP32-S3-BOX-3B does (which we’ll actually turn to our advantage, as you’ll see at the end of this article).

          Prerequisites


          To activate Assist on the M5Stack CoreS3SE, you’ll need:

          • You have set up Assist in Home Assistant.
          • A M5Stack CoreS3SE device.
          • A USB-C data cable to power the DATA board (with a charging cable you will not be able to install the software) .

          🥑 If you're setting up Assist, I highly recommend checking out the workshop from the academy to get the most out of it!

          Configuring the M5Stack CoreS3SE

          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

           

          Source: AguacaTEC
          Author: TitoTB
          2025-04-30

          If you've ever needed to update firmware on an STM32-based device, you know the struggle—setting up an debugger, dealing with drivers, and ensuring proper connections. What if you could do it all without a dedicated programmer, using just your M5Stack Core2 or CoreS3? Enter M5 DAPLink, a powerful solution that transforms your M5 device into a standalone offline programmer.

          Why Use M5 DAPLink?

          Imagine being able to flash firmware anywhere, anytime—without needing a PC connection or extra hardware. Whether you're in the field, a classroom, or a factory line, M5 DAPLink makes firmware updates seamless. Just load your firmware onto a MicroSD card (for Core2) or a virtual USB drive (for CoreS3), and you're ready to go!

          1. Preparations

          Required hardware:
          • Core2 / CoreS3
          • Module Bus
          • MicroSD card
          • Card reader
          • Male-to-female Dupont wires
          • Female-to-female Dupont wires

          2. Flashing the DAPLink Firmware

          M5Burner
          Download the M5Burner firmware flashing tool for your operating system from the links below. Extract and launch the application.

          Software Version

          Download Link

          M5Burner_Windows

           Download

          M5Burner_MacOS

           Download

          M5Burner_Linux

           Download

          Open the burner tool, select the corresponding device type from the left menu, and download the matching firmware for your device.

          CoreS3 DAPLink

          Download the firmware for CoreS3: CoreS3 → CoreS3 DAPLink. Refer to the CoreS3 documentation to learn how to enter download mode. Once the device is detected by your computer, proceed with flashing.

          CoreS2 DAPLink

          Download the firmware for Core2: Core2 → Core2 DAPLink. Refer to the Core2 documentation to install the required USB driver. Once the device is detected, proceed with flashing.

           

          3. Importing Flashing Algorithms and Firmware

          Download the algorithm package below. This package, along with the firmware, is imported into the host device and used to match different chip models during flashing. Some algorithms are preloaded in the firmware, while manual import allows for additional algorithm support. Import methods vary by device—refer to the details below.

          algorithm


          • Virtual USB Drive Import
          This method is currently only supported for CoreS3.
          Extract the algorithm package and copy it to the CoreS3 virtual USB drive. Create a program folder in the root directory to store the firmware files (hex/bin) for flashing.

          • MicroSD Import
          This method is currently only supported for Core2.
          Extract the algorithm package and copy it to the MicroSD card. Create a program folder in the root directory to store the firmware files (hex/bin). The directory structure is the same as the CoreS3 virtual USB method.

          • Web Import
          This method works for both Core2 and CoreS3. Imported data is automatically saved to the device's flash storage partition. (Note: For Core2 with an SD card, files are stored on the SD card. For CoreS3, safely eject the virtual USB drive before importing via the web.)
          Power on the device to enable its AP hotspot. Connect your computer to the hotspot and visit 192.168.4.1 in a browser. Click Program to navigate to the file upload page, then upload the algorithm and firmware files.

          4. Device Connection

          The DAPLink pin mappings for the firmware are as follows:

          For example, to update the firmware of a Unit EXT.IO2, locate the programming pads after opening the device casing and connect them according to the pin mapping above. If contact is unstable, tilt the Dupont wire pins to ensure proper connection.

          5. Starting the Flashing Process

          After importing the algorithms and firmware, the device will display available options upon startup. Select the algorithm and firmware matching your target device. Click Idle, then Busy to begin flashing. (Note: Some chips, like STM32F0xx series, may require pressing Busy twice.)

          6. Using with Module Bus

          For daily DAPLink debugging, the Module Bus is highly recommended for easier wiring. It extends the MBus interface to the board's edge and includes two sets of 2.54-15P 90° headers for seamless Dupont wire connections.

          Why M5 DAPLink is a Game-Changer

          • No extra hardware needed – Your M5 device becomes a portable STM32 programmer.
          • Works offline – No need for a PC once set up.
          • Flexible import methods – USB, SD card, or web upload.
          • Perfect for fieldwork and education – Quick firmware updates anywhere.

          With M5 DAPLink, you turn ideas into reality faster—no hassle, no complicated setups. Ready to give it a try? Download the firmware today and start flashing like a pro!

          2025-04-15

          In this article, we will integrate the M5Stack Air Quality Kit with Home Assistant to monitor air quality.

          Index

          • Air Quality Sensor
          • M5Stack Air Quality Kit
          • Prerequisites
          • Configuration in ESPHome
          • Device Information

          Air Quality Sensor

          While air quality may not be a concern for everyone, those of us living in large cities or near industrial areas are increasingly worried about the air we breathe at home. This concern is not unfounded—numerous studies have shown that long-term exposure to pollutants can lead to respiratory diseases such as asthma and bronchitis. Over time, it can also shorten lifespan and increase the risk of chronic illnesses like lung cancer.

          From this perspective, home automation can help mitigate these effects by monitoring air quality, sending alerts when pollution levels rise, or even activating ventilation or air purification systems. If you're concerned about overall environmental pollution, you can refer to indexes like the World Air Quality Index.

          However, whether you distrust external data (for instance, if monitoring stations are conveniently placed in green zones) or simply want to measure the specific data in your own home, an air quality sensor is essential. When it comes to finding a sensor that is comprehensive, integrable, and reasonably priced, debates always arise.

          M5Stack Air Quality Kit

          M5Stack is a well-known brand that offers devices like the M5Stack CoreS3SE and the historically significant Atom Echo. In this case, we’ll integrate the M5Stack Air Quality Kit with Home Assistant. This device is based on the ESP32S3FN8 chip and can measure CO2, VOCs, PM1.0, PM2.5, PM4, and PM10 particles, along with temperature and humidity (though some reviews suggest the accuracy of the latter two may be questionable). It also features an e-ink display and a built-in battery.

           

          By the way, while this article focuses on integrating the M5Stack  Air Quality Kit with Home Assistant, you can also use it directly with your mobile device to monitor its readings. The video below explains the setup process.

          Prerequisites

          To integrate the M5Stack Air Quality Kit into Home Assistant, you will need:

          • M5Stack Air Quality Kit 
          • ESPHome installed in Home Assistant.
          • A USB-C cable to power the DATA board (a charging-only cable will not allow software installation).

          🥑 If you’re new to ESPHome, I recommend checking out the Academy workshop to get the most of it!

          Configuration in ESPHome

          Follow these steps to integrate the M5Stack Air Quality Kit into Home Assistant:

          1. In Home Assistant, go to your ESPHome plugin, click “New Device,” and then click “Continue.”

          2. Name your device (e.g., “ Air Quality Kit”) 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” on the device block above. Copy the code that appears and save it, as you will need some parts of it later.

          5. Copy the following code (which I found on reddit and edited slightly) and replace the above code in ESPHome.

          substitutions:
            devicename: lounge-airq
            friendlyname: Lounge AirQ
            location: Lounge
            sensor_interval: 10s
          
          esphome:
            name: ${devicename}
            friendly_name: ${friendlyname}
            area: ${location}
            platformio_options:
              board_build.mcu: esp32s3
              board_build.name: "M5Stack StampS3"
              board_build.upload.flash_size: 8MB
              board_build.upload.maximum_size: 8388608
              board_build.vendor: M5Stack
            on_boot:
            - priority: 800
              then:
                - output.turn_on: enable
            - priority: 800
              then:
                - pcf8563.read_time
          
          esp32:
            board: esp32-s3-devkitc-1 #m5stack-stamps3
            variant: esp32s3
            framework:
              type: arduino
          
          # Enable logging
          logger:
          
          # Enable Home Assistant API
          api:
            encryption:
              key: REDACTED
          
          ota:
            - platform: esphome
              password: REDACTED
          
          wifi:
            ssid: !secret wifi_ssid
            password: !secret wifi_password
          
            # Enable fallback hotspot (captive portal) in case wifi connection fails
            ap:
              ssid: "Lounge-Airq Fallback Hotspot"
              password: REDACTED
          
          captive_portal:
          
          output:
            - platform: gpio
              pin: GPIO10
              id: enable
          
          web_server:
             port: 80
             include_internal: true
          
          i2c:
            sda: GPIO11
            scl: GPIO12
            scan: true
            frequency: 100kHz
            id: bus_a
          
          spi:
            clk_pin: GPIO05
            mosi_pin: GPIO06
          
          time:
            - platform: pcf8563
              address: 0x51
              update_interval: 10min
            - platform: homeassistant
              id: esptime
          
          light:
            - platform: esp32_rmt_led_strip
              rgb_order: GRB
              pin: GPIO21
              num_leds: 1
              rmt_channel: 0
              chipset: SK6812
              name: "LED"
              restore_mode: ALWAYS_OFF
              id: id_led
          
          text_sensor:
            - platform: wifi_info
              ip_address:
                name: IP
              ssid:
                name: SSID
              bssid:
                name: BSSID
              mac_address:
                name: MAC
              dns_address:
                name: DNS
          
            - platform: template
              name: "VOC IAQ Classification"
              id: iaq_voc
              icon: "mdi:checkbox-marked-circle-outline"
              lambda: |-
                if (int(id(voc).state) < 100.0) {
                  return {"Great"};
                }
                else if (int(id(voc).state) <= 200.0) {
                  return {"Good"};
                }
                else if (int(id(voc).state) <= 300.0) {
                  return {"Light"};
                }
                else if (int(id(voc).state) <= 400.0) {
                  return {"Moderate"};
                }
                else if (int(id(voc).state) <= 500.0) {
                  return {"Heavy"};
                }
                else {
                  return {"unknown"};
                }
          
            - platform: template
              name: "NOX IAQ Classification"
              id: iaq_nox
              icon: "mdi:checkbox-marked-circle-outline"
              lambda: |-
                if (int(id(nox).state) < 100.0) {
                  return {"Great"};
                }
                else if (int(id(nox).state) <= 200.0) {
                  return {"Good"};
                }
                else if (int(id(nox).state) <= 300.0) {
                  return {"Light"};
                }
                else if (int(id(nox).state) <= 400.0) {
                  return {"Moderate"};
                }
                else if (int(id(nox).state) <= 500.0) {
                  return {"Heavy"};
                }
                else {
                  return {"unknown"};
                }
          
          sensor:
            - platform: scd4x
              co2:
                name: CO2
                id: CO2
                filters:
                  - lambda: |-
                      float MIN_VALUE = 300.0;
                      float MAX_VALUE = 2500.0;
                      if (MIN_VALUE <= x && x <= MAX_VALUE) return x;
                      else return {};         
              temperature:
                name: CO2 Temperature
                id: CO2_temperature
                filters:
                  - lambda: |-
                      float MIN_VALUE = -40.0;
                      float MAX_VALUE = 100.0;
                      if (MIN_VALUE <= x && x <= MAX_VALUE) return x;
                      else return {};      
              humidity:
                name: CO2 Humidity
                id: CO2_humidity
                filters:
                  - lambda: |-
                      float MIN_VALUE = 0.0;
                      float MAX_VALUE = 100.0;
                      if (MIN_VALUE <= x && x <= MAX_VALUE) return x;
                      else return {};      
              altitude_compensation: 0m
              address: 0x62
              update_interval: $sensor_interval
          
            - platform: wifi_signal # Reports the WiFi signal strength/RSSI in dB
              name: "Wifi Signal dB"
              id: wifi_signal_db
              update_interval: 60s
              entity_category: "diagnostic"
          
            - platform: sen5x
              id: sen55
              pm_1_0:
                name: "PM 1"
                id: PM1_0
                accuracy_decimals: 2
              pm_2_5:
                name: "PM 2.5"
                id: PM2_5
                accuracy_decimals: 2
              pm_4_0:
                name: "PM 4"
                id: PM4_0
                accuracy_decimals: 2
              pm_10_0:
                name: "PM 10"
                id: PM10_0
                accuracy_decimals: 2
              temperature:
                name: "SEN55 Temperature"
                id: sen55_temperature
                accuracy_decimals: 2
              humidity:
                name: "SEN55 Humidity"
                id: sen55_humidity
                accuracy_decimals: 2
              voc:
                name: VOC
                id: voc
                accuracy_decimals: 2
                algorithm_tuning:
                  index_offset: 100
                  learning_time_offset_hours: 12
                  learning_time_gain_hours: 12
                  gating_max_duration_minutes: 180
                  std_initial: 50
                  gain_factor: 230
              nox:
                name: NOX
                id: nox
                accuracy_decimals: 2      
                algorithm_tuning:
                  index_offset: 100
                  learning_time_offset_hours: 12
                  learning_time_gain_hours: 12
                  gating_max_duration_minutes: 180
                  std_initial: 50
                  gain_factor: 230
              temperature_compensation:
                offset: 0
                normalized_offset_slope: 0
                time_constant: 0
              acceleration_mode: low
              store_baseline: true
              address: 0x69
              update_interval: $sensor_interval
          
            - platform: template
              name: Temperature
              id: temperature
              lambda: |-
                return (( id(sen55_temperature).state + id(CO2_temperature).state ) / 2 ) - id(temperature_offset).state;
              unit_of_measurement: "°C"
              icon: "mdi:thermometer"
              device_class: "temperature"
              state_class: "measurement"
              update_interval: $sensor_interval
              accuracy_decimals: 2
          
            - platform: template
              name: Humidity
              id: humidity
              lambda: |-
                return (( id(sen55_humidity).state + id(CO2_humidity).state ) / 2) - id(humidity_offset).state;
              unit_of_measurement: "%"
              icon: "mdi:water-percent"
              device_class: "humidity"
              state_class: "measurement"    
              update_interval: $sensor_interval
              accuracy_decimals: 2
          
          binary_sensor:
            - platform: gpio
              name: Button A
              pin:
                number: GPIO0
                ignore_strapping_warning: true
                mode:
                  input: true
                inverted: true
              on_press:
                then:
                  - component.update: disp
                
            - platform: gpio
              pin:
                number: GPIO08
                mode:
                  input: true
                  pullup: true
                inverted: true
              name: Button B
              
            - platform: gpio
              pin:
                number: GPIO46
                ignore_strapping_warning: true
              name: Button Hold
          
            - platform: gpio
              pin: 
                number: GPIO42
              name: Button Power
          
          button:
            - platform: restart
              name: Restart
              
            - platform: template
              name: "CO2 Force Manual Calibration"
              entity_category: "config"
              on_press:
                then:
                  - scd4x.perform_forced_calibration:
                      value: !lambda 'return id(co2_cal).state;'
          
            - platform: template
              name: "SEN55 Force Manual Clean"
              entity_category: "config"
              on_press:
                then:
                  - sen5x.start_fan_autoclean: sen55
          
          number:
            - platform: template
              name: "CO2 Calibration Value"
              optimistic: true
              min_value: 400
              max_value: 1000
              step: 5
              id: co2_cal
              icon: "mdi:molecule-co2"
              entity_category: "config"
          
            - platform: template
              name: Humidity Offset
              id: humidity_offset
              restore_value: true
              initial_value: 0.0
              min_value: -70.0
              max_value: 70.0
              entity_category: "CONFIG"
              unit_of_measurement: "%"
              optimistic: true
              update_interval: never
              step: 0.1
              mode: box
          
            - platform: template
              name: Temperature Offset
              id: temperature_offset
              restore_value: true
              initial_value: 0.0
              min_value: -70.0
              max_value: 70.0
              entity_category: "CONFIG"
              unit_of_measurement: "°C"
              optimistic: true
              update_interval: never
              step: 0.1
              mode: box
          
          display:
            - platform: waveshare_epaper
              model: 1.54inv2
              id: disp
              cs_pin: GPIO04
              dc_pin: GPIO03
              reset_pin: GPIO02
              busy_pin:
                number: GPIO01
                inverted: false
              full_update_every: 6
              reset_duration: 2ms
              update_interval: 10s
              lambda: |-
                auto now = id(esptime).now().strftime("%H:%M %d/%m/%y").c_str();
                it.printf(it.get_width()/2, 0, id(f12), TextAlign::TOP_CENTER, "${location} @ %s", now);
          
                it.print(0, 23, id(f24), TextAlign::TOP_LEFT, "PM 1: "); 
                it.print(0, 48, id(f24), TextAlign::TOP_LEFT, "PM 2.5: "); 
                it.print(0, 73, id(f24), TextAlign::TOP_LEFT, "PM 4: "); 
                it.print(0, 98, id(f24), TextAlign::TOP_LEFT, "PM 10: "); 
                it.print(0, 123, id(f24), TextAlign::TOP_LEFT, "CO2: "); 
                it.print(0, 148, id(f24), TextAlign::TOP_LEFT, "VOC: ");
                it.print(0, 173, id(f24), TextAlign::TOP_LEFT, "NOX: ");
          
                it.printf(it.get_width(), 23, id(f24), TextAlign::TOP_RIGHT, "%.0f", id(PM1_0).state);
                it.printf(it.get_width(), 48, id(f24), TextAlign::TOP_RIGHT, "%.0f", id(PM2_5).state);
                it.printf(it.get_width(), 73, id(f24), TextAlign::TOP_RIGHT, "%.0f", id(PM4_0).state); 
                it.printf(it.get_width(), 98, id(f24), TextAlign::TOP_RIGHT, "%.0f", id(PM10_0).state);
                it.printf(it.get_width(), 123, id(f24), TextAlign::TOP_RIGHT, "%.0fppm", id(CO2).state);
                it.printf(it.get_width(), 148, id(f24), TextAlign::TOP_RIGHT, "%.0f", id(voc).state);
                it.printf(it.get_width(), 173, id(f24), TextAlign::TOP_RIGHT, "%.0f", id(nox).state);
          
          font:
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
              id: f16
              size: 16
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
              id: f18
              size: 18
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              id: f12
              size: 12
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              id: f24
              size: 24
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              id: f36
              size: 36
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              id: f48
              size: 48
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              id: f32
              size: 32
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
          
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 500
              id: f64
              size: 64
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
          
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 800
              id: f64b
              size: 64
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
          
            - file:
                type: gfonts
                family: Noto Sans Display
                weight: 800
              id: f55b
              size: 55
              glyphs: ['&', '@', '!', ',', '.', '"', '%', '(', ')', '+', '-', '_', ':', '°', '0',
                  '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E',
                  'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
                  'T', 'U', 'V', 'W', 'X', 'Y', 'Z', ' ', 'a', 'b', 'c', 'd', 'e', 'f',
                  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
                  'u', 'v', 'w', 'x', 'y', 'z','å', 'ä', 'ö', '/', 'µ', '³', '’']
          
            - file: 
                type: gfonts
                family: Material Symbols Sharp
                weight: 400
              id: font_weather_icons_xsmall
              size: 20
              glyphs:
                - "\U0000F159" # clear-night
                - "\U0000F15B" # cloudy
                - "\U0000F172" # partlycloudy
                - "\U0000E818" # fog      
                - "\U0000F67F" # hail
                - "\U0000EBDB" # lightning, lightning-rainy
                - "\U0000F61F" # pouring
                - "\U0000F61E" # rainy
                - "\U0000F61C" # snowy
                - "\U0000F61D" # snowy-rainy
                - "\U0000E81A" # sunny
                - "\U0000EFD8" # windy, windy-variant
                - "\U0000F7F3" # exceptional
            - file: 
                type: gfonts
                family: Material Symbols Sharp
                weight: 400
              id: font_weather_icons_small
              size: 32
              glyphs:
                - "\U0000F159" # clear-night
                - "\U0000F15B" # cloudy
                - "\U0000F172" # partlycloudy
                - "\U0000E818" # fog      
                - "\U0000F67F" # hail
                - "\U0000EBDB" # lightning, lightning-rainy
                - "\U0000F61F" # pouring
                - "\U0000F61E" # rainy
                - "\U0000F61C" # snowy
                - "\U0000F61D" # snowy-rainy
                - "\U0000E81A" # sunny
                - "\U0000EFD8" # windy, windy-variant
                - "\U0000F7F3" # exceptional
          
            - file:
                type: gfonts
                family: Open Sans
                weight: 700    
              id: font_clock
              glyphs: "0123456789:"
              size: 70
            - file:
                type: gfonts
                family: Open Sans
                weight: 700    
              id: font_clock_big
              glyphs: "0123456789:"
              size: 100
            - file: "gfonts://Roboto"
              id: font_temp
              size: 28
            - file:
                type: gfonts
                family: Open Sans
                weight: 500    
              id: font_small
              size: 30
              glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz→»"
            - file:
                type: gfonts
                family: Open Sans
                weight: 500    
              id: font_medium
              size: 45
              glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz→»"
            - file:
                type: gfonts
                family: Open Sans
                weight: 300    
              id: font_xsmall
              size: 16  
              glyphs: "!\"%()+=,-_.:°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz→»"
          
          
          
              

           

          6. Important: This code does not include the credentials for connecting the device to your Wi-Fi and Home Assistant instance, so you will need to enter them manually. Specifically, I am referring to the following lines of code that 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 find the corresponding lines in the code (at the beginning) and add the necessary information.

          8. Now, click “Save” and “Install.” Select “Manual download” and wait for the code to compile.

          9. When finished, select the “Modern format” option to download the corresponding .bin” file.

          10. Connect the M5Stack Air Quality Kit to your computer using the USB-C data cable via the port on the bottom.

          11. Now go to the ESPHome page   and click "Connect." In the pop-up window, select your board and click "Connect."

          12. Now click on “Install” and select the '.bin' file obtained in step 9. Again, click on “Install”.

          13. Go back to Home Assistant and navigate to Settings > Devices & Services. Your device should be discovered and appear at the top, ready for you to click the “Configure” button. If not, click the “Add Integration” button, search for "ESPHome," and enter your board’s IP address in the "Host" field.

          Device information

          If you navigate to Settings > Devices & Services > ESPHome and select the M5Stack Air Quality Kit, you'll find several entities providing information about air quality.

          Additionally, the buttons on the device's top-left corner have also been exposed as entities. This means you can create an automation to trigger an action when you press them, such as activating your ventilation system.

          To set this up, go to Settings > Automations and Scenes > Create Automation. In the "When" section, add a "State" trigger. In the entity field, select the one corresponding to your device (e.g., 'binary_sensor.airq_button_a'), and in the "To" field, choose "On." Then, simply add the desired actions.

          Source: AguacaTEC
          Author: TitoTB

          2025-03-31

          On November 18, 2024, M5Stack opened its doors to individual visitors for the first time. More than 70 visitors from all around the world attended the event, with some showcasing their projects built with M5 products or sharing their stories with M5Stack.

          M5Stack Open Day 2024

          The event began at 3:00 PM, with many visitors checking in and receiving their gifts—custom T-shirts featuring the M5Stack logo. After a group photo, the first agenda kicked off: The Factory and Office Tour.

          M5Stack Factory and Office Tour

          The visitors were divided into three groups, with introductions provided in Chinese, English, and Japanese. M5Stack operates an all-in-one facility where product design, production, packaging, quality control, marketing, and shipping are seamlessly managed under one place. Visitors could take a close look at the whole flow.

          Factory of M5Stack

          In the show board area, we display all the products we've created so far, including the classic 5x5 stack series—such as M5Basic Core, M5Core2, M5Core3, and Stick series—like the M5StickC, along with the Stamp Series (M5Cardputer, M5StampFly), and the Atom series (M5Atom Echo), among others. M5Stack’s CEO, Jimmy Lai, also explains "What is M5Stack?"

          M5Stack product show board

          M5Stack is an open-source hardware IoT solution provider, primarily focused on products built around the ESP32. We also offer a range of accessories and our own visual programming platform, UIFlow. The "M" in M5Stack stands for Module, "5" represents the 5x5 cm size, and "Stack" refers to their stackable design. 

          M5Stack is committed to delivering convenient, stackable and easy-to-use development components and tools, with at least one new product every Friday, we launch over 50 new products every year. We provide not only standard products, also customized products. Our partner includes Amazon, Microsoft, Arduino, SONY, etc.

          Projects or Stories with M5Stack

          After the tour, it’s time for the Meetup. This year, we have 9 speakers. Jimmy began by sharing the company’s history, followed by presentations from the speakers. TAKASU Masakazu shared the success stories of M5Stack in Japan, where the Japanese market accounts for 35% of M5Stack’s global sales. Many companies and schools in Japan use M5Stack products, and you can even find books on how to use M5Stack products on Amazon. 

          TAKASU Masakazu share the M5Stack's successful story in Japan

          Other speakers introduced their innovative projects, including the Creative Dot Matrix Clock, the M5Unified Library, M5Cuffbox, and the Blue Tears Incubator, how to make stack-chan bigger, etc.

          speaker shows his 3 projects using M5stack's product

          It was a truly enjoyable experience to come together with M5Stack fans from all over. We appreciate everyone’s enthusiasm and participation, and we’re already looking forward to welcoming you again next year. See you then!

          M5Stack Open Day 2024

          2025-03-31