Ciao! Qualche settimana fa abbiamo visto come installare BlueZ e bluepy sul computer Raspberry Pi 3, ed usufruire quindi del Bluetooth Low Energy. Oggi andremo ad instaurare una comunicazione Bluetooth Low Energy tra Raspberry Pi 3 e The Tactigon tramite uno script Python.
Lo sketch che andremo a caricare sulla nostra scheda The Tactigon verificherà la sua inclinazione (questa volta semplicemente analizzando l’accelerazione) e, ad un cambio di stato, invierà il comando tramite la caratteristica Bluetooth Low Energy. Lo script Python in esecuzione su Rasbperry Pi 3 B, alla ricezione del messaggio, ne effettuerà il parse per poi eseguire un’azione, nel nostro caso il cambio di stato di 4 GPIO.
Comunicazione Bluetooth Low Energy tra Raspberry Pi 3 e The Tactigon
Requisiti
Oggi avremo bisogno del nostro computer Raspberry Pi 3 con BlueZ e bluepy installati, come mostrato nell’articolo Bluetooth Low Energy con Raspberry Pi e Python pubblicato qualche settimana fa. Per programmare la scheda The Tactigon, invece, avremo bisogno solamente dell’IDE Arduino configurato come abbiamo visto nell’articolo dedicato.
Sketch
Come anticipato, The Tactigon verifica l’attuale posizione, e la comunica tramite Bluetooth Low Energy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | /** Questo sketch invia dei messaggi tramite **/ /** Bluetooth Low Energy in base all'inclinazione **/ /** Michele Valentini - Red Sheep Labs - www.yarosia.it **/ #include "tactigon_led.h" #include "tactigon_IMU.h" #include "tactigon_BLE.h" T_Led rLed, bLed, gLed; T_ACC accMeter; T_AccData accData; T_QUAT qMeter; T_QData qData; T_BLE bleManager; UUID uuid; T_BLE_Characteristic bleChar; //Ultimo stato inviato boolean lastInfZ = false; boolean lastSupZ = false; boolean lastInfY = false; boolean lastSupY = false; boolean lastInfX = false; boolean lastSupX = false; //Questa variabile viene utilizzata come "debounce" degli stati di inclinazione boolean justSent = true; //Soglia minima dell'accelerazione float threshold = 9.6; int ticks; //Usati per il controllo ciclico dei LEDs int ticksLed, stp; void setup() { ticks = 0; rLed.init(T_Led::RED); gLed.init(T_Led::GREEN); bLed.init(T_Led::BLUE); rLed.off(); gLed.off(); bLed.off(); bleManager.InitRole(TACTIGON_BLE_PERIPHERAL); //bleManager.setName("TTGPS"); //Aggiungiamo la caratteristica uuid.set("7ac71000-503d-4920-b000-acc000000001"); bleChar = bleManager.addNewChar(uuid, 2); } //Accende tutti e tre i colori void flash() { rLed.on(); gLed.on(); bLed.on(); } void loop() { //Buffer di trasmissione unsigned char buffData[2] = {0x00, 0x00}; //Eseguiamo il codice a 50Hz if (GetCurrentMilli() >= (ticksLed + (1000 / 50))) { //Aggiorniamo i valori di accelerazione accData = accMeter.getAxis(); //Se non sono stati appena inviati i dati if (!justSent) { //Verifico quale posizione è stata impostata if (accData.z > threshold && !lastSupZ) { lastSupZ = true; lastInfZ = false; lastInfX = false; lastSupX = false; lastInfY = false; lastSupY = false; buffData[0] = 0x00; buffData[1] = 0xFF; flash(); //bleChar.update(buffData); justSent = true; } if (accData.z < -threshold && !lastInfZ) { lastInfZ = true; lastSupZ = false; lastInfX = false; lastSupX = false; lastInfY = false; lastSupY = false; buffData[0] = 0xFF; buffData[1] = 0xFF; flash(); //bleChar.update(buffData); justSent = true; } if (accData.y > threshold && !lastSupY) { lastSupY = true; lastInfY = false; lastInfZ = false; lastSupZ = false; lastInfX = false; lastSupX = false; lastInfY = false; buffData[0] = 0x5A; buffData[1] = 0x5A; flash(); bleChar.update(buffData); justSent = true; } if (accData.y < -threshold && !lastInfY) { lastInfY = true; lastSupY = false; lastInfZ = false; lastSupZ = false; lastInfX = false; lastSupX = false; lastSupY = false; buffData[0] = 0x00; buffData[1] = 0x88; flash(); bleChar.update(buffData); justSent = true; } if (accData.x > threshold && !lastSupX) { lastSupX = true; lastInfX = false; lastInfZ = false; lastSupZ = false; lastInfX = false; lastInfY = false; lastSupY = false; buffData[0] = 0x00; buffData[1] = 0xBB; flash(); bleChar.update(buffData); justSent = true; } if (accData.x < -threshold && !lastInfX) { lastInfX = true; lastSupX = false; lastInfZ = false; lastSupZ = false; lastSupX = false; lastInfY = false; lastSupY = false; buffData[0] = 0x15; buffData[1] = 0x15; flash(); bleChar.update(buffData); justSent = true; } } } //Controllo ciclico dei LEDs if (GetCurrentMilli() >= (ticksLed + (1000 / 2))) { //Resettiamo il flag justSent justSent = false; ticksLed = GetCurrentMilli(); if (stp == 0) { rLed.on(); gLed.off(); bLed.off(); } else if (stp == 1) { rLed.off(); gLed.off(); bLed.on(); } else if (stp == 2) { rLed.off(); gLed.on(); bLed.off(); } stp = (stp + 1) % 3; } } |
Descrizione
La variabile:
boolean justSent = true; |
ha il compito di interrompere la comunicazione dei dati per un periodo di tempo (una sorta di debounce) successivo all’invio di un dato.
I dati inviati sono i seguenti:
- 5A5A
- 00FF
- FFFF
- 1515
- 0088
- 00BB
Script Python
Lo script python da eseguire su Raspberry Pi è il seguente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | #Script Python per la ricezione di comandi tramite #Bluetooth Low Energy da The Tactigon #Michele Valentini - Red Sheep Labs - www.yarosia.it import RPi.GPIO as GPIO import struct import binascii import bluepy from bluepy import btle #Configurazione dei GPIO GPIO.setwarnings(False) GPIO.setmode(GPIO.BOARD) gpios = [7, 11, 13, 15] GPIO.setup(gpios, GPIO.OUT, initial=GPIO.LOW) configA=[gpios[1],gpios[3]] configB=[gpios[0],gpios[2]] #Classe per gestione NOTIFICATION class MyDelegate(btle.DefaultDelegate): def __init__(self): btle.DefaultDelegate.__init__(self) #Analisi del messaggio e azione def handleNotification(self, cHandle, data): print("handleNotification()") val=binascii.b2a_hex(data) val=binascii.unhexlify(val) val = struct.unpack('>H', val)[0] if(val==0x5A5A): GPIO.output(configB, GPIO.HIGH) GPIO.output(configA, GPIO.LOW) print("Configuration B ON") elif(val==0x0088): GPIO.output(configA, GPIO.HIGH) GPIO.output(configB, GPIO.LOW) print("Configuration A ON") elif(val==0x00BB): GPIO.output(gpios, GPIO.HIGH) print("All ON") elif(val==0x1515): GPIO.output(gpios, GPIO.LOW) print("All OFF") #Impostazione device Bluetooth Low Energy #Ricordiamoci di modificare gli UUID di service e characteristic #oltre al MAC Address del nostro dispositivo The Tactigon tactigon_uuid = btle.UUID("7ac71000-503d-4920-b000-acc000000001") service_uuid = btle.UUID("bea5760d-503d-4920-b000-101e7306b000") p = btle.Peripheral("BE:A5:7F:2F:76:68") p.setDelegate(MyDelegate()) #Lista delle caratteristiche tactigonService = p.getServiceByUUID(service_uuid) #for ch in tactigonService.getCharacteristics(): #print str(ch) #Otteniamo il dato dalla caratteristica tactigonSensorValue = tactigonService.getCharacteristics(tactigon_uuid)[0] val = tactigonSensorValue.read() #print "Characteristic value: ", binascii.b2a_hex(val) #Impostiamo la NOTIFICATION notifyTactigon = tactigonService.getDescriptors(forUUID=0x2902)[0] notifyTactigon.write(struct.pack('<bb', 0x01, 0x00)) #Attendiamo NOTIFICATIONS try: while True: if p.waitForNotifications(1.0): continue except: print("Terminato...") |
Prima di tutto dobbiamo ricordarci di cambiare il MAC Address e gli UUID contenuti nel codice, in base a quello che abbiamo a disposizione o programmiamo nella scheda The Tactigon.
Lo script esegue la scansione dei device Bluetooth, una volta trovato The Tactigon (tramite MAC Address, appunto) verifica la presenza del servizio e quindi della caratteristica richiesta, e si sottoscrive alle notifiche. Non viene più eseguito il polling, ma viene lanciata la callback
def handleNotification(self, cHandle, data): |
se ci sono dei dati ricevuti.
Questa funzione appena definita andrà ad analizzare il messaggio ricevuto, e azionerà quindi i GPIO corretti.
Conclusioni
Questo piccolo script apre le porte al controllo di luci, apparecchi, o anche software tramite gesture, che in questo caso sono solamente posizioni della scheda The Tactigon, ma con un po di lavoro potrebbero diventare movimenti complessi catturati ed elaborati dalla scheda stessa.
Download
Qui è possibile scaricare il file sorgente dello sketch analizzato in questo articolo.