From f9f63669d96d4f8c6911875108c3351c1e845de7 Mon Sep 17 00:00:00 2001 From: xiewenxiang Date: Thu, 12 Dec 2024 09:51:05 +0800 Subject: [PATCH] feat(ble_services): Support Current Time Service --- .gitlab/ci/build.yml | 21 +- .../bluetooth/ble_services/CMakeLists.txt | 5 + components/bluetooth/ble_services/Kconfig | 1 + components/bluetooth/ble_services/README.md | 11 +- .../bluetooth/ble_services/cts/Kconfig.in | 33 +++ .../ble_services/cts/include/esp_cts.h | 181 ++++++++++++++ .../bluetooth/ble_services/cts/src/esp_cts.c | 222 ++++++++++++++++++ docs/Doxyfile | 1 + docs/en/bluetooth/ble_cts.rst | 15 ++ docs/en/bluetooth/ble_services.rst | 1 + docs/sphinx-known-warnings.txt | 2 +- docs/zh_CN/bluetooth/ble_cts.rst | 15 ++ docs/zh_CN/bluetooth/ble_services.rst | 1 + examples/.build-rules.yml | 7 + .../ble_services/ble_cts/CMakeLists.txt | 6 + .../bluetooth/ble_services/ble_cts/README.md | 90 +++++++ .../ble_services/ble_cts/main/CMakeLists.txt | 1 + .../ble_cts/main/Kconfig.projbuild | 13 + .../ble_services/ble_cts/main/app_main.c | 125 ++++++++++ .../ble_cts/main/idf_component.yml | 8 + .../ble_services/ble_cts/sdkconfig.ci.nimble | 1 + .../ble_services/ble_cts/sdkconfig.defaults | 7 + 22 files changed, 757 insertions(+), 10 deletions(-) create mode 100644 components/bluetooth/ble_services/cts/Kconfig.in create mode 100644 components/bluetooth/ble_services/cts/include/esp_cts.h create mode 100644 components/bluetooth/ble_services/cts/src/esp_cts.c create mode 100644 docs/en/bluetooth/ble_cts.rst create mode 100644 docs/zh_CN/bluetooth/ble_cts.rst create mode 100644 examples/bluetooth/ble_services/ble_cts/CMakeLists.txt create mode 100644 examples/bluetooth/ble_services/ble_cts/README.md create mode 100644 examples/bluetooth/ble_services/ble_cts/main/CMakeLists.txt create mode 100644 examples/bluetooth/ble_services/ble_cts/main/Kconfig.projbuild create mode 100644 examples/bluetooth/ble_services/ble_cts/main/app_main.c create mode 100644 examples/bluetooth/ble_services/ble_cts/main/idf_component.yml create mode 100644 examples/bluetooth/ble_services/ble_cts/sdkconfig.ci.nimble create mode 100644 examples/bluetooth/ble_services/ble_cts/sdkconfig.defaults diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index e8246b9a7..7437becf6 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -185,7 +185,7 @@ build_example_bluetooth_ble_services_ble_bas: variables: EXAMPLE_DIR: examples/bluetooth/ble_services/ble_bas -build_example_bluetooth_ble_services_ble_dis: +build_example_bluetooth_ble_services_ble_bcs: extends: - .build_examples_template - .rules:build:example_bluetooth_ble_services @@ -196,9 +196,9 @@ build_example_bluetooth_ble_services_ble_dis: - IMAGE: espressif/idf:release-v5.1 - IMAGE: espressif/idf:release-v5.2 variables: - EXAMPLE_DIR: examples/bluetooth/ble_services/ble_dis + EXAMPLE_DIR: examples/bluetooth/ble_services/ble_bcs -build_example_bluetooth_ble_services_ble_bcs: +build_example_bluetooth_ble_services_ble_cts: extends: - .build_examples_template - .rules:build:example_bluetooth_ble_services @@ -209,7 +209,20 @@ build_example_bluetooth_ble_services_ble_bcs: - IMAGE: espressif/idf:release-v5.1 - IMAGE: espressif/idf:release-v5.2 variables: - EXAMPLE_DIR: examples/bluetooth/ble_services/ble_bcs + EXAMPLE_DIR: examples/bluetooth/ble_services/ble_cts + +build_example_bluetooth_ble_services_ble_dis: + extends: + - .build_examples_template + - .rules:build:example_bluetooth_ble_services + parallel: + matrix: + - IMAGE: espressif/idf:release-v4.4 + - IMAGE: espressif/idf:release-v5.0 + - IMAGE: espressif/idf:release-v5.1 + - IMAGE: espressif/idf:release-v5.2 + variables: + EXAMPLE_DIR: examples/bluetooth/ble_services/ble_dis build_example_bluetooth_ble_services_ble_hrs: extends: diff --git a/components/bluetooth/ble_services/CMakeLists.txt b/components/bluetooth/ble_services/CMakeLists.txt index 30b198211..dea8a10b2 100644 --- a/components/bluetooth/ble_services/CMakeLists.txt +++ b/components/bluetooth/ble_services/CMakeLists.txt @@ -44,6 +44,11 @@ list(APPEND srcs "uds/src/esp_uds.c") list(APPEND include "uds/include") endif() +if(CONFIG_BLE_CTS) +list(APPEND srcs "cts/src/esp_cts.c") +list(APPEND include "cts/include") +endif() + list(APPEND req "ble_conn_mgr") idf_component_register(SRCS "${srcs}" diff --git a/components/bluetooth/ble_services/Kconfig b/components/bluetooth/ble_services/Kconfig index 378d10f0c..4e3157322 100644 --- a/components/bluetooth/ble_services/Kconfig +++ b/components/bluetooth/ble_services/Kconfig @@ -3,6 +3,7 @@ menu "BLE Standard Services" orsource "./ans/Kconfig.in" orsource "./bas/Kconfig.in" orsource "./bcs/Kconfig.in" + orsource "./cts/Kconfig.in" orsource "./dis/Kconfig.in" orsource "./hrs/Kconfig.in" orsource "./hts/Kconfig.in" diff --git a/components/bluetooth/ble_services/README.md b/components/bluetooth/ble_services/README.md index 31b37c86c..a66414f01 100644 --- a/components/bluetooth/ble_services/README.md +++ b/components/bluetooth/ble_services/README.md @@ -29,11 +29,12 @@ The example will be downloaded to the current folder. You can navigate into it f 1. [ble_ans](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_ans) 2. [ble_bas](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_bas) 3. [ble_bcs](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_bcs) -4. [ble_dis](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_dis) -5. [ble_hrs](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_hrs) -6. [ble_hts](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_hts) -7. [ble_tps](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_tps) -8. [ble_uds](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_uds) +4. [ble_cts](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_cts) +5. [ble_dis](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_dis) +6. [ble_hrs](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_hrs) +7. [ble_hts](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_hts) +8. [ble_tps](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_tps) +9. [ble_uds](https://github.com/espressif/esp-iot-solution/tree/master/examples/bluetooth/ble_services/ble_uds) ### Q&A diff --git a/components/bluetooth/ble_services/cts/Kconfig.in b/components/bluetooth/ble_services/cts/Kconfig.in new file mode 100644 index 000000000..1648c9c06 --- /dev/null +++ b/components/bluetooth/ble_services/cts/Kconfig.in @@ -0,0 +1,33 @@ +# Current Time Service + +menuconfig BLE_CTS + bool "GATT Current Time Service" + +if BLE_CTS + +config BLE_CTS_CURRENT_TIME_WRITE_ENABLE + bool "Current time write enable" + default y + help + Set y to enable write or n to disable it. + +config BLE_CTS_LOCAL_TIME_CHAR_ENABLE + bool "Local Time Information Characteristic" + default y + help + Set y to support CTS Local time characteristic or n to disable it. + +config BLE_CTS_LOCAL_TIME_WRITE_ENABLE + bool "Local time write enable" + default y + depends on BLE_CTS_LOCAL_TIME_CHAR_ENABLE + help + Set y to enable write or n to disable it. + +config BLE_CTS_REF_TIME_CHAR_ENABLE + bool "Reference Time Information Characteristic" + default y + help + Set y to support CTS Reference time characteristic or n to disable it. + +endif # BLE_CTS diff --git a/components/bluetooth/ble_services/cts/include/esp_cts.h b/components/bluetooth/ble_services/cts/include/esp_cts.h new file mode 100644 index 000000000..e1f7025bf --- /dev/null +++ b/components/bluetooth/ble_services/cts/include/esp_cts.h @@ -0,0 +1,181 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include + +#include "esp_err.h" +#include "esp_event_base.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +/** @cond **/ +/* BLE CTS EVENTS BASE */ +ESP_EVENT_DECLARE_BASE(BLE_CTS_EVENTS); +/** @endcond **/ + +/* 16 Bit Current Time Service UUID */ +#define BLE_CTS_UUID16 0x1805 + +/* 16 Bit Current Time Characteristics UUID */ +#define BLE_CTS_CHR_UUID16_CURRENT_TIME 0x2A2B +#define BLE_CTS_CHR_UUID16_LOCAL_TIME 0x2A0F +#define BLE_CTS_CHR_UUID16_REFERENCE_TIME 0x2A14 + +/* CTS Adjust Reason Masks */ +#define BLE_CTS_MANUAL_TIME_UPDATE_MASK (1 << 0) +#define BLE_CTS_EXTERNAL_REFERENCE_TIME_UPDATE_MASK (1 << 1) +#define BLE_CTS_CHANGE_OF_TIME_ZONE_MASK (1 << 2) +#define BLE_CTS_CHANGE_OF_DST_MASK (1 << 3) + +/** + * @brief Current Time + */ +typedef struct { + struct { + uint16_t year; /*!< Year as defined by the Gregorian calendar, Valid range 1582 to 9999 */ + uint8_t month; /*!< Month of the year as defined by the Gregorian calendar, Valid range 1 (January) to 12 (December) */ + uint8_t day; /*!< Day of the month as defined by the Gregorian calendar, Valid range 1 to 31 */ + uint8_t hours; /*!< Number of hours past midnight, Valid range 0 to 23 */ + uint8_t minutes; /*!< Number of minutes since the start of the hour. Valid range 0 to 59 */ + uint8_t seconds; /*!< Number of seconds since the start of the minute. Valid range 0 to 59 */ + } __attribute__((packed)) timestamp; /*!< The date and time */ + + uint8_t day_of_week; /*!< Day_of_week. valid range : 1(Monday) - 7(Sunday), 0 means day of week not known */ + uint8_t fractions_256; /*!< Fractions_256. the number of 1 / 256 fractions of second, valid range : 0 - 255 */ + uint8_t adjust_reason; /*!< This field represents reason(s) for adjusting time */ +} __attribute__((packed)) esp_ble_cts_cur_time_t; + +/** + * @brief Local Time Information + */ +typedef struct { + /* This field represents the offset from UTC in number of 15-minute increments. + * Valid range from -48 to +56. + * A value of -128 means that the time zone offset is not known. + * All other values are Reserved for Future Use. + * The offset defined in this characteristic is constant regardless of whether daylight savings is in effect. + */ + int8_t timezone; /*!< Time zone */ + /** dst_offset. + * allowed values : 0, 2, 4, 8, 255 + */ + uint8_t dst_offset; /*!< Dst offset */ +} __attribute__((packed)) esp_ble_cts_local_time_t; + +/** + * @brief reference time information + */ +typedef struct { + /** time_source. + * valid range : 0 - 253 + * 255 means not known + */ + uint8_t time_source; /*!< Time source */ + /* + * This field represents accuracy (drift) of time information in steps of 1/8 of a second (125ms) compared to a reference time source. + * Valid range from 0 to 253 (0s to 31.625s). + * A value of 254 means drift is larger than 31.625s. + * A value of 255 means drift is unknown. + */ + uint8_t time_accuracy; /*!< Time accuracy */ + /** days_since_update. + * valid range : 0 - 254 + * A value of 255 is used when the time span is greater than or equal to 255 days + */ + uint8_t days_since_update; /*!< Days since update */ + /** hours_since_update. + * valid range : 0 - 23 + * A value of 255 is used when the time span is greater than or equal to 255 days + */ + uint8_t hours_since_update; /*!< Hours since update */ +} __attribute__((packed)) esp_ble_cts_ref_time_t; + +/** + * @brief Read the value of Current Time characteristic. + * + * @param[in] out_val The pointer to store the Current Time Increment. + * + * @return + * - ESP_OK on successful + * - ESP_ERR_INVALID_ARG on wrong parameter + */ +esp_err_t esp_ble_cts_get_current_time(esp_ble_cts_cur_time_t *out_val); + +/** + * @brief Set the Current Time characteristic value. + * + * @param[in] in_val The pointer to store the Current Time. + * + * @param[in] need_send send the current time to remote client. + * + * @return + * - ESP_OK on successful + * - ESP_ERR_INVALID_ARG on wrong initialization + * - ESP_FAIL on error + */ +esp_err_t esp_ble_cts_set_current_time(esp_ble_cts_cur_time_t *in_val, bool need_send); + +/** + * @brief Read the value of Local Time characteristic. + * + * @param[in] out_val The pointer to store the Local Time Increment. + * + * @return + * - ESP_OK on successful + * - ESP_ERR_INVALID_ARG on wrong parameter + */ +esp_err_t esp_ble_cts_get_local_time(esp_ble_cts_local_time_t *out_val); + +/** + * @brief Set the Local Time characteristic value. + * + * @param[in] in_val The pointer to store the Local Time. + * + * @return + * - ESP_OK on successful + */ +esp_err_t esp_ble_cts_set_local_time(esp_ble_cts_local_time_t *in_val); + +/** + * @brief Read the value of Reference Time characteristic. + * + * @param[in] out_val The pointer to store the Reference Time Increment. + * + * @return + * - ESP_OK on successful + * - ESP_ERR_INVALID_ARG on wrong parameter + */ +esp_err_t esp_ble_cts_get_reference_time(esp_ble_cts_ref_time_t *out_val); + +/** + * @brief Set the Reference Time characteristic value. + * + * @param[in] in_val The pointer to store the Reference Time. + * + * @return + * - ESP_OK on successful + */ +esp_err_t esp_ble_cts_set_reference_time(esp_ble_cts_ref_time_t *in_val); + +/** + * @brief Initialization Current Time Service + * + * @return + * - ESP_OK on successful + * - ESP_ERR_INVALID_ARG on wrong initialization + * - ESP_FAIL on error + */ +esp_err_t esp_ble_cts_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/bluetooth/ble_services/cts/src/esp_cts.c b/components/bluetooth/ble_services/cts/src/esp_cts.c new file mode 100644 index 000000000..ab524feef --- /dev/null +++ b/components/bluetooth/ble_services/cts/src/esp_cts.c @@ -0,0 +1,222 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Current Time Service(CTS) + */ + +#include + +#include "esp_log.h" + +#include "esp_ble_conn_mgr.h" +#include "esp_cts.h" + +ESP_EVENT_DEFINE_BASE(BLE_CTS_EVENTS); + +/* CTS Current Time Characteristic Value */ +static esp_ble_cts_cur_time_t s_cur_time; + +/* CTS Local Time Characteristic Value */ +static esp_ble_cts_local_time_t s_local_time; + +/* CTS Reference Time Characteristic Value */ +static esp_ble_cts_ref_time_t s_ref_time; + +esp_err_t esp_ble_cts_get_current_time(esp_ble_cts_cur_time_t *out_val) +{ + if (!out_val) { + ESP_LOGE("ERROR", "out_val is NULL."); + return ESP_ERR_INVALID_ARG; + } + + memcpy(out_val, &s_cur_time, sizeof(esp_ble_cts_cur_time_t)); + + return ESP_OK; +} + +esp_err_t esp_ble_cts_set_current_time(esp_ble_cts_cur_time_t *in_val, bool need_send) +{ + if (in_val == NULL) { + ESP_LOGE("ERROR", "Invalid parameter."); + return ESP_ERR_INVALID_ARG; + } + + memcpy(&s_cur_time, in_val, sizeof(esp_ble_cts_cur_time_t)); + + if (need_send) { + esp_ble_conn_data_t conn_data = { + .type = BLE_CONN_UUID_TYPE_16, + .uuid = { + .uuid16 = BLE_CTS_CHR_UUID16_CURRENT_TIME, + }, + .data = (uint8_t *)(&s_cur_time), + .data_len = sizeof(s_cur_time), + }; + + return esp_ble_conn_notify(&conn_data); + } + + return ESP_OK; +} + +esp_err_t esp_ble_cts_get_local_time(esp_ble_cts_local_time_t *out_val) +{ + if (!out_val) { + ESP_LOGE("ERROR", "out_val is NULL."); + return ESP_ERR_INVALID_ARG; + } + + memcpy(out_val, &s_local_time, sizeof(esp_ble_cts_local_time_t)); + + return ESP_OK; +} + +esp_err_t esp_ble_cts_set_local_time(esp_ble_cts_local_time_t *in_val) +{ + if (in_val == NULL) { + ESP_LOGE("ERROR", "Invalid parameter."); + return ESP_ERR_INVALID_ARG; + } + + memcpy(&s_local_time, in_val, sizeof(esp_ble_cts_local_time_t)); + + return ESP_OK; +} + +esp_err_t esp_ble_cts_get_reference_time(esp_ble_cts_ref_time_t *out_val) +{ + if (!out_val) { + ESP_LOGE("ERROR", "out_val is NULL."); + return ESP_ERR_INVALID_ARG; + } + + memcpy(out_val, &s_ref_time, sizeof(esp_ble_cts_ref_time_t)); + + return ESP_OK; +} + +esp_err_t esp_ble_cts_set_reference_time(esp_ble_cts_ref_time_t *in_val) +{ + if (in_val == NULL) { + ESP_LOGE("ERROR", "Invalid parameter."); + return ESP_ERR_INVALID_ARG; + } + + memcpy(&s_ref_time, in_val, sizeof(esp_ble_cts_ref_time_t)); + + return ESP_OK; +} + +#ifdef CONFIG_BLE_CTS_REF_TIME_CHAR_ENABLE +static esp_err_t cts_ref_time_cb(const uint8_t *inbuf, uint16_t inlen, + uint8_t **outbuf, uint16_t *outlen, void *priv_data) +{ + uint8_t len = sizeof(s_ref_time); + + memcpy(&s_ref_time, inbuf, MIN(len, inlen)); + esp_event_post(BLE_CTS_EVENTS, BLE_CTS_CHR_UUID16_REFERENCE_TIME, ((uint8_t *)(&s_ref_time)), sizeof(esp_ble_cts_ref_time_t), portMAX_DELAY); + + return ESP_OK; +} +#endif + +#ifdef CONFIG_BLE_CTS_LOCAL_TIME_CHAR_ENABLE +static esp_err_t cts_local_time_cb(const uint8_t *inbuf, uint16_t inlen, + uint8_t **outbuf, uint16_t *outlen, void *priv_data) +{ + uint8_t len = sizeof(s_local_time); + +#ifdef CONFIG_BLE_CTS_LOCAL_TIME_WRITE_ENABLE + if (inbuf) { + memcpy(&s_local_time, inbuf, MIN(len, inlen)); + esp_event_post(BLE_CTS_EVENTS, BLE_CTS_CHR_UUID16_LOCAL_TIME, ((uint8_t *)(&s_local_time)), sizeof(esp_ble_cts_local_time_t), portMAX_DELAY); + return ESP_OK; + } +#endif + + if (!outbuf || !outlen) { + return ESP_ERR_INVALID_ARG; + } + + *outbuf = calloc(1, len); + if (!(*outbuf)) { + return ESP_ERR_NO_MEM; + } + + memcpy(*outbuf, &s_local_time, len); + *outlen = len; + + return ESP_OK; +} +#endif + +static esp_err_t cts_cur_time_cb(const uint8_t *inbuf, uint16_t inlen, + uint8_t **outbuf, uint16_t *outlen, void *priv_data) +{ + uint8_t len = sizeof(s_cur_time); + +#ifdef CONFIG_BLE_CTS_CURRENT_TIME_WRITE_ENABLE + if (inbuf) { + memcpy(&s_cur_time, inbuf, MIN(len, inlen)); + esp_event_post(BLE_CTS_EVENTS, BLE_CTS_CHR_UUID16_CURRENT_TIME, ((uint8_t *)(&s_cur_time)), sizeof(esp_ble_cts_cur_time_t), portMAX_DELAY); + return ESP_OK; + } +#endif + + if (!outbuf || !outlen) { + return ESP_ERR_INVALID_ARG; + } + + *outbuf = calloc(1, len); + if (!(*outbuf)) { + return ESP_ERR_NO_MEM; + } + + memcpy(*outbuf, &s_cur_time, len); + *outlen = len; + + return ESP_OK; +} + +static const esp_ble_conn_character_t nu_lookup_table[] = { + { + "Current Time", BLE_CONN_UUID_TYPE_16, BLE_CONN_GATT_CHR_READ | BLE_CONN_GATT_CHR_NOTIFY +#ifdef CONFIG_BLE_CTS_CURRENT_TIME_WRITE_ENABLE + | BLE_CONN_GATT_CHR_WRITE +#endif + , { BLE_CTS_CHR_UUID16_CURRENT_TIME }, cts_cur_time_cb + }, +#ifdef CONFIG_BLE_CTS_LOCAL_TIME_CHAR_ENABLE + { + "Local Time", BLE_CONN_UUID_TYPE_16, BLE_CONN_GATT_CHR_READ +#ifdef CONFIG_BLE_CTS_LOCAL_TIME_WRITE_ENABLE + | BLE_CONN_GATT_CHR_WRITE +#endif + , { BLE_CTS_CHR_UUID16_LOCAL_TIME }, cts_local_time_cb + }, +#endif +#ifdef CONFIG_BLE_CTS_REF_TIME_CHAR_ENABLE + { + "Reference Time", BLE_CONN_UUID_TYPE_16, BLE_CONN_GATT_CHR_WRITE + , { BLE_CTS_CHR_UUID16_REFERENCE_TIME }, cts_ref_time_cb + }, +#endif +}; + +static const esp_ble_conn_svc_t svc = { + .type = BLE_CONN_UUID_TYPE_16, + .uuid = { + .uuid16 = BLE_CTS_UUID16, + }, + .nu_lookup_count = sizeof(nu_lookup_table) / sizeof(nu_lookup_table[0]), + .nu_lookup = (esp_ble_conn_character_t *)nu_lookup_table +}; + +esp_err_t esp_ble_cts_init(void) +{ + return esp_ble_conn_add_svc(&svc); +} diff --git a/docs/Doxyfile b/docs/Doxyfile index b4bee1bf7..2105e2b3a 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -32,6 +32,7 @@ INPUT = \ $(PROJECT_PATH)/components/bluetooth/ble_services/ans/include/esp_ans.h \ $(PROJECT_PATH)/components/bluetooth/ble_services/bas/include/esp_bas.h \ $(PROJECT_PATH)/components/bluetooth/ble_services/bcs/include/esp_bcs.h \ + $(PROJECT_PATH)/components/bluetooth/ble_services/cts/include/esp_cts.h \ $(PROJECT_PATH)/components/bluetooth/ble_services/dis/include/esp_dis.h \ $(PROJECT_PATH)/components/bluetooth/ble_services/hrs/include/esp_hrs.h \ $(PROJECT_PATH)/components/bluetooth/ble_services/hts/include/esp_hts.h \ diff --git a/docs/en/bluetooth/ble_cts.rst b/docs/en/bluetooth/ble_cts.rst new file mode 100644 index 000000000..533bdcbbf --- /dev/null +++ b/docs/en/bluetooth/ble_cts.rst @@ -0,0 +1,15 @@ +Current Time Service +============================== +:link_to_translation:`zh_CN:[中文]` + +The Current Time service defines how a Bluetooth device can expose date and time information to other Bluetooth devices. + +Examples +-------------- + +:example:`bluetooth/ble_services/ble_cts`. + +API Reference +----------------- + +.. include-build-file:: inc/esp_cts.inc diff --git a/docs/en/bluetooth/ble_services.rst b/docs/en/bluetooth/ble_services.rst index f219444a6..7c31632bc 100644 --- a/docs/en/bluetooth/ble_services.rst +++ b/docs/en/bluetooth/ble_services.rst @@ -8,6 +8,7 @@ BLE Services Alert Notification Service Battery Service Body Composition Service + Current Time Service Device Information Service Heart Rate Service Health Thermometer Service diff --git a/docs/sphinx-known-warnings.txt b/docs/sphinx-known-warnings.txt index a91427968..5d7c3bb3a 100644 --- a/docs/sphinx-known-warnings.txt +++ b/docs/sphinx-known-warnings.txt @@ -4,7 +4,7 @@ Declaration is '.. cpp:member:: lightbulb_color_mapping_data_t * table'. lightbulb.inc:line: WARNING: Duplicate C++ declaration, also defined at electrical_lighting_solution/lightbulb_driver:line. -Declaration is '.. cpp:member:: struct lightbulb_config_t::@42::@46 precise'. +Declaration is '.. cpp:member:: struct lightbulb_config_t::@43::@47 precise'. lightbulb.inc:line: WARNING: Duplicate C++ declaration, also defined at electrical_lighting_solution/lightbulb_driver:line. diff --git a/docs/zh_CN/bluetooth/ble_cts.rst b/docs/zh_CN/bluetooth/ble_cts.rst new file mode 100644 index 000000000..7802d8420 --- /dev/null +++ b/docs/zh_CN/bluetooth/ble_cts.rst @@ -0,0 +1,15 @@ +当前时间服务 +============================== +:link_to_translation:`en:[English]` + +当前时间服务定义了蓝牙设备如何向其他蓝牙设备展示日期和时间信息。 + +示例 +-------------- + +:example:`bluetooth/ble_services/ble_cts`. + +API 参考 +----------------- + +.. include-build-file:: inc/esp_cts.inc diff --git a/docs/zh_CN/bluetooth/ble_services.rst b/docs/zh_CN/bluetooth/ble_services.rst index 1a6d3260c..1254d6ca3 100644 --- a/docs/zh_CN/bluetooth/ble_services.rst +++ b/docs/zh_CN/bluetooth/ble_services.rst @@ -8,6 +8,7 @@ BLE 服务 警报通知服务 电池服务 人体组织成分服务 + 当前时间服务 设备信息服务 心率服务 健康温度计服务 diff --git a/examples/.build-rules.yml b/examples/.build-rules.yml index 455e43dcf..1a507d54f 100644 --- a/examples/.build-rules.yml +++ b/examples/.build-rules.yml @@ -72,6 +72,13 @@ examples/bluetooth/ble_services/ble_bcs: - if: IDF_TARGET in ["esp32","esp32s3","esp32c2","esp32c3"] and (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR == 0) - if: IDF_TARGET in ["esp32","esp32s3","esp32c2","esp32c3","esp32c6","esp32h2"] and (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR == 1) +examples/bluetooth/ble_services/ble_cts: + enable: + - if: IDF_TARGET in ["esp32","esp32c3"] and (IDF_VERSION_MAJOR == 4 and IDF_VERSION_MINOR == 3) + - if: IDF_TARGET in ["esp32","esp32s3","esp32c3"] and (IDF_VERSION_MAJOR == 4 and IDF_VERSION_MINOR == 4) + - if: IDF_TARGET in ["esp32","esp32s3","esp32c2","esp32c3"] and (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR == 0) + - if: IDF_TARGET in ["esp32","esp32s3","esp32c2","esp32c3","esp32c6","esp32h2"] and (IDF_VERSION_MAJOR == 5 and IDF_VERSION_MINOR == 1) + examples/bluetooth/ble_services/ble_dis: enable: - if: IDF_TARGET in ["esp32","esp32c3"] and (IDF_VERSION_MAJOR == 4 and IDF_VERSION_MINOR == 3) diff --git a/examples/bluetooth/ble_services/ble_cts/CMakeLists.txt b/examples/bluetooth/ble_services/ble_cts/CMakeLists.txt new file mode 100644 index 000000000..7c3efa99e --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ble_cts) diff --git a/examples/bluetooth/ble_services/ble_cts/README.md b/examples/bluetooth/ble_services/ble_cts/README.md new file mode 100644 index 000000000..476ea2953 --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/README.md @@ -0,0 +1,90 @@ +| Supported Targets | ESP32 | ESP32-C3 | ESP32-C2 | ESP32-S3 | ESP32-H2 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | + +# BLE User Data Service Example + +(See the README.md file in the upper level 'examples' directory for more information about examples.) + +This example creates a GATT server and starts advertising, waiting to be connected by a GATT client. + +The device information service exposes manufacturer and/or vendor information about a device. + +It uses Bluetooth controller based on BLE connection management. + +This example aims at understanding BLE Body Composition service and BLE connection management APIs. + +To test this demo, any BLE scanner app can be used. + +## How to Use Example + +Before project configuration and build, be sure to set the correct chip target using: + +```bash +idf.py set-target +``` + +### Hardware Required + +* A development board with ESP32/ESP32-C3/ESP32-C2/ESP32-S3 SoC +* A USB cable for Power supply and programming + +See [Development Boards](https://www.espressif.com/en/products/devkits) for more information about it. + +### Configure the project + +Open the project configuration menu: + +```bash +idf.py menuconfig +``` + +In the `Example Configuration` menu: + +* Select advertisement name of device from `Example Configuration --> Advertisement name`, default is `BLE_CTS`. + +In the `BLE Standard Services` menu: + +* Select the optional functions of device from `GATT Current Time Service`, default is disable. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://idf.espressif.com/) for full steps to configure and use ESP-IDF to build projects. + +## Example Output1 + +There is this console output when bleprph is connected and characteristic is read: + +``` +I (330) BLE_INIT: BT controller compile version [9359a4d] +I (340) system_api: Base MAC address is not set +I (340) system_api: read default base MAC address from EFUSE +I (350) BLE_INIT: Bluetooth MAC: 58:cf:79:1e:9e:de + +I (350) phy_init: phy_version 1150,7c3c08f,Jan 24 2024,17:32:21 +I (410) blecm_nimble: BLE Host Task Started +I (410) blecm_nimble: No characteristic(0x2a00) found +I (410) blecm_nimble: No characteristic(0x2a01) found +I (410) blecm_nimble: No characteristic(0x2a05) found +I (420) NimBLE: GAP procedure initiated: stop advertising. + +I (430) NimBLE: GAP procedure initiated: advertise; +I (430) NimBLE: disc_mode=2 +I (440) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=256 adv_itvl_max=256 +I (450) NimBLE: + +I (450) main_task: Returned from app_main() + + +``` + +## Troubleshooting + +For any technical queries, please open an [issue](https://github.com/espressif/esp-iot-solution/issues) on GitHub. We will get back to you soon. diff --git a/examples/bluetooth/ble_services/ble_cts/main/CMakeLists.txt b/examples/bluetooth/ble_services/ble_cts/main/CMakeLists.txt new file mode 100644 index 000000000..5695bd667 --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/main/CMakeLists.txt @@ -0,0 +1 @@ +idf_component_register(SRCS "app_main.c") diff --git a/examples/bluetooth/ble_services/ble_cts/main/Kconfig.projbuild b/examples/bluetooth/ble_services/ble_cts/main/Kconfig.projbuild new file mode 100644 index 000000000..f0642cfb6 --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/main/Kconfig.projbuild @@ -0,0 +1,13 @@ +menu "Example Configuration" + config EXAMPLE_BLE_ADV_NAME + string "Advertisement name" + default "BLE_CTS" + help + The device name inside advertisement. + + config EXAMPLE_BLE_SUB_ADV + string "Subsequent advertisement data" + default "SUB_ADV" + help + The data inside subsequent advertisements. +endmenu diff --git a/examples/bluetooth/ble_services/ble_cts/main/app_main.c b/examples/bluetooth/ble_services/ble_cts/main/app_main.c new file mode 100644 index 000000000..e4c751339 --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/main/app_main.c @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +#include "esp_log.h" +#include "nvs_flash.h" + +#include "esp_idf_version.h" +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) +#include "esp_random.h" +#include "esp_mac.h" +#else +#include "esp_system.h" +#endif + +#include "esp_ble_conn_mgr.h" +#include "esp_cts.h" + +static const char *TAG = "app_main"; + +static void app_ble_cts_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) +{ + esp_ble_cts_cur_time_t *cur_time = NULL; + esp_ble_cts_local_time_t *local_time = NULL; + esp_ble_cts_ref_time_t *ref_time = NULL; + + if ((base != BLE_CTS_EVENTS) || (event_data == NULL)) { + return; + } + + switch (id) { +#ifdef CONFIG_BLE_CTS_CURRENT_TIME_WRITE_ENABLE + case BLE_CTS_CHR_UUID16_CURRENT_TIME: + cur_time = (esp_ble_cts_cur_time_t *)event_data; + ESP_LOGI(TAG, "Current time, year = %d, month = %d", cur_time->timestamp.year, cur_time->timestamp.month); + break; +#endif +#if defined(CONFIG_BLE_CTS_LOCAL_TIME_CHAR_ENABLE) && defined(CONFIG_BLE_CTS_LOCAL_TIME_WRITE_ENABLE) + case BLE_CTS_CHR_UUID16_LOCAL_TIME: + local_time = (esp_ble_cts_local_time_t *)event_data; + ESP_LOGI(TAG, "Local time, timezone = %d", local_time->timezone); + break; +#endif +#ifdef CONFIG_BLE_CTS_REF_TIME_CHAR_ENABLE + case BLE_CTS_CHR_UUID16_REFERENCE_TIME: + ref_time = (esp_ble_cts_ref_time_t *)event_data; + ESP_LOGI(TAG, "Reference time, time accuracy = %d", ref_time->time_accuracy); + break; +#endif + default: + break; + } +} + +static void app_ble_cts_init(void) +{ + esp_ble_cts_init(); + esp_event_handler_register(BLE_CTS_EVENTS, ESP_EVENT_ANY_ID, app_ble_cts_event_handler, NULL); +} + +static void app_ble_conn_event_handler(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) +{ + esp_ble_cts_cur_time_t cur_time = { + .timestamp.year = 2024, + .timestamp.month = 10, + .timestamp.day = 1, + .timestamp.hours = 9, + .timestamp.minutes = 10, + .timestamp.seconds = 25, + .day_of_week = 1, + .fractions_256 = 6, + .adjust_reason = BLE_CTS_MANUAL_TIME_UPDATE_MASK + }; + + if (base != BLE_CONN_MGR_EVENTS) { + return; + } + + switch (id) { + case ESP_BLE_CONN_EVENT_CONNECTED: + ESP_LOGI(TAG, "ESP_BLE_CONN_EVENT_CONNECTED"); + esp_ble_cts_set_current_time(&cur_time, true); + break; + case ESP_BLE_CONN_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "ESP_BLE_CONN_EVENT_DISCONNECTED"); + break; + default: + break; + } +} + +void +app_main(void) +{ + esp_ble_conn_config_t config = { + .device_name = CONFIG_EXAMPLE_BLE_ADV_NAME, + .broadcast_data = CONFIG_EXAMPLE_BLE_SUB_ADV + }; + + esp_err_t ret; + + // Initialize NVS + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + esp_event_loop_create_default(); + esp_event_handler_register(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, app_ble_conn_event_handler, NULL); + + esp_ble_conn_init(&config); + app_ble_cts_init(); + if (esp_ble_conn_start() != ESP_OK) { + esp_ble_conn_stop(); + esp_ble_conn_deinit(); + esp_event_handler_unregister(BLE_CONN_MGR_EVENTS, ESP_EVENT_ANY_ID, app_ble_conn_event_handler); + esp_event_handler_unregister(BLE_CTS_EVENTS, ESP_EVENT_ANY_ID, app_ble_cts_event_handler); + } +} diff --git a/examples/bluetooth/ble_services/ble_cts/main/idf_component.yml b/examples/bluetooth/ble_services/ble_cts/main/idf_component.yml new file mode 100644 index 000000000..5fcf5940f --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/main/idf_component.yml @@ -0,0 +1,8 @@ +dependencies: + idf: ">=4.3" + ble_conn_mgr: + version: "~0.*" + override_path: "../../../../../components/bluetooth/ble_conn_mgr" + ble_services: + version: "~0.*" + override_path: "../../../../../components/bluetooth/ble_services" diff --git a/examples/bluetooth/ble_services/ble_cts/sdkconfig.ci.nimble b/examples/bluetooth/ble_services/ble_cts/sdkconfig.ci.nimble new file mode 100644 index 000000000..f49256fb2 --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/sdkconfig.ci.nimble @@ -0,0 +1 @@ +CONFIG_BT_NIMBLE_ENABLED=y diff --git a/examples/bluetooth/ble_services/ble_cts/sdkconfig.defaults b/examples/bluetooth/ble_services/ble_cts/sdkconfig.defaults new file mode 100644 index 000000000..c9237866b --- /dev/null +++ b/examples/bluetooth/ble_services/ble_cts/sdkconfig.defaults @@ -0,0 +1,7 @@ +# Override some defaults so BT stack is enabled +# by default in this example +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y + +CONFIG_BLE_CONN_MGR_ROLE_PERIPHERAL=y +CONFIG_BLE_CTS=y