// src/main.cpp #ifndef ZIGBEE_MODE_ED #error "Build flag -D ZIGBEE_MODE_ED missing" #endif #include <Arduino.h> #include <Wire.h> #include <Adafruit_BME680.h> #include <Adafruit_NeoPixel.h> #include <cmath> #include "Zigbee.h" // part of Arduino-ESP32 core Zigbee library constexpr uint8_t SDA_PIN = 21; constexpr uint8_t SCL_PIN = 22; constexpr uint8_t BOOT = 9; constexpr uint8_t NEOPIXEL_PIN = 3; constexpr uint8_t NEOPIXEL_COUNT = 1; constexpr uint8_t BME680_ADDRESS = 0x76; constexpr uint16_t SENSOR_PERIOD_MS = 5000; constexpr uint32_t BOOT_HOLD_MS = 3000; constexpr uint32_t IDENTIFY_DURATION_MS = 5000; constexpr uint32_t IDENTIFY_BLINK_INTERVAL_MS = 200; // Use endpoint 1 for simplicity ZigbeeTempSensor tempEp(1); ZigbeePressureSensor pressureEp(2); Adafruit_BME680 bme; Adafruit_NeoPixel statusPixel(NEOPIXEL_COUNT, NEOPIXEL_PIN, NEO_GRB + NEO_KHZ800); static bool bmeReady = false; static volatile bool identifyBlinkActive = false; static volatile bool identifyBlinkReset = false; static volatile uint32_t identifyBlinkDeadline = 0; static void sensor_task(void *arg) { bool bmeWarned = false; for (;;) { if (bmeReady) { if (bme.performReading()) { float tC = bme.temperature; float humidity = bme.humidity; float pressure_hPa = bme.pressure / 100.0f; tempEp.setTemperature(tC); tempEp.setHumidity(humidity); tempEp.report(); // report temp (and humidity) int16_t pressureValue = static_cast<int16_t>(roundf(pressure_hPa)); pressureEp.setPressure(pressureValue); pressureEp.report(); Serial.printf("Env: %.2f °C, %.2f %%RH, %.2f hPa\n", tC, humidity, pressure_hPa); } else { Serial.println("BME680 read failed"); } } else { if (!bmeWarned) { Serial.println("BME680 not initialized"); bmeWarned = true; } } vTaskDelay(pdMS_TO_TICKS(SENSOR_PERIOD_MS)); } } static void handleIdentifyCallback(uint16_t seconds) { if (seconds == 0) { identifyBlinkActive = false; identifyBlinkReset = true; return; } identifyBlinkDeadline = millis() + IDENTIFY_DURATION_MS; identifyBlinkReset = true; identifyBlinkActive = true; } void setup() { Serial.begin(115200); pinMode(BOOT, INPUT_PULLUP); Wire.begin(SDA_PIN, SCL_PIN); statusPixel.begin(); statusPixel.setBrightness(32); statusPixel.clear(); statusPixel.show(); if (bme.begin(BME680_ADDRESS)) { bmeReady = true; bme.setTemperatureOversampling(BME680_OS_8X); bme.setHumidityOversampling(BME680_OS_2X); bme.setPressureOversampling(BME680_OS_4X); bme.setIIRFilterSize(BME680_FILTER_SIZE_3); bme.setGasHeater(0, 0); // disable gas heater to reduce power draw Serial.println("BME680 initialized"); } else { Serial.println("BME680 not found"); } // (Optional) identify string shown by many hubs tempEp.setManufacturerAndModel("Sparrow", "C6-Temp"); pressureEp.setManufacturerAndModel("Sparrow", "C6-Pressure"); // Reasonable range/tolerance and reporting policy tempEp.setMinMaxValue(-40, 125); tempEp.setTolerance(0.1); // 0.1 °C tolerance tempEp.addHumiditySensor(0.0f, 100.0f, 1.0f); pressureEp.setMinMaxValue(300, 1100); pressureEp.setTolerance(1); tempEp.onIdentify(handleIdentifyCallback); pressureEp.onIdentify(handleIdentifyCallback); // Register endpoint and start Zigbee stack as End Device Zigbee.addEndpoint(&tempEp); Zigbee.addEndpoint(&pressureEp); Serial.println("Starting Zigbee…"); if (!Zigbee.begin()) { Serial.println("Zigbee failed to start, restarting…"); delay(1000); ESP.restart(); } Serial.print("Joining network"); while (!Zigbee.connected()) { Serial.print("."); delay(200); } Serial.println("\nJoined!"); // min=0, max=30 → periodic every 30s or when delta triggers (delta in 0.1°C units here) tempEp.setReporting(0, 30, 1); tempEp.setHumidityReporting(0, 60, 1.0f); pressureEp.setReporting(0, 60, 1); // Start periodic temperature task xTaskCreate(sensor_task, "sensor_task", 4096, nullptr, 10, nullptr); } void loop() { static uint32_t lastBlinkToggle = 0; static bool pixelOn = false; static uint32_t bootPressStart = 0; static bool bootResetIssued = false; if (identifyBlinkReset) { identifyBlinkReset = false; lastBlinkToggle = 0; pixelOn = false; statusPixel.clear(); statusPixel.show(); } bool blinkActive = identifyBlinkActive; if (blinkActive) { uint32_t now = millis(); if ((int32_t)(identifyBlinkDeadline - now) <= 0) { identifyBlinkActive = false; blinkActive = false; } else if (now - lastBlinkToggle >= IDENTIFY_BLINK_INTERVAL_MS) { lastBlinkToggle = now; pixelOn = !pixelOn; if (pixelOn) { statusPixel.setPixelColor(0, statusPixel.Color(0, 0, 255)); } else { statusPixel.clear(); } statusPixel.show(); } } if (!blinkActive && pixelOn) { pixelOn = false; statusPixel.clear(); statusPixel.show(); } // Hold BOOT button ~3s to factory-reset Zigbee if you need to re-pair bool bootLow = digitalRead(BOOT) == LOW; if (bootLow) { uint32_t now = millis(); if (bootPressStart == 0) { bootPressStart = now; } else if (!bootResetIssued && (now - bootPressStart) >= BOOT_HOLD_MS) { bootResetIssued = true; identifyBlinkActive = false; identifyBlinkReset = true; statusPixel.clear(); statusPixel.show(); Serial.println("Factory reset Zigbee…"); Zigbee.factoryReset(true); // resets and restarts } } else { bootPressStart = 0; bootResetIssued = false; } delay(50); }