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