5 Commits

Author SHA1 Message Date
kenji c430a53755 add schematic 2025-09-11 09:42:11 -04:00
kenji 2e9159cb57 Fix preserve pairing on power cycle 2025-09-09 15:05:45 -04:00
kenji a1e1ff70fc improve memory management & polling 2025-08-20 21:30:22 -04:00
kenji 5b3a5beb32 remove device CDC debug 2025-08-17 17:18:27 -04:00
kenji 8a8ec23d87 add diagram and video to README 2025-08-15 09:07:30 -04:00
16 changed files with 14314 additions and 148 deletions
+1 -1
View File
@@ -1 +1 @@
/build/**/* /*/**/*
+8
View File
@@ -4,14 +4,20 @@ This project uses a Raspberry Pi Pico W as a Bluetooth Low Energy adapter for
a wired USB HID input peripheral, allowing it to connect to a host device a wired USB HID input peripheral, allowing it to connect to a host device
over BLE using HID over GATT Profile. over BLE using HID over GATT Profile.
## Setup ## Setup
[YouTube Video](https://youtu.be/YuHbTrccshw)
### Hardware ### Hardware
You will need the following hardware to make the device: You will need the following hardware to make the device:
- Raspberry Pi Pico W - Raspberry Pi Pico W
- USB extension cable - USB extension cable
![Wiring schematic](schematic.svg)
You will need to cut the USB extension in half and connect the wires from the You will need to cut the USB extension in half and connect the wires from the
female end to the Raspberry Pi Pico W. The default configuration is to attach female end to the Raspberry Pi Pico W. The default configuration is to attach
the USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You will the USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You will
@@ -27,6 +33,8 @@ respectively. For the data wires, you can solder them to the two test points
TP2 and TP3 on the back of the Raspberry Pi Pico. The white cable goes to TP2 TP2 and TP3 on the back of the Raspberry Pi Pico. The white cable goes to TP2
and the green cable to TP3. and the green cable to TP3.
![Wiring of USB connectors to Raspberry Pi Pico W](diagram.png)
The individual wires on the USB can be fragile, so hot glue or other strain The individual wires on the USB can be fragile, so hot glue or other strain
relief is a good idea. relief is a good idea.
+33 -26
View File
@@ -26,6 +26,7 @@ static btstack_packet_callback_registration_t l2cap_event_callback_registration;
static btstack_packet_callback_registration_t sm_event_callback_registration; static btstack_packet_callback_registration_t sm_event_callback_registration;
static uint8_t battery = 100; static uint8_t battery = 100;
hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID; hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID;
uint16_t conn_interval=1000;
static uint8_t protocol_mode = 1; static uint8_t protocol_mode = 1;
static hids_device_report_t *dev_report_storage; static hids_device_report_t *dev_report_storage;
@@ -45,16 +46,11 @@ const uint8_t adv_data_len = sizeof(adv_data);
static void bt_hid_setup(void); static void bt_hid_setup(void);
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
//extern uint8_t desc_hid_report[];
//extern uint16_t desc_hid_report_len;
// start BTstack // start BTstack
void btstack_main(void){ void btstack_main(void){
bt_hid_setup(); bt_hid_setup();
init_report_buf(); init_report_buf();
hci_power_control(HCI_POWER_ON);
} }
static void bt_hid_setup(void) { static void bt_hid_setup(void) {
@@ -79,7 +75,7 @@ static void bt_hid_setup(void) {
// setup HID Device service // setup HID Device service
dev_report_storage = (hids_device_report_t *)malloc(sizeof(hids_device_report_t)*NUM_REPORT_IDS); dev_report_storage = (hids_device_report_t *)malloc(sizeof(hids_device_report_t)*NUM_REPORT_IDS);
hids_device_init_with_storage(0, get_desc_hid_report(), get_desc_hid_report_len(), NUM_REPORT_IDS, dev_report_storage); hids_device_init_with_storage(0, desc_hid_report, desc_hid_report_len, NUM_REPORT_IDS, dev_report_storage);
// setup advertisements // setup advertisements
uint16_t adv_int_min = 0x0030; uint16_t adv_int_min = 0x0030;
@@ -112,48 +108,56 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
(void) channel; (void) channel;
(void) size; (void) size;
uint16_t conn_interval;
if (packet_type != HCI_EVENT_PACKET) return; if (packet_type != HCI_EVENT_PACKET) return;
switch (hci_event_packet_get_type(packet)) { switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_DISCONNECTION_COMPLETE: case HCI_EVENT_DISCONNECTION_COMPLETE:
con_handle = HCI_CON_HANDLE_INVALID; con_handle = HCI_CON_HANDLE_INVALID;
printf("Disconnected\n"); cdc_print_msg("Disconnected\n");
set_host_state(HOST_STOP_LISTEN); host_state=HOST_STOP_LISTEN;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
break; break;
case SM_EVENT_JUST_WORKS_REQUEST: case SM_EVENT_JUST_WORKS_REQUEST:
printf("Just Works requested\n"); cdc_print_msg("Just Works requested\n");
sm_just_works_confirm(sm_event_just_works_request_get_handle(packet)); sm_just_works_confirm(sm_event_just_works_request_get_handle(packet));
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
break; break;
case SM_EVENT_NUMERIC_COMPARISON_REQUEST: case SM_EVENT_NUMERIC_COMPARISON_REQUEST:
printf("Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet)); cdc_count = sprintf(cdc_buf, "Confirming numeric comparison: %"PRIu32"\n", sm_event_numeric_comparison_request_get_passkey(packet));
cdc_print_str(cdc_buf, cdc_count);
sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet)); sm_numeric_comparison_confirm(sm_event_passkey_display_number_get_handle(packet));
break; break;
case SM_EVENT_PASSKEY_DISPLAY_NUMBER: case SM_EVENT_PASSKEY_DISPLAY_NUMBER:
printf("Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet)); cdc_count = sprintf(cdc_buf, "Display Passkey: %"PRIu32"\n", sm_event_passkey_display_number_get_passkey(packet));
cdc_print_str(cdc_buf, cdc_count);
break; break;
case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE: case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE:
printf("L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet)); cdc_count = sprintf(cdc_buf, "L2CAP Connection Parameter Update Complete, response: %x\n", l2cap_event_connection_parameter_update_response_get_result(packet));
cdc_print_str(cdc_buf, cdc_count);
break; break;
case HCI_EVENT_LE_META: case HCI_EVENT_LE_META:
switch (hci_event_le_meta_get_subevent_code(packet)) { switch (hci_event_le_meta_get_subevent_code(packet)) {
case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
// print connection parameters (without using float operations) // print connection parameters (without using float operations)
conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet); conn_interval = hci_subevent_le_connection_complete_get_conn_interval(packet);
printf("LE Connection Complete:\n"); set_host_poll_interval(conn_interval);
printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); cdc_print_msg("LE Connection Complete:\n");
printf("- Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet)); cdc_count = sprintf(cdc_buf, "- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
cdc_print_str(cdc_buf, cdc_count);
cdc_count = sprintf(cdc_buf, "- Connection Latency: %u\n", hci_subevent_le_connection_complete_get_conn_latency(packet));
cdc_print_str(cdc_buf, cdc_count);
break; break;
case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE: case HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE:
// print connection parameters (without using float operations) // print connection parameters (without using float operations)
conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet); conn_interval = hci_subevent_le_connection_update_complete_get_conn_interval(packet);
printf("LE Connection Update:\n"); set_host_poll_interval(conn_interval);
printf("- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3)); cdc_print_msg("LE Connection Update:\n");
printf("- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet)); cdc_count = sprintf(cdc_buf, "- Connection Interval: %u.%02u ms\n", conn_interval * 125 / 100, 25 * (conn_interval & 3));
cdc_print_str(cdc_buf, cdc_count);
cdc_count = sprintf(cdc_buf, "- Connection Latency: %u\n", hci_subevent_le_connection_update_complete_get_conn_latency(packet));
cdc_print_str(cdc_buf, cdc_count);
break; break;
default: default:
break; break;
@@ -163,9 +167,10 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
switch (hci_event_hids_meta_get_subevent_code(packet)){ switch (hci_event_hids_meta_get_subevent_code(packet)){
case HIDS_SUBEVENT_INPUT_REPORT_ENABLE: case HIDS_SUBEVENT_INPUT_REPORT_ENABLE:
con_handle = hids_subevent_input_report_enable_get_con_handle(packet); con_handle = hids_subevent_input_report_enable_get_con_handle(packet);
printf("Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet)); cdc_count = sprintf(cdc_buf, "Report Characteristic Subscribed %u\n", hids_subevent_input_report_enable_get_enable(packet));
cdc_print_str(cdc_buf, cdc_count);
set_host_state(HOST_START_LISTEN); host_state=HOST_START_LISTEN;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
// request connection param update via L2CAP following Apple Bluetooth Design Guidelines // request connection param update via L2CAP following Apple Bluetooth Design Guidelines
// gap_request_connection_parameter_update(con_handle, 12, 12, 4, 100); // 15 ms, 4, 1s // gap_request_connection_parameter_update(con_handle, 12, 12, 4, 100); // 15 ms, 4, 1s
@@ -176,10 +181,11 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
break; break;
case HIDS_SUBEVENT_PROTOCOL_MODE: case HIDS_SUBEVENT_PROTOCOL_MODE:
protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet); protocol_mode = hids_subevent_protocol_mode_get_protocol_mode(packet);
printf("Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot"); cdc_count = sprintf(cdc_buf, "Protocol Mode: %s mode\n", hids_subevent_protocol_mode_get_protocol_mode(packet) ? "Report" : "Boot");
cdc_print_str(cdc_buf, cdc_count);
break; break;
case HIDS_SUBEVENT_CAN_SEND_NOW: case HIDS_SUBEVENT_CAN_SEND_NOW:
printf("HIDS_SUBEVENT_CAN_SEND_NOW\n"); //cdc_print_msg("HIDS_SUBEVENT_CAN_SEND_NOW\n");
send_report(); send_report();
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1-cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN)); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1-cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN));
break; break;
@@ -197,9 +203,10 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
void update_desc_hid_report(void) { void update_desc_hid_report(void) {
hci_power_control(HCI_POWER_OFF); hci_power_control(HCI_POWER_OFF);
uint16_t len = get_desc_hid_report_len(); if (desc_hid_report_len>0) {
if (len>0) { //cdc_print_msg("Update HID report descriptor: ");
hids_device_init_with_storage(0, get_desc_hid_report(), len, NUM_REPORT_IDS, dev_report_storage); //cdc_print_hex(desc_hid_report, desc_hid_report_len);
hids_device_init_with_storage(0, desc_hid_report, desc_hid_report_len, NUM_REPORT_IDS, dev_report_storage);
hci_power_control(HCI_POWER_ON); hci_power_control(HCI_POWER_ON);
} }
} }
+2
View File
@@ -1,6 +1,8 @@
#ifndef BT_DEVICE_H_ #ifndef BT_DEVICE_H_
#define BT_DEVICE_H_ #define BT_DEVICE_H_
extern uint16_t conn_interval;
void btstack_main(void); void btstack_main(void);
void update_desc_hid_report(void); void update_desc_hid_report(void);
-3
View File
@@ -86,7 +86,4 @@
// To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets // To get the audio demos working even with HCI dump at 115200, this truncates long ACL packets
//#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100 //#define HCI_DUMP_STDOUT_MAX_SIZE_ACL 100
// enable larger HID descriptor
#define MAX_ATTRIBUTE_VALUE_SIZE 512
#endif // _PICO_BTSTACK_BTSTACK_CONFIG_H #endif // _PICO_BTSTACK_BTSTACK_CONFIG_H
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 511 KiB

+43 -37
View File
@@ -11,14 +11,19 @@
#include "usb_device.h" #include "usb_device.h"
#include "usb_host.h" #include "usb_host.h"
#include "bt_device.h"
#include "hid_report.h" #include "hid_report.h"
extern hci_con_handle_t con_handle;
uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE];
uint16_t desc_hid_report_len=0;
static uint8_t buffer_storage[REPORT_BUF_SIZE]; static uint8_t buffer_storage[REPORT_BUF_SIZE];
static btstack_ring_buffer_t report_buf; static btstack_ring_buffer_t report_buf;
static uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE]; static struct report_data *report;
static uint16_t desc_hid_report_len=0;
static struct report_desc *descriptors; static struct report_desc *descriptors;
static struct report_desc* report_desc_alloc(void); static struct report_desc* report_desc_alloc(void);
@@ -30,8 +35,6 @@ static void report_dict_init(struct report_dict *mapping);
static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor); static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor);
static struct report_dict* find_mapping(uint8_t dev_addr, uint8_t instance, uint8_t report_id); static struct report_dict* find_mapping(uint8_t dev_addr, uint8_t instance, uint8_t report_id);
extern hci_con_handle_t con_handle;
// start listening to HID input reports on all mounted devices // start listening to HID input reports on all mounted devices
bool request_hid_reports_all(void) { bool request_hid_reports_all(void) {
// send request to receive reports on all mounted devices // send request to receive reports on all mounted devices
@@ -39,15 +42,18 @@ bool request_hid_reports_all(void) {
for (current=descriptors; current != NULL; current=current->next) { for (current=descriptors; current != NULL; current=current->next) {
if (! current->listening) { if (! current->listening) {
if(request_hid_report(current->dev_addr, current->instance)) { if(request_hid_report(current->dev_addr, current->instance)) {
char tempbuf[40]; cdc_count = sprintf(cdc_buf, "Listening to input reports on [%u:%u]\n", current->dev_addr, current->instance);
size_t count = sprintf(tempbuf, "Listening to input reports on [%u:%u]\n", current->dev_addr, current->instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
current->listening = true; current->listening = true;
} else { } else {
cdc_print_msg("Error listening to input report(s)\n");
stop_hid_reports_all();
return false; return false;
} }
} }
} }
host_state = HOST_LISTENING;
return true; return true;
} }
@@ -58,15 +64,17 @@ bool stop_hid_reports_all(void) {
for (current=descriptors; current != NULL; current=current->next) { for (current=descriptors; current != NULL; current=current->next) {
if (current->listening) { if (current->listening) {
if(stop_hid_report(current->dev_addr, current->instance)) { if(stop_hid_report(current->dev_addr, current->instance)) {
char tempbuf[40]; cdc_count = sprintf(cdc_buf, "Stopping input reports on [%u:%u]\n", current->dev_addr, current->instance);
size_t count = sprintf(tempbuf, "Stopping input reports on [%u:%u]\n", current->dev_addr, current->instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
current->listening = false; current->listening = false;
} else { } else {
cdc_print_msg("Error stopping input report(s)\n");
return false; return false;
} }
} }
} }
host_state = HOST_INACTIVE;
return true; return true;
} }
@@ -74,49 +82,50 @@ bool stop_hid_reports_all(void) {
void send_report(){ void send_report(){
// process queue and send next report // process queue and send next report
if (btstack_ring_buffer_bytes_available(&report_buf)) { if (btstack_ring_buffer_bytes_available(&report_buf)) {
uint8_t dev_addr;
uint8_t instance;
uint8_t len8[2]; uint8_t len8[2];
uint16_t len;
uint32_t num_bytes_read; uint32_t num_bytes_read;
// retrieve dev_addr from ring_buffer // retrieve dev_addr from ring_buffer
btstack_ring_buffer_read(&report_buf, &dev_addr, 1, &num_bytes_read); btstack_ring_buffer_read(&report_buf, &report->dev_addr, 1, &num_bytes_read);
// retrieve instance from ring buffer // retrieve instance from ring buffer
btstack_ring_buffer_read(&report_buf, &instance, 1, &num_bytes_read); btstack_ring_buffer_read(&report_buf, &report->instance, 1, &num_bytes_read);
// retrieve length as two uint8_t and turn into uint16_t // retrieve length as two uint8_t and turn into uint16_t
btstack_ring_buffer_read(&report_buf, len8, 2, &num_bytes_read); btstack_ring_buffer_read(&report_buf, len8, 2, &num_bytes_read);
memcpy(&len, len8, 2); memcpy(&report->len, len8, 2);
// retrieve report from ring buffer // retrieve report from ring buffer
uint8_t report[len]; btstack_ring_buffer_read(&report_buf, report->report, report->len, &num_bytes_read);
btstack_ring_buffer_read(&report_buf, report, len, &num_bytes_read);
// find report id mapping // find report id mapping
struct report_dict * mapping = find_mapping(dev_addr, instance, report[0]); struct report_dict * mapping = find_mapping(report->dev_addr, report->instance, report->report[0]);
char tempbuf[16]; cdc_count=sprintf(cdc_buf, "[%04x](%u)> ", con_handle, mapping->ble_id);
size_t count=sprintf(tempbuf, "[%04x](%u) ", con_handle, mapping->ble_id); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
// send hid report to ble interface // send hid report to ble interface
if (mapping != NULL) { if (mapping != NULL) {
if (mapping->report_id==0) { if (mapping->report_id==0) {
hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report, len); hids_device_send_input_report_for_id(con_handle, mapping->ble_id, report->report, report->len);
cdc_print_hex(report, len); cdc_print_hex(report->report, report->len);
} else { } else {
// replace report ID in original report before sending // replace report ID in original report before sending
hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report[1], len-1); hids_device_send_input_report_for_id(con_handle, mapping->ble_id, &report->report[1], report->len-1);
cdc_print_hex(&report[1], len-1); cdc_print_hex(&report->report[1], report->len-1);
} }
} }
// request send of next report // set host to poll USB
if (host_state == HOST_WAIT_POLL) {
host_state = HOST_POLL;
}
// request sending of next report
if (btstack_ring_buffer_bytes_available(&report_buf)) {
hids_device_request_can_send_now_event(con_handle); hids_device_request_can_send_now_event(con_handle);
} }
} }
}
// add report to the BTstack ring buffer for sending // add report to the BTstack ring buffer for sending
void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) { void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
@@ -125,7 +134,7 @@ void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uin
uint8_t len8[2]; uint8_t len8[2];
memcpy(len8, &len, 2); memcpy(len8, &len, 2);
if (btstack_ring_buffer_bytes_free(&report_buf) >= len+4) { if (btstack_ring_buffer_bytes_free(&report_buf) >= len+5) {
// put instance, length, and report into ring buffer if space available // put instance, length, and report into ring buffer if space available
btstack_ring_buffer_write(&report_buf, &dev_addr, 1); btstack_ring_buffer_write(&report_buf, &dev_addr, 1);
btstack_ring_buffer_write(&report_buf, &instance, 1); btstack_ring_buffer_write(&report_buf, &instance, 1);
@@ -136,11 +145,17 @@ void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uin
// request send on BLE HID interface // request send on BLE HID interface
hids_device_request_can_send_now_event(con_handle); hids_device_request_can_send_now_event(con_handle);
} }
// HID report on device has not been requested, flag so it can be polled
struct report_desc * descriptor;
descriptor = report_desc_find(dev_addr, instance);
descriptor->listening = false;
} }
// allocate memory for HID report ring buffer // allocate memory for HID report ring buffer
void init_report_buf(void) { void init_report_buf(void) {
btstack_ring_buffer_init(&report_buf, buffer_storage, sizeof(buffer_storage)); btstack_ring_buffer_init(&report_buf, buffer_storage, sizeof(buffer_storage));
report = REPORT_DATA_ALLOC();
} }
// allocate memory for USB interface report descriptor // allocate memory for USB interface report descriptor
@@ -358,12 +373,3 @@ static struct report_dict * find_mapping(uint8_t dev_addr, uint8_t instance, uin
return NULL; return NULL;
} }
uint16_t get_desc_hid_report_len(void) {
return desc_hid_report_len;
}
uint8_t const* get_desc_hid_report(void) {
return desc_hid_report;
}
+12 -2
View File
@@ -1,6 +1,7 @@
#ifndef HID_REPORT_H_ #ifndef HID_REPORT_H_
#define HID_REPORT_H_ #define HID_REPORT_H_
#define REPORT_MAX_SIZE 64
#define NUM_REPORT_IDS 16 #define NUM_REPORT_IDS 16
#define REPORT_BUF_SIZE 256 #define REPORT_BUF_SIZE 256
#define DESCRIPTOR_BUF_SIZE 256 #define DESCRIPTOR_BUF_SIZE 256
@@ -22,8 +23,19 @@ struct report_dict {
struct report_dict *next; struct report_dict *next;
}; };
struct report_data {
uint8_t dev_addr;
uint8_t instance;
uint8_t report[REPORT_MAX_SIZE];
uint16_t len;
};
#define REPORT_DESC_ALLOC() (struct report_desc *)malloc(sizeof(struct report_desc)) #define REPORT_DESC_ALLOC() (struct report_desc *)malloc(sizeof(struct report_desc))
#define REPORT_DICT_ALLOC() (struct report_dict *)malloc(sizeof(struct report_dict)) #define REPORT_DICT_ALLOC() (struct report_dict *)malloc(sizeof(struct report_dict))
#define REPORT_DATA_ALLOC() (struct report_data *)malloc(sizeof(struct report_data))
extern uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE];
extern uint16_t desc_hid_report_len;
bool request_hid_reports_all(void); bool request_hid_reports_all(void);
bool stop_hid_reports_all(void); bool stop_hid_reports_all(void);
@@ -33,7 +45,5 @@ void init_report_buf(void);
bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len); bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len);
void remove_instance(uint8_t dev_addr, uint8_t instance); void remove_instance(uint8_t dev_addr, uint8_t instance);
bool generate_report_descriptor(void); bool generate_report_descriptor(void);
uint16_t get_desc_hid_report_len(void);
uint8_t const* get_desc_hid_report(void);
#endif #endif
+21 -28
View File
@@ -12,17 +12,29 @@
#include "usb_device.h" #include "usb_device.h"
#include "usb_host.h" #include "usb_host.h"
static void usb_main(void);
// main loop // main loop
int main(void) { int main(void) {
// for PIO USB, we want clock speed to be multiple of 12MHz
set_sys_clock_khz(144000, true);
set_sys_clock_khz(192000, true); // need so BTstack can save pairing information to flash
flash_safe_execute_core_init();
sleep_ms(10); sleep_ms(10);
// setup BLE on core 1 // run BLE on core 1
multicore_reset_core1(); multicore_reset_core1();
multicore_launch_core1(btstack_main); multicore_launch_core1(btstack_main);
// run usb on core 0
usb_main();
return 0;
}
static void usb_main(void) {
// init and run usb host // init and run usb host
usb_host_init(); usb_host_init();
@@ -30,35 +42,18 @@ int main(void) {
usb_device_init(); usb_device_init();
while (true) { while (true) {
switch ( get_host_state() ) { switch ( host_state ) {
case HOST_NEW_DESCRIPTOR: case HOST_NEW_DESCRIPTOR:
if ( host_ready() ) { host_ready();
if(generate_report_descriptor()) {
if ( get_desc_hid_report_len() > 0 ) {
set_host_state(HOST_MOUNTED);
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
} else {
set_host_state(HOST_INACTIVE);
}
cdc_print_msg("Updating HID report map\n");
update_desc_hid_report();
}
}
break; break;
case HOST_START_LISTEN: case HOST_START_LISTEN:
if (request_hid_reports_all()) { request_hid_reports_all();
set_host_state(HOST_LISTENING);
} else {
cdc_print_msg("Error listening to input report(s)\n");
set_host_state(HOST_INACTIVE);
}
break; break;
case HOST_STOP_LISTEN: case HOST_STOP_LISTEN:
if (stop_hid_reports_all()) { stop_hid_reports_all();
set_host_state(HOST_INACTIVE); break;
} else { case HOST_POLL:
cdc_print_msg("Error stopping input report(s)\n"); poll_devices();
}
break; break;
default: default:
break; break;
@@ -67,6 +62,4 @@ int main(void) {
tud_task(); // tinyusb device task tud_task(); // tinyusb device task
tud_cdc_write_flush(); tud_cdc_write_flush();
} }
return 0;
} }
+14111
View File
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 255 KiB

+4 -4
View File
@@ -88,10 +88,10 @@
// CDC FIFO size of TX and RX // CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 32 #define CFG_TUD_CDC_RX_BUFSIZE 32
#define CFG_TUD_CDC_TX_BUFSIZE 64 #define CFG_TUD_CDC_TX_BUFSIZE 128
// CDC Endpoint transfer buffer size, more is faster // CDC Endpoint transfer buffer size, more is faster
#define CFG_TUD_CDC_EP_BUFSIZE 64 #define CFG_TUD_CDC_EP_BUFSIZE 128
//-------------------------------------------------------------------- //--------------------------------------------------------------------
// HOST CONFIGURATION // HOST CONFIGURATION
@@ -121,8 +121,8 @@
#define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB*3+1) // hub typically has 4 ports #define CFG_TUH_DEVICE_MAX (CFG_TUH_HUB*3+1) // hub typically has 4 ports
#define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX+1) #define CFG_TUH_HID (3*CFG_TUH_DEVICE_MAX+1)
#define CFG_TUH_HID_EPIN_BUFSIZE 64 #define CFG_TUH_HID_EPIN_BUFSIZE 128
#define CFG_TUH_HID_EPOUT_BUFSIZE 64 #define CFG_TUH_HID_EPOUT_BUFSIZE 128
#ifdef __cplusplus #ifdef __cplusplus
} }
+1
View File
@@ -100,6 +100,7 @@ char const* string_desc_arr [] =
"Raspberry Pi", // 1: Manufacturer "Raspberry Pi", // 1: Manufacturer
"Pico BLE HID", // 2: Product "Pico BLE HID", // 2: Product
NULL, // 3: Serials, should use chip ID NULL, // 3: Serials, should use chip ID
"Pico BLE HID CDC", // 4: Product
}; };
static uint16_t _desc_str[32+1]; static uint16_t _desc_str[32+1];
+12 -6
View File
@@ -9,6 +9,10 @@
#include "usb_device.h" #include "usb_device.h"
char cdc_buf[64];
uint16_t cdc_len;
size_t cdc_count;
void usb_device_init(void) { void usb_device_init(void) {
// run TinyUSB device // run TinyUSB device
tusb_rhport_init_t dev_init = { tusb_rhport_init_t dev_init = {
@@ -44,21 +48,23 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
// print message to CDC in raw hex // print message to CDC in raw hex
void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) { void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) {
char tempbuf[8]; (void) msg;
size_t count; (void) msg_len;
for (int i=0; i<msg_len; i++) { for (int i=0; i<msg_len; i++) {
count=sprintf(tempbuf, "%02X ", msg[i]); cdc_count=sprintf(cdc_buf, "%02X ", msg[i]);
tud_cdc_write(tempbuf, count); tud_cdc_write(cdc_buf, cdc_count);
} }
tud_cdc_write_str("\n"); tud_cdc_write_str("\n");
} }
// print text message to CDC // print text message to CDC
void cdc_print_str(char const* msg, uint16_t msg_len) { void cdc_print_str(char const* msg, uint16_t msg_len) {
(void) msg;
(void) msg_len;
tud_cdc_write(msg, msg_len); tud_cdc_write(msg, msg_len);
} }
void cdc_print_msg(char const* msg) { void cdc_print_msg(char const* msg) {
uint16_t msg_len = strlen(msg); cdc_len = strlen(msg);
cdc_print_str(msg, msg_len); cdc_print_str(msg, cdc_len);
} }
+6 -2
View File
@@ -1,5 +1,9 @@
#ifndef MAIN_DEVICE_H_ #ifndef USB_DEVICE_H_
#define MAIN_DEVICE_H_ #define USB_DEVICE_H_
extern char cdc_buf[64];
extern uint16_t cdc_len;
extern size_t cdc_count;
void usb_device_init(void); void usb_device_init(void);
void cdc_print_hex(uint8_t const* msg, uint16_t msg_len); void cdc_print_hex(uint8_t const* msg, uint16_t msg_len);
+46 -30
View File
@@ -11,11 +11,14 @@
#include "hid_report.h" #include "hid_report.h"
#include "usb_device.h" #include "usb_device.h"
#include "bt_device.h"
#include "usb_host.h" #include "usb_host.h"
static host_state_t host_state; host_state_t host_state;
static absolute_time_t request_time; static absolute_time_t request_time;
static absolute_time_t last_report;
static uint16_t host_poll_interval=HOST_POLL_INTERVAL;
// initialize usb host // initialize usb host
void usb_host_init(void) { void usb_host_init(void) {
@@ -32,7 +35,7 @@ void usb_host_init(void) {
tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT); tuh_hid_set_default_protocol(HID_PROTOCOL_REPORT);
tusb_init(BOARD_TUH_RHPORT, &host_init); tusb_init(BOARD_TUH_RHPORT, &host_init);
set_host_state(HOST_INACTIVE); host_state=HOST_INACTIVE;
} }
@@ -44,16 +47,12 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance); uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
/// send device vid:pid information to CDC for debugging /// send device vid:pid information to CDC for debugging
char tempbuf[CFG_TUD_CDC_TX_BUFSIZE]; cdc_count = sprintf(cdc_buf, "Mount: [%04x:%04x][%u:%u] Protocol = %u\n", vid, pid, dev_addr, instance, itf_protocol);
size_t count = sprintf(tempbuf, "Mount: [%04x:%04x][%u:%u] Protocol = %u\n", vid, pid, dev_addr, instance, itf_protocol); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
// print descriptor
//cdc_print_hex(desc_report, desc_len);
// add to HID report descriptor // add to HID report descriptor
if ( add_descriptor(dev_addr, instance, desc_report, desc_len)) { if ( add_descriptor(dev_addr, instance, desc_report, desc_len)) {
set_host_state(HOST_NEW_DESCRIPTOR); host_state=HOST_NEW_DESCRIPTOR;
request_time=get_absolute_time(); request_time=get_absolute_time();
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0); cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
} }
@@ -63,58 +62,75 @@ void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_re
void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
{ {
// send device address:instance to CDC for debugging // send device address:instance to CDC for debugging
char tempbuf[CFG_TUD_CDC_TX_BUFSIZE]; cdc_count = sprintf(cdc_buf, "Unmount: [%u:%u]\n", dev_addr, instance);
size_t count = sprintf(tempbuf, "Unmount: [%u:%u]\n", dev_addr, instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
if (stop_hid_report(dev_addr, instance)) { if (stop_hid_report(dev_addr, instance)) {
cdc_print_msg("Successfully stopped receiving reports\n"); cdc_print_msg("Successfully stopped receiving reports\n");
} }
remove_instance(dev_addr, instance); remove_instance(dev_addr, instance);
set_host_state(HOST_NEW_DESCRIPTOR); host_state=HOST_NEW_DESCRIPTOR;
request_time=get_absolute_time(); request_time=get_absolute_time();
} }
// Invoked when received report from device via interrupt endpoint // Invoked when received report from device via interrupt endpoint
void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
{ {
char tempbuf[32]; cdc_count = sprintf(cdc_buf, "[%u:%u](%u)< ", dev_addr, instance, len);
size_t count = sprintf(tempbuf, "[%04x:%04x](%u) ", dev_addr, instance, len); cdc_print_str(cdc_buf,cdc_count);
cdc_print_str(tempbuf,count);
cdc_print_hex(report, len); cdc_print_hex(report, len);
last_report = get_absolute_time();
host_state = HOST_WAIT_POLL;
queue_report(dev_addr, instance, report, len); queue_report(dev_addr, instance, report, len);
// continue to request to receive report // continue to request to receive report
if ( !tuh_hid_receive_report(dev_addr, instance) ) /*if ( !tuh_hid_receive_report(dev_addr, instance) )
{ {
cdc_print_msg("Error: cannot request report\r\n"); cdc_print_msg("Error: cannot request report\r\n");
}*/
}
// get host ready by updating descriptors
void host_ready(void) {
if (absolute_time_diff_us(request_time, get_absolute_time()) >= 1000000){
if(generate_report_descriptor()) {
if ( desc_hid_report_len > 0 ) {
host_state=HOST_MOUNTED;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
} else {
host_state=HOST_INACTIVE;
}
cdc_print_msg("Updating HID report map\n");
update_desc_hid_report();
}
} }
} }
// set the state of the host // set devices to listening once polling interval has passed
void set_host_state(host_state_t new_host_state) { void poll_devices(void) {
host_state = new_host_state; if (absolute_time_diff_us(last_report, get_absolute_time()) >= host_poll_interval) {
request_hid_reports_all();
host_state = HOST_LISTENING;
}
} }
// get the current state of the host // set the USB host polling interval based on the BT connection interval
host_state_t get_host_state(void) { void set_host_poll_interval(uint16_t bt_conn_interval) {
return host_state; if (bt_conn_interval*1250 > HOST_POLL_INTERVAL) {
host_poll_interval = bt_conn_interval*1250;
} else {
host_poll_interval = HOST_POLL_INTERVAL;
} }
// indicate whether host is ready for updating descriptors
bool host_ready(void) {
return (absolute_time_diff_us(request_time, get_absolute_time()) > 1000000);
} }
// request HID input reports on specified device address and instance // request HID input reports on specified device address and instance
bool request_hid_report(uint8_t dev_addr, uint8_t instance) { bool request_hid_report(uint8_t dev_addr, uint8_t instance) {
// request to receive reports HID devices // request to receive reports HID devices
if ( !tuh_hid_receive_report(dev_addr, instance) ) { if ( !tuh_hid_receive_report(dev_addr, instance) ) {
char tempbuf[CFG_TUD_CDC_TX_BUFSIZE]; cdc_count = sprintf(cdc_buf, "Error: cannot request report on [%u:%u]\n", dev_addr, instance);
size_t count = sprintf(tempbuf, "Error: cannot request report on [%u:%u]\n", dev_addr, instance); cdc_print_str(cdc_buf, cdc_count);
cdc_print_str(tempbuf, count);
return false; return false;
} }
return true; return true;
+10 -5
View File
@@ -1,5 +1,7 @@
#ifndef MAIN_HOST_H_ #ifndef USB_HOST_H_
#define MAIN_HOST_H_ #define USB_HOST_H_
#define HOST_POLL_INTERVAL 8000
typedef enum { typedef enum {
HOST_INACTIVE=0, HOST_INACTIVE=0,
@@ -7,14 +9,17 @@ typedef enum {
HOST_MOUNTED, HOST_MOUNTED,
HOST_START_LISTEN, HOST_START_LISTEN,
HOST_LISTENING, HOST_LISTENING,
HOST_WAIT_POLL,
HOST_POLL,
HOST_STOP_LISTEN, HOST_STOP_LISTEN,
} host_state_t; } host_state_t;
extern host_state_t host_state;
void usb_host_init(void); void usb_host_init(void);
void set_host_state(host_state_t new_host_state); void host_ready(void);
host_state_t get_host_state(void); void poll_devices(void);
bool host_ready(void); void set_host_poll_interval(uint16_t bt_conn_interval);
bool request_hid_report(uint8_t dev_addr, uint8_t instance); bool request_hid_report(uint8_t dev_addr, uint8_t instance);
bool stop_hid_report(uint8_t dev_addr, uint8_t instance); bool stop_hid_report(uint8_t dev_addr, uint8_t instance);