17 #ifndef SERVICES_EDDYSTONEBEACON_H_ 18 #define SERVICES_EDDYSTONEBEACON_H_ 20 #warning ble/services/EddystoneService.h is deprecated. Please use the example in 'github.com/ARMmbed/ble-examples/tree/master/BLE_EddystoneService'. 23 #include "CircularBuffer.h" 28 #if BLE_FEATURE_GATT_SERVER 30 static const uint8_t BEACON_EDDYSTONE[] = {0xAA, 0xFE};
34 #define DBG(MSG, ...) printf("[EddyStone: DBG]" MSG " \t[%s,%d]\r\n", \ 38 #define WARN(MSG, ...) printf("[EddyStone: WARN]" MSG " \t[%s,%d]\r\n", \ 42 #define ERR(MSG, ...) printf("[EddyStone: ERR]" MSG " \t[%s,%d]\r\n", \ 47 #define DBG(x, ...) //wait_us(10); 48 #define WARN(x, ...) //wait_us(10); 53 #define INFO(x, ...) printf("[EddyStone: INFO]"x " \t[%s,%d]\r\n", \ 77 static const int SERVICE_DATA_MAX = 31;
80 #define EDDYSTONE_MAX_FRAMETYPE 3 81 void (*frames[EDDYSTONE_MAX_FRAMETYPE])(uint8_t *, uint32_t);
82 static const int URI_DATA_MAX = 18;
83 typedef uint8_t UriData_t[URI_DATA_MAX];
87 static const int UID_NAMESPACEID_SIZE = 10;
88 typedef uint8_t UIDNamespaceID_t[UID_NAMESPACEID_SIZE];
89 static const int UID_INSTANCEID_SIZE = 6;
90 typedef uint8_t UIDInstanceID_t[UID_INSTANCEID_SIZE];
93 static const uint8_t FRAME_TYPE_UID = 0x00;
94 static const uint8_t FRAME_TYPE_URL = 0x10;
95 static const uint8_t FRAME_TYPE_TLM = 0x20;
97 static const uint8_t FRAME_SIZE_TLM = 14;
98 static const uint8_t FRAME_SIZE_UID = 20;
110 UIDNamespaceID_t namespaceID,
111 UIDInstanceID_t instanceID,
112 float uidAdvPeriodIn,
113 uint16_t RFU = 0x0000) {
114 if (0.0f == uidAdvPeriodIn) {
125 defaultUidPower = power;
126 memcpy(defaultUidNamespaceID, namespaceID, UID_NAMESPACEID_SIZE);
127 memcpy(defaultUidInstanceID, instanceID, UID_INSTANCEID_SIZE);
128 uidRFU = (uint16_t)RFU;
129 uidAdvPeriod = uidAdvPeriodIn;
139 unsigned constructUIDFrame(uint8_t *Data, uint8_t maxSize) {
142 Data[index++] = FRAME_TYPE_UID;
144 if (defaultUidPower > 20) {
145 defaultUidPower = 20;
147 if (defaultUidPower < -100) {
148 defaultUidPower = -100;
150 Data[index++] = defaultUidPower;
152 DBG(
"UID NamespaceID = '0x");
153 for (
size_t x = 0; x < UID_NAMESPACEID_SIZE; x++) {
154 Data[index++] = defaultUidNamespaceID[x];
155 DBG(
"%x,", defaultUidNamespaceID[x]);
159 DBG(
"UID InstanceID = '0x");
160 for (
size_t x = 0; x< UID_INSTANCEID_SIZE; x++) {
161 Data[index++] = defaultUidInstanceID[x];
162 DBG(
"%x,", defaultUidInstanceID[x]);
167 Data[index++] = (uint8_t)(uidRFU >> 0);
168 Data[index++] = (uint8_t)(uidRFU >> 8);
170 DBG(
"construcUIDFrame %d, %d", maxSize, index);
182 if (0.0f == urlAdvPeriodIn) {
186 encodeURL(urlIn, defaultUriData, defaultUriDataLength);
187 if (defaultUriDataLength > URI_DATA_MAX) {
190 defaultUrlPower = power;
191 urlAdvPeriod = urlAdvPeriodIn;
205 if (0.0f == urlAdvPeriodIn) {
209 memcpy(defaultUriData, encodedUrlIn, encodedUrlInLength);
210 if (defaultUriDataLength > URI_DATA_MAX) {
213 defaultUrlPower = power;
214 defaultUriDataLength = encodedUrlInLength;
215 urlAdvPeriod = urlAdvPeriodIn;
226 int constructURLFrame(uint8_t *Data, uint8_t maxSize) {
228 Data[index++] = FRAME_TYPE_URL;
229 Data[index++] = defaultUrlPower;
230 for (
int x = 0; x < defaultUriDataLength; x++) {
231 Data[index++] = defaultUriData[x];
233 DBG(
"constructURLFrame: %d, %d", maxSize, index);
245 void setTLMFrameData(uint8_t version = 0,
246 float advPeriod = 60.0f,
247 uint16_t batteryVoltage = 0,
248 uint16_t beaconTemp = 0x8000,
249 uint32_t pduCount = 0,
250 uint32_t timeSinceBoot = 0) {
251 if (0.0f == advPeriod) {
255 TlmVersion = version;
256 TlmBatteryVoltage = batteryVoltage;
257 TlmBeaconTemp = beaconTemp;
258 TlmPduCount = pduCount;
259 TlmTimeSinceBoot = timeSinceBoot;
260 TlmAdvPeriod = advPeriod;
270 int constructTLMFrame(uint8_t *Data, uint8_t maxSize) {
271 uint32_t now = timeSinceBootTimer.read_ms();
272 TlmTimeSinceBoot += (now - lastBootTimerRead) / 100;
273 lastBootTimerRead = now;
276 Data[index++] = FRAME_TYPE_TLM;
277 Data[index++] = TlmVersion;
278 Data[index++] = (uint8_t)(TlmBatteryVoltage >> 8);
279 Data[index++] = (uint8_t)(TlmBatteryVoltage >> 0);
280 Data[index++] = (uint8_t)(TlmBeaconTemp >> 8);
281 Data[index++] = (uint8_t)(TlmBeaconTemp >> 0);
282 Data[index++] = (uint8_t)(TlmPduCount >> 24);
283 Data[index++] = (uint8_t)(TlmPduCount >> 16);
284 Data[index++] = (uint8_t)(TlmPduCount >> 8);
285 Data[index++] = (uint8_t)(TlmPduCount >> 0);
286 Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 24);
287 Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 16);
288 Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 8);
289 Data[index++] = (uint8_t)(TlmTimeSinceBoot >> 0);
290 DBG(
"constructURLFrame: %d, %d", maxSize, index);
299 void updateTlmBatteryVoltage(uint16_t voltagemv) {
300 TlmBatteryVoltage = voltagemv;
308 void updateTlmBeaconTemp(uint16_t temp) {
309 TlmBeaconTemp = temp;
317 void updateTlmPduCount(uint32_t pduCount) {
318 TlmPduCount = pduCount;
326 void updateTlmTimeSinceBoot(uint32_t timeSinceBoot) {
327 TlmTimeSinceBoot = timeSinceBoot;
334 bool updateAdvPacket(uint8_t serviceData[],
unsigned serviceDataLen) {
336 DBG(
"Updating AdvFrame: %d", serviceDataLen);
338 ble.clearAdvertisingPayload();
340 ble.setAdvertisingInterval(100);
355 void swapOutFrames(FrameTypes frameType) {
356 uint8_t serviceData[SERVICE_DATA_MAX];
357 unsigned serviceDataLen = 0;
359 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[0];
360 serviceData[serviceDataLen++] = BEACON_EDDYSTONE[1];
367 DBG(
"Swapping in TLM Frame: version=%x, Batt=%d, Temp = %d, PDUCnt = %d, TimeSinceBoot=%d",
373 serviceDataLen += constructTLMFrame(serviceData + serviceDataLen, 20);
374 DBG(
"\t Swapping in TLM Frame: len=%d", serviceDataLen);
375 updateAdvPacket(serviceData, serviceDataLen);
381 DBG(
"Swapping in URL Frame: Power: %d", defaultUrlPower);
382 serviceDataLen += constructURLFrame(serviceData + serviceDataLen, 20);
383 DBG(
"\t Swapping in URL Frame: len=%d ", serviceDataLen);
384 updateAdvPacket(serviceData, serviceDataLen);
391 DBG(
"Swapping in UID Frame: Power: %d", defaultUidPower);
392 serviceDataLen += constructUIDFrame(serviceData + serviceDataLen, 20);
393 DBG(
"\t Swapping in UID Frame: len=%d", serviceDataLen);
394 updateAdvPacket(serviceData, serviceDataLen);
399 ERR(
"You have not initialized a Frame yet, please initialize one before starting a beacon");
400 ERR(
"uidIsSet = %d, urlIsSet = %d, tlmIsSet = %d", uidIsSet, urlIsSet, tlmIsSet);
407 void urlCallback(
void) {
409 if (
false == advLock) {
413 swapOutFrames(frameIndex);
414 ble.startAdvertising();
417 INFO(
"URI(%d) cannot complete, %d is currently broadcasting", url, frameIndex);
426 void uidCallback(
void) {
428 if (
false == advLock) {
432 swapOutFrames(frameIndex);
433 ble.startAdvertising();
436 INFO(
"UID(%d) cannot complete, %d is currently broadcasting", uid, frameIndex);
445 void tlmCallback(
void) {
447 if (
false == advLock) {
452 swapOutFrames(frameIndex);
453 ble.startAdvertising();
456 INFO(
"TLM(%d) cannot complete, %d is currently broadcasting", tlm, frameIndex);
462 void stopAdvCallback(
void) {
463 if (overflow.
empty()) {
465 ble.stopAdvertising();
471 INFO(
"Re-Transmitting %d", x);
479 #define EDDYSTONE_SWAPFRAME_DELAYMS 1 480 void radioNotificationCallback(
bool radioActive) {
488 stopAdv.
attach_us(
this, &EddystoneService::stopAdvCallback, 1);
502 uint16_t beaconPeriodus = 100,
503 uint8_t txPowerIn = 0) :
505 advPeriodus(beaconPeriodus),
518 urlTicker.
attach(
this, &EddystoneService::urlCallback, (
float) advPeriodus / 1000.0f);
519 DBG(
"attached urlCallback every %d seconds", urlAdvPeriod);
523 uidTicker.
attach(
this, &EddystoneService::uidCallback, uidAdvPeriod);
524 DBG(
"attached uidCallback every %d seconds", uidAdvPeriod);
529 updateTlmPduCount(0);
530 updateTlmTimeSinceBoot(0);
531 lastBootTimerRead = 0;
532 timeSinceBootTimer.start();
533 tlmTicker.
attach(
this, &EddystoneService::tlmCallback, TlmAdvPeriod);
534 DBG(
"attached tlmCallback every %d seconds", TlmAdvPeriod);
536 if (NONE == frameIndex) {
537 MBED_ERROR(
MBED_MAKE_ERROR(MBED_MODULE_BLE, MBED_ERROR_CODE_BLE_NO_FRAME_INITIALIZED),
"No Frames were Initialized! Please initialize a frame before starting an eddystone beacon.");
541 ble.setTxPower(txPower);
542 ble.gap().onRadioNotification(
this, &EddystoneService::radioNotificationCallback);
549 uint16_t advPeriodus;
551 mbed::Timer timeSinceBootTimer;
552 volatile uint32_t lastBootTimerRead;
553 volatile bool advLock;
554 volatile FrameTypes frameIndex;
559 uint8_t defaultUriDataLength;
560 UriData_t defaultUriData;
561 int8_t defaultUrlPower;
567 UIDNamespaceID_t defaultUidNamespaceID;
568 UIDInstanceID_t defaultUidInstanceID;
569 int8_t defaultUidPower;
577 volatile uint16_t TlmBatteryVoltage;
578 volatile uint16_t TlmBeaconTemp;
579 volatile uint32_t TlmPduCount;
580 volatile uint32_t TlmTimeSinceBoot;
589 static void encodeURL(
const char *uriDataIn, UriData_t uriDataOut, uint8_t &sizeofURIDataOut) {
590 DBG(
"Encode URL = %s", uriDataIn);
591 const char *prefixes[] = {
597 const size_t NUM_PREFIXES =
sizeof(prefixes) /
sizeof(
char *);
598 const char *suffixes[] = {
614 const size_t NUM_SUFFIXES =
sizeof(suffixes) /
sizeof(
char *);
616 sizeofURIDataOut = 0;
617 memset(uriDataOut, 0,
sizeof(UriData_t));
619 if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
626 for (
unsigned i = 0; i < NUM_PREFIXES; i++) {
627 size_t prefixLen = strlen(prefixes[i]);
628 if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
629 uriDataOut[sizeofURIDataOut++] = i;
630 uriDataIn += prefixLen;
638 while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
641 for (i = 0; i < NUM_SUFFIXES; i++) {
642 size_t suffixLen = strlen(suffixes[i]);
643 if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
644 uriDataOut[sizeofURIDataOut++] = i;
645 uriDataIn += suffixLen;
650 INFO(
"Encoding URI: No Suffix Found");
651 if (i == NUM_SUFFIXES) {
652 uriDataOut[sizeofURIDataOut++] = *uriDataIn;
659 #endif // BLE_FEATURE_GATT_SERVER 661 #endif // SERVICES_EDDYSTONEBEACON_H_ Templated Circular buffer class.
Abstract away BLE-capable radio transceivers or SOCs.
Peripheral device is discoverable at any moment.
Complete list of 16-bit Service IDs.
bool setURLFrameData(int8_t power, const char *urlIn, float urlAdvPeriodIn)
Set Eddystone URL Frame information.
void push(const T &data)
Push the transaction to the buffer.
bool pop(T &data)
Pop the transaction from the buffer.
Peripheral device is LE only and does not support Bluetooth Enhanced DataRate.
Device is not connectable and not scannable.
void attach_us(Callback< void()> func, us_timestamp_t t)
Attach a function to be called by the Ticker, specifying the interval in microseconds.
A Ticker is used to call a function at a recurring interval.
bool setURLFrameEncodedData(int8_t power, const char *encodedUrlIn, uint8_t encodedUrlInLength, float urlAdvPeriodIn)
Set Eddystone URL Frame information.
void attach(Callback< void()> func, float t)
Attach a function to be called by the Ticker, specifying the interval in seconds. ...
Entry namespace for all BLE API definitions.
void setUIDFrameData(int8_t power, UIDNamespaceID_t namespaceID, UIDInstanceID_t instanceID, float uidAdvPeriodIn, uint16_t RFU=0x0000)
Set Eddystone UID Frame information.
bool empty() const
Check if the buffer is empty.
A Timeout is used to call a function at a point in the future.