feat(nimble): Added blecsc profile support for nimble examples

This commit is contained in:
ESPAbhinav 2023-05-25 12:13:48 +05:30 committed by Abhinav Kudnar
parent 28f68a0376
commit f8b2ab7955
10 changed files with 1320 additions and 1 deletions

View File

@ -1,4 +1,4 @@
[codespell]
skip = build,*.yuv,components/fatfs/src/*,alice.txt,*.rgb
ignore-words-list = ser,dout,rsource,fram,inout,shs
ignore-words-list = ser,dout,rsource,fram,inout,shs,ans
write-changes = true

View File

@ -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.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(blecsc)

View File

@ -0,0 +1,135 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- |
# BLE CSC(Cycling Speed and Cadence) Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This Bluetooth Low Energy (BLE) Cycling Speed and Cadence (CSC) profile is designed to provide real-time measurements of a cyclist's speed and cadence. It allows cycling-related devices, such as bike sensors or fitness trackers, to communicate this data wirelessly to a receiving device, such as a smartphone or cycling computer. The major tasks of the BLE CSC profile include:
Connection Establishment: This example creates GATT server and then starts advertising, waiting to be connected to a GATT client.
Service Discovery: Once the connection is established, the client device performs service discovery to identify the available services and characteristics supported by the CSC Sensor. The CSC profile typically utilizes the Generic Attribute Profile (GATT) for this purpose.
CSC Measurement: The CSC Sensor periodically sends CSC Measurement notifications to the client device. These notifications contain the current speed and cadence data measured by the sensor. The client device can subscribe to these notifications to receive real-time updates. The `blecsc_simulate_speed_and_cadence()` function is used to calculate simulated CSC measurements, including speed and cadence.
It uses ESP's Bluetooth controller and NimBLE stack based BLE host.
This example aims at understanding CSC measurements, GATT database configuration, handling GATT reads and writes, handling subscribe events and understanding advertisement.
Note :
* To install the dependency packages needed, please refer to the top level [README file](../../../README.md#running-test-python-script-ttfw).
* Currently this Python utility is only supported on Linux (BLE communication is via BLuez + DBus).
## How to Use Example
Before project configuration and build, be sure to set the correct chip target using:
```bash
idf.py set-target <chip_name>
```
### Configure the project
Open the project configuration menu:
```bash
idf.py menuconfig
```
In the `Example Configuration` menu:
* Select I/O capabilities of device from `Example Configuration --> I/O Capability`, default is `Just_works`.
* Enable/Disable other security related parameters `Bonding, MITM option, secure connection(SM SC)`.
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
(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 Output
## There is this console output when blecsc starts advertising
```
I (424) NimBLE_BLE_CSC: BLE Host Task Started
I (424) NimBLE: GAP procedure initiated: stop advertising.
I (424) NimBLE: Failed to restore IRKs from store; status=8
I (434) NimBLE: GAP procedure initiated: advertise;
I (434) NimBLE: disc_mode=2
I (434) NimBLE: adv_channel_map=0 own_addr_type=0 adv_filter_policy=0 adv_itvl_min=0 adv_itvl_max=0
I (444) NimBLE:
I (454) main_task: Returned from app_main()
I (1424) NimBLE: CSC simulated values: speed = 1 kph, cadence = 21
I (1524) NimBLE: CSC simulated values: speed = 2 kph, cadence = 22
I (1624) NimBLE: CSC simulated values: speed = 3 kph, cadence = 23
I (1724) NimBLE: CSC simulated values: speed = 4 kph, cadence = 24
I (1824) NimBLE: CSC simulated values: speed = 5 kph, cadence = 25
I (1924) NimBLE: CSC simulated values: speed = 6 kph, cadence = 26
I (2024) NimBLE: CSC simulated values: speed = 7 kph, cadence = 27
I (2124) NimBLE: CSC simulated values: speed = 8 kph, cadence = 28
I (2224) NimBLE: CSC simulated values: speed = 9 kph, cadence = 29
I (2324) NimBLE: CSC simulated values: speed = 10 kph, cadence = 30
```
## This is the Console output when blecsc is connected and sends notifications.
```
I (8974) NimBLE: connection established; status=0
I (9024) NimBLE: CSC simulated values: speed = 7 kph, cadence = 97
I (9124) NimBLE: CSC simulated values: speed = 8 kph, cadence = 98
I (9224) NimBLE: CSC simulated values: speed = 9 kph, cadence = 99
I (26454) NimBLE: csc measurement notify state = 1
I (26534) NimBLE: CSC simulated values: speed = 7 kph, cadence = 32
I (26534) NimBLE: GATT procedure initiated: notify;
I (26534) NimBLE: att_handle=3
I (26634) NimBLE: CSC simulated values: speed = 8 kph, cadence = 33
I (26634) NimBLE: GATT procedure initiated: notify;
I (26634) NimBLE: att_handle=3
I (26734) NimBLE: CSC simulated values: speed = 9 kph, cadence = 34
I (26734) NimBLE: GATT procedure initiated: notify;
I (26734) NimBLE: att_handle=3
I (26834) NimBLE: CSC simulated values: speed = 10 kph, cadence = 35
I (26834) NimBLE: GATT procedure initiated: notify;
I (26834) NimBLE: att_handle=3
```
## Note
* NVS support is there for bonding. So, bonding is persistent across reboot.
## Troubleshooting
For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.

View File

@ -0,0 +1,5 @@
set(srcs "main.c"
"gatt_svr.c")
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,105 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifndef H_BLECSC_SENSOR_
#define H_BLECSC_SENSOR_
#include "modlog/modlog.h"
#include "nimble/ble.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Cycling Speed and Cadence configuration */
#define GATT_CSC_UUID 0x1816
#define GATT_CSC_MEASUREMENT_UUID 0x2A5B
#define GATT_CSC_FEATURE_UUID 0x2A5C
#define GATT_SENSOR_LOCATION_UUID 0x2A5D
#define GATT_SC_CONTROL_POINT_UUID 0x2A55
/* Device Information configuration */
#define GATT_DEVICE_INFO_UUID 0x180A
#define GATT_MANUFACTURER_NAME_UUID 0x2A29
#define GATT_MODEL_NUMBER_UUID 0x2A24
/*CSC Measurement flags*/
#define CSC_MEASUREMENT_WHEEL_REV_PRESENT 0x01
#define CSC_MEASUREMENT_CRANK_REV_PRESENT 0x02
/* CSC feature flags */
#define CSC_FEATURE_WHEEL_REV_DATA 0x01
#define CSC_FEATURE_CRANK_REV_DATA 0x02
#define CSC_FEATURE_MULTIPLE_SENSOR_LOC 0x04
/* Sensor location enum */
#define SENSOR_LOCATION_OTHER 0
#define SENSOR_LOCATION_TOP_OF_SHOE 1
#define SENSOR_LOCATION_IN_SHOE 2
#define SENSOR_LOCATION_HIP 3
#define SENSOR_LOCATION_FRONT_WHEEL 4
#define SENSOR_LOCATION_LEFT_CRANK 5
#define SENSOR_LOCATION_RIGHT_CRANK 6
#define SENSOR_LOCATION_LEFT_PEDAL 7
#define SENSOR_LOCATION_RIGHT_PEDAL 8
#define SENSOR_LOCATION_FROT_HUB 9
#define SENSOR_LOCATION_REAR_DROPOUT 10
#define SENSOR_LOCATION_CHAINSTAY 11
#define SENSOR_LOCATION_REAR_WHEEL 12
#define SENSOR_LOCATION_REAR_HUB 13
#define SENSOR_LOCATION_CHEST 14
#define SENSOR_LOCATION_SPIDER 15
#define SENSOR_LOCATION_CHAIN_RING 16
/* SC Control Point op codes */
#define SC_CP_OP_SET_CUMULATIVE_VALUE 1
#define SC_CP_OP_START_SENSOR_CALIBRATION 2
#define SC_CP_OP_UPDATE_SENSOR_LOCATION 3
#define SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS 4
#define SC_CP_OP_RESPONSE 16
/*SC Control Point response values */
#define SC_CP_RESPONSE_SUCCESS 1
#define SC_CP_RESPONSE_OP_NOT_SUPPORTED 2
#define SC_CP_RESPONSE_INVALID_PARAM 3
#define SC_CP_RESPONSE_OP_FAILED 4
/* CSC simulation configuration */
#define CSC_FEATURES (CSC_FEATURE_WHEEL_REV_DATA | \
CSC_FEATURE_CRANK_REV_DATA |\
CSC_FEATURE_MULTIPLE_SENSOR_LOC)
struct ble_csc_measurement_state {
uint32_t cumulative_wheel_rev;
uint16_t last_wheel_evt_time;
uint16_t cumulative_crank_rev;
uint16_t last_crank_evt_time;
};
extern uint16_t csc_measurement_handle;
extern uint16_t csc_control_point_handle;
int gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state);
int gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle);
void gatt_svr_set_cp_indicate(uint8_t indication_status);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,367 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "blecsc_sens.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "services/ans/ble_svc_ans.h"
#define CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED 0x81
static const char *manuf_name = "Apache Mynewt";
static const char *model_num = "Mynewt CSC Sensor";
static const uint8_t csc_supported_sensor_locations[] = {
SENSOR_LOCATION_FRONT_WHEEL,
SENSOR_LOCATION_REAR_DROPOUT,
SENSOR_LOCATION_CHAINSTAY,
SENSOR_LOCATION_REAR_WHEEL
};
static uint8_t sensor_location = SENSOR_LOCATION_REAR_DROPOUT;
static struct ble_csc_measurement_state * measurement_state;
uint16_t csc_measurement_handle;
uint16_t csc_control_point_handle;
uint8_t csc_cp_indication_status;
static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg);
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
{
/* Service: Cycling Speed and Cadence */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_CSC_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: Cycling Speed and Cadence Measurement */
.uuid = BLE_UUID16_DECLARE(GATT_CSC_MEASUREMENT_UUID),
.access_cb = gatt_svr_chr_access_csc_measurement,
.val_handle = &csc_measurement_handle,
.flags = BLE_GATT_CHR_F_NOTIFY,
}, {
/* Characteristic: Cycling Speed and Cadence features */
.uuid = BLE_UUID16_DECLARE(GATT_CSC_FEATURE_UUID),
.access_cb = gatt_svr_chr_access_csc_feature,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Sensor Location */
.uuid = BLE_UUID16_DECLARE(GATT_SENSOR_LOCATION_UUID),
.access_cb = gatt_svr_chr_access_sensor_location,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: SC Control Point*/
.uuid = BLE_UUID16_DECLARE(GATT_SC_CONTROL_POINT_UUID),
.access_cb = gatt_svr_chr_access_sc_control_point,
.val_handle = &csc_control_point_handle,
.flags = BLE_GATT_CHR_F_WRITE | BLE_GATT_CHR_F_INDICATE,
}, {
0, /* No more characteristics in this service */
}, }
},
{
/* Service: Device Information */
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = BLE_UUID16_DECLARE(GATT_DEVICE_INFO_UUID),
.characteristics = (struct ble_gatt_chr_def[]) { {
/* Characteristic: * Manufacturer name */
.uuid = BLE_UUID16_DECLARE(GATT_MANUFACTURER_NAME_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
/* Characteristic: Model number string */
.uuid = BLE_UUID16_DECLARE(GATT_MODEL_NUMBER_UUID),
.access_cb = gatt_svr_chr_access_device_info,
.flags = BLE_GATT_CHR_F_READ,
}, {
0, /* No more characteristics in this service */
}, }
},
{
0, /* No more services */
},
};
static int
gatt_svr_chr_access_csc_measurement(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
return BLE_ATT_ERR_READ_NOT_PERMITTED;
}
static int
gatt_svr_chr_access_csc_feature(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
static const uint16_t csc_feature = CSC_FEATURES;
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &csc_feature, sizeof(csc_feature));
return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int
gatt_svr_chr_access_sensor_location(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR);
rc = os_mbuf_append(ctxt->om, &sensor_location, sizeof(sensor_location));
return (rc == 0) ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
static int
gatt_svr_chr_access_sc_control_point(uint16_t conn_handle,
uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt,
void *arg)
{
uint8_t op_code;
uint8_t new_sensor_location;
uint8_t new_cumulative_wheel_rev_arr[4];
struct os_mbuf *om_indication;
uint8_t response = SC_CP_RESPONSE_OP_NOT_SUPPORTED;
int ii;
int rc;
assert(ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR);
if (!csc_cp_indication_status) {
return CSC_ERR_CCC_DESC_IMPROPERLY_CONFIGURED;
}
/* Read control point op code*/
rc = os_mbuf_copydata(ctxt->om, 0, sizeof(op_code), &op_code);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
/* Allocate response buffer */
om_indication = ble_hs_mbuf_att_pkt();
switch(op_code){
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
case SC_CP_OP_SET_CUMULATIVE_VALUE:
/* Read new cumulative wheel revolutions value*/
rc = os_mbuf_copydata(ctxt->om, 1,
sizeof(new_cumulative_wheel_rev_arr),
new_cumulative_wheel_rev_arr);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
measurement_state->cumulative_wheel_rev =
get_le32(new_cumulative_wheel_rev_arr);
response = SC_CP_RESPONSE_SUCCESS;
break;
#endif
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
case SC_CP_OP_UPDATE_SENSOR_LOCATION:
/* Read new sensor location value*/
rc = os_mbuf_copydata(ctxt->om, 1, 1, &new_sensor_location);
if (rc != 0){
return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
}
/* Verify if requested new location is on supported locations list */
response = SC_CP_RESPONSE_INVALID_PARAM;
for (ii = 0; ii < sizeof(csc_supported_sensor_locations); ii++){
if (new_sensor_location == csc_supported_sensor_locations[ii]){
sensor_location = new_sensor_location;
response = SC_CP_RESPONSE_SUCCESS;
break;
}
}
break;
case SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS:
response = SC_CP_RESPONSE_SUCCESS;
break;
#endif
default:
break;
}
/* Append response value */
rc = os_mbuf_append(om_indication, &response, sizeof(response));
if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
#if (CSC_FEATURES & CSC_FEATURE_MULTIPLE_SENSOR_LOC)
/* In case of supported locations request append locations list */
if (op_code == SC_CP_OP_REQ_SUPPORTED_SENSOR_LOCATIONS){
rc = os_mbuf_append(om_indication, &csc_supported_sensor_locations,
sizeof(csc_supported_sensor_locations));
}
if (rc != 0){
return BLE_ATT_ERR_INSUFFICIENT_RES;
}
#endif
rc = ble_gatts_indicate_custom(conn_handle, csc_control_point_handle,
om_indication);
return rc;
}
static int
gatt_svr_chr_access_device_info(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
uint16_t uuid;
int rc;
uuid = ble_uuid_u16(ctxt->chr->uuid);
if (uuid == GATT_MODEL_NUMBER_UUID) {
rc = os_mbuf_append(ctxt->om, model_num, strlen(model_num));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
if (uuid == GATT_MANUFACTURER_NAME_UUID) {
rc = os_mbuf_append(ctxt->om, manuf_name, strlen(manuf_name));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
int
gatt_svr_chr_notify_csc_measurement(uint16_t conn_handle)
{
int rc;
struct os_mbuf *om;
uint8_t data_buf[11];
uint8_t data_offset = 1;
memset(data_buf, 0, sizeof(data_buf));
#if (CSC_FEATURES & CSC_FEATURE_WHEEL_REV_DATA)
data_buf[0] |= CSC_MEASUREMENT_WHEEL_REV_PRESENT;
put_le16(&(data_buf[5]), measurement_state->last_wheel_evt_time);
put_le32(&(data_buf[1]), measurement_state->cumulative_wheel_rev);
data_offset += 6;
#endif
#if (CSC_FEATURES & CSC_FEATURE_CRANK_REV_DATA)
data_buf[0] |= CSC_MEASUREMENT_CRANK_REV_PRESENT;
put_le16(&(data_buf[data_offset]),
measurement_state->cumulative_crank_rev);
put_le16(&(data_buf[data_offset + 2]),
measurement_state->last_crank_evt_time);
data_offset += 4;
#endif
om = ble_hs_mbuf_from_flat(data_buf, data_offset);
rc = ble_gatts_notify_custom(conn_handle, csc_measurement_handle, om);
return rc;
}
void
gatt_svr_set_cp_indicate(uint8_t indication_status)
{
csc_cp_indication_status = indication_status;
}
void
gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg)
{
switch (ctxt->op) {
case BLE_GATT_REGISTER_OP_SVC:
break;
case BLE_GATT_REGISTER_OP_CHR:
break;
case BLE_GATT_REGISTER_OP_DSC:
break;
default:
assert(0);
break;
}
}
int
gatt_svr_init(struct ble_csc_measurement_state * csc_measurement_state)
{
int rc;
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
measurement_state = csc_measurement_state;
return 0;
}

View File

@ -0,0 +1,337 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include "esp_log.h"
#include "nvs_flash.h"
/* BLE */
#include "console/console.h"
#include "nimble/ble.h"
#include "host/ble_hs.h"
#include "services/gap/ble_svc_gap.h"
#include "blecsc_sens.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#if CONFIG_EXAMPLE_EXTENDED_ADV
static uint8_t ext_adv_pattern_1[] = {
0x02, 0x01, 0x06,
0x03, 0x03, 0xab, 0xcd,
0x03, 0x03, 0x18, 0x11,
0x10, 0X09, 'n', 'i', 'm', 'b', 'l', 'e', '-', 'b', 'l', 'e', 'c', 's', 'c','-', 'e',
};
#endif
/* Wheel size for simulation calculations */
#define CSC_SIM_WHEEL_CIRCUMFERENCE_MM 2000
/* Simulated cadence lower limit */
#define CSC_SIM_CRANK_RPM_MIN 20
/* Simulated cadence upper limit */
#define CSC_SIM_CRANK_RPM_MAX 100
/* Simulated speed lower limit */
#define CSC_SIM_SPEED_KPH_MIN 0
/* Simulated speed upper limit */
#define CSC_SIM_SPEED_KPH_MAX 35
/* Notification status */
static bool notify_state = false;
/* Connection handle */
static uint16_t conn_handle;
static uint8_t blecsc_addr_type;
/* Advertised device name */
static const char *device_name = "blecsc_sensor";
/* Measurement and notification timer */
static struct ble_npl_callout blecsc_measure_timer;
/* Variable holds current CSC measurement state */
static struct ble_csc_measurement_state csc_measurement_state;
/* Variable holds simulated speed (kilometers per hour) */
static uint16_t csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
/* Variable holds simulated cadence (RPM) */
static uint8_t csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
static int blecsc_gap_event(struct ble_gap_event *event, void *arg);
static const char *tag = "NimBLE_BLE_CSC";
/*
* Enables advertising with parameters:
* o General discoverable mode
* o Undirected connectable mode
*/
static void
blecsc_advertise(void)
{
struct ble_gap_adv_params adv_params;
struct ble_hs_adv_fields fields;
int rc;
/*
* Set the advertisement data included in our advertisements:
* o Flags (indicates advertisement type and other general info)
* o Advertising tx power
* o Device name
*/
memset(&fields, 0, sizeof(fields));
/*
* Advertise two flags:
* o Discoverability in forthcoming advertisement (general)
* o BLE-only (BR/EDR unsupported)
*/
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
/*
* Indicate that the TX power level field should be included; have the
* stack fill this value automatically. This is done by assigning the
* special value BLE_HS_ADV_TX_PWR_LVL_AUTO.
*/
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
/*
* Set appearance.
*/
fields.appearance = ble_svc_gap_device_appearance();
fields.appearance_is_present = 1;
rc = ble_gap_adv_set_fields(&fields);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error setting advertisement data; rc=%d\n", rc);
return;
}
/* Begin advertising */
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, blecsc_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
}
/* Update simulated CSC measurements.
* Each call increments wheel and crank revolution counters by one and
* computes last event time in order to match simulated candence and speed.
* Last event time is expressedd in 1/1024th of second units.
*
* 60 * 1024
* crank_dt = --------------
* cadence[RPM]
*
*
* circumference[mm] * 1024 * 60 * 60
* wheel_dt = -------------------------------------
* 10^6 * speed [kph]
*/
static void
blecsc_simulate_speed_and_cadence(void)
{
uint16_t wheel_rev_period;
uint16_t crank_rev_period;
/* Update simulated crank and wheel rotation speed */
csc_sim_speed_kph++;
if (csc_sim_speed_kph >= CSC_SIM_SPEED_KPH_MAX) {
csc_sim_speed_kph = CSC_SIM_SPEED_KPH_MIN;
}
csc_sim_crank_rpm++;
if (csc_sim_crank_rpm >= CSC_SIM_CRANK_RPM_MAX) {
csc_sim_crank_rpm = CSC_SIM_CRANK_RPM_MIN;
}
/* Calculate simulated measurement values */
if (csc_sim_speed_kph > 0){
wheel_rev_period = (36*64*CSC_SIM_WHEEL_CIRCUMFERENCE_MM) /
(625*csc_sim_speed_kph);
csc_measurement_state.cumulative_wheel_rev++;
csc_measurement_state.last_wheel_evt_time += wheel_rev_period;
}
if (csc_sim_crank_rpm > 0){
crank_rev_period = (60*1024) / csc_sim_crank_rpm;
csc_measurement_state.cumulative_crank_rev++;
csc_measurement_state.last_crank_evt_time += crank_rev_period;
}
MODLOG_DFLT(INFO, "CSC simulated values: speed = %d kph, cadence = %d \n",
csc_sim_speed_kph, csc_sim_crank_rpm);
}
/* Run CSC measurement simulation and notify it to the client */
static void
blecsc_measurement(struct ble_npl_event *ev)
{
int rc;
rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 10);
assert(rc == 0);
blecsc_simulate_speed_and_cadence();
if (notify_state) {
rc = gatt_svr_chr_notify_csc_measurement(conn_handle);
assert(rc == 0);
}
}
static int
blecsc_gap_event(struct ble_gap_event *event, void *arg)
{
switch (event->type) {
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed */
MODLOG_DFLT(INFO, "connection %s; status=%d\n",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
if (event->connect.status != 0) {
/* Connection failed; resume advertising */
blecsc_advertise();
conn_handle = 0;
}
else {
conn_handle = event->connect.conn_handle;
}
break;
case BLE_GAP_EVENT_DISCONNECT:
MODLOG_DFLT(INFO, "disconnect; reason=%d\n", event->disconnect.reason);
conn_handle = 0;
/* Connection terminated; resume advertising */
blecsc_advertise();
break;
case BLE_GAP_EVENT_ADV_COMPLETE:
MODLOG_DFLT(INFO, "adv complete\n");
break;
case BLE_GAP_EVENT_SUBSCRIBE:
MODLOG_DFLT(INFO, "subscribe event attr_handle=%d\n",
event->subscribe.attr_handle);
if (event->subscribe.attr_handle == csc_measurement_handle) {
notify_state = event->subscribe.cur_notify;
MODLOG_DFLT(INFO, "csc measurement notify state = %d\n",
notify_state);
}
else if (event->subscribe.attr_handle == csc_control_point_handle) {
gatt_svr_set_cp_indicate(event->subscribe.cur_indicate);
MODLOG_DFLT(INFO, "csc control point indicate state = %d\n",
event->subscribe.cur_indicate);
}
break;
case BLE_GAP_EVENT_MTU:
MODLOG_DFLT(INFO, "mtu update event; conn_handle=%d mtu=%d\n",
event->mtu.conn_handle,
event->mtu.value);
break;
}
return 0;
}
static void
blecsc_on_sync(void)
{
int rc;
/* Figure out address to use while advertising (no privacy) */
rc = ble_hs_id_infer_auto(0, &blecsc_addr_type);
assert(rc == 0);
/* Begin advertising */
blecsc_advertise();
}
void blecsc_host_task(void *param)
{
ESP_LOGI(tag, "BLE Host Task Started");
/* This function will return only when nimble_port_stop() is executed */
nimble_port_run();
nimble_port_freertos_deinit();
}
/*
* main
*
* The main task for the project. This function initializes the packages,
* then starts serving events from default event queue.
*
* @return int NOTE: this function should never return!
*/
int
app_main(void)
{
int rc;
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t 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);
ret = nimble_port_init();
if (ret != ESP_OK) {
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
return -1;
}
/* Initialize the NimBLE host configuration */
ble_hs_cfg.sync_cb = blecsc_on_sync;
/* Initialize measurement and notification timer */
ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(),
blecsc_measurement, NULL);
rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 100);
assert(rc == 0);
rc = gatt_svr_init(&csc_measurement_state);
assert(rc == 0);
/* Set the default device name */
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
nimble_port_freertos_init(blecsc_host_task);
return 0;
}

