Login Login
MORE

WIDGETS

Widgets

Wanted articles
Who is online?
Article tools

Domotica Arduino Integrazione Bluetooth

From Aino Wiki

Jump to: navigation, search

Introduzione

Fondamentale è la guida di Arduino sul BLE (Bluetooth Low Energy): docs.arduino.cc

BLE

E' una tecnologia di comunicazione wireless progettata per lo scambio di dati a corto raggio tra dispositivi elettronici.
A differenza del suo predecessore, Bluetooth Classic, ottimizzato per comunicazioni continue e a velocità di trasmissione dati relativamente elevate, BLE punta alla riduzione al minimo del consumo energetico mantenendo la connettività. BLE è particolarmente adatto per applicazioni che richiedono una lunga durata della batteria.
BLE opera alla frequenza di banda 2.4GHz ISM ((Industrial, Scientific, and Medical), questa banda è poi suddivisa in canali multipli.
Il range è circa di 50 metri che è influenzato da diversi fattori:

  • ostacoli, come muri;
  • interferenze, ad es. per la presenza di altri dispositivi a 2.4 GHz;
  • design dell'antenna;
  • orientamento.

Central e Peripheral

Nella comunicazione tra due dispositivi esistono due ruoli: il dispositivo "Central" e il dispositivo "Peripheral".
Sebbene la maggior parte dei dispositivi possa svolgere entrambi i ruoli, se si sta sviluppando un'applicazione Android per connettersi a un dispositivo fisico tramite BLE, il dispositivo Android svolgerà il ruolo Central. Allo stesso modo, se si sta progettando un dispositivo basato su microcontrollore si sta costruendo un dispositivo Peripheral.
I dispositivi central sono clients, essi leggono e scrivono dati da dispositivi periferici (peripheral).
I dispositivi Peripheral sono server, forniscono dati dai sensori come caratteristiche in lettura e forniscono caratteristiche in lettura\scrittura per controllare attuatori come motori, luci e così via.

Quando si crea un'applicazione Central, ci sono tre passaggi di alto livello da implementare:

  • Trovare i dispositivi Periphral e filtrare fino al dispositivo e al servizio di interesse.
  • Definire caratteristiche di lettura e scrittura.
  • Disconnettersi quando desiderato.

Il dispositivo Peripheral definisce un insieme di services (servizi), characteristics (caratteristiche) e descriptors (descrittori):

  • I services sono indispensabili: un dispositivo senza almeno un servizio non è utile. Un service potrebbe apparire come un cardiofrequenzimetro, un termometro, un sensore generico. I services hanno delle caratteristiche.
  • Le characteristics sono simili alle "variabili membro" in un linguaggio orientato agli oggetti in quanto contengono dati e rappresentano valori.  Le characteristics includono valori come la frequenza cardiaca (battiti al minuto) o il volume. Le characteristics possono essere di sola lettura, di sola scrittura, di lettura/scrittura e così via. Scrivere un nuovo valore in una characteristic è in qualche modo analogo all'invocazione di un "setter" in un linguaggio orientato agli oggetti, poiché il metodo non solo aggiornerà il valore, ma avrà anche la possibilità di intervenire in seguito all'aggiornamento di tale valore.
  • I descriptors forniscono informazioni aggiuntive su una caratteristica.

UUID

Ogni dispositivo fornisce dei servizi che a loro volta forniscono caratteristiche. Si posson definire i propri servizi o utilizzarne degli standard (vedi bluetooth.com). BLE identifica i servizi e le caratteristiche con a un identificatore univoco universale, o UUID.
Un UUID è un numero a 128 bit. I 96 bit più bassi sono tutti uguali e ci interessano solo i 32 bit più alti. E, per la maggior parte, i 16 bit più alti sono zero. Quindi, in generale, ci rimangono 16 bit di interesse.

Ecco un esempio di UUID BLE:

0000fa01-0000-1000-8000-00805f9b34fb

L'unica parte di questo UUID che ci interessa davvero è fa01, questa è la parte che identifica questo dispositivo.

BLE consente ai produttori di dispositivi di definire un numero qualsiasi di servizi, caratteristiche e descrittori, ma esistono alcuni valori pre-assegnati per dispositivi e caratteristiche che vengono utilizzati per contenere informazioni che tutti i dispositivi devono fornire, come il nome o l'aspetto.


Materiale e link utili:

Arduino Nano ESP32

Grazie al chip ESP32-S3 che supporta sia sia Bluetooth Classic che il BLE (Bluetooth Low Energy).
Il BLE è ideale per dispositivi a batteria e app mobile.

  • Le librerie più diffuse sono:
    • La libreria standard inclusa nel pacchetto ESP32 che è BLEDevice.h (Usando VS Code con PlatformIO non c'è bisogno di installare nulla, si può già includere).
    • Altra libreria disponibile senza installazioni è la ArduinoBLE.h (anche con PlatformIO).
    • Valida alternativa alle precedenti ma meno efficiente è la libreria Bluetooth Classic BluetoothSerial.h che consente una connessione seriale semplice (tipo RS232).
  • L'app per smartphone potrebbe essere: "nRF Connect for Mobile" (Nordic Semiconductor) o "Serial Bluetooth Terminal" (per emulare la seriale via BLE).

Libreria: BLEDevice.h

NOTA Per usare la libreria si generano dei codici univoci alfanumerici ad identificare la connessione, UUID, per generarli si può usare un servizio on-line su uuidgenerator.net

Esempio Base

Esempio minimale per impostare il dispositivo microcontrollore come server:

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
 
// UUID generati casualmente (puoi cambiarli)
#define SERVICE_UUID        "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHARACTERISTIC_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
 
void setup() {
  Serial.begin(115200);
  Serial.println("Avvio BLE...");
 
  // Inizializza il dispositivo BLE
  BLEDevice::init("NanoESP32-LED");
 
  // Crea il server BLE
  BLEServer *pServer = BLEDevice::createServer();
 
  // Crea il servizio BLE
  BLEService *pService = pServer->createService(SERVICE_UUID);
 
  // Crea la caratteristica BLE
  BLECharacteristic *pCharacteristic = pService->createCharacteristic(
                                         CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ |
                                         BLECharacteristic::PROPERTY_WRITE
                                       );
  // Imposta un valore iniziale
  pCharacteristic->setValue("Benvenuto ospite");
  pService->start();
 
  // Inizia l'advertising (rendere il dispositivo visibile)
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pAdvertising->setScanResponse(true);
  BLEDevice::startAdvertising();
 
  Serial.println("Il dispositivo ora è visibile e pronto alla connessione!");
}
 
void loop() {
  // Esempio base
  delay(2000);
}

Lato smartphone:

  1. Aprire l'app nRF Connect quindi avviare la scansione dei dispositivi disponibili alla comunicazione.
  2. Cercare il dispositivo usando il nome che si è scelto di dare nello sketch, quindi cliccare su "Connect".
  3. Una volta connesso, vedrai un servizio con il UUID definito. Puoi leggere il valore ("Ciao Cellulare") o scriverne uno nuovo.

Libreria: ArduinoBLE.h

E' una libreria ufficiale anche se in VS Code con PlatformIO è necessario installarla (in automatico si risolveranno delle dipendenze); come nella libreia BLE*.h si fa uso di UUID.
Doc ufficiale:


La libreria instanzia direttamente diverse classi e come accennato precedentemente, si definiscono Service, Characteristic e Descriptor:

  • BLE viene utilizzato per abilitare il modulo Bluetooth® Low Energy.
  • BLEDevice viene utilizzato per ottenere informazioni sui dispositivi connessi o rilevati durante la scansione.
  • BLEService viene utilizzato per abilitare i servizi forniti dalla scheda o per interagire con i servizi forniti da una scheda remota.
  • BLECharacteristic viene utilizzato per abilitare le caratteristiche offerte dalla scheda in un servizio o per interagire con le caratteristiche fornite da una scheda remota. Come descritto prima, le caratteristiche possono agire con una combinazione di read\write; per una stessa caratteristica si posson definite concatenandoli col pipe | , ad es. BLEUnsignedCharCharacteristic buttonCharacteristic("2102", BLERead | BLEWrite | BLENotify);
  • BLEDescriptor viene utilizzato per descrivere una caratteristica offerta dalla scheda. Es. BLEDescriptor mydescriptor1("2103","Mia descrizione");

In merito alle characteristic un dispositivo central può averle di tipo:

  • read, per chiedere alla periferica di restituire il valore corrente della caratteristica. Spesso utilizzato per characteristic che non cambiano molto spesso, ad esempio caratteristiche utilizzate per la configurazione, i numeri di versione, ecc.
  • write, per modificare il valore della characteristic . Spesso utilizzato per operazioni simili a comandi, ad esempio per dire alla periferica di accendere o spegnere un motore.
  • indicate e notify, per chiedere alla periferica di inviare continuamente valori aggiornati della caratteristica, senza che la centrale debba richiederlo costantemente.

Oggetto BLE: metodi di interesse

Istruzione Descrizione Esempio
BLE.begin() Usata per inizializzare il Bluetooth Low Energy del dispositivo.
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
 
    while (1);
  }
BLE.end() Interrompe l'uso del BLE.
// Supponendo si sia già correttamente inizializzato:
  BLE.end();
BLE.disconnect() Disconnette qualsiasi dispositivo BLE connesso.
if (BLE.connected()) {
    BLE.disconnect();
  }
BLE.address() Ottiene l'indirizzo MAC dei dispositivi connessi.
String address = BLE.address();
Serial.print("Local address is: ");
Serial.println(address); //Esempio:  65:d1:4e:41:9c:ac
BLE.rssi() Restituisce la potenza del segnale RSSI (Received signal strength indication) del dispositivo connesso.
if (BLE.connected()) {
    Serial.print("RSSI = ");
    Serial.println(BLE.rssi());
  }
BLE.setLocalName(name) Imposta il nome del dispositivo in fase iniziale di annuncio
// begin initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }
  BLE.setLocalName("LED");
  // ...
  // start advertising
  BLE.advertise();
BLE.setDeviceName(name) Imposta il nome del dispositivo comecaratteristica predefinita. Se non impostato, il default è "Arduino".
// begin initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }
  BLE.setDeviceName("Arduino_LED");
  // ...
  // start advertising
  BLE.advertise();
BLE.advertise() Inizializza l'annuncio alla prima connessione. Restituisce 1 in caso di successo e 0 altrimenti.
 
BLE.central() Restituisce il nome del dispositivo connesso.
// listen for Bluetooth® Low Energy peripherals to connect:
  BLEDevice central = BLE.central();
  // if a central is connected to peripheral:
  if (central) {
    Serial.print("Connected to central: ");
    // print the central's MAC address:
    Serial.println(central.address());
  }
BLE.scan() Inizia la scansione per individuare dispositivi BLE che si annunciano
// begin initialization
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }
  Serial.println("BLE Central scan");
  // start scanning for peripheral
  BLE.scan();
  BLEDevice peripheral = BLE.available();
  if (peripheral) {
     // ...
  }
