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