Need help structuring custom device, getting Load Access Fault Error

97cweb
Posts: 1
Joined: Wed Sep 11, 2024 2:13 pm

Need help structuring custom device, getting Load Access Fault Error

Postby 97cweb » Wed Sep 11, 2024 2:16 pm

Good morning all
I am trying to make my own custom device using an ESP32-C6 with zigbee. I cannot find any good documentation on how to make a custom device, and not even a good description of endpoint vs cluster, vs attribute lists.

I have posted my code below, the 2 functions that are bad are esp_zb_task and zb_attribute_handler

When I get this working, I will write up more info so others don't have to fight it as badly as I am

Thank you

Code: Select all

// Copyright 2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

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

#include "esp_zigbee_core.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ha/esp_zigbee_ha_standard.h"

// Define the model and manufacturer
#define DEVICE_MODEL        "\x14""Beeton Train Decoder"   // 20 characters (14 in hex is 20 in decimal)
#define VERSION_NUMBER             "\x06""0.0.01"
#define DEVICE_MANUFACTURER "\x14""YottaRock Industries"  // 20 characters (14 in hex is 20 in decimal)

#define ZCL_BASIC_POWER_SOURCE 4

// Custom endpoint definitions
#define MOTOR_CONTROL_ENDPOINT    1
#define OUTPUT_CONTROL_ENDPOINT    2
#define IO_CONTROL_ENDPOINT        3
#define SOUND_CONTROL_ENDPOINT     4

// Motion control attributes
int motor_speed = 0;    // Motor speed range from -1024 to 1023
bool motor_direction = true;  // True: forward, False: reverse

// Digital output attributes
bool outputs[8] = {false};  // On/Off states for 8 outputs

// IO control attributes
bool io_direction = true;  // True: output, False: input

// Sound control attributes
char sound_file[128] = "";  // Current sound file being played
bool is_playing = false;
int volume = 50;  // Volume level (0-100)

/* Default End Device config */
#define ESP_ZB_ZED_CONFIG() \
  { \
    .esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, .install_code_policy = INSTALLCODE_POLICY_ENABLE, \
    .nwk_cfg = { \
      .zed_cfg = { \
        .ed_timeout = ED_AGING_TIMEOUT, \
        .keep_alive = ED_KEEP_ALIVE, \
      }, \
    }, \
  }

#define ESP_ZB_DEFAULT_RADIO_CONFIG() \
  { .radio_mode = ZB_RADIO_MODE_NATIVE, }

#define ESP_ZB_DEFAULT_HOST_CONFIG() \
  { .host_connection_mode = ZB_HOST_CONNECTION_MODE_NONE, }

/* Zigbee configuration */
#define INSTALLCODE_POLICY_ENABLE false /* enable the install code policy for security */
#define ED_AGING_TIMEOUT ESP_ZB_ED_AGING_TIMEOUT_64MIN
#define ED_KEEP_ALIVE 3000                                               /* 3000 millisecond */
#define BEETON_TRAIN_ENDPOINT 1                                          /* esp light bulb device endpoint, used to process light controlling commands */
#define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK /* Zigbee primary channel mask use in the example */

/********************* Zigbee functions **************************/

static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask) {
  ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask));
}

void esp_zb_app_signal_handler(esp_zb_app_signal_t* signal_struct) {
  log_i("Handling signals...");
  uint32_t* p_sg_p = signal_struct->p_app_signal;
  esp_err_t err_status = signal_struct->esp_err_status;
  esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t)*p_sg_p;

  switch (sig_type) {
    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
      log_i("Zigbee stack initialized");
      esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
      break;
    case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
    case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
      if (err_status == ESP_OK) {
        log_i("Device started up in %s factory-reset mode", esp_zb_bdb_is_factory_new() ? "" : "non");
        if (esp_zb_bdb_is_factory_new()) {
          log_i("Start network formation");
          esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
        } else {
          log_i("Device rebooted");
        }
      } else {
        log_w("Failed to initialize Zigbee stack (status: %s)", esp_err_to_name(err_status));
      }
      break;
    case ESP_ZB_BDB_SIGNAL_STEERING:
      if (err_status == ESP_OK) {
        esp_zb_ieee_addr_t extended_pan_id;
        esp_zb_get_extended_pan_id(extended_pan_id);
        rgbLedWrite(RGB_BUILTIN, 10, 0, 0);
        log_i(
          "Joined network successfully (Extended PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, PAN ID: 0x%04hx, Channel:%d, Short Address: 0x%04hx)",
          extended_pan_id[7], extended_pan_id[6], extended_pan_id[5], extended_pan_id[4], extended_pan_id[3], extended_pan_id[2], extended_pan_id[1],
          extended_pan_id[0], esp_zb_get_pan_id(), esp_zb_get_current_channel(), esp_zb_get_short_address());
      } else {
        rgbLedWrite(RGB_BUILTIN, 10, 0, 10);
        log_i("Network steering was not successful (status: %s)", esp_err_to_name(err_status));
        esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
      }
      break;
    default:
      log_i("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type, esp_err_to_name(err_status));
      break;
  }
}