BLE.scanForName(name) Avvia la scansione dei dispositivi BLE pubblicizzati con un nome (locale) specifico. 1=OK, 0=KO
  if (!BLE.begin()) {
    Serial.println("starting Bluetooth® Low Energy module failed!");
    while (1);
  }
  Serial.println("BLE Central scan");
  // start scanning for peripheral
  BLE.scanForName("LED");
  BLEDevice peripheral = BLE.available();
  if (peripheral) {
     // ...
  }

Oggetto BLEDevice: metodi di interesse

ToDo...

 

Oggetto BLEService: metodi di interesse

ToDo...

 

Oggetto BLECharacteristic: metodi di interesse

ToDo...

Istruzione Descrizione Esempio
BLECharacteristic() Crea una caratteristica al servizio.

uuid: 16-bit or 128-bit UUID in String format
properties: mask of the properties (BLEBroadcast, BLERead, BLEWriteWithoutResponse, BLEWrite, BLENotify, BLEIndicate)
valueSize: (maximum) size of characteristic value
fixedLength: if true, size of characteristic value is fixed
stringValue: value as a string

Istruzione Tipo Esempio
BLECharacteristic(uuid, properties, valueSize)
 
BLECharacteristic(uuid, properties, valueSize, fixedLength)
 
BLECharacteristic(uuid, properties, valueSize)
 
BLEBoolCharacteristic(uuid, properties)

BLEBooleanCharacteristic(uuid, properties)

Caratteristica di tipo booleano
 
BLECharCharacteristic(uuid, properties)
 
BLEUnsignedCharCharacteristic(uuid, properties)
 
BLEByteCharacteristic(uuid, properties)
 
BLEShortCharacteristic(uuid, properties)
 
BLEUnsignedShortCharacteristic(uuid, properties)
 
BLEWordCharacteristic(uuid, properties)
 
BLEIntCharacteristic(uuid, properties)
 
BLEUnsignedIntCharacteristic(uuid, properties)
 
BLELongCharacteristic(uuid, properties)
 
BLEUnsignedLongCharacteristic(uuid, properties)
 
BLEFloatCharacteristic(uuid, properties)
 
BLEDoubleCharacteristic(uuid, properties)
 
 
x
 

Oggetto BLEDescriptor: metodi di interesse

ToDo...

 

Esempio 1

L'esempio seguente configura l'ESP32 come una periferica BLE con servizio "Serial" personalizzato.
Si è anche accodato l'invio di una stringa contatore ogni 2 secondi, questo ad esemplificare l'invio di dati dal microcontrollore verso il cellulare e non dal serial monitor del microcontrollore al cellulare.
NOTE:

  • l'uso del Monitor seriale è fondamentale in questo esempio, lo sketch non va avanti se non trova il monitor seriale --> while (!Serial);
  • All'apertura del Monitor seriale, scrivere comunque qualcosa anche se non si visualizza quanto si sta scrivendo ed infine prendete invio, il messaggio arriverà sullo smartphone.
