Mistake on this page?
Report an issue in GitHub or email us
URIBeaconConfigService.h
1 /* mbed Microcontroller Library
2  * Copyright (c) 2006-2013 ARM Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SERVICES_URIBEACONCONFIGSERVICE_H_
18 #define SERVICES_URIBEACONCONFIGSERVICE_H_
19 
20 #include "ble/BLE.h"
21 #include "ble/pal/Deprecated.h"
22 
23 #ifdef YOTTA_CFG_MBED_OS
24 #include "mbed-drivers/mbed.h"
25 #else
26 
27 #endif
28 
29 #if BLE_FEATURE_GATT_SERVER
30 #if BLE_ROLE_BROADCASTER
31 
32 BLE_DEPRECATED_API_USE_BEGIN()
33 
34 extern const uint8_t UUID_URI_BEACON_SERVICE[UUID::LENGTH_OF_LONG_UUID];
35 extern const uint8_t UUID_LOCK_STATE_CHAR[UUID::LENGTH_OF_LONG_UUID];
36 extern const uint8_t UUID_LOCK_CHAR[UUID::LENGTH_OF_LONG_UUID];
37 extern const uint8_t UUID_UNLOCK_CHAR[UUID::LENGTH_OF_LONG_UUID];
38 extern const uint8_t UUID_URI_DATA_CHAR[UUID::LENGTH_OF_LONG_UUID];
39 extern const uint8_t UUID_FLAGS_CHAR[UUID::LENGTH_OF_LONG_UUID];
40 extern const uint8_t UUID_ADV_POWER_LEVELS_CHAR[UUID::LENGTH_OF_LONG_UUID];
41 extern const uint8_t UUID_TX_POWER_MODE_CHAR[UUID::LENGTH_OF_LONG_UUID];
42 extern const uint8_t UUID_BEACON_PERIOD_CHAR[UUID::LENGTH_OF_LONG_UUID];
43 extern const uint8_t UUID_RESET_CHAR[UUID::LENGTH_OF_LONG_UUID];
44 
45 extern const uint8_t BEACON_UUID[sizeof(UUID::ShortUUIDBytes_t)];
46 
47 /**
48  * @class URIBeaconConfigService
49  * @brief UriBeacon Configuration Service. You can use this to set URL, adjust power levels and set flags.
50  * See http://uribeacon.org
51  *
52  * @deprecated This service is deprecated, and no replacement is currently available.
53  */
55  "mbed-os-5.11",
56  "This service is deprecated, and no replacement is currently available."
57 )
59  public:
60  /**
61  * @brief Transmission power modes for UriBeacon.
62  */
63  static const uint8_t TX_POWER_MODE_LOWEST = 0; /*!< Lowest TX power mode. */
64  static const uint8_t TX_POWER_MODE_LOW = 1; /*!< Low TX power mode. */
65  static const uint8_t TX_POWER_MODE_MEDIUM = 2; /*!< Medium TX power mode. */
66  static const uint8_t TX_POWER_MODE_HIGH = 3; /*!< High TX power mode. */
67  static const unsigned NUM_POWER_MODES = 4; /*!< Number of power modes defined. */
68 
69  static const int ADVERTISING_INTERVAL_MSEC = 1000; // Advertising interval for config service.
70  static const int SERVICE_DATA_MAX = 31; // Maximum size of service data in ADV packets.
71 
72  typedef uint8_t Lock_t[16]; /* 128 bits. */
73  typedef int8_t PowerLevels_t[NUM_POWER_MODES];
74 
75  static const int URI_DATA_MAX = 18;
76  typedef uint8_t UriData_t[URI_DATA_MAX];
77 
78  struct Params_t {
79  Lock_t lock;
80  uint8_t uriDataLength;
81  UriData_t uriData;
82  uint8_t flags;
83  PowerLevels_t advPowerLevels; // Current value of AdvertisedPowerLevels.
84  uint8_t txPowerMode; // Firmware power levels used with setTxPower().
85  uint16_t beaconPeriod;
86  };
87 
88  /**
89  * @param[in] bleIn
90  * BLE object for the underlying controller.
91  * @param[in,out] paramsIn
92  * Reference to application-visible beacon state, loaded
93  * from persistent storage at startup.
94  * @param[in] resetToDefaultsFlag
95  * Applies to the state of the 'paramsIn' parameter.
96  * If true, it indicates that paramsIn is potentially
97  * un-initialized, and default values should be used
98  * instead. Otherwise, paramsIn overrides the defaults.
99  * @param[in] defaultURIDataIn
100  * Default un-encoded URI. Applies only if the resetToDefaultsFlag is true.
101  * @param[in] defaultAdvPowerLevelsIn
102  * Default power-levels array. Applies only if the resetToDefaultsFlag is true.
103  *
104  * @deprecated This service is deprecated, and no replacement is currently available.
105  */
107  "mbed-os-5.11",
108  "This service is deprecated, and no replacement is currently available."
109  )
110  URIBeaconConfigService(BLE &bleIn,
111  Params_t &paramsIn,
112  bool resetToDefaultsFlag,
113  const char *defaultURIDataIn,
114  PowerLevels_t &defaultAdvPowerLevelsIn) :
115  ble(bleIn),
116  params(paramsIn),
117  defaultUriDataLength(),
118  defaultUriData(),
119  defaultAdvPowerLevels(defaultAdvPowerLevelsIn),
120  initSucceeded(false),
121  resetFlag(),
122  lockedStateChar(UUID_LOCK_STATE_CHAR, &lockedState),
123  lockChar(UUID_LOCK_CHAR, &params.lock),
124  uriDataChar(UUID_URI_DATA_CHAR, params.uriData, 0, URI_DATA_MAX,
125  GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_WRITE),
126  unlockChar(UUID_UNLOCK_CHAR, &params.lock),
127  flagsChar(UUID_FLAGS_CHAR, &params.flags),
128  advPowerLevelsChar(UUID_ADV_POWER_LEVELS_CHAR, &params.advPowerLevels),
129  txPowerModeChar(UUID_TX_POWER_MODE_CHAR, &params.txPowerMode),
130  beaconPeriodChar(UUID_BEACON_PERIOD_CHAR, &params.beaconPeriod),
131  resetChar(UUID_RESET_CHAR, &resetFlag) {
132 
133  encodeURI(defaultURIDataIn, defaultUriData, defaultUriDataLength);
134  if (defaultUriDataLength > URI_DATA_MAX) {
135  return;
136  }
137 
138  if (!resetToDefaultsFlag && (params.uriDataLength > URI_DATA_MAX)) {
139  resetToDefaultsFlag = true;
140  }
141  if (resetToDefaultsFlag) {
142  resetToDefaults();
143  } else {
144  updateCharacteristicValues();
145  }
146 
147  lockedState = isLocked();
148 
149  lockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::lockAuthorizationCallback);
150  unlockChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::unlockAuthorizationCallback);
151  uriDataChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::uriDataWriteAuthorizationCallback);
152  flagsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint8_t>);
153  advPowerLevelsChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<PowerLevels_t>);
154  txPowerModeChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::powerModeAuthorizationCallback);
155  beaconPeriodChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint16_t>);
156  resetChar.setWriteAuthorizationCallback(this, &URIBeaconConfigService::basicAuthorizationCallback<uint8_t>);
157 
158  static GattCharacteristic *charTable[] = {
159  &lockedStateChar, &lockChar, &unlockChar, &uriDataChar,
160  &flagsChar, &advPowerLevelsChar, &txPowerModeChar, &beaconPeriodChar, &resetChar
161  };
162 
163  GattService configService(UUID_URI_BEACON_SERVICE, charTable, sizeof(charTable) / sizeof(GattCharacteristic *));
164 
165  ble.addService(configService);
166  ble.onDataWritten(this, &URIBeaconConfigService::onDataWrittenCallback);
167 
168  setupURIBeaconConfigAdvertisements(); /* Set up advertising for the config service. */
169 
170  initSucceeded = true;
171  }
172 
173  bool configuredSuccessfully(void) const {
174  return initSucceeded;
175  }
176 
177  /* Start out by advertising the config service for a limited time after
178  * startup. Then switch to the normal non-connectible beacon functionality.
179  */
180  void setupURIBeaconConfigAdvertisements()
181  {
182  const char DEVICE_NAME[] = "mUriBeacon Config";
183 
184  ble.gap().clearAdvertisingPayload();
185 
187 
188  // UUID is in different order in the ADV frame (!)
189  uint8_t reversedServiceUUID[sizeof(UUID_URI_BEACON_SERVICE)];
190  for (unsigned int i = 0; i < sizeof(UUID_URI_BEACON_SERVICE); i++) {
191  reversedServiceUUID[i] = UUID_URI_BEACON_SERVICE[sizeof(UUID_URI_BEACON_SERVICE) - i - 1];
192  }
193  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, reversedServiceUUID, sizeof(reversedServiceUUID));
194  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_TAG);
195  ble.gap().accumulateScanResponse(GapAdvertisingData::COMPLETE_LOCAL_NAME, reinterpret_cast<const uint8_t *>(&DEVICE_NAME), sizeof(DEVICE_NAME));
196  ble.gap().accumulateScanResponse(GapAdvertisingData::TX_POWER_LEVEL,
197  reinterpret_cast<uint8_t *>(&defaultAdvPowerLevels[URIBeaconConfigService::TX_POWER_MODE_LOW]),
198  sizeof(uint8_t));
199 
200  ble.gap().setTxPower(params.advPowerLevels[params.txPowerMode]);
201  ble.gap().setDeviceName(reinterpret_cast<const uint8_t *>(&DEVICE_NAME));
202  ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
203  ble.gap().setAdvertisingInterval(GapAdvertisingParams::MSEC_TO_ADVERTISEMENT_DURATION_UNITS(ADVERTISING_INTERVAL_MSEC));
204  }
205 
206  /* Helper function to switch to the non-connectible normal mode for UriBeacon. This gets called after a timeout. */
207  void setupURIBeaconAdvertisements()
208  {
209  /* Reinitialize the BLE stack. This will clear away the existing services and advertising state. */
210  ble.shutdown();
211  ble.init();
212 
213  // Fields from the service.
214  unsigned beaconPeriod = params.beaconPeriod;
215  unsigned txPowerMode = params.txPowerMode;
216  unsigned uriDataLength = params.uriDataLength;
217  URIBeaconConfigService::UriData_t &uriData = params.uriData;
218  URIBeaconConfigService::PowerLevels_t &advPowerLevels = params.advPowerLevels;
219  uint8_t flags = params.flags;
220 
221  extern void saveURIBeaconConfigParams(const Params_t *paramsP); /* Forward declaration; necessary to avoid a circular dependency. */
222  saveURIBeaconConfigParams(&params);
223 
224  ble.gap().clearAdvertisingPayload();
225  ble.gap().setTxPower(params.advPowerLevels[params.txPowerMode]);
227  ble.gap().setAdvertisingInterval(beaconPeriod);
229  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, BEACON_UUID, sizeof(BEACON_UUID));
230 
231  uint8_t serviceData[SERVICE_DATA_MAX];
232  unsigned serviceDataLen = 0;
233  serviceData[serviceDataLen++] = BEACON_UUID[0];
234  serviceData[serviceDataLen++] = BEACON_UUID[1];
235  serviceData[serviceDataLen++] = flags;
236  serviceData[serviceDataLen++] = advPowerLevels[txPowerMode];
237  for (unsigned j = 0; j < uriDataLength; j++) {
238  serviceData[serviceDataLen++] = uriData[j];
239  }
240  ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::SERVICE_DATA, serviceData, serviceDataLen);
241  }
242 
243  private:
244  // True if the lock bits are non-zero.
245  bool isLocked() {
246  Lock_t testLock;
247  memset(testLock, 0, sizeof(Lock_t));
248  return memcmp(params.lock, testLock, sizeof(Lock_t));
249  }
250 
251  /*
252  * This callback is invoked when a GATT client attempts to modify any of the
253  * characteristics of this service. These attempts are also applied to
254  * the internal state of this service object.
255  */
256  void onDataWrittenCallback(const GattWriteCallbackParams *writeParams) {
257  uint16_t handle = writeParams->handle;
258 
259  if (handle == lockChar.getValueHandle()) {
260  // Validated earlier,
261  memcpy(params.lock, writeParams->data, sizeof(Lock_t));
262  // Use isLocked() in case bits are being set to all zeros.
263  lockedState = isLocked();
264  } else if (handle == unlockChar.getValueHandle()) {
265  // Validated earlier.
266  memset(params.lock, 0, sizeof(Lock_t));
267  lockedState = false;
268  } else if (handle == uriDataChar.getValueHandle()) {
269  params.uriDataLength = writeParams->len;
270  memcpy(params.uriData, writeParams->data, params.uriDataLength);
271  } else if (handle == flagsChar.getValueHandle()) {
272  params.flags = *(writeParams->data);
273  } else if (handle == advPowerLevelsChar.getValueHandle()) {
274  memcpy(params.advPowerLevels, writeParams->data, sizeof(PowerLevels_t));
275  } else if (handle == txPowerModeChar.getValueHandle()) {
276  params.txPowerMode = *(writeParams->data);
277  } else if (handle == beaconPeriodChar.getValueHandle()) {
278  params.beaconPeriod = *((uint16_t *)(writeParams->data));
279 
280  /* Remap beaconPeriod to within permissible bounds if necessary. */
281  if (params.beaconPeriod != 0) {
282  bool paramsUpdated = false;
283  if (params.beaconPeriod < ble.gap().getMinAdvertisingInterval()) {
284  params.beaconPeriod = ble.gap().getMinAdvertisingInterval();
285  paramsUpdated = true;
286  } else if (params.beaconPeriod > ble.gap().getMaxAdvertisingInterval()) {
287  params.beaconPeriod = ble.gap().getMaxAdvertisingInterval();
288  paramsUpdated = true;
289  }
290  if (paramsUpdated) {
291  ble.gattServer().write(beaconPeriodChar.getValueHandle(), reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
292  }
293  }
294  } else if (handle == resetChar.getValueHandle()) {
295  resetToDefaults();
296  }
297  }
298 
299  /*
300  * Reset the default values.
301  */
302  void resetToDefaults(void) {
303  lockedState = false;
304  memset(params.lock, 0, sizeof(Lock_t));
305  memcpy(params.uriData, defaultUriData, URI_DATA_MAX);
306  params.uriDataLength = defaultUriDataLength;
307  params.flags = 0;
308  memcpy(params.advPowerLevels, defaultAdvPowerLevels, sizeof(PowerLevels_t));
309  params.txPowerMode = TX_POWER_MODE_LOW;
310  params.beaconPeriod = 1000;
311  updateCharacteristicValues();
312  }
313 
314  /*
315  * Internal helper function used to update the GATT database following any
316  * change to the internal state of the service object.
317  */
318  void updateCharacteristicValues(void) {
319  ble.gattServer().write(lockedStateChar.getValueHandle(), &lockedState, 1);
320  ble.gattServer().write(uriDataChar.getValueHandle(), params.uriData, params.uriDataLength);
321  ble.gattServer().write(flagsChar.getValueHandle(), &params.flags, 1);
322  ble.gattServer().write(beaconPeriodChar.getValueHandle(),
323  reinterpret_cast<uint8_t *>(&params.beaconPeriod), sizeof(uint16_t));
324  ble.gattServer().write(txPowerModeChar.getValueHandle(), &params.txPowerMode, 1);
325  ble.gattServer().write(advPowerLevelsChar.getValueHandle(),
326  reinterpret_cast<uint8_t *>(params.advPowerLevels), sizeof(PowerLevels_t));
327  }
328 
329 protected:
330  void lockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
331  if (lockedState) {
333  } else if (authParams->len != sizeof(Lock_t)) {
335  } else if (authParams->offset != 0) {
337  } else {
339  }
340  }
341 
342 
343  void unlockAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
344  if (!lockedState) {
346  } else if (authParams->len != sizeof(Lock_t)) {
348  } else if (authParams->offset != 0) {
350  } else if (memcmp(authParams->data, params.lock, sizeof(Lock_t)) != 0) {
352  } else {
354  }
355  }
356 
357  void uriDataWriteAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
358  if (lockedState) {
360  } else if (authParams->offset != 0) {
362  } else {
364  }
365  }
366 
367  void powerModeAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
368  if (lockedState) {
370  } else if (authParams->len != sizeof(uint8_t)) {
372  } else if (authParams->offset != 0) {
374  } else if (*((uint8_t *)authParams->data) >= NUM_POWER_MODES) {
376  } else {
378  }
379  }
380 
381  template <typename T>
382  void basicAuthorizationCallback(GattWriteAuthCallbackParams *authParams) {
383  if (lockedState) {
385  } else if (authParams->len != sizeof(T)) {
387  } else if (authParams->offset != 0) {
389  } else {
391  }
392  }
393 
394 protected:
395  BLE &ble;
396  Params_t &params;
397 
398  size_t defaultUriDataLength; // Default value that is restored on reset.
399  UriData_t defaultUriData; // Default value that is restored on reset.
400  PowerLevels_t &defaultAdvPowerLevels; // Default value that is restored on reset.
401 
402  uint8_t lockedState;
403  bool initSucceeded;
404  uint8_t resetFlag;
405 
406  ReadOnlyGattCharacteristic<uint8_t> lockedStateChar;
408  GattCharacteristic uriDataChar;
412  ReadWriteGattCharacteristic<uint8_t> txPowerModeChar;
413  ReadWriteGattCharacteristic<uint16_t> beaconPeriodChar;
415 
416 public:
417  /*
418  * Encode a human-readable URI into the binary format defined by the UriBeacon spec (https://github.com/google/uribeacon/tree/master/specification).
419  */
420  static void encodeURI(const char *uriDataIn, UriData_t uriDataOut, size_t &sizeofURIDataOut) {
421  const char *prefixes[] = {
422  "http://www.",
423  "https://www.",
424  "http://",
425  "https://",
426  "urn:uuid:"
427  };
428  const size_t NUM_PREFIXES = sizeof(prefixes) / sizeof(char *);
429  const char *suffixes[] = {
430  ".com/",
431  ".org/",
432  ".edu/",
433  ".net/",
434  ".info/",
435  ".biz/",
436  ".gov/",
437  ".com",
438  ".org",
439  ".edu",
440  ".net",
441  ".info",
442  ".biz",
443  ".gov"
444  };
445  const size_t NUM_SUFFIXES = sizeof(suffixes) / sizeof(char *);
446 
447  sizeofURIDataOut = 0;
448  memset(uriDataOut, 0, sizeof(UriData_t));
449 
450  if ((uriDataIn == NULL) || (strlen(uriDataIn) == 0)) {
451  return;
452  }
453 
454  /*
455  * handle prefix
456  */
457  for (unsigned i = 0; i < NUM_PREFIXES; i++) {
458  size_t prefixLen = strlen(prefixes[i]);
459  if (strncmp(uriDataIn, prefixes[i], prefixLen) == 0) {
460  uriDataOut[sizeofURIDataOut++] = i;
461  uriDataIn += prefixLen;
462  break;
463  }
464  }
465 
466  /*
467  * Handle suffixes.
468  */
469  while (*uriDataIn && (sizeofURIDataOut < URI_DATA_MAX)) {
470  /* check for suffix match */
471  unsigned i;
472  for (i = 0; i < NUM_SUFFIXES; i++) {
473  size_t suffixLen = strlen(suffixes[i]);
474  if (strncmp(uriDataIn, suffixes[i], suffixLen) == 0) {
475  uriDataOut[sizeofURIDataOut++] = i;
476  uriDataIn += suffixLen;
477  break; /* From the for loop for checking against suffixes. */
478  }
479  }
480  /* This is the default case where we've got an ordinary character that doesn't match a suffix. */
481  if (i == NUM_SUFFIXES) {
482  uriDataOut[sizeofURIDataOut++] = *uriDataIn;
483  ++uriDataIn;
484  }
485  }
486  }
487 };
488 
489 BLE_DEPRECATED_API_USE_END()
490 
491 #endif // BLE_ROLE_BROADCASTER
492 #endif // BLE_FEATURE_GATT_SERVER
493 
494 #endif // SERVICES_URIBEACONCONFIGSERVICE_H_
GattAuthCallbackReply_t authorizationReply
Authorization result.
const uint8_t * data
Incoming data.
const uint8_t * data
Pointer to the data to write.
static uint16_t MSEC_TO_ADVERTISEMENT_DURATION_UNITS(uint32_t durationInMillis)
Convert milliseconds to units of 0.625ms.
Abstract away BLE-capable radio transceivers or SOCs.
Definition: BLE.h:139
UriBeacon Configuration Service.
GATT write authorization request event.
Peripheral device is discoverable at any moment.
ATT Error: The specified offset was past the end of the attribute.
Representation of a Universally Unique Identifier (UUID).
Definition: UUID.h:74
GATT Write event definition.
uint16_t len
Length (in bytes) of the data to write.
Peripheral device is LE only and does not support Bluetooth Enhanced DataRate.
Representation of a GattServer characteristic.
Device is not connectable and not scannable.
uint16_t offset
Offset for the write operation.
uint16_t len
Length of the incoming data.
GattAttribute::Handle_t handle
Handle of the attribute to which the write operation applies.
Representation of a GattServer service.
Definition: GattService.h:38
Device is connectable, scannable and doesn&#39;t expect connection from a specific peer.
Entry namespace for all BLE API definitions.
ATT Error: Used in ATT as "insufficient authorization".
static const uint8_t TX_POWER_MODE_LOW
#define MBED_DEPRECATED_SINCE(D, M)
MBED_DEPRECATED("message string") Mark a function declaration as deprecated, if it used then a warnin...
Important Information for this Arm website

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies. If you are not happy with the use of these cookies, please review our Cookie Policy to learn how they can be disabled. By disabling cookies, some features of the site will not work.