Saturday November 25 2017

HID Mouse Repeater Arduino Code

// *** ALL MY Code is 100% free of cost and liability *** 

//-----------------------------------------------------------------------------------//
#include < avr/pgmspace.h >
#include < avrpins.h >
#include < max3421e.h >
#include < usbhost.h >
#include < usb_ch9.h >
#include < Usb.h >
#include < usbhub.h >
#include < avr/pgmspace.h >
#include < address.h >
#include < hidboot.h >
#include < printhex.h >
#include < message.h >
#include < hexdump.h >
#include < parsetools.h >
//----------------------------------------//
struct {
    uint8_t buttons;
    int8_t x;
    int8_t y;
    int8_t wheel;   /* Not yet implemented */
} mouseReport;
//----------------------------------------//
class MouseRptParser : public MouseReportParser {
protected:
    virtual void OnMouseMove        (MOUSEINFO* mi);
    virtual void OnLeftButtonUp     (MOUSEINFO* mi);
    virtual void OnLeftButtonDown   (MOUSEINFO* mi);
    virtual void OnRightButtonUp    (MOUSEINFO* mi);
    virtual void OnRightButtonDown  (MOUSEINFO* mi);
    virtual void OnMiddleButtonUp   (MOUSEINFO* mi);
    virtual void OnMiddleButtonDown (MOUSEINFO* mi);
};
void MouseRptParser::OnMouseMove(MOUSEINFO* mi)        {ProcessDelta(mi);}
void MouseRptParser::OnLeftButtonUp(MOUSEINFO* mi)     {ProcessDelta(mi);}
void MouseRptParser::OnLeftButtonDown(MOUSEINFO* mi)   {ProcessDelta(mi);}
void MouseRptParser::OnRightButtonUp(MOUSEINFO* mi)    {ProcessDelta(mi);}
void MouseRptParser::OnRightButtonDown(MOUSEINFO* mi)  {ProcessDelta(mi);}
void MouseRptParser::OnMiddleButtonUp(MOUSEINFO* mi)   {ProcessDelta(mi);}
void MouseRptParser::OnMiddleButtonDown(MOUSEINFO* mi) {ProcessDelta(mi);}
//----------------------------------------//
// Usb / HID mouse
USB Usb;
HIDBoot Mouse(&Usb);
MouseRptParser Prs;
//-----------------------------------------------------------------------------------//
// LEDs/Pins
const byte CaptureSwitchSig_pin   = 2; // INT0 (don't move)
const byte PlaybackSwitchSig_pin  = 3; // INT1 (don't move)
const byte CaptureReadyLED_pin    = A5;
const byte PlaybackRunningLED_pin = A3;
// Memory/Capture/Playback
#define MAX_DELTAS 419
#define XmitDelay 10   // ms Delay between transmitted deltas (tuned)
const bool InvertAxis          = false;
volatile bool CaptureOn        = false;
volatile bool PlaybackEnabled  = false;
volatile uint16_t CaptureIndex = 0;
volatile bool CompressionMode  = false;
bool HaveCapturedAtLeastOne    = false;
MOUSEINFO CaptureBuffer[MAX_DELTAS] = {0};
//-----------------------------------------------------------------------------------//
void setup() {
    Serial.begin(9600);
    // LED Pin modes (use adjacent pin as ground)
    pinMode(CaptureReadyLED_pin,OUTPUT);
    pinMode(CaptureReadyLED_pin-1,OUTPUT);
    pinMode(PlaybackRunningLED_pin,OUTPUT);
    pinMode(PlaybackRunningLED_pin-1,OUTPUT);
    // Pin init (and GND set)
    digitalWrite(CaptureReadyLED_pin,LOW);
    digitalWrite(CaptureReadyLED_pin-1,LOW);    // GND
    digitalWrite(PlaybackRunningLED_pin,LOW);
    digitalWrite(PlaybackRunningLED_pin-1,LOW); // GND
    // Switches PWR/GND (Sig vals seen by INTR)
    pinMode(4,OUTPUT);
    pinMode(5,OUTPUT);
    pinMode(6,OUTPUT);
    pinMode(7,OUTPUT);
    digitalWrite(4,LOW);
    digitalWrite(5,HIGH);
    digitalWrite(6,HIGH);
    digitalWrite(7,LOW);
    attachInterrupt(0, CaptureSwitchISR, FALLING); // Falling edge because switch is on-off-on
    attachInterrupt(1, PlaybackSwitchISR, CHANGE); // Rising edge because switch is off-on-off
    // Init USB/Mouse
    if (Usb.Init() == -1) FreezeInErrorState();
    delay(200);
    Mouse.SetReportParser(0,(HIDReportParser*)&Prs);
    // Blink LEDs to show On
    for(uint8_t i=0;i<7;i++) {
        digitalWrite(CaptureReadyLED_pin,   (i%2)?HIGH:LOW);
        digitalWrite(PlaybackRunningLED_pin,(i%2)?HIGH:LOW);
        delay(500);
    }
}
//-----------------------------------------------------------------------------------//
void CaptureSwitchISR() {
    // Read value again to be sure and debounce
    bool SwitchKicked = true;
    for(uint8_t i=0;i<16;i++) {
        SwitchKicked &= (digitalRead(CaptureSwitchSig_pin)==0);// Switch is on-off-on, should fall to zero
    }
    if(SwitchKicked) {
        HaveCapturedAtLeastOne = true;  // So playback is possible
        CaptureOn = !CaptureOn;         // Toggle on ISR call
        digitalWrite(CaptureReadyLED_pin,CaptureOn?HIGH:LOW); // Update LED status
    }
}
//-----------------------------------------------------------------------------------//
void PlaybackSwitchISR() {
    // Read value again to be sure and debounce
    bool SwitchKicked = true;
    for(uint8_t i=0;i<16;i++) {
        SwitchKicked &= (digitalRead(PlaybackSwitchSig_pin)!=0);// Switch is off-on-off, should rise to one
    }
    if(SwitchKicked&&HaveCapturedAtLeastOne) {
        if(!CaptureOn) {
            PlaybackEnabled = true;  // Flag for polling (can't use delay in ISR)
            digitalWrite(PlaybackRunningLED_pin,HIGH);
        } else {   // Reset buffer index OR toggle CompressionMode
            if(CaptureIndex == 0) {
                CompressionMode = !CompressionMode;
                if(CompressionMode) BlinkLED(PlaybackRunningLED_pin,11); // Odd -> end with LOW
                else                BlinkLED(PlaybackRunningLED_pin,5);  // Odd -> end with LOW
            } else { // Reset buffer index
                CaptureIndex = 0;
                BlinkLED(CaptureReadyLED_pin,8); // Even -> end with HIGH
            }
        }
    }
}
//-----------------------------------------------------------------------------------//
void loop() {
    Usb.Task();
    if(PlaybackEnabled) PlayCapSeq(); // Playback poll:
    PlaybackEnabled = false;          // Playback once guarentee
}
//-----------------------------------------------------------------------------------//
void ProcessDelta(MOUSEINFO* Info) {
    if(CaptureOn) {
        if(CaptureIndex == MAX_DELTAS) {
            CaptureIndex=0;
            // Blink Capture LED (ending with HIGH)
            for(uint8_t i=0;i<8;i++) {
                digitalWrite(CaptureReadyLED_pin,(i%2)?HIGH:LOW);
                delay(250);
            }
        }
        // Capture  (Lets hope the compiler will fix this lazy coding and use some kind of temp)
        if((CompressionMode)&&(CaptureIndex!=0)&&
           (CaptureBuffer[CaptureIndex-1].bmLeftButton   == Info->bmLeftButton)&&
           (CaptureBuffer[CaptureIndex-1].bmRightButton  == Info->bmRightButton)&&
           (CaptureBuffer[CaptureIndex-1].bmMiddleButton == Info->bmMiddleButton)) {  
            // Capture compressed
            CaptureBuffer[CaptureIndex-1].dX += Info->dX;
            CaptureBuffer[CaptureIndex-1].dY += Info->dY;
        } else {
            CaptureBuffer[CaptureIndex].dX             = Info->dX;
            CaptureBuffer[CaptureIndex].dY             = Info->dY;
            CaptureBuffer[CaptureIndex].bmLeftButton   = Info->bmLeftButton;
            CaptureBuffer[CaptureIndex].bmRightButton  = Info->bmRightButton;
            CaptureBuffer[CaptureIndex].bmMiddleButton = Info->bmMiddleButton;
            CaptureIndex++;
        }
    }
    RepeateDelta(Info); // Always repeate (it's hard to know where the pointer will end up)
}
//-----------------------------------------------------------------------------------//
void PlayCapSeq() {
   uint16_t i;
   // Flicker before sending (ending with HIGH)
   for(i=0;i<20;i++) {
       digitalWrite(PlaybackRunningLED_pin,(i%2)?HIGH:LOW);
       delay(100);
   }
   // Send Buffer (delay built in)
   for(i=0;i < CaptureIndex;i++) RepeateDelta(&CaptureBuffer[i]);
   // Flicker after sending (ending with LOW)
   for(i=0;i<21;i++) {
       digitalWrite(PlaybackRunningLED_pin,(i%2)?HIGH:LOW);
       delay(100);
   }
}
//----------------------------------------------------------------------------------//
void RepeateDelta(MOUSEINFO* Info) {
    mouseReport.buttons = Info->bmLeftButton | ((Info->bmRightButton)<<1) | ((Info->bmMiddleButton)<<2);
    mouseReport.x = (InvertAxis)? -Info->dX : Info->dX;
    mouseReport.y = (InvertAxis)? -Info->dY : Info->dY;
    mouseReport.wheel = 0; // Not yet implemented in HEX!
    Serial.write((uint8_t*)&mouseReport, 4);
    delay(XmitDelay);
}
//-----------------------------------------------------------------------------------//
void BlinkLED(uint8_t pin, uint8_t count) {   // Blink w/o the delay function (used in the ISRs above)
    for(uint8_t i=0;i < count;i++) {
        for(uint16_t j=0;j<41668;j++) {
            digitalWrite(pin,(i%2)?HIGH:LOW); //(41667*(3us + ~3us) = 250ms
        }
    }
}
//-----------------------------------------------------------------------------------//
void FreezeInErrorState() {
    digitalWrite(CaptureReadyLED_pin, HIGH);
    digitalWrite(PlaybackRunningLED_pin, HIGH);
    while(true) {}
}
//-----------------------------------------------------------------------------------//