Z2M not registering due to no long code

I am trying to get a Seeed ESP32 C6 to register in Home Assistant over Z2M. However, there is no Zigbee Mac and AI has hit a brick wall trying to get it sorted, due to there being no MAC transmitted so HA won't accept it for pairing.

According to AI, there is no way to initialise the Zigbee partition with Arduino, so it's pushing me over to ESP-IDF, but I's rather stay with Arduino if I can.

Grateful for advice please.

Here's the output of the code...

--- ZIGBEE INIT ---
[MAC] 58:E6:C5:14:38:70
[ZIGBEE] Init
[ZIGBEE] Stack initialised

[WIFI MAC] 0x58e6c5143870
[ZB MAC] 0x0000000000000000

[PAIRING] steering started

...this is what Z2M on HA is seeing...

[2026-05-07 09:02:07]  debug : 	zh:ember:ezsp: ezspIncomingMessageHandler: type=UNICAST apsFrame={"profileId":260,"clusterId":6,"sourceEndpoint":1,"destinationEndpoint":1,"options":256,"groupId":0,"sequence":57} packetInfo:{"senderShortId":21594,"senderLongId":"0x0000000000000000","bindingIndex":255,"addressIndex":9,"lastHopLqi":164,"lastHopRssi":-59,"lastHopTimestamp":2462000105} messageContents=08390a00001000

...and this is the code...

#include <Arduino.h>
#include "esp_zigbee_core.h"
#include "esp_mac.h"


#define PAIR_BUTTON 9

static bool pairing = false;
static bool last_state = true;
static uint32_t press_start = 0;


// ==================== IEEE / MAC PRINT ====================
void print_zb_ext_addr(const uint8_t *ext_addr) {
  Serial.print("[ZB MAC] 0x");
  for (int i = 0; i < 8; i++) {
    Serial.printf("%02x", ext_addr[7 - i]);   // Zigbee big‑endian
  }
  Serial.println("");
}

void show_zigbee_ieee(void) {
  Serial.println();

  uint8_t mac[6];
  esp_read_mac(mac, ESP_MAC_WIFI_STA);
  Serial.print("[WIFI MAC] 0x");
  for (int i = 0; i < 6; i++) {
    Serial.printf("%02x", mac[i]);
  }
  Serial.println("");

  uint8_t ext_addr[8];
  esp_zb_get_long_address(ext_addr);
  print_zb_ext_addr(ext_addr);

  Serial.println();
}


// =================== ZIGBEE SIGNAL HANDLER ===================
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
    uint32_t *params = (uint32_t *)esp_zb_app_signal_get_params((uint32_t *)signal_struct);

    esp_err_t status = ESP_FAIL;
    if (params != NULL) {
        status = (esp_err_t)(*params);
    }

    Serial.print("[ZB] status=");
    Serial.println((int)status);

    if (status == ESP_OK) {
        Serial.println("[ZB] STEERING SUCCESS");
        pairing = false;
    }
}


// =================== ZIGBEE INIT ===================
void zigbee_init()
{
    Serial.println("\n--- ZIGBEE INIT ---");

    // ===================== MAC PRINT (debug) =====================
    uint8_t mac[6];
    esp_read_mac(mac, ESP_MAC_WIFI_STA);
    Serial.printf("[MAC] %02X:%02X:%02X:%02X:%02X:%02X\n",
                  mac[0], mac[1], mac[2],
                  mac[3], mac[4], mac[5]);

    Serial.println("[ZIGBEE] Init");
    esp_zb_cfg_t cfg = {};
    esp_zb_init(&cfg);

    Serial.println("[ZIGBEE] Stack initialised");

    // ============ PRINT ZIGBEE IEEE BEFORE JOIN ============
    show_zigbee_ieee();

    // ============ START PAIRING ===============
    esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
    pairing = true;
    Serial.println("[PAIRING] steering started");
}


// =================== SETUP ===================
void setup()
{
    Serial.begin(115200);
    delay(500);

    pinMode(PAIR_BUTTON, INPUT_PULLUP);

    zigbee_init();
}


// =================== LOOP ===================
void loop()
{
    delay(100);
}

Hi @msknight!

You may have a look at this video

How to Flash ESP32-C6 in Arduino and let it talk Zigbee with Zigbee2MQTT in Home Assistant

where the Arduino IDE is used for this purpose.

Good luck!
ec2021

Thanks. I'll take a look.

Great stuff. Now I can merge this into my code. To think of the time I've wasted battling with AI. Thank you.

AI can be of assistance but in most cases only there where you are able to verify its results.

Never forget: The I in AI does not really stand for Intelligence rather than for the I in "pretendIng intelligence" ...

Unfortunately, I've run into a problem with the library. It's a single switch and it does pair, so I have marked this as a solution... but what I didn't specify in my problem, was that I need to use it for a remote, to get one of 20 buttons back to HA ... and this library can't do it.

