Differences

This shows you the differences between two versions of the page.

Link to this comparison view

iothings:laboratoare:2025_code:lab5_3 [2025/10/27 21:09] (current)
dan.tudose created
Line 1: Line 1:
 +<code C main.cpp>​
 +// Sparrow Zigbee nodes: switch (end device) and light (router)
 + 
 +#include <​Arduino.h>​
 +#include <​esp_sleep.h>​
 +#include "​driver/​gpio.h"​
 +#include "​Zigbee.h"​
 + 
 +constexpr uint8_t SWITCH_PIN = 9;
 +constexpr uint32_t FACTORY_RESET_HOLD_MS = 3000;
 +constexpr uint32_t BUTTON_DEBOUNCE_MS = 50;
 + 
 +#if defined(SPARROW_SWITCH)
  
 +#include "​ep/​ZigbeeSwitch.h"​
 +#include "​zcl/​esp_zigbee_zcl_command.h"​
 +#include "​zcl/​esp_zigbee_zcl_on_off.h"​
 +
 +constexpr uint8_t SWITCH_ENDPOINT = 1;
 +constexpr uint32_t COMMISSIONING_AWAKE_MS = 15000;
 +constexpr uint32_t POST_COMMAND_HOLD_MS = 250;
 +constexpr uint32_t BUTTON_RELEASE_TIMEOUT_MS = 5000;
 +
 +ZigbeeSwitch switchEp(SWITCH_ENDPOINT);​
 +
 +RTC_DATA_ATTR bool switchIsOn = false;
 +
 +static void waitForButtonRelease() {
 +  uint32_t start = millis();
 +  while (digitalRead(SWITCH_PIN) == LOW) {
 +    uint32_t heldMs = millis() - start;
 +    if (heldMs >= FACTORY_RESET_HOLD_MS) {
 +      Serial.println("​Factory reset Zigbee (switch)..."​);​
 +      Zigbee.factoryReset(true); ​ // does not return
 +    }
 +    if (heldMs >= BUTTON_RELEASE_TIMEOUT_MS) {
 +      break;
 +    }
 +    delay(10);
 +  }
 +}
 +
 +static void enterDeepSleep(uint32_t graceMs = 0) {
 +  if (graceMs > 0) {
 +    delay(graceMs);​
 +  }
 +  Serial.println("​Switch entering deep sleep…"​);​
 +  Serial.flush();​
 +  esp_deep_sleep_start();​
 +}
 +
 +static bool sendOnOffCommand(bool turnOn) {
 +  esp_zb_zcl_on_off_cmd_t cmd_req = {};
 +  cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;​
 +  cmd_req.zcl_basic_cmd.src_endpoint = SWITCH_ENDPOINT;​
 +  cmd_req.on_off_cmd_id = turnOn ? ESP_ZB_ZCL_CMD_ON_OFF_ON_ID : ESP_ZB_ZCL_CMD_ON_OFF_OFF_ID;​
 +
 +  esp_zb_lock_acquire(portMAX_DELAY);​
 +  esp_err_t err = esp_zb_zcl_on_off_cmd_req(&​cmd_req);​
 +  esp_zb_lock_release();​
 +  if (err != ESP_OK) {
 +    Serial.printf("​Failed to send on/off command: 0x%x\n",​ err);
 +    return false;
 +  }
 +  return true;
 +}
 +
 +void setup() {
 +  Serial.begin(115200);​
 +  delay(20);
 +  pinMode(SWITCH_PIN,​ INPUT_PULLUP);​
 +
 +  esp_err_t pdCfg = esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH,​ ESP_PD_OPTION_ON);​
 +  if (pdCfg != ESP_OK) {
 +    Serial.printf("​RTC peripheral power config failed: 0x%x\n",​ pdCfg);
 +  }
 +  esp_err_t wakeCfg = esp_deep_sleep_enable_gpio_wakeup(1ULL << SWITCH_PIN, ESP_GPIO_WAKEUP_GPIO_LOW);​
 +  if (wakeCfg != ESP_OK) {
 +    Serial.printf("​Failed to enable EXT0 wakeup: 0x%x\n",​ wakeCfg);
 +  }
 +
 +  switchEp.setManufacturerAndModel("​Sparrow",​ "​Switch"​);​
 +  switchEp.setPowerSource(ZB_POWER_SOURCE_BATTERY,​ 100);
 +
 +  Zigbee.addEndpoint(&​switchEp);​
 +  Serial.println("​Starting Zigbee (switch)..."​);​
 +  if (!Zigbee.begin()) {
 +    Serial.println("​Zigbee failed to start, restarting…"​);​
 +    delay(1000);​
 +    ESP.restart();​
 +  }
 +
 +  Serial.print("​Joining network"​);​
 +  uint32_t joinStart = millis();
 +  while (!Zigbee.connected()) {
 +    Serial.print("​."​);​
 +    delay(200);
 +    if (millis() - joinStart > 20000) {
 +      Serial.println("​\nJoin timeout, restarting…"​);​
 +      ESP.restart();​
 +    }
 +  }
 +  Serial.println("​\nJoined!"​);​
 +
 +  esp_sleep_wakeup_cause_t wakeCause = esp_sleep_get_wakeup_cause();​
 +  bool wokeFromButton = (wakeCause == ESP_SLEEP_WAKEUP_EXT0);​
 +
 +  if (wokeFromButton) {
 +    switchIsOn = !switchIsOn;​
 +    Serial.printf("​Switch state → %s\n", switchIsOn ? "​ON"​ : "​OFF"​);​
 +    if (sendOnOffCommand(switchIsOn)) {
 +      Serial.println("​On/​Off command sent."​);​
 +    }
 +    waitForButtonRelease();​
 +    enterDeepSleep(POST_COMMAND_HOLD_MS);​
 +  } else {
 +    Serial.println("​Cold boot: staying awake for commissioning window."​);​
 +    delay(COMMISSIONING_AWAKE_MS);​
 +    waitForButtonRelease();​
 +    enterDeepSleep();​
 +  }
 +}
 +
 +void loop() {
 +  enterDeepSleep();​
 +}
 + 
 +#elif defined(SPARROW_LIGHT)
 + 
 +#include <​Adafruit_NeoPixel.h>​
 +#include "​ep/​ZigbeeLight.h"​
 + 
 +constexpr uint8_t LIGHT_ENDPOINT = 1;
 +constexpr uint8_t NEOPIXEL_PIN = 3;
 +constexpr uint8_t NEOPIXEL_COUNT = 1;
 +constexpr uint8_t NEOPIXEL_BRIGHTNESS = 48;
 +constexpr uint32_t IDENTIFY_BLINK_INTERVAL_MS = 250;
 +constexpr uint32_t IDENTIFY_DEFAULT_DURATION_MS = 5000;
 + 
 +ZigbeeLight lightEp(LIGHT_ENDPOINT);​
 +Adafruit_NeoPixel statusPixel(NEOPIXEL_COUNT,​ NEOPIXEL_PIN,​ NEO_GRB + NEO_KHZ800);​
 + 
 +static bool lightState = false;
 +static bool lastButtonLevel = HIGH;
 +static uint32_t lastButtonChangeMs = 0;
 +static uint32_t pressStartMs = 0;
 +static bool longPressHandled = false;
 + 
 +static bool identifyActive = false;
 +static bool identifyBlinkOn = false;
 +static bool identifyReset = false;
 +static uint32_t identifyDeadlineMs = 0;
 +static uint32_t identifyLastToggleMs = 0;
 + 
 +static void refreshNeoPixel() {
 +  if (identifyActive) {
 +    if (identifyBlinkOn) {
 +      statusPixel.setPixelColor(0,​ statusPixel.Color(0,​ 0, 255)); ​ // blue during identify
 +    } else {
 +      statusPixel.clear();​
 +    }
 +  } else {
 +    if (lightState) {
 +      statusPixel.setPixelColor(0,​ statusPixel.Color(255,​ 200, 80));  // warm white
 +    } else {
 +      statusPixel.clear();​
 +    }
 +  }
 +  statusPixel.show();​
 +}
 + 
 +static void handleLightChange(bool on) {
 +  lightState = on;
 +  if (!identifyActive) {
 +    refreshNeoPixel();​
 +  }
 +}
 + 
 +static void handleIdentify(uint16_t seconds) {
 +  if (seconds == 0) {
 +    identifyActive = false;
 +    identifyReset = true;
 +    return;
 +  }
 + 
 +  uint32_t durationMs = seconds > 0 ? static_cast<​uint32_t>​(seconds) * 1000UL : IDENTIFY_DEFAULT_DURATION_MS;​
 +  if (durationMs == 0) {
 +    durationMs = IDENTIFY_DEFAULT_DURATION_MS;​
 +  }
 + 
 +  identifyDeadlineMs = millis() + durationMs;
 +  identifyLastToggleMs = 0;
 +  identifyBlinkOn = false;
 +  identifyActive = true;
 +  identifyReset = true;
 +}
 + 
 +void setup() {
 +  Serial.begin(115200);​
 +  pinMode(SWITCH_PIN,​ INPUT_PULLUP);​
 + 
 +  statusPixel.begin();​
 +  statusPixel.setBrightness(NEOPIXEL_BRIGHTNESS);​
 +  statusPixel.clear();​
 +  statusPixel.show();​
 + 
 +  lightEp.setManufacturerAndModel("​Sparrow",​ "​Light"​);​
 +  lightEp.onLightChange(handleLightChange);​
 +  lightEp.onIdentify(handleIdentify);​
 + 
 +  Zigbee.addEndpoint(&​lightEp);​
 +  Serial.println("​Starting Zigbee (light)..."​);​
 +  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!"​);​
 + 
 +  lightEp.setLight(false);​
 +}
 + 
 +void loop() {
 +  uint32_t now = millis();
 + 
 +  if (identifyReset) {
 +    identifyReset = false;
 +    refreshNeoPixel();​
 +  }
 + 
 +  if (identifyActive) {
 +    if ((int32_t)(identifyDeadlineMs - now) <= 0) {
 +      identifyActive = false;
 +      refreshNeoPixel();​
 +    } else if (identifyLastToggleMs == 0 || (now - identifyLastToggleMs) >= IDENTIFY_BLINK_INTERVAL_MS) {
 +      identifyLastToggleMs = now;
 +      identifyBlinkOn = !identifyBlinkOn;​
 +      refreshNeoPixel();​
 +    }
 +  }
 + 
 +  bool reading = digitalRead(SWITCH_PIN);​
 +  if (reading != lastButtonLevel && (now - lastButtonChangeMs) >= BUTTON_DEBOUNCE_MS) {
 +    lastButtonChangeMs = now;
 +    lastButtonLevel = reading;
 +    if (reading == LOW) {
 +      pressStartMs = now;
 +      longPressHandled = false;
 +    } else {
 +      if (pressStartMs != 0 && !longPressHandled) {
 +        bool newState = !lightState;​
 +        Serial.printf("​Local light toggle → %s\n", newState ? "​ON"​ : "​OFF"​);​
 +        lightEp.setLight(newState);​
 +      }
 +      pressStartMs = 0;
 +    }
 +  }
 + 
 +  if (lastButtonLevel == LOW && pressStartMs != 0 && !longPressHandled && (now - pressStartMs) >= FACTORY_RESET_HOLD_MS) {
 +    longPressHandled = true;
 +    identifyActive = false;
 +    identifyReset = true;
 +    Serial.println("​Factory reset Zigbee (light)..."​);​
 +    Zigbee.factoryReset(true);​
 +  }
 + 
 +  delay(10);
 +}
 + 
 +#else
 + 
 +#error "​Define SPARROW_SWITCH or SPARROW_LIGHT for this firmware."​
 + 
 +#endif
 +
 +</​code>​
iothings/laboratoare/2025_code/lab5_3.txt · Last modified: 2025/10/27 21:09 by dan.tudose
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0