Differences

This shows you the differences between two versions of the page.

Link to this comparison view

iothings:laboratoare:2025:lab2 [2025/10/07 13:23]
dan.tudose [BLE Security and Pairing]
iothings:laboratoare:2025:lab2 [2025/10/07 13:44] (current)
dan.tudose [BLE Security and Pairing]
Line 881: Line 881:
 We can test pairing with the nRF Connect mobile app.  We can test pairing with the nRF Connect mobile app. 
  
-  - Flash your ESP32-C6 firmware with security-enabled GATT characteristics.+  - Flash your ESP32-C6 firmware with the code below, which has security-enabled GATT characteristics.
   - Open **nRF Connect → Scan → Connect** to your ESP32-C6.   - Open **nRF Connect → Scan → Connect** to your ESP32-C6.
   - Try to **read** a protected characteristic — expect something like: <​code>​Error 0x05: Insufficient Authentication</​code>​   - Try to **read** a protected characteristic — expect something like: <​code>​Error 0x05: Insufficient Authentication</​code>​
Line 893: Line 893:
 <code C main.cpp>​ <code C main.cpp>​
 #include <​Arduino.h>​ #include <​Arduino.h>​
 +#include <​Wire.h>​
 +#include <​cmath>​
 +#include <​Adafruit_BME680.h>​
 #include <​NimBLEDevice.h>​ #include <​NimBLEDevice.h>​
 #include <​NimBLEAdvertisementData.h>​ #include <​NimBLEAdvertisementData.h>​
 +
 +
  
 namespace { namespace {
  
-constexpr char kDeviceName[] = "Dan ESP32C6 ​Secure GATT"; +#define I2C_SDA_PIN 21 
-const NimBLEUUID ​kSecureServiceUUID("​12345678-90ab-cdef-1234-567890abcdef"​); +#define I2C_SCL_PIN 22 
-const NimBLEUUID ​kSecureValueCharUUID("​12345678-90ab-cdef-1234-567890abcdea"​); +#define BME680_I2C_ADDR 0x77 
-const NimBLEUUID ​kSecureControlCharUUID("​12345678-90ab-cdef-1234-567890abcdeb"​);+ 
 +constexpr char kDeviceName[] = "​ESP32C6 ​ESS"; 
 +const NimBLEUUID ​kEnvironmentalServiceUUID((uint16_t)0x181A); 
 +const NimBLEUUID ​kTemperatureCharUUID((uint16_t)0x2A6E); 
 +const NimBLEUUID ​kHumidityCharUUID((uint16_t)0x2A6F);​ 
 +const NimBLEUUID kPressureCharUUID((uint16_t)0x2A6D);
 constexpr uint32_t kSecurityPasskey = 654321; // 6-digit static passkey for pairing constexpr uint32_t kSecurityPasskey = 654321; // 6-digit static passkey for pairing
-constexpr uint32_t kNotifyIntervalMs = 5000;  // send secure ​notify every 5 seconds+constexpr uint32_t kNotifyIntervalMs = 5000;  // send notify every 5 seconds
  
-NimBLECharacteristic* ​g_valueCharacteristic ​= nullptr; +Adafruit_BME680 g_bme; 
-NimBLECharacteristic* ​g_controlCharacteristic ​= nullptr;+bool g_bmeReady = false; 
 +NimBLECharacteristic* g_temperatureCharacteristic = nullptr; 
 +NimBLECharacteristic* ​g_humidityCharacteristic ​= nullptr; 
 +NimBLECharacteristic* ​g_pressureCharacteristic ​= nullptr;
 volatile bool g_isConnected = false; volatile bool g_isConnected = false;
 uint32_t g_lastNotify = 0; uint32_t g_lastNotify = 0;
Line 948: Line 961:
 class ValueCallbacks : public NimBLECharacteristicCallbacks { class ValueCallbacks : public NimBLECharacteristicCallbacks {
   void onRead(NimBLECharacteristic* characteristic,​ NimBLEConnInfo&​ connInfo) override {   void onRead(NimBLECharacteristic* characteristic,​ NimBLEConnInfo&​ connInfo) override {
-    Serial.printf("​[GATT] ​Secure read from %s, value='​%s'\n",+    Serial.printf("​[GATT] ​Read from %s on characteristic ​%s\n",
                   connInfo.getAddress().toString().c_str(),​                   connInfo.getAddress().toString().c_str(),​
-                  characteristic->​getValue().c_str());​+                  characteristic->​getUUID().toString().c_str());​
   }   }
  
   void onWrite(NimBLECharacteristic* characteristic,​ NimBLEConnInfo&​ connInfo) override {   void onWrite(NimBLECharacteristic* characteristic,​ NimBLEConnInfo&​ connInfo) override {
     std::string value = characteristic->​getValue();​     std::string value = characteristic->​getValue();​
-    Serial.printf("​[GATT] ​Secure write from %s, value='​%s'\n",+    Serial.printf("​[GATT] ​Write to %s from %s, %zu bytes\n"
 +                  characteristic->​getUUID().toString().c_str(),
                   connInfo.getAddress().toString().c_str(),​                   connInfo.getAddress().toString().c_str(),​
-                  value.c_str());+                  value.size());
   }   }
 }; };
  
-class ControlCallbacks : public NimBLECharacteristicCallbacks { +ServerCallbacks g_serverCallbacks
-  void onWrite(NimBLECharacteristic* characteristic,​ NimBLEConnInfo&​ connInfo) override { +ValueCallbacks g_valueCallbacks;
-    const std::string value = characteristic->​getValue()+
-    ​Serial.printf("​[CTRL] Command from %s: '​%s'​\n",​ connInfo.getAddress().toString().c_str(),​ value.c_str());+
  
-    ​if (value == "​reset"​) { +bool initBME680() { 
-      ​g_valueCharacteristic->​setValue("secure-data-reset"); +  ​if (!g_bme.begin(BME680_I2C_ADDR)) { 
-      ​g_valueCharacteristic->​notify(); +    ​Serial.println("[BME680] Not found on 0x77, trying 0x76..."); 
-      Serial.println("​[CTRLValue characteristic reset & notified");+    ​if ​(!g_bme.begin(0x76)) { 
 +      Serial.println("​[BME680Sensor not found. Check wiring/​power.")
 +      return false;
     }     }
   }   }
-}; 
  
-ServerCallbacks g_serverCallbacks+  g_bme.setTemperatureOversampling(BME680_OS_8X)
-ValueCallbacks g_valueCallbacks+  ​g_bme.setHumidityOversampling(BME680_OS_2X)
-ControlCallbacks g_controlCallbacks;+  ​g_bme.setPressureOversampling(BME680_OS_4X); 
 +  g_bme.setIIRFilterSize(BME680_FILTER_SIZE_3);​ 
 +  g_bme.setGasHeater(0,​ 0); // Disable gas heater for periodic environmental sampling 
 + 
 +  Serial.println("​[BME680] Sensor initialized"​);​ 
 +  return true; 
 +
 + 
 +bool readBME680(float&​ temperatureC,​ float& humidityPct,​ float& pressurePa) { 
 +  if (!g_bme.performReading()) { 
 +    Serial.println("​[BME680] Failed to perform reading"​);​ 
 +    return false; 
 +  } 
 + 
 +  temperatureC = g_bme.temperature;​ 
 +  humidityPct = g_bme.humidity;​ 
 +  pressurePa = g_bme.pressure;​ // Library returns Pascals 
 +  return true; 
 +}
  
 void setupSecurity() { void setupSecurity() {
Line 989: Line 1020:
   server->​setCallbacks(&​g_serverCallbacks,​ false);   server->​setCallbacks(&​g_serverCallbacks,​ false);
  
-  NimBLEService* ​secureService ​= server->​createService(kSecureServiceUUID);+  NimBLEService* ​environmentalService ​= server->​createService(kEnvironmentalServiceUUID);
  
-  ​g_valueCharacteristic ​secureService->​createCharacteristic( +  ​g_temperatureCharacteristic ​environmentalService->​createCharacteristic( 
-      ​kSecureValueCharUUID,+      ​kTemperatureCharUUID,
       NIMBLE_PROPERTY::​READ | NIMBLE_PROPERTY::​READ_ENC |       NIMBLE_PROPERTY::​READ | NIMBLE_PROPERTY::​READ_ENC |
           NIMBLE_PROPERTY::​READ_AUTHEN |           NIMBLE_PROPERTY::​READ_AUTHEN |
-          NIMBLE_PROPERTY::​WRITE | NIMBLE_PROPERTY::​WRITE_ENC | 
-          NIMBLE_PROPERTY::​WRITE_AUTHEN | 
           NIMBLE_PROPERTY::​NOTIFY);​           NIMBLE_PROPERTY::​NOTIFY);​
-  ​g_valueCharacteristic->​setCallbacks(&​g_valueCallbacks);​ +  ​g_temperatureCharacteristic->​setCallbacks(&​g_valueCallbacks);​ 
-  ​g_valueCharacteristic->​setValue("​secure-data-init"​);+  ​uint8_t tempPlaceholder[2] = {0xFF, 0x7F}; // NaN equivalent (0x7FFF) 
 +  g_temperatureCharacteristic->​setValue(tempPlaceholder,​ sizeof(tempPlaceholder));
  
-  ​g_controlCharacteristic ​secureService->​createCharacteristic( +  ​g_humidityCharacteristic ​environmentalService->​createCharacteristic( 
-      ​kSecureControlCharUUID+      ​kHumidityCharUUID
-      NIMBLE_PROPERTY::​WRITE | NIMBLE_PROPERTY::​WRITE_ENC ​+      NIMBLE_PROPERTY::​READ | NIMBLE_PROPERTY::​READ_ENC ​
-          NIMBLE_PROPERTY::​WRITE_AUTHEN ​+          NIMBLE_PROPERTY::​READ_AUTHEN ​
-          NIMBLE_PROPERTY::​READ | NIMBLE_PROPERTY::​READ_ENC | +          NIMBLE_PROPERTY::​NOTIFY); 
-          NIMBLE_PROPERTY::​READ_AUTHEN); +  ​g_humidityCharacteristic->​setCallbacks(&​g_valueCallbacks); 
-  ​g_controlCharacteristic->​setCallbacks(&​g_controlCallbacks); +  ​uint8_t humidityPlaceholder[2] = {0xFF, 0xFF}; 
-  ​g_controlCharacteristic->​setValue("​pending"​);+  g_humidityCharacteristic->​setValue(humidityPlaceholder,​ sizeof(humidityPlaceholder));
  
-  ​secureService->​start();​+  ​g_pressureCharacteristic = environmentalService->​createCharacteristic( 
 +      kPressureCharUUID,​ 
 +      NIMBLE_PROPERTY::​READ | NIMBLE_PROPERTY::​READ_ENC | 
 +          NIMBLE_PROPERTY::​READ_AUTHEN | 
 +          NIMBLE_PROPERTY::​NOTIFY);​ 
 +  g_pressureCharacteristic->​setCallbacks(&​g_valueCallbacks);​ 
 +  uint8_t pressurePlaceholder[4] = {0xFF, 0xFF, 0xFF, 0xFF}; 
 +  g_pressureCharacteristic->​setValue(pressurePlaceholder,​ sizeof(pressurePlaceholder));​ 
 + 
 +  environmentalService->​start();​
  
   NimBLEAdvertising* advertising = NimBLEDevice::​getAdvertising();​   NimBLEAdvertising* advertising = NimBLEDevice::​getAdvertising();​
   NimBLEAdvertisementData advData;   NimBLEAdvertisementData advData;
   advData.setName(kDeviceName);​   advData.setName(kDeviceName);​
-  advData.addServiceUUID(kSecureServiceUUID); +  advData.addServiceUUID(kEnvironmentalServiceUUID); 
-  advData.setAppearance(0x0080); // Generic Computer+  advData.setAppearance(0x0341); // Thermometer
   advertising->​setAdvertisementData(advData);​   advertising->​setAdvertisementData(advData);​
  
   NimBLEAdvertisementData scanData;   NimBLEAdvertisementData scanData;
-  scanData.setName("​Secure GATT", false);+  scanData.setName("​Env Sensing", false);
   advertising->​setScanResponseData(scanData);​   advertising->​setScanResponseData(scanData);​
  
Line 1027: Line 1066:
  
   if (advertising->​start()) {   if (advertising->​start()) {
-    Serial.println("​[BLE] Advertising ​secure GATT service"​);​+    Serial.println("​[BLE] Advertising ​environmental sensing ​service"​);​
   } else {   } else {
     Serial.println("​[BLE] Failed to start advertising"​);​     Serial.println("​[BLE] Failed to start advertising"​);​
Line 1033: Line 1072:
 } }
  
-void notifySecureValue() { +void updateEnvironmentalMeasurements() { 
-  if (!g_isConnected || g_valueCharacteristic ​== nullptr) {+  if (!g_isConnected || !g_bmeReady || 
 +      g_temperatureCharacteristic == nullptr || 
 +      g_humidityCharacteristic == nullptr || 
 +      g_pressureCharacteristic ​== nullptr) {
     return;     return;
   }   }
Line 1044: Line 1086:
   g_lastNotify = now;   g_lastNotify = now;
  
-  ​char buffer[32]; +  ​float temperatureC = 0.0f; 
-  ​snprintf(buffer, sizeof(buffer), "tick-%lu", static_cast<​unsigned long>(now / 1000)); +  float humidityPct = 0.0f; 
-  ​g_valueCharacteristic->​setValue(buffer); +  float pressurePa = 0.0f; 
-  ​g_valueCharacteristic->​notify();​ +  if (!readBME680(temperatureC,​ humidityPct,​ pressurePa)) { 
-  Serial.printf("​[GATT] Notified ​encrypted value '%s'\n", ​buffer);+    return; 
 +  } 
 + 
 +  const int16_t temperatureHundredths = 
 +      static_cast<​int16_t>​(std::​lround(temperatureC * 100.0)); 
 +  const uint16_t humidityHundredths = 
 +      static_cast<​uint16_t>​(std::​lround(humidityPct * 100.0)); 
 +  const uint32_t pressureValue = static_cast<​uint32_t>​(std::​lround(pressurePa));​ 
 + 
 +  uint8_t temperaturePayload[2= { 
 +      static_cast<​uint8_t>​(temperatureHundredths & 0xFF), 
 +      static_cast<​uint8_t>​((temperatureHundredths >> 8) & 0xFF), 
 +  }
 +  ​g_temperatureCharacteristic->​setValue(temperaturePayload, sizeof(temperaturePayload));​ 
 +  g_temperatureCharacteristic->​notify();​ 
 + 
 +  uint8_t humidityPayload[2] = { 
 +      static_cast<​uint8_t>​(humidityHundredths & 0xFF), 
 +      static_cast<​uint8_t>​((humidityHundredths >> 8) & 0xFF), 
 +  }; 
 +  g_humidityCharacteristic->​setValue(humidityPayloadsizeof(humidityPayload));​ 
 +  g_humidityCharacteristic->​notify();​ 
 + 
 +  uint8_t pressurePayload[4] = { 
 +      ​static_cast<​uint8_t>(pressureValue & 0xFF)
 +      static_cast<​uint8_t>​((pressureValue >> 8& 0xFF), 
 +      static_cast<​uint8_t>​((pressureValue >> 16) & 0xFF), 
 +      static_cast<​uint8_t>​((pressureValue >> 24) & 0xFF), 
 +  }
 +  ​g_pressureCharacteristic->​setValue(pressurePayload,​ sizeof(pressurePayload)); 
 +  ​g_pressureCharacteristic->​notify();​ 
 + 
 +  Serial.printf("​[ESS] Notified ​T=%.2fC H=%.2f%% P=%.2fhPa\n", 
 +                temperatureC,​ 
 +                humidityPct,​ 
 +                pressurePa / 100.0f);
 } }
  
Line 1060: Line 1137:
   delay(1000);​   delay(1000);​
   Serial.println();​   Serial.println();​
-  Serial.println("​[BOOT] ESP32-C6 ​secure GATT server");+  Serial.println("​[BOOT] ESP32-C6 ​Environmental Sensing Server"); 
 + 
 +  Wire.begin(I2C_SDA_PIN,​ I2C_SCL_PIN);​ 
 +  g_bmeReady = initBME680();​ 
 +  if (!g_bmeReady) { 
 +    Serial.println("​[BOOT] BME680 not ready; characteristics will remain invalid"​);​ 
 +  }
  
   NimBLEDevice::​init(kDeviceName);​   NimBLEDevice::​init(kDeviceName);​
Line 1068: Line 1151:
  
 void loop() { void loop() {
-  ​notifySecureValue();+  ​updateEnvironmentalMeasurements();
   delay(100);   delay(100);
 } }
 +
  
 </​code>​ </​code>​
iothings/laboratoare/2025/lab2.1759832611.txt.gz · Last modified: 2025/10/07 13:23 by dan.tudose
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0