I've been struggling for some time to get L2CAP transfer between an NRF51822 and a Linux machine using bluez.
I've successfully created a connection between the central (Linux) and peripheral (NRF51822) and created a socket, but I get no data through. I can confirm via LED indicators that the connection is established (i.e. hci_le_create_conn
completes ok).
The code seems to block on the accept()
call and executes no further. Even though the peripheral is transmitting periodically.
This is the bluez code (the channel I'm using is 0x41):
int main() {
printf("init\n");
bdaddr_t src_addr, dst_addr;
struct sockaddr_l2 l2cap_address;
// Get the destination address
str2ba("e1:a3:1e:75:29:b0", &dst_addr);
socklen_t opt = sizeof(dst_addr);
// Get host address
int hci_device_id = hci_get_route(NULL);
int hci_socket = hci_open_dev(hci_device_id);
hci_devba(hci_device_id, &src_addr);
/* create L2CAP socket, and bind it to the local adapter */
printf("Creating socket...\n");
int l2cap_socket = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
if(l2cap_socket < 0) {
printf("Error creating socket\n");
return -1;
}
memset(&l2cap_address, 0, sizeof(l2cap_address));
l2cap_address.l2_family = AF_BLUETOOTH;
l2cap_address.l2_bdaddr = src_addr;
l2cap_address.l2_cid = htobs(ATT_CID);
printf("Binding socket...\n");
bind(l2cap_socket, (struct sockaddr*)&l2cap_address, sizeof(l2cap_address));
listen(l2cap_socket, 1);
// Connect to bluetooth peripheral
printf("Creating connection... ");
uint16_t hci_handle;
int result = hci_le_create_conn(hci_socket,
htobs(0x0004), htobs(0x0004), 0,
LE_RANDOM_ADDRESS, dst_addr, LE_PUBLIC_ADDRESS,
htobs(0x0006) /*min_interval*/, htobs(0x0020) /*max_interval*/,
htobs(0) /*latency*/, htobs(200) /*supervision_timeout*/,
htobs(0x0001), htobs(0x0001), &hci_handle, 25000);
printf("%d\n", result);
fd_set afds;
struct timeval tv = {
.tv_usec = 3000
};
int len;
uint8_t buffer[1024];
printf("Entering loop\n");
while (1) {
printf("Selecting...\n");
/* now select and accept() client connections. */
select(l2cap_socket + 1, &afds, NULL, NULL, &tv);
printf("Accepting...\n");
bdaddr_t addr;
len = sizeof(addr);
int client_socket = accept(l2cap_socket, (struct sockaddr *)&addr, &len);
printf("Reading...\n");
/* you can now read() what the client sends you */
int ret = read(client_socket, buffer, sizeof(buffer));
printf("data len: %d\n", ret);
for (int i = 0; i < ret; i++) {
printf("%02x", ((int)buffer[i]) & 0xff);
}
printf("\n");
close(client_socket);
}
return 0;
}
On the ARM side, I'm calling this function upon a BLE connection:
static void l2cap_init() {
uint32_t err_code = sd_ble_l2cap_cid_register(CID);
APP_ERROR_CHECK(err_code);
}
And on a fixed interval I'm calling this function:
static void l2cap_transmit() {
uint32_t err_code;
ble_l2cap_header_t tx_head;
uint8_t tx_data[] = "Hello!";
tx_head.cid = CID;
tx_head.len = sizeof(tx_data);
err_code = sd_ble_l2cap_tx(m_conn_handle, &tx_head, tx_data);
APP_ERROR_CHECK(err_code);
}
I've confirmed that sd_ble_l2cap_tx
is returning NRF_SUCCESS
and the BLE_EVT_TX_COMPLETE
event occurs, so the problem seems to lie on the central side.
Can anyone guide me in the right direction here? I need to be able to send data from the peripheral and receive them on the central at a pretty fast rate.
I'm also aware of using sd_ble_gatts_hvx
to send notifications to the central. But I've failed to find any info on how to set the central up to be able to receive the data. Would a GATT server be the way to go here?