summary refs log tree commit diff
path: root/quantum
diff options
context:
space:
mode:
authorlucwastiaux <luc.wastiaux@airpost.net>2016-12-10 12:49:47 +0800
committerlucwastiaux <luc.wastiaux@airpost.net>2016-12-10 12:49:47 +0800
commit9240f27ba909aece233bda59e4ec15f7666fdece (patch)
tree7b6e2b05a0722734e4739dd7b52817d82fae51c6 /quantum
parentdc4c8875ba2b961deb5d9712f422b00ce7c90979 (diff)
parent985a091a739c99736d5b17de5161831488dbc219 (diff)
Merge remote-tracking branch 'upstream/master'
Diffstat (limited to 'quantum')
-rw-r--r--quantum/api.c178
-rw-r--r--quantum/api.h59
-rw-r--r--quantum/api/api_sysex.c29
-rw-r--r--quantum/api/api_sysex.h10
-rw-r--r--quantum/config_common.h147
-rw-r--r--quantum/keymap.h4
-rw-r--r--quantum/keymap_extras/keymap_br_abnt2.h58
-rw-r--r--quantum/keymap_extras/keymap_jp.h62
-rw-r--r--quantum/keymap_extras/keymap_nordic.h2
-rwxr-xr-xquantum/light_ws2812.c151
-rwxr-xr-xquantum/light_ws2812.h21
-rw-r--r--quantum/matrix.c326
-rw-r--r--quantum/pincontrol.h37
-rw-r--r--quantum/process_keycode/process_printer.c254
-rw-r--r--quantum/process_keycode/process_printer.h8
-rw-r--r--quantum/process_keycode/process_printer_bb.c260
-rw-r--r--quantum/process_keycode/process_unicode.h1
-rw-r--r--quantum/quantum.c59
-rw-r--r--quantum/quantum.h11
-rw-r--r--quantum/rgblight.c145
-rw-r--r--quantum/rgblight.h15
-rw-r--r--quantum/variable_trace.c110
-rw-r--r--quantum/variable_trace.h34
23 files changed, 1712 insertions, 269 deletions
diff --git a/quantum/api.c b/quantum/api.c
new file mode 100644
index 0000000000..4ca3b96762
--- /dev/null
+++ b/quantum/api.c
@@ -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
+    }
+
+}
\ No newline at end of file
diff --git a/quantum/api.h b/quantum/api.h
new file mode 100644
index 0000000000..00dcdb8954
--- /dev/null
+++ b/quantum/api.h
@@ -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
\ No newline at end of file
diff --git a/quantum/api/api_sysex.c b/quantum/api/api_sysex.c
new file mode 100644
index 0000000000..a4a554e764
--- /dev/null
+++ b/quantum/api/api_sysex.c
@@ -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(" ");
+    // }
+}
\ No newline at end of file
diff --git a/quantum/api/api_sysex.h b/quantum/api/api_sysex.h
new file mode 100644
index 0000000000..b947b60e54
--- /dev/null
+++ b/quantum/api/api_sysex.h
@@ -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
\ No newline at end of file
diff --git a/quantum/config_common.h b/quantum/config_common.h
index 8ed5f4a106..17c11faeb6 100644
--- a/quantum/config_common.h
+++ b/quantum/config_common.h
@@ -5,55 +5,56 @@
 #define COL2ROW 0
 #define ROW2COL 1
 /* I/O pins */
-#define B0 0x30
-#define B1 0x31
-#define B2 0x32
-#define B3 0x33
-#define B4 0x34
-#define B5 0x35
-#define B6 0x36
-#define B7 0x37
-#define C0 0x60
-#define C1 0x61
-#define C2 0x62
-#define C3 0x63
-#define C4 0x64
-#define C5 0x65
-#define C6 0x66
-#define C7 0x67
-#define D0 0x90
-#define D1 0x91
-#define D2 0x92
-#define D3 0x93
-#define D4 0x94
-#define D5 0x95
-#define D6 0x96
-#define D7 0x97
-#define E0 0xC0
-#define E1 0xC1
-#define E2 0xC2
-#define E3 0xC3
-#define E4 0xC4
-#define E5 0xC5
-#define E6 0xC6
-#define E7 0xC7
-#define F0 0xF0
-#define F1 0xF1
-#define F2 0xF2
-#define F3 0xF3
-#define F4 0xF4
-#define F5 0xF5
-#define F6 0xF6
-#define F7 0xF7
-#define A0 0x00
-#define A1 0x01
-#define A2 0x02
-#define A3 0x03
-#define A4 0x04
-#define A5 0x05
-#define A6 0x06
-#define A7 0x07
-
+#ifndef F0
+    #define B0 0x30
+    #define B1 0x31
+    #define B2 0x32
+    #define B3 0x33
+    #define B4 0x34
+    #define B5 0x35
+    #define B6 0x36
+    #define B7 0x37
+    #define C0 0x60
+    #define C1 0x61
+    #define C2 0x62
+    #define C3 0x63
+    #define C4 0x64
+    #define C5 0x65
+    #define C6 0x66
+    #define C7 0x67
+    #define D0 0x90
+    #define D1 0x91
+    #define D2 0x92
+    #define D3 0x93
+    #define D4 0x94
+    #define D5 0x95
+    #define D6 0x96
+    #define D7 0x97
+    #define E0 0xC0
+    #define E1 0xC1
+    #define E2 0xC2
+    #define E3 0xC3
+    #define E4 0xC4
+    #define E5 0xC5
+    #define E6 0xC6
+    #define E7 0xC7
+    #define F0 0xF0
+    #define F1 0xF1
+    #define F2 0xF2
+    #define F3 0xF3
+    #define F4 0xF4
+    #define F5 0xF5
+    #define F6 0xF6
+    #define F7 0xF7
+    #define A0 0x00
+    #define A1 0x01
+    #define A2 0x02
+    #define A3 0x03
+    #define A4 0x04
+    #define A5 0x05
+    #define A6 0x06
+    #define A7 0x07
+#endif
 
 /* USART configuration */
 #ifdef BLUETOOTH_ENABLE
@@ -76,53 +77,7 @@
         } while(0)
 #   else
 #       error "USART configuration is needed."
-#endif
-
-// I'm fairly sure these aren't needed, but oh well - Jack
-
-/*
- * PS/2 Interrupt configuration
- */
-#ifdef PS2_USE_INT
-/* uses INT1 for clock line(ATMega32U4) */
-#define PS2_CLOCK_PORT  PORTD
-#define PS2_CLOCK_PIN   PIND
-#define PS2_CLOCK_DDR   DDRD
-#define PS2_CLOCK_BIT   1
-
-#define PS2_DATA_PORT   PORTD
-#define PS2_DATA_PIN    PIND
-#define PS2_DATA_DDR    DDRD
-#define PS2_DATA_BIT    0
-
-#define PS2_INT_INIT()  do {    \
-    EICRA |= ((1<<ISC11) |      \
-              (0<<ISC10));      \
-} while (0)
-#define PS2_INT_ON()  do {      \
-    EIMSK |= (1<<INT1);         \
-} while (0)
-#define PS2_INT_OFF() do {      \
-    EIMSK &= ~(1<<INT1);        \
-} while (0)
-#define PS2_INT_VECT    INT1_vect
-#endif
-
-/*
- * PS/2 Busywait configuration
- */
-#ifdef PS2_USE_BUSYWAIT
-#define PS2_CLOCK_PORT  PORTD
-#define PS2_CLOCK_PIN   PIND
-#define PS2_CLOCK_DDR   DDRD
-#define PS2_CLOCK_BIT   1
-
-#define PS2_DATA_PORT   PORTD
-#define PS2_DATA_PIN    PIND
-#define PS2_DATA_DDR    DDRD
-#define PS2_DATA_BIT    0
-#endif
-
+#   endif
 #endif
 
 #endif
diff --git a/quantum/keymap.h b/quantum/keymap.h
index a01bbfbd14..ae56d16c75 100644
--- a/quantum/keymap.h
+++ b/quantum/keymap.h
@@ -178,6 +178,10 @@ enum quantum_keycodes {
     // Right shift, close paren
     KC_RSPC,
 
+    // Printing
+    PRINT_ON,
+    PRINT_OFF,
+
     // always leave at the end
     SAFE_RANGE
 };
diff --git a/quantum/keymap_extras/keymap_br_abnt2.h b/quantum/keymap_extras/keymap_br_abnt2.h
new file mode 100644
index 0000000000..0df177721d
--- /dev/null
+++ b/quantum/keymap_extras/keymap_br_abnt2.h
@@ -0,0 +1,58 @@
+#ifndef KEYMAP_BR_ABNT2_H
+#define KEYMAP_BR_ABNT2_H
+
+#include "keymap_common.h"
+
+/* Scan codes for the Brazilian ABNT2 keyboard layout */
+
+#define BR_CCDL KC_SCLN      //  Ç   same scancode as ;: on US layout
+#define BR_SCLN KC_SLSH      //  ;:  same scancode as /? on US layout
+#define BR_QUOT KC_GRV       //  '"  same scancode as `~ on US layout
+#define BR_TILD KC_QUOT      //  ~^  dead keys, same scancode as '" on US layout
+#define BR_ACUT KC_LBRC      //  ´`  dead keys, same scancode as [{ on US layout
+#define BR_LBRC KC_RBRC      //  [{  same scancode as ]} on US layout
+#define BR_RBRC KC_BSLS      //  ]}  same scancode as \| on US layout
+#define BR_BSLS KC_NUBS      //  \|  uses the non-US hash scancode (#~, sometimes §±)
+#define BR_SLSH KC_INT1      //  /?  uses the INTL1 scancode
+
+#define BR_COLN LSFT(BR_SCLN)   // shifted :
+#define BR_DQT  LSFT(BR_QUOT)   // shifted "
+#define BR_CIRC LSFT(BR_TILD)   // shifted ^ (dead key)
+#define BR_GRAV LSFT(BR_ACUT)   // shifted ` (dead key)
+#define BR_LCBR LSFT(BR_LBRC)   // shifted {
+#define BR_RCBR LSFT(BR_RBRC)   // shifted }
+#define BR_PIPE LSFT(BR_BSLS)   // shifted |
+#define BR_QUES LSFT(BR_SLSH)   // shifted ?
+#define BR_TRMA LSFT(KC_6)      // shifted ¨ (dead key - trema accent)
+
+// On the ABNT2 the keypad comma and the keypad dot scancodes are switched
+// (presumably because in Brazil comma is used as the decimal separator)
+#define BR_KPDT KC_KP_COMMA  //  keypad .
+#define BR_KPCM KC_KP_DOT    //  keypad ,
+
+#define BR_1UP    LALT(KC_1)      // 1 superscript                    ¹   alt+1
+#define BR_2UP    LALT(KC_2)      // 2 superscript                    ²   alt+2
+#define BR_3UP    LALT(KC_3)      // 3 superscript                    ³   alt+3
+#define BR_PND    LALT(KC_4)      // Pound sign                       £   alt+4
+#define BR_CENT   LALT(KC_5)      // Cent sign                        ¢   alt+5
+#define BR_NOT    LALT(KC_6)      // Not sign                         ¬   alt+6
+#define BR_SECT   LALT(KC_EQL)    // Section sign                     §   alt+=
+#define BR_FORD   LALT(BR_LBRC)   // Feminine Ordinal Sign            ª   alt+[
+#define BR_MORD   LALT(BR_RBRC)   // Masculine Ordinal Sign           º   alt+]
+#define BR_DGRE   LALT(BR_SLSH)   // Degree sign                      °   alt+/
+
+#define BR_EURO   LALT(KC_E)      // Euro sign                        €   alt+e
+#define BR_NDTD   LALT(BR_TILD)   // Non-dead key tilde               ~   alt+~
+#define BR_NDAC   LALT(BR_ACUT)   // Non-dead key acute accent        ´   alt+´
+#define BR_NDGV   LALT(BR_QUOT)   // Non-dead key grave accent        `   alt+'
+#define BR_NDCR   LALT(BR_CIRC)   // Non-dead key circumflex accent   ^   alt+^ (alt+shift+~)
+#define BR_NDTR   LALT(BR_TRMA)   // Non-dead key trema accent        ¨   alt+¨ (alt+shift+6)
+
+// For 101-key keyboard layouts, the ABNT2 layout allows
+// the slash and question mark to be typed using alt+q and alt+w.
+// The shortcuts are provided here for completeness' sake,
+// but it's recommended to use BR_SLSH and BR_QUES instead
+#define BR_ASLS   LALT(KC_Q)
+#define BR_AQST   LALT(KC_W)
+
+#endif
diff --git a/quantum/keymap_extras/keymap_jp.h b/quantum/keymap_extras/keymap_jp.h
new file mode 100644
index 0000000000..e81b5952e0
--- /dev/null
+++ b/quantum/keymap_extras/keymap_jp.h
@@ -0,0 +1,62 @@
+/* JP106-layout (Japanese Standard)
+ *
+ * For more information, see
+ * http://www2d.biglobe.ne.jp/~msyk/keyboard/layout/usbkeycode.html
+ * note: This website is written in Japanese.
+ */
+
+
+#ifndef KEYMAP_JP_H
+#define KEYMAP_JP_H
+
+
+#include "keymap.h"
+
+
+#define JP_ZHTG KC_GRV  // hankaku/zenkaku|kanzi
+#define JP_YEN  KC_INT3 // yen, |
+#define JP_CIRC KC_EQL  // ^, ~
+#define JP_AT   KC_LBRC // @, `
+#define JP_LBRC KC_RBRC // [, {
+#define JP_COLN KC_QUOT // :, *
+#define JP_RBRC KC_NUHS // ], }
+#define JP_BSLS KC_INT1 // \, _
+#define JP_MHEN KC_INT5 // muhenkan
+#define JP_HENK KC_INT4 // henkan
+#define JP_KANA KC_INT2 // katakana/hiragana|ro-mazi
+
+
+//Aliases for shifted symbols
+#define JP_DQT  LSFT(KC_2)    // "
+#define JP_AMPR LSFT(KC_6)    // &
+#define JP_QUOT LSFT(KC_7)    // '
+#define JP_LPRN LSFT(KC_8)    // (
+#define JP_RPRN LSFT(KC_9)    // )
+#define JP_EQL  LSFT(KC_MINS) // =
+#define JP_TILD LSFT(JP_CIRC) // ~
+#define JP_PIPE LSFT(JP_YEN)  // |
+#define JP_GRV  LSFT(JP_AT)   // `
+#define JP_LCBR LSFT(JP_LBRC) // {
+#define JP_PLUS LSFT(KC_SCLN) // +
+#define JP_ASTR LSFT(JP_COLN) // *
+#define JP_RCBR LSFT(JP_RBRC) // }
+#define JP_UNDS LSFT(JP_BSLS) // _
+
+
+// These symbols are correspond to US101-layout.
+#define JP_MINS KC_MINS // -
+#define JP_SCLN KC_SCLN // ;
+#define JP_COMM KC_COMM // ,
+#define JP_DOT  KC_DOT  // .
+#define JP_SLSH KC_SLSH // /
+// shifted
+#define JP_EXLM KC_EXLM // !
+#define JP_HASH KC_HASH // #
+#define JP_DLR  KC_DLR  // $
+#define JP_PERC KC_PERC // %
+#define JP_LT   KC_LT   // <
+#define JP_GT   KC_GT   // >
+#define JP_QUES KC_QUES // ?
+
+
+#endif
diff --git a/quantum/keymap_extras/keymap_nordic.h b/quantum/keymap_extras/keymap_nordic.h
index da5c829757..9b0ef35ca9 100644
--- a/quantum/keymap_extras/keymap_nordic.h
+++ b/quantum/keymap_extras/keymap_nordic.h
@@ -13,7 +13,7 @@
 #define NO_ACUT	KC_EQL
 
 #define NO_AM	KC_LBRC
-#define NO_QUOT	KC_RBRC
+#define NO_QUOT	KC_RBRC // this is the "umlaut" char on Nordic keyboards, Apple layout
 #define NO_AE	KC_SCLN
 #define NO_OSLH	KC_QUOT
 #define	NO_APOS	KC_NUHS
diff --git a/quantum/light_ws2812.c b/quantum/light_ws2812.c
index 401845e855..a883b13884 100755
--- a/quantum/light_ws2812.c
+++ b/quantum/light_ws2812.c
@@ -16,14 +16,128 @@
 #include <util/delay.h>
 #include "debug.h"
 
+#ifdef RGBW_BB_TWI
+
+// Port for the I2C
+#define I2C_DDR DDRD
+#define I2C_PIN PIND
+#define I2C_PORT PORTD
+
+// Pins to be used in the bit banging
+#define I2C_CLK 0
+#define I2C_DAT 1
+
+#define I2C_DATA_HI()\
+I2C_DDR &= ~ (1 << I2C_DAT);\
+I2C_PORT |= (1 << I2C_DAT);
+#define I2C_DATA_LO()\
+I2C_DDR |= (1 << I2C_DAT);\
+I2C_PORT &= ~ (1 << I2C_DAT);
+
+#define I2C_CLOCK_HI()\
+I2C_DDR &= ~ (1 << I2C_CLK);\
+I2C_PORT |= (1 << I2C_CLK);
+#define I2C_CLOCK_LO()\
+I2C_DDR |= (1 << I2C_CLK);\
+I2C_PORT &= ~ (1 << I2C_CLK);
+
+#define I2C_DELAY 1
+
+void I2C_WriteBit(unsigned char c)
+{
+    if (c > 0)
+    {
+        I2C_DATA_HI();
+    }
+    else
+    {
+        I2C_DATA_LO();
+    }
+
+    I2C_CLOCK_HI();
+    _delay_us(I2C_DELAY);
+
+    I2C_CLOCK_LO();
+    _delay_us(I2C_DELAY);
+
+    if (c > 0)
+    {
+        I2C_DATA_LO();
+    }
+
+    _delay_us(I2C_DELAY);
+}
+
+// Inits bitbanging port, must be called before using the functions below
+//
+void I2C_Init()
+{
+    I2C_PORT &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
+
+    I2C_CLOCK_HI();
+    I2C_DATA_HI();
+
+    _delay_us(I2C_DELAY);
+}
+
+// Send a START Condition
+//
+void I2C_Start()
+{
+    // set both to high at the same time
+    I2C_DDR &= ~ ((1 << I2C_DAT) | (1 << I2C_CLK));
+    _delay_us(I2C_DELAY);
+
+    I2C_DATA_LO();
+    _delay_us(I2C_DELAY);
+
+    I2C_CLOCK_LO();
+    _delay_us(I2C_DELAY);
+}
+
+// Send a STOP Condition
+//
+void I2C_Stop()
+{
+    I2C_CLOCK_HI();
+    _delay_us(I2C_DELAY);
+
+    I2C_DATA_HI();
+    _delay_us(I2C_DELAY);
+}
+
+// write a byte to the I2C slave device
+//
+unsigned char I2C_Write(unsigned char c)
+{
+    for (char i = 0; i < 8; i++)
+    {
+        I2C_WriteBit(c & 128);
+
+        c <<= 1;
+    }
+
+    
+    I2C_WriteBit(0);
+    _delay_us(I2C_DELAY);
+    _delay_us(I2C_DELAY);
+  
+    // _delay_us(I2C_DELAY);
+    //return I2C_ReadBit();
+    return 0;
+}
+
+
+#endif
+
 // Setleds for standard RGB
-void inline ws2812_setleds(struct cRGB *ledarray, uint16_t leds)
+void inline ws2812_setleds(LED_TYPE *ledarray, uint16_t leds)
 {
    // ws2812_setleds_pin(ledarray,leds, _BV(ws2812_pin));
    ws2812_setleds_pin(ledarray,leds, _BV(RGB_DI_PIN & 0xF));
 }
 
-void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pinmask)
+void inline ws2812_setleds_pin(LED_TYPE *ledarray, uint16_t leds, uint8_t pinmask)
 {
   // ws2812_DDRREG |= pinmask; // Enable DDR
   // new universal format (DDR)
@@ -34,14 +148,41 @@ void inline ws2812_setleds_pin(struct cRGB *ledarray, uint16_t leds, uint8_t pin
 }
 
 // Setleds for SK6812RGBW
-void inline ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t leds)
+void inline ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t leds)
 {
+
+  #ifdef RGBW_BB_TWI
+    uint8_t sreg_prev, twcr_prev;
+    sreg_prev=SREG;
+    twcr_prev=TWCR;
+    cli();
+    TWCR &= ~(1<<TWEN);
+    I2C_Init();
+    I2C_Start();
+    I2C_Write(0x84);
+    uint16_t datlen = leds<<2;
+    uint8_t curbyte;
+    uint8_t * data = (uint8_t*)ledarray;
+    while (datlen--) {
+      curbyte=*data++;
+      I2C_Write(curbyte);
+    }
+    I2C_Stop();
+    SREG=sreg_prev;
+    TWCR=twcr_prev;
+  #endif
+
+
   // ws2812_DDRREG |= _BV(ws2812_pin); // Enable DDR
   // new universal format (DDR)
   _SFR_IO8((RGB_DI_PIN >> 4) + 1) |= _BV(RGB_DI_PIN & 0xF);
 
   ws2812_sendarray_mask((uint8_t*)ledarray,leds<<2,_BV(RGB_DI_PIN & 0xF));
-  _delay_us(80);
+
+
+  #ifndef RGBW_BB_TWI
+    _delay_us(80);
+  #endif
 }
 
 void ws2812_sendarray(uint8_t *data,uint16_t datlen)
@@ -123,7 +264,7 @@ void inline ws2812_sendarray_mask(uint8_t *data,uint16_t datlen,uint8_t maskhi)
   cli();
 
   while (datlen--) {
-    curbyte=*data++;
+    curbyte=(*data++);
 
     asm volatile(
     "       ldi   %0,8  \n\t"
diff --git a/quantum/light_ws2812.h b/quantum/light_ws2812.h
index 54eef22d9e..9498e550e9 100755
--- a/quantum/light_ws2812.h
+++ b/quantum/light_ws2812.h
@@ -16,6 +16,21 @@
 #include <avr/io.h>
 #include <avr/interrupt.h>
 //#include "ws2812_config.h"
+//#include "i2cmaster.h"
+
+#define LIGHT_I2C 1
+#define LIGHT_I2C_ADDR        0x84
+#define LIGHT_I2C_ADDR_WRITE  ( (LIGHT_I2C_ADDR<<1) | I2C_WRITE )
+#define LIGHT_I2C_ADDR_READ   ( (LIGHT_I2C_ADDR<<1) | I2C_READ  )
+
+#define RGBW 1
+
+#ifdef RGBW
+  #define LED_TYPE struct cRGBW
+#else
+  #define LED_TYPE struct cRGB
+#endif
+
 
 /*
  *  Structure of the LED array
@@ -42,9 +57,9 @@ struct cRGBW { uint8_t g; uint8_t r; uint8_t b; uint8_t w;};
  *         - Wait 50�s to reset the LEDs
  */
 
-void ws2812_setleds     (struct cRGB  *ledarray, uint16_t number_of_leds);
-void ws2812_setleds_pin (struct cRGB  *ledarray, uint16_t number_of_leds,uint8_t pinmask);
-void ws2812_setleds_rgbw(struct cRGBW *ledarray, uint16_t number_of_leds);
+void ws2812_setleds     (LED_TYPE *ledarray, uint16_t number_of_leds);
+void ws2812_setleds_pin (LED_TYPE *ledarray, uint16_t number_of_leds,uint8_t pinmask);
+void ws2812_setleds_rgbw(LED_TYPE *ledarray, uint16_t number_of_leds);
 
 /*
  * Old interface / Internal functions
diff --git a/quantum/matrix.c b/quantum/matrix.c
index 3174e07390..07eb87bc36 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -25,37 +25,65 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #include "debug.h"
 #include "util.h"
 #include "matrix.h"
+#include "timer.h"
+
 
 /* Set 0 if debouncing isn't needed */
 
 #ifndef DEBOUNCING_DELAY
 #   define DEBOUNCING_DELAY 5
 #endif
-static uint8_t debouncing = DEBOUNCING_DELAY;
+
+#if (DEBOUNCING_DELAY > 0)
+    static uint16_t debouncing_time;
+    static bool debouncing = false;
+#endif
+
+#if (MATRIX_COLS <= 8)
+#    define print_matrix_header()  print("\nr/c 01234567\n")
+#    define print_matrix_row(row)  print_bin_reverse8(matrix_get_row(row))
+#    define matrix_bitpop(i)       bitpop(matrix[i])
+#    define ROW_SHIFTER ((uint8_t)1)
+#elif (MATRIX_COLS <= 16)
+#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF\n")
+#    define print_matrix_row(row)  print_bin_reverse16(matrix_get_row(row))
+#    define matrix_bitpop(i)       bitpop16(matrix[i])
+#    define ROW_SHIFTER ((uint16_t)1)
+#elif (MATRIX_COLS <= 32)
+#    define print_matrix_header()  print("\nr/c 0123456789ABCDEF0123456789ABCDEF\n")
+#    define print_matrix_row(row)  print_bin_reverse32(matrix_get_row(row))
+#    define matrix_bitpop(i)       bitpop32(matrix[i])
+#    define ROW_SHIFTER  ((uint32_t)1)
+#endif
+
+#ifdef MATRIX_MASKED
+    extern const matrix_row_t matrix_mask[];
+#endif
 
 static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
 static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
 
 /* matrix state(1:on, 0:off) */
 static matrix_row_t matrix[MATRIX_ROWS];
+
+static matrix_row_t matrix_raw[MATRIX_ROWS];
 static matrix_row_t matrix_debouncing[MATRIX_ROWS];
 
-#if DIODE_DIRECTION == ROW2COL
-    static matrix_row_t matrix_reversed[MATRIX_COLS];
-    static matrix_row_t matrix_reversed_debouncing[MATRIX_COLS];
-#endif
 
-#if MATRIX_COLS > 16
-    #define SHIFTER 1UL
-#else
-    #define SHIFTER 1
+#if (DIODE_DIRECTION == COL2ROW)
+    static void init_cols(void);
+    static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row);
+    static void unselect_rows(void);
+    static void select_row(uint8_t row);
+    static void unselect_row(uint8_t row);
+#else // ROW2COL
+    static void init_rows(void);
+    static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col);
+    static void unselect_cols(void);
+    static void unselect_col(uint8_t col);
+    static void select_col(uint8_t col);
 #endif
 
