diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a3b0de --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +.DS_Store +build +sdkconfig +sdkconfig.old \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..42ed158 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,3 @@ +cmake_minimum_required(VERSION 3.16) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(zh_espnow_open_sensor_esp32) \ No newline at end of file diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..593c759 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_build_get_property(project_dir PROJECT_DIR) +idf_component_register(SRCS "zh_espnow_open_sensor_esp32.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/main/zh_espnow_open_sensor_esp32.c b/main/zh_espnow_open_sensor_esp32.c new file mode 100644 index 0000000..0eb4106 --- /dev/null +++ b/main/zh_espnow_open_sensor_esp32.c @@ -0,0 +1,224 @@ +#include "stdio.h" +#include "string.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "esp_event.h" +// #include "driver/gpio.h" +#include "driver/uart.h" +#include "esp_timer.h" +#include "esp_ota_ops.h" +#include "zh_espnow.h" +#include "zh_config.h" + +#define ZH_MESSAGE_TASK_PRIORITY 2 +#define ZH_MESSAGE_STACK_SIZE 2048 + +static uint8_t s_relay_status = OFF; + +static uint8_t s_gateway_mac[6] = {0}; +static bool s_gateway_is_available = false; + +// static TaskHandle_t s_switch_attributes_message_task = {0}; +// static TaskHandle_t s_switch_config_message_task = {0}; +// static TaskHandle_t s_switch_keep_alive_message_task = {0}; +// static TaskHandle_t s_switch_status_message_task = {0}; + +static const esp_partition_t *s_update_partition = NULL; +static esp_ota_handle_t s_update_handle = 0; +static uint16_t s_ota_message_part_number = 0; + +static void s_zh_load_status(void); +static void s_zh_save_status(void); + +static void s_zh_send_sensor_attributes_message(void); +static void s_zh_send_sensor_config_message(void); +static void s_zh_send_sensor_status_message(void); + +static void s_zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); + +void app_main(void) +{ + const esp_partition_t *running = esp_ota_get_running_partition(); + esp_ota_img_states_t ota_state = {0}; + esp_ota_get_state_partition(running, &ota_state); + nvs_flash_init(); + esp_netif_init(); + esp_event_loop_create_default(); + s_zh_load_status(); + wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); + esp_wifi_init(&wifi_init_config); + esp_wifi_set_mode(WIFI_MODE_STA); + esp_wifi_start(); + zh_espnow_init_config_t zh_espnow_init_config = ZH_ESPNOW_INIT_CONFIG_DEFAULT(); + zh_espnow_init(&zh_espnow_init_config); + esp_event_handler_instance_register(ZH_ESPNOW, ESP_EVENT_ANY_ID, &s_zh_espnow_event_handler, NULL, NULL); + s_zh_send_sensor_config_message(); + s_zh_send_sensor_attributes_message(); + s_zh_send_sensor_status_message(); + if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) + { + esp_ota_mark_app_valid_cancel_rollback(); + } +} + +static void s_zh_load_status(void) +{ + nvs_handle_t nvs_handle = 0; + nvs_open("status", NVS_READWRITE, &nvs_handle); + uint8_t status_is_present = 0; + if (nvs_get_u8(nvs_handle, "present", &status_is_present) == ESP_ERR_NVS_NOT_FOUND) + { + nvs_set_u8(nvs_handle, "present", 0xFE); + nvs_close(nvs_handle); + s_zh_save_status(); + return; + } + nvs_get_u8(nvs_handle, "relay_state", &s_relay_status); + nvs_close(nvs_handle); +} + +static void s_zh_save_status(void) +{ + nvs_handle_t nvs_handle = 0; + nvs_open("status", NVS_READWRITE, &nvs_handle); + nvs_set_u8(nvs_handle, "relay_state", s_relay_status); + nvs_close(nvs_handle); +} + +static void s_zh_send_sensor_attributes_message(void) +{ + const esp_app_desc_t *app_info = esp_app_get_description(); + zh_attributes_message_t attributes_message = {0}; + attributes_message.chip_type = HACHT_ESP32; + strcpy(attributes_message.flash_size, CONFIG_ESPTOOLPY_FLASHSIZE); + attributes_message.cpu_frequency = CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ; + attributes_message.reset_reason = (uint8_t)esp_reset_reason(); + strcpy(attributes_message.app_name, app_info->project_name); + strcpy(attributes_message.app_version, app_info->version); + zh_espnow_data_t data = {0}; + data.device_type = ZHDT_BINARY_SENSOR; + data.payload_type = ZHPT_ATTRIBUTES; + attributes_message.heap_size = esp_get_free_heap_size(); + attributes_message.min_heap_size = esp_get_minimum_free_heap_size(); + attributes_message.uptime = esp_timer_get_time() / 1000000; + data.payload_data = (zh_payload_data_t)attributes_message; + zh_espnow_send(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t)); +} + +static void s_zh_send_sensor_config_message(void) +{ + zh_switch_config_message_t switch_config_message = {0}; + switch_config_message.unique_id = 1; + switch_config_message.device_class = HASWDC_SWITCH; + switch_config_message.payload_on = HAONOFT_ON; + switch_config_message.payload_off = HAONOFT_OFF; + switch_config_message.enabled_by_default = true; + switch_config_message.optimistic = false; + switch_config_message.qos = 2; + switch_config_message.retain = true; + zh_config_message_t config_message = {0}; + config_message = (zh_config_message_t)switch_config_message; + zh_espnow_data_t data = {0}; + data.device_type = ZHDT_SWITCH; + data.payload_type = ZHPT_CONFIG; + data.payload_data = (zh_payload_data_t)config_message; + zh_espnow_send(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t)); +} + +static void s_zh_send_sensor_status_message(void) +{ + zh_switch_status_message_t switch_status_message = {0}; + zh_status_message_t status_message = {0}; + zh_espnow_data_t data = {0}; + data.device_type = ZHDT_SWITCH; + data.payload_type = ZHPT_STATE; + switch_status_message.status = (s_relay_status == ON) ? HAONOFT_ON : HAONOFT_OFF; + status_message = (zh_status_message_t)switch_status_message; + data.payload_data = (zh_payload_data_t)status_message; + zh_espnow_send(NULL, (uint8_t *)&data, sizeof(zh_espnow_data_t)); +} + +static void s_zh_espnow_event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) +{ + const esp_app_desc_t *app_info = esp_app_get_description(); + zh_espnow_data_t data_in = {0}; + zh_espnow_data_t data_out = {0}; + zh_espnow_ota_message_t espnow_ota_message = {0}; + data_out.device_type = ZHDT_BINARY_SENSOR; + espnow_ota_message.chip_type = HACHT_ESP32; + data_out.payload_data = (zh_payload_data_t)espnow_ota_message; + switch (event_id) + { + case ZH_ESPNOW_ON_RECV_EVENT:; + zh_espnow_event_on_recv_t *recv_data = event_data; + if (recv_data->data_len != sizeof(zh_espnow_data_t)) + { + goto ZH_ESPNOW_EVENT_HANDLER_EXIT; + } + memcpy(&data_in, recv_data->data, recv_data->data_len); + switch (data_in.device_type) + { + case ZHDT_GATEWAY: + if (s_gateway_is_available == false) + { + s_gateway_is_available = true; + memcpy(s_gateway_mac, recv_data->mac_addr, 6); + } + switch (data_in.payload_type) + { + case ZHPT_UPDATE: + s_update_partition = esp_ota_get_next_update_partition(NULL); + strcpy(espnow_ota_message.app_name, app_info->project_name); + strcpy(espnow_ota_message.app_version, app_info->version); + data_out.payload_type = ZHPT_UPDATE; + data_out.payload_data = (zh_payload_data_t)espnow_ota_message; + zh_espnow_send(s_gateway_mac, (uint8_t *)&data_out, sizeof(zh_espnow_data_t)); + break; + case ZHPT_UPDATE_BEGIN: + esp_ota_begin(s_update_partition, OTA_SIZE_UNKNOWN, &s_update_handle); + s_ota_message_part_number = 1; + break; + case ZHPT_UPDATE_PROGRESS: + if (s_ota_message_part_number == data_in.payload_data.espnow_ota_message.part) + { + ++s_ota_message_part_number; + esp_ota_write(s_update_handle, (const void *)data_in.payload_data.espnow_ota_message.data, data_in.payload_data.espnow_ota_message.data_len); + } + data_out.payload_type = ZHPT_UPDATE_PROGRESS; + zh_espnow_send(s_gateway_mac, (uint8_t *)&data_out, sizeof(zh_espnow_data_t)); + break; + case ZHPT_UPDATE_ERROR: + esp_ota_end(s_update_handle); + break; + case ZHPT_UPDATE_END: + if (esp_ota_end(s_update_handle) != ESP_OK) + { + data_out.payload_type = ZHPT_UPDATE_FAIL; + zh_espnow_send(s_gateway_mac, (uint8_t *)&data_out, sizeof(zh_espnow_data_t)); + break; + } + esp_ota_set_boot_partition(s_update_partition); + data_out.payload_type = ZHPT_UPDATE_SUCCESS; + zh_espnow_send(s_gateway_mac, (uint8_t *)&data_out, sizeof(zh_espnow_data_t)); + vTaskDelay(1000 / portTICK_PERIOD_MS); + esp_restart(); + break; + case ZHPT_RESTART: + esp_restart(); + break; + default: + break; + } + break; + default: + break; + } + ZH_ESPNOW_EVENT_HANDLER_EXIT: + free(recv_data->data); + break; + case ZH_ESPNOW_ON_SEND_EVENT: + break; + default: + break; + } +} \ No newline at end of file diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 0000000..1ec9a54 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,7 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, , 0x4000, +otadata, data, ota, , 0x2000, +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, , 1500K, +ota_1, app, ota_1, , 1500K, \ No newline at end of file diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..bda2440 --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,18 @@ +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x1000 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +CONFIG_BOOTLOADER_LOG_LEVEL_NONE=y +CONFIG_BOOTLOADER_LOG_LEVEL=0 +CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y + +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="4MB" + +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 + +CONFIG_COMPILER_OPTIMIZATION_SIZE=y + +CONFIG_LOG_DEFAULT_LEVEL_NONE=y +CONFIG_LOG_DEFAULT_LEVEL=0 \ No newline at end of file diff --git a/version.txt b/version.txt new file mode 100644 index 0000000..72f9fa8 --- /dev/null +++ b/version.txt @@ -0,0 +1 @@ +0.2.4 \ No newline at end of file