Initial project for reptile feeder
Open multiple boxes to release crickets using 16 channel controller from ESP
This commit is contained in:
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
# PlatformIO
|
||||
.pio
|
||||
.vscode
|
||||
|
||||
# Build artifacts
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
*.elf
|
||||
*.bin
|
||||
*.hex
|
||||
*.map
|
||||
*.d
|
||||
|
||||
# Other
|
||||
*.pyc
|
||||
*.pyo
|
||||
__pycache__/
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
11
platformio.ini
Normal file
11
platformio.ini
Normal file
@ -0,0 +1,11 @@
|
||||
[env:esp32-c6-devkitc-1]
|
||||
platform = espressif32
|
||||
board = esp32-c6-devkitc-1
|
||||
framework = arduino, espidf
|
||||
monitor_speed = 115200
|
||||
lib_deps =
|
||||
adafruit/Adafruit PWM Servo Driver Library@2.4.1
|
||||
|
||||
build_flags =
|
||||
-DARDUINO_USB_MODE=1
|
||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||
180
src/main.cpp
Normal file
180
src/main.cpp
Normal file
@ -0,0 +1,180 @@
|
||||
#include <Arduino.h>
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_PWMServoDriver.h>
|
||||
#include "esp_zigbee_core.h"
|
||||
|
||||
// --- Configuration ---
|
||||
#define CHANNEL_COUNT 16 // 16 servos
|
||||
#define ZIGBEE_ENDPOINT_START 1
|
||||
|
||||
// PCA9685 Settings
|
||||
Adafruit_PWMServoDriver pca9685 = Adafruit_PWMServoDriver();
|
||||
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz
|
||||
#define SERVO_MIN_PULSE 150 // Corresponds to 0 degrees
|
||||
#define SERVO_MAX_PULSE 600 // Corresponds to 180 degrees
|
||||
#define SERVO_OPEN_ANGLE 90 // Angle in degrees for "open" state
|
||||
#define SERVO_CLOSED_ANGLE 0 // Angle in degrees for "closed" state
|
||||
|
||||
// --- Zigbee Device Setup ---
|
||||
// Basic Zigbee device configuration
|
||||
#define ESP_ZB_PRIMARY_CHANNEL_MASK (1l << 15) // Use Zigbee channel 15
|
||||
#define ESP_ZB_MANUFACTURER_NAME "T3-Labs"
|
||||
#define ESP_ZB_MODEL_NAME "ReptileFeeder.16x"
|
||||
|
||||
// --- Function Declarations ---
|
||||
void move_servo(uint8_t servo_num, uint8_t angle);
|
||||
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask);
|
||||
static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message);
|
||||
static esp_err_t zb_action_handler(esp_zb_core_action_signal_t *signal_struct);
|
||||
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct);
|
||||
|
||||
// --- Main Application ---
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
Wire.begin(); // Default I2C pins for ESP32-C6 are typically GPIO8/GPIO9
|
||||
|
||||
// Initialize PCA9685
|
||||
pca9685.begin();
|
||||
pca9685.setPWMFreq(SERVO_FREQ);
|
||||
Serial.println("PCA9685 Initialized. Setting all servos to closed position.");
|
||||
for (int i = 0; i < CHANNEL_COUNT; i++) {
|
||||
move_servo(i, SERVO_CLOSED_ANGLE);
|
||||
delay(50);
|
||||
}
|
||||
Serial.println("Servos initialized.");
|
||||
|
||||
// Initialize Zigbee stack
|
||||
esp_zb_cfg_t zb_cfg = ESP_ZB_DEFAULT_CFG();
|
||||
zb_cfg.esp_zb_role = ESP_ZB_DEVICE_TYPE_ROUTER;
|
||||
zb_cfg.install_code_policy = false;
|
||||
zb_cfg.nwk_cfg.max_children = 0; // Router doesn't need to manage children
|
||||
esp_zb_init(&zb_cfg);
|
||||
|
||||
// Create On/Off clusters for each servo
|
||||
for (int i = 0; i < CHANNEL_COUNT; ++i) {
|
||||
esp_zb_cluster_list_t *cluster_list = esp_zb_zcl_cluster_list_create();
|
||||
|
||||
// On/Off cluster
|
||||
esp_zb_on_off_cluster_cfg_t on_off_cfg;
|
||||
on_off_cfg.on_off = false; // Default to off
|
||||
esp_zb_attribute_list_t *on_off_cluster = esp_zb_on_off_cluster_create(&on_off_cfg);
|
||||
esp_zb_cluster_list_add_cluster(cluster_list, on_off_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
// Basic cluster
|
||||
esp_zb_basic_cluster_cfg_t basic_cfg;
|
||||
basic_cfg.zcl_version = ESP_ZB_ZCL_VERSION_DEFAULT;
|
||||
basic_cfg.power_source = ESP_ZB_ZCL_BASIC_POWER_SOURCE_UNKNOWN;
|
||||
esp_zb_attribute_list_t *basic_cluster = esp_zb_basic_cluster_create(&basic_cfg);
|
||||
esp_zb_cluster_list_add_cluster(cluster_list, basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
|
||||
|
||||
// Create an endpoint for the servo
|
||||
esp_zb_endpoint_config_t endpoint_cfg = {
|
||||
.endpoint = (uint8_t)(ZIGBEE_ENDPOINT_START + i),
|
||||
.app_profile_id = ESP_ZB_AF_HA_PROFILE_ID,
|
||||
.app_device_id = ESP_ZB_HA_ON_OFF_LIGHT_DEVICE_ID,
|
||||
.app_device_version = 0
|
||||
};
|
||||
esp_zb_ep_list_add_ep(esp_zb_get_ep_list(), cluster_list, endpoint_cfg);
|
||||
}
|
||||
|
||||
// Set Zigbee stack signal handler
|
||||
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
|
||||
ESP_ERROR_CHECK(esp_zb_start(false));
|
||||
esp_zb_set_app_signal_handler(esp_zb_app_signal_handler);
|
||||
|
||||
Serial.println("Zigbee Initialized and Started.");
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// The Zigbee stack runs in its own task.
|
||||
// The loop can be used for other non-blocking tasks if needed.
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// --- Function Definitions ---
|
||||
|
||||
/**
|
||||
* @brief Moves a servo to a specific angle.
|
||||
* @param servo_num The servo channel (0-15).
|
||||
* @param angle The target angle (0-180).
|
||||
*/
|
||||
void move_servo(uint8_t servo_num, uint8_t angle)
|
||||
{
|
||||
if (servo_num >= CHANNEL_COUNT || angle > 180) {
|
||||
return; // Invalid input
|
||||
}
|
||||
uint16_t pulselen = map(angle, 0, 180, SERVO_MIN_PULSE, SERVO_MAX_PULSE);
|
||||
pca9685.setPWM(servo_num, 0, pulselen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Zigbee application signal handler.
|
||||
*/
|
||||
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
|
||||
{
|
||||
uint32_t *p_sg_p = signal_struct->p_app_signal;
|
||||
esp_err_t err_status = signal_struct->esp_err_status;
|
||||
|
||||
switch (signal_struct->signal_type) {
|
||||
case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
|
||||
Serial.println("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) {
|
||||
Serial.println("Device started, commissioning.");
|
||||
esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
|
||||
} else {
|
||||
Serial.println("Failed to initialize Zigbee stack");
|
||||
}
|
||||
break;
|
||||
case ESP_ZB_BDB_SIGNAL_STEERING_COMPLETE:
|
||||
if (err_status == ESP_OK) {
|
||||
esp_zb_ieee_addr_t extended_pan_id;
|
||||
esp_zb_get_extended_pan_id(extended_pan_id);
|
||||
Serial.printf("Successfully joined network. PAN ID: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
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]);
|
||||
} else {
|
||||
Serial.printf("Network steering failed, status: %d\n", err_status);
|
||||
}
|
||||
break;
|
||||
case ESP_ZB_ZCL_SIGNAL_SET_ATTR_VALUE:
|
||||
zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *)p_sg_p);
|
||||
break;
|
||||
default:
|
||||
// Serial.printf("Received Zigbee signal: %d\n", signal_struct->signal_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handles incoming Zigbee attribute changes (e.g., On/Off commands).
|
||||
*/
|
||||
static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message)
|
||||
{
|
||||
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t endpoint = message->info.dst_endpoint;
|
||||
uint16_t cluster_id = message->info.cluster;
|
||||
uint16_t attr_id = message->attribute.id;
|
||||
bool is_on = false;
|
||||
|
||||
if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF && attr_id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID) {
|
||||
is_on = *(bool *)message->attribute.data.value;
|
||||
uint8_t servo_num = endpoint - ZIGBEE_ENDPOINT_START;
|
||||
|
||||
Serial.printf("Endpoint %d (Servo %d) command: %s\n", endpoint, servo_num, is_on ? "ON" : "OFF");
|
||||
|
||||
if (servo_num < CHANNEL_COUNT) {
|
||||
move_servo(servo_num, is_on ? SERVO_OPEN_ANGLE : SERVO_CLOSED_ANGLE);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user