Bluetooth Low Energy Shield – Bleshield

La Bleshield: Bluetooth Low Energy Shield è una shield prodotta da redbearlab che ti permette di controllare fino a 6 pin digitali e 6 analogici con il bluetooth dal tuo smartphone.

bleshield project - Bluetooth Low Energy Shield

E’ la prima volta che scrivo di un progetto bluetooth fatto da me, in passato ho pubblicato alcuni progetti fatti da appassionati, ma io in prima persona non ho mai provato questa tecnologia fino ad oggi.

C’è sempre una prima volta ed il 90% delle volte è molto divertente !!!

La Bleshield potrei definirla un progetto completo in quanto il produttore rilascia tutta la documentazione completa per poterla utilizzare, le librerie, gli sketch di esempio e, sopratutto, le App per i due principali sistemi operativi.

bleshield app iPhone

Ho testato la shield in pochi minuti utilizzando appunto uno degli esempi forniti.

Documentazione di montaggio e installazione della Bluetooth Low Energy Shield

Puoi trovare una dettagliatissima descrizione della Bluetooth Low Energy Shield sul sito del produttore che ha rilasciato un interessante guida passo passo anche per l’installazione delle librerie necessari al funzionamnto con arduino della bleshield.

Il cablaggio del progetto usato come test

Per verificare che tutti i 6 pin potessero essere facilmente controllati dall’app per iPhone ( possiedo solo un iPhone con cui fare i test, ma se vuoi pubblicare il tuo test con Android scrivimelo nei commenti )

ho realizzato questo semplice schema di collegamento in cui i pin dal 2 al 7 della Bluetooth Low Energy Shield sono connessi ciascuno ad un led da 5mm:

bleshield project alto Bluetooth Low Energy Shield

in questo modo dal cellulare l’accensione del pin 2 corrisponderà all’accensione del primo led ed a seguire fino al pin 7 a cui corrisponde il sesto led.

Non penso sia necessario uno schema per replicare questo circuito data la sua banalità, tuttavia ho realizzato una foto ravvicinata del cablaggio dei pin usati:

bleshield connessioni

Lo sketch di esempio

Per il test con l’App scaricata gratuitamente dall’Apple Store puoi comunicare subito con la tua Bluetooth Low Energy Shield semplicemente usando lo sketch di esempio fornito:

/*

Copyright (c) 2012, 2013 RedBearLab

Permission is hereby granted, free of charge, to any person 
obtaining a copy of this software and associated documentation 
files (the "Software"), to deal in the Software without restriction, 
including without limitation the rights to use, copy, modify, merge, 
publish, distribute, sublicense, and/or sell copies of the Software, 
and to permit persons to whom the Software is furnished to do so, 
subject to the following conditions:

The above copyright notice and this permission notice shall be included 
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 
OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/

#include <Servo.h>
#include <SPI.h>
#include <boards.h>
#include <RBL_nRF8001.h>
#include <services.h>
#include "Boards.h"

#define PROTOCOL_MAJOR_VERSION   0 //
#define PROTOCOL_MINOR_VERSION   0 //
#define PROTOCOL_BUGFIX_VERSION  2 // bugfix

#define PIN_CAPABILITY_NONE      0x00
#define PIN_CAPABILITY_DIGITAL   0x01
#define PIN_CAPABILITY_ANALOG    0x02
#define PIN_CAPABILITY_PWM       0x04
#define PIN_CAPABILITY_SERVO     0x08
#define PIN_CAPABILITY_I2C       0x10

// pin modes
//#define INPUT                 0x00 // defined in wiring.h
//#define OUTPUT                0x01 // defined in wiring.h
#define ANALOG                  0x02 // analog pin in analogInput mode
#define PWM                     0x03 // digital pin in PWM output mode
#define SERVO                   0x04 // digital pin in Servo output mode

byte pin_mode[TOTAL_PINS];
byte pin_state[TOTAL_PINS];
byte pin_pwm[TOTAL_PINS];
byte pin_servo[TOTAL_PINS];

Servo servos[MAX_SERVOS];

void setup()
{
  Serial.begin(57600);
  Serial.println("BLE Arduino Slave");
  
  /* Default all to digital input */
  for (int pin = 0; pin < TOTAL_PINS; pin++)
  {
    // Set pin to input with internal pull up
    pinMode(pin, INPUT);
    digitalWrite(pin, HIGH);

    // Save pin mode and state
    pin_mode[pin] = INPUT;
    pin_state[pin] = LOW;
  }

  // Default pins set to 9 and 8 for REQN and RDYN
  // Set your REQN and RDYN here before ble_begin() if you need
  //ble_set_pins(3, 2);
  
  // Set your BLE Shield name here, max. length 10
  //ble_set_name("My Name");
  
  // Init. and start BLE library.
  ble_begin();
}

static byte buf_len = 0;

void ble_write_string(byte *bytes, uint8_t len)
{
  if (buf_len + len > 20)
  {
    for (int j = 0; j < 15000; j++)
      ble_do_events();
    
    buf_len = 0;
  }
  
  for (int j = 0; j < len; j++)
  {
    ble_write(bytes[j]);
    buf_len++;
  }
    
  if (buf_len == 20)
  {
    for (int j = 0; j < 15000; j++)
      ble_do_events();
    
    buf_len = 0;
  }  
}

byte reportDigitalInput()
{
  if (!ble_connected())
    return 0;

  static byte pin = 0;
  byte report = 0;
  
  if (!IS_PIN_DIGITAL(pin))
  {
    pin++;
    if (pin >= TOTAL_PINS)
      pin = 0;
    return 0;
  }
  
  if (pin_mode[pin] == INPUT)
  {
      byte current_state = digitalRead(pin);
            
      if (pin_state[pin] != current_state)
      {
        pin_state[pin] = current_state;
        byte buf[] = {'G', pin, INPUT, current_state};
        ble_write_string(buf, 4);
        
        report = 1;
      }
  }
  
  pin++;
  if (pin >= TOTAL_PINS)
    pin = 0;
    
  return report;
}

void reportPinCapability(byte pin)
{
  byte buf[] = {'P', pin, 0x00};
  byte pin_cap = 0;
                    
  if (IS_PIN_DIGITAL(pin))
    pin_cap |= PIN_CAPABILITY_DIGITAL;
            
  if (IS_PIN_ANALOG(pin))
    pin_cap |= PIN_CAPABILITY_ANALOG;

  if (IS_PIN_PWM(pin))
    pin_cap |= PIN_CAPABILITY_PWM;

  if (IS_PIN_SERVO(pin))
    pin_cap |= PIN_CAPABILITY_SERVO;

  buf[2] = pin_cap;
  ble_write_string(buf, 3);
}

void reportPinServoData(byte pin)
{
//  if (IS_PIN_SERVO(pin))
//    servos[PIN_TO_SERVO(pin)].write(value);
//  pin_servo[pin] = value;
  
  byte value = pin_servo[pin];
  byte mode = pin_mode[pin];
  byte buf[] = {'G', pin, mode, value};         
  ble_write_string(buf, 4);
}

byte reportPinAnalogData()
{
  if (!ble_connected())
    return 0;
    
  static byte pin = 0;
  byte report = 0;
  
  if (!IS_PIN_DIGITAL(pin))
  {
    pin++;
    if (pin >= TOTAL_PINS)
      pin = 0;
    return 0;
  }
  
  if (pin_mode[pin] == ANALOG)
  {
    uint16_t value = analogRead(pin);
    byte value_lo = value;
    byte value_hi = value>>8;
    
    byte mode = pin_mode[pin];
    mode = (value_hi << 4) | mode;
    
    byte buf[] = {'G', pin, mode, value_lo};         
    ble_write_string(buf, 4);
  }
  
  pin++;
  if (pin >= TOTAL_PINS)
    pin = 0;
    
  return report;
}

void reportPinDigitalData(byte pin)
{
  byte state = digitalRead(pin);
  byte mode = pin_mode[pin];
  byte buf[] = {'G', pin, mode, state};         
  ble_write_string(buf, 4);
}

void reportPinPWMData(byte pin)
{
  byte value = pin_pwm[pin];
  byte mode = pin_mode[pin];
  byte buf[] = {'G', pin, mode, value};         
  ble_write_string(buf, 4);
}

void sendCustomData(uint8_t *buf, uint8_t len)
{
  uint8_t data[20] = "Z";
  memcpy(&data[1], buf, len);
  ble_write_string(data, len+1);
}

byte queryDone = false;

void loop()
{
  while(ble_available())
  {
    byte cmd;
    cmd = ble_read();
    Serial.write(cmd);
    
    // Parse data here
    switch (cmd)
    {
      case 'V': // query protocol version
        {
          byte buf[] = {'V', 0x00, 0x00, 0x01};
          ble_write_string(buf, 4);
        }
        break;
      
      case 'C': // query board total pin count
        {
          byte buf[2];
          buf[0] = 'C';
          buf[1] = TOTAL_PINS; 
          ble_write_string(buf, 2);
        }        
        break;
      
      case 'M': // query pin mode
        {  
          byte pin = ble_read();
          byte buf[] = {'M', pin, pin_mode[pin]}; // report pin mode
          ble_write_string(buf, 3);
        }  
        break;
      
      case 'S': // set pin mode
        {
          byte pin = ble_read();
          byte mode = ble_read();
          
          if (IS_PIN_SERVO(pin) && mode != SERVO \
                                && servos[PIN_TO_SERVO(pin)].attached())
            servos[PIN_TO_SERVO(pin)].detach();
  
          /* ToDo: check the mode is in its capability or not */
          /* assume always ok */
          if (mode != pin_mode[pin])
          {              
            pinMode(pin, mode);
            pin_mode[pin] = mode;
          
            if (mode == OUTPUT)
            {
              digitalWrite(pin, LOW);
              pin_state[pin] = LOW;
            }
            else if (mode == INPUT)
            {
              digitalWrite(pin, HIGH);
              pin_state[pin] = HIGH;
            }
            else if (mode == ANALOG)
            {
              if (IS_PIN_ANALOG(pin)) {
                if (IS_PIN_DIGITAL(pin)) {
                  pinMode(PIN_TO_DIGITAL(pin), LOW);
                }
              }
            }
            else if (mode == PWM)
            {
              if (IS_PIN_PWM(pin))
              {
                pinMode(PIN_TO_PWM(pin), OUTPUT);
                analogWrite(PIN_TO_PWM(pin), 0);
                pin_pwm[pin] = 0;
                pin_mode[pin] = PWM;
              }
            }
            else if (mode == SERVO)
            {
              if (IS_PIN_SERVO(pin))
              {
                pin_servo[pin] = 0;
                pin_mode[pin] = SERVO;
                if (!servos[PIN_TO_SERVO(pin)].attached())
                  servos[PIN_TO_SERVO(pin)].attach(PIN_TO_DIGITAL(pin));
              }
            }
          }
            
  //        if (mode == ANALOG)
  //          reportPinAnalogData(pin);
          if ( (mode == INPUT) || (mode == OUTPUT) )
            reportPinDigitalData(pin);
          else if (mode == PWM)
            reportPinPWMData(pin);
          else if (mode == SERVO)
            reportPinServoData(pin);
        }
        break;

      case 'G': // query pin data
        {
          byte pin = ble_read();
          reportPinDigitalData(pin);
        }
        break;
        
      case 'T': // set pin digital state
        {
          byte pin = ble_read();
          byte state = ble_read();
          
          digitalWrite(pin, state);
          reportPinDigitalData(pin);
        }
        break;
      
      case 'N': // set PWM
        {
          byte pin = ble_read();
          byte value = ble_read();
          
          analogWrite(PIN_TO_PWM(pin), value);
          pin_pwm[pin] = value;
          reportPinPWMData(pin);
        }
        break;
      
      case 'O': // set Servo
        {
          byte pin = ble_read();
          byte value = ble_read();

          if (IS_PIN_SERVO(pin))
            servos[PIN_TO_SERVO(pin)].write(value);
          pin_servo[pin] = value;
          reportPinServoData(pin);
        }
        break;
      
      case 'A': // query all pin status
        for (int pin = 0; pin < TOTAL_PINS; pin++)
        {
          reportPinCapability(pin);
          if ( (pin_mode[pin] == INPUT) || (pin_mode[pin] == OUTPUT) )
            reportPinDigitalData(pin);
          else if (pin_mode[pin] == PWM)
            reportPinPWMData(pin);
          else if (pin_mode[pin] == SERVO)
            reportPinServoData(pin);  
        }
        
        queryDone = true; 
        {
          uint8_t str[] = "ABC";
          sendCustomData(str, 3);
        }
       
        break;
          
      case 'P': // query pin capability
        {
          byte pin = ble_read();
          reportPinCapability(pin);
        }
        break;
        
      case 'Z':
        {
          byte len = ble_read();
          byte buf[len];
          for (int i=0;i<len;i++)
            buf[i] = ble_read();
          Serial.println("->");
          Serial.print("Received: ");
          Serial.print(len);
          Serial.println(" byte(s)");
          Serial.print(" Hex: ");
          for (int i=0;i<len;i++)
            Serial.print(buf[i], HEX);
          Serial.println();
        }
    }

    // send out any outstanding data
    ble_do_events();
    buf_len = 0;
    
    return; // only do this task in this loop
  }

  // process text data
  if (Serial.available())
  {
    byte d = 'Z';
    ble_write(d);

    delay(5);
    while(Serial.available())
    {
      d = Serial.read();
      ble_write(d);
    }
    
    ble_do_events();
    buf_len = 0;
    
    return;    
  }

  // No input data, no commands, process analog data
  if (!ble_connected())
    queryDone = false; // reset query state
    
  if (queryDone) // only report data after the query state
  { 
    byte input_data_pending = reportDigitalInput();  
    if (input_data_pending)
    {
      ble_do_events();
      buf_len = 0;
      
      return; // only do this task in this loop
    }
  
    reportPinAnalogData();
    
    ble_do_events();
    buf_len = 0;
    
    return;  
  }
    
  ble_do_events();
  buf_len = 0;
}

lo sketch è in grado di gestire sia segnali di input digitali o analogici sia output digitali e PWM per controllare i servo per cui nella prime linee 026-031: includi tutte le librerie di cui hai bisogno per interpretare i comandi che giungono dallo smartphone.

Lo sketch è molto lungo, circa 481 linee, per cui eviterò di annoiarti con tutte le linee, tanto non le leggeresti, mi soffermerò sulle principali:

linee 033-054: imposti tutte le costanti necessarie al corretto funzionamento dello sketch e della comunicazione con la Bluetooth Low Energy Shield;

linee 064-073: per tutti pin arduino imposta la modalità INPUT, linea 067, attiva la resistenza di pull-up, linea 068, e porta lo stato del pin a LOW, linea 072;

linea 083: instanzia la classe ble con il comando “ble_begin()”

linea 246: inizia un ciclo while la cui condizione è che arrivi una comunicazione bluetooth, questa linea ricorda molto quelle che ho usato negli esempi con XBee in cui aspetti che la comunicazione xbee-serial arrivi ad arduino. In fondo la comunicazione bluetooth è di tipo seriale come la comunicazione xbee;

linee 248-250: imposti la variabile cmd di tipo byte, leggi il primo valore che arriva dal buffer bluetooth e scrivi sul monitor seriale il comando ricevuto;

linea 253: imposta un controllo basato sul comando “switch” che verifichi il comando ricevuto sottoforma di lettera come: V,C,M,S,G,T,N,O,A,P e Z ciascuna delle quali attiva dei comandi su arduino e sulla Bluetooth Low Energy Shield;

linee 255-260: se il cmd contiene la lettera “V” lo sketch predispone una variabile buf di 4 byte in cui scrive: “V”,0x00,0x00,0x01 ed alla linea successiva invia questa informazione al client connesso via Bluetooth;

linee 262-277: eseguono comandi del tutto simili a quelli visti nelle linee 255-260 ma per le lettere C ed M;

linea 279: se il cmd ricevuto è “S” lo sketch sa che deve impostare ( Setting – abbreviato Set ) l’uso di un pin in una modalità e tipologia che gli sarà inviato dal client;

linea 281: legge il buffer bluetooth per apprendere quale pin vuoi che sia impostato;

linea 282: legge, nuovamente, il buffer bluetooth per impostare la modalità;

linee 284-286: esegui un controllo molto importante, verifichi che il pin ricevuto sia di tipo SERVO ( è una modalità definita lato app o client ) e che la modalità non sia SERVO ed infine che esista un servo attached ( collegato ) al pin in questione; in questo caso provvede ad eseguire il detach;

linee 290-333: verifichi la modalità in cui vuoi impostare il pin inviato e se differente da quella precedentemente impostata provvedi alla modifica;

linee 337-343: se il pinMode è impostato in una delle seguenti modalità: INPUT, OUTPUT, PWM o SERVO richiama una funzione specifica per ciascuna modalità;

linee 346-351: in caso di cmd “G” legge e restituisce il valore digitale letto sul pin corrispondente. La modalità di acquisizione del pin è la medesima già vista fino a questo punto;

linee 353-384: imposta lo stato del pin se riceve “T” o “N” od “O” rispettivamente per i pin di tipo digitale, pwm e servo;

linee 386-404: leggi e restituisci lo stato di tutti i pin;

linee 406-411: restituisce la capability del pin richiesto;

linee 413-427: in caso di cmd “Z” scrive sul monitor seriale i byte ricevuti e l’Hex ( valore esadecimale ) ricevuto subito dopo il comando;

linea 431: esegue il comando di invio dati impostato in una delle opzioni precedenti;

linea 432: ripulisce la variabile di buf_len;

linee 438-454: accetta valori dal monitor seriale e li strasferisce via bluetooth al client ( smartphone ).

Avrai notato che ci sono delle altre funzioni accessorie che sono richiamate dal ciclo loop() principale e che non ho, volutamente, decsritto in quanto eseguono semplici operazioni su arduino restituendo il risultato alla Bluetooth Low Energy Shield attraverso i comandi ble_

Il video del test

Ho realizzato un video durante il mio test con 6 led collegati alla Bluetooth Low Energy Shield e con l’App per iPhone:

Cortesemente, prima di inserire i commenti leggi il regolamento

Permanent link to this article: http://www.mauroalfieri.it/elettronica/bluetooth-low-energy-shield-bleshield.html

2 comments

1 ping

    • Roberto on 27 aprile 2015 at 12:13
    • Reply

    Salve molto bello ed interessante questo progetto ma come posso modificarlo per accendere soli 2 led?

    1. Ciao Roberto,
      descrivo ogni sketch linea per linea proprio con l’idea di aiutare chi, come te, vuole eseguire delle modifiche e capire di più di ogni progetto.
      Se hai dubbi riguardo a quello che leggi puoi usare i commenti per chiedere chiarimenti.

  1. […] « Bluetooth Low Energy Shield – Bleshield […]

Lascia un commento

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.