petzval / btferret

Python and C Bluetooth Library
MIT License
122 stars 21 forks source link

How to connect 2 LE servers together? #48

Closed gregoiregentil closed 2 months ago

gregoiregentil commented 2 months ago

I have two devices A and B running le_server(). they both include a bunch of characteristics including some notifications.

I have an Android phone using the Android BLE API that connects to both devices A and B at the same time. Obviously, both devices have different MAC address. No problem so far. I want to keep those connections open.

Now, I want to connect A and B together, get the notifications of each other. I might have prefered to connect them through classic but BLE would be OK.

In other words, which architecture should I implement to let le_server A and B talk to each other? Thank you in advance.

petzval commented 2 months ago

The library does not allow a device to be a Classic and LE server at the same time. I have tried the setup and confirmed that A can connect to B and receive notifications, but then B cannot connect to A. The simplest solution is to send data from B to A as notifications, but send from A to B by writing to a B characteristic. So Device A must perform two operations - write to the local characteristic to send a notification to Android, and write to Device B's Receivedata characteristic.

* NOTE Do not call read_notify. The le_server mechanism checks for notifications.

Characteristic definitions:

Device A Node = 1
LECHAR = Notifydata   PERMIT = 12 SIZE = 1 UUID = ABCD    ; index 0

Device B Node = 2
LECHAR = Notifydata   PERMIT = 12 SIZE = 1 UUID = ABCD    ; index 0
LECHAR = Receivedata  PERMIT = 06 SIZE = 1 UUID = CDEF    ; index 1

Device A LE callback code
int le_callback(int clientnode,int operation,int cticn)
  {
  static int bconnected = 0;
  unsigned char buf[256]; 

  if(operation == LE_CONNECT)
    {
    printf("  %s has connected\n",device_name(clientnode));
    if(bconnected == 0)
      {
      // Connect to Device B and enable Notifydata notifications
      connect_node(2,CHANNEL_LE,0);
      find_ctics(2);
      notify_ctic(2,0,NOTIFY_ENABLE,ncall);
      bconnected = 1;
      }

  if(operation == LE_TIMER)
    {
    // Send notification to Android
    buf[0] = 56;
    write_ctic(localnode(),0,buf,1);
    // Send data to Device B Receivedata
    write_ctic(2,1,buf,1);  
    }
  }

Device A notify callback

int ncall(int node,int cticn,unsigned char *buf,int nread)
  {
  // Receive notification from Device B
  printf("Data = %d\n",buf[0]);
  }

Device B LE callback code 

int callback(int clientnode,int operation,int cticn)
  {
  unsigned char buf[8];

  if(operation == LE_WRITE)
    {
    // Receivedata from Device A - cticn should be 1
    read_ctic(localnode(),cticn,buf,8);  
    printf("Data = %d\n",buf[0]);
    }

  if(operation == LE_TIMER)
    {
    // Send notifications to Android and Device A
    buf[0] = 57;
    write_ctic(localnode(),0,buf,1);
    }
}
gregoiregentil commented 2 months ago

You are not completely getting my requirement: both deviceA and deviceB should be LE servers in order to communicate directly with a native Android app and both devices should communicate together.

I have prepared an example: https://gentil.com/tmp/download.html?d=test-20240905-214900.zip

deviceB is started as LE server only (press 's'). If deviceA is LE client only (press 'c'), no problem. If I start LE server (press 's') after LE client on device A, no problem too.

But if deviceB and deviceA are started as LE server (press 's') and then deviceA is started as LE client (press 'c'), it's not working.

petzval commented 2 months ago

The suggested solution does implement the requirement, but it cannot be done from existing btferret commands, you need to modify the callback code for Device A (le_callback in btferret.c), or write an LE server program from scratch - start with le_server.c. The procedure is as follows:

  1. Start Device A as an LE server
  2. Start Device B as an LE server
  3. Connect to both from Android
  4. From within Device A's LE callback code, connect to Device B. As an example I have modified the code to do this in the LE_CONNECT block, so it is triggered when the Android connects. So Device A is now an LE server for Device B and Android, and an LE client for Device B. Device B is an LE server for Device A and Android.
  5. Device B sends data to Android and Device A via a notification .
  6. Device A sends data to Android via a notification, and as a separate operation sends data to Device B as a characteristic write.
  7. You need to add the data send code to le_callback for Device A and Device B - maybe in the LE_TIMER block as in the modified code example, so start le_server with a timer value.
gregoiregentil commented 2 months ago

Thank you.