Skip to content

Commit 81d2344

Browse files
authored
Merge pull request #18 from seanngpack/macos
Template support, better logging, bug fixes, better documentation
2 parents 75e2596 + ee58a58 commit 81d2344

File tree

377 files changed

+20410
-118683
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

377 files changed

+20410
-118683
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "external/googletest"]
2+
path = external/googletest
3+
url = https://github.com/google/googletest.git

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ branches:
55
- gh-pages
66
- master
77
- macos
8+
- logging-support
89

910
matrix:
1011
include:

CMakeLists.txt

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.17)
22
set(CMAKE_CXX_STANDARD 17)
33

44
project(feeling-blue
5-
VERSION 1.0
5+
VERSION 1.02
66
DESCRIPTION "mylib description")
77

88
include(GNUInstallDirs)
@@ -18,6 +18,11 @@ option(BUILD_DOCUMENTATION "Create the API documentation" OFF)
1818
option(BUILD_TESTS "Build the tests" OFF)
1919
option(VERBOSE_MODE "NSLog what your bluetooth device is doing" ON)
2020

21+
# --------------------------------------------------------------------------------
22+
# Include directories
23+
# --------------------------------------------------------------------------------
24+
25+
include_directories(${PROJECT_SOURCE_DIR}/include)
2126

2227
# --------------------------------------------------------------------------------
2328
# Public headers API
@@ -31,16 +36,9 @@ set(macOS_PUBLIC_HEADERS
3136
)
3237

3338
set(macOS_PRIVATE_HEADERS
34-
39+
./include/detail/conversions.h
3540
)
3641

37-
# --------------------------------------------------------------------------------
38-
# Enable Testing
39-
# --------------------------------------------------------------------------------
40-
41-
include(CTest)
42-
enable_testing()
43-
4442

4543
# --------------------------------------------------------------------------------
4644
# Subdirectories
@@ -49,8 +47,10 @@ enable_testing()
4947
add_subdirectory(macOS)
5048

5149
if (BUILD_TESTS)
50+
include(CTest)
51+
enable_testing()
5252
message("enabled building tests")
53-
add_subdirectory(${PROJECT_SOURCE_DIR}/external/googletest-release-1.10.0)
53+
add_subdirectory(${PROJECT_SOURCE_DIR}/external/googletest)
5454
add_subdirectory(test)
5555
endif()
5656

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,10 @@ int main() {
8888
8989
current_temp_char->set_notify(print_temp); // will call print_temp() whenever device notifies
9090
91-
std::string units = units_char.read_string();
91+
std::string units = units_char.read<std::string>();
9292
std::cout << "the current temperature unit is: " << units << std::endl
93-
units_char->write_without_response("kelvin");
94-
units = units_char.read_string();
93+
units_char->write_without_response<std::string>("kelvin"); // set new temperature unit to device
94+
units = units_char->read<std::string>();
9595
std::cout << "the new temperature unit is: " << units << std::endl
9696
9797
while (true) {
@@ -107,7 +107,6 @@ int main() {
107107

108108
## Upcoming features:
109109
- methods to get status of peripherals, characteristics, etc
110-
- better error logging
111110
- Windows support
112111

113112
## Help & bug reports

docs/demo/demo.rst

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -148,61 +148,57 @@ Reading characteristic value
148148
============================
149149

150150
Let's get some data! To read the value of your characteristic, call the ``read()`` method. This method blocks
151-
the calling thread until the data has been read from your characteristic and assigned to your variable.
151+
the calling thread until the data has been read from your characteristic and assigned to your variable. The read method
152+
is templated and supports multiple types described in the :ref:`supported-template-types`.
152153

153154
.. code:: c++
154155

155-
std::vector<std::byte> data = characteristic->read();
156+
std::vector<std::byte> data = characteristic->read<std::byte>();
156157

157-
There are additional read methods available to convert the payload into human-readable datatypes. If your device is sending
158-
four bytes representing an integer, you can it directly as an integer instead of doing the conversion yourself:
158+
If you want something more human-readable, and you know your device's characteristic is represented in four bytes in
159+
little-endian order, you just just read your data as an integer:
159160

160161
.. code:: c++
161162

162-
int data = characteristic->read_int();
163-
163+
int data = characteristic->read<int>();
164164

165+
By default, you should read in your data into ``std::vector<std::byte>`` unless you know for sure your device is outputting
166+
convertible types.
165167

166168

167169
Writing to characteristic
168170
=========================
169171

170172
There are two main options to write to your device. First we can ``write_without_response()`` which writes to your
171173
devices asynchronously and does not block your calling thread. If your write fails, you will not get a message
172-
telling you that it failed. You must provide this method the data as a ``std::vector<std::byte>``
174+
telling you that it failed. This method is templated and supports multiple types described in the :ref:`supported-template-types`.
173175

174176
.. code:: c++
175177

176-
characteristic->write_without_response(data);
178+
std::vector<std::byte> data = {...};
179+
characteristic->write_without_response<std::byte>(data);
177180

178181

179182
And if you write with a response, then the method will block your calling thread and wait until your data has been
180183
successfully written to the device.
181184

182185
.. code:: c++
183186

184-
rotate_char->write_with_response(data);
185-
186-
There are convenience overloaded methods to write to your device if you'd rather send a human-readable datatype rather
187-
than a vector of bytes. If you use these convenience methods, just make on the other end, your device is configured to handle
188-
this information. These convenience methods assume little-endian ordering of bytes.
189-
190-
.. code:: c++
187+
std::vector<std::byte> data = {...};
188+
rotate_char->write_with_response<std::byte>(data);
191189

192-
std::string data = "Celcius";
193-
switch_units_char->write_with_response(data);
190+
Like the read() method, you can write to your device using different formats that will end up being converted to byte arrays.
194191

195192

196193
Notifying characteristic
197194
========================
198195

199-
200196
If your device and characteristic supports notifications, then let's use it. First, just double check that your characteristic
201197
has notification support and that it's enabled. So now when your device sends your computer notifications with a data payload you can capture
202198
that payload and write your own function to do something with it!
203199

204200
Non-member function event handler
205-
----------------------------
201+
---------------------------------
206202

207203
Let's write a callback event handler that takes in a ``std::vector<std::byte>`` and enable notifications, passing the function as a parameter.
208204

@@ -220,7 +216,7 @@ IMPORTANT! All event handlers must follow this signature: ``void (std::vector<st
220216

221217

222218
Member function event handler
223-
------------------------
219+
-----------------------------
224220

225221
member functions are a little trickier to write, but you just have to bind their class to std::function
226222
and add a placeholder parameter, then pass it like normal.
@@ -248,4 +244,12 @@ and add a placeholder parameter, then pass it like normal.
248244
Passing member functions is really powerful because you can do things such as update an instance variable when notified.
249245

250246
If you're passing the same function to multiple characteristic notifications, then just make sure your function contents are
251-
thread-safe, this applies to both member and non-member functions.
247+
thread-safe, this applies to both member and non-member functions.
248+
249+
.. _supported-template-types:
250+
251+
.. include:: ../template_types.rst
252+
253+
254+
255+

docs/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@
2323
:maxdepth: 4
2424
:hidden:
2525

26-
API/API
2726
API/central
2827
API/peripheral
2928
API/service
30-
API/characteristic
29+
API/characteristic
30+
template_types

docs/template_types.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Supported Template Types
2+
========================
3+
4+
Primitive:
5+
6+
- uint8_t
7+
- int
8+
- float
9+
- double
10+
11+
Non Primitive:
12+
13+
- std::string
14+
- std::vector<std::byte>

examples/arduino_temperature_device.ino

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,14 @@ const char * battery_level_uuid = "19B10001-E8F2-537E-4F6C-D104768A1214";
1313
const char * temp_units_uuid = "19B10002-E8F2-537E-4F6C-D104768A1214";
1414
const char * current_temp_uuid = "19B10003-E8F2-537E-4F6C-D104768A1214";
1515
// create battery characteristic that relays current battery level
16-
BLECharacteristic battery_level_char(battery_level_uuid, BLERead, 20u);
16+
BLECharacteristic battery_level_char(battery_level_uuid, BLERead | BLENotify, 20u);
1717
// create String type characteristic that sets the units of the temperature. Can read or write.
1818
// This characteristic converts byte data to string. We're using this to demonstrate that you don't have to always
1919
// deal in bytes with the ArduinoBLE library.
2020
BLEStringCharacteristic temp_units_char(temp_units_uuid, BLERead | BLEWrite, 20u);
2121
// create temperature characteristic that has notifications enabled
2222
BLECharacteristic current_temp_char(current_temp_uuid, BLERead | BLENotify, 20u);
2323

24-
char* default_unit = "fahrenh";
25-
2624
// how often the current_temp_char will update
2725
const unsigned long interval = 1000; // in ms
2826
unsigned long start_time;
@@ -59,8 +57,13 @@ void setup() {
5957
temp_units_char.setEventHandler(BLERead, tempCharacteristicRead);
6058

6159
// set an initial value for characteristics
62-
battery_level_char.setValue((char*)int_to_bytes(100));
63-
Serial.println(battery_level_char.valueLength());
60+
uint8_t batt_value[4];
61+
int_to_bytes(100, batt_value);
62+
battery_level_char.writeValue(batt_value, 4);
63+
Serial.println(battery_level_char.value()[0]);
64+
Serial.println(battery_level_char.value()[1]);
65+
Serial.println(battery_level_char.value()[2]);
66+
Serial.println(battery_level_char.value()[3]);
6467
temp_units_char.setValue("fahrenheit");
6568
current_temp_char.setValue(0);
6669

@@ -94,8 +97,10 @@ void updateTemp(BLECharacteristic characteristic) {
9497
currentTemp = currentTempF;
9598
}
9699

100+
uint8_t temp_array[4];
101+
float_to_bytes(currentTemp, temp_array);
97102
// this should trigger a notify
98-
characteristic.writeValue(float_to_bytes(currentTemp), sizeof(float));
103+
characteristic.writeValue(temp_array, sizeof(float));
99104
}
100105

101106
/*******************
@@ -139,22 +144,31 @@ void tempCharacteristicRead(BLEDevice central, BLECharacteristic characteristic)
139144
*
140145
*******************/
141146

142-
uint8_t *float_to_bytes(float f) {
143-
uint8_t *swag = reinterpret_cast<uint8_t*>(&f);;
144-
return swag;
147+
148+
/**
149+
* Convert 4-byte float to array of uint8_t. Caller manages array memory.
150+
*/
151+
uint8_t *float_to_bytes(float f, uint8_t array[]) {
152+
uint8_t *temp = reinterpret_cast<uint8_t*>(&f);
153+
array[0] = temp[0];
154+
array[1] = temp[1];
155+
array[2] = temp[2];
156+
array[3] = temp[3];
157+
return array;
145158
}
146159

160+
147161
float bytes_to_float(const uint8_t* bytes) {
148162
float f;
149163
memcpy(&f, bytes, sizeof(f));
150164
return f;
151165
}
152166

153-
const uint8_t *int_to_bytes(int data) {
154-
uint8_t *byte_vector;
155-
byte_vector[0] = data & 0xff;
156-
byte_vector[1] = (data>> 8) & 0xff;
157-
byte_vector[2] = (data>> 16) & 0xff;
158-
byte_vector[3] = (data>> 24) & 0xff;
159-
return byte_vector;
160-
}
167+
// Convert 4 byte long to bytes. Caller manages array memory.
168+
uint8_t* int_to_bytes(long data, uint8_t array[]) {
169+
array[3] = data & 0xff;
170+
array[2] = (data>> 8) & 0xff;
171+
array[1] = (data>> 16) & 0xff;
172+
array[0] = (data>> 24) & 0xff;
173+
return array;
174+
}

examples/temperature_device.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,22 +59,22 @@ int main() {
5959
current_temp_char->notify(print_data);
6060

6161
// or read it without waiting for updates
62-
std::cout << "the current temperature is: " << current_temp_char->read_float() << std::endl;
62+
std::cout << "the current temperature is: " << current_temp_char->read<float>() << std::endl;
6363

6464
// read the battery level
65-
std::vector<std::byte> a = battery_level_char->read();
65+
std::vector<std::byte> a = battery_level_char->read<std::vector<std::byte>>();
6666
std::cout << "printing bytes of battery" <<std::endl;
6767
for (auto &x: a) {
6868
std::cout << " " << (int)x;
6969
}
7070
std::cout << std::endl;
71-
std::cout << "the battery level is: " << battery_level_char->read_int() << std::endl;
71+
std::cout << "the battery level is: " << battery_level_char->read<int>() << std::endl;
7272

7373
// current value is "fahrenheit"
74-
std::string unit = temp_units_char->read_string();
74+
std::string unit = temp_units_char->read<std::string>();
7575
std::cout << "the current unit is: " << unit << std::endl;
7676
// write to the characteristic to change units.
77-
temp_units_char->write_with_response("celsius");
77+
temp_units_char->write_with_response<std::string>("celsius");
7878

7979

8080
using namespace std::chrono_literals;

examples/temperature_device_class.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ class SomeClass {
6161
}
6262

6363
int read_temp() {
64-
return current_temp_char->read_int();
64+
return current_temp_char->read<int>();
6565
}
6666

6767
void set_temp_units(const std::string &units) {
68-
temp_units_char->write_with_response(units);
68+
temp_units_char->write_with_response<std::string>(units);
6969
}
7070

7171
std::string get_temp_units() {
72-
return temp_units_char->read_string();
72+
return temp_units_char->read<std::string>();
7373
}
7474

7575

0 commit comments

Comments
 (0)