본문 바로가기

ESPRESSIF/ESP32-C6

[ESP32C6 SSM] Zigbee 온습도 장치(SHT20) 제작 및 슬립모드 구현

Zigbee 온도 센서 장치 제작 코드를 이용해서 온습도센서 SHT20의 온습도 데이터를 전송할 수 있는 Zigbee ED 장치를 만들어 보자.

이번에는 상용품에서 사용될 수 있도록 평소에는 슬립모드로 전류소모를 줄이다 특정 시간마다 깨어나서 온습도 정보를 전송할 수 있도록 할 예정이다.

 

온습도 센서는 SHT20 이 실장되어 있는 I2C 온습도 모듈을 이용 하였다.

슬립모드 테스트 예제는 ESP32C6 Deep Sleep Mode Test 예제를 사용하였다.

 

#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif

#include "Zigbee.h"       // ESP Zigbee 라이브러리
#include "uFire_SHT20.h"  // SHT20 온습도 센서 라이브러리

uFire_SHT20 sht20;  // SHT20 센서 객체 생성
#define LED_PIN1    14
#define Led1On()    digitalWrite(LED_PIN1, 0)
#define Led1Off()   digitalWrite(LED_PIN1, 1)


#define USE_GLOBAL_ON_RESPONSE_CALLBACK 1  
// Zigbee 응답 처리 방식 선택 매크로
// 1: 글로벌 콜백(onGlobalResponse) 사용
// 0: 개별 엔드포인트에 onResponse 콜백 지정


/* Zigbee 온습도 센서 엔드포인트 설정 */
#define TEMP_SENSOR_ENDPOINT_NUMBER 10  
// 이 기기(Zigbee End Device)가 사용할 엔드포인트 번호


// --- Deep Sleep 관련 매크로 ---
#define uS_TO_S_FACTOR 1000000ULL  /* 초 → 마이크로초 변환 상수 */
#define TIME_TO_SLEEP  55          /* Deep Sleep 시간 (55초) 
                                    * → 기기 연결/리포트 준비 시간 약 5초 포함 
                                    * → 총 주기 = 60초 (1분마다 데이터 전송) */
#define REPORT_TIMEOUT 1000        /* Coordinator 응답 대기 타임아웃 (ms) */


uint8_t button = BOOT_PIN;  
// Zigbee Factory Reset 용 버튼 핀 (보드 BOOT 버튼과 매핑됨)


ZigbeeTempSensor zbTempSensor = ZigbeeTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
// Zigbee Temperature + Humidity Sensor 클러스터 객체 생성
// (엔드포인트 번호 10번에 등록됨)


uint8_t dataToSend = 2;  
// 보고할 데이터 개수 (온도 1개 + 습도 1개 = 총 2개 값 보고)
// 이 값은 전송 완료 여부 확인에도 사용됨

bool resend = false;  
// 데이터 전송 실패 시 재전송 플래그

void setup() {
  Serial.begin(115200);  // 시리얼 디버깅 시작

  // --- 버튼 초기화 ---
  // 내부 풀업저항을 활성화하여 버튼 입력 설정
  pinMode(button, INPUT_PULLUP);
  pinMode(LED_PIN1, INPUT_PULLUP);
  

  // --- I2C 초기화 및 센서 시작 ---
  Wire.begin();
  sht20.begin();  // SHT20 온습도 센서 초기화

  // --- Deep Sleep 타이머 설정 ---
  // 매 55초마다 기기 깨우기 (TIME_TO_SLEEP 단위: 초, uS_TO_S_FACTOR: us 변환용)
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

  // --- Zigbee Temperature Sensor Endpoint 설정 ---
  zbTempSensor.setManufacturerAndModel("Espressif", "SleepyZigbeeTempSensor");  // 제조사/모델명
  zbTempSensor.setMinMaxValue(10, 50);   // 온도 측정 최소/최대값 (10~50°C)
  zbTempSensor.setTolerance(1);          // 온도 측정 오차 허용치 (±1°C)

  // 전원 공급 방식, 배터리 잔량(%), 배터리 전압(×0.01V 단위)
  // 여기서는 100% 잔량, 3.5V 로 표시 (데모 목적)
  zbTempSensor.setPowerSource(ZB_POWER_SOURCE_BATTERY, 100, 35);

  // 습도 클러스터 추가 (0~100%, 허용 오차 ±1%)
  zbTempSensor.addHumiditySensor(0, 100, 1);

  // --- Zigbee Default Response Callback 설정 ---
#if USE_GLOBAL_ON_RESPONSE_CALLBACK
  // 글로벌 콜백: 엔드포인트/클러스터 정보까지 포함
  Zigbee.onGlobalDefaultResponse(onGlobalResponse);
#else
  // 특정 엔드포인트용 콜백
  zbTempSensor.onDefaultResponse(onResponse);
#endif

  // --- 엔드포인트 Zigbee Core에 등록 ---
  Zigbee.addEndpoint(&zbTempSensor);

  // --- Zigbee End Device 설정 ---
  esp_zb_cfg_t zigbeeConfig = ZIGBEE_DEFAULT_ED_CONFIG();
  zigbeeConfig.nwk_cfg.zed_cfg.keep_alive = 10000;  // Keep-Alive 10초 (리포트 간섭 방지 목적)

  // 배터리 절약을 위해 네트워크 Join 타임아웃을 10초로 줄임 (기본 30초)
  Zigbee.setTimeout(10000);

  // Zigbee End Device 모드 시작
  if (!Zigbee.begin(&zigbeeConfig, false)) {
    Serial.println("Zigbee failed to start!");
    Serial.println("Rebooting...");
    ESP.restart();  // Zigbee 시작 실패 시 재부팅
  }

  // --- 네트워크 연결 대기 ---
  Serial.println("Connecting to network");
  while (!Zigbee.connected()) {
    Serial.print(".");
    delay(100);
  }
  Serial.println();
  Serial.println("Successfully connected to Zigbee network");

  // --- 온습도 측정 및 Deep Sleep 태스크 시작 ---
  xTaskCreate(meausureAndSleep, "temp_sensor_update", 2048, NULL, 10, NULL);
}

void loop() {
  // --- 버튼을 이용한 Zigbee Factory Reset ---
  if (digitalRead(button) == LOW) {  // 버튼 눌림 감지
    delay(100);                      // 디바운스 처리
    int startTime = millis();

    // 버튼이 계속 눌린 상태 감시
    while (digitalRead(button) == LOW) {
      delay(50);
      if ((millis() - startTime) > 10000) {  // 10초 이상 누름
        Serial.println("Resetting Zigbee to factory and rebooting in 1s.");
        delay(1000);

        // Zigbee NVRAM(네트워크 정보) 삭제 → 재시작
        // false 로 설정하면 재시작 대신 무한 Deep Sleep으로 들어감
        Zigbee.factoryReset(false);

        Serial.println("Going to endless sleep, press RESET button or power off/on the device to wake up");

        // 타이머 기반 Wake-up 비활성화 후 Deep Sleep 진입
        esp_sleep_disable_wakeup_source(ESP_SLEEP_WAKEUP_TIMER);
        esp_deep_sleep_start();
      }
    }
  }

  delay(100);  // loop 주기
}

 

 

온습도 데이터 측정은 RTOS로 새로운 타스크를 생성해서 슬립모드에서 께어나 전송 및 전송 상태 확인 하는 코드로 작성하였다.

// 온습도 측정 → Zigbee 전송 → 전송 확인 → Deep Sleep 으로 진입하는 함수
static void meausureAndSleep(void *arg) {
  // --- 1. 센서 측정 ---
  // SHT20 센서로부터 온도/습도 값 측정
  sht20.measure_all();

  float temperature = sht20.tempC;   // 섭씨 온도 값
  float humidity    = sht20.RH;      // 상대 습도 값

  // --- 2. Zigbee 클러스터 업데이트 ---
  // Zigbee Temperature/Humidity Cluster에 최신 측정값 반영
  zbTempSensor.setTemperature(temperature);
  zbTempSensor.setHumidity(humidity);

  // --- 3. 데이터 리포트 전송 ---
  // Zigbee Coordinator/Hub에 측정 데이터 전송
  // (만약 습도 센서가 없는 경우, 온도 값만 전송됨)
  zbTempSensor.report();
  Serial.printf("Reported temperature: %.2f°C, Humidity: %.2f%%\r\n", temperature, humidity);

  // --- 4. 전송 확인 대기 ---
  unsigned long startTime = millis();
  const unsigned long timeout = REPORT_TIMEOUT;  // 리포트 응답 대기 시간

  Serial.printf("Waiting for data report to be confirmed \r\n");

  int tries = 0;           // 재시도 횟수
  const int maxTries = 3;  // 최대 재시도 횟수
  int led_flag = 0;

  // Zigbee 스택에서 dataToSend 변수를 통해 전송 성공 여부 확인
  while (dataToSend != 0 && tries < maxTries) {
    // 전송 실패로 인한 재전송 플래그가 설정된 경우
    if (resend) {
      Serial.println("Resending data on failure!");
      resend = false;
      dataToSend = 2;        // "데이터 전송 요청 상태"로 다시 설정
      zbTempSensor.report(); // 다시 리포트 전송
    }

    // 타임아웃이 발생하면 재전송
    if (millis() - startTime >= timeout) {
      Serial.println("\nReport timeout! Report Again");
      dataToSend = 2;        // 전송 요청 상태로 변경
      zbTempSensor.report(); // 다시 리포트 전송
      startTime = millis();  // 타이머 리셋
      tries++;               // 재시도 횟수 증가


     led_flag ^= 1;
      if(led_flag)Led1On();
      else Led1Off();
    }

    Serial.printf(".");  // 진행 상황 출력
    delay(50);           // 50ms 대기 (busy-waiting 방지)
  }

  // --- 5. Deep Sleep 진입 ---
  // 데이터가 성공적으로 전송되었거나, 재시도 횟수 초과 시 Deep Sleep으로 전환
  Serial.println("Going to sleep now");
  esp_deep_sleep_start();  // ESP32C6 Deep Sleep 모드 진입
}

 

 

프로그램 실행하면 55초마다 한번씩 깨어나서 온습도 데이터 값을 읽어 전송한다. 만약 전송에 실패하면 3번정도 재시도 하고 슬립모드로 들어가는 동작을 한다. 깨어 있는동안은 LED를 깜박이도록해서 디버깅 할 수 있도록 했다.

 

반응형