-static matrix_row_t read_cols(void);
-static void init_cols(void);
-static void unselect_rows(void);
-static void select_row(uint8_t row);
-
 __attribute__ ((weak))
 void matrix_init_quantum(void) {
     matrix_init_kb();
@@ -95,7 +123,7 @@ uint8_t matrix_cols(void) {
 }
 
 // void matrix_power_up(void) {
-// #if DIODE_DIRECTION == COL2ROW
+// #if (DIODE_DIRECTION == COL2ROW)
 //     for (int8_t r = MATRIX_ROWS - 1; r >= 0; --r) {
 //         /* DDRxn */
 //         _SFR_IO8((row_pins[r] >> 4) + 1) |= _BV(row_pins[r] & 0xF);
@@ -119,19 +147,26 @@ uint8_t matrix_cols(void) {
 // }
 
 void matrix_init(void) {
+
     // To use PORTF disable JTAG with writing JTD bit twice within four cycles.
-    #ifdef __AVR_ATmega32U4__
+    #if  (defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB1287__) || defined(__AVR_ATmega32U4__))
         MCUCR |= _BV(JTD);
         MCUCR |= _BV(JTD);
     #endif
 
     // initialize row and col
+#if (DIODE_DIRECTION == COL2ROW)
     unselect_rows();
     init_cols();
+#else // ROW2COL
+    unselect_cols();
+    init_rows();
+#endif
 
     // initialize matrix state: all keys off
     for (uint8_t i=0; i < MATRIX_ROWS; i++) {
         matrix[i] = 0;
+        matrix_raw[i] = 0;
         matrix_debouncing[i] = 0;
     }
 
@@ -141,71 +176,60 @@ void matrix_init(void) {
 uint8_t matrix_scan(void)
 {
 
-#if DIODE_DIRECTION == COL2ROW
-    for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-        select_row(i);
-        wait_us(30);  // without this wait read unstable value.
-        matrix_row_t cols = read_cols();
-        if (matrix_debouncing[i] != cols) {
-            matrix_debouncing[i] = cols;
-            if (debouncing) {
-                debug("bounce!: "); debug_hex(debouncing); debug("\n");
-            }
-            debouncing = DEBOUNCING_DELAY;
-        }
-        unselect_rows();
-    }
+#if (DIODE_DIRECTION == COL2ROW)
 
-    if (debouncing) {
-        if (--debouncing) {
-            wait_ms(1);
-        } else {
-            for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-                matrix[i] = matrix_debouncing[i];
+    // Set row, read cols
+    for (uint8_t current_row = 0; current_row < MATRIX_ROWS; current_row++) {
+#       if (DEBOUNCING_DELAY > 0)
+            bool matrix_changed = read_cols_on_row(matrix_debouncing, current_row);
+
+            if (matrix_changed) {
+                debouncing = true;
+                debouncing_time = timer_read();
             }
-        }
+
+#       else
+            read_cols_on_row(matrix, current_row);
+#       endif
+
     }
-#else
-    for (uint8_t i = 0; i < MATRIX_COLS; i++) {
-        select_row(i);
-        wait_us(30);  // without this wait read unstable value.
-        matrix_row_t rows = read_cols();
-        if (matrix_reversed_debouncing[i] != rows) {
-            matrix_reversed_debouncing[i] = rows;
-            if (debouncing) {
-                debug("bounce!: "); debug_hex(debouncing); debug("\n");
+
+#else // ROW2COL
+
+    // Set col, read rows
+    for (uint8_t current_col = 0; current_col < MATRIX_COLS; current_col++) {
+#       if (DEBOUNCING_DELAY > 0)
+            bool matrix_changed = read_rows_on_col(matrix_debouncing, current_col);
+            if (matrix_changed) {
+                debouncing = true;
+                debouncing_time = timer_read();
             }
-            debouncing = DEBOUNCING_DELAY;
-        }
-        unselect_rows();
+#       else
+             read_rows_on_col(matrix, current_col);
+#       endif
+
     }
 
-    if (debouncing) {
-        if (--debouncing) {
-            wait_ms(1);
-        } else {
-            for (uint8_t i = 0; i < MATRIX_COLS; i++) {
-                matrix_reversed[i] = matrix_reversed_debouncing[i];
+#endif
+
+#   if (DEBOUNCING_DELAY > 0)
+        if (debouncing && (timer_elapsed(debouncing_time) > DEBOUNCING_DELAY)) {
+            for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
+                matrix[i] = matrix_debouncing[i];
             }
+            debouncing = false;
         }
-    }
-    for (uint8_t y = 0; y < MATRIX_ROWS; y++) {
-        matrix_row_t row = 0;
-        for (uint8_t x = 0; x < MATRIX_COLS; x++) {
-            row |= ((matrix_reversed[x] & (1<<y)) >> y) << x;
-        }
-        matrix[y] = row;
-    }
-#endif
+#   endif
 
     matrix_scan_quantum();
-
     return 1;
 }
 
 bool matrix_is_modified(void)
 {
+#if (DEBOUNCING_DELAY > 0)
     if (debouncing) return false;
+#endif
     return true;
 }
 
@@ -218,15 +242,22 @@ bool matrix_is_on(uint8_t row, uint8_t col)
 inline
 matrix_row_t matrix_get_row(uint8_t row)
 {
+    // Matrix mask lets you disable switches in the returned matrix data. For example, if you have a
+    // switch blocker installed and the switch is always pressed.
+#ifdef MATRIX_MASKED
+    return matrix[row] & matrix_mask[row];
+#else
     return matrix[row];
+#endif
 }
 
 void matrix_print(void)
 {
-    print("\nr/c 0123456789ABCDEF\n");
+    print_matrix_header();
+
     for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
         phex(row); print(": ");
-        pbin_reverse16(matrix_get_row(row));
+        print_matrix_row(row);
         print("\n");
     }
 }
@@ -235,63 +266,148 @@ uint8_t matrix_key_count(void)
 {
     uint8_t count = 0;
     for (uint8_t i = 0; i < MATRIX_ROWS; i++) {
-        count += bitpop16(matrix[i]);
+        count += matrix_bitpop(i);
     }
     return count;
 }
 
+
+
+#if (DIODE_DIRECTION == COL2ROW)
+
 static void init_cols(void)
 {
-#if DIODE_DIRECTION == COL2ROW
-    for(int x = 0; x < MATRIX_COLS; x++) {
-        int pin = col_pins[x];
-#else
-    for(int x = 0; x < MATRIX_ROWS; x++) {
-        int pin = row_pins[x];
-#endif
-        _SFR_IO8((pin >> 4) + 1) &=  ~_BV(pin & 0xF);
-        _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF);
+    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
+        uint8_t pin = col_pins[x];
+        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
     }
 }
 
-static matrix_row_t read_cols(void)
+static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
 {
-    matrix_row_t result = 0;
+    // Store last value of row prior to reading
+    matrix_row_t last_row_value = current_matrix[current_row];
 
-#if DIODE_DIRECTION == COL2ROW
-    for(int x = 0; x < MATRIX_COLS; x++) {     
-        int pin = col_pins[x];
-#else
-    for(int x = 0; x < MATRIX_ROWS; x++) {
-        int pin = row_pins[x];
-#endif
-        result |= (_SFR_IO8(pin >> 4) & _BV(pin & 0xF)) ? 0 : (SHIFTER << x);
+    // Clear data in matrix row
+    current_matrix[current_row] = 0;
+
+    // Select row and wait for row selecton to stabilize
+    select_row(current_row);
+    wait_us(30);
+
+    // For each col...
+    for(uint8_t col_index = 0; col_index < MATRIX_COLS; col_index++) {
+
+        // Select the col pin to read (active low)
+        uint8_t pin = col_pins[col_index];
+        uint8_t pin_state = (_SFR_IO8(pin >> 4) & _BV(pin & 0xF));
+
+        // Populate the matrix row with the state of the col pin
+        current_matrix[current_row] |=  pin_state ? 0 : (ROW_SHIFTER << col_index);
     }
-    return result;
+
+    // Unselect row
+    unselect_row(current_row);
+
+    return (last_row_value != current_matrix[current_row]);
+}
+
+static void select_row(uint8_t row)
+{
+    uint8_t pin = row_pins[row];
+    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF); // OUT
+    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
+}
+
+static void unselect_row(uint8_t row)
+{
+    uint8_t pin = row_pins[row];
+    _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+    _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
 }
 
 static void unselect_rows(void)
 {
-#if DIODE_DIRECTION == COL2ROW
-    for(int x = 0; x < MATRIX_ROWS; x++) { 
-        int pin = row_pins[x];
-#else
-    for(int x = 0; x < MATRIX_COLS; x++) { 
-        int pin = col_pins[x];
-#endif
-        _SFR_IO8((pin >> 4) + 1) &=  ~_BV(pin & 0xF);
-        _SFR_IO8((pin >> 4) + 2) |= _BV(pin & 0xF);
+    for(uint8_t x = 0; x < MATRIX_ROWS; x++) {
+        uint8_t pin = row_pins[x];
+        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
     }
 }
 
-static void select_row(uint8_t row)
+#else // ROW2COL
+
+static void init_rows(void)
 {
+    for(uint8_t x = 0; x < MATRIX_ROWS; x++) {
+        uint8_t pin = row_pins[x];
+        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
+    }
+}
 
-#if DIODE_DIRECTION == COL2ROW
-    int pin = row_pins[row];
-#else
-    int pin = col_pins[row];
-#endif
-    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF);
-    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF);
+static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
+{
+    bool matrix_changed = false;
+
+    // Select col and wait for col selecton to stabilize
+    select_col(current_col);
+    wait_us(30);
+
+    // For each row...
+    for(uint8_t row_index = 0; row_index < MATRIX_ROWS; row_index++)
+    {
+
+        // Store last value of row prior to reading
+        matrix_row_t last_row_value = current_matrix[row_index];
+
+        // Check row pin state
+        if ((_SFR_IO8(row_pins[row_index] >> 4) & _BV(row_pins[row_index] & 0xF)) == 0)
+        {
+            // Pin LO, set col bit
+            current_matrix[row_index] |= (ROW_SHIFTER << current_col);
+        }
+        else
+        {
+            // Pin HI, clear col bit
+            current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
+        }
+
+        // Determine if the matrix changed state
+        if ((last_row_value != current_matrix[row_index]) && !(matrix_changed))
+        {
+            matrix_changed = true;
+        }
+    }
+
+    // Unselect col
+    unselect_col(current_col);
+
+    return matrix_changed;
 }
+
+static void select_col(uint8_t col)
+{
+    uint8_t pin = col_pins[col];
+    _SFR_IO8((pin >> 4) + 1) |=  _BV(pin & 0xF); // OUT
+    _SFR_IO8((pin >> 4) + 2) &= ~_BV(pin & 0xF); // LOW
+}
+
+static void unselect_col(uint8_t col)
+{
+    uint8_t pin = col_pins[col];
+    _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+    _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
+}
+
+static void unselect_cols(void)
+{
+    for(uint8_t x = 0; x < MATRIX_COLS; x++) {
+        uint8_t pin = col_pins[x];
+        _SFR_IO8((pin >> 4) + 1) &= ~_BV(pin & 0xF); // IN
+        _SFR_IO8((pin >> 4) + 2) |=  _BV(pin & 0xF); // HI
+    }
+}
+
+#endif
diff --git a/quantum/pincontrol.h b/quantum/pincontrol.h
new file mode 100644
index 0000000000..36ce29ef22
--- /dev/null
+++ b/quantum/pincontrol.h
@@ -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);
+}
diff --git a/quantum/process_keycode/process_printer.c b/quantum/process_keycode/process_printer.c
new file mode 100644
index 0000000000..2e11dd366c
--- /dev/null
+++ b/quantum/process_keycode/process_printer.c
@@ -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;
+
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_printer.h b/quantum/process_keycode/process_printer.h
new file mode 100644
index 0000000000..fdd36d75a8
--- /dev/null
+++ b/quantum/process_keycode/process_printer.h
@@ -0,0 +1,8 @@
+#ifndef PROCESS_PRINTER_H
+#define PROCESS_PRINTER_H
+
+#include "quantum.h"
+
+#include "protocol/serial.h"
+
+#endif
\ No newline at end of file
diff --git a/quantum/process_keycode/process_printer_bb.c b/quantum/process_keycode/process_printer_bb.c
new file mode 100644
index 0000000000..1924d03774
--- /dev/null
+++ b/quantum/process_keycode/process_printer_bb.c
@@ -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;
+
+}
\ No newline at end of file
diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h
index 065eeb5f6a..f17cfa6cf2 100644
--- a/quantum/process_keycode/process_unicode.h
+++ b/quantum/process_keycode/process_unicode.h
@@ -22,6 +22,7 @@ void register_hex(uint16_t hex);
 bool process_unicode(uint16_t keycode, keyrecord_t *record);
 
 #ifdef UNICODEMAP_ENABLE
+void unicode_map_input_error(void);
 bool process_unicode_map(uint16_t keycode, keyrecord_t *record);
 #endif
 
diff --git a/quantum/quantum.c b/quantum/quantum.c
index 098312e6ef..f653564a67 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -1,5 +1,9 @@
 #include "quantum.h"
 
+#ifndef TAPPING_TERM
+#define TAPPING_TERM 200
+#endif
+
 static void do_code16 (uint16_t code, void (*f) (uint8_t)) {
   switch (code) {
   case QK_MODS ... QK_MODS_MAX:
@@ -75,6 +79,7 @@ void reset_keyboard(void) {
 #endif
 
 static bool shift_interrupted[2] = {0, 0};
+static uint16_t scs_timer = 0;
 
 bool process_record_quantum(keyrecord_t *record) {
 
@@ -129,6 +134,9 @@ bool process_record_quantum(keyrecord_t *record) {
   #ifdef UCIS_ENABLE
     process_ucis(keycode, record) &&
   #endif
+  #ifdef PRINTING_ENABLE
+    process_printer(keycode, record) &&
+  #endif
   #ifdef UNICODEMAP_ENABLE
     process_unicode_map(keycode, record) &&
   #endif
@@ -283,6 +291,7 @@ bool process_record_quantum(keyrecord_t *record) {
     case KC_LSPO: {
       if (record->event.pressed) {
         shift_interrupted[0] = false;
+        scs_timer = timer_read ();
         register_mods(MOD_BIT(KC_LSFT));
       }
       else {
@@ -292,7 +301,7 @@ bool process_record_quantum(keyrecord_t *record) {
             shift_interrupted[1] = true;
           }
         #endif
-        if (!shift_interrupted[0]) {
+        if (!shift_interrupted[0] && timer_elapsed(scs_timer) < TAPPING_TERM) {
           register_code(LSPO_KEY);
           unregister_code(LSPO_KEY);
         }
@@ -305,6 +314,7 @@ bool process_record_quantum(keyrecord_t *record) {
     case KC_RSPC: {
       if (record->event.pressed) {
         shift_interrupted[1] = false;
+        scs_timer = timer_read ();
         register_mods(MOD_BIT(KC_RSFT));
       }
       else {
@@ -314,7 +324,7 @@ bool process_record_quantum(keyrecord_t *record) {
             shift_interrupted[1] = true;
           }
         #endif
-        if (!shift_interrupted[1]) {
+        if (!shift_interrupted[1] && timer_elapsed(scs_timer) < TAPPING_TERM) {
           register_code(RSPC_KEY);
           unregister_code(RSPC_KEY);
         }
@@ -799,6 +809,51 @@ void backlight_set(uint8_t level)
 #endif // backlight
 
 
+// Functions for spitting out values
+//
+
+void send_dword(uint32_t number) { // this might not actually work
+    uint16_t word = (number >> 16);
+    send_word(word);
+    send_word(number & 0xFFFFUL);
+}
+
+void send_word(uint16_t number) {
+    uint8_t byte = number >> 8;
+    send_byte(byte);
+    send_byte(number & 0xFF);
+}
+
+void send_byte(uint8_t number) {
+    uint8_t nibble = number >> 4;
+    send_nibble(nibble);
+    send_nibble(number & 0xF);
+}
+
+void send_nibble(uint8_t number) {
+    switch (number) {
+        case 0:
+            register_code(KC_0);
+            unregister_code(KC_0);
+            break;
+        case 1 ... 9:
+            register_code(KC_1 + (number - 1));
+            unregister_code(KC_1 + (number - 1));
+            break;
+        case 0xA ... 0xF:
+            register_code(KC_A + (number - 0xA));
+            unregister_code(KC_A + (number - 0xA));
+            break;
+    }
+}
+
+void api_send_unicode(uint32_t unicode) {
+#ifdef API_ENABLE
+    uint8_t chunk[4];
+    dword_to_bytes(unicode, chunk);
+    MT_SEND_DATA(DT_UNICODE, chunk, 5);
+#endif
+}
 
 __attribute__ ((weak))
 void led_set_user(uint8_t usb_led) {
diff --git a/quantum/quantum.h b/quantum/quantum.h
index 0c60466495..e6adf974ab 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -59,6 +59,10 @@ extern uint32_t default_layer_state;
 
 #include "process_tap_dance.h"
 
+#ifdef PRINTING_ENABLE
+	#include "process_printer.h"
+#endif
+
 #define SEND_STRING(str) send_string(PSTR(str))
 void send_string(const char *str);
 
@@ -106,8 +110,15 @@ void breathing_speed_dec(uint8_t value);
 #endif
 
 #endif
+void send_dword(uint32_t number);
+void send_word(uint16_t number);
+void send_byte(uint8_t number);
+void send_nibble(uint8_t number);
+
 
 void led_set_user(uint8_t usb_led);
 void led_set_kb(uint8_t usb_led);
 
+void api_send_unicode(uint32_t unicode);
+
 #endif
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index d550c58660..625971e0fe 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -69,11 +69,12 @@ const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {100, 50, 20};
 
 rgblight_config_t rgblight_config;
 rgblight_config_t inmem_config;
-struct cRGB led[RGBLED_NUM];
-uint8_t rgblight_inited = 0;
 
+LED_TYPE led[RGBLED_NUM];
+uint8_t rgblight_inited = 0;
+bool rgblight_timer_enabled = false;
 
-void sethsv(uint16_t hue, uint8_t sat, uint8_t val, struct cRGB *led1) {
+void sethsv(uint16_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1) {
   uint8_t r = 0, g = 0, b = 0, base, color;
 
   if (sat == 0) { // Acromatic color (gray). Hue doesn't mind.
@@ -124,7 +125,7 @@ void sethsv(uint16_t hue, uint8_t sat, uint8_t val, struct cRGB *led1) {
   setrgb(r, g, b, led1);
 }
 
-void setrgb(uint8_t r, uint8_t g, uint8_t b, struct cRGB *led1) {
+void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1) {
   (*led1).r = r;
   (*led1).g = g;
   (*led1).b = b;
@@ -141,9 +142,9 @@ void eeconfig_update_rgblight_default(void) {
   dprintf("eeconfig_update_rgblight_default\n");
   rgblight_config.enable = 1;
   rgblight_config.mode = 1;
-  rgblight_config.hue = 200;
-  rgblight_config.sat = 204;
-  rgblight_config.val = 204;
+  rgblight_config.hue = 0;
+  rgblight_config.sat = 255;
+  rgblight_config.val = 255;
   eeconfig_update_rgblight(rgblight_config.raw);
 }
 void eeconfig_debug_rgblight(void) {
@@ -173,7 +174,7 @@ void rgblight_init(void) {
   }
   eeconfig_debug_rgblight(); // display current eeprom values
 
-  #if !defined(AUDIO_ENABLE) && defined(RGBLIGHT_TIMER)
+  #ifdef RGBLIGHT_ANIMATIONS
     rgblight_timer_init(); // setup the timer
   #endif
 
@@ -182,6 +183,19 @@ void rgblight_init(void) {
   }
 }
 
+void rgblight_update_dword(uint32_t dword) {
+  rgblight_config.raw = dword;
+  eeconfig_update_rgblight(rgblight_config.raw);
+  if (rgblight_config.enable)
+    rgblight_mode(rgblight_config.mode);
+  else {
+    #ifdef RGBLIGHT_ANIMATIONS
+      rgblight_timer_disable();
+    #endif
+      rgblight_set();
+  }
+}
+
 void rgblight_increase(void) {
   uint8_t mode = 0;
   if (rgblight_config.mode < RGBLIGHT_MODES) {
@@ -220,7 +234,7 @@ void rgblight_mode(uint8_t mode) {
   eeconfig_update_rgblight(rgblight_config.raw);
   xprintf("rgblight mode: %u\n", rgblight_config.mode);
   if (rgblight_config.mode == 1) {
-    #if !defined(AUDIO_ENABLE) && defined(RGBLIGHT_TIMER)
+    #ifdef RGBLIGHT_ANIMATIONS
       rgblight_timer_disable();
     #endif
   } else if (rgblight_config.mode >= 2 && rgblight_config.mode <= 23) {
@@ -230,7 +244,7 @@ void rgblight_mode(uint8_t mode) {
     // MODE 15-20, snake
     // MODE 21-23, knight
 
-    #if !defined(AUDIO_ENABLE) && defined(RGBLIGHT_TIMER)
+    #ifdef RGBLIGHT_ANIMATIONS
       rgblight_timer_enable();
     #endif
   }
@@ -244,7 +258,7 @@ void rgblight_toggle(void) {
   if (rgblight_config.enable) {
     rgblight_mode(rgblight_config.mode);
   } else {
-    #if !defined(AUDIO_ENABLE) && defined(RGBLIGHT_TIMER)
+    #ifdef RGBLIGHT_ANIMATIONS
       rgblight_timer_disable();
     #endif
     _delay_ms(50);
@@ -252,6 +266,13 @@ void rgblight_toggle(void) {
   }
 }
 
+void rgblight_enable(void) {
+  rgblight_config.enable = 1;
+  eeconfig_update_rgblight(rgblight_config.raw);
+  xprintf("rgblight enable: rgblight_config.enable = %u\n", rgblight_config.enable);
+  rgblight_mode(rgblight_config.mode);
+}
+
 
 void rgblight_increase_hue(void) {
   uint16_t hue;
@@ -307,7 +328,7 @@ void rgblight_decrease_val(void) {
 void rgblight_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) {
   inmem_config.raw = rgblight_config.raw;
   if (rgblight_config.enable) {
-    struct cRGB tmp_led;
+    LED_TYPE tmp_led;
     sethsv(hue, sat, val, &tmp_led);
     inmem_config.hue = hue;
     inmem_config.sat = sat;
@@ -351,66 +372,84 @@ void rgblight_setrgb(uint8_t r, uint8_t g, uint8_t b) {
 
 void rgblight_set(void) {
   if (rgblight_config.enable) {
-    ws2812_setleds(led, RGBLED_NUM);
+    #ifdef RGBW
+      ws2812_setleds_rgbw(led, RGBLED_NUM);
+    #else
+      ws2812_setleds(led, RGBLED_NUM);
+    #endif
   } else {
     for (uint8_t i = 0; i < RGBLED_NUM; i++) {
       led[i].r = 0;
       led[i].g = 0;
       led[i].b = 0;
     }
-    ws2812_setleds(led, RGBLED_NUM);
+    #ifdef RGBW
+      ws2812_setleds_rgbw(led, RGBLED_NUM);
+    #else
+      ws2812_setleds(led, RGBLED_NUM);
+    #endif
   }
 }
 
-#if !defined(AUDIO_ENABLE) && defined(RGBLIGHT_TIMER)
+#ifdef RGBLIGHT_ANIMATIONS
 
 // Animation timer -- AVR Timer3
 void rgblight_timer_init(void) {
-  static uint8_t rgblight_timer_is_init = 0;
-  if (rgblight_timer_is_init) {
-    return;
-  }
-  rgblight_timer_is_init = 1;
-  /* Timer 3 setup */
-  TCCR3B = _BV(WGM32) //CTC mode OCR3A as TOP
-        | _BV(CS30); //Clock selelct: clk/1
-  /* Set TOP value */
-  uint8_t sreg = SREG;
-  cli();
-  OCR3AH = (RGBLED_TIMER_TOP >> 8) & 0xff;
-  OCR3AL = RGBLED_TIMER_TOP & 0xff;
-  SREG = sreg;
+  // static uint8_t rgblight_timer_is_init = 0;
+  // if (rgblight_timer_is_init) {
+  //   return;
+  // }
+  // rgblight_timer_is_init = 1;
+  // /* Timer 3 setup */
+  // TCCR3B = _BV(WGM32) // CTC mode OCR3A as TOP
+  //       | _BV(CS30); // Clock selelct: clk/1
+  // /* Set TOP value */
+  // uint8_t sreg = SREG;
+  // cli();
+  // OCR3AH = (RGBLED_TIMER_TOP >> 8) & 0xff;
+  // OCR3AL = RGBLED_TIMER_TOP & 0xff;
+  // SREG = sreg;
+
+  rgblight_timer_enabled = true;
 }
 void rgblight_timer_enable(void) {
-  TIMSK3 |= _BV(OCIE3A);
+  rgblight_timer_enabled = true;
   dprintf("TIMER3 enabled.\n");
 }
 void rgblight_timer_disable(void) {
-  TIMSK3 &= ~_BV(OCIE3A);
+  rgblight_timer_enabled = false;
   dprintf("TIMER3 disabled.\n");
 }
 void rgblight_timer_toggle(void) {
-  TIMSK3 ^= _BV(OCIE3A);
+  rgblight_timer_enabled ^= rgblight_timer_enabled;
   dprintf("TIMER3 toggled.\n");
 }
 
-ISR(TIMER3_COMPA_vect) {
-  // mode = 1, static light, do nothing here
-  if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) {
-    // mode = 2 to 5, breathing mode
-    rgblight_effect_breathing(rgblight_config.mode - 2);
-  } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 8) {
-    // mode = 6 to 8, rainbow mood mod
-    rgblight_effect_rainbow_mood(rgblight_config.mode - 6);
-  } else if (rgblight_config.mode >= 9 && rgblight_config.mode <= 14) {
-    // mode = 9 to 14, rainbow swirl mode
-    rgblight_effect_rainbow_swirl(rgblight_config.mode - 9);
-  } else if (rgblight_config.mode >= 15 && rgblight_config.mode <= 20) {
-    // mode = 15 to 20, snake mode
-    rgblight_effect_snake(rgblight_config.mode - 15);
-  } else if (rgblight_config.mode >= 21 && rgblight_config.mode <= 23) {
-    // mode = 21 to 23, knight mode
-    rgblight_effect_knight(rgblight_config.mode - 21);
+void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) {
+  rgblight_enable();
+  rgblight_mode(1);
+  rgblight_setrgb(r, g, b);
+}
+
+void rgblight_task(void) {
+  if (rgblight_timer_enabled) {
+    // mode = 1, static light, do nothing here
+    if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) {
+      // mode = 2 to 5, breathing mode
+      rgblight_effect_breathing(rgblight_config.mode - 2);
+    } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 8) {
+      // mode = 6 to 8, rainbow mood mod
+      rgblight_effect_rainbow_mood(rgblight_config.mode - 6);
+    } else if (rgblight_config.mode >= 9 && rgblight_config.mode <= 14) {
+      // mode = 9 to 14, rainbow swirl mode
+      rgblight_effect_rainbow_swirl(rgblight_config.mode - 9);
+    } else if (rgblight_config.mode >= 15 && rgblight_config.mode <= 20) {
+      // mode = 15 to 20, snake mode
+      rgblight_effect_snake(rgblight_config.mode - 15);
+    } else if (rgblight_config.mode >= 21 && rgblight_config.mode <= 23) {
+      // mode = 21 to 23, knight mode
+      rgblight_effect_knight(rgblight_config.mode - 21);
+    }
   }
 }
 
@@ -449,7 +488,7 @@ void rgblight_effect_rainbow_swirl(uint8_t interval) {
   last_timer = timer_read();
   for (i = 0; i < RGBLED_NUM; i++) {
     hue = (360 / RGBLED_NUM * i + current_hue) % 360;
-    sethsv(hue, rgblight_config.sat, rgblight_config.val, &led[i]);
+    sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
   }
   rgblight_set();
 
@@ -486,7 +525,7 @@ void rgblight_effect_snake(uint8_t interval) {
         k = k + RGBLED_NUM;
       }
       if (i == k) {
-        sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val*(RGBLIGHT_EFFECT_SNAKE_LENGTH-j)/RGBLIGHT_EFFECT_SNAKE_LENGTH), &led[i]);
+        sethsv(rgblight_config.hue, rgblight_config.sat, (uint8_t)(rgblight_config.val*(RGBLIGHT_EFFECT_SNAKE_LENGTH-j)/RGBLIGHT_EFFECT_SNAKE_LENGTH), (LED_TYPE *)&led[i]);
       }
     }
   }
@@ -506,7 +545,7 @@ void rgblight_effect_knight(uint8_t interval) {
   static uint16_t last_timer = 0;
   uint8_t i, j, cur;
   int8_t k;
-  struct cRGB preled[RGBLED_NUM];
+  LED_TYPE preled[RGBLED_NUM];
   static int8_t increment = -1;
   if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_KNIGHT_INTERVALS[interval])) {
     return;
@@ -525,7 +564,7 @@ void rgblight_effect_knight(uint8_t interval) {
         k = RGBLED_NUM - 1;
       }
       if (i == k) {
-        sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, &preled[i]);
+        sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&preled[i]);
       }
     }
   }
diff --git a/quantum/rgblight.h b/quantum/rgblight.h
index 17f04ffcf2..aa1d026e0e 100644
--- a/quantum/rgblight.h
+++ b/quantum/rgblight.h
@@ -1,8 +1,7 @@
 #ifndef RGBLIGHT_H
 #define RGBLIGHT_H
 
-
-#if !defined(AUDIO_ENABLE) && defined(RGBLIGHT_TIMER)
+#ifdef RGBLIGHT_ANIMATIONS
 	#define RGBLIGHT_MODES 23
 #else
 	#define RGBLIGHT_MODES 1
@@ -34,6 +33,7 @@
 #endif
 
 #define RGBLED_TIMER_TOP F_CPU/(256*64)
+// #define RGBLED_TIMER_TOP 0xFF10
 
 #include <stdint.h>
 #include <stdbool.h>
@@ -61,9 +61,11 @@ void rgblight_init(void);
 void rgblight_increase(void);
 void rgblight_decrease(void);
 void rgblight_toggle(void);
+void rgblight_enable(void);
 void rgblight_step(void);
 void rgblight_mode(uint8_t mode);
 void rgblight_set(void);
+void rgblight_update_dword(uint32_t dword);
 void rgblight_increase_hue(void);
 void rgblight_decrease_hue(void);
 void rgblight_increase_sat(void);
@@ -78,10 +80,15 @@ void eeconfig_update_rgblight(uint32_t val);
 void eeconfig_update_rgblight_default(void);
 void eeconfig_debug_rgblight(void);
 
-void sethsv(uint16_t hue, uint8_t sat, uint8_t val, struct cRGB *led1);
-void setrgb(uint8_t r, uint8_t g, uint8_t b, struct cRGB *led1);
+void sethsv(uint16_t hue, uint8_t sat, uint8_t val, LED_TYPE *led1);
+void setrgb(uint8_t r, uint8_t g, uint8_t b, LED_TYPE *led1);
 void rgblight_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val);
 
+#define EZ_RGB(val) rgblight_show_solid_color((val >> 16) & 0xFF, (val >> 8) & 0xFF, val & 0xFF)
+void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b);
+
+void rgblight_task(void);
+
 void rgblight_timer_init(void);
 void rgblight_timer_enable(void);
 void rgblight_timer_disable(void);
diff --git a/quantum/variable_trace.c b/quantum/variable_trace.c
new file mode 100644
index 0000000000..de580244c3
--- /dev/null
+++ b/quantum/variable_trace.c
@@ -0,0 +1,110 @@
+#include "variable_trace.h"
+#include <stddef.h>
+#include <string.h>
+
+#ifdef NO_PRINT
+#error "You need undef NO_PRINT to use the variable trace feature"
+#endif
+
+#ifndef CONSOLE_ENABLE
+#error "The console needs to be enabled in the makefile to use the variable trace feature"
+#endif
+
+
+#define NUM_TRACED_VARIABLES 1
+#ifndef MAX_VARIABLE_TRACE_SIZE
+    #define MAX_VARIABLE_TRACE_SIZE 4
+#endif
+
+typedef struct {
+    const char* name;
+    void* addr;
+    unsigned size;
+    const char* func;
+    int line;
+    uint8_t last_value[MAX_VARIABLE_TRACE_SIZE];
+
+} traced_variable_t;
+
+static traced_variable_t traced_variables[NUM_TRACED_VARIABLES];
+
+void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line) {
+    verify_traced_variables(func, line);
+    if (size > MAX_VARIABLE_TRACE_SIZE) {
+#if defined(__AVR__)
+       xprintf("Traced variable \"%S\" exceeds the maximum size %d\n", name, size);
+#else
+       xprintf("Traced variable \"%s\" exceeds the maximum size %d\n", name, size);
+#endif
+       size = MAX_VARIABLE_TRACE_SIZE;
+    }
+    int index = -1;
+    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) {
+        if (index == -1 && traced_variables[i].addr == NULL){
+            index = i;
+        }
+        else if (strcmp_P(name, traced_variables[i].name)==0) {
+            index = i;
+            break;
+        }
+    }
+
+    if (index == -1) {
+        xprintf("You can only trace %d variables at the same time\n", NUM_TRACED_VARIABLES);
+        return;
+    }
+
+    traced_variable_t* t = &traced_variables[index];
+    t->name = name;
+    t->addr = addr;
+    t->size = size;
+    t->func = func;
+    t->line = line;
+    memcpy(&t->last_value[0], addr, size);
+
+}
+
+void remove_traced_variable(const char* name, const char* func, int line) {
+    verify_traced_variables(func, line);
+    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) {
+        if (strcmp_P(name, traced_variables[i].name)==0) {
+            traced_variables[i].name = 0;
+            traced_variables[i].addr = NULL;
+            break;
+        }
+    }
+}
+
+void verify_traced_variables(const char* func, int line) {
+    for (int i = 0; i < NUM_TRACED_VARIABLES; i++) {
+        traced_variable_t* t = &traced_variables[i];
+        if (t->addr != NULL && t->name != NULL) {
+            if (memcmp(t->last_value, t->addr, t->size)!=0){
+#if defined(__AVR__)
+               xprintf("Traced variable \"%S\" has been modified\n", t->name);
+               xprintf("Between %S:%d\n", t->func, t->line);
+               xprintf("And %S:%d\n", func, line);
+
+#else
+               xprintf("Traced variable \"%s\" has been modified\n", t->name);
+               xprintf("Between %s:%d\n", t->func, t->line);
+               xprintf("And %s:%d\n", func, line);
+#endif
+               xprintf("Previous value ");
+               for (int j=0; j<t->size;j++) {
+                   print_hex8(t->last_value[j]);
+               }
+               xprintf("\nNew value ");
+               uint8_t* addr = (uint8_t*)(t->addr);
+               for (int j=0; j<t->size;j++) {
+                   print_hex8(addr[j]);
+               }
+               xprintf("\n");
+               memcpy(t->last_value, addr, t->size);
+           }
+        }
+
+        t->func = func;
+        t->line = line;
+    }
+}
diff --git a/quantum/variable_trace.h b/quantum/variable_trace.h
new file mode 100644
index 0000000000..46bd827861
--- /dev/null
+++ b/quantum/variable_trace.h
@@ -0,0 +1,34 @@
+#ifndef VARIABLE_TRACE_H
+#define VARIABLE_TRACE_H
+
+// For more information about the variable tracing see the readme.
+
+#include "print.h"
+
+#ifdef NUM_TRACED_VARIABLES
+
+// Start tracing a variable at the memory address addr
+// The name can be anything and is used only for reporting
+// The size should usually be the same size as the variable you are interested in
+#define ADD_TRACED_VARIABLE(name, addr, size) \
+    add_traced_variable(PSTR(name), (void*)addr, size, PSTR(__FILE__), __LINE__)
+
+// Stop tracing the variable with the given name
+#define REMOVE_TRACED_VARIABLE(name) remove_traced_variable(PSTR(name), PSTR(__FILE__), __LINE__)
+
+// Call to get messages when the variable has been changed
+#define VERIFY_TRACED_VARIABLES() verify_traced_variables(PSTR(__FILE__), __LINE__)
+
+#else
+
+#define ADD_TRACED_VARIABLE(name, addr, size)
+#define REMOVE_TRACED_VARIABLE(name)
+#define VERIFY_TRACED_VARIABLES()
+
+#endif
+
+// Don't call directly, use the macros instead
+void add_traced_variable(const char* name, void* addr, unsigned size, const char* func, int line);
+void remove_traced_variable(const char* name, const char* func, int line);
+void verify_traced_variables(const char* func, int line);
+#endif