Kenji Arai / TYBLE16_mbedlized_os5_several_examples_2nd

Dependencies:   nRF51_Vdd

Fork of TYBLE16_mbedlized_os5_several_examples by Kenji Arai

Committer:
kenjiArai
Date:
Tue Feb 12 20:58:56 2019 +0000
Revision:
4:416b3d1e1481
mbed-os5 revision up

Who changed what in which revision?

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