Skip to content

Instantly share code, notes, and snippets.

@cheery
Created July 10, 2016 17:32
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 cheery/10c7b2a55ceb8353b8f5a5dfb2f59e84 to your computer and use it in GitHub Desktop.
Save cheery/10c7b2a55ceb8353b8f5a5dfb2f59e84 to your computer and use it in GitHub Desktop.
Keyboard driver to my yet unnamed keyboard
// Keyboard firmware, for a custom keyboard that is yet unnamed.
#include <HID.h>
#if !defined(_USING_HID)
#error HID required
#endif
// I didn't stop looking what the HID report descriptor here means.
// It looks like it describes the shape of my keyboard report and the ID
// of the report.
// If you wonder what each thing means in that list, go fetch the USB spec.
static const uint8_t _hidReportDescriptor[] PROGMEM = {
// Keyboard
0x05, 0x01, // USAGE_PAGE (Generic Desktop) // 47
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x85, 0x02, // REPORT_ID (2)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (1)
0x75, 0x08, // REPORT_SIZE (8)
0x81, 0x03, // INPUT (Cnst,Var,Abs)
0x95, 0x06, // REPORT_COUNT (6)
0x75, 0x08, // REPORT_SIZE (8)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x80, // LOGICAL_MAXIMUM (0x80, was 0x65, consider change USAGE_MAXIMUM too when you change this)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x80, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0, // END_COLLECTION
};
typedef struct
{
uint8_t modifiers;
uint8_t reserved;
uint8_t keys[6];
} KeyReport;
// I'm not sure whether these things are required to be wrapped into this silly small class.
// There was not much pressure to reduce code smells so I left it in here when I got it working.
class USBKeyboard {
private:
KeyReport keyReport;
public:
USBKeyboard(void) {
static HIDSubDescriptor node(_hidReportDescriptor, sizeof(_hidReportDescriptor));
HID().AppendDescriptor(&node);
keyReport.modifiers = 0;
keyReport.reserved = 0;
keyReport.keys[0] = 0;
keyReport.keys[1] = 0;
keyReport.keys[2] = 0;
keyReport.keys[3] = 0;
keyReport.keys[4] = 0;
keyReport.keys[5] = 0;
}
void sendReport() {
HID().SendReport(2,&keyReport,sizeof(KeyReport));
}
void begin() {
}
void end() {
}
void press(int k) {
if (0xE0 <= k && k <= 0xE8) {
keyReport.modifiers |= 1 << (k - 0xE0);
sendReport();
return;
}
if (keyReport.keys[0] != k && keyReport.keys[1] != k &&
keyReport.keys[2] != k && keyReport.keys[3] != k &&
keyReport.keys[4] != k && keyReport.keys[5] != k) {
for (int i=0; i<6; i++) {
if (keyReport.keys[i] == 0x00) {
keyReport.keys[i] = k;
break;
}
}
}
sendReport();
}
void release(int k) {
if (0xE0 <= k && k <= 0xE8) {
keyReport.modifiers &= ~(1 << (k - 0xE0));
sendReport();
return;
}
for (int i=0; i<6; i++) {
if (0 != k && keyReport.keys[i] == k) {
keyReport.keys[i] = 0x00;
}
}
sendReport();
}
};
USBKeyboard usb_keyboard;
// Keys are wired in a matrix, which is being rapidly polled every moment.
// Row pins are normally set high, and during scanning
// each pin lowers momentarily for reading.
// The column pins are configured with pullup so
// we don't need external pull-up resistors in our circuit.
#define COLC 7
#define ROWC 6
const int rows[ROWC] = {0, 1, 2, 3, 4, 5};
const int cols[COLC] = {6, 7, 8, 9, 10, 11, 12};
// http://www.mindrunway.ru/IgorPlHex/USBKeyScan.pdf
// https://www.arduino.cc/en/Reference/KeyboardModifiers
#define MLCT 0xE0
#define MLSH 0xE1
#define MLAL 0xE2
#define MLGU 0xE3
// We need the mapping because our keys do not directly map to ascii.
const int mapping[ROWC*COLC] = {
0x2A, 0x64, 0x35, 0x68, 0x69, 0x2E, MLCT,
0x4C, 0x31, 0x6A, 0x6B, 0x6C, 0x6D, MLAL,
0x29, 0x1E, 0x1F, 0x20, 0x21, 0x22, MLGU,
0x2B, 0x14, 0x1A, 0x08, 0x15, 0x17, 0x4A,
0x39, 0x04, 0x16, 0x07, 0x09, 0x0A, 0x4D,
MLSH, 0x1D, 0x1B, 0x06, 0x19, 0x05, 0x00,
};
// And we need a state table because the keyboard protocol must
// send state changes rather than states themselves.
int state[ROWC*COLC];
void setup() {
//Serial.begin(9600);
usb_keyboard.begin();
for (int x = 0; x < COLC; x++) {
pinMode(cols[x], INPUT_PULLUP);
}
for (int y = 0; y < ROWC; y++) {
pinMode(rows[y], OUTPUT);
digitalWrite(rows[y], HIGH);
}
for (int y = 0; y < ROWC; y++) {
for (int x = 0; x < COLC; x++) {
state[y*COLC + x] = HIGH;
}
}
}
void loop() {
for (int y = 0; y < ROWC; y++) {
digitalWrite(rows[y], LOW);
delayMicroseconds(500); // wait a little bit for signal to stabilize.
for (int x = 0; x < COLC; x++) {
int now = digitalRead(cols[x]);
int was = state[y*COLC + x];
if (now != was) {
if (now == HIGH) {
//Serial.print(mapping[y*COLC + x], HEX);
//Serial.println("H");
usb_keyboard.release(mapping[y*COLC + x]);
} else {
usb_keyboard.press(mapping[y*COLC + x]);
}
}
state[y*COLC + x] = now;
}
digitalWrite(rows[y], HIGH);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment