Coder Social home page Coder Social logo

libblepp's Introduction

libble++

Implementation of Bluetooth Low Energy functions in modern C++, without the BlueZ DBUS API.

Includes

  • Scanning for bluetooth packets
  • Implementation of the GATT profile and ATT protocol
  • Lots of comments, complete with references to the specific part of the Bluetooth 4.0 standard.
  • Example programs

Design

Clean, modern C++ with callbacks. Feed it with lambdas (or whatever you like) to perform an event happens. Access provided to the raw socket FD, so you can use select(), poll() or blocking IO.

The example programs

  • lescan_simple: Simplest possible program for scanning for devices. Only 2 non boilerplate lines.
  • lescan: A "proper" scanning program that cleans up properly. It's got the same 2 lines of BLE related code and a bit of pretty standard unix for dealing with non blocking I/O and signals.
  • temperature: A program for logging temperature values from a device providing a standard temperature characteristic. Very short to indicate the usave, but not much error checking.

Building the library

There are currently autoconf (./configure) and CMake options. It's not a complex library to build, so either option should work fine.
Autoconf:

./configure  
make

CMake:

mkdir build && cd build
cmake ..
make install

CMake with examples:
Examples will be in build/examples

mkdir build && cd build
cmake -DWITH_EXAMPLES=ON ..
make install

libblepp's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

libblepp's Issues

Example programs should be in subdirectory

Just a small request. All examples or code which is not a part of library itself should be in separate directory. This will simplify life of others who would like to use lib. At current moment I've to add a lot of excludes per each file in my IDE to not compile things which I don't need.

Low-level I/O connect system call failed by Memory allocation failure.

Hello there,

We have tested several BLE IMU sensors of MbientLab in which libblepp is wrapped in driver level, during these tests, we have encountered systematically one low level I/O problem after about 10-15 min :

  • In BLE driver level, one system call of socket connect() is failed by a memory allocation issue.
  • Then, getaddrinfo() function is failed indeed, then the error exception handler of netdb.h and socket_ops.ipp is triggered by this what() : Cannot allocate memory.
  • Which corresponds # define EAI_MEMORY -10 /* Memory allocation failure. */ in netdb.h and
  • no_memory in socket_ops.ipp

Please find below the trace error when the exception is triggered :

error 1591608658.880516: Error on line: 296 (src/blestatemachine.cc): Cannot allocate memory
terminate called after throwing an instance of 'BLEPP::SocketConnectFailed'
what():  Cannot allocate memory
2020-06-08 11:30:58 sigHandler: Unhandled signal 6, terminating

This issue became more frequent and significant in these cases below :

  • with increasing number of sensors, in our case : 15 sensors.
  • put the sensors on the human body instead of test bench

In this context, we would be very grateful if you could perhaps give some help of debugging or responses about this question.

Best regards,
Ming

Timeout if no connection

So, if I run the temperature example and do not connect a thermometer within about a minute I get

error 1570640902.713533: Error on line: 296 (src/blestatemachine.cc): Operation now in progress
terminate called after throwing an instance of 'std::logic_error'
what(): Trying to read socket while connecting
Aborted

In my own code, I catch the exception and reconnect, but then it seems to give up after the second attempt.

No way to detect connection state (or connection failure)

is there a way, that I am missing, to prevent my Programm to call
BLEGATTStateMachine::read_and_process_next
if the previous call to void BLEGATTStateMachine::connect was failing? (e.g. because device is gone out of range)

I loop over read_and_process_next and stderr is flooded with "Trying to read_and_process_next while disconnected".
I haven't found something like "getState" or "isConnected" or anything and BLEGATTStateMachine::state is private.
Named methods are also void, so I can not catch the error message in code.

Do you want me to write a getter for BLEGATTStateMachine::state or something else?

Regards and thanks
Ben

Get rid of boost?

Hi,
This library/project is something which I was searching for some time, but one big disadvantage from my perspective is Boost dependency. I wonder if you consider removing this dependency in future? Since it's used only in one place. Adding boost when I do cross-compilation is really painful.

verison number?

Hi, how stable this library is? Is there a version number?
If there's, would you mind tagging it? I'm thinking of basing my new project on this library :)

-lble++ flag missing

Hi, after compiling the library the pkgconfig/libblepp.pc set

Libs: -lbluetooth

while it should have -lble++ too in order to link to this

Is libblepp based on libbluetooth / GPL?

Hi !
I am looking for a way to implement BLE scann using linux and I found libblep whch is excellent for my problem.
As far as I know, libbluetooth (from ) is based on D-Bus APIs, right? You are saying that libblepp does not uses D-Bus. Does this means that libblepp is self contained (does not uses any other libraries success as libbluetooth.so or libbluetooth.h)?

My problem is: AFAIK libbluetooth.so and libbluetooth.h are GPL. libblepp is not? So if I use libblepp for my listenning linux program should I release program under GPL? Can I release it as closed source?

Thanks a lot
Alex

setup_standard_scan overrides your callbacks.

This is unacceptable from perspective of user, if I set my callbacks on object then I'm not expecting some magic overwrites of it. Especially if I must call this method to get characteristics. I believe there should be channing of callbacks or listener approach or 2 levels of callbacks (special case of chainning).

Subscribing to several characteristics in service

Hi,

Thanks to your great examples i was able to connect to my device and read characteristic values with notifications. However, when i try to get the device to notify on several characteristics i mess up the state machine.

I have expanded the already existing lambda. The program runs as intended with only the first of the two if statements.

