Library that exposes BLE characteristics as ordinary variables.
Dependents: SimpleBLE-Example-mbed-os-5 SimpleBLE-Example ObCP_ENSMM_Test SimpleBLE-ObCP_ENSMM_V2019_Test_BLE
Looking for example code? See SimpleBLE-Example.
Looking for the blog post about this library? See A new Bluetooth library: SimpleBLE.
SimpleBLE is a library which allows you to easily add Bluetooth Low Energy support to any application, without having to change the way you write code. It's goal is to expose any value or sensor over BLE in a single line of code. Let's say you're reading a sensor value every second from pin A0:
uint16_t lightValue; AnalogIn light(A0); void read() { lightValue = light.read_u16(); printf("Value is now %d\n", lightValue); } Ticker t; t.attach(&read, 1.0f);
Now we can expose this sensor to any BLE client using SimpleBLE:
SimpleChar<uint16_t> lightValue = ble.readOnly_u16(0x8000, 0x8001); AnalogIn light(A0); void read() { lightValue = light.read_u16(); printf("Value is now %d\n", lightValue); } Ticker t; t.attach(&read, 1.0f);
SimpleBLE will automatically gather services for you, bootstrap the BLE runtime, react to write callbacks and update the underlying BLE characteristic whenever the variable gets changed.
Setting up the library
First import this library to the application where you want to use it. Then load SimpleBLE like this:
#include "mbed.h" #include "SimpleBLE.h" SimpleBLE ble("DEVICE_NAME"); // second argument is the advertisement interval (default: 1000 ms.) // variables here! int main(int, char**) { ble.start(); while (1) { ble.waitForEvent(); } }
You'll also need to import the BLE library for your platform (e.g. nrf51822 for nRF51-DK and micro:bit). If you grab the example program, all should be set.
After you build and flash this application the device will now show up as 'DEVICE_NAME' in any BLE scanner like nRF Master Control Panel.
Reading and writing values
Every variable that you declare after creating the `SimpleBLE` object, and calling `start()` will automatically be exposed over BLE. We have three different types of objects:
- readOnly - Variable can only be read over BLE.
- readWrite - Variable can be read and written over BLE.
- writeOnly - Variable can be written over BLE, but not read.
Creating a new variable is done by calling the respective function on the simpleBLE object, postfixed by the type you want the variable to have:
SimpleBLE ble("DEVICE_NAME"); SimpleChar<uint8_t> myVar = ble.readOnly_u8(0x8200, 0x8201); // creates uint8_t
The postfix has to be either: u8 (uint8_t), u16 (uint16_t), u32 (uint32_t), i8 (int8_t), i16 (int16_t), i32 (int32_t), bool (bool) or float (float).
The functions take four (non-write) or five (write) arguments:
- serviceUUID - Which service should this variable fall under. Either use a uint16_t (shorthand) or you can use a string (full UUID).
- characterUUID - Which character this variable should have. Also, use uint16_t or a string.
- observable - Whether a BLE client can subscribe to updates on this characteristic (default: true).
- defaultValue - Default value of the characteristic (default: 0 or 0.0f, depending on your type).
- callback - Function to be called whenever the value of your characteristic was updated over BLE (only available for variables that support writing).
For example, this is how we make a non-observable readWrite characteristic, with a default value of 100, and a callback function:
SimpleBLE ble("DEVICE_NAME"); void updated(uint32_t newValue) { printf("My value was updated, and is now %d\n", newValue); } SimpleChar<uint32_t> myVar = ble.readWrite_u32(0x9341, 0x9342, false, 100, &updated);
Just like normal variables
SimpleBLE variables behave just like normal variables, you can read and write straight from them. For example, this will compile just fine:
SimpleChar<uint8_t> heartrate = ble.readOnly_u8(0x180d, 0x2a37, true, 100); void updateHr() { heartrate = heartrate + 1; if (heartrate > 180) { heartrate = 100; } } Ticker t; t.attach(updateHr, 1.0f);
What to use it for?
I wrote this library because I do quite a lot of workshops and hackathons, and I have seen that BLE_API is too complicated for people who do not do embedded development or C++ on a daily basis. Exposing a new sensor over Bluetooth should be a single line of code. So if you're running any workshops, give this lib a go and let me know your experiences.
SimpleBLE.h@8:953b3164e80f, 2018-02-19 (annotated)
- Committer:
- Jan Jongboom
- Date:
- Mon Feb 19 13:29:45 2018 +0800
- Revision:
- 8:953b3164e80f
- Parent:
- 6:15ec6cd75928
Revert back to 15329a3de04c, mbed os 5 fork now lives in separate repository
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
janjongboom | 1:17c8f5afa7bc | 1 | /* |
janjongboom | 1:17c8f5afa7bc | 2 | * Copyright (c) 2015 ARM Limited. All rights reserved. |
janjongboom | 1:17c8f5afa7bc | 3 | * SPDX-License-Identifier: Apache-2.0 |
janjongboom | 1:17c8f5afa7bc | 4 | * Licensed under the Apache License, Version 2.0 (the License); you may |
janjongboom | 1:17c8f5afa7bc | 5 | * not use this file except in compliance with the License. |
janjongboom | 1:17c8f5afa7bc | 6 | * You may obtain a copy of the License at |
janjongboom | 1:17c8f5afa7bc | 7 | * |
janjongboom | 1:17c8f5afa7bc | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
janjongboom | 1:17c8f5afa7bc | 9 | * |
janjongboom | 1:17c8f5afa7bc | 10 | * Unless required by applicable law or agreed to in writing, software |
janjongboom | 1:17c8f5afa7bc | 11 | * distributed under the License is distributed on an AS IS BASIS, WITHOUT |
janjongboom | 1:17c8f5afa7bc | 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
janjongboom | 1:17c8f5afa7bc | 13 | * See the License for the specific language governing permissions and |
janjongboom | 1:17c8f5afa7bc | 14 | * limitations under the License. |
janjongboom | 0:2ecd71f6ab04 | 15 | */ |
Jan Jongboom |
8:953b3164e80f | 16 | |
janjongboom | 0:2ecd71f6ab04 | 17 | #include <string> |
janjongboom | 0:2ecd71f6ab04 | 18 | #include <sstream> |
janjongboom | 0:2ecd71f6ab04 | 19 | #include <vector> |
janjongboom | 0:2ecd71f6ab04 | 20 | #include <map> |
janjongboom | 0:2ecd71f6ab04 | 21 | #include "ble/BLE.h" |
janjongboom | 0:2ecd71f6ab04 | 22 | |
janjongboom | 1:17c8f5afa7bc | 23 | #define def_fn(T, postfix) \ |
janjongboom | 1:17c8f5afa7bc | 24 | SimpleChar<T> readOnly_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 25 | (uint16_t serviceUuid, const UUID& charUuid, bool enableNotify = true, T defaultValue = T()) { \ |
janjongboom | 1:17c8f5afa7bc | 26 | return readOnly<T>(serviceUuid, charUuid, enableNotify, defaultValue); \ |
janjongboom | 1:17c8f5afa7bc | 27 | }\ |
janjongboom | 1:17c8f5afa7bc | 28 | SimpleChar<T> readOnly_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 29 | (const char* serviceUuid, const UUID& charUuid, bool enableNotify = true, T defaultValue = T()) { \ |
janjongboom | 1:17c8f5afa7bc | 30 | return readOnly<T>(serviceUuid, charUuid, enableNotify, defaultValue); \ |
janjongboom | 1:17c8f5afa7bc | 31 | }\ |
janjongboom | 1:17c8f5afa7bc | 32 | \ |
janjongboom | 1:17c8f5afa7bc | 33 | SimpleChar<T> readWrite_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 34 | (uint16_t serviceUuid, const UUID& charUuid, bool enableNotify = true, T defaultValue = T(), void(*callback)(T) = NULL) { \ |
janjongboom | 1:17c8f5afa7bc | 35 | return readWrite<T>(serviceUuid, charUuid, enableNotify, defaultValue, callback); \ |
janjongboom | 1:17c8f5afa7bc | 36 | }\ |
janjongboom | 1:17c8f5afa7bc | 37 | SimpleChar<T> readWrite_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 38 | (const char* serviceUuid, const UUID& charUuid, bool enableNotify = true, T defaultValue = T(), void(*callback)(T) = NULL) { \ |
janjongboom | 1:17c8f5afa7bc | 39 | return readWrite<T>(serviceUuid, charUuid, enableNotify, defaultValue, callback); \ |
janjongboom | 1:17c8f5afa7bc | 40 | }\ |
janjongboom | 1:17c8f5afa7bc | 41 | SimpleChar<T> readWrite_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 42 | (uint16_t serviceUuid, const UUID& charUuid, void(*callback)(T) = NULL) { \ |
janjongboom | 1:17c8f5afa7bc | 43 | return readWrite<T>(serviceUuid, charUuid, callback); \ |
janjongboom | 1:17c8f5afa7bc | 44 | }\ |
janjongboom | 1:17c8f5afa7bc | 45 | SimpleChar<T> readWrite_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 46 | (const char* serviceUuid, const UUID& charUuid, void(*callback)(T) = NULL) { \ |
janjongboom | 1:17c8f5afa7bc | 47 | return readWrite<T>(serviceUuid, charUuid, callback); \ |
janjongboom | 1:17c8f5afa7bc | 48 | }\ |
janjongboom | 1:17c8f5afa7bc | 49 | \ |
janjongboom | 1:17c8f5afa7bc | 50 | SimpleChar<T> writeOnly_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 51 | (uint16_t serviceUuid, const UUID& charUuid, void(*callback)(T) = NULL) { \ |
janjongboom | 1:17c8f5afa7bc | 52 | return writeOnly<T>(serviceUuid, charUuid, callback); \ |
janjongboom | 1:17c8f5afa7bc | 53 | }\ |
janjongboom | 1:17c8f5afa7bc | 54 | SimpleChar<T> writeOnly_##postfix \ |
janjongboom | 1:17c8f5afa7bc | 55 | (const char* serviceUuid, const UUID& charUuid, void(*callback)(T) = NULL) { \ |
janjongboom | 1:17c8f5afa7bc | 56 | return writeOnly<T>(serviceUuid, charUuid, callback); \ |
janjongboom | 1:17c8f5afa7bc | 57 | } |
janjongboom | 1:17c8f5afa7bc | 58 | |
janjongboom | 0:2ecd71f6ab04 | 59 | using namespace std; |
janjongboom | 0:2ecd71f6ab04 | 60 | |
janjongboom | 0:2ecd71f6ab04 | 61 | /** |
janjongboom | 0:2ecd71f6ab04 | 62 | * Class so we can call onDataWritten on any SimpleCharInternal regardless of <T,U> |
janjongboom | 0:2ecd71f6ab04 | 63 | */ |
janjongboom | 0:2ecd71f6ab04 | 64 | class Updatable { |
janjongboom | 0:2ecd71f6ab04 | 65 | public: |
janjongboom | 0:2ecd71f6ab04 | 66 | virtual void onDataWritten(const uint8_t* data, size_t len) = 0; |
janjongboom | 0:2ecd71f6ab04 | 67 | }; |
janjongboom | 0:2ecd71f6ab04 | 68 | |
janjongboom | 0:2ecd71f6ab04 | 69 | /** |
janjongboom | 0:2ecd71f6ab04 | 70 | * Class that we wrap in SimpleChar so we can just implement operators, |
janjongboom | 0:2ecd71f6ab04 | 71 | * without having to type the full type of U when using this code. |
janjongboom | 0:2ecd71f6ab04 | 72 | * Whenever we get 'auto' we can get rid of this. |
janjongboom | 0:2ecd71f6ab04 | 73 | */ |
janjongboom | 0:2ecd71f6ab04 | 74 | template <class T> |
janjongboom | 0:2ecd71f6ab04 | 75 | class SimpleCharBase { |
janjongboom | 0:2ecd71f6ab04 | 76 | public: |
janjongboom | 0:2ecd71f6ab04 | 77 | virtual void update(T newValue) = 0; |
Jan Jongboom |
8:953b3164e80f | 78 | virtual T* getValue(void) = 0; |
janjongboom | 0:2ecd71f6ab04 | 79 | }; |
janjongboom | 0:2ecd71f6ab04 | 80 | |
janjongboom | 0:2ecd71f6ab04 | 81 | /** |
janjongboom | 0:2ecd71f6ab04 | 82 | * Actual implementation of the char |
janjongboom | 0:2ecd71f6ab04 | 83 | * T is the underlying type, U is the GattCharacteristic it's wrapping |
janjongboom | 0:2ecd71f6ab04 | 84 | */ |
janjongboom | 0:2ecd71f6ab04 | 85 | template <class T, template <typename T2> class U> |
janjongboom | 0:2ecd71f6ab04 | 86 | class SimpleCharInternal : public Updatable, public SimpleCharBase<T> { |
janjongboom | 0:2ecd71f6ab04 | 87 | public: |
Jan Jongboom |
8:953b3164e80f | 88 | SimpleCharInternal(BLE* aBle, |
Jan Jongboom |
8:953b3164e80f | 89 | const UUID &uuid, |
Jan Jongboom |
8:953b3164e80f | 90 | GattCharacteristic::Properties_t aGattChar, |
janjongboom | 0:2ecd71f6ab04 | 91 | T aDefaultValue, |
janjongboom | 0:2ecd71f6ab04 | 92 | void(*aCallback)(T) = NULL) : |
janjongboom | 0:2ecd71f6ab04 | 93 | ble(aBle), value(new T(aDefaultValue)), callback(aCallback) |
janjongboom | 0:2ecd71f6ab04 | 94 | { |
janjongboom | 0:2ecd71f6ab04 | 95 | state = new U<T>(uuid, value, aGattChar); |
janjongboom | 0:2ecd71f6ab04 | 96 | } |
Jan Jongboom |
8:953b3164e80f | 97 | |
janjongboom | 0:2ecd71f6ab04 | 98 | ~SimpleCharInternal() { |
janjongboom | 0:2ecd71f6ab04 | 99 | if (state) { |
janjongboom | 0:2ecd71f6ab04 | 100 | free(state); |
janjongboom | 0:2ecd71f6ab04 | 101 | } |
janjongboom | 0:2ecd71f6ab04 | 102 | if (value) { |
janjongboom | 0:2ecd71f6ab04 | 103 | free(value); |
janjongboom | 0:2ecd71f6ab04 | 104 | } |
janjongboom | 0:2ecd71f6ab04 | 105 | } |
Jan Jongboom |
8:953b3164e80f | 106 | |
janjongboom | 0:2ecd71f6ab04 | 107 | virtual void update(T newValue) { |
janjongboom | 0:2ecd71f6ab04 | 108 | *value = newValue; |
janjongboom | 0:2ecd71f6ab04 | 109 | ble->gattServer().write(state->getValueHandle(), (uint8_t *)value, sizeof(T)); |
janjongboom | 0:2ecd71f6ab04 | 110 | } |
Jan Jongboom |
8:953b3164e80f | 111 | |
janjongboom | 0:2ecd71f6ab04 | 112 | U<T>* getChar(void) { |
janjongboom | 0:2ecd71f6ab04 | 113 | return state; |
janjongboom | 0:2ecd71f6ab04 | 114 | } |
janjongboom | 0:2ecd71f6ab04 | 115 | |
janjongboom | 0:2ecd71f6ab04 | 116 | virtual T* getValue(void) { |
janjongboom | 0:2ecd71f6ab04 | 117 | return value; |
janjongboom | 0:2ecd71f6ab04 | 118 | } |
Jan Jongboom |
8:953b3164e80f | 119 | |
janjongboom | 0:2ecd71f6ab04 | 120 | virtual void onDataWritten(const uint8_t* data, size_t len) { |
janjongboom | 0:2ecd71f6ab04 | 121 | *value = ((T*)data)[0]; |
janjongboom | 0:2ecd71f6ab04 | 122 | if (callback) { |
janjongboom | 0:2ecd71f6ab04 | 123 | callback(*value); |
janjongboom | 0:2ecd71f6ab04 | 124 | } |
janjongboom | 0:2ecd71f6ab04 | 125 | } |
janjongboom | 0:2ecd71f6ab04 | 126 | |
janjongboom | 0:2ecd71f6ab04 | 127 | private: |
janjongboom | 0:2ecd71f6ab04 | 128 | BLE* ble; |
janjongboom | 0:2ecd71f6ab04 | 129 | T* value; |
janjongboom | 0:2ecd71f6ab04 | 130 | U<T>* state; |
janjongboom | 0:2ecd71f6ab04 | 131 | void(*callback)(T); |
janjongboom | 0:2ecd71f6ab04 | 132 | }; |
janjongboom | 0:2ecd71f6ab04 | 133 | |
janjongboom | 0:2ecd71f6ab04 | 134 | /** |
janjongboom | 0:2ecd71f6ab04 | 135 | * This is what the user gets back. it's nice and short so don't have to type much. |
janjongboom | 0:2ecd71f6ab04 | 136 | * If we get 'auto' we can get rid of this. |
janjongboom | 0:2ecd71f6ab04 | 137 | */ |
janjongboom | 0:2ecd71f6ab04 | 138 | template <class T> |
janjongboom | 0:2ecd71f6ab04 | 139 | class SimpleChar { |
janjongboom | 0:2ecd71f6ab04 | 140 | public: |
janjongboom | 0:2ecd71f6ab04 | 141 | SimpleChar(SimpleCharBase<T>* aBase) : base(aBase) { |
janjongboom | 0:2ecd71f6ab04 | 142 | } |
janjongboom | 0:2ecd71f6ab04 | 143 | ~SimpleChar() { |
janjongboom | 0:2ecd71f6ab04 | 144 | if (base) { |
janjongboom | 0:2ecd71f6ab04 | 145 | delete base; |
janjongboom | 0:2ecd71f6ab04 | 146 | } |
janjongboom | 0:2ecd71f6ab04 | 147 | } |
janjongboom | 0:2ecd71f6ab04 | 148 | |
janjongboom | 0:2ecd71f6ab04 | 149 | T operator=(const T& newValue) { |
janjongboom | 0:2ecd71f6ab04 | 150 | base->update(newValue); |
janjongboom | 0:2ecd71f6ab04 | 151 | return newValue; |
janjongboom | 0:2ecd71f6ab04 | 152 | }; |
janjongboom | 0:2ecd71f6ab04 | 153 | operator T() const { |
janjongboom | 0:2ecd71f6ab04 | 154 | return *(base->getValue()); |
janjongboom | 0:2ecd71f6ab04 | 155 | }; |
Jan Jongboom |
8:953b3164e80f | 156 | |
janjongboom | 0:2ecd71f6ab04 | 157 | private: |
janjongboom | 0:2ecd71f6ab04 | 158 | SimpleCharBase<T>* base; |
janjongboom | 0:2ecd71f6ab04 | 159 | }; |
janjongboom | 0:2ecd71f6ab04 | 160 | |
janjongboom | 0:2ecd71f6ab04 | 161 | |
janjongboom | 0:2ecd71f6ab04 | 162 | class SimpleBLE { |
janjongboom | 0:2ecd71f6ab04 | 163 | public: |
Jan Jongboom |
8:953b3164e80f | 164 | SimpleBLE(const char* aName, uint16_t aInterval = 1000, bool aLogging = true) |
Jan Jongboom |
8:953b3164e80f | 165 | : name(aName), interval(aInterval), logging(aLogging) |
janjongboom | 1:17c8f5afa7bc | 166 | { |
janjongboom | 0:2ecd71f6ab04 | 167 | ble = &BLE::Instance(); |
janjongboom | 0:2ecd71f6ab04 | 168 | } |
janjongboom | 0:2ecd71f6ab04 | 169 | ~SimpleBLE() {} |
Jan Jongboom |
8:953b3164e80f | 170 | |
Jan Jongboom |
8:953b3164e80f | 171 | void start() { |
janjongboom | 0:2ecd71f6ab04 | 172 | ble->init(this, &SimpleBLE::bleInitComplete); |
Jan Jongboom |
8:953b3164e80f | 173 | |
Jan Jongboom |
8:953b3164e80f | 174 | /* SpinWait for initialization to complete. This is necessary because the |
Jan Jongboom |
8:953b3164e80f | 175 | * BLE object is used in the main loop below. */ |
Jan Jongboom |
8:953b3164e80f | 176 | while (ble->hasInitialized() == false) { /* spin loop */ } |
janjongboom | 0:2ecd71f6ab04 | 177 | } |
Jan Jongboom |
8:953b3164e80f | 178 | |
Jan Jongboom |
8:953b3164e80f | 179 | // Start up the BLE service and just run with it! |
Jan Jongboom |
8:953b3164e80f | 180 | void waitForEvent() { |
Jan Jongboom |
8:953b3164e80f | 181 | ble->waitForEvent(); |
Jan Jongboom |
8:953b3164e80f | 182 | } |
Jan Jongboom |
8:953b3164e80f | 183 | |
Jan Jongboom |
8:953b3164e80f | 184 | void onDisconnection(Gap::DisconnectionEventCallback_t callback) { |
Jan Jongboom |
8:953b3164e80f | 185 | ble->gap().onDisconnection(callback); |
Jan Jongboom |
8:953b3164e80f | 186 | } |
Jan Jongboom |
8:953b3164e80f | 187 | |
janjongboom | 1:17c8f5afa7bc | 188 | void onConnection(Gap::ConnectionEventCallback_t callback) { |
janjongboom | 1:17c8f5afa7bc | 189 | ble->gap().onConnection(callback); |
janjongboom | 1:17c8f5afa7bc | 190 | } |
Jan Jongboom |
8:953b3164e80f | 191 | |
janjongboom | 1:17c8f5afa7bc | 192 | BLE* getBle(void) { |
janjongboom | 1:17c8f5afa7bc | 193 | return ble; |
janjongboom | 1:17c8f5afa7bc | 194 | } |
Jan Jongboom |
8:953b3164e80f | 195 | |
janjongboom | 1:17c8f5afa7bc | 196 | def_fn(uint8_t, u8) |
janjongboom | 1:17c8f5afa7bc | 197 | def_fn(uint16_t, u16) |
janjongboom | 1:17c8f5afa7bc | 198 | def_fn(uint32_t, u32) |
janjongboom | 1:17c8f5afa7bc | 199 | def_fn(int8_t, i8) |
janjongboom | 1:17c8f5afa7bc | 200 | def_fn(int16_t, i16) |
janjongboom | 1:17c8f5afa7bc | 201 | def_fn(int32_t, i32) |
janjongboom | 1:17c8f5afa7bc | 202 | def_fn(bool, bool) |
janjongboom | 2:15329a3de04c | 203 | def_fn(float, float) |
Jan Jongboom |
8:953b3164e80f | 204 | |
janjongboom | 1:17c8f5afa7bc | 205 | private: |
janjongboom | 1:17c8f5afa7bc | 206 | void bleInitComplete(BLE::InitializationCompleteCallbackContext *params) |
janjongboom | 1:17c8f5afa7bc | 207 | { |
janjongboom | 1:17c8f5afa7bc | 208 | if (logging) printf("bleInitComplete\r\n"); |
Jan Jongboom |
8:953b3164e80f | 209 | |
janjongboom | 1:17c8f5afa7bc | 210 | BLE& ble = params->ble; |
janjongboom | 1:17c8f5afa7bc | 211 | ble_error_t error = params->error; |
Jan Jongboom |
8:953b3164e80f | 212 | |
janjongboom | 1:17c8f5afa7bc | 213 | if (error != BLE_ERROR_NONE) { |
janjongboom | 1:17c8f5afa7bc | 214 | if (logging) printf("BLE Init error %d\r\n", error); |
janjongboom | 1:17c8f5afa7bc | 215 | return; |
janjongboom | 1:17c8f5afa7bc | 216 | } |
Jan Jongboom |
8:953b3164e80f | 217 | |
janjongboom | 1:17c8f5afa7bc | 218 | /* Ensure that it is the default instance of BLE */ |
janjongboom | 1:17c8f5afa7bc | 219 | if(ble.getInstanceID() != BLE::DEFAULT_INSTANCE) { |
janjongboom | 1:17c8f5afa7bc | 220 | return; |
janjongboom | 1:17c8f5afa7bc | 221 | } |
janjongboom | 1:17c8f5afa7bc | 222 | |
janjongboom | 1:17c8f5afa7bc | 223 | ble.gattServer().onDataWritten(this, &SimpleBLE::onDataWrittenCallback); |
Jan Jongboom |
8:953b3164e80f | 224 | |
janjongboom | 1:17c8f5afa7bc | 225 | // let's add some services yo (why is there no 'auto' in mbed?) |
janjongboom | 1:17c8f5afa7bc | 226 | uint16_t uuid16_list[uint16_services.size()]; |
janjongboom | 1:17c8f5afa7bc | 227 | size_t uuid16_counter = 0; |
Jan Jongboom |
8:953b3164e80f | 228 | { |
janjongboom | 1:17c8f5afa7bc | 229 | typedef std::map<uint16_t, vector<GattCharacteristic*>* >::iterator it_type; |
janjongboom | 1:17c8f5afa7bc | 230 | for(it_type it = uint16_services.begin(); it != uint16_services.end(); it++) { |
janjongboom | 1:17c8f5afa7bc | 231 | if (logging) printf("Creating service 0x%x\n", it->first); |
Jan Jongboom |
8:953b3164e80f | 232 | uuid16_list[uuid16_counter++] = it->first; |
Jan Jongboom |
8:953b3164e80f | 233 | |
janjongboom | 1:17c8f5afa7bc | 234 | GattCharacteristic* charTable[it->second->size()]; |
janjongboom | 1:17c8f5afa7bc | 235 | for (size_t git = 0; git < it->second->size(); git++) { |
janjongboom | 1:17c8f5afa7bc | 236 | charTable[git] = it->second->at(git); |
janjongboom | 1:17c8f5afa7bc | 237 | } |
Jan Jongboom |
8:953b3164e80f | 238 | |
janjongboom | 1:17c8f5afa7bc | 239 | GattService service(it->first, charTable, it->second->size()); |
janjongboom | 1:17c8f5afa7bc | 240 | ble.gattServer().addService(service); |
janjongboom | 1:17c8f5afa7bc | 241 | } |
janjongboom | 1:17c8f5afa7bc | 242 | } |
janjongboom | 1:17c8f5afa7bc | 243 | |
janjongboom | 1:17c8f5afa7bc | 244 | // 128 Bit services |
janjongboom | 1:17c8f5afa7bc | 245 | const char* uuid128_list[uint128_services.size()]; |
janjongboom | 1:17c8f5afa7bc | 246 | size_t uuid128_counter = 0; |
janjongboom | 1:17c8f5afa7bc | 247 | { |
janjongboom | 1:17c8f5afa7bc | 248 | typedef std::map<string, vector<GattCharacteristic*>* >::iterator it_type; |
janjongboom | 1:17c8f5afa7bc | 249 | for(it_type it = uint128_services.begin(); it != uint128_services.end(); it++) { |
janjongboom | 1:17c8f5afa7bc | 250 | if (logging) printf("Creating service %s\n", it->first.c_str()); |
Jan Jongboom |
8:953b3164e80f | 251 | uuid128_list[uuid128_counter++] = it->first.c_str(); |
Jan Jongboom |
8:953b3164e80f | 252 | |
janjongboom | 1:17c8f5afa7bc | 253 | GattCharacteristic* charTable[it->second->size()]; |
janjongboom | 1:17c8f5afa7bc | 254 | for (size_t git = 0; git < it->second->size(); git++) { |
janjongboom | 1:17c8f5afa7bc | 255 | charTable[git] = it->second->at(git); |
janjongboom | 1:17c8f5afa7bc | 256 | } |
Jan Jongboom |
8:953b3164e80f | 257 | |
janjongboom | 1:17c8f5afa7bc | 258 | GattService service(UUID(it->first.c_str()), charTable, it->second->size()); |
janjongboom | 1:17c8f5afa7bc | 259 | ble.gattServer().addService(service); |
janjongboom | 1:17c8f5afa7bc | 260 | } |
janjongboom | 1:17c8f5afa7bc | 261 | } |
janjongboom | 1:17c8f5afa7bc | 262 | |
janjongboom | 1:17c8f5afa7bc | 263 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE); |
janjongboom | 1:17c8f5afa7bc | 264 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, uuid16_counter); |
janjongboom | 1:17c8f5afa7bc | 265 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_128BIT_SERVICE_IDS, (uint8_t *)uuid128_list, uuid128_counter); |
janjongboom | 1:17c8f5afa7bc | 266 | ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)name, strlen(name)); |
janjongboom | 1:17c8f5afa7bc | 267 | ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED); |
janjongboom | 1:17c8f5afa7bc | 268 | ble.gap().setAdvertisingInterval(interval); |
janjongboom | 1:17c8f5afa7bc | 269 | ble.gap().startAdvertising(); |
Jan Jongboom |
8:953b3164e80f | 270 | |
janjongboom | 1:17c8f5afa7bc | 271 | if (logging) printf("Started advertising\r\n"); |
janjongboom | 1:17c8f5afa7bc | 272 | } |
Jan Jongboom |
8:953b3164e80f | 273 | |
janjongboom | 1:17c8f5afa7bc | 274 | void onDataWrittenCallback(const GattWriteCallbackParams *params) { |
janjongboom | 1:17c8f5afa7bc | 275 | // see if we know for which char this message is... |
janjongboom | 1:17c8f5afa7bc | 276 | typedef std::map<GattCharacteristic*, Updatable* >::iterator it_type; |
janjongboom | 1:17c8f5afa7bc | 277 | for(it_type it = writeCallbacks.begin(); it != writeCallbacks.end(); it++) { |
janjongboom | 1:17c8f5afa7bc | 278 | if (it->first->getValueHandle() == params->handle) { |
janjongboom | 1:17c8f5afa7bc | 279 | it->second->onDataWritten(params->data, params->len); |
janjongboom | 1:17c8f5afa7bc | 280 | } |
janjongboom | 1:17c8f5afa7bc | 281 | } |
janjongboom | 1:17c8f5afa7bc | 282 | } |
Jan Jongboom |
8:953b3164e80f | 283 | |
janjongboom | 1:17c8f5afa7bc | 284 | void addToServices(uint16_t uuid, GattCharacteristic* c) { |
janjongboom | 1:17c8f5afa7bc | 285 | if (uint16_services.count(uuid) == 0) { |
janjongboom | 1:17c8f5afa7bc | 286 | uint16_services[uuid] = new vector<GattCharacteristic*>(); |
janjongboom | 1:17c8f5afa7bc | 287 | } |
janjongboom | 1:17c8f5afa7bc | 288 | |
janjongboom | 1:17c8f5afa7bc | 289 | uint16_services[uuid]->push_back(c); |
janjongboom | 1:17c8f5afa7bc | 290 | } |
Jan Jongboom |
8:953b3164e80f | 291 | |
janjongboom | 1:17c8f5afa7bc | 292 | void addToServices(const char* aUuid, GattCharacteristic* c) { |
janjongboom | 1:17c8f5afa7bc | 293 | string uuid(aUuid); |
janjongboom | 1:17c8f5afa7bc | 294 | if (uint128_services.count(uuid) == 0) { |
janjongboom | 1:17c8f5afa7bc | 295 | uint128_services[uuid] = new vector<GattCharacteristic*>(); |
janjongboom | 1:17c8f5afa7bc | 296 | } |
janjongboom | 1:17c8f5afa7bc | 297 | |
janjongboom | 1:17c8f5afa7bc | 298 | uint128_services[uuid]->push_back(c); |
janjongboom | 0:2ecd71f6ab04 | 299 | } |
Jan Jongboom |
8:953b3164e80f | 300 | |
janjongboom | 0:2ecd71f6ab04 | 301 | // === START READONLY === |
Jan Jongboom |
8:953b3164e80f | 302 | |
janjongboom | 0:2ecd71f6ab04 | 303 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 304 | SimpleChar<T> readOnly(uint16_t serviceUuid, |
Jan Jongboom |
8:953b3164e80f | 305 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 306 | bool enableNotify = true, |
janjongboom | 0:2ecd71f6ab04 | 307 | T defaultValue = T()) { |
Jan Jongboom |
8:953b3164e80f | 308 | GattCharacteristic::Properties_t gattChar = enableNotify ? |
janjongboom | 0:2ecd71f6ab04 | 309 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY : |
janjongboom | 0:2ecd71f6ab04 | 310 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ; |
janjongboom | 0:2ecd71f6ab04 | 311 | |
Jan Jongboom |
8:953b3164e80f | 312 | SimpleCharInternal<T, ReadOnlyGattCharacteristic>* c = |
janjongboom | 0:2ecd71f6ab04 | 313 | new SimpleCharInternal<T, ReadOnlyGattCharacteristic>(ble, charUuid, gattChar, defaultValue); |
Jan Jongboom |
8:953b3164e80f | 314 | |
janjongboom | 0:2ecd71f6ab04 | 315 | addToServices(serviceUuid, c->getChar()); |
Jan Jongboom |
8:953b3164e80f | 316 | |
janjongboom | 0:2ecd71f6ab04 | 317 | return *(new SimpleChar<T>(c)); |
janjongboom | 0:2ecd71f6ab04 | 318 | } |
janjongboom | 0:2ecd71f6ab04 | 319 | |
janjongboom | 0:2ecd71f6ab04 | 320 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 321 | SimpleChar<T> readOnly(const char* serviceUuid, |
Jan Jongboom |
8:953b3164e80f | 322 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 323 | bool enableNotify = true, |
janjongboom | 0:2ecd71f6ab04 | 324 | T defaultValue = T()) { |
Jan Jongboom |
8:953b3164e80f | 325 | GattCharacteristic::Properties_t gattChar = enableNotify ? |
janjongboom | 0:2ecd71f6ab04 | 326 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY : |
janjongboom | 0:2ecd71f6ab04 | 327 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ; |
janjongboom | 0:2ecd71f6ab04 | 328 | |
Jan Jongboom |
8:953b3164e80f | 329 | SimpleCharInternal<T, ReadOnlyGattCharacteristic>* c = |
janjongboom | 0:2ecd71f6ab04 | 330 | new SimpleCharInternal<T, ReadOnlyGattCharacteristic>(ble, charUuid, gattChar, defaultValue); |
Jan Jongboom |
8:953b3164e80f | 331 | |
janjongboom | 0:2ecd71f6ab04 | 332 | addToServices(serviceUuid, c->getChar()); |
janjongboom | 0:2ecd71f6ab04 | 333 | |
janjongboom | 0:2ecd71f6ab04 | 334 | return *(new SimpleChar<T>(c)); |
janjongboom | 0:2ecd71f6ab04 | 335 | } |
janjongboom | 0:2ecd71f6ab04 | 336 | |
janjongboom | 0:2ecd71f6ab04 | 337 | // === END READONLY === |
Jan Jongboom |
8:953b3164e80f | 338 | |
janjongboom | 0:2ecd71f6ab04 | 339 | // === START READWRITE === |
janjongboom | 0:2ecd71f6ab04 | 340 | |
janjongboom | 0:2ecd71f6ab04 | 341 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 342 | SimpleChar<T> readWrite(uint16_t serviceUuid, |
Jan Jongboom |
8:953b3164e80f | 343 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 344 | bool enableNotify = true, |
janjongboom | 0:2ecd71f6ab04 | 345 | T defaultValue = T(), |
janjongboom | 0:2ecd71f6ab04 | 346 | void(*callback)(T) = NULL) { |
Jan Jongboom |
8:953b3164e80f | 347 | GattCharacteristic::Properties_t gattChar = enableNotify ? |
janjongboom | 0:2ecd71f6ab04 | 348 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY : |
janjongboom | 0:2ecd71f6ab04 | 349 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ; |
janjongboom | 0:2ecd71f6ab04 | 350 | |
Jan Jongboom |
8:953b3164e80f | 351 | SimpleCharInternal<T, ReadWriteGattCharacteristic>* c = |
janjongboom | 0:2ecd71f6ab04 | 352 | new SimpleCharInternal<T, ReadWriteGattCharacteristic>(ble, charUuid, gattChar, defaultValue, callback); |
janjongboom | 0:2ecd71f6ab04 | 353 | |
janjongboom | 0:2ecd71f6ab04 | 354 | addToServices(serviceUuid, c->getChar()); |
janjongboom | 0:2ecd71f6ab04 | 355 | |
janjongboom | 0:2ecd71f6ab04 | 356 | writeCallbacks[c->getChar()] = c; |
janjongboom | 0:2ecd71f6ab04 | 357 | |
janjongboom | 0:2ecd71f6ab04 | 358 | return *(new SimpleChar<T>(c)); |
janjongboom | 0:2ecd71f6ab04 | 359 | } |
Jan Jongboom |
8:953b3164e80f | 360 | |
Jan Jongboom |
8:953b3164e80f | 361 | |
janjongboom | 0:2ecd71f6ab04 | 362 | |
janjongboom | 0:2ecd71f6ab04 | 363 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 364 | SimpleChar<T> readWrite(const char* serviceUuid, |
Jan Jongboom |
8:953b3164e80f | 365 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 366 | bool enableNotify = true, |
janjongboom | 0:2ecd71f6ab04 | 367 | T defaultValue = T(), |
janjongboom | 0:2ecd71f6ab04 | 368 | void(*callback)(T) = NULL) { |
Jan Jongboom |
8:953b3164e80f | 369 | GattCharacteristic::Properties_t gattChar = enableNotify ? |
janjongboom | 0:2ecd71f6ab04 | 370 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY : |
janjongboom | 0:2ecd71f6ab04 | 371 | GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_READ; |
janjongboom | 0:2ecd71f6ab04 | 372 | |
Jan Jongboom |
8:953b3164e80f | 373 | SimpleCharInternal<T, ReadWriteGattCharacteristic>* c = |
janjongboom | 0:2ecd71f6ab04 | 374 | new SimpleCharInternal<T, ReadWriteGattCharacteristic>(ble, charUuid, gattChar, defaultValue, callback); |
janjongboom | 0:2ecd71f6ab04 | 375 | |
janjongboom | 0:2ecd71f6ab04 | 376 | addToServices(serviceUuid, c->getChar()); |
janjongboom | 0:2ecd71f6ab04 | 377 | |
janjongboom | 0:2ecd71f6ab04 | 378 | writeCallbacks[c->getChar()] = c; |
Jan Jongboom |
8:953b3164e80f | 379 | |
janjongboom | 0:2ecd71f6ab04 | 380 | return *(new SimpleChar<T>(c)); |
janjongboom | 0:2ecd71f6ab04 | 381 | } |
Jan Jongboom |
8:953b3164e80f | 382 | |
janjongboom | 0:2ecd71f6ab04 | 383 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 384 | SimpleChar<T> readWrite(uint16_t serviceUuid, |
Jan Jongboom |
8:953b3164e80f | 385 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 386 | void(*callback)(T) = NULL) { |
janjongboom | 0:2ecd71f6ab04 | 387 | return readWrite(serviceUuid, charUuid, true, T(), callback); |
janjongboom | 0:2ecd71f6ab04 | 388 | } |
janjongboom | 0:2ecd71f6ab04 | 389 | |
janjongboom | 0:2ecd71f6ab04 | 390 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 391 | SimpleChar<T> readWrite(const char* serviceUuid, |
Jan Jongboom |
8:953b3164e80f | 392 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 393 | void(*callback)(T) = NULL) { |
janjongboom | 0:2ecd71f6ab04 | 394 | return readWrite(serviceUuid, charUuid, true, T(), callback); |
janjongboom | 0:2ecd71f6ab04 | 395 | } |
Jan Jongboom |
8:953b3164e80f | 396 | |
janjongboom | 0:2ecd71f6ab04 | 397 | // === END READWRITE === |
Jan Jongboom |
8:953b3164e80f | 398 | |
janjongboom | 0:2ecd71f6ab04 | 399 | // === START WRITEONLY === |
janjongboom | 0:2ecd71f6ab04 | 400 | |
janjongboom | 0:2ecd71f6ab04 | 401 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 402 | SimpleChar<T> writeOnly(uint16_t serviceUuid, |
janjongboom | 0:2ecd71f6ab04 | 403 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 404 | void(*callback)(T) = NULL) { |
Jan Jongboom |
8:953b3164e80f | 405 | SimpleCharInternal<T, WriteOnlyGattCharacteristic>* c = |
janjongboom | 1:17c8f5afa7bc | 406 | new SimpleCharInternal<T, WriteOnlyGattCharacteristic>(ble, charUuid, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NONE, T(), callback); |
janjongboom | 0:2ecd71f6ab04 | 407 | |
janjongboom | 0:2ecd71f6ab04 | 408 | addToServices(serviceUuid, c->getChar()); |
janjongboom | 0:2ecd71f6ab04 | 409 | |
janjongboom | 0:2ecd71f6ab04 | 410 | writeCallbacks[c->getChar()] = c; |
janjongboom | 0:2ecd71f6ab04 | 411 | |
janjongboom | 0:2ecd71f6ab04 | 412 | return *(new SimpleChar<T>(c)); |
janjongboom | 0:2ecd71f6ab04 | 413 | } |
janjongboom | 0:2ecd71f6ab04 | 414 | |
janjongboom | 0:2ecd71f6ab04 | 415 | template <typename T> |
Jan Jongboom |
8:953b3164e80f | 416 | SimpleChar<T> writeOnly(const char* serviceUuid, |
janjongboom | 0:2ecd71f6ab04 | 417 | const UUID& charUuid, |
janjongboom | 0:2ecd71f6ab04 | 418 | void(*callback)(T) = NULL) { |
janjongboom | 0:2ecd71f6ab04 | 419 | |
Jan Jongboom |
8:953b3164e80f | 420 | SimpleCharInternal<T, WriteOnlyGattCharacteristic>* c = |
janjongboom | 1:17c8f5afa7bc | 421 | new SimpleCharInternal<T, WriteOnlyGattCharacteristic>(ble, charUuid, GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NONE, T(), callback); |
janjongboom | 0:2ecd71f6ab04 | 422 | |
janjongboom | 0:2ecd71f6ab04 | 423 | addToServices(serviceUuid, c->getChar()); |
janjongboom | 0:2ecd71f6ab04 | 424 | |
janjongboom | 0:2ecd71f6ab04 | 425 | writeCallbacks[c->getChar()] = c; |
Jan Jongboom |
8:953b3164e80f | 426 | |
janjongboom | 0:2ecd71f6ab04 | 427 | return *(new SimpleChar<T>(c)); |
janjongboom | 0:2ecd71f6ab04 | 428 | } |
Jan Jongboom |
8:953b3164e80f | 429 | |
janjongboom | 1:17c8f5afa7bc | 430 | // === END WRITEONLY === |
janjongboom | 0:2ecd71f6ab04 | 431 | |
janjongboom | 0:2ecd71f6ab04 | 432 | BLE* ble; |
janjongboom | 0:2ecd71f6ab04 | 433 | const char* name; |
janjongboom | 0:2ecd71f6ab04 | 434 | uint16_t interval; |
janjongboom | 1:17c8f5afa7bc | 435 | bool logging; |
janjongboom | 0:2ecd71f6ab04 | 436 | map<uint16_t, vector<GattCharacteristic*>* > uint16_services; |
janjongboom | 0:2ecd71f6ab04 | 437 | map<string, vector<GattCharacteristic*>* > uint128_services; |
janjongboom | 0:2ecd71f6ab04 | 438 | map<GattCharacteristic*, Updatable*> writeCallbacks; |
janjongboom | 0:2ecd71f6ab04 | 439 | }; |
janjongboom | 0:2ecd71f6ab04 | 440 |