Initial commit of existing code

This commit is contained in:
Bora 2025-12-28 20:38:58 +01:00
commit 59defedb8e
137 changed files with 21411 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.hex
build/
.DS_Store

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,53 @@
#include <Arduino.h>
HardwareSerial mySerial(1);
void sendPacket(const uint8_t *packet, size_t length) {
Serial.print("TX: ");
for (size_t i = 0; i < length; i++) {
Serial.printf("%02X ", packet[i]);
mySerial.write(packet[i]);
}
Serial.println();
mySerial.flush();
}
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("Starting HS2xx3A test (Binary mode only)...");
// RX=17, TX=16 for ESP32-C6, 115200 baud
mySerial.begin(115200, SERIAL_8N1, 17, 16);
delay(500);
// Try turning LED OFF
// const uint8_t ledOffPacket[] = { 0x53, 0x59, 0x0A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 };
// Serial.println("Turning LED OFF...");
// sendPacket(ledOffPacket, sizeof(ledOffPacket));
}
void loop() {
// Read data from sensor
while (mySerial.available()) {
String line = mySerial.readStringUntil('\n');
Serial.print("RX: ");
Serial.println(line);
if (line.startsWith("$JYBSS")) {
if (line.indexOf(",1") >= 0) {
Serial.println("Presence Detected: YES");
} else {
Serial.println("Presence Detected: NO");
}
}
if (line.startsWith("$JYRPO")) {
if (line.indexOf(",1") >= 0) {
Serial.println("JYRPO Detected: YES");
} else {
Serial.println("JYRPO Detected: NO");
}
}
}
}

View File

@ -0,0 +1,67 @@
#include "LeapmmwRadar.h"
LeapmmwRadar::LeapmmwRadar(HardwareSerial* serial, Stream* debugStream) {
_serial = serial;
_debug = debugStream;
}
void LeapmmwRadar::begin(uint32_t baudrate) {
_serial->begin(baudrate);
if (_debug) _debug->println("[Leapmmw] Serial started");
}
// LED ON command (factory: 0x55 0xAA 0x03 0x03 0x01 0x00)
void LeapmmwRadar::ledOn() {
const uint8_t cmd[] = { 0x55, 0xAA, 0x03, 0x03, 0x01, 0x00 };
sendCommand(cmd, sizeof(cmd));
}
// LED OFF command (factory: 0x55 0xAA 0x03 0x03 0x00 0x00)
void LeapmmwRadar::ledOff() {
const uint8_t cmd[] = { 0x55, 0xAA, 0x03, 0x03, 0x00, 0x00 };
sendCommand(cmd, sizeof(cmd));
}
void LeapmmwRadar::sendCommand(const uint8_t* cmd, size_t len) {
if (_debug) {
_debug->print("[TX] ");
for (size_t i = 0; i < len; i++) {
_debug->printf("0x%02X ", cmd[i]);
}
_debug->println();
}
_serial->write(cmd, len);
}
// Parse presence detection from response
int LeapmmwRadar::readPresence() {
static uint8_t buffer[32];
static size_t index = 0;
while (_serial->available()) {
uint8_t b = _serial->read();
if (_debug) {
_debug->printf("[RX] 0x%02X\n", b);
}
if (index == 0 && b != 0x55) continue;
if (index == 1 && b != 0xAA) {
index = 0;
continue;
}
buffer[index++] = b;
// Minimum valid frame: 8 bytes for presence data
if (index >= 8) {
if (buffer[0] == 0x55 && buffer[1] == 0xAA && buffer[2] == 0x03 && buffer[3] == 0x01) {
int presence = buffer[4]; // 0x00 = no movement, 0x01 = movement
index = 0;
return presence;
} else {
index = 0; // unrecognized frame
}
}
}
return -1; // no new data
}

View File

@ -0,0 +1,20 @@
#ifndef LEAPMMW_RADAR_H
#define LEAPMMW_RADAR_H
#include <Arduino.h>
class LeapmmwRadar {
public:
LeapmmwRadar(HardwareSerial* serial, Stream* debugStream = nullptr);
void begin(uint32_t baudrate = 115200);
void ledOn();
void ledOff();
int readPresence(); // returns 1 = movement, 0 = no movement, -1 = nothing new
private:
HardwareSerial* _serial;
Stream* _debug;
void sendCommand(const uint8_t* cmd, size_t len);
};
#endif

View File

@ -0,0 +1,63 @@
// hs2xx3a.cpp
#include "hs2xx3a.h"
HS2xx3A::HS2xx3A(HardwareSerial &serial) : serial_(serial) {}
void HS2xx3A::begin() {
serial_.begin(115200);
}
void HS2xx3A::sendCommand(const uint8_t *cmd, size_t len) {
Serial.print("TX: ");
for (size_t i = 0; i < len; ++i) {
Serial.printf("%02X ", cmd[i]);
}
Serial.println();
serial_.write(cmd, len);
serial_.flush();
}
bool HS2xx3A::readResponse(uint8_t *buffer, size_t expected_len) {
unsigned long start = millis();
size_t index = 0;
while (millis() - start < 200 && index < expected_len) {
if (serial_.available()) {
buffer[index++] = serial_.read();
}
}
if (index == expected_len) {
Serial.print("RX: ");
for (size_t i = 0; i < expected_len; ++i) {
Serial.printf("%02X ", buffer[i]);
}
Serial.println();
return true;
}
Serial.println("RX Timeout or incomplete response");
return false;
}
void HS2xx3A::setLED(bool enable) {
const uint8_t cmd_on[] = {0x53, 0x59, 0x0A, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00};
const uint8_t cmd_off[] = {0x53, 0x59, 0x0A, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00};
sendCommand(enable ? cmd_on : cmd_off, sizeof(cmd_on));
// Optional: read back ACK if needed
uint8_t buffer[8];
readResponse(buffer, 8);
}
bool HS2xx3A::checkForPresence() {
const uint8_t cmd_presence[] = {0x53, 0x59, 0x01, 0x00};
sendCommand(cmd_presence, sizeof(cmd_presence));
uint8_t response[8] = {0};
if (readResponse(response, 8)) {
// Validate packet header
if (response[0] == 0x53 && response[1] == 0x59 && response[2] == 0x01) {
return response[4] == 1; // response[4] == 1 means presence detected
}
}
return false;
}

View File

@ -0,0 +1,17 @@
// hs2xx3a.h
#pragma once
#include <Arduino.h>
class HS2xx3A {
public:
HS2xx3A(HardwareSerial &serial);
void begin();
bool checkForPresence();
void setLED(bool enable);
private:
HardwareSerial &serial_;
void sendCommand(const uint8_t *cmd, size_t len);
bool readResponse(uint8_t *buffer, size_t expected_len);
};

View File

@ -0,0 +1,64 @@
#include "mmWaveRadarCustom.h"
mmWaveRadarCustom::mmWaveRadarCustom(HardwareSerial* serial, Stream* debugStream) {
_serial = serial;
_debug = debugStream;
}
void mmWaveRadarCustom::begin(uint32_t baudrate) {
_serial->begin(baudrate);
if (_debug) _debug->println("[Radar] Serial started");
}
void mmWaveRadarCustom::ledOn() {
const uint8_t cmd[] = { 0xFD, 0xFC, 0x05, 0x00, 0x01, 0x01, 0x00 };
sendCommand(cmd, sizeof(cmd));
}
void mmWaveRadarCustom::ledOff() {
const uint8_t cmd[] = { 0xFD, 0xFC, 0x05, 0x00, 0x01, 0x00, 0x00 };
sendCommand(cmd, sizeof(cmd));
}
void mmWaveRadarCustom::sendCommand(const uint8_t* cmd, size_t len) {
if (_debug) {
_debug->print("[Radar] TX: ");
for (size_t i = 0; i < len; i++) {
_debug->print("0x");
if (cmd[i] < 0x10) _debug->print("0");
_debug->print(cmd[i], HEX);
_debug->print(" ");
}
_debug->println();
}
_serial->write(cmd, len);
}
int mmWaveRadarCustom::readPresence() {
if (_serial->available()) {
uint8_t header = _serial->read();
if (header == 0xAA) {
delay(2); // wait for more bytes
if (_serial->available() >= 4) {
uint8_t type = _serial->read(); // e.g., 0x01
uint8_t len = _serial->read(); // e.g., 0x01
uint8_t data = _serial->read(); // 0x00 = no motion, 0x01 = motion
uint8_t checksum = _serial->read(); // discard or validate
if (_debug) {
_debug->print("[Radar] RX: 0xAA 0x");
_debug->print(type, HEX);
_debug->print(" 0x");
_debug->print(len, HEX);
_debug->print(" 0x");
_debug->print(data, HEX);
_debug->print(" 0x");
_debug->println(checksum, HEX);
}
return (data == 0x01) ? 1 : 0;
}
}
}
return -1; // nothing to read
}

View File

@ -0,0 +1,20 @@
#ifndef MMWAVE_RADAR_CUSTOM_H
#define MMWAVE_RADAR_CUSTOM_H
#include <Arduino.h>
class mmWaveRadarCustom {
public:
mmWaveRadarCustom(HardwareSerial* serial, Stream* debugStream = nullptr);
void begin(uint32_t baudrate = 115200);
void ledOn();
void ledOff();
int readPresence(); // 1 = movement, 0 = no movement
private:
HardwareSerial* _serial;
Stream* _debug;
void sendCommand(const uint8_t* cmd, size_t len);
};
#endif

View File

@ -0,0 +1,71 @@
/*
https://wiki.dfrobot.com/mmWave_Radar_Human_Presence_Detection_SKU_SEN0395#Tutorial%20for%20FireBeetle%20ESP32
https://www.reddit.com/r/homeassistant/comments/y0bk16/how_to_reliable_room_presence_sensor_using/
SEN0395 (Leapmmw HS2xx3A series)
https://community.home-assistant.io/t/mmwave-presence-detection-esphome-style/382778
https://github.com/hjmcnew/esphome-hs2xx3a-custom-component/tree/main
It's just a rebranded LeapMMW
https://www.reddit.com/r/homeassistant/comments/wvug41/any_alternative_to_the_dfrobot_sen0395_mmwave/
https://www.youtube.com/watch?v=Viqvx7hMMJs
https://www.youtube.com/watch?v=pTYclnk9W4M
https://www.youtube.com/watch?v=uXrYwfS0Pqw
*/
/*!
@file DFRobot_mmWave_Radar.ino
@ Read whether there is people or object moving in the detection range of the sensor.
@ The sensor detection range and output delay time can be configured. Also you can restore the sensor to factory default settings.
@n Experimental phenomenon: When the sensor starts successfully, 0 or 1 will be printed on the serial monitor.
@ 0 means that there is no human or object moving in sensing area, 1 means the oppposite.
@copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
@licence The MIT License (MIT)
@author [huyujie](yujie.hu@dfrobot.com)
@version V1.0
@date 2020-3-25
@https://github.com/DFRobot
*/
#include <DFRobot_mmWave_Radar.h>
HardwareSerial mySerial(1);
DFRobot_mmWave_Radar sensor(&mySerial);
int ledPin = 0;
void setup()
{
Serial.begin(115200);
delay(100);
Serial.println("Tutoduino Zigbee temperature sensor start!");
mySerial.begin(115200, SERIAL_8N1, 17, 16);//RX,TX
pinMode(ledPin, OUTPUT);
sensor.factoryReset(); //Restore to the factory settings
sensor.DetRangeCfg(0, 1); //The detection range is as far as 9m
sensor.OutputLatency(0, 0);
}
void loop()
{
int val = sensor.readPresenceDetection();
digitalWrite(ledPin, val);
Serial.println(val);
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,167 @@
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
void setRange(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setRange 0 3");
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setSensitivity(int sensitivity){
Serial.println("setSensitivity");
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setSensitivity " + String(sensitivity));
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setLatency(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setLatency 0.5 1");
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setLedMode(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setLedMode 1 1"); // turn off LED
// mmWaveSerial.println("setLedMode 1 0"); // turn on LED
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setUartOutput(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setUartOutput 2 1 1 2");
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void turnOnSensor(){
Serial.println("turnOnSensor");
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setUartOutput 1 0");
delay(20);
mmWaveSerial.println("setUartOutput 2 1 1 2");
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void readOutput(){
String line = mmWaveSerial.readStringUntil('\n');
Serial.println("");
Serial.print("RX: ");
Serial.println(line);
if (line.startsWith("$JYBSS")) {
if (line.indexOf(",1") >= 0) {
Serial.println("Presence Detected: YES");
} else {
Serial.println("Presence Detected: NO");
}
}
}
void setup() {
Serial.println(" -------------------------- Starting mmWave Presence + Distance --------------------------");
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(1000); // Give sensor time to boot
// turnOnSensor();
delay(1000); // Give sensor time to boot
setSensitivity(3);
setUartOutput();
delay(200);
}
void loop() {
for (int i = 0; i < numCommands; i++) {
const char* cmd = commands[i];
// Send command string via UART
mmWaveSerial.println(cmd);
Serial.println("");
Serial.print("Sent: ");
Serial.println(cmd);
unsigned long startTime = millis();
// Wait for 1 second while reading any available response
while (millis() - startTime < 1000) {
if (mmWaveSerial.available()) {
char c = mmWaveSerial.read();
Serial.write(c); // Print response to Serial Monitor
}
}
readOutput();
delay(10); // small delay before next command (optional)
}
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,333 @@
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
void setRange(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setRange 0 3");
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setSensitivity(int sensitivity){
Serial.println("setSensitivity");
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setSensitivity " + String(sensitivity));
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
Serial.println("setSensitivity finished");
}
void setLatency(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setLatency 0.5 1");
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setLedMode(){
mmWaveSerial.println("sensorStop");
delay(20);
mmWaveSerial.println("setLedMode 1 1"); // turn off LED
// mmWaveSerial.println("setLedMode 1 0"); // turn on LED
delay(20);
mmWaveSerial.println("saveConfig");
delay(20);
mmWaveSerial.println("sensorStart");
delay(20);
}
void setUartOutput(){
// setUartOutput <par1> <par2> [<par3> <par4>]
// Parameter Description
// par1 Data/Message Type:
// 1 = Detection result ($JYBSS)
// 2 = Point cloud target ($JYRPO)
// par2 Enable/Disable Output:
// 0 = Disable this message type
// 1 = Enable this message type
// par3 Output Trigger Mode:
// 0 = Periodic output based on par4
// 1 = Output immediately when data changes, otherwise output per par4
// par4 Output Interval or Passive Mode Trigger:
// 0.0251500 = Time interval (seconds) for active output
// >1500 = Passive mode (no auto-output; only on getOutput command)
// Note: If par3 and par4 are omitted, only par1 and par2 will be set.
// $JYBSS,<par1>,<par2>,<par3>,<par4>*
// Parameter Description
// par1 Detection result:
// 0 = No presence
// 1 = Presence detected (includes still, motion, micro-motion)
// par2 Placeholder (reserved, empty space)
// par3 Placeholder (reserved, empty space)
// par4 Placeholder (reserved, empty space)
// $JYBSS,1, , ,* → Human presence detected
// $JYBSS,0, , ,* → No presence detected
// $JYRPO,<par1>,<par2>,<par3>,<par4>,<par5>,<par6>,<par7>*
// Parameter Description
// par1 Total number of detected targets (18)
// par2 Current target index (1 to par1)
// par3 Distance of current target (in meters)
// par4 Placeholder (reserved, empty space)
// par5 Signal-to-noise ratio (SNR) of the target (float, higher = more reliable)
// par6 Placeholder (reserved, empty space)
// par7 Placeholder (reserved, empty space)
// $JYRPO,4,1,0.882, ,1.562, ,*
// $JYRPO,4,2,1.773, ,0.718, ,*
// $JYRPO,4,3,3.687, ,1.468, ,*
// $JYRPO,4,4,4.281, ,0.687, ,*
Serial.println("setUartOutput started");
// mmWaveSerial.println("sensorStop");
// delay(100);
// mmWaveSerial.println("setUartOutput 1 1 1 2"); // Enable presence detection output, send immediately when changed, otherwise every 2 seconds:
// mmWaveSerial.println("setUartOutput 1 1 0 1600"); // Disable periodic output, switch to passive mode (query only)
mmWaveSerial.println("setUartOutput 1 0"); // Disable presence detection output
delay(100);
line = mmWaveSerial.readStringUntil('\n');
Serial.println("RX: " + line);
mmWaveSerial.println("setUartOutput 2 1"); // Enable point cloud data output ($JYRPO):
delay(100);
line = mmWaveSerial.readStringUntil('\n');
Serial.println("RX: " + line);
mmWaveSerial.println("saveConfig");
delay(100);
mmWaveSerial.println("sensorStart");
delay(100);
Serial.println("setUartOutput finished");
}
void turnOnSensor_old(int sensitivity){
Serial.println("turnOnSensor");
mmWaveSerial.println("sensorStop");
delay(100);
mmWaveSerial.println("resetCfg");
delay(100);
mmWaveSerial.println("setUartOutput 1 0"); // Disable presence detection output
delay(100);
mmWaveSerial.println("setUartOutput 2 1"); // Enable point cloud data output ($JYRPO):
delay(100);
mmWaveSerial.println("setSensitivity " + String(sensitivity));
delay(100);
mmWaveSerial.println("setRange 0 3");
delay(100);
mmWaveSerial.println("setLedMode 1 1"); // turn off LED
// mmWaveSerial.println("setLedMode 1 0"); // turn on LED
delay(100);
mmWaveSerial.println("setGpioMode 2 1"); // HIGH when someone is present
delay(100);
mmWaveSerial.println("saveConfig");
delay(100);
mmWaveSerial.println("sensorStart");
delay(100);
}
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Disable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void readOutput(){
String line = mmWaveSerial.readStringUntil('\n');
Serial.println("");
Serial.print("RX: ");
Serial.println(line);
if (line.startsWith("$JYBSS")) {
if (line.indexOf(",1") >= 0) {
Serial.println("Presence Detected: YES");
} else {
Serial.println("Presence Detected: NO");
}
}
}
#define SERIAL_BAUD 115200
#define SENSOR_SERIAL Serial1 // Adjust depending on your board (e.g. Serial2)
enum MessageType { NONE, JYBSS, JYRPO };
struct PresenceResult {
bool presence; // true if someone is detected
};
struct PointCloudTarget {
int totalTargets;
int targetIndex;
float distance;
float snr;
};
// --- FUNCTION DECLARATION ---
MessageType parseSensorMessage(String input, PresenceResult &presence, PointCloudTarget &target);
void setup() {
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
Serial.println(" -------------------------- Starting mmWave Presence + Distance --------------------------");
// delay(1000); // Give sensor time to boot
turnOnSensor(7);
delay(1000); // Give sensor time to boot
// setUartOutput();
// setSensitivity(7);
// String cmd = "getUartOutput"
// mmWaveSerial.println(cmd);
pinMode(23, INPUT_PULLDOWN); // connected to GPIO2 of the sensor; HIGH in case of presence detection
delay(200);
}
unsigned long startTime = millis();
void loop() {
// Read data from sensor
while (mmWaveSerial.available()) {
String line = mmWaveSerial.readStringUntil('\n');
line.trim(); // Remove whitespace and newline chars
if (line.length() == 0) {
Serial.println("RX empty.");
return;
}
Serial.print("RX: ");
Serial.println(line);
// Check for presence detection
// if (line.startsWith("$JYBSS")) {
// int presence = line.charAt(8) - '0'; // Fast access to value
// Serial.print("Presence Detected: ");
// Serial.println(presence == 1 ? "YES" : "NO");
// }
if (line.startsWith("$JYBSS")) {
if (line.indexOf(",1") >= 0) {
Serial.println("Presence Detected: YES");
} else {
Serial.println("Presence Detected: NO");
}
}
// Check for point cloud detection
else if (line.startsWith("$JYRPO")) {
int comma1 = line.indexOf(',', 7);
int comma2 = line.indexOf(',', comma1 + 1);
int targetIndex = line.substring(comma1 + 1, comma2).toInt();
Serial.print("JYRPO Target Index: ");
Serial.println(targetIndex);
}
}
// Wait for 1 second while reading any available response
if (millis() - startTime > 1000) {
int sensorVal = digitalRead(23);
Serial.print("GPIO2 state: ");
Serial.println(sensorVal);
startTime = millis();
}
}

View File

@ -0,0 +1,142 @@
/*
* DFRobot Beetle ESP32-C6 I2C Diagnostic Tool
* Tests ALL possible I2C pin combinations + pullup detection
* For BME680(0x76/77), SHT40(0x44/45), SGP40(0x59), T6713(0x15)
*/
#include "Wire.h"
/*
* DFRobot Beetle ESP32-C6 I2C Diagnostic Tool (Fixed)
* Tests ALL possible I2C pin combinations + pullup detection
*/
void setup() {
Serial.begin(115200);
delay(2000);
Serial.println("\n=== DFRobot Beetle ESP32-C6 I2C DIAGNOSTIC v1.1 ===");
Serial.println("Expected devices: BME680(0x76/77), SHT40(0x44/45), SGP40(0x59), T6713(0x15)");
Serial.println();
testAllI2CPinCombinations();
testPullupsAndClock();
printPinoutGuide();
}
void loop() {
// Continuous scan on button press (BOOT_PIN)
if (digitalRead(BOOT_PIN) == LOW) {
delay(200);
if (digitalRead(BOOT_PIN) == LOW) {
Serial.println("\n=== RE-SCAN ALL PINS ===");
testAllI2CPinCombinations();
Serial.println("=== END RE-SCAN ===\n");
}
}
delay(100);
}
void testAllI2CPinCombinations() {
// Common DFRobot Beetle C6 pinouts
struct PinTest {
int sda, scl;
const char* name;
};
PinTest tests[] = {
{21, 22, "GPIO21/22 (Main code)"},
{22, 21, "GPIO22/21 (SWAPPED)"},
{19, 20, "GPIO19/9 (Alternate)"},
{20, 19, "GPIO20/19 (Node11 style)"},
{-1, -1, "DEFAULT (GPIO8/9)"} // Wire.begin() only
};
for (int i = 0; i < 5; i++) {
Serial.printf("\n--- %s ---\n", tests[i].name);
if (tests[i].sda == -1) {
Wire.begin(); // Default pins
} else {
Wire.begin(tests[i].sda, tests[i].scl);
}
Wire.setClock(100000); // 100kHz
scanBusAndReport(tests[i].sda, tests[i].scl);
}
}
void scanBusAndReport(int sdaPin, int sclPin) {
Serial.printf("Clock: %lu Hz | Testing SDA=GPIO%d SCL=GPIO%d\n", Wire.getClock(), sdaPin, sclPin);
int deviceCount = 0;
for (byte addr = 1; addr < 127; addr++) {
Wire.beginTransmission(addr);
byte error = Wire.endTransmission();
if (error == 0) {
Serial.printf(" ✓ 0x%02X ", addr);
deviceCount++;
// Known sensors
switch(addr) {
case 0x15: Serial.print("(T6713)"); break;
case 0x44: Serial.print("(SHT40)"); break;
case 0x45: Serial.print("(SHT40 alt)"); break;
case 0x59: Serial.print("(SGP40)"); break;
case 0x76: Serial.print("(BME680)"); break;
case 0x77: Serial.print("(BME680 alt)"); break;
default: Serial.print("(Unknown)");
}
Serial.println();
}
}
Serial.printf(" Total devices: %d\n", deviceCount);
if (deviceCount == 0) {
Serial.println(" ⚠️ NO DEVICES - Check power/pullups/wiring!");
}
delay(500); // Stabilize bus
}
void testPullupsAndClock() {
Serial.println("\n--- PULLUP & CLOCK TEST ---");
Wire.begin(6, 20); // Most likely pins
// Test clock speeds
uint32_t clocks[] = {100000, 400000, 1000000};
for (int i = 0; i < 3; i++) {
Wire.setClock(clocks[i]);
Serial.printf("Clock %lu Hz: ", Wire.getClock());
// Quick scan for 0x59 (SGP40)
Wire.beginTransmission(0x59);
if (Wire.endTransmission() == 0) {
Serial.println("✓ SGP40 OK");
} else {
Serial.println("✗ No ACK");
}
}
}
void printPinoutGuide() {
Serial.println("\n=== WIRING GUIDE ===");
Serial.println("ESP32-C6 → Sensors");
Serial.println("GPIO6 → SDA (all sensors)");
Serial.println("GPIO20 → SCL (all sensors)");
Serial.println("3.3V → VCC (NOT 5V!)");
Serial.println("GND → GND");
Serial.println("");
Serial.println("REQUIRED: 4.7kΩ pullups");
Serial.println("SDA ───┬── 4.7kΩ ─── 3.3V");
Serial.println("");
Serial.println(" Sensors");
Serial.println("SCL ───┬── 4.7kΩ ─── 3.3V");
Serial.println("");
Serial.println("=== TROUBLESHOOTING ===");
Serial.println("1. Measure 3.3V at sensor VCC pins");
Serial.println("2. Check SDA/SCL continuity with multimeter");
Serial.println("3. Add 4.7kΩ pullups if missing");
Serial.println("4. Press BOOT button to re-scan");
Serial.println("5. Verify NO 5V power (sensors damaged!)");
Serial.println("=======================\n");
}

View File

@ -0,0 +1,44 @@
#include <Wire.h>
int I2C_SDA = 20;
int I2C_SCL = 18;
void setup() {
Wire.begin(I2C_SDA, I2C_SCL);
Serial.begin(115200);
Serial.println("\nI2C Scanner");
}
void loop() {
byte error, address;
int nDevices;
Serial.println("Scanning...");
nDevices = 0;
for(address = 1; address < 127; address++ ) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device found at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
nDevices++;
}
else if (error==4) {
Serial.print("Unknow error at address 0x");
if (address<16) {
Serial.print("0");
}
Serial.println(address,HEX);
}
}
if (nDevices == 0) {
Serial.println("No I2C devices found\n");
}
else {
Serial.println("done\n");
}
delay(5000);
}

View File

@ -0,0 +1,414 @@
/*
Seeed Xiao ESP32-C6
LED: LED_BUILTIN, on when LOW
Partition Scheme: Zigbee ZCZR 4MB with spiffs
Zigbee Mode: Zigbee ZR/ZC (coordinator,router)
#define LEDPIN LED_BUILTIN
#define I2CSDAPIN 20
#define I2CSCLPIN 18
*/
#ifndef ZIGBEE_MODE_ZCZR
#error Zigbee coordinator/router mode is not selected in Tools->Zigbee mode
#endif
#define DEBUG_TRACE // Comment out DEBUG_TRACE to reduce normal-path logging
#include "Zigbee.h"
#include "Wire.h"
#include "bme68xLibrary.h"
#include "Adafruit_SHT4x.h"
#include "Adafruit_SGP40.h"
// -----------------------------------------------------------------------------
// Hardware definitions (shared I2C bus)
// -----------------------------------------------------------------------------
#define LED_PIN LED_BUILTIN
#define I2C_SDA_PIN 20
#define I2C_SCL_PIN 18
// Endpoint numbers reassigned for new sensors
#define BME_TEMP_ENDPOINT 10
#define BME_ANALOG_ENDPOINT 11
#define SHT_SGP_TEMP_ENDPOINT 12
#define SGP_ANALOG_ENDPOINT 13
// Sensor objects
Bme68x bme;
Adafruit_SHT4x sht4;
Adafruit_SGP40 sgp;
// Zigbee sensor objects
ZigbeeTempSensor zbTempSensorBME(BME_TEMP_ENDPOINT);
ZigbeeAnalog zbAnalogBME(BME_ANALOG_ENDPOINT);
ZigbeeTempSensor zbTempSensorSHTSGP(SHT_SGP_TEMP_ENDPOINT);
ZigbeeAnalog zbAnalogSGP(SGP_ANALOG_ENDPOINT);
// -----------------------------------------------------------------------------
// Health watchdog globals updated for new sensors
// -----------------------------------------------------------------------------
SemaphoreHandle_t i2cMutex;
bool bmeFirstOk = false;
bool shtsgpFirstOk = false;
// Last-change tracking
float lastTempBme = NAN, lastHumBme = NAN, lastGasRes = NAN;
unsigned long lastTempBmeChangeMs = 0, lastHumBmeChangeMs = 0, lastGasResChangeMs = 0;
float lastTempSht = NAN, lastHumSht = NAN, lastRawSgp = NAN;
unsigned long lastTempShtChangeMs = 0, lastHumShtChangeMs = 0, lastRawSgpChangeMs = 0;
unsigned long lastZigbeeOkMs = 0;
// Error counters
int bmeErrorCount = 0, shtsgpErrorCount = 0;
const int maxI2cErrors = 10;
// Watchdog thresholds
const unsigned long WATCHDOG_PERIOD_MS = 10000; // 10s
const unsigned long SENSORS_STUCK_TIMEOUT_MS = 30UL * 60UL * 1000UL; // 30min
const unsigned long ZIGBEE_DOWN_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10min
// -----------------------------------------------------------------------------
// Zigbee health touch after successful reports
// -----------------------------------------------------------------------------
inline void zigbeeHealthTouch() {
if (Zigbee.connected()) lastZigbeeOkMs = millis();
}
// -----------------------------------------------------------------------------
// Helper: I2C guarded execution (use mutex)
// -----------------------------------------------------------------------------
#include <functional>
bool withI2C(std::function<bool(void)> fn, uint32_t timeoutMs = 100) {
if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(timeoutMs)) == pdTRUE) {
bool ok = fn();
xSemaphoreGive(i2cMutex);
return ok;
}
return false;
}
// -----------------------------------------------------------------------------
// BME68x task adapted, endpoint 10/11
// -----------------------------------------------------------------------------
static void bme680sensorvalueupdate(void *arg) {
for (;;) {
float temperature = NAN, humidity = NAN, gasResistance = NAN;
bme68xData data;
bool ok = withI2C([&]() {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature;
humidity = data.humidity;
gasResistance = data.gas_resistance;
return true;
}
return false;
});
if (ok) {
bmeErrorCount = 0;
unsigned long now = millis();
if (!bmeFirstOk) {
bmeFirstOk = true;
lastTempBme = temperature;
lastHumBme = humidity;
lastGasRes = gasResistance;
lastTempBmeChangeMs = lastHumBmeChangeMs = lastGasResChangeMs = now;
} else {
if (temperature != lastTempBme) {
lastTempBme = temperature;
lastTempBmeChangeMs = now;
}
if (humidity != lastHumBme) {
lastHumBme = humidity;
lastHumBmeChangeMs = now;
}
if (gasResistance != lastGasRes) {
lastGasRes = gasResistance;
lastGasResChangeMs = now;
}
}
zbTempSensorBME.setTemperature(temperature);
zbTempSensorBME.setHumidity(humidity);
// zbTempSensorBME.report();
zigbeeHealthTouch();
zbAnalogBME.setAnalogInput(round(gasResistance));
// zbAnalogBME.reportAnalogInput();
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.printf("BME68x T:%.2fC H:%.2f Gas:%.2f ohm\n", temperature, humidity, gasResistance);
#endif
} else {
bmeErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("BME68x read error or I2C busy.");
#endif
if (bmeErrorCount >= maxI2cErrors) {
Serial.println("BME68x too many errors, restarting...");
delay(200);
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// SHT40SGP40 task from Node11, endpoint 12/13
// -----------------------------------------------------------------------------
static void sgp40sht40sensorvalueupdate(void *arg) {
for (;;) {
float currentTemp = NAN, currentHum = NAN;
uint16_t currentRaw = NAN;
bool ok = withI2C([&]() {
sensors_event_t humidity, temp;
if (!sht4.getEvent(&humidity, &temp)) return false;
currentTemp = temp.temperature;
currentHum = humidity.relative_humidity;
currentRaw = sgp.measureRaw(currentTemp, currentHum);
return true;
});
if (ok) {
shtsgpErrorCount = 0;
unsigned long now = millis();
if (!shtsgpFirstOk) {
shtsgpFirstOk = true;
lastTempSht = currentTemp;
lastHumSht = currentHum;
lastRawSgp = currentRaw;
lastTempShtChangeMs = lastHumShtChangeMs = lastRawSgpChangeMs = now;
} else {
if (currentTemp != lastTempSht) {
lastTempSht = currentTemp;
lastTempShtChangeMs = now;
}
if (currentHum != lastHumSht) {
lastHumSht = currentHum;
lastHumShtChangeMs = now;
}
if (currentRaw != lastRawSgp) {
lastRawSgp = currentRaw;
lastRawSgpChangeMs = now;
}
}
zbTempSensorSHTSGP.setTemperature(currentTemp);
zbTempSensorSHTSGP.setHumidity(currentHum);
// zbTempSensorSHTSGP.report();
zigbeeHealthTouch();
zbAnalogSGP.setAnalogInput(round(currentRaw));
// zbAnalogSGP.reportAnalogInput();
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.printf("SHT40SGP40 T:%.2fC H:%.2f Raw:%d\n", currentTemp, currentHum, currentRaw);
#endif
} else {
shtsgpErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("SHT40SGP40 read error or I2C busy.");
#endif
if (shtsgpErrorCount >= maxI2cErrors) {
Serial.println("SHT40SGP40 too many errors, restarting...");
delay(200);
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// Watchdog task updated checks for new sensors
// -----------------------------------------------------------------------------
void watchdogTask(void *param) {
for (;;) {
#ifdef DEBUG_TRACE
Serial.println("Watchdog ran.");
#endif
unsigned long now = millis();
bool zigbeeBad = !Zigbee.connected() || (now - lastZigbeeOkMs > ZIGBEE_DOWN_TIMEOUT_MS);
bool bmeStuck = bmeFirstOk &&
(now - lastTempBmeChangeMs > SENSORS_STUCK_TIMEOUT_MS ||
now - lastHumBmeChangeMs > SENSORS_STUCK_TIMEOUT_MS ||
now - lastGasResChangeMs > SENSORS_STUCK_TIMEOUT_MS);
bool shtsgpStuck = shtsgpFirstOk &&
(now - lastTempShtChangeMs > SENSORS_STUCK_TIMEOUT_MS ||
now - lastHumShtChangeMs > SENSORS_STUCK_TIMEOUT_MS ||
now - lastRawSgpChangeMs > SENSORS_STUCK_TIMEOUT_MS);
if (zigbeeBad || bmeStuck || shtsgpStuck) {
Serial.println("Watchdog fault detected, restarting...");
delay(500);
ESP.restart();
}
vTaskDelay(pdMS_TO_TICKS(WATCHDOG_PERIOD_MS));
}
}
// -----------------------------------------------------------------------------
// setup
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
delay(100);
// Global manufacturer/model
// Zigbee.setManufacturer("Espressif");
// Zigbee.setModel("NodeNewBME680SHT40SGP40v1");
// I2C init
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
i2cMutex = xSemaphoreCreateMutex();
// BME680 init
Serial.println("Connecting to BME680...");
bme.begin(BME68X_I2C_ADDR_HIGH, Wire);
// Step-by-step status checks
if (bme.checkStatus() == BME68X_ERROR) {
Serial.println("❌ BME68X_ERROR: " + bme.statusString());
} else if (bme.checkStatus() == BME68X_WARNING) {
Serial.println("⚠️ BME68X_WARNING: " + bme.statusString());
} else {
Serial.println("✓ BME68X status OK");
}
bme.setTPH(BME68X_OS_8X, BME68X_OS_2X, BME68X_OS_8X);
bme.setHeaterProf(300, 100);
bme.setOpMode(BME68X_FORCED_MODE);
zbTempSensorBME.setManufacturerAndModel("Espressif", "Node11bme680th");
zbTempSensorBME.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensorBME.setMinMaxValue(-20, 80);
zbTempSensorBME.setTolerance(1);
zbTempSensorBME.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensorBME);
zbAnalogBME.setManufacturerAndModel("Espressif", "Node11bme680r");
zbAnalogBME.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbAnalogBME.addAnalogInput();
Zigbee.addEndpoint(&zbAnalogBME);
// SHT40SGP40 init
Serial.println("Initializing SHT40...");
if (!sht4.begin(&Wire)) {
Serial.println("SHT40 not found! Check wiring.");
while (1) delay(10);
}
sht4.setPrecision(SHT4X_HIGH_PRECISION);
sht4.setHeater(SHT4X_NO_HEATER); // SHT4X_NO_HEATER, SHT4X_MED_HEATER_100MS
Serial.println("Initializing SGP40...");
if (!sgp.begin(&Wire)) {
Serial.println("SGP40 not found! Check wiring.");
while (1) delay(10);
}
zbTempSensorSHTSGP.setManufacturerAndModel("Espressif", "Node11sht40sgp40th");
zbTempSensorSHTSGP.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensorSHTSGP.setMinMaxValue(-20, 80);
zbTempSensorSHTSGP.setTolerance(1);
zbTempSensorSHTSGP.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensorSHTSGP);
zbAnalogSGP.setManufacturerAndModel("Espressif", "Node11sgp40raw");
zbAnalogSGP.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbAnalogSGP.addAnalogInput();
Zigbee.addEndpoint(&zbAnalogSGP);
// Start Zigbee as router
Serial.println("Starting Zigbee...");
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start! Rebooting...");
ESP.restart();
}
Serial.println("Zigbee started successfully! Connecting to network...");
bool ledState = false;
unsigned long startTime = millis();
const unsigned long timeoutMs = 60000;
while (!Zigbee.connected()) {
Serial.print(".");
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
delay(100);
if (millis() - startTime > timeoutMs) {
Serial.println(" timed out 60s. Restarting...");
ESP.restart();
}
}
digitalWrite(LED_PIN, HIGH);
Serial.println(" connected!");
lastZigbeeOkMs = millis();
// Create sensor tasks
xTaskCreate(bme680sensorvalueupdate, "bme task", 4096, NULL, 10, NULL);
xTaskCreate(sgp40sht40sensorvalueupdate, "shtsgp task", 4096, NULL, 10, NULL);
xTaskCreate(watchdogTask, "watchdog", 4096, NULL, 5, NULL);
// Reporting configuration after Zigbee.begin
zbTempSensorBME.setReporting(60, 0, 0.5);
zbAnalogBME.setAnalogInputReporting(60, 600, 10);
zbTempSensorSHTSGP.setReporting(60, 0, 0.5);
zbAnalogSGP.setAnalogInputReporting(60, 600, 10);
}
// -----------------------------------------------------------------------------
// loop light - button reset test commands
// -----------------------------------------------------------------------------
void loop() {
// Test commands via Serial
if (Serial.available()) {
char cmd = Serial.read();
switch (cmd) {
case 'Z': // Force Zigbee failure watchdog test
Serial.println("SIM Zigbee disconnect test");
lastZigbeeOkMs = 0; // Watchdog sees Zigbee down
break;
case 'E': // Force stuck sensor detection
Serial.println("SIM Stuck sensor test");
lastTempBmeChangeMs = lastHumBmeChangeMs = lastGasResChangeMs = 0;
lastTempShtChangeMs = lastHumShtChangeMs = lastRawSgpChangeMs = 0;
break;
case 'R': // Manual restart
Serial.println("SIM Manual restart");
ESP.restart();
break;
}
}
// Factory reset on long button press
static uint8_t button = BOOT_PIN;
if (digitalRead(button) == LOW) { // push button pressed
delay(100); // debounce
int startTime = millis();
while (digitalRead(button) == LOW) delay(50);
if (millis() - startTime > 3000) {
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,869 @@
// Xiao ESP32-C6
// uploading new script
// Zigbee connection establishment --> .......
// force remove deivce from Z2M with permit join OFF
// swich permit JOIN ON
// restart ESP32
// leave permit jion on until configuration finished
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// #ifndef ZIGBEE_MODE_ED
// #error "Zigbee end device mode is not selected in Tools->Zigbee mode"
// #endif
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
// #include <HardwareSerial.h>
// #define SENSOR_RX 17 // ESP32 RX connected to sensor TX
// #define SENSOR_TX 16 // ESP32 TX connected to sensor RX
// HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
// #define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
// uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
// ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 18
#define SDA_PIN 20
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
ZigbeeTempSensor zbTempSensor_BME68X = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
// #define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
// ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
#define ANALOG_DEVICE_ENDPOINT_NUMBER 11
ZigbeeAnalog zbAnalogDevice_BME68X = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
/************************ Temp sensor *****************************/
static void bme680_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor_BME68X.setTemperature(temperature);
zbTempSensor_BME68X.setHumidity(humidity);
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
// zbAnalogDevice_BME68X.setAnalogInput(round(gasResistance / 1000.0));
zbAnalogDevice_BME68X.setAnalogInput(round(gasResistance));
// zbAnalogDevice_BME68X.reportAnalogInput();
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("BME680: Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(10000);
}
}
// #include <Wire.h>
#include "Adafruit_SHT4x.h"
#include "Adafruit_SGP40.h"
// --- Hardware Config ---
// #define I2C_SDA_PIN 6 // Default C6 SDA (Adjust if using a specific board like Seeed Xiao)
// #define I2C_SCL_PIN 7 // Default C6 SCL
// If using standard DevKit, you can often just use Wire.begin() without pins
// --- Objects ---
Adafruit_SHT4x sht4;
Adafruit_SGP40 sgp;
// Define a Zigbee Endpoint for Environmental Data
#define ENDPOINT_ENV 12
// ZigbeeTempSensor zbEnvSensor_sgp40_sht40(ENDPOINT_ENV);
ZigbeeTempSensor zbEnvSensor_sgp40_sht40 = ZigbeeTempSensor(ENDPOINT_ENV);
#define SGP_ANALOG_DEVICE_ENDPOINT_NUMBER 13
ZigbeeAnalog zbAnalogDevice_sgp40_sht40 = ZigbeeAnalog(SGP_ANALOG_DEVICE_ENDPOINT_NUMBER);
static void sgp40_sht40_sensor_value_update(void *arg) {
for (;;) {
float currentTemp(NAN), currentHum(NAN);
uint16_t currentRaw(NAN);
int32_t currentVOC(NAN);
// uint8_t percentage;
// Prepare the BME680 for measurement
// bme68xData data;
// int8_t rslt;
// bme.setOpMode(BME68X_FORCED_MODE);
// delayMicroseconds(bme.getMeasDur());
// if (bme.fetchData()) {
// bme.getData(data);
// temperature = data.temperature; // Temperature in °C
// humidity = data.humidity; // Humidity in %
// pressure = data.pressure; // Pressure in hPa
// gasResistance = data.gas_resistance; // Gas resistance in ohms
// errorCount = 0; // Reset error count on successful read
// } else {
// //Serial.println("Failed to read data from BME680!");
// //return; // Exit if reading failed
// errorCount++;
// Serial.println("Failed to read data from BME680!");
// if (errorCount >= maxErrors) {
// Serial.println("Too many errors! Restarting...");
// ESP.restart(); // Restart the ESP32 after too many errors
// }
// }
Serial.println("Starting readout of SHT40");
// --- 1. Read SHT40 (Temp/Hum) ---
sensors_event_t humidity, temp;
sht4.getEvent(&humidity, &temp); // Populate temp and humidity objects
currentTemp = temp.temperature;
currentHum = humidity.relative_humidity;
Serial.print("Temperature: "); Serial.print(currentTemp); Serial.println(" degrees C");
Serial.print("Humidity: "); Serial.print(currentHum); Serial.println("% rH");
Serial.println("Starting readout of SGP40");
// --- 2. Read SGP40 (VOC) ---
// SGP40 requires precise Temp/Hum for accurate compensation.
// We pass the raw SHT40 values directly to the SGP40 algorithm.
currentRaw = sgp.measureRaw(currentTemp, currentHum);
currentVOC = sgp.measureVocIndex(currentTemp, currentHum);
Serial.print("Raw measurement: ");
Serial.println(currentRaw);
Serial.print("Voc Index: ");
Serial.println(currentVOC);
// // Temp: Cluster 0x0402, Attribute 0x0000 (MeasuredValue), Type INT16, Value = Temp * 100
// int16_t zigbeeTemp = (int16_t)(currentTemp * 100);
// zbEnvSensor_sgp40_sht40.reportAttribute(ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT,
// ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID,
// ESP_ZB_ZCL_ATTR_TYPE_S16,
// &zigbeeTemp);
// // Humidity: Cluster 0x0405, Attribute 0x0000 (MeasuredValue), Type UINT16, Value = Hum * 100
// uint16_t zigbeeHum = (uint16_t)(currentHum * 100);
// zbEnvSensor_sgp40_sht40.reportAttribute(ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY,
// ESP_ZB_ZCL_ATTR_REL_HUMIDITY_MEASUREMENT_VALUE_ID,
// ESP_ZB_ZCL_ATTR_TYPE_U16,
// &zigbeeHum);
// // VOC (Analog Input): Cluster 0x000C, Attribute 0x0055 (PresentValue), Type SINGLE (Float)
// // Using Analog Input Present Value for VOC
// float zigbeeVOC = (float)currentVOC;
// zbEnvSensor_sgp40_sht40.reportAttribute(ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT,
// ESP_ZB_ZCL_ATTR_ANALOG_INPUT_PRESENT_VALUE_ID,
// ESP_ZB_ZCL_ATTR_TYPE_SINGLE,
// &zigbeeVOC);
// Serial.printf("T: %.2f C | H: %.2f %% | VOC Index: %d\n", zigbeeTemp, zigbeeHum, zigbeeVOC);
Serial.println("finished readout of SGP40");
// // Update temperature and humidity values in Temperature sensor EP
zbEnvSensor_sgp40_sht40.setTemperature(currentTemp);
zbEnvSensor_sgp40_sht40.setHumidity(currentHum);
// // zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
// zbAnalogDevice_sgp40_sht40.setAnalogInput(round(currentRaw / 1000.0));
zbAnalogDevice_sgp40_sht40.setAnalogInput(round(currentRaw));
// // zbAnalogDevice_BME68X.reportAnalogInput();
Serial.println("finished setting values");
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", currentTemp, currentHum, currentVOC);
// Serial.printf("SGP/SHT: T: %.2f C | H: %.2f %% | Raw SGP: %d | VOC Index: %d\n", currentTemp, currentHum, currentRaw, currentVOC);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(10000);
}
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
// #define ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT 9
// uint8_t illuminance_sensor_pin = 1; // Insert the analog pin to which the sensor (e.g. photoresistor) is connected
// ZigbeeIlluminanceSensor zbIlluminanceSensor = ZigbeeIlluminanceSensor(ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT);
// /********************* Illuminance sensor **************************/
// static void illuminance_sensor_value_update(void *arg) {
// for (;;) {
// // read the raw analog value from the sensor
// int lsens_analog_raw = analogRead(illuminance_sensor_pin);
// Serial.printf("[Illuminance Sensor] raw analog value: %d\r\n", lsens_analog_raw);
// // conversion into zigbee raw illuminance value (typically between 0 in darkness and 50000 in direct sunlight)
// // depends on the value range of the raw analog sensor values and will need calibration for correct lux values
// // for demonstration purpose map the 12-bit ADC value (0-4095) to Zigbee illuminance range (0-50000)
// int lsens_illuminance_raw = map(lsens_analog_raw, 0, 4095, 0, 50000);
// Serial.printf("[Illuminance Sensor] raw illuminance value: %d\r\n", lsens_illuminance_raw);
// // according to zigbee documentation the formular 10^(lsens_illuminance_raw/10000)-1 can be used to calculate lux value from raw illuminance value
// // Note: Zigbee2MQTT seems to be using the formular 10^(lsens_illuminance_raw/10000) instead (without -1)
// int lsens_illuminance_lux = round(pow(10, (lsens_illuminance_raw / 10000.0)) - 1);
// Serial.printf("[Illuminance Sensor] lux value: %d lux\r\n", lsens_illuminance_lux);
// // Update illuminance in illuminance sensor EP
// zbIlluminanceSensor.setIlluminance(lsens_illuminance_raw); // use raw illuminance here!
// delay(10000); // reduce delay (in ms), if you want your device to react more quickly to changes in illuminance
// }
// }
// // 1. Define the task function (Global scope, outside setup/loop)
// void pirTask(void * parameter) {
// // Move the static variable here. It doesn't need to be 'static' anymore
// // because this function only runs once and stays in the loop below.
// bool occupancy = false;
// // 2. Infinite Loop: Tasks must never return!
// for(;;) {
// // --- Your Original Logic Start ---
// if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// zbOccupancySensor.setOccupancy(true);
// zbOccupancySensor.report();
// occupancy = true;
// analogWrite(ledPin, 0);
// Serial.println("GPIO2 HIGH");
// } else if (digitalRead(sensor_pin) == LOW && occupancy) {
// zbOccupancySensor.setOccupancy(false);
// zbOccupancySensor.report();
// occupancy = false;
// analogWrite(ledPin, 255);
// Serial.println("GPIO2 LOW");
// }
// // --- Your Original Logic End ---
// // 3. CRITICAL: Delay to yield control
// // Polling a PIR every 50ms is plenty fast and saves CPU.
// vTaskDelay(50 / portTICK_PERIOD_MS);
// }
// }
// const char* commands[] = {
// "getRange",
// "getSensitivity",
// "getLatency",
// "getUart",
// "getGpioMode 2",
// "getLedMode 1",
// "getEcho",
// "getUartOutput 1",
// "getUartOutput 2",
// // "sensorStop",
// // "sensorStart",
// // "saveConfig",
// // "resetCfg",
// // "resetSystem",
// "getHWV",
// "getSWV",
// "getOutput"
// };
// const int numCommands = sizeof(commands) / sizeof(commands[0]);
// String line = "";
// bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// // Send the command
// sensorSerial.println(command);
// Serial.print("Sending: ");
// Serial.println(command);
// // Wait for "Done" response
// unsigned long startTime = millis();
// String response;
// while (millis() - startTime < timeout) {
// while (sensorSerial.available()) {
// char c = sensorSerial.read();
// Serial.write(c); // Print response to Serial Monitor
// response += c;
// // If "Done" is found in the response, return success
// if (response.indexOf("Done") >= 0) {
// Serial.println("✓ Done received.");
// return true;
// }
// // Optional: detect "Error" for debugging
// if (response.indexOf("Error") >= 0) {
// Serial.println("✗ Error received.");
// return false;
// }
// }
// }
// Serial.println("✗ Timeout waiting for Done.");
// return false;
// }
// void turnOnSensor(int sensitivity) {
// Serial.println("=== Turning On Sensor ===");
// sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
// sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// // sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// // sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
// sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
// sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
// sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
// sendCommandAndWaitForDone("setLatency 0.025 0.5", mmWaveSerial);
// sendCommandAndWaitForDone("setEcho 1", mmWaveSerial); // Enable echo — sensor prints back commands and shows leapMMW:/> prompt (default).
// sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
// sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
// sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
// sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
// }
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
// mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
// delay(100); // Give sensor time to boot
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// turnOnSensor(5); // initialization of mmwave sensor
// for (int i = 0; i < numCommands; i++) {
// const char* cmd = commands[i];
// sendCommandAndWaitForDone(cmd, mmWaveSerial);
// delay(10); // small delay before next command (optional)
// }
// Optional: configure analog input
analogSetAttenuation(ADC_11db); // set analog to digital converter (ADC) attenuation to 11 dB (up to ~3.3V input)
analogReadResolution(12); // set analog read resolution to 12 bits (value range from 0 to 4095), 12 is default
// // Init button + PIR sensor
// pinMode(button, INPUT_PULLUP);
// pinMode(sensor_pin, INPUT_PULLUP);
// pinMode(ledPin, OUTPUT);
// analogWrite(ledPin, 255); // pin, dutyCycle
// // Optional: set Zigbee device name and model
// zbOccupancySensor.setManufacturerAndModel("Espressif", "Node11_bme680_sht40_sgp40");
// // Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
// zbOccupancySensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
// // Add endpoint to Zigbee Core
// Zigbee.addEndpoint(&zbOccupancySensor);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Optional: set Zigbee device name and model
zbTempSensor_BME68X.setManufacturerAndModel("Espressif", "Node11_bme680_sht40_sgp40");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbTempSensor_BME68X.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor_BME68X.setMinMaxValue(0, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor_BME68X.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor_BME68X.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor_BME68X);
// // Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
// zbTempSensor2.setMinMaxValue(10, 50);
// // Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
// zbTempSensor2.setTolerance(1);
// // Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// // The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// // zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// // Add humidity cluster to the temperature sensor device with min, max and tolerance values
// zbTempSensor2.addHumiditySensor(0, 100, 1);
// // Add endpoint to Zigbee Core
// Zigbee.addEndpoint(&zbTempSensor2);
// // Set minimum and maximum carbon dioxide measurement value in ppm
// zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// // Add endpoints to Zigbee Core
// Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice_BME68X.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice_BME68X);
// // Set minimum and maximum for raw illuminance value (0 min and 50000 max equals to 0 lux - 100,000 lux)
// zbIlluminanceSensor.setMinMaxValue(0, 50000);
// // Optional: Set tolerance for raw illuminance value
// zbIlluminanceSensor.setTolerance(1);
// // Add endpoint to Zigbee Core
// Serial.println("Adding Zigbee illuminance sensor endpoint to Zigbee Core");
// Zigbee.addEndpoint(&zbIlluminanceSensor);
Serial.println("Initializing SHT40...");
if (!sht4.begin(&Wire)) {
Serial.println("SHT40 not found! Check wiring.");
while (1) delay(10);
}
sht4.setPrecision(SHT4X_HIGH_PRECISION);
sht4.setHeater(SHT4X_NO_HEATER);
Serial.println("Initializing SGP40...");
if (!sgp.begin(&Wire)) {
Serial.println("SGP40 not found! Check wiring.");
while (1) delay(10);
}
// --- 2. Configure Zigbee Endpoint ---
// Set Manufacturer/Model
// zbEnvSensor.setManufacturerAndModel("Espressif", "ESP32C6-Env-Sensor");
// // A. Add Temperature Cluster (Standard)
// zbEnvSensor_sgp40_sht40.addCluster(ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT);
// // B. Add Humidity Cluster
// // We manually add this cluster to the endpoint
// zbEnvSensor_sgp40_sht40.addCluster(ESP_ZB_ZCL_CLUSTER_ID_REL_HUMIDITY);
// // C. Add Analog Input Cluster (for VOC Index)
// // We use Analog Input (0x000C) as a generic container for the VOC value (0-500)
// zbEnvSensor_sgp40_sht40.addCluster(ESP_ZB_ZCL_CLUSTER_ID_ANALOG_INPUT);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbEnvSensor_sgp40_sht40.setMinMaxValue(0, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbEnvSensor_sgp40_sht40.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbEnvSensor_sgp40_sht40.addHumiditySensor(0, 100, 1);
// Register the endpoint
Zigbee.addEndpoint(&zbEnvSensor_sgp40_sht40);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice_sgp40_sht40.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice_sgp40_sht40);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
// if (!Zigbee.begin(&zigbeeConfig, false)) {
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// // 4. Create the task
// xTaskCreate(
// pirTask, // Function to call
// "PIR_Check", // Name for debugging
// 4096, // Stack size (bytes) - Zigbee operations need space!
// NULL, // Parameter to pass
// 1, // Priority (1 is standard, higher numbers = higher priority)
// NULL // Task handle
// );
// Start Temperature sensor reading task
xTaskCreate(bme680_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor_BME68X.setReporting(0, 15, 1);
// zbTempSensor2.setReporting(0, 15, 0);
// zbCarbonDioxideSensor.setReporting(0, 30, 0);
zbAnalogDevice_BME68X.setAnalogInputReporting(0, 15, 10); // report every 30 seconds if value changes by 10
// Start illuminance sensor reading task
// xTaskCreate(illuminance_sensor_value_update, "illuminance_sensor_update", 2048, NULL, 10, NULL);
// Set reporting schedule for illuminance value measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta
// if min = 1 and max = 0, delta = 1000, reporting is sent when raw illuminance value changes by 1000, but at most once per second
// if min = 0 and max = 10, delta = 1000, reporting is sent every 10 seconds or if raw illuminance value changes by 1000
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of illuminance change
// Note: On pairing with Zigbee Home Automation or Zigbee2MQTT the reporting schedule will most likely be overwritten with their default settings
// zbIlluminanceSensor.setReporting(1, 0, 1);
xTaskCreate(sgp40_sht40_sensor_value_update, "sgp40_sht40_sensor_value_update", 2048, NULL, 10, NULL);
zbEnvSensor_sgp40_sht40.setReporting(0, 15, 1);
zbAnalogDevice_sgp40_sht40.setAnalogInputReporting(0, 15, 10); // report every 30 seconds if value changes by 10
}
void loop() {
// Checking PIR sensor for occupancy change
// static bool occupancy = false;
// if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// // Update occupancy sensor value
// zbOccupancySensor.setOccupancy(true);
// zbOccupancySensor.report();
// occupancy = true;
// analogWrite(ledPin, 0);
// Serial.println("GPIO2 HIGH");
// } else if (digitalRead(sensor_pin) == LOW && occupancy) {
// zbOccupancySensor.setOccupancy(false);
// zbOccupancySensor.report();
// occupancy = false;
// analogWrite(ledPin, 255);
// Serial.println("GPIO2 LOW");
// }
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// force report of illuminance when button is pressed
// zbIlluminanceSensor.report();
zbTempSensor_BME68X.report();
// zbCarbonDioxideSensor.report();
zbAnalogDevice_BME68X.reportAnalogInput();
// zbEnvSensor_sgp40_sht40.report();
}
delay(100);
}

View File

@ -0,0 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
{
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"name": "Debug with JLink",
"request": "launch",
"type": "cortex-debug",
"device": "",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"servertype": "jlink"
},
{
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"name": "Debug with JLink",
"request": "launch",
"type": "cortex-debug",
"device": "",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"servertype": "jlink"
}
]
}

View File

@ -0,0 +1,259 @@
// DFRobot Beetle ESP32-C6
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
#include "DHT.h"
#define DHTPIN 17 // Digital pin connected to the DHT sensor
#define DHTPOWER 4 // GPIO pin to power the sensor
//https://github.com/adafruit/DHT-sensor-library/blob/master/examples/DHTtester/DHTtester.ino
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 295 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
uint8_t button = BOOT_PIN;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
const int batteryPin = 0; // A0
#define BAT_ADC_PIN 0 // ADC input pin
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.0;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float val;
val = (x - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(BAT_ADC_PIN); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
/************************ Temp sensor *****************************/
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature * 2;
// Use DHT11 sensor
// Read temperature as Celsius (the default)
float temperature = dht.readTemperature();
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float humidity = dht.readHumidity();
//float humidity = 43;
// Check if any reads failed and exit early (to try again).
if (isnan(humidity) || isnan(temperature)) {
Serial.println(F("Failed to read from DHT sensor!"));
delay(100);
return;
}
delay(100);
digitalWrite(DHTPOWER, LOW); // Power off sensor
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
delay(1000);
// // read the analog / millivolts value for pin 0:
// int analogValue = analogRead(batteryPin);
// int analogVolts = analogReadMilliVolts(batteryPin);
// uint8_t percentage = mapFloat(analogVolts * 2 / 1000, minVoltage, maxVoltage);
// print out the values you read:
// Serial.print("ADC analog value = ");
// Serial.println(analogValue);
// Serial.print("ADC millivolts value = ");
// Serial.print(analogVolts);
// Serial.println(" mV");
// // Please adjust the calculation coefficient according to the actual measurement.
// Serial.print("BAT millivolts value = ");
// Serial.print(analogVolts * 2);
// Serial.println(" mV");
// Serial.print("BAT percentage value = ");
// Serial.print(percentage);
// Serial.println(" %");
// Serial.println("--------------");
uint8_t percentage;
float vBat;
vBat = getVbatt();
percentage = mapFloat(vBat, minVoltage, maxVoltage);
#ifdef DEBUG_TRACE
Serial.printf("Battery: %.2fV (%d%%)\n", vBat, percentage);
#endif
zbTempSensor.setBatteryPercentage(percentage);
zbTempSensor.reportBatteryPercentage();
// Add small delay to allow the data to be sent before going to sleep
//delay(1000);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
//delay(100); // wait for a second
// Put device to deep sleep
Serial.println("Going to sleep now");
delay(100);
esp_deep_sleep_start();
// delay(1000);
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
pinMode(DHTPOWER, OUTPUT);
digitalWrite(DHTPOWER, HIGH); // Power on the sensor
delay(1000); // Wait for a second to allow the Serial Monitor to open and DHT11 to stabilize
Serial.println("Starting setup...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor_Node14");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(0.1);
// Set power source to battery and set battery percentage to measured value (now 100% for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) anytime
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 99);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 3000;
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Successfully connected to Zigbee network");
dht.begin();
Serial.println(F("DHTxx test!"));
//set the resolution to 12 bits (0-4096)
analogReadResolution(12);
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
// Call the function to measure temperature and put the device to sleep
meausureAndSleep();
}

View File

@ -0,0 +1,494 @@
// DFRobot Beetle ESP32-C6 Partition Scheme: Zigbee ZCZR 4MB with spiffs
// Zigbee Mode: Zigbee ZR/ZC (coordinator,router)
// LED: GPIO15, on when HIGH
#ifndef ZIGBEE_MODE_ZCZR
#error Zigbee coordinator/router mode is not selected in Tools->Zigbee mode
#endif
#define DEBUG_TRACE // Comment out DEBUG_TRACE to reduce normal-path logging
#include "Zigbee.h"
#include "Wire.h"
#include "bme68xLibrary.h"
#include "Adafruit_SHT4x.h"
#include "Adafruit_SGP40.h"
// -----------------------------------------------------------------------------
// Hardware definitions (shared I2C bus)
// -----------------------------------------------------------------------------
#define LED_PIN 15
#define I2C_SDA_PIN 22
#define I2C_SCL_PIN 21
// T6713 I2C address and registers
#define T6713_ADDR 0x15
#define T6713_REG_ABC_LOGIC 0x03EE
#define T6713_REG_STATUS 0x138A
// Endpoint numbers (reassigned for new sensors)
#define BME_TEMP_ENDPOINT 10
#define BME_ANALOG_ENDPOINT 11
#define SHT_SGP_TEMP_ENDPOINT 12
#define SGP_ANALOG_ENDPOINT 13
#define T6713_CO2_ENDPOINT 15
// Sensor objects
Bme68x bme;
Adafruit_SHT4x sht4;
Adafruit_SGP40 sgp;
ZigbeeTempSensor zbTempSensorBME(BME_TEMP_ENDPOINT);
ZigbeeAnalog zbAnalogBME(BME_ANALOG_ENDPOINT);
ZigbeeTempSensor zbTempSensorSHTSGP(SHT_SGP_TEMP_ENDPOINT);
ZigbeeAnalog zbAnalogSGP(SGP_ANALOG_ENDPOINT);
ZigbeeCarbonDioxideSensor zbCo2T6713(T6713_CO2_ENDPOINT);
// -----------------------------------------------------------------------------
// Health watchdog globals (updated for new sensors)
// -----------------------------------------------------------------------------
SemaphoreHandle_t i2cMutex;
bool bmeFirstOk = false;
bool shtsgpFirstOk = false;
bool t6713FirstOk = false;
// Last-change tracking
float lastTempBme = NAN, lastHumBme = NAN, lastGasRes = NAN;
unsigned long lastTempBmeChangeMs = 0, lastHumBmeChangeMs = 0, lastGasResChangeMs = 0;
float lastTempSht = NAN, lastHumSht = NAN, lastRawSgp = NAN;
unsigned long lastTempShtChangeMs = 0, lastHumShtChangeMs = 0, lastRawSgpChangeMs = 0;
int lastCo2T6713 = -1;
unsigned long lastCo2T6713ChangeMs = 0;
unsigned long lastZigbeeOkMs = 0;
// Error counters
int bmeErrorCount = 0, shtsgpErrorCount = 0, t6713ErrorCount = 0;
const int maxI2cErrors = 10;
// Watchdog thresholds
const unsigned long WATCHDOG_PERIOD_MS = 10000; // 10s
const unsigned long SENSOR_STUCK_TIMEOUT_MS = 30UL * 60UL * 1000UL; // 30min
const unsigned long ZIGBEE_DOWN_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10min
// -----------------------------------------------------------------------------
// Zigbee health touch after successful reports
// -----------------------------------------------------------------------------
inline void zigbeeHealthTouch() {
if (Zigbee.connected()) lastZigbeeOkMs = millis();
}
// -----------------------------------------------------------------------------
// Helper: I2C guarded execution (use mutex)
// -----------------------------------------------------------------------------
#include <functional>
bool withI2C(std::function<bool(void)> fn, uint32_t timeoutMs = 100) {
if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(timeoutMs)) == pdTRUE) {
bool ok = fn();
xSemaphoreGive(i2cMutex);
return ok;
}
return false;
}
// -----------------------------------------------------------------------------
// T6713 helper functions (unchanged)
// -----------------------------------------------------------------------------
int readT6713() {
int result = -1;
bool ok = withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04); // Function: Read Input Registers
Wire.write(0x13); Wire.write(0x8B); // 0x138B
Wire.write(0x00); Wire.write(0x01);
if (Wire.endTransmission() != 0) return false;
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() >= 4) {
Wire.read(); Wire.read(); // skip func+len
byte high = Wire.read();
byte low = Wire.read();
result = (high << 8) | low;
return true;
}
return false;
});
if (!ok) result = -1;
return result;
}
void configureT6713(bool enableABC) {
delay(1000);
// Optional status check
withI2C([&]() {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04); Wire.write(0x13); Wire.write(0x8A); // 0x138A
Wire.write(0x00); Wire.write(0x01);
Wire.endTransmission();
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() >= 4) {
Wire.read(); Wire.read();
uint16_t status = (Wire.read() << 8) | Wire.read();
#ifdef DEBUG_TRACE
if (status == 0x08) Serial.println("T6713 Warming up...");
#endif
}
return true;
});
uint16_t abcValue = enableABC ? 0xFF00 : 0x0000;
withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x05); // Write Single Coil
Wire.write(highByte(T6713_REG_ABC_LOGIC));
Wire.write(lowByte(T6713_REG_ABC_LOGIC));
Wire.write(highByte(abcValue));
Wire.write(lowByte(abcValue));
byte error = Wire.endTransmission();
if (error == 0) {
#ifdef DEBUG_TRACE
Serial.print("T6713 ABC Logic set to "); Serial.println(enableABC ? "ON" : "OFF");
#endif
} else {
#ifdef DEBUG_TRACE
Serial.println("T6713 Failed to set ABC Logic");
#endif
}
return (error == 0);
});
delay(100);
}
// -----------------------------------------------------------------------------
// BME68x task (adapted, endpoint 10/11)
// -----------------------------------------------------------------------------
static void bme680sensorvalueupdate(void *arg) {
for (;;) {
float temperature = NAN, humidity = NAN, gasResistance = NAN;
bme68xData data;
bool ok = withI2C([&]() {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature;
humidity = data.humidity;
gasResistance = data.gas_resistance;
return true;
}
return false;
});
if (ok) {
bmeErrorCount = 0;
unsigned long now = millis();
if (!bmeFirstOk) {
bmeFirstOk = true;
lastTempBme = temperature; lastHumBme = humidity; lastGasRes = gasResistance;
lastTempBmeChangeMs = lastHumBmeChangeMs = lastGasResChangeMs = now;
} else {
if (temperature != lastTempBme) { lastTempBme = temperature; lastTempBmeChangeMs = now; }
if (humidity != lastHumBme) { lastHumBme = humidity; lastHumBmeChangeMs = now; }
if (gasResistance != lastGasRes) { lastGasRes = gasResistance; lastGasResChangeMs = now; }
}
zbTempSensorBME.setTemperature(temperature);
zbTempSensorBME.setHumidity(humidity);
// zbTempSensorBME.report();
zigbeeHealthTouch();
zbAnalogBME.setAnalogInput(round(gasResistance));
// zbAnalogBME.reportAnalogInput();
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.printf("BME68x T:%.2fC H:%.2f%% Gas:%.2f ohm\n", temperature, humidity, gasResistance);
#endif
} else {
bmeErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("BME68x read error or I2C busy.");
#endif
if (bmeErrorCount >= maxI2cErrors) {
Serial.println("BME68x too many errors, restarting...");
delay(200); ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// SHT40/SGP40 task (from Node11, endpoint 12/13)
// -----------------------------------------------------------------------------
static void sgp40sht40sensorvalueupdate(void *arg) {
for (;;) {
float currentTemp = NAN, currentHum = NAN;
uint16_t currentRaw = NAN;
bool ok = withI2C([&]() {
sensors_event_t humidity, temp;
if (!sht4.getEvent(&humidity, &temp)) return false;
currentTemp = temp.temperature;
currentHum = humidity.relative_humidity;
currentRaw = sgp.measureRaw(currentTemp, currentHum);
return true;
});
if (ok) {
shtsgpErrorCount = 0;
unsigned long now = millis();
if (!shtsgpFirstOk) {
shtsgpFirstOk = true;
lastTempSht = currentTemp; lastHumSht = currentHum; lastRawSgp = currentRaw;
lastTempShtChangeMs = lastHumShtChangeMs = lastRawSgpChangeMs = now;
} else {
if (currentTemp != lastTempSht) { lastTempSht = currentTemp; lastTempShtChangeMs = now; }
if (currentHum != lastHumSht) { lastHumSht = currentHum; lastHumShtChangeMs = now; }
if (currentRaw != lastRawSgp) { lastRawSgp = currentRaw; lastRawSgpChangeMs = now; }
}
zbTempSensorSHTSGP.setTemperature(currentTemp);
zbTempSensorSHTSGP.setHumidity(currentHum);
// zbTempSensorSHTSGP.report();
zigbeeHealthTouch();
zbAnalogSGP.setAnalogInput(round(currentRaw));
// zbAnalogSGP.reportAnalogInput();
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.printf("SHT40/SGP40 T:%.2fC H:%.2f%% Raw:%d\n", currentTemp, currentHum, currentRaw);
#endif
} else {
shtsgpErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("SHT40/SGP40 read error or I2C busy.");
#endif
if (shtsgpErrorCount >= maxI2cErrors) {
Serial.println("SHT40/SGP40 too many errors, restarting...");
delay(200); ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// T6713 task (unchanged)
// -----------------------------------------------------------------------------
static void t6713sensorvalueupdate(void *arg) {
for (;;) {
int co2t6713 = readT6713();
if (co2t6713 >= 0) {
t6713ErrorCount = 0;
unsigned long now = millis();
if (!t6713FirstOk) {
t6713FirstOk = true;
lastCo2T6713 = co2t6713;
lastCo2T6713ChangeMs = now;
} else if (co2t6713 != lastCo2T6713) {
lastCo2T6713 = co2t6713;
lastCo2T6713ChangeMs = now;
#ifdef DEBUG_TRACE
Serial.print("T6713 CO2: "); Serial.print(co2t6713); Serial.println(" ppm");
#endif
}
zbCo2T6713.setCarbonDioxide(co2t6713);
// zbCo2T6713.report();
zigbeeHealthTouch();
} else {
t6713ErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("T6713 read error.");
#endif
if (t6713ErrorCount >= maxI2cErrors) {
Serial.println("T6713 too many errors, restarting...");
delay(200); ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// Watchdog task (updated checks for new sensors)
// -----------------------------------------------------------------------------
void watchdogTask(void *param) {
for (;;) {
#ifdef DEBUG_TRACE
Serial.println("Watchdog ran.");
#endif
unsigned long now = millis();
bool zigbeeBad = !Zigbee.connected() || (now - lastZigbeeOkMs > ZIGBEE_DOWN_TIMEOUT_MS);
bool bmeStuck = bmeFirstOk && (
(now - lastTempBmeChangeMs > SENSOR_STUCK_TIMEOUT_MS) ||
(now - lastHumBmeChangeMs > SENSOR_STUCK_TIMEOUT_MS) ||
(now - lastGasResChangeMs > SENSOR_STUCK_TIMEOUT_MS));
bool shtsgpStuck = shtsgpFirstOk && (
(now - lastTempShtChangeMs > SENSOR_STUCK_TIMEOUT_MS) ||
(now - lastHumShtChangeMs > SENSOR_STUCK_TIMEOUT_MS) ||
(now - lastRawSgpChangeMs > SENSOR_STUCK_TIMEOUT_MS));
bool t6713Stuck = t6713FirstOk && (now - lastCo2T6713ChangeMs > SENSOR_STUCK_TIMEOUT_MS);
if (zigbeeBad || bmeStuck || shtsgpStuck || t6713Stuck) {
Serial.println("Watchdog fault detected, restarting...");
delay(500); ESP.restart();
}
vTaskDelay(pdMS_TO_TICKS(WATCHDOG_PERIOD_MS));
}
}
// -----------------------------------------------------------------------------
// setup
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(100);
// Global manufacturer/model
// Zigbee.setManufacturer("Espressif");
// Zigbee.setModel("Node_New_BME680_T6713_SHT40_SGP40_v1");
// I2C init
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
i2cMutex = xSemaphoreCreateMutex();
// BME680 init
Serial.println("Connecting to BME680...");
bme.begin(BME68X_I2C_ADDR_HIGH, Wire);
// Step-by-step status checks
if (bme.checkStatus() == BME68X_ERROR) {
Serial.println("❌ BME68X_ERROR: " + bme.statusString());
} else if (bme.checkStatus() == BME68X_WARNING) {
Serial.println("⚠️ BME68X_WARNING: " + bme.statusString());
} else {
Serial.println("✓ BME68X status OK");
}
bme.setTPH(BME68X_OS_8X, BME68X_OS_2X, BME68X_OS_8X);
bme.setHeaterProf(300, 100);
bme.setOpMode(BME68X_FORCED_MODE);
zbTempSensorBME.setManufacturerAndModel("Espressif", "Node_14_bme680th");
zbTempSensorBME.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensorBME.setMinMaxValue(-20, 80);
zbTempSensorBME.setTolerance(1);
zbTempSensorBME.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensorBME);
zbAnalogBME.setManufacturerAndModel("Espressif", "Node_14_bme680r");
zbAnalogBME.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbAnalogBME.addAnalogInput();
Zigbee.addEndpoint(&zbAnalogBME);
// SHT40/SGP40 init
Serial.println("Initializing SHT40...");
if (!sht4.begin(&Wire)) {
Serial.println("SHT40 not found! Check wiring.");
while (1) delay(10);
}
sht4.setPrecision(SHT4X_HIGH_PRECISION);
sht4.setHeater(SHT4X_NO_HEATER);
Serial.println("Initializing SGP40...");
if (!sgp.begin(&Wire)) {
Serial.println("SGP40 not found! Check wiring.");
while (1) delay(10);
}
zbTempSensorSHTSGP.setManufacturerAndModel("Espressif", "Node_14_sht40sgp40th");
zbTempSensorSHTSGP.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensorSHTSGP.setMinMaxValue(-20, 80);
zbTempSensorSHTSGP.setTolerance(1);
zbTempSensorSHTSGP.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensorSHTSGP);
zbAnalogSGP.setManufacturerAndModel("Espressif", "Node_14_sgp40raw");
zbAnalogSGP.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbAnalogSGP.addAnalogInput();
Zigbee.addEndpoint(&zbAnalogSGP);
// T6713 config & endpoint
configureT6713(true); // Enable ABC
zbCo2T6713.setManufacturerAndModel("Espressif", "Node_14_t6713");
zbCo2T6713.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbCo2T6713.setMinMaxValue(0, 10000);
Zigbee.addEndpoint(&zbCo2T6713);
// Start Zigbee as router
Serial.println("Starting Zigbee...");
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start! Rebooting...");
ESP.restart();
}
Serial.println("Zigbee started successfully! Connecting to network...");
bool ledState = false;
unsigned long startTime = millis();
const unsigned long timeoutMs = 60000;
while (!Zigbee.connected()) {
Serial.print(".");
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
delay(100);
if (millis() - startTime > timeoutMs) {
Serial.println(" timed out (60s). Restarting...");
ESP.restart();
}
}
digitalWrite(LED_PIN, LOW);
Serial.println(" connected!");
lastZigbeeOkMs = millis();
// Create sensor tasks
xTaskCreate(bme680sensorvalueupdate, "bme_task", 4096, NULL, 10, NULL);
xTaskCreate(sgp40sht40sensorvalueupdate, "shtsgp_task", 4096, NULL, 10, NULL);
xTaskCreate(t6713sensorvalueupdate, "t6713_task", 4096, NULL, 10, NULL);
xTaskCreate(watchdogTask, "watchdog", 4096, NULL, 5, NULL);
// Reporting configuration (after Zigbee.begin)
zbTempSensorBME.setReporting(60, 0, 0.5);
zbAnalogBME.setAnalogInputReporting(60, 600, 10);
zbTempSensorSHTSGP.setReporting(60, 0, 0.5);
zbAnalogSGP.setAnalogInputReporting(60, 600, 10);
zbCo2T6713.setReporting(30, 300, 10);
}
// -----------------------------------------------------------------------------
// loop (light - button reset + test commands)
// -----------------------------------------------------------------------------
void loop() {
// Test commands via Serial
if (Serial.available()) {
char cmd = Serial.read();
switch (cmd) {
case 'Z': // Force Zigbee failure (watchdog test)
Serial.println("SIM Zigbee disconnect test");
lastZigbeeOkMs = 0; // Watchdog sees Zigbee down
break;
case 'E': // Force stuck sensor detection
Serial.println("SIM Stuck sensor test");
lastTempBmeChangeMs = lastHumBmeChangeMs = lastGasResChangeMs = 0;
lastTempShtChangeMs = lastHumShtChangeMs = lastRawSgpChangeMs = 0;
lastCo2T6713ChangeMs = 0;
break;
case 'R': // Manual restart
Serial.println("SIM Manual restart");
ESP.restart();
break;
}
}
// Factory reset on long button press
static uint8_t button = BOOT_PIN;
if (digitalRead(button) == LOW) { // push button pressed
delay(100); // debounce
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if (millis() - startTime > 3000) {
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
ESP.restart();
}
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}

View File

@ -0,0 +1,133 @@
/*
* ESP32-C6 Zigbee Router with DHT11 Sensor
*
* Logic:
* 1. Initialize as a Zigbee Router (ZR).
* 2. Create an endpoint with Temperature and Humidity clusters.
* 3. Read DHT11 sensor periodically.
* 4. Update and report values to the Zigbee network.
*
* Libraries required:
* - EspZigbee (Standard with ESP32 Board package v3.0.0+)
* - DHT sensor library by Adafruit (Install via Library Manager)
*/
#include "Zigbee.h"
#include "DHT.h"
// --- Hardware Config ---
#define DHTPIN 17 // Pin connected to DHT11 Data
#define DHTTYPE DHT11 // Sensor type
#define DHTPOWER 4 // GPIO pin to power the sensor
// --- Zigbee Config ---
// Identify the endpoint (arbitrary number, 1-240)
#define SENSOR_ENDPOINT_NUM 10
// --- Global Objects ---
DHT dht(DHTPIN, DHTTYPE);
// Define the Zigbee Temperature Sensor object
// Note: Depending on the specific library version, this helper class
// might primarily handle Temperature. We will configure it to report.
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(SENSOR_ENDPOINT_NUM);
// Timer for reporting
unsigned long lastReportTime = 0;
const unsigned long REPORT_INTERVAL = 10000; // Report every 10 seconds
void setup() {
Serial.begin(115200);
pinMode(DHTPOWER, OUTPUT);
digitalWrite(DHTPOWER, HIGH); // Power on the sensor
// Initialize DHT Sensor
dht.begin();
Serial.println("DHT11 Sensor Initialized");
// --- Zigbee Setup ---
// 1. Set Manufacturer and Model details (visible in Home Assistant/Zigbee2MQTT)
zbTempSensor.setManufacturerAndModel("Espressif", "ESP32C6-Router-Temp");
// 2. Add the Temperature and Humidity clusters to the endpoint
// The default ZigbeeTempSensor includes the Temperature Measurement cluster.
// We explicitly try to set the sensor config.
// Note: If you need a dedicated Humidity cluster separate from this helper,
// you might need to use the generic EspZigbee::ZigbeeEP helper, but
// most modern helpers support adding standard clusters.
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(0.1);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// 3. Add Endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// 4. Initialize Zigbee as a ROUTER
// ZIGBEE_ROUTER: Mains-powered, always on, routes packets for other devices.
// Serial.println("Starting Zigbee as Router...");
// if (!Zigbee.begin(ZIGBEE_ROUTER)) {
// Serial.println("Zigbee failed to start! Restarting...");
// delay(1000);
// ESP.restart();
// }
// Serial.println("Zigbee Started. Waiting for network...");
// Zigbee.factoryReset();
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in Router mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
}
void loop() {
// Check if enough time has passed to report data
if (millis() - lastReportTime > REPORT_INTERVAL) {
lastReportTime = millis();
// 1. Read Sensor
float temp = dht.readTemperature();
float hum = dht.readHumidity();
// Check if read failed
if (isnan(temp) || isnan(hum)) {
Serial.println("Failed to read from DHT sensor!");
return;
}
Serial.printf("Temp: %.1f C, Humidity: %.1f %%\n", temp, hum);
// 2. Update Zigbee Clusters
// The ZigbeeTempSensor class updates the local attribute
zbTempSensor.setTemperature(temp);
// Note: If your specific version of ZigbeeTempSensor does not support setHumidity,
// you may need to add a separate Humidity endpoint or use raw attribute setting.
// However, recent ESP32-Arduino versions often bundle these.
zbTempSensor.setHumidity(hum); // Uncomment if supported by your version
// 3. Report Values
// This triggers a report attribute command to the coordinator
zbTempSensor.report();
}
}

View File

@ -0,0 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
{
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"name": "Debug with JLink",
"request": "launch",
"type": "cortex-debug",
"device": "",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"servertype": "jlink"
},
{
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"name": "Debug with JLink",
"request": "launch",
"type": "cortex-debug",
"device": "",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"servertype": "jlink"
}
]
}

View File

@ -0,0 +1,295 @@
//Seeedstudio Xiao ESP32-C6
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
//#include "DHT.h"
//#define DHTPIN 17 // Digital pin connected to the DHT sensor
//#define DHTPOWER 4 // GPIO pin to power the sensor
//https://github.com/adafruit/DHT-sensor-library/blob/master/examples/DHTtester/DHTtester.ino
//#define DHTTYPE DHT11 // DHT 11
//DHT dht(DHTPIN, DHTTYPE);
#include <Arduino.h>
#include <Wire.h>
#include "Adafruit_SHT31.h"
bool enableHeater = false;
uint8_t loopCnt = 0;
#define SHT31_POWER_PIN 16 // GPIO used to power the sensor
Adafruit_SHT31 sht31 = Adafruit_SHT31();
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 295 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
uint8_t button = BOOT_PIN;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
const int batteryPin = 0; // A0
#define BAT_ADC_PIN A0 // ADC input pin
#define BAT_GPIO_ENABLE 17 // GPIO controlling R1
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.2;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float x_corrected = x * 2 / 1000;
Serial.printf("mapFloat: x = %.2f° V, in_min = %.2f° V, in_max = %.2f%%\r\n", x_corrected, in_min, in_max);
float val;
val = (x_corrected - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(batteryPin); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
/************************ Temp sensor *****************************/
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature * 2;
// Use DHT11 sensor
// Read temperature as Celsius (the default)
//float temperature = dht.readTemperature();
//float temperature = 23.5;
float temperature = round(sht31.readTemperature() * 10) / 10.0;
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
//float humidity = dht.readHumidity();
//float humidity = 43;
float humidity = round(sht31.readHumidity() * 10) / 10.0;
// Check if any reads failed and exit early (to try again).
if (isnan(humidity) || isnan(temperature)) {
Serial.println(F("Failed to read from SHT31D sensor!"));
delay(100);
return;
}
delay(100);
digitalWrite(SHT31_POWER_PIN, LOW); // Power off sensor
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
delay(1000);
// Enable the voltage divider
digitalWrite(BAT_GPIO_ENABLE, HIGH);
delay(5); // Give time for voltage to stabilize
// read the analog / millivolts value for pin 0:
int analogValue = analogRead(BAT_ADC_PIN);
int analogVolts = analogReadMilliVolts(BAT_ADC_PIN);
uint8_t percentage = mapFloat(analogVolts, minVoltage, maxVoltage);
// print out the values you read:
Serial.print("ADC analog value = ");
Serial.println(analogValue);
Serial.print("ADC millivolts value = ");
Serial.print(analogVolts);
Serial.println(" mV");
// Please adjust the calculation coefficient according to the actual measurement.
Serial.print("BAT millivolts value = ");
Serial.print(analogVolts * 2);
Serial.println(" mV");
Serial.print("BAT percentage value = ");
Serial.print(percentage);
Serial.println(" %");
Serial.println("--------------");
zbTempSensor.setBatteryPercentage(percentage);
zbTempSensor.reportBatteryPercentage();
// Disable the divider to save power
// pinMode(BAT_GPIO_ENABLE, INPUT); // High-Z to avoid sinking current
digitalWrite(BAT_GPIO_ENABLE, LOW);
// Add small delay to allow the data to be sent before going to sleep
//delay(1000);
digitalWrite(LED_BUILTIN, LOW); // turn the LED on (HIGH is the voltage level)
delay(200); // wait for a second
digitalWrite(LED_BUILTIN, HIGH); // turn the LED off by making the voltage LOW
//delay(100); // wait for a second
// Put device to deep sleep
Serial.println("Going to sleep now");
delay(100);
esp_deep_sleep_start();
// delay(1000);
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
pinMode(SHT31_POWER_PIN, OUTPUT);
digitalWrite(SHT31_POWER_PIN, HIGH); // Power on the sensor
delay(1000); // Wait for a second to allow the Serial Monitor to open and DHT11 to stabilize
Serial.println("Starting setup...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor_Node15");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(0.1);
// Set power source to battery and set battery percentage to measured value (now 100% for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) anytime
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 99);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 3000;
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Successfully connected to Zigbee network");
//dht.begin();
//Serial.println(F("DHTxx test!"));
Serial.println("SHT31 test");
if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr
Serial.println("Couldn't find SHT31");
while (1) delay(1);
} else {
Serial.println("SHT31 sensor initialized successfully");
//Serial.print("Temp *C = "); Serial.print(sht31.readTemperature()); Serial.print("\t\t");
//Serial.print("Hum. % = "); Serial.println(sht31.readHumidity());
}
Serial.print("Heater Enabled State: ");
if (sht31.isHeaterEnabled())
Serial.println("ENABLED");
else
Serial.println("DISABLED");
//set the resolution to 12 bits (0-4096)
analogReadResolution(12);
pinMode(BAT_GPIO_ENABLE, OUTPUT);
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED off by making the voltage LOW
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
// Call the function to measure temperature and put the device to sleep
meausureAndSleep();
}

View File

@ -0,0 +1,272 @@
/*
Xiao ESP32-C6
NodeXX_Zigbee_SHT31D_Sleepy_v1.ino
DFRobot Beetle ESP32-C6 or Seeedstudio Xiao ESP32-C6
Zigbee End Device (sleepy) with SHT31D temperature/humidity sensor
Battery powered with deep sleep support
Based on Node14 (multi-sensor router) + Node15 (SHT31D sleepy end device)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router mode is not selected in Tools > Zigbee mode"
#endif
#define DEBUGTRACE // Comment out to reduce logging
#include <Zigbee.h>
#include <Wire.h>
#include <Adafruit_SHT31.h>
// -----------------------------------------------------------------------------
// Hardware definitions
// -----------------------------------------------------------------------------
#define LED_PIN LED_BUILTIN
#define I2C_SDA_PIN 22
#define I2C_SCL_PIN 23
#define SHT31_POWER_PIN 16 // Optional power control for SHT31D
// Endpoint numbers
#define SHT31_TEMP_ENDPOINT 10
#define SHT31_HUM_ENDPOINT 11 // Using analog for humidity if needed
// Sensor object
Adafruit_SHT31 sht31 = Adafruit_SHT31();
// Zigbee objects
ZigbeeTempSensor zbTempSensorSHT31(SHT31_TEMP_ENDPOINT);
// -----------------------------------------------------------------------------
// Health monitoring globals
// -----------------------------------------------------------------------------
SemaphoreHandle_t i2cMutex = NULL;
bool sht31FirstOk = false;
float lastTempSht31 = NAN, lastHumSht31 = NAN;
unsigned long lastTempSht31ChangeMs = 0, lastHumSht31ChangeMs = 0;
unsigned long lastZigbeeOkMs = 0;
int sht31ErrorCount = 0;
const int maxI2cErrors = 10;
// Watchdog thresholds
const unsigned long WATCHDOG_PERIOD_MS = 10000; // 10s
const unsigned long SENSOR_STUCK_TIMEOUT_MS = 30UL * 60UL * 1000UL; // 30min
const unsigned long ZIGBEE_DOWN_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10min
// -----------------------------------------------------------------------------
// Zigbee health touch
// -----------------------------------------------------------------------------
inline void zigbeeHealthTouch() {
if (Zigbee.connected()) {
lastZigbeeOkMs = millis();
}
}
// -----------------------------------------------------------------------------
// Helper: I2C guarded execution
// -----------------------------------------------------------------------------
#include <functional>
bool withI2C(std::function<bool(void)> fn, uint32_t timeoutMs = 100) {
if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(timeoutMs)) == pdTRUE) {
bool ok = fn();
xSemaphoreGive(i2cMutex);
return ok;
}
return false;
}
// -----------------------------------------------------------------------------
// SHT31D sensor task
// -----------------------------------------------------------------------------
static void sht31SensorValueUpdate(void* arg) {
for (;;) {
float temperature = NAN, humidity = NAN;
// Read SHT31D with I2C protection
bool ok = withI2C([&]() -> bool {
temperature = sht31.readTemperature();
humidity = sht31.readHumidity();
return !isnan(temperature) && !isnan(humidity);
});
if (ok) {
sht31ErrorCount = 0;
unsigned long now = millis();
if (!sht31FirstOk) {
sht31FirstOk = true;
lastTempSht31 = temperature;
lastHumSht31 = humidity;
lastTempSht31ChangeMs = lastHumSht31ChangeMs = now;
} else {
if (temperature != lastTempSht31) {
lastTempSht31 = temperature;
lastTempSht31ChangeMs = now;
}
if (humidity != lastHumSht31) {
lastHumSht31 = humidity;
lastHumSht31ChangeMs = now;
}
}
// Update and report Zigbee values
zbTempSensorSHT31.setTemperature(temperature);
zbTempSensorSHT31.setHumidity(humidity);
// zbTempSensorSHT31.report();
zigbeeHealthTouch();
#ifdef DEBUGTRACE
Serial.printf("SHT31D T:%.2fC H:%.2f%%\n", temperature, humidity);
#endif
} else {
sht31ErrorCount++;
#ifdef DEBUGTRACE
Serial.println("SHT31D read error or I2C busy.");
#endif
if (sht31ErrorCount >= maxI2cErrors) {
Serial.println("SHT31D too many errors, restarting...");
delay(200);
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s interval
}
}
// -----------------------------------------------------------------------------
// Watchdog task
// -----------------------------------------------------------------------------
void watchdogTask(void* param) {
for (;;) {
#ifdef DEBUGTRACE
Serial.println("Watchdog ran.");
#endif
unsigned long now = millis();
bool zigbeeBad = !Zigbee.connected() || (now - lastZigbeeOkMs > ZIGBEE_DOWN_TIMEOUT_MS);
bool sht31Stuck = sht31FirstOk &&
(now - lastTempSht31ChangeMs > SENSOR_STUCK_TIMEOUT_MS ||
now - lastHumSht31ChangeMs > SENSOR_STUCK_TIMEOUT_MS);
if (zigbeeBad || sht31Stuck) {
Serial.println("Watchdog fault detected, restarting...");
delay(500);
ESP.restart();
}
vTaskDelay(pdMS_TO_TICKS(WATCHDOG_PERIOD_MS));
}
}
// -----------------------------------------------------------------------------
// setup
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(100);
// Global manufacturer/model
// Zigbee.setManufacturer("Espressif");
// Zigbee.setModel("NodeXX-SHT31D-Router-v1");
// I2C init
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
i2cMutex = xSemaphoreCreateMutex();
// SHT31D init
Serial.println("Connecting to SHT31D...");
pinMode(SHT31_POWER_PIN, OUTPUT);
digitalWrite(SHT31_POWER_PIN, HIGH); // Power on if controlled
delay(100);
if (!sht31.begin(0x44)) { // 0x45 for alternate i2c addr
Serial.println("SHT31D not found! Check wiring.");
while (1) delay(10);
}
Serial.println("SHT31D initialized successfully!");
// Configure Zigbee endpoint
zbTempSensorSHT31.setManufacturerAndModel("Espressif", "Node_15-sht31d-th");
zbTempSensorSHT31.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensorSHT31.setMinMaxValue(-20, 80);
zbTempSensorSHT31.setTolerance(0.1);
zbTempSensorSHT31.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensorSHT31);
// Start Zigbee as router
Serial.println("Starting Zigbee...");
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start! Rebooting...");
ESP.restart();
}
Serial.println("Zigbee started successfully! Connecting to network...");
bool ledState = false;
unsigned long startTime = millis();
const unsigned long timeoutMs = 60000;
while (!Zigbee.connected()) {
Serial.print(".");
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
delay(100);
if (millis() - startTime > timeoutMs) {
Serial.println("\nConnection timed out (60s). Restarting...");
ESP.restart();
}
}
digitalWrite(LED_PIN, LOW);
Serial.println("\nConnected!");
lastZigbeeOkMs = millis();
// Create sensor tasks
xTaskCreate(sht31SensorValueUpdate, "sht31task", 4096, NULL, 10, NULL);
xTaskCreate(watchdogTask, "watchdog", 4096, NULL, 5, NULL);
// Reporting configuration
zbTempSensorSHT31.setReporting(60, 0, 0.1); // Min 60s, default, tol 0.5C
}
void loop() {
// Serial test commands
if (Serial.available()) {
char cmd = Serial.read();
switch (cmd) {
case 'Z': // Force Zigbee failure watchdog test
Serial.println("SIM Zigbee disconnect test");
lastZigbeeOkMs = 0;
break;
case 'E': // Force stuck sensor detection
Serial.println("SIM Stuck sensor test");
lastTempSht31ChangeMs = lastHumSht31ChangeMs = 0;
break;
case 'R': // Manual restart
Serial.println("SIM Manual restart");
ESP.restart();
break;
}
}
// Factory reset on long button press
static uint8_t button = BOOT_PIN;
if (digitalRead(button) == LOW) { // push button pressed
delay(100); // debounce
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if (millis() - startTime > 3000) {
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
ESP.restart();
}
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}

View File

@ -0,0 +1,413 @@
// DFRobot Beetle ESP32-C6 Partition Scheme: Zigbee ZCZR 4MB with spiffs
// Zigbee Mode: Zigbee ZR/ZC (coordinator,router)
// LED: GPIO15, on when HIGH
// DFRobot FireBeetle2 ESP32-C6
// Partition Scheme: Zigbee ZCZR 4MB with spiffs
// Zigbee Mode: Zigbee ZR/ZC (coordinator,router)
// LED: LED_BUILTIN, on when HIGH
// Node16: BME680 + T6713 only
#ifndef ZIGBEE_MODE_ZCZR
#error Zigbee coordinator/router mode is not selected in Tools->Zigbee mode
#endif
#define DEBUG_TRACE // Comment out DEBUG_TRACE to reduce normal-path logging
#include "Zigbee.h"
#include "Wire.h"
#include "bme68xLibrary.h"
// -----------------------------------------------------------------------------
// Hardware definitions (shared I2C bus)
// -----------------------------------------------------------------------------
#define LED_PIN LED_BUILTIN
#define I2C_SDA_PIN 17
#define I2C_SCL_PIN 16
#define POWER_PIN 21 // GPIO pin to power the sensor
// T6713 I2C address and registers
#define T6713_ADDR 0x15
#define T6713_REG_ABC_LOGIC 0x03EE
#define T6713_REG_STATUS 0x138A
// Endpoint numbers (BME680 + T6713 only)
#define BME_TEMP_ENDPOINT 10
#define BME_ANALOG_ENDPOINT 11
#define T6713_CO2_ENDPOINT 12
// Sensor objects (BME680 + T6713 only)
Bme68x bme;
ZigbeeTempSensor zbTempSensorBME(BME_TEMP_ENDPOINT);
ZigbeeAnalog zbAnalogBME(BME_ANALOG_ENDPOINT);
ZigbeeCarbonDioxideSensor zbCo2T6713(T6713_CO2_ENDPOINT);
// -----------------------------------------------------------------------------
// Health watchdog globals (BME + T6713 only)
// -----------------------------------------------------------------------------
SemaphoreHandle_t i2cMutex;
bool bmeFirstOk = false;
bool t6713FirstOk = false;
// Last-change tracking (BME + T6713 only)
float lastTempBme = NAN, lastHumBme = NAN, lastGasRes = NAN;
unsigned long lastTempBmeChangeMs = 0, lastHumBmeChangeMs = 0, lastGasResChangeMs = 0;
int lastCo2T6713 = -1;
unsigned long lastCo2T6713ChangeMs = 0;
unsigned long lastZigbeeOkMs = 0;
// Error counters (BME + T6713 only)
int bmeErrorCount = 0, t6713ErrorCount = 0;
const int maxI2cErrors = 10;
// Watchdog thresholds
const unsigned long WATCHDOG_PERIOD_MS = 10000; // 10s
const unsigned long SENSOR_STUCK_TIMEOUT_MS = 30UL * 60UL * 1000UL; // 30min
const unsigned long ZIGBEE_DOWN_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10min
// -----------------------------------------------------------------------------
// Zigbee health touch after successful reports
// -----------------------------------------------------------------------------
inline void zigbeeHealthTouch() {
if (Zigbee.connected()) lastZigbeeOkMs = millis();
}
// -----------------------------------------------------------------------------
// Helper: I2C guarded execution (use mutex)
// -----------------------------------------------------------------------------
#include <functional>
bool withI2C(std::function<bool(void)> fn, uint32_t timeoutMs = 100) {
if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(timeoutMs)) == pdTRUE) {
bool ok = fn();
xSemaphoreGive(i2cMutex);
return ok;
}
return false;
}
// -----------------------------------------------------------------------------
// T6713 helper functions (unchanged)
// -----------------------------------------------------------------------------
int readT6713() {
int result = -1;
bool ok = withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04); // Function: Read Input Registers
Wire.write(0x13); Wire.write(0x8B); // 0x138B
Wire.write(0x00); Wire.write(0x01);
if (Wire.endTransmission() != 0) return false;
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() >= 4) {
Wire.read(); Wire.read(); // skip func+len
byte high = Wire.read();
byte low = Wire.read();
result = (high << 8) | low;
return true;
}
return false;
});
if (!ok) result = -1;
return result;
}
void configureT6713(bool enableABC) {
delay(1000);
// Optional status check
withI2C([&]() {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04); Wire.write(0x13); Wire.write(0x8A); // 0x138A
Wire.write(0x00); Wire.write(0x01);
Wire.endTransmission();
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() >= 4) {
Wire.read(); Wire.read();
uint16_t status = (Wire.read() << 8) | Wire.read();
#ifdef DEBUG_TRACE
if (status == 0x08) Serial.println("T6713 Warming up...");
#endif
}
return true;
});
uint16_t abcValue = enableABC ? 0xFF00 : 0x0000;
withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x05); // Write Single Coil
Wire.write(highByte(T6713_REG_ABC_LOGIC));
Wire.write(lowByte(T6713_REG_ABC_LOGIC));
Wire.write(highByte(abcValue));
Wire.write(lowByte(abcValue));
byte error = Wire.endTransmission();
if (error == 0) {
#ifdef DEBUG_TRACE
Serial.print("T6713 ABC Logic set to "); Serial.println(enableABC ? "ON" : "OFF");
#endif
} else {
#ifdef DEBUG_TRACE
Serial.println("T6713 Failed to set ABC Logic");
#endif
}
return (error == 0);
});
delay(100);
}
// -----------------------------------------------------------------------------
// BME68x task (endpoint 10/11)
// -----------------------------------------------------------------------------
static void bme680sensorvalueupdate(void *arg) {
for (;;) {
float temperature = NAN, humidity = NAN, gasResistance = NAN;
bme68xData data;
bool ok = withI2C([&]() {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature;
humidity = data.humidity;
gasResistance = data.gas_resistance;
return true;
}
return false;
});
if (ok) {
bmeErrorCount = 0;
unsigned long now = millis();
if (!bmeFirstOk) {
bmeFirstOk = true;
lastTempBme = temperature; lastHumBme = humidity; lastGasRes = gasResistance;
lastTempBmeChangeMs = lastHumBmeChangeMs = lastGasResChangeMs = now;
} else {
if (temperature != lastTempBme) { lastTempBme = temperature; lastTempBmeChangeMs = now; }
if (humidity != lastHumBme) { lastHumBme = humidity; lastHumBmeChangeMs = now; }
if (gasResistance != lastGasRes) { lastGasRes = gasResistance; lastGasResChangeMs = now; }
}
zbTempSensorBME.setTemperature(temperature);
zbTempSensorBME.setHumidity(humidity);
zigbeeHealthTouch();
zbAnalogBME.setAnalogInput(round(gasResistance));
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.printf("BME68x T:%.2fC H:%.2f%% Gas:%.2f ohm\n", temperature, humidity, gasResistance);
#endif
} else {
bmeErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("BME68x read error or I2C busy.");
#endif
if (bmeErrorCount >= maxI2cErrors) {
Serial.println("BME68x too many errors, restarting...");
delay(200); ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// T6713 task (endpoint 15)
// -----------------------------------------------------------------------------
static void t6713sensorvalueupdate(void *arg) {
for (;;) {
int co2t6713 = readT6713();
if (co2t6713 >= 0) {
t6713ErrorCount = 0;
unsigned long now = millis();
if (!t6713FirstOk) {
t6713FirstOk = true;
lastCo2T6713 = co2t6713;
lastCo2T6713ChangeMs = now;
} else if (co2t6713 != lastCo2T6713) {
lastCo2T6713 = co2t6713;
lastCo2T6713ChangeMs = now;
#ifdef DEBUG_TRACE
Serial.print("T6713 CO2: "); Serial.print(co2t6713); Serial.println(" ppm");
#endif
}
zbCo2T6713.setCarbonDioxide(co2t6713);
zigbeeHealthTouch();
} else {
t6713ErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("T6713 read error.");
#endif
if (t6713ErrorCount >= maxI2cErrors) {
Serial.println("T6713 too many errors, restarting...");
delay(200); ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // 10s
}
}
// -----------------------------------------------------------------------------
// Watchdog task (BME + T6713 only)
// -----------------------------------------------------------------------------
void watchdogTask(void *param) {
for (;;) {
#ifdef DEBUG_TRACE
Serial.println("Watchdog ran.");
#endif
unsigned long now = millis();
bool zigbeeBad = !Zigbee.connected() || (now - lastZigbeeOkMs > ZIGBEE_DOWN_TIMEOUT_MS);
bool bmeStuck = bmeFirstOk && (
(now - lastTempBmeChangeMs > SENSOR_STUCK_TIMEOUT_MS) ||
(now - lastHumBmeChangeMs > SENSOR_STUCK_TIMEOUT_MS) ||
(now - lastGasResChangeMs > SENSOR_STUCK_TIMEOUT_MS));
bool t6713Stuck = t6713FirstOk && (now - lastCo2T6713ChangeMs > SENSOR_STUCK_TIMEOUT_MS);
if (zigbeeBad || bmeStuck || t6713Stuck) {
Serial.println("Watchdog fault detected, restarting...");
delay(500); ESP.restart();
}
vTaskDelay(pdMS_TO_TICKS(WATCHDOG_PERIOD_MS));
}
}
// -----------------------------------------------------------------------------
// setup
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
pinMode(POWER_PIN, OUTPUT);
digitalWrite(POWER_PIN, HIGH); // Power on the sensor(s)
delay(100);
// I2C init
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
i2cMutex = xSemaphoreCreateMutex();
// BME680 init
Serial.println("Connecting to BME680...");
bme.begin(BME68X_I2C_ADDR_LOW, Wire); // BME68X_I2C_ADDR_LOW, BME68X_I2C_ADDR_HIGH
if (bme.checkStatus() == BME68X_ERROR) {
Serial.println("❌ BME68X_ERROR: " + bme.statusString());
} else if (bme.checkStatus() == BME68X_WARNING) {
Serial.println("⚠️ BME68X_WARNING: " + bme.statusString());
} else {
Serial.println("✓ BME68X status OK");
}
bme.setTPH(BME68X_OS_8X, BME68X_OS_2X, BME68X_OS_8X);
bme.setHeaterProf(300, 100);
bme.setOpMode(BME68X_FORCED_MODE);
zbTempSensorBME.setManufacturerAndModel("Espressif", "Node16_bme680th");
zbTempSensorBME.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensorBME.setMinMaxValue(-20, 80);
zbTempSensorBME.setTolerance(1);
zbTempSensorBME.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensorBME);
zbAnalogBME.setManufacturerAndModel("Espressif", "Node16_bme680r");
zbAnalogBME.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbAnalogBME.addAnalogInput();
Zigbee.addEndpoint(&zbAnalogBME);
// T6713 config & endpoint
configureT6713(true); // Enable ABC
zbCo2T6713.setManufacturerAndModel("Espressif", "Node16_t6713");
zbCo2T6713.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbCo2T6713.setMinMaxValue(0, 10000);
Zigbee.addEndpoint(&zbCo2T6713);
// Start Zigbee as router
Serial.println("Starting Zigbee...");
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start! Rebooting...");
ESP.restart();
}
Serial.println("Zigbee started successfully! Connecting to network...");
bool ledState = false;
unsigned long startTime = millis();
const unsigned long timeoutMs = 60000;
while (!Zigbee.connected()) {
Serial.print(".");
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
delay(100);
if (millis() - startTime > timeoutMs) {
Serial.println(" timed out (60s). Restarting...");
ESP.restart();
}
}
digitalWrite(LED_PIN, LOW);
Serial.println(" connected!");
lastZigbeeOkMs = millis();
// Create sensor tasks
xTaskCreate(bme680sensorvalueupdate, "bme_task", 4096, NULL, 10, NULL);
xTaskCreate(t6713sensorvalueupdate, "t6713_task", 4096, NULL, 10, NULL);
xTaskCreate(watchdogTask, "watchdog", 4096, NULL, 5, NULL);
// Reporting configuration (after Zigbee.begin)
zbTempSensorBME.setReporting(60, 0, 0.5);
zbAnalogBME.setAnalogInputReporting(60, 600, 10);
zbCo2T6713.setReporting(30, 300, 10);
}
// -----------------------------------------------------------------------------
// loop (light - button reset + test commands)
// -----------------------------------------------------------------------------
void loop() {
// Test commands via Serial
if (Serial.available()) {
char cmd = Serial.read();
switch (cmd) {
case 'Z': // Force Zigbee failure (watchdog test)
Serial.println("SIM Zigbee disconnect test");
lastZigbeeOkMs = 0; // Watchdog sees Zigbee down
break;
case 'E': // Force stuck sensor detection
Serial.println("SIM Stuck sensor test");
lastTempBmeChangeMs = lastHumBmeChangeMs = lastGasResChangeMs = 0;
lastCo2T6713ChangeMs = 0;
break;
case 'R': // Manual restart
Serial.println("SIM Manual restart");
ESP.restart();
break;
}
}
// Factory reset on long button press
static uint8_t button = BOOT_PIN;
if (digitalRead(button) == LOW) { // push button pressed
delay(100); // debounce
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if (millis() - startTime > 3000) {
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
ESP.restart();
}
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}

View File

@ -0,0 +1,443 @@
/*
Device: DF Robot Firebeetle 2 ESP32-C6
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
// Uncomment the following line to display debug traces in serial monitor of Arduino IDE
#define DEBUG_TRACE
#include "Zigbee.h"
//#include <BME280I2C.h>
//#include <Wire.h>
#include "Arduino.h"
#include <bme68xLibrary.h>
#include "TelaireT6713.h"
// Define the I2C pins
#define SCL_PIN 16
#define SDA_PIN 17
#define BAT_ADC_PIN 0 // ADC input pin
#define POWER_PIN 21 // GPIO pin to power the sensor
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 300 /* Sleep for 10 minutes */
/* Zigbee temperature + humidity sensor configuration */
#define BME_TEMP_SENSOR_ENDPOINT_NUMBER 10
ZigbeeTempSensor zbTempSensor_bme = ZigbeeTempSensor(BME_TEMP_SENSOR_ENDPOINT_NUMBER);
#define BME_CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 11
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor_bme = ZigbeeCarbonDioxideSensor(BME_CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
#define T6713_CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 12
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor_t6713 = ZigbeeCarbonDioxideSensor(T6713_CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
TelaireT6713 co2Sensor;
/* Zigbee contact sensor configuration */
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// uint8_t float_switch_pin = 3;
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// #define MAGNETIC_SENSOR_PIN 12
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// bool contact_open = false;
/* Zigbee analog device configuration */
// #define ANALOG_DEVICE_ENDPOINT_NUMBER 15
// uint8_t analogPin = 3;
// ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.0;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float val;
val = (x - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(BAT_ADC_PIN); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
// Get data from BME280 sensor and go to deep sleep mode
void meausureAndSleep() {
// Measure temperature sensor value
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
float vBat;
// BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
// BME280::PresUnit presUnit(BME280::PresUnit_hPa);
// Read temperature and humidity on BME280 sensor
// sensor.read(pressure, temperature, humidity, tempUnit, presUnit);
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
// Set the desired settings for the BME680
//bme.amb_temp = 25; // Set ambient temperature for compensation
//bme.meas_status = 0; // Clear previous measurement status
// Perform a measurement
//rslt = bme68x_get_sensor_data(BME68X_ALL, &data, &bme);
//if (rslt == BME68X_OK) {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// // Checking pin for contact change
// static bool contact = false;
// if (digitalRead(float_switch_pin) == HIGH && !contact) {
// // Update contact sensor value
// // Serial.println("Float sensor: true");
// zbContactSwitch.setOpen();
// contact = true;
// } else if (digitalRead(float_switch_pin) == LOW && contact) {
// // Serial.println("Float sensor: true");
// zbContactSwitch.setClosed();
// contact = false;
// }
// #ifdef DEBUG_TRACE
// Serial.println(contact ? "Float sensor: true" : "Float sensor: false");
// #endif
// // Read ADC value and update the analog value every 2s
// float analog = (float)analogRead(analogPin);
// Serial.printf("Updating analog input to %.1f\r\n", analog);
// zbAnalogDevice.setAnalogInput(analog);
// delay(100);
// // Analog input supports reporting
// zbAnalogDevice.reportAnalogInput();
int co2ppm = co2Sensor.readCO2();
if (co2ppm >= 0) {
Serial.print("CO2: "); Serial.print(co2ppm); Serial.println(" ppm");
} else {
Serial.println("CO2 read error");
}
delay(100);
digitalWrite(POWER_PIN, LOW); // Power off sensor
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor_bme.setTemperature(temperature);
zbTempSensor_bme.setHumidity(humidity);
//zbTempSensor.setGasResistance(gasResistance); // Assuming you have a method for gas resistance
// Measure battery voltage
vBat = getVbatt();
percentage = mapFloat(vBat, minVoltage, maxVoltage);
#ifdef DEBUG_TRACE
Serial.printf("Battery: %.2fV (%d%%)\n", vBat, percentage);
#endif
// Update battery percentage
zbTempSensor_bme.setBatteryPercentage(percentage);
//zbTempSensor.setBatteryVoltage(vBat * 10); // voltage in 100mV
// Magnetic contact sensor
// static bool contact = false;
// if (digitalRead(MAGNETIC_SENSOR_PIN) == HIGH && !contact) {
// // Update contact sensor value
// zbContactSwitch.setOpen();
// contact = true;
// Serial.printf("contact = true");
// } else if (digitalRead(MAGNETIC_SENSOR_PIN) == LOW && contact) {
// zbContactSwitch.setClosed();
// contact = false;
// Serial.printf("contact = false");
// }
// Report values
zbTempSensor_bme.report();
zbTempSensor_bme.reportBatteryPercentage();
zbCarbonDioxideSensor_bme.setCarbonDioxide(gasResistance);
zbCarbonDioxideSensor_bme.report();
zbCarbonDioxideSensor_t6713.setCarbonDioxide(co2ppm);
zbCarbonDioxideSensor_t6713.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// Turn on the builtin LED for a very short time
flashLED();
// Add small delay to allow the data to be sent before going to sleep
delay(500);
// Put device to deep sleep
#ifdef DEBUG_TRACE
Serial.println("Going to sleep now");
#endif
esp_deep_sleep_start();
// delay(2000);
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100);
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Configure use of external antenna
// pinMode(WIFI_ENABLE, OUTPUT); // pinMode(3, OUTPUT);
// digitalWrite(WIFI_ENABLE, LOW); // digitalWrite(3, LOW); // Activate RF switch control
// delay(100);
// pinMode(WIFI_ANT_CONFIG, OUTPUT); // pinMode(14, OUTPUT);
// digitalWrite(WIFI_ANT_CONFIG, HIGH); // digitalWrite(14, HIGH); // Use external antenna
// Configure builtin LED and turn it OFF (HIGH)
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(POWER_PIN, OUTPUT);
digitalWrite(POWER_PIN, HIGH); // Power on the sensor(s)
/*
// Init BME280 sensor
Wire.begin();
while (!sensor.begin()) {
#ifdef DEBUG_TRACE
Serial.println("Could not find BME280 sensor!");
#endif
delay(1000);
}
*/
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_LOW, Wire); // BME68X_I2C_ADDR_HIGH=0x76 (SDO CONNECTED TO VCC), BME68X_I2C_ADDR_LOW=0x77 (SDO CONNECTED TO GND)
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Configure A0 as ADC input for reading battery voltage
pinMode(BAT_ADC_PIN, INPUT);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor_bme.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor_Node16");
//zbCarbonDioxideSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor");
// Set minimum and maximum temperature measurement value
zbTempSensor_bme.setMinMaxValue(-20, 80);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor_bme.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
//zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
zbTempSensor_bme.setPowerSource(ZB_POWER_SOURCE_BATTERY);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor_bme.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor_bme);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor_bme.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor_bme);
// pinMode(MAGNETIC_SENSOR_PIN, INPUT_PULLUP); // Assuming switch pulls LOW when closed
// Zigbee.addEndpoint(&zbContactSwitch);
// pinMode(float_switch_pin, INPUT_PULLUP);
// Zigbee.addEndpoint(&zbContactSwitch);
// // Set analog resolution to 10 bits
// analogReadResolution(10);
// // Add analog clusters to Zigbee Analog according your needs
// zbAnalogDevice.addAnalogInput();
// // Add endpoints to Zigbee Core
// Zigbee.addEndpoint(&zbAnalogDevice);
// zbAnalogDevice.setAnalogInputReporting(5, 60, 1.0); // Example: report every 560s if change >= 1.0
// Telaire CO2 sensor init
co2Sensor.setI2CPins(SDA_PIN, SCL_PIN);
co2Sensor.setAddress(0x15);
co2Sensor.begin();
co2Sensor.enableABC();
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor_t6713.setMinMaxValue(0, 100000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor_t6713);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
// If Zigbee does not start with 30s default timeout (ZB_BEGIN_TIMEOUT_DEFAULT) then restart
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
//zbCarbonDioxideSensor.setReporting(0, 30, 0);
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
// Call the function to measure temperature and put the device to deep sleep
// meausureAndSleep();
}
void loop() {
// No actions are performed in the loop (the ESP32C6 enters the setup function when it exits deep sleep).
meausureAndSleep();
}
x

View File

@ -0,0 +1,112 @@
#include "TelaireT6713.h"
TelaireT6713::TelaireT6713(uint8_t address, uint8_t sda, uint8_t scl)
: _address(address), _sda(sda), _scl(scl) {}
void TelaireT6713::setI2CPins(uint8_t sda, uint8_t scl) {
_sda = sda;
_scl = scl;
}
void TelaireT6713::setAddress(uint8_t address) {
_address = address;
}
void TelaireT6713::begin() {
Wire.begin(_sda, _scl);
delay(1000);
Serial.println("Telaire T6713 Initialized.");
printSensorStatus();
}
int TelaireT6713::readCO2(bool debug) {
int data[4];
Wire.beginTransmission(_address);
Wire.write(0x04); Wire.write(0x13); Wire.write(0x8B);
Wire.write(0x00); Wire.write(0x01);
Wire.endTransmission();
delay(2000);
Wire.requestFrom(_address, (uint8_t)4);
if (Wire.available() < 4) {
if (debug) Serial.println("Error: Less than 4 bytes received");
return -1;
}
data[0] = Wire.read();
data[1] = Wire.read();
data[2] = Wire.read();
data[3] = Wire.read();
if (debug) {
Serial.print("FUNC: 0x"); Serial.println(data[0], HEX);
Serial.print("BYTE COUNT: 0x"); Serial.println(data[1], HEX);
Serial.print("MSB: 0x"); Serial.println(data[2], HEX);
Serial.print("LSB: 0x"); Serial.println(data[3], HEX);
}
int ppm = ((data[2] & 0x3F) << 8) | data[3];
return ppm;
}
void TelaireT6713::enableABC() {
uint8_t cmd[] = { 0x05, 0x03, 0xEE, 0xFF, 0x00 };
sendCommand(cmd, sizeof(cmd));
Serial.println("ABC Enabled.");
}
void TelaireT6713::disableABC() {
uint8_t cmd[] = { 0x05, 0x03, 0xEE, 0x00, 0x00 };
sendCommand(cmd, sizeof(cmd));
Serial.println("ABC Disabled.");
}
void TelaireT6713::printSensorStatus() {
uint8_t statusCmd[] = { 0x04, 0x13, 0x8A, 0x00, 0x01 };
sendCommand(statusCmd, sizeof(statusCmd));
delay(100);
uint8_t response[4];
if (!readBytes(response, 4)) {
Serial.println("Failed to read sensor status.");
return;
}
uint8_t msb = response[2];
uint8_t lsb = response[3];
Serial.print("Sensor status: ");
if (msb == 0x00 && lsb == 0x00) Serial.println("No error.");
else if (msb == 0x00 && lsb == 0x01) Serial.println("Error condition.");
else if (msb == 0x00 && lsb == 0x02) Serial.println("Flash error.");
else if (msb == 0x00 && lsb == 0x03) Serial.println("Calibration error.");
else if (msb == 0x04 && lsb == 0x00) Serial.println("Reboot.");
else if (msb == 0x08 && lsb == 0x00) Serial.println("Warm-up mode.");
else if (msb == 0x80 && lsb == 0x00) Serial.println("Single point calibration.");
else {
Serial.print("Unknown status (0x");
Serial.print(msb, HEX);
Serial.print(" 0x");
Serial.print(lsb, HEX);
Serial.println(")");
}
}
void TelaireT6713::sendCommand(const uint8_t* cmd, size_t len) {
Wire.beginTransmission(_address);
for (size_t i = 0; i < len; ++i) {
Wire.write(cmd[i]);
}
Wire.endTransmission();
}
bool TelaireT6713::readBytes(uint8_t* buffer, size_t length) {
Wire.requestFrom(_address, (uint8_t)length);
size_t i = 0;
while (Wire.available() && i < length) {
buffer[i++] = Wire.read();
}
return i == length;
}

View File

@ -0,0 +1,26 @@
#ifndef TELAIRE_T6713_H
#define TELAIRE_T6713_H
#include <Arduino.h>
#include <Wire.h>
class TelaireT6713 {
public:
TelaireT6713(uint8_t address = 0x15, uint8_t sda = 8, uint8_t scl = 14);
void begin();
int readCO2(bool debug = false);
void enableABC();
void disableABC();
void printSensorStatus();
void setI2CPins(uint8_t sda, uint8_t scl);
void setAddress(uint8_t address);
private:
uint8_t _address;
uint8_t _sda, _scl;
void sendCommand(const uint8_t* cmd, size_t len);
bool readBytes(uint8_t* buffer, size_t length);
};
#endif

View File

@ -0,0 +1,803 @@
// DFRobot Beetle ESP32-C6
// Partition Scheme: Zigbee ZCZR 4MB with spiffs
// Zigbee Mode: Zigbee ZRZC (coordinator,router)
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
#endif
// Comment out DEBUG_TRACE to reduce normal-path logging
#define DEBUG_TRACE
#include "Zigbee.h"
#include "DFRobot_C4001.h"
#include <Wire.h>
#include <SensirionI2cScd4x.h>
#include <bme68xLibrary.h>
// -----------------------------------------------------------------------------
// Hardware definitions
// -----------------------------------------------------------------------------
#define LED_PIN 15
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 4;
// Radar UART pins (DFRobot C4001)
#define RADAR_RX_PIN 17
#define RADAR_TX_PIN 16
// I2C pins
#define I2C_SCL_PIN 20
#define I2C_SDA_PIN 6
// T6713 I2C address and registers
#define T6713_ADDR 0x15
#define T6713_REG_ABC_LOGIC 0x03EE
#define T6713_REG_STATUS 0x138A
// Endpoint numbers
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER_SCD41 11
#define TEMP_SENSOR_ENDPOINT_NUMBER_SCD41 12
#define TEMP_SENSOR_ENDPOINT_NUMBER_BME68X 13
#define ANALOG_DEVICE_ENDPOINT_NUMBER_BME68X 14
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER_T6713 15
// Radar / occupancy
ZigbeeOccupancySensor zbOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
HardwareSerial RadarSerial(1);
DFRobot_C4001_UART radar(&RadarSerial, 9600, RADAR_RX_PIN, RADAR_TX_PIN);
eMode_t currentMode = eExitMode; // 0=presence, 1=speed
unsigned long lastSpeedReport = 0;
bool lastOccupancy = false;
unsigned long occupancyStartTime = 0;
// Radar presence configuration
const unsigned long OCCUPANCY_TIMEOUT = 4; // 4 -> 2 seconds (sensor-side keep time)
const unsigned long TRIGGER_DELAY = 10; // 10 -> 100 ms
// -----------------------------------------------------------------------------
// Health / watchdog globals
// -----------------------------------------------------------------------------
// I2C mutex to guard shared bus across tasks
SemaphoreHandle_t i2cMutex;
// “first data received” guards
bool scd41FirstOk = false;
bool bmeFirstOk = false;
bool t6713FirstOk = false;
// SCD41 last-change tracking
uint16_t lastCo2Scd = 0;
float lastTempScd = NAN;
float lastHumScd = NAN;
unsigned long lastCo2ScdChangeMs = 0;
unsigned long lastTempScdChangeMs = 0;
unsigned long lastHumScdChangeMs = 0;
// T6713 last-change tracking
int lastCo2T6713 = -1;
unsigned long lastCo2T6713ChangeMs = 0;
// BME68x last-change tracking
float lastTempBme = NAN;
float lastHumBme = NAN;
float lastGasRes = NAN;
unsigned long lastTempBmeChangeMs = 0;
unsigned long lastHumBmeChangeMs = 0;
unsigned long lastGasResChangeMs = 0;
// Zigbee health
unsigned long lastZigbeeOkMs = 0;
// Watchdog thresholds
const unsigned long WATCHDOG_PERIOD_MS = 10000; // 10 s
const unsigned long SENSOR_STUCK_TIMEOUT_MS = 30UL * 60UL * 1000UL; // 30 min
const unsigned long ZIGBEE_DOWN_TIMEOUT_MS = 10UL * 60UL * 1000UL; // 10 min
// I2C error counters
int scd41ErrorCount = 0;
int bmeErrorCount = 0;
int t6713ErrorCount = 0;
const int maxI2cErrors = 10;
// -----------------------------------------------------------------------------
// Zigbee sensor endpoints
// -----------------------------------------------------------------------------
SensirionI2cScd4x scd41;
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor(
CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER_SCD41);
ZigbeeTempSensor zbTempSensor_SCD4X(
TEMP_SENSOR_ENDPOINT_NUMBER_SCD41);
Bme68x bme;
ZigbeeTempSensor zbTempSensor_BME68X(
TEMP_SENSOR_ENDPOINT_NUMBER_BME68X);
ZigbeeAnalog zbAnalogDevice_BME68X(
ANALOG_DEVICE_ENDPOINT_NUMBER_BME68X);
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor_T6713(
CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER_T6713);
// -----------------------------------------------------------------------------
// Helper: Zigbee health “touch” after successful reports
// -----------------------------------------------------------------------------
inline void zigbeeHealthTouch() {
if (Zigbee.connected()) {
lastZigbeeOkMs = millis();
}
}
// -----------------------------------------------------------------------------
// Helper: I2C guarded execution (use mutex)
// If your toolchain dislikes std::function, inline xSemaphoreTake/Give in tasks.
// -----------------------------------------------------------------------------
#include <functional>
bool withI2C(std::function<bool(void)> fn, uint32_t timeoutMs = 100) {
if (xSemaphoreTake(i2cMutex, pdMS_TO_TICKS(timeoutMs)) == pdTRUE) {
bool ok = fn();
xSemaphoreGive(i2cMutex);
return ok;
}
return false;
}
// -----------------------------------------------------------------------------
// Radar configuration
// -----------------------------------------------------------------------------
void setPresenceMode() {
radar.setSensorMode(eExitMode);
currentMode = eExitMode;
#ifdef DEBUG_TRACE
Serial.println("Switched to PRESENCE mode");
#endif
sSensorStatus_t data;
data = radar.getStatus();
#ifdef DEBUG_TRACE
Serial.print("work status = ");
Serial.println(data.workStatus);
Serial.print("work mode = ");
Serial.println(data.workMode);
Serial.print("init status = ");
Serial.println(data.initStatus);
Serial.println();
#endif
if (radar.setDetectionRange(/*min*/ 30, /*max*/ 500, /*trig*/ 500)) {
#ifdef DEBUG_TRACE
Serial.println("Radar: set detection range successfully");
#endif
if (radar.setTrigSensitivity(8)) {
#ifdef DEBUG_TRACE
Serial.println("Radar: set trig sensitivity successfully");
#endif
if (radar.setKeepSensitivity(8)) {
#ifdef DEBUG_TRACE
Serial.println("Radar: set keep sensitivity successfully");
#endif
if (radar.setDelay(/*trig*/ TRIGGER_DELAY, /*keep*/ OCCUPANCY_TIMEOUT)) {
#ifdef DEBUG_TRACE
Serial.println("Radar: set delay successfully");
Serial.print("trig sensitivity = ");
Serial.println(radar.getTrigSensitivity());
Serial.print("keep sensitivity = ");
Serial.println(radar.getKeepSensitivity());
Serial.print("min range = ");
Serial.println(radar.getMinRange());
Serial.print("max range = ");
Serial.println(radar.getMaxRange());
Serial.print("trig range = ");
Serial.println(radar.getTrigRange());
Serial.print("keep time = ");
Serial.println(radar.getKeepTimerout());
Serial.print("trig delay = ");
Serial.println(radar.getTrigDelay());
#endif
}
}
}
}
}
void setSpeedMode() {
radar.setSensorMode(eSpeedMode);
currentMode = eSpeedMode;
#ifdef DEBUG_TRACE
Serial.println("Switched to SPEED mode");
#endif
sSensorStatus_t data;
data = radar.getStatus();
#ifdef DEBUG_TRACE
Serial.print("work status = ");
Serial.println(data.workStatus);
Serial.print("work mode = ");
Serial.println(data.workMode);
Serial.print("init status = ");
Serial.println(data.initStatus);
Serial.println();
#endif
if (radar.setDetectThres(/*min*/ 11, /*max*/ 1200, /*thres*/ 10)) {
#ifdef DEBUG_TRACE
Serial.println("Radar: set detect threshold successfully");
#endif
radar.setFrettingDetection(eON);
#ifdef DEBUG_TRACE
Serial.print("min range = ");
Serial.println(radar.getTMinRange());
Serial.print("max range = ");
Serial.println(radar.getTMaxRange());
Serial.print("threshold range = ");
Serial.println(radar.getThresRange());
Serial.print("fretting detection = ");
Serial.println(radar.getFrettingDetection());
#endif
}
}
// Optional: serial commands to switch radar mode
void handleSerialCommands() {
while (Serial.available()) {
char cmd = Serial.read();
if (cmd == 'P' || cmd == 'p') {
setPresenceMode();
} else if (cmd == 'S' || cmd == 's') {
setSpeedMode();
}
}
}
// -----------------------------------------------------------------------------
// Radar / occupancy task (FreeRTOS)
// -----------------------------------------------------------------------------
void pirTask(void *parameter) {
for (;;) {
if (currentMode == eExitMode) {
bool motionNow = radar.motionDetection();
if (motionNow && !lastOccupancy) {
#ifdef DEBUG_TRACE
Serial.println("MOTION DETECTED → OCCUPANCY ON");
#endif
lastOccupancy = true;
occupancyStartTime = millis();
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
zigbeeHealthTouch();
} else if (motionNow && lastOccupancy) {
// Motion still present → reset timeout
occupancyStartTime = millis();
} else if (!motionNow && lastOccupancy &&
(millis() - occupancyStartTime > OCCUPANCY_TIMEOUT * 1000UL / 2UL)) {
#ifdef DEBUG_TRACE
Serial.println("OCCUPANCY TIMEOUT → OFF");
#endif
lastOccupancy = false;
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
zigbeeHealthTouch();
} else {
// no motion and already off, or still within timeout
}
} else if (currentMode == eSpeedMode) {
#ifdef DEBUG_TRACE
Serial.print("Targets: ");
Serial.print(radar.getTargetNumber());
Serial.print(" | Speed: ");
Serial.print(radar.getTargetSpeed(), 2);
Serial.print(" m/s | Range: ");
Serial.print(radar.getTargetRange(), 2);
Serial.print(" m | Energy: ");
Serial.println(radar.getTargetEnergy());
#endif
}
vTaskDelay(pdMS_TO_TICKS(50));
}
}
// -----------------------------------------------------------------------------
// SCD41 task
// -----------------------------------------------------------------------------
static void scd4x_sensor_value_update(void *arg) {
for (;;) {
uint16_t co2_scd = 0;
float temperature_scd = 0.0f;
float humidity_scd = 0.0f;
uint16_t error = 1;
bool i2cOk = withI2C([&]() -> bool {
error = scd41.readMeasurement(co2_scd, temperature_scd, humidity_scd);
return (error == 0);
});
if (i2cOk && error == 0 && co2_scd != 0) {
scd41ErrorCount = 0;
unsigned long now = millis();
if (!scd41FirstOk) {
scd41FirstOk = true;
lastCo2Scd = co2_scd;
lastTempScd = temperature_scd;
lastHumScd = humidity_scd;
lastCo2ScdChangeMs = now;
lastTempScdChangeMs = now;
lastHumScdChangeMs = now;
} else {
if (co2_scd != lastCo2Scd) {
lastCo2Scd = co2_scd;
lastCo2ScdChangeMs = now;
}
if (temperature_scd != lastTempScd) {
lastTempScd = temperature_scd;
lastTempScdChangeMs = now;
}
if (humidity_scd != lastHumScd) {
lastHumScd = humidity_scd;
lastHumScdChangeMs = now;
}
}
zbCarbonDioxideSensor.setCarbonDioxide(co2_scd);
// zbCarbonDioxideSensor.report();
zigbeeHealthTouch();
zbTempSensor_SCD4X.setTemperature(temperature_scd);
zbTempSensor_SCD4X.setHumidity(humidity_scd);
// zbTempSensor_SCD4X.report();
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.print("[SCD41] CO2: "); Serial.print(co2_scd); Serial.print(" ppm");
Serial.print(" | Temp: "); Serial.print(temperature_scd); Serial.print(" C");
Serial.print(" | Hum: "); Serial.print(humidity_scd); Serial.println(" %");
#endif
} else {
scd41ErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("SCD41 read error or I2C busy.");
#endif
if (scd41ErrorCount >= maxI2cErrors) {
Serial.println("SCD41: too many errors, restarting...");
delay(200);
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // ~10 s
}
}
// -----------------------------------------------------------------------------
// BME68x task
// -----------------------------------------------------------------------------
static void bme680_sensor_value_update(void *arg) {
for (;;) {
float temperature = NAN, humidity = NAN, pressure = NAN, gasResistance = NAN;
bme68xData data;
bool ok = withI2C([&]() -> bool {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature;
humidity = data.humidity;
pressure = data.pressure;
gasResistance = data.gas_resistance;
return true;
}
return false;
});
if (ok) {
bmeErrorCount = 0;
unsigned long now = millis();
if (!bmeFirstOk) {
bmeFirstOk = true;
lastTempBme = temperature;
lastHumBme = humidity;
lastGasRes = gasResistance;
lastTempBmeChangeMs = now;
lastHumBmeChangeMs = now;
lastGasResChangeMs = now;
} else {
if (temperature != lastTempBme) {
lastTempBme = temperature;
lastTempBmeChangeMs = now;
}
if (humidity != lastHumBme) {
lastHumBme = humidity;
lastHumBmeChangeMs = now;
}
if (gasResistance != lastGasRes) {
lastGasRes = gasResistance;
lastGasResChangeMs = now;
}
}
zbTempSensor_BME68X.setTemperature(temperature);
zbTempSensor_BME68X.setHumidity(humidity);
// zbTempSensor_BME68X.report();
zigbeeHealthTouch();
zbAnalogDevice_BME68X.setAnalogInput(round(gasResistance));
// zbAnalogDevice_BME68X.reportAnalogInput();
zigbeeHealthTouch();
#ifdef DEBUG_TRACE
Serial.printf("BME68x: T=%.2fC H=%.2f%% Gas=%.2f ohm\r\n",
temperature, humidity, gasResistance);
#endif
} else {
bmeErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("BME68x read error or I2C busy.");
#endif
if (bmeErrorCount >= maxI2cErrors) {
Serial.println("BME68x: too many errors, restarting...");
delay(200);
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // ~10 s
}
}
// -----------------------------------------------------------------------------
// T6713 helper and task
// -----------------------------------------------------------------------------
int readT6713() {
int result = -1;
bool ok = withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04); // Function: Read Input Registers
Wire.write(0x13);
Wire.write(0x8B);
Wire.write(0x00);
Wire.write(0x01);
if (Wire.endTransmission() != 0) return false;
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() == 4) {
Wire.read(); Wire.read(); // skip func & len
byte high = Wire.read();
byte low = Wire.read();
result = (high << 8) | low;
return true;
}
return false;
});
if (!ok) {
result = -1;
}
return result;
}
void configureT6713(bool enableABC) {
delay(1000);
// Optional status check
withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04);
Wire.write(0x13); Wire.write(0x8A); // 0x138A
Wire.write(0x00); Wire.write(0x01);
Wire.endTransmission();
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() == 4) {
Wire.read(); Wire.read();
uint16_t status = (Wire.read() << 8) | Wire.read();
#ifdef DEBUG_TRACE
if (status & 0x08) {
Serial.println("T6713: Warming up...");
}
#endif
}
return true;
});
uint16_t abcValue = enableABC ? 0xFF00 : 0x0000;
withI2C([&]() -> bool {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x05); // Write Single Coil
Wire.write(highByte(T6713_REG_ABC_LOGIC));
Wire.write(lowByte(T6713_REG_ABC_LOGIC));
Wire.write(highByte(abcValue));
Wire.write(lowByte(abcValue));
byte error = Wire.endTransmission();
if (error == 0) {
#ifdef DEBUG_TRACE
Serial.print("T6713: ABC Logic set to ");
Serial.println(enableABC ? "ON" : "OFF");
#endif
} else {
#ifdef DEBUG_TRACE
Serial.println("T6713: Failed to set ABC Logic");
#endif
}
return true;
});
delay(100);
}
static void t6713_sensor_value_update(void *arg) {
for (;;) {
int co2_t6713 = readT6713();
if (co2_t6713 >= 0) {
t6713ErrorCount = 0;
unsigned long now = millis();
if (!t6713FirstOk) {
t6713FirstOk = true;
lastCo2T6713 = co2_t6713;
lastCo2T6713ChangeMs = now;
} else if (co2_t6713 != lastCo2T6713) {
lastCo2T6713 = co2_t6713;
lastCo2T6713ChangeMs = now;
}
#ifdef DEBUG_TRACE
Serial.print("T6713 | CO2: "); Serial.print(co2_t6713); Serial.println(" ppm");
#endif
zbCarbonDioxideSensor_T6713.setCarbonDioxide(co2_t6713);
// zbCarbonDioxideSensor_T6713.report();
zigbeeHealthTouch();
} else {
t6713ErrorCount++;
#ifdef DEBUG_TRACE
Serial.println("T6713 read error.");
#endif
if (t6713ErrorCount >= maxI2cErrors) {
Serial.println("T6713: too many errors, restarting...");
delay(200);
ESP.restart();
}
}
vTaskDelay(pdMS_TO_TICKS(10000)); // ~10 s
}
}
// -----------------------------------------------------------------------------
// Watchdog task: checks Zigbee and “no-change” conditions
// -----------------------------------------------------------------------------
void watchdogTask(void *param) {
for (;;) {
// #ifdef DEBUG_TRACE
// Serial.println("Watchdog ran.");
// Serial.printf("scd41FirstOk=%i, lastCo2ScdChangeMs=%i\r\n",
// scd41FirstOk, lastCo2ScdChangeMs);
// #endif
unsigned long now = millis();
bool zigbeeBad = (!Zigbee.connected() &&
(now - lastZigbeeOkMs > ZIGBEE_DOWN_TIMEOUT_MS));
bool co2ScdStuck = scd41FirstOk &&
(now - lastCo2ScdChangeMs > SENSOR_STUCK_TIMEOUT_MS);
bool tempScdStuck = scd41FirstOk &&
(now - lastTempScdChangeMs > SENSOR_STUCK_TIMEOUT_MS);
bool humScdStuck = scd41FirstOk &&
(now - lastHumScdChangeMs > SENSOR_STUCK_TIMEOUT_MS);
bool co2T6713Stuck = t6713FirstOk &&
(now - lastCo2T6713ChangeMs > SENSOR_STUCK_TIMEOUT_MS);
bool tempBmeStuck = bmeFirstOk &&
(now - lastTempBmeChangeMs > SENSOR_STUCK_TIMEOUT_MS);
bool humBmeStuck = bmeFirstOk &&
(now - lastHumBmeChangeMs > SENSOR_STUCK_TIMEOUT_MS);
bool gasResStuck = bmeFirstOk &&
(now - lastGasResChangeMs > SENSOR_STUCK_TIMEOUT_MS);
if (zigbeeBad || co2ScdStuck || tempScdStuck || humScdStuck ||
co2T6713Stuck || tempBmeStuck || humBmeStuck || gasResStuck) {
Serial.println("Watchdog: fault detected, restarting...");
delay(500);
ESP.restart();
}
vTaskDelay(pdMS_TO_TICKS(WATCHDOG_PERIOD_MS));
}
}
// -----------------------------------------------------------------------------
// setup()
// -----------------------------------------------------------------------------
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
delay(100);
// Global (affects all endpoints)
// Zigbee.setManufacturer("Espressif");
// Zigbee.setModel("Node17_MultiSensor_v3");
// Radar UART
RadarSerial.begin(9600, SERIAL_8N1, RADAR_RX_PIN, RADAR_TX_PIN);
while (!radar.begin()) {
Serial.println("NO Radar device found!");
delay(1000);
}
Serial.println("C4001 connected!");
setPresenceMode();
Serial.println("Send 'P' for presence, 'S' for speed mode");
// Occupancy Zigbee endpoint
zbOccupancySensor.setManufacturerAndModel("Espressif", "Node17"); // Node17_c4001
// zbOccupancySensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
zbCarbonDioxideSensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
Zigbee.addEndpoint(&zbOccupancySensor);
// I2C init
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
i2cMutex = xSemaphoreCreateMutex();
// SCD41 init
scd41.begin(Wire, SCD41_I2C_ADDR_62);
scd41.stopPeriodicMeasurement();
Serial.println("SCD4x: Begin ok!");
scd41.startPeriodicMeasurement();
// SCD41 Zigbee endpoints
zbCarbonDioxideSensor.setManufacturerAndModel("Espressif", "Node17"); // Node17_scd41_co2
zbCarbonDioxideSensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbCarbonDioxideSensor.setMinMaxValue(0, 10000);
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
zbTempSensor_SCD4X.setManufacturerAndModel("Espressif", "Node17"); // Node17_scd41_th
zbTempSensor_SCD4X.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensor_SCD4X.setMinMaxValue(-20, 80);
zbTempSensor_SCD4X.setTolerance(1);
zbTempSensor_SCD4X.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensor_SCD4X);
// BME68x init
Serial.println("Connecting to BME680...");
bme.begin(BME68X_I2C_ADDR_HIGH, Wire);
bme.setTPH(BME68X_OS_8X, BME68X_OS_2X, BME68X_OS_8X);
bme.setHeaterProf(300, 100);
bme.setOpMode(BME68X_FORCED_MODE);
zbTempSensor_BME68X.setManufacturerAndModel("Espressif", "Node17"); // Node17_bme680_th
zbTempSensor_BME68X.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbTempSensor_BME68X.setMinMaxValue(-20, 80);
zbTempSensor_BME68X.setTolerance(1);
zbTempSensor_BME68X.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensor_BME68X);
zbAnalogDevice_BME68X.setManufacturerAndModel("Espressif", "Node17"); // Node17_bme680_r
zbAnalogDevice_BME68X.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbAnalogDevice_BME68X.addAnalogInput();
Zigbee.addEndpoint(&zbAnalogDevice_BME68X);
// T6713 config
configureT6713(true);
zbCarbonDioxideSensor_T6713.setManufacturerAndModel("Espressif", "Node17"); // Node17_t6713
zbCarbonDioxideSensor_T6713.setPowerSource(ZB_POWER_SOURCE_MAINS);
zbCarbonDioxideSensor_T6713.setMinMaxValue(0, 10000);
Zigbee.addEndpoint(&zbCarbonDioxideSensor_T6713);
// Start Zigbee as router
Serial.println("Starting Zigbee...");
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
bool ledState = false;
unsigned long startTime = millis();
const unsigned long timeoutMs = 60000;
while (!Zigbee.connected()) {
Serial.print(".");
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
delay(100);
if (millis() - startTime > timeoutMs) {
Serial.println("\nConnection timed out (60s). Restarting...");
ESP.restart();
}
}
digitalWrite(LED_PIN, LOW);
Serial.println("\nZigbee connected!");
Serial.println();
lastZigbeeOkMs = millis();
// Create sensor tasks
xTaskCreate(bme680_sensor_value_update, "bme680_sensor_value_update", 4096, NULL, 10, NULL);
xTaskCreate(scd4x_sensor_value_update, "scd4x_sensor_value_update", 4096, NULL, 10, NULL);
xTaskCreate(t6713_sensor_value_update, "t6713_sensor_value_update", 4096, NULL, 10, NULL);
xTaskCreate(pirTask, "PIR_Check", 4096, NULL, 1, NULL);
xTaskCreate(watchdogTask, "watchdog", 4096, NULL, 5, NULL);
// Reporting configuration
zbCarbonDioxideSensor.setReporting(30, 300, 10);
zbTempSensor_SCD4X.setReporting(60, 0, 0.5);
zbTempSensor_BME68X.setReporting(60, 0, 0.5);
zbAnalogDevice_BME68X.setAnalogInputReporting(60, 600, 10);
zbCarbonDioxideSensor_T6713.setReporting(30, 300, 10);
}
// -----------------------------------------------------------------------------
// loop()
// -----------------------------------------------------------------------------
void loop() {
// Optional: for manual mode switches
// handleSerialCommands();
// Test commands via Serial
// if (Serial.available()) {
// char cmd = Serial.read();
// switch(cmd) {
// case 'Z': // Force Zigbee failure
// Serial.println("SIM: Zigbee disconnect test");
// lastZigbeeOkMs = 0; // Watchdog sees Zigbee down
// break;
// case 'E': // Force "stuck sensor" detection
// Serial.println("SIM: Stuck sensor test");
// // Set ALL change timestamps to ancient past → triggers stuck detection
// lastCo2ScdChangeMs = lastTempScdChangeMs = lastHumScdChangeMs = 0;
// lastCo2T6713ChangeMs = lastTempBmeChangeMs = lastHumBmeChangeMs = 0;
// lastGasResChangeMs = 0;
// break;
// case 'S': // Force SINGLE sensor restart (per-task check)
// Serial.println("SIM: SCD41 error threshold → restart");
// scd41ErrorCount = maxI2cErrors; // Task will check & restart itself
// break;
// case 'R': // Manual restart
// Serial.println("SIM: Manual restart");
// ESP.restart();
// break;
// }
// }
// Factory reset logic on long button press
if (digitalRead(button) == LOW) { // push button pressed
delay(100); // debounce
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
// Zigbee.factoryReset() usually does not return
ESP.restart();
}
}
}
// Keep loop light; everything else runs in tasks
vTaskDelay(pdMS_TO_TICKS(50));
}

View File

@ -0,0 +1,786 @@
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#define LED_PIN 15
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 4;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#include "DFRobot_C4001.h"
HardwareSerial RadarSerial(1);
DFRobot_C4001_UART radar(&RadarSerial, 9600, /*rx*/17, /*tx*/16); // ESP32-C6 pins
eMode_t currentMode = eExitMode; // 0=presence, 1=speed
unsigned long lastSpeedReport = 0;
bool lastOccupancy = false;
unsigned long occupancyStartTime = 0;
const unsigned long OCCUPANCY_TIMEOUT = 4; // 4 -> 2 seconds - adjust as needed
const unsigned long TRIGGER_DELAY = 10; // 10 -> 100 ms
// Define your custom SCL/SDA pins
#define I2C_SCL_PIN 20 // Change to your SCL pin
#define I2C_SDA_PIN 6 // Change to your SDA pin
// Create custom I2C instance
// TwoWire sharedI2C = TwoWire(0); // I2C0 (or TwoWire(1) for I2C1)
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 11
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
// #include <DFRobot_SCD4X.h>
// // DFRobot_SCD4X SCD4X(&Wire, /*i2cAddr = */SCD4X_I2C_ADDR);
// // SCD41 with custom I2C
// DFRobot_SCD4X SCD4X(&sharedI2C, SCD4X_I2C_ADDR);
#include <Arduino.h>
#include <SensirionI2cScd4x.h>
#include <Wire.h>
SensirionI2cScd4x scd41;
#define TEMP_SENSOR_ENDPOINT_NUMBER 12
ZigbeeTempSensor zbTempSensor_SCD4X = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// --- BME680 Settings ---
#include <bme68xLibrary.h>
Bme68x bme;
// TwoWire BME68X_I2C = TwoWire(0); // I2C0 (or TwoWire(1) for I2C1)
// #include <DFRobot_BME680.h> // BME680 library
// DFRobot_BME680_I2C bme680(0x76); // 0x76
// #include <Adafruit_Sensor.h>
// #include "Adafruit_BME680.h"
// Adafruit_BME680 bme680;
unsigned long bme_report_time = millis();
#define TEMP_SENSOR_ENDPOINT_NUMBER 13
ZigbeeTempSensor zbTempSensor_BME68X = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
#define ANALOG_DEVICE_ENDPOINT_NUMBER 14
ZigbeeAnalog zbAnalogDevice_BME68X = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
// --- T6713 Settings ---
#define T6713_ADDR 0x15
// T6713 Register Addresses (Modbus)
#define T6713_REG_ABC_LOGIC 0x03EE
#define T6713_REG_STATUS 0x138A
unsigned long t6713_report_time = millis();
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER_T6713 15
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor_T6713 = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER_T6713);
void setPresenceMode() {
radar.setSensorMode(eExitMode);
currentMode = eExitMode;
Serial.println("Switched to PRESENCE mode");
// Presence mode settings from sample
// radar.setDetectionRange(30, 1000, 1000); // min,max,trig (cm)
// radar.setTrigSensitivity(1);
// radar.setKeepSensitivity(2);
// radar.setDelay(100, 4); // trig,keep
sSensorStatus_t data;
data = radar.getStatus();
// 0 stop 1 start
Serial.print("work status = ");
Serial.println(data.workStatus);
// 0 is exist 1 speed
Serial.print("work mode = ");
Serial.println(data.workMode);
// 0 no init 1 init success
Serial.print("init status = ");
Serial.println(data.initStatus);
Serial.println();
/*
* min Detection range Minimum distance, unit cm, range 0.3~25m (30~2500), not exceeding max, otherwise the function is abnormal.
* max Detection range Maximum distance, unit cm, range 2.4~25m (240~2500)
* trig Detection range Maximum distance, unit cm, default trig = max
*/
if(radar.setDetectionRange(/*min*/30, /*max*/500, /*trig*/500)){
Serial.println("set detection range successfully!");
}
// set trigger sensitivity 0 - 9
if(radar.setTrigSensitivity(8)){
Serial.println("set trig sensitivity successfully!");
}
// set keep sensitivity 0 - 9
if(radar.setKeepSensitivity(8)){
Serial.println("set keep sensitivity successfully!");
}
/*
* trig Trigger delay, unit 0.01s, range 0~2s (0~200)
* keep Maintain the detection timeout, unit 0.5s, range 2~1500 seconds (4~3000)
*/
if(radar.setDelay(/*trig*/TRIGGER_DELAY, /*keep*/OCCUPANCY_TIMEOUT)){
Serial.println("set delay successfully!");
}
// get confige params
Serial.print("trig sensitivity = ");
Serial.println(radar.getTrigSensitivity());
Serial.print("keep sensitivity = ");
Serial.println(radar.getKeepSensitivity());
Serial.print("min range = ");
Serial.println(radar.getMinRange());
Serial.print("max range = ");
Serial.println(radar.getMaxRange());
Serial.print("trig range = ");
Serial.println(radar.getTrigRange());
Serial.print("keep time = ");
Serial.println(radar.getKeepTimerout());
Serial.print("trig delay = ");
Serial.println(radar.getTrigDelay());
}
void setSpeedMode() {
radar.setSensorMode(eSpeedMode);
currentMode = eSpeedMode;
Serial.println("Switched to SPEED mode");
// Speed mode settings from sample
// radar.setDetectThres(11, 1200, 10); // min,max,thres (cm)
// radar.setFrettingDetection(eON);
sSensorStatus_t data;
data = radar.getStatus();
// 0 stop 1 start
Serial.print("work status = ");
Serial.println(data.workStatus);
// 0 is exist 1 speed
Serial.print("work mode = ");
Serial.println(data.workMode);
// 0 no init 1 init success
Serial.print("init status = ");
Serial.println(data.initStatus);
Serial.println();
/*
* min Detection range Minimum distance, unit cm, range 0.3~20m (30~2500), not exceeding max, otherwise the function is abnormal.
* max Detection range Maximum distance, unit cm, range 2.4~20m (240~2500)
* thres Target detection threshold, dimensionless unit 0.1, range 0~6553.5 (0~65535)
*/
if (radar.setDetectThres(/*min*/ 11, /*max*/ 1200, /*thres*/ 10)) {
Serial.println("set detect threshold successfully");
}
// set Fretting Detection
radar.setFrettingDetection(eON);
// get confige params
Serial.print("min range = ");
Serial.println(radar.getTMinRange());
Serial.print("max range = ");
Serial.println(radar.getTMaxRange());
Serial.print("threshold range = ");
Serial.println(radar.getThresRange());
Serial.print("fretting detection = ");
Serial.println(radar.getFrettingDetection());
}
void handleSerialCommands() {
while (Serial.available()) {
char cmd = Serial.read();
if (cmd == 'P' || cmd == 'p') {
setPresenceMode();
} else if (cmd == 'S' || cmd == 's') {
setSpeedMode();
}
}
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
bool ledState = false;
digitalWrite(LED_PIN, HIGH);
// while (!Serial);
// unsigned long start = millis();
// while (!Serial && millis() - start < 3000);
delay(100);
RadarSerial.begin(9600, SERIAL_8N1, 17, 16); // C4001 UART (RX=16, TX=17)
while (!radar.begin()) {
Serial.println("NO Device found!");
delay(1000);
}
Serial.println("C4001 connected!");
// Start in presence mode
setPresenceMode();
Serial.println("Send 'P' for presence, 'S' for speed mode");
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node17");
zbOccupancySensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
// Initialize custom I2C with YOUR pins
// SCD41_I2C.begin(SCD41_SDA_PIN, SCD41_SCL_PIN, 100000); // 100kHz standard
// Initialize SHARED I2C bus ONCE
// sharedI2C.begin(I2C_SDA_PIN, I2C_SCL_PIN, 100000);
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
scd41.begin(Wire, SCD41_I2C_ADDR_62);
// while( !SCD4X.begin() ){
// Serial.println("Communication with device failed, please check connection");
// delay(3000);
// }
scd41.stopPeriodicMeasurement();
Serial.println("SCD4x: Begin ok!");
scd41.startPeriodicMeasurement();
// SCD4X.setTempComp(4.0);
// float temp = 0;
// temp = SCD4X.getTempComp();
// Serial.print("The current temperature compensation value : ");
// Serial.print(temp);
// Serial.println(" C");
// SCD4X.setSensorAltitude(540);
// uint16_t altitude = 0;
// altitude = SCD4X.getSensorAltitude();
// Serial.print("Set the current environment altitude : ");
// Serial.print(altitude);
// Serial.println(" m");
// SCD4X.enablePeriodMeasure(SCD4X_START_PERIODIC_MEASURE);
zbCarbonDioxideSensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node17");
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 1500);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor_SCD4X.setMinMaxValue(10, 50);
// Optional: Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor_SCD4X.setTolerance(1);
// Optional: Time cluster configuration (default params, as this device will revieve time from coordinator)
// zbTempSensor.addTimeCluster();
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor_SCD4X.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor_SCD4X);
Serial.println("Connecting to BME680...");
// // Init BME680
// if (bme680.begin(sharedI2C) != 0) {
// Serial.println("BME680 init failed!");
// } else {
// Serial.println("BME680 ready!");
// }
// Wire.begin(SCD41_SDA_PIN, SCD41_SCL_PIN);
// Wire.setClock(100000); // Set I2C clock speed to 100kHz
// // Initialize the BME680
// bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// bme.begin(BME68X_I2C_ADDR_HIGH, &BME68X_I2C); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// bme680.begin()
// // Set up the BME680
// // bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// // bme.setOversampling(T2, OS_2X);
// // bme.setOversampling(T1, OS_16X);
// // bme.setOversampling(H1, OS_2X);
// // bme.setFilter(FILTER_SIZE_3);
// // bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
// if(bme.checkStatus())
// {
// if (bme.checkStatus() == BME68X_ERROR)
// {
// Serial.println("Sensor error:" + bme.statusString());
// return;
// }
// else if (bme.checkStatus() == BME68X_WARNING)
// {
// Serial.println("Sensor Warning:" + bme.statusString());
// }
// }
// /* Set the default configuration for temperature, pressure and humidity */
// bme.setTPH();
// /* Set the heater configuration to 300 deg C for 100ms for Forced mode */
// bme.setHeaterProf(300, 100);
// bme68xData data;
// bme.setOpMode(BME68X_FORCED_MODE);
// Note the syntax: .begin(Address, TwoWirePointer)
// if (!bme680.begin(0x77, &sharedI2C)) {
// Serial.println("BME680 not found at 0x77, trying 0x76...");
// if (!bme680.begin(0x76, &sharedI2C)) {
// Serial.println("BME680 Init Failed! Check wiring.");
// while (1);
// }
// }
// Serial.println("BME680 initialized via TwoWire.");
// // BME680 Settings
// bme680.setTemperatureOversampling(BME680_OS_8X);
// bme680.setHumidityOversampling(BME680_OS_2X);
// bme680.setPressureOversampling(BME680_OS_4X);
// bme680.setIIRFilterSize(BME680_FILTER_SIZE_3);
// bme680.setGasHeater(320, 150);
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
/* Set the default configuration for temperature, pressure and humidity */
// setTPH(uint8_t os_temp, uint8_t os_pres, uint8_t os_hum); BME68X_OS_NONE, BME68X_OS_1X, BME68X_OS_2X, BME68X_OS_4X, BME68X_OS_8X, BME68X_OS_16X
bme.setTPH(BME68X_OS_8X, BME68X_OS_2X, BME68X_OS_8X);
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
zbTempSensor_BME68X.setMinMaxValue(-20, 80);
zbTempSensor_BME68X.setTolerance(1);
zbTempSensor_BME68X.addHumiditySensor(0, 100, 1);
Zigbee.addEndpoint(&zbTempSensor_BME68X);
// Set analog resolution to 10 bits
// analogReadResolution(10);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice_BME68X.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice_BME68X);
// TRUE = Normal Home/Office use
// FALSE = Greenhouse / Constantly occupied
configureT6713(true);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor_T6713.setMinMaxValue(0, 1500);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor_T6713);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
// Increase keep-alive to 5 seconds (default is often 3000ms)
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 5000;
// zigbeeConfig.nwk_cfg.zed_cfg.ed_timeout = ESP_ZB_ED_AGING_TIMEOUT_256MIN; // 4 hours; How long the parent remembers this device.
// 1. Our custom config (zigbeeConfig)
// 2. false = Do NOT erase NVS (keep network credentials)
// 3. true = Start automatically (optional, defaults to true)
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
// while (!Zigbee.connected()) {
// Serial.print(".");
// delay(100);
// }
// Record the start time
unsigned long startTime = millis();
const unsigned long timeoutMs = 60000; // 120 seconds
while (!Zigbee.connected()) {
Serial.print(".");
ledState = !ledState;
digitalWrite(LED_PIN, ledState);
delay(100);
// Check if 120 seconds have passed
if (millis() - startTime > timeoutMs) {
Serial.println("\nConnection timed out (120s). Restarting...");
ESP.restart();
}
}
digitalWrite(LED_PIN, LOW);
Serial.println("\nZigbee connected!");
Serial.println();
// Set reporting interval for carbon dioxide measurement to be done every 30 seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (carbon dioxide change in ppm)
// if min = 1 and max = 0, reporting is sent only when carbon dioxide changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or when carbon dioxide changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of delta change
zbCarbonDioxideSensor.setReporting(0, 30, 0);
zbTempSensor_SCD4X.setReporting(1, 0, 1);
zbTempSensor_BME68X.setReporting(1, 0, 1);
// zbAnalogDevice.setAnalogInputReporting(0, 30, 10); // report every 30 seconds if value changes by 10
}
void loop() {
// handleSerialCommands();
// // Remove if (radar.available()) - just read directly
// sSensorStatus_t status = radar.getStatus(); // Always safe to call
// Serial.print("Status: work=");
// Serial.print(status.workStatus);
// Serial.print(", mode=");
// Serial.print(status.workMode);
// Serial.print(", init=");
// Serial.println(status.initStatus);
if (currentMode == eExitMode) {
// PRESENCE MODE
// static bool occupancy = false;
// bool occupancyNow = radar.motionDetection();
// Serial.println(occupancyNow);
// // if (radar.motionDetection() == 1 && !occupancy) {
// if (occupancyNow != lastOccupancy) {
// Serial.println("MOTION DETECTED");
// zbOccupancySensor.setOccupancy(occupancyNow);
// zbOccupancySensor.report();
// // occupancy = true;
// lastOccupancy = occupancyNow;
// // } else if (radar.motionDetection() == 0 && occupancy) {
// // } else if (radar.motionDetection() == 0) {
// // Serial.println("MOTION ENDED");
// // zbOccupancySensor.setOccupancy(false);
// // zbOccupancySensor.report();
// // occupancy = false;
// }
// PRESENCE MODE - proper state machine
bool motionNow = radar.motionDetection();
// Serial.println(motionNow);
// Serial.println(lastOccupancy);
// Serial.println(millis() - occupancyStartTime);
if (motionNow && !lastOccupancy) {
// Motion STARTED → Set occupancy ON
Serial.println("MOTION DETECTED → OCCUPANCY ON");
lastOccupancy = true;
occupancyStartTime = millis();
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
} else if (motionNow && lastOccupancy) {
// Motion ENDED → Start timeout timer
// Serial.println("Motion still present → timeout reset");
occupancyStartTime = millis();
} else if (!motionNow && lastOccupancy && (millis() - occupancyStartTime > OCCUPANCY_TIMEOUT*1000/2)) {
// TIMEOUT → Clear occupancy
Serial.println("OCCUPANCY TIMEOUT → OFF");
lastOccupancy = false;
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
} else if (!motionNow && lastOccupancy) {
// Motion ENDED → Start timeout timer
// Serial.println("Motion ended → timeout started");
// occupancyStartTime = millis();
}
} else if (currentMode == eSpeedMode) {
// SPEED MODE
Serial.print("Targets: ");
Serial.print(radar.getTargetNumber());
Serial.print(" | Speed: ");
Serial.print(radar.getTargetSpeed(), 2);
Serial.print(" m/s | Range: ");
Serial.print(radar.getTargetRange(), 2);
Serial.print(" m | Energy: ");
Serial.println(radar.getTargetEnergy());
}
// --- Read SCD41 ---
uint16_t co2_scd = 0;
float temperature_scd = 0.0f;
float humidity_scd = 0.0f;
bool scdReady = false;
uint16_t error = scd41.readMeasurement(co2_scd, temperature_scd, humidity_scd);
if (error == 0 && co2_scd != 0) {
scdReady = true;
}
if (scdReady) {
Serial.print("[SCD41] CO2: "); Serial.print(co2_scd); Serial.print(" ppm");
Serial.print(" | Temp: "); Serial.print(temperature_scd); Serial.print(" C");
Serial.print(" | Hum: "); Serial.print(humidity_scd); Serial.println(" %");
zbCarbonDioxideSensor.setCarbonDioxide(co2_scd);
zbCarbonDioxideSensor.report();
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor_SCD4X.setTemperature(temperature_scd);
zbTempSensor_SCD4X.setHumidity(humidity_scd);
// Report temperature and humidity values
zbTempSensor_SCD4X.report(); // reports temperature and humidity values (if humidity sensor is not added, only temperature is reported)
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Serial.print("SCD41 reported.");
}
// else {
// Serial.println("[SCD41] Waiting for measurement...");
// }
// if(SCD4X.getDataReadyStatus()) {
// DFRobot_SCD4X::sSensorMeasurement_t data;
// SCD4X.readMeasurement(&data);
// Serial.print("Carbon dioxide concentration : ");
// Serial.print(data.CO2ppm);
// Serial.println(" ppm");
// Serial.print("Environment temperature : ");
// Serial.print(data.temp);
// Serial.println(" C");
// Serial.print("Relative humidity : ");
// Serial.print(data.humidity);
// Serial.println(" RH");
// Serial.println();
// zbCarbonDioxideSensor.setCarbonDioxide(data.CO2ppm);
// zbCarbonDioxideSensor.report();
// // Update temperature and humidity values in Temperature sensor EP
// zbTempSensor_SCD4X.setTemperature(data.temp);
// zbTempSensor_SCD4X.setHumidity(data.humidity);
// // Report temperature and humidity values
// zbTempSensor_SCD4X.report(); // reports temperature and humidity values (if humidity sensor is not added, only temperature is reported)
// // Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Serial.print("SCD41 reported.");
// }
// Serial.println("---------------------------");
float bme_temperature(NAN), bme_humidity(NAN), bme_pressure(NAN), bme_gasResistance(NAN);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
// delay(bme.getMeasDur(BME68X_FORCED_MODE) / 1000 + 50);
if (bme.fetchData() && (millis() - bme_report_time > 5000)) {
bme.getData(data);
bme_temperature = data.temperature; // Temperature in °C
bme_humidity = data.humidity; // Humidity in %
bme_pressure = data.pressure; // Pressure in hPa
bme_gasResistance = data.gas_resistance; // Gas resistance in ohms
// errorCount = 0; // Reset error count on successful read
Serial.printf("[BME680] temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", bme_temperature, bme_humidity, bme_gasResistance);
zbTempSensor_BME68X.setTemperature(bme_temperature);
zbTempSensor_BME68X.setHumidity(bme_humidity);
// Report temperature and humidity values
zbTempSensor_BME68X.report(); // reports temperature and humidity values (if humidity sensor is not added, only temperature is reported)
zbAnalogDevice_BME68X.setAnalogInput(bme_gasResistance);
zbAnalogDevice_BME68X.reportAnalogInput();
digitalWrite(LED_PIN, HIGH);
delay(50);
digitalWrite(LED_PIN, LOW);
// Serial.print("BME68X reported.");
bme_report_time = millis();
}
// else {
// Serial.println("BME680 -> Waiting...");
// }
// --- Read T6713 (CO2) ---
int co2_t6713 = readT6713();
if ((co2_t6713 >= 0) && (millis() - t6713_report_time > 5000)) {
Serial.print("T6713 | CO2: "); Serial.print(co2_t6713); Serial.println(" ppm");
zbCarbonDioxideSensor_T6713.setCarbonDioxide(co2_t6713);
zbCarbonDioxideSensor_T6713.report();
t6713_report_time = millis();
}
// else {
// Serial.println("T6713 | Read Error");
// }
delay(100);
}
// Helper function for T6713
int readT6713() {
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04); // Function: Read Input Registers
Wire.write(0x13); // Address High
Wire.write(0x8B); // Address Low
Wire.write(0x00);
Wire.write(0x01); // Length (1 register)
if (Wire.endTransmission() != 0) return -1;
delay(10); // Processing delay
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() == 4) {
Wire.read(); Wire.read(); // Skip Func & Len
byte high = Wire.read();
byte low = Wire.read();
return (high << 8) | low;
}
return -1;
}
void configureT6713(bool enableABC) {
// 1. Wait for Sensor Warm-up (at least 500ms after power on, datasheet says much longer for accuracy)
delay(1000);
// 2. Check Status Register (Optional but good practice)
// We read register 0x138A (5002) to check for errors/warmup
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x04);
Wire.write(0x13); Wire.write(0x8A); // Address 0x138A
Wire.write(0x00); Wire.write(0x01);
Wire.endTransmission();
delay(10);
Wire.requestFrom(T6713_ADDR, 4);
if (Wire.available() == 4) {
Wire.read(); Wire.read(); // Func, Len
uint16_t status = (Wire.read() << 8) | Wire.read();
// Bit 3 is Warm-up mode. If 1, sensor is still warming up.
if (status & 0x08) {
Serial.println("T6713: Warming up...");
}
}
// 3. Configure ABC Logic (Automatic Background Calibration)
// Write to Register 0x03EE (1006). Value: 0xFF00 = Enable, 0x0000 = Disable
uint16_t abcValue = enableABC ? 0xFF00 : 0x0000;
Wire.beginTransmission(T6713_ADDR);
Wire.write(0x05); // Function: Write Single Coil
Wire.write(highByte(T6713_REG_ABC_LOGIC));
Wire.write(lowByte(T6713_REG_ABC_LOGIC));
Wire.write(highByte(abcValue));
Wire.write(lowByte(abcValue));
byte error = Wire.endTransmission();
if (error == 0) {
Serial.print("T6713: ABC Logic set to ");
Serial.println(enableABC ? "ON" : "OFF");
} else {
Serial.println("T6713: Failed to set ABC Logic");
}
// 4. Wait for the write to "stick"
delay(100);
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,363 @@
// https://tutoduino.fr/en/tutorials/esp32c6-zigbee/
// original: https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
// Board: esp32 -> ESP32C6 Dev Module
// Core Debug Level: Verbose
// Erase All Flash Before Sketch Upload: Enabled
// Partition scheme: Zigbee 4MB with spiffs
// Zigbee Mode: Zigbee ED
//
// 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.
/**
* @brief Zigbee temperature and humidity sensor Zigbee Sleepy End Device.
*
* https://tutoduino.fr/tutoriels/esp32c6-zigbee/
* This code is based on example "Zigbee temperature and humidity sensor Sleepy device" created by Jan Procházka
* https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
// Uncomment the following line to display debug traces in serial monitor of Arduino IDE
#define DEBUG_TRACE
#include "Zigbee.h"
//#include <BME280I2C.h>
//#include <Wire.h>
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 7
#define SDA_PIN 6
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Sleep for 10 minutes */
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 11
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
// #define MAGNETIC_SENSOR_PIN 12
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// bool contact_open = false;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.0;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float val;
val = (x - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(A0); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
// Get data from BME280 sensor and go to deep sleep mode
void meausureAndSleep() {
// Measure temperature sensor value
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
float vBat;
// BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
// BME280::PresUnit presUnit(BME280::PresUnit_hPa);
// Read temperature and humidity on BME280 sensor
// sensor.read(pressure, temperature, humidity, tempUnit, presUnit);
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
// Set the desired settings for the BME680
//bme.amb_temp = 25; // Set ambient temperature for compensation
//bme.meas_status = 0; // Clear previous measurement status
// Perform a measurement
//rslt = bme68x_get_sensor_data(BME68X_ALL, &data, &bme);
//if (rslt == BME68X_OK) {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
//zbTempSensor.setGasResistance(gasResistance); // Assuming you have a method for gas resistance
// Measure battery voltage
vBat = getVbatt();
percentage = mapFloat(vBat, minVoltage, maxVoltage);
#ifdef DEBUG_TRACE
Serial.printf("Battery: %.2fV (%d%%)\n", vBat, percentage);
#endif
// Update battery percentage
zbTempSensor.setBatteryPercentage(percentage);
//zbTempSensor.setBatteryVoltage(vBat * 10); // voltage in 100mV
// Magnetic contact sensor
// static bool contact = false;
// if (digitalRead(MAGNETIC_SENSOR_PIN) == HIGH && !contact) {
// // Update contact sensor value
// zbContactSwitch.setOpen();
// contact = true;
// Serial.printf("contact = true");
// } else if (digitalRead(MAGNETIC_SENSOR_PIN) == LOW && contact) {
// zbContactSwitch.setClosed();
// contact = false;
// Serial.printf("contact = false");
// }
// Report values
zbTempSensor.report();
zbTempSensor.reportBatteryPercentage();
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// Turn on the builtin LED for a very short time
flashLED();
// Add small delay to allow the data to be sent before going to sleep
delay(500);
// Put device to deep sleep
#ifdef DEBUG_TRACE
Serial.println("Going to sleep now");
#endif
// esp_deep_sleep_start();
delay(5000);
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100);
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Configure use of external antenna
// pinMode(WIFI_ENABLE, OUTPUT); // pinMode(3, OUTPUT);
// digitalWrite(WIFI_ENABLE, LOW); // digitalWrite(3, LOW); // Activate RF switch control
// delay(100);
// pinMode(WIFI_ANT_CONFIG, OUTPUT); // pinMode(14, OUTPUT);
// digitalWrite(WIFI_ANT_CONFIG, HIGH); // digitalWrite(14, HIGH); // Use external antenna
// Configure builtin LED and turn it OFF (HIGH)
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
/*
// Init BME280 sensor
Wire.begin();
while (!sensor.begin()) {
#ifdef DEBUG_TRACE
Serial.println("Could not find BME280 sensor!");
#endif
delay(1000);
}
*/
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Configure A0 as ADC input for reading battery voltage
pinMode(A0, INPUT);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor_Node18");
//zbCarbonDioxideSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor");
// Set minimum and maximum temperature measurement value
zbTempSensor.setMinMaxValue(-20, 80);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
//zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// pinMode(MAGNETIC_SENSOR_PIN, INPUT_PULLUP); // Assuming switch pulls LOW when closed
// Zigbee.addEndpoint(&zbContactSwitch);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
// If Zigbee does not start with 30s default timeout (ZB_BEGIN_TIMEOUT_DEFAULT) then restart
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
//zbCarbonDioxideSensor.setReporting(0, 30, 0);
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
// Call the function to measure temperature and put the device to deep sleep
// meausureAndSleep();
}
void loop() {
// No actions are performed in the loop (the ESP32C6 enters the setup function when it exits deep sleep).
meausureAndSleep();
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,346 @@
// ESP32C6 Dev Module
// Partition Scheme: ZIgbee ZCZR 4MB with spiffs
// Zigbee Mode: Zigbee ZRZC (coordinator,router)
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// #ifndef ZIGBEE_MODE_ED
// #error "Zigbee end device mode is not selected in Tools->Zigbee mode"
// #endif
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
// #include <HardwareSerial.h>
// #define SENSOR_RX 17 // ESP32 RX connected to sensor TX
// #define SENSOR_TX 16 // ESP32 TX connected to sensor RX
// HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
// #define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
// uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
// ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 7
#define SDA_PIN 6
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
ZigbeeTempSensor zbTempSensor_BME68X = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
// #define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
// ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
#define ANALOG_DEVICE_ENDPOINT_NUMBER 11
ZigbeeAnalog zbAnalogDevice_BME68X = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
/************************ Temp sensor *****************************/
static void bme680_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor_BME68X.setTemperature(temperature);
zbTempSensor_BME68X.setHumidity(humidity);
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
zbAnalogDevice_BME68X.setAnalogInput(round(gasResistance));
// zbAnalogDevice_BME68X.reportAnalogInput();
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("BME680: Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
delay(10000);
}
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Optional: configure analog input
analogSetAttenuation(ADC_11db); // set analog to digital converter (ADC) attenuation to 11 dB (up to ~3.3V input)
analogReadResolution(12); // set analog read resolution to 12 bits (value range from 0 to 4095), 12 is default
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Optional: set Zigbee device name and model
zbTempSensor_BME68X.setManufacturerAndModel("Espressif", "Node18_bme680_router");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbTempSensor_BME68X.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor_BME68X.setMinMaxValue(0, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor_BME68X.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor_BME68X.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor_BME68X);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice_BME68X.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice_BME68X);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
// if (!Zigbee.begin(&zigbeeConfig, false)) {
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// Start Temperature sensor reading task
xTaskCreate(bme680_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor_BME68X.setReporting(0, 15, 1);
// zbTempSensor2.setReporting(0, 15, 0);
// zbCarbonDioxideSensor.setReporting(0, 30, 0);
zbAnalogDevice_BME68X.setAnalogInputReporting(0, 15, 10); // report every 30 seconds if value changes by 10
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// force report of illuminance when button is pressed
// zbIlluminanceSensor.report();
zbTempSensor_BME68X.report();
// zbCarbonDioxideSensor.report();
zbAnalogDevice_BME68X.reportAnalogInput();
// zbEnvSensor_sgp40_sht40.report();
}
delay(100);
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,364 @@
// https://tutoduino.fr/en/tutorials/esp32c6-zigbee/
// original: https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
// Board: esp32 -> EDFRobot FireBeetle 2 ESP32-C6
// Core Debug Level: Verbose
// Erase All Flash Before Sketch Upload: Enabled
// Partition scheme: Zigbee 4MB with spiffs
// Zigbee Mode: Zigbee ED
//
// 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.
/**
* @brief Zigbee temperature and humidity sensor Zigbee Sleepy End Device.
*
* https://tutoduino.fr/tutoriels/esp32c6-zigbee/
* This code is based on example "Zigbee temperature and humidity sensor Sleepy device" created by Jan Procházka
* https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
// Uncomment the following line to display debug traces in serial monitor of Arduino IDE
#define DEBUG_TRACE
#include "Zigbee.h"
//#include <BME280I2C.h>
//#include <Wire.h>
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 8
#define SDA_PIN 14
#define BAT_ADC_PIN 0 // ADC input pin
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 300 /* Sleep for 10 minutes */
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 11
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
// #define MAGNETIC_SENSOR_PIN 12
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// bool contact_open = false;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.0;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float val;
val = (x - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(BAT_ADC_PIN); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
// Get data from BME280 sensor and go to deep sleep mode
void meausureAndSleep() {
// Measure temperature sensor value
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
float vBat;
// BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
// BME280::PresUnit presUnit(BME280::PresUnit_hPa);
// Read temperature and humidity on BME280 sensor
// sensor.read(pressure, temperature, humidity, tempUnit, presUnit);
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
// Set the desired settings for the BME680
//bme.amb_temp = 25; // Set ambient temperature for compensation
//bme.meas_status = 0; // Clear previous measurement status
// Perform a measurement
//rslt = bme68x_get_sensor_data(BME68X_ALL, &data, &bme);
//if (rslt == BME68X_OK) {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
//zbTempSensor.setGasResistance(gasResistance); // Assuming you have a method for gas resistance
// Measure battery voltage
vBat = getVbatt();
percentage = mapFloat(vBat, minVoltage, maxVoltage);
#ifdef DEBUG_TRACE
Serial.printf("Battery: %.2fV (%d%%)\n", vBat, percentage);
#endif
// Update battery percentage
zbTempSensor.setBatteryPercentage(percentage);
//zbTempSensor.setBatteryVoltage(vBat * 10); // voltage in 100mV
// Magnetic contact sensor
// static bool contact = false;
// if (digitalRead(MAGNETIC_SENSOR_PIN) == HIGH && !contact) {
// // Update contact sensor value
// zbContactSwitch.setOpen();
// contact = true;
// Serial.printf("contact = true");
// } else if (digitalRead(MAGNETIC_SENSOR_PIN) == LOW && contact) {
// zbContactSwitch.setClosed();
// contact = false;
// Serial.printf("contact = false");
// }
// Report values
zbTempSensor.report();
zbTempSensor.reportBatteryPercentage();
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// Turn on the builtin LED for a very short time
flashLED();
// Add small delay to allow the data to be sent before going to sleep
delay(500);
// Put device to deep sleep
#ifdef DEBUG_TRACE
Serial.println("Going to sleep now");
#endif
esp_deep_sleep_start();
// delay(5000);
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100);
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Configure use of external antenna
// pinMode(WIFI_ENABLE, OUTPUT); // pinMode(3, OUTPUT);
// digitalWrite(WIFI_ENABLE, LOW); // digitalWrite(3, LOW); // Activate RF switch control
// delay(100);
// pinMode(WIFI_ANT_CONFIG, OUTPUT); // pinMode(14, OUTPUT);
// digitalWrite(WIFI_ANT_CONFIG, HIGH); // digitalWrite(14, HIGH); // Use external antenna
// Configure builtin LED and turn it OFF (HIGH)
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
/*
// Init BME280 sensor
Wire.begin();
while (!sensor.begin()) {
#ifdef DEBUG_TRACE
Serial.println("Could not find BME280 sensor!");
#endif
delay(1000);
}
*/
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Configure A0 as ADC input for reading battery voltage
pinMode(BAT_ADC_PIN, INPUT);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor_Node19");
//zbCarbonDioxideSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor");
// Set minimum and maximum temperature measurement value
zbTempSensor.setMinMaxValue(-20, 80);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
//zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// pinMode(MAGNETIC_SENSOR_PIN, INPUT_PULLUP); // Assuming switch pulls LOW when closed
// Zigbee.addEndpoint(&zbContactSwitch);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
// If Zigbee does not start with 30s default timeout (ZB_BEGIN_TIMEOUT_DEFAULT) then restart
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
//zbCarbonDioxideSensor.setReporting(0, 30, 0);
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
// Call the function to measure temperature and put the device to deep sleep
meausureAndSleep();
}
void loop() {
// No actions are performed in the loop (the ESP32C6 enters the setup function when it exits deep sleep).
// meausureAndSleep();
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,440 @@
// https://tutoduino.fr/en/tutorials/esp32c6-zigbee/
// original: https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
// Board: esp32 -> EDFRobot FireBeetle 2 ESP32-C6
// Core Debug Level: Verbose
// Erase All Flash Before Sketch Upload: Enabled
// Partition scheme: Zigbee 4MB with spiffs
// Zigbee Mode: Zigbee ED
//
// 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.
/**
* @brief Zigbee temperature and humidity sensor Zigbee Sleepy End Device.
*
* https://tutoduino.fr/tutoriels/esp32c6-zigbee/
* This code is based on example "Zigbee temperature and humidity sensor Sleepy device" created by Jan Procházka
* https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
// Uncomment the following line to display debug traces in serial monitor of Arduino IDE
#define DEBUG_TRACE
#include "Zigbee.h"
//#include <BME280I2C.h>
//#include <Wire.h>
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 8
#define SDA_PIN 14
#define BAT_ADC_PIN 0 // ADC input pin
#define POWER_PIN 21 // GPIO pin to power the sensor
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 300 /* Sleep for 10 minutes */
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 11
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
// /* Zigbee contact sensor configuration */
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// uint8_t float_switch_pin = 3;
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// #define MAGNETIC_SENSOR_PIN 12
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// bool contact_open = false;
/* Zigbee analog device configuration */
#define ANALOG_DEVICE_ENDPOINT_NUMBER 13
uint8_t analogPin = 3;
ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.0;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float val;
val = (x - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(BAT_ADC_PIN); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
// Get data from BME280 sensor and go to deep sleep mode
void meausureAndSleep() {
// Measure temperature sensor value
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
float vBat;
// BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
// BME280::PresUnit presUnit(BME280::PresUnit_hPa);
// Read temperature and humidity on BME280 sensor
// sensor.read(pressure, temperature, humidity, tempUnit, presUnit);
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
// Set the desired settings for the BME680
//bme.amb_temp = 25; // Set ambient temperature for compensation
//bme.meas_status = 0; // Clear previous measurement status
// Perform a measurement
//rslt = bme68x_get_sensor_data(BME68X_ALL, &data, &bme);
//if (rslt == BME68X_OK) {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// // Checking pin for contact change
// static bool contact = false;
// if (digitalRead(float_switch_pin) == HIGH && !contact) {
// // Update contact sensor value
// // Serial.println("Float sensor: true");
// zbContactSwitch.setOpen();
// contact = true;
// } else if (digitalRead(float_switch_pin) == LOW && contact) {
// // Serial.println("Float sensor: true");
// zbContactSwitch.setClosed();
// contact = false;
// }
// #ifdef DEBUG_TRACE
// Serial.println(contact ? "Float sensor: true" : "Float sensor: false");
// #endif
// Read ADC value and update the analog value every 2s
float analog = (float)analogRead(analogPin);
Serial.printf("Updating analog input to %.1f\r\n", analog);
zbAnalogDevice.setAnalogInput(analog);
// Analog input supports reporting
zbAnalogDevice.reportAnalogInput();
delay(100);
digitalWrite(POWER_PIN, LOW); // Power off sensor
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
//zbTempSensor.setGasResistance(gasResistance); // Assuming you have a method for gas resistance
// Measure battery voltage
vBat = getVbatt();
percentage = mapFloat(vBat, minVoltage, maxVoltage);
#ifdef DEBUG_TRACE
Serial.printf("Battery: %.2fV (%d%%)\n", vBat, percentage);
#endif
// Update battery percentage
zbTempSensor.setBatteryPercentage(percentage);
//zbTempSensor.setBatteryVoltage(vBat * 10); // voltage in 100mV
// Magnetic contact sensor
// static bool contact = false;
// if (digitalRead(MAGNETIC_SENSOR_PIN) == HIGH && !contact) {
// // Update contact sensor value
// zbContactSwitch.setOpen();
// contact = true;
// Serial.printf("contact = true");
// } else if (digitalRead(MAGNETIC_SENSOR_PIN) == LOW && contact) {
// zbContactSwitch.setClosed();
// contact = false;
// Serial.printf("contact = false");
// }
// Report values
zbTempSensor.report();
zbTempSensor.reportBatteryPercentage();
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// Turn on the builtin LED for a very short time
flashLED();
// Add small delay to allow the data to be sent before going to sleep
delay(500);
// Put device to deep sleep
#ifdef DEBUG_TRACE
Serial.println("Going to sleep now");
#endif
esp_deep_sleep_start();
// delay(5000);
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100);
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Configure use of external antenna
// pinMode(WIFI_ENABLE, OUTPUT); // pinMode(3, OUTPUT);
// digitalWrite(WIFI_ENABLE, LOW); // digitalWrite(3, LOW); // Activate RF switch control
// delay(100);
// pinMode(WIFI_ANT_CONFIG, OUTPUT); // pinMode(14, OUTPUT);
// digitalWrite(WIFI_ANT_CONFIG, HIGH); // digitalWrite(14, HIGH); // Use external antenna
// Configure builtin LED and turn it OFF (HIGH)
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(POWER_PIN, OUTPUT);
digitalWrite(POWER_PIN, HIGH); // Power on the sensor(s)
// Set analog resolution to 10 bits
analogReadResolution(10);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice);
/*
// Init BME280 sensor
Wire.begin();
while (!sensor.begin()) {
#ifdef DEBUG_TRACE
Serial.println("Could not find BME280 sensor!");
#endif
delay(1000);
}
*/
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Configure A0 as ADC input for reading battery voltage
pinMode(BAT_ADC_PIN, INPUT);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor_Node19");
//zbCarbonDioxideSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor");
// Set minimum and maximum temperature measurement value
zbTempSensor.setMinMaxValue(-20, 80);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
//zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// pinMode(MAGNETIC_SENSOR_PIN, INPUT_PULLUP); // Assuming switch pulls LOW when closed
// Zigbee.addEndpoint(&zbContactSwitch);
// pinMode(float_switch_pin, INPUT_PULLUP);
// Zigbee.addEndpoint(&zbContactSwitch);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
// If Zigbee does not start with 30s default timeout (ZB_BEGIN_TIMEOUT_DEFAULT) then restart
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
//zbCarbonDioxideSensor.setReporting(0, 30, 0);
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
// Call the function to measure temperature and put the device to deep sleep
// meausureAndSleep();
}
void loop() {
// No actions are performed in the loop (the ESP32C6 enters the setup function when it exits deep sleep).
meausureAndSleep();
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,579 @@
// https://tutoduino.fr/en/tutorials/esp32c6-zigbee/
// original: https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
// https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples
// Board: esp32 -> EDFRobot FireBeetle 2 ESP32-C6
// Core Debug Level: Verbose
// Erase All Flash Before Sketch Upload: Enabled
// Partition scheme: Zigbee 4MB with spiffs
// Zigbee Mode: Zigbee ED
//
// 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.
/**
* @brief Zigbee temperature and humidity sensor Zigbee Sleepy End Device.
*
* https://tutoduino.fr/tutoriels/esp32c6-zigbee/
* This code is based on example "Zigbee temperature and humidity sensor Sleepy device" created by Jan Procházka
* https://github.com/espressif/arduino-esp32/tree/master/libraries/Zigbee/examples/Zigbee_Temp_Hum_Sensor_Sleepy
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
// Uncomment the following line to display debug traces in serial monitor of Arduino IDE
#define DEBUG_TRACE
#define USE_GLOBAL_ON_RESPONSE_CALLBACK 1 // Set to 0 to use local callback specified directly for the endpoint.
#include "Zigbee.h"
//#include <BME280I2C.h>
//#include <Wire.h>
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 8
#define SDA_PIN 14
#define BAT_ADC_PIN 0 // ADC input pin
#define POWER_PIN 21 // GPIO pin to power the sensor
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 55 /* Sleep for 10 minutes */
#define REPORT_TIMEOUT 1000 /* Timeout for response from coordinator in ms */
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
uint8_t dataToSend = 4; // 10: T, rH, Batt; 11: CO2/resistance
bool resend = false;
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 11
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
// /* Zigbee contact sensor configuration */
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// uint8_t float_switch_pin = 3;
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// #define MAGNETIC_SENSOR_PIN 12
// #define CONTACT_SWITCH_ENDPOINT_NUMBER 12
// ZigbeeContactSwitch zbContactSwitch = ZigbeeContactSwitch(CONTACT_SWITCH_ENDPOINT_NUMBER);
// bool contact_open = false;
/* Zigbee analog device configuration */
#define ANALOG_DEVICE_ENDPOINT_NUMBER 13
uint8_t analogPin = 0;
ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
/************************ Callbacks *****************************/
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
void onGlobalResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster) {
Serial.printf("Global response command: %d, status: %s, endpoint: %d, cluster: 0x%04x, dataToSend: %i\r\n", command, esp_zb_zcl_status_to_name(status), endpoint, cluster, dataToSend);
if ((command == ZB_CMD_REPORT_ATTRIBUTE) && (endpoint == TEMP_SENSOR_ENDPOINT_NUMBER)) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
}
}
}
#else
void onResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status) {
Serial.printf("Response command: %d, status: %s\r\n", command, esp_zb_zcl_status_to_name(status));
if (command == ZB_CMD_REPORT_ATTRIBUTE) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
}
}
}
#endif
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
// 3.7 V Li-Ion battery voltage
const float minVoltage = 3.0;
const float maxVoltage = 4.0;
// Mapp float values to percentage
uint8_t mapFloat(float x, float in_min, float in_max) {
float val;
val = (x - in_min) * (100) / (in_max - in_min);
if (val < 0) {
val = 0;
} else if (val > 100) {
val = 100;
}
return (uint8_t)val;
}
// Get battery voltage en V
float getVbatt() {
uint32_t Vbatt = 0;
for (int i = 0; i < 16; i++) {
Vbatt += analogReadMilliVolts(BAT_ADC_PIN); // Read and accumulate ADC voltage
}
return (2 * Vbatt / 16 / 1000.0); // Adjust for 1:2 divider and convert to volts
}
// Get data from BME280 sensor and go to deep sleep mode
static void meausureAndSleep(void *arg) {
// Measure temperature sensor value
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
float vBat;
// BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
// BME280::PresUnit presUnit(BME280::PresUnit_hPa);
// Read temperature and humidity on BME280 sensor
// sensor.read(pressure, temperature, humidity, tempUnit, presUnit);
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
// Set the desired settings for the BME680
//bme.amb_temp = 25; // Set ambient temperature for compensation
//bme.meas_status = 0; // Clear previous measurement status
// Perform a measurement
//rslt = bme68x_get_sensor_data(BME68X_ALL, &data, &bme);
//if (rslt == BME68X_OK) {
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// // Checking pin for contact change
// static bool contact = false;
// if (digitalRead(float_switch_pin) == HIGH && !contact) {
// // Update contact sensor value
// // Serial.println("Float sensor: true");
// zbContactSwitch.setOpen();
// contact = true;
// } else if (digitalRead(float_switch_pin) == LOW && contact) {
// // Serial.println("Float sensor: true");
// zbContactSwitch.setClosed();
// contact = false;
// }
// #ifdef DEBUG_TRACE
// Serial.println(contact ? "Float sensor: true" : "Float sensor: false");
// #endif
delay(100);
digitalWrite(POWER_PIN, LOW); // Power off sensor
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
//zbTempSensor.setGasResistance(gasResistance); // Assuming you have a method for gas resistance
// Measure battery voltage
vBat = getVbatt();
percentage = mapFloat(vBat, minVoltage, maxVoltage);
#ifdef DEBUG_TRACE
Serial.printf("Battery: %.2fV (%d%%)\n", vBat, percentage);
#endif
// Update battery percentage
zbTempSensor.setBatteryPercentage(percentage);
//zbTempSensor.setBatteryVoltage(vBat * 10); // voltage in 100mV
// Magnetic contact sensor
// static bool contact = false;
// if (digitalRead(MAGNETIC_SENSOR_PIN) == HIGH && !contact) {
// // Update contact sensor value
// zbContactSwitch.setOpen();
// contact = true;
// Serial.printf("contact = true");
// } else if (digitalRead(MAGNETIC_SENSOR_PIN) == LOW && contact) {
// zbContactSwitch.setClosed();
// contact = false;
// Serial.printf("contact = false");
// }
// Read ADC value and update the analog value every 2s
float analog = (float)analogRead(analogPin);
Serial.printf("Updating analog input to %.1f\r\n", analog);
zbAnalogDevice.setAnalogInput(analog);
// Analog input supports reporting
// zbAnalogDevice.reportAnalogInput();
// Report values
zbTempSensor.report();
zbTempSensor.reportBatteryPercentage();
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
Serial.printf("dataToSend: %i\r\n", dataToSend);
delay(50);
#endif
// Turn on the builtin LED for a very short time
flashLED();
// Add small delay to allow the data to be sent before going to sleep
delay(50);
// Put device to deep sleep
unsigned long startTime = millis();
const unsigned long timeout = REPORT_TIMEOUT;
Serial.printf("Waiting for data report to be confirmed \r\n");
// Wait until data was successfully sent
int tries = 0;
const int maxTries = 3;
while (dataToSend != 0 && tries < maxTries) {
if (resend) {
Serial.println("Resending data on failure!");
resend = false;
dataToSend = 2;
zbTempSensor.report(); // report again
}
if (millis() - startTime >= timeout) {
Serial.println("\nReport timeout! Report Again");
dataToSend = 2;
zbTempSensor.report(); // report again
startTime = millis();
tries++;
}
Serial.printf(".");
delay(50); // 50ms delay to avoid busy-waiting
}
#ifdef DEBUG_TRACE
Serial.println("Going to sleep now");
#endif
esp_deep_sleep_start();
// delay(5000);
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100);
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Configure use of external antenna
// pinMode(WIFI_ENABLE, OUTPUT); // pinMode(3, OUTPUT);
// digitalWrite(WIFI_ENABLE, LOW); // digitalWrite(3, LOW); // Activate RF switch control
// delay(100);
// pinMode(WIFI_ANT_CONFIG, OUTPUT); // pinMode(14, OUTPUT);
// digitalWrite(WIFI_ANT_CONFIG, HIGH); // digitalWrite(14, HIGH); // Use external antenna
// Configure builtin LED and turn it OFF (HIGH)
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, HIGH);
pinMode(POWER_PIN, OUTPUT);
digitalWrite(POWER_PIN, HIGH); // Power on the sensor(s)
// Set callback for default response to handle status of reported data, there are 2 options.
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
// Global callback for all endpoints with more params to determine the endpoint and cluster in the callback function.
Zigbee.onGlobalDefaultResponse(onGlobalResponse);
#else
// Callback specified for endpoint
zbTempSensor.onDefaultResponse(onResponse);
#endif
// Set analog resolution to 10 bits
analogReadResolution(10);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice);
/*
// Init BME280 sensor
Wire.begin();
while (!sensor.begin()) {
#ifdef DEBUG_TRACE
Serial.println("Could not find BME280 sensor!");
#endif
delay(1000);
}
*/
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Configure A0 as ADC input for reading battery voltage
pinMode(BAT_ADC_PIN, INPUT);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor_Node19");
//zbCarbonDioxideSensor.setManufacturerAndModel("Tutoduino", "ESP32C6TempSensor");
// Set minimum and maximum temperature measurement value
zbTempSensor.setMinMaxValue(-20, 80);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
//zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// pinMode(MAGNETIC_SENSOR_PIN, INPUT_PULLUP); // Assuming switch pulls LOW when closed
// Zigbee.addEndpoint(&zbContactSwitch);
// pinMode(float_switch_pin, INPUT_PULLUP);
// Zigbee.addEndpoint(&zbContactSwitch);
// Create a default Zigbee configuration for End Device
// esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
// If Zigbee does not start with 30s default timeout (ZB_BEGIN_TIMEOUT_DEFAULT) then restart
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
//zbCarbonDioxideSensor.setReporting(0, 30, 0);
// Optional: Add reporting for analog input
// zbAnalogDevice.setAnalogInputReporting(0, 30, 10); // report every 30 seconds if value changes by 10
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
// delay(1000);
// Call the function to measure temperature and put the device to deep sleep
// meausureAndSleep();
// Start Temperature sensor reading task
xTaskCreate(meausureAndSleep, "temp_sensor_update", 2048, NULL, 10, NULL);
}
void loop() {
// No actions are performed in the loop (the ESP32C6 enters the setup function when it exits deep sleep).
// meausureAndSleep();
}

View File

@ -0,0 +1,207 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// #ifndef ZIGBEE_MODE_ED
// #error "Zigbee end device mode is not selected in Tools->Zigbee mode"
// #endif
#include "Zigbee.h"
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void setup() {
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
turnOnSensor(7);
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node21");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking PIR sensor for occupancy change
static bool occupancy = false;
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// Update occupancy sensor value
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
Serial.println("GPIO2 LOW");
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}

View File

@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,230 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// #ifndef ZIGBEE_MODE_ED
// #error "Zigbee end device mode is not selected in Tools->Zigbee mode"
// #endif
#include "Zigbee.h"
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
Serial.write(c); // Print response to Serial Monitor
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
sendCommandAndWaitForDone("setLatency 0.025 0.5", mmWaveSerial);
sendCommandAndWaitForDone("setEcho 1", mmWaveSerial); // Enable echo — sensor prints back commands and shows leapMMW:/> prompt (default).
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void setup() {
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
turnOnSensor(7);
for (int i = 0; i < numCommands; i++) {
const char* cmd = commands[i];
sendCommandAndWaitForDone(cmd, mmWaveSerial);
delay(10); // small delay before next command (optional)
}
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
analogWrite(ledPin, 255); // pin, dutyCycle
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node21");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking PIR sensor for occupancy change
static bool occupancy = false;
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// Update occupancy sensor value
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
analogWrite(ledPin, 0);
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
analogWrite(ledPin, 255);
Serial.println("GPIO2 LOW");
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
delay(100);
}

