diff --git a/LICENSE b/LICENSE index 4ff0a0f..9d6c6a1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018 gdsports +Copyright (c) 2018 gdsports625@gmail.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 77b890e..1e04387 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,80 @@ -# rfm69-usb-devices -RFM69 USB extender +# Wireless USB Keyboard Extender Using RFM69 +A long range wireless USB keyboard in two parts. + +The USB keyboard plugs into the sender. The sender must have USB host +capabiiity to communicate with the keyboard. The SAMD21 M0 is used for this +project. Every key press and release generates a USB HID (Human Interface +Descriptor) report. The report is a 8 byte data struture which is sent using +the RFM69 radio to the receiver. + +The receiver plugs into the computer via a USB port. No other components are +required because the RFM69HCW radio is integrated with the Feather board. The +receiver takes USB HID reports from the RFM69 radio then sends to the computer +over USB. + +## USB keyboard receiver + +915 MHz -> Feather 32u4 RFM69HCW -> USB cable -> Computer + +The receiver may use the M0 or 32u4 Feather version. The 32u4 was used here for +no other reason than it was available. + +* Adafruit Feather 32u4 RFM69HCW Packet Radio + +Headers do not need to be installed on the board. An antenna is required. + +Upload the RFM69KbdRFM69Rx.ino sketch. This program reads USB HID reports from +the RFM69 radio then sends them out the USB keyboard device interface to the +computer. The Feather is powered from the computer. + +## USB keyboard sender + +USB USB OTG Host +Keyboard -> cable/adapter -> Feather M0 RFM69HCW -> 915 MHz + GND USB Tx + | ^ | + | | | + | | v + GND 5V RxD + CP2104 USB to serial -> Computer + +WARNING: The sender must use the M0 Feather version because the M0 has USB host +capability. The 32u4 does not. + +* USB keyboard +* USB OTG Host Cable - MicroB OTG male to A female +* Adafruit Feather M0 RFM69HCW Packet Radio +* Adafruit CP2104 Friend - USB to Serial Converter + +Headers and breadboard are used to connect to power to the Feather board. An +antenna is required. + +Upload the RFM69KbdRFM69Tx.ino sketch. This program reads USB HID reports from +the USB keyboard then sends the USB HID reports to the RFM69 radio. + +### Upload mode + +Powering the Feather is trickier compared to the receiver. When uploading, the +Feather is powered from the computer. The other 5V power source (see below) +MUST be disconnected. + +WARNING: Do not connect more than one power source to the Feather at the same +time. + +WARNING: When using USB host mode, the Feather RESET button must be pressed +twice to put the board in upload mode. Automatic upload does not work. + +Feather M0 RFM69HCW -> USB cable -> Computer + +### Sender mode + +When using the Feather in keyboard sender mode, the Feather must be powered by +a 5V power source connected to its USB pin. In this case, a CP2104 USB serial +board provides the 5V power as well as USB serial for debug output via the +Serial1 UART Tx pin. + +When debugging is completed, the CP2104 board is not needed but a 5V power +supply is still required. + +The Feather battery power option is not useful here because Lithium batteries +do not provide 5V. diff --git a/USBKbdRFM69Rx/USBKbdRFM69Rx.ino b/USBKbdRFM69Rx/USBKbdRFM69Rx.ino new file mode 100644 index 0000000..e982640 --- /dev/null +++ b/USBKbdRFM69Rx/USBKbdRFM69Rx.ino @@ -0,0 +1,165 @@ +/* + * Keyboard Wireless Extender Example -- Receiver + * Receive USB keyboard HID report from RFM69 raw mode then send on USB keyboard + * device interface. Tested on Adafruit Feather 32u4 with RFM69HCW. + * Based on an example included in Adafruit's fork of the RadioHead library. + */ + +//8#define DEBUG_KEYBOARD_RAW + +#include + +#include +#include +#include + +/************ Radio Setup ***************/ + +// Change to 434.0 or other frequency, must match RX's freq! +#define RF69_FREQ 915.0 + +// who am i? (server address) +#define MY_ADDRESS 1 + + +#if defined (__AVR_ATmega32U4__) // Feather 32u4 w/Radio + #define RFM69_CS 8 + #define RFM69_INT 7 + #define RFM69_RST 4 + #define LED 13 +#endif + +#if defined(ARDUINO_SAMD_FEATHER_M0) // Adafruit Feather M0 w/Radio + #define RFM69_CS 8 + #define RFM69_INT 3 + #define RFM69_RST 4 + #define LED LED_BUILTIN +#elif defined(ARDUINO_ITSYBITSY_M0) // Adafruit ItsyBitsy M0 + #define RFM69_CS 10 + #define RFM69_INT 7 + #define RFM69_RST 9 + #define LED LED_BUILTIN +#elif defined(ARDUINO_SAMD_ZERO) // Arduino Zero + #define RFM69_CS 4 + #define RFM69_INT 3 + #define RFM69_RST 2 + #define LED LED_BUILTIN +#endif + +#if defined (__AVR_ATmega328P__) // Feather 328P w/wing + #define RFM69_INT 3 // + #define RFM69_CS 4 // + #define RFM69_RST 2 // "A" + #define LED 13 +#endif + +#if defined(ESP8266) // ESP8266 feather w/wing + #define RFM69_CS 2 // "E" + #define RFM69_IRQ 15 // "B" + #define RFM69_RST 16 // "D" + #define LED 0 +#endif + +#if defined(ESP32) // ESP32 feather w/wing + #define RFM69_RST 13 // same as LED + #define RFM69_CS 33 // "B" + #define RFM69_INT 27 // "A" + #define LED 13 +#endif + +/* Teensy 3.x w/wing + #define RFM69_RST 9 // "A" + #define RFM69_CS 10 // "B" + #define RFM69_IRQ 4 // "C" + #define RFM69_IRQN digitalPinToInterrupt(RFM69_IRQ ) +*/ + +/* WICED Feather w/wing + #define RFM69_RST PA4 // "A" + #define RFM69_CS PB4 // "B" + #define RFM69_IRQ PA15 // "C" + #define RFM69_IRQN RFM69_IRQ +*/ + +// Singleton instance of the radio driver +RH_RF69 rf69(RFM69_CS, RFM69_INT); + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram rf69_manager(rf69, MY_ADDRESS); + +void setup() +{ + SERIAL_PORT_MONITOR.begin(115200); + //while (!Serial) { delay(1); } // wait until serial console is open, remove if not tethered to computer + + pinMode(LED, OUTPUT); + pinMode(RFM69_RST, OUTPUT); + digitalWrite(RFM69_RST, LOW); + + SERIAL_PORT_MONITOR.println("Feather RFM69 RX Test!"); + SERIAL_PORT_MONITOR.println(); + + // manual reset + digitalWrite(RFM69_RST, HIGH); + delay(10); + digitalWrite(RFM69_RST, LOW); + delay(10); + + if (!rf69_manager.init()) { + SERIAL_PORT_MONITOR.println("RFM69 radio init failed"); + while (1); + } + SERIAL_PORT_MONITOR.println("RFM69 radio init OK!"); + + // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) + // No encryption + if (!rf69.setFrequency(RF69_FREQ)) { + SERIAL_PORT_MONITOR.println("setFrequency failed"); + } + + // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the + // ishighpowermodule flag set like this: + rf69.setTxPower(20, true); // range from 14-20 for power, 2nd arg must be true for 69HCW + + // The encryption key has to be the same as the one in the server + uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 + }; + rf69.setEncryptionKey(key); + + pinMode(LED, OUTPUT); + + SERIAL_PORT_MONITOR.print("RFM69 radio @"); + SERIAL_PORT_MONITOR.print((int)RF69_FREQ); + SERIAL_PORT_MONITOR.println(" MHz"); + + Keyboard.begin(); +} + +uint8_t buf[RH_RF69_MAX_MESSAGE_LEN]; + +void loop() { + if (rf69_manager.available()) { + // Should be a message for us now + uint8_t len = sizeof(buf); + uint8_t from; + if (rf69_manager.recvfromAck(buf, &len, &from)) { + if (!len) return; + HID().SendReport(2, buf, len); +#ifdef DEBUG_KEYBOARD_RAW + SERIAL_PORT_MONITOR.print("Received ["); + SERIAL_PORT_MONITOR.print(len); + SERIAL_PORT_MONITOR.print("]: "); + for (uint8_t i = 0; i < len ; i++) { + SERIAL_PORT_MONITOR.print(' '); SERIAL_PORT_MONITOR.print(buf[i], HEX); + } + SERIAL_PORT_MONITOR.println(); + SERIAL_PORT_MONITOR.print("RSSI: "); + SERIAL_PORT_MONITOR.println(rf69.lastRssi(), DEC); +#endif + } else { + SERIAL_PORT_MONITOR.println("Receive failed"); + } + } +} + diff --git a/USBKbdRFM69Tx/USBKbdRFM69Tx.ino b/USBKbdRFM69Tx/USBKbdRFM69Tx.ino new file mode 100644 index 0000000..ec1a752 --- /dev/null +++ b/USBKbdRFM69Tx/USBKbdRFM69Tx.ino @@ -0,0 +1,196 @@ +/* + * Keyboard Wireless Extender Example -- Sender + * Intercept USB keyboard HID report then send it via RFM69 raw mode. + * Based on an example included in Adafruit's fork of the RadioHead library. + */ + +//#define DEBUG_KEYBOARD_RAW 1 + +#include +#include +#include + +/************ Radio Setup ***************/ + +// Change to 434.0 or other frequency, must match RX's freq! +#define RF69_FREQ 915.0 + +// Where to send packets to! +#define DEST_ADDRESS 1 +// change addresses for each client board, any number :) +#define MY_ADDRESS 2 + + +#if defined (__AVR_ATmega32U4__) // Feather 32u4 w/Radio + #define RFM69_CS 8 + #define RFM69_INT 7 + #define RFM69_RST 4 + #define LED 13 +#endif + +#if defined(ARDUINO_SAMD_FEATHER_M0) // Adafruit Feather M0 w/Radio + #define RFM69_CS 8 + #define RFM69_INT 3 + #define RFM69_RST 4 + #define LED LED_BUILTIN + #ifdef SERIAL_PORT_MONITOR + #undef SERIAL_PORT_MONITOR + #endif + #define SERIAL_PORT_MONITOR Serial1 +#elif defined(ARDUINO_ITSYBITSY_M0) // Adafruit ItsyBitsy M0 + #define RFM69_CS 10 + #define RFM69_INT 7 + #define RFM69_RST 9 + #define LED LED_BUILTIN + #ifdef SERIAL_PORT_MONITOR + #undef SERIAL_PORT_MONITOR + #endif + #define SERIAL_PORT_MONITOR Serial1 +#elif defined(ARDUINO_SAMD_ZERO) // Arduino Zero + #define RFM69_CS 4 + #define RFM69_INT 3 + #define RFM69_RST 2 + #define LED LED_BUILTIN +#endif + +#if defined (__AVR_ATmega328P__) // Feather 328P w/wing + #define RFM69_INT 3 // + #define RFM69_CS 4 // + #define RFM69_RST 2 // "A" + #define LED 13 +#endif + +#if defined(ESP8266) // ESP8266 feather w/wing + #define RFM69_CS 2 // "E" + #define RFM69_IRQ 15 // "B" + #define RFM69_RST 16 // "D" + #define LED 0 +#endif + +#if defined(ESP32) // ESP32 feather w/wing + #define RFM69_RST 13 // same as LED + #define RFM69_CS 33 // "B" + #define RFM69_INT 27 // "A" + #define LED 13 +#endif + +/* Teensy 3.x w/wing +#define RFM69_RST 9 // "A" +#define RFM69_CS 10 // "B" +#define RFM69_IRQ 4 // "C" +#define RFM69_IRQN digitalPinToInterrupt(RFM69_IRQ ) +*/ + +/* WICED Feather w/wing +#define RFM69_RST PA4 // "A" +#define RFM69_CS PB4 // "B" +#define RFM69_IRQ PA15 // "C" +#define RFM69_IRQN RFM69_IRQ +*/ + +// Singleton instance of the radio driver +RH_RF69 rf69(RFM69_CS, RFM69_INT); + +// Class to manage message delivery and receipt, using the driver declared above +RHReliableDatagram rf69_manager(rf69, MY_ADDRESS); + +#include + +class KeyboardRaw : public KeyboardReportParser { +public: + KeyboardRaw(USBHost &usb) : hostKeyboard(&usb) { + hostKeyboard.SetReportParser(0, this); + }; + + void Parse(HID *hid, uint32_t is_rpt_id, uint32_t len, uint8_t *buf); + +private: + HIDBoot hostKeyboard; +}; + +void KeyboardRaw::Parse(HID *hid, uint32_t is_rpt_id, uint32_t len, uint8_t *buf) +{ +#ifdef DEBUG_KEYBOARD_RAW + SERIAL_PORT_MONITOR.print("KeyboardRaw::Parse"); + // Show USB HID keyboard report + for (uint8_t i = 0; i < len ; i++) { + SERIAL_PORT_MONITOR.print(' '); SERIAL_PORT_MONITOR.print(buf[i], HEX); + } + SERIAL_PORT_MONITOR.println(); +#endif + + // Call parent/super method + KeyboardReportParser::Parse(hid, is_rpt_id, len, buf); + + // On error - return + if (buf[2] == 1) + return; + + if (len == 8) { + rf69_manager.sendtoWait(buf, len, DEST_ADDRESS); + } +} + +// Initialize USB Controller +USBHost usb; + +// Attach keyboard controller to USB +KeyboardRaw keyboard(usb); + +void setup() +{ + SERIAL_PORT_MONITOR.begin( 115200 ); + //while (!SERIAL_PORT_MONITOR); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection + SERIAL_PORT_MONITOR.println("KeyboardRaw Program started"); + + if (usb.Init() == -1) + SERIAL_PORT_MONITOR.println("USB host controller did not start."); + + delay( 20 ); + + pinMode(LED, OUTPUT); + pinMode(RFM69_RST, OUTPUT); + digitalWrite(RFM69_RST, LOW); + + SERIAL_PORT_MONITOR.println("Feather RFM69 TX Test!"); + SERIAL_PORT_MONITOR.println(); + + // manual reset + digitalWrite(RFM69_RST, HIGH); + delay(10); + digitalWrite(RFM69_RST, LOW); + delay(10); + + if (!rf69_manager.init()) { + SERIAL_PORT_MONITOR.println("RFM69 radio init failed"); + while (1); + } + SERIAL_PORT_MONITOR.println("RFM69 radio init OK!"); + // Defaults after init are 434.0MHz, modulation GFSK_Rb250Fd250, +13dbM (for low power module) + // No encryption + if (!rf69.setFrequency(RF69_FREQ)) { + SERIAL_PORT_MONITOR.println("setFrequency failed"); + } + + // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the + // ishighpowermodule flag set like this: + rf69.setTxPower(20, true); // range from 14-20 for power, 2nd arg must be true for 69HCW + + // The encryption key has to be the same as the one in the server + uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + rf69.setEncryptionKey(key); + + pinMode(LED, OUTPUT); + + SERIAL_PORT_MONITOR.print("RFM69 radio @"); + SERIAL_PORT_MONITOR.print((int)RF69_FREQ); + SERIAL_PORT_MONITOR.println(" MHz"); +} + +void loop() +{ + // Process USB tasks + usb.Task(); +} +