Skip to content

Commit

Permalink
feat: switch to using PlatformIO scripting to build SDK from source
Browse files Browse the repository at this point in the history
* chore: set to cleanup build by default

* refactor: remove static libaries

* feat: begin to use PlatfomIO's extra script for build

* feat: finalise building library from source

* docs: include basic fan example

* docs: update README
  • Loading branch information
Brawrdon committed Jan 7, 2021
1 parent d025451 commit 620f5ad
Show file tree
Hide file tree
Showing 21 changed files with 279 additions and 4,728 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.tmp
.DS_Store
*.tar*
*.tar*
lib/*
!lib/esp_hap_config.h
1 change: 1 addition & 0 deletions ESP32HomeKit.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#include <ESP32HomeKit.h>
3 changes: 3 additions & 0 deletions ESP32HomeKit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "lib/hap.h"
#include "lib/hap_apple_chars.h"
#include "lib/hap_apple_servs.h"
204 changes: 4 additions & 200 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,7 @@

This library provides the official [ESP-IDF HomeKit SDK](https://github.com/espressif/esp-homekit-sdk) for ESP32 devices running the Arduino framework.

**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!

## Building Process

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.

Below is a list of modified entries in `sdkconfig` that are used to build the example project:

- `CONFIG_HAP_HTTP_MAX_OPEN_SOCKETS=8` => 6
- `CONFIG_HAP_HTTP_SERVER_PORT=80` => 8080

The adapted version be found here: [esp-idf3-homekit-sdk](https://github.com/Brawrdon/esp-idf3-homekit-sdk).
**Note:** This wrapper uses a version of the SDK which can't be used in commercial products due to it not being MFi certified, feel free to use it in your hobby projects though!

## Installation

Expand All @@ -23,202 +12,17 @@ If you're using [PlatformIO](https://docs.platformio.org/en/latest/librarymanage
pio lib install 'ESP32 HomeKit SDK for Arduino'
```

You can also use the Arduino IDE to install the library via Library Manager or ZIP file.
You can use the Arduino IDE to install the library by downloading the prebuilt ZIP file in the Releases section.

## Usage

This is based on the fan example of the original ESP-IDF SDK.

```cpp
#include <Arduino.h>
#include <Wifi.h>
#include <hap.h>
#include <hap_apple_servs.h>
#include <hap_apple_chars.h>

const char* ssid = "yourNetworkName";
const char* password = "yourNetworkPassword";


/* Mandatory identify routine for the accessory.
* In a real accessory, something like LED blink should be implemented
* got visual identification
*/
static int identify(hap_acc_t *ha)
{
ESP_LOGI(TAG, "Accessory identified");
return HAP_SUCCESS;
}

/* A dummy callback for handling a read on the "Direction" characteristic of Fan.
* In an actual accessory, this should read from hardware.
* Read routines are generally not required as the value is available with th HAP core
* when it is updated from write routines. For external triggers (like fan switched on/off
* using physical button), accessories should explicitly call hap_char_update_val()
* instead of waiting for a read request.
*/
static int fan_read(hap_char_t *hc, hap_status_t *status_code, void *serv_priv, void *read_priv)
{
if (hap_req_get_ctrl_id(read_priv)) {
ESP_LOGI(TAG, "Received read from %s", hap_req_get_ctrl_id(read_priv));
}
if (!strcmp(hap_char_get_type_uuid(hc), HAP_CHAR_UUID_ROTATION_DIRECTION)) {
/* Read the current value, toggle it and set the new value.
* A separate variable should be used for the new value, as the hap_char_get_val()
* API returns a const pointer
*/
const hap_val_t *cur_val = hap_char_get_val(hc);

hap_val_t new_val;
if (cur_val->i == 1) {
new_val.i = 0;
} else {
new_val.i = 1;
}
hap_char_update_val(hc, &new_val);
*status_code = HAP_STATUS_SUCCESS;
}
return HAP_SUCCESS;
}


/* A dummy callback for handling a write on the "On" characteristic of Fan.
* In an actual accessory, this should control the hardware
*/
static int fan_write(hap_write_data_t write_data[], int count, void *serv_priv, void *write_priv)
{
if (hap_req_get_ctrl_id(write_priv))
{
ESP_LOGI(TAG, "Received write from %s", hap_req_get_ctrl_id(write_priv));
}

ESP_LOGI(TAG, "Fan Write called with %d chars", count);
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_ON))
{
ESP_LOGI(TAG, "Received Write. Fan %s", write->val.b ? "On" : "Off");

/* TODO: Control Actual Hardware */
hap_char_update_val(write->hc, &(write->val));
*(write->status) = HAP_STATUS_SUCCESS;
}
else if (!strcmp(hap_char_get_type_uuid(write->hc), HAP_CHAR_UUID_ROTATION_DIRECTION))
{
if (write->val.i > 1)
{
*(write->status) = HAP_STATUS_VAL_INVALID;
ret = HAP_FAIL;
}
else
{
ESP_LOGI(TAG, "Received Write. Fan %s", write->val.i ? "AntiClockwise" : "Clockwise");
hap_char_update_val(write->hc, &(write->val));
*(write->status) = HAP_STATUS_SUCCESS;
}
}
else
{
*(write->status) = HAP_STATUS_RES_ABSENT;
}
}
return ret;
}

void setup(){
Serial.begin(115200);

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED)
{
delay(1000);
Serial.println("Establishing connection to WiFi..");
}
An example of implementing a basic fan accessory can be found in the examples folder. It's based off of the fan example from the origin ESP-IDF SDK.

Serial.println("Connected to network.");

hap_acc_t *accessory;
hap_serv_t *service;

/* Configure HomeKit core to make the Accessory name (and thus the WAC SSID) unique,
* instead of the default configuration wherein only the WAC SSID is made unique.
*/
hap_cfg_t hap_cfg;
hap_get_config(&hap_cfg);
hap_cfg.unique_param = UNIQUE_NAME;
hap_set_config(&hap_cfg);

/* Initialize the HAP core */
hap_init(HAP_TRANSPORT_WIFI);

/* Initialise the mandatory parameters for Accessory which will be added as
* the mandatory services internally
*/
hap_acc_cfg_t cfg = {
.name = "Esp-Fan",
.model = "Espressif",
.manufacturer = "EspFan01",
.serial_num = "001122334455",
.fw_rev = "0.0.1",
.hw_rev = NULL,
.pv = "1.1.0",
.cid = HAP_CID_FAN,
.identify_routine = identify,
};

/* Create accessory object */
accessory = hap_acc_create(&cfg);

/* Add a dummy Product Data */
uint8_t product_data[] = {'P', 'L', 'A', 'N', 'T', 'K', 'I', 'T'};
hap_acc_add_product_data(accessory, product_data, sizeof(product_data));

/* Create the Fan Service. Include the "name" since this is a user visible service */
service = hap_serv_fan_create(false);
hap_serv_add_char(service, hap_char_name_create("My Fan"));
hap_serv_add_char(service, hap_char_rotation_direction_create(0));

/* Set the write callback for the service */
hap_serv_set_write_cb(service, fan_write);

/* Set the read callback for the service (optional) */
hap_serv_set_read_cb(service, fan_read);

/* Add the Fan Service to the Accessory Object */
hap_acc_add_serv(accessory, service);

/* Add the Accessory to the HomeKit Database */
hap_add_accessory(accessory);

/* Query the controller count (just for information) */
ESP_LOGI(TAG, "Accessory is paired with %d controllers",
hap_get_paired_controller_count());

/* TODO: Do the actual hardware initialization here */

/* Unique Setup code of the format xxx-xx-xxx. Default: 111-22-333 */
hap_set_setup_code("111-22-333");
/* Unique four character Setup Id. Default: ES32 */
hap_set_setup_id("ES32");

/* After all the initializations are done, start the HAP core */
hap_start();
}

void loop() {
/* Main loop code */
}
```
As the Arduino style API wrappers haven't been implemented yet, if you have any questions or issues it's best that you visit the ESP-IDF HomeKit SDK repository for additional help.

## To Do

- [ ] Add Arduino API wrappers to make it easier to use.
- [ ] Break down and explain usage example.
- [ ] Currently unified provisioning is enabled but I haven't tested if it actually works.

## License
[MIT](https://choosealicense.com/licenses/mit/)
64 changes: 64 additions & 0 deletions build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import subprocess
import os
import glob
import shutil
from distutils.dir_util import copy_tree
from pathlib import Path
import sys

idf_homekit_directory = "lib/esp-homekit-sdk/"
arduino_homekit_directory = "lib/homekit"

priv_includes = "lib/priv_includes"

if len(glob.glob("lib/*.c")) == 0:
# Create the directory path for the ESP-IDF SDK
Path(idf_homekit_directory).mkdir(parents=True, exist_ok=True)

# Check if a .git folder exists, if so assume the project's been cloned.
git_folder = Path(idf_homekit_directory + ".git")
if not git_folder.exists() and not Path(arduino_homekit_directory).exists(): # Add and lib/homekit so i can delete it
commit = "040b0f301223ebc6995597328e5a5cc9f9739a02"
process = subprocess.call(["git", "clone", "--recursive", "https://github.com/espressif/esp-homekit-sdk.git", idf_homekit_directory], stdout=open(os.devnull, 'wb'))
process = subprocess.call(["git", "--git-dir", str(git_folder), "checkout", commit], stdout=open(os.devnull, 'wb'))
shutil.copytree(idf_homekit_directory + "components/homekit", arduino_homekit_directory)
shutil.rmtree(idf_homekit_directory)

for (dirpath, dirnames, filenames) in os.walk(Path(arduino_homekit_directory)):
# 1. Ignore example and test(s)
if "example" in dirpath or "test" in dirpath or "tests" in dirpath:
continue
for file in filenames:
if file.endswith(".c") or file.endswith(".h") and file[:-2] not in mfi_source:
shutil.move(dirpath + "/" + file, "lib/")

#
shutil.rmtree(arduino_homekit_directory)

replacements = {"<jsmn/jsmn.h>": "\"jsmn.h\"", "\"esp32/rom/ets_sys.h\"": "\"rom/ets_sys.h\""}
esp_hap_config = "#include \"esp_hap_config.h\"\n"
for (dirpath, dirnames, filenames) in os.walk("lib"):
for file in filenames:
if file.endswith(".h"):
replacements["<" + file + ">"] = "\"" + file + "\""

for file in filenames:
lines = []
with open("lib/" + file) as infile:
for line in infile:
for src, target in replacements.items():
line = line.replace(src, target)

# Messy manual additions / changes for specific files and lines
if file == "esp_hap_main.c" and "hap_platform_os.h" in line:
line += esp_hap_config
if file == "hap_platform_httpd.c" and "<esp_http_server.h>" in line:
line += esp_hap_config
if file == "esp_mfi_i2c.c" and "esp_mfi_i2c.h" in line:
line += esp_hap_config

lines.append(line)

with open("lib/" + file, 'w') as outfile:
for line in lines:
outfile.write(line)
60 changes: 0 additions & 60 deletions build.sh

This file was deleted.

0 comments on commit 620f5ad

Please sign in to comment.