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);
}
}
