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 ``` :::