Skip to content

Instantly share code, notes, and snippets.

@lpereira
Created September 16, 2017 05:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lpereira/7934df291b7bf1d83e889445afe937a7 to your computer and use it in GitHub Desktop.
Save lpereira/7934df291b7bf1d83e889445afe937a7 to your computer and use it in GitHub Desktop.
/* Based on code by user DorianRudolph on GitHub
https://gist.github.com/DorianRudolph/ca283dfdfd185bc812b7
*/
/*
TODO:
- use menu key as a Fn key to enable multimedia control
- Fn+F1 = toggle mute
- Fn+F2 = vol up
- Fn+F3 = vol dn
- Fn+F5 = prev track
- Fn+F6 = next track
- Fn+F12 = toggle mouse movement (mx+1, mx-1 every second)
*/
#include <Mouse.h>
#include <Keyboard.h>
static const uint8_t PROGMEM keymap_tbl[256] = {
0, 66, 0, 62, 60, 58, 59, 69, 0, 67, 65, 63, 61, 43, 53, 0, 0, 226,
225, 0, 224, 20, 30, 0, 0, 0, 29, 22, 4, 26, 31, 0, 0, 6, 27, 7,
8, 33, 32, 0, 0, 44, 25, 9, 23, 21, 34, 0, 0, 17, 5, 11, 10, 28,
35, 0, 0, 0, 16, 13, 24, 36, 37, 0, 0, 54, 14, 12, 18, 39, 38, 0,
0, 55, 56, 15, 51, 19, 45, 0, 0, 0, 52, 0, 47, 46, 0, 0, 57, 229,
40, 48, 0, 49, 0, 0, 0, 100, 0, 0, 0, 0, 42, 0, 0, 89, 0, 92,
95, 0, 0, 0, 98, 99, 90, 93, 94, 96, 41, /*scrlk*/71, 68, 87, 91, 86, 85, 97,
83, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
static const uint8_t PROGMEM keymap_ext_tbl[256] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 230, 0,
0, 228, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227, 0, 0, 0, 0, 0, 0,
0, 231, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 84, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 77, 0, 80, 74, 0, 0, 0, 73, 76,
81, 0, 79, 82, 0, 0, 0, 0, 78, 0, 70, 75, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0
};
class RingBuffer {
private:
uint8_t buffer[16];
uint8_t first{0}, last{0}, pop{0};
public:
void put(uint8_t b) {
if (pop == sizeof(buffer)) return;
buffer[last] = b;
last = (last + 1) % sizeof(buffer);
pop++;
}
uint8_t get() {
if (!pop) return 0;
uint8_t v = buffer[first];
first = (first + 1) % sizeof(buffer);
pop--;
return v;
}
uint8_t size() {
return pop;
}
bool empty() {
return pop == 0;
}
};
class PS2Device {
private:
const int dataPin, clkPin;
uint32_t prev_ms{0};
uint8_t sendBits{0};
uint8_t bitcount{0};
uint8_t incoming{0};
volatile uint8_t msg{0};
volatile uint8_t bitCount{0};
volatile uint8_t setBits{0};
protected:
RingBuffer buffer;
void beginBase();
public:
PS2Device(int dataPin_, int clkPin_)
: dataPin(dataPin_)
, clkPin(clkPin_) {}
void handleInterruptImpl();
uint8_t readByte() {
return buffer.get();
}
void writeByte(uint8_t msg);
bool available() {
return !buffer.empty();
}
};
class PS2Keyboard : public PS2Device {
private:
KeyReport report;
uint8_t leds{0};
bool send_leds{0};
void addReport(uint8_t k);
void removeReport(uint8_t k);
public:
PS2Keyboard(int dataPin_, int clkPin_) : PS2Device(dataPin_, clkPin_) {}
void loop();
void begin() {
beginBase();
}
void toggleLED(int led);
static void handleInterrupt();
};
class PS2Mouse : public PS2Device {
private:
bool gotDataReportAck{false};
char lastState{0};
void controlUsbMouse(int button, int mask);
public:
PS2Mouse(int dataPin_, int clkPin_) : PS2Device(dataPin_, clkPin_) {}
void loop();
void begin();
static void handleInterrupt();
};
PS2Keyboard ps2Keyboard(4, 3);
PS2Mouse ps2Mouse(5, 2);
void setup() {
Serial.begin(9600);
ps2Keyboard.begin();
Keyboard.begin();
ps2Mouse.begin();
Mouse.begin();
}
void loop() {
if (ps2Keyboard.available())
ps2Keyboard.loop();
if (ps2Mouse.available())
ps2Mouse.loop();
}
void PS2Mouse::begin() {
beginBase();
writeByte(0xF4); // Enable data reporting
}
void PS2Mouse::handleInterrupt() {
ps2Mouse.handleInterruptImpl();
}
void PS2Mouse::loop() {
if (!gotDataReportAck) {
gotDataReportAck = readByte() == 0xFA;
return;
}
if (buffer.size() != 3) return;
int8_t btnState = readByte();
int8_t mx = readByte();
int8_t my = readByte();
if (!(btnState & 1 << 3)) return; /* this bit should be always 1 */
if (Mouse.isPressed(MOUSE_MIDDLE) && my != 0)
Mouse.move(0, 0, my);
else if (mx || my)
Mouse.move(mx, -my, 0);
if (btnState != lastState) {
lastState = btnState;
controlUsbMouse(MOUSE_LEFT, 1 << 0);
controlUsbMouse(MOUSE_RIGHT, 1 << 1);
controlUsbMouse(MOUSE_MIDDLE, 1 << 2);
}
}
void PS2Mouse::controlUsbMouse(int button, int mask)
{
if (lastState & mask)
Mouse.press(button);
else if (Mouse.isPressed(button))
Mouse.release(button);
}
void PS2Keyboard::handleInterrupt() {
ps2Keyboard.handleInterruptImpl();
}
void PS2Keyboard::toggleLED(int led)
{
leds ^= 1 << led;
send_leds = true;
writeByte(0xED);
}
void PS2Keyboard::loop() {
static bool brk, ext;
static uint8_t skip;
uint8_t k;
k = ps2Keyboard.readByte();
if (!k) return;
if (skip) {
--skip;
return;
}
if (k == 0xE0) {
ext = true;
return;
}
if (k == 0xF0) {
brk = true;
return;
}
if (k == 0xFA) {
if (send_leds) {
send_leds = false;
writeByte(leds);
}
return;
}
if (k == 0xE1) {
k = 72;
skip = 7;
brk = true;
addReport(k);
Keyboard.sendReport(&report);
} else {
k = pgm_read_byte(ext ? &keymap_ext_tbl[k] : &keymap_tbl[k]);
}
if (k) {
if (brk) {
removeReport(k);
switch (k) {
case 71: toggleLED(0); break;
case 83: toggleLED(1); break;
case 57: toggleLED(2); break;
}
} else {
addReport(k);
}
Keyboard.sendReport(&report);
}
brk = false;
ext = false;
}
void PS2Keyboard::addReport(uint8_t k) {
if (k >= 224) {
report.modifiers |= 1 << (k - 224);
return;
}
for (uint8_t i = 0; i < 6; i++) {
if (report.keys[i] == k) return;
}
for (uint8_t i = 0; i < 6; ++i) {
if (report.keys[i] == 0) {
report.keys[i] = k;
break;
}
}
}
void PS2Keyboard::removeReport(uint8_t k) {
if (k >= 224) {
report.modifiers &= ~(1 << (k - 224));
return;
}
for (uint8_t i = 0; i < 6; ++i) {
if (report.keys[i] == k) {
report.keys[i] = 0;
return;
}
}
}
void PS2Device::beginBase()
{
if (this == &ps2Keyboard)
attachInterrupt(digitalPinToInterrupt(clkPin), PS2Keyboard::handleInterrupt, FALLING);
else if (this == &ps2Mouse)
attachInterrupt(digitalPinToInterrupt(clkPin), PS2Mouse::handleInterrupt, FALLING);
pinMode(clkPin, INPUT_PULLUP);
pinMode(dataPin, INPUT_PULLUP);
}
void PS2Device::writeByte(uint8_t m) {
noInterrupts();
pinMode(clkPin, OUTPUT);
digitalWrite(clkPin, LOW);
delayMicroseconds(60);
pinMode(clkPin, INPUT_PULLUP);
msg = m;
bitCount = 0;
setBits = 0;
sendBits = 12;
pinMode(dataPin, OUTPUT);
digitalWrite(dataPin, LOW);
interrupts();
}
void PS2Device::handleInterruptImpl()
{
uint32_t now_ms;
uint8_t n, val;
if (!sendBits) {
val = digitalRead(dataPin);
now_ms = millis();
if (now_ms - prev_ms > 1000) {
bitcount = 0;
incoming = 0;
}
prev_ms = now_ms;
n = bitcount - 1;
if (n <= 7) {
incoming |= (val << n);
}
bitcount++;
if (bitcount == 11) {
buffer.put(incoming);
bitcount = 0;
incoming = 0;
}
} else {
--sendBits;
uint8_t b = bitCount - 1;
if (b == 8) {
digitalWrite(dataPin, !(setBits & 1));
} else if (b == 9) {
pinMode(dataPin, INPUT_PULLUP);
} else if (b < 8) {
bool bt = (msg >> b) & 1;
digitalWrite(dataPin, bt);
setBits += bt;
}
++bitCount;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment