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

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ì 8 gennaio 2018

Estimote Eddystone telemetry bridge in Android

Un esempio per ricevere i dati di telemetria dei pacchetti Eddystone (il protocollo di Google per i beacons) e trasmetterli poi ad un server remoto (ho scoperto solo oggi che il nome Eddystone deriva dal faro di Eddystone)



compile 'com.estimote:sdk:1.0.12'

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

    <uses-permission android:name="android.permisssion.ACCESS_COARSE_LOCATION">
</uses-permission>
    <uses-permission android:name="android.permisssion.INTERNET">
</uses-permission>


    <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 innocenti.luca.com.eddyacc;

import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import com.estimote.coresdk.common.config.EstimoteSDK;
import com.estimote.coresdk.recognition.packets.Eddystone;
import com.estimote.coresdk.recognition.packets.EstimoteTelemetry;
import com.estimote.coresdk.service.BeaconManager;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.util.List;





public class MainActivity extends AppCompatActivity {

    private BeaconManager beaconManager;
    private String scanId;




    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        EstimoteSDK.initialize(getApplicationContext(),"eddyacc-ev0","f73efb85963579672c048fc92de64039");

        beaconManager = new BeaconManager(this);

        beaconManager.setEddystoneListener(new BeaconManager.EddystoneListener() {
            @Override public void onEddystonesFound(List<Eddystone> eddystones) {
                for (Eddystone e : eddystones){
                    if (e.telemetry != null)
                    {
                        String aa = "http://150.217.73.108/luca/test.php?temperatura="+Double.toString(e.telemetry.temperature);
                        Double myTaskParameters = e.telemetry.temperature;
                        new invia().execute(myTaskParameters);

                        Log.d("Eddy", e.macAddress + " temperature :" + e.telemetry.temperature + " ");
                    }
                }

            }
        });


        beaconManager.setTelemetryListener(new BeaconManager.TelemetryListener() {
            @Override            public void onTelemetriesFound(List<EstimoteTelemetry> telemetries) {
                for (EstimoteTelemetry tlm : telemetries) {
                    Log.d("TELEMETRY", "beaconID: " + tlm.deviceId +
                            ", temperature: " + tlm.temperature + " °C" + tlm.accelerometer.x);
                }
            }
        });
    }




    @Override protected void onStart() {
        super.onStart();
        beaconManager.connect(new BeaconManager.ServiceReadyCallback() {
            @Override            public void onServiceReady() {
                beaconManager.startEddystoneDiscovery();
                beaconManager.startTelemetryDiscovery();
            }
        });
    }

    @Override protected void onStop() {
        super.onStop();
        beaconManager.stopEddystoneDiscovery();

        beaconManager.stopTelemetryDiscovery();
    }

    @Override protected void onDestroy(){
        super.onDestroy();
        beaconManager.disconnect();
    }

    private class invia extends AsyncTask<Double, Void, String> {

        @Override        protected String doInBackground(Double... params) {
            // These two need to be declared outside the try/catch            // so that they can be closed in the finally block.            HttpURLConnection urlConnection = null;
            Double t = params[0];

            try {

                URL url = new URL("http://150.217.73.108/luca/test.php?temperatura="+Double.toString(t));
                urlConnection = (HttpURLConnection) url.openConnection();
                urlConnection.setRequestMethod("GET");
                urlConnection.connect();
                urlConnection.getInputStream();


            } catch (ProtocolException e) {
                e.printStackTrace();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (urlConnection != null) {
                    urlConnection.disconnect();
                }

            }
            return null;
        }

            @Override            protected void onPostExecute (String s)
            {
                super.onPostExecute(s);
            }

        }
}

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


Geologi

  E so anche espatriare senza praticamente toccare strada asfaltata