diff --git a/examples/RawHID/RawHID.ino b/examples/RawHID/RawHID.ino index 6da4f58..04ff209 100644 --- a/examples/RawHID/RawHID.ino +++ b/examples/RawHID/RawHID.ino @@ -7,6 +7,12 @@ Shows how to send bytes via RawHID. Press a button to send some example values. + Every received data is mirrored to the host. + The use of the RawHIDEvent() function is shown too. + + This sketch only tries to show the possiblities + and is not perfect.You might want to use RawHID differently. + See HID Project documentation for more information. https://github.com/NicoHood/HID/wiki/RawHID-API */ @@ -16,20 +22,26 @@ const int pinLed = LED_BUILTIN; const int pinButton = 2; +uint8_t data[255]; +volatile size_t len = 0; + void setup() { pinMode(pinLed, OUTPUT); pinMode(pinButton, INPUT_PULLUP); - // No begin function needed for RawHID + + // No begin/end function required for RawHID } void loop() { + // Send data to the host if (!digitalRead(pinButton)) { digitalWrite(pinLed, HIGH); - // Create buffer and send it + // Create buffer with numbers and send it uint8_t megabuff[100]; - for (int i = 0; i < sizeof(megabuff); i++) + for (uint8_t i = 0; i < sizeof(megabuff); i++) { megabuff[i] = i; + } RawHID.write(megabuff, sizeof(megabuff)); // Simple debounce @@ -37,20 +49,55 @@ void loop() { digitalWrite(pinLed, LOW); } - uint8_t len = RawHID.available(); + + // This will miss longer RawHID data transmissions + // and return an error to the host if data was missed. + // Only use this for non changing/less important data. + // Or you can use this if the event aborted on a full buffer. + // Please note, that all data after this full buffer is missed anyways. + auto bytesAvailable = RawHID.available(); + while (bytesAvailable--) { + if (len < sizeof(data)) { + data[len++] = RawHID.read(); + } + } + + // Process data from the host if (len) { digitalWrite(pinLed, HIGH); - // Mirror the incoming data from the host back - uint8_t buff[len + 1]; - buff[0] = len; - for (int i = 1; i < sizeof(buff); i++) { - buff[i] = RawHID.read(); - } - RawHID.write(buff, len); + // Disable interrupts while processing the data + uint8_t oldSREG = SREG; + cli(); - // Simple debounce + // Mirror the incoming data from the host back + RawHID.write(data, len); + len = 0; + + SREG = oldSREG; + + // Simple debounce for led delay(300); digitalWrite(pinLed, LOW); } } + +void RawHIDEvent(void) { + // This event is called via interrupt. + // Do not use print inside, or other long function calls! + // If you not use this event function, + // you might miss some data in the loop, + // if the host sends too fast or too much data at once. + + auto bytesAvailable = RawHID.available(); + while (bytesAvailable--) { + // Only add data to the buffer if its not full. + // If it is, no more event will occur + // and the data should be discarded + // or read (as shown) in the loop above. + if (len < sizeof(data)) { + data[len++] = RawHID.read(); + } + } +} + diff --git a/src/HID-Project.h b/src/HID-Project.h index 0a0dde7..8b5aaf2 100644 --- a/src/HID-Project.h +++ b/src/HID-Project.h @@ -27,6 +27,9 @@ THE SOFTWARE. // Software version #define HID_PROJECT_VERSION 240 +// TODO remove https://github.com/arduino/arduino-builder/issues/33 +#include + #if ARDUINO < 10606 #error HID Project requires Arduino IDE 1.6.6 or greater. Please update your IDE. #endif diff --git a/src/HID-Settings.h b/src/HID-Settings.h index b95de02..692186b 100644 --- a/src/HID-Settings.h +++ b/src/HID-Settings.h @@ -82,7 +82,7 @@ THE SOFTWARE. #ifndef HID_REPORTID_RAWHID // This will not work properly in most cases. // The number is just kept from the old number counting. -#define HID_REPORTID_RAWHID 3 +//#define HID_REPORTID_RAWHID 3 #endif #ifndef HID_REPORTID_CONSUMERCONTROL diff --git a/src/SingleReport/RawHID.cpp b/src/SingleReport/RawHID.cpp index 66c9d1b..3cafe96 100644 --- a/src/SingleReport/RawHID.cpp +++ b/src/SingleReport/RawHID.cpp @@ -47,6 +47,12 @@ static const uint8_t _hidReportDescriptorRawHID[] PROGMEM = { 0xC0 /* end collection */ }; +// Weak implementation of the Event function +void RawHIDEvent(void) __attribute__ ((weak)); +void RawHIDEvent(void){ + // Empty +} + RawHID_::RawHID_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_PROTOCOL), idle(1), dataLength(0) { epType[0] = EP_TYPE_INTERRUPT_IN; @@ -55,7 +61,6 @@ RawHID_::RawHID_(void) : PluggableUSBModule(1, 1, epType), protocol(HID_REPORT_P int RawHID_::getInterface(uint8_t* interfaceCount) { - // TODO add a 2nd OUT endpoint to get more speed??? // Maybe as optional device FastRawHID with different USAGE PAGE *interfaceCount += 1; // uses 1 HIDDescriptor hidInterface = { @@ -117,36 +122,40 @@ bool RawHID_::setup(USBSetup& setup) { // Get the data length information and the corresponding bytes int length = setup.wLength; - - // Ensure that there IS some data - if(length > 0) + while(length) { - void* data = malloc(length); - if(data){ - auto recvLength = length; - //TODO loop can be improved maybe? Or does the compiler do this already? - while(recvLength > USB_EP_SIZE){ - USB_RecvControl((uint8_t*)data + (length - recvLength), USB_EP_SIZE); - recvLength -= USB_EP_SIZE; - } - USB_RecvControl((uint8_t*)data + (length - recvLength), recvLength); - - // Only overwrite the buffer if its empty. - // This avoids corrupted data while reading. - if(!dataLength){ - // Save new data - dataLength = length; - dataHead = (uint8_t*) data; - dataTail = (uint8_t*)(data) + length; - - // Clear the passed in pointer to not free the data - data = NULL; - } + // Dont receive more than the USB EP has to offer + // TODO use fixed 64 because control EP always have 64 bytes even on 16 u2. Test! + auto recvLength = length; + if(recvLength > USB_EP_SIZE){ + recvLength = USB_EP_SIZE; + } + if(recvLength > int(sizeof(data))){ + recvLength = int(sizeof(data)); + } + length -= recvLength; + + // Only receive data if the last one was read fully. + // Discard data if it wasnt read yet. + // To not miss any data you can use the (weak) event function. + if(!dataLength) + { + // Write data to fit to the end (not the beginning) of the array + USB_RecvControl(data + (sizeof(data) - recvLength), recvLength); + dataLength = recvLength; + RawHIDEvent(); + } + // Do not read the data, flag an error to the USB Host + // TODO always return no error, even if data was discarded? -> use break then + else{ + return false; } - - // Release data if the pointer still exists - free(data); } + + // Flag no error + // TODO this is required to get hyperion working + // however this blocks the CDC serial!? + return true; } } diff --git a/src/SingleReport/RawHID.h b/src/SingleReport/RawHID.h index 2a365e2..5f2249c 100644 --- a/src/SingleReport/RawHID.h +++ b/src/SingleReport/RawHID.h @@ -40,6 +40,8 @@ THE SOFTWARE. // Keep one byte offset for the reportID if used #if (HID_REPORTID_RAWHID) #define RAWHID_SIZE (USB_EP_SIZE-1) +#error RAWHID does not work properly with a report ID and multiple reports. +#error Please remove this manually if you know what you are doing. #else #define RAWHID_SIZE (USB_EP_SIZE) #endif @@ -84,36 +86,28 @@ public: } virtual int read(){ - if(dataLength){ - // Get next data byte - uint8_t data = *(dataTail - dataLength); - dataLength--; - - // Release buffer if its read fully - if(!dataLength){ - free(dataHead); - } - - return data; + // Check if we have data available + if(dataLength) + { + // Get next data byte (from the start to the end) + return data[sizeof(data) - dataLength--]; } return -1; } virtual int peek(){ + // Check if we have data available if(dataLength){ - return *(dataTail - dataLength); + return data[sizeof(data) - dataLength]; } return -1; } virtual void flush(void){ - // Delete all incoming bytes - if(dataLength){ - free(dataHead); - dataLength = 0; - } + // Writing will always flush by the USB driver } + // Wrapper for a single byte using Print::write; virtual size_t write(uint8_t b){ return write(&b, 1); @@ -153,8 +147,7 @@ protected: // Buffer pointers to hold the received data int dataLength; - uint8_t* dataHead; - uint8_t* dataTail; + uint8_t data[RAWHID_RX_SIZE]; }; extern RawHID_ RawHID;