Всем привет, давно хотелось соорудить свое оконечное устройство на Zigbee, и оказалось это не сильно сложно. Тем более что основа в виде Home Assistant на Raspberry Pi 4 со свистком Zigbee 3 SONOFF USB Dongle Plus-E есть и работает. Правда до этого в нем были только фабричные zigbee устройства, в основном Aqara. Итак погнали.
Что нам нужно кроме вышеуказанного
Инфракрасный датчик движения HC-SR501. Стоит рублей 200 на маркетплейсах, я не покупал, у меня был, куплен лет 8 назад и раньше стоял в паре с обычной релюхой и рулил светом в коридоре.
ESP32-H2 SuperMini, 650 рублей на маркетплейсах, но быстро и в 3 раза дешевле на ali, но медленнее
Устанавливаем в Tools вот такие настройки

Коннектим плату и заливаем такой скетч
Скрытый текст
#include <FastLED.h>
#include "Zigbee.h"
#define LED_PIN 8 // Most ESP32 Mini boards
#define NUM_LEDS 1
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools->Zigbee mode"
#endif
CRGB leds[NUM_LEDS];
/* Zigbee occupancy sensor configuration */
#define OCCUPANCY_SENSOR_ENDPOINT_NUMBER 10
#define TEMP_SENSOR_ENDPOINT_NUMBER 11
uint8_t button = BOOT_PIN;
uint8_t sensor_pin = 10;
ZigbeeOccupancySensor zbOccupancySensor = ZigbeeOccupancySensor(OCCUPANCY_SENSOR_ENDPOINT_NUMBER);
ZigbeeTempSensor zbTempSensor(TEMP_SENSOR_ENDPOINT_NUMBER);
void setup() {
Serial.begin(115200);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(50);
// Init button + PIR sensor
pinMode(button, INPUT_PULLUP);
pinMode(sensor_pin, INPUT);
// Optional: set Zigbee device name and model
zbOccupancySensor.setManufacturerAndModel("ESP32", "ESP32_Occupancy");
zbTempSensor.setManufacturerAndModel("ESP32", "ESP32_Occupancy");
// Add endpoint to Zigbee Core
Zigbee.addEndpoint(&zbOccupancySensor);
Zigbee.addEndpoint(&zbTempSensor);
zbTempSensor.setMinMaxValue(10, 80);
zbTempSensor.setDefaultValue(25.0);
zbTempSensor.setTolerance(0.5); // 0.5°C
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!");
leds[0] = CRGB::Green;
FastLED.show();
}
Serial.println("Connecting to network");
while (!Zigbee.connected()) {
Serial.print(".");
delay(100);
}
Serial.println();
zbTempSensor.setReporting(
10, // min interval seconds
300, // max interval seconds
0.5 // delta °C (минимальное изменение для репорта)
);
}
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;
leds[0] = CRGB::Red;
FastLED.show();
} else if (digitalRead(sensor_pin) == LOW && occupancy) {
zbOccupancySensor.setOccupancy(false);
zbOccupancySensor.report();
occupancy = false;
leds[0] = CRGB::Green;
FastLED.show();
}
static uint32_t lastTempMs = 0;
if (millis() - lastTempMs > 60000) {
lastTempMs = millis();
float t = temperatureRead(); // °C (примерно)
zbTempSensor.setTemperature(t); // или setTemperatureValue(...)
Serial.printf("Chip temp: %.2f C\n", t);
}
// 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);
}
Кроме работы в zigbee как датчика присутствия скетч еще выдает температуру платы, а так же меняет цвет светодида на плате, при обнаружении движения он становится красным, а при не обнаружении - зеленым.
Коннектим плату к сенсору: 3v3<->Vcc, GND<->GND, 10<->OUT

Собственно если вы сделали все правильно, то при махании рукой перед сенсором светодиод должен загораться красным, а через какое-то время, если ничего не происходит перед ним - зеленым. Чувствительность и длительности работы сенсора после определения движения выполняется регулировочными резисторами на сенсоре. На фото они спрятались под провода.
Так как у нас тут мультисенсор с температурой и движением надо подкрутить Zigbee2MQTT. Я для таких редактирований пользуюсь Studio Code Server. Создаем файл zigbee2mqtt/external_converters/esp32_occupancy.js cо следующим содержимым
Скрытый текст
const fz = require('zigbee-herdsman-converters/converters/fromZigbee');
const exposes = require('zigbee-herdsman-converters/lib/exposes');
const e = exposes.presets;
module.exports = [{
fingerprint: [{modelID: 'ESP32_Occupancy', manufacturerName: 'ESP32'}],
model: 'ESP32_Occupancy',
vendor: 'ESP32',
description: 'ESP32 occupancy + chip temperature',
fromZigbee: [fz.occupancy, fz.temperature],
toZigbee: [],
exposes: [
e.occupancy('occ'),
e.temperature().withEndpoint('temp'),
],
endpoint: (device) => ({
occ: 10,
temp: 11,
}),
meta: {multiEndpoint: true},
}];
Перегружаем Zigbee2MQTT, жмем в его интерфейсе Permit Join, и включаем esp32
Если вы все ��делали правильно то должно стать так



Вы скажите, а нафига это все? датчик движения Aqara на Zigbee стоит немного дороже, но при этом имеет меньший размер и живет на одной батарейке год. И будете правы. Но таким же образом в HA можно рулить другими вещами которые изначально не умеют Zigbee.
Не переключайтесь, в следующей серии, если все получится, планирую описать сетап HomeAssistant (Matter, OpenThread )<-> ESP32-C5 <-> 802.15.4 with Thread <-> ESP32-H2 (как сенсор). Планируемые бенефиты - потрогать OpenThread и Thread через 802.15.4, а значит вполне себе реальную ipv6 mesh сетку. Так же, кажется, что практического смысла будет больше, датчики с Thread раза в 3 дороже Zigbee и выбор пока маленький.
