init commit from esp-idf ble example

This commit is contained in:
2025-12-01 00:42:14 +01:00
parent cfcfb6fe37
commit 4116ef780e
17 changed files with 949 additions and 1 deletions

5
main/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
file(GLOB_RECURSE srcs "main.c" "src/*.c")
idf_component_register(SRCS "${srcs}"
PRIV_REQUIRES bt nvs_flash esp_driver_gpio
INCLUDE_DIRS "./include")

42
main/Kconfig.projbuild Normal file
View File

@@ -0,0 +1,42 @@
menu "Example Configuration"
orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps"
choice BLINK_LED
prompt "Blink LED type"
default BLINK_LED_GPIO
help
Select the LED type. A normal level controlled LED or an addressable LED strip.
The default selection is based on the Espressif DevKit boards.
You can change the default selection according to your board.
config BLINK_LED_GPIO
bool "GPIO"
config BLINK_LED_STRIP
bool "LED strip"
endchoice
choice BLINK_LED_STRIP_BACKEND
depends on BLINK_LED_STRIP
prompt "LED strip backend peripheral"
default BLINK_LED_STRIP_BACKEND_RMT if SOC_RMT_SUPPORTED
default BLINK_LED_STRIP_BACKEND_SPI
help
Select the backend peripheral to drive the LED strip.
config BLINK_LED_STRIP_BACKEND_RMT
depends on SOC_RMT_SUPPORTED
bool "RMT"
config BLINK_LED_STRIP_BACKEND_SPI
bool "SPI"
endchoice
config BLINK_GPIO
int "Blink GPIO number"
range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX
default 8
help
GPIO number (IOxx) to blink on and off the LED.
Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink.
endmenu

2
main/idf_component.yml Normal file
View File

@@ -0,0 +1,2 @@
dependencies:
espressif/led_strip: "^2.4.1"

32
main/include/common.h Normal file
View File

@@ -0,0 +1,32 @@
#ifndef COMMON_H
#define COMMON_H
/* Includes */
/* STD APIs */
#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
/* ESP APIs */
#include "esp_log.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
/* FreeRTOS APIs */
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
/* NimBLE stack APIs */
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "host/util/util.h"
#include "nimble/ble.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
/* Defines */
#define TAG "NimBLE_GATT_Server"
#define DEVICE_NAME "NimBLE_GATT"
#endif // COMMON_H

18
main/include/gap.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef GAP_SVC_H
#define GAP_SVC_H
/* Includes */
/* NimBLE GAP APIs */
#include "host/ble_gap.h"
#include "services/gap/ble_svc_gap.h"
/* Defines */
#define BLE_GAP_APPEARANCE_GENERIC_TAG 0x0200
#define BLE_GAP_URI_PREFIX_HTTPS 0x17
#define BLE_GAP_LE_ROLE_PERIPHERAL 0x00
/* Public function declarations */
void adv_init(void);
int gap_init(void);
#endif // GAP_SVC_H

18
main/include/gatt_svc.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef GATT_SVR_H
#define GATT_SVR_H
/* Includes */
/* NimBLE GATT APIs */
#include "host/ble_gatt.h"
#include "services/gatt/ble_svc_gatt.h"
/* NimBLE GAP APIs */
#include "host/ble_gap.h"
/* Public function declarations */
void send_heart_rate_indication(void);
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg);
void gatt_svr_subscribe_cb(struct ble_gap_event *event);
int gatt_svc_init(void);
#endif // GATT_SVR_H

15
main/include/heart_rate.h Normal file
View File

@@ -0,0 +1,15 @@
#ifndef HEART_RATE_H
#define HEART_RATE_H
/* Includes */
/* ESP APIs */
#include "esp_random.h"
/* Defines */
#define HEART_RATE_TASK_PERIOD (1000 / portTICK_PERIOD_MS)
/* Public function declarations */
uint8_t get_heart_rate(void);
void update_heart_rate(void);
#endif // HEART_RATE_H

19
main/include/led.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef LED_H
#define LED_H
/* Includes */
/* ESP APIs */
#include "driver/gpio.h"
#include "led_strip.h"
#include "sdkconfig.h"
/* Defines */
#define BLINK_GPIO CONFIG_BLINK_GPIO
/* Public function declarations */
uint8_t get_led_state(void);
void led_on(void);
void led_off(void);
void led_init(void);
#endif // LED_H

128
main/main.c Normal file
View File

@@ -0,0 +1,128 @@
/* Includes */
#include "common.h"
#include "gap.h"
#include "gatt_svc.h"
#include "heart_rate.h"
#include "led.h"
/* Library function declarations */
void ble_store_config_init(void);
/* Private function declarations */
static void on_stack_reset(int reason);
static void on_stack_sync(void);
static void nimble_host_config_init(void);
static void nimble_host_task(void *param);
/* Private functions */
/*
* Stack event callback functions
* - on_stack_reset is called when host resets BLE stack due to errors
* - on_stack_sync is called when host has synced with controller
*/
static void on_stack_reset(int reason) {
/* On reset, print reset reason to console */
ESP_LOGI(TAG, "nimble stack reset, reset reason: %d", reason);
}
static void on_stack_sync(void) {
/* On stack sync, do advertising initialization */
adv_init();
}
static void nimble_host_config_init(void) {
/* Set host callbacks */
ble_hs_cfg.reset_cb = on_stack_reset;
ble_hs_cfg.sync_cb = on_stack_sync;
ble_hs_cfg.gatts_register_cb = gatt_svr_register_cb;
ble_hs_cfg.store_status_cb = ble_store_util_status_rr;
/* Store host configuration */
ble_store_config_init();
}
static void nimble_host_task(void *param) {
/* Task entry log */
ESP_LOGI(TAG, "nimble host task has been started!");
/* This function won't return until nimble_port_stop() is executed */
nimble_port_run();
/* Clean up at exit */
vTaskDelete(NULL);
}
static void heart_rate_task(void *param) {
/* Task entry log */
ESP_LOGI(TAG, "heart rate task has been started!");
/* Loop forever */
while (1) {
/* Update heart rate value every 1 second */
update_heart_rate();
ESP_LOGI(TAG, "heart rate updated to %d", get_heart_rate());
/* Send heart rate indication if enabled */
send_heart_rate_indication();
/* Sleep */
vTaskDelay(HEART_RATE_TASK_PERIOD);
}
/* Clean up at exit */
vTaskDelete(NULL);
}
void app_main(void) {
/* Local variables */
int rc;
esp_err_t ret;
/* LED initialization */
led_init();
/*
* NVS flash initialization
* Dependency of BLE stack to store configurations
*/
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();
}
if (ret != ESP_OK) {
ESP_LOGE(TAG, "failed to initialize nvs flash, error code: %d ", ret);
return;
}
/* NimBLE stack initialization */
ret = nimble_port_init();
if (ret != ESP_OK) {
ESP_LOGE(TAG, "failed to initialize nimble stack, error code: %d ",
ret);
return;
}
/* GAP service initialization */
rc = gap_init();
if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GAP service, error code: %d", rc);
return;
}
/* GATT server initialization */
rc = gatt_svc_init();
if (rc != 0) {
ESP_LOGE(TAG, "failed to initialize GATT server, error code: %d", rc);
return;
}
/* NimBLE host configuration initialization */
nimble_host_config_init();
/* Start NimBLE host task thread and return */
xTaskCreate(nimble_host_task, "NimBLE Host", 4*1024, NULL, 5, NULL);
xTaskCreate(heart_rate_task, "Heart Rate", 4*1024, NULL, 5, NULL);
return;
}

300
main/src/gap.c Normal file
View File

