initial commit from esp-idf example
This commit is contained in:
5
CMakeLists.txt
Normal file
5
CMakeLists.txt
Normal 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
Kconfig.projbuild
Normal file
42
Kconfig.projbuild
Normal 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
idf_component.yml
Normal file
2
idf_component.yml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
dependencies:
|
||||||
|
espressif/led_strip: "^2.4.1"
|
||||||
32
include/common.h
Normal file
32
include/common.h
Normal 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
include/gap.h
Normal file
18
include/gap.h
Normal 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
include/gatt_svc.h
Normal file
18
include/gatt_svc.h
Normal 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
include/heart_rate.h
Normal file
15
include/heart_rate.h
Normal 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
include/led.h
Normal file
19
include/led.h
Normal 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
|
||||||
124
main.c
Normal file
124
main.c
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#include "common.h"
|
||||||
|
#include "gap.h"
|
||||||
|
#include "gatt_svc.h"
|
||||||
|
#include "heart_rate.h"
|
||||||
|
#include "led.h"
|
||||||
|
|
||||||
|
void ble_store_config_init(void);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
src/gap.c
Normal file
300
src/gap.c
Normal 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, ¶ms);
|
||||||
|
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;
|
||||||
|
}
|
||||||
263
src/gatt_svc.c
Normal file
263
src/gatt_svc.c
Normal file
@@ -0,0 +1,263 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
10
src/heart_rate_mock.c
Normal file
10
src/heart_rate_mock.c
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#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); }
|
||||||
78
src/led.c
Normal file
78
src/led.c
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#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
|
||||||
Reference in New Issue
Block a user