5 Commits

Author SHA1 Message Date
kenji e2e2246c5a fix USB descriptor interface count 2025-11-03 21:10:35 -05:00
kenji 16c743cb22 update README for Pico 2 W 2025-10-26 10:15:18 -04:00
kenji cb7421eab7 add toggle button for USB and improve BLE polling rate 2025-10-18 22:38:23 -04:00
kenji a1a0723e89 update CMake for Pico 2 W build 2025-10-18 10:35:33 -04:00
kenji c430a53755 add schematic 2025-09-11 09:42:11 -04:00
16 changed files with 14473 additions and 153 deletions
+5 -1
View File
@@ -2,7 +2,9 @@ set(PROJECT ble_hid)
cmake_minimum_required(VERSION 3.13)
set(PICO_SDK_PATH /home/kenji/programming/pico/c/pico-sdk)
set(PICO_PIO_USB_PATH /home/kenji/programming/pico/c/Pico-PIO-USB)
set(PICO_BOARD pico_w)
if (NOT DEFINED PICO_BOARD)
set(PICO_BOARD pico_w)
endif()
include (${PICO_SDK_PATH}/external/pico_sdk_import.cmake)
project(${PROJECT} C CXX ASM)
@@ -45,5 +47,7 @@ target_link_libraries(${PROJECT} PRIVATE
pico_btstack_make_gatt_header(ble_hid PRIVATE "${CMAKE_CURRENT_LIST_DIR}/ble_hid.gatt")
set_target_properties(${PROJECT} PROPERTIES OUTPUT_NAME "${PROJECT}-${PICO_BOARD}")
pico_add_extra_outputs(${PROJECT})
+42 -31
View File
@@ -1,8 +1,8 @@
# Pico BLE HID
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
over BLE using HID over GATT Profile.
This project uses a Raspberry Pi Pico W or Pico 2 W as a Bluetooth Low Energy
adapter for a wired USB HID input peripheral, allowing it to connect to a host
device over BLE using HID over GATT Profile.
## Setup
@@ -12,54 +12,65 @@ over BLE using HID over GATT Profile.
### Hardware
You will need the following hardware to make the device:
- Raspberry Pi Pico W
- Raspberry Pi Pico W or Raspberry Pi Pico 2 W
- USB extension cable
- (Optional) Momentary push button
![Wiring schematic](schematic.svg)
You will need to cut the USB extension in half and connect the wires from the
female end to the Raspberry Pi Pico (2) 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 also need to connect the red to 5V VBUS/VSYS (since you'll be powering
from the USB connection, VBUS should be fine) and the black to any ground pin.
Pin 38 is the closest and most convenient, or the ground test point TP1 on the
back will also work. While you can connect the Raspberry Pi Pico (2) W to the
host device using the micro USB port and a micro USB cable, since you have
already sacrificed half of the USB extension cable, you might as well use the
male half to create a standard USB-A connection. If you wish to do so, then
simply connect the red and black wires of the male connector end to VBUS (5V)
and GND, 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 (2) W. The white cable
goes to TP2 and the green cable to TP3.
![Wiring of USB connectors to Raspberry Pi Pico W](diagram.png)
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
the USB's green wire to pin 1/GP0 and USB's white wire to pin 2/GP1. You will
also need to connect the red to 5V VBUS/VSYS (since you'll be powering from the
USB connection, VBUS should be fine) and the black to any ground pin. Pin 38
is the closest and most convenient, or the ground test point TP1 on the back
will also work. While you can connect the Raspberry Pi Pico to the host device
using the micro USB port and a micro USB cable, since you have already
sacrificed half of the USB extension cable, you might as well use the male half
to create a standard USB-A connection. If you wish to do so, then simply
connect the red and black wires of the male connector end to VBUS (5V) and GND,
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
and the green cable to TP3.
Optionally, a momentary push button can be wired to GP5 and GND to switch between Bluetooth and USB passthrough mode.
The individual wires on the USB can be fragile, so hot glue or other strain
relief is a good idea.
### Software
Download the ble_hid.uf2 file from the latest
[release](https://git.kkozai.com/kenji/pico_ble_hid/releases) and flash
onto the Raspberry Pi Pico W by holding down the BOOTSEL button while plugging
Download the ble_hid-pico_w.uf2 (for Pico W) or ble_hid-pico2_w.uf2 (for Pico 2
W) file from the latest
[release](https://git.kkozai.com/kenji/pico_ble_hid/releases) and flash onto
the Raspberry Pi Pico (2) W by holding down the BOOTSEL button while plugging
into your computer so that it appears as a USB drive, then transfer the
firmware file onto the Pico W. After unmounting, the firmware install is
firmware file onto the Pico (2) W. After unmounting, the firmware install is
complete.
## Usage
Connect the Raspberry Pi Pico W into any USB power source (a good, small power
bank will work), then plug in the desired peripheral into the female USB
Connect the Raspberry Pi Pico (2) W into any USB power source (a good, small
power bank will work), then plug in the desired peripheral into the female USB
socket. After about a second, the onboard LED will light up, indicating it is
ready to be paired over Bluetooth LE.
From the host machine, go to your Bluetooth settings and pair with the
"Pico BLE HID" device. The LED should turn off when pairing begins, and it will
turn back on when pairing is complete and the Raspberry Pi Pico W is
ready to receive inputs. This will take a few seconds, so be patient.
Once the light is back on, your device should work over the Bluetooth
connection. When connecting a power-hungry device such as a gaming keyboard
with many LEDs, the sudden power draw when plugging in can cause issues, so it
is recommended to turn off the LEDs before connecting to the Raspberry Pi Pico
W.
turn back on when pairing is complete and the Raspberry Pi Pico (2) W is ready
to receive inputs. This will take a few seconds, so be patient. Once the light
is back on, your device should work over the Bluetooth connection. When
connecting a power-hungry device such as a gaming keyboard with many LEDs, the
sudden power draw when plugging in can cause issues, so it is recommended to
turn off the LEDs before connecting to the Raspberry Pi Pico (2) W.
If you connected a push button to GP5, then pressing the button will switch
between Bluetooth connection mode and USB passthrough mode. In USB passthrough
mode, the inputs will be passed through from the host to the USB connection made
through the test points or the Micro USB port.
In principle, the Pico BLE HID will work with peripherals plugged into
a USB hub, but compatibility will vary with the specific hardware used,
+8
View File
@@ -0,0 +1,8 @@
#ifndef BLEHID_CONFIG_H_
#define BLEHID_CONFIG_H_
#define BUTTON_PIN 5
#define BUTTON_DEBOUNCE 20000
#define BUTTON_LONG_PRESS 3000000
#endif
+23 -12
View File
@@ -28,6 +28,8 @@ static uint8_t battery = 100;
hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID;
uint16_t conn_interval=1000;
static uint8_t protocol_mode = 1;
bt_state_t bt_state;
bt_device_command_t bt_device_command;
static hids_device_report_t *dev_report_storage;
@@ -109,14 +111,20 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
(void) size;
if (packet_type != HCI_EVENT_PACKET) return;
if (packet_type != HCI_EVENT_PACKET) {
cdc_count = sprintf(cdc_buf, "Unexpected packet type %02X\n", packet_type);
cdc_print_str(cdc_buf, cdc_count);
return;
}
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_DISCONNECTION_COMPLETE:
con_handle = HCI_CON_HANDLE_INVALID;
cdc_print_msg("Disconnected\n");
host_state=HOST_STOP_LISTEN;
if ( device_state == DEVICE_INACTIVE) {
host_state = HOST_STOP_LISTEN;
}
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 0);
break;
case SM_EVENT_JUST_WORKS_REQUEST:
@@ -133,6 +141,8 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
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;
case SM_EVENT_IDENTITY_RESOLVING_STARTED:
break;
case L2CAP_EVENT_CONNECTION_PARAMETER_UPDATE_RESPONSE:
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);
@@ -160,6 +170,8 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
cdc_print_str(cdc_buf, cdc_count);
break;
default:
//cdc_count = sprintf(cdc_buf,"LE packet unknown %02X\n", hci_event_le_meta_get_subevent_code(packet));
//cdc_print_str(cdc_buf, cdc_count);
break;
}
break;
@@ -170,13 +182,10 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
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);
host_state=HOST_START_LISTEN;
host_state = HOST_START_LISTEN;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
// 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
// directly update connection params via HCI following Apple Bluetooth Design Guidelines
// gap_update_connection_parameters(con_handle, 12, 12, 4, 100); // 60-75 ms, 4, 1s
// request update to max 125Hz polling
gap_request_connection_parameter_update(con_handle, 6,6,0,100);
break;
case HIDS_SUBEVENT_PROTOCOL_MODE:
@@ -185,27 +194,29 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack
cdc_print_str(cdc_buf, cdc_count);
break;
case HIDS_SUBEVENT_CAN_SEND_NOW:
//cdc_print_msg("HIDS_SUBEVENT_CAN_SEND_NOW\n");
send_report();
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1-cyw43_arch_gpio_get(CYW43_WL_GPIO_LED_PIN));
break;
default:
//cdc_count = sprintf(cdc_buf, "Unknown HID event %02X\n", hci_event_hids_meta_get_subevent_code(packet));
//cdc_print_str(cdc_buf, cdc_count);
break;
}
break;
default:
//cdc_count = sprintf(cdc_buf, "Unknown HCI event %02X\n", hci_event_packet_get_type(packet));
//cdc_print_str(cdc_buf, cdc_count);
break;
}
}
// called to update the report map aka HID report descriptor on the BT interface
void update_desc_hid_report(void) {
con_handle = HCI_CON_HANDLE_INVALID;
hci_power_control(HCI_POWER_OFF);
if (desc_hid_report_len>0) {
//cdc_print_msg("Update HID report descriptor: ");
//cdc_print_hex(desc_hid_report, desc_hid_report_len);
if (bt_state == BT_STATE_ACTIVE && desc_hid_report_len>0) {
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);
}
+18
View File
@@ -1,6 +1,24 @@
#ifndef BT_DEVICE_H_
#define BT_DEVICE_H_
#define BT_LOOP_POLL_INTERVAL 100
typedef enum {
BT_STATE_INACTIVE=0,
BT_STATE_STOP,
BT_STATE_START,
BT_STATE_ACTIVE
} bt_state_t;
typedef enum {
BT_DEVICE_COMMAND_NONE=0,
BT_DEVICE_COMMAND_SWITCH,
BT_DEVICE_COMMAND_UNPAIR,
BT_DEVICE_COMMAND_UPDATE,
} bt_device_command_t;
extern bt_state_t bt_state;
extern bt_device_command_t bt_device_command;
extern uint16_t conn_interval;
void btstack_main(void);
+58 -14
View File
@@ -19,6 +19,7 @@ extern hci_con_handle_t con_handle;
uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE];
uint16_t desc_hid_report_len=0;
uint8_t num_mounted=0;
static uint8_t buffer_storage[REPORT_BUF_SIZE];
static btstack_ring_buffer_t report_buf;
@@ -29,7 +30,7 @@ static struct report_desc *descriptors;
static struct report_desc* report_desc_alloc(void);
static void report_desc_init(struct report_desc *descriptor);
static void report_desc_free(struct report_desc *descriptor);
static struct report_desc* report_desc_find(uint8_t dev_addr, uint8_t instance);
static struct report_desc * report_desc_find(uint8_t dev_addr, uint8_t instance);
static struct report_dict* report_dict_alloc(struct report_desc *descriptor);
static void report_dict_init(struct report_dict *mapping);
static void report_dict_free(struct report_dict *mapping, struct report_desc *descriptor);
@@ -74,7 +75,11 @@ bool stop_hid_reports_all(void) {
}
}
if (num_mounted > 0) {
host_state = HOST_MOUNTED;
} else {
host_state = HOST_INACTIVE;
}
return true;
}
@@ -98,6 +103,8 @@ void send_report(){
// retrieve report from ring buffer
btstack_ring_buffer_read(&report_buf, report->report, report->len, &num_bytes_read);
// send over BLE if connected
if (con_handle != HCI_CON_HANDLE_INVALID) {
// find report id mapping
struct report_dict * mapping = find_mapping(report->dev_addr, report->instance, report->report[0]);
@@ -115,21 +122,32 @@ void send_report(){
cdc_print_hex(&report->report[1], report->len-1);
}
}
// request sending of next report
if (btstack_ring_buffer_bytes_available(&report_buf)) {
hids_device_request_can_send_now_event(con_handle);
}
}
// send over USB if enabled
if ( device_state == DEVICE_ACTIVE ) {
struct report_desc *descriptor = report_desc_find(report->dev_addr, report->instance);
cdc_count=sprintf(cdc_buf, "[%u]> ", descriptor->dev_instance);
cdc_print_str(cdc_buf, cdc_count);
forward_report(descriptor->dev_instance, report->report, report->len);
cdc_print_hex(report->report, report->len);
}
// 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);
}
}
}
// 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) {
if (con_handle != HCI_CON_HANDLE_INVALID) {
if (con_handle != HCI_CON_HANDLE_INVALID || device_state == DEVICE_ACTIVE ) {
// convert len to array of two uint8_t from single uint16_t
uint8_t len8[2];
memcpy(len8, &len, 2);
@@ -142,8 +160,13 @@ void queue_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uin
btstack_ring_buffer_write(&report_buf, report, len);
}
if (con_handle != HCI_CON_HANDLE_INVALID) {
// request send on BLE HID interface
hids_device_request_can_send_now_event(con_handle);
} else {
// BLE is not active, send over USB
send_report();
}
}
// HID report on device has not been requested, flag so it can be polled
@@ -215,6 +238,7 @@ bool add_descriptor(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_repo
descriptor->desc_len = desc_len;
descriptor->dev_addr = dev_addr;
descriptor->instance = instance;
descriptor->dev_instance = instance;
descriptor->listening = false;
return true;
@@ -226,7 +250,8 @@ bool generate_report_descriptor(void) {
memset(desc_hid_report, 0, sizeof(desc_hid_report));
desc_hid_report_len=0;
uint8_t num_mounted = 0;
uint8_t id_counter = 0;
uint8_t instance_counter = 0;
struct report_desc * current;
for (current=descriptors; current != NULL; current=current->next) {
@@ -251,11 +276,11 @@ bool generate_report_descriptor(void) {
if (report_map == NULL) {
return false;
}
num_mounted++;
id_counter++;
report_map->report_id = current->descriptor[i];
report_map->ble_id = num_mounted;
report_map->ble_id = id_counter;
desc_hid_report[desc_hid_report_len+i]=num_mounted;
desc_hid_report[desc_hid_report_len+i]=id_counter;
}
}
@@ -270,30 +295,49 @@ bool generate_report_descriptor(void) {
}
}
desc_hid_report[desc_hid_report_len+col_pos+2] = 0x85;
desc_hid_report[desc_hid_report_len+col_pos+3] = num_mounted+1;
desc_hid_report[desc_hid_report_len+col_pos+3] = id_counter+1;
memcpy(&desc_hid_report[desc_hid_report_len+col_pos+4], &current->descriptor[col_pos+2], current->desc_len-col_pos);
desc_hid_report_len += current->desc_len+2;
// store mapping with report ID of 0
struct report_dict * report_map = report_dict_alloc(current);
num_mounted++;
id_counter++;
report_map->report_id = 0;
report_map->ble_id = num_mounted;
report_map->ble_id = id_counter;
} else {
desc_hid_report_len += current->desc_len;
}
// store mapping for host instance to device instance
current->dev_instance = instance_counter;
instance_counter++;
}
if (num_mounted > NUM_REPORT_IDS) {
if (id_counter > NUM_REPORT_IDS) {
cdc_print_msg("Error: too many report IDs\n");
return false;
}
// update number of instances mounted
num_mounted = instance_counter;
btstack_ring_buffer_reset(&report_buf);
return true;
}
// find a return the report descriptor by dvice address and device instance
struct report_desc * get_report_desc(uint8_t dev_instance) {
struct report_desc *descriptor;
for (descriptor = descriptors; descriptor != NULL; descriptor = descriptor->next) {
if (descriptor->dev_instance==dev_instance) {
break;
}
}
return descriptor;
}
// remove report descriptor for HID interface
void remove_instance(uint8_t dev_addr, uint8_t instance) {
struct report_desc *descriptor = report_desc_find(dev_addr, instance);
+3
View File
@@ -12,6 +12,7 @@ struct report_desc {
uint8_t instance;
uint8_t descriptor[DESCRIPTOR_BUF_SIZE];
uint16_t desc_len;
uint8_t dev_instance;
struct report_desc *next;
struct report_dict *mappings;
bool listening;
@@ -36,6 +37,7 @@ struct report_data {
extern uint8_t desc_hid_report[HID_DESCRIPTOR_SIZE];
extern uint16_t desc_hid_report_len;
extern uint8_t num_mounted;
bool request_hid_reports_all(void);
bool stop_hid_reports_all(void);
@@ -45,5 +47,6 @@ void init_report_buf(void);
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);
bool generate_report_descriptor(void);
struct report_desc * get_report_desc(uint8_t dev_instance);
#endif
+90
View File
@@ -2,6 +2,7 @@
#include "pico/multicore.h"
#include "pico/bootrom.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "pio_usb.h"
#include "tusb.h"
@@ -12,8 +13,16 @@
#include "usb_device.h"
#include "usb_host.h"
#include "blehid_config.h"
static void usb_main(void);
#ifdef BUTTON_PIN
static absolute_time_t last_button_time;
static uint32_t last_button_event;
static void gpio_callback(uint pin, uint32_t events);
#endif
// main loop
int main(void) {
// for PIO USB, we want clock speed to be multiple of 12MHz
@@ -35,12 +44,24 @@ int main(void) {
}
static void usb_main(void) {
// setup GPIO button for pair selection
#ifdef BUTTON_PIN
gpio_init(BUTTON_PIN);
gpio_pull_up(BUTTON_PIN);
gpio_set_irq_enabled_with_callback(BUTTON_PIN, GPIO_IRQ_EDGE_RISE | GPIO_IRQ_EDGE_FALL, true, &gpio_callback);
#endif
// init and run usb host
usb_host_init();
// init and run usb device
usb_device_init();
// set initial state
bt_state = BT_STATE_ACTIVE;
device_state = DEVICE_INACTIVE;
while (true) {
switch ( host_state ) {
case HOST_NEW_DESCRIPTOR:
@@ -58,8 +79,77 @@ static void usb_main(void) {
default:
break;
}
switch ( device_state ) {
case DEVICE_STOP:
case DEVICE_START:
if ( tud_disconnect() ) {
sleep_ms(10);
tud_connect();
}
if ( device_state == DEVICE_STOP ) {
device_state = DEVICE_INACTIVE;
host_state = HOST_STOP_LISTEN;
} else if (device_state == DEVICE_START ) {
device_state = DEVICE_ACTIVE;
if ( num_mounted > 0 ) {
set_host_poll_interval(1);
host_state = HOST_START_LISTEN;
}
}
break;
default:
break;
}
switch ( bt_state ) {
case BT_STATE_STOP:
bt_state = BT_STATE_INACTIVE;
update_desc_hid_report();
break;
case BT_STATE_START:
bt_state = BT_STATE_ACTIVE;
update_desc_hid_report();
break;
default:
break;
}
tuh_task(); // tinyusb host task
tud_task(); // tinyusb device task
tud_cdc_write_flush();
}
}
#ifdef BUTTON_PIN
static void gpio_callback(uint pin, uint32_t events) {
uint64_t diff = absolute_time_diff_us(last_button_time, get_absolute_time());
if (pin == BUTTON_PIN && diff >= BUTTON_DEBOUNCE) {
if (events & GPIO_IRQ_EDGE_FALL) {
// button pressed
last_button_time = get_absolute_time();
} else if (events & GPIO_IRQ_EDGE_RISE) {
// button released
if (diff >= BUTTON_LONG_PRESS) {
//bt_device_command = BT_DEVICE_COMMAND_UNPAIR;
} else {
if ( bt_state == BT_STATE_ACTIVE ) {
bt_state = BT_STATE_STOP;
} else if ( bt_state == BT_STATE_INACTIVE ) {
bt_state = BT_STATE_START;
}
if ( device_state == DEVICE_ACTIVE ) {
device_state = DEVICE_STOP;
} else if ( device_state == DEVICE_INACTIVE ) {
device_state = DEVICE_START;
}
//bt_device_command = BT_DEVICE_COMMAND_SWITCH;
}
last_button_time = get_absolute_time();
}
}
}
#endif
+14111
View File
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 255 KiB

+1
View File
@@ -85,6 +85,7 @@
//------------- Driver configuration -------------//
#define CFG_TUD_CDC 1
#define CFG_TUD_HID 8
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE 32
+65 -62
View File
@@ -1,37 +1,16 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
* sekigon-gonnoc
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#include "tusb.h"
#include "bsp/board_api.h"
#include "hid_report.h"
#include "usb_device.h"
#include "usb_descriptors.h"
//--------------------------------------------------------------------+
// Device Descriptors
//--------------------------------------------------------------------+
// reserve space for HID descriptor
static uint8_t desc_configuration[DESC_CFG_MAX];
static uint16_t _desc_str[32+1];
// USB device descriptor
tusb_desc_device_t const desc_device =
{
.bLength = sizeof(tusb_desc_device_t),
@@ -57,6 +36,17 @@ tusb_desc_device_t const desc_device =
.bNumConfigurations = 0x01
};
// string labels for device
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Raspberry Pi", // 1: Manufacturer
"Pico BLE HID", // 2: Product
NULL, // 3: Serials, should use chip ID
"Pico BLE HID CDC", // 4: CDC
"Pico USB HID", // 5: USB HID Device
};
// Invoked when received GET DEVICE DESCRIPTOR
// Application return pointer to descriptor
uint8_t const * tud_descriptor_device_cb(void)
@@ -64,47 +54,46 @@ uint8_t const * tud_descriptor_device_cb(void)
return (uint8_t const *) &desc_device;
}
//--------------------------------------------------------------------+
// Configuration Descriptor
//--------------------------------------------------------------------+
// full speed configuration
uint8_t const desc_fs_configuration[] =
{
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
};
// Invoked when received GET CONFIGURATION DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
{
(void) index; // for multiple configurations
return desc_fs_configuration;
// set configuration descriptor and CDC descriptor
memset(desc_configuration, 0, sizeof(desc_configuration));
if ( device_state == DEVICE_ACTIVE ) {
uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+1] = {
TUD_CONFIG_DESCRIPTOR(1, 2+num_mounted, 0, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+num_mounted*TUD_HID_DESC_LEN, 0x00, 100),
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
};
memcpy(desc_configuration, desc_initial, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN);
} else {
uint8_t desc_initial[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+1] = {
TUD_CONFIG_DESCRIPTOR(1, 4, 0, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN, 0x00, 100),
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
};
memcpy(desc_configuration, desc_initial, TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN);
}
// add a HID descriptor for each interface mounted on host
if ( device_state == DEVICE_ACTIVE ) {
struct report_desc * descriptor;
for (uint8_t i=0; i<num_mounted;i++) {
descriptor = get_report_desc(i);
if ( descriptor != NULL ) {
uint8_t hid_desc[TUD_HID_DESC_LEN+1] = {
TUD_HID_DESCRIPTOR(ITF_NUM_HID+i, 5, HID_ITF_PROTOCOL_NONE, descriptor->desc_len, EPNUM_HID+i, CFG_TUD_HID_EP_BUFSIZE, 1)
};
memcpy(&desc_configuration[TUD_CONFIG_DESC_LEN+TUD_CDC_DESC_LEN+i*TUD_HID_DESC_LEN], hid_desc, TUD_HID_DESC_LEN);
}
}
}
return desc_configuration;
}
//--------------------------------------------------------------------+
// String Descriptors
//--------------------------------------------------------------------+
// array of pointer to string descriptors
char const* string_desc_arr [] =
{
(const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
"Raspberry Pi", // 1: Manufacturer
"Pico BLE HID", // 2: Product
NULL, // 3: Serials, should use chip ID
"Pico BLE HID CDC", // 4: Product
};
static uint16_t _desc_str[32+1];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
@@ -145,3 +134,17 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid)
return _desc_str;
}
// Invoked when received GET HID REPORT DESCRIPTOR
// Application return pointer to descriptor
// Descriptor contents must exist long enough for transfer to complete
uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf)
{
// find HID report descriptor for indicated interface on the host and forward to device
struct report_desc * descriptor = get_report_desc(itf);
if ( descriptor != NULL ) {
return descriptor->descriptor;
}
return NULL;
}
+3 -1
View File
@@ -5,6 +5,7 @@ enum
{
ITF_NUM_CDC=0,
ITF_NUM_CDC_DATA,
ITF_NUM_HID,
ITF_NUM_TOTAL
};
@@ -15,7 +16,8 @@ enum
#define EPNUM_CDC_NOTIF 0x81
#define EPNUM_CDC_OUT 0x02
#define EPNUM_CDC_IN 0x82
#define EPNUM_HID 0x83
#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
#define DESC_CFG_MAX (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN + CFG_TUD_HID*TUD_HID_DESC_LEN)
#endif /* USB_DESCRIPTORS_H_ */
+6
View File
@@ -13,6 +13,8 @@ char cdc_buf[64];
uint16_t cdc_len;
size_t cdc_count;
device_state_t device_state;
void usb_device_init(void) {
// run TinyUSB device
tusb_rhport_init_t dev_init = {
@@ -46,6 +48,10 @@ uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_t
return 0;
}
bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len) {
return tud_hid_n_report(instance, 0, report, len);
}
// print message to CDC in raw hex
void cdc_print_hex(uint8_t const* msg, uint16_t msg_len) {
(void) msg;
+9
View File
@@ -1,11 +1,20 @@
#ifndef USB_DEVICE_H_
#define USB_DEVICE_H_
typedef enum {
DEVICE_INACTIVE=0,
DEVICE_START,
DEVICE_STOP,
DEVICE_ACTIVE
} device_state_t;
extern char cdc_buf[64];
extern uint16_t cdc_len;
extern size_t cdc_count;
extern device_state_t device_state;
void usb_device_init(void);
bool forward_report(uint8_t instance, uint8_t const* report, uint16_t len);
void cdc_print_hex(uint8_t const* msg, uint16_t msg_len);
void cdc_print_str(char const* msg, uint16_t msg_len);
void cdc_print_msg(char const* msg);
+9 -10
View File
@@ -84,26 +84,25 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
last_report = get_absolute_time();
host_state = HOST_WAIT_POLL;
queue_report(dev_addr, instance, report, len);
// continue to request to receive report
/*if ( !tuh_hid_receive_report(dev_addr, instance) )
{
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;
if ( generate_report_descriptor() ) {
if ( num_mounted > 0 ) {
host_state = HOST_MOUNTED;
cyw43_arch_gpio_put(CYW43_WL_GPIO_LED_PIN, 1);
} else {
host_state=HOST_INACTIVE;
host_state = HOST_INACTIVE;
}
cdc_print_msg("Updating HID report map\n");
update_desc_hid_report();
if ( device_state == DEVICE_ACTIVE ) {
device_state = DEVICE_START;
}
}
}
}
+1 -1
View File
@@ -1,7 +1,7 @@
#ifndef USB_HOST_H_
#define USB_HOST_H_
#define HOST_POLL_INTERVAL 8000
#define HOST_POLL_INTERVAL 7500
typedef enum {
HOST_INACTIVE=0,