smol_gkos/smol_gkos.ino

469 lines
14 KiB
Arduino
Raw Normal View History

2021-02-27 21:27:34 +00:00
#include <Wire.h>
#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
2021-02-27 21:27:34 +00:00
const byte PIN_A = 33;
const byte PIN_B = 32;
const byte PIN_C = 31;
const byte PIN_D = 24;
const byte PIN_E = 25;
const byte PIN_F = 26;
const byte PIN_MOUSE_UL = 27;
const byte PIN_MOUSE_UR = 30;
const byte PIN_MOUSE_DL = 28;
const byte PIN_MOUSE_DR = 29;
const byte PIN_LED1 = 6;
const byte PIN_LED2 = 1;
const byte PIN_LED3 = 3;
const byte PIN_BALL_SDA = 18;
const byte PIN_BALL_SCL = 19;
enum Button { A, B, C, D, E, F, MouseUL, MouseUR, MouseDL, MouseDR };
2021-02-27 21:27:34 +00:00
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 = 50;
2021-02-27 21:27:34 +00:00
int button_timers[10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
// different actions that the keyboard can take
// - press (and release) a key
// - toggle a modifier
enum ActionType { PressKey, ToggleModifier };
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
};
2021-02-27 21:27:34 +00:00
/*
backspace, space (keep those two first since they overlap with the rest )
g, k, o, s, w, native
*/
const byte chords[8] = { 7, 56, 24, 48, 3, 6, 40, 5 };
// buttons that can be combined with a chord for a keypress
const byte chord_buttons[24] = {
Button::D, Button::E, Button::F,
Button::A, Button::B, Button::C,
Button::A, Button::B, Button::C,
Button::A, Button::B, Button::C,
Button::D, Button::E, Button::F,
Button::D, Button::E, Button::F,
Button::A, Button::B, Button::C,
Button::D, Button::E, Button::F,
};
// keypresses for chords in sets of 4
// index 0 is the chord on its own, 1-3 correspond to chord_buttons
const int chord_targets[32] = {
KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME,
KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END,
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_symbol[32] = {
KEY_BACKSPACE, KEY_LEFT, KEY_LEFT, KEY_HOME,
KEY_SPACE, KEY_RIGHT, KEY_RIGHT, KEY_END,
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,
};
2021-02-27 21:27:34 +00:00
/*
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
2021-02-27 21:27:34 +00:00
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_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] = {
// _ ` ´ "
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, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0
};
2021-02-27 21:27:34 +00:00
byte key_pressed = 0;
byte key_pressed_total = 0;
bool chord_used = false;
bool chorded_pressed = false;
2021-02-26 21:13:24 +00:00
bool mod_shift = false;
bool mod_shift_lock = false;
bool mod_symbol = false;
bool mod_symbol_lock = false;
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;
2021-03-02 20:47:32 +00:00
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;
2021-02-26 20:54:27 +00:00
void setup() {
2021-02-27 21:27:34 +00:00
pinMode(PIN_A, INPUT_PULLUP);
pinMode(PIN_B, INPUT_PULLUP);
pinMode(PIN_C, INPUT_PULLUP);
pinMode(PIN_D, INPUT_PULLUP);
pinMode(PIN_E, INPUT_PULLUP);
pinMode(PIN_F, INPUT_PULLUP);
pinMode(PIN_MOUSE_UL, INPUT_PULLUP);
pinMode(PIN_MOUSE_UR, INPUT_PULLUP);
pinMode(PIN_MOUSE_DL, INPUT_PULLUP);
pinMode(PIN_MOUSE_DR, INPUT_PULLUP);
pinMode(PIN_LED1, OUTPUT);
pinMode(PIN_LED2, OUTPUT);
pinMode(PIN_LED3, OUTPUT);
2021-03-02 20:47:32 +00:00
// poll trackball at 10kHz
trackball_timer.begin(trackball_isr, 100);
2021-02-27 21:27:34 +00:00
Wire.begin();
2021-02-26 20:54:27 +00:00
}
void loop() {
/*
BUTTONS
*/
2021-02-27 21:27:34 +00:00
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]++;
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;
}
}
2021-02-27 21:27:34 +00:00
}
}
// button is released
else {
// if the button had been debounced, it counts as a press
2021-02-27 21:27:34 +00:00
if (button_timers[b] >= debounce_time) {
// gkos key released
2021-02-27 21:27:34 +00:00
if (b < 6) {
key_pressed &= ~(1 << b);
// check if it completed a chord
key_released(b);
update_leds();
2021-02-27 21:27:34 +00:00
if (key_pressed == 0) {
key_pressed_total = 0;
// clear flags
chord_used = false;
chorded_pressed = false;
2021-02-27 21:27:34 +00:00
}
}
// 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;
2021-03-02 20:47:32 +00:00
mouse_scroll_dist = 0;
break;
}
}
2021-02-27 21:27:34 +00:00
}
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;
2021-03-02 20:47:32 +00:00
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 {
2021-03-02 20:47:32 +00:00
Mouse.move(mouse_x * mouse_acceleration, mouse_y * mouse_acceleration);
}
trackball_update = false;
}
interrupts();
2021-02-27 21:27:34 +00:00
}
// returns true if a keypress was completed
void key_released(byte key) {
int target = 0;
2021-02-27 21:27:34 +00:00
/*
SPECIALS
*/
bool is_special = false;
byte special = 0;
for (byte b = 0; b < 22; b++) {
if (key_pressed_total == specials[b]) {
is_special = true;
special = b;
break;
}
}
if (is_special && !chord_used) {
2021-02-27 21:27:34 +00:00
// only register a special keypress when all keys have been released
if (key_pressed != 0)
return;
target = special_action_targets[special];
if (mod_shift || mod_shift_lock || mod_symbol || mod_symbol_lock) {
if (special_action_targets_symbol[special] != 0)
target = special_action_targets_symbol[special];
}
2021-02-27 21:27:34 +00:00
switch (special_action_target_types[special]) {
case ActionType::PressKey:
press_key(target);
return;
case ActionType::ToggleModifier:
switch (target) {
case Modifier::Shift:
mod_symbol = mod_symbol_lock = false;
if (mod_shift_lock) {
mod_shift_lock = false;
} else if (mod_shift) {
mod_shift = false;
mod_shift_lock = true;
} else {
mod_shift = true;
}
return;
case Modifier::SymbolShift:
mod_shift = mod_shift_lock = mod_symbol_lock = false;
mod_symbol = !mod_symbol;
return;
case Modifier::Keyset:
mod_shift = mod_shift_lock = mod_symbol = false;
mod_symbol_lock = !mod_symbol_lock;
return;
case Modifier::Control:
mod_control = !mod_control;
return;
case Modifier::Alt:
mod_alt = !mod_alt;
return;
}
2021-02-27 21:27:34 +00:00
}
return;
2021-02-27 21:27:34 +00:00
}
/*
CHORDS
2021-02-27 21:27:34 +00:00
*/
bool is_chord = false;
byte chord = 0;
2021-02-27 21:27:34 +00:00
for (byte b = 0; b < 8; b++) {
if ((key_pressed_total & chords[b]) == chords[b]) {
is_chord = true;
chord = b;
chord_used = true;
2021-02-27 21:27:34 +00:00
break;
}
}
if (is_chord) {
// chord on its own
if (!chorded_pressed && key_pressed == 0 &&
key_pressed_total == chords[chord]) {
target = chord_targets[chord * 4];
if (mod_symbol || mod_symbol_lock)
target = chord_targets_symbol[chord * 4];
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_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;
press_key(target);
return;
}
2021-02-27 21:27:34 +00:00
}
return;
2021-02-27 21:27:34 +00:00
}
/*
REGULAR KEYS
*/
// keypress
if (mod_symbol || mod_symbol_lock)
press_key(button_characters_symbol[key]);
else
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() {
digitalWrite(PIN_LED1, mod_shift || mod_shift_lock);
digitalWrite(PIN_LED2, mod_symbol || mod_symbol_lock);
digitalWrite(PIN_LED3, mod_control || mod_alt);
2021-02-26 20:54:27 +00:00
}
int sign(int num) {
if (num >= 0) return 1;
return -1;
}
void trackball_isr() {
trackball_update = true;
}