Part of Eddystone specification for Bluetooth Low Energy is sending telemetry data such as temperature and operating voltage of the Beacons.
Bluetooth 4.0 Low Energy (BLE) allows you to send advertising frames, which can also carry useful data. For their coding, there are several competing standards, such iBeacon of Apple and the Eddystone format of Google. A special feature of Eddystone is that in addition to pure user data such as a URL, the transmission of telemetry data is possible to provide information about the internal state of the Beacons. These are special Eddystone TLM packets that contain in their first version the information:
The data is stored in big-endian format (MSB first, Motorola).
The example below shows the programming of a sketch for the RFduino. The program is sending the TLM frames not permanently, but rather alternately with Eddystone URL frames (Interleaving Telemetry). Setting the advertising data and starting and stopping the Bluetooth stack is thus outsourced to the function advertise(). The main loop in loop() calls then advertise() alternating with URL and TLM data. In addition, this part takes care of the acquisition of the measured values for temperature and battery voltage. The functions ulong2adv() and so on are responsible for the conversion of the measured values into the format required for Eddystone TLM.
To receive and test the Eddystone frames you may use, for example, a smartphone with Android 4.4 or higher and a corresponding app. The figure shows the reception of an Eddystone TLM frame with the nRF Master Control Panel of Nordic Semiconductor - a very useful tool for developers of applications for Bluetooth beacons.
/* * RFduino as Eddystone TLM beacon. */ #include <RFduinoBLE.h> /* Normal advertising data, see http://www.kompf.de/tech/eddystoneurl.html */ uint8_t advdata_url[] = { 0x03, 0x03, 0xAA, 0xFE, 0x13, 0x16, 0xAA, 0xFE, 0x10, 0xF8, 0x03, 'g', 'o', 'o', '.', 'g', 'l', '/', '1', 'G', 'q', 's', 'y', 'i', }; /* TLM advertising data: */ uint8_t advdata_tlm[] = { 0x03, // Length 0x03, // Param: Service List 0xAA, 0xFE, // Eddystone ID 0x11, // Length 0x16, // Service Data 0xAA, 0xFE, // Eddystone ID 0x20, // TLM flag 0x00, // TLM version /* [10] */ 0x00, 0x00, // Battery voltage /* [12] */ 0x80, 0x00, // Beacon temperature /* [14] */ 0x00, 0x00, 0x00, 0x00, // Advertising PDU count /* [18] */ 0x00, 0x00, 0x00, 0x00 // Time since reboot }; unsigned long pdu_count = 0; void setup() { // Setup battery measurement // See http://forum.rfduino.com/index.php?topic=265.0 analogReference(VBG); analogSelection(VDD_1_3_PS); } /* * Advertise the given data for the specified time ms */ void advertise(uint8_t *data, uint32_t len, uint32_t ms) { RFduinoBLE_advdata = data; RFduinoBLE_advdata_len = len; RFduinoBLE.advertisementInterval = 300; // ms // start the BLE stack RFduinoBLE.begin(); // sleep for ms milliseconds RFduino_ULPDelay(ms); // stop the BLE stack RFduinoBLE.end(); } void loop() { // advertise standard URL frames advertise(advdata_url, sizeof(advdata_url), SECONDS(10); // acquire data for TLM pdu_count++; float temp = RFduino_temperature(CELSIUS); // battery voltage NRF_ADC->TASKS_START = 1; int sensorValue = analogRead(1); float batteryVoltage = sensorValue * (3.6 / 1023.0); NRF_ADC->TASKS_STOP = 1; // convert data to TLM frame format int2adv(advdata_tlm, 10, (int) (1000 * batteryVoltage)); float2adv(advdata_tlm, 12, temp); ulong2adv(advdata_tlm, 14, pdu_count); ulong2adv(advdata_tlm, 18, millis() / 100); // advertise TLM frames advertise(advdata_tlm, sizeof(advdata_tlm), SECONDS(1)); } void ulong2adv(uint8_t* a, int off, unsigned long val) { off+=3; for (int i = 0; i < 4; ++i) { uint8_t bval = val & 0xff; a[off] = bval; val >>= 8; off--; } } void float2adv(uint8_t* a, int off, float val) { int2adv(a, off, (int) (256.0 * val)); } void int2adv(uint8_t* a, int off, int val) { a[off+1] = val & 0xff; val >>= 8; a[off] = val & 0xff; }