#include <Arduino.h>
#include <ArduinoBLE.h>
 
long m_lastUpdateSmartPhone = 0;
int m_intCounter = 0;
String m_strCount = "";
 
// UUID generici per il servizio UART (Nordic style)
BLEService         m_uartService("6E400001-B5A3-F393-E0A9-E50E24DCCA9E"); 
BLEStringCharacteristic m_rxChar("6E400002-B5A3-F393-E0A9-E50E24DCCA9E", BLEWrite, 20);
BLEStringCharacteristic m_txChar("6E400003-B5A3-F393-E0A9-E50E24DCCA9E", BLENotify, 20);
BLEDescriptor m_DescripRX("2901", "Caratteristica: ricezione da smartphone (RX)");
BLEDescriptor m_DescripTX("2901", "Caratteristica: trasmissione vs smartphone (TX)");
 
void setup() {
  Serial.begin(9600);  
 
  // Configura il pin del LED blu come output
  pinMode(LED_BLUE, OUTPUT);
 
  //Si attende che la seriale sia pronta, con un timeout di 2 secondi
  //altrimenti se se ci fosse l'istruzione  while (!Serial) e non si avviasse 
  //il Monitor Seriale l'applicazione andrebbe in loop infinito
  unsigned long previousMillis = millis();
  unsigned long currentMillis = millis();
  while (!Serial 
    && (currentMillis - previousMillis <= 2000)) {	//timeout 2s per evitare di restare bloccati
      currentMillis = millis();
  }
 
  if (!BLE.begin()) {
    Serial.println("Errore nell'inizializzazione BLE!");
    while (1);
  }
 
  String address = BLE.address();
  Serial.println("Il MAC address di questo dispositivo è: [" + address + "]");
 
  // Nome del dispositivo BLE. Può essere visualizzato dagli smartphone durante la scansione dei dispositivi BLE.
  // E' sufficiente questo, non serve anche definire setDeviceName
  BLE.setLocalName("NanoESP32_Aino"); 
  BLE.setDeviceName("NanoESP32_Aino"); // Non è strettamente necessario se si è già definito setLocalName
  BLE.setAdvertisedService(m_uartService);
 
  // Le descrizioni sono opzionali, ma aiuta a identificare le caratteristiche quando si esplorano i servizi BLE con app come nRF Connect.
  m_rxChar.addDescriptor(m_DescripRX);
  m_txChar.addDescriptor(m_DescripTX);
 
  m_uartService.addCharacteristic(m_rxChar);
  m_uartService.addCharacteristic(m_txChar);
 
  BLE.addService(m_uartService);
 
  BLE.advertise(); // Inizia a pubblicizzare il servizio BLE
  Serial.println("Dispositivo BLE pronto. In attesa di connessione...");
 
  //Eventuali handler aggiuntivi per eventi BLE possono essere configurati qui, ad esempio:
  //  BLE.setEventHandler(BLEConnected, onBLEConnected);
  //  BLE.setEventHandler(BLEDisconnected, onBLEDisconnected);
}
 
void loop() {
  BLEDevice central = BLE.central();
 
  if (central) { // Questo perché il cellulare funge da central mentre il cellulare o altro dispositivo funge da peripheral
    digitalWrite(LED_BLUE, LOW); // Accende il LED blu con LOW perché è in logica inversa
 
    Serial.print("Connesso al dispositivo con MAC: "); Serial.print(central.address());    
    Serial.print("Potenza RSSI: "); Serial.println(central.rssi());
    if (central.hasLocalName()) {
      Serial.print("Nome Locale: "); Serial.println(central.localName());
    }
 
    while (central.connected()) {
      // 1. Dallo Smartphone al Monitor Seriale
      if (m_rxChar.written()) {
        Serial.print("Da smartphone: ");
        Serial.println(m_rxChar.value());
      }
 
      // 2.1 Dal Monitor Seriale allo Smartphone
      if (Serial.available()) {
        String msg = Serial.readStringUntil('\n') + "\n";
        m_txChar.writeValue(msg);
        Serial.print("Da Arduino: ");
        Serial.println(msg);
        delay(100); // Piccola pausa per evitare di sovraccaricare la comunicazione BLE
      }
 
      // 2.2 Sullo Smartphone ad intervalli regolari (ad esempio ogni 2 secondi) 
      if (millis() - m_lastUpdateSmartPhone > 2000) {
        m_lastUpdateSmartPhone = millis();
        m_intCounter++;
        m_strCount = "Contatore: " + String(m_intCounter) + "\n";
        m_txChar.writeValue(m_strCount);
        Serial.print("Contatore inviato: ");
        Serial.println(m_strCount);
      }
    }
 
    Serial.println("Disconnesso.");
    digitalWrite(LED_BLUE, HIGH); // Spegne il LED blu con HIGH perché è in logica inversa
  }
}

Libreria: Bluetooth Classic

Anche questa libreria è immediatamente disponibile sia su Arduino IDE che su VS Code con plugin PlatformIO.
Gli esempi si riferiscono all'uso della libreria "BluetoothSerial.h"

Esempio 1

Il seguente consente sia la lettura, dal Serial Monitor di Arduino al cellulare, che viceversa:

#include "BluetoothSerial.h"
 
BluetoothSerial SerialBT;
 
void setup() {
  Serial.begin(115200);
  SerialBT.begin("NanoESP32-Classic"); // Nome Bluetooth
  Serial.println("Dispositivo pronto per il pairing");
}
 
void loop() {
  if (Serial.available()) {
    SerialBT.write(Serial.read());
  }
  if (SerialBT.available()) {
    Serial.write(SerialBT.read());
  }
}

Per questa modalità, utilizza l'app "Serial Bluetooth Terminal", associa il dispositivo dalle impostazioni Bluetooth del telefono, poi connettilo nell'app.

Altro esempio per pilotare azioni sul dispositivo (NOTA, controlla se il bluetooth è abilitato! )
Il seguente sketch imposta il Nano ESP32 come server BLE che accende e spegne il LED integrato ed invia dati allo smartphone:

#include "BluetoothSerial.h"
 
// Controlla se il Bluetooth è abilitato
#if !defined(CONFIG_BT_ENABLED) || !defined(CONFIG_BLUEDROID_ENABLED)
#error Bluetooth is not enabled! Please run `make menuconfig` to and enable it
#endif
 
BluetoothSerial SerialBT;
 
void setup() {
  Serial.begin(115200);
  // Nome del dispositivo Bluetooth
  SerialBT.begin("NanoESP32_BT");
  Serial.println("Il dispositivo è pronto per l'accoppiamento");
  pinMode(LED_BUILTIN, OUTPUT); // LED integrato
}
 
void loop() {
  if (SerialBT.available()) {
    char incomingChar = SerialBT.read();
    Serial.write(incomingChar); // Echo sul monitor seriale del PC
 
    // Controllo LED con comandi
    if (incomingChar == '1') {
      digitalWrite(LED_BUILTIN, HIGH);
      SerialBT.println("LED Acceso");
    } else if (incomingChar == '0') {
      digitalWrite(LED_BUILTIN, LOW);
      SerialBT.println("LED Spento");
    }
  }
  delay(20);
}

(Mappa e Link)


Integrazioni tipiche | Arduino indice | Arduino Progetti


C++ Info fondamentali | ESP32 indice | ESP8266 | Domotica | Dizionario Elettronica | Elettronica | Elettronica Appunti


Parole chiave:

Author