main/app_main.c
changeset 0 31a65edead29
child 1 289a6264a47e
equal deleted inserted replaced
-1:000000000000 0:31a65edead29
       
     1 /*
       
     2  * ESPRESSIF MIT License
       
     3  *
       
     4  * Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
       
     5  *
       
     6  * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
       
     7  * it is free of charge, to any person obtaining a copy of this software and associated
       
     8  * documentation files (the "Software"), to deal in the Software without restriction, including
       
     9  * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
       
    10  * and/or sell copies of the Software, and to permit persons to whom the Software is furnished
       
    11  * to do so, subject to the following conditions:
       
    12  *
       
    13  * The above copyright notice and this permission notice shall be included in all copies or
       
    14  * substantial portions of the Software.
       
    15  *
       
    16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
       
    18  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
       
    19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
       
    20  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
       
    21  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
       
    22  *
       
    23  */
       
    24 /* HomeKit Door Homekit
       
    25  */
       
    26 #include <stdio.h>
       
    27 #include <string.h>
       
    28 #include <freertos/FreeRTOS.h>
       
    29 #include <freertos/task.h>
       
    30 #include <freertos/timers.h>
       
    31 #include <freertos/queue.h>
       
    32 #include <esp_log.h>
       
    33 #include <driver/gpio.h>
       
    34 
       
    35 #include <hap.h>
       
    36 
       
    37 #include <hap_apple_servs.h>
       
    38 #include <hap_apple_chars.h>
       
    39 
       
    40 #include <app_wifi.h>
       
    41 #include <app_hap_setup_payload.h>
       
    42 
       
    43 static const char *TAG = "HAP door";
       
    44 
       
    45 #define DOOR_TASK_PRIORITY  1
       
    46 #define DOOR_TASK_STACKSIZE 4 * 1024
       
    47 #define DOOR_TASK_NAME      "hap_door"
       
    48 
       
    49 #define DOOR_LOCK_GPIO_PIN GPIO_NUM_21
       
    50 #define DOOR_BELL_GPIO_PIN GPIO_NUM_14
       
    51 #define DOOR_LOCK_GPIO_LOCKED 0
       
    52 #define DOOR_LOCK_GPIO_UNLOCKED 1
       
    53 
       
    54 #define HAP_LOCK_TARGET_STATE_UNSECURED 0
       
    55 #define HAP_LOCK_TARGET_STATE_SECURED 1
       
    56 
       
    57 static hap_val_t HAP_LOCK_CURRENT_STATE_UNSECURED = {.u = 0};
       
    58 static hap_val_t HAP_LOCK_CURRENT_STATE_SECURED = {.u = 1};
       
    59 
       
    60 static hap_val_t HAP_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS = {.u = 0};
       
    61 
       
    62 #define ESP_INTR_FLAG_DEFAULT 0
       
    63 
       
    64 static const uint8_t DOOR_EVENT_QUEUE_BELL = 1;
       
    65 static const uint8_t DOOR_EVENT_QUEUE_UNLOCK = 2;
       
    66 static const uint8_t DOOR_EVENT_QUEUE_LOCK = 3;
       
    67 static const uint8_t DOOR_EVENT_QUEUE_LOCK_TIMEOUT = 4;
       
    68 
       
    69 static xQueueHandle door_event_queue = NULL;
       
    70 static TimerHandle_t door_lock_timer = NULL;
       
    71 
       
    72 /**
       
    73  * @brief the recover door bell gpio interrupt function
       
    74  */
       
    75 static void IRAM_ATTR door_bell_isr(void* arg) {
       
    76 	xQueueSendFromISR(door_event_queue, (void*) &DOOR_EVENT_QUEUE_BELL, NULL);
       
    77 }
       
    78 
       
    79 /**
       
    80  * Enable a GPIO Pin for Door Bell
       
    81  */
       
    82 static void door_bell_init(uint32_t key_gpio_pin) {
       
    83 	gpio_config_t io_conf;
       
    84 
       
    85 	io_conf.intr_type		= GPIO_INTR_NEGEDGE;	 /* Interrupt for falling edge  */
       
    86 	io_conf.pin_bit_mask	= 1 << key_gpio_pin;	 /* Bit mask of the pins */
       
    87 	io_conf.mode			= GPIO_MODE_INPUT;	 /* Set as input mode */
       
    88 	io_conf.pull_up_en	= GPIO_PULLUP_DISABLE;	 /* Disable internal pull-up */
       
    89 	io_conf.pull_down_en	= GPIO_PULLDOWN_DISABLE;		 /* Disable internal pull-down */
       
    90 
       
    91 	gpio_config(&io_conf);	 /* Set the GPIO configuration */
       
    92 
       
    93 	gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);	 /* Install gpio isr service */
       
    94 	gpio_isr_handler_add(key_gpio_pin, door_bell_isr, (void*)key_gpio_pin);		 /* Hook isr handler for specified gpio pin */
       
    95 }
       
    96 
       
    97 /**
       
    98  * Enable a GPIO Pin for Door Lock
       
    99  */
       
   100 static void door_lock_init(uint32_t key_gpio_pin) {
       
   101 	gpio_config_t io_conf;
       
   102 
       
   103 	io_conf.intr_type		= GPIO_INTR_DISABLE;	 /* Interrupt for falling edge  */
       
   104 	io_conf.pin_bit_mask	= 1 << key_gpio_pin;	 /* Bit mask of the pins */
       
   105 	io_conf.mode			= GPIO_MODE_OUTPUT;	 /* Set as input mode */
       
   106 	io_conf.pull_up_en	= GPIO_PULLUP_DISABLE;	 /* Disable internal pull-up */
       
   107 	io_conf.pull_down_en	= GPIO_PULLDOWN_DISABLE;		 /* Disable internal pull-down */
       
   108 
       
   109 	gpio_config(&io_conf);	 /* Set the GPIO configuration */
       
   110 }
       
   111 
       
   112 /**
       
   113  * Initialize the Door Hardware. Here, we just enebale the Door Bell detection.
       
   114  */
       
   115 void door_hardware_init(gpio_num_t door_bell_gpio_num, gpio_num_t door_lock_gpio_num) {
       
   116 	int queue_len = 4;
       
   117 	int queue_item_size = sizeof(uint8_t);
       
   118 
       
   119 	door_event_queue = xQueueCreate(queue_len, queue_item_size);
       
   120 	if (door_event_queue != NULL) {
       
   121 		door_bell_init(door_bell_gpio_num);
       
   122 		door_lock_init(door_lock_gpio_num);
       
   123 	}
       
   124 }
       
   125 
       
   126 /* Mandatory identify routine for the accessory.
       
   127  * In a real accessory, something like LED blink should be implemented
       
   128  * got visual identification
       
   129  */
       
   130 static int door_identify(hap_acc_t *ha) {
       
   131 	ESP_LOGI(TAG, "Accessory identified");
       
   132 	return HAP_SUCCESS;
       
   133 }
       
   134 
       
   135 static void door_bell_ring(hap_char_t *door_bell_current_state) {
       
   136 	ESP_LOGI(TAG, "Door bell ring event processed");
       
   137 
       
   138 	hap_char_update_val(door_bell_current_state, &HAP_PROGRAMMABLE_SWITCH_EVENT_SINGLE_PRESS);
       
   139 }
       
   140 
       
   141 static void door_unlock(hap_char_t *door_lock_current_state) {
       
   142 	ESP_LOGI(TAG, "Door unlock event processed");
       
   143 
       
   144 	gpio_set_level(DOOR_LOCK_GPIO_PIN, DOOR_LOCK_GPIO_UNLOCKED);
       
   145 	hap_char_update_val(door_lock_current_state, &HAP_LOCK_CURRENT_STATE_UNSECURED);
       
   146 
       
   147 	xTimerReset(door_lock_timer, 10);
       
   148 }
       
   149 
       
   150 static void door_lock(hap_char_t *door_lock_current_state) {
       
   151 	ESP_LOGI(TAG, "Door lock event processed");
       
   152 
       
   153 	gpio_set_level(DOOR_LOCK_GPIO_PIN, DOOR_LOCK_GPIO_LOCKED);
       
   154 	hap_char_update_val(door_lock_current_state, &HAP_LOCK_CURRENT_STATE_SECURED);
       
   155 }
       
   156 
       
   157 static void door_lock_timeout(hap_char_t *door_lock_target_state) {
       
   158 	ESP_LOGI(TAG, "Door lock timeout event processed");
       
   159 
       
   160 	xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_LOCK, 10);
       
   161 	hap_val_t target_lock_secured = {.u = HAP_LOCK_TARGET_STATE_SECURED};
       
   162 	hap_char_update_val(door_lock_target_state, &target_lock_secured);
       
   163 }
       
   164 
       
   165 static int door_lock_write_cb(hap_write_data_t write_data[], int count, void *serv_priv, void *write_priv) {
       
   166 	int i, ret = HAP_SUCCESS;
       
   167 	hap_write_data_t *write;
       
   168 	for (i = 0; i < count; i++) {
       
   169 		write = &write_data[i];
       
   170 		if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_LOCK_TARGET_STATE)) {
       
   171 			ESP_LOGI(TAG, "Received Write. Door lock %d", write->val.u);
       
   172 
       
   173 			switch (write->val.u) {
       
   174 				case HAP_LOCK_TARGET_STATE_UNSECURED:
       
   175 					xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_UNLOCK, 10);
       
   176 					break;
       
   177 				case HAP_LOCK_TARGET_STATE_SECURED:
       
   178 					xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_LOCK, 10);
       
   179 					break;
       
   180 			}
       
   181 
       
   182 			/* Update target state */
       
   183 			hap_char_update_val(write->hc, &(write->val));
       
   184 			*(write->status) = HAP_STATUS_SUCCESS;
       
   185 		} else {
       
   186 			*(write->status) = HAP_STATUS_RES_ABSENT;
       
   187 		}
       
   188 	}
       
   189 	return ret;
       
   190 }
       
   191 
       
   192 static void door_lock_timer_cb(TimerHandle_t timer) {
       
   193 	ESP_LOGI(TAG, "Door lock timer fired - event queued");
       
   194 	xQueueSendToBack(door_event_queue, (void*) &DOOR_EVENT_QUEUE_LOCK_TIMEOUT, 10);
       
   195 }
       
   196 
       
   197 /*The main thread for handling the Door Accessory */
       
   198 static void door_thread_entry(void *p) {
       
   199 	hap_init(HAP_TRANSPORT_WIFI);	 /* Initialize the HAP core */
       
   200 
       
   201 	/* Initialise the mandatory parameters for Accessory which will be added as
       
   202 	 * the mandatory services internally
       
   203 	 */
       
   204 	hap_acc_cfg_t cfg = {
       
   205 		.name = "Door",
       
   206 		.manufacturer = "Luke Hoersten",
       
   207 		.model = "Esp32Door01",
       
   208 		.serial_num = "001122334455",
       
   209 		.fw_rev = "0.1.0",
       
   210 		.hw_rev = NULL,
       
   211 		.pv = "1.1.0",
       
   212 		.identify_routine = door_identify,
       
   213 		.cid = HAP_CID_DOOR,
       
   214 	};
       
   215 
       
   216 	hap_acc_t *door_accessory = hap_acc_create(&cfg);	 /* Create accessory object */
       
   217 
       
   218 	/* Add a dummy Product Data */
       
   219 	uint8_t product_data[] = {'E','S','P','3','2','H','A','P'};
       
   220 	hap_acc_add_product_data(door_accessory, product_data, sizeof(product_data));
       
   221 
       
   222 	hap_serv_t *door_bell_service = hap_serv_stateless_programmable_switch_create(0);
       
   223 	hap_serv_add_char(door_bell_service, hap_char_name_create("Doorbell"));
       
   224 	hap_char_t *door_bell_current_state = hap_serv_get_char_by_uuid(door_bell_service, HAP_CHAR_UUID_PROGRAMMABLE_SWITCH_EVENT);
       
   225 
       
   226 	hap_serv_t *door_lock_service = hap_serv_lock_mechanism_create(HAP_LOCK_CURRENT_STATE_SECURED.u, HAP_LOCK_TARGET_STATE_SECURED);
       
   227 	hap_serv_add_char(door_lock_service, hap_char_name_create("Door Lock"));
       
   228 	hap_char_t *door_lock_current_state = hap_serv_get_char_by_uuid(door_lock_service, HAP_CHAR_UUID_LOCK_CURRENT_STATE);
       
   229 	hap_char_t *door_lock_target_state = hap_serv_get_char_by_uuid(door_lock_service, HAP_CHAR_UUID_LOCK_TARGET_STATE);
       
   230 
       
   231 	/* Get pointer to the door in use characteristic which we need to monitor for state changes */
       
   232 	hap_serv_set_write_cb(door_lock_service, door_lock_write_cb);	 /* Set the write callback for the service */
       
   233 
       
   234 	hap_acc_add_serv(door_accessory, door_bell_service);
       
   235 	hap_acc_add_serv(door_accessory, door_lock_service);
       
   236 
       
   237 	hap_add_accessory(door_accessory);	 /* Add the Accessory to the HomeKit Database */
       
   238 
       
   239 	door_hardware_init(DOOR_BELL_GPIO_PIN, DOOR_LOCK_GPIO_PIN);	 /* Initialize the appliance specific hardware. This enables out-in-use detection */
       
   240 
       
   241 	/* For production accessories, the setup code shouldn't be programmed on to
       
   242 	 * the device. Instead, the setup info, derived from the setup code must
       
   243 	 * be used. Use the factory_nvs_gen utility to generate this data and then
       
   244 	 * flash it into the factory NVS partition.
       
   245 	 *
       
   246 	 * By default, the setup ID and setup info will be read from the factory_nvs
       
   247 	 * Flash partition and so, is not required to set here explicitly.
       
   248 	 *
       
   249 	 * However, for testing purpose, this can be overridden by using hap_set_setup_code()
       
   250 	 * and hap_set_setup_id() APIs, as has been done here.
       
   251 	 */
       
   252 #ifdef CONFIG_HOMEKIT_USE_HARDCODED_SETUP_CODE
       
   253 	/* Unique Setup code of the format xxx-xx-xxx. Default: 111-22-333 */
       
   254 	hap_set_setup_code(CONFIG_HOMEKIT_SETUP_CODE);
       
   255 	/* Unique four character Setup Id. Default: ES32 */
       
   256 	hap_set_setup_id(CONFIG_HOMEKIT_SETUP_ID);
       
   257 #ifdef CONFIG_APP_WIFI_USE_WAC_PROVISIONING
       
   258 	app_hap_setup_payload(CONFIG_HOMEKIT_SETUP_CODE, CONFIG_HOMEKIT_SETUP_ID, true, cfg.cid);
       
   259 #else
       
   260 	app_hap_setup_payload(CONFIG_HOMEKIT_SETUP_CODE, CONFIG_HOMEKIT_SETUP_ID, false, cfg.cid);
       
   261 #endif
       
   262 #endif
       
   263 
       
   264 	hap_enable_mfi_auth(HAP_MFI_AUTH_HW);	 /* Enable Hardware MFi authentication (applicable only for MFi variant of SDK) */
       
   265 
       
   266 	app_wifi_init();	 /* Initialize Wi-Fi */
       
   267 	hap_start();	 /* After all the initializations are done, start the HAP core */
       
   268 	app_wifi_start(portMAX_DELAY);	 /* Start Wi-Fi */
       
   269 
       
   270 	door_lock_timer = xTimerCreate("door_lock_timer", pdMS_TO_TICKS(CONFIG_HOMEKIT_DOOR_LOCK_TIMEOUT), pdFALSE, 0, door_lock_timer_cb);
       
   271 
       
   272 	/* Listen for doorbell state change events. Other read/write functionality will be handled by the HAP Core.  When the
       
   273 	 * doorbell in Use GPIO goes low, it means doorbell is not ringing.  When the Door in Use GPIO goes high, it means
       
   274 	 * the doorbell is ringing.  Applications can define own logic as per their hardware.
       
   275 	 */
       
   276 	uint8_t door_event_queue_item = DOOR_EVENT_QUEUE_LOCK;
       
   277 
       
   278 	while (1) {
       
   279 		if (xQueueReceive(door_event_queue, &door_event_queue_item, portMAX_DELAY) == pdFALSE) {
       
   280 			ESP_LOGI(TAG, "Door event queue trigger FAIL");
       
   281 		} else {
       
   282 			switch(door_event_queue_item) {
       
   283 				case DOOR_EVENT_QUEUE_BELL:
       
   284 					door_bell_ring(door_bell_current_state);
       
   285 					break;
       
   286 				case DOOR_EVENT_QUEUE_UNLOCK:
       
   287 					door_unlock(door_lock_current_state);
       
   288 					break;
       
   289 				case DOOR_EVENT_QUEUE_LOCK:
       
   290 					door_lock(door_lock_current_state);
       
   291 					break;
       
   292 				case DOOR_EVENT_QUEUE_LOCK_TIMEOUT:
       
   293 					door_lock_timeout(door_lock_target_state);
       
   294 					break;
       
   295 			}
       
   296 		}
       
   297 	}
       
   298 }
       
   299 
       
   300 void app_main() {
       
   301 	xTaskCreate(door_thread_entry, DOOR_TASK_NAME, DOOR_TASK_STACKSIZE, NULL, DOOR_TASK_PRIORITY, NULL);
       
   302 }