petzval / btferret Goto Github PK
View Code? Open in Web Editor NEWPython and C Bluetooth Library
License: MIT License
Python and C Bluetooth Library
License: MIT License
I want to use your nice C package to connect from my PC to a BLE chessboard (Chessnut Air). All communications to the board work well, except the notifiaction callbacks always receive only 20 bytes from the actually sent 38 bytes by the board.
I am sure that the board is OK, since it works well with an USB cable & Android apps.
Some lines of my code:
cticn = 5;
int flag = 1; // inputint("0=Disable 1=Enable");
char *s = "enabled";
if (ctic_ok(node,cticn) == 0) { printf("Invalid index\n"); return 1; }
if (notify_ctic(node,cticn,flag, conf_callback) == 0) s = "failed";
printf("notifyle %s %s %s cticn=%d\n",device_name(node),ctic_name(node,cticn),s,cticn);
cticn = 3;
if (notify_ctic(node,cticn,flag,board_notify_callback) == 0) s = "failed";
printf("notifyle %s %s %s cticn=%d\n",device_name(node),ctic_name(node,cticn),s,cticn);
Those are Android logs. Note that my Android app requests the high connection priority.
Version 9 (with //paramreply[PAKHEADSIZE+j+6] = insdat[n+j+3];) ends in 7.5ms:
2023-04-13 20:42:37.331 32740-405 bt_stack pid-32740 I [INFO:le_scanning_manager.cc(318)] SetScanParameters: scan_type=1, scan_interval=6553, scan_window=6553
2023-04-13 20:42:37.495 32740-405 bt_stack pid-32740 I [INFO:le_scanning_manager.cc(318)] SetScanParameters: scan_type=1, scan_interval=2977, scan_window=324
2023-04-13 20:42:39.444 9443-9576 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=6 latency=0 timeout=500 status=0
2023-04-13 20:42:39.599 9443-9576 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=6 latency=0 timeout=500 status=59
2023-04-13 20:42:39.722 9443-9443 Capacitor/Console sport.inout.inout I File: http://localhost/main.js - Line 377 - Msg: Request HIGH ConnectionPriority for interval
2023-04-13 20:42:39.749 9443-9458 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=6 latency=0 timeout=500 status=30
Version 10 ends in 45ms:
2023-04-13 20:38:08.310 32740-405 bt_stack pid-32740 I [INFO:le_scanning_manager.cc(318)] SetScanParameters: scan_type=1, scan_interval=6553, scan_window=6553
2023-04-13 20:38:08.449 32740-405 bt_stack pid-32740 I [INFO:le_scanning_manager.cc(318)] SetScanParameters: scan_type=1, scan_interval=2977, scan_window=324
2023-04-13 20:38:09.445 9443-9559 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=6 latency=0 timeout=500 status=0
2023-04-13 20:38:09.667 9443-9559 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=36 latency=0 timeout=500 status=0
2023-04-13 20:38:09.773 9443-9443 Capacitor/Console sport.inout.inout I File: http://localhost/main.js - Line 377 - Msg: Request HIGH ConnectionPriority for interval
2023-04-13 20:38:09.850 9443-9576 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=36 latency=0 timeout=500 status=30
Version 10 + "if(op == LE_CONNECT) set_le_interval_server(clientnode,6,6);" ends in 11.25ms:
2023-04-13 20:50:55.174 32740-405 bt_stack pid-32740 I [INFO:le_scanning_manager.cc(318)] SetScanParameters: scan_type=1, scan_interval=6553, scan_window=6553
2023-04-13 20:50:55.414 32740-405 bt_stack pid-32740 I [INFO:le_scanning_manager.cc(318)] SetScanParameters: scan_type=1, scan_interval=2977, scan_window=324
2023-04-13 20:50:56.419 9443-9459 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=9 latency=0 timeout=500 status=0
2023-04-13 20:50:56.598 9443-9459 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=6 latency=0 timeout=500 status=0
2023-04-13 20:50:56.721 9443-10133 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=9 latency=0 timeout=500 status=0
2023-04-13 20:50:56.776 9443-9443 Capacitor/Console sport.inout.inout I File: http://localhost/main.js - Line 377 - Msg: Request HIGH ConnectionPriority for interval
2023-04-13 20:50:56.801 9443-10133 BluetoothGatt sport.inout.inout D onConnectionUpdated() - Device=702C1F_A interval=9 latency=0 timeout=500 status=30
How can I get back my 7.5ms?
When I call 'read_notify' in my C-program, after all initialisations to the device have been properly done, it consumes 100% of CPU time of my Ubuntu-Notebook (1 thread is completely busy). Easy to see with 'top'.
The same effect occurs with the test program btferret.
Is there any way to prevent this behaviour?
I have a hard-time to make work notification. Here is a simple case that doesn't work: https://gentil.com/tmp/test.zip
Hi, me again!
I would like to know if you could give a void pointer to the callback so I can send a structure to the callback (as I need an object from the main function).
So when calling le_server it would receive a third void pointer parameter.
le_server(le_callback,0,(void *)myStruct);
And the callback function would then receive this pointer when called:
int le_callback(int clientnode,int operation,int cticn,void *pvParameter)
{
struct sObject *myStruct = (struct sObject*) pvParameter;
//some more code...
}
Does your library provide support for BLE connection with 6-digit passkey authentication?
If yes, could you please provide any examples on how to implement a BLE Client and connect it to a server using this functionality?
Hi,
Thank you for sharing this extremely useful tool!
Is there a way to run both a LE and Classic server at the same time on the same RPI device?
Will I have to start to independently threaded instances of LE_Server and Node_Server?
Many Thanks!
A very important configuration is the Gatt server uuid. It's important because if you use the web bluetooth api, you can filter by service uuid.
Right now, the value is hard-coded in line https://github.com/petzval/btferret/blob/main/btlib.c#L669 and also line https://github.com/petzval/btferret/blob/main/btlib.c#L6592.
I think that it would make sense to add one line in devices.txt, something like: gatt uuid=....
Hi,
In file btlib.c the LEDATLEN is defined as 20. Does that mean the MTU size, right?
I want to send and receive data up to 128 byte. Is it possible? If yes how?
I changed LEDATLEN to 128, and in my nRF52840 firmware also, but it sends/receives still only 20 byte.
I think in btlib.c lots of arraysizes are hardcoded with 20.
I have problems with saving found LE-devices in the file 'device.txt'. First I do a scan of LE-devices with btferret. First I do a LE-scan with btferret:
'> b'
.... This is my device:
3 FOUND 00:1B:10:4F:FFFFF - Fixed
Flags = 06
Manuf specific = 50 44 43 53 CB 54 77 64 00 00 B8 AD 4F 10 1B 00
Tx Power Level = 0A
No name so set = LE node 1003
New device LE node 1003
....
Then connect to node 1003 and all the rest works correctly.
But if I try to save the device in 'devices.txt' as
DEVICE = Chessnut TYPE=LE node=1 ADDRESS = 00:1B:10:4F:FFFFF
I get an format error:
Device data from devices.txt file
DEVICE = Chessnut TYPE=LE node=1 ADDRESS = 00:1B:10:4F:FFFFF
Hex format error - odd number of digits
ERROR **** Device address must be 6 bytes
************ initblue() FAILED ************
So I have to scan for LE-devices each time at program start, which is a little bit annoying🙁.
Can you tell me what would be the correct entry for this?
Hey, BLE noobie here...I don't see a way to add more than one BLE service using either the documented methods or the Devices.txt file. I would also like to change the one that is used by default to something else on the fly. Is this a possibility and I am just missing something?
I have the multiple characteristics within the default service seemingly working great. Thanks
Hi!
Thank you for sharing this very useful library & brilliant explanation (readme).
Nice low-level approach.
But I encountered some problems, main is that after first run (>b ; >g) , PC (Ubuntu16) where program is running lost BT-mouse.
Attempting to run a btferrent once more leads to :
Unknown device 52:24:94:23:C7:A5 connected - rejecting.. // that is my mouse
Unable to read local board address
Device data from devices.txt file
I suppose that is because Bluez — «off».
ioctl with HCIDEVUP before exit has no effect.
I run vers.12. Any ideas?
This is a great library. I have a case that doesn't seem to be fully supported: I would like to connect from an Android app using Bluetooth Classic. More important, the connection/pairing procedure needs to be initiated by the Android app. From my experiment, classic_server needs to know the Android remote address that it will accept connection from.
Why isn't it there a "classic_server(-1, ..." that would accept connection from any client?
The only strategy I see is to have Android to connect to BLE to btfferret, write to a characteristic or something so that btferret is aware of the Android address. Then connect from Android to classic_server(known_android_node,...)
Am I misunderstanding the library?
Hi,
I try to establish a communication to my nRF52840 Module. With btferret I can see my device in the list, I can connect to it and can list the characteristics, I can subscribe to notification and I can receive the notification from nRF52840. Everything works as expected.
However, I wrote a minimal code to connect to nRF52840 but the code gives always error: Unknown Connection Identifier
My nRF52840 sends advertisement every 200ms.
What is wrong in my code?
Thanks.
devices.txt:
DEVICE = My Rpi TYPE = MESH NODE = 1001 ADDRESS = <bt address of pi>
DEVICE = CO2 Sensor Module TYPE = LE NODE = 1002 ADDRESS = <address of nRF52840>
main.c:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "btlib.h"
int main(int argc, char *argv[])
{
int retval = 0;
if (init_blue("devices.txt") == 0) {
return (0);
}
set_print_flag(PRINT_VERBOSE);
//le_scan();
set_le_wait(12000); //ms
retval = connect_node(1002, CHANNEL_LE, 0);
while (1)
{
/* code */
}
printf("ret:%d\nDisconnecting everything and exit..\n",retval);
close_all();
return (0);
}
Console output:
Initialising...
Device data from devices.txt file
DEVICE = Tunnel Air Quality TYPE = MESH NODE = 1 ADDRESS = <bt address of pi>
DEVICE = CO2 Sensor Module TYPE = LE NODE = 2 ADDRESS = <address of nRF52840>
SEND LE connect to CO2 Sensor Module
Set [10].. board address reversed 05..E0
< HCI OGF=08 OCF=0D
0000 01 0D 20 19 60 00 60 00 - 00 00 05 C6 27 97 D0 E0
0010 00 18 00 28 00 00 00 11 - 01 00 00 00 00
> Event 0F = 00 01 0D 20
0000 04 0F 04 00 01 0D 20
Timed out waiting for expected packet
< HCI OGF=08 OCF=0E
0000 01 0E 20 00
> Event 0E = 01 0E 20 00
0000 04 0E 04 01 0E 20 00
Unknown Connection Identifier
> Event 3E = 01 02 40 00 00 00 05 C6 27 97...
0000 04 3E 13 01 02 40 00 00 - 00 05 C6 27 97 D0 E0 27
0010 00 00 00 11 01 00
STATUS OK
Fail - no handle
This device may have a random board address which changes
Scan for LE devices to find the current address
leconnect: 0
Compilation of btferret following the instructions in the README results in the following two errors:
mmourier@optiplex:~/BLE/btferret$ gcc btferret.c btlib.c -o btferret
btferret.c: In function ‘getstring’:
btferret.c:1777:5: warning: format not a string literal and no format arguments [-Wformat-security]
1777 | printf(prompt);
| ^~~~~~
btlib.c: In function ‘localctics’:
btlib.c:2047:36: warning: ' ' flag used with ‘%o’ gnu_printf format [-Wformat=]
2047 | NPRINT "%shandle must be % or greater\n",errs,starthandle);
| ^
The fix is pretty straightforward, as shown by the diff's below:
mmourier@optiplex:~/BLE/btferret$ git diff
diff --git a/btferret.c b/btferret.c
index bec3d51..15e442c 100644
--- a/btferret.c
+++ b/btferret.c
@@ -1774,7 +1774,7 @@ void getstring(char *prompt,char *s,int len)
do
{
- printf(prompt);
+ printf("%s",prompt);
fgets(s,len,stdin);
}
while(s[0] == 10);
diff --git a/btlib.c b/btlib.c
index 8b07ad3..e62a10d 100644
--- a/btlib.c
+++ b/btlib.c
@@ -2044,7 +2044,7 @@ int localctics(int starthandle)
{
if(cp->chandle < starthandle)
{
- NPRINT "%shandle must be % or greater\n",errs,starthandle);
+ NPRINT "%s handle must be %d or greater\n",errs,starthandle);
return(0);
}
And when compilation warnings are enabled, gcc prints lots of them, many related to mixing up pointers of different sign: e.g. "char *" and "unsigned char *". I think it would be "a good thing" if the code could compile warning-free ...
Hi petzval, I am trying to make a mesh from 3 Raspberry Pis. One node will broadcast data to the two other nodes, this part works, but at the same time, I want them to also listen to the other Raspberry Pis for data. However, I am having a problem with the latter. For example, when the mesh server starts listening in raspi A, I tried to send data to other raspi but there is an error like this.
Initialising...
Bluez down failed
Bind failed
HCI socket ERROR - no Bluetooth?
Is it possible to make the node broadcast data and listen for data from the other nodes at the same time? If possible, How can I do it? I'm sorry I am a beginner at this topic. Thank you so much!
Another issue I am facing right now is I tried to code the MESH BROADCASTER but when I try to run it, I cannot receive the data that it sends to other nodes. This is my code:
#include <stdio.h>
#include <stdlib.h>
#include "btlib.h"
int main()
{
if(init_blue("devices.txt") == 0)
return(0);
char buf[4];
buf[0] = 0x12;
buf[1] = 0x34;
// broadcast two-byte mesh packet
write_mesh(buf,2);
// broadcast D disconnect command
write_mesh("D",1);
return(0);
}
trying the sample i get an error while binding the socket to the local adapter in VM, did't change anything just tried to run sample.c and btferret.c
I'm interested to improve the communication between btferret peripheral GATT server and iOS. You might want to read this page https://punchthrough.com/maximizing-ble-throughput-on-ios-and-android/ for interesting information.
Right now I see: https://pastebin.com/e0T1s95H. I think that 30ms makes sense considering the link above. How can I put btferret in "HID device" GATT server mode?
All the writable characteristics are without response. It would be great to have an option to make it with response.
During advertisement of a BLE server, the peripheral can suggest some values for min-interval, max-interval and supervision to the central. The central who usually plays the role of client decides in the end. It would be great to be able to modify those values especially the recommending ones on server side?
I have a scenario of high traffic data going from a device/BLE/peripheral/GATT_server to a Linux_PC/BLE/central/GATT_receiver, both using btferret. The device sender uses write_ctic, the PC receiver uses notification on the same characteristic. High traffic means packet every 10ms.
How can I know that the device sender can write new data to write_ctic? Right now, I need to put a large usleep before writing to write_ctic otherwise btferret goes wild and the hci kernel stack is screwed up, requiring a reboot. The PC receiver listening to the notification is working fine whatever the library (btferret, bluez, webBluetooth) is used.
(Note1: the same scenario works with bluez on both sides though it's not optimal and it has its own set of problems. But at least, it shows that the scenario is possible from a BLE hardware point of view.
Note2: the same scenario works even better using Bluetooth Classic with or without btferret. That's not surprising taking into account the bandwidth of Classic vs. BLE. But I need to make work my scenario with BLE.)
Hello,
at first thank you very much for nice library. I would like to ask you if I can connect 2 BLE devices simultaneously?
I tried to create 2 pthread_t threads and have each process of BLE device under 1 thread, but it doesn't work.
It seems that only 1 connection is allowed.
I'm not skilled at Bluetooth. Can I use it for this purpose?
Thanks
I'm trying to integrate this library in an application that already uses stdin. It would be great to add a daemon option to the library so that there is no conflict about stdin.
Accessory, I would be interested to know how I should patch btlib.c to prevent stdin usage. Should I return 0 in setkeymode and readkey?
I'm trying to connect to 15 BLE devices by splitting up the connections between three adapters. I can see that I should use init_blue_ex
to specify the hci
number of the adapter but this seems to set some global state. Is there a way that I can initialize three separate adapters such that I can work with all of them?
Hello --
I just stumbled across your excellent library, and I'm curious to try to compile as well for macOS and Windows (using mingw).
I think it should be possible, with some tweaks. Has anyone tried either of these?
I found a bug by calling the read_ctic function.
After trying out multiple times, I found out that reading 1, 2 and 4 bytes does work but not 3 (gives ERROR_FATAL).
(For my particular case I am sending a 3 byte long information for rgb colors).
Here is the code I'm using:
typedef struct sColor
{
uint8_t r;
uint8_t g;
uint8_t b;
} sColor_t;
//Some more code...
char cBuffer[5];
iError = read_ctic(localnode(),3,cBuffer,3);
if(ERROR_FATAL != iError)
{
sChoosedColor.r = (uint8_t)cBuffer[0];
sChoosedColor.g = (uint8_t)cBuffer[1];
sChoosedColor.b = (uint8_t)cBuffer[2];
printf("sChoosedColor = r:%d g:%d b:%d\n",sChoosedColor.r,sChoosedColor.g,sChoosedColor.b);
}
else
{
printf("Fatal error by Color reading\n");
}
During the boot sequence of my embedded device on which I run btferret, I need to run a Broadcom script to load the Bluetooth firmware. In my app, I wait for hci0 to come up:
socket(31, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, BTPROTO_HCI);
while (failure) {
ioctl(dd, HCIGETDEVINFO, (void*) &di);
check di info
}
init_blue();
le_server(le_callback, 0);
Then, the device advertises correctly and I can see the advertisement on a phone or a PC. btferret outputs "Listening for LE clients to connect (x=stop server)". No problem so far.
But if I connect from that phone or PC, nothing is received by btferret. log doesn't show any "< HCI OGF=08 OCF=0A" when the connection is tried from the phone.
I need to insert a sleep between 1 and 5 seconds before calling init_blue and then btferret can successfully receive data from the adapter.
Obviously, If I start my app manually a "long time" after boot, everything works correctly including a device connection.
How could I debug this situation? Or more interestingly, what do I need to check to confirm it's not too early to start btferret during the boot sequence?
Sometimes the LE-server of my chessnut device disconnects (in case of weak connections).
Then I want to start a new connection, but my client thread gets stuck in function 'readhci()' and so no further actions are possible.
(Anyway, every 10 seconds I see a message '... not connected' from btlib.c).
This is the output of btlib.c:
...
4999 6234 board_cb received 38 byte
chessnut has disconnected
chessnut not connected as LE server
chessnut not connected as LE server
...
How is it possible to react to this disconnections?
To start I would like to thank you for developping this library, because a tested a lot of different libraries and they were either badly documented or not functionnal on my raspberry and yours was the first I found that worked fine the first time I used it.
For my testing procedures I'm using the nRF Connect Application on my iPhone XS and the bluetooth server is running on a Raspberry Pi B3 (if it can help for the debugging).
I tried multiple things and found out that updating the devices.txt file between two runs wasn't working as it kept finding the characteristics I've set the first time on the nRF Connect application.
Here are screenshots of the application logs:
And the here is my devices.txt file:
devices.txt
Here you can see that on nRF Connect there isn't the new "Send" Characteristic I added:
Hi,
I'm currently doing a project on BLE mesh networks, and your interface seems perfect for this.
However I keep getting the following errors:
Initialising...
Bluez down failed
Bind failed
HCI socket ERROR - no Bluetooth?
What would the problem be and how can I fix it?
In the code, I see:
unsigned char leadvert[40] = { 36,0,0,0,0x01,0x08,0x20,0x20,0x0F,0x08,0xFF,0x34,0x12,
0x00,0x00,0xC0,0xDE,0x99,0x05,0x08,0x61,0x62,0x63,0x64,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
btmon says:
< HCI Command: LE Set Advertising Data (0x08|0x0008) plen 32 [hci0] 6.798348
Length: 17
Company: not assigned (50087)
Data: 0000c0de99
What is the relation between 0xC0,0xDE,0x99 and 50087? Android sees 50087.
Failing to compile in aarh64-linux-gnu. Function declaration doesn't match implementation. It compiles when changing declaration to:
int le_server(int(*callback)(int clientnode, int operation, int cticn),int timerds);
Hi petzval,
I have been using this library for the past week and it is great! The documentation is so clear. But I am running into a problem. I'm communicating between two Raspberry pis, one acting as a LE node server, that listens and receives packets, and the other as a node client, that sends packets to the node server continuously, every 500ms, until the loop is exited through a keyboard press. I'm using the function write_node() at the client to send the data. This data is just a 3 byte array of chars. But after a while, after the first 300 messages, the transfer lags a lot. By a few seconds. This is a huge delay, considering I am sending values to control the speed of a motor. How to I reduce this huge delay?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.