From 8a7b9c6a1ce165c349cf06dda56d3a55fb5167b9 Mon Sep 17 00:00:00 2001 From: Luke Hoersten Date: Sun, 7 Mar 2021 17:33:37 -0600 Subject: Initial commit. --- .vscode/c_cpp_properties.json | 29 ++++ .vscode/launch.json | 13 ++ .vscode/settings.json | 14 ++ .vscode/tasks.json | 300 ++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 12 ++ Makefile | 9 ++ main/CMakeLists.txt | 1 + main/Kconfig.projbuild | 60 ++++++++ main/app_main.c | 325 ++++++++++++++++++++++++++++++++++++++++++ main/component.mk | 4 + partitions_hap.csv | 10 ++ 11 files changed, 777 insertions(+) create mode 100644 .vscode/c_cpp_properties.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CMakeLists.txt create mode 100755 Makefile create mode 100644 main/CMakeLists.txt create mode 100644 main/Kconfig.projbuild create mode 100644 main/app_main.c create mode 100644 main/component.mk create mode 100644 partitions_hap.csv diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..7492318 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,29 @@ +{ + "configurations": [ + { + "name": "ESP-IDF", + "compilerPath": "${default}", + "cStandard": "c11", + "cppStandard": "c++17", + "intelliSenseMode": "macos-clang-x64", + "compileCommands": "${workspaceFolder}/build/compile_commands.json", + "includePath": [ + "${config:idf.espIdfPath}/components/**", + "${config:idf.espIdfPathWin}/components/**", + "${workspaceFolder}/**" + ], + "macFrameworkPath": [ + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "browse": { + "path": [ + "${config:idf.espIdfPath}/components", + "${config:idf.espIdfPathWin}/components", + "${workspaceFolder}" + ], + "limitSymbolsToIncludedHeaders": false + } + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..7e32a3e --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "espidf", + "name": "Launch", + "request": "launch", + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..be2aa7d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,14 @@ +{ + "C_Cpp.clang_format_style": "Visual Studio", + "editor.formatOnSave": false, + "[cpp]": { + "editor.quickSuggestions": true + }, + "[c]": { + "editor.quickSuggestions": true + }, + "C_Cpp.intelliSenseEngine": "Tag Parser", + "files.associations": { + "stdio.h": "c" + } +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..3a649a6 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,300 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Build - Build project", + "type": "shell", + "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py build", + "windows": { + "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py build", + "options": { + "env": { + "PATH": "${env:PATH};${config:idf.customExtraPaths}" + } + } + }, + "options": { + "env": { + "PATH": "${env:PATH}:${config:idf.customExtraPaths}" + } + }, + "problemMatcher": [ + { + "owner": "cpp", + "fileLocation": [ + "relative", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": { + "regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + ], + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "label": "Set ESP-IDF Target", + "type": "shell", + "command": "${command:espIdf.setTarget}", + "problemMatcher": { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": { + "regexp": "^(.*):(//d+):(//d+)://s+(warning|error)://s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + }, + { + "label": "Clean - Clean the project", + "type": "shell", + "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py fullclean", + "windows": { + "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py fullclean", + "options": { + "env": { + "PATH": "${env:PATH};${config:idf.customExtraPaths}" + } + } + }, + "options": { + "env": { + "PATH": "${env:PATH}:${config:idf.customExtraPaths}" + } + }, + "problemMatcher": [ + { + "owner": "cpp", + "fileLocation": [ + "relative", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": { + "regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + ] + }, + { + "label": "Flash - Flash the device", + "type": "shell", + "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py -p ${config:idf.port} -b ${config:idf.flashBaudRate} flash", + "windows": { + "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py flash -p ${config:idf.portWin} -b ${config:idf.flashBaudRate}", + "options": { + "env": { + "PATH": "${env:PATH};${config:idf.customExtraPaths}" + } + } + }, + "options": { + "env": { + "PATH": "${env:PATH}:${config:idf.customExtraPaths}" + } + }, + "problemMatcher": [ + { + "owner": "cpp", + "fileLocation": [ + "relative", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": { + "regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + ] + }, + { + "label": "Monitor: Start the monitor", + "type": "shell", + "command": "${config:idf.pythonBinPath} ${config:idf.espIdfPath}/tools/idf.py -p ${config:idf.port} monitor", + "windows": { + "command": "${config:idf.pythonBinPathWin} ${config:idf.espIdfPathWin}\\tools\\idf.py -p ${config:idf.portWin} monitor", + "options": { + "env": { + "PATH": "${env:PATH};${config:idf.customExtraPaths}" + } + } + }, + "options": { + "env": { + "PATH": "${env:PATH}:${config:idf.customExtraPaths}" + } + }, + "problemMatcher": [ + { + "owner": "cpp", + "fileLocation": [ + "relative", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^\\.\\.(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + }, + { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": { + "regexp": "^[^\\.](.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + ], + "dependsOn": "Flash - Flash the device" + }, + { + "label": "OpenOCD: Start openOCD", + "type": "shell", + "presentation": { + "echo": true, + "reveal": "never", + "focus": false, + "panel": "new" + }, + "command": "openocd -s ${command:espIdf.getOpenOcdScriptValue} ${command:espIdf.getOpenOcdConfigs}", + "windows": { + "command": "openocd.exe -s ${command:espIdf.getOpenOcdScriptValue} ${command:espIdf.getOpenOcdConfigs}", + "options": { + "env": { + "PATH": "${env:PATH};${config:idf.customExtraPaths}" + } + } + }, + "options": { + "env": { + "PATH": "${env:PATH}:${config:idf.customExtraPaths}" + } + }, + "problemMatcher": { + "owner": "cpp", + "fileLocation": "absolute", + "pattern": { + "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + }, + { + "label": "adapter", + "type": "shell", + "command": "${config:idf.pythonBinPath}", + "isBackground": true, + "options": { + "env": { + "PATH": "${env:PATH}:${config:idf.customExtraPaths}", + "PYTHONPATH": "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter" + } + }, + "problemMatcher": { + "background": { + "beginsPattern": "\bDEBUG_ADAPTER_STARTED\b", + "endsPattern": "DEBUG_ADAPTER_READY2CONNECT", + "activeOnStart": true + }, + "pattern": { + "regexp": "(\\d+)-(\\d+)-(\\d+)\\s(\\d+):(\\d+):(\\d+),(\\d+)\\s-(.+)\\s(ERROR)", + "file": 8, + "line": 2, + "column": 3, + "severity": 4, + "message": 9 + } + }, + "args": [ + "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter_main.py", + "-e", + "${workspaceFolder}/build/${command:espIdf.getProjectName}.elf", + "-s", + "${command:espIdf.getOpenOcdScriptValue}", + "-ip", + "localhost", + "-dn", + "${config:idf.adapterTargetName}", + "-om", + "connect_to_instance" + ], + "windows": { + "command": "${config:idf.pythonBinPathWin}", + "options": { + "env": { + "PATH": "${env:PATH};${config:idf.customExtraPaths}", + "PYTHONPATH": "${command:espIdf.getExtensionPath}/esp_debug_adapter/debug_adapter" + } + } + } + } + ] +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..474ac99 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.5) + +if(DEFINED ENV{HOMEKIT_PATH}) + set(HOMEKIT_PATH $ENV{HOMEKIT_PATH}) +else() + set(HOMEKIT_PATH ${CMAKE_CURRENT_LIST_DIR}/../..) +endif(DEFINED ENV{HOMEKIT_PATH}) + +set(EXTRA_COMPONENT_DIRS ${HOMEKIT_PATH}/components ${HOMEKIT_PATH}/components/homekit ${CMAKE_CURRENT_LIST_DIR}/../common) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp-homekit-door) diff --git a/Makefile b/Makefile new file mode 100755 index 0000000..cbf989e --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +HOMEKIT_PATH ?= $(abspath $(IDF_PATH)/../esp-homekit-sdk) +COMMON_COMPONENT_PATH ?= $(HOMEKIT_PATH)/examples/common + +PROJECT_NAME := esp-homekit-door +EXTRA_COMPONENT_DIRS += $(HOMEKIT_PATH)/components/ +EXTRA_COMPONENT_DIRS += $(HOMEKIT_PATH)/components/homekit +EXTRA_COMPONENT_DIRS += $(COMMON_COMPONENT_PATH) + +include $(IDF_PATH)/make/project.mk diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..5695bd6 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "app_main.c") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild new file mode 100644 index 0000000..d5f429d --- /dev/null +++ b/main/Kconfig.projbuild @@ -0,0 +1,60 @@ +menu "HomeKit Door Configuration" + + config HOMEKIT_USE_HARDCODED_SETUP_CODE + bool "Use hard-coded setup code" + default y + help + HomeKit does not recommend having the setup code programmed in the accessory as is. + Instead, it expects setup info (salt-verifier pair) generated for the given setup code. + Use this option only for testing purposes. For production, the setup info should be + used. + + config HOMEKIT_SETUP_CODE + string "HomeKit Setup Code" + default "111-22-333" + depends on HOMEKIT_USE_HARDCODED_SETUP_CODE + help + Setup code to be used for HomeKit pairing, if hard-coded setup code is enabled. + + config HOMEKIT_SETUP_ID + string "HomeKit Setup Id" + default "ES32" + depends on HOMEKIT_USE_HARDCODED_SETUP_CODE + help + Setup id to be used for HomeKit pairing, if hard-coded setup code is enabled. + + config HOMEKIT_DOOR_LOCK_TIMEOUT + int "HomeKit Door Millisecond Timeout" + default 10000 + help + Milliseconds before locking door + + config HOMEKIT_DOOR_LOCK_GPIO_PIN + int "Door lock GPIO pin number" + range 0 34 + default 21 + help + GPIO pin number (IOxx) to control lock relay. + + config HOMEKIT_DOOR_BELL_GPIO_PIN + int "Doorbell GPIO pin number" + range 0 34 + default 34 + help + GPIO pin number (IOxx) from which to read doorbell signal. + + config HOMEKIT_DOOR_WIFI_RESET_GPIO_PIN + int "Door WIFI reset GPIO pin number" + range 0 34 + default 0 + help + GPIO pin number (IOxx) to reset wifi. + + config HOMEKIT_DOOR_LED_GPIO_PIN + int "LED GPIO pin number" + range 0 34 + default 13 + help + GPIO pin number (IOxx) for LED. + +endmenu diff --git a/main/app_main.c b/main/app_main.c new file mode 100644 index 0000000..13b9c3d --- /dev/null +++ b/main/app_main.c @@ -0,0 +1,325 @@ +/* HomeKit Door Homekit + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +static const char *TAG = "HAP door"; + +#define DOOR_TASK_PRIORITY 1 +#define DOOR_TASK_STACKSIZE 4 * 1024 +#define DOOR_TASK_NAME "hap_door" + +#define RESET_NETWORK_BUTTON_TIMEOUT 3 + +#define DOOR_LOCK_GPIO_LOCKED 0 +#define DOOR_LOCK_GPIO_UNLOCKED 1 + +#define HAP_LOCK_TARGET_STATE_UNSECURED 0 +#define HAP_LOCK_TARGET_STATE_SECURED 1 +static hap_val_t HAP_LOCK_CURRENT_STATE_UNSECURED = {.u = 0}; +static hap_val_t HAP_LOCK_CURRENT_STATE_SECURED = {.u = 1}; +static hap_val_t HAP_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS = {.u = 0}; + +#define ESP_INTR_FLAG_DEFAULT 0 + +static const uint8_t DOOR_EVENT_QUEUE_BELL = 1; +static const uint8_t DOOR_EVENT_QUEUE_UNLOCK = 2; +static const uint8_t DOOR_EVENT_QUEUE_LOCK = 3; +static const uint8_t DOOR_EVENT_QUEUE_LOCK_TIMEOUT = 4; + +static uint8_t tlv8buff[128]; +static hap_data_val_t null_tlv8 = { .buf = &tlv8buff, .buflen = 127 }; + +static xQueueHandle door_event_queue = NULL; +static TimerHandle_t door_lock_timer = NULL; + +/** + * @brief the recover door bell gpio interrupt function + */ +static void IRAM_ATTR door_bell_isr(void* arg) { + xQueueSendFromISR(door_event_queue, (void*) &DOOR_EVENT_QUEUE_BELL, NULL); +} + +/** + * Enable a GPIO Pin for Door Bell + */ +static void door_bell_init(uint32_t key_gpio_pin) { + gpio_config_t io_conf; + + io_conf.intr_type = GPIO_INTR_NEGEDGE; /* Interrupt for falling edge */ + io_conf.pin_bit_mask = 1 << key_gpio_pin; /* Bit mask of the pins */ + io_conf.mode = GPIO_MODE_INPUT; /* Set as input mode */ + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; /* Disable internal pull-up */ + io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; /* Enable internal pull-down */ + + gpio_config(&io_conf); /* Set the GPIO configuration */ + + gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); /* Install gpio isr service */ + gpio_isr_handler_add(key_gpio_pin, door_bell_isr, (void*)key_gpio_pin); /* Hook isr handler for specified gpio pin */ +} + +/** + * Enable a GPIO Pin for Door Lock + */ +static void door_lock_init(uint32_t key_gpio_pin) { + gpio_config_t io_conf; + + io_conf.intr_type = GPIO_INTR_DISABLE; /* Interrupt for falling edge */ + io_conf.pin_bit_mask = 1 << key_gpio_pin; /* Bit mask of the pins */ + io_conf.mode = GPIO_MODE_OUTPUT; /* Set as input mode */ + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; /* Disable internal pull-up */ + io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; /* Enable internal pull-down */ + + gpio_config(&io_conf); /* Set the GPIO configuration */ +} + +/** + * Enable a GPIO Pin for LED + */ +static void led_init(uint32_t key_gpio_pin) { + gpio_config_t io_conf; + + io_conf.intr_type = GPIO_INTR_DISABLE; /* Interrupt for falling edge */ + io_conf.pin_bit_mask = 1 << key_gpio_pin; /* Bit mask of the pins */ + io_conf.mode = GPIO_MODE_OUTPUT; /* Set as input mode */ + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; /* Disable internal pull-up */ + io_conf.pull_down_en = GPIO_PULLDOWN_ENABLE; /* Enable internal pull-down */ + + gpio_config(&io_conf); /* Set the GPIO configuration */ +} + +static void reset_network_handler(void* arg) { + ESP_LOGI(TAG, "Resetting network"); + hap_reset_network(); +} + +static void reset_init(uint32_t key_gpio_pin) { + button_handle_t handle = iot_button_create(key_gpio_pin, BUTTON_ACTIVE_LOW); + iot_button_add_on_release_cb(handle, RESET_NETWORK_BUTTON_TIMEOUT, reset_network_handler, NULL); +} + +/** + * Initialize the Door Hardware. Here, we just enebale the Door Bell detection. + */ +void door_hardware_init(gpio_num_t reset_gpio_num, gpio_num_t door_bell_gpio_num, gpio_num_t door_lock_gpio_num, gpio_num_t led_gpio_num) { + int queue_len = 4; + int queue_item_size = sizeof(uint8_t); + + door_event_queue = xQueueCreate(queue_len, queue_item_size); + if (door_event_queue != NULL) { + /* reset_init(reset_gpio_num); */ + door_bell_init(door_bell_gpio_num); + door_lock_init(door_lock_gpio_num); + led_init(led_gpio_num); + + } +} + +/* Mandatory identify routine for the accessory. + * In a real accessory, something like LED blink should be implemented + * got visual identification + */ +static int door_identify(hap_acc_t *ha) { + ESP_LOGI(TAG, "Accessory identified"); + + for (int i = 0; i < 3; i++) { + gpio_set_level(CONFIG_HOMEKIT_DOOR_LED_GPIO_PIN, 1); + vTaskDelay(pdMS_TO_TICKS(500)); + gpio_set_level(CONFIG_HOMEKIT_DOOR_LED_GPIO_PIN, 0); + vTaskDelay(pdMS_TO_TICKS(500)); + } + + return HAP_SUCCESS; +} + +static void door_bell_ring(hap_char_t *door_bell_current_state) { + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(ADC1_CHANNEL_2, ADC_ATTEN_DB_0); + int val = adc1_get_raw(ADC1_CHANNEL_2); + + /* int level = gpio_get_level(CONFIG_HOMEKIT_DOOR_BELL_GPIO_PIN); */ + + ESP_LOGI(TAG, "Door bell ring event processed [%d]", val); + + /* hap_char_update_val(door_bell_current_state, &HAP_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS); */ +} + +static void door_unlock(hap_char_t *door_lock_current_state) { + ESP_LOGI(TAG, "Door unlock event processed"); + + gpio_set_level(CONFIG_HOMEKIT_DOOR_LOCK_GPIO_PIN, DOOR_LOCK_GPIO_UNLOCKED); + hap_char_update_val(door_lock_current_state, &HAP_LOCK_CURRENT_STATE_UNSECURED); + + xTimerReset(door_lock_timer, 10); +} + +static void door_lock(hap_char_t *door_lock_current_state) { + ESP_LOGI(TAG, "Door lock event processed"); + + gpio_set_level(CONFIG_HOMEKIT_DOOR_LOCK_GPIO_PIN, DOOR_LOCK_GPIO_LOCKED); + hap_char_update_val(door_lock_current_state, &HAP_LOCK_CURRENT_STATE_SECURED); +} + +static void door_lock_timeout(hap_char_t *door_lock_target_state) { + ESP_LOGI(TAG, "Door lock timeout event processed"); + + xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_LOCK, 10); + hap_val_t target_lock_secured = {.u = HAP_LOCK_TARGET_STATE_SECURED}; + hap_char_update_val(door_lock_target_state, &target_lock_secured); +} + +static int door_lock_write_cb(hap_write_data_t write_data[], int count, void *serv_priv, void *write_priv) { + int i, ret = HAP_SUCCESS; + hap_write_data_t *write; + for (i = 0; i < count; i++) { + write = &write_data[i]; + if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_LOCK_TARGET_STATE)) { + ESP_LOGI(TAG, "Received Write. Door lock %d", write->val.u); + + switch (write->val.u) { + case HAP_LOCK_TARGET_STATE_UNSECURED: + xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_UNLOCK, 10); + break; + case HAP_LOCK_TARGET_STATE_SECURED: + xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_LOCK, 10); + break; + } + + /* Update target state */ + hap_char_update_val(write->hc, &(write->val)); + *(write->status) = HAP_STATUS_SUCCESS; + } else { + *(write->status) = HAP_STATUS_RES_ABSENT; + } + } + return ret; +} + +static void door_lock_timer_cb(TimerHandle_t timer) { + ESP_LOGI(TAG, "Door lock timer fired - event queued"); + + xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_LOCK_TIMEOUT, 10); +} + +/*The main thread for handling the Door Accessory */ +static void door_thread_entry(void *p) { + hap_init(HAP_TRANSPORT_WIFI); /* Initialize the HAP core */ + + /* Initialise the mandatory parameters for Accessory which will be added as + * the mandatory services internally + */ + hap_acc_cfg_t cfg = { + .name = "Door", + .manufacturer = "Luke Hoersten", + .model = "Esp32Door01", + .serial_num = "001122334455", + .fw_rev = "0.1.0", + .hw_rev = NULL, + .pv = "1.1.0", + .identify_routine = door_identify, + .cid = HAP_CID_DOOR, + }; + + hap_acc_t *door_accessory = hap_acc_create(&cfg); /* Create accessory object */ + + /* Add a dummy Product Data */ + uint8_t product_data[] = {'E','S','P','3','2','H','A','P'}; + hap_acc_add_product_data(door_accessory, product_data, sizeof(product_data)); + + hap_serv_t *door_bell_service = hap_serv_doorbell_create(0); + hap_serv_add_char(door_bell_service, hap_char_name_create("Doorbell")); + hap_char_t *door_bell_current_state = hap_serv_get_char_by_uuid(door_bell_service, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT); + + hap_serv_t *door_lock_service = hap_serv_lock_mechanism_create(HAP_LOCK_CURRENT_STATE_SECURED.u, HAP_LOCK_TARGET_STATE_SECURED); + hap_serv_add_char(door_lock_service, hap_char_name_create("Door Lock")); + hap_char_t *door_lock_current_state = hap_serv_get_char_by_uuid(door_lock_service, HAP_CHAR_UUID_LOCK_CURRENT_STATE); + hap_char_t *door_lock_target_state = hap_serv_get_char_by_uuid(door_lock_service, HAP_CHAR_UUID_LOCK_TARGET_STATE); + + /* Get pointer to the door in use characteristic which we need to monitor for state changes */ + hap_serv_set_write_cb(door_lock_service, door_lock_write_cb); /* Set the write callback for the service */ + + hap_acc_add_serv(door_accessory, door_bell_service); + hap_acc_add_serv(door_accessory, door_lock_service); + + hap_add_accessory(door_accessory); /* Add the Accessory to the HomeKit Database */ + + /* Initialize the appliance specific hardware. This enables out-in-use detection */ + door_hardware_init(CONFIG_HOMEKIT_DOOR_WIFI_RESET_GPIO_PIN, CONFIG_HOMEKIT_DOOR_BELL_GPIO_PIN, CONFIG_HOMEKIT_DOOR_LOCK_GPIO_PIN, CONFIG_HOMEKIT_DOOR_LED_GPIO_PIN); + + /* For production accessories, the setup code shouldn't be programmed on to + * the device. Instead, the setup info, derived from the setup code must + * be used. Use the factory_nvs_gen utility to generate this data and then + * flash it into the factory NVS partition. + * + * By default, the setup ID and setup info will be read from the factory_nvs + * Flash partition and so, is not required to set here explicitly. + * + * However, for testing purpose, this can be overridden by using hap_set_setup_code() + * and hap_set_setup_id() APIs, as has been done here. + */ +#ifdef CONFIG_HOMEKIT_USE_HARDCODED_SETUP_CODE + /* Unique Setup code of the format xxx-xx-xxx. Default: 111-22-333 */ + hap_set_setup_code(CONFIG_HOMEKIT_SETUP_CODE); + /* Unique four character Setup Id. Default: ES32 */ + hap_set_setup_id(CONFIG_HOMEKIT_SETUP_ID); +#ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING + app_hap_setup_payload(CONFIG_HOMEKIT_SETUP_CODE, CONFIG_HOMEKIT_SETUP_ID, true, cfg.cid); +#else + app_hap_setup_payload(CONFIG_HOMEKIT_SETUP_CODE, CONFIG_HOMEKIT_SETUP_ID, false, cfg.cid); +#endif +#endif + + hap_enable_mfi_auth(HAP_MFI_AUTH_HW); /* Enable Hardware MFi authentication (applicable only for MFi variant of SDK) */ + + app_wifi_init(); /* Initialize Wi-Fi */ + hap_start(); /* After all the initializations are done, start the HAP core */ + app_wifi_start(portMAX_DELAY); /* Start Wi-Fi */ + + door_lock_timer = xTimerCreate("door_lock_timer", pdMS_TO_TICKS(CONFIG_HOMEKIT_DOOR_LOCK_TIMEOUT), pdFALSE, 0, door_lock_timer_cb); + + /* Listen for doorbell state change events. Other read/write functionality will be handled by the HAP Core. When the + * doorbell in Use GPIO goes low, it means doorbell is not ringing. When the Door in Use GPIO goes high, it means + * the doorbell is ringing. Applications can define own logic as per their hardware. + */ + uint8_t door_event_queue_item = DOOR_EVENT_QUEUE_LOCK; + + while (1) { + if (xQueueReceive(door_event_queue, &door_event_queue_item, portMAX_DELAY) == pdFALSE) { + ESP_LOGI(TAG, "Door event queue trigger FAIL"); + } else { + switch(door_event_queue_item) { + case DOOR_EVENT_QUEUE_BELL: + door_bell_ring(door_bell_current_state); + break; + case DOOR_EVENT_QUEUE_UNLOCK: + door_unlock(door_lock_current_state); + break; + case DOOR_EVENT_QUEUE_LOCK: + door_lock(door_lock_current_state); + break; + case DOOR_EVENT_QUEUE_LOCK_TIMEOUT: + door_lock_timeout(door_lock_target_state); + break; + } + } + } +} + +void app_main() { + xTaskCreate(door_thread_entry, DOOR_TASK_NAME, DOOR_TASK_STACKSIZE, NULL, DOOR_TASK_PRIORITY, NULL); +} diff --git a/main/component.mk b/main/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/partitions_hap.csv b/partitions_hap.csv new file mode 100644 index 0000000..f42e062 --- /dev/null +++ b/partitions_hap.csv @@ -0,0 +1,10 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: Firmware partition offset needs to be 64K aligned, initial 36K (9 sectors) are reserved for bootloader and partition table +sec_cert, 0x3F, ,0xd000, 0x3000, , # Never mark this as an encrypted partition +nvs, data, nvs, 0x10000, 0x6000, +otadata, data, ota, , 0x2000 +phy_init, data, phy, , 0x1000, +ota_0, app, ota_0, 0x20000, 1600K, +ota_1, app, ota_1, , 1600K, +factory_nvs, data, nvs, 0x340000, 0x6000 +nvs_keys, data, nvs_keys,0x346000, 0x1000 -- cgit v1.2.3