Apple HomekitADK for Linux(include raspi, esp32)
===
## 1. Env:
- ubuntu 20.04 arm64/amd64/x86_64
## 2. Official HomekitADK Open Source:
- Apple homekitADK: https://github.com/apple/HomeKitADK
---
1. Install dependence:
```
sudo apt install clang libavahi-compat-libdnssd-dev libsqlite3-dev libfaac-dev libasound-dev libopus-dev libavahi-compat-libdnssd-dev git docker.io
```
2. Download ADK
```
git clone https://github.com/apple/HomeKitADK
```
3. Compiler example (透過docker)
```
cd HomeKitADK
sudo make TARGET=Linux apps
```
4. Compiler Tools and generate key (驗證金鑰生成工具):
```
cd HomeKitADK
sudo make TARGET=tools
./Tools/provision_raspi.sh --category 5 --setup-code 111-22-333 .HomeKitStore
```
:::info
1. .HomeKitStore檔案生成位置,要位於等等要執行程式的位置。
2. 111-22-333 為連結驗證金鑰
:::
5. Execute example (執行範例):
```
cd HomeKitADK
./Output/Linux-x86_64-pc-linux-gnu/Debug/IP/Applications/Lightbulb.OpenSSL
```
:::info
路徑中的"Linux-x86_64-pc-linux-gnu",表示我目前編譯的平台。若是不同平台,生成的目錄名稱會不同喔。
:::
6. 與這台裝置連上同一個區域網路,並開啟IOS裝置的家庭程式,新增裝置。
:::info
APPLE 官網: https://support.apple.com/zh-tw/HT204893
:::
---
以上藉著範例,說明ADK該如何使用。接著,我們實際修改ADK,串聯新的裝置。以下範例透過PC模擬(Raspberry pi做法與PC相同,只要加入自己的感測器即可)。
* ADK將程式專案放置在Applications目錄:
- https://github.com/apple/HomeKitADK/tree/master/Applications/Lightbulb
* ADK以裝置為整體,裝置內部可以搭載不同感測器(例如: 溫度感測器等)與不同控制功能(燈控制)。主要以APP.c, APP.h, DB.c, DB.h為主要裝置程式。
* DB.c,DB.h:
- 描述了裝置內部組成。例如: 溫溼度感測裝置,就描述了溫度感測器和濕度感測器的資料,以及其相關服務。一個裝置可以包含多個服務。例如: 可以同時包含溫濕度感測與電燈開關。
* APP.c,App.h:
- 描述裝置整個資訊,包含廠商,類型,序號,提供的服務等。這兩個檔案也包含整體裝置讀取寫入的實作。
接著,我們可以開始建立自己的服務了。
1. 從APP.c開始:
```Clike=
static HAPAccessory accessory = { .aid = 1,
.category = kHAPAccessoryCategory_Lighting,
.name = "Acme Light Bulb",
.manufacturer = "Acme",
.model = "LightBulb1,1",
.serialNumber = "099DB48E9E28",
.firmwareVersion = "1",
.hardwareVersion = "1",
.services = (const HAPService* const[]) {
&accessoryInformationService,
&hapProtocolInformationService,
&pairingService,
&lightBulbService,
NULL },
.callbacks = { .identify = IdentifyAccessory } };
```
* 該結構描述了整個裝置資訊及提供的服務。
```
1. category 裝置的類型
2. name 裝置的名稱
3. manufacturer 廠商名稱
4. model 模組及版本
5. serialNumber 裝置序號
6. firmwareVersion 韌體版本
7. hardwareVersion 硬體版本
8. services 包含的服務
```
:::info
裝置的類型 宣告於: https://github.com/apple/HomeKitADK/blob/master/HAP/HAP.h
:::
:::info
服務描述放置在DB.c,範例如下:
```Clike=
/**
* The Light Bulb service that contains the 'On' characteristic.
*/
const HAPService lightBulbService = {
.iid = kIID_LightBulb,
.serviceType = &kHAPServiceType_LightBulb,
.debugDescription = kHAPServiceDebugDescription_LightBulb,
.name = "Light Bulb",
.properties = { .primaryService = true, .hidden = false, .ble = { .supportsConfiguration = false } },
.linkedServices = NULL,
.characteristics = (const HAPCharacteristic* const[]) { &lightBulbServiceSignatureCharacteristic,
&lightBulbNameCharacteristic,
&lightBulbOnCharacteristic,
NULL }
};
```
:::
2. 接著來到DB.c:
我們找到lightBulbService的實作:
```Clike=
const HAPService lightBulbService = {
.iid = kIID_LightBulb,
.serviceType = &kHAPServiceType_LightBulb,
.debugDescription = kHAPServiceDebugDescription_LightBulb,
.name = "Light Bulb",
.properties = { .primaryService = true, .hidden = false, .ble = { .supportsConfiguration = false } },
.linkedServices = NULL,
.characteristics = (const HAPCharacteristic* const[]) { &lightBulbServiceSignatureCharacteristic,
&lightBulbNameCharacteristic,
&lightBulbOnCharacteristic,
NULL }
};
```
:::info
1. iid: 服務ID。唯一,不可與其他重複。
2. serviceType: 服務類型。表示這個服務應該包含的內容。定義在[HAPServiceTypes.h](https://github.com/apple/HomeKitADK/blob/master/HAP/HAPServiceTypes.h)
```Clike=
/**
* Light Bulb.
*
* This service describes a light bulb.
*
* Required Characteristics:
* - On
*
* Optional Characteristics:
* - Brightness
* - Hue
* - Name
* - Saturation
* - Color Temperature
*
* @see HomeKit Accessory Protocol Specification R14
* Section 8.23 Light Bulb
*/
/**@{*/
#define kHAPServiceDebugDescription_LightBulb "lightbulb"
extern const HAPUUID kHAPServiceType_LightBulb;
```
4. debugDescription: 裝置描述。與服務類型成對存在。
5. name: 服務名稱。
6. properties: 屬性。
7. linkedServices: 連結的服務。
8. characteristics: 服務的特性。表示服務的組成。
:::
透過lightBulbService,我們看到電燈開關包含了兩個特性。
```
lightBulbNameCharacteristic,
lightBulbOnCharacteristic,
```
實作一樣在DB.c裡,我們以lightBulbOnCharacteristic當作講解目標(其他都一樣)。
```Clike=
/**
* The 'On' characteristic of the Light Bulb service.
*/
const HAPBoolCharacteristic lightBulbOnCharacteristic = {
.format = kHAPCharacteristicFormat_Bool,
.iid = kIID_LightBulbOn,
.characteristicType = &kHAPCharacteristicType_On,
.debugDescription = kHAPCharacteristicDebugDescription_On,
.manufacturerDescription = NULL,
.properties = { .readable = true,
.writable = true,
.supportsEventNotification = true,
.hidden = false,
.requiresTimedWrite = false,
.supportsAuthorizationData = false,
.ip = { .controlPoint = false, .supportsWriteResponse = false },
.ble = { .supportsBroadcastNotification = true,
.supportsDisconnectedNotification = true,
.readableWithoutSecurity = false,
.writableWithoutSecurity = false } },
.callbacks = { .handleRead = HandleLightBulbOnRead, .handleWrite = HandleLightBulbOnWrite }
};
```
:::info
1. 裝置輸出入類型: HAPBoolCharacteristic, kHAPCharacteristicFormat_Bool。不同特性要用不同類型。定義在[HAPCharacteristic.h](https://github.com/apple/HomeKitADK/blob/master/HAP/HAPCharacteristic.h)
```Clike=
/**@}*/
/**
* On.
*
* This characteristic represents the states for "on" and "off".
*
* - Format: Bool
* - Permissions: Paired Read, Paired Write, Notify
*
* @see HomeKit Accessory Protocol Specification R14
* Section 9.70 On
*/
/**@*/
#define kHAPCharacteristicDebugDescription_On "on"
extern const HAPUUID kHAPCharacteristicType_On;
```
2. iid: id必須唯一。
3. characteristicType: 特性類型。
4. debugDescription: 特性描述。與特性類型成對存在。
5. properties: 特性的屬性(是否可讀可寫等等)。
6. callbacks: 讀取和寫入的回乎函式(撰寫在APP.c, APP.h)。
:::
3. 等等app.c會用到callbacks, 服務這些服務函式或者變數。因此,將這些我們剛剛宣告的寫到db.h之中:
```Clike=
/**
* Light Bulb service.
*/
extern const HAPService lightBulbService;
/**
* The 'On' characteristic of the Light Bulb service.
*/
extern const HAPBoolCharacteristic lightBulbOnCharacteristic;
```
4. 我們來到app.h,先宣告兩個callback函式的原型。
```Clike=
/**
* Handle read request to the 'On' characteristic of the Light Bulb service.
*/
HAP_RESULT_USE_CHECK
HAPError HandleLightBulbOnRead(
HAPAccessoryServerRef* server,
const HAPBoolCharacteristicReadRequest* request,
bool* value,
void* _Nullable context);
/**
* Handle write request to the 'On' characteristic of the Light Bulb service.
*/
HAP_RESULT_USE_CHECK
HAPError HandleLightBulbOnWrite(
HAPAccessoryServerRef* server,
const HAPBoolCharacteristicWriteRequest* request,
bool value,
void* _Nullable context);
```
:::info
變數傳入型態由裝置特性(HAPXXXCharacteristic)的型態決定。
該變數表示由裝置傳入的需求。
```
const HAPBoolCharacteristicReadRequest* request,
```
該變數表示要回傳給裝置的資料。
```
bool* value,
```
該變數表示由裝置傳入的資料。
```
bool value,
```
:::
5. 我們來到app.c,實作這兩個callback函式。
```Clike=
HAP_RESULT_USE_CHECK
HAPError HandleLightBulbOnRead(
HAPAccessoryServerRef* server HAP_UNUSED,
const HAPBoolCharacteristicReadRequest* request HAP_UNUSED,
bool* value,
void* _Nullable context HAP_UNUSED) {
*value = accessoryConfiguration.state.lightBulbOn;
HAPLogInfo(&kHAPLog_Default, "%s: %s", __func__, *value ? "true" : "false");
return kHAPError_None;
}
HAP_RESULT_USE_CHECK
HAPError HandleLightBulbOnWrite(
HAPAccessoryServerRef* server,
const HAPBoolCharacteristicWriteRequest* request,
bool value,
void* _Nullable context HAP_UNUSED) {
HAPLogInfo(&kHAPLog_Default, "%s: %s", __func__, value ? "true" : "false");
if (accessoryConfiguration.state.lightBulbOn != value) {
accessoryConfiguration.state.lightBulbOn = value;
SaveAccessoryState();
HAPAccessoryServerRaiseEvent(server, request->characteristic, request->service, request->accessory);
}
return kHAPError_None;
}
```
:::info
由於APPLE對於程式碼需求較高,編譯警告會全部變成錯誤,確保程式碼品質。
因此,對於傳入未使用的參數請用
```
HAP_UNUSED
```
修飾它
:::
6. 回到根目錄,編譯程式:
```
cd HomeKitADK
sudo make TARGET=tools
```
---
Espressif官方已經將ADK porting到ESP-IDF了。因此可透過官方範例,快速開發。(本篇不介紹ESP-IDF的安裝與使用教學)
:::info
esp-apple-homekit-adk: https://github.com/espressif/esp-apple-homekit-adk
:::
第一步,下載官方範例:
```
git clone --recursive https://github.com/espressif/esp-apple-homekit-adk.git
```
第二步,切換ESP-IDF的mbedtls版本:
```
cd $IDF_PATH/components/mbedtls/mbedtls
git pull
git checkout -b mbedtls-2.16.6-adk origin/mbedtls-2.16.6-adk
```
第三步,編譯和燒錄:
``` Clike=
// 進入範例
cd /path/to/esp-apple-homekit-adk/examples/Lightbulb
// 設定燒錄裝置位置
export ESPPORT=/dev/tty.SLAB_USBtoUART #Set your board's serial port here
// 指定裝置
idf.py set-target <esp32/esp32s2>
// 專案設定
idf.py menuconfig # Set Example Configuration -> WiFi SSID/Password
// 程式燒錄
idf.py flash
// 驗證資訊燒錄
esptool.py -p $ESPPORT write_flash 0x340000 accessory_setup.bin
```
:::info
如果,IOT裝置無法被Apple裝置找到,基本上就是驗證資訊問題。這時,需要將ESP32 Flash擦除,重新執行整個燒錄過程。
```
esptool.py -p $ESPPORT erase_flash
idf.py set-target <esp32/esp32s2>
idf.py flash
esptool.py -p $ESPPORT write_flash 0x340000 accessory_setup.bin
```
:::