View File

@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,332 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// #ifndef ZIGBEE_MODE_ED
// #error "Zigbee end device mode is not selected in Tools->Zigbee mode"
// #endif
#include "Zigbee.h"
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
#define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
// Read temperature sensor value
// float tsens_value = temperatureRead();
float tsens_value = random(180, 300) / 10.0;
// Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// Update temperature value in Temperature sensor EP
zbTempSensor.setTemperature(tsens_value);
zbTempSensor2.setTemperature(tsens_value);
float humidity = random(300, 700) / 10.0;
zbTempSensor.setHumidity(humidity);
zbTempSensor2.setHumidity(humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
Serial.write(c); // Print response to Serial Monitor
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
sendCommandAndWaitForDone("setLatency 0.025 0.5", mmWaveSerial);
sendCommandAndWaitForDone("setEcho 1", mmWaveSerial); // Enable echo — sensor prints back commands and shows leapMMW:/> prompt (default).
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void setup() {
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
turnOnSensor(7);
for (int i = 0; i < numCommands; i++) {
const char* cmd = commands[i];
sendCommandAndWaitForDone(cmd, mmWaveSerial);
delay(10); // small delay before next command (optional)
}
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
analogWrite(ledPin, 255); // pin, dutyCycle
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node21");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor2.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor2.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor2.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor2);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 0);
zbTempSensor2.setReporting(0, 15, 0);
}
void loop() {
// Checking PIR sensor for occupancy change
static bool occupancy = false;
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// Update occupancy sensor value
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
analogWrite(ledPin, 0);
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
analogWrite(ledPin, 255);
Serial.println("GPIO2 LOW");
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
// zbTempSensor.report();
delay(100);
}

View File

@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,571 @@
// Xiao ESP32-C6
// uploading new script
// Zigbee connection establishment --> .......
// force remove deivce from Z2M with permit join OFF
// swich permit JOIN ON
// restart ESP32
// leave permit jion on until configuration finished
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 21
#define SDA_PIN 22
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(10000);
}
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
#define ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT 9
uint8_t illuminance_sensor_pin = 1; // Insert the analog pin to which the sensor (e.g. photoresistor) is connected
ZigbeeIlluminanceSensor zbIlluminanceSensor = ZigbeeIlluminanceSensor(ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT);
/********************* Illuminance sensor **************************/
static void illuminance_sensor_value_update(void *arg) {
for (;;) {
// read the raw analog value from the sensor
int lsens_analog_raw = analogRead(illuminance_sensor_pin);
Serial.printf("[Illuminance Sensor] raw analog value: %d\r\n", lsens_analog_raw);
// conversion into zigbee raw illuminance value (typically between 0 in darkness and 50000 in direct sunlight)
// depends on the value range of the raw analog sensor values and will need calibration for correct lux values
// for demonstration purpose map the 12-bit ADC value (0-4095) to Zigbee illuminance range (0-50000)
int lsens_illuminance_raw = map(lsens_analog_raw, 0, 4095, 0, 50000);
Serial.printf("[Illuminance Sensor] raw illuminance value: %d\r\n", lsens_illuminance_raw);
// according to zigbee documentation the formular 10^(lsens_illuminance_raw/10000)-1 can be used to calculate lux value from raw illuminance value
// Note: Zigbee2MQTT seems to be using the formular 10^(lsens_illuminance_raw/10000) instead (without -1)
int lsens_illuminance_lux = round(pow(10, (lsens_illuminance_raw / 10000.0)) - 1);
Serial.printf("[Illuminance Sensor] lux value: %d lux\r\n", lsens_illuminance_lux);
// Update illuminance in illuminance sensor EP
zbIlluminanceSensor.setIlluminance(lsens_illuminance_raw); // use raw illuminance here!
delay(10000); // reduce delay (in ms), if you want your device to react more quickly to changes in illuminance
}
}
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
Serial.write(c); // Print response to Serial Monitor
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
sendCommandAndWaitForDone("setLatency 0.025 0.5", mmWaveSerial);
sendCommandAndWaitForDone("setEcho 1", mmWaveSerial); // Enable echo — sensor prints back commands and shows leapMMW:/> prompt (default).
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
turnOnSensor(7); // initialization of mmwave sensor
for (int i = 0; i < numCommands; i++) {
const char* cmd = commands[i];
sendCommandAndWaitForDone(cmd, mmWaveSerial);
delay(10); // small delay before next command (optional)
}
// Optional: configure analog input
analogSetAttenuation(ADC_11db); // set analog to digital converter (ADC) attenuation to 11 dB (up to ~3.3V input)
analogReadResolution(12); // set analog read resolution to 12 bits (value range from 0 to 4095), 12 is default
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
analogWrite(ledPin, 255); // pin, dutyCycle
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node21");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbOccupancySensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// // Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
// zbTempSensor2.setMinMaxValue(10, 50);
// // Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
// zbTempSensor2.setTolerance(1);
// // Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// // The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// // zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// // Add humidity cluster to the temperature sensor device with min, max and tolerance values
// zbTempSensor2.addHumiditySensor(0, 100, 1);
// // Add endpoint to Zigbee Core
// Zigbee.addEndpoint(&zbTempSensor2);
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Set minimum and maximum for raw illuminance value (0 min and 50000 max equals to 0 lux - 100,000 lux)
zbIlluminanceSensor.setMinMaxValue(0, 50000);
// Optional: Set tolerance for raw illuminance value
zbIlluminanceSensor.setTolerance(1);
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee illuminance sensor endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbIlluminanceSensor);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 0);
// zbTempSensor2.setReporting(0, 15, 0);
zbCarbonDioxideSensor.setReporting(0, 30, 0);
// Start illuminance sensor reading task
xTaskCreate(illuminance_sensor_value_update, "illuminance_sensor_update", 2048, NULL, 10, NULL);
// Set reporting schedule for illuminance value measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta
// if min = 1 and max = 0, delta = 1000, reporting is sent when raw illuminance value changes by 1000, but at most once per second
// if min = 0 and max = 10, delta = 1000, reporting is sent every 10 seconds or if raw illuminance value changes by 1000
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of illuminance change
// Note: On pairing with Zigbee Home Automation or Zigbee2MQTT the reporting schedule will most likely be overwritten with their default settings
zbIlluminanceSensor.setReporting(1, 0, 1000);
}
void loop() {
// Checking PIR sensor for occupancy change
static bool occupancy = false;
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// Update occupancy sensor value
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
analogWrite(ledPin, 0);
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
analogWrite(ledPin, 255);
Serial.println("GPIO2 LOW");
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// force report of illuminance when button is pressed
zbIlluminanceSensor.report();
zbTempSensor.report();
zbCarbonDioxideSensor.report();
}
delay(100);
}

View File

@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,138 @@
// Copyright 2025 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.
/**
* @brief This example demonstrates simple Zigbee Range Extender (router).
*
* The example demonstrates how to use Zigbee library to create a Zigbee network ragbe extender (router).
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator/router mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define USE_CUSTOM_ZIGBEE_CONFIG 1
#define ZIGBEE_EXTENDER_ENDPOINT 1
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
#ifndef LED_BUILTIN
#define LED_BUILTIN 4
#endif
uint8_t led = LED_BUILTIN;
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
ZigbeeRangeExtender zbExtender = ZigbeeRangeExtender(ZIGBEE_EXTENDER_ENDPOINT);
/************************** Identify ******************************/
// Create a task on identify call to handle the identify function
void identify(uint16_t time) {
static uint8_t blink = 1;
log_d("Identify called for %d seconds", time);
if (time == 0) {
digitalWrite(led, LOW);
return;
}
digitalWrite(led, blink);
blink = !blink;
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
// Optional: Set callback function for device identify
// zbExtender.onIdentify(identify);
// Optional: Set Zigbee device name and model
zbExtender.setManufacturerAndModel("Espressif", "ZigbeeRangeExtender_Node21");
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee Extender endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbExtender);
Zigbee.addEndpoint(&zbOccupancySensor);
#if USE_CUSTOM_ZIGBEE_CONFIG
// Optional: Create a custom Zigbee configuration for Zigbee Extender
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ROUTER_CONFIG();
zigbeeConfig.nwk_cfg.zczr_cfg.max_children = 20; // 10 is default
// When all EPs are registered, start Zigbee with custom config
if (!Zigbee.begin(&zigbeeConfig)) {
#else
// When all EPs are registered, start Zigbee as ROUTER device
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
#endif
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
}
void loop() {
// Checking PIR sensor for occupancy change
static bool occupancy = false;
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// Update occupancy sensor value
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
Serial.println("GPIO2 LOW");
}
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
}

View File

@ -0,0 +1,68 @@
# Arduino-ESP32 Zigbee Range Extender (Router) Example
This example shows how to configure the Zigbee Router device and use it as a Home Automation (HA) network range extender.
To see if the communication with your Zigbee network works, use the Serial monitor and watch for output there.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
* Board (ESP32-H2 or ESP32-C6) as Zigbee router device and upload the Zigbee_Range_Extender example
* Zigbee network / coordinator (Other board with switch examples or Zigbee2mqtt or ZigbeeHomeAssistant like application)
### Configure the Project
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the Coordinator/Router device Zigbee mode: `Tools -> Zigbee mode: Zigbee ZCZR (coordinator/router)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs` (select correct size)
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the Router device flashed with this example is not connecting to the coordinator, erase the flash of the Router device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,6 @@
{
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
"requires": [
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,626 @@
// Xiao ESP32-C6
// uploading new script
// Zigbee connection establishment --> .......
// force remove deivce from Z2M with permit join OFF
// swich permit JOIN ON
// restart ESP32
// leave permit jion on until configuration finished
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 21
#define SDA_PIN 22
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
// #define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
// ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
#define ANALOG_DEVICE_ENDPOINT_NUMBER 13
ZigbeeAnalog zbAnalogDevice_BME68X = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
zbAnalogDevice_BME68X.setAnalogInput(round(gasResistance / 1000.0));
// zbAnalogDevice_BME68X.reportAnalogInput();
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(10000);
}
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
#define ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT 9
uint8_t illuminance_sensor_pin = 1; // Insert the analog pin to which the sensor (e.g. photoresistor) is connected
ZigbeeIlluminanceSensor zbIlluminanceSensor = ZigbeeIlluminanceSensor(ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT);
/********************* Illuminance sensor **************************/
static void illuminance_sensor_value_update(void *arg) {
for (;;) {
// read the raw analog value from the sensor
int lsens_analog_raw = analogRead(illuminance_sensor_pin);
Serial.printf("[Illuminance Sensor] raw analog value: %d\r\n", lsens_analog_raw);
// conversion into zigbee raw illuminance value (typically between 0 in darkness and 50000 in direct sunlight)
// depends on the value range of the raw analog sensor values and will need calibration for correct lux values
// for demonstration purpose map the 12-bit ADC value (0-4095) to Zigbee illuminance range (0-50000)
int lsens_illuminance_raw = map(lsens_analog_raw, 0, 4095, 0, 50000);
Serial.printf("[Illuminance Sensor] raw illuminance value: %d\r\n", lsens_illuminance_raw);
// according to zigbee documentation the formular 10^(lsens_illuminance_raw/10000)-1 can be used to calculate lux value from raw illuminance value
// Note: Zigbee2MQTT seems to be using the formular 10^(lsens_illuminance_raw/10000) instead (without -1)
int lsens_illuminance_lux = round(pow(10, (lsens_illuminance_raw / 10000.0)) - 1);
Serial.printf("[Illuminance Sensor] lux value: %d lux\r\n", lsens_illuminance_lux);
// Update illuminance in illuminance sensor EP
zbIlluminanceSensor.setIlluminance(lsens_illuminance_raw); // use raw illuminance here!
delay(10000); // reduce delay (in ms), if you want your device to react more quickly to changes in illuminance
}
}
// 1. Define the task function (Global scope, outside setup/loop)
void pirTask(void * parameter) {
// Move the static variable here. It doesn't need to be 'static' anymore
// because this function only runs once and stays in the loop below.
bool occupancy = false;
// 2. Infinite Loop: Tasks must never return!
for(;;) {
// --- Your Original Logic Start ---
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
analogWrite(ledPin, 0);
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
analogWrite(ledPin, 255);
Serial.println("GPIO2 LOW");
}
// --- Your Original Logic End ---
// 3. CRITICAL: Delay to yield control
// Polling a PIR every 50ms is plenty fast and saves CPU.
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
Serial.write(c); // Print response to Serial Monitor
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 3", mmWaveSerial);
sendCommandAndWaitForDone("setLatency 0.025 0.5", mmWaveSerial);
sendCommandAndWaitForDone("setEcho 1", mmWaveSerial); // Enable echo — sensor prints back commands and shows leapMMW:/> prompt (default).
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
turnOnSensor(5); // initialization of mmwave sensor
for (int i = 0; i < numCommands; i++) {
const char* cmd = commands[i];
sendCommandAndWaitForDone(cmd, mmWaveSerial);
delay(10); // small delay before next command (optional)
}
// Optional: configure analog input
analogSetAttenuation(ADC_11db); // set analog to digital converter (ADC) attenuation to 11 dB (up to ~3.3V input)
analogReadResolution(12); // set analog read resolution to 12 bits (value range from 0 to 4095), 12 is default
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
analogWrite(ledPin, 255); // pin, dutyCycle
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node21");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbOccupancySensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(0, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// // Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
// zbTempSensor2.setMinMaxValue(10, 50);
// // Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
// zbTempSensor2.setTolerance(1);
// // Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// // The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// // zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// // Add humidity cluster to the temperature sensor device with min, max and tolerance values
// zbTempSensor2.addHumiditySensor(0, 100, 1);
// // Add endpoint to Zigbee Core
// Zigbee.addEndpoint(&zbTempSensor2);
// // Set minimum and maximum carbon dioxide measurement value in ppm
// zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// // Add endpoints to Zigbee Core
// Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice_BME68X.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice_BME68X);
// Set minimum and maximum for raw illuminance value (0 min and 50000 max equals to 0 lux - 100,000 lux)
zbIlluminanceSensor.setMinMaxValue(0, 50000);
// Optional: Set tolerance for raw illuminance value
zbIlluminanceSensor.setTolerance(1);
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee illuminance sensor endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbIlluminanceSensor);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// 4. Create the task
xTaskCreate(
pirTask, // Function to call
"PIR_Check", // Name for debugging
4096, // Stack size (bytes) - Zigbee operations need space!
NULL, // Parameter to pass
1, // Priority (1 is standard, higher numbers = higher priority)
NULL // Task handle
);
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 1);
// zbTempSensor2.setReporting(0, 15, 0);
// zbCarbonDioxideSensor.setReporting(0, 30, 0);
zbAnalogDevice_BME68X.setAnalogInputReporting(0, 15, 10); // report every 30 seconds if value changes by 10
// Start illuminance sensor reading task
xTaskCreate(illuminance_sensor_value_update, "illuminance_sensor_update", 2048, NULL, 10, NULL);
// Set reporting schedule for illuminance value measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta
// if min = 1 and max = 0, delta = 1000, reporting is sent when raw illuminance value changes by 1000, but at most once per second
// if min = 0 and max = 10, delta = 1000, reporting is sent every 10 seconds or if raw illuminance value changes by 1000
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of illuminance change
// Note: On pairing with Zigbee Home Automation or Zigbee2MQTT the reporting schedule will most likely be overwritten with their default settings
zbIlluminanceSensor.setReporting(1, 0, 1);
}
void loop() {
// Checking PIR sensor for occupancy change
// static bool occupancy = false;
// if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// // Update occupancy sensor value
// zbOccupancySensor.setOccupancy(true);
// zbOccupancySensor.report();
// occupancy = true;
// analogWrite(ledPin, 0);
// Serial.println("GPIO2 HIGH");
// } else if (digitalRead(sensor_pin) == LOW && occupancy) {
// zbOccupancySensor.setOccupancy(false);
// zbOccupancySensor.report();
// occupancy = false;
// analogWrite(ledPin, 255);
// Serial.println("GPIO2 LOW");
// }
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// force report of illuminance when button is pressed
zbIlluminanceSensor.report();
zbTempSensor.report();
// zbCarbonDioxideSensor.report();
zbAnalogDevice_BME68X.reportAnalogInput();
}
delay(100);
}

View File

@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,630 @@
// Xiao ESP32-C6
// uploading new script
// Zigbee connection establishment --> .......
// force remove deivce from Z2M with permit join OFF
// swich permit JOIN ON
// restart ESP32
// leave permit jion on until configuration finished
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee occupancy sensor.
*
* The example demonstrates how to use Zigbee library to create a end device occupancy sensor.
* The occupancy sensor is a Zigbee end device, which is reporting data to the Zigbee network.
* Tested with PIR sensor HC-SR501 connected to GPIO4.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
// #ifndef ZIGBEE_MODE_ED
// #error "Zigbee end device mode is not selected in Tools->Zigbee mode"
// #endif
#ifndef ZIGBEE_MODE_ZCZR
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
#include <HardwareSerial.h>
#define SENSOR_RX 17 // ESP32 RX connected to sensor TX
#define SENSOR_TX 16 // ESP32 TX connected to sensor RX
HardwareSerial mmWaveSerial(1); // UART2
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 1
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 23; // connected to GPIO2 of the sensor; HIGH in case of presence detection
// Fade LED PIN (replace with LED_BUILTIN constant for the built-in LED)
// #define LED_PIN D5
const int ledPin = LED_BUILTIN;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 21
#define SDA_PIN 22
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
// #define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
// ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
#define ANALOG_DEVICE_ENDPOINT_NUMBER 13
ZigbeeAnalog zbAnalogDevice_BME68X = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
zbAnalogDevice_BME68X.setAnalogInput(round(gasResistance));
// zbAnalogDevice_BME68X.reportAnalogInput();
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(10000);
}
}
// Internal Led flash
void flashLED() {
// Turn on LED for 100ms
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
}
#define ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT 9
uint8_t illuminance_sensor_pin = 1; // Insert the analog pin to which the sensor (e.g. photoresistor) is connected
ZigbeeIlluminanceSensor zbIlluminanceSensor = ZigbeeIlluminanceSensor(ZIGBEE_ILLUMINANCE_SENSOR_ENDPOINT);
/********************* Illuminance sensor **************************/
static void illuminance_sensor_value_update(void *arg) {
for (;;) {
// read the raw analog value from the sensor
int lsens_analog_raw = analogRead(illuminance_sensor_pin);
Serial.printf("[Illuminance Sensor] raw analog value: %d\r\n", lsens_analog_raw);
// conversion into zigbee raw illuminance value (typically between 0 in darkness and 50000 in direct sunlight)
// depends on the value range of the raw analog sensor values and will need calibration for correct lux values
// for demonstration purpose map the 12-bit ADC value (0-4095) to Zigbee illuminance range (0-50000)
int lsens_illuminance_raw = map(lsens_analog_raw, 0, 4095, 0, 50000);
Serial.printf("[Illuminance Sensor] raw illuminance value: %d\r\n", lsens_illuminance_raw);
// according to zigbee documentation the formular 10^(lsens_illuminance_raw/10000)-1 can be used to calculate lux value from raw illuminance value
// Note: Zigbee2MQTT seems to be using the formular 10^(lsens_illuminance_raw/10000) instead (without -1)
int lsens_illuminance_lux = round(pow(10, (lsens_illuminance_raw / 10000.0)) - 1);
Serial.printf("[Illuminance Sensor] lux value: %d lux\r\n", lsens_illuminance_lux);
// Update illuminance in illuminance sensor EP
zbIlluminanceSensor.setIlluminance(lsens_illuminance_raw); // use raw illuminance here!
delay(10000); // reduce delay (in ms), if you want your device to react more quickly to changes in illuminance
}
}
// 1. Define the task function (Global scope, outside setup/loop)
void pirTask(void * parameter) {
// Move the static variable here. It doesn't need to be 'static' anymore
// because this function only runs once and stays in the loop below.
bool occupancy = false;
// 2. Infinite Loop: Tasks must never return!
for(;;) {
// --- Your Original Logic Start ---
if (digitalRead(sensor_pin) == HIGH && !occupancy) {
zbOccupancySensor.setOccupancy(true);
zbOccupancySensor.report();
occupancy = true;
analogWrite(ledPin, 0);
Serial.println("GPIO2 HIGH");
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
analogWrite(ledPin, 255);
Serial.println("GPIO2 LOW");
}
// --- Your Original Logic End ---
// 3. CRITICAL: Delay to yield control
// Polling a PIR every 50ms is plenty fast and saves CPU.
vTaskDelay(50 / portTICK_PERIOD_MS);
}
}
const char* commands[] = {
"getRange",
"getSensitivity",
"getLatency",
"getUart",
"getGpioMode 2",
"getLedMode 1",
"getEcho",
"getUartOutput 1",
"getUartOutput 2",
// "sensorStop",
// "sensorStart",
// "saveConfig",
// "resetCfg",
// "resetSystem",
"getHWV",
"getSWV",
"getOutput"
};
const int numCommands = sizeof(commands) / sizeof(commands[0]);
String line = "";
bool sendCommandAndWaitForDone(String command, Stream &sensorSerial, unsigned long timeout = 1000) {
// Send the command
sensorSerial.println(command);
Serial.print("Sending: ");
Serial.println(command);
// Wait for "Done" response
unsigned long startTime = millis();
String response;
while (millis() - startTime < timeout) {
while (sensorSerial.available()) {
char c = sensorSerial.read();
Serial.write(c); // Print response to Serial Monitor
response += c;
// If "Done" is found in the response, return success
if (response.indexOf("Done") >= 0) {
Serial.println("✓ Done received.");
return true;
}
// Optional: detect "Error" for debugging
if (response.indexOf("Error") >= 0) {
Serial.println("✗ Error received.");
return false;
}
}
}
Serial.println("✗ Timeout waiting for Done.");
return false;
}
void turnOnSensor(int sensitivity) {
Serial.println("=== Turning On Sensor ===");
sendCommandAndWaitForDone("sensorStop", mmWaveSerial);
sendCommandAndWaitForDone("resetCfg", mmWaveSerial); // Optional
// sendCommandAndWaitForDone("setUartOutput 1 0", mmWaveSerial); // Disable $JYBSS
// sendCommandAndWaitForDone("setUartOutput 2 1", mmWaveSerial); // Enable $JYRPO
sendCommandAndWaitForDone("setUartOutput 1 1", mmWaveSerial); // Enable $JYBSS
sendCommandAndWaitForDone("setUartOutput 2 0", mmWaveSerial); // Disable $JYRPO
sendCommandAndWaitForDone("setSensitivity " + String(sensitivity), mmWaveSerial);
sendCommandAndWaitForDone("setRange 0 6", mmWaveSerial);
sendCommandAndWaitForDone("setLatency 0.025 0.5", mmWaveSerial);
sendCommandAndWaitForDone("setEcho 1", mmWaveSerial); // Enable echo — sensor prints back commands and shows leapMMW:/> prompt (default).
sendCommandAndWaitForDone("setLedMode 1 1", mmWaveSerial); // LED off
sendCommandAndWaitForDone("setGpioMode 2 1", mmWaveSerial); // GPIO2 high when presence
sendCommandAndWaitForDone("saveConfig", mmWaveSerial);
sendCommandAndWaitForDone("sensorStart", mmWaveSerial);
}
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
mmWaveSerial.begin(115200, SERIAL_8N1, SENSOR_RX, SENSOR_TX);
delay(100); // Give sensor time to boot
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
turnOnSensor(5); // initialization of mmwave sensor
for (int i = 0; i < numCommands; i++) {
const char* cmd = commands[i];
sendCommandAndWaitForDone(cmd, mmWaveSerial);
delay(10); // small delay before next command (optional)
}
// Optional: configure analog input
analogSetAttenuation(ADC_11db); // set analog to digital converter (ADC) attenuation to 11 dB (up to ~3.3V input)
analogReadResolution(12); // set analog read resolution to 12 bits (value range from 0 to 4095), 12 is default
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
analogWrite(ledPin, 255); // pin, dutyCycle
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("Espressif", "ZigbeeOccupancyPIRSensor_Node21");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbOccupancySensor.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(0, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// // Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
// zbTempSensor2.setMinMaxValue(10, 50);
// // Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
// zbTempSensor2.setTolerance(1);
// // Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// // The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// // zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// // Add humidity cluster to the temperature sensor device with min, max and tolerance values
// zbTempSensor2.addHumiditySensor(0, 100, 1);
// // Add endpoint to Zigbee Core
// Zigbee.addEndpoint(&zbTempSensor2);
// // Set minimum and maximum carbon dioxide measurement value in ppm
// zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// // Add endpoints to Zigbee Core
// Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Add analog clusters to Zigbee Analog according your needs
zbAnalogDevice_BME68X.addAnalogInput();
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice_BME68X);
// Set minimum and maximum for raw illuminance value (0 min and 50000 max equals to 0 lux - 100,000 lux)
zbIlluminanceSensor.setMinMaxValue(0, 50000);
// Optional: Set tolerance for raw illuminance value
zbIlluminanceSensor.setTolerance(1);
// Add endpoint to Zigbee Core
Serial.println("Adding Zigbee illuminance sensor endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbIlluminanceSensor);
// Create a default Zigbee configuration for End Device
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
// if (!Zigbee.begin(&zigbeeConfig, false)) {
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// 4. Create the task
xTaskCreate(
pirTask, // Function to call
"PIR_Check", // Name for debugging
4096, // Stack size (bytes) - Zigbee operations need space!
NULL, // Parameter to pass
1, // Priority (1 is standard, higher numbers = higher priority)
NULL // Task handle
);
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 1);
// zbTempSensor2.setReporting(0, 15, 0);
// zbCarbonDioxideSensor.setReporting(0, 30, 0);
zbAnalogDevice_BME68X.setAnalogInputReporting(0, 15, 10); // report every 30 seconds if value changes by 10
// Start illuminance sensor reading task
xTaskCreate(illuminance_sensor_value_update, "illuminance_sensor_update", 2048, NULL, 10, NULL);
// Set reporting schedule for illuminance value measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta
// if min = 1 and max = 0, delta = 1000, reporting is sent when raw illuminance value changes by 1000, but at most once per second
// if min = 0 and max = 10, delta = 1000, reporting is sent every 10 seconds or if raw illuminance value changes by 1000
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of illuminance change
// Note: On pairing with Zigbee Home Automation or Zigbee2MQTT the reporting schedule will most likely be overwritten with their default settings
zbIlluminanceSensor.setReporting(1, 0, 1);
}
void loop() {
// Checking PIR sensor for occupancy change
// static bool occupancy = false;
// if (digitalRead(sensor_pin) == HIGH && !occupancy) {
// // Update occupancy sensor value
// zbOccupancySensor.setOccupancy(true);
// zbOccupancySensor.report();
// occupancy = true;
// analogWrite(ledPin, 0);
// Serial.println("GPIO2 HIGH");
// } else if (digitalRead(sensor_pin) == LOW && occupancy) {
// zbOccupancySensor.setOccupancy(false);
// zbOccupancySensor.report();
// occupancy = false;
// analogWrite(ledPin, 255);
// Serial.println("GPIO2 LOW");
// }
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// force report of illuminance when button is pressed
zbIlluminanceSensor.report();
zbTempSensor.report();
// zbCarbonDioxideSensor.report();
zbAnalogDevice_BME68X.reportAnalogInput();
}
delay(100);
}

View File

@ -0,0 +1,57 @@
# Arduino-ESP32 Zigbee Occupancy Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) occupancy sensor (PIR).
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
Set the Sensor GPIO by changing the `sensor_pin` variable.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,385 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates simple Zigbee light bulb.
*
* The example demonstrates how to use Zigbee library to create a end device light bulb.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_LIGHT_ENDPOINT 10
//uint8_t led = LED_BUILTIN;
uint8_t led = 6;
uint8_t button = BOOT_PIN;
#define LED_PIN 15 // Onboard LED
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 22
#define SDA_PIN 21
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);
// ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/********************* RGB LED functions **************************/
void setLED(bool value) {
Serial.println("setLED called");
digitalWrite(LED_PIN, value);
digitalWrite(led, value);
}
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
static void temp_sensor_value_update_old(void *arg) {
for (;;) {
// Read temperature sensor value
// float tsens_value = temperatureRead();
float tsens_value = random(180, 300) / 10.0;
// Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// Update temperature value in Temperature sensor EP
zbTempSensor.setTemperature(tsens_value);
float humidity = random(300, 700) / 10.0;
zbTempSensor.setHumidity(humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature;
// Simulate temperature between 18.0°C and 30.0°C
float temperature = random(180, 300) / 10.0;
// Simulate humidity between 30.0% and 70.0%
float humidity = random(300, 700) / 10.0;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Add small delay to allow the data to be sent before going to sleep
delay(100);
// Put device to deep sleep
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100); // Give sensor time to boot
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
//Optional: set Zigbee device name and model
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb_Node22");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbLight.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Set callback function for light change
zbLight.onLightChange(setLED);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbLight);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Create a default Zigbee configuration for End Device
// esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 0);
zbCarbonDioxideSensor.setReporting(0, 30, 0);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
zbLight.setLight(!zbLight.getLightState());
// Call the function to measure temperature and put the device to sleep
// meausureAndSleep();
// zbTempSensor.reportTemperature();
zbTempSensor.report();
zbCarbonDioxideSensor.report();
}
delay(100);
}

View File

@ -0,0 +1,70 @@
# Arduino-ESP32 Zigbee On/Off Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) on/off light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with Zigbee_On_Off_switch example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_On_Off_Light example
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,431 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates simple Zigbee light bulb.
*
* The example demonstrates how to use Zigbee library to create a end device light bulb.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_FAN_CONTROL_ENDPOINT 10
//uint8_t led = LED_BUILTIN;
uint8_t led = 6;
uint8_t button = BOOT_PIN;
#define LED_PIN 15 // Onboard LED
#include "Arduino.h"
#include <bme68xLibrary.h>
// Define the I2C pins
#define SCL_PIN 22
#define SDA_PIN 21
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define TEMP_SENSOR_ENDPOINT_NUMBER_2 12
// ZigbeeTempSensor zbTempSensor2 = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER_2);
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 13
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
/* BME280 sensor */
//BME280I2C sensor;
Bme68x bme;
int errorCount = 0; // Initialize an error counter
const int maxErrors = 5; // Maximum number of allowed errors before restart
ZigbeeFanControl zbFanControl = ZigbeeFanControl(ZIGBEE_FAN_CONTROL_ENDPOINT);
// ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/********************* RGB LED functions **************************/
void setLED(bool value) {
Serial.println("setLED called");
digitalWrite(LED_PIN, value);
digitalWrite(led, value);
}
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
float temperature(NAN), humidity(NAN), pressure(NAN), gasResistance(NAN);
uint8_t percentage;
// Prepare the BME680 for measurement
bme68xData data;
int8_t rslt;
bme.setOpMode(BME68X_FORCED_MODE);
delayMicroseconds(bme.getMeasDur());
if (bme.fetchData()) {
bme.getData(data);
temperature = data.temperature; // Temperature in °C
humidity = data.humidity; // Humidity in %
pressure = data.pressure; // Pressure in hPa
gasResistance = data.gas_resistance; // Gas resistance in ohms
errorCount = 0; // Reset error count on successful read
} else {
//Serial.println("Failed to read data from BME680!");
//return; // Exit if reading failed
errorCount++;
Serial.println("Failed to read data from BME680!");
if (errorCount >= maxErrors) {
Serial.println("Too many errors! Restarting...");
ESP.restart(); // Restart the ESP32 after too many errors
}
}
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
zbCarbonDioxideSensor.setCarbonDioxide(gasResistance); // Assuming you have a method for gas resistance
// Report values
// zbTempSensor.report();
// zbTempSensor.reportBatteryPercentage();
// zbCarbonDioxideSensor.setCarbonDioxide(gasResistance);
// zbCarbonDioxideSensor.report();
#ifdef DEBUG_TRACE
//Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%, Gas Resistance: %.2f ohms\r\n", temperature, humidity, gasResistance);
#endif
// // Read temperature sensor value
// // float tsens_value = temperatureRead();
// float tsens_value = random(180, 300) / 10.0;
// // Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// // Update temperature value in Temperature sensor EP
// zbTempSensor.setTemperature(tsens_value);
// zbTempSensor2.setTemperature(tsens_value);
// float humidity = random(300, 700) / 10.0;
// zbTempSensor.setHumidity(humidity);
// zbTempSensor2.setHumidity(humidity);
// Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
static void temp_sensor_value_update_old(void *arg) {
for (;;) {
// Read temperature sensor value
// float tsens_value = temperatureRead();
float tsens_value = random(180, 300) / 10.0;
// Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// Update temperature value in Temperature sensor EP
zbTempSensor.setTemperature(tsens_value);
float humidity = random(300, 700) / 10.0;
zbTempSensor.setHumidity(humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature;
// Simulate temperature between 18.0°C and 30.0°C
float temperature = random(180, 300) / 10.0;
// Simulate humidity between 30.0% and 70.0%
float humidity = random(300, 700) / 10.0;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Add small delay to allow the data to be sent before going to sleep
delay(100);
// Put device to deep sleep
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
/********************* fan control callback function **************************/
void setFan(ZigbeeFanMode mode) {
switch (mode) {
case FAN_MODE_OFF:
rgbLedWrite(led, 0, 0, 0); // Off
Serial.println("Fan mode: OFF");
break;
case FAN_MODE_LOW:
rgbLedWrite(led, 0, 0, 255); // Blue
Serial.println("Fan mode: LOW");
break;
case FAN_MODE_MEDIUM:
rgbLedWrite(led, 255, 255, 0); // Yellow
Serial.println("Fan mode: MEDIUM");
break;
case FAN_MODE_HIGH:
rgbLedWrite(led, 255, 0, 0); // Red
Serial.println("Fan mode: HIGH");
break;
case FAN_MODE_ON:
rgbLedWrite(led, 255, 255, 255); // White
Serial.println("Fan mode: ON");
break;
default: log_e("Unhandled fan mode: %d", mode); break;
}
}
/********************* Arduino functions **************************/
void setup() {
#ifdef DEBUG_TRACE
Serial.begin(115200);
delay(100); // Give sensor time to boot
Serial.println();
Serial.println("Tutoduino Zigbee temperature sensor start!");
#endif
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
//Optional: set Zigbee device name and model
zbFanControl.setManufacturerAndModel("Espressif", "ZBLightBulb_Node22");
// Optional: Set power source (choose between ZB_POWER_SOURCE_MAINS and ZB_POWER_SOURCE_BATTERY), defaults to unknown
zbFanControl.setPowerSource(ZB_POWER_SOURCE_MAINS);
// Set the fan mode sequence to LOW_MED_HIGH
zbFanControl.setFanModeSequence(FAN_MODE_SEQUENCE_LOW_MED_HIGH);
// Set callback function for fan mode change
zbFanControl.onFanModeChange(setFan);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeFanControl endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbFanControl);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(100000); // Set I2C clock speed to 100kHz
// Initialize the BME680
bme.begin(BME68X_I2C_ADDR_HIGH, Wire); // BME68X_I2C_ADDR_HIGH=0x76, BME68X_I2C_ADDR_LOW=0x77
// Set up the BME680
// bme.setGasStatus(BME680_ENABLE_GAS_MEAS);
// bme.setOversampling(T2, OS_2X);
// bme.setOversampling(T1, OS_16X);
// bme.setOversampling(H1, OS_2X);
// bme.setFilter(FILTER_SIZE_3);
// bme.setGasSettings(320, 25, 0x4, 130, 410, 4, 0x01, 0x10, 0);
if(bme.checkStatus())
{
if (bme.checkStatus() == BME68X_ERROR)
{
Serial.println("Sensor error:" + bme.statusString());
return;
}
else if (bme.checkStatus() == BME68X_WARNING)
{
Serial.println("Sensor Warning:" + bme.statusString());
}
}
/* Set the default configuration for temperature, pressure and humidity */
bme.setTPH();
/* Set the heater configuration to 300 deg C for 100ms for Forced mode */
bme.setHeaterProf(300, 100);
bme68xData data;
bme.setOpMode(BME68X_FORCED_MODE);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 150000000);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
// Create a default Zigbee configuration for End Device
// esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
#ifdef DEBUG_TRACE
Serial.println("Starting Zigbee");
#endif
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
#ifdef DEBUG_TRACE
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting ESP32!");
#endif
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
#ifdef DEBUG_TRACE
Serial.println("Connecting to network");
#endif
while (!Zigbee.connected()) {
#ifdef DEBUG_TRACE
Serial.print(".");
#endif
delay(100);
}
#ifdef DEBUG_TRACE
Serial.println("Successfully connected to Zigbee network");
#endif
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 0);
zbCarbonDioxideSensor.setReporting(0, 30, 0);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
// zbLight.setLight(!zbLight.getLightState());
// Call the function to measure temperature and put the device to sleep
// meausureAndSleep();
// zbTempSensor.reportTemperature();
zbTempSensor.report();
zbCarbonDioxideSensor.report();
}
delay(100);
}

View File

@ -0,0 +1,70 @@
# Arduino-ESP32 Zigbee On/Off Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) on/off light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with Zigbee_On_Off_switch example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_On_Off_Light example
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,188 @@
/*
how to define various analog devices:
https://github.com/espressif/esp-zigbee-sdk/blob/main/components/esp-zigbee-lib/include/zcl/esp_zigbee_zcl_analog_input.h
zbAnalogTemp.setAnalogInputApplication(ESP_ZB_ZCL_AI_TEMPERATURE_OTHER);
*/
// Copyright 2025 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.
/**
* @brief This example demonstrates Zigbee analog input / output device.
*
* The example demonstrates how to use Zigbee library to create a end device analog device.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
* Modified by Pat Clay
*/
// #ifndef ZIGBEE_MODE_ZCZR
// #error "Zigbee coordinator/router device mode is not selected in Tools->Zigbee mode"
// #endif
#include "Zigbee.h"
/* Zigbee analog device configuration */
#define ANALOG_DEVICE_ENDPOINT_NUMBER 1
uint8_t analogPin = A0;
uint8_t button = BOOT_PIN;
ZigbeeAnalog zbAnalogDevice = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER);
ZigbeeAnalog zbAnalogTemp = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 1);
ZigbeeAnalog zbAnalogFan = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 2);
ZigbeeAnalog zbAnalogPercent = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 3);
void onAnalogOutputChange(float analog_output) {
Serial.printf("Received analog output change: %.1f\r\n", analog_output);
}
void setup() {
Serial.begin(115200);
Serial.println("Starting...");
// Init button switch
pinMode(button, INPUT_PULLUP);
// Set analog resolution to 10 bits
analogReadResolution(10);
// Optional: set Zigbee device name and model
zbAnalogDevice.setManufacturerAndModel("Espressif", "ZigbeeAnalogDevice_Node23");
// Set up analog input
zbAnalogDevice.addAnalogInput();
zbAnalogDevice.setAnalogInputApplication(ESP_ZB_ZCL_AI_POWER_IN_WATTS_CONSUMPTION);
zbAnalogDevice.setAnalogInputDescription("Power Consumption (Watts)");
zbAnalogDevice.setAnalogInputResolution(0.01);
// Set up analog output
zbAnalogDevice.addAnalogOutput();
zbAnalogDevice.setAnalogOutputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
zbAnalogDevice.setAnalogOutputDescription("Fan Speed (RPM)");
zbAnalogDevice.setAnalogOutputResolution(1);
// Set the min and max values for the analog output which is used by HA to limit the range of the analog output
zbAnalogDevice.setAnalogOutputMinMax(-10000, 10000); //-10000 to 10000 RPM
// If analog output cluster is added, set callback function for analog output change
zbAnalogDevice.onAnalogOutputChange(onAnalogOutputChange);
// Set up analog input
zbAnalogTemp.addAnalogInput();
zbAnalogTemp.setAnalogInputApplication(ESP_ZB_ZCL_AI_TEMPERATURE_OTHER);
zbAnalogTemp.setAnalogInputDescription("Temperature");
zbAnalogTemp.setAnalogInputResolution(0.1);
// Set up analog input
zbAnalogFan.addAnalogInput();
zbAnalogFan.setAnalogInputApplication(ESP_ZB_ZCL_AI_RPM_OTHER);
zbAnalogFan.setAnalogInputDescription("RPM");
zbAnalogFan.setAnalogInputResolution(1);
// Set up analog input
zbAnalogPercent.addAnalogInput();
zbAnalogPercent.setAnalogInputApplication(ESP_ZB_ZCL_AI_PERCENTAGE_OTHER);
zbAnalogPercent.setAnalogInputDescription("Percentage");
zbAnalogPercent.setAnalogInputResolution(0.01);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbAnalogDevice);
Zigbee.addEndpoint(&zbAnalogTemp);
Zigbee.addEndpoint(&zbAnalogFan);
Zigbee.addEndpoint(&zbAnalogPercent);
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in Router Device mode
if (!Zigbee.begin(ZIGBEE_ROUTER)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println("Connected");
// Optional: Add reporting for analog input
zbAnalogDevice.setAnalogInputReporting(0, 30, 10); // report every 30 seconds if value changes by 10
}
void loop() {
static uint32_t timeCounter = 0;
// Read ADC value and update the analog value every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
float analog = (float)analogRead(analogPin);
Serial.printf("Updating analog input to %.1f\r\n", analog);
zbAnalogDevice.setAnalogInput(analog);
zbAnalogTemp.setAnalogInput(analog / 100);
zbAnalogFan.setAnalogInput(analog);
zbAnalogPercent.setAnalogInput(analog / 10);
// Analog input supports reporting
zbAnalogDevice.reportAnalogInput();
zbAnalogTemp.reportAnalogInput();
zbAnalogFan.reportAnalogInput();
zbAnalogPercent.reportAnalogInput();
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// For demonstration purposes, increment the analog output value by 100
zbAnalogDevice.setAnalogOutput(zbAnalogDevice.getAnalogOutput() + 100);
zbAnalogDevice.reportAnalogOutput();
}
delay(100);
}

