3.3.7
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
BLE5 extended scan example for esp32 C3 and S3
|
||||
with this code it is simple to scan legacy (BLE4) compatible advertising,
|
||||
and BLE5 extended advertising. New coded added in BLEScan is not changing old behavior,
|
||||
which can be used with old esp32, but is adding functionality to use on C3/S3.
|
||||
With this new API advertised device wont be stored in API, it is now user responsibility
|
||||
|
||||
author: chegewara
|
||||
*/
|
||||
#ifndef CONFIG_BLUEDROID_ENABLED
|
||||
#error "NimBLE does not support extended scan yet. Try using Bluedroid."
|
||||
#elif !defined(SOC_BLE_50_SUPPORTED)
|
||||
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
|
||||
#else
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
|
||||
uint32_t scanTime = 100; //In 10ms (1000ms)
|
||||
BLEScan *pBLEScan;
|
||||
|
||||
class MyBLEExtAdvertisingCallbacks : public BLEExtAdvertisingCallbacks {
|
||||
void onResult(esp_ble_gap_ext_adv_report_t report) {
|
||||
if (report.event_type & ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) {
|
||||
// here we can receive regular advertising data from BLE4.x devices
|
||||
Serial.println("BLE4.2");
|
||||
} else {
|
||||
// here we will get extended advertising data that are advertised over data channel by BLE5 devices
|
||||
Serial.printf("Ext advertise: data_le: %d, data_status: %d \n", report.adv_data_len, report.data_status);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setExtendedScanCallback(new MyBLEExtAdvertisingCallbacks());
|
||||
pBLEScan->setExtScanParams(); // use with pre-defined/default values, overloaded function allows to pass parameters
|
||||
delay(1000); // it is just for simplicity this example, to let ble stack to set extended scan params
|
||||
pBLEScan->startExtScan(scanTime, 3); // scan duration in n * 10ms, period - repeat after n seconds (period >= duration)
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
||||
#endif // SOC_BLE_50_SUPPORTED
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires:
|
||||
- CONFIG_SOC_BLE_50_SUPPORTED=y
|
||||
- CONFIG_BLUEDROID_ENABLED=y
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
Simple BLE5 multi advertising example on esp32 C3/S3
|
||||
only ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND is backward compatible
|
||||
and can be scanned with BLE4.2 devices
|
||||
|
||||
author: chegewara
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_BLUEDROID_ENABLED
|
||||
#error "NimBLE does not support multi advertising yet. Try using Bluedroid."
|
||||
#elif !defined(CONFIG_BT_BLE_50_FEATURES_SUPPORTED)
|
||||
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
|
||||
#else
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEAdvertising.h>
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_1M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE,
|
||||
.interval_min = 0x30,
|
||||
.interval_max = 0x30,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr = {0, 0, 0, 0, 0, 0},
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_CODED,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.sid = 0,
|
||||
.scan_req_notif = false,
|
||||
};
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
|
||||
.interval_min = 0x40,
|
||||
.interval_max = 0x40,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr = {0, 0, 0, 0, 0, 0},
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_2M,
|
||||
.sid = 1,
|
||||
.scan_req_notif = false,
|
||||
};
|
||||
|
||||
esp_ble_gap_ext_adv_params_t legacy_adv_params = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_LEGACY_IND,
|
||||
.interval_min = 0x45,
|
||||
.interval_max = 0x45,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr = {0, 0, 0, 0, 0, 0},
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.sid = 2,
|
||||
.scan_req_notif = false,
|
||||
};
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_coded = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE,
|
||||
.interval_min = 0x50,
|
||||
.interval_max = 0x50,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr = {0, 0, 0, 0, 0, 0},
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_CODED,
|
||||
.sid = 3,
|
||||
.scan_req_notif = false,
|
||||
};
|
||||
|
||||
static uint8_t raw_adv_data_1m[] = {0x02, 0x01, 0x06, 0x02, 0x0a, 0xeb, 0x12, 0x09, 'E', 'S', 'P', '_', 'M',
|
||||
'U', 'L', 'T', 'I', '_', 'A', 'D', 'V', '_', '1', 'M', 0X0};
|
||||
|
||||
static uint8_t raw_scan_rsp_data_2m[] = {0x02, 0x01, 0x06, 0x02, 0x0a, 0xeb, 0x12, 0x09, 'E', 'S', 'P', '_', 'M',
|
||||
'U', 'L', 'T', 'I', '_', 'A', 'D', 'V', '_', '2', 'M', 0X0};
|
||||
|
||||
static uint8_t legacy_adv_data[] = {0x02, 0x01, 0x06, 0x02, 0x0a, 0xeb, 0x15, 0x09, 'E', 'S', 'P', '_', 'M', 'U',
|
||||
'L', 'T', 'I', '_', 'A', 'D', 'V', '_', 'C', 'O', 'D', 'E', 'D', 0X0};
|
||||
|
||||
static uint8_t legacy_scan_rsp_data[] = {0x02, 0x01, 0x06, 0x02, 0x0a, 0xeb, 0x16, 0x09, 'E', 'S', 'P', '_', 'M', 'U', 'L',
|
||||
'T', 'I', '_', 'A', 'D', 'V', '_', 'L', 'E', 'G', 'A', 'C', 'Y', 0X0};
|
||||
|
||||
static uint8_t raw_scan_rsp_data_coded[] = {0x37, 0x09, 'V', 'E', 'R', 'Y', '_', 'L', 'O', 'N', 'G', '_', 'D', 'E', 'V', 'I', 'C', 'E', '_',
|
||||
'N', 'A', 'M', 'E', '_', 'S', 'E', 'N', 'T', '_', 'U', 'S', 'I', 'N', 'G', '_', 'E', 'X', 'T',
|
||||
'E', 'N', 'D', 'E', 'D', '_', 'A', 'D', 'V', 'E', 'R', 'T', 'I', 'S', 'I', 'N', 'G', 0X0};
|
||||
|
||||
uint8_t addr_1m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x01};
|
||||
uint8_t addr_2m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02};
|
||||
uint8_t addr_legacy[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x03};
|
||||
uint8_t addr_coded[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x04};
|
||||
|
||||
BLEMultiAdvertising advert(4); // max number of advertisement data
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Multi-Advertising...");
|
||||
|
||||
BLEDevice::init("");
|
||||
|
||||
advert.setAdvertisingParams(0, &ext_adv_params_1M);
|
||||
advert.setAdvertisingData(0, sizeof(raw_adv_data_1m), &raw_adv_data_1m[0]);
|
||||
advert.setInstanceAddress(0, addr_1m);
|
||||
advert.setDuration(0);
|
||||
|
||||
advert.setAdvertisingParams(1, &ext_adv_params_2M);
|
||||
advert.setScanRspData(1, sizeof(raw_scan_rsp_data_2m), &raw_scan_rsp_data_2m[0]);
|
||||
advert.setInstanceAddress(1, addr_2m);
|
||||
advert.setDuration(1);
|
||||
|
||||
advert.setAdvertisingParams(2, &legacy_adv_params);
|
||||
advert.setAdvertisingData(2, sizeof(legacy_adv_data), &legacy_adv_data[0]);
|
||||
advert.setScanRspData(2, sizeof(legacy_scan_rsp_data), &legacy_scan_rsp_data[0]);
|
||||
advert.setInstanceAddress(2, addr_legacy);
|
||||
advert.setDuration(2);
|
||||
|
||||
advert.setAdvertisingParams(3, &ext_adv_params_coded);
|
||||
advert.setDuration(3);
|
||||
advert.setScanRspData(3, sizeof(raw_scan_rsp_data_coded), &raw_scan_rsp_data_coded[0]);
|
||||
advert.setInstanceAddress(3, addr_coded);
|
||||
|
||||
delay(1000);
|
||||
advert.start(4, 0);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(2000);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires:
|
||||
- CONFIG_SOC_BLE_50_SUPPORTED=y
|
||||
- CONFIG_BLUEDROID_ENABLED=y
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
Simple BLE5 periodic advertising example on esp32 C3/S3
|
||||
only ESP_BLE_GAP_SET_EXT_ADV_PROP_NONCONN_NONSCANNABLE_UNDIRECTED can be used for periodic advertising
|
||||
|
||||
author: chegewara
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_BLUEDROID_ENABLED
|
||||
#error "NimBLE does not support periodic advertising yet. Try using Bluedroid."
|
||||
#elif !defined(CONFIG_BT_BLE_50_FEATURES_SUPPORTED)
|
||||
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
|
||||
#else
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEAdvertising.h>
|
||||
|
||||
esp_ble_gap_ext_adv_params_t ext_adv_params_2M = {
|
||||
.type = ESP_BLE_GAP_SET_EXT_ADV_PROP_NONCONN_NONSCANNABLE_UNDIRECTED,
|
||||
.interval_min = 0x40,
|
||||
.interval_max = 0x40,
|
||||
.channel_map = ADV_CHNL_ALL,
|
||||
.own_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.peer_addr = {0, 0, 0, 0, 0, 0},
|
||||
.filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
||||
.tx_power = EXT_ADV_TX_PWR_NO_PREFERENCE,
|
||||
.primary_phy = ESP_BLE_GAP_PHY_1M,
|
||||
.max_skip = 0,
|
||||
.secondary_phy = ESP_BLE_GAP_PHY_2M,
|
||||
.sid = 1,
|
||||
.scan_req_notif = false,
|
||||
};
|
||||
|
||||
static uint8_t raw_scan_rsp_data_2m[] = {0x02, 0x01, 0x06, 0x02, 0x0a, 0xeb, 0x12, 0x09, 'E', 'S', 'P', '_', 'M',
|
||||
'U', 'L', 'T', 'I', '_', 'A', 'D', 'V', '_', '2', 'M', 0X0};
|
||||
|
||||
static esp_ble_gap_periodic_adv_params_t periodic_adv_params = {
|
||||
.interval_min = 0x320, // 1000 ms interval
|
||||
.interval_max = 0x640,
|
||||
.properties = 0, // Do not include TX power
|
||||
};
|
||||
|
||||
static uint8_t periodic_adv_raw_data[] = {0x02, 0x01, 0x06, 0x02, 0x0a, 0xeb, 0x03, 0x03, 0xab, 0xcd, 0x11, 0x09, 'E', 'S',
|
||||
'P', '_', 'P', 'E', 'R', 'I', 'O', 'D', 'I', 'C', '_', 'A', 'D', 'V'};
|
||||
|
||||
uint8_t addr_2m[6] = {0xc0, 0xde, 0x52, 0x00, 0x00, 0x02};
|
||||
|
||||
BLEMultiAdvertising advert(1); // max number of advertisement data
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Multi-Advertising...");
|
||||
|
||||
BLEDevice::init("");
|
||||
|
||||
advert.setAdvertisingParams(0, &ext_adv_params_2M);
|
||||
advert.setAdvertisingData(0, sizeof(raw_scan_rsp_data_2m), &raw_scan_rsp_data_2m[0]);
|
||||
advert.setInstanceAddress(0, addr_2m);
|
||||
advert.setDuration(0, 0, 0);
|
||||
|
||||
delay(100);
|
||||
advert.start();
|
||||
advert.setPeriodicAdvertisingParams(0, &periodic_adv_params);
|
||||
advert.setPeriodicAdvertisingData(0, sizeof(periodic_adv_raw_data), &periodic_adv_raw_data[0]);
|
||||
advert.startPeriodicAdvertising(0);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(2000);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires:
|
||||
- CONFIG_SOC_BLE_50_SUPPORTED=y
|
||||
- CONFIG_BLUEDROID_ENABLED=y
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
BLE5 extended scan example for esp32 C3 and S3
|
||||
with this code it is simple to scan legacy (BLE4) compatible advertising,
|
||||
and BLE5 extended advertising. New coded added in BLEScan is not changing old behavior,
|
||||
which can be used with old esp32, but is adding functionality to use on C3/S3.
|
||||
With this new API advertised device wont be stored in API, it is now user responsibility
|
||||
|
||||
author: chegewara
|
||||
*/
|
||||
#ifndef CONFIG_BLUEDROID_ENABLED
|
||||
#error "NimBLE does not support periodic sync yet. Try using Bluedroid."
|
||||
#elif !defined(SOC_BLE_50_SUPPORTED)
|
||||
#error "This SoC does not support BLE5. Try using ESP32-C3, or ESP32-S3"
|
||||
#else
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
|
||||
BLEScan *pBLEScan;
|
||||
static bool periodic_sync = false;
|
||||
|
||||
static esp_ble_gap_periodic_adv_sync_params_t periodic_adv_sync_params = {
|
||||
.filter_policy = 0,
|
||||
.sid = 0,
|
||||
.addr_type = BLE_ADDR_TYPE_RANDOM,
|
||||
.addr = {0, 0, 0, 0, 0, 0},
|
||||
.skip = 10,
|
||||
.sync_timeout = 1000, // timeout: 1000 * 10ms
|
||||
};
|
||||
|
||||
class MyBLEExtAdvertisingCallbacks : public BLEExtAdvertisingCallbacks {
|
||||
void onResult(esp_ble_gap_ext_adv_report_t params) {
|
||||
uint8_t *adv_name = NULL;
|
||||
uint8_t adv_name_len = 0;
|
||||
adv_name = esp_ble_resolve_adv_data(params.adv_data, ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
|
||||
if ((adv_name != NULL) && (memcmp(adv_name, "ESP_MULTI_ADV_2M", adv_name_len) == 0) && !periodic_sync) {
|
||||
periodic_sync = true;
|
||||
char adv_temp_name[60] = {'0'};
|
||||
memcpy(adv_temp_name, adv_name, adv_name_len);
|
||||
log_i("Start create sync with the peer device %s", adv_temp_name);
|
||||
periodic_adv_sync_params.sid = params.sid;
|
||||
// periodic_adv_sync_params.addr_type = params.addr_type;
|
||||
memcpy(periodic_adv_sync_params.addr, params.addr, sizeof(esp_bd_addr_t));
|
||||
esp_ble_gap_periodic_adv_create_sync(&periodic_adv_sync_params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class MyPeriodicScan : public BLEPeriodicScanCallbacks {
|
||||
// void onCreateSync(esp_bt_status_t status){}
|
||||
// void onCancelSync(esp_bt_status_t status){}
|
||||
// void onTerminateSync(esp_bt_status_t status){}
|
||||
|
||||
void onStop(esp_bt_status_t status) {
|
||||
log_i("ESP_GAP_BLE_EXT_SCAN_STOP_COMPLETE_EVT");
|
||||
periodic_sync = false;
|
||||
pBLEScan->startExtScan(0, 0); // scan duration in n * 10ms, period - repeat after n seconds (period >= duration)
|
||||
}
|
||||
|
||||
void onLostSync(uint16_t sync_handle) {
|
||||
log_i("ESP_GAP_BLE_PERIODIC_ADV_SYNC_LOST_EVT");
|
||||
esp_ble_gap_stop_ext_scan();
|
||||
}
|
||||
|
||||
void onSync(esp_ble_periodic_adv_sync_estab_param_t params) {
|
||||
log_i("ESP_GAP_BLE_PERIODIC_ADV_SYNC_ESTAB_EVT, status %d", params.status);
|
||||
// esp_log_buffer_hex("sync addr", param->periodic_adv_sync_estab.adv_addr, 6);
|
||||
log_i("sync handle %d sid %d perioic adv interval %d adv phy %d", params.sync_handle, params.sid, params.period_adv_interval, params.adv_phy);
|
||||
}
|
||||
|
||||
void onReport(esp_ble_gap_periodic_adv_report_t params) {
|
||||
log_i("periodic adv report, sync handle %d data status %d data len %d rssi %d", params.sync_handle, params.data_status, params.data_length, params.rssi);
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Periodic scan...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setExtendedScanCallback(new MyBLEExtAdvertisingCallbacks());
|
||||
pBLEScan->setExtScanParams(); // use with pre-defined/default values, overloaded function allows to pass parameters
|
||||
pBLEScan->setPeriodicScanCallback(new MyPeriodicScan());
|
||||
delay(100); // it is just for simplicity this example, to let ble stack to set extended scan params
|
||||
pBLEScan->startExtScan(0, 0);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(2000);
|
||||
}
|
||||
|
||||
#endif // SOC_BLE_50_SUPPORTED
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires:
|
||||
- CONFIG_SOC_BLE_50_SUPPORTED=y
|
||||
- CONFIG_BLUEDROID_ENABLED=y
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
Changed to a beacon scanner to report iBeacon, EddystoneURL and EddystoneTLM beacons by beegee-tokyo
|
||||
Upgraded Eddystone part by Tomas Pilny on Feb 20, 2023
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
#include <BLEEddystoneURL.h>
|
||||
#include <BLEEddystoneTLM.h>
|
||||
#include <BLEBeacon.h>
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
BLEScan *pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
if (advertisedDevice.haveName()) {
|
||||
Serial.print("Device name: ");
|
||||
Serial.println(advertisedDevice.getName().c_str());
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
if (advertisedDevice.haveServiceUUID()) {
|
||||
BLEUUID devUUID = advertisedDevice.getServiceUUID();
|
||||
Serial.print("Found ServiceUUID: ");
|
||||
Serial.println(devUUID.toString().c_str());
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
if (advertisedDevice.haveManufacturerData() == true) {
|
||||
String strManufacturerData = advertisedDevice.getManufacturerData();
|
||||
|
||||
// Buffer to store manufacturer data (BLE max is 255 bytes)
|
||||
uint8_t cManufacturerData[255];
|
||||
size_t dataLength = strManufacturerData.length();
|
||||
|
||||
// Bounds checking to prevent buffer overflow
|
||||
if (dataLength <= sizeof(cManufacturerData)) {
|
||||
memcpy(cManufacturerData, strManufacturerData.c_str(), dataLength);
|
||||
|
||||
if (dataLength == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) {
|
||||
Serial.println("Found an iBeacon!");
|
||||
BLEBeacon oBeacon = BLEBeacon();
|
||||
oBeacon.setData(strManufacturerData);
|
||||
Serial.printf("iBeacon Frame\n");
|
||||
Serial.printf(
|
||||
"ID: %04X Major: %d Minor: %d UUID: %s Power: %d\n", oBeacon.getManufacturerId(), ENDIAN_CHANGE_U16(oBeacon.getMajor()),
|
||||
ENDIAN_CHANGE_U16(oBeacon.getMinor()), oBeacon.getProximityUUID().toString().c_str(), oBeacon.getSignalPower()
|
||||
);
|
||||
} else {
|
||||
Serial.println("Found another manufacturers beacon!");
|
||||
Serial.printf("strManufacturerData: %zu ", dataLength);
|
||||
for (int i = 0; i < dataLength; i++) {
|
||||
Serial.printf("[%X]", cManufacturerData[i]);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
}
|
||||
} else {
|
||||
Serial.printf("Manufacturer data too large (%zu bytes), skipping\n", dataLength);
|
||||
}
|
||||
}
|
||||
|
||||
if (advertisedDevice.getFrameType() == BLE_EDDYSTONE_URL_FRAME) {
|
||||
Serial.println("Found an EddystoneURL beacon!");
|
||||
BLEEddystoneURL EddystoneURL = BLEEddystoneURL(&advertisedDevice);
|
||||
Serial.printf("URL bytes: 0x");
|
||||
String url = EddystoneURL.getURL();
|
||||
for (auto byte : url) {
|
||||
Serial.printf("%02X", byte);
|
||||
}
|
||||
Serial.printf("\n");
|
||||
Serial.printf("Decoded URL: %s\n", EddystoneURL.getDecodedURL().c_str());
|
||||
Serial.printf("EddystoneURL.getDecodedURL(): %s\n", EddystoneURL.getDecodedURL().c_str());
|
||||
Serial.printf("TX power %d (Raw 0x%02X)\n", EddystoneURL.getPower(), EddystoneURL.getPower());
|
||||
Serial.println("\n");
|
||||
}
|
||||
|
||||
if (advertisedDevice.getFrameType() == BLE_EDDYSTONE_TLM_FRAME) {
|
||||
Serial.println("Found an EddystoneTLM beacon!");
|
||||
BLEEddystoneTLM EddystoneTLM(&advertisedDevice);
|
||||
Serial.printf("Reported battery voltage: %dmV\n", EddystoneTLM.getVolt());
|
||||
Serial.printf("Reported temperature: %.2f°C (raw data=0x%04X)\n", EddystoneTLM.getTemp(), EddystoneTLM.getRawTemp());
|
||||
Serial.printf("Reported advertise count: %lu\n", EddystoneTLM.getCount());
|
||||
Serial.printf("Reported time since last reboot: %lus\n", EddystoneTLM.getTime());
|
||||
Serial.println("\n");
|
||||
Serial.print(EddystoneTLM.toString().c_str());
|
||||
Serial.println("\n");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
BLEScanResults *foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices->getCount());
|
||||
Serial.println("Scan done!");
|
||||
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
## BLE Beacon Scanner
|
||||
|
||||
Initiates a BLE device scan.
|
||||
Checks if the discovered devices are
|
||||
- an iBeacon
|
||||
- an Eddystone TLM beacon
|
||||
- an Eddystone URL beacon
|
||||
|
||||
and sends the decoded beacon information over Serial log
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,158 @@
|
||||
/**
|
||||
* A BLE client example that is rich in capabilities.
|
||||
* There is a lot new capabilities implemented.
|
||||
* author unknown
|
||||
* updated by chegewara
|
||||
*/
|
||||
|
||||
#include "BLEDevice.h"
|
||||
//#include "BLEScan.h"
|
||||
|
||||
// The remote service we wish to connect to.
|
||||
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
|
||||
// The characteristic of the remote service we are interested in.
|
||||
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
|
||||
|
||||
static boolean doConnect = false;
|
||||
static boolean connected = false;
|
||||
static boolean doScan = false;
|
||||
static BLERemoteCharacteristic *pRemoteCharacteristic;
|
||||
static BLEAdvertisedDevice *myDevice;
|
||||
|
||||
// Callback function to handle notifications
|
||||
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
|
||||
Serial.print("Notify callback for characteristic ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" of data length ");
|
||||
Serial.println(length);
|
||||
Serial.print("data: ");
|
||||
Serial.write(pData, length);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
class MyClientCallback : public BLEClientCallbacks {
|
||||
void onConnect(BLEClient *pclient) {}
|
||||
|
||||
void onDisconnect(BLEClient *pclient) {
|
||||
connected = false;
|
||||
Serial.println("onDisconnect");
|
||||
}
|
||||
};
|
||||
|
||||
bool connectToServer() {
|
||||
Serial.print("Forming a connection to ");
|
||||
Serial.println(myDevice->getAddress().toString().c_str());
|
||||
|
||||
BLEClient *pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
pClient->setClientCallbacks(new MyClientCallback());
|
||||
|
||||
// Connect to the remove BLE Server.
|
||||
pClient->connect(myDevice); // if you pass BLEAdvertisedDevice instead of address, it will be recognized type of peer device address (public or private)
|
||||
Serial.println(" - Connected to server");
|
||||
pClient->setMTU(517); //set client to request maximum MTU from server (default is 23 otherwise)
|
||||
|
||||
// Obtain a reference to the service we are after in the remote BLE server.
|
||||
BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Failed to find our service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our service");
|
||||
|
||||
// Obtain a reference to the characteristic in the service of the remote BLE server.
|
||||
pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find our characteristic UUID: ");
|
||||
Serial.println(charUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our characteristic");
|
||||
|
||||
// Read the value of the characteristic.
|
||||
if (pRemoteCharacteristic->canRead()) {
|
||||
String value = pRemoteCharacteristic->readValue();
|
||||
Serial.print("The characteristic value was: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
if (pRemoteCharacteristic->canNotify()) {
|
||||
// Register/Subscribe for notifications
|
||||
pRemoteCharacteristic->registerForNotify(notifyCallback);
|
||||
}
|
||||
|
||||
connected = true;
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Scan for BLE servers and find the first one that advertises the service we are looking for.
|
||||
*/
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
/**
|
||||
* Called for each advertising BLE server.
|
||||
*/
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("BLE Advertised Device found: ");
|
||||
Serial.println(advertisedDevice.toString().c_str());
|
||||
|
||||
// We have found a device, let us now see if it contains the service we are looking for.
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
|
||||
|
||||
BLEDevice::getScan()->stop();
|
||||
myDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
doConnect = true;
|
||||
doScan = true;
|
||||
|
||||
} // Found our server
|
||||
} // onResult
|
||||
}; // MyAdvertisedDeviceCallbacks
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Arduino BLE Client application...");
|
||||
BLEDevice::init("");
|
||||
|
||||
// Retrieve a Scanner and set the callback we want to use to be informed when we
|
||||
// have detected a new device. Specify that we want active scanning and start the
|
||||
// scan to run for 5 seconds.
|
||||
BLEScan *pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
} // End of setup.
|
||||
|
||||
// This is the Arduino main loop function.
|
||||
void loop() {
|
||||
|
||||
// If the flag "doConnect" is true then we have scanned for and found the desired
|
||||
// BLE Server with which we wish to connect. Now we connect to it. Once we are
|
||||
// connected we set the connected flag to be true.
|
||||
if (doConnect == true) {
|
||||
if (connectToServer()) {
|
||||
Serial.println("We are now connected to the BLE Server.");
|
||||
} else {
|
||||
Serial.println("We have failed to connect to the server; there is nothing more we will do.");
|
||||
}
|
||||
doConnect = false;
|
||||
}
|
||||
|
||||
// If we are connected to a peer BLE Server, update the characteristic each time we are reached
|
||||
// with the current time since boot.
|
||||
if (connected) {
|
||||
String newValue = "Time since boot: " + String(millis() / 1000);
|
||||
Serial.println("Setting new characteristic value to \"" + newValue + "\"");
|
||||
|
||||
// Set the characteristic's value to be the array of bytes that is actually a string.
|
||||
pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
|
||||
} else if (doScan) {
|
||||
BLEDevice::getScan()->start(0); // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
|
||||
}
|
||||
|
||||
delay(1000); // Delay a second between loops.
|
||||
} // End of loop
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* BLE HID Gamepad Client Example
|
||||
*
|
||||
* This example demonstrates how to connect to a BLE HID Gamepad and read its input.
|
||||
* The ESP32 acts as a BLE Central (client) that connects to a BLE gamepad peripheral.
|
||||
*
|
||||
* Features:
|
||||
* - Scans for BLE HID gamepad devices
|
||||
* - Connects to the first gamepad found
|
||||
* - Secure pairing with bonding
|
||||
* - Subscribes to input report notifications
|
||||
* - Parses and displays gamepad input (buttons and axes)
|
||||
* - Automatic reconnection on disconnect
|
||||
*
|
||||
* Usage:
|
||||
* 1. Upload this sketch to your ESP32
|
||||
* 2. Turn on your BLE gamepad (or run the Server_Gamepad example on another ESP32)
|
||||
* 3. The ESP32 will scan, connect, and display gamepad input in the serial monitor
|
||||
*
|
||||
* Note: This example uses "Just Works" pairing for automatic connection without
|
||||
* PIN entry or confirmation. The bond is saved for future connections.
|
||||
*
|
||||
* Compatible with gamepads using the standard HID Report Descriptor format
|
||||
*
|
||||
* Created by lucasssvaz
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLESecurity.h>
|
||||
|
||||
// HID Service UUID (standard UUID for HID over GATT)
|
||||
static BLEUUID hidServiceUUID((uint16_t)0x1812);
|
||||
// HID Report characteristic UUID (used for input/output reports)
|
||||
static BLEUUID reportCharUUID((uint16_t)0x2A4D);
|
||||
// HID Report Map characteristic UUID
|
||||
static BLEUUID reportMapUUID((uint16_t)0x2A4B);
|
||||
|
||||
static boolean doConnect = false;
|
||||
static boolean connected = false;
|
||||
static boolean doScan = false;
|
||||
static BLERemoteCharacteristic *pInputReportCharacteristic = nullptr;
|
||||
static BLEAdvertisedDevice *myDevice = nullptr;
|
||||
static BLEClient *pClient = nullptr;
|
||||
|
||||
// Gamepad report structure (adjust based on your gamepad's report descriptor)
|
||||
// This matches the Server_Gamepad example format
|
||||
struct GamepadReport {
|
||||
uint8_t reportId; // Report ID
|
||||
int8_t x; // X axis (-127 to 127)
|
||||
int8_t y; // Y axis (-127 to 127)
|
||||
uint8_t buttons; // 8 buttons (bit 0-7)
|
||||
} __attribute__((packed));
|
||||
|
||||
// Callback function to handle gamepad input notifications
|
||||
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
|
||||
Serial.printf("Received %d bytes: ", length);
|
||||
|
||||
// Check if data length matches our expected gamepad report
|
||||
if (length == sizeof(GamepadReport)) {
|
||||
GamepadReport *report = (GamepadReport *)pData;
|
||||
|
||||
Serial.printf("ID=%d, X=%4d, Y=%4d, Buttons=0x%02X [", report->reportId, report->x, report->y, report->buttons);
|
||||
|
||||
// Display which buttons are pressed
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (report->buttons & (1 << i)) {
|
||||
Serial.printf("%d ", i + 1);
|
||||
}
|
||||
}
|
||||
Serial.println("]");
|
||||
} else {
|
||||
// Unknown format, just display hex dump
|
||||
Serial.print("Raw data: ");
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
Serial.printf("%02X ", pData[i]);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
|
||||
// Client callbacks to handle connection events
|
||||
class MyClientCallback : public BLEClientCallbacks {
|
||||
void onConnect(BLEClient *pclient) {
|
||||
Serial.println("Connected to gamepad");
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient *pclient) {
|
||||
connected = false;
|
||||
Serial.println("Disconnected from gamepad");
|
||||
}
|
||||
};
|
||||
|
||||
// Function to connect to the gamepad
|
||||
bool connectToServer() {
|
||||
Serial.print("Connecting to gamepad at ");
|
||||
Serial.println(myDevice->getAddress().toString().c_str());
|
||||
|
||||
pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
pClient->setClientCallbacks(new MyClientCallback());
|
||||
|
||||
// Connect to the gamepad
|
||||
pClient->connect(myDevice);
|
||||
Serial.println(" - Connected to server");
|
||||
pClient->setMTU(185); // Set MTU for larger data transfers
|
||||
|
||||
// Obtain a reference to the HID service
|
||||
BLERemoteService *pRemoteService = pClient->getService(hidServiceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.println("Failed to find HID service");
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found HID service");
|
||||
|
||||
// Get all characteristics to find input reports
|
||||
std::map<std::string, BLERemoteCharacteristic *> *pCharMap = pRemoteService->getCharacteristics();
|
||||
|
||||
// Look for input report characteristics (UUID 0x2A4D)
|
||||
for (auto const &entry : *pCharMap) {
|
||||
BLERemoteCharacteristic *pChar = entry.second;
|
||||
|
||||
if (pChar->getUUID().equals(reportCharUUID)) {
|
||||
// Check if this characteristic has notify property (input report)
|
||||
if (pChar->canNotify()) {
|
||||
Serial.printf(" - Found input report characteristic (handle: 0x%04X)\n", pChar->getHandle());
|
||||
|
||||
// Try to read Report Reference Descriptor to identify report type and ID
|
||||
BLERemoteDescriptor *pReportRefDesc = pChar->getDescriptor(BLEUUID((uint16_t)0x2908));
|
||||
if (pReportRefDesc != nullptr) {
|
||||
String refValue = pReportRefDesc->readValue();
|
||||
if (refValue.length() >= 2) {
|
||||
uint8_t reportId = refValue[0];
|
||||
uint8_t reportType = refValue[1];
|
||||
Serial.printf(" Report ID: %d, Type: %d (1=Input, 2=Output, 3=Feature)\n", reportId, reportType);
|
||||
|
||||
// We want input reports (type = 1)
|
||||
if (reportType == 1) {
|
||||
pInputReportCharacteristic = pChar;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No report reference descriptor, assume it's an input report
|
||||
pInputReportCharacteristic = pChar;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pInputReportCharacteristic == nullptr) {
|
||||
Serial.println("Failed to find input report characteristic");
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Subscribe to input report notifications
|
||||
Serial.println(" - Subscribing to input report notifications");
|
||||
pInputReportCharacteristic->registerForNotify(notifyCallback);
|
||||
|
||||
connected = true;
|
||||
Serial.println("Successfully connected and subscribed to gamepad!");
|
||||
|
||||
// Note: Security/encryption will be automatically handled by the BLE stack
|
||||
// when the HID device requires it (using "Just Works" pairing).
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Scan callback to detect gamepad devices
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("BLE Device found: ");
|
||||
Serial.print(advertisedDevice.toString().c_str());
|
||||
|
||||
// Check if device advertises HID service
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(hidServiceUUID)) {
|
||||
Serial.print(" - HID Device!");
|
||||
|
||||
// Check if it's a gamepad by appearance (0x03C4 = HID Gamepad)
|
||||
if (advertisedDevice.haveAppearance()) {
|
||||
uint16_t appearance = advertisedDevice.getAppearance();
|
||||
Serial.printf(" (Appearance: 0x%04X)", appearance);
|
||||
if (appearance == 0x03C4) {
|
||||
Serial.print(" - GAMEPAD!");
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
// Stop scanning and connect
|
||||
BLEDevice::getScan()->stop();
|
||||
myDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
doConnect = true;
|
||||
doScan = true;
|
||||
} else {
|
||||
Serial.println();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n=== BLE HID Gamepad Client ===");
|
||||
Serial.println("Scanning for BLE HID gamepads...\n");
|
||||
|
||||
BLEDevice::init("ESP32-Gamepad-Client");
|
||||
|
||||
// Configure BLE Security for pairing with HID devices
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
|
||||
// Set security capabilities and authentication mode
|
||||
// HID devices typically use "Just Works" pairing (no MITM) with bonding
|
||||
|
||||
// Set IO capability to NONE for "Just Works" pairing
|
||||
pSecurity->setCapability(ESP_IO_CAP_NONE);
|
||||
|
||||
// Bonding, no MITM, secure connections (for "Just Works" pairing)
|
||||
pSecurity->setAuthenticationMode(true, false, true);
|
||||
|
||||
// Set security callbacks (using default implementation)
|
||||
BLEDevice::setSecurityCallbacks(new BLESecurityCallbacks());
|
||||
|
||||
Serial.println("Security configured: Bonding + Secure Connections\n");
|
||||
|
||||
// Create scanner and set callbacks
|
||||
BLEScan *pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Connect to gamepad if found
|
||||
if (doConnect == true) {
|
||||
if (connectToServer()) {
|
||||
Serial.println("\n*** Ready to receive gamepad input ***\n");
|
||||
} else {
|
||||
Serial.println("Failed to connect to gamepad");
|
||||
}
|
||||
doConnect = false;
|
||||
}
|
||||
|
||||
// Restart scanning if disconnected
|
||||
if (!connected && doScan) {
|
||||
Serial.println("\nScanning for gamepads...");
|
||||
BLEDevice::getScan()->start(5, false);
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,280 @@
|
||||
/*
|
||||
* BLE Client and Server Coexistence Example
|
||||
*
|
||||
* This example demonstrates how to run both BLE client and server
|
||||
* functionality on the same ESP32 device simultaneously.
|
||||
*
|
||||
* The device will:
|
||||
* - Act as a BLE server, advertising a service with a characteristic
|
||||
* - Act as a BLE client, scanning for other BLE servers
|
||||
* - Connect to found servers and interact with their services
|
||||
* - Handle both incoming and outgoing connections
|
||||
*
|
||||
* You can test this example by uploading it to two ESP32 boards.
|
||||
*
|
||||
* Author: lucasssvaz
|
||||
* Based on Arduino BLE examples
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEClient.h>
|
||||
#include <BLEScan.h>
|
||||
|
||||
// Server-side definitions
|
||||
#define SERVER_SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define SERVER_CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
// Client-side definitions (looking for the same service)
|
||||
static BLEUUID clientServiceUUID(SERVER_SERVICE_UUID);
|
||||
static BLEUUID clientCharUUID(SERVER_CHARACTERISTIC_UUID);
|
||||
|
||||
// Server objects
|
||||
BLEServer *pServer = nullptr;
|
||||
BLECharacteristic *pServerCharacteristic = nullptr;
|
||||
|
||||
// Client objects
|
||||
static boolean doConnect = false;
|
||||
static boolean clientConnected = false;
|
||||
static BLERemoteCharacteristic *pRemoteCharacteristic;
|
||||
static BLEAdvertisedDevice *targetDevice;
|
||||
static BLEClient *pClient = nullptr;
|
||||
BLEScan *pBLEScan = nullptr;
|
||||
|
||||
// Server callbacks
|
||||
class ServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *pServer) {
|
||||
Serial.println("Server: Client connected");
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer *pServer) {
|
||||
Serial.println("Server: Client disconnected");
|
||||
// Restart advertising
|
||||
BLEDevice::startAdvertising();
|
||||
}
|
||||
};
|
||||
|
||||
// Characteristic callbacks for server
|
||||
class CharacteristicCallbacks : public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
String value = pCharacteristic->getValue();
|
||||
Serial.print("Server: Characteristic written, value: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
void onRead(BLECharacteristic *pCharacteristic) {
|
||||
Serial.println("Server: Characteristic read");
|
||||
}
|
||||
};
|
||||
|
||||
// Client callbacks
|
||||
class ClientCallbacks : public BLEClientCallbacks {
|
||||
void onConnect(BLEClient *pClient) {
|
||||
Serial.println("Client: Connected to server");
|
||||
clientConnected = true;
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient *pClient) {
|
||||
Serial.println("Client: Disconnected from server");
|
||||
clientConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Client notification callback
|
||||
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
|
||||
Serial.print("Client: Notify callback for characteristic ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" of data length ");
|
||||
Serial.println(length);
|
||||
Serial.print("Client: Data: ");
|
||||
Serial.write(pData, length);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
// Scan callbacks
|
||||
class AdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("Client: Found device: ");
|
||||
Serial.println(advertisedDevice.toString().c_str());
|
||||
|
||||
// Check if this device has our target service
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(clientServiceUUID) && !clientConnected) {
|
||||
|
||||
Serial.println("Client: Found target service, attempting connection...");
|
||||
BLEDevice::getScan()->stop();
|
||||
targetDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
doConnect = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bool connectToServer() {
|
||||
Serial.print("Client: Forming connection to ");
|
||||
Serial.println(targetDevice->getAddress().toString().c_str());
|
||||
|
||||
// Create client if it doesn't exist, otherwise reuse existing one
|
||||
if (pClient == nullptr) {
|
||||
pClient = BLEDevice::createClient();
|
||||
pClient->setClientCallbacks(new ClientCallbacks());
|
||||
Serial.println("Client: Created new client");
|
||||
} else {
|
||||
Serial.println("Client: Reusing existing client");
|
||||
}
|
||||
|
||||
if (!pClient->connect(targetDevice)) {
|
||||
Serial.println("Client: Failed to connect");
|
||||
return false;
|
||||
}
|
||||
|
||||
Serial.println("Client: Connected to server");
|
||||
pClient->setMTU(517); // Request maximum MTU
|
||||
|
||||
// Get the service
|
||||
BLERemoteService *pRemoteService = pClient->getService(clientServiceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Client: Failed to find service UUID: ");
|
||||
Serial.println(clientServiceUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println("Client: Found service");
|
||||
|
||||
// Get the characteristic
|
||||
pRemoteCharacteristic = pRemoteService->getCharacteristic(clientCharUUID);
|
||||
if (pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Client: Failed to find characteristic UUID: ");
|
||||
Serial.println(clientCharUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println("Client: Found characteristic");
|
||||
|
||||
// Read the initial value
|
||||
if (pRemoteCharacteristic->canRead()) {
|
||||
String value = pRemoteCharacteristic->readValue();
|
||||
Serial.print("Client: Initial characteristic value: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
// Register for notifications if available
|
||||
if (pRemoteCharacteristic->canNotify()) {
|
||||
pRemoteCharacteristic->registerForNotify(notifyCallback);
|
||||
Serial.println("Client: Registered for notifications");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void setupServer() {
|
||||
Serial.println("Setting up BLE Server...");
|
||||
|
||||
// Create server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new ServerCallbacks());
|
||||
|
||||
// Create service
|
||||
BLEService *pService = pServer->createService(SERVER_SERVICE_UUID);
|
||||
|
||||
// Create characteristic
|
||||
pServerCharacteristic = pService->createCharacteristic(
|
||||
SERVER_CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
|
||||
pServerCharacteristic->setCallbacks(new CharacteristicCallbacks());
|
||||
pServerCharacteristic->setValue("Hello from Coexistence Server");
|
||||
|
||||
// Start service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVER_SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06);
|
||||
pAdvertising->setMinPreferred(0x12);
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
|
||||
Serial.println("Server: Advertising started");
|
||||
}
|
||||
|
||||
void setupClient() {
|
||||
Serial.println("Setting up BLE Client...");
|
||||
|
||||
// Create scanner
|
||||
pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99);
|
||||
|
||||
Serial.println("Client: Scanner configured");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE Client-Server Coexistence Example...");
|
||||
|
||||
// Initialize BLE device with a name
|
||||
BLEDevice::init("ESP32-Coexistence");
|
||||
|
||||
// Setup both server and client
|
||||
setupServer();
|
||||
setupClient();
|
||||
|
||||
// Start initial scan
|
||||
pBLEScan->start(10, false); // Scan for 10 seconds, don't repeat
|
||||
|
||||
Serial.println("Setup complete. Device is advertising as server and scanning as client.");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static unsigned long lastServerUpdate = 0;
|
||||
static unsigned long lastClientWrite = 0;
|
||||
static unsigned long lastScanStart = 0;
|
||||
unsigned long currentTime = millis();
|
||||
|
||||
// Handle client connection attempts
|
||||
if (doConnect && !clientConnected) {
|
||||
if (connectToServer()) {
|
||||
Serial.println("Client: Successfully connected to remote server");
|
||||
} else {
|
||||
Serial.println("Client: Failed to connect, will retry scanning");
|
||||
// Restart scanning after failed connection
|
||||
pBLEScan->start(10, false);
|
||||
}
|
||||
doConnect = false;
|
||||
}
|
||||
|
||||
// Update server characteristic periodically
|
||||
if (currentTime - lastServerUpdate > 5000) { // Every 5 seconds
|
||||
String value = "Server time: " + String(millis() / 1000);
|
||||
pServerCharacteristic->setValue(value.c_str());
|
||||
pServerCharacteristic->notify(); // Notify connected clients
|
||||
Serial.print("Server: Updated characteristic to: ");
|
||||
Serial.println(value);
|
||||
|
||||
lastServerUpdate = currentTime;
|
||||
}
|
||||
|
||||
// Write to remote characteristic if connected as client
|
||||
if (clientConnected && pRemoteCharacteristic && currentTime - lastClientWrite > 3000) {
|
||||
if (pRemoteCharacteristic->canWrite()) {
|
||||
String clientValue = "Client msg: " + String(millis() / 1000);
|
||||
pRemoteCharacteristic->writeValue(clientValue.c_str(), clientValue.length());
|
||||
Serial.print("Client: Wrote to remote characteristic: ");
|
||||
Serial.println(clientValue);
|
||||
lastClientWrite = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
// Restart scanning periodically if not connected
|
||||
if (!clientConnected && currentTime - lastScanStart > 15000) { // Every 15 seconds
|
||||
Serial.println("Client: Restarting scan...");
|
||||
pBLEScan->start(10, false);
|
||||
lastScanStart = currentTime;
|
||||
}
|
||||
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,276 @@
|
||||
/**
|
||||
* A BLE client example that connects to multiple BLE servers simultaneously.
|
||||
*
|
||||
* This example demonstrates how to:
|
||||
* - Scan for multiple BLE servers
|
||||
* - Connect to multiple servers at the same time
|
||||
* - Interact with characteristics on different servers
|
||||
* - Handle disconnections and reconnections
|
||||
*
|
||||
* The example looks for servers advertising the service UUID: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
* and connects to up to MAX_SERVERS servers.
|
||||
*
|
||||
* Created by lucasssvaz
|
||||
* Based on the original Client example by Neil Kolban and chegewara
|
||||
*/
|
||||
|
||||
#include "BLEDevice.h"
|
||||
|
||||
// The remote service we wish to connect to.
|
||||
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
|
||||
// The characteristic of the remote service we are interested in.
|
||||
static BLEUUID charUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
|
||||
|
||||
// Maximum number of servers to connect to
|
||||
#define MAX_SERVERS 3
|
||||
|
||||
// Structure to hold information about each connected server
|
||||
struct ServerConnection {
|
||||
BLEClient *pClient;
|
||||
BLEAdvertisedDevice *pDevice;
|
||||
BLERemoteCharacteristic *pRemoteCharacteristic;
|
||||
bool connected;
|
||||
bool doConnect;
|
||||
String name;
|
||||
};
|
||||
|
||||
// Array to manage multiple server connections
|
||||
ServerConnection servers[MAX_SERVERS];
|
||||
int connectedServers = 0;
|
||||
static bool doScan = true;
|
||||
|
||||
// Callback function to handle notifications from any server
|
||||
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
|
||||
// Find which server this notification came from
|
||||
for (int i = 0; i < MAX_SERVERS; i++) {
|
||||
if (servers[i].connected && servers[i].pRemoteCharacteristic == pBLERemoteCharacteristic) {
|
||||
Serial.print("Notify from server ");
|
||||
Serial.print(servers[i].name);
|
||||
Serial.print(" - Characteristic: ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" | Length: ");
|
||||
Serial.print(length);
|
||||
Serial.print(" | Data: ");
|
||||
Serial.write(pData, length);
|
||||
Serial.println();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Client callback class to handle connect/disconnect events
|
||||
class MyClientCallback : public BLEClientCallbacks {
|
||||
int serverIndex;
|
||||
|
||||
public:
|
||||
MyClientCallback(int index) : serverIndex(index) {}
|
||||
|
||||
void onConnect(BLEClient *pclient) {
|
||||
Serial.print("Connected to server ");
|
||||
Serial.println(servers[serverIndex].name);
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient *pclient) {
|
||||
servers[serverIndex].connected = false;
|
||||
connectedServers--;
|
||||
Serial.print("Disconnected from server ");
|
||||
Serial.print(servers[serverIndex].name);
|
||||
Serial.print(" | Total connected: ");
|
||||
Serial.println(connectedServers);
|
||||
doScan = true; // Resume scanning to find replacement servers
|
||||
}
|
||||
};
|
||||
|
||||
// Function to connect to a specific server
|
||||
bool connectToServer(int serverIndex) {
|
||||
Serial.print("Connecting to server ");
|
||||
Serial.print(serverIndex);
|
||||
Serial.print(" at address: ");
|
||||
Serial.println(servers[serverIndex].pDevice->getAddress().toString().c_str());
|
||||
|
||||
servers[serverIndex].pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
// Set the callback for this specific server connection
|
||||
servers[serverIndex].pClient->setClientCallbacks(new MyClientCallback(serverIndex));
|
||||
|
||||
// Connect to the remote BLE Server
|
||||
servers[serverIndex].pClient->connect(servers[serverIndex].pDevice);
|
||||
Serial.println(" - Connected to server");
|
||||
servers[serverIndex].pClient->setMTU(517); // Request maximum MTU from server
|
||||
|
||||
// Obtain a reference to the service we are after in the remote BLE server
|
||||
BLERemoteService *pRemoteService = servers[serverIndex].pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Failed to find service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
servers[serverIndex].pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found service");
|
||||
|
||||
// Obtain a reference to the characteristic in the service
|
||||
servers[serverIndex].pRemoteCharacteristic = pRemoteService->getCharacteristic(charUUID);
|
||||
if (servers[serverIndex].pRemoteCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find characteristic UUID: ");
|
||||
Serial.println(charUUID.toString().c_str());
|
||||
servers[serverIndex].pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found characteristic");
|
||||
|
||||
// Read the value of the characteristic
|
||||
if (servers[serverIndex].pRemoteCharacteristic->canRead()) {
|
||||
String value = servers[serverIndex].pRemoteCharacteristic->readValue();
|
||||
Serial.print("Initial characteristic value: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
// Register for notifications if available
|
||||
if (servers[serverIndex].pRemoteCharacteristic->canNotify()) {
|
||||
servers[serverIndex].pRemoteCharacteristic->registerForNotify(notifyCallback);
|
||||
Serial.println(" - Registered for notifications");
|
||||
}
|
||||
|
||||
servers[serverIndex].connected = true;
|
||||
connectedServers++;
|
||||
Serial.print("Successfully connected! Total servers connected: ");
|
||||
Serial.println(connectedServers);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Scan callback class to find BLE servers
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("BLE Device found: ");
|
||||
Serial.println(advertisedDevice.toString().c_str());
|
||||
|
||||
// Check if this device has the service we're looking for
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
|
||||
Serial.println(" -> This device has our service!");
|
||||
|
||||
// Check if we already know about this device
|
||||
String deviceAddress = advertisedDevice.getAddress().toString().c_str();
|
||||
bool alreadyKnown = false;
|
||||
|
||||
for (int i = 0; i < MAX_SERVERS; i++) {
|
||||
if (servers[i].pDevice != nullptr) {
|
||||
if (servers[i].pDevice->getAddress().toString() == deviceAddress) {
|
||||
alreadyKnown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (alreadyKnown) {
|
||||
Serial.println(" -> Already connected or connecting to this device");
|
||||
return;
|
||||
}
|
||||
|
||||
// Find an empty slot for this server
|
||||
for (int i = 0; i < MAX_SERVERS; i++) {
|
||||
if (servers[i].pDevice == nullptr || (!servers[i].connected && !servers[i].doConnect)) {
|
||||
servers[i].pDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
servers[i].doConnect = true;
|
||||
servers[i].name = "Server_" + String(i);
|
||||
Serial.print(" -> Assigned to slot ");
|
||||
Serial.println(i);
|
||||
|
||||
// If we've found enough servers, stop scanning
|
||||
int pendingConnections = 0;
|
||||
for (int j = 0; j < MAX_SERVERS; j++) {
|
||||
if (servers[j].connected || servers[j].doConnect) {
|
||||
pendingConnections++;
|
||||
}
|
||||
}
|
||||
if (pendingConnections >= MAX_SERVERS) {
|
||||
Serial.println("Found enough servers, stopping scan");
|
||||
BLEDevice::getScan()->stop();
|
||||
doScan = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("=================================");
|
||||
Serial.println("BLE Multi-Client Example");
|
||||
Serial.println("=================================");
|
||||
Serial.print("Max servers to connect: ");
|
||||
Serial.println(MAX_SERVERS);
|
||||
Serial.println();
|
||||
|
||||
// Initialize all server connections
|
||||
for (int i = 0; i < MAX_SERVERS; i++) {
|
||||
servers[i].pClient = nullptr;
|
||||
servers[i].pDevice = nullptr;
|
||||
servers[i].pRemoteCharacteristic = nullptr;
|
||||
servers[i].connected = false;
|
||||
servers[i].doConnect = false;
|
||||
servers[i].name = "";
|
||||
}
|
||||
|
||||
// Initialize BLE
|
||||
BLEDevice::init("ESP32_MultiClient");
|
||||
|
||||
// Set up BLE scanner
|
||||
BLEScan *pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
|
||||
Serial.println("Scanning for BLE servers...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Process any pending connections
|
||||
for (int i = 0; i < MAX_SERVERS; i++) {
|
||||
if (servers[i].doConnect) {
|
||||
if (connectToServer(i)) {
|
||||
Serial.println("Connection successful");
|
||||
} else {
|
||||
Serial.println("Connection failed");
|
||||
// Clear this slot so we can try another server
|
||||
delete servers[i].pDevice;
|
||||
servers[i].pDevice = nullptr;
|
||||
}
|
||||
servers[i].doConnect = false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we're connected to servers, send data to each one
|
||||
if (connectedServers > 0) {
|
||||
for (int i = 0; i < MAX_SERVERS; i++) {
|
||||
if (servers[i].connected && servers[i].pRemoteCharacteristic != nullptr) {
|
||||
// Create a unique message for each server
|
||||
String newValue = servers[i].name + " | Time: " + String(millis() / 1000);
|
||||
|
||||
Serial.print("Sending to ");
|
||||
Serial.print(servers[i].name);
|
||||
Serial.print(": ");
|
||||
Serial.println(newValue);
|
||||
|
||||
// Write the value to the characteristic
|
||||
servers[i].pRemoteCharacteristic->writeValue(newValue.c_str(), newValue.length());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Serial.println("No servers connected");
|
||||
}
|
||||
|
||||
// Resume scanning if we have room for more connections
|
||||
if (doScan && connectedServers < MAX_SERVERS) {
|
||||
Serial.println("Resuming scan for more servers...");
|
||||
BLEDevice::getScan()->start(5, false);
|
||||
doScan = false;
|
||||
delay(5000); // Wait for scan to complete
|
||||
}
|
||||
|
||||
delay(2000); // Delay between loop iterations
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
Secure client with static passkey and IRK retrieval
|
||||
|
||||
This example demonstrates how to create a secure BLE client that connects to
|
||||
a secure BLE server using a static passkey without prompting the user.
|
||||
The client will automatically use the same passkey (123456) as the server.
|
||||
|
||||
After successful bonding, the example demonstrates how to retrieve the
|
||||
server's Identity Resolving Key (IRK) in multiple formats:
|
||||
- Comma-separated hex format: 0x1A,0x1B,0x1C,...
|
||||
- Base64 encoded (for Home Assistant Private BLE Device service)
|
||||
- Reverse hex order (for Home Assistant ESPresense)
|
||||
|
||||
This client is designed to work with the Server_secure_static_passkey example.
|
||||
|
||||
Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE.
|
||||
Bluedroid initiates security on-connect, while NimBLE initiates security on-demand.
|
||||
This means that in NimBLE you can read the insecure characteristic without entering
|
||||
the passkey. This is not possible in Bluedroid.
|
||||
|
||||
IMPORTANT:
|
||||
- MITM (Man-In-The-Middle protection) must be enabled for password prompts to work.
|
||||
- Bonding must be enabled to store and retrieve the IRK.
|
||||
- The server must distribute its Identity Key during pairing.
|
||||
|
||||
Based on examples from Neil Kolban and h2zero.
|
||||
Created by lucasssvaz.
|
||||
*/
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLESecurity.h"
|
||||
#include "nvs_flash.h"
|
||||
|
||||
// The remote service we wish to connect to.
|
||||
static BLEUUID serviceUUID("4fafc201-1fb5-459e-8fcc-c5c9c331914b");
|
||||
// The characteristics of the remote service we are interested in.
|
||||
static BLEUUID insecureCharUUID("beb5483e-36e1-4688-b7f5-ea07361b26a8");
|
||||
static BLEUUID secureCharUUID("ff1d2614-e2d6-4c87-9154-6625d39ca7f8");
|
||||
|
||||
// This must match the server's passkey
|
||||
#define CLIENT_PIN 123456
|
||||
|
||||
static boolean doConnect = false;
|
||||
static boolean connected = false;
|
||||
static boolean doScan = false;
|
||||
static BLEClient *pClient = nullptr;
|
||||
static BLERemoteCharacteristic *pRemoteInsecureCharacteristic;
|
||||
static BLERemoteCharacteristic *pRemoteSecureCharacteristic;
|
||||
static BLEAdvertisedDevice *myDevice;
|
||||
|
||||
// Print an IRK buffer as hex with leading zeros and ':' separator
|
||||
static void printIrkBinary(uint8_t *irk) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (irk[i] < 0x10) {
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(irk[i], HEX);
|
||||
if (i < 15) {
|
||||
Serial.print(":");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_peer_irk(BLEAddress peerAddr) {
|
||||
Serial.println("\n=== Retrieving peer IRK (Server) ===\n");
|
||||
|
||||
uint8_t irk[16];
|
||||
|
||||
// Get IRK in binary format
|
||||
if (BLEDevice::getPeerIRK(peerAddr, irk)) {
|
||||
Serial.println("Successfully retrieved peer IRK in binary format:");
|
||||
printIrkBinary(irk);
|
||||
Serial.println("\n");
|
||||
}
|
||||
|
||||
// Get IRK in different string formats
|
||||
String irkString = BLEDevice::getPeerIRKString(peerAddr);
|
||||
String irkBase64 = BLEDevice::getPeerIRKBase64(peerAddr);
|
||||
String irkReverse = BLEDevice::getPeerIRKReverse(peerAddr);
|
||||
|
||||
if (irkString.length() > 0) {
|
||||
Serial.println("Successfully retrieved peer IRK in multiple formats:\n");
|
||||
Serial.print("IRK (comma-separated hex): ");
|
||||
Serial.println(irkString);
|
||||
Serial.print("IRK (Base64 for Home Assistant Private BLE Device): ");
|
||||
Serial.println(irkBase64);
|
||||
Serial.print("IRK (reverse hex for Home Assistant ESPresense): ");
|
||||
Serial.println(irkReverse);
|
||||
Serial.println();
|
||||
} else {
|
||||
Serial.println("!!! Failed to retrieve peer IRK !!!");
|
||||
Serial.println("This is expected if bonding is disabled or the peer doesn't distribute its Identity Key.");
|
||||
Serial.println("To enable bonding, change setAuthenticationMode to: pSecurity->setAuthenticationMode(true, true, true);\n");
|
||||
}
|
||||
|
||||
Serial.println("=======================================\n");
|
||||
}
|
||||
|
||||
// Callback function to handle notifications
|
||||
static void notifyCallback(BLERemoteCharacteristic *pBLERemoteCharacteristic, uint8_t *pData, size_t length, bool isNotify) {
|
||||
Serial.print("Notify callback for characteristic ");
|
||||
Serial.print(pBLERemoteCharacteristic->getUUID().toString().c_str());
|
||||
Serial.print(" of data length ");
|
||||
Serial.println(length);
|
||||
Serial.print("data: ");
|
||||
Serial.write(pData, length);
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
class MyClientCallback : public BLEClientCallbacks {
|
||||
void onConnect(BLEClient *pclient) {
|
||||
Serial.println("Connected to secure server");
|
||||
}
|
||||
|
||||
void onDisconnect(BLEClient *pclient) {
|
||||
connected = false;
|
||||
Serial.println("Disconnected from server");
|
||||
}
|
||||
};
|
||||
|
||||
// Security callbacks to print IRKs once authentication completes
|
||||
class MySecurityCallbacks : public BLESecurityCallbacks {
|
||||
#if defined(CONFIG_BLUEDROID_ENABLED)
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t desc) override {
|
||||
// Print the IRK received by the peer
|
||||
BLEAddress peerAddr(desc.bd_addr);
|
||||
get_peer_irk(peerAddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NIMBLE_ENABLED)
|
||||
void onAuthenticationComplete(ble_gap_conn_desc *desc) override {
|
||||
// Print the IRK received by the peer
|
||||
BLEAddress peerAddr(desc->peer_id_addr.val, desc->peer_id_addr.type);
|
||||
get_peer_irk(peerAddr);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
bool connectToServer() {
|
||||
Serial.print("Forming a secure connection to ");
|
||||
Serial.println(myDevice->getAddress().toString().c_str());
|
||||
|
||||
pClient = BLEDevice::createClient();
|
||||
Serial.println(" - Created client");
|
||||
|
||||
pClient->setClientCallbacks(new MyClientCallback());
|
||||
|
||||
// Connect to the remote BLE Server.
|
||||
pClient->connect(myDevice);
|
||||
Serial.println(" - Connected to server");
|
||||
|
||||
// Set MTU to maximum for better performance
|
||||
pClient->setMTU(517);
|
||||
|
||||
// Obtain a reference to the service we are after in the remote BLE server.
|
||||
BLERemoteService *pRemoteService = pClient->getService(serviceUUID);
|
||||
if (pRemoteService == nullptr) {
|
||||
Serial.print("Failed to find our service UUID: ");
|
||||
Serial.println(serviceUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found our service");
|
||||
|
||||
// Obtain a reference to the insecure characteristic
|
||||
pRemoteInsecureCharacteristic = pRemoteService->getCharacteristic(insecureCharUUID);
|
||||
if (pRemoteInsecureCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find insecure characteristic UUID: ");
|
||||
Serial.println(insecureCharUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found insecure characteristic");
|
||||
|
||||
// Obtain a reference to the secure characteristic
|
||||
pRemoteSecureCharacteristic = pRemoteService->getCharacteristic(secureCharUUID);
|
||||
if (pRemoteSecureCharacteristic == nullptr) {
|
||||
Serial.print("Failed to find secure characteristic UUID: ");
|
||||
Serial.println(secureCharUUID.toString().c_str());
|
||||
pClient->disconnect();
|
||||
return false;
|
||||
}
|
||||
Serial.println(" - Found secure characteristic");
|
||||
|
||||
// Read the value of the insecure characteristic (should work without authentication)
|
||||
if (pRemoteInsecureCharacteristic->canRead()) {
|
||||
String value = pRemoteInsecureCharacteristic->readValue();
|
||||
Serial.print("Insecure characteristic value: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
// For Bluedroid, we need to set the authentication request type for the secure characteristic
|
||||
// This is not needed for NimBLE and will be ignored.
|
||||
pRemoteSecureCharacteristic->setAuth(ESP_GATT_AUTH_REQ_MITM);
|
||||
|
||||
// Try to read the secure characteristic (this will trigger security negotiation in NimBLE)
|
||||
if (pRemoteSecureCharacteristic->canRead()) {
|
||||
Serial.println("Attempting to read secure characteristic...");
|
||||
String value = pRemoteSecureCharacteristic->readValue();
|
||||
Serial.print("Secure characteristic value: ");
|
||||
Serial.println(value.c_str());
|
||||
}
|
||||
|
||||
// Register for notifications on both characteristics if they support it
|
||||
if (pRemoteInsecureCharacteristic->canNotify()) {
|
||||
pRemoteInsecureCharacteristic->registerForNotify(notifyCallback);
|
||||
Serial.println(" - Registered for insecure characteristic notifications");
|
||||
}
|
||||
|
||||
if (pRemoteSecureCharacteristic->canNotify()) {
|
||||
pRemoteSecureCharacteristic->registerForNotify(notifyCallback);
|
||||
Serial.println(" - Registered for secure characteristic notifications");
|
||||
}
|
||||
|
||||
connected = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan for BLE servers and find the first one that advertises the service we are looking for.
|
||||
*/
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
/**
|
||||
* Called for each advertising BLE server.
|
||||
*/
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.print("BLE Advertised Device found: ");
|
||||
Serial.println(advertisedDevice.toString().c_str());
|
||||
|
||||
// We have found a device, let us now see if it contains the service we are looking for.
|
||||
if (advertisedDevice.haveServiceUUID() && advertisedDevice.isAdvertisingService(serviceUUID)) {
|
||||
Serial.println("Found our secure server!");
|
||||
BLEDevice::getScan()->stop();
|
||||
myDevice = new BLEAdvertisedDevice(advertisedDevice);
|
||||
doConnect = true;
|
||||
doScan = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting Secure BLE Client application...");
|
||||
|
||||
// Clear NVS to remove any cached pairing information
|
||||
// This ensures fresh authentication for testing
|
||||
Serial.println("Clearing NVS pairing data...");
|
||||
nvs_flash_erase();
|
||||
nvs_flash_init();
|
||||
|
||||
BLEDevice::init("Secure BLE Client");
|
||||
|
||||
// Set up security with the same passkey as the server
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
|
||||
// Set security parameters
|
||||
// Default parameters:
|
||||
// - IO capability is set to NONE
|
||||
// - Initiator and responder key distribution flags are set to both encryption and identity keys.
|
||||
// - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it.
|
||||
// - Key size is set to 16 bytes
|
||||
|
||||
// Set the same static passkey as the server
|
||||
// The first argument defines if the passkey is static or random.
|
||||
// The second argument is the passkey (ignored when using a random passkey).
|
||||
pSecurity->setPassKey(true, CLIENT_PIN);
|
||||
|
||||
// Set authentication mode to match server requirements
|
||||
// Enable bonding, MITM (for password prompts), and secure connection for this example
|
||||
// Bonding is required to store and retrieve the IRK
|
||||
pSecurity->setAuthenticationMode(true, true, true);
|
||||
|
||||
// Set IO capability to KeyboardOnly
|
||||
// We need the proper IO capability for MITM authentication even
|
||||
// if the passkey is static and won't be entered by the user
|
||||
// See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/
|
||||
pSecurity->setCapability(ESP_IO_CAP_IN);
|
||||
|
||||
// Set callbacks to handle authentication completion and print IRKs
|
||||
BLEDevice::setSecurityCallbacks(new MySecurityCallbacks());
|
||||
|
||||
// Retrieve a Scanner and set the callback we want to use to be informed when we
|
||||
// have detected a new device. Specify that we want active scanning and start the
|
||||
// scan to run for 5 seconds.
|
||||
BLEScan *pBLEScan = BLEDevice::getScan();
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setInterval(1349);
|
||||
pBLEScan->setWindow(449);
|
||||
pBLEScan->setActiveScan(true);
|
||||
pBLEScan->start(5, false);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// If the flag "doConnect" is true then we have scanned for and found the desired
|
||||
// BLE Server with which we wish to connect. Now we connect to it.
|
||||
if (doConnect == true) {
|
||||
if (connectToServer()) {
|
||||
Serial.println("We are now connected to the secure BLE Server.");
|
||||
} else {
|
||||
Serial.println("We have failed to connect to the server; there is nothing more we will do.");
|
||||
}
|
||||
doConnect = false;
|
||||
}
|
||||
|
||||
// If we are connected to a peer BLE Server, demonstrate secure communication
|
||||
if (connected) {
|
||||
// Write to the insecure characteristic
|
||||
String insecureValue = "Client time: " + String(millis() / 1000);
|
||||
if (pRemoteInsecureCharacteristic->canWrite()) {
|
||||
pRemoteInsecureCharacteristic->writeValue(insecureValue.c_str(), insecureValue.length());
|
||||
Serial.println("Wrote to insecure characteristic: " + insecureValue);
|
||||
}
|
||||
|
||||
// Write to the secure characteristic
|
||||
String secureValue = "Secure client time: " + String(millis() / 1000);
|
||||
if (pRemoteSecureCharacteristic->canWrite()) {
|
||||
pRemoteSecureCharacteristic->writeValue(secureValue.c_str(), secureValue.length());
|
||||
Serial.println("Wrote to secure characteristic: " + secureValue);
|
||||
}
|
||||
} else if (doScan) {
|
||||
// Restart scanning if we're disconnected
|
||||
BLEDevice::getScan()->start(0);
|
||||
}
|
||||
|
||||
delay(2000); // Delay 2 seconds between loops
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
EddystoneTLM beacon by BeeGee based on https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino
|
||||
EddystoneTLM frame specification https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
|
||||
*/
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic Eddystone URL frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
To read data advertised by this beacon use second ESP with example sketch BLE_Beacon_Scanner
|
||||
*/
|
||||
#include "sys/time.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEBeacon.h"
|
||||
#include "BLEAdvertising.h"
|
||||
#include "BLEEddystoneTLM.h"
|
||||
|
||||
#include "esp_sleep.h"
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
#define BEACON_POWER ESP_PWR_LVL_N12
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval nowTimeStruct;
|
||||
|
||||
time_t lastTenth;
|
||||
|
||||
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
|
||||
|
||||
// Check
|
||||
// https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md
|
||||
// and http://www.hugi.scene.org/online/coding/hugi%2015%20-%20cmtadfix.htm
|
||||
// for the temperature value. It is a 8.8 fixed-point notation
|
||||
void setBeacon() {
|
||||
BLEEddystoneTLM EddystoneTLM;
|
||||
EddystoneTLM.setVolt((uint16_t)random(2800, 3700)); // 3300mV = 3.3V
|
||||
EddystoneTLM.setTemp(random(-3000, 3000) / 100.0f); // 3000 = 30.00 ˚C
|
||||
Serial.printf("Random Battery voltage is %d mV = 0x%04X\n", EddystoneTLM.getVolt(), EddystoneTLM.getVolt());
|
||||
Serial.printf("Random temperature is %.2f°C\n", EddystoneTLM.getTemp());
|
||||
Serial.printf("Converted to 8.8 format: 0x%04X\n", EddystoneTLM.getRawTemp());
|
||||
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
oScanResponseData.setServiceData(BLEUUID((uint16_t)0xFEAA), String(EddystoneTLM.getData().c_str(), EddystoneTLM.getData().length()));
|
||||
|
||||
oAdvertisementData.setName("ESP32 TLM Beacon");
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&nowTimeStruct, NULL);
|
||||
|
||||
Serial.printf("Starting ESP32. Bootcount = %lu\n", bootcount++);
|
||||
Serial.printf("Deep sleep (%llds since last reset, %llds since last boot)\n", nowTimeStruct.tv_sec, nowTimeStruct.tv_sec - last);
|
||||
|
||||
last = nowTimeStruct.tv_sec;
|
||||
lastTenth = nowTimeStruct.tv_sec * 10; // Time since last reset as 0.1 second resolution counter
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("TLMBeacon");
|
||||
|
||||
BLEDevice::setPower(BEACON_POWER);
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
setBeacon();
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertising started for 10s ...");
|
||||
delay(10000);
|
||||
pAdvertising->stop();
|
||||
Serial.printf("Enter deep sleep for 10s\n");
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
@@ -0,0 +1,14 @@
|
||||
## Eddystone TLM beacon
|
||||
EddystoneTLM beacon by BeeGee based on
|
||||
[pcbreflux ESP32 Eddystone TLM deepsleep](https://github.com/pcbreflux/espressif/blob/master/esp32/arduino/sketchbook/ESP32_Eddystone_TLM_deepsleep/ESP32_Eddystone_TLM_deepsleep.ino)
|
||||
|
||||
[EddystoneTLM frame specification](https://github.com/google/eddystone/blob/master/eddystone-tlm/tlm-plain.md)
|
||||
|
||||
Create a BLE server that will send periodic Eddystone TLM frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
EddystoneURL beacon by BeeGee
|
||||
EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md
|
||||
|
||||
Upgraded on: Feb 20, 2023
|
||||
By: Tomas Pilny
|
||||
*/
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic Eddystone URL frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
|
||||
*/
|
||||
#include "sys/time.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include "BLEDevice.h"
|
||||
#include "BLEUtils.h"
|
||||
#include "BLEBeacon.h"
|
||||
#include "BLEAdvertising.h"
|
||||
#include "BLEEddystoneURL.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
char unprintable[] = {0x01, 0xFF, 0xDE, 0xAD};
|
||||
String URL[] = {
|
||||
"http://www.espressif.com/", // prefix 0x00, suffix 0x00
|
||||
"https://www.texas.gov", // prefix 0x01, suffix 0x0D
|
||||
"http://en.mapy.cz", // prefix 0x02, no valid suffix
|
||||
"https://arduino.cc", // prefix 0x03, no valid suffix
|
||||
"google.com", // URL without specified prefix - the function will assume default prefix "http://www." = 0x00
|
||||
"diginfo.tv", // URL without specified prefix - the function will assume default prefix "http://www." = 0x00
|
||||
// "http://www.URLsAbove17BytesAreNotAllowed.com", // Too long URL - setSmartURL() will return 0 = ERR
|
||||
// "", // Empty string - setSmartURL() will return 0 = ERR
|
||||
// String(unprintable), // Unprintable characters / corrupted String - setSmartURL() will return 0 = ERR
|
||||
};
|
||||
|
||||
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
||||
#define BEACON_POWER ESP_PWR_LVL_N12
|
||||
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
||||
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
BLEAdvertising *pAdvertising;
|
||||
struct timeval now;
|
||||
|
||||
int setBeacon() {
|
||||
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
||||
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
||||
|
||||
BLEEddystoneURL EddystoneURL;
|
||||
EddystoneURL.setPower(BEACON_POWER); // This is only information about the power. The actual power is set by `BLEDevice::setPower(BEACON_POWER)`
|
||||
if (EddystoneURL.setSmartURL(URL[bootcount % (sizeof(URL) / sizeof(URL[0]))])) {
|
||||
String frame = EddystoneURL.getFrame();
|
||||
String data(EddystoneURL.getFrame().c_str(), frame.length());
|
||||
oAdvertisementData.addData(data);
|
||||
oScanResponseData.setName("ESP32 URLBeacon");
|
||||
pAdvertising->setAdvertisementData(oAdvertisementData);
|
||||
pAdvertising->setScanResponseData(oScanResponseData);
|
||||
Serial.printf("Advertise URL \"%s\"\n", URL[bootcount % (sizeof(URL) / sizeof(URL[0]))].c_str());
|
||||
return 1; // OK
|
||||
} else {
|
||||
Serial.println("Smart URL set ERR");
|
||||
return 0; // ERR
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
Serial.printf("Start ESP32 %lu\n", bootcount++);
|
||||
Serial.printf("Deep sleep (%llds since last reset, %llds since last boot)\n", now.tv_sec, now.tv_sec - last);
|
||||
|
||||
last = now.tv_sec;
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("URLBeacon");
|
||||
BLEDevice::setPower(BEACON_POWER);
|
||||
|
||||
// Create the BLE Server
|
||||
// BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
|
||||
|
||||
pAdvertising = BLEDevice::getAdvertising();
|
||||
|
||||
if (setBeacon()) {
|
||||
// Start advertising
|
||||
pAdvertising->start();
|
||||
Serial.println("Advertising started...");
|
||||
delay(10000);
|
||||
pAdvertising->stop();
|
||||
}
|
||||
Serial.println("Enter deep sleep");
|
||||
bootcount++;
|
||||
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
||||
}
|
||||
|
||||
void loop() {}
|
||||
@@ -0,0 +1,14 @@
|
||||
## Eddystone URL beacon
|
||||
EddystoneURL beacon by BeeGee based on
|
||||
[pcbreflux ESP32 Eddystone URL deepsleep](https://github.com/pcbreflux/espressif/tree/master/esp32/arduino/sketchbook/ESP32_Eddystone_URL_deepsleep)
|
||||
|
||||
[EddystoneURL frame specification](https://github.com/google/eddystone/blob/master/eddystone-url/README.md)
|
||||
|
||||
Create a BLE server that will send periodic Eddystone URL frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
6. deep sleep
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updated by chegewara
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
A connect handler associated with the server starts a background task that performs notification
|
||||
every couple of seconds.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
#include <BLE2901.h>
|
||||
|
||||
BLEServer *pServer = NULL;
|
||||
BLECharacteristic *pCharacteristic = NULL;
|
||||
BLE2901 *descriptor_2901 = NULL;
|
||||
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint32_t value = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *pServer) {
|
||||
deviceConnected = true;
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer *pServer) {
|
||||
deviceConnected = false;
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE
|
||||
);
|
||||
|
||||
// Creates BLE Descriptor 0x2902: Client Characteristic Configuration Descriptor (CCCD)
|
||||
// Descriptor 2902 is not required when using NimBLE as it is automatically added based on the characteristic properties
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
// Adds also the Characteristic User Description - 0x2901 descriptor
|
||||
descriptor_2901 = new BLE2901();
|
||||
descriptor_2901->setDescription("My own description for this characteristic.");
|
||||
descriptor_2901->setAccessPermissions(ESP_GATT_PERM_READ); // enforce read only - default is Read|Write
|
||||
pCharacteristic->addDescriptor(descriptor_2901);
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// notify changed value
|
||||
if (deviceConnected) {
|
||||
pCharacteristic->setValue((uint8_t *)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
delay(500);
|
||||
}
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("start advertising");
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = deviceConnected;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
|
||||
int scanTime = 5; //In seconds
|
||||
BLEScan *pBLEScan;
|
||||
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) {
|
||||
Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Scanning...");
|
||||
|
||||
BLEDevice::init("");
|
||||
pBLEScan = BLEDevice::getScan(); //create new scan
|
||||
pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster
|
||||
pBLEScan->setInterval(100);
|
||||
pBLEScan->setWindow(99); // less or equal setInterval value
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
BLEScanResults *foundDevices = pBLEScan->start(scanTime, false);
|
||||
Serial.print("Devices found: ");
|
||||
Serial.println(foundDevices->getCount());
|
||||
Serial.println("Scan done!");
|
||||
pBLEScan->clearResults(); // delete results fromBLEScan buffer to release memory
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updates by chegewara
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE work!");
|
||||
|
||||
if (!BLEDevice::init("BLE Server Example")) {
|
||||
Serial.println("BLE initialization failed!");
|
||||
return;
|
||||
}
|
||||
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
BLECharacteristic *pCharacteristic =
|
||||
pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
|
||||
|
||||
pCharacteristic->setValue("Hello World says Neil");
|
||||
pService->start();
|
||||
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Characteristic defined! Now you can read it in your phone!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* BLE HID Gamepad Example
|
||||
*
|
||||
* This example demonstrates how to create a BLE HID Gamepad device using ESP32.
|
||||
* The gamepad will appear as a standard HID game controller on Windows, macOS, Linux, Android, and iOS.
|
||||
*
|
||||
* Features:
|
||||
* - 8 buttons (mapped to buttons 1-8)
|
||||
* - 2 axes (X, Y for joystick movement)
|
||||
* - Secure pairing with bonding
|
||||
* - Battery level reporting
|
||||
* - Automatic reconnection after power cycle
|
||||
*
|
||||
* Usage:
|
||||
* 1. Upload this sketch to your ESP32
|
||||
* 2. Pair with your device (Windows: Settings > Bluetooth & devices)
|
||||
* 3. The gamepad will send test input (circular motion and button toggle)
|
||||
* 4. Test in any game or with gamepad testing tools like https://hardwaretester.com/gamepad
|
||||
*
|
||||
* Note: This example uses "Just Works" pairing for automatic connection without
|
||||
* PIN entry or confirmation, just like real gamepads. The bond is saved for future connections.
|
||||
*
|
||||
* Created by lucasssvaz
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEHIDDevice.h>
|
||||
#include <BLESecurity.h>
|
||||
|
||||
// HID Report Descriptor for a gamepad with 8 buttons and 2 axes (X, Y)
|
||||
// This descriptor defines the gamepad as having:
|
||||
// - 8 buttons (usage buttons 1-8)
|
||||
// - 2 8-bit axes (X, Y) with range -127 to 127
|
||||
const uint8_t hidReportDescriptor[] = {
|
||||
0x05, 0x01, // Usage Page (Generic Desktop)
|
||||
0x09, 0x05, // Usage (Gamepad)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x09, 0x01, // Usage (Pointer)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x15, 0x81, // Logical Minimum (-127)
|
||||
0x25, 0x7F, // Logical Maximum (127)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x19, 0x01, // Usage Minimum (Button 1)
|
||||
0x29, 0x08, // Usage Maximum (Button 8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x08, // Report Count (8)
|
||||
0x81, 0x02, // Input (Data, Variable, Absolute)
|
||||
0xC0 // End Collection
|
||||
};
|
||||
|
||||
// Gamepad report structure (matches the HID descriptor)
|
||||
struct GamepadReport {
|
||||
uint8_t reportId; // Report ID (must be 1)
|
||||
int8_t x; // X axis (-127 to 127)
|
||||
int8_t y; // Y axis (-127 to 127)
|
||||
uint8_t buttons; // 8 buttons (bit 0-7)
|
||||
} __attribute__((packed));
|
||||
|
||||
BLEHIDDevice *hid;
|
||||
BLECharacteristic *inputGamepad;
|
||||
BLEServer *server;
|
||||
bool deviceConnected = false;
|
||||
|
||||
// Server callbacks to track connection status
|
||||
class ServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *pServer) {
|
||||
deviceConnected = true;
|
||||
Serial.println("Client connected");
|
||||
}
|
||||
|
||||
void onDisconnect(BLEServer *pServer) {
|
||||
deviceConnected = false;
|
||||
Serial.println("Client disconnected");
|
||||
// Restart advertising so we can reconnect
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Advertising restarted");
|
||||
}
|
||||
};
|
||||
|
||||
// Security callbacks for "Just Works" pairing
|
||||
class SecurityCallbacks : public BLESecurityCallbacks {
|
||||
bool onSecurityRequest() {
|
||||
return true; // Accept all pairing requests
|
||||
}
|
||||
|
||||
#if defined(CONFIG_BLUEDROID_ENABLED)
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t auth_cmpl) {
|
||||
if (auth_cmpl.success) {
|
||||
Serial.println("Pairing successful!");
|
||||
} else {
|
||||
Serial.printf("Pairing failed, status: %d\n", auth_cmpl.fail_reason);
|
||||
}
|
||||
}
|
||||
#elif defined(CONFIG_NIMBLE_ENABLED)
|
||||
void onAuthenticationComplete(ble_gap_conn_desc *desc) {
|
||||
if (desc->sec_state.encrypted) {
|
||||
Serial.println("Pairing successful!");
|
||||
} else {
|
||||
Serial.println("Pairing failed");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// These are not used with "Just Works" pairing (ESP_IO_CAP_NONE)
|
||||
uint32_t onPassKeyRequest() {
|
||||
return 0;
|
||||
}
|
||||
void onPassKeyNotify(uint32_t pass_key) {}
|
||||
bool onConfirmPIN(uint32_t pass_key) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE HID Gamepad");
|
||||
|
||||
// Initialize BLE
|
||||
BLEDevice::init("ESP32-Gamepad");
|
||||
|
||||
// Configure BLE Security for pairing and bonding
|
||||
// Use "Just Works" pairing - no user interaction required (like a real gamepad)
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
|
||||
// Set IO capability to NONE (no display, no keyboard - like a real gamepad)
|
||||
pSecurity->setCapability(ESP_IO_CAP_NONE);
|
||||
|
||||
// Set authentication mode: bonding=true, MITM=false, secure connection=true
|
||||
// This enables "Just Works" pairing with bonding
|
||||
pSecurity->setAuthenticationMode(true, false, true);
|
||||
|
||||
// Set security callbacks
|
||||
BLEDevice::setSecurityCallbacks(new SecurityCallbacks());
|
||||
|
||||
// Create BLE Server
|
||||
server = BLEDevice::createServer();
|
||||
server->setCallbacks(new ServerCallbacks());
|
||||
|
||||
// Create HID Device
|
||||
hid = new BLEHIDDevice(server);
|
||||
|
||||
// Set HID device information
|
||||
hid->manufacturer()->setValue("Espressif");
|
||||
hid->pnp(0x02, 0x05ac, 0x820a, 0x0110); // Vendor ID, Product ID, Product Version
|
||||
hid->hidInfo(0x00, 0x01); // HID version, country code
|
||||
|
||||
// Set Report Map (HID descriptor)
|
||||
hid->reportMap((uint8_t *)hidReportDescriptor, sizeof(hidReportDescriptor));
|
||||
|
||||
// Create input report characteristic for gamepad
|
||||
inputGamepad = hid->inputReport(1); // Report ID 1
|
||||
|
||||
// Set battery level to 100%
|
||||
hid->setBatteryLevel(100);
|
||||
|
||||
// Start HID services
|
||||
hid->startServices();
|
||||
|
||||
// Setup advertising
|
||||
BLEAdvertising *advertising = BLEDevice::getAdvertising();
|
||||
advertising->setAppearance(0x03C4); // HID Gamepad appearance
|
||||
advertising->addServiceUUID(hid->hidService()->getUUID());
|
||||
advertising->setScanResponse(true);
|
||||
advertising->setMinPreferred(0x06); // Help with connection issues
|
||||
advertising->setMaxPreferred(0x12);
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
|
||||
Serial.println("BLE HID Gamepad ready!");
|
||||
Serial.println("Waiting for connection...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (deviceConnected) {
|
||||
static uint32_t lastReportTime = 0;
|
||||
static uint32_t counter = 0;
|
||||
|
||||
// Send a report every 50ms (20Hz)
|
||||
if (millis() - lastReportTime >= 50) {
|
||||
lastReportTime = millis();
|
||||
counter++;
|
||||
|
||||
GamepadReport report;
|
||||
report.reportId = 1;
|
||||
|
||||
// Simulate some movement (sine wave pattern)
|
||||
report.x = (int8_t)(127 * sin(counter * 0.1)); // X axis: -127 to 127
|
||||
report.y = (int8_t)(127 * cos(counter * 0.1)); // Y axis: -127 to 127
|
||||
report.buttons = (counter % 40 < 20) ? 0x01 : 0x00; // Toggle first button every second
|
||||
|
||||
// Send the report
|
||||
inputGamepad->setValue((uint8_t *)&report, sizeof(report));
|
||||
inputGamepad->notify();
|
||||
|
||||
// Print status every 2 seconds
|
||||
if (counter % 40 == 0) {
|
||||
Serial.printf("Report #%lu: X=%d, Y=%d, Buttons=0x%02X\n", counter, report.x, report.y, report.buttons);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delay(10);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
updated by chegewara
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The server will continue advertising for more connections after the first one and will notify
|
||||
the value of a counter to all connected clients.
|
||||
|
||||
The service advertises itself as: 4fafc201-1fb5-459e-8fcc-c5c9c331914b
|
||||
And has a characteristic of: beb5483e-36e1-4688-b7f5-ea07361b26a8
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
A connect handler associated with the server starts a background task that performs notification
|
||||
every couple of seconds.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer *pServer = NULL;
|
||||
BLECharacteristic *pCharacteristic = NULL;
|
||||
int connectedClients = 0;
|
||||
bool deviceConnected = false;
|
||||
uint32_t value = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *pServer) {
|
||||
connectedClients++;
|
||||
Serial.print("Client connected. Total clients: ");
|
||||
Serial.println(connectedClients);
|
||||
// Continue advertising for more connections
|
||||
BLEDevice::startAdvertising();
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer *pServer) {
|
||||
connectedClients--;
|
||||
Serial.print("Client disconnected. Total clients: ");
|
||||
Serial.println(connectedClients);
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("ESP32");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID,
|
||||
BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY | BLECharacteristic::PROPERTY_INDICATE
|
||||
);
|
||||
|
||||
// Descriptor 2902 is not required when using NimBLE as it is automatically added based on the characteristic properties
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(false);
|
||||
pAdvertising->setMinPreferred(0x0); // set value to 0x00 to not advertise this parameter
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Waiting for client connections to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Notify changed value to all connected clients
|
||||
if (connectedClients > 0) {
|
||||
Serial.print("Notifying value: ");
|
||||
Serial.print(value);
|
||||
Serial.print(" to ");
|
||||
Serial.print(connectedClients);
|
||||
Serial.println(" client(s)");
|
||||
pCharacteristic->setValue((uint8_t *)&value, 4);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
// Bluetooth stack will go into congestion, if too many packets are sent.
|
||||
// In 6 hours of testing, I was able to go as low as 3ms.
|
||||
// When using core debug level "debug" or "verbose", the delay can be increased in
|
||||
// order to reduce the number of debug messages in the serial monitor.
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// Disconnecting - restart advertising when no clients are connected
|
||||
if (connectedClients == 0 && deviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("No clients connected, restarting advertising");
|
||||
deviceConnected = false;
|
||||
}
|
||||
|
||||
// Connecting - update state when first client connects
|
||||
if (connectedClients > 0 && !deviceConnected) {
|
||||
// do stuff here on first connecting
|
||||
deviceConnected = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
Simple BLE Server Authorization Example
|
||||
|
||||
This example demonstrates how to create a BLE server with authorization
|
||||
requirements. It shows the essential setup for:
|
||||
- Authorization with static passkey
|
||||
- Secure connection
|
||||
- MITM (Man-In-The-Middle) protection
|
||||
|
||||
The server creates a single characteristic that requires authorization
|
||||
to access. Clients must provide the correct passkey (123456) to read
|
||||
or write to the characteristic.
|
||||
|
||||
Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE.
|
||||
Bluedroid initiates security on-connect, while NimBLE initiates security on-demand.
|
||||
|
||||
Due to a bug in ESP-IDF's Bluedroid, this example will currently not work with ESP32.
|
||||
|
||||
IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts
|
||||
to work. Without MITM, the BLE stack assumes no user interaction is needed and will use
|
||||
"Just Works" pairing method (with encryption if secure connection is enabled).
|
||||
|
||||
Created by lucasssvaz.
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLESecurity.h>
|
||||
#include <nvs_flash.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
// Example passkey - change this for production use
|
||||
#define AUTH_PASSKEY 123456
|
||||
|
||||
static int s_readCount = 0;
|
||||
static BLECharacteristic *s_pCharacteristic;
|
||||
|
||||
class MySecurityCallbacks : public BLESecurityCallbacks {
|
||||
bool onAuthorizationRequest(uint16_t connHandle, uint16_t attrHandle, bool isRead) {
|
||||
Serial.println("Authorization request received");
|
||||
if (isRead) {
|
||||
s_readCount++;
|
||||
// Keep value length <= (MTU - 1) to avoid a follow-up read request
|
||||
uint16_t maxLen = BLEDevice::getServer()->getPeerMTU(connHandle) - 1;
|
||||
String msg = "Authorized #" + String(s_readCount);
|
||||
if (msg.length() > maxLen) {
|
||||
msg = msg.substring(0, maxLen);
|
||||
}
|
||||
s_pCharacteristic->setValue(msg);
|
||||
// Grant authorization to the first 3 reads
|
||||
if (s_readCount <= 3) {
|
||||
Serial.println("Authorization granted");
|
||||
return true;
|
||||
} else {
|
||||
Serial.println("Authorization denied, read count exceeded");
|
||||
Serial.println("Please reset the read counter to continue");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Fallback to deny
|
||||
Serial.println("Authorization denied");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE Authorization Example!");
|
||||
|
||||
// Initialize the BOOT pin for resetting the read count
|
||||
pinMode(BOOT_PIN, INPUT_PULLUP);
|
||||
|
||||
// Clear NVS to remove any cached pairing information
|
||||
// This ensures fresh authentication for testing
|
||||
Serial.println("Clearing NVS pairing data...");
|
||||
nvs_flash_erase();
|
||||
nvs_flash_init();
|
||||
|
||||
Serial.print("Using BLE stack: ");
|
||||
Serial.println(BLEDevice::getBLEStackString());
|
||||
|
||||
BLEDevice::init("BLE Auth Server");
|
||||
|
||||
// Set MTU to 517 to avoid a follow-up read request
|
||||
BLEDevice::setMTU(517);
|
||||
|
||||
// Configure BLE Security
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
|
||||
// Set static passkey for authentication
|
||||
pSecurity->setPassKey(true, AUTH_PASSKEY);
|
||||
|
||||
// Set IO capability to DisplayOnly for MITM authentication
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
|
||||
// Enable authorization requirements:
|
||||
// - bonding: true (for persistent storage of the keys)
|
||||
// - MITM: true (enables Man-In-The-Middle protection for password prompts)
|
||||
// - secure connection: true (enables secure connection for encryption)
|
||||
pSecurity->setAuthenticationMode(true, true, true);
|
||||
|
||||
// Set the security callbacks
|
||||
BLEDevice::setSecurityCallbacks(new MySecurityCallbacks());
|
||||
|
||||
// Create BLE Server
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
pServer->advertiseOnDisconnect(true);
|
||||
|
||||
// Create BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create characteristic with read and write properties
|
||||
uint32_t properties = BLECharacteristic::PROPERTY_READ;
|
||||
|
||||
// For NimBLE: Add authentication properties
|
||||
// These properties ensure the characteristic requires authorization
|
||||
// (ignored by Bluedroid but harmless)
|
||||
properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_READ_AUTHOR;
|
||||
|
||||
s_pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID, properties);
|
||||
|
||||
// For Bluedroid: Set access permissions that require encryption and MITM
|
||||
// This ensures authorization is required (ignored by NimBLE)
|
||||
s_pCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_READ_AUTHORIZATION);
|
||||
|
||||
// Set initial value
|
||||
s_pCharacteristic->setValue("Hello! You needed authorization to read this!");
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Configure and start advertising
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06); // helps with iPhone connections
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
|
||||
BLEDevice::startAdvertising();
|
||||
|
||||
Serial.println("BLE Server is running!");
|
||||
Serial.println("Authorization is required to access the characteristic.");
|
||||
Serial.printf("Use passkey: %d when prompted\n", AUTH_PASSKEY);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Reset the read count if the BOOT pin is pressed
|
||||
if (digitalRead(BOOT_PIN) == LOW) {
|
||||
s_readCount = 0;
|
||||
Serial.println("Read count reset");
|
||||
}
|
||||
|
||||
delay(100);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
targets:
|
||||
esp32: false
|
||||
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
Secure server with static passkey
|
||||
|
||||
This example demonstrates how to create a secure BLE server with no
|
||||
IO capability using a static passkey.
|
||||
The server will accept connections from devices that have the same passkey set.
|
||||
The example passkey is set to 123456.
|
||||
The server will create a service and a secure and an insecure characteristic
|
||||
to be used as example.
|
||||
|
||||
This server is designed to be used with the Client_secure_static_passkey example.
|
||||
|
||||
Note that ESP32 uses Bluedroid by default and the other SoCs use NimBLE.
|
||||
Bluedroid initiates security on-connect, while NimBLE initiates security on-demand.
|
||||
This means that in NimBLE you can read the insecure characteristic without entering
|
||||
the passkey. This is not possible in Bluedroid.
|
||||
|
||||
IMPORTANT: MITM (Man-In-The-Middle protection) must be enabled for password prompts
|
||||
to work. Without MITM, the BLE stack assumes no user interaction is needed and will use
|
||||
"Just Works" pairing method (with encryption if secure connection is enabled).
|
||||
|
||||
Based on examples from Neil Kolban and h2zero.
|
||||
Created by lucasssvaz.
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLESecurity.h>
|
||||
#include <nvs_flash.h>
|
||||
#include <string>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define INSECURE_CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
#define SECURE_CHARACTERISTIC_UUID "ff1d2614-e2d6-4c87-9154-6625d39ca7f8"
|
||||
|
||||
// This is an example passkey. You should use a different or random passkey.
|
||||
#define SERVER_PIN 123456
|
||||
|
||||
// Print an IRK buffer as hex with leading zeros and ':' separator
|
||||
static void printIrkBinary(uint8_t *irk) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (irk[i] < 0x10) {
|
||||
Serial.print("0");
|
||||
}
|
||||
Serial.print(irk[i], HEX);
|
||||
if (i < 15) {
|
||||
Serial.print(":");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_peer_irk(BLEAddress peerAddr) {
|
||||
Serial.println("\n=== Retrieving peer IRK (Client) ===\n");
|
||||
|
||||
uint8_t irk[16];
|
||||
|
||||
// Get IRK in binary format
|
||||
if (BLEDevice::getPeerIRK(peerAddr, irk)) {
|
||||
Serial.println("Successfully retrieved peer IRK in binary format:");
|
||||
printIrkBinary(irk);
|
||||
Serial.println("\n");
|
||||
}
|
||||
|
||||
// Get IRK in different string formats
|
||||
String irkString = BLEDevice::getPeerIRKString(peerAddr);
|
||||
String irkBase64 = BLEDevice::getPeerIRKBase64(peerAddr);
|
||||
String irkReverse = BLEDevice::getPeerIRKReverse(peerAddr);
|
||||
|
||||
if (irkString.length() > 0) {
|
||||
Serial.println("Successfully retrieved peer IRK in multiple formats:\n");
|
||||
Serial.print("IRK (comma-separated hex): ");
|
||||
Serial.println(irkString);
|
||||
Serial.print("IRK (Base64 for Home Assistant Private BLE Device): ");
|
||||
Serial.println(irkBase64);
|
||||
Serial.print("IRK (reverse hex for Home Assistant ESPresense): ");
|
||||
Serial.println(irkReverse);
|
||||
Serial.println();
|
||||
} else {
|
||||
Serial.println("!!! Failed to retrieve peer IRK !!!");
|
||||
Serial.println("This is expected if bonding is disabled or the peer doesn't distribute its Identity Key.");
|
||||
Serial.println("To enable bonding, change setAuthenticationMode to: pSecurity->setAuthenticationMode(true, true, true);\n");
|
||||
}
|
||||
|
||||
Serial.println("=======================================\n");
|
||||
}
|
||||
|
||||
// Security callbacks to print IRKs once authentication completes
|
||||
class MySecurityCallbacks : public BLESecurityCallbacks {
|
||||
#if defined(CONFIG_BLUEDROID_ENABLED)
|
||||
void onAuthenticationComplete(esp_ble_auth_cmpl_t desc) override {
|
||||
// Print the IRK received by the peer
|
||||
BLEAddress peerAddr(desc.bd_addr);
|
||||
get_peer_irk(peerAddr);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NIMBLE_ENABLED)
|
||||
void onAuthenticationComplete(ble_gap_conn_desc *desc) override {
|
||||
// Print the IRK received by the peer
|
||||
BLEAddress peerAddr(desc->peer_id_addr.val, desc->peer_id_addr.type);
|
||||
get_peer_irk(peerAddr);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Starting BLE work!");
|
||||
|
||||
// Clear NVS to remove any cached pairing information
|
||||
// This ensures fresh authentication for testing
|
||||
Serial.println("Clearing NVS pairing data...");
|
||||
nvs_flash_erase();
|
||||
nvs_flash_init();
|
||||
|
||||
Serial.print("Using BLE stack: ");
|
||||
Serial.println(BLEDevice::getBLEStackString());
|
||||
|
||||
BLEDevice::init("Secure BLE Server");
|
||||
|
||||
BLESecurity *pSecurity = new BLESecurity();
|
||||
|
||||
// Set security parameters
|
||||
// Default parameters:
|
||||
// - IO capability is set to NONE
|
||||
// - Initiator and responder key distribution flags are set to both encryption and identity keys.
|
||||
// - Passkey is set to BLE_SM_DEFAULT_PASSKEY (123456). It will warn if you don't change it.
|
||||
// - Key size is set to 16 bytes
|
||||
|
||||
// Set static passkey
|
||||
// The first argument defines if the passkey is static or random.
|
||||
// The second argument is the passkey (ignored when using a random passkey).
|
||||
pSecurity->setPassKey(true, SERVER_PIN);
|
||||
|
||||
// Set IO capability to DisplayOnly
|
||||
// We need the proper IO capability for MITM authentication even
|
||||
// if the passkey is static and won't be shown to the user
|
||||
// See https://www.bluetooth.com/blog/bluetooth-pairing-part-2-key-generation-methods/
|
||||
pSecurity->setCapability(ESP_IO_CAP_OUT);
|
||||
|
||||
// Set authentication mode
|
||||
// Enable bonding, MITM (for password prompts), and secure connection for this example
|
||||
pSecurity->setAuthenticationMode(true, true, true);
|
||||
|
||||
// Set callbacks to handle authentication completion and print IRKs
|
||||
BLEDevice::setSecurityCallbacks(new MySecurityCallbacks());
|
||||
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
pServer->advertiseOnDisconnect(true);
|
||||
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
uint32_t insecure_properties = BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE;
|
||||
uint32_t secure_properties = insecure_properties;
|
||||
|
||||
// NimBLE uses properties to secure characteristics.
|
||||
// These special permission properties are not supported by Bluedroid and will be ignored.
|
||||
// This can be removed if only using Bluedroid (ESP32).
|
||||
// Check the BLECharacteristic.h file for more information.
|
||||
secure_properties |= BLECharacteristic::PROPERTY_READ_AUTHEN | BLECharacteristic::PROPERTY_WRITE_AUTHEN;
|
||||
|
||||
BLECharacteristic *pSecureCharacteristic = pService->createCharacteristic(SECURE_CHARACTERISTIC_UUID, secure_properties);
|
||||
BLECharacteristic *pInsecureCharacteristic = pService->createCharacteristic(INSECURE_CHARACTERISTIC_UUID, insecure_properties);
|
||||
|
||||
// Bluedroid uses permissions to secure characteristics.
|
||||
// This is the same as using the properties above.
|
||||
// NimBLE does not use permissions and will ignore these calls.
|
||||
// This can be removed if only using NimBLE (any SoC except ESP32).
|
||||
pSecureCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ_ENC_MITM | ESP_GATT_PERM_WRITE_ENC_MITM);
|
||||
pInsecureCharacteristic->setAccessPermissions(ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE);
|
||||
|
||||
// Set value for secure characteristic
|
||||
pSecureCharacteristic->setValue("Secure Hello World!");
|
||||
|
||||
// Set value for insecure characteristic
|
||||
// When using NimBLE you will be able to read this characteristic without entering the passkey.
|
||||
pInsecureCharacteristic->setValue("Insecure Hello World!");
|
||||
|
||||
pService->start();
|
||||
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
|
||||
pAdvertising->addServiceUUID(SERVICE_UUID);
|
||||
pAdvertising->setScanResponse(true);
|
||||
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
|
||||
pAdvertising->setMaxPreferred(0x12);
|
||||
BLEDevice::startAdvertising();
|
||||
Serial.println("Characteristic defined! Now you can read it in your phone!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
Video: https://www.youtube.com/watch?v=oCMOYS71NIU
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleNotify.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
|
||||
Create a BLE server that, once we receive a connection, will send periodic notifications.
|
||||
The service advertises itself as: 6E400001-B5A3-F393-E0A9-E50E24DCCA9E
|
||||
Has a characteristic of: 6E400002-B5A3-F393-E0A9-E50E24DCCA9E - used for receiving data with "WRITE"
|
||||
Has a characteristic of: 6E400003-B5A3-F393-E0A9-E50E24DCCA9E - used to send data with "NOTIFY"
|
||||
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create a BLE Service
|
||||
3. Create a BLE Characteristic on the Service
|
||||
4. Create a BLE Descriptor on the characteristic
|
||||
5. Start the service.
|
||||
6. Start advertising.
|
||||
|
||||
In this example rxValue is the data received (only accessible inside that function).
|
||||
And txValue is the data to be sent, in this example just a byte incremented every second.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
|
||||
BLEServer *pServer = NULL;
|
||||
BLECharacteristic *pTxCharacteristic;
|
||||
bool deviceConnected = false;
|
||||
bool oldDeviceConnected = false;
|
||||
uint8_t txValue = 0;
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" // UART service UUID
|
||||
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *pServer) {
|
||||
deviceConnected = true;
|
||||
Serial.println("Device connected");
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer *pServer) {
|
||||
deviceConnected = false;
|
||||
Serial.println("Device disconnected");
|
||||
}
|
||||
};
|
||||
|
||||
class MyCallbacks : public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
String rxValue = pCharacteristic->getValue();
|
||||
|
||||
if (rxValue.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("Received Value: ");
|
||||
for (int i = 0; i < rxValue.length(); i++) {
|
||||
Serial.print(rxValue[i]);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
// Create the BLE Device
|
||||
BLEDevice::init("UART Service");
|
||||
|
||||
// Create the BLE Server
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pTxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX, BLECharacteristic::PROPERTY_NOTIFY);
|
||||
|
||||
// Descriptor 2902 is not required when using NimBLE as it is automatically added based on the characteristic properties
|
||||
pTxCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
BLECharacteristic *pRxCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX, BLECharacteristic::PROPERTY_WRITE);
|
||||
|
||||
pRxCharacteristic->setCallbacks(new MyCallbacks());
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
// Start advertising
|
||||
pServer->getAdvertising()->start();
|
||||
Serial.println("Waiting a client connection to notify...");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
if (deviceConnected) {
|
||||
Serial.print("Notifying Value: ");
|
||||
Serial.println(txValue);
|
||||
pTxCharacteristic->setValue(&txValue, 1);
|
||||
pTxCharacteristic->notify();
|
||||
txValue++;
|
||||
delay(1000); // Notifying every 1 second
|
||||
}
|
||||
|
||||
// disconnecting
|
||||
if (!deviceConnected && oldDeviceConnected) {
|
||||
delay(500); // give the bluetooth stack the chance to get things ready
|
||||
pServer->startAdvertising(); // restart advertising
|
||||
Serial.println("Started advertising again...");
|
||||
oldDeviceConnected = false;
|
||||
}
|
||||
// connecting
|
||||
if (deviceConnected && !oldDeviceConnected) {
|
||||
// do stuff here on connecting
|
||||
oldDeviceConnected = true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleWrite.cpp
|
||||
Ported to Arduino ESP32 by Evandro Copercini
|
||||
*/
|
||||
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLEServer.h>
|
||||
|
||||
// See the following for generating UUIDs:
|
||||
// https://www.uuidgenerator.net/
|
||||
|
||||
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
|
||||
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
|
||||
|
||||
class MyCallbacks : public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
String value = pCharacteristic->getValue();
|
||||
|
||||
if (value.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("New value: ");
|
||||
for (int i = 0; i < value.length(); i++) {
|
||||
Serial.print(value[i]);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
|
||||
Serial.println("1- Download and install an BLE scanner app in your phone");
|
||||
Serial.println("2- Scan for BLE devices in the app");
|
||||
Serial.println("3- Connect to MyESP32");
|
||||
Serial.println("4- Go to CUSTOM CHARACTERISTIC in CUSTOM SERVICE and write something");
|
||||
Serial.println("5- See the magic =)");
|
||||
|
||||
BLEDevice::init("MyESP32");
|
||||
BLEServer *pServer = BLEDevice::createServer();
|
||||
|
||||
BLEService *pService = pServer->createService(SERVICE_UUID);
|
||||
|
||||
BLECharacteristic *pCharacteristic =
|
||||
pService->createCharacteristic(CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE);
|
||||
|
||||
pCharacteristic->setCallbacks(new MyCallbacks());
|
||||
|
||||
pCharacteristic->setValue("Hello World");
|
||||
pService->start();
|
||||
|
||||
BLEAdvertising *pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// put your main code here, to run repeatedly:
|
||||
delay(2000);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,5 @@
|
||||
fqbn_append: PartitionScheme=huge_app
|
||||
|
||||
requires_any:
|
||||
- CONFIG_SOC_BLE_SUPPORTED=y
|
||||
- CONFIG_ESP_HOSTED_ENABLE_BT_NIMBLE=y
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
Based on 31337Ghost's reference code from https://github.com/nkolban/esp32-snippets/issues/385#issuecomment-362535434
|
||||
which is based on pcbreflux's Arduino ESP32 port of Neil Kolban's example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp
|
||||
*/
|
||||
|
||||
/*
|
||||
Create a BLE server that will send periodic iBeacon frames.
|
||||
The design of creating the BLE server is:
|
||||
1. Create a BLE Server
|
||||
2. Create advertising data
|
||||
3. Start advertising.
|
||||
4. wait
|
||||
5. Stop advertising.
|
||||
*/
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEServer.h>
|
||||
#include <BLEUtils.h>
|
||||
#include <BLE2902.h>
|
||||
#include <BLEBeacon.h>
|
||||
|
||||
#define DEVICE_NAME "ESP32"
|
||||
#define SERVICE_UUID "7A0247E7-8E88-409B-A959-AB5092DDB03E"
|
||||
#define BEACON_UUID "2D7A9F0C-E0E8-4CC9-A71B-A21DB2D034A1"
|
||||
#define BEACON_UUID_REV "A134D0B2-1DA2-1BA7-C94C-E8E00C9F7A2D"
|
||||
#define CHARACTERISTIC_UUID "82258BAA-DF72-47E8-99BC-B73D7ECD08A5"
|
||||
|
||||
BLEServer *pServer;
|
||||
BLECharacteristic *pCharacteristic;
|
||||
bool deviceConnected = false;
|
||||
uint8_t value = 0;
|
||||
|
||||
class MyServerCallbacks : public BLEServerCallbacks {
|
||||
void onConnect(BLEServer *pServer) {
|
||||
deviceConnected = true;
|
||||
Serial.println("deviceConnected = true");
|
||||
};
|
||||
|
||||
void onDisconnect(BLEServer *pServer) {
|
||||
deviceConnected = false;
|
||||
Serial.println("deviceConnected = false");
|
||||
|
||||
// Restart advertising to be visible and connectable again
|
||||
BLEAdvertising *pAdvertising;
|
||||
pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->start();
|
||||
Serial.println("iBeacon advertising restarted");
|
||||
}
|
||||
};
|
||||
|
||||
class MyCallbacks : public BLECharacteristicCallbacks {
|
||||
void onWrite(BLECharacteristic *pCharacteristic) {
|
||||
String rxValue = pCharacteristic->getValue();
|
||||
|
||||
if (rxValue.length() > 0) {
|
||||
Serial.println("*********");
|
||||
Serial.print("Received Value: ");
|
||||
for (int i = 0; i < rxValue.length(); i++) {
|
||||
Serial.print(rxValue[i]);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println("*********");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void init_service() {
|
||||
BLEAdvertising *pAdvertising;
|
||||
pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->stop();
|
||||
|
||||
// Create the BLE Service
|
||||
BLEService *pService = pServer->createService(BLEUUID(SERVICE_UUID));
|
||||
|
||||
// Create a BLE Characteristic
|
||||
pCharacteristic = pService->createCharacteristic(
|
||||
CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ | BLECharacteristic::PROPERTY_WRITE | BLECharacteristic::PROPERTY_NOTIFY
|
||||
);
|
||||
pCharacteristic->setCallbacks(new MyCallbacks());
|
||||
pCharacteristic->addDescriptor(new BLE2902());
|
||||
|
||||
pAdvertising->addServiceUUID(BLEUUID(SERVICE_UUID));
|
||||
|
||||
// Start the service
|
||||
pService->start();
|
||||
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void init_beacon() {
|
||||
BLEAdvertising *pAdvertising;
|
||||
pAdvertising = pServer->getAdvertising();
|
||||
pAdvertising->stop();
|
||||
// iBeacon
|
||||
BLEBeacon myBeacon;
|
||||
myBeacon.setManufacturerId(0x4c00);
|
||||
myBeacon.setMajor(5);
|
||||
myBeacon.setMinor(88);
|
||||
myBeacon.setSignalPower(0xc5);
|
||||
myBeacon.setProximityUUID(BLEUUID(BEACON_UUID_REV));
|
||||
|
||||
BLEAdvertisementData advertisementData;
|
||||
advertisementData.setFlags(0x1A);
|
||||
advertisementData.setManufacturerData(myBeacon.getData());
|
||||
pAdvertising->setAdvertisementData(advertisementData);
|
||||
|
||||
pAdvertising->start();
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println();
|
||||
Serial.println("Initializing...");
|
||||
Serial.flush();
|
||||
|
||||
BLEDevice::init(DEVICE_NAME);
|
||||
pServer = BLEDevice::createServer();
|
||||
pServer->setCallbacks(new MyServerCallbacks());
|
||||
|
||||
init_service();
|
||||
init_beacon();
|
||||
|
||||
Serial.println("iBeacon + service defined and advertising!");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
if (deviceConnected) {
|
||||
Serial.printf("*** NOTIFY: %d ***\n", value);
|
||||
pCharacteristic->setValue(&value, 1);
|
||||
pCharacteristic->notify();
|
||||
value++;
|
||||
}
|
||||
delay(2000);
|
||||
}
|
||||
Reference in New Issue
Block a user