Compare commits
2 commits
c7de79c4e5
...
2ab8d0a68a
Author | SHA1 | Date | |
---|---|---|---|
2ab8d0a68a | |||
ec0f7eda73 |
1 changed files with 221 additions and 103 deletions
324
smol_gkos.ino
324
smol_gkos.ino
|
@ -3,6 +3,10 @@
|
|||
#define TRACKBALL_ADDR 0x0A
|
||||
#define TRACKBALL_REG_LEFT 0x04
|
||||
|
||||
// using my own masks here since the inbuilt ones conflict with arrow keys etc.
|
||||
#define MASK_SHIFT 0x100
|
||||
#define MASK_ALTGR 0x200
|
||||
|
||||
const byte PIN_A = 33;
|
||||
const byte PIN_B = 32;
|
||||
const byte PIN_C = 31;
|
||||
|
@ -21,25 +25,24 @@ const byte PIN_LED3 = 3;
|
|||
|
||||
const byte PIN_BALL_SDA = 18;
|
||||
const byte PIN_BALL_SCL = 19;
|
||||
const byte PIN_BALL_INT = 11;
|
||||
|
||||
enum Button { A, B, C, D, E, F, MouseUL, MouseUR, MouseDL, MouseDR };
|
||||
const int button_pins[10] = {
|
||||
PIN_A, PIN_B, PIN_C, PIN_D, PIN_E, PIN_F,
|
||||
PIN_MOUSE_UL, PIN_MOUSE_UR, PIN_MOUSE_DL, PIN_MOUSE_DR
|
||||
};
|
||||
const int debounce_time = 400;
|
||||
const int debounce_time = 50;
|
||||
int button_timers[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
// different actions that the keyboard can take
|
||||
// - print a single character
|
||||
// - press (and release) a key
|
||||
// - toggle a modifier
|
||||
enum ActionType { PrintCharacter, PressKey, ToggleModifier };
|
||||
enum ActionType { PressKey, ToggleModifier };
|
||||
|
||||
const char button_characters[6] = { 'a', 'b', 'c', 'd', 'e', 'f' };
|
||||
const char button_characters_shifted[6] = { 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||
const char button_characters_symbol[6] = { '1', '2', '3', '4', '5', '6' };
|
||||
const int button_characters[6] = { KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F };
|
||||
const int button_characters_symbol[6] = {
|
||||
KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6
|
||||
};
|
||||
|
||||
/*
|
||||
backspace, space (keep those two first since they overlap with the rest )
|
||||
|
@ -62,44 +65,27 @@ const byte chord_buttons[24] = {
|
|||
const int chord_targets[32] = {
|
||||
KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME,
|
||||
KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END,
|
||||
'g', 'h', 'i', 'j',
|
||||
'k', 'l', 'm', 'n',
|
||||
'o', 'p', 'q', 'r',
|
||||
's', 't', 'u', 'v',
|
||||
'w', 'x', 'y', 'z',
|
||||
KEY_G, KEY_H, KEY_I, KEY_J,
|
||||
KEY_K, KEY_L, KEY_M, KEY_N,
|
||||
KEY_O, KEY_P, KEY_Q, KEY_R,
|
||||
KEY_S, KEY_T, KEY_U, KEY_V,
|
||||
KEY_W, KEY_X, KEY_Y, KEY_Z,
|
||||
0, 0, 0, 0
|
||||
};
|
||||
const int chord_targets_shifted[32] = {
|
||||
KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME,
|
||||
KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END,
|
||||
'G', 'H', 'I', 'J',
|
||||
'K', 'L', 'M', 'N',
|
||||
'O', 'P', 'Q', 'R',
|
||||
'S', 'T', 'U', 'V',
|
||||
'W', 'X', 'Y', 'Z',
|
||||
0, 0, 0, 0
|
||||
};
|
||||
// TODO: check that all the symbols work
|
||||
// TODO: match symbols to keypresses in the keyboard layout
|
||||
const int chord_targets_symbol[32] = {
|
||||
KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME,
|
||||
KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END,
|
||||
'0', '7', '8', '9',
|
||||
'#', '@', '½', '&',
|
||||
'+', '%', '=', '^',
|
||||
'*', '$', '€', '£',
|
||||
'(', '[', '<', '{',
|
||||
')', ']', '>', '}'
|
||||
};
|
||||
const byte chord_target_types[32] = {
|
||||
1, 1, 1, 1,
|
||||
1, 1, 1, 1,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0
|
||||
KEY_0, KEY_7, KEY_8, KEY_9,
|
||||
// # @ ½ &
|
||||
KEY_BACKSLASH, KEY_QUOTE + MASK_SHIFT, KEY_5 + MASK_ALTGR, KEY_7 + MASK_SHIFT,
|
||||
// + % = ^
|
||||
KEY_EQUAL + MASK_SHIFT, KEY_5 + MASK_SHIFT, KEY_EQUAL, KEY_6 + MASK_SHIFT,
|
||||
// * $ € £
|
||||
KEY_8 + MASK_SHIFT, KEY_4 + MASK_SHIFT, KEY_4 + MASK_ALTGR, KEY_3 + MASK_SHIFT,
|
||||
// ( [ < {
|
||||
KEY_9 + MASK_SHIFT, KEY_LEFT_BRACE, KEY_COMMA + MASK_SHIFT, KEY_LEFT_BRACE + MASK_SHIFT,
|
||||
// ) ] > }
|
||||
KEY_0 + MASK_SHIFT, KEY_RIGHT_BRACE, KEY_PERIOD + MASK_SHIFT, KEY_RIGHT_BRACE + MASK_SHIFT,
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -107,25 +93,34 @@ dash, backslash, slash, apostrophe, comma, exclamation point, question mark,
|
|||
period, up, down, page up, page down, shift, symbol shift, switch keyset,
|
||||
escape, control, alt, delete, insert, tab, enter
|
||||
*/
|
||||
// TODO: add the middle specials for function keys instead of the weird symbols
|
||||
const byte specials[22] = {
|
||||
17, 51, 30, 10, 20, 12, 33, 34, 9, 36, 27,
|
||||
54, 18, 45, 63, 31, 47, 55, 62, 43, 61, 59
|
||||
};
|
||||
enum Modifier { Shift, SymbolShift, Keyset, Control, Alt };
|
||||
const int special_action_targets[22] = {
|
||||
'-', '\\', '/', '\'', ',', '!', '?', '/', KEY_UP, KEY_DOWN, KEY_PAGE_UP,
|
||||
KEY_PAGE_DOWN, Modifier::Shift, Modifier::SymbolShift, Modifier::Keyset,
|
||||
// - \ / '
|
||||
KEY_MINUS, KEY_MINUS + MASK_ALTGR, KEY_SLASH, KEY_QUOTE,
|
||||
// , ! ? .
|
||||
KEY_COMMA, KEY_1 + MASK_SHIFT, KEY_SLASH + MASK_SHIFT, KEY_PERIOD,
|
||||
KEY_UP, KEY_DOWN, KEY_PAGE_UP, KEY_PAGE_DOWN,
|
||||
Modifier::Shift, Modifier::SymbolShift, Modifier::Keyset,
|
||||
KEY_ESC, Modifier::Control, Modifier::Alt, KEY_DELETE, KEY_INSERT,
|
||||
KEY_TAB, KEY_ENTER
|
||||
};
|
||||
const int special_action_targets_symbol[22] = {
|
||||
'_', '\`', 0, '\"', ';', '|', '~', ':', 0, 0, 0,
|
||||
// _ ` ´ "
|
||||
KEY_MINUS + MASK_SHIFT, KEY_TILDE, KEY_SEMICOLON + MASK_ALTGR, KEY_2 + MASK_SHIFT,
|
||||
// ; | ~ :
|
||||
KEY_SEMICOLON, KEY_TILDE + MASK_ALTGR, KEY_BACKSLASH + MASK_SHIFT, KEY_SEMICOLON + MASK_SHIFT,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0, 0
|
||||
};
|
||||
const byte special_action_target_types[22] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
byte key_pressed = 0;
|
||||
|
@ -137,9 +132,29 @@ bool mod_shift = false;
|
|||
bool mod_shift_lock = false;
|
||||
bool mod_symbol = false;
|
||||
bool mod_symbol_lock = false;
|
||||
// TODO: control and alt modifiers
|
||||
bool mod_control = false;
|
||||
bool mod_alt = false;
|
||||
bool mod_altgr = false;
|
||||
|
||||
IntervalTimer trackball_timer;
|
||||
volatile bool trackball_update = false;
|
||||
|
||||
byte mouse_up = 0;
|
||||
byte mouse_down = 0;
|
||||
byte mouse_left = 0;
|
||||
byte mouse_right = 0;
|
||||
bool mouse_button = false;
|
||||
|
||||
const int num_mouse_samples = 10;
|
||||
int current_mouse_sample = 0;
|
||||
int mouse_movement_sample = 0;
|
||||
float mouse_acceleration = 0.0f;
|
||||
const float mouse_accel_factor = 0.7f;
|
||||
|
||||
const int mouse_scroll_sensitivity = 6;
|
||||
int mouse_scroll_dist = 0;
|
||||
bool mouse_middle_pressed = false;
|
||||
bool mouse_scrolled = false;
|
||||
|
||||
void setup() {
|
||||
pinMode(PIN_A, INPUT_PULLUP);
|
||||
|
@ -158,35 +173,54 @@ void setup() {
|
|||
pinMode(PIN_LED2, OUTPUT);
|
||||
pinMode(PIN_LED3, OUTPUT);
|
||||
|
||||
Serial.begin(115200);
|
||||
Serial.println("helo");
|
||||
// poll trackball at 10kHz
|
||||
trackball_timer.begin(trackball_isr, 100);
|
||||
|
||||
Wire.begin();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
/*
|
||||
BUTTONS
|
||||
*/
|
||||
for (byte b = 0; b < 10; b++) {
|
||||
// button is pressed
|
||||
if (!digitalRead(button_pins[b])) {
|
||||
// increment button timer if it's not full
|
||||
if (button_timers[b] < debounce_time)
|
||||
button_timers[b]++;
|
||||
// update pressed gkos keys
|
||||
else if (b < 6) {
|
||||
key_pressed |= 1 << b;
|
||||
key_pressed_total |= 1 << b;
|
||||
button_timers[b]++;
|
||||
else {
|
||||
// update pressed gkos keys
|
||||
if (b < 6) {
|
||||
key_pressed |= 1 << b;
|
||||
key_pressed_total |= 1 << b;
|
||||
}
|
||||
// update pressed mouse keys
|
||||
else {
|
||||
switch (b) {
|
||||
case Button::MouseUL:
|
||||
Mouse.press(MOUSE_LEFT);
|
||||
break;
|
||||
case Button::MouseUR:
|
||||
Mouse.press(MOUSE_RIGHT);
|
||||
break;
|
||||
case Button::MouseDL:
|
||||
case Button::MouseDR:
|
||||
mouse_middle_pressed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// button is released
|
||||
else {
|
||||
// if the button had been debounced, it counts as a keypress
|
||||
// if the button had been debounced, it counts as a press
|
||||
if (button_timers[b] >= debounce_time) {
|
||||
// gkos key released
|
||||
if (b < 6) {
|
||||
key_pressed &= ~(1 << b);
|
||||
// gkos key was released, check if it completed a chord
|
||||
// clear temporary modifiers if a keypress was completed
|
||||
if (key_released(b))
|
||||
mod_shift = mod_symbol = mod_control = mod_alt = false;
|
||||
// check if it completed a chord
|
||||
key_released(b);
|
||||
update_leds();
|
||||
if (key_pressed == 0) {
|
||||
key_pressed_total = 0;
|
||||
|
@ -195,14 +229,74 @@ void loop() {
|
|||
chorded_pressed = false;
|
||||
}
|
||||
}
|
||||
// mouse button released
|
||||
else {
|
||||
switch (b) {
|
||||
case Button::MouseUL:
|
||||
Mouse.release(MOUSE_LEFT);
|
||||
break;
|
||||
case Button::MouseUR:
|
||||
Mouse.release(MOUSE_RIGHT);
|
||||
break;
|
||||
case Button::MouseDL:
|
||||
case Button::MouseDR:
|
||||
if (!mouse_scrolled)
|
||||
Mouse.click(MOUSE_MIDDLE);
|
||||
mouse_middle_pressed = mouse_scrolled = false;
|
||||
mouse_scroll_dist = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
button_timers[b] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
TRACKBALL
|
||||
*/
|
||||
noInterrupts();
|
||||
if (trackball_update) {
|
||||
Wire.beginTransmission(TRACKBALL_ADDR);
|
||||
Wire.write(TRACKBALL_REG_LEFT);
|
||||
Wire.endTransmission();
|
||||
|
||||
Wire.requestFrom(TRACKBALL_ADDR, 5);
|
||||
mouse_left = Wire.read();
|
||||
mouse_right = Wire.read();
|
||||
mouse_up = Wire.read();
|
||||
mouse_down = Wire.read();
|
||||
mouse_button = Wire.read();
|
||||
|
||||
int mouse_x = mouse_right - mouse_left;
|
||||
int mouse_y = mouse_down - mouse_up;
|
||||
|
||||
mouse_movement_sample += abs(mouse_x) + abs(mouse_y);
|
||||
current_mouse_sample++;
|
||||
if (current_mouse_sample > num_mouse_samples) {
|
||||
current_mouse_sample = 0;
|
||||
mouse_acceleration = 1.0f + mouse_movement_sample * mouse_accel_factor;
|
||||
mouse_movement_sample = 0;
|
||||
}
|
||||
|
||||
if (mouse_middle_pressed) {
|
||||
mouse_scroll_dist += mouse_y;
|
||||
if (abs(mouse_scroll_dist) > mouse_scroll_sensitivity) {
|
||||
Mouse.scroll(mouse_scroll_dist % mouse_scroll_sensitivity);
|
||||
mouse_scroll_dist %= mouse_scroll_sensitivity;
|
||||
mouse_scrolled = true;
|
||||
}
|
||||
} else {
|
||||
Mouse.move(mouse_x * mouse_acceleration, mouse_y * mouse_acceleration);
|
||||
}
|
||||
|
||||
trackball_update = false;
|
||||
}
|
||||
interrupts();
|
||||
}
|
||||
|
||||
// returns true if a keypress was completed
|
||||
bool key_released(byte key) {
|
||||
void key_released(byte key) {
|
||||
int target = 0;
|
||||
|
||||
/*
|
||||
|
@ -221,7 +315,7 @@ bool key_released(byte key) {
|
|||
if (is_special && !chord_used) {
|
||||
// only register a special keypress when all keys have been released
|
||||
if (key_pressed != 0)
|
||||
return false;
|
||||
return;
|
||||
|
||||
target = special_action_targets[special];
|
||||
if (mod_shift || mod_shift_lock || mod_symbol || mod_symbol_lock) {
|
||||
|
@ -230,13 +324,9 @@ bool key_released(byte key) {
|
|||
}
|
||||
|
||||
switch (special_action_target_types[special]) {
|
||||
case ActionType::PrintCharacter:
|
||||
Keyboard.print((char)target);
|
||||
return true;
|
||||
case ActionType::PressKey:
|
||||
Keyboard.press(target);
|
||||
Keyboard.release(target);
|
||||
return true;
|
||||
press_key(target);
|
||||
return;
|
||||
case ActionType::ToggleModifier:
|
||||
switch (target) {
|
||||
case Modifier::Shift:
|
||||
|
@ -249,24 +339,24 @@ bool key_released(byte key) {
|
|||
} else {
|
||||
mod_shift = true;
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
case Modifier::SymbolShift:
|
||||
mod_shift = mod_shift_lock = mod_symbol_lock = false;
|
||||
mod_symbol = !mod_symbol;
|
||||
return false;
|
||||
return;
|
||||
case Modifier::Keyset:
|
||||
mod_shift = mod_shift_lock = mod_symbol = false;
|
||||
mod_symbol_lock = !mod_symbol_lock;
|
||||
return false;
|
||||
return;
|
||||
case Modifier::Control:
|
||||
mod_control = !mod_control;
|
||||
return false;
|
||||
return;
|
||||
case Modifier::Alt:
|
||||
mod_alt = !mod_alt;
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -288,59 +378,78 @@ bool key_released(byte key) {
|
|||
if (!chorded_pressed && key_pressed == 0 &&
|
||||
key_pressed_total == chords[chord]) {
|
||||
target = chord_targets[chord * 4];
|
||||
if (mod_shift || mod_shift_lock)
|
||||
target = chord_targets_shifted[chord * 4];
|
||||
else if (mod_symbol || mod_symbol_lock)
|
||||
if (mod_symbol || mod_symbol_lock)
|
||||
target = chord_targets_symbol[chord * 4];
|
||||
|
||||
switch (chord_target_types[chord * 4]) {
|
||||
case ActionType::PrintCharacter:
|
||||
Keyboard.print((char)target);
|
||||
return true;
|
||||
case ActionType::PressKey:
|
||||
Keyboard.press(target);
|
||||
Keyboard.release(target);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
press_key(target);
|
||||
return;
|
||||
}
|
||||
// keys that can be chorded with
|
||||
for (byte b = 0; b < 3; b++) {
|
||||
if (key == chord_buttons[chord * 3 + b]) {
|
||||
target = chord_targets[chord * 4 + 1 + b];
|
||||
if (mod_shift || mod_shift_lock)
|
||||
target = chord_targets_shifted[chord * 4 + 1 + b];
|
||||
else if (mod_symbol || mod_symbol_lock)
|
||||
if (mod_symbol || mod_symbol_lock)
|
||||
target = chord_targets_symbol[chord * 4 + 1 + b];
|
||||
|
||||
// erase key from total so you can hold the chord down for
|
||||
// multiple chorded keypresses
|
||||
key_pressed_total &= ~(1 << key);
|
||||
chorded_pressed = true;
|
||||
switch (chord_target_types[chord * 4 + 1 + b]) {
|
||||
case ActionType::PrintCharacter:
|
||||
Keyboard.print((char)target);
|
||||
return true;
|
||||
case ActionType::PressKey:
|
||||
Keyboard.press(target);
|
||||
Keyboard.release(target);
|
||||
return true;
|
||||
}
|
||||
press_key(target);
|
||||
return;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
REGULAR KEYS
|
||||
*/
|
||||
if (mod_shift || mod_shift_lock)
|
||||
Keyboard.print(button_characters_shifted[key]);
|
||||
else if (mod_symbol || mod_symbol_lock)
|
||||
Keyboard.print(button_characters_symbol[key]);
|
||||
// keypress
|
||||
if (mod_symbol || mod_symbol_lock)
|
||||
press_key(button_characters_symbol[key]);
|
||||
else
|
||||
Keyboard.print(button_characters[key]);
|
||||
return true;
|
||||
press_key(button_characters[key]);
|
||||
return;
|
||||
}
|
||||
|
||||
void press_key(int key) {
|
||||
// check if modifiers need to be forced
|
||||
if (key & MASK_SHIFT) {
|
||||
mod_shift = true;
|
||||
key &= ~(MASK_SHIFT);
|
||||
}
|
||||
if (key & MASK_ALTGR) {
|
||||
mod_altgr = true;
|
||||
key &= ~(MASK_ALTGR);
|
||||
}
|
||||
|
||||
// modifiers
|
||||
if (mod_shift || mod_shift_lock)
|
||||
Keyboard.press(KEY_LEFT_SHIFT);
|
||||
if (mod_control)
|
||||
Keyboard.press(KEY_LEFT_CTRL);
|
||||
if (mod_alt)
|
||||
Keyboard.press(KEY_LEFT_ALT);
|
||||
if (mod_altgr)
|
||||
Keyboard.press(KEY_RIGHT_ALT);
|
||||
|
||||
// keypress
|
||||
Keyboard.press(key);
|
||||
Keyboard.release(key);
|
||||
|
||||
// release modifiers
|
||||
if (mod_shift || mod_shift_lock)
|
||||
Keyboard.release(KEY_LEFT_SHIFT);
|
||||
if (mod_control)
|
||||
Keyboard.release(KEY_LEFT_CTRL);
|
||||
if (mod_alt)
|
||||
Keyboard.release(KEY_LEFT_ALT);
|
||||
if (mod_altgr)
|
||||
Keyboard.release(KEY_RIGHT_ALT);
|
||||
|
||||
// clear temporary modifiers
|
||||
mod_shift = mod_symbol = mod_control = mod_alt = mod_altgr = false;
|
||||
}
|
||||
|
||||
void update_leds() {
|
||||
|
@ -348,3 +457,12 @@ void update_leds() {
|
|||
digitalWrite(PIN_LED2, mod_symbol || mod_symbol_lock);
|
||||
digitalWrite(PIN_LED3, mod_control || mod_alt);
|
||||
}
|
||||
|
||||
int sign(int num) {
|
||||
if (num >= 0) return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void trackball_isr() {
|
||||
trackball_update = true;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue