Now that I made a few examples of the HomeKit accessories, It’s time for some in depth HomeKit Code Analysis. For this analysis I took the LED example from the HomeKit Demo library witch I used in my first blog here to demonstrate how it works. First I start with cutting up the code and try to explain whats happening in that part.So let’s start the HomeKit Code Analysis!

Libraries

Libraries are files written in C  (.c) which provide your sketches with extra functionality (e.g. the ability to control an e.g LED, or read an encoder, etc.).

To use a library you need to insert an #include statement at the top of the sketch for each header (.h). These statements make the public functions and constants defined by the library available to your sketch. They also signal the compiler environment to link that library’s code with your sketch when it is compiled.

If a sketch no longer needs a library, simply delete its #include statements from the top of your code. This will stop the compiler from linking the library with your sketch and decrease the amount of space used on the ESP module. The other possibility is to “disable the Library temporarily by adding // before the #include statements.

//Start defining libraries
#include <stdio.h>
#include <espressif/esp_wifi.h>
#include <espressif/esp_sta.h>
#include <esp/uart.h>
#include <esp8266.h>
#include <FreeRTOS.h>
#include <task.h>

#include <homekit/homekit.h>
#include <homekit/characteristics.h>
#include <wifi_config.h>
//End defining libraries

(stdio.h) C library to perform Input/Output operations
Input and Output operations can also be performed in C++ using the C Standard Input and Output Library (cstdio, known as stdio.h in the C language). This library uses what are called streams to operate with physical devices such as keyboards, printers, terminals or with any other type of files supported by the system. Streams are an abstraction to interact with these in an uniform way; All streams have similar properties independently of the individual characteristics of the physical media they are associated with.

(esp_wifi.h) The WiFi libraries provide support for configuring and monitoring the ESP32 WiFi networking functionality.

This includes configuration for:

  • Station mode (aka STA mode or WiFi client mode). ESP32 connects to an access point.
  • AP mode (aka Soft-AP mode or Access Point mode). Stations connect to the ESP32.
  • Combined AP-STA mode (ESP32 is concurrently an access point and a station connected to another access point).
  • Various security modes for the above (WPA, WPA2, WEP, etc.)
  • Scanning for access points (active & passive scanning).
  • Promiscuous mode for monitoring of IEEE802.11 WiFi packets.

(esp_sta.h) Station API
This library scans for available Wifi networks.

(uart.h) UART Library
Interrupt UART library using the built-in UART with transmit and receive circular buffers. This library can be used to transmit and receive data through the built in UART. An interrupt is generated when the UART has finished transmitting or receiving a byte. The interrupt handling routines use circular buffers for buffering received and transmitted data.

(esp8266.h) ESP8266WiFi library
ESP8266WiFi library has been developed basing on ESP8266 SDK, using naming convention and overall functionality philosophy of the Arduino WiFi Shield library. Over time the wealth Wi-Fi features ported from ESP8266 SDK to this library outgrew the APIs of WiFi Shield library and it became apparent that we need to provide separate documentation on what is new and extra.

(FreeRTOS.h) Real Time Operating System
FreeRTOS is a real-time operating system kernel for embedded devices that has been ported to 35 micro-controller platforms. It is distributed under the MIT License. FreeRTOS is designed to be small and simple. The kernel itself consists of only three C files. To make the code readable, easy to port, and maintainable, it is written mostly in C, but there are a few assembly functions included where needed (mostly in architecture-specific scheduler routines).

(homekit.h) Apple HomeKit Accessory Protocol (HAP) library for ESP-OPEN-RTOS
HomeKit Accessory Protocol (HAP) is Apple’s proprietary protocol that enables third-party accessories in the home (e.g., lights, thermostats and door locks) and Apple products to communicate with each other. HAP supports two transports, IP and Bluetooth LE. The information provided in the HomeKit Accessory Protocol Specification (Non-Commercial Version) describes how to implement HAP in an accessory that you create for non-commercial use and that will not be distributed or sold.

(characteristics.h) Apple HomeKit accessory server library for ESP-OPEN-RTOS
This library can create an accessory that implements HAP using custom characteristics. In order to control this accessory, you will need to develop a custom app that uses HomeKit APIs. You will not be able to control it through the Home app or Siri.

(wifi_config.h) Library for esp-open-rtos to bootstrap WiFi-enabled accessories WiFi config
Library uses sysparams to store configuration. When you initialize it it tries to connect to configured WiFi network. If no configuration exists or network is not available, it starts it’s own WiFi AP (with given name and optional password). AP runs a captive portal, so when user connects to it a popup window is displayed asking user to select one of WiFi networks that are present in that location (and a password if network is secured) and configures device to connect to that network. After successful connection it calls provided callback so you can continue accessory initialization.

 

Identify Hardware

If you use more than one ESP module, it can be useful that you can identify each ESP module before you make a change to it. First we define the GPIO where the LED is connected to. We use the onboard LED. Here after we say that when it’s activated blink twice – three times.

// Start Indentify with onboard LED
const int led_gpio = 2; 
bool led_on = false;  

void led_write(bool on) {
    gpio_write(led_gpio, on ? 0 : 1);
}

void led_init() {
    gpio_enable(led_gpio, GPIO_OUTPUT);
    led_write(led_on);
}

void led_identify_task(void *_args) {
    for (int i=0; i<3; i++) {
        for (int j=0; j<2; j++) {
            led_write(true);
            vTaskDelay(100 / portTICK_PERIOD_MS);
            led_write(false);
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }

        vTaskDelay(250 / portTICK_PERIOD_MS);
    }

    led_write(led_on);

    vTaskDelete(NULL);
}

void led_identify(homekit_value_t _value) {
    printf("LED identify\n");
    xTaskCreate(led_identify_task, "LED identify", 128, NULL, 2, NULL);
}

// End Indentify with onboard LED

When You add your Accessory to HomeKit you will see a button called Identify Accessory when you press it you will see that the Onboard LED of your ESP module will blink like this.

 

 

 

Hardware Settings

Now we need to define what the attached hardware needs to do, in this case the (onboard) LED needs to turn on and off. the code below looks at the state that HomeKit sends back. when te returned value is not recognized it returns Invalid value format: with the value it received. so this part is for debugging.

//Start Hardware Settings
homekit_value_t led_on_get() {
    return HOMEKIT_BOOL(led_on);
}

void led_on_set(homekit_value_t value) {
    if (value.format != homekit_format_bool) {
        printf("Invalid value format: %d\n", value.format);
        return;
    }

    led_on = value.bool_value;
    led_write(led_on);
}
//End Hardware Settings

 

 

HomeKit Settings

In this part of the code we are going to define the settings for HomeKit we need choose the right category and fill in some fields:

NAME:Here you can fill in the accessories name
MANUFACTURER:Name of the “Manufacturer”.
SERIAL_NUMBER:The Serial number so you can track your “Production”.
MODEL:Module Number, in case you have several models.
FIRMWARE_REVISION:Here you can put the Firmware revision number, This is important if you are still developing your device, to keep track.
IDENTIFY:Here you put “led_identify” this will execute the LED identify part as described above.

Hereafter you can a function to the HomeKit device in this case to be able to turn on and of the LED. When everything is set we need to add a password to the device. By default this is “111-11-111”.

// Start HomeKit Settings
homekit_accessory_t *accessories[] = {
    HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_lightbulb, .services=(homekit_service_t*[]){
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]){
            HOMEKIT_CHARACTERISTIC(NAME, "LED"),
            HOMEKIT_CHARACTERISTIC(MANUFACTURER, "StudioPieters®"),
            HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "037A2BABF19D"),
            HOMEKIT_CHARACTERISTIC(MODEL, "A3SP1/A1"),
            HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.0.1"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, led_identify),
            NULL
        }),
        HOMEKIT_SERVICE(LIGHTBULB, .primary=true, .characteristics=(homekit_characteristic_t*[]){
            HOMEKIT_CHARACTERISTIC(NAME, "LED"),
            HOMEKIT_CHARACTERISTIC(
                ON, false,
                .getter=led_on_get,
                .setter=led_on_set
            ),
            NULL
        }),
        NULL
    }),
    NULL
};

homekit_server_config_t config = {
    .accessories = accessories,
    .password = "111-11-111"
};

// End HomeKit Settings
Execute

Now that everything is set we can  tell the software to run it. we also set the communication baud rate on witch it can communicate trough e.g a serial monitor.

//Start Running Software
void user_init(void) {
    uart_set_baud(0, 115200);

    //wifi_init();
    wifi_config_init();
    led_init();
    homekit_server_init(&config);
}
//End Running Software
Complete code

When we put everything together, this is what you will get. This makes a basic configuration for a HomeKit device.

//Start defining libraries
#include <stdio.h>
#include <espressif/esp_wifi.h>
#include <espressif/esp_sta.h>
#include <esp/uart.h>
#include <esp8266.h>
#include <FreeRTOS.h>
#include <task.h>

#include <homekit/homekit.h>
#include <homekit/characteristics.h>
#include <wifi_config.h>
//End defining libraries

// Start Indentify with onboard LED
const int led_gpio = 2; 
bool led_on = false;  

void led_write(bool on) {
    gpio_write(led_gpio, on ? 0 : 1);
}

void led_init() {
    gpio_enable(led_gpio, GPIO_OUTPUT);
    led_write(led_on);
}

void led_identify_task(void *_args) {
    for (int i=0; i<3; i++) {
        for (int j=0; j<2; j++) {
            led_write(true);
            vTaskDelay(100 / portTICK_PERIOD_MS);
            led_write(false);
            vTaskDelay(100 / portTICK_PERIOD_MS);
        }

        vTaskDelay(250 / portTICK_PERIOD_MS);
    }

    led_write(led_on);

    vTaskDelete(NULL);
}

void led_identify(homekit_value_t _value) {
    printf("LED identify\n");
    xTaskCreate(led_identify_task, "LED identify", 128, NULL, 2, NULL);
}

// End Indentify with onboard LED
//Start Hardware Settings
homekit_value_t led_on_get() {
    return HOMEKIT_BOOL(led_on);
}

void led_on_set(homekit_value_t value) {
    if (value.format != homekit_format_bool) {
        printf("Invalid value format: %d\n", value.format);
        return;
    }

    led_on = value.bool_value;
    led_write(led_on);
}
//End Hardware Settings
homekit_accessory_t *accessories[] = {
    HOMEKIT_ACCESSORY(.id=1, .category=homekit_accessory_category_lightbulb, .services=(homekit_service_t*[]){
        HOMEKIT_SERVICE(ACCESSORY_INFORMATION, .characteristics=(homekit_characteristic_t*[]){
            HOMEKIT_CHARACTERISTIC(NAME, "LED"),
            HOMEKIT_CHARACTERISTIC(MANUFACTURER, "StudioPieters®"),
            HOMEKIT_CHARACTERISTIC(SERIAL_NUMBER, "037A2BABF19D"),
            HOMEKIT_CHARACTERISTIC(MODEL, "A3SP1/A1"),
            HOMEKIT_CHARACTERISTIC(FIRMWARE_REVISION, "0.0.1"),
            HOMEKIT_CHARACTERISTIC(IDENTIFY, led_identify),
            NULL
        }),
        HOMEKIT_SERVICE(LIGHTBULB, .primary=true, .characteristics=(homekit_characteristic_t*[]){
            HOMEKIT_CHARACTERISTIC(NAME, "LED"),
            HOMEKIT_CHARACTERISTIC(
                ON, false,
                .getter=led_on_get,
                .setter=led_on_set
            ),
            NULL
        }),
        NULL
    }),
    NULL
};

homekit_server_config_t config = {
    .accessories = accessories,
    .password = "111-11-111"
};

// End HomeKit Settings
//Start Running Software
void user_init(void) {
    uart_set_baud(0, 115200);

    //wifi_init();
    wifi_config_init();
    led_init();
    homekit_server_init(&config);
}
//End Running Software

Now its ready to be compiled!

Note: To produce and sell HomeKit compatible accessories, your company need to be certified for that (https://developer.apple.com/homekit/, If you’re interested in developing or manufacturing a HomeKit accessory that will be distributed or sold, your company must enroll in the MFi Program.) Espressif have their implementation of HomeKit framework, but it will give you it only if you have MFi certification (notice this text at the bottom of page you mentioned: Please note that the Espressif HomeKit SDK is available to MFi licensees only, and you need to provide the Account Number for verification purposes when requesting the SDK.).This project is a non-commercial implementation of HAP protocol, not meant for commercial use.


REFERENCE

Maxim Kulkin,  esp-wifi-config (2019), Library to bootstrap WiFi-enabled accessories WiFi config, https://github.com/maximkulkin/esp-wifi-config Paul Sokolovsky,  esp-open-sdk (2019), Free and open (as much as possible) integrated SDK for ESP8266/ESP8285 chips, https://github.com/pfalcon/esp-open-sdk Espressif Systems,  esptool  (2019), ESP8266 and ESP32 serial bootloader utility,  https://github.com/espressif/esptool HomeACcessoryKid,  life-cycle-manager (2019), Initial install, WiFi settings and over the air firmware upgrades for any esp-open-rtos repository on GitHub, https://github.com/HomeACcessoryKid/life-cycle-manager