For the edification of others, did you enable Zigbee in the IDE/Tools menu?

Ah. Yes, I did. Enabled Zigbee and also ensured that the partition had the necessary partition to store the Zigbee connection data.

Starting to get there a little. It relies on a converter file uploaded into /homeassistant/zigbee2mqtt/external_converters because I'm using the temperature report in order to get a figure back to HA, and then doing some conversion in HA to recognise the remote and present the temperature reading as a button press.

import * as exposes from 'zigbee-herdsman-converters/lib/exposes';
import * as fz from 'zigbee-herdsman-converters/converters/fromZigbee';

const e = exposes.presets;
const ea = exposes.access;

const keypadState = {
    cluster: 'msTemperatureMeasurement',
    type: ['attributeReport', 'readResponse'],
    convert: (model, msg) => {
        const raw = msg.data?.measuredValue;
        if (raw === undefined) return {};
        return { keypad_state: raw / 100 };
    },
};

export default {
    zigbeeModel: ['RS10_Remote_Office'],
    model: 'RS10_Remote_Office',
    vendor: 'SeeedC6-RS10',
    description: 'RS10 keypad remote',
    fromZigbee: [keypadState],
    toZigbee: [],
    exposes: [
        e.numeric('keypad_state', ea.STATE)
            .withDescription('Last pressed key index'),
    ],
};

This code receives the temperature from the remote and then converts it down to a single digit, rather than hundreds of degrees.

This is the starting code for the remote. At the moment it attempts to pair immediately on upload and won't run until it has. There's work to do on that. And I haven't done anything on sleep yet either.

#include <Arduino.h>

#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"

const char* DEVICE_NAME = "RS10_Remote_Office";

const int ROWS = 5;
const int COLS = 4;

int rowPins[ROWS] = {16, 17, 19, 20, 18};
int colPins[COLS] = {2, 21, 22, 23};

char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'},
  {'E','F','G','H'}
};

const int LED_PIN = 1;
const uint8_t ENDPOINT = 10;

ZigbeeTempSensor zbTempSensor(ENDPOINT);

const char keyList[20] = {
  '1','2','3','A',
  '4','5','6','B',
  '7','8','9','C',
  '*','0','#','D',
  'E','F','G','H'
};

uint8_t keyToIndex(char key) {
  for (uint8_t i = 0; i < 20; i++) {
    if (keyList[i] == key) return i + 1;
  }
  return 0;
}

void sendKey(char key) {
  uint8_t idx = keyToIndex(key);
  if (idx == 0) return;

  Serial.print("Key pressed: ");
  Serial.print(key);
  Serial.print(" -> index ");
  Serial.println(idx);

  float value = (float)idx;
  zbTempSensor.setTemperature(value);
  zbTempSensor.reportTemperature();

  digitalWrite(LED_PIN, HIGH);
  delay(300);
  digitalWrite(LED_PIN, LOW);
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  for (int r = 0; r < ROWS; r++) {
    pinMode(rowPins[r], OUTPUT);
    digitalWrite(rowPins[r], HIGH);
  }

  for (int c = 0; c < COLS; c++) {
    pinMode(colPins[c], INPUT_PULLUP);
  }

  zbTempSensor.setManufacturerAndModel("SeeedC6-RS10", "RS10_Remote_Office");
  zbTempSensor.setMinMaxValue(1, 20);
  zbTempSensor.setDefaultValue(1.0);
  zbTempSensor.setTolerance(1.0);
  zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
  zbTempSensor.addTimeCluster();

  Zigbee.addEndpoint(&zbTempSensor);

  Serial.println("Starting Zigbee...");

  if (!Zigbee.begin()) {
    Serial.println("Zigbee failed to start!");
    ESP.restart();
  }

  Serial.println("Connecting to network");
  while (!Zigbee.connected()) {
    Serial.print(".");
    delay(100);
  }
  Serial.println();
  Serial.println("Successfully connected to Zigbee network");

  zbTempSensor.setReporting(1, 0, 1);
}

void loop() {

  for (int r = 0; r < ROWS; r++) {
    digitalWrite(rowPins[r], LOW);

    for (int c = 0; c < COLS; c++) {
      if (digitalRead(colPins[c]) == LOW) {
        delay(20);
        if (digitalRead(colPins[c]) == LOW) {
          sendKey(keys[r][c]);
          while (digitalRead(colPins[c]) == LOW) {
            delay(10);
          }
        }
      }
    }

    digitalWrite(rowPins[r], HIGH);
  }

  delay(10);
}

In progressing the code, it has stopped transmitting temperature changes. It pairs and handshakes fine, but the Z2M logs show no temperature packet transmissions.

#include <Arduino.h>

#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"

