Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 108 additions & 0 deletions examples/hci/hci.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
This file is part of the Arduino_RouterBridge library.

Copyright (c) 2025 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.

*/

#include <Arduino_RouterBridge.h>

void setup() {
Serial.begin(115200);
while (!Serial) {
delay(10);
}

Serial.println("Arduino HCI Example - Read Local Version");

if (!Bridge.begin()) {
Serial.println("Failed to setup Bridge");
return;
}

if (!HCI.begin("hci0")) {
Serial.println("Failed to open HCI device");
return;
}

Serial.println("HCI device opened successfully");

delay(1000);
readLocalVersion();
}

void loop() {
// Nothing to do in loop
delay(1000);
}

void readLocalVersion() {
uint8_t cmd[4];
cmd[0] = 0x01; // HCI Command Packet Type
cmd[1] = 0x01; // OCF = 0x0001 (lower byte)
cmd[2] = 0x10; // OGF = 0x04 (0x04 << 2 = 0x10 in upper 6 bits)
cmd[3] = 0x00; // Parameter length = 0

Serial.println("Sending HCI Read Local Version command...");

int sent = HCI.send(cmd, sizeof(cmd));
if (sent < 0) {
Serial.println("Failed to send HCI command");
return;
}

Serial.print("Sent ");
Serial.print(sent);
Serial.println(" bytes");

// Wait for response with timeout
Serial.println("Waiting for response...");
int avail = 0;
unsigned long startTime = millis();
while (avail == 0 && (millis() - startTime) < 1000) { // 1 second timeout
avail = HCI.available();
if (avail == 0) {
delay(10); // Small delay between polls
}
}

Serial.print("Available bytes: ");
Serial.println(avail);

if (avail == 0) {
Serial.println("Timeout: No response received");
return;
}

// Read response
uint8_t response[255];
int received = HCI.recv(response, sizeof(response));

if (received > 0) {
Serial.print("Received ");
Serial.print(received);
Serial.println(" bytes:");

// Print response in hex
for (int i = 0; i < received; i++) {
if (response[i] < 0x10) Serial.print("0");
Serial.print(response[i], HEX);
Serial.print(" ");
}
Serial.println();

// Parse Command Complete Event
// Event format: Packet Type, Event Code, Param Length, Num_HCI_Command_Packets, Opcode, Status, ...
if (received >= 6 && response[0] == 0x04 && response[1] == 0x0E) {
Serial.println("Command Complete Event received");
Serial.print("Status: 0x");
Serial.println(response[6], HEX);
}
} else {
Serial.println("No response received");
}
}
1 change: 1 addition & 0 deletions src/Arduino_RouterBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,6 @@
#include "monitor.h"
#include "tcp_client.h"
#include "tcp_server.h"
#include "hci.h"

#endif //ARDUINO_ROUTER_BRIDGE_H
137 changes: 137 additions & 0 deletions src/hci.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
This file is part of the Arduino_RouterBridge library.

Copyright (c) 2025 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.

*/

#pragma once

#ifndef BRIDGE_HCI_H
#define BRIDGE_HCI_H

#include "bridge.h"

#define HCI_OPEN_METHOD "hci/open"
#define HCI_CLOSE_METHOD "hci/close"
#define HCI_SEND_METHOD "hci/send"
#define HCI_RECV_METHOD "hci/recv"
#define HCI_AVAIL_METHOD "hci/avail"
#define HCI_BUFFER_SIZE 1024

template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
BridgeClass *bridge;
struct k_mutex hci_mutex;
bool initialized = false;

public:
explicit BridgeHCI(BridgeClass &bridge): bridge(&bridge) {

}

bool begin(const char *device = "hci0") {
k_mutex_init(&hci_mutex);

if (!(*bridge) && !bridge->begin()) {
return false;
}

bool result;
if (bridge->call(HCI_OPEN_METHOD, String(device)).result(result)) {
initialized = result;
}

return initialized;
}

void end() {
if (!initialized) {
return;
}

bool result;
bridge->call(HCI_CLOSE_METHOD).result(result);
initialized = false;
}

explicit operator bool() const {
return initialized;
}

int send(const uint8_t *buffer, size_t size) {
if (!initialized) {
return -1;
}

k_mutex_lock(&hci_mutex, K_FOREVER);

MsgPack::arr_t<uint8_t> send_buffer;
for (size_t i = 0; i < size; ++i) {
send_buffer.push_back(buffer[i]);
}

size_t bytes_sent;
const bool ret = bridge->call(HCI_SEND_METHOD, send_buffer).result(bytes_sent);

k_mutex_unlock(&hci_mutex);

if (ret) {
return bytes_sent;
}
return -1;
}

int recv(uint8_t *buffer, size_t max_size) {
if (!initialized) {
return -1;
}

k_mutex_lock(&hci_mutex, K_FOREVER);

MsgPack::arr_t<uint8_t> message;
bool ret = bridge->call(HCI_RECV_METHOD, max_size).result(message);

if (ret) {
size_t bytes_to_copy = message.size() < max_size ? message.size() : max_size;
for (size_t i = 0; i < bytes_to_copy; ++i) {
buffer[i] = message[i];
}
k_mutex_unlock(&hci_mutex);
return bytes_to_copy;
}

k_mutex_unlock(&hci_mutex);
return 0;
}

int available() {
if (!initialized) {
return 0;
}

k_mutex_lock(&hci_mutex, K_FOREVER);

bool result;
bool ret = bridge->call(HCI_AVAIL_METHOD).result(result);

k_mutex_unlock(&hci_mutex);

return ret && result;
}

};

extern BridgeClass Bridge;

namespace RouterBridge {
BridgeHCI<> HCI(Bridge);
}

// Make available in global namespace for backward compatibility
using RouterBridge::HCI;

#endif // BRIDGE_HCI_H