Skip to content

Commit 60272c9

Browse files
committed
Add HCI bridge.
Signed-off-by: iabdalkader <[email protected]>
1 parent 9c4100f commit 60272c9

File tree

3 files changed

+246
-0
lines changed

3 files changed

+246
-0
lines changed

examples/hci/hci.ino

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
This file is part of the Arduino_RouterBridge library.
3+
4+
Copyright (c) 2025 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
10+
*/
11+
12+
#include <Arduino_RouterBridge.h>
13+
14+
void setup() {
15+
Serial.begin(115200);
16+
while (!Serial) {
17+
delay(10);
18+
}
19+
20+
Serial.println("Arduino HCI Example - Read Local Version");
21+
22+
if (!Bridge.begin()) {
23+
Serial.println("Failed to setup Bridge");
24+
return;
25+
}
26+
27+
if (!HCI.begin("hci0")) {
28+
Serial.println("Failed to open HCI device");
29+
return;
30+
}
31+
32+
Serial.println("HCI device opened successfully");
33+
34+
delay(1000);
35+
readLocalVersion();
36+
}
37+
38+
void loop() {
39+
// Nothing to do in loop
40+
delay(1000);
41+
}
42+
43+
void readLocalVersion() {
44+
uint8_t cmd[4];
45+
cmd[0] = 0x01; // HCI Command Packet Type
46+
cmd[1] = 0x01; // OCF = 0x0001 (lower byte)
47+
cmd[2] = 0x10; // OGF = 0x04 (0x04 << 2 = 0x10 in upper 6 bits)
48+
cmd[3] = 0x00; // Parameter length = 0
49+
50+
Serial.println("Sending HCI Read Local Version command...");
51+
52+
int sent = HCI.send(cmd, sizeof(cmd));
53+
if (sent < 0) {
54+
Serial.println("Failed to send HCI command");
55+
return;
56+
}
57+
58+
Serial.print("Sent ");
59+
Serial.print(sent);
60+
Serial.println(" bytes");
61+
62+
// Wait for response with timeout
63+
Serial.println("Waiting for response...");
64+
int avail = 0;
65+
unsigned long startTime = millis();
66+
while (avail == 0 && (millis() - startTime) < 1000) { // 1 second timeout
67+
avail = HCI.available();
68+
if (avail == 0) {
69+
delay(10); // Small delay between polls
70+
}
71+
}
72+
73+
Serial.print("Available bytes: ");
74+
Serial.println(avail);
75+
76+
if (avail == 0) {
77+
Serial.println("Timeout: No response received");
78+
return;
79+
}
80+
81+
// Read response
82+
uint8_t response[255];
83+
int received = HCI.recv(response, sizeof(response));
84+
85+
if (received > 0) {
86+
Serial.print("Received ");
87+
Serial.print(received);
88+
Serial.println(" bytes:");
89+
90+
// Print response in hex
91+
for (int i = 0; i < received; i++) {
92+
if (response[i] < 0x10) Serial.print("0");
93+
Serial.print(response[i], HEX);
94+
Serial.print(" ");
95+
}
96+
Serial.println();
97+
98+
// Parse Command Complete Event
99+
// Event format: Packet Type, Event Code, Param Length, Num_HCI_Command_Packets, Opcode, Status, ...
100+
if (received >= 6 && response[0] == 0x04 && response[1] == 0x0E) {
101+
Serial.println("Command Complete Event received");
102+
Serial.print("Status: 0x");
103+
Serial.println(response[6], HEX);
104+
}
105+
} else {
106+
Serial.println("No response received");
107+
}
108+
}

src/Arduino_RouterBridge.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@
1717
#include "monitor.h"
1818
#include "tcp_client.h"
1919
#include "tcp_server.h"
20+
#include "hci.h"
2021

2122
#endif //ARDUINO_ROUTER_BRIDGE_H

src/hci.h

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
This file is part of the Arduino_RouterBridge library.
3+
4+
Copyright (c) 2025 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
10+
*/
11+
12+
#pragma once
13+
14+
#ifndef BRIDGE_HCI_H
15+
#define BRIDGE_HCI_H
16+
17+
#include "bridge.h"
18+
19+
#define HCI_OPEN_METHOD "hci/open"
20+
#define HCI_CLOSE_METHOD "hci/close"
21+
#define HCI_SEND_METHOD "hci/send"
22+
#define HCI_RECV_METHOD "hci/recv"
23+
#define HCI_AVAIL_METHOD "hci/avail"
24+
#define HCI_BUFFER_SIZE 1024
25+
26+
template<size_t BufferSize=HCI_BUFFER_SIZE> class BridgeHCI {
27+
BridgeClass *bridge;
28+
struct k_mutex hci_mutex;
29+
bool initialized = false;
30+
31+
public:
32+
explicit BridgeHCI(BridgeClass &bridge): bridge(&bridge) {
33+
34+
}
35+
36+
bool begin(const char *device = "hci0") {
37+
k_mutex_init(&hci_mutex);
38+
39+
if (!(*bridge) && !bridge->begin()) {
40+
return false;
41+
}
42+
43+
bool result;
44+
if (bridge->call(HCI_OPEN_METHOD, String(device)).result(result)) {
45+
initialized = result;
46+
}
47+
48+
return initialized;
49+
}
50+
51+
void end() {
52+
if (!initialized) {
53+
return;
54+
}
55+
56+
bool result;
57+
bridge->call(HCI_CLOSE_METHOD).result(result);
58+
initialized = false;
59+
}
60+
61+
explicit operator bool() const {
62+
return initialized;
63+
}
64+
65+
int send(const uint8_t *buffer, size_t size) {
66+
if (!initialized) {
67+
return -1;
68+
}
69+
70+
k_mutex_lock(&hci_mutex, K_FOREVER);
71+
72+
MsgPack::arr_t<uint8_t> send_buffer;
73+
for (size_t i = 0; i < size; ++i) {
74+
send_buffer.push_back(buffer[i]);
75+
}
76+
77+
size_t bytes_sent;
78+
const bool ret = bridge->call(HCI_SEND_METHOD, send_buffer).result(bytes_sent);
79+
80+
k_mutex_unlock(&hci_mutex);
81+
82+
if (ret) {
83+
return bytes_sent;
84+
}
85+
return -1;
86+
}
87+
88+
int recv(uint8_t *buffer, size_t max_size) {
89+
if (!initialized) {
90+
return -1;
91+
}
92+
93+
k_mutex_lock(&hci_mutex, K_FOREVER);
94+
95+
MsgPack::arr_t<uint8_t> message;
96+
bool ret = bridge->call(HCI_RECV_METHOD, max_size).result(message);
97+
98+
if (ret) {
99+
size_t bytes_to_copy = message.size() < max_size ? message.size() : max_size;
100+
for (size_t i = 0; i < bytes_to_copy; ++i) {
101+
buffer[i] = message[i];
102+
}
103+
k_mutex_unlock(&hci_mutex);
104+
return bytes_to_copy;
105+
}
106+
107+
k_mutex_unlock(&hci_mutex);
108+
return 0;
109+
}
110+
111+
int available() {
112+
if (!initialized) {
113+
return 0;
114+
}
115+
116+
k_mutex_lock(&hci_mutex, K_FOREVER);
117+
118+
bool result;
119+
bool ret = bridge->call(HCI_AVAIL_METHOD).result(result);
120+
121+
k_mutex_unlock(&hci_mutex);
122+
123+
return ret && result;
124+
}
125+
126+
};
127+
128+
extern BridgeClass Bridge;
129+
130+
namespace RouterBridge {
131+
BridgeHCI<> HCI(Bridge);
132+
}
133+
134+
// Make available in global namespace for backward compatibility
135+
using RouterBridge::HCI;
136+
137+
#endif // BRIDGE_HCI_H

0 commit comments

Comments
 (0)