serverbluetoothclientadapterrfcomm

Running two Bluetooth programs on one machine using two adapters


New Post

I now understand my problem slightly better and rather than making a new thread I'll just update this one.

So using hci_open_dev is erroneous as it opens a socket to the local BT adapter only. It does not help me establish a connection to a remote device.

To summarise - I have a built in BT adapter in my laptop and I've bought a little USB BT adapter which I've plugged into the same laptop. Now I want to run two programs on this one machine that can communicate. To do this they need to use different adapters.

The second argument to bind is a struct sockaddr*, which I simply cast my previous struct sockaddr_rc* to. The bdaddr_t field of a listening socket's struct sockaddr specifies which BT adapter to use, allowing me to do this:

struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
// address of the built in BT adapter on my machine
const char* adapter = "60:F2:62:1B:9C:74";
// converting it to a bdaddr_t*
bdaddr_t* adapterbdadd = strtoba(adapter);

// allocate socket
int s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

// bind socket to port 1
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = *adapterbdadd;
loc_addr.rc_channel = (uint8_t) 1;
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));

// set the socket to listening mode
listen(s, 1);

// accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt);

// convert the bdaddr to a char* (just assume buf is defined elsewhere)
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);

// free socket resources
close(client);
close(s);

For an outgoing connection, however, I am not sure how to specify which adapter to use. The code looks roughly like this:

struct sockaddr_rc addr = { 0 };
// This is the same address that should be used by the listening program above
char dest[18] = "60:F2:62:1B:9C:74";

// allocate a socket
int s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

// set the connection parameters. Want to connect to the listening program
// and connect through port 1. If I change both programs to use port 0 I
// get an Invalid Argument error from this program.
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1;
str2ba( dest, &addr.rc_bdaddr );

// connect to listening program
int status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

// This program never reaches beyond this point
if( status == 0 ) {
    printf("successfully connected\n");
} else {
    perror("error connecting");
}

close(s);

As seen above I can only specify which device to connect to, which device to use. When I first start up the listening program followed by the client, the connection is never established. The output from running hciconfig looks like this:

hci1:   Type: Primary  Bus: USB
    BD Address: 60:F2:62:1B:9C:74  ACL MTU: 1021:4  SCO MTU: 96:6
    UP RUNNING 
    RX bytes:16967 acl:0 sco:0 events:2751 errors:0
    TX bytes:678007 acl:0 sco:0 commands:2749 errors:0

hci0:   Type: Primary  Bus: USB
    BD Address: 5C:F3:70:9D:BC:07  ACL MTU: 1021:8  SCO MTU: 64:1
    UP RUNNING 
    RX bytes:1016 acl:0 sco:0 events:57 errors:0
    TX bytes:3679 acl:0 sco:0 commands:57 errors:0

I am not BT expert but I don't see anything here to alarm me. Does anyone else? If anyone can point me towards why these two programs can not speak to each other I would be extremely grateful.


Old Post

I am developing some Bluetooth (BT) (normal Bluetooth, not Bluetooth Low Energy) software on my laptop and in order to test them on one machine, I bought a second BT adapter (Asus USB-BT400). After I plugged it in I could immediately see it using hciconfig.

I tried to adjust my programs to use the proper adapters but it did not work and I am not sure why. I am quite new to BT programming so I might have misunderstood something. To get program A to use the adapter with address addr1 I ran

int dev_id = hci_devid("60:F2:62:1B:9C:74");
int err = hci_open_dev(dev_id);    
if(err < 0) {
    printf("error opening adapter device\n");
}

where "60:F2:62:1B:9C:74" is addr1. The companion program did the same but used the other adapters address.

When I run the two programs the errors are not printed (meaning the adapters were chosen successfully?) but the programs do nothing. After a while the client program prints Uh Oh! The Host is Down. If anyone can explain what I am doing wrong when choosing the adapters I'd be very grateful. I also tried running the server on one laptop and the client on another, but nothing happened there either. In that case, I did not explicitly choose the BT adapter as I expected it to default to the only one available. I have not considered that the example code might be wrong but if anyone sees any errors, please point them out.

The complete code can be seen below. I am trying to run a simple example with an RFCOMM connection between a server and a client. The example was borrowed from the book Bluetooth essentials for programmers by Albert S. Huang and Larry Rudolph, after which I've tried to modify it.


