Use Bluez Stack as a Peripheral (Advertiser)

Use BlueZ Stack As A Peripheral (Advertiser)

With your Bluetooth dongle plugged in, running the following command will tell you the device name and give its state:

$ hciconfig

The output should look something like this:

hci0:    Type: BR/EDR  Bus: USB
BD Address: 00:01:02:aa:bb:cc ACL MTU: 1021:8 SCO MTU: 64:1
DOWN
RX bytes:1000 acl:0 sco:0 events:47 errors:0
TX bytes:1072 acl:0 sco:0 commands:47 errors:0

This indicates the device is called hci0 is in a down state. Issue the following command to bring it up:

$ sudo hciconfig hci0 up

Now it should look like:

$ hciconfig
hci0: Type: BR/EDR Bus: USB
BD Address: 00:01:02:aa:bb:cc ACL MTU: 1021:8 SCO MTU: 64:1
UP RUNNING
RX bytes:1000 acl:0 sco:0 events:47 errors:0
TX bytes:1072 acl:0 sco:0 commands:47 errors:0

Next, execute the following example command to configure the advertising data to be sent.

$ sudo hcitool -i hci0 cmd 0x08 0x0008 1e 02 01 1a 1a ff 4c 00 02 15 e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 00 00 00 00 c5 00 00 00 00 00 00 00 00 00 00 00 00 00

You can change the hex bytes (starting with 1e) to send different byte sequences for your advertisement. One that literally sends the ASCII codes for "HELLO WORLD" would use: 48 45 4c 4c 4f 57 4f 52 4c 44 (EDIT: But you will also have to prefix this message with a valid header, see here.)

Now, use the following command to activate advertising on the dongle, this will start sending "Helo World" packets.

$ sudo hciconfig hci0 leadv 0

EDIT: the above command makes the advertised service connectable. If you don't want to allow connections, change it to $ sudo hciconfig hci0 leadv 3

You can also disable advertising using the following command:

$ sudo hciconfig hci0 noleadv

Bluetooth Low Energy: Use BlueZ stack as a peripheral (with custom services and characteristics)

You can see gatt-example practice, or defined profiles under profile/ directory such as alert/server.c. Basically, you just have to register your service using gatt_service_add() function, following the existing code. For example :

 gatt_service_add(adapter, GATT_PRIM_SVC_UUID, 0xFFFF,
/* Char 1 */
GATT_OPT_CHR_UUID16, 0xAAAA,
GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, read_func_callback,

/* Char 2 Define here */
...
/* Char 3 Define here */
...
GATT_OPT_INVALID);
}

Also, I forgot the details but in order to get alert server working, you need to enable experimental (and maintainer mode?) during configuration by adding "--enable-maintainer-mode" and "--enable-experimental"

To run, run the compiled "bluetoothd" with -n and -d options to debug (also -E for enabling experimental services). You may want to reset your adapter again after running bluetoothd. And then you can connect from remote device using gatttool (also with bluetoothd running on remote device).

Wrong payload when using BlueZ stack as peripheral

Two issues:

First, in order to get BlueZ to advertise, the byte sequence you supply must include a valid BLE advertisement header, which is a minimum of 8 bytes. So to advertise "helloworld" you actually need to send:

sudo hcitool -i hci0 cmd 0x08 0x0008 10 02 01 1a 0c ff 18 01 48 45 4c 4c 4f 57 4f 52 4c 44

The first 8 bytes are the header and the next 10 bytes are the string "helloworld" encoded as 8-bit ASCII.

The first 8 bytes can be broken down like this:

10 # Total length of the advertising packet
02 # Number of bytes that follow in first AD structure
01 # Flags AD type
1A # Flags value 0x1A = 000011010
bit 0 (OFF) LE Limited Discoverable Mode
bit 1 (ON) LE General Discoverable Mode
bit 2 (OFF) BR/EDR Not Supported
bit 3 (ON) Simultaneous LE and BR/EDR to Same Device Capable (controller)
bit 4 (ON) Simultaneous LE and BR/EDR to Same Device Capable (Host)
0C # Number of bytes that follow in second (and last) AD structure
FF # Manufacturer specific data AD type
18 01 # Company identifier code (0x0118 == Radius Networks)

Note that this header contains two different length fields that you must adjust if you change the length of the "helloworld" payload. Also, for experimentation purposes, you are welcome to use any two bytes for the company identifier that you want.

Second, you can't see the raw bytes of a detected advertisement with the hcitool lescan command. To see the raw bytes, you have to use this command in combination with the hcidump command. See here for details: https://stackoverflow.com/a/21790504/1461050

Bluez: advertise service / gatt server example?

Eventually, I discovered the answers to all the questions I had.

I will start by answering the last question:

The commands I use only set-up the BLE device to advertise some data, but iOS reports that the connection is accepted. What part of bluez is accepting incoming connections?

This one was answered on the bluez mailing-list, in response to me.

Summary: the BLE connection is accepted at the HCI level by the kernel. If you want to use that connection from user space you need to use an l2cap socket with the ATT channel ID (which is 4).

Bleno has a good example of using an L2CAP socket.

How an L2CAP socket works is basically like this:

/* create L2CAP socket, and bind it to the local adapter */
l2cap_socket = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);

hci_device_id = hci_get_route(NULL);
hci_socket = hci_open_dev(hci_device_id);
memset(&l2cap_address, sizeof(l2cap_address));
l2cap_address.l2_family = AF_BLUETOOTH;
l2cap_address.l2_bdaddr = hci_device_address;
l2cap_address.l2_cid = htobs(ATT_CID);

bind(l2cap_socket, (struct sockaddr*)&l2cap_address, sizeof(l2cap_address));
listen(l2cap_socket, 1);

while (1) {
/* now select and accept() client connections. */
select(l2cap_socket + 1, &afds, NULL, NULL, &tv);
client_socket = accept(l2cap_socket, (struct sockaddr *)&l2cap_address, &len);

/* you can now read() what the client sends you */
int ret = read(client_socket, buffer, sizeof(buffer));
printf("data len: %d\n", ret);
for (i = 0; i < ret; i++) {
printf("%02x", ((int)buffer[i]) & 0xff);
}
printf("\n");
close(client_socket);
}

How to advertise a service?

I realized I needed an answer to the previous question to answer that one.

Once you can read the data over L2CAP socket, everything makes more sense, for example, if your Android phone does gatt.discoverServices(), then the little program above will read (i.e. receive):

10 0100 ffff 0028

Which basically means:

10: READ_BY_GROUP
0100: from handle 0001
ffff: to handle ffff
0028: with UUID 2800

This request is the way any BLE peripheral will request the list of services.

Then, you can answer this request with the list of services your device provides, formatted according to the GATT protocol.

Again, see the implementation of this in Bleno.



Related Topics



Leave a reply



Submit