Visualizzazione post con etichetta BluetoothLe. Mostra tutti i post
Visualizzazione post con etichetta BluetoothLe. Mostra tutti i post

venerdì 14 gennaio 2022

Web bluetooth e ITag

Sto facendo un po' di esperimenti con il supporto bluetooth per browser in relazione ad dispositivo ITag (non quello di Apple...la versione cinese)


ITag Anti Lost smart bluetooth tracker

Il dispositivo espone alcuni servizi .. a me interessa il Custom Service che corrisponde ad una notifica legata alla pressione del pulsante




da questo link si osserva che l'UUID del servizio e della caratteristica non e' costante  tra i vari dispositivi che sono in vendita

#define SERVICE_UUID1 "0000ffe0-0000-1000-8000-00805f9b34fb"
#define SERVICE_UUID2 "0000fff0-0000-1000-8000-00805f9b34fb"

#define CHAR_UUID1 "0000ffe1-0000-1000-8000-00805f9b34fb"
#define CHAR_UUID2 "0000fff1-0000-1000-8000-00805f9b34fb"


Per accendere si preme il pulsante per 3 secondi e si sente un doppio beep, per spengere sempre 3 secondi di pulsante con lungo segnale acustico. Per alcune impostazione, come la disattivazione della suoneria si puo' usare la app Kindelf per Android ed IOS


Per prima cosa c'e' da dire che non e' una passeggiata ed il supporto delle API al momento e' molto limitato ...praticamente solo Chrome 


 In ogni caso anche per Chrome si deve abilitare esplicitamente le API tramite

chrome://flags/#enable-experimental-web-platform-features

nonostante avere abilitato la flag non ho trovato il sistema per rendere affidabile la comunicazione tra il pulsante e il computer...inoltre c'e' da segnalare che Chrome su IOS non ha la possibilita' di abilitare questo flag (non e' presente nella lista)

Utilizzando chrome://bluetooth-internals/#adapter si puo' verificare che non tutte le funzionalita' sono ancora implementate (se si apre il widget di ricerca del sistema operativo per fare una scansione il discovering da browser diventa rosso)



questa schermata e' stata fatta su un Lenovo T470 con RHEL 

Per i test ho trovato questa applicazione in ReactJS
Per installarla si inizia con 


git clone https://github.com/rdeprey/web-bluetooth-starter

npm install -g create-react-app

npm install --global yarn

npm install (per scaricare le librerie collegate)

npm start (per lanciare il programma)

per creare una versione da inserire in produzione si puo' digitare npm run build oppure yarn build (verra' creata una directory build in cui sono contenuti i file da mettere sul server di produzione)

Ho modificato la funzione di subscribe to notification per impostare il service e la characteristic sulla base del pulsante

const connectToDeviceAndSubscribeToUpdates = async () => {
try {
// Search for Bluetooth devices that advertise a battery service
const device = await navigator.bluetooth
.requestDevice({
filters: [{
namePrefix: "iTAG"
//services: ['battery_service']
}],
//0000FFF0-0000-1000-8000-00805F9B34FB
optionalServices: ["0000ffe0-0000-1000-8000-00805f9b34fb"]
});

setIsDisconnected(false);

// Add an event listener to detect when a device disconnects
device.addEventListener('gattserverdisconnected', onDisconnected);

// Try to connect to the remote GATT Server running on the Bluetooth device
const server = await device.gatt.connect();

// Get service from the Bluetooth device
const service = await server.getPrimaryService('0000ffe0-0000-1000-8000-00805f9b34fb');

// Get the characteristic from the Bluetooth device
const characteristic = await service.getCharacteristic('0000ffe1-0000-1000-8000-00805f9b34fb');

// Subscribe to notifications
characteristic.startNotifications();

// When the battery level changes, call a function
characteristic.addEventListener('characteristicvaluechanged',
handleCharacteristicValueChanged);
const reading = await characteristic.readValue();

// Show the initial reading on the web page
//setBatteryLevel(reading.getUint8(0));
} catch(error) {
console.log(`There was an error: ${error}`);
}
};

Una volta avviato il programma si riesce ad ottenere la prima notifica di pressione del pulsante e subito dopo viene generato l'errore


index.js:1 Uncaught Error: The error you provided does not contain a stack trace.

at M (index.js:1:1)

at ee (index.js:1:1)

at index.js:1:1

at index.js:1:1

at l (index.js:1:1)

M @ index.js:1

ee @ index.js:1

(anonymous) @ index.js:1

(anonymous) @ index.js:1

l @ index.js:1

localhost/:1 Uncaught (in promise) DOMException: GATT Error: Not supported


Il problema sembra legato alla funzione characteristic.startNotifications()). I valori possono essere letti dal dispositivo ma non possono essere ricevuti come notifica bluetooth
Peraltro il valore della characteristic della pressione pulsante rimane settata ad 1 dopo le pressione e non ritorna  a zero quando il pulsante viene rilasciato

Ho provato anche un pulsante di remote shutter per la video camera (AB Shutter 3 su Amazon) che funziona in modo differente..in pratica si comporta come un dispositivo HID  Bluetooth...in pratica una tastiera con un solo tasto...accoppiando il pulsante con GNOME il risultato e' che si attiva la funzione di alzare il volume e quindi non e' utile per lo scopo che mi ero prefisso






venerdì 14 maggio 2021

Esp32 Page Tuner Elgato Clone

Visto il precedente post volevo fare qualcosa di ancora piu' semplice e meno costoso

Ho provato ad usare come controller una ESP32...la prima idea era quella di usare la libreria BleKeyboard con dei tasti fisici per i controlli ma alla fine avevo bisogno di un po' di saldature...volevo qualcosa di ancora piu' semplice...ho quindi usato i pin touch della Esp32 che necessitano solo del contatto di un dito per essere usati come interrutori






Nell'esempio i pin Touch4 e Touch5 sono associati al tasto spazio e Freccia a sinistra per abilitare la funzione Play e Rewind su Youtube e VLC

======================================================

#include <BleKeyboard.h>
#define interval 1000

BleKeyboard bleKeyboard;
int threshold = 40;
bool touch4detected = false;
bool touch5detected = false;
long previousMills = 0; 
void gotTouch5(){ 
 if (millis()-previousMills > interval)
    {
    touch5detected = true;
    previousMills = millis();
    }
}
void gotTouch4(){ 
 if (millis()-previousMills > interval)
    {
    touch4detected = true;
    previousMills = millis();
    }
}
void setup() {
  bleKeyboard.begin();
  delay(1000);
  while (!bleKeyboard.isConnected()) {
    Serial.println("in attesa");
  }
  
  Serial.begin(115200);
  delay(1000);
  
  touchAttachInterrupt(T4, gotTouch4, threshold);
  touchAttachInterrupt(T5, gotTouch5, threshold);
}
void loop(){
if(touch4detected){
  touch4detected = false;
  Serial.println("Play"); 
  bleKeyboard.print(" ");
  }
if(touch5detected){
  touch5detected = false;
  Serial.println("Rewind"); 
  bleKeyboard.write(KEY_LEFT_ARROW );
  }
}

lunedì 27 gennaio 2020

Mobike e BLE

Mentre stavo giocherellando con lo scanner Bluetooth LE mi e' apparso in lista un dispositivo Mobike ed ho visto che era stata appena parcheggiata una bicicletta a noleggio fuori dall'ufficio. Avevo sempre pensato che le Mobike fossero basate tutte su comunicazioni dati su rete cellulare ma a quanto sembra non e' proprio cosi..di fatto vengono esposte due servizi di uno scrivibile via BLE




c'e ovviamente chi ha fatto un'analisi decisamente piu' apprfondita

https://gsec.hitb.org/materials/sg2018/D2%20-%20Hacking%20BLE%20Bicycle%20Locks%20for%20Fun%20and%20(a%20Small)%20Profit%20-%20Vincent%20Tan.pdf

e c'e' anche chi ha smontato un lucchetto Mobike trovandoci dentro un modulo telefonico, un GPS ed un processore STM32

https://projectgus.com/2017/04/sharing-bikes-in-shanghai/

In ogni caso la gran parte del lavoro di scambio dati per lo sblocco e' svolto dal telefono del cliente
Sarei curioso di sapere come e' stato risolto da Mobike il problema di alimentare un sistema cosi' energivoro (GPS+Cellulare possono drenare in modo molto veloce le batterie)

BLE Write Characteristic da Android

Dopo il precedente tentativo di leggere una caratteristica BLE da Python adesso un tentativo di scrivere una caratteristica (lo so e' una brutta traduzione) da Android su un dispositivo BLE

Lo sketch Arduino e' stato modificato per esporre una caratteristica 2A30 in modalita' lettura/scrittura, settare il valore a 3, leggere il valore e se e' differente da 3 spengere il Led di Arduino

=================================================================
#include <ArduinoBLE.h>

BLEService numero("180F");
BLEUnsignedCharCharacteristic casuale("2A19",BLERead | BLENotify); 
BLEUnsignedCharCharacteristic scrittura("2A30",BLERead | BLEWrite); 


long previousMillis = 0;  
byte test =3 ;

void setup() {
  Serial.begin(9600);  
  while (!Serial);
  pinMode(LED_BUILTIN, OUTPUT); 
  if (!BLE.begin()) {
    while (1);
  }

  BLE.setLocalName("Random");
  BLE.setAdvertisedService(numero); 
  numero.addCharacteristic(casuale);
  numero.addCharacteristic(scrittura);
  
  BLE.addService(numero); 
  casuale.writeValue(0); // set initial value for this characteristic
  scrittura.writeValue(2);
  BLE.advertise();
}

void loop() {
  int i = 0;
  BLEDevice central = BLE.central();
  if (central) {
    while (central.connected()) {
      long currentMillis = millis();
      if (currentMillis - previousMillis >= 20) {
        previousMillis = currentMillis;
        scrittura.readValue(test);
        if(test ==3)
            {
            Serial.println(test);
            digitalWrite(LED_BUILTIN, HIGH);
            }
            else
            {
            Serial.println(test);
            digitalWrite(LED_BUILTIN, LOW);
            }
            
      }
    }
    digitalWrite(LED_BUILTIN, LOW);    
  }
}

=================================================================

Per Android ho usato la libreria a questo indirizzo . Ne esistono di piu' complete ma
in questo caso avevo bisogno di semplicita'
=================================================================
package com.ble.ellamma.ble;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import com.ble.ellamma.bleand.Ble;
import java.util.UUID;

public class MainActivity extends AppCompatActivity {

    private static final String MAC_ADDRESS = "ED:CB:86:2A:68:C1";    private static final UUID BLE_NOTIFY_CHARACTER =  UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb");    //.00002a19-0000-1000-8000-00805f9b34fb--18
    private static final UUID BLE_SERVICE = UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb");
    private static final UUID CLIENT_CCD =  UUID.fromString("00002a30-0000-1000-8000-00805f9b34fb");

    ArrayList<String> getDevicess;    List<BluetoothDevice> getConnectedDevicess;


    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);        
        setContentView(R.layout.activity_main);
        getDevicess = new ArrayList<>();        
        getConnectedDevicess = new ArrayList<>();
        Ble ble = new Ble(this,getApplicationContext(),".Myreceiver");
        ble.enableBle();        
        //ble.scanLeDevice(true,1000);        
        //ble.getDevices();

        ble.connectBLE(MAC_ADDRESS);        
        byte dati[] = "A".getBytes();        
        ble.writeCharacteristics(dati,BLE_SERVIVE,CLIENT_CCD,true);        
        ble.disConnectBLE();
    }

    public void onReceive(Context context, Intent intent) {
        //String data = intent.getStringExtra("data");        
        //Log.w("Luca", "Request Recieved On MyReciever ::" + data);
    }

}





giovedì 23 gennaio 2020

BLE Arduino Nano 33

Un eesmpio di scambio dati via Bluetooth LE tra una scheda Arduino Nano 33 BLE ed un PC



La Arduino Nano BLE 33 e' una scheda interessante perche' oltre al Bluetooth LE sono disponibili una IMU (LMS9DS1), un microfono digitale (MP34DT05), un sensore di colore e prossimita' (APDS9960), un sensore di pressione atmosferica (LPS22HB), un sensore di umidita' relativa e temperatura (HTS221)

Lo sketch su Arduino apre un servizio su BLE con numero 180F ed una caratteristica 2A19 in cui vengono salvati i dati di un contatore progressivo da 0 a 255 (questo per fare in modo che sul lato PC si sia in grado di vedere se si perdono pacchetti)

Lato Arduino
================================================
#include <ArduinoBLE.h>

BLEService numero("180F");
BLEUnsignedCharCharacteristic casuale("2A19",BLERead | BLENotify); 

long previousMillis = 0;  

void setup() {
  Serial.begin(9600);  
  while (!Serial);
  pinMode(LED_BUILTIN, OUTPUT); 
  if (!BLE.begin()) {
    while (1);
  }

  BLE.setLocalName("Random");
  BLE.setAdvertisedService(numero); 
  numero.addCharacteristic(casuale); 
  BLE.addService(numero); 
  casuale.writeValue(0); // set initial value for this characteristic
  BLE.advertise();
}

void loop() {
  int i = 0;
  BLEDevice central = BLE.central();
  if (central) {
    digitalWrite(LED_BUILTIN, HIGH);
    while (central.connected()) {
      long currentMillis = millis();
      if (currentMillis - previousMillis >= 20) {
        i++;
        casuale.writeValue(i%255);
        previousMillis = currentMillis;
      }
    }
    digitalWrite(LED_BUILTIN, LOW);    
  }
}
================================================

Per verificare che i dati vengano inviati correttamente si puo' usare gattool

================================================
gatttool -b ED:CB:86:2A:68:C1 -I
[ED:CB:86:2A:68:C1][LE]> connect
Attempting to connect to ED:CB:86:2A:68:C1
Connection successful
[ED:CB:86:2A:68:C1][LE]> char-read-uuid 2A19
handle: 0x000b value: 41
================================================

A questo punto con la libreria Bluepy si possono leggere i dati inviati dalla Arduino

================================================
from bluepy import btle
from bluepy.btle import UUID, Peripheral

addr = "ED:CB:86:2A:68:C1"
conn = Peripheral(addr, "public")

print ("-Servizi")
services = conn.getServices()
for service in services:
print(service.uuid)


print("-Caratteristica")
charac_dic = service.getCharacteristics()
for charac in charac_dic:
print(charac.uuid)
if charac.uuid == "2a19":
print ("Trovata")
Data_char = charac
print(Data_char)
Data_handle = Data_char.getHandle()
print Data_handle
while True:
print (ord(charac.read()))
================================================

la trasmissione dati in questo modo e' piuttosto lenta..circa un dato ogni decimo di secondo.
Usando la tecnica di subscribe and notify la Arduino invia i dati al PC senza la necessita' per quest'ultimo di richiederli. Con questa tecnica non si perde nessun pacchetto

================================================
from bluepy import btle
from bluepy.btle import UUID, Peripheral
import struct

def listAll(p):
for svc in p.getServices():
print(svc.uuid.getCommonName())
for ch in svc.getCharacteristics():
print(" " + str(ch.valHandle) + ": " + ch.uuid.getCommonName())

class MyDelegate(btle.DefaultDelegate):
    def __init__(self):
        btle.DefaultDelegate.__init__(self)

    def handleNotification(self, cHandle, data):
print(ord(data))

addr = "ED:CB:86:2A:68:C1"
p = btle.Peripheral(addr,"public")
services=p.getServices()
for service in services:
   print(service.uuid)

p.setDelegate( MyDelegate() )

listAll(p)

svc = p.getServiceByUUID("0000180f-0000-1000-8000-00805f9b34fb")
ch = svc.getCharacteristics()[0]
p.writeCharacteristic(ch.valHandle+1, b'\x01\x00', withResponse=True)

while True:
    if p.waitForNotifications(1.0):
        continue
    print "Waiting..."

martedì 21 agosto 2018

Bluetooth LE cached name scan

Lavorando ad un progetto in cui un beacon BLE cambia spesso il proprio nome ho scoperto che la funzione ScanResult (vedi qui per esempio) non aggiorna i nomi dei beacon una volta scoperti (come se avesse una cache interna). Usando invece la funzioen ScanRecord, come nel codice sottostante, tutto funziona in modo corretto


-----------------------------
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="com.blescan.lucainnocenti.blescan">

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>



    <uses-feature        android:name="android.hardware.bluetooth_le"        android:required="true" />

    <application        android:allowBackup="true"        android:icon="@mipmap/ic_launcher"        android:label="@string/app_name"        android:roundIcon="@mipmap/ic_launcher_round"        android:supportsRtl="true"        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
-----------------------------


-----------------------------
package com.blescan.lucainnocenti.blescan;

import android.Manifest;
import android.app.AlertDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanRecord;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    private BluetoothAdapter mBluetoothAdapter  = null;
    private BluetoothLeScanner mBluetoothLeScanner = null;

    public static final int REQUEST_BT_PERMISSIONS = 0;
    public static final int REQUEST_BT_ENABLE = 1;

    private boolean mScanning = false;
    private Handler mHandler = null;

    private Button btnScan = null;



    private ScanCallback mLeScanCallback =
            new ScanCallback() {

                @Override                public void onScanResult(int callbackType, final ScanResult result) {
                    super.onScanResult(callbackType, result);
                    final ScanRecord scanRecord = result.getScanRecord();


                    if (scanRecord.getDeviceName() != null) {
                        Log.d("BLE", scanRecord.getDeviceName());
                    }

                }

                @Override                public void onScanFailed(int errorCode) {
                    super.onScanFailed(errorCode);
                    Log.d("BLE", "error");
                }
            };






    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnScan = (Button) findViewById(R.id.btnScan);

        new Handler().postDelayed(new Runnable() {
            @Override            public void run() {
                if (mScanning){
                    mScanning = false;
                    scanLeDevice(false);
                    btnScan.setText("STOP");
                } else {
                    mScanning = true;
                    scanLeDevice(true);
                    btnScan.setText("SCAN");
                }
            }
        }, 30000);




        btnScan.setOnClickListener(new View.OnClickListener() {
            @Override            public void onClick(View view) {
                onBtnScan();
            }
        });
        this.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        this.mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        this.mHandler = new Handler();

        checkBtPermissions();
        enableBt();
    }

    public void onBtnScan(){
        if (mScanning){
            mScanning = false;
            scanLeDevice(false);
            btnScan.setText("STOP");
        } else {
            mScanning = true;
            scanLeDevice(true);
            btnScan.setText("SCAN");
        }
    }

    public void checkBtPermissions() {
        this.requestPermissions(
                new String[]{
                        Manifest.permission.BLUETOOTH, Manifest.permission.BLUETOOTH_ADMIN                },
                REQUEST_BT_PERMISSIONS);
    }

    public void enableBt(){
        if (mBluetoothAdapter == null) {
            // Device does not support Bluetooth        }
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableBtIntent, REQUEST_BT_ENABLE);
        }
    }

    public void scanLeDevice(final boolean enable) {
        // ScanSettings mScanSettings = new ScanSettings.Builder().setScanMode(ScanSettings.SCAN_MODE_BALANCED).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).build();
        if (enable) {
            mScanning = true;
            Log.i("Scanning", "start");
            mBluetoothLeScanner.startScan(mLeScanCallback);
        } else {
            Log.i("Scanning", "stop");
            mScanning = false;
            mBluetoothLeScanner.stopScan(mLeScanCallback);
        }
    }
-----------------------------

mercoledì 24 agosto 2016

Bluetooth non e' BluetoothLE

Ho dovuto imparare (letteralmente a mie spese) una lezione ovvero che, nonostante il mio nome simile, Bluetooth 2.0 e BluetoothLe (o Bluetooth 4.0) non sono la stessa cosa e non hanno compatibilita' diretta


Il problema deriva dal fatto che volevo convertire un progetto Arduino Bluetooth standard realizzato con un modulo HC-05 in qualcosa di piu' tascabile ed integrato e mi sono comprato una scheda Blend Micro ed una Genuino 101 (la prima come hardware definitivo, la seconda come piattaforma di prova)

Solo dopo ho scoperto che BT 2.0 non e' compatibile nemmeno a livello hardware con BT 4.0...di fatto i telefoni che hanno il doppio protocollo utilizzano di fatto due sistemi radio distinti ed il chip Nordic nRF8001 permette solo BTLe...in pratica dei soldi buttati ...almeno per questo progetto


Geologi

  E so anche espatriare senza praticamente toccare strada asfaltata