const char* MANUFACTURER_NAME = "SeeedC6-RS10";
const char* MODEL_NAME = "RS10_Remote";

const int ROWS = 5;
const int COLS = 4;

int rowPins[ROWS] = {16, 17, 19, 20, 18};
int colPins[COLS] = {2, 21, 22, 23};

char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'},
  {'E','F','G','H'}
};

const int LED_PIN = 1;
const uint8_t ENDPOINT = 10;

ZigbeeTempSensor zbTempSensor(ENDPOINT);

const char keyList[20] = {
  '1','2','3','A',
  '4','5','6','B',
  '7','8','9','C',
  '*','0','#','D',
  'E','F','G','H'
};

uint8_t keyToIndex(char key) {
  for (uint8_t i = 0; i < 20; i++) {
    if (keyList[i] == key) return i + 1;
  }
  return 0;
}

void reportValue(float value) {
  zbTempSensor.setTemperature(value);
  zbTempSensor.reportTemperature();
  Serial.println(value);
}

void sendKey(char key) {
  uint8_t idx = keyToIndex(key);
  if (idx == 0) return;

  Serial.print("Key pressed: ");
  Serial.print(key);
  Serial.print(" -> index ");
  Serial.println(idx);

  reportValue((float)idx);
  delay(500);
}

void blinkLedDuringStartup() {
  digitalWrite(LED_PIN, HIGH);
  delay(150);
  digitalWrite(LED_PIN, LOW);
  delay(150);
}

void factoryResetAndReboot() {
  Serial.println("Factory reset requested");
  digitalWrite(LED_PIN, LOW);
  Zigbee.factoryReset();
  delay(1000);
  ESP.restart();
}

void setup() {
  Serial.begin(115200);
  delay(1000);

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  for (int r = 0; r < ROWS; r++) {
    pinMode(rowPins[r], OUTPUT);
    digitalWrite(rowPins[r], HIGH);
  }

  for (int c = 0; c < COLS; c++) {
    pinMode(colPins[c], INPUT_PULLUP);
  }

  zbTempSensor.setManufacturerAndModel(MANUFACTURER_NAME, MODEL_NAME);
  zbTempSensor.setMinMaxValue(1, 20);
  zbTempSensor.setDefaultValue(1.0);
  zbTempSensor.setTolerance(0.1);
  zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
  zbTempSensor.addTimeCluster();

  Zigbee.addEndpoint(&zbTempSensor);

  Serial.print("Starting Zigbee for ");
  Serial.println(MODEL_NAME);

  if (!Zigbee.begin()) {
    Serial.println("Zigbee failed to start!");
    ESP.restart();
  }

  Serial.println("Connecting to network");
  while (!Zigbee.connected()) {
    blinkLedDuringStartup();
    Serial.print(".");
    delay(100);
  }
  Serial.println();
  Serial.println("Successfully connected to Zigbee network");

  zbTempSensor.setReporting(1, 60, 1);
}

void loop() {
  for (int r = 0; r < ROWS; r++) {
    digitalWrite(rowPins[r], LOW);

    for (int c = 0; c < COLS; c++) {
      if (digitalRead(colPins[c]) == LOW) {
        delay(20);
        if (digitalRead(colPins[c]) == LOW) {
          char key = keys[r][c];
          uint8_t idx = keyToIndex(key);
          if (idx == 0) continue;

          digitalWrite(LED_PIN, HIGH);
          sendKey(key);

          unsigned long pressedAt = millis();
          bool isOneKey = (key == '1');

          while (digitalRead(colPins[c]) == LOW) {
            if (isOneKey && (millis() - pressedAt >= 5000)) {
              factoryResetAndReboot();
            }
            delay(10);
          }

//          reportValue(0.0f);
          digitalWrite(LED_PIN, LOW);
        }
      }
    }

    digitalWrite(rowPins[r], HIGH);
  }

  delay(10);
}

Converter code was changed to the following to reflect the change of device name to be slightly more generic, but still unique to the remote controller itself ...

import * as exposes from 'zigbee-herdsman-converters/lib/exposes';
import * as fz from 'zigbee-herdsman-converters/converters/fromZigbee';

const e = exposes.presets;
const ea = exposes.access;

const keypadState = {
    cluster: 'msTemperatureMeasurement',
    type: ['attributeReport', 'readResponse'],
    convert: (model, msg) => {
        const raw = msg.data?.measuredValue;
        if (raw === undefined) return {};
        return { keypad_state: raw / 100 };
    },
};

export default {
    zigbeeModel: ['RS10_Remote'],
    model: 'RS10_Remote',
    vendor: 'SeeedC6-RS10',
    description: 'RS10 keypad remote',
    fromZigbee: [keypadState],
    toZigbee: [],
    exposes: [
        e.numeric('keypad_state', ea.STATE)
            .withDescription('Last pressed key index'),
    ],
};