diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d7f062 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +.config +*.o +*.pyc + + +# Example project files +examples/**/sdkconfig +examples/**/sdkconfig.old +examples/**/build + +# emacs +.dir-locals.el + +# eclipse setting +.settings + +# vscode +.vscode + +# Doc build artifacts +docs/*/_build/ +docs/*/doxygen-warning-log.txt +docs/*/sphinx-warning-log.txt +docs/*/sphinx-warning-log-sanitized.txt +docs/*/xml/ +docs/*/xml_in/ +docs/*/man/ +docs/doxygen_sqlite3.db + +# MacOS directory files +.DS_Store + +# Tools +tools/factory_nvs_gen/*.bin +tools/factory_nvs_gen/*.csv + +# Patch files +*.patch + +#CTags +tags diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..9ea704f --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,92 @@ +stages: + - build + +build_homekit_examples: + stage: build + image: $CI_DOCKER_REGISTRY/esp32-ci-env + tags: + - build + + variables: + IDF_PATH: "$CI_PROJECT_DIR/esp-idf" + GIT_STRATEGY: clone + + artifacts: + paths: + - examples/fan/build/*.bin + - examples/fan/build/*.elf + - examples/fan/build/*.map + - examples/fan/build/bootloader/*.bin + expire_in: 6 mos + + before_script: + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - echo -n $GITLAB_KEY >> ~/.ssh/id_rsa_base64 + - base64 --decode --ignore-garbage ~/.ssh/id_rsa_base64 > ~/.ssh/id_rsa + - chmod 600 ~/.ssh/id_rsa + - echo -e "Host gitlab.espressif.cn\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config + + script: + - git submodule update --init --recursive + # Cloning esp-idf + - git clone -b release/v4.2 --recursive --depth 1 https://github.com/espressif/esp-idf.git + - cd esp-idf + - ./install.sh + - . export.sh + + - cd ../examples/fan + - idf.py build + - cd ../bridge + - idf.py build + - cd ../lightbulb + - idf.py build + - cd ../smart_outlet + - idf.py build + - cd ../data_tlv8 + - idf.py build + - cd ../aws-iot + - mkdir components + - cd components + - git clone --depth 1 --single-branch --recursive https://github.com/espressif/esp-aws-iot.git + - export AWS_IOT_PATH=$PWD/esp-aws-iot + - cd .. + - touch main/certs/device.crt main/certs/device.key + - idf.py build + - cd ../emulator + - idf.py build + - cd ../ethernet_bridge + - idf.py build + - cd ../fan + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 + - cd ../bridge + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 + - cd ../lightbulb + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 + - cd ../smart_outlet + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 + - cd ../data_tlv8 + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 + - cd ../aws-iot + - rm -rf build sdkconfig sdkconfig.old + - touch main/certs/device.crt main/certs/device.key + - make defconfig + - make -j4 + - cd ../emulator + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 + - cd ../ethernet_bridge + - rm -rf build sdkconfig sdkconfig.old + - make defconfig + - make -j4 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c3b42fc --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "components/homekit/json_parser/upstream"] + path = components/homekit/json_parser/upstream + url = https://github.com/shahpiyushv/json_parser.git +[submodule "components/homekit/json_generator/upstream"] + path = components/homekit/json_generator/upstream + url = https://github.com/shahpiyushv/json_generator.git diff --git a/MFI_README.md b/MFI_README.md new file mode 100644 index 0000000..6b3e156 --- /dev/null +++ b/MFI_README.md @@ -0,0 +1,3 @@ +# ESP HomeKit SDK (MFi) + +The Open source variant of esp-homekit-sdk cannot be used for commercial products. If you want to build commercial products and have an MFi License already, please get in touch with us by visiting [here](https://www.espressif.com/en/products/sdks/esp-homekit-sdk). diff --git a/README.md b/README.md index d0df919..133b944 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,129 @@ # ESP HomeKit SDK +## Introduction -Espressif HomeKit SDK developed in-house \ No newline at end of file +[HomeKit](https://developer.apple.com/homekit/) is a framework developed by Apple for communicating with and controlling connected accessories in a user’s home using iOS devices. +ESP HomeKit SDK has been developed in-house by Espressif to build Apple HomeKit compatible accessories using ESP32/ESP32-S2 SoCs. + +> Note: If you want to use HomeKit for commercial products, please check [here](https://www.espressif.com/en/products/sdks/esp-homekit-sdk) for access to the MFi variant of this SDK. It has the exact same APIs as this and so, moving to it should be easy. + +> If you want to use a port of Apple's ADK instead, please check [here](https://github.com/espressif/esp-apple-homekit-adk) + +Features of this SDK: + +* Easy APIs to implement all standard HomeKit profiles defined by Apple. +* Facility to add customised accessory specific Services and Characteristics. +* Samples (Fan, Lightbulb, Outlet, Bridge, Data-TLV8, Ethernet) to enable quick accessory development. +* Support for ESP Unified Provisioning. + +## Get Started + +### Set up Host environment + +Set up the host environment and ESP IDF (**master** branch) as per the steps given [here](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html). + +### Get esp-homekit-sdk + +Please clone this repository using the below command: + +```text +git clone --recursive https://github.com/espressif/esp-homekit-sdk.git +``` + +> Note the --recursive option. This is required to pull in the JSON dependencies into esp-homekit-sdk. In case you have already cloned the repository without this option, execute this to pull in the submodules: +> `git submodule update --init --recursive` + + +### Compile and Flash + +You can use esp-homekit-sdk with any ESP32 or ESP32-S2 board. We have provided multiple examples for reference. Compile and flash as below (fan used as example): + +```text +$ cd /path/to/esp-homekit-sdk/examples/fan +$ export ESPPORT=/dev/tty.SLAB_USBtoUART #Set your board's serial port here +$ idf.py set-target +$ idf.py flash monitor +``` + +As the device boots up, you will see two QR codes, a small one for HomeKit and a larger one for Wi-Fi provisioning. Please use any of the [Espressif Provisioning Apps](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/provisioning/provisioning.html#provisioning-tools) for Wi-Fi provisioning. + +> Note: For the Open source HomeKit SDK, the HomeKit QR code cannot be used for provisioning from the Home app. It can be used only for pairing, after the Wi-Fi provisioning is done. For provisioning from Home app, you will need the MFi variant of the SDK. + +> Note: If you want to use hard-coded credentials instead of Provisioning, please set the ssid and passphrase by navigating to `idf.py menuconfig -> App Wi-Fi -> Source of Wi-Fi Credentials -> Use Hardcoded` + +After the device connects to your Home Wi-Fi network it can be added in the Home app + +### Add acccessory in the Home app + +Open the Home app on your iPhone/iPad and follow these steps + +- Tap on "Add Accessory" and scan the small QR code mentioned above. +- If QR code is not visible correctly, you may use the link printed on the serial terminal or follow these steps: + - Choose the "I Don't Have a Code or Cannot Scan" option. + - Tap on "Esp-Fan-xxxxxx" in the list of Nearby Accessories. + - Select the "Add Anyway" option for the "Uncertified Accessory" prompt. + - Enter 11122333 as the Setup code. +- You should eventually see the "Esp-Fan-xxxxxx added" message. +- Give a custom name, assign to a room, create scenes as required and you are done. + +### Changing the Setup Code + +The HomeKit setup code can be changed by using the `CONFIG_EXAMPLE_SETUP_CODE` config option (`idf.py menuconfig -> Example Configuration -> HomeKit Setup Code`). Please also refer the [HomeKit Setup Configuration](#homeKit-setup-configuration) below. + + +## Using Standard Apple HomeKit Profiles +The examples provided in the SDK are for fan, outlet, lightbulb. For using any other standard profiles, please refer the files under `components/homekit/hap_apple_profiles/`. + +## Adding Custom Profiles +Please refer the Firmware Upgrades Custom Profile (`components/homekit/extras/`) to see how to add your own profile. You can use some UUID generator like [this](https://www.uuidgenerator.net/) to generate your own UUIDs for the services and characteristics. + +## HomeKit Setup Configuration + +As per the HomeKit requirements, the raw setup code should not be stored on the accessory. Instead, the SRP Salt and Verifier generated from the code should be stored. This salt-verifier combination, called as setup info in the ESP HomeKit SDK, needs to be unique for every accessory. This can be provided to the HAP Core using the `hap_set_setup_info()` API. Another parameter required for setup is the setup id, which also needs to be unique. It is provided to the HAP Core using `hap_set_setup_id()` API. + +In order to ease the task of providing unique setup info and setup id for each accessory, instead of using the above APIs, the same information can be stored in a separate flash partition called factory\_nvs (Please see the `partitions_hap.csv` file in any example). This allows to keep the same firmware image and just flash different factory\_nvs images for different accessories. The factory\_nvs image fan\_factory.bin has been provided for Fan, which can be flashed using esptool. Custom images can be created using the `factory_nvs_gen` utility in tools/ as below + +Usage: + +``` +./factory_nvs_gen.py 11122333 ES32 factory +``` + +> Replace ES32 and 11122333 with the setup code and setup id of your choice. This has been tested with **Python 3.7.3**. + +Flash this using the esptool as below. + +```text +$ esptool.py -p $ESPPORT write_flash 0x340000 factory.bin +``` + +To use the setup info from this image, you will have to disable the hardcoded setup code by setting `CONFIG_EXAMPLE_SETUP_CODE=n` (`idf.py menuconfig -> Example Configuration -> Use hard-coded setup code -> disable`). + +The factory\_nvs partition can also hold other information like serial numbers, cloud credentials, firmware verification keys, etc. For that, just create a CSV file as per the requirements of [NVS Partition Generator Utility](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/storage/nvs_partition_gen.html#) and provide it to the `factory_nvs_gen` using the `--infile` option. These variables (if stored as binary data) can then be read in the accessory code using `hap_factory_keystore_get()` API. + +Sample CSV (say sample.csv) + +``` +key,type,encoding,value +app,namespace,, +serial_num,data,binary,12345 +``` + +Usage: + +``` +./factory_nvs_gen.py --infile sample.csv 11122333 ES32 factory +``` + +Flash this using the same command given above. + +```text +$ esptool.py -p $ESPPORT write_flash 0x340000 factory.bin +``` + +## Additional MFi requirements + +If you have access to the MFi variant of esp-homekit-sdk, please check additional information [here](MFI_README.md). + +## Reporting issues + +You can report issues directly on [Github issues](https://github.com/espressif/esp-homekit-sdk/issues). diff --git a/components/button/CMakeLists.txt b/components/button/CMakeLists.txt new file mode 100644 index 0000000..1135b6d --- /dev/null +++ b/components/button/CMakeLists.txt @@ -0,0 +1,9 @@ +set(COMPONENT_ADD_INCLUDEDIRS ./button/include) + +# Edit following two lines to set component requirements (see docs) +set(COMPONENT_REQUIRES ) +set(COMPONENT_PRIV_REQUIRES ) + +set(COMPONENT_SRCS ./button/button.c ./button/button_obj.cpp) + +register_component() diff --git a/components/button/Kconfig b/components/button/Kconfig new file mode 100644 index 0000000..8125118 --- /dev/null +++ b/components/button/Kconfig @@ -0,0 +1,6 @@ +menu "Button" + config IO_GLITCH_FILTER_TIME_MS + int "IO glitch filter timer ms (10~100)" + range 10 100 + default 50 +endmenu \ No newline at end of file diff --git a/components/button/button/button.c b/components/button/button/button.c new file mode 100644 index 0000000..a8a4144 --- /dev/null +++ b/components/button/button/button.c @@ -0,0 +1,363 @@ +/* + * ESPRSSIF MIT License + * + * Copyright (c) 2015 + * + * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define IOT_CHECK(tag, a, ret) if(!(a)) { \ + ESP_LOGE(tag,"%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__); \ + return (ret); \ + } +#define ERR_ASSERT(tag, param) IOT_CHECK(tag, (param) == ESP_OK, ESP_FAIL) +#define POINT_ASSERT(tag, param, ret) IOT_CHECK(tag, (param) != NULL, (ret)) + +typedef enum { + BUTTON_STATE_IDLE = 0, + BUTTON_STATE_PUSH, + BUTTON_STATE_PRESSED, +} button_status_t; + +typedef struct button_dev button_dev_t; +typedef struct btn_cb button_cb_t; + +struct btn_cb{ + TickType_t interval; + button_cb cb; + void* arg; + uint8_t on_press; + TimerHandle_t tmr; + button_dev_t *pbtn; + button_cb_t *next_cb; +}; + +struct button_dev{ + uint8_t io_num; + uint8_t active_level; + uint32_t serial_thres_sec; + uint8_t taskq_on; + QueueHandle_t taskq; + QueueHandle_t argq; + button_status_t state; + button_cb_t tap_short_cb; + button_cb_t tap_psh_cb; + button_cb_t tap_rls_cb; + button_cb_t press_serial_cb; + button_cb_t* cb_head; +}; + +#define BUTTON_GLITCH_FILTER_TIME_MS CONFIG_IO_GLITCH_FILTER_TIME_MS +static const char* TAG = "button"; + +static void button_press_cb(xTimerHandle tmr) +{ + button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr); + button_dev_t* btn = btn_cb->pbtn; + // low, then restart + if (btn->active_level == gpio_get_level(btn->io_num)) { + btn->state = BUTTON_STATE_PRESSED; + if (btn->taskq != NULL && btn->argq != NULL && btn->taskq_on && !btn_cb->on_press) { + void *tmp = btn_cb->cb; + xQueueOverwrite(btn->taskq, &tmp); + xQueueOverwrite(btn->argq, &btn_cb->arg); + } else if (btn_cb->cb) { + btn_cb->cb(btn_cb->arg); + } + } +} + +static void button_tap_psh_cb(xTimerHandle tmr) +{ + button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr); + button_dev_t* btn = btn_cb->pbtn; + xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY); + int lv = gpio_get_level(btn->io_num); + + if (btn->active_level == lv) { + // True implies key is pressed + btn->state = BUTTON_STATE_PUSH; + if (btn->press_serial_cb.tmr) { + xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY); + xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY); + } + if (btn->tap_psh_cb.cb) { + btn->tap_psh_cb.cb(btn->tap_psh_cb.arg); + } + } else { + // 50ms, check if this is a real key up + if (btn->tap_rls_cb.tmr) { + xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY); + xTimerReset(btn->tap_rls_cb.tmr, portMAX_DELAY); + } + } +} + +static void button_tap_rls_cb(xTimerHandle tmr) +{ + button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr); + button_dev_t* btn = btn_cb->pbtn; + xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY); + if (btn->active_level == gpio_get_level(btn->io_num)) { + + } else { + // high, then key is up + button_cb_t *pcb = btn->cb_head; + while (pcb != NULL) { + if (pcb->tmr != NULL) { + xTimerStop(pcb->tmr, portMAX_DELAY); + } + pcb = pcb->next_cb; + } + if (btn->taskq != NULL && btn->argq != NULL && btn->taskq_on && uxQueueMessagesWaiting(btn->taskq) != 0 && btn->state != BUTTON_STATE_IDLE) { + void (*task)(void*); + void *arg; + xQueueReceive(btn->taskq, &task, 0); + xQueueReceive(btn->argq, &arg, 0); + task(arg); + } + if (btn->press_serial_cb.tmr && btn->press_serial_cb.tmr != NULL) { + xTimerStop(btn->press_serial_cb.tmr, portMAX_DELAY); + } + if (btn->tap_short_cb.cb && btn->state == BUTTON_STATE_PUSH) { + btn->tap_short_cb.cb(btn->tap_short_cb.arg); + } + if(btn->tap_rls_cb.cb && btn->state != BUTTON_STATE_IDLE) { + btn->tap_rls_cb.cb(btn->tap_rls_cb.arg); + } + btn->state = BUTTON_STATE_IDLE; + } +} + +static void button_press_serial_cb(xTimerHandle tmr) +{ + button_dev_t* btn = (button_dev_t*) pvTimerGetTimerID(tmr); + if (btn->press_serial_cb.cb) { + btn->press_serial_cb.cb(btn->press_serial_cb.arg); + } + xTimerChangePeriod(btn->press_serial_cb.tmr, btn->press_serial_cb.interval, portMAX_DELAY); + xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY); +} + +static void button_gpio_isr_handler(void* arg) +{ + button_dev_t* btn = (button_dev_t*) arg; + portBASE_TYPE HPTaskAwoken = pdFALSE; + int level = gpio_get_level(btn->io_num); + if (level == btn->active_level) { + if (btn->tap_psh_cb.tmr) { + xTimerStopFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken); + xTimerResetFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken); + } + + button_cb_t *pcb = btn->cb_head; + while (pcb != NULL) { + if (pcb->tmr != NULL) { + xTimerStopFromISR(pcb->tmr, &HPTaskAwoken); + xTimerResetFromISR(pcb->tmr, &HPTaskAwoken); + } + pcb = pcb->next_cb; + } + } else { + // 50ms, check if this is a real key up + if (btn->tap_rls_cb.tmr) { + xTimerStopFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken); + xTimerResetFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken); + } + } + if(HPTaskAwoken == pdTRUE) { + portYIELD_FROM_ISR(); + } +} + +static void button_free_tmr(xTimerHandle* tmr) +{ + if (tmr && *tmr) { + xTimerStop(*tmr, portMAX_DELAY); + xTimerDelete(*tmr, portMAX_DELAY); + *tmr = NULL; + } +} + +esp_err_t iot_button_delete(button_handle_t btn_handle) +{ + POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); + button_dev_t* btn = (button_dev_t*) btn_handle; + gpio_set_intr_type(btn->io_num, GPIO_INTR_DISABLE); + gpio_isr_handler_remove(btn->io_num); + + button_free_tmr(&btn->tap_rls_cb.tmr); + button_free_tmr(&btn->tap_psh_cb.tmr); + button_free_tmr(&btn->tap_short_cb.tmr); + button_free_tmr(&btn->press_serial_cb.tmr); + + button_cb_t *pcb = btn->cb_head; + while (pcb != NULL) { + button_cb_t *cb_next = pcb->next_cb; + button_free_tmr(&pcb->tmr); + free(pcb); + pcb = cb_next; + } + free(btn); + return ESP_OK; +} + +button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level) +{ + IOT_CHECK(TAG, gpio_num < GPIO_NUM_MAX, NULL); + button_dev_t* btn = (button_dev_t*) calloc(1, sizeof(button_dev_t)); + POINT_ASSERT(TAG, btn, NULL); + btn->active_level = active_level; + btn->io_num = gpio_num; + btn->state = BUTTON_STATE_IDLE; + btn->taskq_on = 0; + btn->taskq = xQueueCreate(1, sizeof(void*)); + btn->argq = xQueueCreate(1, sizeof(void *)); + btn->tap_rls_cb.arg = NULL; + btn->tap_rls_cb.cb = NULL; + btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; + btn->tap_rls_cb.pbtn = btn; + btn->tap_rls_cb.tmr = xTimerCreate("btn_rls_tmr", btn->tap_rls_cb.interval, pdFALSE, + &btn->tap_rls_cb, button_tap_rls_cb); + btn->tap_psh_cb.arg = NULL; + btn->tap_psh_cb.cb = NULL; + btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; + btn->tap_psh_cb.pbtn = btn; + btn->tap_psh_cb.tmr = xTimerCreate("btn_psh_tmr", btn->tap_psh_cb.interval, pdFALSE, + &btn->tap_psh_cb, button_tap_psh_cb); + gpio_install_isr_service(0); + gpio_config_t gpio_conf; + gpio_conf.intr_type = GPIO_INTR_ANYEDGE; + gpio_conf.mode = GPIO_MODE_INPUT; + gpio_conf.pin_bit_mask = (1 << gpio_num); + gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&gpio_conf); + gpio_isr_handler_add(gpio_num, button_gpio_isr_handler, btn); + return (button_handle_t) btn; +} + +esp_err_t iot_button_rm_cb(button_handle_t btn_handle, button_cb_type_t type) +{ + button_dev_t* btn = (button_dev_t*) btn_handle; + button_cb_t* btn_cb = NULL; + if (type == BUTTON_CB_PUSH) { + btn_cb = &btn->tap_psh_cb; + } else if (type == BUTTON_CB_RELEASE) { + btn_cb = &btn->tap_rls_cb; + } else if (type == BUTTON_CB_TAP) { + btn_cb = &btn->tap_short_cb; + } else if (type == BUTTON_CB_SERIAL) { + btn_cb = &btn->press_serial_cb; + } + btn_cb->cb = NULL; + btn_cb->arg = NULL; + btn_cb->pbtn = btn; + button_free_tmr(&btn_cb->tmr); + return ESP_OK; +} + +esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_after_sec, TickType_t interval_tick, button_cb cb, void* arg) +{ + button_dev_t* btn = (button_dev_t*) btn_handle; + btn->serial_thres_sec = start_after_sec; + if (btn->press_serial_cb.tmr == NULL) { + btn->press_serial_cb.tmr = xTimerCreate("btn_serial_tmr", btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, + pdFALSE, btn, button_press_serial_cb); + } + btn->press_serial_cb.arg = arg; + btn->press_serial_cb.cb = cb; + btn->press_serial_cb.interval = interval_tick; + btn->press_serial_cb.pbtn = btn; + xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY); + return ESP_OK; +} + +esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t type, button_cb cb, void* arg) +{ + POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); + button_dev_t* btn = (button_dev_t*) btn_handle; + if (type == BUTTON_CB_PUSH) { + btn->tap_psh_cb.arg = arg; + btn->tap_psh_cb.cb = cb; + btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS; + btn->tap_psh_cb.pbtn = btn; + xTimerChangePeriod(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY); + } else if (type == BUTTON_CB_RELEASE) { + btn->tap_rls_cb.arg = arg; + btn->tap_rls_cb.cb = cb; + btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS; + btn->tap_rls_cb.pbtn = btn; + xTimerChangePeriod(btn->tap_rls_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY); + } else if (type == BUTTON_CB_TAP) { + btn->tap_short_cb.arg = arg; + btn->tap_short_cb.cb = cb; + btn->tap_short_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS; + btn->tap_short_cb.pbtn = btn; + } else if (type == BUTTON_CB_SERIAL) { + iot_button_set_serial_cb(btn_handle, 1, 1000 / portTICK_RATE_MS, cb, arg); + } + return ESP_OK; +} + +esp_err_t iot_button_add_on_press_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg) +{ + POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); + IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG); + button_dev_t* btn = (button_dev_t*) btn_handle; + button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t)); + POINT_ASSERT(TAG, cb_new, ESP_FAIL); + cb_new->on_press = 1; + cb_new->arg = arg; + cb_new->cb = cb; + cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS; + cb_new->pbtn = btn; + cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb); + cb_new->next_cb = btn->cb_head; + btn->cb_head = cb_new; + return ESP_OK; +} + +esp_err_t iot_button_add_on_release_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg) +{ + POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); + IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG); + button_dev_t* btn = (button_dev_t*) btn_handle; + button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t)); + POINT_ASSERT(TAG, cb_new, ESP_FAIL); + btn->taskq_on = 1; + cb_new->arg = arg; + cb_new->cb = cb; + cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS; + cb_new->pbtn = btn; + cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb); + cb_new->next_cb = btn->cb_head; + btn->cb_head = cb_new; + return ESP_OK; +} + diff --git a/components/button/button/button_obj.cpp b/components/button/button/button_obj.cpp new file mode 100644 index 0000000..8a1f287 --- /dev/null +++ b/components/button/button/button_obj.cpp @@ -0,0 +1,64 @@ +/* + * ESPRESSIF MIT License + * + * Copyright (c) 2017 + * + * Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include + +CButton::CButton(gpio_num_t gpio_num, button_active_t active_level) +{ + m_btn_handle = iot_button_create(gpio_num, active_level); +} + +CButton::~CButton() +{ + iot_button_delete(m_btn_handle); + m_btn_handle = NULL; +} + +esp_err_t CButton::set_evt_cb(button_cb_type_t type, button_cb cb, void* arg) +{ + return iot_button_set_evt_cb(m_btn_handle, type, cb, arg); +} + +esp_err_t CButton::set_serial_cb(button_cb cb, void* arg, TickType_t interval_tick, uint32_t start_after_sec) +{ + return iot_button_set_serial_cb(m_btn_handle, start_after_sec, interval_tick, cb, arg); +} + +esp_err_t CButton::add_on_press_cb(uint32_t press_sec, button_cb cb, void* arg) +{ + return iot_button_add_on_press_cb(m_btn_handle, press_sec, cb, arg); +} + +esp_err_t CButton::add_on_release_cb(uint32_t press_sec, button_cb cb, void* arg) +{ + return iot_button_add_on_release_cb(m_btn_handle, press_sec, cb, arg); +} + +esp_err_t CButton::rm_cb(button_cb_type_t type) +{ + return iot_button_rm_cb(m_btn_handle, type); +} diff --git a/components/button/button/component.mk b/components/button/button/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/components/button/button/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/components/button/button/include/iot_button.h b/components/button/button/include/iot_button.h new file mode 100644 index 0000000..3f5de51 --- /dev/null +++ b/components/button/button/include/iot_button.h @@ -0,0 +1,280 @@ +/* + * ESPRSSIF MIT License + * + * Copyright (c) 2015 + * + * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, + * it is free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the Software is furnished + * to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _IOT_BUTTON_H_ +#define _IOT_BUTTON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +typedef void (* button_cb)(void*); +typedef void* button_handle_t; + +typedef enum { + BUTTON_ACTIVE_HIGH = 1, /*!