|
| 1 | +// SPDX-FileCopyrightText: 2024 Ha Thach for Adafruit Industries |
| 2 | +// |
| 3 | +// SPDX-License-Identifier: MIT |
| 4 | + |
| 5 | +/********************************************************************* |
| 6 | + Adafruit invests time and resources providing this open source code, |
| 7 | + please support Adafruit and open-source hardware by purchasing |
| 8 | + products from Adafruit! |
| 9 | +
|
| 10 | + MIT license, check LICENSE for more information |
| 11 | + Copyright (c) 2019 Ha Thach for Adafruit Industries |
| 12 | + All text above, and the splash screen below must be included in |
| 13 | + any redistribution |
| 14 | +*********************************************************************/ |
| 15 | + |
| 16 | +#include <Arduino.h> |
| 17 | +#include "Adafruit_TinyUSB.h" |
| 18 | +#include <Adafruit_NeoPixel.h> |
| 19 | + |
| 20 | +const int neopixel_pin = PA4; |
| 21 | +#define LED_COUNT 1 |
| 22 | +Adafruit_NeoPixel pixels(LED_COUNT, neopixel_pin, NEO_GRB + NEO_KHZ800); |
| 23 | + |
| 24 | +//------------- Input Pins -------------// |
| 25 | +// Array of pins and its keycode. |
| 26 | +// Notes: these pins can be replaced by PIN_BUTTONn if defined in setup() |
| 27 | + |
| 28 | +uint8_t pins[] = {PB1, PB0, PA1, PA0}; //A0, A1, A2, A3 |
| 29 | + |
| 30 | +// HID report descriptor using TinyUSB's template |
| 31 | +// Single Report (no ID) descriptor |
| 32 | +uint8_t const desc_hid_report[] = { |
| 33 | + TUD_HID_REPORT_DESC_KEYBOARD() |
| 34 | +}; |
| 35 | + |
| 36 | +// USB HID object. For ESP32 these values cannot be changed after this declaration |
| 37 | +// desc report, desc len, protocol, interval, use out endpoint |
| 38 | +Adafruit_USBD_HID usb_hid; |
| 39 | + |
| 40 | + |
| 41 | +// number of pins |
| 42 | +uint8_t pincount = sizeof(pins) / sizeof(pins[0]); |
| 43 | + |
| 44 | +// For keycode definition check out https://github.com/hathach/tinyusb/blob/master/src/class/hid/hid.h |
| 45 | +uint8_t hidcode[] = {HID_KEY_0, HID_KEY_1, HID_KEY_2, HID_KEY_3}; |
| 46 | + |
| 47 | +bool activeState = false; |
| 48 | + |
| 49 | +// the setup function runs once when you press reset or power the board |
| 50 | +void setup() { |
| 51 | + pixels.begin(); |
| 52 | + // Manual begin() is required on core without built-in support e.g. mbed rp2040 |
| 53 | + if (!TinyUSBDevice.isInitialized()) { |
| 54 | + TinyUSBDevice.begin(0); |
| 55 | + } |
| 56 | + |
| 57 | + // Setup HID |
| 58 | + usb_hid.setBootProtocol(HID_ITF_PROTOCOL_KEYBOARD); |
| 59 | + usb_hid.setPollInterval(2); |
| 60 | + usb_hid.setReportDescriptor(desc_hid_report, sizeof(desc_hid_report)); |
| 61 | + usb_hid.setStringDescriptor("TinyUSB Keyboard"); |
| 62 | + |
| 63 | + // Set up output report (on control endpoint) for Capslock indicator |
| 64 | + usb_hid.setReportCallback(NULL, hid_report_callback); |
| 65 | + |
| 66 | + usb_hid.begin(); |
| 67 | + |
| 68 | + // overwrite input pin with PIN_BUTTONx |
| 69 | +#ifdef PIN_BUTTON1 |
| 70 | + pins[0] = PIN_BUTTON1; |
| 71 | +#endif |
| 72 | + |
| 73 | +#ifdef PIN_BUTTON2 |
| 74 | + pins[1] = PIN_BUTTON2; |
| 75 | +#endif |
| 76 | + |
| 77 | +#ifdef PIN_BUTTON3 |
| 78 | + pins[2] = PIN_BUTTON3; |
| 79 | +#endif |
| 80 | + |
| 81 | +#ifdef PIN_BUTTON4 |
| 82 | + pins[3] = PIN_BUTTON4; |
| 83 | +#endif |
| 84 | + |
| 85 | + // Set up pin as input |
| 86 | + for (uint8_t i = 0; i < pincount; i++) { |
| 87 | + pinMode(pins[i], activeState ? INPUT_PULLDOWN : INPUT_PULLUP); |
| 88 | + } |
| 89 | +} |
| 90 | + |
| 91 | +void process_hid() { |
| 92 | + // used to avoid send multiple consecutive zero report for keyboard |
| 93 | + static bool keyPressedPreviously = false; |
| 94 | + |
| 95 | + uint8_t count = 0; |
| 96 | + uint8_t keycode[6] = {0}; |
| 97 | + |
| 98 | + // scan normal key and send report |
| 99 | + for (uint8_t i = 0; i < pincount; i++) { |
| 100 | + if (activeState == digitalRead(pins[i])) { |
| 101 | + // if pin is active (low), add its hid code to key report |
| 102 | + keycode[count++] = hidcode[i]; |
| 103 | + |
| 104 | + // 6 is max keycode per report |
| 105 | + if (count == 6) break; |
| 106 | + } |
| 107 | + } |
| 108 | + |
| 109 | + if (TinyUSBDevice.suspended() && count) { |
| 110 | + // Wake up host if we are in suspend mode |
| 111 | + // and REMOTE_WAKEUP feature is enabled by host |
| 112 | + TinyUSBDevice.remoteWakeup(); |
| 113 | + } |
| 114 | + |
| 115 | + // skip if hid is not ready e.g still transferring previous report |
| 116 | + if (!usb_hid.ready()) return; |
| 117 | + |
| 118 | + if (count) { |
| 119 | + // Send report if there is key pressed |
| 120 | + uint8_t const report_id = 0; |
| 121 | + uint8_t const modifier = 0; |
| 122 | + |
| 123 | + keyPressedPreviously = true; |
| 124 | + usb_hid.keyboardReport(report_id, modifier, keycode); |
| 125 | + } else { |
| 126 | + // Send All-zero report to indicate there is no keys pressed |
| 127 | + // Most of the time, it is, though we don't need to send zero report |
| 128 | + // every loop(), only a key is pressed in previous loop() |
| 129 | + if (keyPressedPreviously) { |
| 130 | + keyPressedPreviously = false; |
| 131 | + usb_hid.keyboardRelease(0); |
| 132 | + } |
| 133 | + } |
| 134 | +} |
| 135 | + |
| 136 | +void loop() { |
| 137 | + #ifdef TINYUSB_NEED_POLLING_TASK |
| 138 | + // Manual call tud_task since it isn't called by Core's background |
| 139 | + TinyUSBDevice.task(); |
| 140 | + #endif |
| 141 | + |
| 142 | + // not enumerated()/mounted() yet: nothing to do |
| 143 | + if (!TinyUSBDevice.mounted()) { |
| 144 | + return; |
| 145 | + } |
| 146 | + |
| 147 | + // poll gpio once each 2 ms |
| 148 | + static uint32_t ms = 0; |
| 149 | + if (millis() - ms > 2) { |
| 150 | + ms = millis(); |
| 151 | + process_hid(); |
| 152 | + } |
| 153 | +} |
| 154 | + |
| 155 | +void setLED(bool state) { |
| 156 | + pixels.setPixelColor(0, pixels.Color(0, state ? 150 : 0, 0)); |
| 157 | + pixels.show(); |
| 158 | +} |
| 159 | + |
| 160 | +// Output report callback for LED indicator such as Caplocks |
| 161 | +void hid_report_callback(uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) { |
| 162 | + (void) report_id; |
| 163 | + (void) bufsize; |
| 164 | + |
| 165 | + // LED indicator is output report with only 1 byte length |
| 166 | + if (report_type != HID_REPORT_TYPE_OUTPUT) return; |
| 167 | + |
| 168 | + // The LED bit map is as follows: (also defined by KEYBOARD_LED_* ) |
| 169 | + // Kana (4) | Compose (3) | ScrollLock (2) | CapsLock (1) | Numlock (0) |
| 170 | + uint8_t ledIndicator = buffer[0]; |
| 171 | + |
| 172 | + // turn on LED if capslock is set |
| 173 | + setLED(ledIndicator & KEYBOARD_LED_CAPSLOCK); |
| 174 | +} |
0 commit comments