Estimote Telemetry

Estimote Telemetry is a Bluetooth packet broadcast by some Estimote Beacons, which contains real-time sensor readings & beacon health data. Bluetooth-enabled devices (such as iOS and Android smartphones) can discover and parse these packets, allowing you to access all the sensor & health data without having to connect to the beacon.

What’s ahead (aka Table of Contents)

Prerequisites

  • 1 or more Proximity Beacon (hardware revision “G” or “J”) or Location Beacon (hardware revision “F” or “I”)

Enabling Estimote Telemetry

Both Proximity and Location Beacons ship with Estimote Telemetry enabled by default. That hasn’t always been the case though, so when in doubt, double-check to make sure it’s enabled on your beacons.

The easiest way to enable Estimote Telemetry, and also adjust its transmit power and advertising interval if you want, is with an Estimote iOS or Android app. Simply find your beacon on the radar or list, tap on it, wait for the app to connect to the beacon, then find the Estimote Telemetry packet options.

Estimote Telemetry packets description

Estimote Telemetry actually consists of two kinds of packets: we call them “Telemetry A” and “Telemetry B”. Your beacon will take turns broadcasting them: “A”, “B”, “A”, “B”, etc. Here’s what’s inside:

  • each packet:
    • short identifier of the beacon (= first 16 characters of its usual, 32-character–long identifier)
  • Telemetry A:
    • accelerometer readings on 3 axes
    • is the beacon currently in motion or not
    • “current” and “previous” motion state duration
      • e.g., if the beacon is currently in motion: for how long (“current”), and how long it’s been motionless before the motion happened (“previous”)
      • e.g., if the beacon is currently still: for how long (“current”), and how long it’s been in motion before it stopped moving (“previous”)
    • [Location Beacons] state of GPIO pins (high or low)
    • [Location Beacons with hardware revision “F3.x” and “I”] atmospheric pressure
    • error codes:
      • “application error” if the beacon had to be restarted because of a firmware problem
      • “real-time clock error” if the RTC is not properly set
        • most commonly, this means that the battery was taken out, resetting the RTC
        • this is only applicable to beacons actually equipped with RTC, which is mainly Location Beacons
        • features which rely on time (Secure Monitoring, scheduled advertising) will be disabled
  • Telemetry B:
    • temperature sensor reading
    • battery voltage
    • battery level (%)
    • beacon uptime (how long since the beacon’s last restart)
    • [Location Beacons] ambient light level
    • [Location Beacons with hardware revision “F”] magnetometer readings on 3 axes

Keep in mind: The above list is valid for the latest version of the Telemetry protocol. If you’re on an older firmware, some of the fields may be missing, or be in different places. (for example, error codes originally started in Telemetry B, and were moved to A later on)

Full specification is available on our GitHub: Estimote packets specs

Detecting Estimote Telemetry packets

iOS

We have a helper library for detecting and parsing all kinds of Estimote packets, including the Telemetry packets:

https://github.com/estimote/ios-bluetooth-scanning

It’s a dynamic framework, which means you need to drop it into your Xcode project, and then add it to the “Embedded Binaries” section in your target’s config. This should also automatically add it to the “Linked Frameworks and Libraries” section.

You can also get it via CocoaPods: https://cocoapods.org/pods/EstimoteBluetoothScanning.

Tip: Estimote Proximity SDK for iOS pulls the EstimoteBluetoothScanning library as a dependency, which means that if you’re using the Proximity SDK in your app, you already have access to the EstimoteBluetoothScanning library. In this case, you can jump straight to the code below.

Once you have the EstimoteBluetoothScanning library integrated into your project, here’s a code snippet for Telemetry scanning:

import EstimoteBluetoothScanning

class ViewController: UIViewController, EBSUniversalScannerDelegate {

    let scanner = EBSUniversalScanner()

    override func viewDidLoad() {
        super.viewDidLoad()

        universalScanner.delegate = self
        universalScanner.startScanForDevicesRepresented(
            byClasses: [EBSScanInfoEstimoteTelemetryA.self,
                        EBSScanInfoEstimoteTelemetryB.self])
    }

    func universalScanner(_ universalScanner: EBSUniversalScannerProtocol,
                          didScanEstimoteDevice scanInfo: EBSScanInfo) {
        if let scanInfo = scanInfo as? EBSScanInfoEstimoteTelemetryA {
            print("telemetry A detected")
            print("beacon short ID = \(scanInfo.shortIdentifier)")
            print("beacon in motion = \(scanInfo.motionState)")
        }
        if let scanInfo = scanInfo as? EBSScanInfoEstimoteTelemetryB {
            print("telemetry B detected")
            print("beacon short ID = \(scanInfo.shortIdentifier)")
            print("temperature = \(scanInfo.temperatureInCelsius)")
        }
    }

    func universalScanner(_ universalScanner: EBSUniversalScannerProtocol,
                          didFailToScanWithError error: Error) {
        NSLog("scanner error: \(error)")
    }
}

Background support

If you want your app to keep scanning for telemetry in the background:

  • go to your target’s settings
  • go to the Capabilities tab
  • enable Background Modes
  • tick the “Uses Bluetooth LE accessories” checkbox

EstimoteBluetoothScanning library doesn’t support re-launching from a killed state at this time.

Android

We have a helper library for detecting and parsing all kinds of Estimote packets, including the Telemetry packets. You can add it to your project by adding the following line to your build.gradle file:

implementation 'com.estimote:scanning-plugin:0.25.0'
// for Gradle older than 3.0, use "compile" instead of "implementation"

You can check what’s the latest available version on: https://bintray.com/estimote/android/scanning-plugin

Tip: Estimote Proximity SDK for Android pulls the scanning-plugin as a dependency, which means that if you’re using the Proximity SDK in your app, you already have access to the scanning-plugin. In this case, you can jump straight to the code below.

Once you have the scanning-plugin integrated into your project, here’s a code snippet for Telemetry scanning:

class MainActivity : AppCompatActivity() {

    private lateinit var bluetoothScanner: BluetoothScanner
    private telemetryScanHandler: ScanHandler? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        bluetoothScanner =
            EstimoteBluetoothScannerFactory(applicationContext).getSimpleScanner()
        telemetryScanHandler = bluetoothScanner
                .estimoteTelemetryFrameAScan() // or estimoteTelemetryFrameBScan
                .withOnPacketFoundAction {
                    Log.d("TLM", "telemetry A detected: $it")
                }
                .withOnScanErrorAction { Log.e("TLM", "scan failed: $it") }
                .start()
    }

    override fun onDestroy() {
        super.onDestroy()
        telemetryScanHandler?.stop()
    }
}

Request location permissions

Performing a Bluetooth scan on Android requires the app to obtain an ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission from the user.

See “Request location permissions” in our Android Proximity SDK tutorial to learn how to go about that.

Background support

If you want the scanning-plugin to work even when the user leaves the app, you’ll need to keep the app running with a foreground service. In the code above, replace getSimpleScanner with getScannerWithForegroundService. You’ll also need to create a Notification to show to the user, to let them know that the app is still running:

val channelId = "temperature-monitoring"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  val channel = NotificationChannel(channelId,
        "Temperature monitoring", NotificationManager.IMPORTANCE_LOW)
  val notificationMngr = context.getSystemService(NotificationManager::class.java)
  notificationMngr.createNotificationChannel(channel)
}

val notification = Notification.Builder(this, channelId)
        .setSmallIcon(R.drawable.notification_icon_background)
        .setContentTitle("Temperature monitoring is active")
        .setPriority(Notification.PRIORITY_LOW)
        .build()

bluetoothScanner = EstimoteBluetoothScannerFactory(applicationContext)
        .getScannerWithForegroundService(notification)
// ...

scanning-plugin doesn’t support re-launching from a killed state at this time. However, with the foreground service active, your app should generally not be killed, even if the user swipes it away at the task switcher. Some Android manufacturers modify their Android system code to behave differently though, so your mileage may vary.