Initial commit of existing code
This commit is contained in:
commit
631e7c590f
|
|
@ -0,0 +1,3 @@
|
||||||
|
*.hex
|
||||||
|
build/
|
||||||
|
.DS_Store
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
#include <Wire.h>
|
||||||
|
|
||||||
|
// Default I2C Address for T6713 is 0x15 (decimal 21)
|
||||||
|
#define T6713_ADDR 0x15
|
||||||
|
#define I2C_SCL_PIN 20 // Change to your SCL pin
|
||||||
|
#define I2C_SDA_PIN 6 // Change to your SDA pin
|
||||||
|
|
||||||
|
#include <bme68xLibrary.h>
|
||||||
|
#ifndef BME68X_I2C_ADDR
|
||||||
|
#define BME68X_I2C_ADDR 0x76 // Change to 0x77 if sensor not found
|
||||||
|
#endif
|
||||||
|
Bme68x bme;
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN); // Join I2C bus
|
||||||
|
|
||||||
|
Serial.println("Initializing Sensors...");
|
||||||
|
|
||||||
|
// 1. Initialize BME680
|
||||||
|
bme.begin(BME68X_I2C_ADDR, Wire);
|
||||||
|
if(bme.checkStatus()) {
|
||||||
|
Serial.println("BME680 Error: " + bme.statusString());
|
||||||
|
if (bme.checkStatus() == BME68X_ERROR) {
|
||||||
|
Serial.println("Check wiring or I2C address (0x76 vs 0x77)");
|
||||||
|
while(1); // Halt if BME fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Serial.println("BME680 Found!");
|
||||||
|
|
||||||
|
// Configure BME680 for "Forced Mode" (On-Demand)
|
||||||
|
bme.setTPH(); // Set default oversampling for Temp, Pres, Hum
|
||||||
|
|
||||||
|
// Configure Heater for Gas Measurement (300°C for 100ms)
|
||||||
|
bme.setHeaterProf(300, 100);
|
||||||
|
|
||||||
|
Serial.println("Sensors Ready. Starting Loop...");
|
||||||
|
delay(2000); // Let sensor warm up
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void loop() {
|
||||||
|
Serial.println("-----------------------------");
|
||||||
|
|
||||||
|
// --- Read T6713 (CO2) ---
|
||||||
|
int co2 = readT6713();
|
||||||
|
if (co2 >= 0) {
|
||||||
|
Serial.print("T6713 | CO2: "); Serial.print(co2); Serial.println(" ppm");
|
||||||
|
} else {
|
||||||
|
Serial.println("T6713 | Read Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Read BME680 (Temp, Hum, Pres, Gas) ---
|
||||||
|
bme68xData data;
|
||||||
|
bme.setOpMode(BME68X_FORCED_MODE); // Trigger measurement
|
||||||
|
|
||||||
|
// Wait for measurement to complete (depends on heater duration)
|
||||||
|
delay(bme.getMeasDur(BME68X_FORCED_MODE) / 1000 + 50);
|
||||||
|
|
||||||
|
if (bme.fetchData()) {
|
||||||
|
bme.getData(data);
|
||||||
|
Serial.print("BME680 | Temp: "); Serial.print(data.temperature); Serial.println(" C");
|
||||||
|
Serial.print("BME680 | Hum: "); Serial.print(data.humidity); Serial.println(" %");
|
||||||
|
Serial.print("BME680 | Pres: "); Serial.print(data.pressure / 100.0); Serial.println(" hPa");
|
||||||
|
Serial.print("BME680 | Gas: "); Serial.print(data.gas_resistance / 1000.0); Serial.println(" kOhm");
|
||||||
|
} else {
|
||||||
|
Serial.println("BME680 | Failed to fetch data");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait 5 seconds before next cycle
|
||||||
|
delay(5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue