Use the HIDBoot class to parse the incoming data

This commit is contained in:
Kristian Lauszus 2013-11-24 23:51:45 +01:00
parent f783b97cb9
commit aba004b047
5 changed files with 186 additions and 80 deletions

View file

@ -18,18 +18,14 @@
#include "BTHID.h" #include "BTHID.h"
// To enable serial debugging see "settings.h" // To enable serial debugging see "settings.h"
//#define EXTRADEBUG // Uncomment to get even more debugging data //#define EXTRADEBUG // Uncomment to get even more debugging data
//#define PRINTREPORT // Uncomment to print the report send by the Wii controllers //#define PRINTREPORT // Uncomment to print the report send by the HID device
const uint8_t BUTTONS[] PROGMEM = { // Mouse buttons
0x04, // MIDDLE
0x02, // RIGHT
0, // Skip
0x01, // LEFT
};
BTHID::BTHID(BTD *p, bool pair, const char *pin) : BTHID::BTHID(BTD *p, bool pair, const char *pin) :
pBtd(p) // pointer to USB class instance - mandatory pBtd(p) // pointer to USB class instance - mandatory
{ {
for(uint8_t i = 0; i < epMUL; i++)
pRptParser[i] = NULL;
if (pBtd) if (pBtd)
pBtd->registerServiceClass(this); // Register it as a Bluetooth service pBtd->registerServiceClass(this); // Register it as a Bluetooth service
@ -196,22 +192,21 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
if (l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT if (l2capinbuf[8] == 0xA1) { // HID_THDR_DATA_INPUT
switch (l2capinbuf[9]) { switch (l2capinbuf[9]) {
case 0x01: // Keyboard events case 0x01: // Keyboard events
if (pRptParser[KEYBOARD_PARSER_ID]) {
uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);
pRptParser[KEYBOARD_PARSER_ID]->Parse((HID*)this, 0, (uint8_t) length, &l2capinbuf[10]);
}
break; break;
case 0x02: // Mouse events case 0x02: // Mouse events
case 0x1A: case 0x1A:
ButtonState = l2capinbuf[10]; if (pRptParser[MOUSE_PARSER_ID]) {
/*xAxis = l2capinbuf[11] | ((int16_t)l2capinbuf[12] << 8); uint16_t length = ((uint16_t)l2capinbuf[5] << 8 | l2capinbuf[4]);
yAxis = l2capinbuf[13] | ((int16_t)l2capinbuf[14] << 8); pRptParser[MOUSE_PARSER_ID]->Parse((HID*)this, 0, (uint8_t) length, &l2capinbuf[10]);
scroll = l2capinbuf[15] | ((int16_t)l2capinbuf[16] << 8);*/
if (ButtonState != OldButtonState) {
ButtonClickState = ButtonState & ~OldButtonState; // Update click state variable
OldButtonState = ButtonState;
} }
break; break;
case 0x03: case 0x03:
#ifdef DEBUG_USB_HOST #ifdef EXTRADEBUG
Notify(PSTR("\r\nChange mode event: "), 0x80); Notify(PSTR("\r\nChange mode event: "), 0x80);
D_PrintHex<uint8_t > (l2capinbuf[11], 0x80); D_PrintHex<uint8_t > (l2capinbuf[11], 0x80);
#endif #endif
@ -246,7 +241,7 @@ void BTHID::ACLData(uint8_t* l2capinbuf) {
void BTHID::L2CAP_task() { void BTHID::L2CAP_task() {
switch (l2cap_state) { switch (l2cap_state) {
/* These states are used if the Wiimote is the host */ /* These states are used if the HID device is the host */
case L2CAP_CONTROL_SUCCESS: case L2CAP_CONTROL_SUCCESS:
if (l2cap_config_success_control_flag) { if (l2cap_config_success_control_flag) {
#ifdef DEBUG_USB_HOST #ifdef DEBUG_USB_HOST
@ -382,29 +377,6 @@ void BTHID::Run() {
/* HID Commands */ /* HID Commands */
/************************************************************/ /************************************************************/
void BTHID::setProtocol() { void BTHID::setProtocol() {
uint8_t command = 0x71; // Set Protocol to "Report Protocol Mode", see HID specs page 33 uint8_t command = 0x70 | protocolMode; // Set Protocol, see HID specs page 33
pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]); pBtd->L2CAP_Command(hci_handle, &command, 1, control_scid[0], control_scid[1]);
}
/************************************************************/
/* BT HID Commands */
/************************************************************/
bool BTHID::getButtonPress(Button b) { // Return true when a button is pressed
return (bool)((ButtonState & pgm_read_byte(&BUTTONS[(uint8_t)b])));
}
bool BTHID::getButtonClick(Button b) { // Only return true when a button is clicked
uint8_t button = pgm_read_byte(&BUTTONS[(uint8_t)b]);
bool click = (ButtonClickState & button);
ButtonClickState &= ~button; // Clear "click" event
return click;
}
void BTHID::onInit() {
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
else {
// Do nothing
}
} }

52
BTHID.h
View file

@ -20,6 +20,7 @@
#include "BTD.h" #include "BTD.h"
#include "controllerEnums.h" #include "controllerEnums.h"
#include "hidboot.h"
/* Bluetooth L2CAP states for L2CAP_task() */ /* Bluetooth L2CAP states for L2CAP_task() */
#define L2CAP_WAIT 0 #define L2CAP_WAIT 0
@ -59,6 +60,10 @@
#define l2cap_connection_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_CONTROL_REQUEST) #define l2cap_connection_request_control_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_CONTROL_REQUEST)
#define l2cap_connection_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST) #define l2cap_connection_request_interrupt_flag (l2cap_event_flag & L2CAP_FLAG_CONNECTION_INTERRUPT_REQUEST)
#define KEYBOARD_PARSER_ID 0
#define MOUSE_PARSER_ID 1
#define epMUL 2
/** This BluetoothService class implements support for the HID keyboard and mice. */ /** This BluetoothService class implements support for the HID keyboard and mice. */
class BTHID : public BluetoothService { class BTHID : public BluetoothService {
public: public:
@ -84,38 +89,27 @@ public:
virtual void disconnect(); virtual void disconnect();
/**@}*/ /**@}*/
HIDReportParser *GetReportParser(uint8_t id) {
return pRptParser[id];
};
bool SetReportParser(uint8_t id, HIDReportParser *prs) {
pRptParser[id] = prs;
return true;
};
void setProtocolMode(uint8_t mode) {
protocolMode = mode;
};
/** True if a device is connected */ /** True if a device is connected */
bool connected; bool connected;
/** @name HID mouse functions */
/**
* getButtonPress(Button b) will return true as long as the button is held down.
*
* While getButtonClick(Button b) will only return it once.
*
* So you instance if you need to increase a variable once you would use getButtonClick(Button b),
* but if you need to drive a robot forward you would use getButtonPress(Button b).
*/
bool getButtonPress(Button b);
bool getButtonClick(Button b);
/**@}*/
/** @name HID mouse functions */
/*int16_t getXaxis() {
return xAxis;
}
int16_t getYaxis() {
return yAxis;
}
int16_t getScroll() {
return scroll;
}*/
/**@}*/
/** Call this to start the paring sequence with a controller */ /** Call this to start the paring sequence with a controller */
void pair(void) { void pair(void) {
if (pBtd) if (pBtd)
pBtd->pairWithHID(); pBtd->pairWithHID();
} };
/** /**
* Used to call your own function when the controller is successfully initialized. * Used to call your own function when the controller is successfully initialized.
@ -128,15 +122,21 @@ public:
private: private:
BTD *pBtd; // Pointer to BTD instance BTD *pBtd; // Pointer to BTD instance
HIDReportParser *pRptParser[epMUL];
/** Set report protocol. */ /** Set report protocol. */
void setProtocol(); void setProtocol();
uint8_t protocolMode;
/** /**
* Called when the controller is successfully initialized. * Called when the controller is successfully initialized.
* Use attachOnInit(void (*funcOnInit)(void)) to call your own function. * Use attachOnInit(void (*funcOnInit)(void)) to call your own function.
* This is useful for instance if you want to set the LEDs in a specific way. * This is useful for instance if you want to set the LEDs in a specific way.
*/ */
void onInit(); void onInit() {
if (pFuncOnInit)
pFuncOnInit(); // Call the user function
}
void (*pFuncOnInit)(void); // Pointer to function called in onInit() void (*pFuncOnInit)(void); // Pointer to function called in onInit()
void L2CAP_task(); // L2CAP state machine void L2CAP_task(); // L2CAP state machine

View file

@ -6,32 +6,42 @@
#include <BTHID.h> #include <BTHID.h>
#include <usbhub.h> #include <usbhub.h>
#include "KeyboardParser.h"
#include "MouseParser.h"
USB Usb; USB Usb;
//USBHub Hub1(&Usb); // Some dongles have a hub inside //USBHub Hub1(&Usb); // Some dongles have a hub inside
BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so BTD Btd(&Usb); // You have to create the Bluetooth Dongle instance like so
/* You can create the instance of the class in two ways */ /* You can create the instance of the class in two ways */
BTHID hid(&Btd, PAIR, "0000"); // This will start an inquiry and then pair with your Wiimote - you only have to do this once // This will start an inquiry and then pair with your device - you only have to do this once
//BTHID hid(&Btd); // After that you can simply create the instance like so and then press any button on the Wiimote // If you are using a Bluetooth keyboard, then you should type in the password on the keypad and then press enter
BTHID hid(&Btd, PAIR, "0000");
// After that you can simply create the instance like so and then press any button on the device
//BTHID hid(&Btd);
KbdRptParser keyboardPrs;
MouseRptParser mousePrs;
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection while (!Serial); // Wait for serial port to connect - used on Leonardo, Teensy and other boards with built-in USB CDC serial connection
if (Usb.Init() == -1) { if (Usb.Init() == -1) {
Serial.print(F("\r\nOSC did not start")); Serial.print(F("\r\nOSC did not start"));
while (1); //halt while (1); // Halt
} }
hid.SetReportParser(KEYBOARD_PARSER_ID, (HIDReportParser*)&keyboardPrs);
hid.SetReportParser(MOUSE_PARSER_ID, (HIDReportParser*)&mousePrs);
// If "Boot Protocol Mode" does not work, then try "Report Protocol Mode"
// If that does not work either, then uncomment PRINTREPORT in BTHID.cpp to see the raw report
hid.setProtocolMode(HID_BOOT_PROTOCOL); // Boot Protocol Mode
//hid.setProtocolMode(HID_RPT_PROTOCOL); // Report Protocol Mode
Serial.print(F("\r\nHID Bluetooth Library Started")); Serial.print(F("\r\nHID Bluetooth Library Started"));
} }
void loop() { void loop() {
Usb.Task(); Usb.Task();
if (hid.connected) {
if (hid.getButtonClick(LEFT)) // Print mouse buttons
Serial.println(F("Left"));
if (hid.getButtonClick(RIGHT))
Serial.println(F("Right"));
if (hid.getButtonClick(MIDDLE))
Serial.println(F("Middle"));
}
} }

View file

@ -0,0 +1,78 @@
#ifndef __kbdrptparser_h_
#define __kbdrptparser_h_
class KbdRptParser : public KeyboardReportParser {
private:
void PrintKey(uint8_t mod, uint8_t key);
protected:
virtual void OnControlKeysChanged(uint8_t before, uint8_t after);
virtual void OnKeyDown(uint8_t mod, uint8_t key);
virtual void OnKeyUp(uint8_t mod, uint8_t key);
virtual void OnKeyPressed(uint8_t key);
};
void KbdRptParser::PrintKey(uint8_t m, uint8_t key) {
MODIFIERKEYS mod;
*((uint8_t*)&mod) = m;
Serial.print((mod.bmLeftCtrl == 1) ? F("C") : F(" "));
Serial.print((mod.bmLeftShift == 1) ? F("S") : F(" "));
Serial.print((mod.bmLeftAlt == 1) ? F("A") : F(" "));
Serial.print((mod.bmLeftGUI == 1) ? F("G") : F(" "));
Serial.print(F(" >"));
PrintHex<uint8_t>(key, 0x80);
Serial.print(F("< "));
Serial.print((mod.bmRightCtrl == 1) ? F("C") : F(" "));
Serial.print((mod.bmRightShift == 1) ? F("S") : F(" "));
Serial.print((mod.bmRightAlt == 1) ? F("A") : F(" "));
Serial.println((mod.bmRightGUI == 1) ? F("G") : F(" "));
};
void KbdRptParser::OnKeyDown(uint8_t mod, uint8_t key) {
Serial.print(F("DN "));
PrintKey(mod, key);
uint8_t c = OemToAscii(mod, key);
if (c)
OnKeyPressed(c);
}
void KbdRptParser::OnControlKeysChanged(uint8_t before, uint8_t after) {
MODIFIERKEYS beforeMod;
*((uint8_t*)&beforeMod) = before;
MODIFIERKEYS afterMod;
*((uint8_t*)&afterMod) = after;
if (beforeMod.bmLeftCtrl != afterMod.bmLeftCtrl)
Serial.println(F("LeftCtrl changed"));
if (beforeMod.bmLeftShift != afterMod.bmLeftShift)
Serial.println(F("LeftShift changed"));
if (beforeMod.bmLeftAlt != afterMod.bmLeftAlt)
Serial.println(F("LeftAlt changed"));
if (beforeMod.bmLeftGUI != afterMod.bmLeftGUI)
Serial.println(F("LeftGUI changed"));
if (beforeMod.bmRightCtrl != afterMod.bmRightCtrl)
Serial.println(F("RightCtrl changed"));
if (beforeMod.bmRightShift != afterMod.bmRightShift)
Serial.println(F("RightShift changed"));
if (beforeMod.bmRightAlt != afterMod.bmRightAlt)
Serial.println(F("RightAlt changed"));
if (beforeMod.bmRightGUI != afterMod.bmRightGUI)
Serial.println(F("RightGUI changed"));
}
void KbdRptParser::OnKeyUp(uint8_t mod, uint8_t key) {
Serial.print(F("UP "));
PrintKey(mod, key);
}
void KbdRptParser::OnKeyPressed(uint8_t key) {
Serial.print(F("ASCII: "));
Serial.println((char)key);
};
#endif

View file

@ -0,0 +1,46 @@
#ifndef __mouserptparser_h__
#define __mouserptparser_h__
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) {
Serial.print(F("dx="));
Serial.print(mi->dX, DEC);
Serial.print(F(" dy="));
Serial.println(mi->dY, DEC);
};
void MouseRptParser::OnLeftButtonUp(MOUSEINFO *mi) {
Serial.println(F("L Butt Up"));
};
void MouseRptParser::OnLeftButtonDown(MOUSEINFO *mi) {
Serial.println(F("L Butt Dn"));
};
void MouseRptParser::OnRightButtonUp(MOUSEINFO *mi) {
Serial.println(F("R Butt Up"));
};
void MouseRptParser::OnRightButtonDown(MOUSEINFO *mi) {
Serial.println(F("R Butt Dn"));
};
void MouseRptParser::OnMiddleButtonUp(MOUSEINFO *mi) {
Serial.println(F("M Butt Up"));
};
void MouseRptParser::OnMiddleButtonDown(MOUSEINFO *mi) {
Serial.println(F("M Butt Dn"));
};
#endif