GithubHelp home page GithubHelp logo

guruthree / pico-superkey-board Goto Github PK

View Code? Open in Web Editor NEW
37.0 3.0 5.0 33 KB

A single key keyboard, a demonstration of a USB HID device for the Raspberry Pi Pico in C

CMake 6.51% C++ 60.25% C 33.24%
raspberry-pi-pico usb-hid tinyusb keyboard usb arduino

pico-superkey-board's Introduction

Pico SuperKey Board

This project contains the code for a simple, single key keyboard based on the Raspberry Pi Pico. Short the contacts, get a key press on a USB HID keyboard.

Project goals:

  • Add a key on my desk for the missing super (Windows) key on my IBM Model M keyboard.
  • Investigate USB device support on the Pico.
  • Find an easy way to emulate a USB HID device in C/C++ on the Pico using the Pico SDK. The last goal was the most important for me as there is (at the time of writing) currently no HID device example in pico-examples code, referring to the standard TinyUSB example, which is somewhat lacking in comments to elucidate what does what, and how to compile this with the Pico SDK.

(Note, when this project was originally carried out, there was no Arduino Core for the RP2040.)

Searching on how to use TinyUSB led me to Adafruit's TinyUSB Arduino library. This is an implementation of TinyUSB on top of the Arduino core for some chips, including the RP2040. A bit more searching revealed the TinyUSB Mouse and Keyboard library, a library sitting on top of the Adafruit TinyUSB libraries providing the same API for mouse and keyboard USB HID as the Arduino API.

My train of thought ran thus: if the libraries could be made to work somehow I would have a familiar interface for emulating a keyboard. The first problem was trying to work out what functions of the code in the Pico SDK example were included within the libraries, and what code I would need to keep from the example. The second problem was that the Arduino libraries above all depend on the Arduino core libraries and certain features of this would need to be re-implemented. The third problem was how to debug and fix any problems along the way.

To tackle both the first and second problem, I started with a blank project, included "TinyUSB_Mouse_and_Keyboard/TinyUSB_Mouse_and_Keyboard.h", and worked my way down the include tree until every missing reference was resolved. This required a USBDevice instance from Adafruit TinyUSB. In order to compile, a global define add_definitions(-DARDUINO_ARCH_RP2040) was needed in CMakeLists.txt. This is the define for the Arduino RP2040 core, and triggers all of the hardware specific functions in Adafruit TinyUSB to make USB functionality work. Most noticeably it sets up a looping task to handle USB packets.

The USBDevice handles most of the underlying bits, but annoyingly it also forces an instance of a serial device, which I did not want. In order to not have the serial device, two things were needed. First, I had to use add_definitions(-DCFG_TUSB_CONFIG_FILE="includes/tusb_config.h") in CMakeLists.txt to specify my own tusb_config.h that enabled only the HID USB device class. Second, an Adafruit_USBD_CDC class was needed that would create empty objects and methods for those needed by USBDevice.begin().

The bulk of the missing elements from the Arduino core libraries were the Print, Stream, and String classes. The first I could not find a C standard library equivalent for and so needed to be copied directly from the library. The second inherits from Print. No new member functions were used and so Stream was replaced with Print. The last can be replaced with std::string. There were additionally some special functions for accessing flash memory that needed to have standard C replacements and delay(), which was replaced sleep_ms(). I'm not entirely sure about the special function replacements, but testing suggests they work. The substitutions were made entirely using C preprocessor #defines in a set of stub .h files in includes/.

Also, note USE_TINYUSB needed to be defined via add_definitions(-DUSE_TINYUSB) in the CMakesLists.txt file to enable USB support in the Pico SDK. To define what the USB device name is, USB_PRODUCT should similarly be defined, e.g., using add_definitions(-DUSB_PRODUCT="Pico Keyboard"). Adafruit_USBD_Device.cpp lists additional defines.

Debugging was a slightly annoying task. What was not immediately apparent was that a good chunk of the code in the SDK composite HID example was there to control the Pico's built-in LED based on the state of the USB device and did nothing for actually being the USB device. Particularly, the board_init() function for the example just initialises the built-in LED. The led_blinking_task() is responsible for turning the LED on and off at the interval specified in blink_interval_ms. That variable is altered by the tud_*_cb() callback functions from TinyUSB. Adding this code back in allowed me to have a rough idea of what was going on without the ability to use SWD (software debug) or serial print statements over USB. You may be aware of the Hello World/USB example, which creates a USB serial device that responds to printf(). However, examining stdio_usb.c where this functionality is coded up shows that if we explicitly want to link to TinyUSB, say for creating our own Keyboard device, this functionality is disabled. Thus, debugging was limited to the blinking LED and using well-placed gpio_put() calls to turn the LED on once a point in the code was reached.

The biggest issue I had once the code "should have" worked was that it looked like the Pico was locking up. It would get partway through registering as USB device on my desktop and then time out.

Feb 27 19:18:11 localhost kernel: usb 3-6: new full-speed USB device number 20 using xhci_hcd
Feb 27 19:18:11 localhost kernel: usb 3-6: config index 0 descriptor too short (expected 9, got 0)
Feb 27 19:18:11 localhost kernel: usb 3-6: can't read configurations, error -22

This turned out to be some issue with the usb initialisation.

Further work... I don't see why the rest of the Adafruit TinyUSB library wouldn't work on the Pico. You should be able to use this (maybe with some Arduino core library tweaks) to have a CDC serial device along with your keyboard device. Or mass storage. Or midi. For whatever devices you're going to have though, remember that tusb_config.h will need to be updated.