View File

@ -0,0 +1,72 @@
# Arduino-ESP32 Zigbee Analog Input Output Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) analog input/output device.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Analog Sensor Functions
* After this board first starts up, it would be configured locally to report an analog input on change or every 30 seconds.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured value to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
Set the ADC GPIO by changing the `analogPin` variable. By default, it's the pin `A0`.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,6 @@
{
"fqbn_append": "PartitionScheme=zigbee_zczr,ZigbeeMode=zczr",
"requires": [
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,238 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#define USE_GLOBAL_ON_RESPONSE_CALLBACK 1 // Set to 0 to use local callback specified directly for the endpoint.
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
#define REPORT_TIMEOUT 1000 /* Timeout for response from coordinator in ms */
uint8_t button = BOOT_PIN;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// #define ANALOG_DEVICE_ENDPOINT_NUMBER 1
// ZigbeeAnalog zbAnalogPercent = ZigbeeAnalog(ANALOG_DEVICE_ENDPOINT_NUMBER + 3);
uint8_t dataToSend = 2; // Temperature and humidity values are reported in same endpoint, so 2 values are reported
bool resend = false;
/************************ Callbacks *****************************/
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
void onGlobalResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status, uint8_t endpoint, uint16_t cluster) {
Serial.printf("Global response command: %d, status: %s, endpoint: %d, cluster: 0x%04x\r\n", command, esp_zb_zcl_status_to_name(status), endpoint, cluster);
if ((command == ZB_CMD_REPORT_ATTRIBUTE) && (endpoint == TEMP_SENSOR_ENDPOINT_NUMBER)) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
}
}
}
#else
void onResponse(zb_cmd_type_t command, esp_zb_zcl_status_t status) {
Serial.printf("Response command: %d, status: %s\r\n", command, esp_zb_zcl_status_to_name(status));
if (command == ZB_CMD_REPORT_ATTRIBUTE) {
switch (status) {
case ESP_ZB_ZCL_STATUS_SUCCESS: dataToSend--; break;
case ESP_ZB_ZCL_STATUS_FAIL: resend = true; break;
default: break; // add more statuses like ESP_ZB_ZCL_STATUS_INVALID_VALUE, ESP_ZB_ZCL_STATUS_TIMEOUT etc.
}
}
}
#endif
/************************ Temp sensor *****************************/
static void meausureAndSleep(void *arg) {
// Measure temperature sensor value
float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
float humidity = temperature;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report(); // reports temperature and humidity values (if humidity sensor is not added, only temperature is reported)
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// float analogpercent = temperature * 0.5;
// zbAnalogPercent.reportAnalogInput();
// Serial.printf("Reported zbAnalogPercent: %.2f°C\r\n", analogpercent);
unsigned long startTime = millis();
const unsigned long timeout = REPORT_TIMEOUT;
Serial.printf("Waiting for data report to be confirmed \r\n");
// Wait until data was successfully sent
int tries = 0;
const int maxTries = 3;
while (dataToSend != 0 && tries < maxTries) {
if (resend) {
Serial.println("Resending data on failure!");
resend = false;
dataToSend = 2;
zbTempSensor.report(); // report again
}
if (millis() - startTime >= timeout) {
Serial.println("\nReport timeout! Report Again");
dataToSend = 2;
zbTempSensor.report(); // report again
startTime = millis();
tries++;
}
Serial.printf(".");
delay(50); // 50ms delay to avoid busy-waiting
}
// Put device to deep sleep after data was sent successfully or timeout
Serial.println("Going to sleep now");
esp_deep_sleep_start();
// delay(1000);
// Serial.println("\n---------------Next cycle-----------------");
}
/********************* Arduino functions **************************/
void setup() {
Serial.println("---------------Setup begin-----------------");
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor_Node23");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Set up analog input
// zbAnalogPercent.addAnalogInput();
// zbAnalogPercent.setAnalogInputApplication(ESP_ZB_ZCL_AI_PERCENTAGE_OTHER);
// zbAnalogPercent.setAnalogInputDescription("Percentage");
// zbAnalogPercent.setAnalogInputResolution(0.01);
// Set callback for default response to handle status of reported data, there are 2 options.
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
// Global callback for all endpoints with more params to determine the endpoint and cluster in the callback function.
Zigbee.onGlobalDefaultResponse(onGlobalResponse);
#else
// Callback specified for endpoint
zbTempSensor.onDefaultResponse(onResponse);
#endif
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Zigbee.addEndpoint(&zbAnalogPercent);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart(); // If Zigbee failed to start, reboot the device and try again
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Successfully connected to Zigbee network");
// Start Temperature sensor reading task
xTaskCreate(meausureAndSleep, "temp_sensor_update", 2048, NULL, 10, NULL);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 10000) {
// If key pressed for more than 10secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
// Optional set reset in factoryReset to false, to not restart device after erasing nvram, but set it to endless sleep manually instead
Zigbee.factoryReset(false);
Serial.println("Going to endless sleep, press RESET button or power off/on the device to wake up");
esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
esp_deep_sleep_start();
}
}
}
delay(100);
}

View File

@ -0,0 +1,75 @@
# Arduino-ESP32 Zigbee Temperature and Humidity Sensor Sleepy Device Example
This example demonstrates how to use the Zigbee library to create an end device temperature/humidity sensor and use it as a Home Automation (HA) extended temperature sensor.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Temperature Sensor Functions
1. Initialize a Zigbee temperature and humidity sensor.
2. Measure temperature and humidity values.
3. Report the measured values to the Zigbee network.
4. Put the device to sleep to save power.
## Hardware Required
* ESP32-H2 or ESP32-C6 development board
* A USB cable for power supply and programming
### Configure the Project
In this example, to demonstrate the functionality the chip temperature is used and reported as temperature and humidity.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,209 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates simple Zigbee light bulb.
*
* The example demonstrates how to use Zigbee library to create a end device light bulb.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#define DEBUG_TRACE
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_LIGHT_ENDPOINT 10
//uint8_t led = LED_BUILTIN;
uint8_t led = 6;
uint8_t button = BOOT_PIN;
#define LED_PIN 15 // Onboard LED
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/********************* RGB LED functions **************************/
void setLED(bool value) {
Serial.println("setLED called");
digitalWrite(LED_PIN, value);
digitalWrite(led, value);
}
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
// Read temperature sensor value
// float tsens_value = temperatureRead();
float tsens_value = random(180, 300) / 10.0;
// Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// Update temperature value in Temperature sensor EP
zbTempSensor.setTemperature(tsens_value);
float humidity = random(300, 700) / 10.0;
zbTempSensor.setHumidity(humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature;
// Simulate temperature between 18.0°C and 30.0°C
float temperature = random(180, 300) / 10.0;
// Simulate humidity between 30.0% and 70.0%
float humidity = random(300, 700) / 10.0;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Add small delay to allow the data to be sent before going to sleep
delay(100);
// Put device to deep sleep
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
//Optional: set Zigbee device name and model
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
// Set callback function for light change
zbLight.onLightChange(setLED);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbLight);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
// if (!Zigbee.begin(&zigbeeConfig, false)) {
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 15, 0);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
zbLight.setLight(!zbLight.getLightState());
// Call the function to measure temperature and put the device to sleep
// meausureAndSleep();
// zbTempSensor.reportTemperature();
zbTempSensor.report();
}
delay(100);
}

View File

@ -0,0 +1,70 @@
# Arduino-ESP32 Zigbee On/Off Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) on/off light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with Zigbee_On_Off_switch example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_On_Off_Light example
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

73
SHT31test/SHT31test.ino Normal file
View File

@ -0,0 +1,73 @@
/***************************************************
This is an example for the SHT31-D Humidity & Temp Sensor
Designed specifically to work with the SHT31-D sensor from Adafruit
----> https://www.adafruit.com/products/2857
These sensors use I2C to communicate, 2 pins are required to
interface
****************************************************/
#include <Arduino.h>
#include <Wire.h>
#include "Adafruit_SHT31.h"
bool enableHeater = false;
uint8_t loopCnt = 0;
Adafruit_SHT31 sht31 = Adafruit_SHT31();
void setup() {
Serial.begin(9600);
Serial.println("Start");
while (!Serial)
delay(10); // will pause Zero, Leonardo, etc until serial console opens
Serial.println("SHT31 test");
if (! sht31.begin(0x44)) { // Set to 0x45 for alternate i2c addr
Serial.println("Couldn't find SHT31");
while (1) delay(1);
}
Serial.print("Heater Enabled State: ");
if (sht31.isHeaterEnabled())
Serial.println("ENABLED");
else
Serial.println("DISABLED");
}
void loop() {
float t = sht31.readTemperature();
float h = sht31.readHumidity();
if (! isnan(t)) { // check if 'is not a number'
Serial.print("Temp *C = "); Serial.print(t); Serial.print("\t\t");
} else {
Serial.println("Failed to read temperature");
}
if (! isnan(h)) { // check if 'is not a number'
Serial.print("Hum. % = "); Serial.println(h);
} else {
Serial.println("Failed to read humidity");
}
delay(1000);
// Toggle heater enabled state every 30 seconds
// An ~3.0 degC temperature increase can be noted when heater is enabled
if (loopCnt >= 30) {
enableHeater = !enableHeater;
sht31.heater(enableHeater);
Serial.print("Heater Enabled State: ");
if (sht31.isHeaterEnabled())
Serial.println("ENABLED");
else
Serial.println("DISABLED");
loopCnt = 0;
}
loopCnt++;
}

View File

@ -0,0 +1,63 @@
// https://www.14core.com/wiring-the-telaire-t6713-t67xx-a-carbon-dioxide-co2-sensor/
#include <Wire.h> //Import Arduino Wire Library
#define T6713_Address 0x15 //T6713 i2C Address
int data [4];
int ppmValue;
void setup(){
Serial.begin(9600);
Serial.println("14CORE | T67XX / AN161 CO2 Sensor Test Code");
Serial.println("Initializing...............................");
delay(2000);
Serial.println("Starting i2C Communicate ADDR 0x15.........");
Wire.begin(8, 14);
delay(1000);
}
void loop(){
int ppmValue = readSensor();
if (ppmValue >= 0) {
Serial.println("CO2-Carbon Dioxide Read Value > ");
Serial.println(ppmValue);
} else {
Serial.println("ERROR | Failed to communicate to the sensor");
}
delay(2000);
}
int readSensor(){
Wire.beginTransmission(T6713_Address);
Wire.write(0x04);
Wire.write(0x13);
Wire.write(0x8B);
Wire.write(0x00);
Wire.write(0x01);
Wire.endTransmission();
delay(2000);
Wire.requestFrom(T6713_Address, 4); //Request 4 bytes from the sensor
data[0] = Wire.read();
data[1] = Wire.read();
data[2] = Wire.read();
data[3] = Wire.read();
Serial.println("FUNCTION CODE >");
Serial.println(data[0], HEX);
Serial.println("");
Serial.println("BYTE COUNT > ");
Serial.println(data[1], HEX);
Serial.println("");
Serial.println("MOST SIGNIFICANT BIT > 0x");
Serial.println(data[2],HEX);
Serial.println("");
Serial.println("LEAST SIGNIFICANT BIT > 0x");
Serial.println(data[3],HEX);
ppmValue = (((data[2] & 0x3F ) << 8) | data[3]);
Serial.println("CO2-Carbon Dioxide Read Value > ");
Serial.println(ppmValue);
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,157 @@
// https://www.waveshare.com/product/1.54inch-e-paper-module-b.htm
// https://www.waveshare.com/wiki/1.54inch_e-Paper_Module_(B)_Manual#ESP32.2F8266
// https://learn.adafruit.com/adafruit-1-54-eink-display-breakouts/arduino-usage
/***************************************************
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
Written by Limor Fried/Ladyada for Adafruit Industries.
MIT license, all text above must be included in any redistribution
****************************************************/
#include "Adafruit_ThinkInk.h"
#ifdef ARDUINO_ADAFRUIT_FEATHER_RP2040_THINKINK // detects if compiling for
// Feather RP2040 ThinkInk
#define EPD_DC PIN_EPD_DC // ThinkInk 24-pin connector DC
#define EPD_CS PIN_EPD_CS // ThinkInk 24-pin connector CS
#define EPD_BUSY PIN_EPD_BUSY // ThinkInk 24-pin connector Busy
#define SRAM_CS -1 // use onboard RAM
#define EPD_RESET PIN_EPD_RESET // ThinkInk 24-pin connector Reset
#define EPD_SPI &SPI1 // secondary SPI for ThinkInk
#else
#define EPD_DC 16
#define EPD_CS 17
#define EPD_BUSY 22 // can set to -1 to not use a pin (will wait a fixed delay)
#define SRAM_CS -1
#define EPD_RESET 23 // can set to -1 and share with microcontroller Reset!
#define EPD_SPI &SPI // primary SPI
#endif
// 1.54" 152x152 Tricolor EPD with ILI0373 chipset
// ThinkInk_154_Tricolor_Z17 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 1.54" 152x152 Tricolor EPD with SSD1680 chipset
// ThinkInk_154_Tricolor_RW display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 1.54" 200x200 Tricolor EPD with SSD1681 chipset
ThinkInk_154_Tricolor_Z90 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
EPD_BUSY, EPD_SPI);
// 2.13" Tricolor EPD with SSD1680 chipset
// ThinkInk_213_Tricolor_RW display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS, EPD_BUSY,
// EPD_SPI);
// 2.13" Tricolor EPD with IL0373 chipset
// ThinkInk_213_Tricolor_Z16 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 2.7" Tricolor Featherwing or Breakout with IL91874 chipset
// ThinkInk_270_Tricolor_C44 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 2.7" Tricolor Featherwing or Breakout with EK79686 chipset
// ThinkInk_270_Tricolor_Z70 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 2.9" Tricolor Featherwing or Breakout with IL0373 chipset
// ThinkInk_290_Tricolor_Z10 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 2.9" Tricolor Featherwing or Breakout with UC8151D chipset
// ThinkInk_290_Tricolor_Z13 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// 2.9" Tricolor Featherwing or Breakout with SSD1680 chipset and negative
// offset
// ThinkInk_290_Tricolor_Z94 display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI);
// ThinkInk_420_Tricolor_RW display(EPD_DC, EPD_RESET, EPD_CS, SRAM_CS,
// EPD_BUSY, EPD_SPI); ThinkInk_420_Tricolor_Z21 display(EPD_DC, EPD_RESET,
// EPD_CS, SRAM_CS, EPD_BUSY, EPD_SPI);
void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10);
}
Serial.println("Adafruit EPD full update test in red/black/white");
display.begin(THINKINK_TRICOLOR);
display.setRotation(2);
// 0: Default (0°)
// 1: Rotate 90° clockwise
// 2: Rotated 180°
// 3: Rotated 270° clockwise
}
void loop() {
Serial.println("Banner demo");
display.clearBuffer();
display.setTextSize(3);
display.setCursor((display.width() - 144) / 2, (display.height() - 24) / 2);
display.setTextColor(EPD_BLACK);
display.print("Tri");
display.setTextColor(EPD_RED);
display.print("Color");
display.display(); // refresh the screen
display.powerDown(); // save power
delay(15000);
Serial.println("Color rectangle demo");
display.clearBuffer();
display.fillRect(display.width() / 3, 0, display.width() / 3,
display.height(), EPD_BLACK);
display.fillRect((display.width() * 2) / 3, 0, display.width() / 3,
display.height(), EPD_RED);
display.display(); // refresh the screen
display.powerDown(); // save power
delay(15000);
Serial.println("Text demo");
// large block of text
display.clearBuffer();
display.setTextSize(1);
testdrawtext(
"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur "
"adipiscing ante sed nibh tincidunt feugiat. Maecenas enim massa, "
"fringilla sed malesuada et, malesuada sit amet turpis. Sed porttitor "
"neque ut ante pretium vitae malesuada nunc bibendum. Nullam aliquet "
"ultrices massa eu hendrerit. Ut sed nisi lorem. In vestibulum purus a "
"tortor imperdiet posuere. ",
EPD_BLACK);
display.display(); // refresh the screen
display.powerDown(); // save power
delay(15000);
display.clearBuffer();
for (int16_t i = 0; i < display.width(); i += 4) {
display.drawLine(0, 0, i, display.height() - 1, EPD_BLACK);
}
for (int16_t i = 0; i < display.height(); i += 4) {
display.drawLine(display.width() - 1, 0, 0, i, EPD_RED);
}
display.display(); // refresh the screen
display.powerDown(); // save power
delay(15000);
}
void testdrawtext(const char *text, uint16_t color) {
display.setCursor(0, 0);
display.setTextColor(color);
display.setTextWrap(true);
display.print(text);
}

View File

@ -0,0 +1,72 @@
# Arduino-ESP32 Carbon dioxide (CO2) Sensor Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) simple sensor device type with carbon dioxide measuring.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Pressure + Flow Sensor Functions
* After this board first starts up, it would be configured locally to report the carbon dioxide on every 30 seconds.
* By clicking the button (BOOT) on this board, this board will immediately send a report of the current measured carbon dioxide to the network.
## Hardware Required
* A USB cable for power supply and programming
### Configure the Project
In this example, the internal temperature sensor is used to demonstrate reading of the carbon dioxide sensors.
Set the Button GPIO by changing the `button` variable. By default, it's the pin `BOOT_PIN` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,109 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee carbon dioxide sensor.
*
* The example demonstrates how to use Zigbee library to create a end device carbon dioxide sensor.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee carbon dioxide sensor configuration */
#define CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER 10
uint8_t button = BOOT_PIN;
ZigbeeCarbonDioxideSensor zbCarbonDioxideSensor = ZigbeeCarbonDioxideSensor(CARBON_DIOXIDE_SENSOR_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Optional: set Zigbee device name and model
zbCarbonDioxideSensor.setManufacturerAndModel("Espressif", "ZigbeeCarbonDioxideSensor");
// Set minimum and maximum carbon dioxide measurement value in ppm
zbCarbonDioxideSensor.setMinMaxValue(0, 1500);
// Add endpoints to Zigbee Core
Zigbee.addEndpoint(&zbCarbonDioxideSensor);
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000; // 30 seconds
Serial.println("Starting Zigbee...");
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
} else {
Serial.println("Zigbee started successfully!");
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Set reporting interval for carbon dioxide measurement to be done every 30 seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (carbon dioxide change in ppm)
// if min = 1 and max = 0, reporting is sent only when carbon dioxide changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or when carbon dioxide changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of delta change
zbCarbonDioxideSensor.setReporting(0, 30, 0);
}
void loop() {
static uint32_t timeCounter = 0;
// Read carbon dioxide sensor every 2s
if (!(timeCounter++ % 20)) { // delaying for 100ms x 20 = 2s
// Read sensor value - here is chip temperature used + 300 as a dummy value for demonstration
uint16_t carbon_dioxide_value = 300 + (uint16_t)temperatureRead();
Serial.printf("Updating carbon dioxide sensor value to %d ppm\r\n", carbon_dioxide_value);
zbCarbonDioxideSensor.setCarbonDioxide(carbon_dioxide_value);
}
// Checking button for factory reset and reporting
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
zbCarbonDioxideSensor.report();
}
delay(100);
}

View File

@ -0,0 +1,6 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y"
]
}

View File

@ -0,0 +1,8 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
]
}

View File

@ -0,0 +1,70 @@
# Arduino-ESP32 Zigbee On/Off Light Example
This example shows how to configure the Zigbee end device and use it as a Home Automation (HA) on/off light.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Hardware Required
* One development board (ESP32-H2 or ESP32-C6) acting as Zigbee coordinator (loaded with Zigbee_On_Off_switch example)
* A USB cable for power supply and programming
* Choose another board (ESP32-H2 or ESP32-C6) as Zigbee end device and upload the Zigbee_On_Off_Light example
### Configure the Project
Set the LED GPIO by changing the `LED_PIN` definition. By default, the LED_PIN is `RGB_BUILTIN`.
By default, the `rgbLedWrite` function is used to control the LED. You can change it to digitalWrite to control a simple LED.
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,202 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates simple Zigbee light bulb.
*
* The example demonstrates how to use Zigbee library to create a end device light bulb.
* The light bulb is a Zigbee end device, which is controlled by a Zigbee coordinator.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee light bulb configuration */
#define ZIGBEE_LIGHT_ENDPOINT 10
//uint8_t led = LED_BUILTIN;
uint8_t led = 6;
uint8_t button = BOOT_PIN;
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
ZigbeeLight zbLight = ZigbeeLight(ZIGBEE_LIGHT_ENDPOINT);
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/********************* RGB LED functions **************************/
void setLED(bool value) {
// Serial.println("setLED called");
digitalWrite(led, value);
}
/************************ Temp sensor *****************************/
static void temp_sensor_value_update(void *arg) {
for (;;) {
// Read temperature sensor value
// float tsens_value = temperatureRead();
float tsens_value = random(180, 300) / 10.0;
// Serial.printf("Updated temperature sensor value to %.2f°C\r\n", tsens_value);
// Update temperature value in Temperature sensor EP
zbTempSensor.setTemperature(tsens_value);
float humidity = random(300, 700) / 10.0;
zbTempSensor.setHumidity(humidity);
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", tsens_value, humidity);
delay(1000);
}
}
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature;
// Simulate temperature between 18.0°C and 30.0°C
float temperature = random(180, 300) / 10.0;
// Simulate humidity between 30.0% and 70.0%
float humidity = random(300, 700) / 10.0;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Add small delay to allow the data to be sent before going to sleep
delay(100);
// Put device to deep sleep
// Serial.println("Going to sleep now");
// esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init LED and turn it OFF (if LED_PIN == RGB_BUILTIN, the rgbLedWrite() will be used under the hood)
pinMode(led, OUTPUT);
digitalWrite(led, LOW);
// Init button for factory reset
pinMode(button, INPUT_PULLUP);
//Optional: set Zigbee device name and model
zbLight.setManufacturerAndModel("Espressif", "ZBLightBulb");
// Set callback function for light change
zbLight.onLightChange(setLED);
//Add endpoint to Zigbee Core
Serial.println("Adding ZigbeeLight endpoint to Zigbee Core");
Zigbee.addEndpoint(&zbLight);
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery, battery percentage and battery voltage (now 100% and 3.5V for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) or zbTempSensor.setBatteryVoltage(voltage) anytime after Zigbee.begin()
// zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// For battery powered devices, it can be better to set timeout for Zigbee Begin to lower value to save battery
// If the timeout has been reached, the network channel mask will be reset and the device will try to connect again after reset (scanning all channels)
Zigbee.setTimeout(10000); // Set timeout for Zigbee Begin to 10s (default is 30s)
// When all EPs are registered, start Zigbee. By default acts as ZIGBEE_END_DEVICE
// if (!Zigbee.begin(&zigbeeConfig, false)) {
if (!Zigbee.begin()) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
// Start Temperature sensor reading task
xTaskCreate(temp_sensor_value_update, "temp_sensor_update", 2048, NULL, 10, NULL);
// Set reporting interval for temperature measurement in seconds, must be called after Zigbee.begin()
// min_interval and max_interval in seconds, delta (temp change in 0,1 °C)
// if min = 1 and max = 0, reporting is sent only when temperature changes by delta
// if min = 0 and max = 10, reporting is sent every 10 seconds or temperature changes by delta
// if min = 0, max = 10 and delta = 0, reporting is sent every 10 seconds regardless of temperature change
// zbTempSensor.setReporting(1, 0, 1);
zbTempSensor.setReporting(0, 10, 0);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
// Toggle light by pressing the button
zbLight.setLight(!zbLight.getLightState());
// Call the function to measure temperature and put the device to sleep
// meausureAndSleep();
// zbTempSensor.reportTemperature();
zbTempSensor.report();
}
delay(100);
}

View File

@ -0,0 +1,7 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y",
"CONFIG_ZB_ENABLED=y"
]
}

View File

@ -0,0 +1,75 @@
# Arduino-ESP32 Zigbee Temperature and Humidity Sensor Sleepy Device Example
This example demonstrates how to use the Zigbee library to create an end device temperature/humidity sensor and use it as a Home Automation (HA) extended temperature sensor.
# Supported Targets
Currently, this example supports the following targets.
| Supported Targets | ESP32-C6 | ESP32-H2 |
| ----------------- | -------- | -------- |
## Temperature Sensor Functions
1. Initialize a Zigbee temperature and humidity sensor.
2. Measure temperature and humidity values.
3. Report the measured values to the Zigbee network.
4. Put the device to sleep to save power.
## Hardware Required
* ESP32-H2 or ESP32-C6 development board
* A USB cable for power supply and programming
### Configure the Project
In this example, to demonstrate the functionality the chip temperature is used and reported as temperature and humidity.
Set the Button GPIO by changing the `BUTTON_PIN` definition. By default, it's the pin `9` (BOOT button on ESP32-C6 and ESP32-H2).
#### Using Arduino IDE
To get more information about the Espressif boards see [Espressif Development Kits](https://www.espressif.com/en/products/devkits).
* Before Compile/Verify, select the correct board: `Tools -> Board`.
* Select the End device Zigbee mode: `Tools -> Zigbee mode: Zigbee ED (end device)`
* Select Partition Scheme for Zigbee: `Tools -> Partition Scheme: Zigbee 4MB with spiffs`
* Select the COM port: `Tools -> Port: xxx` where the `xxx` is the detected COM port.
* Optional: Set debug level to verbose to see all logs from Zigbee stack: `Tools -> Core Debug Level: Verbose`.
## Troubleshooting
If the End device flashed with this example is not connecting to the coordinator, erase the flash of the End device before flashing the example to the board. It is recommended to do this if you re-flash the coordinator.
You can do the following:
* In the Arduino IDE go to the Tools menu and set `Erase All Flash Before Sketch Upload` to `Enabled`.
* Add to the sketch `Zigbee.factoryReset();` to reset the device and Zigbee stack.
By default, the coordinator network is closed after rebooting or flashing new firmware.
To open the network you have 2 options:
* Open network after reboot by setting `Zigbee.setRebootOpenNetwork(time);` before calling `Zigbee.begin();`.
* In application you can anytime call `Zigbee.openNetwork(time);` to open the network for devices to join.
***Important: Make sure you are using a good quality USB cable and that you have a reliable power source***
* **LED not blinking:** Check the wiring connection and the IO selection.
* **Programming Fail:** If the programming/flash procedure fails, try reducing the serial connection speed.
* **COM port not detected:** Check the USB cable and the USB to Serial driver installation.
If the error persists, you can ask for help at the official [ESP32 forum](https://esp32.com) or see [Contribute](#contribute).
## Contribute
To know how to contribute to this project, see [How to contribute.](https://github.com/espressif/arduino-esp32/blob/master/CONTRIBUTING.rst)
If you have any **feedback** or **issue** to report on this example/library, please open an issue or fix it by creating a new PR. Contributions are more than welcome!
Before creating a new issue, be sure to try Troubleshooting and check if the same issue was already created by someone else.
## Resources
* Official ESP32 Forum: [Link](https://esp32.com)
* Arduino-ESP32 Official Repository: [espressif/arduino-esp32](https://github.com/espressif/arduino-esp32)
* ESP32-C6 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
* ESP32-H2 Datasheet: [Link to datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-h2_datasheet_en.pdf)
* Official ESP-IDF documentation: [ESP-IDF](https://idf.espressif.com)

View File

@ -0,0 +1,132 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee coordinator mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#define BUTTON_PIN 9 //Boot button for C6/H2
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 55 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/************************ Temp sensor *****************************/
void meausureAndSleep() {
//log_d("Teeeeeest2");
// Measure temperature sensor value
float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
//float humidity = temperature;
//float humidity = analogRead(10); // or digitalRead(GPIO_PIN)
float humidity = 50;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.reportTemperature();
zbTempSensor.reportHumidity();
log_d("Temperature: %.2f°C, Humidity: %.2f%", temperature, humidity);
// Put device to deep sleep
esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
// Init button switch
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensorTest");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(1);
// Set power source to battery and set battery percentage to measured value (now 100% for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) anytime
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
//log_d("Teeeeeest1");
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// When all EPs are registered, start Zigbee in End Device mode
Zigbee.begin(&zigbeeConfig, false);
// Wait for Zigbee to start
while (!Zigbee.isStarted()) {
delay(100);
}
// Delay 5s to allow establishing connection with coordinator, needed for sleepy devices
delay(5000);
}
void loop() {
// Checking button for factory reset
if (digitalRead(BUTTON_PIN) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(BUTTON_PIN) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Zigbee.factoryReset();
}
}
}
// Call the function to measure temperature and put the device to sleep
meausureAndSleep();
}

View File

@ -0,0 +1,6 @@
{
"fqbn_append": "PartitionScheme=zigbee,ZigbeeMode=ed",
"requires": [
"CONFIG_SOC_IEEE802154_SUPPORTED=y"
]
}

View File

@ -0,0 +1,139 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 55 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
uint8_t button = BOOT_PIN;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/************************ Temp sensor *****************************/
void meausureAndSleep() {
// Measure temperature sensor value
float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
float humidity = temperature * 2;
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Add small delay to allow the data to be sent before going to sleep
delay(100);
// Put device to deep sleep
Serial.println("Going to sleep now");
esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensorTest");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(0.1);
// Set power source to battery and set battery percentage to measured value (now 100% for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) anytime
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 99);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Successfully connected to Zigbee network");
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
// Call the function to measure temperature and put the device to sleep
meausureAndSleep();
}

View File

@ -0,0 +1,29 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
{
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"name": "Debug with JLink",
"request": "launch",
"type": "cortex-debug",
"device": "",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"servertype": "jlink"
},
{
"cwd": "${workspaceFolder}",
"executable": "./bin/executable.elf",
"name": "Debug with JLink",
"request": "launch",
"type": "cortex-debug",
"device": "",
"runToEntryPoint": "main",
"showDevDebugOutput": "none",
"servertype": "jlink"
}
]
}

View File

@ -0,0 +1,164 @@
// Copyright 2024 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.
/**
* @brief This example demonstrates Zigbee temperature and humidity sensor Sleepy device.
*
* The example demonstrates how to use Zigbee library to create an end device temperature and humidity sensor.
* The sensor is a Zigbee end device, which is reporting data to the Zigbee network.
*
* Proper Zigbee mode must be selected in Tools->Zigbee mode
* and also the correct partition scheme must be selected in Tools->Partition Scheme.
*
* Please check the README.md for instructions and more detailed description.
*
* Created by Jan Procházka (https://github.com/P-R-O-C-H-Y/)
*/
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
#include "Zigbee.h"
#include "DHT.h"
#define DHTPIN 17 // Digital pin connected to the DHT sensor
//https://github.com/adafruit/DHT-sensor-library/blob/master/examples/DHTtester/DHTtester.ino
#define DHTTYPE DHT11 // DHT 11
DHT dht(DHTPIN, DHTTYPE);
/* Zigbee temperature + humidity sensor configuration */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
#define TIME_TO_SLEEP 5 /* Sleep for 55s will + 5s delay for establishing connection => data reported every 1 minute */
uint8_t button = BOOT_PIN;
ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
/************************ Temp sensor *****************************/
void meausureAndSleep() {
// Measure temperature sensor value
// float temperature = temperatureRead();
// Use temperature value as humidity value to demonstrate both temperature and humidity
// float humidity = temperature * 2;
// Use DHT11 sensor
// Read temperature as Celsius (the default)
float temperature = dht.readTemperature();
// Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
float humidity = dht.readHumidity();
//float humidity = 43;
// Check if any reads failed and exit early (to try again).
if (isnan(humidity) || isnan(temperature)) {
Serial.println(F("Failed to read from DHT sensor!"));
return;
}
delay(100);
// Update temperature and humidity values in Temperature sensor EP
zbTempSensor.setTemperature(temperature);
zbTempSensor.setHumidity(humidity);
// Report temperature and humidity values
zbTempSensor.report();
Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);
// Add small delay to allow the data to be sent before going to sleep
delay(100);
// Put device to deep sleep
Serial.println("Going to sleep now");
//esp_deep_sleep_start();
}
/********************* Arduino functions **************************/
void setup() {
Serial.begin(115200);
// Init button switch
pinMode(button, INPUT_PULLUP);
// Configure the wake up source and set to wake up every 5 seconds
esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);
// Optional: set Zigbee device name and model
zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor_Node14");
// Set minimum and maximum temperature measurement value (10-50°C is default range for chip temperature measurement)
zbTempSensor.setMinMaxValue(10, 50);
// Set tolerance for temperature measurement in °C (lowest possible value is 0.01°C)
zbTempSensor.setTolerance(0.1);
// Set power source to battery and set battery percentage to measured value (now 100% for demonstration)
// The value can be also updated by calling zbTempSensor.setBatteryPercentage(percentage) anytime
zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 99);
// Add humidity cluster to the temperature sensor device with min, max and tolerance values
zbTempSensor.addHumiditySensor(0, 100, 1);
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbTempSensor);
// Create a custom Zigbee configuration for End Device with keep alive 10s to avoid interference with reporting data
esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;
// When all EPs are registered, start Zigbee in End Device mode
if (!Zigbee.begin(&zigbeeConfig, false)) {
Serial.println("Zigbee failed to start!");
Serial.println("Rebooting...");
ESP.restart();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
Serial.println("Successfully connected to Zigbee network");
dht.begin();
Serial.println(F("DHTxx test!"));
// Delay approx 1s (may be adjusted) to allow establishing proper connection with coordinator, needed for sleepy devices
delay(1000);
}
void loop() {
// Checking button for factory reset
if (digitalRead(button) == LOW) { // Push button pressed
// Key debounce handling
delay(100);
int startTime = millis();
while (digitalRead(button) == LOW) {
delay(50);
if ((millis() - startTime) > 3000) {
// If key pressed for more than 3secs, factory reset Zigbee and reboot
Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
delay(1000);
Zigbee.factoryReset();
}
}
}
// Call the function to measure temperature and put the device to sleep
meausureAndSleep();
}

Some files were not shown because too many files have changed in this diff Show More