commit
fc80aa9974
@ -1,3 +1,8 @@ |
|||||||
|
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
|
||||||
|
COMMAND_ENABLE = no # Commands for debug and configuration
|
||||||
|
RGBLIGHT_ENABLE ?= yes
|
||||||
|
MIDI_ENABLE ?= yes
|
||||||
|
|
||||||
ifndef MAKEFILE_INCLUDED |
ifndef MAKEFILE_INCLUDED |
||||||
include ../../../Makefile
|
include ../../../Makefile
|
||||||
endif |
endif |
||||||
|
@ -0,0 +1,9 @@ |
|||||||
|
# Having a file like this allows you to override Makefile definitions
|
||||||
|
# for your own particular keymap
|
||||||
|
|
||||||
|
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
|
||||||
|
COMMAND_ENABLE = no # Commands for debug and configuration
|
||||||
|
|
||||||
|
ifndef QUANTUM_DIR |
||||||
|
include ../../../../Makefile
|
||||||
|
endif |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
@ -0,0 +1,6 @@ |
|||||||
|
RGBLIGHT_ENABLE ?= yes
|
||||||
|
MIDI_ENABLE ?= yes
|
||||||
|
|
||||||
|
ifndef QUANTUM_DIR |
||||||
|
include ../../../../Makefile
|
||||||
|
endif |
@ -0,0 +1,17 @@ |
|||||||
|
#ifndef CONFIG_USER_H |
||||||
|
#define CONFIG_USER_H |
||||||
|
|
||||||
|
#include "../../config.h" |
||||||
|
|
||||||
|
/* ws2812 RGB LED */ |
||||||
|
#define RGB_DI_PIN D7 |
||||||
|
#define RGBLIGHT_ANIMATIONS |
||||||
|
#define RGBLED_NUM 15 // Number of LEDs
|
||||||
|
#define RGBLIGHT_HUE_STEP 12 |
||||||
|
#define RGBLIGHT_SAT_STEP 255 |
||||||
|
#define RGBLIGHT_VAL_STEP 12 |
||||||
|
|
||||||
|
#define RGB_MIDI |
||||||
|
#define RGBW_BB_TWI |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,79 @@ |
|||||||
|
#include "ergodox.h" |
||||||
|
#include "debug.h" |
||||||
|
#include "action_layer.h" |
||||||
|
#include "version.h" |
||||||
|
#include "keymap_fr_ch.h" |
||||||
|
#include "keymap_french.h" |
||||||
|
#include "keymap_german.h" |
||||||
|
#include "keymap_german_ch.h" |
||||||
|
#include "keymap_nordic.h" |
||||||
|
#include "keymap_norwegian.h" |
||||||
|
#include "keymap_spanish.h" |
||||||
|
|
||||||
|
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { |
||||||
|
|
||||||
|
|
||||||
|
[0] = KEYMAP(NO_LESS,KC_1,KC_2,KC_3,KC_4,KC_5,KC_BSPACE,KC_TAB,KC_Q,KC_W,KC_E,KC_R,KC_T,TG(1),KC_BSPACE,KC_A,KC_S,KC_D,KC_F,KC_G,SFT_T(NO_APOS),CTL_T(KC_Z),KC_X,KC_C,KC_V,KC_B,SFT_T(KC_EQUAL),MO(1),CTL_T(KC_GRAVE),KC_LGUI,KC_LEFT,KC_RIGHT,KC_ESCAPE,KC_CAPSLOCK,KC_HOME,KC_SPACE,KC_LGUI,KC_LALT,KC_DELETE,KC_6,KC_7,KC_8,KC_9,KC_0,NO_PLUS,TG(1),KC_Y,KC_U,KC_I,KC_O,KC_P,NO_AM,KC_H,KC_J,KC_K,KC_L,LT(2,NO_OSLH),NO_AE,SFT_T(KC_RBRC),KC_N,KC_M,KC_COMMA,KC_DOT,CTL_T(KC_SLASH),SFT_T(NO_APOS),KC_DOWN,KC_UP,NO_LPRN,NO_RPRN,MO(1),NO_QUOT,CTL_T(KC_ESCAPE),NO_APOS,KC_LALT,KC_LGUI,KC_ENTER), |
||||||
|
|
||||||
|
[1] = KEYMAP(M(0),KC_F1,KC_F2,KC_F3,KC_F4,KC_F5,KC_BSPACE,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_BSPACE,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_LSHIFT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_LCTL,KC_LGUI,KC_LEFT,KC_RIGHT,KC_ESCAPE,KC_TRANSPARENT,KC_HOME,KC_SPACE,KC_LGUI,KC_LALT,KC_DELETE,KC_F6,KC_F7,KC_F8,KC_F9,KC_F10,KC_F11,KC_TRANSPARENT,KC_7,KC_8,KC_9,KC_TRANSPARENT,KC_TRANSPARENT,KC_F12,KC_4,KC_5,KC_6,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_0,KC_1,KC_2,KC_3,NO_LBRC,NO_RBRC,KC_LSHIFT,KC_COMMA,KC_DOT,LSFT(NO_LBRC),LSFT(NO_RBRC),KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_LALT,KC_LGUI,KC_ENTER), |
||||||
|
|
||||||
|
[2] = KEYMAP(KC_ESCAPE,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_MS_UP,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_MS_LEFT,KC_MS_DOWN,KC_MS_RIGHT,KC_TRANSPARENT,KC_LSHIFT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_LCTL,KC_LALT,KC_LGUI,KC_MS_BTN1,KC_MS_BTN2,KC_ESCAPE,KC_TRANSPARENT,KC_TRANSPARENT,KC_SPACE,KC_LGUI,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_MEDIA_PREV_TRACK,KC_MEDIA_PLAY_PAUSE,KC_MEDIA_NEXT_TRACK,KC_TRANSPARENT,KC_TRANSPARENT,KC_AUDIO_VOL_UP,KC_AUDIO_VOL_DOWN,KC_AUDIO_MUTE,KC_TRANSPARENT,KC_TRANSPARENT,KC_TRANSPARENT,KC_ESCAPE,KC_MS_WH_UP,KC_MS_WH_DOWN,KC_MS_ACCEL0,KC_MS_ACCEL1), |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
const uint16_t PROGMEM fn_actions[] = { |
||||||
|
[1] = ACTION_LAYER_TAP_TOGGLE(1) |
||||||
|
}; |
||||||
|
|
||||||
|
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) |
||||||
|
{ |
||||||
|
switch(id) { |
||||||
|
case 0: |
||||||
|
if (record->event.pressed) { |
||||||
|
SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
return MACRO_NONE; |
||||||
|
}; |
||||||
|
|
||||||
|
void matrix_scan_user(void) { |
||||||
|
|
||||||
|
uint8_t layer = biton32(layer_state); |
||||||
|
|
||||||
|
ergodox_board_led_off(); |
||||||
|
ergodox_right_led_1_off(); |
||||||
|
ergodox_right_led_2_off(); |
||||||
|
ergodox_right_led_3_off(); |
||||||
|
switch (layer) { |
||||||
|
case 1: |
||||||
|
ergodox_right_led_1_on(); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
ergodox_right_led_2_on(); |
||||||
|
break; |
||||||
|
case 3: |
||||||
|
ergodox_right_led_3_on(); |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
ergodox_right_led_1_on(); |
||||||
|
ergodox_right_led_2_on(); |
||||||
|
break; |
||||||
|
case 5: |
||||||
|
ergodox_right_led_1_on(); |
||||||
|
ergodox_right_led_3_on(); |
||||||
|
break; |
||||||
|
case 6: |
||||||
|
ergodox_right_led_2_on(); |
||||||
|
ergodox_right_led_3_on(); |
||||||
|
break; |
||||||
|
case 7: |
||||||
|
ergodox_right_led_1_on(); |
||||||
|
ergodox_right_led_2_on(); |
||||||
|
ergodox_right_led_3_on(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,151 @@ |
|||||||
|
#include "ergodox.h" |
||||||
|
#include "debug.h" |
||||||
|
#include "action_layer.h" |
||||||
|
#include "version.h" |
||||||
|
|
||||||
|
enum custom_keycodes { |
||||||
|
PLACEHOLDER = SAFE_RANGE, // can always be here
|
||||||
|
RGB_FF0000, |
||||||
|
RGB_00FF00, |
||||||
|
RGB_0000FF, |
||||||
|
RGB_FFFFFF, |
||||||
|
RGB_TOGGLE, |
||||||
|
LED1, |
||||||
|
LED2, |
||||||
|
LED3 |
||||||
|
}; |
||||||
|
|
||||||
|
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { |
||||||
|
|
||||||
|
[0] = KEYMAP( |
||||||
|
RGB_TOGGLE, RGB_FF0000, RGB_00FF00, RGB_0000FF, RGB_FFFFFF, KC_5, KC_LPRN, |
||||||
|
KC_GRAVE, KC_A, KC_B, KC_C, KC_D, KC_E, KC_EXLM, |
||||||
|
KC_HASH, KC_J, KC_K, KC_L, KC_M, KC_N, |
||||||
|
KC_AMPR, KC_T, KC_U, KC_V, KC_W, KC_X, KC_DLR, |
||||||
|
KC_PIPE, KC_R, KC_PLUS, KC_LCBR, KC_RCBR, |
||||||
|
|
||||||
|
KC_F, KC_G, |
||||||
|
KC_H, |
||||||
|
KC_P, KC_O, KC_I, |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// RIGHT HAND
|
||||||
|
KC_RPRN, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, |
||||||
|
KC_AT, KC_F, KC_G, KC_H, KC_I, KC_COLN, KC_BSLS, |
||||||
|
KC_O, KC_P, KC_Q, KC_R, KC_S, KC_QUOT, |
||||||
|
LSFT(KC_COMM), KC_Y, KC_Z, KC_COMM, KC_DOT, KC_SLSH, KC_ASTR, |
||||||
|
KC_A, KC_B, KC_C, KC_D, KC_PIPE, |
||||||
|
|
||||||
|
LED1, KC_E, |
||||||
|
LED2, |
||||||
|
LED3, KC_J, KC_K |
||||||
|
) |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const uint16_t PROGMEM fn_actions[] = { |
||||||
|
[1] = ACTION_LAYER_TAP_TOGGLE(1) |
||||||
|
}; |
||||||
|
|
||||||
|
const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt) |
||||||
|
{ |
||||||
|
switch(id) { |
||||||
|
case 0: |
||||||
|
if (record->event.pressed) { |
||||||
|
SEND_STRING (QMK_KEYBOARD "/" QMK_KEYMAP " @ " QMK_VERSION); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
return MACRO_NONE; |
||||||
|
}; |
||||||
|
|
||||||
|
bool status_led1_on = false, status_led2_on = false, status_led3_on = false; |
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t *record) { |
||||||
|
switch (keycode) { |
||||||
|
// dynamically generate these.
|
||||||
|
case RGB_FF0000: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
EZ_RGB(0xff0000); |
||||||
|
register_code(KC_1); unregister_code(KC_1); |
||||||
|
#endif |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case RGB_00FF00: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
EZ_RGB(0x00ff00); |
||||||
|
register_code(KC_2); unregister_code(KC_2); |
||||||
|
#endif |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case RGB_0000FF: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
EZ_RGB(0x0000ff); |
||||||
|
register_code(KC_3); unregister_code(KC_3); |
||||||
|
#endif |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case RGB_FFFFFF: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
EZ_RGB(0xffffff); |
||||||
|
register_code(KC_4); unregister_code(KC_4); |
||||||
|
#endif |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case RGB_TOGGLE: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
rgblight_toggle(); |
||||||
|
register_code(KC_EQL); unregister_code(KC_EQL); |
||||||
|
#endif |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case LED1: |
||||||
|
if (record->event.pressed) { |
||||||
|
if(status_led1_on) { |
||||||
|
ergodox_right_led_1_off(); |
||||||
|
status_led1_on = false; |
||||||
|
} else { |
||||||
|
ergodox_right_led_1_on(); |
||||||
|
status_led1_on = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case LED2: |
||||||
|
if (record->event.pressed) { |
||||||
|
if(status_led2_on) { |
||||||
|
ergodox_right_led_2_off(); |
||||||
|
status_led2_on = false; |
||||||
|
} else { |
||||||
|
ergodox_right_led_2_on(); |
||||||
|
status_led2_on = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case LED3: |
||||||
|
if (record->event.pressed) { |
||||||
|
if(status_led3_on) { |
||||||
|
ergodox_right_led_3_off(); |
||||||
|
status_led3_on = false; |
||||||
|
} else { |
||||||
|
ergodox_right_led_3_on(); |
||||||
|
status_led3_on = true; |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
# Robot test layout |
||||||
|
|
||||||
|
Use this layout if you like to pretend you're [Norman](https://www.youtube.com/watch?v=-sbxFBay-tg), the ErgoDox EZ manufacturing robot. |
||||||
|
|
||||||
|
It's really meant just for internal use, but we're posting it on GitHub anyway, because hurray to open source. :) |
@ -0,0 +1,29 @@ |
|||||||
|
# Please remove if no longer applicable
|
||||||
|
$(warning THIS FILE MAY BE TOO LARGE FOR YOUR KEYBOARD) |
||||||
|
$(warning Please disable some options in the Makefile to resolve) |
||||||
|
|
||||||
|
|
||||||
|
# Build Options
|
||||||
|
# change to "no" to disable the options, or define them in the Makefile in
|
||||||
|
# the appropriate keymap folder that will get included automatically
|
||||||
|
#
|
||||||
|
BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
|
||||||
|
MOUSEKEY_ENABLE = yes # Mouse keys(+4700)
|
||||||
|
EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
|
||||||
|
CONSOLE_ENABLE = no # Console for debug(+400)
|
||||||
|
COMMAND_ENABLE = yes # Commands for debug and configuration
|
||||||
|
NKRO_ENABLE = yes # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
|
||||||
|
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
|
||||||
|
MIDI_ENABLE = no # MIDI controls
|
||||||
|
AUDIO_ENABLE = yes # Audio output on port C6
|
||||||
|
UNICODE_ENABLE = no # Unicode
|
||||||
|
BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID
|
||||||
|
RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. Do not enable this with audio at the same time.
|
||||||
|
PRINTING_ENABLE = yes
|
||||||
|
|
||||||
|
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
|
||||||
|
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
|
||||||
|
|
||||||
|
ifndef QUANTUM_DIR |
||||||
|
include ../../../../Makefile
|
||||||
|
endif |
@ -0,0 +1,23 @@ |
|||||||
|
#ifndef CONFIG_USER_H |
||||||
|
#define CONFIG_USER_H |
||||||
|
|
||||||
|
#include "../../config.h" |
||||||
|
|
||||||
|
# define SERIAL_UART_BAUD 19200 |
||||||
|
# define SERIAL_UART_DATA UDR1 |
||||||
|
# define SERIAL_UART_UBRR (F_CPU / (16UL * SERIAL_UART_BAUD) - 1) |
||||||
|
# define SERIAL_UART_RXD_VECT USART1_RX_vect |
||||||
|
# define SERIAL_UART_TXD_READY (UCSR1A & _BV(UDRE1)) |
||||||
|
# define SERIAL_UART_INIT() do { \ |
||||||
|
/* baud rate */ \
|
||||||
|
UBRR1L = SERIAL_UART_UBRR; \
|
||||||
|
/* baud rate */ \
|
||||||
|
UBRR1H = SERIAL_UART_UBRR >> 8; \
|
||||||
|
/* enable TX */ \
|
||||||
|
UCSR1B = _BV(TXEN1); \
|
||||||
|
/* 8-bit data */ \
|
||||||
|
UCSR1C = _BV(UCSZ11) | _BV(UCSZ10); \
|
||||||
|
sei(); \
|
||||||
|
} while(0) |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,314 @@ |
|||||||
|
// This is the canonical layout file for the Quantum project. If you want to add another keyboard,
|
||||||
|
// this is the style you want to emulate.
|
||||||
|
|
||||||
|
#include "planck.h" |
||||||
|
#include "action_layer.h" |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
#include "audio.h" |
||||||
|
#endif |
||||||
|
#include "eeconfig.h" |
||||||
|
|
||||||
|
extern keymap_config_t keymap_config; |
||||||
|
|
||||||
|
// Each layer gets a name for readability, which is then used in the keymap matrix below.
|
||||||
|
// The underscores don't mean anything - you can have a layer called STUFF or any other name.
|
||||||
|
// Layer names don't all need to be of the same length, obviously, and you can also skip them
|
||||||
|
// entirely and just use numbers.
|
||||||
|
#define _QWERTY 0 |
||||||
|
#define _COLEMAK 1 |
||||||
|
#define _DVORAK 2 |
||||||
|
#define _LOWER 3 |
||||||
|
#define _RAISE 4 |
||||||
|
#define _PLOVER 5 |
||||||
|
#define _ADJUST 16 |
||||||
|
|
||||||
|
enum planck_keycodes { |
||||||
|
QWERTY = SAFE_RANGE, |
||||||
|
COLEMAK, |
||||||
|
DVORAK, |
||||||
|
PLOVER, |
||||||
|
LOWER, |
||||||
|
RAISE, |
||||||
|
BACKLIT, |
||||||
|
EXT_PLV |
||||||
|
}; |
||||||
|
|
||||||
|
// Fillers to make layering more clear
|
||||||
|
#define _______ KC_TRNS |
||||||
|
#define XXXXXXX KC_NO |
||||||
|
|
||||||
|
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = { |
||||||
|
|
||||||
|
/* Qwerty
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | Tab | Q | W | E | R | T | Y | U | I | O | P | Bksp | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | Esc | A | S | D | F | G | H | J | K | L | ; | " | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* | Shift| Z | X | C | V | B | N | M | , | . | / |Enter | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | Brite| Ctrl | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
[_QWERTY] = { |
||||||
|
{KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_BSPC}, |
||||||
|
{KC_ESC, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT}, |
||||||
|
{KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT }, |
||||||
|
{BACKLIT, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT} |
||||||
|
}, |
||||||
|
|
||||||
|
/* Colemak
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | Tab | Q | W | F | P | G | J | L | U | Y | ; | Bksp | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | Esc | A | R | S | T | D | H | N | E | I | O | " | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* | Shift| Z | X | C | V | B | K | M | , | . | / |Enter | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | Brite| Ctrl | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
[_COLEMAK] = { |
||||||
|
{KC_TAB, KC_Q, KC_W, KC_F, KC_P, KC_G, KC_J, KC_L, KC_U, KC_Y, KC_SCLN, KC_BSPC}, |
||||||
|
{KC_ESC, KC_A, KC_R, KC_S, KC_T, KC_D, KC_H, KC_N, KC_E, KC_I, KC_O, KC_QUOT}, |
||||||
|
{KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_K, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_ENT }, |
||||||
|
{BACKLIT, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT} |
||||||
|
}, |
||||||
|
|
||||||
|
/* Dvorak
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | Tab | " | , | . | P | Y | F | G | C | R | L | Bksp | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | Esc | A | O | E | U | I | D | H | T | N | S | / | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* | Shift| ; | Q | J | K | X | B | M | W | V | Z |Enter | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | Brite| Ctrl | Alt | GUI |Lower | Space |Raise | Left | Down | Up |Right | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
[_DVORAK] = { |
||||||
|
{KC_TAB, KC_QUOT, KC_COMM, KC_DOT, KC_P, KC_Y, KC_F, KC_G, KC_C, KC_R, KC_L, KC_BSPC}, |
||||||
|
{KC_ESC, KC_A, KC_O, KC_E, KC_U, KC_I, KC_D, KC_H, KC_T, KC_N, KC_S, KC_SLSH}, |
||||||
|
{KC_LSFT, KC_SCLN, KC_Q, KC_J, KC_K, KC_X, KC_B, KC_M, KC_W, KC_V, KC_Z, KC_ENT }, |
||||||
|
{BACKLIT, KC_LCTL, KC_LALT, KC_LGUI, LOWER, KC_SPC, KC_SPC, RAISE, KC_LEFT, KC_DOWN, KC_UP, KC_RGHT} |
||||||
|
}, |
||||||
|
|
||||||
|
/* Lower
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | ~ | ! | @ | # | $ | % | ^ | & | * | ( | ) | Bksp | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | Del | F1 | F2 | F3 | F4 | F5 | F6 | _ | + | { | } | | | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* | | F7 | F8 | F9 | F10 | F11 | F12 |ISO ~ |ISO | | | |Enter | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | | | | | | | | Next | Vol- | Vol+ | Play | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
[_LOWER] = { |
||||||
|
{KC_TILD, KC_EXLM, KC_AT, KC_HASH, KC_DLR, KC_PERC, KC_CIRC, KC_AMPR, KC_ASTR, KC_LPRN, KC_RPRN, KC_BSPC}, |
||||||
|
{KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_UNDS, KC_PLUS, KC_LCBR, KC_RCBR, KC_PIPE}, |
||||||
|
{_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12,S(KC_NUHS),S(KC_NUBS),_______, _______, _______}, |
||||||
|
{_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY} |
||||||
|
}, |
||||||
|
|
||||||
|
/* Raise
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | ` | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | Bksp | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | Del | F1 | F2 | F3 | F4 | F5 | F6 | - | = | [ | ] | \ | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* | | F7 | F8 | F9 | F10 | F11 | F12 |ISO # |ISO / | | |Enter | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | | | | | | | | Next | Vol- | Vol+ | Play | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
[_RAISE] = { |
||||||
|
{KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_BSPC}, |
||||||
|
{KC_DEL, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_MINS, KC_EQL, KC_LBRC, KC_RBRC, KC_BSLS}, |
||||||
|
{_______, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_NUHS, KC_NUBS, _______, _______, _______}, |
||||||
|
{_______, _______, _______, _______, _______, _______, _______, _______, KC_MNXT, KC_VOLD, KC_VOLU, KC_MPLY} |
||||||
|
}, |
||||||
|
|
||||||
|
/* Plover layer (http://opensteno.org)
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | # | # | # | # | # | # | # | # | # | # | # | # | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | | S | T | P | H | * | * | F | P | L | T | D | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* |TogOut| S | K | W | R | * | * | R | B | G | S | Z | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | Exit | | | A | O | | E | U | | | | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
|
||||||
|
[_PLOVER] = { |
||||||
|
{KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1, KC_1 }, |
||||||
|
{XXXXXXX, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC}, |
||||||
|
{XXXXXXX, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT}, |
||||||
|
{EXT_PLV, XXXXXXX, XXXXXXX, KC_C, KC_V, XXXXXXX, XXXXXXX, KC_N, KC_M, XXXXXXX, XXXXXXX, XXXXXXX} |
||||||
|
}, |
||||||
|
|
||||||
|
/* Adjust (Lower + Raise)
|
||||||
|
* ,-----------------------------------------------------------------------------------. |
||||||
|
* | | Reset| | Print|no prnt | | | | | | | Del | |
||||||
|
* |------+------+------+------+------+-------------+------+------+------+------+------| |
||||||
|
* | | | |Aud on|Audoff|AGnorm|AGswap|Qwerty|Colemk|Dvorak|Plover| | |
||||||
|
* |------+------+------+------+------+------|------+------+------+------+------+------| |
||||||
|
* | |Voice-|Voice+|Mus on|Musoff|MIDIon|MIDIof| | | | | | |
||||||
|
* |------+------+------+------+------+------+------+------+------+------+------+------| |
||||||
|
* | | | | | | | | | | | | |
||||||
|
* `-----------------------------------------------------------------------------------' |
||||||
|
*/ |
||||||
|
[_ADJUST] = { |
||||||
|
{_______, RESET, _______, PRINT_ON, PRINT_OFF, _______, _______, _______, _______, _______, _______, KC_DEL}, |
||||||
|
{_______, _______, _______, AU_ON, AU_OFF, AG_NORM, AG_SWAP, QWERTY, COLEMAK, DVORAK, PLOVER, _______}, |
||||||
|
{_______, MUV_DE, MUV_IN, MU_ON, MU_OFF, MI_ON, MI_OFF, _______, _______, _______, _______, _______}, |
||||||
|
{_______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______, _______} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
|
||||||
|
float tone_startup[][2] = SONG(STARTUP_SOUND); |
||||||
|
float tone_qwerty[][2] = SONG(QWERTY_SOUND); |
||||||
|
float tone_dvorak[][2] = SONG(DVORAK_SOUND); |
||||||
|
float tone_colemak[][2] = SONG(COLEMAK_SOUND); |
||||||
|
float tone_plover[][2] = SONG(PLOVER_SOUND); |
||||||
|
float tone_plover_gb[][2] = SONG(PLOVER_GOODBYE_SOUND); |
||||||
|
float music_scale[][2] = SONG(MUSIC_SCALE_SOUND); |
||||||
|
|
||||||
|
float tone_goodbye[][2] = SONG(GOODBYE_SOUND); |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
void persistant_default_layer_set(uint16_t default_layer) { |
||||||
|
eeconfig_update_default_layer(default_layer); |
||||||
|
default_layer_set(default_layer); |
||||||
|
} |
||||||
|
|
||||||
|
bool process_record_user(uint16_t keycode, keyrecord_t *record) { |
||||||
|
switch (keycode) { |
||||||
|
case QWERTY: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
PLAY_NOTE_ARRAY(tone_qwerty, false, 0); |
||||||
|
#endif |
||||||
|
persistant_default_layer_set(1UL<<_QWERTY); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case COLEMAK: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
PLAY_NOTE_ARRAY(tone_colemak, false, 0); |
||||||
|
#endif |
||||||
|
persistant_default_layer_set(1UL<<_COLEMAK); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case DVORAK: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
PLAY_NOTE_ARRAY(tone_dvorak, false, 0); |
||||||
|
#endif |
||||||
|
persistant_default_layer_set(1UL<<_DVORAK); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case LOWER: |
||||||
|
if (record->event.pressed) { |
||||||
|
layer_on(_LOWER); |
||||||
|
update_tri_layer(_LOWER, _RAISE, _ADJUST); |
||||||
|
} else { |
||||||
|
layer_off(_LOWER); |
||||||
|
update_tri_layer(_LOWER, _RAISE, _ADJUST); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case RAISE: |
||||||
|
if (record->event.pressed) { |
||||||
|
layer_on(_RAISE); |
||||||
|
update_tri_layer(_LOWER, _RAISE, _ADJUST); |
||||||
|
} else { |
||||||
|
layer_off(_RAISE); |
||||||
|
update_tri_layer(_LOWER, _RAISE, _ADJUST); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case BACKLIT: |
||||||
|
if (record->event.pressed) { |
||||||
|
register_code(KC_RSFT); |
||||||
|
#ifdef BACKLIGHT_ENABLE |
||||||
|
backlight_step(); |
||||||
|
#endif |
||||||
|
} else { |
||||||
|
unregister_code(KC_RSFT); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case PLOVER: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
stop_all_notes(); |
||||||
|
PLAY_NOTE_ARRAY(tone_plover, false, 0); |
||||||
|
#endif |
||||||
|
layer_off(_RAISE); |
||||||
|
layer_off(_LOWER); |
||||||
|
layer_off(_ADJUST); |
||||||
|
layer_on(_PLOVER); |
||||||
|
if (!eeconfig_is_enabled()) { |
||||||
|
eeconfig_init(); |
||||||
|
} |
||||||
|
keymap_config.raw = eeconfig_read_keymap(); |
||||||
|
keymap_config.nkro = 1; |
||||||
|
eeconfig_update_keymap(keymap_config.raw); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case EXT_PLV: |
||||||
|
if (record->event.pressed) { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
PLAY_NOTE_ARRAY(tone_plover_gb, false, 0); |
||||||
|
#endif |
||||||
|
layer_off(_PLOVER); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void matrix_init_user(void) { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
startup_user(); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
|
||||||
|
void startup_user() |
||||||
|
{ |
||||||
|
_delay_ms(20); // gets rid of tick
|
||||||
|
PLAY_NOTE_ARRAY(tone_startup, false, 0); |
||||||
|
} |
||||||
|
|
||||||
|
void shutdown_user() |
||||||
|
{ |
||||||
|
PLAY_NOTE_ARRAY(tone_goodbye, false, 0); |
||||||
|
_delay_ms(150); |
||||||
|
stop_all_notes(); |
||||||
|
} |
||||||
|
|
||||||
|
void music_on_user(void) |
||||||
|
{ |
||||||
|
music_scale_user(); |
||||||
|
} |
||||||
|
|
||||||
|
void music_scale_user(void) |
||||||
|
{ |
||||||
|
PLAY_NOTE_ARRAY(music_scale, false, 0); |
||||||
|
} |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,2 @@ |
|||||||
|
# The Default Planck Layout |
||||||
|
|
@ -1,25 +1,3 @@ |
|||||||
|
|
||||||
|
|
||||||
# Build Options
|
|
||||||
# change to "no" to disable the options, or define them in the Makefile in
|
|
||||||
# the appropriate keymap folder that will get included automatically
|
|
||||||
#
|
|
||||||
BOOTMAGIC_ENABLE = no # Virtual DIP switch configuration(+1000)
|
|
||||||
MOUSEKEY_ENABLE = yes # Mouse keys(+4700)
|
|
||||||
EXTRAKEY_ENABLE = yes # Audio control and System control(+450)
|
|
||||||
CONSOLE_ENABLE = no # Console for debug(+400)
|
|
||||||
COMMAND_ENABLE = yes # Commands for debug and configuration
|
|
||||||
NKRO_ENABLE = yes # Nkey Rollover - if this doesn't work, see here: https://github.com/tmk/tmk_keyboard/wiki/FAQ#nkro-doesnt-work
|
|
||||||
BACKLIGHT_ENABLE = yes # Enable keyboard backlight functionality
|
|
||||||
MIDI_ENABLE = no # MIDI controls
|
|
||||||
AUDIO_ENABLE = yes # Audio output on port C6
|
|
||||||
UNICODE_ENABLE = no # Unicode
|
|
||||||
BLUETOOTH_ENABLE = no # Enable Bluetooth with the Adafruit EZ-Key HID
|
|
||||||
RGBLIGHT_ENABLE = no # Enable WS2812 RGB underlight. Do not enable this with audio at the same time.
|
|
||||||
|
|
||||||
# Do not enable SLEEP_LED_ENABLE. it uses the same timer as BACKLIGHT_ENABLE
|
|
||||||
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend
|
|
||||||
|
|
||||||
ifndef QUANTUM_DIR |
ifndef QUANTUM_DIR |
||||||
include ../../../../Makefile
|
include ../../../../Makefile
|
||||||
endif |
endif |
@ -0,0 +1,178 @@ |
|||||||
|
#include "api.h" |
||||||
|
#include "quantum.h" |
||||||
|
|
||||||
|
void dword_to_bytes(uint32_t dword, uint8_t * bytes) { |
||||||
|
bytes[0] = (dword >> 24) & 0xFF; |
||||||
|
bytes[1] = (dword >> 16) & 0xFF;
|
||||||
|
bytes[2] = (dword >> 8) & 0xFF;
|
||||||
|
bytes[3] = (dword >> 0) & 0xFF;
|
||||||
|
} |
||||||
|
|
||||||
|
uint32_t bytes_to_dword(uint8_t * bytes, uint8_t index) { |
||||||
|
return ((uint32_t)bytes[index + 0] << 24) | ((uint32_t)bytes[index + 1] << 16) | ((uint32_t)bytes[index + 2] << 8) | (uint32_t)bytes[index + 3]; |
||||||
|
} |
||||||
|
|
||||||
|
__attribute__ ((weak)) |
||||||
|
bool process_api_quantum(uint8_t length, uint8_t * data) { |
||||||
|
return process_api_keyboard(length, data); |
||||||
|
} |
||||||
|
|
||||||
|
__attribute__ ((weak)) |
||||||
|
bool process_api_keyboard(uint8_t length, uint8_t * data) { |
||||||
|
return process_api_user(length, data); |
||||||
|
} |
||||||
|
|
||||||
|
__attribute__ ((weak)) |
||||||
|
bool process_api_user(uint8_t length, uint8_t * data) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void process_api(uint16_t length, uint8_t * data) { |
||||||
|
// SEND_STRING("\nRX: ");
|
||||||
|
// for (uint8_t i = 0; i < length; i++) {
|
||||||
|
// send_byte(data[i]);
|
||||||
|
// SEND_STRING(" ");
|
||||||
|
// }
|
||||||
|
if (!process_api_quantum(length, data)) |
||||||
|
return; |
||||||
|
|
||||||
|
switch (data[0]) { |
||||||
|
case MT_SET_DATA: |
||||||
|
switch (data[1]) { |
||||||
|
case DT_DEFAULT_LAYER: { |
||||||
|
eeconfig_update_default_layer(data[2]); |
||||||
|
default_layer_set((uint32_t)(data[2])); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_KEYMAP_OPTIONS: { |
||||||
|
eeconfig_update_keymap(data[2]); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_RGBLIGHT: { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
uint32_t rgblight = bytes_to_dword(data, 2); |
||||||
|
rgblight_update_dword(rgblight); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
case MT_GET_DATA: |
||||||
|
switch (data[1]) { |
||||||
|
case DT_HANDSHAKE: { |
||||||
|
MT_GET_DATA_ACK(DT_HANDSHAKE, NULL, 0); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_DEBUG: { |
||||||
|
uint8_t debug_bytes[1] = { eeprom_read_byte(EECONFIG_DEBUG) }; |
||||||
|
MT_GET_DATA_ACK(DT_DEBUG, debug_bytes, 1); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_DEFAULT_LAYER: { |
||||||
|
uint8_t default_bytes[1] = { eeprom_read_byte(EECONFIG_DEFAULT_LAYER) }; |
||||||
|
MT_GET_DATA_ACK(DT_DEFAULT_LAYER, default_bytes, 1); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_CURRENT_LAYER: { |
||||||
|
uint8_t layer_state_bytes[4]; |
||||||
|
dword_to_bytes(layer_state, layer_state_bytes); |
||||||
|
MT_GET_DATA_ACK(DT_CURRENT_LAYER, layer_state_bytes, 4); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_AUDIO: { |
||||||
|
#ifdef AUDIO_ENABLE |
||||||
|
uint8_t audio_bytes[1] = { eeprom_read_byte(EECONFIG_AUDIO) }; |
||||||
|
MT_GET_DATA_ACK(DT_AUDIO, audio_bytes, 1); |
||||||
|
#else |
||||||
|
MT_GET_DATA_ACK(DT_AUDIO, NULL, 0); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_BACKLIGHT: { |
||||||
|
#ifdef BACKLIGHT_ENABLE |
||||||
|
uint8_t backlight_bytes[1] = { eeprom_read_byte(EECONFIG_BACKLIGHT) }; |
||||||
|
MT_GET_DATA_ACK(DT_BACKLIGHT, backlight_bytes, 1); |
||||||
|
#else |
||||||
|
MT_GET_DATA_ACK(DT_BACKLIGHT, NULL, 0); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_RGBLIGHT: { |
||||||
|
#ifdef RGBLIGHT_ENABLE |
||||||
|
uint8_t rgblight_bytes[4]; |
||||||
|
dword_to_bytes(eeconfig_read_rgblight(), rgblight_bytes); |
||||||
|
MT_GET_DATA_ACK(DT_RGBLIGHT, rgblight_bytes, 4); |
||||||
|
#else |
||||||
|
MT_GET_DATA_ACK(DT_RGBLIGHT, NULL, 0); |
||||||
|
#endif |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_KEYMAP_OPTIONS: { |
||||||
|
uint8_t keymap_bytes[1] = { eeconfig_read_keymap() }; |
||||||
|
MT_GET_DATA_ACK(DT_KEYMAP_OPTIONS, keymap_bytes, 1); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_KEYMAP_SIZE: { |
||||||
|
uint8_t keymap_size[2] = {MATRIX_ROWS, MATRIX_COLS}; |
||||||
|
MT_GET_DATA_ACK(DT_KEYMAP_SIZE, keymap_size, 2); |
||||||
|
break; |
||||||
|
} |
||||||
|
case DT_KEYMAP: { |
||||||
|
uint8_t keymap_data[MATRIX_ROWS * MATRIX_COLS * 4 + 3]; |
||||||
|
keymap_data[0] = data[2]; |
||||||
|
keymap_data[1] = MATRIX_ROWS; |
||||||
|
keymap_data[2] = MATRIX_COLS; |
||||||
|
for (int i = 0; i < MATRIX_ROWS; i++) { |
||||||
|
for (int j = 0; j < MATRIX_COLS; j++) { |
||||||
|
keymap_data[3 + (i*MATRIX_COLS*2) + (j*2)] = pgm_read_word(&keymaps[data[2]][i][j]) >> 8; |
||||||
|
keymap_data[3 + (i*MATRIX_COLS*2) + (j*2) + 1] = pgm_read_word(&keymaps[data[2]][i][j]) & 0xFF; |
||||||
|
} |
||||||
|
} |
||||||
|
MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, MATRIX_ROWS * MATRIX_COLS * 4 + 3); |
||||||
|
// uint8_t keymap_data[5];
|
||||||
|
// keymap_data[0] = data[2];
|
||||||
|
// keymap_data[1] = data[3];
|
||||||
|
// keymap_data[2] = data[4];
|
||||||
|
// keymap_data[3] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) >> 8;
|
||||||
|
// keymap_data[4] = pgm_read_word(&keymaps[data[2]][data[3]][data[4]]) & 0xFF;
|
||||||
|
|
||||||
|
// MT_GET_DATA_ACK(DT_KEYMAP, keymap_data, 5);
|
||||||
|
break; |
||||||
|
} |
||||||
|
default: |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
case MT_SET_DATA_ACK: |
||||||
|
case MT_GET_DATA_ACK: |
||||||
|
break; |
||||||
|
case MT_SEND_DATA: |
||||||
|
break; |
||||||
|
case MT_SEND_DATA_ACK: |
||||||
|
break; |
||||||
|
case MT_EXE_ACTION: |
||||||
|
break; |
||||||
|
case MT_EXE_ACTION_ACK: |
||||||
|
break; |
||||||
|
case MT_TYPE_ERROR: |
||||||
|
break; |
||||||
|
default: ; // command not recognised
|
||||||
|
SEND_BYTES(MT_TYPE_ERROR, DT_NONE, data, length); |
||||||
|
break; |
||||||
|
|
||||||
|
// #ifdef RGBLIGHT_ENABLE
|
||||||
|
// case 0x27: ; // RGB LED functions
|
||||||
|
// switch (*data++) {
|
||||||
|
// case 0x00: ; // Update HSV
|
||||||
|
// rgblight_sethsv((data[0] << 8 | data[1]) % 360, data[2], data[3]);
|
||||||
|
// break;
|
||||||
|
// case 0x01: ; // Update RGB
|
||||||
|
// break;
|
||||||
|
// case 0x02: ; // Update mode
|
||||||
|
// rgblight_mode(data[0]);
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// break;
|
||||||
|
// #endif
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,59 @@ |
|||||||
|
#ifndef _API_H_ |
||||||
|
#define _API_H_ |
||||||
|
|
||||||
|
#include "lufa.h" |
||||||
|
|
||||||
|
enum MESSAGE_TYPE { |
||||||
|
MT_GET_DATA = 0x10, // Get data from keyboard
|
||||||
|
MT_GET_DATA_ACK = 0x11, // returned data to process (ACK)
|
||||||
|
MT_SET_DATA = 0x20, // Set data on keyboard
|
||||||
|
MT_SET_DATA_ACK = 0x21, // returned data to confirm (ACK)
|
||||||
|
MT_SEND_DATA = 0x30, // Sending data/action from keyboard
|
||||||
|
MT_SEND_DATA_ACK = 0x31, // returned data/action confirmation (ACK)
|
||||||
|
MT_EXE_ACTION = 0x40, // executing actions on keyboard
|
||||||
|
MT_EXE_ACTION_ACK =0x41, // return confirmation/value (ACK)
|
||||||
|
MT_TYPE_ERROR = 0x80 // type not recofgnised (ACK)
|
||||||
|
}; |
||||||
|
|
||||||
|
enum DATA_TYPE { |
||||||
|
DT_NONE = 0x00, |
||||||
|
DT_HANDSHAKE, |
||||||
|
DT_DEFAULT_LAYER, |
||||||
|
DT_CURRENT_LAYER, |
||||||
|
DT_KEYMAP_OPTIONS, |
||||||
|
DT_BACKLIGHT, |
||||||
|
DT_RGBLIGHT, |
||||||
|
DT_UNICODE, |
||||||
|
DT_DEBUG, |
||||||
|
DT_AUDIO, |
||||||
|
DT_QUANTUM_ACTION, |
||||||
|
DT_KEYBOARD_ACTION, |
||||||
|
DT_USER_ACTION, |
||||||
|
DT_KEYMAP_SIZE, |
||||||
|
DT_KEYMAP |
||||||
|
}; |
||||||
|
|
||||||
|
void dword_to_bytes(uint32_t dword, uint8_t * bytes); |
||||||
|
uint32_t bytes_to_dword(uint8_t * bytes, uint8_t index); |
||||||
|
|
||||||
|
#define MT_GET_DATA(data_type, data, length) SEND_BYTES(MT_GET_DATA, data_type, data, length) |
||||||
|
#define MT_GET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_GET_DATA_ACK, data_type, data, length) |
||||||
|
#define MT_SET_DATA(data_type, data, length) SEND_BYTES(MT_SET_DATA, data_type, data, length) |
||||||
|
#define MT_SET_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SET_DATA_ACK, data_type, data, length) |
||||||
|
#define MT_SEND_DATA(data_type, data, length) SEND_BYTES(MT_SEND_DATA, data_type, data, length) |
||||||
|
#define MT_SEND_DATA_ACK(data_type, data, length) SEND_BYTES(MT_SEND_DATA_ACK, data_type, data, length) |
||||||
|
#define MT_EXE_ACTION(data_type, data, length) SEND_BYTES(MT_EXE_ACTION, data_type, data, length) |
||||||
|
#define MT_EXE_ACTION_ACK(data_type, data, length) SEND_BYTES(MT_EXE_ACTION_ACK, data_type, data, length) |
||||||
|
|
||||||
|
void process_api(uint16_t length, uint8_t * data); |
||||||
|
|
||||||
|
__attribute__ ((weak)) |
||||||
|
bool process_api_quantum(uint8_t length, uint8_t * data); |
||||||
|
|
||||||
|
__attribute__ ((weak)) |
||||||
|
bool process_api_keyboard(uint8_t length, uint8_t * data); |
||||||
|
|
||||||
|
__attribute__ ((weak)) |
||||||
|
bool process_api_user(uint8_t length, uint8_t * data); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,29 @@ |
|||||||
|
#include "api_sysex.h" |
||||||
|
|
||||||
|
void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t * bytes, uint16_t length) { |
||||||
|
// SEND_STRING("\nTX: ");
|
||||||
|
// for (uint8_t i = 0; i < length; i++) {
|
||||||
|
// send_byte(bytes[i]);
|
||||||
|
// SEND_STRING(" ");
|
||||||
|
// }
|
||||||
|
uint8_t * precode = malloc(sizeof(uint8_t) * (length + 2)); |
||||||
|
precode[0] = message_type; |
||||||
|
precode[1] = data_type; |
||||||
|
memcpy(precode + 2, bytes, length); |
||||||
|
uint8_t * encoded = malloc(sizeof(uint8_t) * (sysex_encoded_length(length + 2))); |
||||||
|
uint16_t encoded_length = sysex_encode(encoded, precode, length + 2); |
||||||
|
uint8_t * array = malloc(sizeof(uint8_t) * (encoded_length + 5)); |
||||||
|
array[0] = 0xF0; |
||||||
|
array[1] = 0x00; |
||||||
|
array[2] = 0x00; |
||||||
|
array[3] = 0x00; |
||||||
|
array[encoded_length + 4] = 0xF7; |
||||||
|
memcpy(array + 4, encoded, encoded_length); |
||||||
|
midi_send_array(&midi_device, encoded_length + 5, array); |
||||||
|
|
||||||
|
// SEND_STRING("\nTD: ");
|
||||||
|
// for (uint8_t i = 0; i < encoded_length + 5; i++) {
|
||||||
|
// send_byte(array[i]);
|
||||||
|
// SEND_STRING(" ");
|
||||||
|
// }
|
||||||
|
} |
@ -0,0 +1,10 @@ |
|||||||
|
#ifndef _API_SYSEX_H_ |
||||||
|
#define _API_SYSEX_H_ |
||||||
|
|
||||||
|
#include "api.h" |
||||||
|
|
||||||
|
void send_bytes_sysex(uint8_t message_type, uint8_t data_type, uint8_t * bytes, uint16_t length); |
||||||
|
|
||||||
|
#define SEND_BYTES(mt, dt, b, l) send_bytes_sysex(mt, dt, b, l) |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,37 @@ |
|||||||
|
#pragma once |
||||||
|
// Some helpers for controlling gpio pins
|
||||||
|
#include <avr/io.h> |
||||||
|
|
||||||
|
enum { |
||||||
|
PinDirectionInput = 0, |
||||||
|
PinDirectionOutput = 1, |
||||||
|
PinLevelHigh = 1, |
||||||
|
PinLevelLow = 0, |
||||||
|
}; |
||||||
|
|
||||||
|
// ex: pinMode(B0, PinDirectionOutput);
|
||||||
|
static inline void pinMode(uint8_t pin, int mode) { |
||||||
|
uint8_t bv = _BV(pin & 0xf); |
||||||
|
if (mode == PinDirectionOutput) { |
||||||
|
_SFR_IO8((pin >> 4) + 1) |= bv; |
||||||
|
} else { |
||||||
|
_SFR_IO8((pin >> 4) + 1) &= ~bv; |
||||||
|
_SFR_IO8((pin >> 4) + 2) &= ~bv; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// ex: digitalWrite(B0, PinLevelHigh);
|
||||||
|
static inline void digitalWrite(uint8_t pin, int mode) { |
||||||
|
uint8_t bv = _BV(pin & 0xf); |
||||||
|
if (mode == PinLevelHigh) { |
||||||
|
_SFR_IO8((pin >> 4) + 2) |= bv; |
||||||
|
} else { |
||||||
|
_SFR_IO8((pin >> 4) + 2) &= ~bv; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Return true if the pin is HIGH
|
||||||
|
// digitalRead(B0)
|
||||||
|
static inline bool digitalRead(uint8_t pin) { |
||||||
|
return _SFR_IO8(pin >> 4) & _BV(pin & 0xf); |
||||||
|
} |
@ -0,0 +1,254 @@ |
|||||||
|
#include "process_printer.h" |
||||||
|
#include "action_util.h" |
||||||
|
|
||||||
|
bool printing_enabled = false; |
||||||
|
uint8_t character_shift = 0; |
||||||
|
|
||||||
|
void enabled_printing() { |
||||||
|
printing_enabled = true; |
||||||
|
serial_init(); |
||||||
|
} |
||||||
|
|
||||||
|
void disable_printing() { |
||||||
|
printing_enabled = false; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29}; |
||||||
|
|
||||||
|
// uint8_t keycode_to_ascii[0xFF][2];
|
||||||
|
|
||||||
|
// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
|
||||||
|
|
||||||
|
void print_char(char c) { |
||||||
|
USB_Disable(); |
||||||
|
serial_send(c); |
||||||
|
USB_Init(); |
||||||
|
} |
||||||
|
|
||||||
|
void print_box_string(uint8_t text[]) { |
||||||
|
uint8_t len = strlen(text); |
||||||
|
uint8_t out[len * 3 + 8]; |
||||||
|
out[0] = 0xDA; |
||||||
|
for (uint8_t i = 0; i < len; i++) { |
||||||
|
out[i+1] = 0xC4; |
||||||
|
} |
||||||
|
out[len + 1] = 0xBF; |
||||||
|
out[len + 2] = '\n'; |
||||||
|
|
||||||
|
out[len + 3] = 0xB3; |
||||||
|
for (uint8_t i = 0; i < len; i++) { |
||||||
|
out[len + 4 + i] = text[i]; |
||||||
|
} |
||||||
|
out[len * 2 + 4] = 0xB3; |
||||||
|
out[len * 2 + 5] = '\n'; |
||||||
|
|
||||||
|
|
||||||
|
out[len * 2 + 6] = 0xC0; |
||||||
|
for (uint8_t i = 0; i < len; i++) { |
||||||
|
out[len * 2 + 7 + i] = 0xC4; |
||||||
|
} |
||||||
|
out[len * 3 + 7] = 0xD9; |
||||||
|
out[len * 3 + 8] = '\n'; |
||||||
|
|
||||||
|
print_string(out);
|
||||||
|
} |
||||||
|
|
||||||
|
void print_string(char c[]) { |
||||||
|
for(uint8_t i = 0; i < strlen(c); i++) |
||||||
|
print_char(c[i]); |
||||||
|
} |
||||||
|
|
||||||
|
bool process_printer(uint16_t keycode, keyrecord_t *record) { |
||||||
|
if (keycode == PRINT_ON) { |
||||||
|
enabled_printing(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (keycode == PRINT_OFF) { |
||||||
|
disable_printing(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (printing_enabled) { |
||||||
|
switch(keycode) { |
||||||
|
case KC_EXLM ... KC_RPRN: |
||||||
|
case KC_UNDS: |
||||||
|
case KC_PLUS: |
||||||
|
case KC_LCBR: |
||||||
|
case KC_RCBR: |
||||||
|
case KC_PIPE: |
||||||
|
case KC_TILD: |
||||||
|
keycode &= 0xFF; |
||||||
|
case KC_LSFT: |
||||||
|
case KC_RSFT: |
||||||
|
if (record->event.pressed) { |
||||||
|
character_shift++; |
||||||
|
} else { |
||||||
|
character_shift--; |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
switch(keycode) { |
||||||
|
case KC_F1: |
||||||
|
if (record->event.pressed) { |
||||||
|
print_box_string("This is a line of text!"); |
||||||
|
} |
||||||
|
return false; |
||||||
|
case KC_ESC: |
||||||
|
if (record->event.pressed) { |
||||||
|
print_char(0x1B); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_SPC: |
||||||
|
if (record->event.pressed) { |
||||||
|
print_char(0x20); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_A ... KC_Z: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x41 + (keycode - KC_A)); |
||||||
|
} else { |
||||||
|
print_char(0x61 + (keycode - KC_A)); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_1 ... KC_0: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(shifted_numbers[keycode - KC_1]); |
||||||
|
} else { |
||||||
|
print_char(0x30 + ((keycode - KC_1 + 1) % 10)); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_ENT: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x0C); |
||||||
|
} else { |
||||||
|
print_char(0x0A); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_BSPC: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x18); |
||||||
|
} else { |
||||||
|
print_char(0x1A); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_DOT: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x3E); |
||||||
|
} else { |
||||||
|
print_char(0x2E); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_COMM: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x3C); |
||||||
|
} else { |
||||||
|
print_char(0x2C); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_SLSH: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x3F); |
||||||
|
} else { |
||||||
|
print_char(0x2F); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_QUOT: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x22); |
||||||
|
} else { |
||||||
|
print_char(0x27); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_GRV: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7E); |
||||||
|
} else { |
||||||
|
print_char(0x60); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_MINS: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x5F); |
||||||
|
} else { |
||||||
|
print_char(0x2D); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_EQL: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x2B); |
||||||
|
} else { |
||||||
|
print_char(0x3D); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_LBRC: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7B); |
||||||
|
} else { |
||||||
|
print_char(0x5B); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_RBRC: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7D); |
||||||
|
} else { |
||||||
|
print_char(0x5D); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_BSLS: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7C); |
||||||
|
} else { |
||||||
|
print_char(0x5C); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
#ifndef PROCESS_PRINTER_H |
||||||
|
#define PROCESS_PRINTER_H |
||||||
|
|
||||||
|
#include "quantum.h" |
||||||
|
|
||||||
|
#include "protocol/serial.h" |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,260 @@ |
|||||||
|
#include "process_printer.h" |
||||||
|
#include "action_util.h" |
||||||
|
|
||||||
|
bool printing_enabled = false; |
||||||
|
uint8_t character_shift = 0; |
||||||
|
|
||||||
|
#define SERIAL_PIN_DDR DDRD |
||||||
|
#define SERIAL_PIN_PORT PORTD |
||||||
|
#define SERIAL_PIN_MASK _BV(PD3) |
||||||
|
#define SERIAL_DELAY 52 |
||||||
|
|
||||||
|
inline static |
||||||
|
void serial_delay(void) { |
||||||
|
_delay_us(SERIAL_DELAY); |
||||||
|
} |
||||||
|
|
||||||
|
inline static |
||||||
|
void serial_high(void) { |
||||||
|
SERIAL_PIN_PORT |= SERIAL_PIN_MASK; |
||||||
|
} |
||||||
|
|
||||||
|
inline static |
||||||
|
void serial_low(void) { |
||||||
|
SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK; |
||||||
|
} |
||||||
|
|
||||||
|
inline static |
||||||
|
void serial_output(void) { |
||||||
|
SERIAL_PIN_DDR |= SERIAL_PIN_MASK; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void enabled_printing() { |
||||||
|
printing_enabled = true; |
||||||
|
serial_output(); |
||||||
|
serial_high(); |
||||||
|
} |
||||||
|
|
||||||
|
void disable_printing() { |
||||||
|
printing_enabled = false; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t shifted_numbers[10] = {0x21, 0x40, 0x23, 0x24, 0x25, 0x5E, 0x26, 0x2A, 0x28, 0x29}; |
||||||
|
|
||||||
|
// uint8_t keycode_to_ascii[0xFF][2];
|
||||||
|
|
||||||
|
// keycode_to_ascii[KC_MINS] = {0x2D, 0x5F};
|
||||||
|
|
||||||
|
void print_char(char c) { |
||||||
|
uint8_t b = 8; |
||||||
|
serial_output(); |
||||||
|
while( b-- ) { |
||||||
|
if(c & (1 << b)) { |
||||||
|
serial_high(); |
||||||
|
} else { |
||||||
|
serial_low(); |
||||||
|
} |
||||||
|
serial_delay(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void print_string(char c[]) { |
||||||
|
for(uint8_t i = 0; i < strlen(c); i++) |
||||||
|
print_char(c[i]); |
||||||
|
} |
||||||
|
|
||||||
|
bool process_printer(uint16_t keycode, keyrecord_t *record) { |
||||||
|
if (keycode == PRINT_ON) { |
||||||
|
enabled_printing(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
if (keycode == PRINT_OFF) { |
||||||
|
disable_printing(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (printing_enabled) { |
||||||
|
switch(keycode) { |
||||||
|
case KC_EXLM ... KC_RPRN: |
||||||
|
case KC_UNDS: |
||||||
|
case KC_PLUS: |
||||||
|
case KC_LCBR: |
||||||
|
case KC_RCBR: |
||||||
|
case KC_PIPE: |
||||||
|
case KC_TILD: |
||||||
|
keycode &= 0xFF; |
||||||
|
case KC_LSFT: |
||||||
|
case KC_RSFT: |
||||||
|
if (record->event.pressed) { |
||||||
|
character_shift++; |
||||||
|
} else { |
||||||
|
character_shift--; |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
switch(keycode) { |
||||||
|
case KC_F1: |
||||||
|
if (record->event.pressed) { |
||||||
|
print_string("This is a line of text!\n\n\n"); |
||||||
|
} |
||||||
|
return false; |
||||||
|
case KC_ESC: |
||||||
|
if (record->event.pressed) { |
||||||
|
print_char(0x1B); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_SPC: |
||||||
|
if (record->event.pressed) { |
||||||
|
print_char(0x20); |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_A ... KC_Z: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x41 + (keycode - KC_A)); |
||||||
|
} else { |
||||||
|
print_char(0x61 + (keycode - KC_A)); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_1 ... KC_0: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(shifted_numbers[keycode - KC_1]); |
||||||
|
} else { |
||||||
|
print_char(0x30 + ((keycode - KC_1 + 1) % 10)); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_ENT: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x0C); |
||||||
|
} else { |
||||||
|
print_char(0x0A); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_BSPC: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x18); |
||||||
|
} else { |
||||||
|
print_char(0x1A); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_DOT: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x3E); |
||||||
|
} else { |
||||||
|
print_char(0x2E); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_COMM: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x3C); |
||||||
|
} else { |
||||||
|
print_char(0x2C); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_SLSH: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x3F); |
||||||
|
} else { |
||||||
|
print_char(0x2F); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_QUOT: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x22); |
||||||
|
} else { |
||||||
|
print_char(0x27); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_GRV: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7E); |
||||||
|
} else { |
||||||
|
print_char(0x60); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_MINS: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x5F); |
||||||
|
} else { |
||||||
|
print_char(0x2D); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_EQL: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x2B); |
||||||
|
} else { |
||||||
|
print_char(0x3D); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_LBRC: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7B); |
||||||
|
} else { |
||||||
|
print_char(0x5B); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_RBRC: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7D); |
||||||
|
} else { |
||||||
|
print_char(0x5D); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
case KC_BSLS: |
||||||
|
if (record->event.pressed) { |
||||||
|
if (character_shift) { |
||||||
|
print_char(0x7C); |
||||||
|
} else { |
||||||
|
print_char(0x5C); |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,805 @@ |
|||||||
|
#include "adafruit_ble.h" |
||||||
|
#include <stdio.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <alloca.h> |
||||||
|
#include <util/delay.h> |
||||||
|
#include <util/atomic.h> |
||||||
|
#include "debug.h" |
||||||
|
#include "pincontrol.h" |
||||||
|
#include "timer.h" |
||||||
|
#include "action_util.h" |
||||||
|
#include "ringbuffer.hpp" |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
// These are the pin assignments for the 32u4 boards.
|
||||||
|
// You may define them to something else in your config.h
|
||||||
|
// if yours is wired up differently.
|
||||||
|
#ifndef AdafruitBleResetPin |
||||||
|
#define AdafruitBleResetPin D4 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef AdafruitBleCSPin |
||||||
|
#define AdafruitBleCSPin B4 |
||||||
|
#endif |
||||||
|
|
||||||
|
#ifndef AdafruitBleIRQPin |
||||||
|
#define AdafruitBleIRQPin E6 |
||||||
|
#endif |
||||||
|
|
||||||
|
|
||||||
|
#define SAMPLE_BATTERY |
||||||
|
#define ConnectionUpdateInterval 1000 /* milliseconds */ |
||||||
|
|
||||||
|
static struct { |
||||||
|
bool is_connected; |
||||||
|
bool initialized; |
||||||
|
bool configured; |
||||||
|
|
||||||
|
#define ProbedEvents 1 |
||||||
|
#define UsingEvents 2 |
||||||
|
bool event_flags; |
||||||
|
|
||||||
|
#ifdef SAMPLE_BATTERY |
||||||
|
uint16_t last_battery_update; |
||||||
|
uint32_t vbat; |
||||||
|
#endif |
||||||
|
uint16_t last_connection_update; |
||||||
|
} state; |
||||||
|
|
||||||
|
// Commands are encoded using SDEP and sent via SPI
|
||||||
|
// https://github.com/adafruit/Adafruit_BluefruitLE_nRF51/blob/master/SDEP.md
|
||||||
|
|
||||||
|
#define SdepMaxPayload 16 |
||||||
|
struct sdep_msg { |
||||||
|
uint8_t type; |
||||||
|
uint8_t cmd_low; |
||||||
|
uint8_t cmd_high; |
||||||
|
struct __attribute__((packed)) { |
||||||
|
uint8_t len:7; |
||||||
|
uint8_t more:1; |
||||||
|
}; |
||||||
|
uint8_t payload[SdepMaxPayload]; |
||||||
|
} __attribute__((packed)); |
||||||
|
|
||||||
|
// The recv latency is relatively high, so when we're hammering keys quickly,
|
||||||
|
// we want to avoid waiting for the responses in the matrix loop. We maintain
|
||||||
|
// a short queue for that. Since there is quite a lot of space overhead for
|
||||||
|
// the AT command representation wrapped up in SDEP, we queue the minimal
|
||||||
|
// information here.
|
||||||
|
|
||||||
|
enum queue_type { |
||||||
|
QTKeyReport, // 1-byte modifier + 6-byte key report
|
||||||
|
QTConsumer, // 16-bit key code
|
||||||
|
#ifdef MOUSE_ENABLE |
||||||
|
QTMouseMove, // 4-byte mouse report
|
||||||
|
#endif |
||||||
|
}; |
||||||
|
|
||||||
|
struct queue_item { |
||||||
|
enum queue_type queue_type; |
||||||
|
uint16_t added; |
||||||
|
union __attribute__((packed)) { |
||||||
|
struct __attribute__((packed)) { |
||||||
|
uint8_t modifier; |
||||||
|
uint8_t keys[6]; |
||||||
|
} key; |
||||||
|
|
||||||
|
uint16_t consumer; |
||||||
|
struct __attribute__((packed)) { |
||||||
|
uint8_t x, y, scroll, pan; |
||||||
|
} mousemove; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
// Items that we wish to send
|
||||||
|
static RingBuffer<queue_item, 40> send_buf; |
||||||
|
// Pending response; while pending, we can't send any more requests.
|
||||||
|
// This records the time at which we sent the command for which we
|
||||||
|
// are expecting a response.
|
||||||
|
static RingBuffer<uint16_t, 2> resp_buf; |
||||||
|
|
||||||
|
static bool process_queue_item(struct queue_item *item, uint16_t timeout); |
||||||
|
|
||||||
|
enum sdep_type { |
||||||
|
SdepCommand = 0x10, |
||||||
|
SdepResponse = 0x20, |
||||||
|
SdepAlert = 0x40, |
||||||
|
SdepError = 0x80, |
||||||
|
SdepSlaveNotReady = 0xfe, // Try again later
|
||||||
|
SdepSlaveOverflow = 0xff, // You read more data than is available
|
||||||
|
}; |
||||||
|
|
||||||
|
enum ble_cmd { |
||||||
|
BleInitialize = 0xbeef, |
||||||
|
BleAtWrapper = 0x0a00, |
||||||
|
BleUartTx = 0x0a01, |
||||||
|
BleUartRx = 0x0a02, |
||||||
|
}; |
||||||
|
|
||||||
|
enum ble_system_event_bits { |
||||||
|
BleSystemConnected = 0, |
||||||
|
BleSystemDisconnected = 1, |
||||||
|
BleSystemUartRx = 8, |
||||||
|
BleSystemMidiRx = 10, |
||||||
|
}; |
||||||
|
|
||||||
|
// The SDEP.md file says 2MHz but the web page and the sample driver
|
||||||
|
// both use 4MHz
|
||||||
|
#define SpiBusSpeed 4000000 |
||||||
|
|
||||||
|
#define SdepTimeout 150 /* milliseconds */ |
||||||
|
#define SdepShortTimeout 10 /* milliseconds */ |
||||||
|
#define SdepBackOff 25 /* microseconds */ |
||||||
|
#define BatteryUpdateInterval 10000 /* milliseconds */ |
||||||
|
|
||||||
|
static bool at_command(const char *cmd, char *resp, uint16_t resplen, |
||||||
|
bool verbose, uint16_t timeout = SdepTimeout); |
||||||
|
static bool at_command_P(const char *cmd, char *resp, uint16_t resplen, |
||||||
|
bool verbose = false); |
||||||
|
|
||||||
|
struct SPI_Settings { |
||||||
|
uint8_t spcr, spsr; |
||||||
|
}; |
||||||
|
|
||||||
|
static struct SPI_Settings spi; |
||||||
|
|
||||||
|
// Initialize 4Mhz MSBFIRST MODE0
|
||||||
|
void SPI_init(struct SPI_Settings *spi) { |
||||||
|
spi->spcr = _BV(SPE) | _BV(MSTR); |
||||||
|
spi->spsr = _BV(SPI2X); |
||||||
|
|
||||||
|
static_assert(SpiBusSpeed == F_CPU / 2, "hard coded at 4Mhz"); |
||||||
|
|
||||||
|
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { |
||||||
|
// Ensure that SS is OUTPUT High
|
||||||
|
digitalWrite(B0, PinLevelHigh); |
||||||
|
pinMode(B0, PinDirectionOutput); |
||||||
|
|
||||||
|
SPCR |= _BV(MSTR); |
||||||
|
SPCR |= _BV(SPE); |
||||||
|
pinMode(B1 /* SCK */, PinDirectionOutput); |
||||||
|
pinMode(B2 /* MOSI */, PinDirectionOutput); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static inline void SPI_begin(struct SPI_Settings*spi) { |
||||||
|
SPCR = spi->spcr; |
||||||
|
SPSR = spi->spsr; |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint8_t SPI_TransferByte(uint8_t data) { |
||||||
|
SPDR = data; |
||||||
|
asm volatile("nop"); |
||||||
|
while (!(SPSR & _BV(SPIF))) { |
||||||
|
; // wait
|
||||||
|
} |
||||||
|
return SPDR; |
||||||
|
} |
||||||
|
|
||||||
|
static inline void spi_send_bytes(const uint8_t *buf, uint8_t len) { |
||||||
|
if (len == 0) return; |
||||||
|
const uint8_t *end = buf + len; |
||||||
|
while (buf < end) { |
||||||
|
SPDR = *buf; |
||||||
|
while (!(SPSR & _BV(SPIF))) { |
||||||
|
; // wait
|
||||||
|
} |
||||||
|
++buf; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint16_t spi_read_byte(void) { |
||||||
|
return SPI_TransferByte(0x00 /* dummy */); |
||||||
|
} |
||||||
|
|
||||||
|
static inline void spi_recv_bytes(uint8_t *buf, uint8_t len) { |
||||||
|
const uint8_t *end = buf + len; |
||||||
|
if (len == 0) return; |
||||||
|
while (buf < end) { |
||||||
|
SPDR = 0; // write a dummy to initiate read
|
||||||
|
while (!(SPSR & _BV(SPIF))) { |
||||||
|
; // wait
|
||||||
|
} |
||||||
|
*buf = SPDR; |
||||||
|
++buf; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#if 0 |
||||||
|
static void dump_pkt(const struct sdep_msg *msg) { |
||||||
|
print("pkt: type="); |
||||||
|
print_hex8(msg->type); |
||||||
|
print(" cmd="); |
||||||
|
print_hex8(msg->cmd_high); |
||||||
|
print_hex8(msg->cmd_low); |
||||||
|
print(" len="); |
||||||
|
print_hex8(msg->len); |
||||||
|
print(" more="); |
||||||
|
print_hex8(msg->more); |
||||||
|
print("\n"); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
// Send a single SDEP packet
|
||||||
|
static bool sdep_send_pkt(const struct sdep_msg *msg, uint16_t timeout) { |
||||||
|
SPI_begin(&spi); |
||||||
|
|
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelLow); |
||||||
|
uint16_t timerStart = timer_read(); |
||||||
|
bool success = false; |
||||||
|
bool ready = false; |
||||||
|
|
||||||
|
do { |
||||||
|
ready = SPI_TransferByte(msg->type) != SdepSlaveNotReady; |
||||||
|
if (ready) { |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Release it and let it initialize
|
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelHigh); |
||||||
|
_delay_us(SdepBackOff); |
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelLow); |
||||||
|
} while (timer_elapsed(timerStart) < timeout); |
||||||
|
|
||||||
|
if (ready) { |
||||||
|
// Slave is ready; send the rest of the packet
|
||||||
|
spi_send_bytes(&msg->cmd_low, |
||||||
|
sizeof(*msg) - (1 + sizeof(msg->payload)) + msg->len); |
||||||
|
success = true; |
||||||
|
} |
||||||
|
|
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelHigh); |
||||||
|
|
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
static inline void sdep_build_pkt(struct sdep_msg *msg, uint16_t command, |
||||||
|
const uint8_t *payload, uint8_t len, |
||||||
|
bool moredata) { |
||||||
|
msg->type = SdepCommand; |
||||||
|
msg->cmd_low = command & 0xff; |
||||||
|
msg->cmd_high = command >> 8; |
||||||
|
msg->len = len; |
||||||
|
msg->more = (moredata && len == SdepMaxPayload) ? 1 : 0; |
||||||
|
|
||||||
|
static_assert(sizeof(*msg) == 20, "msg is correctly packed"); |
||||||
|
|
||||||
|
memcpy(msg->payload, payload, len); |
||||||
|
} |
||||||
|
|
||||||
|
// Read a single SDEP packet
|
||||||
|
static bool sdep_recv_pkt(struct sdep_msg *msg, uint16_t timeout) { |
||||||
|
bool success = false; |
||||||
|
uint16_t timerStart = timer_read(); |
||||||
|
bool ready = false; |
||||||
|
|
||||||
|
do { |
||||||
|
ready = digitalRead(AdafruitBleIRQPin); |
||||||
|
if (ready) { |
||||||
|
break; |
||||||
|
} |
||||||
|
_delay_us(1); |
||||||
|
} while (timer_elapsed(timerStart) < timeout); |
||||||
|
|
||||||
|
if (ready) { |
||||||
|
SPI_begin(&spi); |
||||||
|
|
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelLow); |
||||||
|
|
||||||
|
do { |
||||||
|
// Read the command type, waiting for the data to be ready
|
||||||
|
msg->type = spi_read_byte(); |
||||||
|
if (msg->type == SdepSlaveNotReady || msg->type == SdepSlaveOverflow) { |
||||||
|
// Release it and let it initialize
|
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelHigh); |
||||||
|
_delay_us(SdepBackOff); |
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelLow); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// Read the rest of the header
|
||||||
|
spi_recv_bytes(&msg->cmd_low, sizeof(*msg) - (1 + sizeof(msg->payload))); |
||||||
|
|
||||||
|
// and get the payload if there is any
|
||||||
|
if (msg->len <= SdepMaxPayload) { |
||||||
|
spi_recv_bytes(msg->payload, msg->len); |
||||||
|
} |
||||||
|
success = true; |
||||||
|
break; |
||||||
|
} while (timer_elapsed(timerStart) < timeout); |
||||||
|
|
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelHigh); |
||||||
|
} |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
static void resp_buf_read_one(bool greedy) { |
||||||
|
uint16_t last_send; |
||||||
|
if (!resp_buf.peek(last_send)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (digitalRead(AdafruitBleIRQPin)) { |
||||||
|
struct sdep_msg msg; |
||||||
|
|
||||||
|
again: |
||||||
|
if (sdep_recv_pkt(&msg, SdepTimeout)) { |
||||||
|
if (!msg.more) { |
||||||
|
// We got it; consume this entry
|
||||||
|
resp_buf.get(last_send); |
||||||
|
dprintf("recv latency %dms\n", TIMER_DIFF_16(timer_read(), last_send)); |
||||||
|
} |
||||||
|
|
||||||
|
if (greedy && resp_buf.peek(last_send) && digitalRead(AdafruitBleIRQPin)) { |
||||||
|
goto again; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} else if (timer_elapsed(last_send) > SdepTimeout * 2) { |
||||||
|
dprintf("waiting_for_result: timeout, resp_buf size %d\n", |
||||||
|
(int)resp_buf.size()); |
||||||
|
|
||||||
|
// Timed out: consume this entry
|
||||||
|
resp_buf.get(last_send); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void send_buf_send_one(uint16_t timeout = SdepTimeout) { |
||||||
|
struct queue_item item; |
||||||
|
|
||||||
|
// Don't send anything more until we get an ACK
|
||||||
|
if (!resp_buf.empty()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (!send_buf.peek(item)) { |
||||||
|
return; |
||||||
|
} |
||||||
|
if (process_queue_item(&item, timeout)) { |
||||||
|
// commit that peek
|
||||||
|
send_buf.get(item); |
||||||
|
dprintf("send_buf_send_one: have %d remaining\n", (int)send_buf.size()); |
||||||
|
} else { |
||||||
|
dprint("failed to send, will retry\n"); |
||||||
|
_delay_ms(SdepTimeout); |
||||||
|
resp_buf_read_one(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static void resp_buf_wait(const char *cmd) { |
||||||
|
bool didPrint = false; |
||||||
|
while (!resp_buf.empty()) { |
||||||
|
if (!didPrint) { |
||||||
|
dprintf("wait on buf for %s\n", cmd); |
||||||
|
didPrint = true; |
||||||
|
} |
||||||
|
resp_buf_read_one(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
static bool ble_init(void) { |
||||||
|
state.initialized = false; |
||||||
|
state.configured = false; |
||||||
|
state.is_connected = false; |
||||||
|
|
||||||
|
pinMode(AdafruitBleIRQPin, PinDirectionInput); |
||||||
|
pinMode(AdafruitBleCSPin, PinDirectionOutput); |
||||||
|
digitalWrite(AdafruitBleCSPin, PinLevelHigh); |
||||||
|
|
||||||
|
SPI_init(&spi); |
||||||
|
|
||||||
|
// Perform a hardware reset
|
||||||
|
pinMode(AdafruitBleResetPin, PinDirectionOutput); |
||||||
|
digitalWrite(AdafruitBleResetPin, PinLevelHigh); |
||||||
|
digitalWrite(AdafruitBleResetPin, PinLevelLow); |
||||||
|
_delay_ms(10); |
||||||
|
digitalWrite(AdafruitBleResetPin, PinLevelHigh); |
||||||
|
|
||||||
|
_delay_ms(1000); // Give it a second to initialize
|
||||||
|
|
||||||
|
state.initialized = true; |
||||||
|
return state.initialized; |
||||||
|
} |
||||||
|
|
||||||
|
static inline uint8_t min(uint8_t a, uint8_t b) { |
||||||
|
return a < b ? a : b; |
||||||
|
} |
||||||
|
|
||||||
|
static bool read_response(char *resp, uint16_t resplen, bool verbose) { |
||||||
|
char *dest = resp; |
||||||
|
char *end = dest + resplen; |
||||||
|
|
||||||
|
while (true) { |
||||||
|
struct sdep_msg msg; |
||||||
|
|
||||||
|
if (!sdep_recv_pkt(&msg, 2 * SdepTimeout)) { |
||||||
|
dprint("sdep_recv_pkt failed\n"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (msg.type != SdepResponse) { |
||||||
|
*resp = 0; |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t len = min(msg.len, end - dest); |
||||||
|
if (len > 0) { |
||||||
|
memcpy(dest, msg.payload, len); |
||||||
|
dest += len; |
||||||
|
} |
||||||
|
|
||||||
|
if (!msg.more) { |
||||||
|
// No more data is expected!
|
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Ensure the response is NUL terminated
|
||||||
|
*dest = 0; |
||||||
|
|
||||||
|
// "Parse" the result text; we want to snip off the trailing OK or ERROR line
|
||||||
|
// Rewind past the possible trailing CRLF so that we can strip it
|
||||||
|
--dest; |
||||||
|
while (dest > resp && (dest[0] == '\n' || dest[0] == '\r')) { |
||||||
|
*dest = 0; |
||||||
|
--dest; |
||||||
|
} |
||||||
|
|
||||||
|
// Look back for start of preceeding line
|
||||||
|
char *last_line = strrchr(resp, '\n'); |
||||||
|
if (last_line) { |
||||||
|
++last_line; |
||||||
|
} else { |
||||||
|
last_line = resp; |
||||||
|
} |
||||||
|
|
||||||
|
bool success = false; |
||||||
|
static const char kOK[] PROGMEM = "OK"; |
||||||
|
|
||||||
|
success = !strcmp_P(last_line, kOK ); |
||||||
|
|
||||||
|
if (verbose || !success) { |
||||||
|
dprintf("result: %s\n", resp); |
||||||
|
} |
||||||
|
return success; |
||||||
|
} |
||||||
|
|
||||||
|
static bool at_command(const char *cmd, char *resp, uint16_t resplen, |
||||||
|
bool verbose, uint16_t timeout) { |
||||||
|
const char *end = cmd + strlen(cmd); |
||||||
|
struct sdep_msg msg; |
||||||
|
|
||||||
|
if (verbose) { |
||||||
|
dprintf("ble send: %s\n", cmd); |
||||||
|
} |
||||||
|
|
||||||
|
if (resp) { |
||||||
|
// They want to decode the response, so we need to flush and wait
|
||||||
|
// for all pending I/O to finish before we start this one, so
|
||||||
|
// that we don't confuse the results
|
||||||
|
resp_buf_wait(cmd); |
||||||
|
*resp = 0; |
||||||
|
} |
||||||
|
|
||||||
|
// Fragment the command into a series of SDEP packets
|
||||||
|
while (end - cmd > SdepMaxPayload) { |
||||||
|
sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, SdepMaxPayload, true); |
||||||
|
if (!sdep_send_pkt(&msg, timeout)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
cmd += SdepMaxPayload; |
||||||
|
} |
||||||
|
|
||||||
|
sdep_build_pkt(&msg, BleAtWrapper, (uint8_t *)cmd, end - cmd, false); |
||||||
|
if (!sdep_send_pkt(&msg, timeout)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (resp == NULL) { |
||||||
|
auto now = timer_read(); |
||||||
|
while (!resp_buf.enqueue(now)) { |
||||||
|
resp_buf_read_one(false); |
||||||
|
} |
||||||
|
auto later = timer_read(); |
||||||
|
if (TIMER_DIFF_16(later, now) > 0) { |
||||||
|
dprintf("waited %dms for resp_buf\n", TIMER_DIFF_16(later, now)); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
return read_response(resp, resplen, verbose); |
||||||
|
} |
||||||
|
|
||||||
|
bool at_command_P(const char *cmd, char *resp, uint16_t resplen, bool verbose) { |
||||||
|
auto cmdbuf = (char *)alloca(strlen_P(cmd) + 1); |
||||||
|
strcpy_P(cmdbuf, cmd); |
||||||
|
return at_command(cmdbuf, resp, resplen, verbose); |
||||||
|
} |
||||||
|
|
||||||
|
bool adafruit_ble_is_connected(void) { |
||||||
|
return state.is_connected; |
||||||
|
} |
||||||
|
|
||||||
|
bool adafruit_ble_enable_keyboard(void) { |
||||||
|
char resbuf[128]; |
||||||
|
|
||||||
|
if (!state.initialized && !ble_init()) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
state.configured = false; |
||||||
|
|
||||||
|
// Disable command echo
|
||||||
|
static const char kEcho[] PROGMEM = "ATE=0"; |
||||||
|
// Make the advertised name match the keyboard
|
||||||
|
static const char kGapDevName[] PROGMEM = |
||||||
|
"AT+GAPDEVNAME=" STR(PRODUCT) " " STR(DESCRIPTION); |
||||||
|
// Turn on keyboard support
|
||||||
|
static const char kHidEnOn[] PROGMEM = "AT+BLEHIDEN=1"; |
||||||
|
|
||||||
|
// Adjust intervals to improve latency. This causes the "central"
|
||||||
|
// system (computer/tablet) to poll us every 10-30 ms. We can't
|
||||||
|
// set a smaller value than 10ms, and 30ms seems to be the natural
|
||||||
|
// processing time on my macbook. Keeping it constrained to that
|
||||||
|
// feels reasonable to type to.
|
||||||
|
static const char kGapIntervals[] PROGMEM = "AT+GAPINTERVALS=10,30,,"; |
||||||
|
|
||||||
|
// Reset the device so that it picks up the above changes
|
||||||
|
static const char kATZ[] PROGMEM = "ATZ"; |
||||||
|
|
||||||
|
// Turn down the power level a bit
|
||||||
|
static const char kPower[] PROGMEM = "AT+BLEPOWERLEVEL=-12"; |
||||||
|
static PGM_P const configure_commands[] PROGMEM = { |
||||||
|
kEcho, |
||||||
|
kGapIntervals, |
||||||
|
kGapDevName, |
||||||
|
kHidEnOn, |
||||||
|
kPower, |
||||||
|
kATZ, |
||||||
|
}; |
||||||
|
|
||||||
|
uint8_t i; |
||||||
|
for (i = 0; i < sizeof(configure_commands) / sizeof(configure_commands[0]); |
||||||
|
++i) { |
||||||
|
PGM_P cmd; |
||||||
|
memcpy_P(&cmd, configure_commands + i, sizeof(cmd)); |
||||||
|
|
||||||
|
if (!at_command_P(cmd, resbuf, sizeof(resbuf))) { |
||||||
|
dprintf("failed BLE command: %S: %s\n", cmd, resbuf); |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
state.configured = true; |
||||||
|
|
||||||
|
// Check connection status in a little while; allow the ATZ time
|
||||||
|
// to kick in.
|
||||||
|
state.last_connection_update = timer_read(); |
||||||
|
fail: |
||||||
|
return state.configured; |
||||||
|
} |
||||||
|
|
||||||
|
static void set_connected(bool connected) { |
||||||
|
if (connected != state.is_connected) { |
||||||
|
if (connected) { |
||||||
|
print("****** BLE CONNECT!!!!\n"); |
||||||
|
} else { |
||||||
|
print("****** BLE DISCONNECT!!!!\n"); |
||||||
|
} |
||||||
|
state.is_connected = connected; |
||||||
|
|
||||||
|
// TODO: if modifiers are down on the USB interface and
|
||||||
|
// we cut over to BLE or vice versa, they will remain stuck.
|
||||||
|
// This feels like a good point to do something like clearing
|
||||||
|
// the keyboard and/or generating a fake all keys up message.
|
||||||
|
// However, I've noticed that it takes a couple of seconds
|
||||||
|
// for macOS to to start recognizing key presses after BLE
|
||||||
|
// is in the connected state, so I worry that doing that
|
||||||
|
// here may not be good enough.
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void adafruit_ble_task(void) { |
||||||
|
char resbuf[48]; |
||||||
|
|
||||||
|
if (!state.configured && !adafruit_ble_enable_keyboard()) { |
||||||
|
return; |
||||||
|
} |
||||||
|
resp_buf_read_one(true); |
||||||
|
send_buf_send_one(SdepShortTimeout); |
||||||
|
|
||||||
|
if (resp_buf.empty() && (state.event_flags & UsingEvents) && |
||||||
|
digitalRead(AdafruitBleIRQPin)) { |
||||||
|
// Must be an event update
|
||||||
|
if (at_command_P(PSTR("AT+EVENTSTATUS"), resbuf, sizeof(resbuf))) { |
||||||
|
uint32_t mask = strtoul(resbuf, NULL, 16); |
||||||
|
|
||||||
|
if (mask & BleSystemConnected) { |
||||||
|
set_connected(true); |
||||||
|
} else if (mask & BleSystemDisconnected) { |
||||||
|
set_connected(false); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (timer_elapsed(state.last_connection_update) > ConnectionUpdateInterval) { |
||||||
|
bool shouldPoll = true; |
||||||
|
if (!(state.event_flags & ProbedEvents)) { |
||||||
|
// Request notifications about connection status changes.
|
||||||
|
// This only works in SPIFRIEND firmware > 0.6.7, which is why
|
||||||
|
// we check for this conditionally here.
|
||||||
|
// Note that at the time of writing, HID reports only work correctly
|
||||||
|
// with Apple products on firmware version 0.6.7!
|
||||||
|
// https://forums.adafruit.com/viewtopic.php?f=8&t=104052
|
||||||
|
if (at_command_P(PSTR("AT+EVENTENABLE=0x1"), resbuf, sizeof(resbuf))) { |
||||||
|
at_command_P(PSTR("AT+EVENTENABLE=0x2"), resbuf, sizeof(resbuf)); |
||||||
|
state.event_flags |= UsingEvents; |
||||||
|
} |
||||||
|
state.event_flags |= ProbedEvents; |
||||||
|
|
||||||
|
// leave shouldPoll == true so that we check at least once
|
||||||
|
// before relying solely on events
|
||||||
|
} else { |
||||||
|
shouldPoll = false; |
||||||
|
} |
||||||
|
|
||||||
|
static const char kGetConn[] PROGMEM = "AT+GAPGETCONN"; |
||||||
|
state.last_connection_update = timer_read(); |
||||||
|
|
||||||
|
if (at_command_P(kGetConn, resbuf, sizeof(resbuf))) { |
||||||
|
set_connected(atoi(resbuf)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef SAMPLE_BATTERY |
||||||
|
// I don't know if this really does anything useful yet; the reported
|
||||||
|
// voltage level always seems to be around 3200mV. We may want to just rip
|
||||||
|
// this code out.
|
||||||
|
if (timer_elapsed(state.last_battery_update) > BatteryUpdateInterval && |
||||||
|
resp_buf.empty()) { |
||||||
|
state.last_battery_update = timer_read(); |
||||||
|
|
||||||
|
if (at_command_P(PSTR("AT+HWVBAT"), resbuf, sizeof(resbuf))) { |
||||||
|
state.vbat = atoi(resbuf); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
static bool process_queue_item(struct queue_item *item, uint16_t timeout) { |
||||||
|
char cmdbuf[48]; |
||||||
|
char fmtbuf[64]; |
||||||
|
|
||||||
|
// Arrange to re-check connection after keys have settled
|
||||||
|
state.last_connection_update = timer_read(); |
||||||
|
|
||||||
|
#if 1 |
||||||
|
if (TIMER_DIFF_16(state.last_connection_update, item->added) > 0) { |
||||||
|
dprintf("send latency %dms\n", |
||||||
|
TIMER_DIFF_16(state.last_connection_update, item->added)); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
switch (item->queue_type) { |
||||||
|
case QTKeyReport: |
||||||
|
strcpy_P(fmtbuf, |
||||||
|
PSTR("AT+BLEKEYBOARDCODE=%02x-00-%02x-%02x-%02x-%02x-%02x-%02x")); |
||||||
|
snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->key.modifier, |
||||||
|
item->key.keys[0], item->key.keys[1], item->key.keys[2], |
||||||
|
item->key.keys[3], item->key.keys[4], item->key.keys[5]); |
||||||
|
return at_command(cmdbuf, NULL, 0, true, timeout); |
||||||
|
|
||||||
|
case QTConsumer: |
||||||
|
strcpy_P(fmtbuf, PSTR("AT+BLEHIDCONTROLKEY=0x%04x")); |
||||||
|
snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->consumer); |
||||||
|
return at_command(cmdbuf, NULL, 0, true, timeout); |
||||||
|
|
||||||
|
#ifdef MOUSE_ENABLE |
||||||
|
case QTMouseMove: |
||||||
|
strcpy_P(fmtbuf, PSTR("AT+BLEHIDMOUSEMOVE=%d,%d,%d,%d")); |
||||||
|
snprintf(cmdbuf, sizeof(cmdbuf), fmtbuf, item->mousemove.x, |
||||||
|
item->mousemove.y, item->mousemove.scroll, item->mousemove.pan); |
||||||
|
return at_command(cmdbuf, NULL, 0, true, timeout); |
||||||
|
#endif |
||||||
|
default: |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, |
||||||
|
uint8_t nkeys) { |
||||||
|
struct queue_item item; |
||||||
|
bool didWait = false; |
||||||
|
|
||||||
|
item.queue_type = QTKeyReport; |
||||||
|
item.key.modifier = hid_modifier_mask; |
||||||
|
item.added = timer_read(); |
||||||
|
|
||||||
|
while (nkeys >= 0) { |
||||||
|
item.key.keys[0] = keys[0]; |
||||||
|
item.key.keys[1] = nkeys >= 1 ? keys[1] : 0; |
||||||
|
item.key.keys[2] = nkeys >= 2 ? keys[2] : 0; |
||||||
|
item.key.keys[3] = nkeys >= 3 ? keys[3] : 0; |
||||||
|
item.key.keys[4] = nkeys >= 4 ? keys[4] : 0; |
||||||
|
item.key.keys[5] = nkeys >= 5 ? keys[5] : 0; |
||||||
|
|
||||||
|
if (!send_buf.enqueue(item)) { |
||||||
|
if (!didWait) { |
||||||
|
dprint("wait for buf space\n"); |
||||||
|
didWait = true; |
||||||
|
} |
||||||
|
send_buf_send_one(); |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
if (nkeys <= 6) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
nkeys -= 6; |
||||||
|
keys += 6; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool adafruit_ble_send_consumer_key(uint16_t keycode, int hold_duration) { |
||||||
|
struct queue_item item; |
||||||
|
|
||||||
|
item.queue_type = QTConsumer; |
||||||
|
item.consumer = keycode; |
||||||
|
|
||||||
|
while (!send_buf.enqueue(item)) { |
||||||
|
send_buf_send_one(); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef MOUSE_ENABLE |
||||||
|
bool adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, |
||||||
|
int8_t pan) { |
||||||
|
struct queue_item item; |
||||||
|
|
||||||
|
item.queue_type = QTMouseMove; |
||||||
|
item.mousemove.x = x; |
||||||
|
item.mousemove.y = y; |
||||||
|
item.mousemove.scroll = scroll; |
||||||
|
item.mousemove.pan = pan; |
||||||
|
|
||||||
|
while (!send_buf.enqueue(item)) { |
||||||
|
send_buf_send_one(); |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
uint32_t adafruit_ble_read_battery_voltage(void) { |
||||||
|
return state.vbat; |
||||||
|
} |
||||||
|
|
||||||
|
bool adafruit_ble_set_mode_leds(bool on) { |
||||||
|
if (!state.configured) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// The "mode" led is the red blinky one
|
||||||
|
at_command_P(on ? PSTR("AT+HWMODELED=1") : PSTR("AT+HWMODELED=0"), NULL, 0); |
||||||
|
|
||||||
|
// Pin 19 is the blue "connected" LED; turn that off too.
|
||||||
|
// When turning LEDs back on, don't turn that LED on if we're
|
||||||
|
// not connected, as that would be confusing.
|
||||||
|
at_command_P(on && state.is_connected ? PSTR("AT+HWGPIO=19,1") |
||||||
|
: PSTR("AT+HWGPIO=19,0"), |
||||||
|
NULL, 0); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// https://learn.adafruit.com/adafruit-feather-32u4-bluefruit-le/ble-generic#at-plus-blepowerlevel
|
||||||
|
bool adafruit_ble_set_power_level(int8_t level) { |
||||||
|
char cmd[46]; |
||||||
|
if (!state.configured) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
snprintf(cmd, sizeof(cmd), "AT+BLEPOWERLEVEL=%d", level); |
||||||
|
return at_command(cmd, NULL, 0, false); |
||||||
|
} |
@ -0,0 +1,60 @@ |
|||||||
|
/* Bluetooth Low Energy Protocol for QMK.
|
||||||
|
* Author: Wez Furlong, 2016 |
||||||
|
* Supports the Adafruit BLE board built around the nRF51822 chip. |
||||||
|
*/ |
||||||
|
#pragma once |
||||||
|
#ifdef ADAFRUIT_BLE_ENABLE |
||||||
|
#include <stdbool.h> |
||||||
|
#include <stdint.h> |
||||||
|
#include <string.h> |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Instruct the module to enable HID keyboard support and reset */ |
||||||
|
extern bool adafruit_ble_enable_keyboard(void); |
||||||
|
|
||||||
|
/* Query to see if the BLE module is connected */ |
||||||
|
extern bool adafruit_ble_query_is_connected(void); |
||||||
|
|
||||||
|
/* Returns true if we believe that the BLE module is connected.
|
||||||
|
* This uses our cached understanding that is maintained by |
||||||
|
* calling ble_task() periodically. */ |
||||||
|
extern bool adafruit_ble_is_connected(void); |
||||||
|
|
||||||
|
/* Call this periodically to process BLE-originated things */ |
||||||
|
extern void adafruit_ble_task(void); |
||||||
|
|
||||||
|
/* Generates keypress events for a set of keys.
|
||||||
|
* The hid modifier mask specifies the state of the modifier keys for |
||||||
|
* this set of keys. |
||||||
|
* Also sends a key release indicator, so that the keys do not remain |
||||||
|
* held down. */ |
||||||
|
extern bool adafruit_ble_send_keys(uint8_t hid_modifier_mask, uint8_t *keys, |
||||||
|
uint8_t nkeys); |
||||||
|
|
||||||
|
/* Send a consumer keycode, holding it down for the specified duration
|
||||||
|
* (milliseconds) */ |
||||||
|
extern bool adafruit_ble_send_consumer_key(uint16_t keycode, int hold_duration); |
||||||
|
|
||||||
|
#ifdef MOUSE_ENABLE |
||||||
|
/* Send a mouse/wheel movement report.
|
||||||
|
* The parameters are signed and indicate positive of negative direction |
||||||
|
* change. */ |
||||||
|
extern bool adafruit_ble_send_mouse_move(int8_t x, int8_t y, int8_t scroll, |
||||||
|
int8_t pan); |
||||||
|
#endif |
||||||
|
|
||||||
|
/* Compute battery voltage by reading an analog pin.
|
||||||
|
* Returns the integer number of millivolts */ |
||||||
|
extern uint32_t adafruit_ble_read_battery_voltage(void); |
||||||
|
|
||||||
|
extern bool adafruit_ble_set_mode_leds(bool on); |
||||||
|
extern bool adafruit_ble_set_power_level(int8_t level); |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#endif // ADAFRUIT_BLE_ENABLE
|
@ -0,0 +1,66 @@ |
|||||||
|
#pragma once |
||||||
|
// A simple ringbuffer holding Size elements of type T
|
||||||
|
template <typename T, uint8_t Size> |
||||||
|
class RingBuffer { |
||||||
|
protected: |
||||||
|
T buf_[Size]; |
||||||
|
uint8_t head_{0}, tail_{0}; |
||||||
|
public: |
||||||
|
inline uint8_t nextPosition(uint8_t position) { |
||||||
|
return (position + 1) % Size; |
||||||
|
} |
||||||
|
|
||||||
|
inline uint8_t prevPosition(uint8_t position) { |
||||||
|
if (position == 0) { |
||||||
|
return Size - 1; |
||||||
|
} |
||||||
|
return position - 1; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool enqueue(const T &item) { |
||||||
|
static_assert(Size > 1, "RingBuffer size must be > 1"); |
||||||
|
uint8_t next = nextPosition(head_); |
||||||
|
if (next == tail_) { |
||||||
|
// Full
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
buf_[head_] = item; |
||||||
|
head_ = next; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool get(T &dest, bool commit = true) { |
||||||
|
auto tail = tail_; |
||||||
|
if (tail == head_) { |
||||||
|
// No more data
|
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
dest = buf_[tail]; |
||||||
|
tail = nextPosition(tail); |
||||||
|
|
||||||
|
if (commit) { |
||||||
|
tail_ = tail; |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool empty() const { return head_ == tail_; } |
||||||
|
|
||||||
|
inline uint8_t size() const { |
||||||
|
int diff = head_ - tail_; |
||||||
|
if (diff >= 0) { |
||||||
|
return diff; |
||||||
|
} |
||||||
|
return Size + diff; |
||||||
|
} |
||||||
|
|
||||||
|
inline T& front() { |
||||||
|
return buf_[tail_]; |
||||||
|
} |
||||||
|
|
||||||
|
inline bool peek(T &item) { |
||||||
|
return get(item, false); |
||||||
|
} |
||||||
|
}; |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue