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

lunedì 26 marzo 2018

Estendere raggio d'azione dei beacons


Ho fatto una prova per estendere il raggio di ricezione del segnale dei Beacons Estimote. Per questo motivo ho preso un dongle USB Bluetooth 4.0 con connessione antenna SMA ed una antenna esterna direzionale della TP-LinK, anche questa dotata di connettore SMA



L'antenna e' nata per WiFi ma visto che sia WiFi che BT lavorano a 2.4 GHz non ci sono problemi

Le misure sono state fatte con un programma Python allontanandosi via via dall'antenna. In una prova di un beacon in modalita' Eddystone sono partito con un valore di RSSI di circa -46 per arrivare a circa 140 m di distanza ad un valore di RSSI -96.


venerdì 12 gennaio 2018

Estimote telemetry su PC

Questo e' un metodo per leggere i dati di telemetria dei beacon Estimote da PC senza quindi appoggiarsi ad dispositivo mobile (telefono)

Si deve installare tramite Node.js il pacchetto

npm install noble

a questo punto si puo' scaricare l'esempio estimote-telemetry.js da GitHub e modificarlo alla bisgona.
si mette in esecuzione tramite

node test.js

questo metodo e' stato testato su Centos 7 con Lenovo T430

test.js
------------------------------------
// Packest from the Estimote family (Telemetry, Connectivity, etc.) are
// broadcast as Service Data (per "ยง 1.11. The Service Data - 16 bit UUID" from
// the BLE spec), with the Service UUID 'fe9a'.
var ESTIMOTE_SERVICE_UUID = 'fe9a';

// Once you obtain the "Estimote" Service Data, here's how to check if it's
// a Telemetry packet, and if so, how to parse it.
function parseEstimoteTelemetryPacket(data) { // data is a 0-indexed byte array/buffer

  // byte 0, lower 4 bits => frame type, for Telemetry it's always 2 (i.e., 0b0010)
  var frameType = data.readUInt8(0) & 0b00001111;
  var ESTIMOTE_FRAME_TYPE_TELEMETRY = 2;
  if (frameType != ESTIMOTE_FRAME_TYPE_TELEMETRY) { return; }

  // byte 0, upper 4 bits => Telemetry protocol version ("0", "1", "2", etc.)
  var protocolVersion = (data.readUInt8(0) & 0b11110000) >> 4;
  // this parser only understands version up to 2
  // (but at the time of this commit, there's no 3 or higher anyway :wink:)
  if (protocolVersion > 2) { return; }

  // bytes 1, 2, 3, 4, 5, 6, 7, 8 => first half of the identifier of the beacon
  var shortIdentifier = data.toString('hex', 1, 9);

  // byte 9, lower 2 bits => Telemetry subframe type
  // to fit all the telemetry data, we currently use two packets, "A" (i.e., "0")
  // and "B" (i.e., "1")
  var subFrameType = data.readUInt8(9) & 0b00000011;

  var ESTIMOTE_TELEMETRY_SUBFRAME_A = 0;
  var ESTIMOTE_TELEMETRY_SUBFRAME_B = 1;

  // ****************
  // * SUBFRAME "A" *
  // ****************
  if (subFrameType == ESTIMOTE_TELEMETRY_SUBFRAME_A) {

    // ***** ACCELERATION
    // byte 10 => acceleration RAW_VALUE on the X axis
    // byte 11 => acceleration RAW_VALUE on the Y axis
    // byte 12 => acceleration RAW_VALUE on the Z axis
    // RAW_VALUE is a signed (two's complement) 8-bit integer
    // RAW_VALUE * 2 / 127.0 = acceleration in "g-unit" (http://www.helmets.org/g.htm)
    var acceleration = {
      x: data.readInt8(10) * 2 / 127.0,
      y: data.readInt8(11) * 2 / 127.0,
      z: data.readInt8(12) * 2 / 127.0
    };

    // ***** MOTION STATE
    // byte 15, lower 2 bits
    // 0b00 ("0") when not moving, 0b01 ("1") when moving
    var isMoving = (data.readUInt8(15) & 0b00000011) == 1;

    // ***** MOTION STATE DURATION
    // byte 13 => "previous" motion state duration
    // byte 14 => "current" motion state duration
    // e.g., if the beacon is currently still, "current" will state how long
    // it's been still and "previous" will state how long it's previously been
    // in motion before it stopped moving
    //
    // motion state duration is composed of two parts:
    // - lower 6 bits is a NUMBER (unsigned 6-bit integer)
    // - upper 2 bits is a unit:
    //     - 0b00 ("0") => seconds
    //     - 0b01 ("1") => minutes
    //     - 0b10 ("2") => hours
    //     - 0b11 ("3") => days if NUMBER is < 32
    //                     if it's >= 32, then it's "NUMBER - 32" weeks
    var parseMotionStateDuration = function(byte) {
      var number = byte & 0b00111111;
      var unitCode = (byte & 0b11000000) >> 6;
      var unit;
      if (unitCode == 0) {
        unit = 'seconds';
      } else if (unitCode == 1) {
        unit = 'minutes';
      } else if (unitCode == 2) {
        unit = 'hours';
      } else if (unitCode == 3 && number < 32) {
        unit = 'days';
      } else {
        unit = 'weeks';
        number = number - 32;
      }
      return {number: number, unit: unit};
    }
    var motionStateDuration = {
      previous: parseMotionStateDuration(data.readUInt8(13)),
      current: parseMotionStateDuration(data.readUInt8(14))
    };

    // ***** GPIO
    // byte 15, upper 4 bits => state of GPIO pins, one bit per pin
    // 0 = state "low", 1 = state "high"
    var gpio = {
      pin0: (data.readUInt8(15) & 0b00010000) >> 4 ? 'high' : 'low',
      pin1: (data.readUInt8(15) & 0b00100000) >> 5 ? 'high' : 'low',
      pin2: (data.readUInt8(15) & 0b01000000) >> 6 ? 'high' : 'low',
      pin3: (data.readUInt8(15) & 0b10000000) >> 7 ? 'high' : 'low',
    };

    // ***** ERROR CODES
    var errors;
    if (protocolVersion == 2) {
      // in protocol version "2"
      // byte 15, bits 2 & 3
      // bit 2 => firmware error
      // bit 3 => clock error (likely, in beacons without Real-Time Clock, e.g.,
      //                      Proximity Beacons, the internal clock is out of sync)
      errors = {
        hasFirmwareError: ((data.readUInt8(15) & 0b00000100) >> 2) == 1,
        hasClockError: ((data.readUInt8(15) & 0b00001000) >> 3) == 1
      };
    } else if (protocolVersion == 1) {
      // in protocol version "1"
      // byte 16, lower 2 bits
      // bit 0 => firmware error
      // bit 1 => clock error
      errors = {
        hasFirmwareError: (data.readUInt8(16) & 0b00000001) == 1,
        hasClockError: ((data.readUInt8(16) & 0b00000010) >> 1) == 1
      };
    } else if (protocolVersion == 0) {
      // in protocol version "0", error codes are in subframe "B" instead
    }

    // ***** ATMOSPHERIC PRESSURE
    var pressure;
    if (protocolVersion == 2) {
      // added in protocol version "2"
      // bytes 16, 17, 18, 19 => atmospheric pressure RAW_VALUE
      // RAW_VALUE is an unsigned 32-bit integer, little-endian encoding,
      //   i.e., least-significant byte comes first
      //   e.g., if bytes are 16th = 0x12, 17th = 0x34, 18th = 0x56, 19th = 0x78
      //         then the value is 0x78563412
      // RAW_VALUE / 256.0 = atmospheric pressure in pascals (Pa)
      // note that unlike what you see on the weather forecast, this value is
      // not normalized to the sea level!
      pressure = data.readUInt32LE(16) / 256.0;
    }

    return {
      shortIdentifier,
      frameType: 'Estimote Telemetry', subFrameType: 'A', protocolVersion,
      acceleration, isMoving, motionStateDuration, pressure, gpio, errors
    };

  // ****************
  // * SUBFRAME "B" *
  // ****************
  } else if (subFrameType == ESTIMOTE_TELEMETRY_SUBFRAME_B) {

    // ***** MAGNETIC FIELD
    // byte 10 => normalized magnetic field RAW_VALUE on the X axis
    // byte 11 => normalized magnetic field RAW_VALUE on the Y axis
    // byte 12 => normalized magnetic field RAW_VALUE on the Z axis
    // RAW_VALUE is a signed (two's complement) 8-bit integer
    // RAW_VALUE / 128.0 = normalized value, between -1 and 1
    // the value will be 0 if the sensor hasn't been calibrated yet
    var magneticField = {
      x: data.readInt8(10) / 128.0,
      y: data.readInt8(11) / 128.0,
      z: data.readInt8(12) / 128.0
    };

    // ***** AMBIENT LIGHT
    // byte 13 => ambient light level RAW_VALUE
    // the RAW_VALUE byte is split into two halves
    // pow(2, RAW_VALUE_UPPER_HALF) * RAW_VALUE_LOWER_HALF * 0.72 = light level in lux (lx)
    var ambientLightUpper = (data.readUInt8(13) & 0b11110000) >> 4;
    var ambientLightLower = data.readUInt8(13) & 0b00001111;
    var ambientLightLevel = Math.pow(2, ambientLightUpper) * ambientLightLower * 0.72;

    // ***** BEACON UPTIME
    // byte 14 + 6 lower bits of byte 15 (i.e., 14 bits total)
    // - the lower 12 bits (i.e., byte 14 + lower 4 bits of byte 15) are
    //   a 12-bit unsigned integer
    // - the upper 2 bits (i.e., bits 4 and 5 of byte 15) denote the unit:
    //   0b00 = seconds, 0b01 = minutes, 0b10 = hours, 0b11 = days
    var uptimeUnitCode = (data.readUInt8(15) & 0b00110000) >> 4;
    var uptimeUnit;
    switch (uptimeUnitCode) {
      case 0: uptimeUnit = 'seconds'; break;
      case 1: uptimeUnit = 'minutes'; break;
      case 2: uptimeUnit = 'hours'; break;
      case 3: uptimeUnit = 'days'; break;
    }
    var uptime = {
      number: ((data.readUInt8(15) & 0b00001111) << 8) | data.readUInt8(14),
      unit: uptimeUnit
    };

    // ***** AMBIENT TEMPERATURE
    // upper 2 bits of byte 15 + byte 16 + lower 2 bits of byte 17
    // => ambient temperature RAW_VALUE, signed (two's complement) 12-bit integer
    // RAW_VALUE / 16.0 = ambient temperature in degrees Celsius
    var temperatureRawValue =
      ((data.readUInt8(17) & 0b00000011) << 10) |
       (data.readUInt8(16)               <<  2) |
      ((data.readUInt8(15) & 0b11000000) >>  6);
    if (temperatureRawValue > 2047) {
      // JavaScript way to convert an unsigned integer to a signed one (:
      temperatureRawValue = temperatureRawValue - 4096;
    }
    temperature = temperatureRawValue / 16.0;

    // ***** BATTERY VOLTAGE
    // upper 6 bits of byte 17 + byte 18 => battery voltage in mini-volts (mV)
    //                                      (unsigned 14-bit integer)
    // if all bits are set to 1, it means it hasn't been measured yet
    var batteryVoltage =
       (data.readUInt8(18)               << 6) |
      ((data.readUInt8(17) & 0b11111100) >> 2);
    if (batteryVoltage == 0b11111111111111) { batteryVoltage = undefined; }

    // ***** ERROR CODES
    // byte 19, lower 2 bits
    // see subframe A documentation of the error codes
    // starting in protocol version 1, error codes were moved to subframe A,
    // thus, you will only find them in subframe B in Telemetry protocol ver 0
    var errors;
    if (protocolVersion == 0) {
      errors = {
        hasFirmwareError: (data.readUInt8(19) & 0b00000001) == 1,
        hasClockError: ((data.readUInt8(19) & 0b00000010) >> 1) == 1
      };
    }

    // ***** BATTERY LEVEL
    // byte 19 => battery level, between 0% and 100%
    // if all bits are set to 1, it means it hasn't been measured yet
    // added in protocol version 1
    var batteryLevel;
    if (protocolVersion >= 1) {
      batteryLevel = data.readUInt8(19);
      if (batteryLevel == 0b11111111) { batteryLevel = undefined; }
    }

    return {
      shortIdentifier,
      frameType: 'Estimote Telemetry', subFrameType: 'B', protocolVersion,
      magneticField, ambientLightLevel, temperature,
      uptime, batteryVoltage, batteryLevel, errors
    };
  }
}

// example how to scan & parse Estimote Telemetry packets with noble

var noble = require('noble');

noble.on('stateChange', function(state) {
  console.log('state has changed', state);
  if (state == 'poweredOn') {
    var serviceUUIDs = [ESTIMOTE_SERVICE_UUID]; // Estimote Service
    var allowDuplicates = true;
    noble.startScanning(serviceUUIDs, allowDuplicates, function(error) {
      if (error) {
        console.log('error starting scanning', error);
      } else {
        console.log('started scanning');
      }
    });
  }
});

noble.on('discover', function(peripheral) {
  var data = peripheral.advertisement.serviceData.find(function(el) {
    return el.uuid == ESTIMOTE_SERVICE_UUID;
  }).data;

  var telemetryPacket = parseEstimoteTelemetryPacket(data);
  if (telemetryPacket) { 
if (telemetryPacket['subFrameType'] == "A"){
console.log(telemetryPacket['shortIdentifier']);
//console.log(telemetryPacket['acceleration']);
console.log("AX " + telemetryPacket['acceleration']['x']);
console.log("AY " + telemetryPacket['acceleration']['y']);
console.log("AZ " + telemetryPacket['acceleration']['z']);


}
 }

});

lunedì 19 ottobre 2015

DIY Homekit con Arduino

Dopo un primo esperimento con DIY HomeKit (a questo post) non ero particolarmente contento perche' comunque i costi di Raspberry e la gestione in generale non erano ottimali ed ho provato a replicare la cosa su Arduino


Per il modulo Bluetooth ho usato un modulo Bluetooth LE HM-10 del costo di circa 5 euro (qui). Attenzione, non tutti i moduli BT per Arduino supportano BT4 LE. Il modulo riporta sul retro la scritta AC-BT-V4 ed al comando AT+NAME? risponde con HMSoft

Il modulo si comanda tramite stringhe AT (per l'elenco completo dei comandi si puo' leggere qui il manuale di istruzioni)

Cio' che interessa e' la sequenza di comando
AT+ROLE1
AT+IMME1
AT+DISI?

all'ultimo comando il dispositivo risponde in questo modo
---------------------------------------------------------
OK+DISIS
OK+DISC:4C000215:B9407F30F5F8466EAFF925556B57FE6D:71884DF6B6:F1A84DF67188:-086
OK+DISC:00000000:00000000000000000000000000000000:0000000000:C493974F69E2:-084
OK+DISC:4C000215:B9407F30F5F8466EAFF925556B57FE6D:DB2A91AEB6:D81691AEDB2A:-084
OK+DISCE
---------------------------------------------------------
Sono stati avvistati 3 beacons (due Estimote ed un Eddystone riconoscibile da tutti zeri perche' il protocollo non e' riconosciuto)

Prendendo la riga evidenziata in giallo
iBeacon ID = B9407F30F5F8466EAFF925556B57FE6D
Major = 7188
Minor = 4DF6
Measured power (182 decimale) =B6
Mac Address = F1A84DF67188
RSSI = -86

Passando quindi ad Arduino

I collegamenti sono banali (verra' usata la Seriale Software di Arduino)
BT VCC +3.3V Arduino
BT GND GND Arduino
BT RXD D2 Arduino
BT TXD D3 Arduino

Nello sketch seguente se viene individuato il beacon con minor AEB6 viene attivato il pin D13 in modo da aprire il contatto su un rele'; se per piu' di 50 secondi non viene piu' messo in lista il beacon con minor AEB6 il pin D13 va Low e si chiude il rele (il codice si commenta da solo)

--------------------------------------------------------------------------------------
#include <SoftwareSerial.h>

SoftwareSerial mySerial(3, 2); 
char risposta[115];
int conta;
int rele = 13;
long tempo;

void setup()  
{
  Serial.begin(9600);
  mySerial.begin(9600);
  mySerial.write("AT+ROLE1");
  mySerial.write("AT+IMME1");
  if (mySerial.available())
     {
       while (mySerial.available()>0){
       }
     }
     
  pinMode(rele, OUTPUT);
  tempo = 0; 
}

void loop() 
{
  mySerial.write("AT+DISI?");
  if (mySerial.available())
     {
       conta = 0;
       while (mySerial.available()>0){
         risposta[conta] = mySerial.read();
         conta = conta + 1;
       }
     }   
  if ((risposta[56] == 'A') && (risposta[57] == 'E') && (risposta[58] == 'B') && (risposta[59] == '6')) 
    {
      //Serial.println("ATTENZIONE");
      digitalWrite(rele, HIGH);
      tempo = 0;
    }
  delay(500);
  tempo = tempo + 1;
  if (tempo > 100)
        {
          digitalWrite(rele, LOW);
          tempo = 101;
        }
  
}

venerdì 28 agosto 2015

EddyStone Beacons


Eddystone e' una specifica per beacons a formato aperto (che si differenzia da IBeacons di marchio Apple) recentemente uscita e sponsorizzata da Google

Non tutti i beacon sono in grado di trasmettere in formato Eddystone ma quelli riprogrammabili (e di  marca) come Radius ed Estimote possono essere trasformati in modalita' Eddystone

Beacon Estimote in modalita' Eddystone

C'e' da dire che le due modalita' sono esclusive (od il beacon trasmette in modalita' IBeacon od in EddyStone) e che la durata stimata delle batterie su Eddystone e' inferiore a quella IBeacon


C'e' pero' da osservare un paio di aspetti importanti:

1) Eddystone permette di trasmettere, oltre all'UUID come IBeacon, anche stringhe di testo. In questo modo si puo' evitare l'utilizzo di un server esterno se per esempio si vuole pubblicizzare un prodotto in una vetrina (per esempio) . Si programma il beacon per puntare ad un link sullo store invece che ad un proprio server che legge l'UUID, interroga il database e fornisce il link del prodotto.

2) il formato e' libero e quindi meno soggetto alle guerre tra marchi (in questo modo verra' sdoganato l'uso dei beacon anche su Android)

Google ha pubblicato gli esempi sull'utilizzo (sia per Android che IOS) a questo link.
Oltre al protocollo Eddystone Google fornisce anche una infrastruttura cloud per gestire una eventuale flotta di beacons (un po' sullo stile di cio' che gia' offre Estimte) tramite le Proximity API

ps: alla data attuale l'applicazione Estimote per IOS non gestisce il protocollo Eddystone e non riconosce il broadcast da questi dispositivi. L'applicazione Estimote per Android invece funziona perfettamente con il nuovo formato

martedì 28 aprile 2015

Schermare beacon

Lavorando con diversi beacon e' piuttosto fastidioso quando interferiscono tra di loro all'interno dell'ufficio



Un sistema banale e' quello di avvolgere i trasmettitori nella carta argentata per uso alimentare (piu' di una volta, un solo giro non e' garanzia di schermatura), altrimenti possono essere messi dentro un forno a microonde

venerdì 26 dicembre 2014

IBeacon ed IOS8

In estate avevo scritto una piccola applicazione per IBeacon
E' passata l'estate e l'autunno ma sono rimasto fedele a IOS 7 e OS X 10.9 perche' i sistemi operativi troppo recenti sono sempre fonti di problemi; in questi giorni mi sono pero' convinto a passare ad IOS 8.1.2 sul telefono ed a OSX 10.10 sul portatile

La sorpresa e' venuta quando sono arrivato a compilare ed eseguire il codice per IBeacon che precedemente funzionava perfettamente. Nonostante non ci fossero errori di compilazione ne' errori di esecuzione il programma semplicemente aveva smesso di riconoscere i beacons vicini

Dopo un paio di giorni a battere il capo sul muro sono arrivato su questo link in cui si segnala che con l'arrivo di IOS 8 si devono aggiungere un paio di stringhe un info.plist del progetto
-----------------------------------------
<key>NSLocationAlwaysUsageDescription</key> 
<string>Messaggio Utente</string>
-----------------------------------------

oppure
-----------------------------------------
<key>NSLocationWhenInUseUsageDescription</key>
 <string>Your message goes here</string>
-----------------------------------------

effettuate queste modifiche il programma ha ricominciato a funzionare (e' in questi momenti che amo la programmazione :<<<<


venerdì 5 dicembre 2014

Estimote Virtual Beacon


Nell'applicazione Estimote per cellulari e' disponibile anche la funzione Virtual Beacon
Il problema e' che, usando gli esempi di Estimote, si riescono a visualizzare i beacon fisici e non il virtual beacon




il problema risiede nel fatto che i beacon fisici Estimote hanno un uuid B9407F30-F5F8-466E-AFF9-25556B57FE6D mentre i virtual beacon hanno un uuid 8492E75F-4FD6-469D-B132-043FE94921D8

Modificando questo parametro e' possibile usare i virtual beacon per gli esempi di Estimote


venerdì 14 novembre 2014

Estimote RSSI

A seguito del precedente post ho ripetuto la prova anche con un Estimote



Valore medio RSSI: -67.1
St.Deviation : 1.95
Scarto massimo positivo : 2.15
Scarto massimo negativo : -3.84


mercoledì 5 novembre 2014

martedì 4 novembre 2014

Trilaterazione 2D con beacon


L'algoritmo di trilaterazione e' una tecnica che permette di individuare la posizione di un punto sconosciuto conosciuto le distanze dello stesso da punti quotati. La versione 3D e' alla base del posizionamento GPS ma quello che mi interessa e' la sua versione 2D per cercare di poter fare posizionamento indoor mediante antenne WiFi o beacons

Su questo argomento esiste una vasta letterature ingegneristica con risultati che vanno da ottimi a pessimi.

Nell'immagine viene riportato il metodo di calcolo (non ricordo la fonte)
Per semplicita' uno dei punti di riferimento e' in (0,0) mentre un altro e' sull'asse X (d,0). Il terzo punto e' in (i,j) mentre le distanze del punto incognito sono  rispettivamente R1, R2 ed R3



Per la prova ho usato tre beacons. Ho quindi realizzato una curva di taratura (per quanto possibile) che mi convertisse il valore di RSSI in metri

La prova e' stata effettuata creando una griglia regolare di 5x5 m e 3x3 m ed effettuando misure su alcuni nodi. In blu sono riportati i valori reali ed in rosso le posizioni calcolate
Importante: per ogni posizione e' stato preso il valore istantaneo di RSSI senza effettuare medie  di lunga durata perche' il tentativo era di tracciare il percorso di una persona in movimento

Risultati griglia 5x5

Risultati griglia 3x3 m

E' evidente che la prova ha avuto un esito decisamente negativo. L'algoritmo di calcolo di trilaterazione funziona, cio' che difetta e' la stabilita' del segnale dei beacons e l'impossibilita' di avere una legge affidabile per convertire i valori di RSSI in distanze

lunedì 3 novembre 2014

RSSI vs Distanza per CC2540/CC2541/Gimbal10

Giusto per prova ho messo in grafico i valori di RSSI e distanza per vedere se si trova una legge semplice per calcolare la distanza (non mi interessa se la legge di correlazione ha un senso fisico, solo che abbia un buon fit)

I valori sono mediati su molte misure (altrimenti si osservano le fluttuazioni del post precedente)

 Come indicazione generale si riesce a calcolare discretamente la distanza per valori inferiori al 1.5 m. CC2540 e' quello per cui i valori di RSSI vanno rapidamente a -100 e quindi sono indicati solo per una prossimita' molto stretta

CC2540 vs CC2541 cc Gimbal

Avendo tre modelli di Beacons ho provato a vedere quali sono le differenze principali
Per rendere comparabili i risultati ho effettuato una serie di misure di RSSI ad una distanza di 1 m con un medesimo telefono (Iphone 4S) ed utilizzando le applicazioni ufficiali dei costruttori

I tre processori sono il TI CC2540 (TI vecchio), TI 2541 (TI nuovo) e Gimbals serie 10
Grafico delle misure
TI CC2540
valore medio : -84.6
st. dev : 1.78
differenza massima positiva : 2.4
differenza massima negativa : -3.6

TI CC2541
valore medio : -73.2
st. dev : 1.77
differenza massima positiva : 2.8
differenza massima negativa : -3.2

Gimbal serie 10
valore medio : -90.6
st. dev : 1.78
differenza massima positiva : 3.3
differenza massima negativa : -4.6


In conclusione :
1) tutti i beacon esaminati hanno un valore simile di standard deviation per cui non esiste un vincitore
2) il valore di RSSI per tutti e' molto variabile, troppo per fare un posizionamento preciso

venerdì 31 ottobre 2014

Gimbal beacons

Aggiornamento
Con alcune app i beacon Gimbal sono visibili ma cambiano continuamente MAC Address per cui la lista continua ad aggiornarsi scoprendo sempre nuovi beacon (in realta' e' sempre il solito che aggiorna il proprio identificativo di basso livello)
Cio' comporta problemi su Android. Secondo quanto riportato da questo post di Radius Network (una ditta concorrente) dopo circa 40 minuti di utilizzo una applicazione Android va in crash a causa del numero limitato della lista di MAC Address che Android riesce a gestire

----------------------------------------

Grazie ad una simpatica iniziativa per gli sviluppatori (spediscono un kit gratuito anche all'estero...nonostante che sia indicato solo per gli USA) sono entrato in possesso di tre beacons Gimbal ovvero Qualcomm


Rispetto agli altri beacon testati basati sul TI CC2540 questi sono basati su processori proprietari della Qualcomm e sono decisamente molto meglio ingegnerizzati e miniturizzati (lo spazio e' quasi totalmente occupato dalla batteria CR2032)

Una prima differenza rispetto agli altri beacon testati e' che questi non vengono segnalati dai programmi basati sul uuid-major-minor. In pratica ogni beacon ha un proprio codice (4 caratteri - 5 caratteri) che deve essere comunicato a Gimbal per l'attivazione; in seguito i beacon solo visibili medianti le applicazione sviluppate con l'SDK di Gimbal (IOS e Android-Beta) ma non con altri software

Con il beacon posto vicino al telefono l'RSSI e' di -40, portandolo a circa ad oltre 3 m scende circa -80. Riportando velocemente in grafico


Il segnale risulta molto stabile (prova effettuata ad un 1 metro di distanza tra beacon ed IPhone) con una standard deviation del valore di RSSI di circa 0.6 unita'


Su Android le cose sono leggermente differenti con un valore di standard deviation di 1.2 unita'. Non male vista la precedente esperienza con i Texas Instruments





venerdì 24 ottobre 2014

Bluetooth LE su MacBook Late 2009

Con il nuovo OsX prende ancora piu' importanza nel mondo Mac la presenza di Bluetooth 4

Avendo un MacBook Pro Late 2009 non e' montato di serie un chip Bluetooth LE.
Ho provato quindi a recuperare il dongle gia' usato per Linux visto sul precedente post


Con sorpresa il chip Broadcom BCM20702 viene riconosciuto da MacOs X 10.9.5 senza problemi.
Per rendere operativo il dongle si puo' usare il programma BeaconScanner (link) per registrare la presenza di beacon nelle vicinanze al portatile ed il programma BeaconOsX (link) per trasformare il MacBook in un beacon

lunedì 25 agosto 2014

Potenza del segnale di CC2541


L'integrato Texas Instruments CC2541 su cui si basano alcuni beacon permette di modificare la potenza di trasmissione da +4 dBm a -23 dBm. Cio' consente di aumentare o diminuire il proprio raggio d'azione


Nell'applicazione che uso i valori di potenza non impostati a 4 valori prefissati

0 dBm : potenza standard
4 dBm : circa il doppio della potenza standard
-6 dBm : un quarto della potenza standard
-23 dBm : un ventesimo della potenza standard

Per vedere se effettivamente le modifiche influenzano il raggio d'azione del beacon ho provato a plottare la potenza contro il valore di rssi (facendo una media e la deviazione standard di una serie di dati a causa della instabilita' intrinseca del segnale). La prova e' stata eseguita mantenendo beacon e telefono a distanza fissa per tutto il tempo


Come si vede dal grafico i casi 0dBm e +4dBm sono praticamente non distinguibili, nel caso di -6dBm il valore di rssi e' di qualita' inferiore a causa della diminuzione del segnale. Quando la potenza del beacon passa a -23dBm il valore di rssi e' praticamente fisso a 95 ovvero il telefono e' praticamente fuori al raggio d'azione del beacon e legge solo un valore di fondo scala


martedì 22 luglio 2014

AndroidIBeaconLibrary e' morta??

Un po' di tempo fa avevo indicato come sviluppare applicazione IBeacon su Android mediante l'utilizzo della libreria AndroidIBeaconLibrary di Radius

In questi giorni avevo bisogno di rimettere mano alla libreria e con mia grande sorpresa il progetto e' scomparso dal sito di Radius Network e la pagina GitHub mostra un laconico messaggio

Per avere un po' di idee sul motivo di una cosi' repentina rimozione si puo' leggere questo post

Ma e' veramente morta questa libreria??
La cosa divertente e' che su GitHub sono presenti ancora tutti i commit per cui e' sufficiente effettuare un fork del progetto (legale in base alla licenza Apache) mediante l'apposito pulsante



per copiare tutta la storia di commit nella propria pagina GitHub.
A questo punto si puo' cliccare il pulsante Clone On Desktop per copiare tutta la storia dei commit anche sul proprio client


con il comando Revert si puo' quindi generare una qualsiasi release della libreria
Ancora piu' semplice, visto che la rete nasconde ma difficilmente cancella, sui server di Amazon sono disponibili ancora i file tar.gz della versione per Eclipse
Per rendere le cose ancora piu' semplici ho copiato le ultime tre versioni sul mio Google Drive (tutte per uso con Eclipse)



martedì 8 luglio 2014

IBeacon ed aggiornamento IOS 7.1.2 su Iphone 4S


Sviluppando per beacon su IOS mi sono trovato di fronte ad una cosa piuttosto strana

Il medesimo codice che girava correttamente su un Iphone 5S si rifiutava di funzionare su Iphone 4S a parita' di IOS installato


Con l'uscita dell'aggiornamento ad IOS 7.1.2 tutto si e' risolto senza modificare nessuna parte del codice dell'applicazione

lunedì 7 luglio 2014

Beacon su IOS


A seguito delle prove effettuate in questo post ed in questo post (usando in entrambi i casi un telefono Moto G con Android e librerie Radius) mi sono convertito ad IOS ed ho ripetuto l'esperienza usando un IPhone 4S con le medesime antenne usate in precedenza e le API di IOS



non ho ancora capito bene il motivo ma sembra che il segnale risulta nettamente piu' stabile su IOS che su Android (hardware o software???)


da notare che al contrario di Android su IOS vengono segnalati come valori negativi le fallite acquisizioni

il codice impiegato per misurare il segnale e' riportato in seguito (ripreso da questo link dall'esempio Art Gallery)

#import "ViewController.h"

#define ESTIMOTE_PROXIMITY_UUID [[NSUUID alloc] initWithUUIDString:@"4506F9C7-00F9-C206-C12C-C2F9C702D3C3"]

int oldminor;

@interface ViewController ()
@property (nonatomic, strong) NSMutableArray *beacons;
@property (nonatomic, strong) NSArray *paintings;
@property (nonatomic, strong) CLLocationManager *locationManager;
@property (nonatomic, strong) CLBeaconRegion *beaconRegion;


@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
    
_beacons = [[NSMutableArray alloc] init];
    
    _locationManager = [[CLLocationManager alloc] init];
    _locationManager.delegate = self;
    
    _beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:ESTIMOTE_PROXIMITY_UUID
                                                       identifier:@"lucainnoc.Ibeareader"];
}



- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (void)viewDidAppear:(BOOL)animated
{
    [_locationManager startRangingBeaconsInRegion:_beaconRegion];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [_locationManager stopRangingBeaconsInRegion:_beaconRegion];
}

- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
    if (beacons.count) {
        
        CLBeacon *closestBeacon;
        
        for (CLBeacon *beacon in beacons) {
         
            NSLog([NSString stringWithFormat:@"%f", beacon.accuracy]);
            
           
        }

    }
    
}



@end

lunedì 26 maggio 2014

CC2540 vs CC2541

Il mio fornitore di beacon ha cambiato l'integrato passando da un CC2540 ad un CC2541 (entrambi Texas Instruments)



Ho fatto una breve prova per vedere se c'era una miglioramento sulla stabilita' del segnale
La prova (seppur con un numero modesto) e' stata fatta a geometria fissa evitando il piu' possibile di influenzare la misura

Alla fine e' emerso che il parametro Accuracy e' stato di 
CC 2540 : 6.79+/-5.23 
CC 2541 : 4.89+/-3.32

Con il 2540 la deviazione standard vale circa il 77% del valore medio mentre con il 2541 circa il 67%
Un miglioramento ma non certo sensibile



mercoledì 21 maggio 2014

IBeacon e direzione

Ho fatto qualche prova per vedere di poter replicare la navigazione con i radiofari (beacon) che veniva utilizzata in aeronautica prima dell'avvento delle nuove tecnologie

In pratica si puo' fare navigazione mettendo un trasmettitore con una antenna omnidirezionale (beacon) ed un ricevitore con una antenna direzionale. In questo modo l'aereo punta direttamente sul radiofaro massimizzando il segnale nella propria antenna direzionale

Si puo' fare qualcosa di simile con IBeacon ed i telefoni cellulari??
Per la prova ho preso un Motorola G ed un AprilBeacon ed ho scritto una piccola applicazione che legge i valori di RSSI di BT4
Dopo ogni serie di misure ho ruotato il telefono di 90° (mantenendolo alla stessa distanza) e ripreso le misure (l'angolo 0° coincide con il telefono puntato in direzione del beacon e 180° con il telefono orientato in direzione opposta al beacon

Serie 1
Come era lecito attendersi i valori di RSSI sono molto variabili e danno luogo a grandi valori della standard deviation. In ogni caso l'orientazione a 90° sembra sensibilmente quella con valori minori



Serie 2
nella serie 2 i dati dell'angolo 0° sono decisamente anomali. In ogni caso si ha che ancora l'angolo 90° mostra i valori minori

In sostanza sembra che ci sia una certa direzionalita' nelle antenna BT dei telefoni cellulari ma i valori di RSSI sono cosi' poco affidabili da rendere sostanzialmente impossibile utilizzare nel mondo reale tale metodo


Debugger integrato ESP32S3

Aggiornamento In realta' il Jtag USB funziona anche sui moduli cinesi Il problema risiede  nell'ID USB della porta Jtag. Nel modulo...