@@ -0,0 +1,300 @@
#include "gap.h"
#include "common.h"
#include "gatt_svc.h"
/* Private function declarations */
inline static void format_addr(char *addr_str, uint8_t addr[]);
static void print_conn_desc(struct ble_gap_conn_desc *desc);
static void start_advertising(void);
static int gap_event_handler(struct ble_gap_event *event, void *arg);
/* Private variables */
static uint8_t own_addr_type;
static uint8_t addr_val[6] = {0};
static uint8_t esp_uri[] = {BLE_GAP_URI_PREFIX_HTTPS, '/', '/', 'e', 's', 'p', 'r', 'e', 's', 's', 'i', 'f', '.', 'c', 'o', 'm'};
/* Private functions */
inline static void format_addr(char *addr_str, uint8_t addr[]) {
sprintf(addr_str, "%02X:%02X:%02X:%02X:%02X:%02X", addr[0], addr[1],
addr[2], addr[3], addr[4], addr[5]);
}
static void print_conn_desc(struct ble_gap_conn_desc *desc) {
/* Local variables */
char addr_str[18] = {0};
/* Connection handle */
ESP_LOGI(TAG, "connection handle: %d", desc->conn_handle);
/* Local ID address */
format_addr(addr_str, desc->our_id_addr.val);
ESP_LOGI(TAG, "device id address: type=%d, value=%s",
desc->our_id_addr.type, addr_str);
/* Peer ID address */
format_addr(addr_str, desc->peer_id_addr.val);
ESP_LOGI(TAG, "peer id address: type=%d, value=%s", desc->peer_id_addr.type,
addr_str);
/* Connection info */
ESP_LOGI(TAG,
"conn_itvl=%d, conn_latency=%d, supervision_timeout=%d, "
"encrypted=%d, authenticated=%d, bonded=%d\n",
desc->conn_itvl, desc->conn_latency, desc->supervision_timeout,
desc->sec_state.encrypted, desc->sec_state.authenticated,
desc->sec_state.bonded);
}
static void start_advertising(void) {
/* Local variables */
int rc = 0;
const char *name;
struct ble_hs_adv_fields adv_fields = {0};
struct ble_hs_adv_fields rsp_fields = {0};
struct ble_gap_adv_params adv_params = {0};
/* Set advertising flags */
adv_fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
/* Set device name */
name = ble_svc_gap_device_name();
adv_fields.name = (uint8_t *)name;
adv_fields.name_len = strlen(name);
adv_fields.name_is_complete = 1;
/* Set device tx power */
adv_fields.tx_pwr_lvl = BLE_HS_ADV_TX_PWR_LVL_AUTO;
adv_fields.tx_pwr_lvl_is_present = 1;
/* Set device appearance */
adv_fields.appearance = BLE_GAP_APPEARANCE_GENERIC_TAG;
adv_fields.appearance_is_present = 1;
/* Set device LE role */
adv_fields.le_role = BLE_GAP_LE_ROLE_PERIPHERAL;
adv_fields.le_role_is_present = 1;
/* Set advertiement fields */
rc = ble_gap_adv_set_fields(&adv_fields);
if (rc != 0) {
ESP_LOGE(TAG, "failed to set advertising data, error code: %d", rc);
return;
}
/* Set device address */
rsp_fields.device_addr = addr_val;
rsp_fields.device_addr_type = own_addr_type;
rsp_fields.device_addr_is_present = 1;
/* Set URI */
rsp_fields.uri = esp_uri;
rsp_fields.uri_len = sizeof(esp_uri);
/* Set advertising interval */
rsp_fields.adv_itvl = BLE_GAP_ADV_ITVL_MS(500);
rsp_fields.adv_itvl_is_present = 1;
/* Set scan response fields */
rc = ble_gap_adv_rsp_set_fields(&rsp_fields);
if (rc != 0) {
ESP_LOGE(TAG, "failed to set scan response data, error code: %d", rc);
return;
}
/* Set non-connetable and general discoverable mode to be a beacon */
adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
/* Set advertising interval */
adv_params.itvl_min = BLE_GAP_ADV_ITVL_MS(500);
adv_params.itvl_max = BLE_GAP_ADV_ITVL_MS(510);
/* Start advertising */
rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER, &adv_params,
gap_event_handler, NULL);
if (rc != 0) {
ESP_LOGE(TAG, "failed to start advertising, error code: %d", rc);
return;
}
ESP_LOGI(TAG, "advertising started!");
}
/*
* NimBLE applies an event-driven model to keep GAP service going
* gap_event_handler is a callback function registered when calling
* ble_gap_adv_start API and called when a GAP event arrives
*/
static int gap_event_handler(struct ble_gap_event *event, void *arg) {
/* Local variables */
int rc = 0;
struct ble_gap_conn_desc desc;
/* Handle different GAP event */
switch (event->type) {
/* Connect event */
case BLE_GAP_EVENT_CONNECT:
/* A new connection was established or a connection attempt failed. */
ESP_LOGI(TAG, "connection %s; status=%d",
event->connect.status == 0 ? "established" : "failed",
event->connect.status);
/* Connection succeeded */
if (event->connect.status == 0) {
/* Check connection handle */
rc = ble_gap_conn_find(event->connect.conn_handle, &desc);
if (rc != 0) {
ESP_LOGE(TAG,
"failed to find connection by handle, error code: %d",
rc);
return rc;
}
/* Print connection descriptor */
print_conn_desc(&desc);
/* Try to update connection parameters */
struct ble_gap_upd_params params = {.itvl_min = desc.conn_itvl,
.itvl_max = desc.conn_itvl,
.latency = 3,
.supervision_timeout =
desc.supervision_timeout};
rc = ble_gap_update_params(event->connect.conn_handle, &params);
if (rc != 0) {
ESP_LOGE(
TAG,
"failed to update connection parameters, error code: %d",
rc);
return rc;
}
}
/* Connection failed, restart advertising */
else {
start_advertising();
}
return rc;
/* Disconnect event */
case BLE_GAP_EVENT_DISCONNECT:
/* A connection was terminated, print connection descriptor */
ESP_LOGI(TAG, "disconnected from peer; reason=%d",
event->disconnect.reason);
/* Restart advertising */
start_advertising();
return rc;
/* Connection parameters update event */
case BLE_GAP_EVENT_CONN_UPDATE:
/* The central has updated the connection parameters. */
ESP_LOGI(TAG, "connection updated; status=%d",
event->conn_update.status);
/* Print connection descriptor */
rc = ble_gap_conn_find(event->conn_update.conn_handle, &desc);
if (rc != 0) {
ESP_LOGE(TAG, "failed to find connection by handle, error code: %d",
rc);
return rc;
}
print_conn_desc(&desc);
return rc;
/* Advertising complete event */
case BLE_GAP_EVENT_ADV_COMPLETE:
/* Advertising completed, restart advertising */
ESP_LOGI(TAG, "advertise complete; reason=%d",
event->adv_complete.reason);
start_advertising();
return rc;
/* Notification sent event */
case BLE_GAP_EVENT_NOTIFY_TX:
if ((event->notify_tx.status != 0) &&
(event->notify_tx.status != BLE_HS_EDONE)) {
/* Print notification info on error */
ESP_LOGI(TAG,
"notify event; conn_handle=%d attr_handle=%d "
"status=%d is_indication=%d",
event->notify_tx.conn_handle, event->notify_tx.attr_handle,
event->notify_tx.status, event->notify_tx.indication);
}
return rc;
/* Subscribe event */
case BLE_GAP_EVENT_SUBSCRIBE:
/* Print subscription info to log */
ESP_LOGI(TAG,
"subscribe event; conn_handle=%d attr_handle=%d "
"reason=%d prevn=%d curn=%d previ=%d curi=%d",
event->subscribe.conn_handle, event->subscribe.attr_handle,
event->subscribe.reason, event->subscribe.prev_notify,
event->subscribe.cur_notify, event->subscribe.prev_indicate,
event->subscribe.cur_indicate);
/* GATT subscribe event callback */
gatt_svr_subscribe_cb(event);
return rc;
/* MTU update event */
case BLE_GAP_EVENT_MTU:
/* Print MTU update info to log */
ESP_LOGI(TAG, "mtu update event; conn_handle=%d cid=%d mtu=%d",
event->mtu.conn_handle, event->mtu.channel_id,
event->mtu.value);
return rc;
}
return rc;
}
/* Public functions */
void adv_init(void) {
/* Local variables */
int rc = 0;
char addr_str[18] = {0};
/* Make sure we have proper BT identity address set (random preferred) */
rc = ble_hs_util_ensure_addr(0);
if (rc != 0) {
ESP_LOGE(TAG, "device does not have any available bt address!");
return;
}
/* Figure out BT address to use while advertising (no privacy for now) */
rc = ble_hs_id_infer_auto(0, &own_addr_type);
if (rc != 0) {
ESP_LOGE(TAG, "failed to infer address type, error code: %d", rc);
return;
}
/* Printing ADDR */
rc = ble_hs_id_copy_addr(own_addr_type, addr_val, NULL);
if (rc != 0) {
ESP_LOGE(TAG, "failed to copy device address, error code: %d", rc);
return;
}
format_addr(addr_str, addr_val);
ESP_LOGI(TAG, "device address: %s", addr_str);
/* Start advertising. */
start_advertising();
}
int gap_init(void) {
/* Local variables */
int rc = 0;
/* Call NimBLE GAP initialization API */
ble_svc_gap_init();
/* Set GAP device name */
rc = ble_svc_gap_device_name_set(DEVICE_NAME);
if (rc != 0) {
ESP_LOGE(TAG, "failed to set device name to %s, error code: %d",
DEVICE_NAME, rc);
return rc;
}
return rc;
}