static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void* message) {
  esp_err_t ret = ESP_OK;
  switch (callback_id) {
    case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:
      ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t*)message);
      break;
    default:
      log_w("Receive Zigbee action(0x%x) callback", callback_id);
      break;
  }
  return ret;
}


static void esp_zb_task(void* pvParameters) {
    esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
    esp_zb_init(&zb_nwk_cfg);

    //info list
    esp_zb_attribute_list_t *esp_zb_basic_cluster = esp_zb_basic_cluster_create(NULL);
    uint8_t powerSource = ZCL_BASIC_POWER_SOURCE;
    esp_zb_cluster_update_attr(esp_zb_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_POWER_SOURCE_ID, &powerSource);
    esp_zb_basic_cluster_add_attr(esp_zb_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MODEL_IDENTIFIER_ID, (void *) DEVICE_MODEL);
    esp_zb_basic_cluster_add_attr(esp_zb_basic_cluster, ESP_ZB_ZCL_ATTR_BASIC_MANUFACTURER_NAME_ID, (void *) DEVICE_MANUFACTURER);

    // Motor control (Endpoint 1)
    esp_zb_attribute_list_t *motor_control = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL);
    esp_zb_attribute_list_t *motor_onoff = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_ON_OFF);
    
    esp_zb_cluster_list_t *motor_cluster = esp_zb_zcl_cluster_list_create();
    esp_zb_cluster_list_add_custom_cluster( motor_cluster, motor_control, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE) ;
    esp_zb_cluster_list_add_custom_cluster(motor_cluster, motor_onoff, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    // Output control (Endpoint 2)
    esp_zb_attribute_list_t *output_control_attributes = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_ON_OFF);
    esp_zb_cluster_list_t *output_control_cluster = esp_zb_zcl_cluster_list_create();
    esp_zb_cluster_list_add_on_off_cluster(output_control_cluster, output_control_attributes, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    // IO control (Endpoint 3)
    esp_zb_attribute_list_t *io_control_attributes = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_MULTI_VALUE);
    esp_zb_cluster_list_t *io_control_cluster = esp_zb_zcl_cluster_list_create();
    esp_zb_cluster_list_add_multistate_value_cluster(io_control_cluster, io_control_attributes, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);

    // Sound control (Endpoint 4)
    esp_zb_attribute_list_t *sound_control_attribute = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_ON_OFF);
    esp_zb_attribute_list_t *sound_volume_attribute = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL);
    esp_zb_attribute_list_t *sound_file_attribute = esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_BASIC);  // Custom attribute for file name
    
    esp_zb_cluster_list_t *sound_control_list = esp_zb_zcl_cluster_list_create();
    esp_zb_cluster_list_add_on_off_cluster(sound_control_list, sound_control_attribute, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
    esp_zb_cluster_list_add_multistate_value_cluster(sound_control_list, sound_volume_attribute, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
    esp_zb_cluster_list_add_custom_cluster(sound_control_list, sound_file_attribute, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);  // Add custom file cluster

    // Register endpoints
    esp_zb_ep_list_t *ep_list = esp_zb_ep_list_create();

    // Motion control endpoint (1)
    esp_zb_endpoint_config_t motion_endpoint = {
        .endpoint = MOTOR_CONTROL_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
        .app_device_version = 0
    };
    esp_zb_ep_list_add_ep(ep_list, motor_cluster, motion_endpoint);

    // Output control endpoint (2)
    esp_zb_endpoint_config_t output_endpoint = {
        .endpoint = OUTPUT_CONTROL_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
        .app_device_version = 0
    };
    esp_zb_ep_list_add_ep(ep_list, output_control_cluster, output_endpoint);

    // IO control endpoint (3)
    esp_zb_endpoint_config_t io_endpoint = {
        .endpoint = IO_CONTROL_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
        .app_device_version = 0
    };
    esp_zb_ep_list_add_ep(ep_list, io_control_cluster, io_endpoint);

    // Sound control endpoint (4)
    esp_zb_endpoint_config_t sound_endpoint = {
        .endpoint = SOUND_CONTROL_ENDPOINT,
        .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
        .app_device_id = ESP_ZB_HA_ON_OFF_SWITCH_DEVICE_ID,
        .app_device_version = 0
    };
    esp_zb_ep_list_add_ep(ep_list, sound_control_list, sound_endpoint);

    // Register and start Zigbee
    esp_zb_device_register(ep_list);


  esp_zb_core_action_handler_register(zb_action_handler);
  esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);


  //Erase NVRAM before creating connection to new Coordinator
  esp_zb_nvram_erase_at_start(true);  //Comment out this line to erase NVRAM data if you are connecting to new Coordinator

  ESP_ERROR_CHECK(esp_zb_start(false));
  esp_zb_main_loop_iteration();
}

