Skip to content

Commit d62851d

Browse files
committed
feat(zigbee): Add OTA client cluster support
1 parent 6fcaf69 commit d62851d

File tree

3 files changed

+188
-16
lines changed

3 files changed

+188
-16
lines changed

libraries/Zigbee/src/ZigbeeEP.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,4 +363,71 @@ void ZigbeeEP::zbReadTimeCluster(const esp_zb_zcl_attribute_t *attribute) {
363363
}
364364
}
365365

366+
// typedef struct esp_zb_ota_cluster_cfg_s {
367+
// uint32_t ota_upgrade_file_version; /*!< The attribute indicates the file version of the running firmware image on the device */
368+
// uint16_t ota_upgrade_manufacturer; /*!< The attribute indicates the value for the manufacturer of the device */
369+
// uint16_t ota_upgrade_image_type; /*!< The attribute indicates the the image type of the file that the client is currently downloading */
370+
// uint32_t ota_upgrade_downloaded_file_ver; /*!< The attribute indicates the file version of the downloaded image on the device*/
371+
// esp_zb_ota_cluster_cfg_t;
372+
373+
// typedef struct esp_zb_zcl_ota_upgrade_client_variable_s {
374+
// uint16_t timer_query; /*!< The field indicates the time of querying OTA image for OTA upgrade client */
375+
// uint16_t hw_version; /*!< The hardware version */
376+
// uint8_t max_data_size; /*!< The maximum size of OTA data */
377+
// } esp_zb_zcl_ota_upgrade_client_variable_t;
378+
379+
void ZigbeeEP::addOTAClient(uint32_t file_version, uint16_t manufacturer, uint16_t image_type, uint32_t downloaded_file_ver,
380+
uint16_t hw_version, uint8_t max_data_size) {
381+
382+
esp_zb_ota_cluster_cfg_t ota_cluster_cfg = {};
383+
ota_cluster_cfg.ota_upgrade_file_version = file_version;//OTA_UPGRADE_RUNNING_FILE_VERSION;
384+
ota_cluster_cfg.ota_upgrade_downloaded_file_ver = downloaded_file_ver;//OTA_UPGRADE_DOWNLOADED_FILE_VERSION;
385+
ota_cluster_cfg.ota_upgrade_manufacturer = manufacturer;//OTA_UPGRADE_MANUFACTURER;
386+
ota_cluster_cfg.ota_upgrade_image_type = image_type;//OTA_UPGRADE_IMAGE_TYPE;
387+
388+
esp_zb_attribute_list_t *ota_cluster = esp_zb_ota_cluster_create(&ota_cluster_cfg);
389+
390+
esp_zb_zcl_ota_upgrade_client_variable_t variable_config = {};
391+
variable_config.timer_query = ESP_ZB_ZCL_OTA_UPGRADE_QUERY_TIMER_COUNT_DEF;
392+
variable_config.hw_version = hw_version;//OTA_UPGRADE_HW_VERSION;
393+
variable_config.max_data_size = max_data_size;//OTA_UPGRADE_MAX_DATA_SIZE;
394+
395+
uint16_t ota_upgrade_server_addr = 0xffff;
396+
uint8_t ota_upgrade_server_ep = 0xff;
397+
398+
ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_CLIENT_DATA_ID, (void *)&variable_config));
399+
ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ADDR_ID, (void *)&ota_upgrade_server_addr));
400+
ESP_ERROR_CHECK(esp_zb_ota_cluster_add_attr(ota_cluster, ESP_ZB_ZCL_ATTR_OTA_UPGRADE_SERVER_ENDPOINT_ID, (void *)&ota_upgrade_server_ep));
401+
402+
ESP_ERROR_CHECK(esp_zb_cluster_list_add_ota_cluster(_cluster_list, ota_cluster, ESP_ZB_ZCL_CLUSTER_CLIENT_ROLE));
403+
}
404+
405+
static void findOTAServer(esp_zb_zdp_status_t zdo_status, uint16_t addr, uint8_t endpoint, void *user_ctx) {
406+
if (zdo_status == ESP_ZB_ZDP_STATUS_SUCCESS) {
407+
esp_zb_ota_upgrade_client_query_interval_set(*((uint8_t *)user_ctx), OTA_UPGRADE_QUERY_INTERVAL);
408+
esp_zb_ota_upgrade_client_query_image_req(addr, endpoint);
409+
log_i("Query OTA upgrade from server endpoint: %d after %d seconds", endpoint, OTA_UPGRADE_QUERY_INTERVAL);
410+
} else {
411+
log_w("No OTA Server found");
412+
}
413+
}
414+
415+
void ZigbeeEP::requestOTAUpdate() {
416+
esp_zb_zdo_match_desc_req_param_t req;
417+
uint16_t cluster_list[] = {ESP_ZB_ZCL_CLUSTER_ID_OTA_UPGRADE};
418+
419+
/* Match the OTA server of coordinator */
420+
req.addr_of_interest = 0x0000;
421+
req.dst_nwk_addr = 0x0000;
422+
req.num_in_clusters = 1;
423+
req.num_out_clusters = 0;
424+
req.profile_id = ESP_ZB_AF_HA_PROFILE_ID;
425+
req.cluster_list = cluster_list;
426+
esp_zb_lock_acquire(portMAX_DELAY);
427+
if (esp_zb_bdb_dev_joined()) {
428+
esp_zb_zdo_match_cluster(&req, findOTAServer, &_endpoint);
429+
}
430+
esp_zb_lock_release();
431+
}
432+
366433
#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED

libraries/Zigbee/src/ZigbeeEP.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
/* Useful defines */
1111
#define ZB_CMD_TIMEOUT 10000 // 10 seconds
12+
#define OTA_UPGRADE_QUERY_INTERVAL (1 * 60) // 1 minutes
1213

1314
#define ZB_ARRAY_LENTH(arr) (sizeof(arr) / sizeof(arr[0]))
1415
#define XYZ_TO_RGB(X, Y, Z, r, g, b) \
@@ -107,6 +108,10 @@ class ZigbeeEP {
107108
return _allow_multiple_binding;
108109
}
109110

111+
void addOTAClient(uint32_t file_version = 0x01010101, uint16_t manufacturer = 0x1001, uint16_t image_type = 0x1011,
112+
uint32_t downloaded_file_ver = 0x01010101, uint16_t hw_version = 0x0101, uint8_t max_data_size = 223);
113+
void requestOTAUpdate();
114+
110115
// findEndpoind may be implemented by EPs to find and bind devices
111116
virtual void findEndpoint(esp_zb_zdo_match_desc_req_param_t *cmd_req) {};
112117

libraries/Zigbee/src/ZigbeeHandlers.cpp

Lines changed: 116 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#if SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED
66

7+
#include "esp_ota_ops.h"
8+
79
// forward declaration of all implemented handlers
810
static esp_err_t zb_attribute_set_handler(const esp_zb_zcl_set_attr_value_message_t *message);
911
static esp_err_t zb_attribute_reporting_handler(const esp_zb_zcl_report_attr_message_t *message);
@@ -13,6 +15,9 @@ static esp_err_t zb_cmd_ias_zone_status_change_handler(const esp_zb_zcl_ias_zone
1315
static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zone_enroll_response_message_t *message);
1416
static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message);
1517
static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message);
18+
static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_value_message_t *message);
19+
static esp_err_t zb_ota_upgrade_query_image_resp_handler(const esp_zb_zcl_ota_upgrade_query_image_resp_message_t *message);
20+
1621

1722
// Zigbee action handlers
1823
[[maybe_unused]]
@@ -32,6 +37,12 @@ static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id,
3237
case ESP_ZB_CORE_WINDOW_COVERING_MOVEMENT_CB_ID:
3338
ret = zb_window_covering_movement_resp_handler((esp_zb_zcl_window_covering_movement_message_t *)message);
3439
break;
40+
case ESP_ZB_CORE_OTA_UPGRADE_VALUE_CB_ID:
41+
ret = zb_ota_upgrade_status_handler((esp_zb_zcl_ota_upgrade_value_message_t *)message);
42+
break;
43+
case ESP_ZB_CORE_OTA_UPGRADE_QUERY_IMAGE_RESP_CB_ID:
44+
ret = zb_ota_upgrade_query_image_resp_handler((esp_zb_zcl_ota_upgrade_query_image_resp_message_t *)message);
45+
break;
3546
case ESP_ZB_CORE_CMD_DEFAULT_RESP_CB_ID: ret = zb_cmd_default_resp_handler((esp_zb_zcl_cmd_default_resp_message_t *)message); break;
3647
default: log_w("Receive unhandled Zigbee action(0x%x) callback", callback_id); break;
3748
}
@@ -186,22 +197,6 @@ static esp_err_t zb_cmd_ias_zone_enroll_response_handler(const esp_zb_zcl_ias_zo
186197
return ESP_OK;
187198
}
188199

189-
static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) {
190-
if (!message) {
191-
log_e("Empty message");
192-
return ESP_FAIL;
193-
}
194-
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
195-
log_e("Received message: error status(%d)", message->info.status);
196-
return ESP_ERR_INVALID_ARG;
197-
}
198-
log_v(
199-
"Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x",
200-
message->info.src_address.u.short_addr, message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster, message->status_code
201-
);
202-
return ESP_OK;
203-
}
204-
205200
static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_window_covering_movement_message_t *message) {
206201
if (!message) {
207202
log_e("Empty message");
@@ -224,4 +219,109 @@ static esp_err_t zb_window_covering_movement_resp_handler(const esp_zb_zcl_windo
224219
return ESP_OK;
225220
}
226221

222+
static esp_err_t zb_ota_upgrade_status_handler(const esp_zb_zcl_ota_upgrade_value_message_t *message)
223+
{
224+
static const esp_partition_t *s_ota_partition = NULL;
225+
static esp_ota_handle_t s_ota_handle = 0;
226+
227+
static uint32_t total_size = 0;
228+
static uint32_t offset = 0;
229+
static int64_t start_time = 0;
230+
esp_err_t ret = ESP_OK;
231+
232+
if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) {
233+
switch (message->upgrade_status) {
234+
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_START:
235+
log_i("Zigbee OTA - Upgrade start");
236+
start_time = esp_timer_get_time();
237+
s_ota_partition = esp_ota_get_next_update_partition(NULL);
238+
assert(s_ota_partition);
239+
ret = esp_ota_begin(s_ota_partition, 0, &s_ota_handle);
240+
if(ret == ESP_OK) {
241+
log_i("Zigbee OTA - OTA partition begin");
242+
} else {
243+
log_e("Zigbee OTA - Failed to begin OTA partition, status: %s", esp_err_to_name(ret));
244+
return ret;
245+
}
246+
break;
247+
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_RECEIVE:
248+
total_size = message->ota_header.image_size;
249+
offset += message->payload_size;
250+
log_i("Zigbee OTA - Client receives data: progress [%ld/%ld]", offset, total_size);
251+
if (message->payload_size && message->payload) {
252+
ret = esp_ota_write(s_ota_handle, (const void *)message->payload, message->payload_size);
253+
if(ret == ESP_OK) {
254+
log_i("Zigbee OTA - Write OTA data to partition");
255+
} else {
256+
log_e("Zigbee OTA - Failed to write OTA data to partition, status: %s", esp_err_to_name(ret));
257+
return ret;
258+
}
259+
}
260+
break;
261+
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_APPLY:
262+
log_i("Zigbee OTA - Upgrade apply");
263+
break;
264+
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_CHECK:
265+
ret = offset == total_size ? ESP_OK : ESP_FAIL;
266+
log_i("Zigbee OTA - Upgrade check status: %s", esp_err_to_name(ret));
267+
break;
268+
case ESP_ZB_ZCL_OTA_UPGRADE_STATUS_FINISH:
269+
log_i("Zigbee OTA - Finish");
270+
log_i("Zigbee OTA - Information: version: 0x%lx, manufacturer code: 0x%x, image type: 0x%x, total size: %ld bytes, cost time: %lld ms",
271+
message->ota_header.file_version, message->ota_header.manufacturer_code, message->ota_header.image_type,
272+
message->ota_header.image_size, (esp_timer_get_time() - start_time) / 1000);
273+
ret = esp_ota_end(s_ota_handle);
274+
if(ret == ESP_OK) {
275+
log_i("Zigbee OTA - OTA partition end");
276+
} else {
277+
log_e("Zigbee OTA - Failed to end OTA partition, status: %s", esp_err_to_name(ret));
278+
return ret;
279+
}
280+
ret = esp_ota_set_boot_partition(s_ota_partition);
281+
if(ret == ESP_OK) {
282+
log_i("Zigbee OTA - Set OTA boot partition");
283+
} else {
284+
log_e("Zigbee OTA - Failed to set OTA boot partition, status: %s", esp_err_to_name(ret));
285+
return ret;
286+
}
287+
log_w("Zigbee OTA - Prepare to restart system");
288+
esp_restart();
289+
break;
290+
default:
291+
log_i("Zigbee OTA - Status: %d", message->upgrade_status);
292+
break;
293+
}
294+
}
295+
return ret;
296+
}
297+
298+
static esp_err_t zb_ota_upgrade_query_image_resp_handler(const esp_zb_zcl_ota_upgrade_query_image_resp_message_t *message)
299+
{
300+
if (message->info.status == ESP_ZB_ZCL_STATUS_SUCCESS) {
301+
log_i("Zigbee - Queried OTA image from address: 0x%04hx, endpoint: %d", message->server_addr.u.short_addr, message->server_endpoint);
302+
log_i("Zigbee - Image version: 0x%lx, manufacturer code: 0x%x, image size: %ld", message->file_version, message->manufacturer_code,
303+
message->image_size);
304+
log_i("Zigbee - Approving OTA image upgrade");
305+
} else {
306+
log_i("Zigbee - OTA image upgrade response status: 0x%x", message->info.status);
307+
}
308+
return ESP_OK;
309+
}
310+
311+
static esp_err_t zb_cmd_default_resp_handler(const esp_zb_zcl_cmd_default_resp_message_t *message) {
312+
if (!message) {
313+
log_e("Empty message");
314+
return ESP_FAIL;
315+
}
316+
if (message->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
317+
log_e("Received message: error status(%d)", message->info.status);
318+
return ESP_ERR_INVALID_ARG;
319+
}
320+
log_v(
321+
"Received default response: from address(0x%x), src_endpoint(%d) to dst_endpoint(%d), cluster(0x%x) with status 0x%x",
322+
message->info.src_address.u.short_addr, message->info.src_endpoint, message->info.dst_endpoint, message->info.cluster, message->status_code
323+
);
324+
return ESP_OK;
325+
}
326+
227327
#endif //SOC_IEEE802154_SUPPORTED && CONFIG_ZB_ENABLED

0 commit comments

Comments
 (0)