std::function<void()> cb = [&gatt, &notify_cb_roll, &notify_cb_pitch, &enable](){

		pretty_print_tree(gatt);

		for(auto& service: gatt.primary_services)
			for(auto& characteristic: service.characteristics) {
				
				
				if(service.uuid == UUID("8efc0000-6ee9-4c70-8615-3456c364d7e6") && characteristic.uuid == UUID("8efc0001-6ee9-4c70-8615-3456c364d7e6"))
				{
					cout << "Found and subscribed to roll characteristic\n";
					characteristic.cb_notify_or_indicate = notify_cb_roll;
					characteristic.set_notify_and_indicate(enable, false);
				}
				
				if(service.uuid == UUID("8efc0000-6ee9-4c70-8615-3456c364d7e6") && characteristic.uuid == UUID("8efc0002-6ee9-4c70-8615-3456c364d7e6"))
				{
					
					cout << "Found and subscribed to pitch characteristic\n";
					characteristic.cb_notify_or_indicate = notify_cb_pitch;
					characteristic.set_notify_and_indicate(enable, false);
					
				}
				
				
			}

	};

When the code looks as above i get the following output when connecting to my device.

info 1543589836.865952: Socket success: 228 (/home/asmus/Documents/Projects/CppBLE/libblepp-master/src/blestatemachine.cc)
info 1543589836.866000: Socket success: 271 (/home/asmus/Documents/Projects/CppBLE/libblepp-master/src/blestatemachine.cc)
info 1543589836.866020: Socket success: 174 (/home/asmus/Documents/Projects/CppBLE/libblepp-master/src/blestatemachine.cc)
info 1543589836.866036: options.omtu = 672
info 1543589836.866053: options.imtu = 672
info 1543589836.866067: options.flush_to = 65535
info 1543589836.866080: options.mode = 0
info 1543589836.866095: options.fcs = 1
info 1543589836.866109: options.max_tx = 3
info 1543589836.866121: options.txwin_size = 63
error 1543589836.866153: Error on line: 298 (/home/asmus/Documents/Projects/CppBLE/libblepp-master/src/blestatemachine.cc): Operation now in progress
Probably connected?
info 1543589837.321752: Socket success: 493 (/home/asmus/Documents/Projects/CppBLE/libblepp-master/src/blestatemachine.cc)
info 1543589837.321819: errval = Success
From read_and_process_next - We're in state: 3
From read_and_process_next - We're in state: 3
From read_and_process_next - We're in state: 4
From read_and_process_next - We're in state: 4
From read_and_process_next - We're in state: 4
From read_and_process_next - We're in state: 4
From read_and_process_next - We're in state: 4
From read_and_process_next - We're in state: 5
From read_and_process_next - We're in state: 5
Primary services:
Start: 0001 End: 0009 UUID: 1800
org.bluetooth.service.generic_access: Generic Access
Characteristic: 2a00
Start: 0002 End: 0003
Flags: Read Write
Value at handle: 3

Characteristic: 2a01
Start: 0004 End: 0005
Flags: Read
Value at handle: 5

Characteristic: 2a04
Start: 0006 End: 0007
Flags: Read
Value at handle: 7

Characteristic: 2aa6
Start: 0008 End: 0009
Flags: Read
Value at handle: 9

Start: 000a End: 000d UUID: 1801
org.bluetooth.service.generic_attribute: Generic Attribute
Characteristic: 2a05
Start: 000b End: 000d
Flags: Indicate
Value at handle: 12
CCC: (000d) 0000

Start: 000e End: ffff UUID: 8efc0000-6ee9-4c70-8615-3456c364d7e6
Unknown
Characteristic: 8efc0001-6ee9-4c70-8615-3456c364d7e6
Start: 000f End: 0011
Flags: Read Write Notify
Value at handle: 16
CCC: (0011) 0000

Characteristic: 8efc0002-6ee9-4c70-8615-3456c364d7e6
Start: 0012 End: ffff
Flags: Notify
Value at handle: 19
CCC: (0014) 0000

Found and subscribed to roll characteristic
In set_notify_and_indicate
In set_notify_and_indicate - State:6
Found and subscribed to pitch characteristic
In set_notify_and_indicate
Oops, someone fouled up: Error trying to issue command mid state
info 1543589838.339664: Socket success: 151 (/home/asmus/Documents/Projects/CppBLE/libblepp-master/src/blestatemachine.cc)

To debug the program it prints the state and which function it exits from. When the state machine enters the set_notify_and_indicate function the state is checked and it exists if the state is not idle.
At the end of the function, the state is set to AwaitingWriteResponse before state_machine_write() is called. Next time the function is called it exits, since the state is still AwaitingWriteResponse.

I suspect that this is not really a bug, but rather a lack of understanding of the state machine. How do i properly turn on notifications for several characteristics, without breaking the state machine?

compile error with clang

Hey,

i have compile errors when i try to compile your library with clang.

my setup:

  • Ubuntu 17.10
  • clang 5.0
  • cmake 3.9.1

~/projects/libblepp$ make [ 50%] Built target ble++ [ 60%] Built target lescan [ 70%] Built target lescan_simple [ 80%] Built target blelogger Scanning dependencies of target bluetooth [ 85%] Building CXX object CMakeFiles/bluetooth.dir/examples/bluetooth.cc.o In file included from /home/ctor/projects/libblepp/examples/bluetooth.cc:32: In file included from /home/ctor/projects/libblepp/cxxgplot.h:1: /home/ctor/projects/libblepp/pstreams/pstream.h:1820:28: error: return type of out-of-line definition of 'redi::basic_pstreambuf::wpipe' differs from that in the declaration basic_pstreambuf<C,T>::wpipe() ^ /home/ctor/projects/libblepp/pstreams/pstream.h:219:7: note: previous declaration is here wpipe(); ^ /home/ctor/projects/libblepp/pstreams/pstream.h:1828:28: error: return type of out-of-line definition of 'redi::basic_pstreambuf::rpipe' differs from that in the declaration basic_pstreambuf<C,T>::rpipe() ^ /home/ctor/projects/libblepp/pstreams/pstream.h:223:7: note: previous declaration is here rpipe(); ^ /home/ctor/projects/libblepp/pstreams/pstream.h:1836:28: error: return type of out-of-line definition of 'redi::basic_pstreambuf::rpipe' differs from that in the declaration basic_pstreambuf<C,T>::rpipe(buf_read_src which) ^ /home/ctor/projects/libblepp/pstreams/pstream.h:227:7: note: previous declaration is here rpipe(buf_read_src which); ^ 3 errors generated. CMakeFiles/bluetooth.dir/build.make:62: recipe for target 'CMakeFiles/bluetooth.dir/examples/bluetooth.cc.o' failed make[2]: *** [CMakeFiles/bluetooth.dir/examples/bluetooth.cc.o] Error 1 CMakeFiles/Makefile2:215: recipe for target 'CMakeFiles/bluetooth.dir/all' failed make[1]: *** [CMakeFiles/bluetooth.dir/all] Error 2 Makefile:129: recipe for target 'all' failed make: *** [all] Error 2
i've solved this problem locally by updating pstreams version 1.0.1

best regards
xqp

blelogger – terminate called after throwing an instance of 'std::logic_error'

I've made and installed this library and am attempting to run the blelogger file with:

sudo ./blelogger C6:E7:40:7B:1B:35

I've also modified the blelogger file to change the debug level to

log_level = Debug;

When I try run blelogger it gives the following

info  1689609679.778310 log_fd_: Socket success: 221 (/home/rbeattie/Documents/libblepp/src/blestatemachine.cc)
info  1689609679.778485 log_fd_: Socket success: 266 (/home/rbeattie/Documents/libblepp/src/blestatemachine.cc)
info  1689609679.778586 log_fd_: Socket success: 171 (/home/rbeattie/Documents/libblepp/src/blestatemachine.cc)
info  1689609679.778676 log_l2cap_options: options.omtu = 672
info  1689609679.778754 log_l2cap_options: options.imtu = 672
info  1689609679.778831 log_l2cap_options: options.flush_to = 65535
info  1689609679.778905 log_l2cap_options: options.mode = 0
info  1689609679.778980 log_l2cap_options: options.fcs = 1
info  1689609679.779055 log_l2cap_options: options.max_tx = 3
info  1689609679.779130 log_l2cap_options: options.txwin_size = 63
debug 1689609679.779211 connect: address = c6:e7:40:7b:1b:35
debug 1689609679.779281 connect: str2ba = 0
error 1689609721.673936 log_fd_: Error on line: 293 (/home/rbeattie/Documents/libblepp/src/blestatemachine.cc): Operation now in progress
terminate called after throwing an instance of 'std::logic_error'
  what():  Trying to read socket while connecting
Aborted

I'm getting the address from lescan so I'm sure that the adapter can see it

where and how to start?

Cool project! -- my goal is to write a plugin to synergy variant barrier and looking for library that is suitable for this purpose. Can anyone confirm libblepp suitable to create a HID service or HID over GATT profile (HOGP) ?

Unfortunately after compiled the examples on a LinuxMint 20.3 Lenovo X1 and running the examples with sudo I am getting:

ivarga@ivargalinux:~/src/libblepp/examples$ sudo ./lescan
warn  1686933312.244332: Received I/O error while setting scan parameters.
warn  1686933312.244353: Switching off HCI scanner
error 1686933312.245387: Error disabling scan:: Input/output error
terminate called after throwing an instance of 'BLEPP::HCIScanner::IOError'
  what():  Error disabling scan:: Input/output error
Aborted

The bluez stack is bluetoothctl: 5.63; what could I be doing wrong?

Missing MTU response

Hello,

I found (after upgrading our BLE device to newer BLE-Stack), that a request for MTU exchange is treated as an error. The state machine is bailing out with "unexpected response". After some debugging I manage to hold the connection alive by sending a faked response (which sends static 24 as MTU, the "old value" from the device).

  • Is there a cleaner way to handle the MTU request? Bluez-Gatttool responses with "request not supported" but I wasn't able to produce this in libblepp.
  • Why is the request killing the state machine? Is the timming just bad luck or is the request wrong in this time slot?

I append my hack as patch to let you have a look.

Regards
Ben

liblepp_mtu_hack.patch.txt

trace of the hack:
mtu_request_ws_trace

Unexpected response. Expected Read By Group Response got Read By Type Request

I am getting the following error when attempt to connect to some devices:

Unexpected response. Expected Read By Group Response got Read By Type Request

At the same time I am able to connect to the same device with the gatttool, like so:

$ gatttool -I -t random -b AA:BB:CC:DD:EE:FF

and can list, read, and write characteristics.

One of the affected devices is Adafruit Feather nRF52 Bluefruit based on Nordic nRF52832 chip. Interesting that there are no problems with another device (mbientlab MetaMotion R) that is also based on Nordic nRF52 chip. The other device that fail to connect is iPhone 7 with the virtual device advertised via the LightBlue Explorer; I tried couple of different combinations of advertised services/characteristics, but it fails in all the cases.

Note: I am using libblepp via Warble library of mbientlab (it connects using random address as well).

OS: Ubuntu 18.04
Bluez: v5.48 / v5.50 (tried both)
BLE dongle: CSR 4.0

Any idea what can be the problem?

Support for timeouts

Is there any plans to add support for timeout for operations such as connect, read, write?

getting error/exception whenever trying to stop() a running HCIScanner

Hi, thank you for this nice library.
Whenever I try to stop() a running HCIScanner I am getting:

Error disabling scan:: Connection timed out
terminate called after throwing an instance of 'BLEPP::HCIScanner::IOError'
  what():  Error disabling scan:: Connection timed out

Am I doing anything wrong, or is this a bug?
You have some nice examples for using the HCIScanner, but they do not use the stop() function at all.

I was able to fix this problem by using the
err = hci_close_dev(hci_fd);
within the stop() function instead of calling hci_le_set_scan_enable(...) there ...
... but then I had to move
hci_open_dev(dev_id)
from constructor() to start().

Create service

Hello,
For my project, i have to create a ble server.
I found your library but i can't find any example to create services and characteristics...
Is it possible to do this with your library ?
If yes, could you send me an example...
Thanks.

terminate called after throwing an instance of 'BLEPP::SocketConnectFailed'

Hi,

i got problems while connecting ...

There are no probs to connect, read etc. via bluetoothctl - tool.

info  1541687672.925407 log_fd_: Socket success: 224 (src/blestatemachine.cc)
info  1541687672.925454 log_fd_: Socket success: 269 (src/blestatemachine.cc)
info  1541687672.925480 log_fd_: Socket success: 174 (src/blestatemachine.cc)
info  1541687672.925501 log_l2cap_options: options.omtu = 672
info  1541687672.925522 log_l2cap_options: options.imtu = 672
info  1541687672.925545 log_l2cap_options: options.flush_to = 65535
info  1541687672.925565 log_l2cap_options: options.mode = 0
info  1541687672.925589 log_l2cap_options: options.fcs = 1
info  1541687672.925611 log_l2cap_options: options.max_tx = 3
info  1541687672.925633 log_l2cap_options: options.txwin_size = 63
debug 1541687672.925658 connect: address = cf:33:8f:4d:9c:3e
debug 1541687672.925679 connect: str2ba = 0
error 1541687713.911835 log_fd_: Error on line: 296 (src/blestatemachine.cc): Connection refused
terminate called after throwing an instance of 'BLEPP::SocketConnectFailed'
  what():  Connection refused
Aborted

Any suggestions?

Setting scan parameters: Operation not permitted

$ examples/lescan
error 1611577006.276827: Setting scan parameters: Operation not permitted
terminate called after throwing an instance of 'BLEPP::HCIScanner::IOError'
  what():  Setting scan parameters: Operation not permitted
Aborted (core dumped)

It looks like libblepp nees to be used with sudo in a default setup for me. Is this an inherent limitation of the non-dbus approach?

connect with security level

I have an BLE device that requires bonding (encrypted connections). How could that work with libblepp?

From PyGattlib I can see a function call 'l2cap_set' with parameter 'opts->sec_level' which later calls function 'setsockopt' (and l2cap_set_lm, rfcomm_set_lm) inside function 'set_sec_level' - could this be the key for an encrypted connection?

https://github.com/nccgroup/BLESuite/blob/58574a2e480893512e1e3afc889f5acf09bda20e/PyGattlib/src/bluez/btio/btio.c#L1487

https://github.com/nccgroup/BLESuite/blob/58574a2e480893512e1e3afc889f5acf09bda20e/PyGattlib/src/bluez/btio/btio.c#L449

And a quote I found [...'Although setting a security level using setsockopt(BT_SECURITY) is
optional for LE sockets (it will default to doing unencrypted/unauthenticated connection), it is a common operation and it is done by BlueZ daemon and some tools.'...]

Maybe we can add setting the security level during connecting? :)

terminate called after throwing an instance of 'std::out_of_range' what():

I am seeing this occur, what might be cause or how can I get more info to investigate. Thx!

sudo ./lescan -d

Found device: 00:24:e4:xx:yy:zz Scan response
  Name: Withings Aura 0E
  RSSI = -72 dBm
Found device: 53:32:d7:xx:yy:zz Connectable undirected
  RSSI = -85 dBm
Found device: 53:32:d7:xx:yy:zz Scan response
  RSSI = -84 dBm
terminate called after throwing an instance of 'std::out_of_range'
  what():
Aborted

Name not populated

I can't find any way to force a wait on a lescan for device name... I can get the addresses, but the name doesn't appear to be populated (yet), but I can't find any way to say 'go get the name' or 'wait for the name to show up' in get_advertisements.
Meaning (using lescan example):

vector<AdvertisingResponse> ads = scanner.get_advertisements();