View File

@ -0,0 +1,12 @@
# Override some defaults so BT stack is enabled
# in this example
#
# BT config
#
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

View File

@ -0,0 +1,349 @@
# BLE CSC Example Walkthrough
## Introduction
This Bluetooth Low Energy (BLE) Cycling Speed and Cadence (CSC) profile is designed to provide real-time measurements of a cyclist's speed and cadence. It allows cycling-related devices, such as bike sensors or fitness trackers, to communicate this data wirelessly to a receiving device, such as a smartphone or cycling computer.
## Includes
This example is located in the examples folder of the ESP-IDF under the [blecsc/main](../main). The [main.c](../main/main.c) file located in the main folder contains all the functionality that we are going to review. The header files contained in [main.c](../main/main.c) are:
These `includes` are required for the FreeRTOS and underlying system components to run, including the logging functionality and a library to store data in non-volatile flash memory. We are interested in `“nimble_port.h”`, `“nimble_port_freertos.h”`, `"ble_hs.h"` and `“ble_svc_gap.h”`, `“blecsc_sens.h”` which expose the BLE APIs required to implement this example.
* `nimble_port.h`: Includes the declaration of functions required for the initialization of the nimble stack.
* `nimble_port_freertos.h`: Initializes and enables nimble host task.
* `ble_hs`.h`: Defines the functionalities to handle the host event
* `ble_svc_gap`.h`: Defines the macros for device name, and device appearance and declares the function to set them.
* `blecsc_sens.h`: This file defines the Cycling Speed and Cadence configuration, Device Information configuration, CSC Measurement flags, CSC feature flags, Sensor location enum CSC simulation configuration, etc.
## Main Entry Point
```c
int
app_main(void)
{
int rc;
/* Initialize NVS — it is used to store PHY calibration data */
esp_err_t 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);
ret = nimble_port_init();
if (ret != ESP_OK) {
ESP_LOGE(tag, "Failed to init nimble %d ", ret);
return -1;
}
/* Initialize the NimBLE host configuration */
ble_hs_cfg.sync_cb = blecsc_on_sync;
/* Initialize measurement and notification timer */
ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(),
blecsc_measurement, NULL);
rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 100);
assert(rc == 0);
rc = gatt_svr_init(&csc_measurement_state);
assert(rc == 0);
/* Set the default device name */
rc = ble_svc_gap_device_name_set(device_name);
assert(rc == 0);
nimble_port_freertos_init(blecsc_host_task);
return 0;
}
```
The main function starts by initializing the non-volatile storage library. This library allows us to save the key-value pairs in flash memory.`nvs_flash_init()` stores the PHY calibration data. In a Bluetooth Low Energy (BLE) device, cryptographic keys used for encryption and authentication are often stored in Non-Volatile Storage (NVS).BLE stores the peer keys, CCCD keys, peer records, etc on NVS. By storing these keys in NVS, the BLE device can quickly retrieve them when needed, without the need for time-consuming key generations.
```c
esp_err_t 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 );
```
## BT Controller and Stack Initialization
The main function calls `nimble_port_init()` to initialize BT Controller and nimble stack. This function initializes the BT controller by first creating its configuration structure named `esp_bt_controller_config_t` with default settings generated by the `BT_CONTROLLER_INIT_CONFIG_DEFAULT()` macro. It implements the Host Controller Interface (HCI) on the controller side, the Link Layer (LL), and the Physical Layer (PHY). The BT Controller is invisible to the user applications and deals with the lower layers of the BLE stack. The controller configuration includes setting the BT controller stack size, priority, and HCI baud rate. With the settings created, the BT controller is initialized and enabled with the `esp_bt_controller_init()` and `esp_bt_controller_enable()` functions:
```c
esp_bt_controller_config_t config_opts = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
ret = esp_bt_controller_init(&config_opts);
```
Next, the controller is enabled in BLE Mode.
```c
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
```
The controller should be enabled in `ESP_BT_MODE_BLE` if you want to use the BLE mode.
There are four Bluetooth modes supported:
1. `ESP_BT_MODE_IDLE`: Bluetooth not running
2. `ESP_BT_MODE_BLE`: BLE mode
3. `ESP_BT_MODE_CLASSIC_BT`: BT Classic mode
4. `ESP_BT_MODE_BTDM`: Dual mode (BLE + BT Classic)
After the initialization of the BT controller, the nimble stack, which includes the common definitions and APIs for BLE, is initialized by using `esp_nimble_init()`:
```c
esp_err_t esp_nimble_init(void)
{
#if !SOC_ESP_NIMBLE_CONTROLLER
/* Initialize the function pointers for OS porting */
npl_freertos_funcs_init();
npl_freertos_mempool_init();
if(esp_nimble_hci_init() != ESP_OK) {
ESP_LOGE(NIMBLE_PORT_LOG_TAG, "hci inits failed\n");
return ESP_FAIL;
}
/* Initialize default event queue */
ble_npl_eventq_init(&g_eventq_dflt);
os_msys_init();
void ble_store_ram_init(void);
/* XXX Need to have a template for store */
ble_store_ram_init();
#endif
/* Initialize the host */
ble_hs_init();
return ESP_OK;
}
```
The host is configured by setting up the callback that gets executed upon Stack-sync.
```c
ble_hs_cfg.sync_cb = blecsc_on_sync;
```
## Callout init and callout reset
```c
ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(),
blecsc_measurement, NULL);
rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 100);
assert(rc == 0);
```
It initializes a timer for measuring and sending notifications. Let's break down the code step by step:
1. `ble_npl_callout_init(&blecsc_measure_timer, nimble_port_get_dflt_eventq(), blecsc_measurement, NULL);`
- This line initializes a timer named `blecsc_measure_timer` using a function or library called `ble_npl_callout_init`.
- `&blecsc_measure_timer` is a pointer to the timer structure that will be initialized.
- `nimble_port_get_dflt_eventq()` is a function call that retrieves the default event queue for a NimBLE (a Bluetooth stack) port. This event queue is likely used for scheduling events and tasks related to Bluetooth communication.
- `blecsc_measurement` is a callback that will be called when the timer expires. It is explained in following parts.
- `NULL` is passed as the last argument, which typically represents user-specific data or context to be passed to the callback function. In this case, it's not used, so it's set to `NULL`.
2. `rc = ble_npl_callout_reset(&blecsc_measure_timer, portTICK_PERIOD_MS * 100);`
- This line resets the `blecsc_measure_timer` timer with a new timeout value.
- `&blecsc_measure_timer` is again a pointer to the timer structure to be reset.
- `portTICK_PERIOD_MS * 100` sets the timer's new expiration time. It appears to be based on the `portTICK_PERIOD_MS` value multiplied by 100, suggesting a timeout of 100 milliseconds.
- The return value of `ble_npl_callout_reset` is stored in `rc`, indicating the result of the timer reset operation.
In summary, this code initializes a timer named `blecsc_measure_timer`, associates it with a specific event queue, and sets it to trigger the `blecsc_measurement` function after a delay of 100 milliseconds. If the timer reset operation is successful (indicated by `rc == 0`), the program continues; otherwise, it will halt if there's an error. This timer is used for periodic measurement and notification tasks in a BLECSC application.
## GATT SERVER INIT
```c
rc = gatt_svr_init();
assert(rc == 0);
```
The gatt_svr_init function is called during the initialization phase of a BLE application to set up the GATT server and define the services and characteristics it supports.
## SETTING DEVICE NAME
The main function calls `ble_svc_gap_device_name_set()` to set the default device name. 'blecsc_sensor' is passed as the default device name to this function.
```c
rc = ble_svc_gap_device_name_set(device_name);
```
## THREAD MODEL
The main function creates a task where nimble will run using `nimble_port_freertos_init()`. This enables the nimble stack by using `esp_nimble_enable()`.
```c
nimble_port_freertos_init(blecsc_host_task);
```
`esp_nimble_enable()` creates a task where the nimble host will run. Nimble stack runs in a separate thread with its own context to handle async operations from the controller and post HCI commands to the controller.
# BLE CSC ADVERTISEMENT
1. CSC advertisement is used by many applications to start advertising from applications. The `blecsc` example provides this feature of advertisement. It uses the following function to start an advertisement.
2. `blecsc_advertise` start by creating the instances of structures `ble_gap_adv_params` and `ble_hs_adv_fields`.
3. Advertising parameters such as connecting modes, discoverable modes, advertising intervals, channel map advertising filter policy, and high duty cycle for directed advertising are defined in these structures.
4. `ble_hs_adv_fields` provides a structure to store advertisement data in a Bluetooth Low Energy (BLE) application. It contains various data members, such as flags (indicating advertisement type and other general information), advertising transmit power, device name, and 16-bit service UUIDs (such as alert notifications), etc.
Below is the implementation to start the advertisement.
## Configuration of Advertising data(`ble_hs_adv_fields fields`)
### Setting advertising flags and transmitting power level
```c
memset(&fields, 0, sizeof fields);
fields.flags = BLE_HS_ADV_F_DISC_GEN |
BLE_HS_ADV_F_BREDR_UNSUP;
fields.tx_pwr_lvl_is_present = 1;
fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
```
All data members of `fields` are initialized to 0 before setting up using the `memset()` function. Discoverability for upcoming advertisements is set to general. Advertisement is set to BLE supported only. Assigning the `tx_pwr_lvl` field to `BLE_HS_ADV_TX_PWR_LVL_AUTO` prompts the stack to automatically fill in the transmit power level field.
### Setting up the device name and appearance
```c
fields.name = (uint8_t *)device_name;
fields.name_len = strlen(device_name);
fields.name_is_complete = 1;
fields.appearance = ble_svc_gap_device_appearance();
fields.appearance_is_present = 1;
```
The app main function calls the API `ble_svc_gap_device_name_set("blecsc_sensor")` which takes the user-defined device name as a parameter. This API sets the device name to the characters array `ble_svc_gap_name` defined in the file [ble_svc_gap.c](../../../../../components/bt/host/nimble/nimble/nimble/host/services/gap/src/ble_svc_gap.c). The `ble_svc_gap_device_name()` API returns the same above-mentioned char array which was set from the main function.
`ble_gap_adv_set_fields()` sets the following advertising data. This API internally depends on other APIs to set the data.APIs can be checked here [ble_hs_adv.c](../../../../../components/bt/host/nimble/nimble/nimble/host/src/ble_hs_adv.c)
1. Adv flags
2. 16-bit service class UUIDS
3. 32-bit service class UUIDS
4. 128-bit service class UUIDS
5. Local Name
6. Tx Power Level
7. Slave connection interval range
8. Service data - 16-bit UUID
9. Public target address
10. Appearance
11. Advertising interval
12. Service data - 32-bit UUID
13. Service data - 128-bit UUID
14. URI
15. Manufacturer-specific data
## Start Advertisement
```c
memset(&adv_params, 0, sizeof(adv_params));
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
rc = ble_gap_adv_start(blecsc_addr_type, NULL, BLE_HS_FOREVER,
&adv_params, blecsc_gap_event, NULL);
if (rc != 0) {
MODLOG_DFLT(ERROR, "error enabling advertisement; rc=%d\n", rc);
return;
}
```
Advertisement parameters are initialized to 0 using `memset`. Enables advertising with general discoverable mode and undirected connectable mode.
The `ble_gap_adv_start()` function configures and starts the advertising procedure.
The description of the `ble_gap_adv_start()` method is as follows:
```c
Return Value : 0 on success, error code on failure
```
```c
Parameters
1. blecsc_addr_type:The type of address the stack should use for itself. Valid values are:
- BLE_OWN_ADDR_PUBLIC
- BLE_OWN_ADDR_RANDOM
- BLE_OWN_ADDR_RPA_PUBLIC_DEFAULT
- BLE_OWN_ADDR_RPA_RANDOM_DEFAULT
2. direct_addr:The peers address for directed advertising. This parameter shall be non-NULL if directed advertising is being used. In this case, we have set it to NULL.
3. duration_ms: The duration of the advertisement procedure. On expiration, the procedure ends and a BLE_GAP_EVENT_ADV_COMPLETE event is reported. Units are milliseconds. Specify BLE_HS_FOREVER for no expiration.
4. adv_params: Additional arguments specifying the particulars of the advertising procedure.
5. cb: The callback to associate with this advertising procedure. If advertising ends, the event is reported through this callback. If advertising results in a connection, the connection inherits this callback as its event-reporting mechanism. The Callback, in this case, is `blecsc_gap_event`.
6. cb_arg: The optional argument to pass to the callback function.
```
### BLECSC Simulate speed and cadance value.
This function, named `blecsc_simulate_speed_and_cadence`, is related to simulating speed and cadence data in the context of cycling. It is used in a Bluetooth Low Energy (BLE) Cycling Speed and Cadence (CSC) application. Following is the break down what this function is doing step by step:
1. **Function Description:**
This function updates simulated measurements for cycling speed and cadence. It calculates the time periods for wheel and crank revolutions based on the simulated speed (in kilometers per hour, kph) and cadence (in revolutions per minute, RPM). The time periods are expressed in 1/1024th of a second units.
2. **Constants:**
There are some constants defined, such as `CSC_SIM_SPEED_KPH_MAX`, `CSC_SIM_SPEED_KPH_MIN`, `CSC_SIM_CRANK_RPM_MAX`, and `CSC_SIM_CRANK_RPM_MIN`, which set the maximum and minimum values for simulated speed and cadence.
3. **Simulated Speed and Cadence Updates:**
The function increments the simulated cycling speed (`csc_sim_speed_kph`) and cadence (`csc_sim_crank_rpm`) by one. If these values reach their maximum limits, they are reset to their minimum values.
4. **Calculating Wheel and Crank Revolution Periods:**
- **Wheel Revolution Period (`wheel_rev_period`):** If the simulated speed is greater than zero, it calculates the time it takes for one wheel revolution based on the circumference of the wheel and the simulated speed. It increments the cumulative wheel revolution counter and updates the last wheel event time by adding the wheel revolution period.
- **Crank Revolution Period (`crank_rev_period`):** If the simulated cadence is greater than zero, it calculates the time it takes for one crank revolution based on the cadence. It increments the cumulative crank revolution counter and updates the last crank event time by adding the crank revolution period.
Overall, this function is part of a simulated cycling environment, used to generate and update simulated speed and cadence measurements based on predefined parameters and equations. These simulated values can be used for testing and development of a cycling-related application, over a BLE connection.
### Mathematical Calculations
```c
60 * 1024
crank_dt = --------------
cadence[RPM]
circumference[mm] * 1024 * 60 * 60
wheel_dt = -------------------------------------
10^6 * speed [kph]
```
### BLECSC Measurement
This function appears to be part of a program that's related to Bluetooth Low Energy (BLE) Cycling Speed and Cadence (CSC) measurements. It's called `blecsc_measurement`, and it seems to be responsible for running the CSC measurement simulation and notifying the simulated measurements to a client. Let's break down what this function is doing point by point:
1. **Function Description:**
This function is responsible for triggering the CSC measurement simulation and notifying the simulated measurements to a client over a BLE connection.
2. **`ble_npl_callout_reset` Call:**
- `ble_npl_callout_reset` is a function call used to schedule an event to run periodically. In this case, it schedules the `blecsc_measure_timer` event to run every 10 milliseconds (`portTICK_PERIOD_MS * 10`).
- The return code is stored in the `rc` variable, and an `assert` statement is used to check if the return code is zero (indicating successful scheduling).
3. **`blecsc_simulate_speed_and_cadence` Call:**
- This function is called to simulate speed and cadence measurements. It updates the simulated values, as explained in the previous response.
4. **Notification to the Client:**
- There is a condition check `if (notify_state)` which seems to determine whether to notify the client about the simulated measurements.
- If `notify_state` is true (non-zero), it proceeds to notify the client about the CSC measurement. It likely uses the function `gatt_svr_chr_notify_csc_measurement` to send this notification.
- The return code of the notification function call is stored in the `rc` variable.
In summary, this function is part of a BLE CSC application and serves as a periodic task that simulates CSC measurements, schedules the next simulation, and notifies the client about these measurements if the `notify_state` condition is met. It includes error checking to ensure the success of scheduling and notification operations.
### BLE CSC GapEvent
This function, `blecsc_gap_event`, appears to handle various events related to the Bluetooth Low Energy (BLE) connection in a Cycling Speed and Cadence (CSC) application. It contains a `switch` statement that handles different event types and takes appropriate actions for each event. Let's go through each case in this switch statement point by point:
1. **BLE_GAP_EVENT_CONNECT:**
- This case handles events related to the establishment of a new connection or the failure of a connection attempt.
- It logs whether the connection was successfully established or if it failed along with the status code.
- If the connection attempt fails (`event->connect.status != 0`), it resumes advertising and sets the connection handle (`conn_handle`) to 0.
- If the connection was successfully established, it sets `conn_handle` to the value of `event->connect.conn_handle`.
2. **BLE_GAP_EVENT_DISCONNECT:**
- This case handles events related to a disconnection. It logs the reason for the disconnection.
- It sets `conn_handle` to 0 and resumes advertising after a disconnection.
3. **BLE_GAP_EVENT_ADV_COMPLETE:**
- This case handles events indicating that advertising has been completed.
- It logs that the advertising is complete.
4. **BLE_GAP_EVENT_SUBSCRIBE:**
- This case handles events related to subscriptions by a client to characteristics. It includes information about which attribute was subscribed and whether notifications or indications are enabled.
- It logs the attribute handle and the current state of notification/indication for the subscribed attribute.
- If the subscribed attribute is the CSC measurement attribute, it updates the `notify_state`.
- If the subscribed attribute is the CSC control point attribute, it updates the `indicate_state` and sets it using the `gatt_svr_set_cp_indicate` function.
5. **BLE_GAP_EVENT_MTU:**
- This case handles events related to the Maximum Transmission Unit (MTU) update. The MTU represents the maximum data packet size that can be sent over the connection.
- It logs the connection handle and the updated MTU value.
In summary, this function is a critical part of managing the BLE connection in a CSC application. It handles connection and disconnection events, tracks subscription states for specific attributes (such as CSC measurements and control points), and logs relevant information for debugging and monitoring purposes. Additionally, it resumes advertising when connections are terminated or when a connection attempt fails
## Conclusion
- This tutorial explains the CSC measurement values i.e how it is calculated and communicated over ble protocol
- It also commented on ble_csc advertising which is similar to peripheral advertisement .
- Various gap events related to this csc service is explained here.

View File

@ -1012,6 +1012,9 @@ examples/bluetooth/esp_ble_mesh/aligenie_demo/components/vendor_model/include/ge
examples/bluetooth/esp_ble_mesh/aligenie_demo/components/vendor_model/include/genie_util.h
examples/bluetooth/nimble/blecent/main/blecent.h
examples/bluetooth/nimble/blecent/main/main.c
examples/bluetooth/nimble/blecsc/main/blecsc_sens.h
examples/bluetooth/nimble/blecsc/main/gatt_svr.c
examples/bluetooth/nimble/blecsc/main/main.c
examples/bluetooth/nimble/blehr/main/blehr_sens.h
examples/bluetooth/nimble/blehr/main/gatt_svr.c
examples/bluetooth/nimble/blehr/main/main.c