Example M5stack Atom Matrix with LED matrix effect - no Home Assistant

There seems to be a shortage of good examples of using ESPHome without Home Assistant. In particular, it took me quite some time to work out how to get an M5Stack Atom Matrix (with it’s single button and 5x5 RGB LED matrix display) to toggle on/off to a specific light effect.

So here is my working example.

Main YAML

# M5 Atom Matrix Configuration for m5atom01 which is an M5Stack Atom Matrix
#    ESP32 Pico, 8x8 RGB LED Matrix (WS2812C), Infra-red LED, 
#    MPU6886 Inertial Sensor - 6-axis attitude sensor with 3-axis gravity accelerometer and 3-axis gyroscope
# NB: This configuration does not use Home Assistant (HA). It uses TotallyInformation's standard MQTT Schema for ESP devices.

# Pins/GPIO
  #
  # === M5Stack Atom Matrix Specific ===
  # @see https://github.com/m5stack/M5Stack/blob/31be29f3f743768778accb8d5f308583ba4d3d4c/src/utility/Config.h
  # @see https://docs.m5stack.com/#/en/core/atom_matrix
  # ------ Buttons ------
  # BUTTON_PIN      | 39 | Under LED's and on side - both are the same button
  # ------ RGB LEDs PIN ------
  # RGB_PIN         | 27 | 8x8 matrix, WS2812C
  # ------ Infra-Red LED PIN ------
  # INFRA_PIN       | 12 | 
  # -------- I2C ---------
  # SDA             | 25 | 
  # SCL             | 21 |
  # 
  # MPU6886 Inertial Sensor - I2C Address 0x68
  #
  # ---- Exposed Ports ----
  # -- | 3V3 | 5-pin connector pin 1
  # 19 | Tx  | 5-pin connector pin 3
  # 22 | Rx  | 5-pin connector pin 2
  # 23 | Neo | 5-pin connector pin 4 (Connected to 34 via 100ohm resistor?)
  # 33 | Adc | 5-pin connector pin 5
  #
  # 21 | SCL | 4-pin connector pin 1
  # 25 | SDA | 4-pin connector pin 2
  # -- | 5V  | 4-pin connector pin 3
  # -- | Gnd | 4-pin connector pin 4
  #
  # -- | Gnd | Grove connector pin 1
  # -- | 5V  | Grove connector pin 2
  # 26 |     | Grove connector pin 3
  # 32 |     | Grove connector pin 4
  #
  # WARNING: When using FastLED lib, the recommended maximum brightness of RGB LED is 20
#

substitutions: # All have to be strings, the substitution process will convert them to the correct types
  devicename: m5atom01
  upper_devicename: M5ATOM01

  mqtt_root: ESP
  mqtt_prefix: $mqtt_root/$devicename
  publish_interval: 50s

  ipaddress: <<REPLACE WITH YOUR REQUIRED IP ADDRESS>>
  wifi_power_save_mode: light # none, light, high
  wifi_fast_connect: "true"
  
  log_level: debug # none, error, warn, info, debug, verbose
  esp_platform: ESP32
  esp_board: m5stick-c
  
  # todo: move secrets to here and file to root
  sun_latitude: !secret sun_latitude
  sun_longitude: !secret sun_longitude 
  mqtt_broker: !secret mqtt_broker 
  mqtt_username: !secret mqtt_username
  mqtt_password: !secret mqtt_password

<<: !include includes/mqtt.yaml # For some reason, the config checker doesn't like this as a package
packages: # These allow merging whereas global includes do not
  logger: !include includes/logger.yaml
  wifi: !include includes/wifi.yaml
  common: !include includes/common.yaml
  common_sensors: !include includes/common-sensors.yaml # Includes number, text and binary
  common_switches: !include includes/common-switches.yaml
  common_intervals: !include includes/common-intervals.yaml
  
# Define I2C interface
i2c:
  sda: 25
  scl: 21
  scan: true
  #frequency: 200kHz