/* Handle the light attribute */
static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t* message) {
  esp_err_t ret = ESP_OK;
  uint8_t endpoint = message->info.dst_endpoint;
  uint16_t cluster_id = message->info.cluster;
  uint16_t attr_id = message->attribute.id;

  if (!message) {
    log_e("Empty message");
  }
  if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
    log_e("Received message: error status(%d)", message->info.status);
  }

  log_i(
    "Received message: endpoint(%d), cluster(0x%x), attribute(0x%x), data size(%d)", endpoint, cluster_id, attr_id,
    message->attribute.data.size);

  
  switch (endpoint) {
    case MOTOR_CONTROL_ENDPOINT:
        if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL) {
            if (attr_id == ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID) {
                motor_speed = *(int *)message->attribute.data.value;
                ESP_LOGI("MOTION", "Motor speed set to %d", motor_speed);
            }
            if (attr_id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID) {
                motor_direction = *(bool *)message->attribute.data.value;
                ESP_LOGI("MOTION", "Motor direction set to %s", motor_direction ? "Forward" : "Reverse");
            }
        }
        break;

    case OUTPUT_CONTROL_ENDPOINT:
        if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
            int output_id = attr_id - ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID;
            if (output_id >= 0 && output_id < 8) {
                outputs[output_id] = *(bool *)message->attribute.data.value;
                ESP_LOGI("OUTPUT", "Output %d set to %s", output_id, outputs[output_id] ? "On" : "Off");
            }
        }
        break;

    case IO_CONTROL_ENDPOINT:
        if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
            io_direction = *(bool *)message->attribute.data.value;
            ESP_LOGI("IO", "IO direction set to %s", io_direction ? "Output" : "Input");
        }
        break;

    case SOUND_CONTROL_ENDPOINT:
        if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
            if (attr_id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID) {
                is_playing = *(bool *)message->attribute.data.value;
                ESP_LOGI("SOUND", "%s audio playback", is_playing ? "Starting" : "Stopping");
            }
        } else if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_LEVEL_CONTROL) {
            if (attr_id == ESP_ZB_ZCL_ATTR_LEVEL_CONTROL_CURRENT_LEVEL_ID) {
                volume = *(int *)message->attribute.data.value;
                ESP_LOGI("SOUND", "Volume set to %d", volume);
            }
        } else if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_BASIC && attr_id == 0x000F) {  // Custom attribute for sound file
            strcpy(sound_file, (char *)message->attribute.data.value);
            ESP_LOGI("SOUND", "Sound file set to: %s", sound_file);
        }
        break;
    }

    return ret;
}

/********************* Arduino functions **************************/
void setup() {
  // Show power light
  rgbLedWrite(RGB_BUILTIN, 0, 10, 0);

  // Init Zigbee
  esp_zb_platform_config_t config = {
    .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
    .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
  };
  ESP_ERROR_CHECK(esp_zb_platform_config(&config));

  // Start Zigbee task
  xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
}

void loop() {
}


Who is online

Users browsing this forum: No registered users and 71 guests