for(const auto& ad: ads) {
    cout << "Found device: " << ad.address << endl;

    if( ! ad.local_name)
        _**ad.getLocalName();**_  // added this as what I'm trying to accomplish
     cout << ad.local_name->name << endl;

Is there a way to do this I'm not seeing? The issue for me is I'm working on a whole group of devices, so I can't depend on filtering by MAC (hoping to just look at them by a name prefix).

configure: error: bluez bluetooth missing

Hi, got this error in installation process.

Environment: Linux Ubuntu 18.04, x86_64.
bluez version (5.48-0ubuntu3.4)
bluetooth version (5.48-0ubuntu3.4).
Also, downloaded and installed ELL 0.9 version from this site:
https://mirrors.edge.kernel.org/pub/linux/libs/ell/

Full logs:
checking for g++... g++
checking whether the C++ compiler works... yes
checking for C++ compiler default output file name... a.out
checking for suffix of executables...
checking whether we are cross compiling... no
checking for suffix of object files... o
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking if compiler flag -Wall works... yes
checking if compiler flag -Wextra works... yes
checking if compiler flag -W works... yes
checking if compiler flag -O3 works... yes
checking if compiler flag -ggdb works... yes
checking whether g++ supports C++14 features by default... yes
checking for pkg-config... /usr/bin/pkg-config
checking for sed... sed
checking for pkg-config library dir... ${exec_prefix}/lib/pkgconfig
checking how to run the C++ preprocessor... g++ -E
checking for grep that handles long lines and -e... /bin/grep
checking for egrep... /bin/grep -E
checking for ANSI C header files... yes
checking for sys/types.h... yes
checking for sys/stat.h... yes
checking for stdlib.h... yes
checking for string.h... yes
checking for memory.h... yes
checking for strings.h... yes
checking for inttypes.h... yes
checking for stdint.h... yes
checking for unistd.h... yes
checking bluetooth/bluetooth.h usability... no
checking bluetooth/bluetooth.h presence... no
checking for bluetooth/bluetooth.h... no
checking for library containing hci_open_dev... no
configure: error: bluez bluetooth missing

Could you please help with installation? Thanks.

New release for packaging

Hi,

I'm considering packaging this library for openSUSE. I see that there were larger changes since the last (and only) release, 1.0. Would you consider making a new release so that the newest version could be packaged?

BLE advertising

This is a very sweet, tiny library. I've already used it to read out the bits in the overflow area in iOS advertisements.

Do you perhaps mind to also add an example on how to advertise with this utility? I've now to use hcitool cmd and hciconfig hci0 leadv 0 (or 3), etc. It would be swell to be quickly configure my laptop as an iBeacon.

Notification with send_write_command/send_write_request

Hello,
first thank you for this great lib!
I couldn't get notifications to work. After some debugging it looks like changing the "write" in method "set_notify_and_indicate" to a request rather than a command helped.
Is this a bug in my BLE device or a problem with libblepp?
Regards Ben

Use the same bluetooth connection to send more than one command receiving notifications?

Using the examples of the library I am able to send one command and recieve the notification from the device which I'm working with.

But currently I'm trying to reuse this connection and send more than one command so that is more efficient than using one connection to send each command.

The problem is that when I wait to recieve the answer from the first command (for example sleeping some seconds) nothing is recieved (if I dont wait and the function finishes the answer it is recieved). Mi idea is to send the next command after receiving the notification and checking that everything is OK.

Pseudocode:

std::function<void()> cb = [& gatt , & notify_cb ,  & enable] () {

		for (auto &service : gatt.primary_services)
		{
			//cout << "Setting up notifications..." << endl;
			if (service.uuid == UUID("UUID"))
			{
				for (auto &characteristic : service.characteristics){
					if (characteristic.uuid == UUID("CHARACTERISTIC UUID"))
					{
						uint8_t buf[1] = {0X0000};
						characteristic.cb_notify_or_indicate = notify_cb; // Function executed when a notification is recieved
						characteristic.set_notify_and_indicate(.....);	 

						// Here I would like to wait for the notification 

						buf[0] = uint8_t(0x0001);
						characteristic.set_notify_and_indicate(.....); // Send another command

						gatt.close(); // End 
						
					}
					else
					{
						cerr << "I am not able to execute the basic command" << endl;
					}				
				}
			}
		}

	};

Is it possible to do this with the library? If so, could you explain how?

Thanks in advance for any help! :)

Cannot get temperature.cc example to work

I modified it to take a hard-coded address and a different characteristic, and ran it against a BLE fitness watch. It connects, and finds the characteristic (which has .notify = true in the debugger) but the notify callback is never executed.

Here is the code, in case it helps, but it's 99% of what is in temperature.cc

int main(int argc, char **argv)
{
    log_level = Error;

    BLEGATTStateMachine gatt;

    std::function<void(const PDUNotificationOrIndication&)> notifyCallback = [&](const PDUNotificationOrIndication& n) {
        auto ms_since_epoch = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
        float temp = bluetooth_float_to_IEEE754(n.value().first+1);

        cout << setprecision(15) << ms_since_epoch.count()/1000. << " " << setprecision(5) << temp << endl;
    };
    
    std::function<void()> found_services_and_characteristics_cb = [&gatt, &notifyCallback]() {
        for (auto& service: gatt.primary_services) {
            for (auto& characteristic: service.characteristics) {
                // My custom characteristic
                if (characteristic.uuid == UUID("3ea6")) {
                    characteristic.cb_notify_or_indicate = notifyCallback;
                    characteristic.set_notify_and_indicate(true, false);
                }
            }
        }
    };
    
    gatt.setup_standard_scan(found_services_and_characteristics_cb);

    gatt.cb_disconnected = [](BLEGATTStateMachine::Disconnect d) {
        cerr << "Disconnect for reason " << BLEGATTStateMachine::get_disconnect_string(d) << endl;
        exit(1);
    };
    
    // My hard-coded address
    gatt.connect_blocking("88:6B:XX:XX:XX:XX");
    for (;;) {
        gatt.read_and_process_next();
    }
}

Example of write.

I started to play with your library, but can't found any example how to write something to BTLE device. I started my experiments from your temperature example and done some modifications, at the end of example I changed

gatt.connect_blocking(argv[1]);
for(;;)
	gatt.read_and_process_next();

into:

gatt.connect_blocking("5C:F8:21:F9:80:BD");
char* data = "!!#RTH\x0c";
while(done == false) {
  gatt.read_and_process_next();
}
gatt.send_write_request(0x12, (const uint8_t*)data, strlen(data));
gatt.write_and_process_next();
while(true) {
  gatt.read_and_process_next();
}

it works, but the structure is quite ugly. I also use variable "done" placed in discover characteristic callback to determine if I'm connected and have all characteristic enumerated. I didn't find a way to do it in more elegant way, but I got often exeption "Error trying to issue command mid state" Is there any set of functions which I can use to determine where I can do read/write to not cause this exception?

Why libblepp depends on bluez-devel?

When i saw met your repo, i was so excited that finally found an alternative to bluez that doesn't require dbus API.

But when i wanted to build libblepp, i got this error message:

↳ cmake ..
-- The C compiler identification is GNU 10.2.1
-- The CXX compiler identification is GNU 10.2.1
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/gcc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/g++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Setting build type to 'Release' as none was specified.
CMake Error at /usr/share/cmake-3.24/Modules/FindPackageHandleStandardArgs.cmake:230 (message):
  Could NOT find Bluez (missing: BLUEZ_LIBRARY BLUEZ_INCLUDE_DIR)
Call Stack (most recent call first):
  /usr/share/cmake-3.24/Modules/FindPackageHandleStandardArgs.cmake:594 (_FPHSA_FAILURE_MESSAGE)
  cmake/modules/FindBluez.cmake:65 (find_package_handle_standard_args)
  CMakeLists.txt:52 (find_package)


-- Configuring incomplete, errors occurred!
See also "/home/linarcx/Desktop/libblepp/build/CMakeFiles/CMakeOutput.log".

Seriously? Why libblepp depends on bluez-devel API?

Read data from btDevice

Hello,

I've been using your code since a few day, and I was wondering if it was possible to read data directly on the device connected ? I'm tried this code but I end up with the error
unexpected responce. Expected opcode, got readResponse

I tried to use directly the send_read_request from the Device but maybe I'm wrong

Do you have any advice ?

Thanks

sensorTag is the GATT device on the code

   bool enable = true;
    bool scanned = false;
    std::function<void()> cb = [&sensorTag, &notify_cb, &enable, &scanned](){

        for(auto& service: sensorTag.primary_services)
            for(auto& characteristic: service.characteristics){
                if(characteristic.uuid == UUID(irTemperatureSensorDataCharacteristicUUID))
                {
                    characteristic.cb_notify_or_indicate = notify_cb;
                    //characteristic.set_notify_and_indicate(true, false);
                }
                if (characteristic.uuid== UUID(irTemperatureSensorConfigurationCharacteristicUUID))
                {
                    const uint8_t configuration[] = {0x01};
                    sensorTag.send_write_request(characteristic.value_handle, configuration, sizeof(configuration));
                    std::cout << "Found sensor, send config to " << characteristic.value_handle << std::endl;
                }
            }
        scanned = true;
        std::cout << "------------------END OF INIT---------------" << std::endl;
    };

    sensorTag.cb_disconnected = [](BLEGATTStateMachine::Disconnect d)
    {
        std::cerr << "Disconnect for reason " << BLEGATTStateMachine::get_disconnect_string(d) << std::endl;
        exit(1);
    };



    try{
        sensorTag.setup_standard_scan(cb);
        sensorTag.connect_nonblocking(av[1]);

        for (int i = 0;;i++)
        {
            struct timeval tv;
            tv.tv_sec = 0;
            tv.tv_usec = 10000;

            FD_ZERO(&read_set);
            FD_ZERO(&write_set);

            FD_SET(sensorTag.socket(), &read_set);

            if(sensorTag.wait_on_write())
                FD_SET(sensorTag.socket(), &write_set);

            int result = select(sensorTag.socket() + 1, &read_set, &write_set, NULL, & tv);

            if(FD_ISSET(sensorTag.socket(), &write_set))
                sensorTag.write_and_process_next();

            if(FD_ISSET(sensorTag.socket(), &read_set))
            {
                sensorTag.read_and_process_next();
            }

          // I know that the data is stored on the handle 36
            if (i%1000 == 0 && scanned)
                sensorTag.dev.send_read_request(36);
        }
    }

How do you avoid "trying to issue command mid state"?

(This is an API usage question, not a bug report or anything)

So I want to do this:

  • Scan for characteristics
    • Set characteristic A to notify
    • Write value to characteristic B

When I try to do the last step, I run into this error. I'm doing those two steps inside the setup_standard_scan callback as I find each characteristic. What should I do instead? Loop on is_idle() inside the callback before I write...?

Here is my (pseudo)code:

BLEGATTStateMachine gatt;

std::function<void(const PDUNotificationOrIndication&)> notifyCallback =
[&](const PDUNotificationOrIndication& n) {
    // ...
};    

std::function<void()> servicesAndCharsCallback = [&gatt, &notifyCallback]() {
    for (auto& service: gatt.primary_services) {
        for (auto& chara: service.characteristics) {
            if (chara.uuid == UUID("ABCD")) {
                chara.cb_notify_or_indicate = notifyCallback;
                chara.set_notify_and_indicate(true, false);
            }
            else if (chara.uuid == UUID("WXYZ")) {
                uint8_t buffer[5];
                // ...
                chara.write_command(buffer);
            }
        }
    }
};

gatt.setup_standard_scan(servicesAndCharsCallback);
gatt.connect_blocking("myaddress");
for (;;) {
    gatt.read_and_process_next();
}

Thanks in advance for taking the time to help if you do :)

Texas Instruments Sensor Tag

Hello:

Thank you for your work.

I would like to use your library to control a TI Sensor Tag. However it is not clear
to me how I would do the following:

1)turning on the sensor tag by writing 0x01 to the characteristic f000aa02-0451-4000-b000-000000000000
2)Then reading the data by a read request on f000aa01-0451-4000-b000-000000000000

I have modified your temperature.cc code however whenever I try to turn on the sensor tag by writing 0x01-(using characteristic.write_request(uint8_t(1)); ) the program exits with a logic error.

Any help is appreciated.

west suhanic

One more (complex) example

Hi, excellent work! If you do not develop C++ every day, I found it difficult to start with the library to figure out how to make a sequence of BLE actions. Finally, I got something working. Maybe we can add this as another example? It's for a device that requires one byte to be sent (authentifcation) to actually start with notifications.
This example shows how to:

  1. connect and scan for characteristics
  2. enable BLE notify for certain characteristic
  3. write one byte for another characteristic
  4. reconnect on disconnect

console output looks like:

BEL: connect
BLE: enable notify
BLE: auth
notify: 19 : D2 64 04 00 FF 00 FF 00 FF FF FF FF C2 31 DF 31 B0 31 19
Disconnect for reason Connection Closed.
BEL: connect
BLE: enable notify
BLE: auth
notify: 19 : D2 64 04 00 FF 00 FF 00 FF FF FF FF C2 31 DF 31 B0 31 19
Disconnect for reason Connection Closed.
BEL: connect
BLE: enable notify
BLE: auth
notify: 19 : D2 64 04 00 FF 00 FF 00 FF FF FF FF C3 31 DF 31 B0 31 19
Disconnect for reason Connection Closed.
BEL: connect
BLE: enable notify
BLE: auth
notify: 19 : D2 64 04 00 FF 00 FF 00 FF FF FF FF C3 31 DF 31 B0 31 19
Disconnect for reason Connection Closed.
#include <iostream>
#include <iomanip>
#include <blepp/blestatemachine.h>
#include <unistd.h>
#include <chrono>
using namespace std;
using namespace chrono;
using namespace BLEPP;

#define DEVICE_UUID    "F4:B8:5E:AF:2A:A0"
#define SERVICE_UUID   "0000fff0-0000-1000-8000-00805f9b34fb"
#define AUTH_UUID      "0000fff3-0000-1000-8000-00805f9b34fb"
#define DATA_UUID      "0000fff4-0000-1000-8000-00805f9b34fb"
#define AUTH_DATA      0xD2

BLEGATTStateMachine gatt;
bool connected = false;

int main(int argc, char **argv){
	//log_level = Info;		

	std::function<void(const PDUNotificationOrIndication&)> notify_cb = [&](const PDUNotificationOrIndication& n)
	{		
    printf("notify: %d : ", n.num_elements());    
    const uint8_t* d = n.value().first;
    for (int i=0; i < n.num_elements(); i++){
      printf("%02X ", d[i]);
    }
    printf("\n");       
    gatt.close();    	
	};
  

	std::function<void()> cbNotify = [&gatt, &notify_cb](){
    for(auto& service: gatt.primary_services)
      if(service.uuid == UUID(SERVICE_UUID))	{
        for(auto& characteristic: service.characteristics){
          if(characteristic.uuid == UUID(DATA_UUID))
          {            
             printf("BLE: enable notify\n");
             characteristic.cb_notify_or_indicate = notify_cb;
             characteristic.set_notify_and_indicate(true, false);                                               
          }                    
        }
      }          
    
    gatt.read_and_process_next();
               
    for(auto& service: gatt.primary_services)
      if(service.uuid == UUID(SERVICE_UUID))	{
        for(auto& characteristic: service.characteristics){
          if(characteristic.uuid == UUID(AUTH_UUID))
          {
              //Send a 1 (you can also send longer chunks of data too)
              printf("BLE: auth\n");            
              characteristic.write_without_response = true;
              characteristic.write_request(uint8_t(AUTH_DATA));                                      
          }      	
        }
      }              
	};
  
   
	gatt.cb_disconnected = [](BLEGATTStateMachine::Disconnect d)
	{
		cerr << "Disconnect for reason " << BLEGATTStateMachine::get_disconnect_string(d) << endl;		    
    gatt.connect_blocking(DEVICE_UUID);
	};
	
	gatt.setup_standard_scan(cbNotify);  


	gatt.cb_disconnected = [](BLEGATTStateMachine::Disconnect d)
	{
		cerr << "Disconnect for reason " << BLEGATTStateMachine::get_disconnect_string(d) << endl;		        
    connected = false;
	};
	
	gatt.setup_standard_scan(cbNotify);  
  
	//This is how to use the blocking interface. It is very simple.  
	for(;;){
    if (!connected){
      printf("BLE: connect\n");	     
      try {     
        gatt.connect_blocking(DEVICE_UUID);
        connected = true;
      }  
      catch (...) {     
        printf("BLE: error connecting\n");
      }  
    }
		gatt.read_and_process_next();    
  }
  
}

BLE advertising channel

I would like to know how to see in which channel (37,38,39) the BLE device is advertising its data by using libblepp?.

Lack of call to cb_disconnected on close()

Library is not calling callback cb_disconnected after call to close().
This is unexpected, since I got callback cb_connected when connection establish, so after close I would expect to have cb_disconnected. I digged a little in your code and looks like you executing cb_disconnected in few places, sometimes after call to close() so adding this callback inside close is not an option. I believe there can be 2 close implementations:

  1. current close() move to innerClose() and all current calls should use this.
  2. create public close() which will call innerClose and cb_disconnected.

I/O error & Switching off HCI scanner