Server program

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int main(int argc, char **argv)
{
    struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
    char buf[1024] = { 0 };
    int s, client, bytes_read;
    socklen_t opt = sizeof(rem_addr);

    int dev_id = hci_devid("60:F2:62:1B:9C:74");
    int err = hci_open_dev(dev_id);    
    if(err < 0) {
        printf("error opening laptop device\n");
    }


    // allocate socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // bind socket to port 1 of the first available 
    // local bluetooth adapter
    loc_addr.rc_family = AF_BLUETOOTH;
    loc_addr.rc_bdaddr = *BDADDR_ANY;
    loc_addr.rc_channel = (uint8_t) 1;
    bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));

    // put socket into listening mode
    listen(s, 1);

    // accept one connection
    client = accept(s, (struct sockaddr *)&rem_addr, &opt);

    ba2str( &rem_addr.rc_bdaddr, buf );
    fprintf(stderr, "accepted connection from %s\n", buf);
    memset(buf, 0, sizeof(buf));

    // read data from the client
    bytes_read = read(client, buf, sizeof(buf));
    if( bytes_read > 0 ) {
        printf("received [%s]\n", buf);
    }

    // close connection
    close(client);
    close(s);
    return 0;
}

Client program

#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>

int main(int argc, char **argv)
{
    struct sockaddr_rc addr = { 0 };
    int s, status;
    char dest[18] = "60:F2:62:1B:9C:74";

    int dev_id = hci_devid("5C:F3:70:9D:BC:07");
    int err = hci_open_dev(dev_id);    
    if(err < 0) {
        printf("error opening adapter device\n");
    }

    // allocate a socket
    s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);

    // set the connection parameters (who to connect to)
    addr.rc_family = AF_BLUETOOTH;
    addr.rc_channel = (uint8_t) 1;
    str2ba( dest, &addr.rc_bdaddr );

    // connect to server
    status = connect(s, (struct sockaddr *)&addr, sizeof(addr));

    // send a message
    if( status == 0 ) {
        status = write(s, "hello!", 6);
    }

    if( status < 0 ) perror("uh oh");

    close(s);
    return 0;
}

Solution

  • Okay so I got some help in Zephyr's slack channel from users @hje and @sjanc. There were a few things that I had to change. First of all, my usages of strtoba were changed to str2ba. strtoba did some wierd swapping.

    Secondly, even the client program can specify which BT adapter to use via bind, similarly to how the server program does it. In my case, I start the server first, after which the client will be assigned whichever adapter is free. I don't explicitly need to state which.

    Lastly, but definitely most important, it turns out that you have to instruct Linux to make the BT adapters discoverable. I did this by running hciconfig hci0 piscan and hciconfig hci1 piscan.

    Complete programs in the end:

    Client

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/rfcomm.h>
    
    int main(int argc, char **argv)
    {
        struct sockaddr_rc addr = { 0 }, laddr = {0};
        int s, status;
        char dest[18]       = "60:F2:62:1B:9C:74";
    
        // allocate a socket
        s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    
        // set the connection parameters (who to connect to)
        addr.rc_family = AF_BLUETOOTH;
        addr.rc_channel = (uint8_t) 1;
        str2ba( dest, &addr.rc_bdaddr );
    
        // connect to server
        status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
    
        // send a message
        if( status == 0 ) {
            printf("successfully connected\n");
        }
    
        close(s);
        return 0;
    }
    
    

    Server

    #include <stdio.h>
    #include <unistd.h>
    #include <sys/socket.h>
    #include <bluetooth/bluetooth.h>
    #include <bluetooth/rfcomm.h>
    
    int main(int argc, char **argv)
    {
        struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
        char buf[1024] = { 0 };
        int s, client, bytes_read;
        socklen_t opt = sizeof(rem_addr);
    
        // I want to use the built in BT adapter
        const char* adapter = "60:F2:62:1B:9C:74";
    
        // allocate socket
        s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
    
        // bind socket to port 1 of the first available 
        // local bluetooth adapter
        loc_addr.rc_family = AF_BLUETOOTH;
        str2ba(adapter, &loc_addr.rc_bdaddr);
        loc_addr.rc_channel = (uint8_t) 1;
        bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
    
        // put socket into listening mode
        listen(s, 1);
    
        // accept one connection
        client = accept(s, (struct sockaddr *)&rem_addr, &opt);
    
        ba2str( &rem_addr.rc_bdaddr, buf );
        fprintf(stderr, "accepted connection from %s\n", buf);
        memset(buf, 0, sizeof(buf));
    
        // close connection
        close(client);
        close(s);
        return 0;
    }