forked from espressif/arduino-esp32
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathZigbeeThermostat.cpp
205 lines (176 loc) · 8.82 KB
/
ZigbeeThermostat.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
#include "ZigbeeThermostat.h"
#if SOC_IEEE802154_SUPPORTED
static float zb_s16_to_temperature(int16_t value) {
return 1.0 * value / 100;
}
// Initialize the static instance of the class
ZigbeeThermostat *ZigbeeThermostat::_instance = nullptr;
ZigbeeThermostat::ZigbeeThermostat(uint8_t endpoint) : ZigbeeEP(endpoint) {
_device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID;
_instance = this; // Set the static pointer to this instance
//use custom config to avoid narrowing error -> must be fixed in zigbee-sdk
esp_zb_thermostat_cfg_t thermostat_cfg = ZB_DEFAULT_THERMOSTAT_CONFIG();
//use custom cluster creating to accept reportings from temperature sensor
_cluster_list = esp_zb_zcl_cluster_list_create();
esp_zb_attribute_list_t *basic_cluster = esp_zb_basic_cluster_create(&(thermostat_cfg.basic_cfg));
esp_zb_cluster_list_add_basic_cluster(_cluster_list, basic_cluster, ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_identify_cluster_create(&(thermostat_cfg.identify_cfg)), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
esp_zb_cluster_list_add_identify_cluster(_cluster_list, esp_zb_zcl_attr_list_create(ESP_ZB_ZCL_CLUSTER_ID_IDENTIFY), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
esp_zb_cluster_list_add_thermostat_cluster(_cluster_list, esp_zb_thermostat_cluster_create(&(thermostat_cfg.thermostat_cfg)), ESP_ZB_ZCL_CLUSTER_SERVER_ROLE);
/* Add temperature measurement cluster for attribute reporting */
esp_zb_cluster_list_add_temperature_meas_cluster(_cluster_list, esp_zb_temperature_meas_cluster_create(NULL), ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE);
_ep_config = {.endpoint = _endpoint, .app_profile_id = ESP_ZB_AF_HA_PROFILE_ID, .app_device_id = ESP_ZB_HA_THERMOSTAT_DEVICE_ID, .app_device_version = 0};
}
void ZigbeeThermostat::bindCb(esp_zb_zdp_status_t zdo_status, void *user_ctx) {
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
if (user_ctx) {
zb_device_params_t *sensor = (zb_device_params_t *)user_ctx;
log_i("The temperature sensor originating from address(0x%x) on endpoint(%d)", sensor->short_addr, sensor->endpoint);
_instance->_bound_devices.push_back(sensor);
} else {
log_v("Local binding success");
}
_is_bound = true;
} else {
log_e("Binding failed!");
}
}
void ZigbeeThermostat::findCb(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
log_i("Found temperature sensor");
esp_zb_zdo_bind_req_param_t bind_req;
/* Store the information of the remote device */
zb_device_params_t *sensor = (zb_device_params_t *)malloc(sizeof(zb_device_params_t));
sensor->endpoint = endpoint;
sensor->short_addr = addr;
esp_zb_ieee_address_by_short(sensor->short_addr, sensor->ieee_addr);
log_d("Temperature sensor found: short address(0x%x), endpoint(%d)", sensor->short_addr, sensor->endpoint);
/* 1. Send binding request to the sensor */
bind_req.req_dst_addr = addr;
log_d("Request temperature sensor to bind us");
/* populate the src information of the binding */
memcpy(bind_req.src_address, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t));
bind_req.src_endp = endpoint;
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
log_d("Bind temperature sensor");
/* populate the dst information of the binding */
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
esp_zb_get_long_address(bind_req.dst_address_u.addr_long);
bind_req.dst_endp = *((uint8_t*)user_ctx); //_endpoint;
log_i("Request temperature sensor to bind us");
esp_zb_zdo_device_bind_req(&bind_req, bindCb, NULL);
/* 2. Send binding request to self */
bind_req.req_dst_addr = esp_zb_get_short_address();
/* populate the src information of the binding */
esp_zb_get_long_address(bind_req.src_address);
bind_req.src_endp = *((uint8_t*)user_ctx); //_endpoint;
bind_req.cluster_id = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
/* populate the dst information of the binding */
bind_req.dst_addr_mode = ESP_ZB_ZDO_BIND_DST_ADDR_MODE_64_BIT_EXTENDED;
memcpy(bind_req.dst_address_u.addr_long, sensor->ieee_addr, sizeof(esp_zb_ieee_addr_t));
bind_req.dst_endp = endpoint;
log_i("Bind temperature sensor");
esp_zb_zdo_device_bind_req(&bind_req, bindCb, (void *)sensor);
}
}
void ZigbeeThermostat::findEndpoint(esp_zb_zdo_match_desc_req_param_t *param) {
uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT};
param->profile_id = ESP_ZB_AF_HA_PROFILE_ID;
param->num_in_clusters = 1;
param->num_out_clusters = 0;
param->cluster_list = cluster_list;
esp_zb_zdo_match_cluster(param, findCb, &_endpoint);
}
void ZigbeeThermostat::zbAttributeRead(uint16_t cluster_id, const esp_zb_zcl_attribute_t *attribute) {
static uint8_t read_config = 0;
if (cluster_id == ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT) {
if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S16) {
int16_t value = attribute->data.value ? *(int16_t *)attribute->data.value : 0;
if (_on_temp_recieve) {
_on_temp_recieve(zb_s16_to_temperature(value));
}
}
if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S16) {
int16_t min_value = attribute->data.value ? *(int16_t *)attribute->data.value : 0;
_min_temp = zb_s16_to_temperature(min_value);
read_config++;
}
if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_S16) {
int16_t max_value = attribute->data.value ? *(int16_t *)attribute->data.value : 0;
_max_temp = zb_s16_to_temperature(max_value);
read_config++;
}
if (attribute->id == ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID && attribute->data.type == ESP_ZB_ZCL_ATTR_TYPE_U16) {
uint16_t tolerance = attribute->data.value ? *(uint16_t *)attribute->data.value : 0;
_tolerance = 1.0 * tolerance / 100;
read_config++;
}
if (read_config == 3) {
read_config = 0;
xSemaphoreGive(lock);
}
}
}
void ZigbeeThermostat::getTemperature() {
/* Send "read attributes" command to the bound sensor */
esp_zb_zcl_read_attr_cmd_t read_req;
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
uint16_t attributes[] = {ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_field = attributes;
log_i("Sending 'read temperature' command");
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();
}
void ZigbeeThermostat::getSensorSettings() {
/* Send "read attributes" command to the bound sensor */
esp_zb_zcl_read_attr_cmd_t read_req;
read_req.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
read_req.zcl_basic_cmd.src_endpoint = _endpoint;
read_req.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
uint16_t attributes[] = {
ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MIN_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_MAX_VALUE_ID, ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_TOLERANCE_ID
};
read_req.attr_number = ZB_ARRAY_LENTH(attributes);
read_req.attr_field = attributes;
log_i("Sending 'read temperature' command");
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_read_attr_cmd_req(&read_req);
esp_zb_lock_release();
//Take semaphore to wait for response of all attributes
if (xSemaphoreTake(lock, portMAX_DELAY) != pdTRUE) {
log_e("Error while reading attributes");
return;
} else {
//Call the callback function when all attributes are read
_on_config_recieve(_min_temp, _max_temp, _tolerance);
}
}
void ZigbeeThermostat::setTemperatureReporting(uint16_t min_interval, uint16_t max_interval, float delta) {
/* Send "configure report attribute" command to the bound sensor */
esp_zb_zcl_config_report_cmd_t report_cmd;
report_cmd.address_mode = ESP_ZB_APS_ADDR_MODE_DST_ADDR_ENDP_NOT_PRESENT;
report_cmd.zcl_basic_cmd.src_endpoint = _endpoint;
report_cmd.clusterID = ESP_ZB_ZCL_CLUSTER_ID_TEMP_MEASUREMENT;
int16_t report_change = (int16_t)delta * 100;
esp_zb_zcl_config_report_record_t records[] = {
{
.direction = ESP_ZB_ZCL_CMD_DIRECTION_TO_SRV,
.attributeID = ESP_ZB_ZCL_ATTR_TEMP_MEASUREMENT_VALUE_ID,
.attrType = ESP_ZB_ZCL_ATTR_TYPE_S16,
.min_interval = min_interval,
.max_interval = max_interval,
.reportable_change = &report_change,
},
};
report_cmd.record_number = ZB_ARRAY_LENTH(records);
report_cmd.record_field = records;
log_i("Sending 'configure reporting' command");
esp_zb_lock_acquire(portMAX_DELAY);
esp_zb_zcl_config_report_cmd_req(&report_cmd);
esp_zb_lock_release();
}
#endif //SOC_IEEE802154_SUPPORTED