diff --git a/include/baros.h b/include/baros.h index b6be05e..229cee4 100644 --- a/include/baros.h +++ b/include/baros.h @@ -1,6 +1,9 @@ #ifndef __BAROS_H__ #define __BAROS_H__ +#include +#include "esp_event_base.h" + #define BAROS_BLE_SERVICE_PUMP_ENABLED (0x4200) #define BAROS_BLE_SERVICE_PUMP_STATE (0x4300) #define BAROS_BLE_SERVICE_PUMP_DURATION (0x4350) @@ -10,4 +13,25 @@ #define BAROS_CHAR_POUR (BAROS_BLE_SERVICE_BARBACK + 1) // 0x4401 #define BAROS_CHAR_BUTTON (BAROS_BLE_SERVICE_BARBACK + 2) // 0x4402 +typedef enum { + BAROS_ROLE_BACK = 0, + BAROS_ROLE_HEAD, + BAROS_ROLE_ADMIN +} baros_role_t; + +typedef struct { + const char *name; + baros_role_t role; +} baros_ble_cfg_t; + +uint8_t baros_ble_init(baros_ble_cfg_t *cfg); + +ESP_EVENT_DECLARE_BASE(EVENT_BAROS_BLE); + +typedef enum { + BAROS_BLE_DISC, + BAROS_BLE_CONNECT, +} baros_ble_event_t; + + #endif diff --git a/src/baros_ble.c b/src/baros_ble.c new file mode 100644 index 0000000..7a20c73 --- /dev/null +++ b/src/baros_ble.c @@ -0,0 +1,266 @@ +#include "freertos/FreeRTOS.h" +#include "esp_system.h" +#include "esp_nimble_hci.h" +#include "nimble/nimble_port.h" +#include "nimble/nimble_port_freertos.h" +#include "host/ble_hs.h" +#include "host/util/util.h" +#include "services/gap/ble_svc_gap.h" +#include "services/bas/ble_svc_bas.h" +#include "esp_event_base.h" +#include "esp_event.h" + +#include "baros.h" + +#define TAG "BOS_BLE" + +ESP_EVENT_DEFINE_BASE(EVENT_BAROS_BLE); + +static uint8_t blehr_addr_type; +static uint16_t conn_handle; +static const char *device_name; + +static const struct ble_gatt_svc_def service_defs[] = { + { NULL } +}; + +static void ble_advertise(void); + +static int ble_gap_event(struct ble_gap_event *event, void *arg) { + switch (event->type) { + case BLE_GAP_EVENT_DISC: { + struct ble_hs_adv_fields fields; + + ESP_ERROR_CHECK(ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data)); + + ESP_LOGV(TAG, "DISCOVERY: %d", event->disc.length_data); + + uint8_t addr[6]; + memcpy(&addr, (uint8_t*)&event->disc.addr + 1, 6); + + ESP_LOGV(TAG, "addr: %02X:%02X:%02X:%02X:%02X:%02X", addr[5], addr[4], addr[3], addr[2], addr[1], addr[0]); + + ESP_ERROR_CHECK(esp_event_post(EVENT_BAROS_BLE, BAROS_BLE_DISC, (void*)&event->disc, sizeof(struct ble_gap_disc_desc), 1000)); + + /* + uint8_t *buf = (uint8_t*)malloc(event->disc.length_data); + memcpy(buf, event->disc.data, event->disc.length_data); + + printf("\ndata: "); + for(uint8_t i = 0; i < event->disc.length_data; i++) { + printf("0x%02X ", buf[i]); + } + printf("\n"); + */ + + if(fields.name != NULL) { + char *name = (char*)malloc(fields.name_len); + memcpy(name, (uint8_t*)fields.name, fields.name_len); + name[fields.name_len] = '\0'; + ESP_LOGV(TAG, "Name: %s", name); + } + + ESP_LOGV(TAG, "uuids: %d", fields.num_uuids16); + + for (uint8_t i = 0; i < fields.num_uuids16; i++) { + ESP_LOGV(TAG, "uuid: 0x%04X", fields.uuids16[i].value); + + if (ble_uuid_u16(&fields.uuids16[i].u) == BLE_SVC_GAP_CHR_UUID16_DEVICE_NAME) { + ESP_LOGV(TAG, "device has name"); + } + } + + return 0; + } + case BLE_GAP_EVENT_CONNECT: + /* A new connection was established or a connection attempt failed */ + conn_handle = event->connect.conn_handle; + + ESP_LOGI(TAG, "connection %s; conn_handle: %d status=%d\n", + event->connect.status == 0 ? "established" : "failed", + conn_handle, + event->connect.status + ); + + // always keep advertising + ble_advertise(); + + break; + + case BLE_GAP_EVENT_DISCONNECT: + ESP_LOGI(TAG, "disconnect; reason=%d\n", event->disconnect.reason); + + /* Connection terminated; resume advertising */ + ble_advertise(); + + break; + + case BLE_GAP_EVENT_ADV_COMPLETE: + ESP_LOGI(TAG, "adv complete\n"); + + ble_advertise(); + break; + + case BLE_GAP_EVENT_SUBSCRIBE: + ESP_LOGI(TAG, "subscribe event; cur_notify=%d\n value handle; " + "val_handle=%d\n", + event->subscribe.cur_notify, event->subscribe.attr_handle); + + break; + + case BLE_GAP_EVENT_CONN_UPDATE: + ESP_LOGI(TAG, "BLE_GAP_EVENT_CONN_UPDATE, conn_handle: %d status: %d", + event->mtu.conn_handle, + event->enc_change.status + ); + + break; + + + case BLE_GAP_EVENT_ENC_CHANGE: + ESP_LOGI(TAG, "BLE_GAP_EVENT_ENC_CHANGE"); + + break; + + case BLE_GAP_EVENT_MTU: + ESP_LOGI(TAG, "mtu update event; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + case BLE_GAP_EVENT_NOTIFY_RX: + ESP_LOGI(TAG, "BLE_GAP_EVENT_NOTIFY_RX; conn_handle=%d mtu=%d attr_handle=%d\n", + event->mtu.conn_handle, + event->mtu.value, + event->notify_rx.attr_handle); + + break; + + case BLE_GAP_EVENT_NOTIFY_TX: + ESP_LOGI(TAG, "BLE_GAP_EVENT_NOTIFY_TX; conn_handle=%d mtu=%d\n", + event->mtu.conn_handle, + event->mtu.value); + break; + + default: + ESP_LOGW(TAG, "unhandled ble_gap_event; conn_handle=%d type=%d\n", + event->mtu.conn_handle, + event->type + ); + } + + return 0; +} + +static void ble_advertise(void) { + // check if already adverting + if(ble_gap_adv_active()) return; + + struct ble_gap_adv_params adv_params; + struct ble_hs_adv_fields fields; + int rc; + + 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; + + fields.name = (uint8_t *)device_name; + fields.name_len = strlen(device_name); + fields.name_is_complete = 1; + + rc = ble_gap_adv_set_fields(&fields); + if (rc != 0) { + ESP_LOGE(TAG, "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(blehr_addr_type, NULL, BLE_HS_FOREVER, + &adv_params, ble_gap_event, NULL); + if (rc != 0) { + ESP_LOGE(TAG, "error enabling advertisement; rc=%d\n", rc); + return; + } +} + +int ble_scan_start() { + uint8_t addr_type; + struct ble_gap_disc_params disc_params = { + .filter_duplicates = 1, + .passive = 1, + .itvl = 0, + .window = 0, + .filter_policy = 0, + .limited = 0, + }; + + ESP_ERROR_CHECK(ble_hs_id_infer_auto(0, &addr_type)); + ble_gap_disc_cancel(); + ESP_ERROR_CHECK(ble_gap_disc(addr_type, 3000, &disc_params, ble_gap_event, &addr_type)); + + return 0; +} + +int ble_scan_stop() { + return ble_gap_disc_cancel(); +} + +void nimble_host_task(void *param) { + nimble_port_run(); +} + +static void on_sync() { + ESP_LOGI(TAG, "on_sync"); + + int err; + + err = ble_hs_id_infer_auto(0, &blehr_addr_type); + ESP_ERROR_CHECK(err); + + uint8_t addr_val[6] = {0}; + err = ble_hs_id_copy_addr(blehr_addr_type, addr_val, NULL); + ESP_ERROR_CHECK(err); + + ble_advertise(); +} + +static void on_reset(int reason) { + ESP_LOGE(TAG, "on_reset, reason: %d", reason); +} + +uint8_t baros_ble_init(baros_ble_cfg_t *cfg) { + esp_err_t err; + + device_name = cfg->name; + + //ESP_ERROR_CHECK(esp_nimble_hci_and_controller_init()); + + nimble_port_init(); + + ble_hs_cfg.sync_cb = on_sync; + ble_hs_cfg.reset_cb = on_reset; + + // initialize services + err = ble_gatts_count_cfg(service_defs); + ESP_ERROR_CHECK(err); + + err = ble_gatts_add_svcs(service_defs); + ESP_ERROR_CHECK(err); + + ESP_LOGI(TAG, "Setting device name: %s", device_name); + + err = ble_svc_gap_device_name_set(device_name); + ESP_ERROR_CHECK(err); + + vTaskDelay(500 / portTICK_PERIOD_MS); + + nimble_port_freertos_init(nimble_host_task); + + return 0; +}