# output:
#   - platform: ledc # 5x5 RGB Matrix (WS2812C)
#     pin: 27
#     id: matrix

binary_sensor:
  - platform: gpio # btn
    id: button
    pin:
      number: 39
      inverted: true
    on_click:
      then:
        - if:
            condition:
              - light.is_on: matrix_light
            then:
              - light.turn_off: matrix_light
            else:
              - light.turn_on:
                  id: matrix_light
                  effect: rainbow
# --- End if binary_sensor: --- #
    
light:
  - platform: fastled_clockless # 5x5 RGB Matrix (WS2812C)
    name: "5x5 Matrix"
    id: matrix_light
    pin: 27
    chipset: WS2812
    num_leds: 25
    color_correct: [50%, 50%, 50%] # The LED's on the Atom Matrix should not be run at full power
    effects:
      - addressable_rainbow:
          name: rainbow
# --- End of light: --- #

# EOF

includes/mqtt.yaml

# Configuration for the MQTT Broker.
# NB: This configuration does not use Home Assistant (HA). It uses TotallyInformation's standard MQTT Schema for ESP devices.
#
# The configuration variables must be set in the master YAML file.

mqtt: # Home MQTT Broker

  broker: $mqtt_broker 
  username: $mqtt_username
  password: $mqtt_password
  topic_prefix: $mqtt_prefix
  id: mqtt_client

  log_topic: # no log to MQTT, it isn't needed and isn't that useful unless you subscribe something to it.

  birth_message:
    topic: $mqtt_prefix
    payload: Online
    retain: true

  will_message:
    topic: $mqtt_prefix
    payload: Offline
    retain: true

  shutdown_message:
    topic: $mqtt_prefix
    payload: Shutdown
    retain: true

  discovery: false # No HA discovery, we aren't using HA

#--- End of mqtt ---#

includes/logger.yaml

logger: # Enable logging
  level: $log_level

includes/wifi.yaml

# NB: secrets.yaml has to be in the same folder as this file
wifi:

  ssid: !secret WiFi_SSID
  password: !secret WiFi_password
  domain: !secret WiFi_domain
  
  # Try to speed up connection
  fast_connect: $wifi_fast_connect
  
  manual_ip:
    static_ip: $ipaddress
    gateway: !secret WiFi_gateway
    subnet: !secret WiFi_subnet
    dns1: !secret WiFi_DNS1
  
  power_save_mode: $wifi_power_save_mode
  
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "${upper_devicename} Fallback Hotspot"
    password: !secret WiFi_AP_Password

#--- End of wifi ---#

includes/common.yaml

esphome:
  name: $devicename
  comment: $device_description
  platform: $esp_platform
  board: $esp_board
  build_path: builds/$devicename
  on_boot:
    - mqtt.publish:
        topic: $mqtt_prefix/info/protocol
        payload: ESPHome
    
web_server: 
  port: 80

time:
  - platform: sntp
    id: sntp_time
    timezone: Europe/London
  
ota: # Allow push OTA updates - https://esphome.io/components/ota.html?highlight=ota

sun: # https://esphome.io/components/sun.html
  latitude: $sun_latitude
  longitude: $sun_longitude

includes/common-sensors.yaml

# Standard sensor definitions. Comment in/out as desired
# Include as a PACKAGE in your master device YAML file
#
# NB: - This configuration does not use Home Assistant (HA). It uses TotallyInformation's standard MQTT Schema for ESP devices.
#     - Sensors with an id but no name are automatically marked as `internal`

# Sensors with numeric outputs
sensor:
  # How long since the device was restarted? In seconds.
  - <<: !include sensors/uptime.yaml
  # The WiFi RSSI signal strength in Db
  - <<: !include sensors/wifi_signal.yaml
  # The WiFi RSSI signal quality as a %
  - <<: !include sensors/wifi_quality.yaml
  # How far is the sun above/below the horizon?
  - <<: !include sensors/sun_elevation.yaml
  # At what absolute compass bearing is the sun?
  - <<: !include sensors/sun_azimuth.yaml

# Sensors with text outputs
text_sensor:
  # IP address, URL, SSID, BSSID, device MAC address
  - <<: !include text_sensors/wifi_info.yaml
  # The base build version of ESPHome used in the compile
  - <<: !include text_sensors/esphome_version.yaml
  # Next sunrise/sunset times (HH:mm:ss)
  - <<: !include text_sensors/sunrise.yaml
  - <<: !include text_sensors/sunset.yaml

   # Template Text Sensor Timestamp - Output by Interval below - https://esphome.io/components/text_sensor/template.html
   # Requires sntp_time component. Defined in common.yaml
  - platform: template
    id: systime
    lambda: |-
      char str[25];
      time_t currTime = id(sntp_time).now().timestamp;
      //strftime(str, sizeof(str), "%Y-%m-%d %H:%M:%S", localtime(&currTime));
      strftime(str, sizeof(str), "%Y-%m-%dT%H:%M:%S.000Z", gmtime(&currTime));
      return (std::string) str;

  - platform: template # Template text sensor wifi channel
    id: wifi_channel
    lambda: |-
      // --- ESP8266 version ---
      //std::string out;
      //char buffer[64];
      //sprintf(buffer, "%u", wifi_get_channel());
      //out.append(buffer);
      //return out;
      // --- ESP32 version ---
      wifi_ap_record_t wifidata;
      esp_wifi_sta_get_ap_info(&wifidata);
      std::string out;
      if (wifidata.primary != 0) {
        char buffer[64];
        sprintf(buffer, "%u", wifidata.primary);
        out.append(buffer);
      }
      return out;

binary_sensor:
  - platform: status
    name: "Device Status"
    id: system_status

includes/common-switches.yaml

switch: 
  - platform: restart # Define a restart switch (allows mqtt restart)
    name: ${upper_devicename} Restart

includes/common-intervals.yaml

# Standard interval definitions. Comment in/out as desired
# Include as a PACKAGE in your master device YAML file
#
# NB: This configuration does not use Home Assistant (HA). It uses TotallyInformation's standard MQTT Schema for ESP devices.

interval:
  # Run every ${publish_interval} but only if MQTT is connected, publish updated/updated_by/wifi-channel to MQTT
  - interval: ${publish_interval}
    then:
      - wait_until:
          mqtt.connected:
      - mqtt.publish:
          topic: $mqtt_prefix/status/updated
          payload: !lambda |-
            return id(systime).state;
      - mqtt.publish:
          topic: $mqtt_prefix/status/updated_by
          payload: $upper_devicename/ESPHome
      - mqtt.publish:
          topic: $mqtt_prefix/info/channel
          payload: !lambda |-
            return id(wifi_channel).state;

Sorry, not included the lower-level sensor includes, ask if you want them.

This is set up so that I can reuse the majority of the code on different device types. Also note that I use MQTT (with Node-RED) and not Home Assistant.

2 Likes

Did you use the MPU-6886 sensor? I’ve tried different combination of i2c frequency and address without any success.

No, I’ve not used that I’m afraid.

The address for the stand-alone version of that sensor from M5 is 0x68

Documentation is here: m5-docs (m5stack.com)

Did you ever get this sensor to work on the m5 atom?

In case anyone is still wanting to make the MPU6886 on this device work in ESPHome, here’s a working config section that returns all values from that sensor:

i2c:
  sda: 25
  scl: 21
  scan: true

sensor:
  - platform: mpu6886
    address: 0x68
    update_interval: 300ms  # Defaults to 60s, but seems to work fine up to at least 300ms
    accel_x:
      name: "MPU6886 Accel X"
    accel_y:
      name: "MPU6886 Accel Y"
    accel_z:
      name: "MPU6886 Accel z"
    gyro_x:
      name: "MPU6886 Gyro X"
    gyro_y:
      name: "MPU6886 Gyro Y"
    gyro_z:
      name: "MPU6886 Gyro z"
    temperature:
      name: "MPU6886 Temperature"

1 Like