264
main/src/gatt_svc.c Normal file
View File

@@ -0,0 +1,264 @@
/* Includes */
#include "gatt_svc.h"
#include "common.h"
#include "heart_rate.h"
#include "led.h"
/* Private function declarations */
static int heart_rate_chr_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
static int led_chr_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg);
/* Private variables */
/* Heart rate service */
static const ble_uuid16_t heart_rate_svc_uuid = BLE_UUID16_INIT(0x180D);
static uint8_t heart_rate_chr_val[2] = {0};
static uint16_t heart_rate_chr_val_handle;
static const ble_uuid16_t heart_rate_chr_uuid = BLE_UUID16_INIT(0x2A37);
static uint16_t heart_rate_chr_conn_handle = 0;
static bool heart_rate_chr_conn_handle_inited = false;
static bool heart_rate_ind_status = false;
/* Automation IO service */
static const ble_uuid16_t auto_io_svc_uuid = BLE_UUID16_INIT(0x1815);
static uint16_t led_chr_val_handle;
static const ble_uuid128_t led_chr_uuid =
BLE_UUID128_INIT(0x23, 0xd1, 0xbc, 0xea, 0x5f, 0x78, 0x23, 0x15, 0xde, 0xef,
0x12, 0x12, 0x25, 0x15, 0x00, 0x00);
/* GATT services table */
static const struct ble_gatt_svc_def gatt_svr_svcs[] = {
/* Heart rate service */
{.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &heart_rate_svc_uuid.u,
.characteristics =
(struct ble_gatt_chr_def[]){
{/* Heart rate characteristic */
.uuid = &heart_rate_chr_uuid.u,
.access_cb = heart_rate_chr_access,
.flags = BLE_GATT_CHR_F_READ | BLE_GATT_CHR_F_INDICATE,
.val_handle = &heart_rate_chr_val_handle},
{
0, /* No more characteristics in this service. */
}}},
/* Automation IO service */
{
.type = BLE_GATT_SVC_TYPE_PRIMARY,
.uuid = &auto_io_svc_uuid.u,
.characteristics =
(struct ble_gatt_chr_def[]){/* LED characteristic */
{.uuid = &led_chr_uuid.u,
.access_cb = led_chr_access,
.flags = BLE_GATT_CHR_F_WRITE,
.val_handle = &led_chr_val_handle},
{0}},
},
{
0, /* No more services. */
},
};
/* Private functions */
static int heart_rate_chr_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) {
/* Local variables */
int rc;
/* Handle access events */
/* Note: Heart rate characteristic is read only */
switch (ctxt->op) {
/* Read characteristic event */
case BLE_GATT_ACCESS_OP_READ_CHR:
/* Verify connection handle */
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
ESP_LOGI(TAG, "characteristic read; conn_handle=%d attr_handle=%d",
conn_handle, attr_handle);
} else {
ESP_LOGI(TAG, "characteristic read by nimble stack; attr_handle=%d",
attr_handle);
}
/* Verify attribute handle */
if (attr_handle == heart_rate_chr_val_handle) {
/* Update access buffer value */
heart_rate_chr_val[1] = get_heart_rate();
rc = os_mbuf_append(ctxt->om, &heart_rate_chr_val,
sizeof(heart_rate_chr_val));
return rc == 0 ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
}
goto error;
/* Unknown event */
default:
goto error;
}
error:
ESP_LOGE(
TAG,
"unexpected access operation to heart rate characteristic, opcode: %d",
ctxt->op);
return BLE_ATT_ERR_UNLIKELY;
}
static int led_chr_access(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg) {
/* Local variables */
int rc;
/* Handle access events */
/* Note: LED characteristic is write only */
switch (ctxt->op) {
/* Write characteristic event */
case BLE_GATT_ACCESS_OP_WRITE_CHR:
/* Verify connection handle */
if (conn_handle != BLE_HS_CONN_HANDLE_NONE) {
ESP_LOGI(TAG, "characteristic write; conn_handle=%d attr_handle=%d",
conn_handle, attr_handle);
} else {
ESP_LOGI(TAG,
"characteristic write by nimble stack; attr_handle=%d",
attr_handle);
}
/* Verify attribute handle */
if (attr_handle == led_chr_val_handle) {
/* Verify access buffer length */
if (ctxt->om->om_len == 1) {
/* Turn the LED on or off according to the operation bit */
if (ctxt->om->om_data[0]) {
led_on();
ESP_LOGI(TAG, "led turned on!");
} else {
led_off();
ESP_LOGI(TAG, "led turned off!");
}
} else {
goto error;
}
return rc;
}
goto error;
/* Unknown event */
default:
goto error;
}
error:
ESP_LOGE(TAG,
"unexpected access operation to led characteristic, opcode: %d",
ctxt->op);
return BLE_ATT_ERR_UNLIKELY;
}
/* Public functions */
void send_heart_rate_indication(void) {
if (heart_rate_ind_status && heart_rate_chr_conn_handle_inited) {
ble_gatts_indicate(heart_rate_chr_conn_handle,
heart_rate_chr_val_handle);
ESP_LOGI(TAG, "heart rate indication sent!");
}
}
/*
* Handle GATT attribute register events
* - Service register event
* - Characteristic register event
* - Descriptor register event
*/
void gatt_svr_register_cb(struct ble_gatt_register_ctxt *ctxt, void *arg) {
/* Local variables */
char buf[BLE_UUID_STR_LEN];
/* Handle GATT attributes register events */
switch (ctxt->op) {
/* Service register event */
case BLE_GATT_REGISTER_OP_SVC:
ESP_LOGD(TAG, "registered service %s with handle=%d",
ble_uuid_to_str(ctxt->svc.svc_def->uuid, buf),
ctxt->svc.handle);
break;
/* Characteristic register event */
case BLE_GATT_REGISTER_OP_CHR:
ESP_LOGD(TAG,
"registering characteristic %s with "
"def_handle=%d val_handle=%d",
ble_uuid_to_str(ctxt->chr.chr_def->uuid, buf),
ctxt->chr.def_handle, ctxt->chr.val_handle);
break;
/* Descriptor register event */
case BLE_GATT_REGISTER_OP_DSC:
ESP_LOGD(TAG, "registering descriptor %s with handle=%d",
ble_uuid_to_str(ctxt->dsc.dsc_def->uuid, buf),
ctxt->dsc.handle);
break;
/* Unknown event */
default:
assert(0);
break;
}
}
/*
* GATT server subscribe event callback
* 1. Update heart rate subscription status
*/
void gatt_svr_subscribe_cb(struct ble_gap_event *event) {
/* Check connection handle */
if (event->subscribe.conn_handle != BLE_HS_CONN_HANDLE_NONE) {
ESP_LOGI(TAG, "subscribe event; conn_handle=%d attr_handle=%d",
event->subscribe.conn_handle, event->subscribe.attr_handle);
} else {
ESP_LOGI(TAG, "subscribe by nimble stack; attr_handle=%d",
event->subscribe.attr_handle);
}
/* Check attribute handle */
if (event->subscribe.attr_handle == heart_rate_chr_val_handle) {
/* Update heart rate subscription status */
heart_rate_chr_conn_handle = event->subscribe.conn_handle;
heart_rate_chr_conn_handle_inited = true;
heart_rate_ind_status = event->subscribe.cur_indicate;
}
}
/*
* GATT server initialization
* 1. Initialize GATT service
* 2. Update NimBLE host GATT services counter
* 3. Add GATT services to server
*/
int gatt_svc_init(void) {
/* Local variables */
int rc;
/* 1. GATT service initialization */
ble_svc_gatt_init();
/* 2. Update GATT services counter */
rc = ble_gatts_count_cfg(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
/* 3. Add GATT services */
rc = ble_gatts_add_svcs(gatt_svr_svcs);
if (rc != 0) {
return rc;
}
return 0;
}

View File

@@ -0,0 +1,11 @@
/* Includes */
#include "common.h"
#include "heart_rate.h"
/* Private variables */
static uint8_t heart_rate;
/* Public functions */
uint8_t get_heart_rate(void) { return heart_rate; }
void update_heart_rate(void) { heart_rate = 60 + (uint8_t)(esp_random() % 21); }

79
main/src/led.c Normal file
View File

@@ -0,0 +1,79 @@
/* Includes */
#include "led.h"
#include "common.h"
/* Private variables */
static uint8_t led_state;
#ifdef CONFIG_BLINK_LED_STRIP
static led_strip_handle_t led_strip;
#endif
/* Public functions */
uint8_t get_led_state(void) { return led_state; }
#ifdef CONFIG_BLINK_LED_STRIP
void led_on(void) {
/* Set the LED pixel using RGB from 0 (0%) to 255 (100%) for each color */
led_strip_set_pixel(led_strip, 0, 16, 16, 16);
/* Refresh the strip to send data */
led_strip_refresh(led_strip);
/* Update LED state */
led_state = true;
}
void led_off(void) {
/* Set all LED off to clear all pixels */
led_strip_clear(led_strip);
/* Update LED state */
led_state = false;
}
void led_init(void) {
ESP_LOGI(TAG, "example configured to blink addressable led!");
/* LED strip initialization with the GPIO and pixels number*/
led_strip_config_t strip_config = {
.strip_gpio_num = CONFIG_BLINK_GPIO,
.max_leds = 1, // at least one LED on board
};
#if CONFIG_BLINK_LED_STRIP_BACKEND_RMT
led_strip_rmt_config_t rmt_config = {
.resolution_hz = 10 * 1000 * 1000, // 10MHz
.flags.with_dma = false,
};
ESP_ERROR_CHECK(
led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
#elif CONFIG_BLINK_LED_STRIP_BACKEND_SPI
led_strip_spi_config_t spi_config = {
.spi_bus = SPI2_HOST,
.flags.with_dma = true,
};
ESP_ERROR_CHECK(
led_strip_new_spi_device(&strip_config, &spi_config, &led_strip));
#else
#error "unsupported LED strip backend"
#endif
/* Set all LED off to clear all pixels */
led_off();
}
#elif CONFIG_BLINK_LED_GPIO
void led_on(void) { gpio_set_level(CONFIG_BLINK_GPIO, true); }
void led_off(void) { gpio_set_level(CONFIG_BLINK_GPIO, false); }
void led_init(void) {
ESP_LOGI(TAG, "example configured to blink gpio led!");
gpio_reset_pin(CONFIG_BLINK_GPIO);
/* Set the GPIO as a push/pull output */
gpio_set_direction(CONFIG_BLINK_GPIO, GPIO_MODE_OUTPUT);
}
#else
#error "unsupported LED type"
#endif