Hi, I trying to run "lescan_simple" from examples.
the environment is ubuntu 20.04 in VM.
I compiled and ran the program, but I got an error and execution aborted with following msg.

warn  1643202449.692793: Received I/O error while setting scan parameters.
warn  1643202449.692880: Switching off HCI scanner
error 1643202449.695767: Error disabling scan:: Input/output error
terminate called after throwing an instance of 'BLEPP::HCIScanner::IOError'
  what():  Error disabling scan:: Input/output error
Aborted

the problem related to HCIScanner of bellow command:
BLEPP::HCIScanner scanner;

Can anyone help me to fix the problem?
any idea

eddystone frame

Hello, i am using this library to scan different types of frames (iBeacon and eddystone). I can receive iBeacon without problems, but when i try to scan an eddystone frame i can't receive it.

Does this library allow receive eddystone frames?

Thanks.

Build as a subproject

Please describe how to add this library to build as a subproject.

When I tried to add it with FetchContent_Declare/FetchContent_MakeAvailable I get:
By not providing "FindBluez.cmake" in CMAKE_MODULE_PATH this project has asked CMake to find a package configuration file provided by "Bluez", but CMake did not find one.
as CMAKE_MODULE_PATH is extended with set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules), but not applied properly.

One-To-Many Example Needed

I sent you an email about this Ed, but I figured I'd open an issue for it. I am planning on using libblepp on a RaspberryPi for a project and it requires the communication of one central controller (the Pi) to many peripherals (teensy/bluefruit friend, bluefruit feather, phone, etc...). I am still learning how to use BLE in general and of the different concepts involved, and as a BLE beginner it is unclear to me if libblepp supports this kind of thing (software engineering professional, never used the bluetooth stack before). This is the only C++ library I have found that wraps BlueZ and I am trying to avoid Python at all costs due to speed and power restrictions.

Library interface

Hi,

I really like the work you've done so far, finally somebody managed to weed out the glib dependency, great job! The library interface is rather convoluted, though. What do you think about something along the lines of:

BLEPP blepp("connection-string");
bleep.characteristics();
blepp.connect();
blepp.read("uuid");
blepp.write("uuid", data);
blepp.writeRequest("uuid", data, lambda);
blepp.listen("uuid", lambda);
blepp.disconnect();

Also I would suggest you switch over to CMake, Autotools are a bit archaic don't you think?
I'm willing to help you out 😄

No notify_cb called

The function notify_cb never called on blelogger and bluetooth example, but our characteristic has the flag of notify
any ideas?

add a ProjectConfig.cmake ?

Hi everyone,

Thanks @edrosten for the great package and others for the switch to CMake.
To complete this, it would be nice to add a config.cmake file has described here :
https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/How-to-create-a-ProjectConfig.cmake-file

Then it could be easy to include this package in another cmake project.
Here is my first quick attempt... https://github.com/JimmyDaSilva/libblepp/tree/config.cmake
The only issue I saw is that it doesn't not include the bluez required cmake, so I had to include it manually in my other CMake project.

Add MTU Renegotiaton

I am working on adding MTU negotiation
See issue #31

In the PDUResponse handling code in the state machine, we need to add code to setsockopt with new MTU values. However, when I try this, I get errno 22.

I created a test case outside of libblepp, and get the same errors. That end bit - setsockopt - that is where it runs into trouble. Anyone have any thoughts on this, or can someone try it?
You can update the code for your needs
devaddr - hci device address you want to use per hciconfig
connectto - gatt server device address.

#include
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/l2cap.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
#include

int main() {
sockaddr_l2 sba;
sockaddr_l2 addr;
bdaddr_t btsrc_addr;
int sock;
int retval;
memset(&sba, 0, sizeof(sba));
std::string devaddr = "00:15:83:EA:57:D8";
std::string connectto = "00:0B:57:7F:72:BF";
sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
int dev_id = hci_devid(devaddr.c_str()); //obtain device id from HCI device name
hci_devba(dev_id, &btsrc_addr);
bacpy(&sba.l2_bdaddr,&btsrc_addr); //lifted from bluez example, populate src sockaddr with address of desired device
sba.l2_family=AF_BLUETOOTH;
sba.l2_cid = htobs(4); //Spec 4.0 G.5.2.2
sba.l2_bdaddr_type = BDADDR_LE_PUBLIC;
retval = bind(sock, (sockaddr*)&sba , sizeof(sba));
std::cout << retval << "\n";
memset(&addr, 0, sizeof(addr));
addr.l2_family = AF_BLUETOOTH;
addr.l2_psm = 0;
addr.l2_cid = htobs(4); //Spec 4.0 G.5.2.2
addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
int rr = str2ba(connectto.c_str(), &addr.l2_bdaddr);
retval = connect(sock, (sockaddr*)&addr, sizeof(addr));
std::cout << retval << "\n";

//set socket options
l2cap_options options;
socklen_t len = sizeof(options);
memset(&options, 0, len);
retval = getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &options, &len);
socklen_t len2 = sizeof(options);

int checksock = 0;
socklen_t errlen;
errlen = sizeof(checksock);
getsockopt(sock, SOL_SOCKET, SO_ERROR, &checksock, &errlen);
std::cout << strerror(checksock) << "\n"; //confirm socket is in an OK state
int s_status = setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &options, sizeof(options));

std::cout << errno << "," << strerror(errno) << "\n";

}

Function not implemented - blestatemachine.cc

In blestatemachine.cc, there's a log_fd call that prints a log message "Function not implemented" at line 296. The message can be ignored but is falsely reporting a problem. The return value for ::connect() is 0 for success and thus the macro fails and prints the message.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.