Usage

  1. Have a working Raspberry Pi Pico C SDK setup.
  2. Clone/download the repository. Don't forget to run git submodule update --init.
  3. Ensure that there is a symbolic link to pico_sdk_import.cmake from the Pico C SDK. (If a symbolic link isn't an option, just copy the file.)
  4. Create a build directory, cd to it, and run cmake ../. (Or your build system equivalents.)
  5. Build by running make. (Or your build system equivalent.)
  6. Reset (or plug in) the Pico holding down the BOOTSEL and copy pico-superkey-board.uf2 to the Pico's USB mass storage device.
  7. You should have a keyboard device now. Shorting the GP20 pin (defined by SUPERKEY_PIN in pico-superkey-board.cpp) to ground will trigger a keypress of the super (Windows) key.

Life during development was made a lot easier by being able to reset the Pico by shorting the RUN pin to ground. My clever way to do this is using an unfolded paperclip.

pico-superkey-board's People

Contributors

guruthree avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pico-superkey-board's Issues

Failed to build on MacOS 11.4

'cmake ..' is successful but 'make' gives the following output. Any suggestion?

~/dev/projects/pi-pico/pico-superkey-board/build (master)$ make
[ 1%] Creating directories for 'ELF2UF2Build'
[ 2%] No download step for 'ELF2UF2Build'
[ 3%] No update step for 'ELF2UF2Build'
[ 4%] No patch step for 'ELF2UF2Build'
[ 6%] Performing configure step for 'ELF2UF2Build'
-- The C compiler identification is AppleClang 12.0.5.12050022
-- The CXX compiler identification is AppleClang 12.0.5.12050022
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /Library/Developer/CommandLineTools/usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Library/Developer/CommandLineTools/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/sean/dev/projects/pi-pico/pico-superkey-board/build/elf2uf2
[ 7%] Performing build step for 'ELF2UF2Build'
[ 50%] Building CXX object CMakeFiles/elf2uf2.dir/main.cpp.o
[100%] Linking CXX executable elf2uf2
[100%] Built target elf2uf2
[ 8%] No install step for 'ELF2UF2Build'
[ 9%] Completed 'ELF2UF2Build'
[ 9%] Built target ELF2UF2Build
Scanning dependencies of target bs2_default
[ 10%] Building ASM object pico-sdk/src/rp2_common/boot_stage2/CMakeFiles/bs2_default.dir/compile_time_choice.S.obj
[ 12%] Linking ASM executable bs2_default.elf
[ 12%] Built target bs2_default
[ 13%] Generating bs2_default.bin
[ 14%] Generating bs2_default_padded_checksummed.S
[ 14%] Built target bs2_default_padded_checksummed_asm
Scanning dependencies of target pico-superkey-board
[ 15%] Building CXX object CMakeFiles/pico-superkey-board.dir/pico-superkey-board.cpp.obj
[ 16%] Building CXX object CMakeFiles/pico-superkey-board.dir/TinyUSB_Mouse_and_Keyboard/TinyUSB_Mouse_and_Keyboard.cpp.obj
[ 18%] Building CXX object CMakeFiles/pico-superkey-board.dir/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp.obj
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp: In constructor 'Adafruit_USBD_HID::Adafruit_USBD_HID()':
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:37:15: error: 'HID_PROTOCOL_NONE' was not declared in this scope; did you mean 'HID_PROTOCOL_BOOT'?
37 | _protocol = HID_PROTOCOL_NONE;
| ^~~~~~~~~~~~~~~~~
| HID_PROTOCOL_BOOT
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp: At global scope:
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:138:16: error: conflicting declaration of 'const uint8_t* tud_hid_descriptor_report_cb()' with 'C' linkage
138 | uint8_t const tud_hid_descriptor_report_cb(void) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:25:
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.h:94:25: note: previous declaration with 'C++' linkage
94 | friend uint8_t const tud_hid_descriptor_report_cb(void);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:148:10: error: conflicting declaration of 'uint16_t tud_hid_get_report_cb(uint8_t, hid_report_type_t, uint8_t
, uint16_t)' with 'C' linkage
148 | uint16_t tud_hid_get_report_cb(uint8_t report_id, hid_report_type_t report_type,
| ^~~~~~~~~~~~~~~~~~~~~
In file included from /Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:25:
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.h:88:19: note: previous declaration with 'C++' linkage
88 | friend uint16_t tud_hid_get_report_cb(uint8_t report_id,
| ^~~~~~~~~~~~~~~~~~~~~
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:158:6: error: conflicting declaration of 'void tud_hid_set_report_cb(uint8_t, hid_report_type_t, const uint8_t
, uint16_t)' with 'C' linkage
158 | void tud_hid_set_report_cb(uint8_t report_id, hid_report_type_t report_type,
| ^~~~~~~~~~~~~~~~~~~~~
In file included from /Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp:25:
/Users/sean/dev/projects/pi-pico/pico-superkey-board/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.h:91:15: note: previous declaration with 'C++' linkage
91 | friend void tud_hid_set_report_cb(uint8_t report_id,
| ^~~~~~~~~~~~~~~~~~~~~
make[2]: *** [CMakeFiles/pico-superkey-board.dir/Adafruit_TinyUSB_Arduino/src/Adafruit_USBD_HID.cpp.obj] Error 1
make[1]: *** [CMakeFiles/pico-superkey-board.dir/all] Error 2
make: *** [all] Error 2

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.