|
1 |
| -# esp-homekit-arduino-sdk |
2 |
| -Arduino wrapper for ESP32 Espressif IDF HomeKit Library |
| 1 | +# ESP32 HomeKit Accessory Protocol SDK for Arduino |
| 2 | + |
| 3 | +This library provides the official [ESP-IDF HomeKit SDK](https://github.com/espressif/esp-homekit-sdk) for ESP32 devices running the Arduino framework. |
| 4 | + |
| 5 | +**Note:** This wrapper uses a version of the SDK which can't be used in commercial products due to it not being MFi certified. Other changes would also mean the library would fail HomeKit certifications. Feel free to use it in your hobby projects though! |
| 6 | + |
| 7 | +## Building Process |
| 8 | + |
| 9 | +Since Arduino for the ESP32 currently supports version 3.3 of the ESP-IDF framework, this library uses a version of the HomeKit SDK that has been ported to ESP-IDF v3.3. An example project is built using the same `sdkconfig` that the Arduino framework to build itself, with the outputted static libraries being extracted. You can view the `build.sh` script to see this process. |
| 10 | + |
| 11 | +Below is a list of modified entries in `sdkconfig` that are used to build the example project: |
| 12 | + |
| 13 | +- `CONFIG_HAP_HTTP_MAX_OPEN_SOCKETS=8` => 6 |
| 14 | +- `CONFIG_HAP_HTTP_SERVER_PORT=80` => 8080 |
| 15 | + |
| 16 | +The adapted version be found here: [esp-idf3-homekit-sdk](https://github.com/Brawrdon/esp-idf3-homekit-sdk). |
| 17 | + |
| 18 | +## Installation |
| 19 | + |
| 20 | +If you're using [PlatformIO](https://docs.platformio.org/en/latest/librarymanager/quickstart.html), update your `platformio.ini` file's dependancies or use the `pio` command in the directory containing your `platformio.ini` file: |
| 21 | + |
| 22 | +```bash |
| 23 | +pio lib install 'ESP32 HomeKit SDK for Arduino' |
| 24 | +``` |
| 25 | + |
| 26 | +You can also use the Arduino IDE to install the library via Library Manager or ZIP file. |
| 27 | + |
| 28 | +## Usage |
| 29 | + |
| 30 | +This is based on the fan example of the original ESP-IDF SDK. |
| 31 | + |
| 32 | +```cpp |
| 33 | +#include <Arduino.h> |
| 34 | +#include <Wifi.h> |
| 35 | +#include <hap.h> |
| 36 | +#include <hap_apple_servs.h> |
| 37 | +#include <hap_apple_chars.h> |
| 38 | + |
| 39 | +const char* ssid = "yourNetworkName"; |
| 40 | +const char* password = "yourNetworkPassword"; |
| 41 | + |
| 42 | + |
| 43 | +/* Mandatory identify routine for the accessory. |
| 44 | + * In a real accessory, something like LED blink should be implemented |
| 45 | + * got visual identification |
| 46 | + */ |
| 47 | +static int identify(hap_acc_t *ha) |
| 48 | +{ |
| 49 | + ESP_LOGI(TAG, "Accessory identified"); |
| 50 | + return HAP_SUCCESS; |
| 51 | +} |
| 52 | + |
| 53 | +/* A dummy callback for handling a read on the "Direction" characteristic of Fan. |
| 54 | + * In an actual accessory, this should read from hardware. |
| 55 | + * Read routines are generally not required as the value is available with th HAP core |
| 56 | + * when it is updated from write routines. For external triggers (like fan switched on/off |
| 57 | + * using physical button), accessories should explicitly call hap_char_update_val() |
| 58 | + * instead of waiting for a read request. |
| 59 | + */ |
| 60 | +static int fan_read(hap_char_t *hc, hap_status_t *status_code, void *serv_priv, void *read_priv) |
| 61 | +{ |
| 62 | + if (hap_req_get_ctrl_id(read_priv)) { |
| 63 | + ESP_LOGI(TAG, "Received read from %s", hap_req_get_ctrl_id(read_priv)); |
| 64 | + } |
| 65 | + if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_ROTATION_DIRECTION)) { |
| 66 | + /* Read the current value, toggle it and set the new value. |
| 67 | + * A separate variable should be used for the new value, as the hap_char_get_val() |
| 68 | + * API returns a const pointer |
| 69 | + */ |
| 70 | + const hap_val_t *cur_val = hap_char_get_val(hc); |
| 71 | + |
| 72 | + hap_val_t new_val; |
| 73 | + if (cur_val->i == 1) { |
| 74 | + new_val.i = 0; |
| 75 | + } else { |
| 76 | + new_val.i = 1; |
| 77 | + } |
| 78 | + hap_char_update_val(hc, &new_val); |
| 79 | + *status_code = HAP_STATUS_SUCCESS; |
| 80 | + } |
| 81 | + return HAP_SUCCESS; |
| 82 | +} |
| 83 | + |
| 84 | + |
| 85 | +/* A dummy callback for handling a write on the "On" characteristic of Fan. |
| 86 | + * In an actual accessory, this should control the hardware |
| 87 | + */ |
| 88 | +static int fan_write(hap_write_data_t write_data[], int count, void *serv_priv, void *write_priv) |
| 89 | +{ |
| 90 | + if (hap_req_get_ctrl_id(write_priv)) |
| 91 | + { |
| 92 | + ESP_LOGI(TAG, "Received write from %s", hap_req_get_ctrl_id(write_priv)); |
| 93 | + } |
| 94 | + |
| 95 | + ESP_LOGI(TAG, "Fan Write called with %d chars", count); |
| 96 | + int i, ret = HAP_SUCCESS; |
| 97 | + hap_write_data_t *write; |
| 98 | + for (i = 0; i < count; i++) |
| 99 | + { |
| 100 | + write = &write_data[i]; |
| 101 | + if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ON)) |
| 102 | + { |
| 103 | + ESP_LOGI(TAG, "Received Write. Fan %s", write->val.b ? "On" : "Off"); |
| 104 | + |
| 105 | + /* TODO: Control Actual Hardware */ |
| 106 | + hap_char_update_val(write->hc, &(write->val)); |
| 107 | + *(write->status) = HAP_STATUS_SUCCESS; |
| 108 | + } |
| 109 | + else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ROTATION_DIRECTION)) |
| 110 | + { |
| 111 | + if (write->val.i > 1) |
| 112 | + { |
| 113 | + *(write->status) = HAP_STATUS_VAL_INVALID; |
| 114 | + ret = HAP_FAIL; |
| 115 | + } |
| 116 | + else |
| 117 | + { |
| 118 | + ESP_LOGI(TAG, "Received Write. Fan %s", write->val.i ? "AntiClockwise" : "Clockwise"); |
| 119 | + hap_char_update_val(write->hc, &(write->val)); |
| 120 | + *(write->status) = HAP_STATUS_SUCCESS; |
| 121 | + } |
| 122 | + } |
| 123 | + else |
| 124 | + { |
| 125 | + *(write->status) = HAP_STATUS_RES_ABSENT; |
| 126 | + } |
| 127 | + } |
| 128 | + return ret; |
| 129 | +} |
| 130 | + |
| 131 | +void setup(){ |
| 132 | + Serial.begin(115200); |
| 133 | + |
| 134 | + WiFi.begin(ssid, password); |
| 135 | + |
| 136 | + while (WiFi.status() != WL_CONNECTED) |
| 137 | + { |
| 138 | + delay(1000); |
| 139 | + Serial.println("Establishing connection to WiFi.."); |
| 140 | + } |
| 141 | + |
| 142 | + Serial.println("Connected to network."); |
| 143 | + |
| 144 | + hap_acc_t *accessory; |
| 145 | + hap_serv_t *service; |
| 146 | + |
| 147 | + /* Configure HomeKit core to make the Accessory name (and thus the WAC SSID) unique, |
| 148 | + * instead of the default configuration wherein only the WAC SSID is made unique. |
| 149 | + */ |
| 150 | + hap_cfg_t hap_cfg; |
| 151 | + hap_get_config(&hap_cfg); |
| 152 | + hap_cfg.unique_param = UNIQUE_NAME; |
| 153 | + hap_set_config(&hap_cfg); |
| 154 | + |
| 155 | + /* Initialize the HAP core */ |
| 156 | + hap_init(HAP_TRANSPORT_WIFI); |
| 157 | + |
| 158 | + /* Initialise the mandatory parameters for Accessory which will be added as |
| 159 | + * the mandatory services internally |
| 160 | + */ |
| 161 | + hap_acc_cfg_t cfg = { |
| 162 | + .name = "Esp-Fan", |
| 163 | + .model = "Espressif", |
| 164 | + .manufacturer = "EspFan01", |
| 165 | + .serial_num = "001122334455", |
| 166 | + .fw_rev = "0.0.1", |
| 167 | + .hw_rev = NULL, |
| 168 | + .pv = "1.1.0", |
| 169 | + .cid = HAP_CID_FAN, |
| 170 | + .identify_routine = identify, |
| 171 | + }; |
| 172 | + |
| 173 | + /* Create accessory object */ |
| 174 | + accessory = hap_acc_create(&cfg); |
| 175 | + |
| 176 | + /* Add a dummy Product Data */ |
| 177 | + uint8_t product_data[] = {'P', 'L', 'A', 'N', 'T', 'K', 'I', 'T'}; |
| 178 | + hap_acc_add_product_data(accessory, product_data, sizeof(product_data)); |
| 179 | + |
| 180 | + /* Create the Fan Service. Include the "name" since this is a user visible service */ |
| 181 | + service = hap_serv_fan_create(false); |
| 182 | + hap_serv_add_char(service, hap_char_name_create("My Fan")); |
| 183 | + hap_serv_add_char(service, hap_char_rotation_direction_create(0)); |
| 184 | + |
| 185 | + /* Set the write callback for the service */ |
| 186 | + hap_serv_set_write_cb(service, fan_write); |
| 187 | + |
| 188 | + /* Set the read callback for the service (optional) */ |
| 189 | + hap_serv_set_read_cb(service, fan_read); |
| 190 | + |
| 191 | + /* Add the Fan Service to the Accessory Object */ |
| 192 | + hap_acc_add_serv(accessory, service); |
| 193 | + |
| 194 | + /* Add the Accessory to the HomeKit Database */ |
| 195 | + hap_add_accessory(accessory); |
| 196 | + |
| 197 | + /* Query the controller count (just for information) */ |
| 198 | + ESP_LOGI(TAG, "Accessory is paired with %d controllers", |
| 199 | + hap_get_paired_controller_count()); |
| 200 | + |
| 201 | + /* TODO: Do the actual hardware initialization here */ |
| 202 | + |
| 203 | + /* Unique Setup code of the format xxx-xx-xxx. Default: 111-22-333 */ |
| 204 | + hap_set_setup_code("111-22-333"); |
| 205 | + /* Unique four character Setup Id. Default: ES32 */ |
| 206 | + hap_set_setup_id("ES32"); |
| 207 | + |
| 208 | + /* After all the initializations are done, start the HAP core */ |
| 209 | + hap_start(); |
| 210 | +} |
| 211 | + |
| 212 | +void loop() { |
| 213 | + /* Main loop code */ |
| 214 | +} |
| 215 | +``` |
| 216 | +
|
| 217 | +## To Do |
| 218 | +
|
| 219 | +- [ ] Add Arduino API wrappers to make it easier to use. |
| 220 | +- [ ] Break down and explain usage example. |
| 221 | +- [ ] Currently unified provisioning is enabled but I haven't tested if it actually works. |
| 222 | +
|
| 223 | +## License |
| 224 | +[MIT](https://choosealicense.com/licenses/mit/) |
0 commit comments