M5Stack NanoC6 Zigbee RGBIC Strip

Make a Zigbee LED Strip with RGBIC effects using an M5Stack NanoC6 and A035 15 LED RGBIC strip

This is made possible by the hard work of @luar123 for the zigbee_esphome external component. (github://luar123/zigbee_esphome)

A lot of the code is based on examples in the github repo, but I was having some issues getting started. I thought I would share my configuration to save some folks time and effort. (This is so heckin’ cool!)

For this tutorial, you’ll need a working install of esphome 2025.7.5, an M5Stack NanoC6, an A035 LED Strip, and a Grove cable:

Mouser doesn’t ship any Grove cables with the LED strip (boo!), but they sell them if you need them:

*(Mouser) M5Stack Grove Cable ~$2 before tax, shipping, and Trump Tariffs of about 25 cents.

I recommend buying a bunch of modules to make the shipping from Mouser worth it. You can buy the C6 module from Amazon, but you’ll pay triple.

  1. Once you have your materials, connect your LED strip to the Grove cable, and the cable to the module.

  2. Go to github and download partitions_zb.csv
    then place in /config/esphome

  3. Open ESPHome and prep a new device.

  4. Once your device is online, replace your YAML code with:

esphome:
  name: nanoc6-2
  friendly_name: M5Stack NanoC6 Light Strip (ZB Only)
  platformio_options:
    platform: https://github.com/platformio/platform-espressif32/archive/refs/tags/v6.7.0.zip
    board_build.f_cpu: 160000000L
    board_build.f_flash: 80000000L
    board_build.flash_size: 4MB
    build_flags: "-DBOARD_HAS_PSRAM"
    board_build.arduino.memory_type: qio_opi

esp32:
  board: esp32-c6-devkitm-1
  variant: esp32c6
  partitions: partitions_zb.csv
  framework:
    type: esp-idf
    version: 5.2.1
    # version: "5.3.0"
    platform_version: 6.8.1
    sdkconfig_options:
      CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y

output:
  - platform: ledc
    pin: GPIO07
    id: gpio_07

switch:
  - platform: gpio
    pin: GPIO19
    id: gpio_19
    restore_mode: ALWAYS_ON
binary_sensor:
  - platform: gpio
    name: "Button"
    id: m5stack_button
    pin:
      number: GPIO9
      inverted: true
    on_press: 
        - light.turn_on:
            id: ledstrip
            effect: random
        - light.turn_on:
            id: button_light
            effect: random
    on_double_click: 
        - light.turn_on:
            id: ledstrip
            effect: rainbow
        - light.turn_on:
            id: button_light
            effect: rainbow
    on_multi_click:
    - timing:
        - ON for at most 1s
        - OFF for at most 1s
        - ON for at most 1s
        - OFF for at most 1s
        - ON for at most 1s
        - OFF for at most 1s
      then:
        - light.turn_on:
            id: ledstrip
            effect: scan
        - light.turn_on:
            id: button_light
            effect: rainbow
    - timing:
        - ON for at least 3s
        - OFF for at least 1s
        - ON for at least 3s
        - OFF for at least 1s
      then:
        - zigbee.reset: zb

light:
  - platform: esp32_rmt_led_strip
    chipset: SK6812
    pin: GPIO02
    num_leds: 15
    rgb_order: GRB
    rmt_symbols: 48
    name: "LED Strip"
    id: "ledstrip"
    effects:
      - random:
      - flicker:
      - addressable_rainbow:   
      - addressable_color_wipe:
      - addressable_scan:
      - addressable_twinkle:
      - addressable_random_twinkle:
      - addressable_fireworks:
      - addressable_flicker:                        
  - platform: esp32_rmt_led_strip
    rgb_order: GRB
    pin: GPIO20
    num_leds: 1
    # rmt_channel: 0 ### this stopped working 2025/02
    chipset: WS2812
    rmt_symbols: 48
    id: "button_light"
    name: "Button LED"
    icon: "mdi:led-outline"
    effects:
      - random:
      - flicker:
      - addressable_rainbow:
  - platform: monochromatic
    output: gpio_07
    id: blueled
    name: "Blue LED"
sensor:
  - platform: internal_temperature
    name: "Internal Temperature"
    id: "i_temp"
    filters:
      - delta: 0.1

external_components:
  - source: github://luar123/zigbee_esphome
    components: [zigbee]

globals:
  - id: color_x
    type: float
    restore_value: no
    initial_value: '0'
  - id: color_y
    type: float
    restore_value: no
    initial_value: '0'

zigbee:
  id: "zb"
  router: true
  endpoints:
    - num: 1
      device_type: COLOR_DIMMABLE_LIGHT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:                
                  - light.control:
                      id: ledstrip
                      state: !lambda "return (bool)x;"
        - id: LEVEL_CONTROL
          attributes:
            - attribute_id: 0
              type: U8
              value: 255
              on_value:
                then:
                  - light.control:
                      id: ledstrip
                      brightness: !lambda "return ((float)x)/255;"
        - id: COLOR_CONTROL
          attributes:
            - attribute_id: 3
              type: U16
              on_value:
                then:
                  - lambda: id(color_x) = (float)x/65536;
                  - light.control:
                      id: ledstrip
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"
            - attribute_id: 4
              type: U16
              on_value:
                then:
                  - lambda: id(color_y) = (float)x/65536;
                  - light.control:
                      id: ledstrip
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"
    - num: 2
      device_type: COLOR_DIMMABLE_LIGHT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:
                  - light.control:
                      id: button_light
                      state: !lambda "return (bool)x;"
        - id: LEVEL_CONTROL
          attributes:
            - attribute_id: 0
              type: U8
              value: 255
              on_value:
                then:
                  - light.control:
                      id: button_light
                      brightness: !lambda "return ((float)x)/255;"
        - id: COLOR_CONTROL
          attributes:
            - attribute_id: 3
              type: U16
              on_value:
                then:
                  - lambda: id(color_x) = (float)x/65536;
                  - light.control:
                      id: button_light
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"
            - attribute_id: 4
              type: U16
              on_value:
                then:
                  - lambda: id(color_y) = (float)x/65536;
                  - light.control:
                      id: button_light
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"   
    - num: 3
      device_type: DIMMABLE_LIGHT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:
                  - light.control:
                      id: blueled
                      state: !lambda "return (bool)x;"
        - id: LEVEL_CONTROL
          attributes:
            - attribute_id: 0
              type: U8
              value: 255
              on_value:
                then:
                  - light.control:
                      id: blueled
                      brightness: !lambda "return ((float)x)/255;"      
    - device_type: TEMPERATURE_SENSOR
      num: 4
      clusters:
        - id: TEMP_MEASUREMENT
          attributes:
            - attribute_id: 0
              type: S16
              report: true
              value: 100
              device: i_temp
              scale: 100

The device should then pair to ZHA right away. I don’t have Z2M, so you’ll have to test this, but I believe this would work just fine. This YAML configures your C6 module as a router, but you can disable that line and make this an end device, but that would be silly.

These work fine as repeaters, but their range isn’t great.

This code exposes:

  • The LED strip as an RGB bulb,
  • NanoC6 button as an RGB Bulb
  • The blue LED as a standard ON/OFF bulb.
  • The internal module temperature as a sensor

These can all be controlled via ZHA as any other LED devices. The Nano C6 Button does the following:

  • One click: Hue-style color loop
  • Double-click: RGBIC rainbow effect on the strip and button
  • Triple-click: RGBIC chase effect on the strip and rainbow for the button

To Reset/Set to Pairing (Only needed if you fiddle with the code a bunch)

  • Press the button for 3 seconds
  • Release for 1 second
  • Press for 3 seconds
  • Release

The lights should turn off. Unplug/Plug in the NanoC6 and you’re back in pairing mode.
Feel free to adapt the code

2 Likes

Here’s an updated variant of my last config with some extra features.

I realized I had some longer waterproof WS2812 LED strings (17 feet with 66 LEDS) laying around, so I made outdoor-ish RGBIC strips. I tweaked the effect settings for my particular LED strings, so you may want to play with this.

In addition to the button and LEDs being exposed, I created switches that activate the lighting effects without the need of a button. You can now automate RGBIC effects via Zigbee:

BTW if anyone knows the best way to pass the lighting effects to ZigBee, I would love some help. This solution is a bit kludgey. I’m not sure the trigger effect goes in the ON_OFF or COLOR_CONTROL cluster and I’m not sure how to pass an array of choices. My guess is it’s not supported (or at least documented) yet.

esphome:
  name: m5stackc6-ws2812-zb
  friendly_name: M5Stack NanoC6 WS2812 (66 LED) ZB
  platformio_options:
    platform: https://github.com/platformio/platform-espressif32/archive/refs/tags/v6.7.0.zip
    board_build.f_cpu: 160000000L
    board_build.f_flash: 80000000L
    board_build.flash_size: 4MB
    build_flags: "-DBOARD_HAS_PSRAM"
    board_build.arduino.memory_type: qio_opi

esp32:
  board: esp32-c6-devkitm-1
  variant: esp32c6
  partitions: partitions_zb.csv
  framework:
    type: esp-idf
    version: 5.2.1
    # version: "5.3.0"
    platform_version: 6.8.1
    sdkconfig_options:
      CONFIG_ESPTOOLPY_FLASHSIZE_4MB: y

output:
  - platform: ledc
    pin: GPIO07
    id: gpio_07

switch:
  - platform: gpio
    pin: GPIO19
    id: gpio_19
    restore_mode: ALWAYS_ON
binary_sensor:
  - platform: gpio
    name: "Button"
    id: m5stack_button
    pin:
      number: GPIO9
      inverted: true
    on_press: 
        - light.turn_on:
            id: ledstrip
            effect: random
    on_double_click: 
        - light.turn_on:
            id: ledstrip
            effect: rainbow
    on_multi_click:
    - timing:
        - ON for at most 1s
        - OFF for at most 1s
        - ON for at most 1s
        - OFF for at most 1s
        - ON for at most 1s
        - OFF for at most 1s
      then:
        - light.turn_on:
            id: ledstrip
            effect: scan
    - timing:
        - ON for at least 3s
        - OFF for at least 1s
        - ON for at least 3s
        - OFF for at least 1s
      then:
        - zigbee.reset: zb

light:
  - platform: esp32_rmt_led_strip
    chipset: WS2812
    pin: GPIO01
    num_leds: 66
    rgb_order: RGB
    rmt_symbols: 48
    name: "LED Strip"
    id: "ledstrip"
    effects:
      - random:
      - flicker:
      - addressable_rainbow:   
      - addressable_scan:
          scan_width: 10
      - addressable_twinkle:
          twinkle_probability: 35%
          progress_interval: 6ms
      - addressable_random_twinkle:
          twinkle_probability: 35%
          progress_interval: 6ms
      - addressable_fireworks:
          update_interval: 75ms
          spark_probability: 40%
          use_random_color: true
          fade_out_rate: 74
      - addressable_flicker:  
          update_interval: 5ms
          intensity: 33%                      
  - platform: esp32_rmt_led_strip
    rgb_order: GRB
    pin: GPIO20
    num_leds: 1
    # rmt_channel: 0 ### this stopped working 2025/02
    chipset: WS2812
    rmt_symbols: 48
    id: "button_light"
    name: "Button LED"
    icon: "mdi:led-outline"
    effects:
      - random:
      - flicker:
      - addressable_rainbow:
  - platform: monochromatic
    output: gpio_07
    id: blueled
    name: "Blue LED"
sensor:
  - platform: internal_temperature
    name: "Internal Temperature"
    id: "i_temp"
    filters:
      - delta: 0.1

external_components:
  - source: github://luar123/zigbee_esphome
    components: [zigbee]

globals:
  - id: color_x
    type: float
    restore_value: no
    initial_value: '0'
  - id: color_y
    type: float
    restore_value: no
    initial_value: '0'

zigbee:
  id: "zb"
  router: true
  endpoints:
    - num: 1
      device_type: COLOR_DIMMABLE_LIGHT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              id: led_strip1
              type: bool
              on_value:
                then:                
                  - light.control:
                      id: ledstrip
                      state: !lambda "return (bool)x;"
        - id: LEVEL_CONTROL
          attributes:
            - attribute_id: 0
              type: U8
              value: 255
              on_value:
                then:
                  - light.control:
                      id: ledstrip
                      brightness: !lambda "return ((float)x)/255;"
        - id: COLOR_CONTROL
          attributes:
            - attribute_id: 3
              type: U16
              on_value:
                then:
                  - lambda: id(color_x) = (float)x/65536;
                  - light.control:
                      id: ledstrip
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"
            - attribute_id: 4
              type: U16
              on_value:
                then:
                  - lambda: id(color_y) = (float)x/65536;
                  - light.control:
                      id: ledstrip
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"
    - num: 2
      device_type: COLOR_DIMMABLE_LIGHT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              id: button
              type: bool
              on_value:
                then:
                  - light.control:
                      id: button_light
                      state: !lambda "return (bool)x;"
        - id: LEVEL_CONTROL
          attributes:
            - attribute_id: 0
              type: U8
              value: 255
              on_value:
                then:
                  - light.control:
                      id: button_light
                      brightness: !lambda "return ((float)x)/255;"
        - id: COLOR_CONTROL
          attributes:
            - attribute_id: 3
              type: U16
              on_value:
                then:
                  - lambda: id(color_x) = (float)x/65536;
                  - light.control:
                      id: button_light
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"
            - attribute_id: 4
              type: U16
              on_value:
                then:
                  - lambda: id(color_y) = (float)x/65536;
                  - light.control:
                      id: button_light
                      red: !lambda "return zigbee::get_r_from_xy(id(color_x), id(color_y));"
                      green: !lambda "return zigbee::get_g_from_xy(id(color_x), id(color_y));"
                      blue: !lambda "return zigbee::get_b_from_xy(id(color_x), id(color_y));"   
    - num: 3
      device_type: DIMMABLE_LIGHT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:
                  - light.control:
                      id: blueled
                      state: !lambda "return (bool)x;"
        - id: LEVEL_CONTROL
          attributes:
            - attribute_id: 0
              type: U8
              value: 255
              on_value:
                then:
                  - light.control:
                      id: blueled
                      brightness: !lambda "return ((float)x)/255;"      
    - device_type: TEMPERATURE_SENSOR
      num: 4
      clusters:
        - id: TEMP_MEASUREMENT
          attributes:
            - attribute_id: 0
              type: S16
              report: true
              value: 100
              device: i_temp
              scale: 100
    - num: 5
      device_type: ON_OFF_OUTPUT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:   
                  - if:
                      condition: 
                        - light.is_off: ledstrip
                      then: 
                        - light.turn_on:
                            id: ledstrip
                            effect: random
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"
                      else:
                        - light.turn_off:
                            id: ledstrip           
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"         
    - num: 6
      device_type: ON_OFF_OUTPUT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:   
                  - if:
                      condition: 
                        - light.is_off: ledstrip
                      then: 
                        - light.turn_on:
                            id: ledstrip
                            effect: rainbow
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"
                      else:
                        - light.turn_off:
                            id: ledstrip           
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"              
    - num: 7
      device_type: ON_OFF_OUTPUT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:   
                  - if:
                      condition: 
                        - light.is_off: ledstrip
                      then: 
                        - light.turn_on:
                            id: ledstrip
                            effect: twinkle
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"
                      else:
                        - light.turn_off:
                            id: ledstrip           
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"         
    - num: 8
      device_type: ON_OFF_OUTPUT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:   
                  - if:
                      condition: 
                        - light.is_off: ledstrip
                      then: 
                        - light.turn_on:
                            id: ledstrip
                            effect: flicker
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"
                      else:
                        - light.turn_off:
                            id: ledstrip           
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"         
    - num: 9
      device_type: ON_OFF_OUTPUT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:   
                  - if:
                      condition: 
                        - light.is_off: ledstrip
                      then: 
                        - light.turn_on:
                            id: ledstrip
                            effect: fireworks
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"
                      else:
                        - light.turn_off:
                            id: ledstrip           
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"        
    - num: 10
      device_type: ON_OFF_OUTPUT
      clusters:
        - id: ON_OFF
          attributes:
            - attribute_id: 0
              type: bool
              on_value:
                then:   
                  - if:
                      condition: 
                        - light.is_off: ledstrip
                      then: 
                        - light.turn_on:
                            id: ledstrip
                            effect: scan
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"
                      else:
                        - light.turn_off:
                            id: ledstrip           
                        - zigbee.setAttr:
                            id: led_strip1
                            value: !lambda "return (bool)x;"   

I’m moving this stuff over to my github repo here: ESPhome Zigbee Projects

I added an STL file for a 3D printable lamp and some extra notes. I’ll keep updating as I learn more.

Hello!

I am trying out your project using a M5Stack Nano C6, and WS2812 strips.
I am very interested - I saw in your GIT you use ZHA- I use Z2M and I’m happy to test things out.

Right now I am stuck - I have a board up, it pairs easily with z2M, but doesn’t light up. The logs just keep throwing this error:

ESP_ZIGBEE_CLUSTER: The requested update cluster ID (0x 6) is not found

I will toy with it more soon, but if you have ideas let me know!

I used your yaml ALMOST verbatim, though I had to delete the version info. It compiled fine after I did that