Compare commits

...
Author SHA1 Message Date
eddy
d6e37322f3 fix: NimBLE 128-bit UUID scan response + Android key/UUID loading
- Firmware: add explicit setScanResponseData with setCompleteServices for
  128-bit service UUID so Android ScanFilter finds the device
- Android: hexStringToBytes accepts any even-length hex (fixes UUID=32chars
  failing the hardcoded 64-char check)
- Android: onActivityResult matches SettingsActivity.RESULT_SCAN instead of
  Activity.RESULT_OK
2026-07-29 19:30:46 +02:00
eddy
96d3620535 feat: migrate BLE to NimBLE — 45% flash, 33% RAM reduction
- Replaced ESP32 BLE Arduino with h2zero/NimBLE-Arduino@^2
- -DCONFIG_BT_NIMBLE_ENABLED to disable built-in Bluedroid
- NIMBLE_PROPERTY::* replaces BLECharacteristic::PROPERTY_* + setAccessPermissions
- BLE2902 descriptor removed (NimBLE auto-creates it)
- pService->start() removed (NimBLE starts services with server)
- setMinPreferred → setMinInterval/setMaxInterval
- onConnect/onDisconnect/onWrite get extra NimBLEConnInfo& param
- Re-advertise: BLEDevice → NimBLEDevice::startAdvertising()
- Firmware: 533KB flash (was 964KB), 29KB RAM (was 44KB)
2026-07-29 18:58:49 +02:00
4 changed files with 31 additions and 28 deletions

View file

@ -213,7 +213,7 @@ class MainActivity : AppCompatActivity() {
@Deprecated("Deprecated in Java") @Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_SETTINGS && resultCode == Activity.RESULT_OK) { if (requestCode == REQUEST_SETTINGS && resultCode == SettingsActivity.RESULT_SCAN) {
stopScan() stopScan()
bluetoothGatt?.close() bluetoothGatt?.close()
bluetoothGatt = null bluetoothGatt = null
@ -260,7 +260,7 @@ class MainActivity : AppCompatActivity() {
private fun hexStringToBytes(hex: String): ByteArray? { private fun hexStringToBytes(hex: String): ByteArray? {
val cleaned = hex.uppercase().filter { it in "0123456789ABCDEF" } val cleaned = hex.uppercase().filter { it in "0123456789ABCDEF" }
if (cleaned.length != 64) return null if (cleaned.isEmpty() || cleaned.length % 2 != 0) return null
return cleaned.chunked(2).map { it.toInt(16).toByte() }.toByteArray() return cleaned.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
} }

View file

@ -10,6 +10,8 @@ build_flags =
-DCORE_DEBUG_LEVEL=5 -DCORE_DEBUG_LEVEL=5
-DARDUINO_USB_CDC_ON_BOOT=1 -DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=1 -DARDUINO_USB_MODE=1
-DCONFIG_BT_NIMBLE_ENABLED
lib_deps = lib_deps =
h2zero/NimBLE-Arduino@^2
upload_port = /dev/ttyACM0 upload_port = /dev/ttyACM0
upload_speed = 115200 upload_speed = 115200

View file

@ -1,8 +1,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <BLEDevice.h> #include <NimBLEDevice.h>
#include <BLEServer.h> #include <NimBLEAdvertising.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <mbedtls/md.h> #include <mbedtls/md.h>
#include <Preferences.h> #include <Preferences.h>
@ -25,7 +23,7 @@
#define NVS_KEY_NAME "key" #define NVS_KEY_NAME "key"
#define NVS_UUID_NAME "uuid" #define NVS_UUID_NAME "uuid"
BLECharacteristic *pCharacteristic; NimBLECharacteristic *pCharacteristic;
bool deviceConnected = false; bool deviceConnected = false;
bool bleRelay1 = false; bool bleRelay1 = false;
bool bleRelay2 = false; bool bleRelay2 = false;
@ -39,19 +37,19 @@ bool keyProvisioned = false;
bool uuidProvisioned = false; bool uuidProvisioned = false;
Preferences preferences; Preferences preferences;
class MyServerCallbacks : public BLEServerCallbacks { class MyServerCallbacks : public NimBLEServerCallbacks {
void onConnect(BLEServer *pServer) override { void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override {
deviceConnected = true; deviceConnected = true;
lastSeq = 0; lastSeq = 0;
Serial.println("BLE: device connected"); Serial.println("BLE: device connected");
} }
void onDisconnect(BLEServer *pServer) override { void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override {
deviceConnected = false; deviceConnected = false;
bleRelay1 = false; bleRelay1 = false;
bleRelay2 = false; bleRelay2 = false;
lastSeq = 0; lastSeq = 0;
Serial.println("BLE: device disconnected"); Serial.println("BLE: device disconnected");
BLEDevice::startAdvertising(); NimBLEDevice::startAdvertising();
} }
}; };
@ -101,8 +99,8 @@ static void parseHexKey(const String &hex, uint8_t *out) {
} }
} }
class MyCharacteristicCallbacks : public BLECharacteristicCallbacks { class MyCharacteristicCallbacks : public NimBLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pChar) override { void onWrite(NimBLECharacteristic *pChar, NimBLEConnInfo &connInfo) override {
std::string value = pChar->getValue(); std::string value = pChar->getValue();
if (value.length() != HMAC_PKT_SIZE) { if (value.length() != HMAC_PKT_SIZE) {
Serial.printf("BLE: bad packet length %d (expected %d)\n", Serial.printf("BLE: bad packet length %d (expected %d)\n",
@ -185,36 +183,39 @@ void setup() {
} }
Serial.println("LED blink done, starting BLE..."); Serial.println("LED blink done, starting BLE...");
BLEDevice::init("SylDa_Server"); NimBLEDevice::init("SylDa_Server");
BLEServer *pServer = BLEDevice::createServer(); NimBLEServer *pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks()); pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID); NimBLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic( pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID, CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_WRITE_NR | BLECharacteristic::PROPERTY_NOTIFY NIMBLE_PROPERTY::WRITE_NR | NIMBLE_PROPERTY::NOTIFY
); );
pCharacteristic->setAccessPermissions(ESP_GATT_PERM_WRITE);
pCharacteristic->addDescriptor(new BLE2902());
pCharacteristic->setCallbacks(new MyCharacteristicCallbacks()); pCharacteristic->setCallbacks(new MyCharacteristicCallbacks());
pCharacteristic->setValue("0"); pCharacteristic->setValue("0");
pService->start();
BLECharacteristic *pUuidChar = pService->createCharacteristic( NimBLECharacteristic *pUuidChar = pService->createCharacteristic(
UUID_CHAR_UUID, UUID_CHAR_UUID,
BLECharacteristic::PROPERTY_READ NIMBLE_PROPERTY::READ
); );
pUuidChar->setValue(deviceUuid, ESP_UUID_LEN); pUuidChar->setValue(deviceUuid, ESP_UUID_LEN);
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising(); NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
pAdvertising->setName("SylDa_Server");
pAdvertising->addServiceUUID(SERVICE_UUID); pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06);
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
Serial.println("BLE: advertising started as 'SylDa_Server'"); NimBLEAdvertisementData scanResp;
scanResp.setCompleteServices(NimBLEUUID(SERVICE_UUID));
pAdvertising->setScanResponseData(scanResp);
pAdvertising->enableScanResponse(true);
pAdvertising->setMinInterval(32);
pAdvertising->setMaxInterval(64);
bool advOk = pAdvertising->start();
Serial.printf("BLE: advertising started as 'SylDa_Server' (start=%d, isAdv=%d)\n",
advOk, pAdvertising->isAdvertising());
if (keyProvisioned) { if (keyProvisioned) {
Serial.print("HMAC Key: "); Serial.print("HMAC Key: ");