So the whole thing is made in Arduino, one Attiny85 board acting as a USB client, a HID keyboard, and another board, a Nano (ATMega 328P) on the lookout for button presses. The two communicate via serial.
And the source code is published on github here (the USB-handling attiny85) and here (and the buttons-handling nano).
Below is the very first, unoptimized implementation, barely fitting onto the flash.
The Attiny85 ino file, the USB client
#include <SoftSerial_INT0.h> #include "DigiKeyboard.h" #define P_RX 2 #define P_TX 1 SoftSerial softSerial(P_RX, P_TX); void setup() { // put your setup code here, to run once: softSerial.begin(9600); DigiKeyboard.sendKeyStroke(0); } char incoming[32]; byte modifMask; void modifMask_check(byte m, byte b){ if (0 != (b & m)){ modifMask |= b; DigiKeyboard.sendKeyPress(0, modifMask); DigiKeyboard.delay(90); } } void digestIncoming(){ if (strlen(incoming) < 2){ return ; } if ('e' == incoming[1]){ char s[2]; s[0] = incoming[2]; s[1] = 0; DigiKeyboard.print(s); return ; } if ('S' == incoming[1]){ // char s[3]; s[0] = incoming[2]; s[1] = incoming[3]; s[2] = 0; byte k = strtol(s, NULL, 16); s[0] = incoming[4]; s[1] = incoming[5]; s[2] = 0; byte m = strtol(s, NULL, 16); DigiKeyboard.sendKeyStroke(k, m); } if ('s' == incoming[1]){ char s[3]; // sequentially press stuff, modifiers first s[0] = incoming[4]; s[1] = incoming[5]; s[2] = 0; byte m = strtol(s, NULL, 16); modifMask = 0; for (int j=0; j<8; j++){ modifMask_check(m, (1 << j)); } s[0] = incoming[2]; s[1] = incoming[3]; s[2] = 0; byte k = strtol(s, NULL, 16); DigiKeyboard.sendKeyPress(k, m); DigiKeyboard.delay(50); DigiKeyboard.sendKeyPress(0, 0); } } void loop() { // put your main code here, to run repeatedly: // this is generally not necessary but with some older systems it seems to // prevent missing the first character after a delay: uint32_t present = millis(); if (softSerial.available()){ // # // S(trike) (press+release) // KK key as hexascii // MM modifier as hexascii // enter or something // # // e(cho) // C(haracter to be echoed) // enter or something byte c = softSerial.read(); if ('#' == c){ incoming[0] = 0; } int lengo = strlen(incoming); if (lengo < sizeof(incoming)-2){ incoming[lengo++] = c; incoming[lengo] = 0; } if ((13 == c)||(10 == c)){ digestIncoming(); incoming[0] = 0; } } }
And the Nano checking the physical buttons.
#include <SoftwareSerial.h> #define PIN_MYSERIAL_RX 2 #define PIN_MYSERIAL_TX A0 SoftwareSerial mySerial(PIN_MYSERIAL_RX, PIN_MYSERIAL_TX); #define MOD_CONTROL_LEFT (1<<0) #define MOD_SHIFT_LEFT (1<<1) #define MOD_ALT_LEFT (1<<2) #define MOD_GUI_LEFT (1<<3) #define MOD_CONTROL_RIGHT (1<<4) #define MOD_SHIFT_RIGHT (1<<5) #define MOD_ALT_RIGHT (1<<6) #define MOD_GUI_RIGHT (1<<7) #define KEY_1 30 #define KEY_2 31 #define KEY_3 32 #define KEY_4 33 #define KEY_5 34 #define KEY_6 35 #define KEY_7 36 #define KEY_8 37 #define KEY_9 38 #define KEY_0 39 #define KEY_F1 58 #define KEY_F2 59 #define KEY_F3 60 #define KEY_F4 61 #define KEY_F5 62 #define KEY_F6 63 #define KEY_F7 64 #define KEY_F8 65 #define KEY_F9 66 #define KEY_F10 67 #define KEY_F11 68 #define KEY_F12 69 void setup(){ Serial.begin(9600); mySerial.begin(9600); pinMode(A1, INPUT_PULLUP); pinMode(A2, INPUT_PULLUP); pinMode(A3, INPUT_PULLUP); //pullup soldered in pinMode(A6, INPUT); pinMode(A7, INPUT); } uint32_t lastReceivedButtonAtMillis; int lastReceivedButton; void receiveButton(int b){ if ((millis() - lastReceivedButtonAtMillis > 1000) || (b!=lastReceivedButton)){ lastReceivedButton = b; char msg[16]; byte key = KEY_F12 - (b - 1); strcpy(msg, "#s"); if (key < 16){ strcat(msg, "0"); } itoa(key, msg+strlen(msg), 16); byte modifier=MOD_CONTROL_LEFT | MOD_SHIFT_LEFT | MOD_ALT_LEFT; if (modifier < 16){ strcat(msg, "0"); } itoa(modifier, msg+strlen(msg), 16); mySerial.println(msg); Serial.println(msg); lastReceivedButtonAtMillis = millis(); } } void loop(){ while (Serial.available()){ char c[2]; c[0] = Serial.read(); c[1] = 0; //echo if ((c[0] != 13)&&(c[0] != 10)){ mySerial.print("#e"); mySerial.println(c); } } if (LOW == digitalRead(A1)){ receiveButton(1); }; if (LOW == digitalRead(A2)){ receiveButton(2); }; if (LOW == digitalRead(A3)){ receiveButton(3); }; if (analogRead(A6) < 64){ receiveButton(4); } if (analogRead(A7) < 64){ receiveButton(5); } }