summary refs log tree commit diff
path: root/quantum
diff options
context:
space:
mode:
Diffstat (limited to 'quantum')
-rw-r--r--quantum/api.h4
-rw-r--r--quantum/audio/audio.c8
-rw-r--r--quantum/audio/audio.h3
-rw-r--r--quantum/audio/audio_arm.c135
-rw-r--r--quantum/audio/song_list.h30
-rw-r--r--quantum/config_common.h257
-rw-r--r--quantum/dynamic_keymap.c230
-rw-r--r--quantum/dynamic_keymap.h63
-rw-r--r--quantum/encoder.c70
-rw-r--r--quantum/encoder.h (renamed from quantum/process_keycode/process_chording.h)21
-rw-r--r--quantum/keymap_common.c2
-rw-r--r--quantum/keymap_extras/keymap_belgian.h9
-rw-r--r--quantum/keymap_extras/keymap_bepo.h153
-rw-r--r--quantum/keymap_extras/keymap_canadian_multilingual.h49
-rw-r--r--quantum/keymap_extras/keymap_fr_ch.h4
-rw-r--r--quantum/keymap_extras/keymap_french.h6
-rw-r--r--quantum/keymap_extras/keymap_german.h4
-rw-r--r--quantum/keymap_extras/keymap_german_ch.h4
-rw-r--r--quantum/keymap_extras/keymap_hungarian.h4
-rw-r--r--quantum/keymap_extras/keymap_italian.h7
-rw-r--r--quantum/keymap_extras/keymap_jp.h3
-rw-r--r--quantum/keymap_extras/keymap_neo2.h2
-rw-r--r--quantum/keymap_extras/keymap_nordic.h4
-rw-r--r--quantum/keymap_extras/keymap_plover_dvorak.h47
-rw-r--r--quantum/keymap_extras/keymap_slovenian.h107
-rw-r--r--quantum/keymap_extras/keymap_spanish.h4
-rw-r--r--quantum/keymap_extras/keymap_swedish.h4
-rw-r--r--quantum/keymap_extras/keymap_uk.h4
-rw-r--r--quantum/keymap_extras/sendstring_german.h81
-rw-r--r--quantum/matrix.c49
-rw-r--r--quantum/process_keycode/process_auto_shift.c2
-rw-r--r--quantum/process_keycode/process_chording.c76
-rw-r--r--quantum/process_keycode/process_clicky.c72
-rw-r--r--quantum/process_keycode/process_clicky.h10
-rw-r--r--quantum/process_keycode/process_leader.c49
-rw-r--r--quantum/process_keycode/process_leader.h2
-rw-r--r--quantum/process_keycode/process_tap_dance.c6
-rw-r--r--quantum/process_keycode/process_tap_dance.h1
-rw-r--r--quantum/process_keycode/process_ucis.c8
-rw-r--r--quantum/process_keycode/process_ucis.h6
-rw-r--r--quantum/process_keycode/process_unicode.c7
-rw-r--r--quantum/process_keycode/process_unicode.h5
-rw-r--r--quantum/process_keycode/process_unicode_common.c223
-rw-r--r--quantum/process_keycode/process_unicode_common.h68
-rw-r--r--quantum/process_keycode/process_unicodemap.c4
-rw-r--r--quantum/process_keycode/process_unicodemap.h4
-rw-r--r--quantum/quantum.c149
-rw-r--r--quantum/quantum.h95
-rw-r--r--quantum/quantum_keycodes.h129
-rw-r--r--quantum/rgb_matrix.c446
-rw-r--r--quantum/rgb_matrix.h120
-rw-r--r--quantum/rgb_matrix_drivers.c82
-rw-r--r--quantum/rgblight.c343
-rw-r--r--quantum/rgblight.h147
-rw-r--r--quantum/rgblight_reconfig.h36
-rw-r--r--quantum/split_common/i2c.c3
-rw-r--r--quantum/split_common/matrix.c160
-rw-r--r--quantum/split_common/matrix.h31
-rw-r--r--quantum/split_common/serial.c548
-rw-r--r--quantum/split_common/serial.h78
-rw-r--r--quantum/split_common/split_util.c51
-rw-r--r--quantum/template/avr/config.h31
-rw-r--r--quantum/template/avr/readme.md2
-rw-r--r--quantum/template/avr/rules.mk25
-rw-r--r--quantum/template/avr/template.h14
-rw-r--r--quantum/template/base/keymaps/default/keymap.c54
-rw-r--r--quantum/template/ps2avrgb/config.h4
-rw-r--r--quantum/template/ps2avrgb/readme.md2
-rw-r--r--quantum/template/ps2avrgb/rules.mk4
-rw-r--r--quantum/template/ps2avrgb/template.h12
70 files changed, 3146 insertions, 1331 deletions
diff --git a/quantum/api.h b/quantum/api.h
index efc0ddca12..fc016391bc 100644
--- a/quantum/api.h
+++ b/quantum/api.h
@@ -17,7 +17,9 @@
 #ifndef _API_H_
 #define _API_H_
 
+#ifdef __AVR__
 #include "lufa.h"
+#endif
 
 enum MESSAGE_TYPE {
     MT_GET_DATA =      0x10, // Get data from keyboard
@@ -28,7 +30,7 @@ enum MESSAGE_TYPE {
     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)
+    MT_TYPE_ERROR =    0x80 // type not recognised (ACK)
 };
 
 enum DATA_TYPE {
diff --git a/quantum/audio/audio.c b/quantum/audio/audio.c
index c948a60d6c..6d6833ec11 100644
--- a/quantum/audio/audio.c
+++ b/quantum/audio/audio.c
@@ -223,7 +223,7 @@ void audio_init()
             TCCR1B = (1 << WGM13)  | (1 << WGM12)  | (0 << CS12)  | (1 << CS11) | (0 << CS10);
             TIMER_1_PERIOD = (uint16_t)(((float)F_CPU) / (440 * CPU_PRESCALER));
             TIMER_1_DUTY_CYCLE = (uint16_t)((((float)F_CPU) / (440 * CPU_PRESCALER)) * note_timbre);
-        #endif 
+        #endif
 
         audio_initialized = true;
     }
@@ -231,7 +231,7 @@ void audio_init()
     if (audio_config.enable) {
         PLAY_SONG(startup_song);
     }
-    
+
 }
 
 void stop_all_notes()
@@ -464,7 +464,7 @@ ISR(TIMER3_AUDIO_vect)
         note_position++;
         bool end_of_note = false;
         if (TIMER_3_PERIOD > 0) {
-            if (!note_resting) 
+            if (!note_resting)
                 end_of_note = (note_position >= (note_length / TIMER_3_PERIOD * 0xFFFF - 1));
             else
                 end_of_note = (note_position >= (note_length));
@@ -604,7 +604,7 @@ ISR(TIMER1_AUDIO_vect)
         note_position++;
         bool end_of_note = false;
         if (TIMER_1_PERIOD > 0) {
-            if (!note_resting) 
+            if (!note_resting)
                 end_of_note = (note_position >= (note_length / TIMER_1_PERIOD * 0xFFFF - 1));
             else
                 end_of_note = (note_position >= (note_length));
diff --git a/quantum/audio/audio.h b/quantum/audio/audio.h
index da09b2bcd2..8136c5b258 100644
--- a/quantum/audio/audio.h
+++ b/quantum/audio/audio.h
@@ -40,7 +40,8 @@ typedef union {
     uint8_t raw;
     struct {
         bool    enable :1;
-        uint8_t level  :7;
+        bool    clicky_enable :1;
+        uint8_t level  :6;
     };
 } audio_config_t;
 
diff --git a/quantum/audio/audio_arm.c b/quantum/audio/audio_arm.c
index d38184f323..989f7a64be 100644
--- a/quantum/audio/audio_arm.c
+++ b/quantum/audio/audio_arm.c
@@ -80,6 +80,9 @@ float startup_song[][2] = STARTUP_SONG;
 static void gpt_cb8(GPTDriver *gptp);
 
 #define DAC_BUFFER_SIZE 720
+#ifndef DAC_SAMPLE_MAX
+#define DAC_SAMPLE_MAX  65535U
+#endif
 
 #define START_CHANNEL_1() gptStart(&GPTD6, &gpt6cfg1); \
     gptStartContinuous(&GPTD6, 2U)
@@ -202,132 +205,16 @@ GPTConfig gpt8cfg1 = {
 
 // squarewave
 static const dacsample_t dac_buffer[DAC_BUFFER_SIZE] = {
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+  // First half is max, second half is 0
+  [0                 ... DAC_BUFFER_SIZE/2-1] = DAC_SAMPLE_MAX,
+  [DAC_BUFFER_SIZE/2 ... DAC_BUFFER_SIZE  -1] = 0,
 };
 
 // squarewave
 static const dacsample_t dac_buffer_2[DAC_BUFFER_SIZE] = {
-
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
-    0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
-   0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047,
-  2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047, 2047
+  // opposite of dac_buffer above
+  [0                 ... DAC_BUFFER_SIZE/2-1] = 0,
+  [DAC_BUFFER_SIZE/2 ... DAC_BUFFER_SIZE  -1] = DAC_SAMPLE_MAX,
 };
 
 /*
@@ -363,7 +250,7 @@ static void error_cb1(DACDriver *dacp, dacerror_t err) {
 }
 
 static const DACConfig dac1cfg1 = {
-  .init         = 2047U,
+  .init         = DAC_SAMPLE_MAX,
   .datamode     = DAC_DHRM_12BIT_RIGHT
 };
 
@@ -375,7 +262,7 @@ static const DACConversionGroup dacgrpcfg1 = {
 };
 
 static const DACConfig dac1cfg2 = {
-  .init         = 2047U,
+  .init         = DAC_SAMPLE_MAX,
   .datamode     = DAC_DHRM_12BIT_RIGHT
 };
 
diff --git a/quantum/audio/song_list.h b/quantum/audio/song_list.h
index e63616a994..9946084977 100644
--- a/quantum/audio/song_list.h
+++ b/quantum/audio/song_list.h
@@ -472,4 +472,34 @@
     H__NOTE(_AS4), W__NOTE(_GS4), W__NOTE(_GS4), W__NOTE(_FS4), W__NOTE(_GS4), \
     H__NOTE(_AS4), WD_NOTE(_DS4)
 
+#define RICK_ROLL      \
+    Q__NOTE(_F4),      \
+    Q__NOTE(_G4),      \
+    Q__NOTE(_BF4),     \
+    Q__NOTE(_G4),      \
+    HD_NOTE(_D5),      \
+    HD_NOTE(_D5),      \
+    W__NOTE(_C5),      \
+    S__NOTE(_REST),    \
+    Q__NOTE(_F4),      \
+    Q__NOTE(_G4),      \
+    Q__NOTE(_BF4),     \
+    Q__NOTE(_G4),      \
+    HD_NOTE(_C5),      \
+    HD_NOTE(_C5),      \
+    W__NOTE(_BF4),     \
+    S__NOTE(_REST),    \
+    Q__NOTE(_F4),      \
+    Q__NOTE(_G4),      \
+    Q__NOTE(_BF4),     \
+    Q__NOTE(_G4),      \
+    W__NOTE(_BF4),     \
+    H__NOTE(_C5),      \
+    H__NOTE(_A4),      \
+    H__NOTE(_A4),      \
+    H__NOTE(_G4),      \
+    H__NOTE(_F4),      \
+    H__NOTE(_F4),      \
+    W__NOTE(_C5),      \
+    W__NOTE(_BF4), 
 #endif
diff --git a/quantum/config_common.h b/quantum/config_common.h
index f6f51b367d..cbff372eaf 100644
--- a/quantum/config_common.h
+++ b/quantum/config_common.h
@@ -1,4 +1,4 @@
-/* Copyright 2015-2017 Jack Humbert
+/* Copyright 2015-2018 Jack Humbert
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,8 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef CONFIG_DEFINITIONS_H
-#define CONFIG_DEFINITIONS_H
+#pragma once
 
 /* diode directions */
 #define COL2ROW       0
@@ -23,57 +22,205 @@
 #define CUSTOM_MATRIX 2 /* Disables built-in matrix scanning code */
 
 #ifdef __AVR__
-  /* I/O pins */
-  #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
+    #ifndef __ASSEMBLER__
+      #include <avr/io.h>
+    #endif
+    #define PORT_SHIFTER 4 // this may be 4 for all AVR chips
+
+    // If you want to add more to this list, reference the PINx definitions in these header
+    // files: https://github.com/vancegroup-mirrors/avr-libc/tree/master/avr-libc/include/avr
+
+    #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega16U4__)
+        #define ADDRESS_BASE 0x00
+        #define PINB_ADDRESS 0x3
+        #define PINC_ADDRESS 0x6
+        #define PIND_ADDRESS 0x9
+        #define PINE_ADDRESS 0xC
+        #define PINF_ADDRESS 0xF
+    #elif defined(__AVR_ATmega32U2__) || defined(__AVR_ATmega16U2__)
+        #define ADDRESS_BASE 0x00
+        #define PINB_ADDRESS 0x3
+        #define PINC_ADDRESS 0x6
+        #define PIND_ADDRESS 0x9
+    #elif defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB646__)
+        #define ADDRESS_BASE 0x00
+        #define PINA_ADDRESS 0x0
+        #define PINB_ADDRESS 0x3
+        #define PINC_ADDRESS 0x6
+        #define PIND_ADDRESS 0x9
+        #define PINE_ADDRESS 0xC
+        #define PINF_ADDRESS 0xF
+    #elif defined(__AVR_ATmega32A__)
+        #define ADDRESS_BASE 0x10
+        #define PIND_ADDRESS 0x0
+        #define PINC_ADDRESS 0x3
+        #define PINB_ADDRESS 0x6
+        #define PINA_ADDRESS 0x9
+    #else
+        #error "Pins are not defined"
+    #endif
+
+    /* I/O pins */
+    #define PINDEF(port, pin) ((PIN##port##_ADDRESS << PORT_SHIFTER) | pin)
+
+    #ifdef PORTA
+        #define A0 PINDEF(A, 0)
+        #define A1 PINDEF(A, 1)
+        #define A2 PINDEF(A, 2)
+        #define A3 PINDEF(A, 3)
+        #define A4 PINDEF(A, 4)
+        #define A5 PINDEF(A, 5)
+        #define A6 PINDEF(A, 6)
+        #define A7 PINDEF(A, 7)
+    #endif
+    #ifdef PORTB
+        #define B0 PINDEF(B, 0)
+        #define B1 PINDEF(B, 1)
+        #define B2 PINDEF(B, 2)
+        #define B3 PINDEF(B, 3)
+        #define B4 PINDEF(B, 4)
+        #define B5 PINDEF(B, 5)
+        #define B6 PINDEF(B, 6)
+        #define B7 PINDEF(B, 7)
+    #endif
+    #ifdef PORTC
+        #define C0 PINDEF(C, 0)
+        #define C1 PINDEF(C, 1)
+        #define C2 PINDEF(C, 2)
+        #define C3 PINDEF(C, 3)
+        #define C4 PINDEF(C, 4)
+        #define C5 PINDEF(C, 5)
+        #define C6 PINDEF(C, 6)
+        #define C7 PINDEF(C, 7)
+    #endif
+    #ifdef PORTD
+        #define D0 PINDEF(D, 0)
+        #define D1 PINDEF(D, 1)
+        #define D2 PINDEF(D, 2)
+        #define D3 PINDEF(D, 3)
+        #define D4 PINDEF(D, 4)
+        #define D5 PINDEF(D, 5)
+        #define D6 PINDEF(D, 6)
+        #define D7 PINDEF(D, 7)
+    #endif
+    #ifdef PORTE
+        #define E0 PINDEF(E, 0)
+        #define E1 PINDEF(E, 1)
+        #define E2 PINDEF(E, 2)
+        #define E3 PINDEF(E, 3)
+        #define E4 PINDEF(E, 4)
+        #define E5 PINDEF(E, 5)
+        #define E6 PINDEF(E, 6)
+        #define E7 PINDEF(E, 7)
+    #endif
+    #ifdef PORTF
+        #define F0 PINDEF(F, 0)
+        #define F1 PINDEF(F, 1)
+        #define F2 PINDEF(F, 2)
+        #define F3 PINDEF(F, 3)
+        #define F4 PINDEF(F, 4)
+        #define F5 PINDEF(F, 5)
+        #define F6 PINDEF(F, 6)
+        #define F7 PINDEF(F, 7)
+    #endif
+
+#elif defined(PROTOCOL_CHIBIOS)
+    #define A0  PAL_LINE(GPIOA, 0)
+    #define A1  PAL_LINE(GPIOA, 1)
+    #define A2  PAL_LINE(GPIOA, 2)
+    #define A3  PAL_LINE(GPIOA, 3)
+    #define A4  PAL_LINE(GPIOA, 4)
+    #define A5  PAL_LINE(GPIOA, 5)
+    #define A6  PAL_LINE(GPIOA, 6)
+    #define A7  PAL_LINE(GPIOA, 7)
+    #define A8  PAL_LINE(GPIOA, 8)
+    #define A9  PAL_LINE(GPIOA, 9)
+    #define A10 PAL_LINE(GPIOA, 10)
+    #define A11 PAL_LINE(GPIOA, 11)
+    #define A12 PAL_LINE(GPIOA, 12)
+    #define A13 PAL_LINE(GPIOA, 13)
+    #define A14 PAL_LINE(GPIOA, 14)
+    #define A15 PAL_LINE(GPIOA, 15)
+    #define B0  PAL_LINE(GPIOB, 0)
+    #define B1  PAL_LINE(GPIOB, 1)
+    #define B2  PAL_LINE(GPIOB, 2)
+    #define B3  PAL_LINE(GPIOB, 3)
+    #define B4  PAL_LINE(GPIOB, 4)
+    #define B5  PAL_LINE(GPIOB, 5)
+    #define B6  PAL_LINE(GPIOB, 6)
+    #define B7  PAL_LINE(GPIOB, 7)
+    #define B8  PAL_LINE(GPIOB, 8)
+    #define B9  PAL_LINE(GPIOB, 9)
+    #define B10 PAL_LINE(GPIOB, 10)
+    #define B11 PAL_LINE(GPIOB, 11)
+    #define B12 PAL_LINE(GPIOB, 12)
+    #define B13 PAL_LINE(GPIOB, 13)
+    #define B14 PAL_LINE(GPIOB, 14)
+    #define B15 PAL_LINE(GPIOB, 15)
+    #define C0  PAL_LINE(GPIOC, 0)
+    #define C1  PAL_LINE(GPIOC, 1)
+    #define C2  PAL_LINE(GPIOC, 2)
+    #define C3  PAL_LINE(GPIOC, 3)
+    #define C4  PAL_LINE(GPIOC, 4)
+    #define C5  PAL_LINE(GPIOC, 5)
+    #define C6  PAL_LINE(GPIOC, 6)
+    #define C7  PAL_LINE(GPIOC, 7)
+    #define C8  PAL_LINE(GPIOC, 8)
+    #define C9  PAL_LINE(GPIOC, 9)
+    #define C10 PAL_LINE(GPIOC, 10)
+    #define C11 PAL_LINE(GPIOC, 11)
+    #define C12 PAL_LINE(GPIOC, 12)
+    #define C13 PAL_LINE(GPIOC, 13)
+    #define C14 PAL_LINE(GPIOC, 14)
+    #define C15 PAL_LINE(GPIOC, 15)
+    #define D0  PAL_LINE(GPIOD, 0)
+    #define D1  PAL_LINE(GPIOD, 1)
+    #define D2  PAL_LINE(GPIOD, 2)
+    #define D3  PAL_LINE(GPIOD, 3)
+    #define D4  PAL_LINE(GPIOD, 4)
+    #define D5  PAL_LINE(GPIOD, 5)
+    #define D6  PAL_LINE(GPIOD, 6)
+    #define D7  PAL_LINE(GPIOD, 7)
+    #define D8  PAL_LINE(GPIOD, 8)
+    #define D9  PAL_LINE(GPIOD, 9)
+    #define D10 PAL_LINE(GPIOD, 10)
+    #define D11 PAL_LINE(GPIOD, 11)
+    #define D12 PAL_LINE(GPIOD, 12)
+    #define D13 PAL_LINE(GPIOD, 13)
+    #define D14 PAL_LINE(GPIOD, 14)
+    #define D15 PAL_LINE(GPIOD, 15)
+    #define E0  PAL_LINE(GPIOE, 0)
+    #define E1  PAL_LINE(GPIOE, 1)
+    #define E2  PAL_LINE(GPIOE, 2)
+    #define E3  PAL_LINE(GPIOE, 3)
+    #define E4  PAL_LINE(GPIOE, 4)
+    #define E5  PAL_LINE(GPIOE, 5)
+    #define E6  PAL_LINE(GPIOE, 6)
+    #define E7  PAL_LINE(GPIOE, 7)
+    #define E8  PAL_LINE(GPIOE, 8)
+    #define E9  PAL_LINE(GPIOE, 9)
+    #define E10 PAL_LINE(GPIOE, 10)
+    #define E11 PAL_LINE(GPIOE, 11)
+    #define E12 PAL_LINE(GPIOE, 12)
+    #define E13 PAL_LINE(GPIOE, 13)
+    #define E14 PAL_LINE(GPIOE, 14)
+    #define E15 PAL_LINE(GPIOE, 15)
+    #define F0  PAL_LINE(GPIOF, 0)
+    #define F1  PAL_LINE(GPIOF, 1)
+    #define F2  PAL_LINE(GPIOF, 2)
+    #define F3  PAL_LINE(GPIOF, 3)
+    #define F4  PAL_LINE(GPIOF, 4)
+    #define F5  PAL_LINE(GPIOF, 5)
+    #define F6  PAL_LINE(GPIOF, 6)
+    #define F7  PAL_LINE(GPIOF, 7)
+    #define F8  PAL_LINE(GPIOF, 8)
+    #define F9  PAL_LINE(GPIOF, 9)
+    #define F10 PAL_LINE(GPIOF, 10)
+    #define F11 PAL_LINE(GPIOF, 11)
+    #define F12 PAL_LINE(GPIOF, 12)
+    #define F13 PAL_LINE(GPIOF, 13)
+    #define F14 PAL_LINE(GPIOF, 14)
+    #define F15 PAL_LINE(GPIOF, 15)
 #endif
 
 /* USART configuration */
@@ -103,5 +250,3 @@
 #define API_SYSEX_MAX_SIZE 32
 
 #include "song_list.h"
-
-#endif
diff --git a/quantum/dynamic_keymap.c b/quantum/dynamic_keymap.c
new file mode 100644
index 0000000000..14627a93d6
--- /dev/null
+++ b/quantum/dynamic_keymap.c
@@ -0,0 +1,230 @@
+/* Copyright 2017 Jason Williams (Wilba)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include "keymap.h" // to get keymaps[][][]
+#include "tmk_core/common/eeprom.h"
+#include "progmem.h" // to read default from flash
+#include "quantum.h" // for send_string()
+#include "dynamic_keymap.h"
+
+#ifdef DYNAMIC_KEYMAP_ENABLE
+
+#ifndef DYNAMIC_KEYMAP_EEPROM_ADDR
+#error DYNAMIC_KEYMAP_EEPROM_ADDR not defined
+#endif
+
+#ifndef DYNAMIC_KEYMAP_LAYER_COUNT
+#error DYNAMIC_KEYMAP_LAYER_COUNT not defined
+#endif
+
+#ifndef DYNAMIC_KEYMAP_MACRO_COUNT
+#error DYNAMIC_KEYMAP_MACRO_COUNT not defined
+#endif
+
+#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR
+#error DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR not defined
+#endif
+
+#ifndef DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE
+#error DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE not defined
+#endif
+
+uint8_t dynamic_keymap_get_layer_count(void)
+{
+	return DYNAMIC_KEYMAP_LAYER_COUNT;
+}
+
+void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column)
+{
+	// TODO: optimize this with some left shifts
+	return ((void*)DYNAMIC_KEYMAP_EEPROM_ADDR) + ( layer * MATRIX_ROWS * MATRIX_COLS * 2 ) +
+		( row * MATRIX_COLS * 2 ) + ( column * 2 );
+}
+
+uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column)
+{
+	void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
+	// Big endian, so we can read/write EEPROM directly from host if we want
+	uint16_t keycode = eeprom_read_byte(address) << 8;
+	keycode |= eeprom_read_byte(address + 1);
+	return keycode;
+}
+
+void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode)
+{
+	void *address = dynamic_keymap_key_to_eeprom_address(layer, row, column);
+	// Big endian, so we can read/write EEPROM directly from host if we want
+	eeprom_update_byte(address, (uint8_t)(keycode >> 8));
+	eeprom_update_byte(address+1, (uint8_t)(keycode & 0xFF));
+}
+
+void dynamic_keymap_reset(void)
+{
+	// Reset the keymaps in EEPROM to what is in flash.
+	// All keyboards using dynamic keymaps should define a layout
+	// for the same number of layers as DYNAMIC_KEYMAP_LAYER_COUNT.
+	for ( int layer = 0; layer < DYNAMIC_KEYMAP_LAYER_COUNT; layer++ )	{
+		for ( int row = 0; row < MATRIX_ROWS; row++ ) {
+			for ( int column = 0; column < MATRIX_COLS; column++ )	{
+				dynamic_keymap_set_keycode(layer, row, column, pgm_read_word(&keymaps[layer][row][column]));
+			}
+		}
+	}
+}
+
+void dynamic_keymap_get_buffer( uint16_t offset, uint16_t size, uint8_t *data )
+{
+	uint16_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
+	void *source = (void*)(DYNAMIC_KEYMAP_EEPROM_ADDR+offset);
+	uint8_t *target = data;
+	for ( uint16_t i = 0; i < size; i++ ) {
+		if ( offset + i < dynamic_keymap_eeprom_size ) {
+			*target = eeprom_read_byte(source);
+		} else {
+			*target = 0x00;
+		}
+		source++;
+		target++;
+	}
+}
+
+void dynamic_keymap_set_buffer( uint16_t offset, uint16_t size, uint8_t *data )
+{
+	uint16_t dynamic_keymap_eeprom_size = DYNAMIC_KEYMAP_LAYER_COUNT * MATRIX_ROWS * MATRIX_COLS * 2;
+	void *target = (void*)(DYNAMIC_KEYMAP_EEPROM_ADDR+offset);
+	uint8_t *source = data;
+	for ( uint16_t i = 0; i < size; i++ ) {
+		if ( offset + i < dynamic_keymap_eeprom_size ) {
+			eeprom_update_byte(target, *source);
+		}
+		source++;
+		target++;
+	}
+}
+
+// This overrides the one in quantum/keymap_common.c
+uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key)
+{
+	if ( layer < DYNAMIC_KEYMAP_LAYER_COUNT &&
+			key.row < MATRIX_ROWS &&
+			key.col < MATRIX_COLS ) {
+		return dynamic_keymap_get_keycode(layer, key.row, key.col);
+	} else {
+		return KC_NO;
+	}
+}
+
+
+
+uint8_t dynamic_keymap_macro_get_count(void)
+{
+	return DYNAMIC_KEYMAP_MACRO_COUNT;
+}
+
+uint16_t dynamic_keymap_macro_get_buffer_size(void)
+{
+	return DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE;
+}
+
+void dynamic_keymap_macro_get_buffer( uint16_t offset, uint16_t size, uint8_t *data )
+{
+	void *source = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR+offset);
+	uint8_t *target = data;
+	for ( uint16_t i = 0; i < size; i++ ) {
+		if ( offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE ) {
+			*target = eeprom_read_byte(source);
+		} else {
+			*target = 0x00;
+		}
+		source++;
+		target++;
+	}
+}
+
+void dynamic_keymap_macro_set_buffer( uint16_t offset, uint16_t size, uint8_t *data )
+{
+	void *target = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR+offset);
+	uint8_t *source = data;
+	for ( uint16_t i = 0; i < size; i++ ) {
+		if ( offset + i < DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE ) {
+			eeprom_update_byte(target, *source);
+		}
+		source++;
+		target++;
+	}
+}
+
+void dynamic_keymap_macro_reset(void)
+{
+	void *p = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
+	void *end = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR+DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
+	while ( p != end ) {
+		eeprom_update_byte(p, 0);
+		++p;
+	}
+}
+
+void dynamic_keymap_macro_send( uint8_t id )
+{
+	if ( id >= DYNAMIC_KEYMAP_MACRO_COUNT )	{
+		return;
+	}
+
+	// Check the last byte of the buffer.
+	// If it's not zero, then we are in the middle
+	// of buffer writing, possibly an aborted buffer
+	// write. So do nothing.
+	void *p = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR+DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE-1);
+	if ( eeprom_read_byte(p) != 0 )	{
+		return;
+	}
+
+	// Skip N null characters
+	// p will then point to the Nth macro
+	p = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR);
+	void *end = (void*)(DYNAMIC_KEYMAP_MACRO_EEPROM_ADDR+DYNAMIC_KEYMAP_MACRO_EEPROM_SIZE);
+	while ( id > 0 ) {
+		// If we are past the end of the buffer, then the buffer
+		// contents are garbage, i.e. there were not DYNAMIC_KEYMAP_MACRO_COUNT
+		// nulls in the buffer.
+		if ( p == end ) {
+			return;
+		}
+		if ( eeprom_read_byte(p) == 0 ) {
+			--id;
+		}
+		++p;
+	}
+
+	// Send the macro string one char at a time
+	// by making temporary 1 char strings
+	char data[2] = { 0, 0 };
+	// We already checked there was a null at the end of
+	// the buffer, so this cannot go past the end
+	while ( 1 ) {
+		data[0] = eeprom_read_byte(p);
+		// Stop at the null terminator of this macro string
+		if ( data[0] == 0 ) {
+			break;
+		}
+		send_string(data);
+		++p;
+	}
+}
+
+#endif // DYNAMIC_KEYMAP_ENABLE
+
diff --git a/quantum/dynamic_keymap.h b/quantum/dynamic_keymap.h
new file mode 100644
index 0000000000..63653f6cb7
--- /dev/null
+++ b/quantum/dynamic_keymap.h
@@ -0,0 +1,63 @@
+/* Copyright 2017 Jason Williams (Wilba)
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+uint8_t dynamic_keymap_get_layer_count(void);
+void *dynamic_keymap_key_to_eeprom_address(uint8_t layer, uint8_t row, uint8_t column);
+uint16_t dynamic_keymap_get_keycode(uint8_t layer, uint8_t row, uint8_t column);
+void dynamic_keymap_set_keycode(uint8_t layer, uint8_t row, uint8_t column, uint16_t keycode);
+void dynamic_keymap_reset(void);
+// These get/set the keycodes as stored in the EEPROM buffer
+// Data is big-endian 16-bit values (the keycodes)
+// Order is by layer/row/column
+// Thus offset 0 = 0,0,0, offset MATRIX_COLS*2 = 0,1,0, offset MATRIX_ROWS*MATRIX_COLS*2 = 1,0,0
+// Note the *2, because offset is in bytes and keycodes are two bytes
+// This is only really useful for host applications that want to get a whole keymap fast,
+// by reading 14 keycodes (28 bytes) at a time, reducing the number of raw HID transfers by
+// a factor of 14.
+void dynamic_keymap_get_buffer( uint16_t offset, uint16_t size, uint8_t *data );
+void dynamic_keymap_set_buffer( uint16_t offset, uint16_t size, uint8_t *data );
+
+// This overrides the one in quantum/keymap_common.c
+// uint16_t keymap_key_to_keycode(uint8_t layer, keypos_t key);
+
+
+
+// Note regarding dynamic_keymap_macro_set_buffer():
+// The last byte of the buffer is used as a valid flag,
+// so macro sending is disabled during writing a new buffer,
+// should it happen during, or after an interrupted transfer.
+//
+// Users writing to the buffer must first set the last byte of the buffer
+// to non-zero (i.e. 0xFF). After (or during) the final write, set the
+// last byte of the buffer to zero.
+//
+// Since the contents of the buffer must be a list of null terminated
+// strings, the last byte must be a null when at maximum capacity,
+// and it not being null means the buffer can be considered in an
+// invalid state.
+
+uint8_t dynamic_keymap_macro_get_count(void);
+uint16_t dynamic_keymap_macro_get_buffer_size(void);
+void dynamic_keymap_macro_get_buffer( uint16_t offset, uint16_t size, uint8_t *data );
+void dynamic_keymap_macro_set_buffer( uint16_t offset, uint16_t size, uint8_t *data );
+void dynamic_keymap_macro_reset(void);
+
+void dynamic_keymap_macro_send( uint8_t id );
+
diff --git a/quantum/encoder.c b/quantum/encoder.c
new file mode 100644
index 0000000000..6629a098b8
--- /dev/null
+++ b/quantum/encoder.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2018 Jack Humbert <jack.humb@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "encoder.h"
+
+#ifndef ENCODER_RESOLUTION
+  #define ENCODER_RESOLUTION 4
+#endif
+
+#ifndef NUMBER_OF_ENCODERS
+  #error "Number of encoders not defined by NUMBER_OF_ENCODERS"
+#endif
+
+#if !defined(ENCODERS_PAD_A) || !defined(ENCODERS_PAD_B)
+  #error "No encoder pads defined by ENCODERS_PAD_A and ENCODERS_PAD_B"
+#endif
+
+static pin_t encoders_pad_a[NUMBER_OF_ENCODERS] = ENCODERS_PAD_A;
+static pin_t encoders_pad_b[NUMBER_OF_ENCODERS] = ENCODERS_PAD_B;
+
+static int8_t encoder_LUT[] = { 0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0 };
+
+static uint8_t encoder_state[NUMBER_OF_ENCODERS] = {0};
+static int8_t encoder_value[NUMBER_OF_ENCODERS] = {0};
+
+__attribute__ ((weak))
+void encoder_update_user(int8_t index, bool clockwise) { }
+
+__attribute__ ((weak))
+void encoder_update_kb(int8_t index, bool clockwise) {
+  encoder_update_user(index, clockwise);
+}
+
+void encoder_init(void) {
+  for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
+    setPinInputHigh(encoders_pad_a[i]);
+    setPinInputHigh(encoders_pad_b[i]);
+
+    encoder_state[i] = (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
+  }
+}
+
+void encoder_read(void) {
+  for (int i = 0; i < NUMBER_OF_ENCODERS; i++) {
+    encoder_state[i] <<= 2;
+    encoder_state[i] |= (readPin(encoders_pad_a[i]) << 0) | (readPin(encoders_pad_b[i]) << 1);
+    encoder_value[i] += encoder_LUT[encoder_state[i] & 0xF];
+    if (encoder_value[i] >= ENCODER_RESOLUTION) {
+        encoder_update_kb(i, COUNTRECLOCKWISE);
+    }
+    if (encoder_value[i] <= -ENCODER_RESOLUTION) { // direction is arbitrary here, but this clockwise
+        encoder_update_kb(i, CLOCKWISE);
+    }
+    encoder_value[i] %= ENCODER_RESOLUTION;
+  }
+}
diff --git a/quantum/process_keycode/process_chording.h b/quantum/encoder.h
index 8c0f4862a8..2024fa303f 100644
--- a/quantum/process_keycode/process_chording.h
+++ b/quantum/encoder.h
@@ -1,4 +1,5 @@
-/* Copyright 2016 Jack Humbert
+/*
+ * Copyright 2018 Jack Humbert <jack.humb@gmail.com>
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -14,19 +15,15 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PROCESS_CHORDING_H
-#define PROCESS_CHORDING_H
+#pragma once
 
 #include "quantum.h"
 
-// Chording stuff
-#define CHORDING_MAX 4
-bool chording = false;
+#define COUNTRECLOCKWISE 0
+#define CLOCKWISE 1
 
-uint8_t chord_keys[CHORDING_MAX] = {0};
-uint8_t chord_key_count = 0;
-uint8_t chord_key_down = 0;
+void encoder_init(void);
+void encoder_read(void);
 
-bool process_chording(uint16_t keycode, keyrecord_t *record);
-
-#endif
+void encoder_update_kb(int8_t index, bool clockwise);
+void encoder_update_user(int8_t index, bool clockwise);
diff --git a/quantum/keymap_common.c b/quantum/keymap_common.c
index 50af15d626..f6c8b70d28 100644
--- a/quantum/keymap_common.c
+++ b/quantum/keymap_common.c
@@ -64,7 +64,7 @@ action_t action_for_key(uint8_t layer, keypos_t key)
         case KC_SYSTEM_POWER ... KC_SYSTEM_WAKE:
             action.code = ACTION_USAGE_SYSTEM(KEYCODE2SYSTEM(keycode));
             break;
-        case KC_AUDIO_MUTE ... KC_MEDIA_REWIND:
+        case KC_AUDIO_MUTE ... KC_BRIGHTNESS_DOWN:
             action.code = ACTION_USAGE_CONSUMER(KEYCODE2CONSUMER(keycode));
             break;
         case KC_MS_UP ... KC_MS_ACCEL2:
diff --git a/quantum/keymap_extras/keymap_belgian.h b/quantum/keymap_extras/keymap_belgian.h
index 764c561417..ab89fbabfe 100644
--- a/quantum/keymap_extras/keymap_belgian.h
+++ b/quantum/keymap_extras/keymap_belgian.h
@@ -18,15 +18,6 @@
 
 #include "keymap.h"
 
-#define BE_LGUI KC_LALT
-#define BE_LALT KC_LGUI
-
-// Alt gr
-#ifndef ALGR
-#define ALGR(kc) RALT(kc)
-#endif
-#define NO_ALGR KC_RALT
-
 // Normal characters
 // Line 1
 #define BE_SUP2	KC_GRV
diff --git a/quantum/keymap_extras/keymap_bepo.h b/quantum/keymap_extras/keymap_bepo.h
index 05fd2b0023..8d7b36ca3d 100644
--- a/quantum/keymap_extras/keymap_bepo.h
+++ b/quantum/keymap_extras/keymap_bepo.h
@@ -19,15 +19,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#ifndef ALTGR
-#define ALTGR(kc)   RALT(kc)
-#endif
-#ifndef ALGR
-#define ALGR(kc)    ALTGR(kc)
-#endif
-#define BP_ALGR KC_RALT
-
 // Normal characters
 // First row (on usual keyboards)
 #define BP_DOLLAR           KC_GRAVE            // $
@@ -142,138 +133,138 @@
 
 // AltGr-ed characters
 // First row
-#define BP_EN_DASH          ALTGR(BP_DOLLAR)    // –
+#define BP_EN_DASH          ALGR(BP_DOLLAR)     // –
 #define BP_NDSH             BP_EN_DASH
-#define BP_EM_DASH          ALTGR(KC_1)         // —
+#define BP_EM_DASH          ALGR(KC_1)          // —
 #define BP_MDSH             BP_EM_DASH
-#define BP_LESS             ALTGR(KC_2)         // <
-#define BP_GREATER          ALTGR(KC_3)         // >
+#define BP_LESS             ALGR(KC_2)          // <
+#define BP_GREATER          ALGR(KC_3)          // >
 #define BP_GRTR             BP_GREATER
-#define BP_LBRACKET         ALTGR(KC_4)         // [
+#define BP_LBRACKET         ALGR(KC_4)          // [
 #define BP_LBRC             BP_LBRACKET
-#define BP_RBRACKET         ALTGR(KC_5)         // ]
+#define BP_RBRACKET         ALGR(KC_5)          // ]
 #define BP_RBRC             BP_RBRACKET
-#define BP_CIRCUMFLEX       ALTGR(KC_6)         // ^
+#define BP_CIRCUMFLEX       ALGR(KC_6)          // ^
 #define BP_CIRC             BP_CIRCUMFLEX
-#define BP_PLUS_MINUS       ALTGR(KC_7)         // ±
+#define BP_PLUS_MINUS       ALGR(KC_7)          // ±
 #define BP_PSMS             BP_PLUS_MINUS
-#define BP_MATH_MINUS       ALTGR(KC_8)         // −
+#define BP_MATH_MINUS       ALGR(KC_8)          // −
 #define BP_MMNS             BP_MATH_MINUS
-#define BP_OBELUS           ALTGR(KC_9)         // ÷
+#define BP_OBELUS           ALGR(KC_9)          // ÷
 #define BP_OBEL             BP_OBELUS
 // more conventional name of the symbol
 #define BP_DIVISION_SIGN    BP_OBELUS
 #define BP_DVSN             BP_DIVISION_SIGN
-#define BP_TIMES            ALTGR(KC_0)         // ×
+#define BP_TIMES            ALGR(KC_0)          // ×
 #define BP_TIMS             BP_TIMES
-#define BP_DIFFERENT        ALTGR(BP_EQUAL)     // ≠
+#define BP_DIFFERENT        ALGR(BP_EQUAL)      // ≠
 #define BP_DIFF             BP_DIFFERENT
-#define BP_PERMILLE         ALTGR(BP_PERCENT)   // ‰
+#define BP_PERMILLE         ALGR(BP_PERCENT)    // ‰
 #define BP_PMIL             BP_PERMILLE
 
 // Second row
-#define BP_PIPE                 ALTGR(BP_B)         // |
-#define BP_DEAD_ACUTE           ALTGR(BP_E_ACUTE)   // dead ´
+#define BP_PIPE                 ALGR(BP_B)          // |
+#define BP_DEAD_ACUTE           ALGR(BP_E_ACUTE)    // dead ´
 #define BP_DACT                 BP_DEAD_ACUTE
-#define BP_AMPERSAND            ALTGR(BP_P)         // &
+#define BP_AMPERSAND            ALGR(BP_P)          // &
 #define BP_AMPR                 BP_AMPERSAND
-#define BP_OE_LIGATURE          ALTGR(BP_O)         // œ
+#define BP_OE_LIGATURE          ALGR(BP_O)          // œ
 #define BP_OE                   BP_OE_LIGATURE
-#define BP_DEAD_GRAVE           ALTGR(BP_E_GRAVE)   // `
+#define BP_DEAD_GRAVE           ALGR(BP_E_GRAVE)    // `
 #define BP_DGRV                 BP_DEAD_GRAVE
-#define BP_INVERTED_EXCLAIM     ALTGR(BP_DEAD_CIRCUMFLEX)   // ¡
+#define BP_INVERTED_EXCLAIM     ALGR(BP_DEAD_CIRCUMFLEX)    // ¡
 #define BP_IXLM                 BP_INVERTED_EXCLAIM
-#define BP_DEAD_CARON           ALTGR(BP_V)         // dead ˇ
+#define BP_DEAD_CARON           ALGR(BP_V)          // dead ˇ
 #define BP_DCAR                 BP_DEAD_CARON
-#define BP_ETH                  ALTGR(BP_D)         // ð
-#define BP_DEAD_SLASH           ALTGR(BP_L)         // dead /
+#define BP_ETH                  ALGR(BP_D)          // ð
+#define BP_DEAD_SLASH           ALGR(BP_L)          // dead /
 #define BP_DSLH                 BP_DEAD_SLASH
-#define BP_IJ_LIGATURE          ALTGR(BP_J)         // ij
+#define BP_IJ_LIGATURE          ALGR(BP_J)          // ij
 #define BP_IJ                   BP_IJ_LIGATURE
-#define BP_SCHWA                ALTGR(BP_Z)         // ə
+#define BP_SCHWA                ALGR(BP_Z)          // ə
 #define BP_SCWA                 BP_SCHWA
-#define BP_DEAD_BREVE           ALTGR(BP_W)         // dead ˘
+#define BP_DEAD_BREVE           ALGR(BP_W)          // dead ˘
 #define BP_DBRV                 BP_DEAD_BREVE
 
 // Third row
-#define BP_AE_LIGATURE              ALTGR(BP_A)         // æ
+#define BP_AE_LIGATURE              ALGR(BP_A)          // æ
 #define BP_AE                       BP_AE_LIGATURE
-#define BP_U_GRAVE                  ALTGR(BP_U)           // ù
+#define BP_U_GRAVE                  ALGR(BP_U)          // ù
 #define BP_UGRV                     BP_U_GRAVE
-#define BP_DEAD_TREMA               ALTGR(BP_I)         // dead ¨ (trema/umlaut/diaresis)
+#define BP_DEAD_TREMA               ALGR(BP_I)          // dead ¨ (trema/umlaut/diaresis)
 #define BP_DTRM                     BP_DEAD_TREMA
-#define BP_EURO                     ALTGR(BP_E)         // €
-#define BP_TYPOGRAPHICAL_APOSTROPHE ALTGR(BP_COMMA)     // ’
+#define BP_EURO                     ALGR(BP_E)          // €
+#define BP_TYPOGRAPHICAL_APOSTROPHE ALGR(BP_COMMA)      // ’
 #define BP_TAPO                     BP_TYPOGRAPHICAL_APOSTROPHE
-#define BP_COPYRIGHT                ALTGR(BP_C)         // ©
+#define BP_COPYRIGHT                ALGR(BP_C)          // ©
 #define BP_CPRT                     BP_COPYRIGHT
-#define BP_THORN                    ALTGR(BP_T)         // þ
+#define BP_THORN                    ALGR(BP_T)          // þ
 #define BP_THRN                     BP_THORN
-#define BP_SHARP_S                  ALTGR(BP_S)         // ß
+#define BP_SHARP_S                  ALGR(BP_S)          // ß
 #define BP_SRPS                     BP_SHARP_S
-#define BP_REGISTERED_TRADEMARK     ALTGR(BP_R)         // ®
+#define BP_REGISTERED_TRADEMARK     ALGR(BP_R)          // ®
 #define BP_RTM                      BP_REGISTERED_TRADEMARK
-#define BP_DEAD_TILDE               ALTGR(BP_N)         // dead ~
+#define BP_DEAD_TILDE               ALGR(BP_N)          // dead ~
 #define BP_DTLD                     BP_DEAD_TILDE
-#define BP_DEAD_MACRON              ALTGR(BP_M)         // dead ¯
+#define BP_DEAD_MACRON              ALGR(BP_M)          // dead ¯
 #define BP_DMCR                     BP_DEAD_MACRON
-#define BP_DEAD_CEDILLA             ALTGR(BP_C_CEDILLA) // dead ¸
+#define BP_DEAD_CEDILLA             ALGR(BP_C_CEDILLA)  // dead ¸
 #define BP_DCED                     BP_DEAD_CEDILLA
 
 // Fourth row
-#define BP_NONUS_SLASH          ALTGR(BP_E_CIRCUMFLEX)  // / on non-us backslash key (102nd key, ê in bépo)
+#define BP_NONUS_SLASH          ALGR(BP_E_CIRCUMFLEX)   // / on non-us backslash key (102nd key, ê in bépo)
 #define BP_NUSL                 BP_NONUS_SLASH
-#define BP_BACKSLASH            ALTGR(BP_A_GRAVE)       /* \ */
+#define BP_BACKSLASH            ALGR(BP_A_GRAVE)        /* \ */
 #define BP_BSLS                 BP_BACKSLASH
-#define BP_LEFT_CURLY_BRACE     ALTGR(BP_Y)             // {
+#define BP_LEFT_CURLY_BRACE     ALGR(BP_Y)              // {
 #define BP_LCBR                 BP_LEFT_CURLY_BRACE
-#define BP_RIGHT_CURLY_BRACE    ALTGR(BP_X)             // }
+#define BP_RIGHT_CURLY_BRACE    ALGR(BP_X)              // }
 #define BP_RCBR                 BP_RIGHT_CURLY_BRACE
-#define BP_ELLIPSIS             ALTGR(BP_DOT)           // …
+#define BP_ELLIPSIS             ALGR(BP_DOT)            // …
 #define BP_ELPS                 BP_ELLIPSIS
-#define BP_TILDE                ALTGR(BP_K)             // ~
+#define BP_TILDE                ALGR(BP_K)              // ~
 #define BP_TILD                 BP_TILDE
-#define BP_INVERTED_QUESTION    ALTGR(BP_QUESTION)      // ¿
+#define BP_INVERTED_QUESTION    ALGR(BP_QUESTION)       // ¿
 #define BP_IQST                 BP_INVERTED_QUESTION
-#define BP_DEAD_RING            ALTGR(BP_Q)             // dead °
+#define BP_DEAD_RING            ALGR(BP_Q)              // dead °
 #define BP_DRNG                 BP_DEAD_RING
-#define BP_DEAD_GREEK           ALTGR(BP_G)             // dead Greek key (following key will make a Greek letter)
+#define BP_DEAD_GREEK           ALGR(BP_G)              // dead Greek key (following key will make a Greek letter)
 #define BP_DGRK                 BP_DEAD_GREEK
-#define BP_DAGGER               ALTGR(BP_H)             // †
+#define BP_DAGGER               ALGR(BP_H)              // †
 #define BP_DAGR                 BP_DAGGER
-#define BP_DEAD_OGONEK          ALTGR(BP_F)             // dead ˛
+#define BP_DEAD_OGONEK          ALGR(BP_F)              // dead ˛
 #define BP_DOGO                 BP_DEAD_OGONEK
 
 // Space bar
-#define BP_UNDERSCORE   ALTGR(KC_SPACE)     // _
+#define BP_UNDERSCORE   ALGR(KC_SPACE)      // _
 #define BP_UNDS         BP_UNDERSCORE
 
 // AltGr-Shifted characters (different from capitalised AltGr-ed characters)
 // First row
-#define BP_PARAGRAPH            ALTGR(BP_HASH)      // ¶
+#define BP_PARAGRAPH            ALGR(BP_HASH)       // ¶
 #define BP_PARG                 BP_PARAGRAPH
-#define BP_LOW_DOUBLE_QUOTE     ALTGR(BP_1)         // „
+#define BP_LOW_DOUBLE_QUOTE     ALGR(BP_1)          // „
 #define BP_LWQT                 BP_LOW_DOUBLE_QUOTE
-#define BP_LEFT_DOUBLE_QUOTE    ALTGR(BP_2)         // “
+#define BP_LEFT_DOUBLE_QUOTE    ALGR(BP_2)          // “
 #define BP_LDQT                 BP_LEFT_DOUBLE_QUOTE
-#define BP_RIGHT_DOUBLE_QUOTE   ALTGR(BP_3)         // ”
+#define BP_RIGHT_DOUBLE_QUOTE   ALGR(BP_3)          // ”
 #define BP_RDQT                 BP_RIGHT_DOUBLE_QUOTE
-#define BP_LESS_OR_EQUAL        ALTGR(BP_4)         // ≤
+#define BP_LESS_OR_EQUAL        ALGR(BP_4)          // ≤
 #define BP_LEQL                 BP_LESS_OR_EQUAL
-#define BP_GREATER_OR_EQUAL     ALTGR(BP_5)         // ≥
+#define BP_GREATER_OR_EQUAL     ALGR(BP_5)          // ≥
 #define BP_GEQL                 BP_GREATER_OR_EQUAL
-// nothing on ALTGR(BP_6)
-#define BP_NEGATION             ALTGR(BP_7)         // ¬
+// nothing on ALGR(BP_6)
+#define BP_NEGATION             ALGR(BP_7)          // ¬
 #define BP_NEGT                 BP_NEGATION
-#define BP_ONE_QUARTER          ALTGR(BP_8)         // ¼
+#define BP_ONE_QUARTER          ALGR(BP_8)          // ¼
 #define BP_1QRT                 BP_ONE_QUARTER
-#define BP_ONE_HALF             ALTGR(BP_9)         // ½
+#define BP_ONE_HALF             ALGR(BP_9)          // ½
 #define BP_1HLF                 BP_ONE_HALF
-#define BP_THREE_QUARTERS       ALTGR(BP_0)         // ¾
+#define BP_THREE_QUARTERS       ALGR(BP_0)          // ¾
 #define BP_3QRT                 BP_THREE_QUARTERS
-#define BP_MINUTES              ALTGR(BP_DEGREE)    // ′
+#define BP_MINUTES              ALGR(BP_DEGREE)     // ′
 #define BP_MNUT                 BP_MINUTES
-#define BP_SECONDS              ALTGR(BP_GRAVE)     // ″
+#define BP_SECONDS              ALGR(BP_GRAVE)      // ″
 #define BP_SCND                 BP_SECONDS
 
 // Second row
@@ -281,7 +272,7 @@
 #define BP_BPIP                 BP_BROKEN_PIPE
 #define BP_DEAD_DOUBLE_ACUTE    LSFT(BP_DEAD_ACUTE)     // ˝
 #define BP_DDCT                 BP_DEAD_DOUBLE_ACUTE
-#define BP_SECTION              ALTGR(LSFT(BP_P))       // §
+#define BP_SECTION              ALGR(LSFT(BP_P))        // §
 #define BP_SECT                 BP_SECTION
 // LSFT(BP_DEAD_GRAVE) is actually the same character as LSFT(BP_PERCENT)
 #define BP_GRAVE_BIS            LSFT(BP_DEAD_GRAVE)     // `
@@ -292,35 +283,35 @@
 #define BP_DDTA                 BP_DEAD_DOT_ABOVE
 #define BP_DEAD_CURRENCY        LSFT(BP_EURO)           // dead ¤ (next key will generate a currency code like ¥ or £)
 #define BP_DCUR                 BP_DEAD_CURRENCY
-#define BP_DEAD_HORN            LSFT(ALTGR(BP_COMMA))   // dead ̛
+#define BP_DEAD_HORN            LSFT(ALGR(BP_COMMA))    // dead ̛
 #define BP_DHRN                 BP_DEAD_HORN
-#define BP_LONG_S               LSFT(ALTGR(BP_C))       // ſ
+#define BP_LONG_S               LSFT(ALGR(BP_C))        // ſ
 #define BP_LNGS                 BP_LONG_S
 #define BP_TRADEMARK            LSFT(BP_REGISTERED_TRADEMARK)   // ™
 #define BP_TM                   BP_TRADEMARK
-#define BP_ORDINAL_INDICATOR_O  LSFT(ALTGR(BP_M))               // º
+#define BP_ORDINAL_INDICATOR_O  LSFT(ALGR(BP_M))        // º
 #define BP_ORDO                 BP_ORDINAL_INDICATOR_O
 #define BP_DEAD_COMMA           LSFT(BP_DEAD_CEDILLA)   // dead ˛
 #define BP_DCOM                 BP_DEAD_COMMA
 
 // Fourth row
-#define BP_LEFT_QUOTE           LSFT(ALTGR(BP_Y))       // ‘
+#define BP_LEFT_QUOTE           LSFT(ALGR(BP_Y))        // ‘
 #define BP_LQOT                 BP_LEFT_QUOTE
-#define BP_RIGHT_QUOTE          LSFT(ALTGR(BP_X))       // ’
+#define BP_RIGHT_QUOTE          LSFT(ALGR(BP_X))        // ’
 #define BP_RQOT                 BP_RIGHT_QUOTE
-#define BP_INTERPUNCT           LSFT(ALTGR(BP_DOT))     // ·
+#define BP_INTERPUNCT           LSFT(ALGR(BP_DOT))      // ·
 #define BP_IPCT                 BP_INTERPUNCT
-#define BP_DEAD_HOOK_ABOVE      LSFT(ALTGR(BP_QUESTION))    // dead ̉
+#define BP_DEAD_HOOK_ABOVE      LSFT(ALGR(BP_QUESTION)) // dead ̉
 #define BP_DHKA                 BP_DEAD_HOOK_ABOVE
 #define BP_DEAD_UNDERDOT        LSFT(BP_DEAD_RING)      // dead ̣
 #define BP_DUDT                 BP_DEAD_UNDERDOT
 #define BP_DOUBLE_DAGGER        LSFT(BP_DAGGER)         // ‡
 #define BP_DDGR                 BP_DOUBLE_DAGGER
-#define BP_ORDINAL_INDICATOR_A  LSFT(ALTGR(BP_F))       // ª
+#define BP_ORDINAL_INDICATOR_A  LSFT(ALGR(BP_F))        // ª
 #define BP_ORDA                 BP_ORDINAL_INDICATOR_A
 
 // Space bar
-#define BP_NARROW_NON_BREAKING_SPACE    ALTGR(BP_NON_BREAKING_SPACE)
+#define BP_NARROW_NON_BREAKING_SPACE    ALGR(BP_NON_BREAKING_SPACE)
 #define BP_NNBS                         BP_NARROW_NON_BREAKING_SPACE
 
 #endif
diff --git a/quantum/keymap_extras/keymap_canadian_multilingual.h b/quantum/keymap_extras/keymap_canadian_multilingual.h
index 1d45bee32e..2b5b95d6fa 100644
--- a/quantum/keymap_extras/keymap_canadian_multilingual.h
+++ b/quantum/keymap_extras/keymap_canadian_multilingual.h
@@ -13,22 +13,11 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-#ifndef KEYMAP_CANADIAN_MULTILINGUAG_H
-#define KEYMAP_CANADIAN_MULTILINGUAG_H
+#ifndef KEYMAP_CANADIAN_MULTILINGUAL_H
+#define KEYMAP_CANADIAN_MULTILINGUAL_H
 
 #include "keymap.h"
 
-// Alt gr
-#ifndef ALTGR
-#define ALTGR(kc)   RALT(kc)
-#endif
-#ifndef ALGR
-#define ALGR(kc)    ALTGR(kc)
-#endif
-
-#define CSA_ALTGR   KC_RALT
-#define CSA_ALGR    CSA_ALTGR
-
 #ifndef GR2A
 #define GR2A(kc)    RCTL(kc)
 #endif
@@ -78,43 +67,43 @@
 
 // Alt Gr-ed characters
 // First row
-#define CSA_PIPE                ALTGR(CSA_SLASH)        // |
-#define CSA_CURRENCY            ALTGR(KC_4)             // ¤
+#define CSA_PIPE                ALGR(CSA_SLASH)         // |
+#define CSA_CURRENCY            ALGR(KC_4)              // ¤
 #define CSA_CURR                CSA_CURRENCY
-#define CSA_LEFT_CURLY_BRACE    ALTGR(KC_7)             // {
+#define CSA_LEFT_CURLY_BRACE    ALGR(KC_7)              // {
 #define CSA_LCBR                CSA_LEFT_CURLY_BRACE
-#define CSA_RIGHT_CURLY_BRACE   ALTGR(KC_8)             // }
+#define CSA_RIGHT_CURLY_BRACE   ALGR(KC_8)              // }
 #define CSA_RCBR                CSA_RIGHT_CURLY_BRACE
-#define CSA_LBRACKET            ALTGR(KC_9)             // [
+#define CSA_LBRACKET            ALGR(KC_9)              // [
 #define CSA_LBRC                CSA_LBRACKET
-#define CSA_RBRACKET            ALTGR(KC_0)             // ]
+#define CSA_RBRACKET            ALGR(KC_0)              // ]
 #define CSA_RBRC                CSA_RBRACKET
-#define CSA_NEGATION            ALTGR(KC_EQUAL)         // ¬
+#define CSA_NEGATION            ALGR(KC_EQUAL)          // ¬
 #define CSA_NEGT                CSA_NEGATION
 
 // Second row
 // euro symbol not available on Linux? (X.org)
-#define CSA_EURO        ALTGR(KC_E)                 // €
-#define CSA_DEAD_GRAVE  ALTGR(CSA_DEAD_CIRCUMFLEX)
+#define CSA_EURO        ALGR(KC_E)                  // €
+#define CSA_DEAD_GRAVE  ALGR(CSA_DEAD_CIRCUMFLEX)
 #define CSA_DGRV        CSA_DEAD_GRAVE              // dead `
-#define CSA_DEAD_TILDE  ALTGR(CSA_C_CEDILLA)        // ~
+#define CSA_DEAD_TILDE  ALGR(CSA_C_CEDILLA)         // ~
 #define CSA_DTLD        CSA_DEAD_TILDE
 
 // Third row
-#define CSA_DEGREE  ALTGR(KC_SCOLON)    // °
+#define CSA_DEGREE  ALGR(KC_SCOLON)     // °
 #define CSA_DEGR    CSA_DEGREE
 
 // Fourth row
-#define CSA_LEFT_GUILLEMET      ALTGR(KC_Z)         // «
+#define CSA_LEFT_GUILLEMET      ALGR(KC_Z)          // «
 #define CSA_LGIL                CSA_LEFT_GUILLEMET
-#define CSA_RIGHT_GUILLEMET     ALTGR(KC_X)         // »
+#define CSA_RIGHT_GUILLEMET     ALGR(KC_X)          // »
 #define CSA_RGIL                CSA_RIGHT_GUILLEMET
-#define CSA_LESS                ALTGR(KC_COMMA)     // <
-#define CSA_GREATER             ALTGR(KC_DOT)       // >
+#define CSA_LESS                ALGR(KC_COMMA)      // <
+#define CSA_GREATER             ALGR(KC_DOT)        // >
 #define CSA_GRTR                CSA_GREATER
 
 // Space bar
-#define CSA_NON_BREAKING_SPACE  ALTGR(KC_SPACE)
+#define CSA_NON_BREAKING_SPACE  ALGR(KC_SPACE)
 #define CSA_NBSP                CSA_NON_BREAKING_SPACE
 
 // GR2A-ed characters
@@ -201,7 +190,7 @@
 // nothing on 2
 #define CSA_POUND               GR2A(LSFT(KC_3))    // £
 #define CSA_GBP                 CSA_POUND_SIGN
-// already on ALTGR(KC_E)
+// already on ALGR(KC_E)
 #define CSA_EURO_BIS            GR2A(LSFT(KC_4))    // €
 #define CSA_EURB                CSA_EURO_BIS
 #define CSA_THREE_EIGHTHS       GR2A(LSFT(KC_5))    // ⅜
diff --git a/quantum/keymap_extras/keymap_fr_ch.h b/quantum/keymap_extras/keymap_fr_ch.h
index c0ca832a6f..69f9547bb9 100644
--- a/quantum/keymap_extras/keymap_fr_ch.h
+++ b/quantum/keymap_extras/keymap_fr_ch.h
@@ -18,10 +18,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define FR_CH_ALGR KC_RALT
-
 // normal characters
 #define FR_CH_Z KC_Y
 #define FR_CH_Y KC_Z
diff --git a/quantum/keymap_extras/keymap_french.h b/quantum/keymap_extras/keymap_french.h
index 3308dc5f77..bef7754707 100644
--- a/quantum/keymap_extras/keymap_french.h
+++ b/quantum/keymap_extras/keymap_french.h
@@ -18,12 +18,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#ifndef ALGR
-#define ALGR(kc) RALT(kc)
-#endif
-#define NO_ALGR KC_RALT
-
 // Normal characters
 #define FR_SUP2	KC_GRV
 #define FR_AMP	KC_1
diff --git a/quantum/keymap_extras/keymap_german.h b/quantum/keymap_extras/keymap_german.h
index e007c26ef5..0ba3570df7 100644
--- a/quantum/keymap_extras/keymap_german.h
+++ b/quantum/keymap_extras/keymap_german.h
@@ -19,10 +19,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define DE_ALGR KC_RALT
-
 // normal characters
 #define DE_Z KC_Y
 #define DE_Y KC_Z
diff --git a/quantum/keymap_extras/keymap_german_ch.h b/quantum/keymap_extras/keymap_german_ch.h
index 67350d6602..bd1ef89a19 100644
--- a/quantum/keymap_extras/keymap_german_ch.h
+++ b/quantum/keymap_extras/keymap_german_ch.h
@@ -18,10 +18,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define CH_ALGR KC_RALT
-
 // normal characters
 #define CH_Z KC_Y
 #define CH_Y KC_Z
diff --git a/quantum/keymap_extras/keymap_hungarian.h b/quantum/keymap_extras/keymap_hungarian.h
index b372440928..ff43535f38 100644
--- a/quantum/keymap_extras/keymap_hungarian.h
+++ b/quantum/keymap_extras/keymap_hungarian.h
@@ -19,10 +19,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define HU_ALGR KC_RALT
-
 // basic letters
 #define HU_Z KC_Y
 #define HU_Y KC_Z
diff --git a/quantum/keymap_extras/keymap_italian.h b/quantum/keymap_extras/keymap_italian.h
index 0ff6ce8762..fe0f5eb847 100644
--- a/quantum/keymap_extras/keymap_italian.h
+++ b/quantum/keymap_extras/keymap_italian.h
@@ -19,14 +19,7 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define IT_ALGR KC_RALT
-
 // normal characters
-
-
-
 #define IT_A KC_A
 #define IT_B KC_B
 #define IT_C KC_C
diff --git a/quantum/keymap_extras/keymap_jp.h b/quantum/keymap_extras/keymap_jp.h
index fb74bce8d4..b0235f1120 100644
--- a/quantum/keymap_extras/keymap_jp.h
+++ b/quantum/keymap_extras/keymap_jp.h
@@ -40,6 +40,9 @@
 #define JP_HENK KC_INT4 // henkan
 #define JP_KANA KC_INT2 // katakana/hiragana|ro-mazi
 
+#define JP_MKANA KC_LANG1 //kana on MacOSX
+#define JP_MEISU KC_LANG2 //eisu on MacOSX
+
 
 //Aliases for shifted symbols
 #define JP_DQT  LSFT(KC_2)    // "
diff --git a/quantum/keymap_extras/keymap_neo2.h b/quantum/keymap_extras/keymap_neo2.h
index 174f4a6eec..818a739c76 100644
--- a/quantum/keymap_extras/keymap_neo2.h
+++ b/quantum/keymap_extras/keymap_neo2.h
@@ -73,6 +73,6 @@
 #define NEO_L1_R DE_HASH
 
 #define NEO_L2_L DE_LESS
-#define NEO_L2_R DE_ALGR
+#define NEO_L2_R KC_ALGR
 
 #endif
diff --git a/quantum/keymap_extras/keymap_nordic.h b/quantum/keymap_extras/keymap_nordic.h
index 6b34db5588..551a4212b2 100644
--- a/quantum/keymap_extras/keymap_nordic.h
+++ b/quantum/keymap_extras/keymap_nordic.h
@@ -18,10 +18,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define NO_ALGR KC_RALT
-
 // Normal characters
 #define NO_HALF	KC_GRV
 #define NO_PLUS	KC_MINS
diff --git a/quantum/keymap_extras/keymap_plover_dvorak.h b/quantum/keymap_extras/keymap_plover_dvorak.h
new file mode 100644
index 0000000000..83bb1e8b87
--- /dev/null
+++ b/quantum/keymap_extras/keymap_plover_dvorak.h
@@ -0,0 +1,47 @@
+/* Copyright 2016 James Kay
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef KEYMAP_PLOVER_DVORAK_H
+#define KEYMAP_PLOVER_DVORAK_H
+
+#include "keymap_dvorak.h"
+
+#define PD_NUM  DV_1
+#define PD_LS   DV_Q
+#define PD_LT   DV_W
+#define PD_LP   DV_E
+#define PD_LH   DV_R
+#define PD_LK   DV_S
+#define PD_LW   DV_D
+#define PD_LR   DV_F
+
+#define PD_STAR DV_Y
+#define PD_RF   DV_U
+#define PD_RP   DV_I
+#define PD_RL   DV_O
+#define PD_RT   DV_P
+#define PD_RD   DV_LBRC
+#define PD_RR   DV_J
+#define PD_RB   DV_K
+#define PD_RG   DV_L
+#define PD_RS   DV_SCLN
+#define PD_RZ   DV_QUOT
+
+#define PD_A    DV_C
+#define PD_O    DV_V
+#define PD_E    DV_N
+#define PD_U    DV_M
+
+#endif
diff --git a/quantum/keymap_extras/keymap_slovenian.h b/quantum/keymap_extras/keymap_slovenian.h
new file mode 100644
index 0000000000..892283e702
--- /dev/null
+++ b/quantum/keymap_extras/keymap_slovenian.h
@@ -0,0 +1,107 @@
+/* Copyright 2018 Žan Pevec
+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEYMAP_SLOVENIAN
+#define KEYMAP_SLOVENIAN
+
+#include "keymap.h"
+
+//Swapped Z and Y
+#define SI_Z KC_Y
+#define SI_Y KC_Z
+
+//Special characters
+#define SI_CV KC_SCLN
+#define SI_SV KC_LBRC
+#define SI_ZV KC_BSLS
+
+#define SI_A KC_A
+#define SI_B KC_B
+#define SI_C KC_C
+#define SI_D KC_D
+#define SI_E KC_E
+#define SI_F KC_F
+#define SI_G KC_G
+#define SI_H KC_H
+#define SI_I KC_I
+#define SI_J KC_J
+#define SI_K KC_K
+#define SI_L KC_L
+#define SI_M KC_M
+#define SI_N KC_N
+#define SI_O KC_O
+#define SI_P KC_P
+#define SI_Q KC_Q
+#define SI_R KC_R
+#define SI_S KC_S
+#define SI_T KC_T
+#define SI_U KC_U
+#define SI_V KC_V
+#define SI_W KC_W
+#define SI_X KC_X
+
+#define SI_0 KC_0
+#define SI_1 KC_1
+#define SI_2 KC_2
+#define SI_3 KC_3
+#define SI_4 KC_4
+#define SI_5 KC_5
+#define SI_6 KC_6
+#define SI_7 KC_7
+#define SI_8 KC_8
+#define SI_9 KC_9
+
+#define SI_DOT KC_DOT
+#define SI_COMM KC_COMM
+
+#define SI_PLUS KC_EQL // + and * and ~
+#define SI_QOT KC_MINS // Single quote
+#define SI_MINS KC_SLSH // - and _
+
+// shifted characters
+#define SI_EXLM LSFT(KC_1) // !
+#define SI_DQOT LSFT(KC_2) // "
+#define SI_HASH LSFT(KC_3) // #
+#define SI_DLR  LSFT(KC_4) // $
+#define SI_PERC LSFT(KC_5) // %
+#define SI_AMPR LSFT(KC_6) // &
+#define SI_SLSH LSFT(KC_7) // /
+#define SI_LPRN LSFT(KC_8) // (
+#define SI_RPRN LSFT(KC_9) // )
+#define SI_EQL  LSFT(KC_0) // =
+#define SI_QST  LSFT(SI_QOT) // ?
+#define SI_ASTR LSFT(SI_PLUS) // *
+#define SI_COLN LSFT(KC_DOT) // :
+#define SI_SCLN LSFT(KC_COMM) // ;
+#define SI_UNDS LSFT(SI_MINS) // _
+
+// Alt Gr-ed characters
+#define SI_CIRC ALGR(KC_3) // ^
+#define SI_DEG ALGR(KC_5) // °
+#define SI_GRV ALGR(KC_7) // `
+#define SI_ACCU ALGR(KC_9) // ´
+#define SI_LCBR ALGR(KC_B) // {
+#define SI_RCBR ALGR(KC_N) // }
+#define SI_LBRC ALGR(KC_F) // [
+#define SI_RBRC ALGR(KC_G) // ]
+#define SI_BSLS ALGR(KC_Q) // backslash
+#define SI_AT  ALGR(KC_V) // @
+#define SI_EURO ALGR(KC_E) // €
+#define SI_TILD ALGR(KC_1) // ~
+#define SI_PIPE ALGR(KC_W) // |
+
+#endif
diff --git a/quantum/keymap_extras/keymap_spanish.h b/quantum/keymap_extras/keymap_spanish.h
index 224db7be16..1f183327f9 100644
--- a/quantum/keymap_extras/keymap_spanish.h
+++ b/quantum/keymap_extras/keymap_spanish.h
@@ -18,10 +18,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define NO_ALGR KC_RALT
-
 // Normal characters
 #define ES_OVRR KC_GRV
 #define ES_APOS	KC_MINS
diff --git a/quantum/keymap_extras/keymap_swedish.h b/quantum/keymap_extras/keymap_swedish.h
index 9044bb74e7..d1a0f4f227 100644
--- a/quantum/keymap_extras/keymap_swedish.h
+++ b/quantum/keymap_extras/keymap_swedish.h
@@ -41,8 +41,8 @@
 #define NO_DLR_MAC  LSFT(KC_4) // $
 #define NO_GRV_MAC ALGR(NO_BSLS) // `
 #define NO_GRTR_MAC LSFT(KC_GRV)  // >
-#define NO_LCBR_MAC ALGR(LSFT(KC_8))  // }
-#define NO_LESS_MAC KC_GRV  // >
+#define NO_LCBR_MAC ALGR(LSFT(KC_8))  // {
+#define NO_LESS_MAC KC_GRV  // <
 #define NO_PIPE_MAC ALGR(KC_7)  // |
 #define NO_RCBR_MAC ALGR(LSFT(KC_9))  // }
 
diff --git a/quantum/keymap_extras/keymap_uk.h b/quantum/keymap_extras/keymap_uk.h
index de47103cb9..cc3d0039e2 100644
--- a/quantum/keymap_extras/keymap_uk.h
+++ b/quantum/keymap_extras/keymap_uk.h
@@ -18,10 +18,6 @@
 
 #include "keymap.h"
 
-// Alt gr
-#define ALGR(kc) RALT(kc)
-#define NO_ALGR KC_RALT
-
 // Normal characters
 #define UK_HASH KC_NUHS
 #define UK_BSLS	KC_NUBS
diff --git a/quantum/keymap_extras/sendstring_german.h b/quantum/keymap_extras/sendstring_german.h
new file mode 100644
index 0000000000..1eaafee317
--- /dev/null
+++ b/quantum/keymap_extras/sendstring_german.h
@@ -0,0 +1,81 @@
+/* Copyright 2018 Patrick Hener
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/* Sendstring definitions for the German layout */
+#ifndef SENDSTRING_GERMAN
+#define SENDSTRING_GERMAN
+
+#include "keymap_german.h"
+
+const bool ascii_to_shift_lut[0x80] PROGMEM = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    
+    0, 1, 1, 0, 1, 1, 1, 1,
+    1, 1, 1, 0, 0, 0, 0, 1,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 1, 1, 0, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 1, 1, 1, 1, 1,
+    1, 1, 1, 0, 0, 0, 0, 1,
+    1, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, 1, 1, 1, 1, 0
+};
+
+/* Until an ALT table/functionality is added, the following symbols will not work:
+* § @ [ ] { } \ ~ äA öÖ ß ´
+* Following characters can be printed using other characters like so:
+* [ in makro will be ü
+* { in makro will be Ü
+* ~ in makro will be °
+*/
+const uint8_t ascii_to_keycode_lut[0x80] PROGMEM = {
+    0, 0, 0, 0, 0, 0, 0, 0,
+    KC_BSPC, KC_TAB, KC_ENT, 0, 0, 0, 0, 0,
+    0, 0, 0, 0, 0, 0, 0, 0,
+    0, 0, 0, KC_ESC, 0, 0, 0, 0,
+
+  /* SPACE   !     "      #        $     %     &      '                    */
+    KC_SPC, KC_1, KC_2, DE_HASH, KC_4, KC_5, KC_6, DE_HASH,
+  /*  (      )     *      +        ,        -        .       /                    */
+    KC_8, KC_9, DE_PLUS, DE_PLUS, KC_COMM, DE_MINS, KC_DOT, KC_7,
+  /*   0     1     2     3     4     5    6     7                    */
+    KC_0, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7,
+  /*   8     9      :       ;         <        =       >       ?                     */
+    KC_8, KC_9, KC_DOT, KC_COMM, DE_LESS,    KC_0, DE_LESS, KC_MINS,
+  /*   @     A     B     C     D     E     F     G                    */
+    KC_2, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G,
+  /*   H     I     J     K     L     M     N     O                    */
+    KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O,
+  /*   P     Q     R     S     T     U     V     W                    */
+    KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W,
+  /*   X     Y     Z      [         \       ]       ^      _                    */
+    KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, DE_CIRC, DE_MINS,
+  /*   `       a     b     c     d     e     f     g                    */
+    DE_ACUT, KC_A, KC_B, KC_C, KC_D, KC_E, KC_F, KC_G,
+  /*   h     i     j     k     l     m     n     o                    */
+    KC_H, KC_I, KC_J, KC_K, KC_L, KC_M, KC_N, KC_O,
+  /*    p    q     r     s     t     u     v     w                      */
+    KC_P, KC_Q, KC_R, KC_S, KC_T, KC_U, KC_V, KC_W,
+  /*    x    y     z      {        |        }        ~    DELETE                    */
+    KC_X, KC_Y, KC_Z, KC_LBRC, KC_BSLS, KC_RBRC, KC_GRV, KC_DEL
+};
+
+#endif
diff --git a/quantum/matrix.c b/quantum/matrix.c
index bc7eb6b585..292171490c 100644
--- a/quantum/matrix.c
+++ b/quantum/matrix.c
@@ -1,5 +1,5 @@
 /*
-Copyright 2012-2017 Jun Wako, Jack Humbert
+Copyright 2012-2018 Jun Wako, Jack Humbert, Yiancar
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -16,15 +16,13 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
 #include <stdint.h>
 #include <stdbool.h>
-#if defined(__AVR__)
-#include <avr/io.h>
-#endif
 #include "wait.h"
 #include "print.h"
 #include "debug.h"
 #include "util.h"
 #include "matrix.h"
 #include "timer.h"
+#include "quantum.h"
 
 
 #if (MATRIX_COLS <= 8)
@@ -49,8 +47,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #endif
 
 #if (DIODE_DIRECTION == ROW2COL) || (DIODE_DIRECTION == COL2ROW)
-static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
-static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+static const pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
+static const pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
 #endif
 
 /* matrix state(1:on, 0:off) */
@@ -198,9 +196,7 @@ uint8_t matrix_key_count(void)
 static void init_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
+        setPinInputHigh(col_pins[x]);
     }
 }
 
@@ -220,8 +216,7 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
     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));
+        uint8_t pin_state = readPin(col_pins[col_index]);
 
         // Populate the matrix row with the state of the col pin
         current_matrix[current_row] |=  pin_state ? 0 : (ROW_SHIFTER << col_index);
@@ -235,24 +230,19 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t 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
+    setPinOutput(row_pins[row]);
+    writePinLow(row_pins[row]);
 }
 
 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
+    setPinInputHigh(row_pins[row]);
 }
 
 static void unselect_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
+        setPinInput(row_pins[x]);
     }
 }
 
@@ -261,9 +251,7 @@ static void unselect_rows(void)
 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
+        setPinInputHigh(row_pins[x]);
     }
 }
 
@@ -283,7 +271,7 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
         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)
+        if (readPin(row_pins[row_index]) == 0)
         {
             // Pin LO, set col bit
             current_matrix[row_index] |= (ROW_SHIFTER << current_col);
@@ -309,24 +297,19 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
 
 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
+    setPinOutput(col_pins[col]);
+    writePinLow(col_pins[col]);
 }
 
 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
+    setPinInputHigh(col_pins[col]);
 }
 
 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
+        setPinInputHigh(col_pins[x]);
     }
 }
 
diff --git a/quantum/process_keycode/process_auto_shift.c b/quantum/process_keycode/process_auto_shift.c
index 01d99445b0..0d0930ee67 100644
--- a/quantum/process_keycode/process_auto_shift.c
+++ b/quantum/process_keycode/process_auto_shift.c
@@ -173,6 +173,8 @@ bool process_auto_shift(uint16_t keycode, keyrecord_t *record) {
       case KC_DOT:
       case KC_SLSH:
       case KC_GRAVE:
+      case KC_NONUS_BSLASH:
+      case KC_NONUS_HASH:
 #endif
 
         autoshift_flush();
diff --git a/quantum/process_keycode/process_chording.c b/quantum/process_keycode/process_chording.c
deleted file mode 100644
index 6c6ebe300a..0000000000
--- a/quantum/process_keycode/process_chording.c
+++ /dev/null
@@ -1,76 +0,0 @@
-/* Copyright 2016 Jack Humbert
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include "process_chording.h"
-
-bool keys_chord(uint8_t keys[]) {
-  uint8_t keys_size = sizeof(keys)/sizeof(keys[0]);
-  bool pass = true;
-  uint8_t in = 0;
-  for (uint8_t i = 0; i < chord_key_count; i++) {
-    bool found = false;
-    for (uint8_t j = 0; j < keys_size; j++) {
-      if (chord_keys[i] == (keys[j] & 0xFF)) {
-        in++; // detects key in chord
-        found = true;
-        break;
-      }
-    }
-    if (found)
-      continue;
-    if (chord_keys[i] != 0)  {
-      pass = false; // makes sure rest are blank
-    }
-  }
-  return (pass && (in == keys_size));
-}
-
-bool process_chording(uint16_t keycode, keyrecord_t *record) {
-  if (keycode >= QK_CHORDING && keycode <= QK_CHORDING_MAX) {
-    if (record->event.pressed) {
-      if (!chording) {
-        chording = true;
-        for (uint8_t i = 0; i < CHORDING_MAX; i++)
-          chord_keys[i] = 0;
-        chord_key_count = 0;
-        chord_key_down = 0;
-      }
-      chord_keys[chord_key_count] = (keycode & 0xFF);
-      chord_key_count++;
-      chord_key_down++;
-      return false;
-    } else {
-      if (chording) {
-        chord_key_down--;
-        if (chord_key_down == 0) {
-          chording = false;
-          // Chord Dictionary
-          if (keys_chord((uint8_t[]){KC_ENTER, KC_SPACE})) {
-            register_code(KC_A);
-            unregister_code(KC_A);
-            return false;
-          }
-          for (uint8_t i = 0; i < chord_key_count; i++) {
-            register_code(chord_keys[i]);
-            unregister_code(chord_keys[i]);
-            return false;
-          }
-        }
-      }
-    }
-  }
-  return true;
-}
diff --git a/quantum/process_keycode/process_clicky.c b/quantum/process_keycode/process_clicky.c
index 1e950d1113..8238c263f9 100644
--- a/quantum/process_keycode/process_clicky.c
+++ b/quantum/process_keycode/process_clicky.c
@@ -3,11 +3,6 @@
 
 #ifdef AUDIO_CLICKY
 
-#ifdef AUDIO_CLICKY_ON
-bool clicky_enable = true;
-#else // AUDIO_CLICKY_ON
-bool clicky_enable = false;
-#endif // AUDIO_CLICKY_ON
 #ifndef AUDIO_CLICKY_FREQ_DEFAULT
 #define AUDIO_CLICKY_FREQ_DEFAULT 440.0f
 #endif // !AUDIO_CLICKY_FREQ_DEFAULT
@@ -25,8 +20,11 @@ bool clicky_enable = false;
 #endif // !AUDIO_CLICKY_FREQ_RANDOMNESS
 
 float clicky_freq = AUDIO_CLICKY_FREQ_DEFAULT;
+float clicky_rand = AUDIO_CLICKY_FREQ_RANDOMNESS;
 float clicky_song[][2]  = {{AUDIO_CLICKY_FREQ_DEFAULT, 3}, {AUDIO_CLICKY_FREQ_DEFAULT, 1}}; // 3 and 1 --> durations
 
+extern audio_config_t audio_config;
+
 #ifndef NO_MUSIC_MODE
 extern bool music_activated;
 extern bool midi_activated;
@@ -36,31 +34,61 @@ void clicky_play(void) {
 #ifndef NO_MUSIC_MODE
   if (music_activated || midi_activated) return;
 #endif // !NO_MUSIC_MODE
-  clicky_song[0][0] = 2.0f * clicky_freq * (1.0f + AUDIO_CLICKY_FREQ_RANDOMNESS * ( ((float)rand()) / ((float)(RAND_MAX)) ) );
-  clicky_song[1][0] = clicky_freq * (1.0f + AUDIO_CLICKY_FREQ_RANDOMNESS * ( ((float)rand()) / ((float)(RAND_MAX)) ) );
+  clicky_song[0][0] = 2.0f * clicky_freq * (1.0f + clicky_rand * ( ((float)rand()) / ((float)(RAND_MAX)) ) );
+  clicky_song[1][0] = clicky_freq * (1.0f + clicky_rand * ( ((float)rand()) / ((float)(RAND_MAX)) ) );
   PLAY_SONG(clicky_song);
 }
 
+void clicky_freq_up(void) {
+  float new_freq = clicky_freq * AUDIO_CLICKY_FREQ_FACTOR;
+  if (new_freq < AUDIO_CLICKY_FREQ_MAX) {
+    clicky_freq = new_freq;
+  }
+}
+
+void clicky_freq_down(void) {
+  float new_freq = clicky_freq / AUDIO_CLICKY_FREQ_FACTOR;
+  if (new_freq > AUDIO_CLICKY_FREQ_MIN) {
+    clicky_freq = new_freq;
+  }
+}
+
+void clicky_freq_reset(void) {
+  clicky_freq = AUDIO_CLICKY_FREQ_DEFAULT;
+}
+
+void clicky_toggle(void) {
+  audio_config.clicky_enable ^= 1;
+  eeconfig_update_audio(audio_config.raw);
+}
+
+void clicky_on(void) {
+  audio_config.clicky_enable = 1;
+  eeconfig_update_audio(audio_config.raw);
+}
+
+void clicky_off(void) {
+  audio_config.clicky_enable = 0;
+  eeconfig_update_audio(audio_config.raw);
+}
+
+bool is_clicky_on(void) {
+      return (audio_config.clicky_enable != 0);
+}
+
 bool process_clicky(uint16_t keycode, keyrecord_t *record) {
-    if (keycode == CLICKY_TOGGLE && record->event.pressed) { clicky_enable = !clicky_enable; }
+    if (keycode == CLICKY_TOGGLE && record->event.pressed) { clicky_toggle(); }
 
-    if (keycode == CLICKY_RESET && record->event.pressed) { clicky_freq = AUDIO_CLICKY_FREQ_DEFAULT; }
+    if (keycode == CLICKY_ENABLE && record->event.pressed) { clicky_on(); }
+    if (keycode == CLICKY_DISABLE && record->event.pressed) { clicky_off(); }
 
-    if (keycode == CLICKY_UP && record->event.pressed) {
-      float new_freq = clicky_freq * AUDIO_CLICKY_FREQ_FACTOR;
-      if (new_freq < AUDIO_CLICKY_FREQ_MAX) {
-        clicky_freq = new_freq;
-      }
-    }
-    if (keycode == CLICKY_DOWN && record->event.pressed) {
-      float new_freq = clicky_freq / AUDIO_CLICKY_FREQ_FACTOR;
-      if (new_freq > AUDIO_CLICKY_FREQ_MIN) {
-        clicky_freq = new_freq;
-      }
-    }
+    if (keycode == CLICKY_RESET && record->event.pressed) { clicky_freq_reset(); }
+
+    if (keycode == CLICKY_UP && record->event.pressed) { clicky_freq_up(); }
+    if (keycode == CLICKY_DOWN && record->event.pressed) { clicky_freq_down(); }
 
 
-    if ( clicky_enable ) {
+    if ( audio_config.clicky_enable ) {
       if (record->event.pressed) {
         clicky_play();;
       }
diff --git a/quantum/process_keycode/process_clicky.h b/quantum/process_keycode/process_clicky.h
index e274af56f1..f746edb951 100644
--- a/quantum/process_keycode/process_clicky.h
+++ b/quantum/process_keycode/process_clicky.h
@@ -4,4 +4,14 @@
 void clicky_play(void);
 bool process_clicky(uint16_t keycode, keyrecord_t *record);
 
+void clicky_freq_up(void);
+void clicky_freq_down(void);
+void clicky_freq_reset(void);
+
+void clicky_toggle(void);
+void clicky_on(void);
+void clicky_off(void);
+
+bool is_clicky_on(void);
+
 #endif
diff --git a/quantum/process_keycode/process_leader.c b/quantum/process_keycode/process_leader.c
index c87ef115af..897e9eabf6 100644
--- a/quantum/process_keycode/process_leader.c
+++ b/quantum/process_keycode/process_leader.c
@@ -14,7 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef DISABLE_LEADER
+#ifdef LEADER_ENABLE
 
 #include "process_leader.h"
 
@@ -35,25 +35,40 @@ uint16_t leader_time = 0;
 uint16_t leader_sequence[5] = {0, 0, 0, 0, 0};
 uint8_t leader_sequence_size = 0;
 
+void qk_leader_start(void) {
+  if (leading) { return; }
+  leader_start();
+  leading = true;
+  leader_time = timer_read();
+  leader_sequence_size = 0;
+  leader_sequence[0] = 0;
+  leader_sequence[1] = 0;
+  leader_sequence[2] = 0;
+  leader_sequence[3] = 0;
+  leader_sequence[4] = 0;
+}
+
 bool process_leader(uint16_t keycode, keyrecord_t *record) {
   // Leader key set-up
   if (record->event.pressed) {
-    if (!leading && keycode == KC_LEAD) {
-      leader_start();
-      leading = true;
-      leader_time = timer_read();
-      leader_sequence_size = 0;
-      leader_sequence[0] = 0;
-      leader_sequence[1] = 0;
-      leader_sequence[2] = 0;
-      leader_sequence[3] = 0;
-      leader_sequence[4] = 0;
-      return false;
-    }
-    if (leading && timer_elapsed(leader_time) < LEADER_TIMEOUT) {
-      leader_sequence[leader_sequence_size] = keycode;
-      leader_sequence_size++;
-      return false;
+    if (leading) {
+      if (timer_elapsed(leader_time) < LEADER_TIMEOUT) {
+#ifndef LEADER_KEY_STRICT_KEY_PROCESSING
+        if ((keycode >= QK_MOD_TAP && keycode <= QK_MOD_TAP_MAX) || (keycode >= QK_LAYER_TAP && keycode <= QK_LAYER_TAP_MAX)) {
+          keycode = keycode & 0xFF;
+        }
+#endif // LEADER_KEY_STRICT_KEY_PROCESSING
+        leader_sequence[leader_sequence_size] = keycode;
+        leader_sequence_size++;
+#ifdef LEADER_PER_KEY_TIMING
+        leader_time = timer_read();
+#endif
+        return false;
+      }
+    } else {
+      if (keycode == KC_LEAD) {
+        qk_leader_start();
+      }
     }
   }
   return true;
diff --git a/quantum/process_keycode/process_leader.h b/quantum/process_keycode/process_leader.h
index 59c3eed1be..15bccc3f67 100644
--- a/quantum/process_keycode/process_leader.h
+++ b/quantum/process_keycode/process_leader.h
@@ -24,7 +24,7 @@ bool process_leader(uint16_t keycode, keyrecord_t *record);
 
 void leader_start(void);
 void leader_end(void);
-
+void qk_leader_start(void);
 
 #define SEQ_ONE_KEY(key) if (leader_sequence[0] == (key) && leader_sequence[1] == 0 && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0)
 #define SEQ_TWO_KEYS(key1, key2) if (leader_sequence[0] == (key1) && leader_sequence[1] == (key2) && leader_sequence[2] == 0 && leader_sequence[3] == 0 && leader_sequence[4] == 0)
diff --git a/quantum/process_keycode/process_tap_dance.c b/quantum/process_keycode/process_tap_dance.c
index 8337806912..16d33dddee 100644
--- a/quantum/process_keycode/process_tap_dance.c
+++ b/quantum/process_keycode/process_tap_dance.c
@@ -16,6 +16,10 @@
 #include "quantum.h"
 #include "action_tapping.h"
 
+#ifndef TAPPING_TERM
+#define TAPPING_TERM 200
+#endif
+
 #ifndef NO_ACTION_ONESHOT
 uint8_t get_oneshot_mods(void);
 #endif
@@ -127,6 +131,7 @@ void preprocess_tap_dance(uint16_t keycode, keyrecord_t *record) {
       if (keycode == action->state.keycode && keycode == last_td)
         continue;
       action->state.interrupted = true;
+      action->state.interrupting_keycode = keycode;
       process_tap_dance_action_on_dance_finished (action);
       reset_tap_dance (&action->state);
     }
@@ -205,5 +210,6 @@ void reset_tap_dance (qk_tap_dance_state_t *state) {
   state->count = 0;
   state->interrupted = false;
   state->finished = false;
+  state->interrupting_keycode = 0;
   last_td = 0;
 }
diff --git a/quantum/process_keycode/process_tap_dance.h b/quantum/process_keycode/process_tap_dance.h
index 8b0a47c49b..ca12f4746e 100644
--- a/quantum/process_keycode/process_tap_dance.h
+++ b/quantum/process_keycode/process_tap_dance.h
@@ -27,6 +27,7 @@ typedef struct
   uint8_t oneshot_mods;
   uint8_t weak_mods;
   uint16_t keycode;
+  uint16_t interrupting_keycode;
   uint16_t timer;
   bool interrupted;
   bool pressed;
diff --git a/quantum/process_keycode/process_ucis.c b/quantum/process_keycode/process_ucis.c
index 86c0937f5e..5de2e41fc3 100644
--- a/quantum/process_keycode/process_ucis.c
+++ b/quantum/process_keycode/process_ucis.c
@@ -32,6 +32,10 @@ void qk_ucis_start_user(void) {
   unicode_input_finish();
 }
 
+__attribute__((weak))
+void qk_ucis_success(uint8_t symbol_index) {
+}
+
 static bool is_uni_seq(char *seq) {
   uint8_t i;
 
@@ -142,6 +146,10 @@ bool process_ucis (uint16_t keycode, keyrecord_t *record) {
     }
     unicode_input_finish();
 
+    if (symbol_found) {
+      qk_ucis_success(i);
+    }
+
     qk_ucis_state.in_progress = false;
     return false;
   }
diff --git a/quantum/process_keycode/process_ucis.h b/quantum/process_keycode/process_ucis.h
index 3f736a709f..b114d839ab 100644
--- a/quantum/process_keycode/process_ucis.h
+++ b/quantum/process_keycode/process_ucis.h
@@ -14,8 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PROCESS_UCIS_H
-#define PROCESS_UCIS_H
+#pragma once
 
 #include "quantum.h"
 #include "process_unicode_common.h"
@@ -45,7 +44,6 @@ extern const qk_ucis_symbol_t ucis_symbol_table[];
 void qk_ucis_start(void);
 void qk_ucis_start_user(void);
 void qk_ucis_symbol_fallback (void);
+void qk_ucis_success(uint8_t symbol_index);
 void register_ucis(const char *hex);
 bool process_ucis (uint16_t keycode, keyrecord_t *record);
-
-#endif
diff --git a/quantum/process_keycode/process_unicode.c b/quantum/process_keycode/process_unicode.c
index fd008eca12..19beb84520 100644
--- a/quantum/process_keycode/process_unicode.c
+++ b/quantum/process_keycode/process_unicode.c
@@ -17,14 +17,8 @@
 #include "action_util.h"
 #include "eeprom.h"
 
-static uint8_t first_flag = 0;
-
 bool process_unicode(uint16_t keycode, keyrecord_t *record) {
   if (keycode > QK_UNICODE && record->event.pressed) {
-    if (first_flag == 0) {
-      set_unicode_input_mode(eeprom_read_byte(EECONFIG_UNICODEMODE));
-      first_flag = 1;
-    }
     uint16_t unicode = keycode & 0x7FFF;
     unicode_input_start();
     register_hex(unicode);
@@ -32,4 +26,3 @@ bool process_unicode(uint16_t keycode, keyrecord_t *record) {
   }
   return true;
 }
-
diff --git a/quantum/process_keycode/process_unicode.h b/quantum/process_keycode/process_unicode.h
index c525b74f03..0913e99107 100644
--- a/quantum/process_keycode/process_unicode.h
+++ b/quantum/process_keycode/process_unicode.h
@@ -13,12 +13,9 @@
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
-#ifndef PROCESS_UNICODE_H
-#define PROCESS_UNICODE_H
+#pragma once
 
 #include "quantum.h"
 #include "process_unicode_common.h"
 
 bool process_unicode(uint16_t keycode, keyrecord_t *record);
-
-#endif
diff --git a/quantum/process_keycode/process_unicode_common.c b/quantum/process_keycode/process_unicode_common.c
index 7f34ad57cf..3286f45b5d 100644
--- a/quantum/process_keycode/process_unicode_common.c
+++ b/quantum/process_keycode/process_unicode_common.c
@@ -16,94 +16,115 @@
 
 #include "process_unicode_common.h"
 #include "eeprom.h"
+#include <ctype.h>
+#include <string.h>
 
-static uint8_t input_mode;
-uint8_t mods;
+unicode_config_t unicode_config;
+#if UNICODE_SELECTED_MODES != -1
+static uint8_t selected[] = { UNICODE_SELECTED_MODES };
+static uint8_t selected_count = sizeof selected / sizeof *selected;
+static uint8_t selected_index;
+#endif
 
-void set_unicode_input_mode(uint8_t os_target)
-{
-  input_mode = os_target;
-  eeprom_update_byte(EECONFIG_UNICODEMODE, os_target);
+void unicode_input_mode_init(void) {
+  unicode_config.raw = eeprom_read_byte(EECONFIG_UNICODEMODE);
+#if UNICODE_SELECTED_MODES != -1
+  #if UNICODE_CYCLE_PERSIST
+  // Find input_mode in selected modes
+  uint8_t i;
+  for (i = 0; i < selected_count; i++) {
+    if (selected[i] == unicode_config.input_mode) {
+      selected_index = i;
+      break;
+    }
+  }
+  if (i == selected_count) {
+    // Not found: input_mode isn't selected, change to one that is
+    unicode_config.input_mode = selected[selected_index = 0];
+  }
+  #else
+  // Always change to the first selected input mode
+  unicode_config.input_mode = selected[selected_index = 0];
+  #endif
+#endif
+  dprintf("Unicode input mode init to: %u\n", unicode_config.input_mode);
 }
 
 uint8_t get_unicode_input_mode(void) {
-  return input_mode;
+  return unicode_config.input_mode;
 }
 
+void set_unicode_input_mode(uint8_t mode) {
+  unicode_config.input_mode = mode;
+  persist_unicode_input_mode();
+  dprintf("Unicode input mode set to: %u\n", unicode_config.input_mode);
+}
+
+void cycle_unicode_input_mode(uint8_t offset) {
+#if UNICODE_SELECTED_MODES != -1
+  selected_index = (selected_index + offset) % selected_count;
+  unicode_config.input_mode = selected[selected_index];
+  #if UNICODE_CYCLE_PERSIST
+  persist_unicode_input_mode();
+  #endif
+  dprintf("Unicode input mode cycle to: %u\n", unicode_config.input_mode);
+#endif
+}
+
+void persist_unicode_input_mode(void) {
+  eeprom_update_byte(EECONFIG_UNICODEMODE, unicode_config.input_mode);
+}
+
+static uint8_t saved_mods;
+
 __attribute__((weak))
-void unicode_input_start (void) {
-  // save current mods
-  mods = keyboard_report->mods;
-
-  // unregister all mods to start from clean state
-  if (mods & MOD_BIT(KC_LSFT)) unregister_code(KC_LSFT);
-  if (mods & MOD_BIT(KC_RSFT)) unregister_code(KC_RSFT);
-  if (mods & MOD_BIT(KC_LCTL)) unregister_code(KC_LCTL);
-  if (mods & MOD_BIT(KC_RCTL)) unregister_code(KC_RCTL);
-  if (mods & MOD_BIT(KC_LALT)) unregister_code(KC_LALT);
-  if (mods & MOD_BIT(KC_RALT)) unregister_code(KC_RALT);
-  if (mods & MOD_BIT(KC_LGUI)) unregister_code(KC_LGUI);
-  if (mods & MOD_BIT(KC_RGUI)) unregister_code(KC_RGUI);
-
-  switch(input_mode) {
+void unicode_input_start(void) {
+  saved_mods = get_mods(); // Save current mods
+  clear_mods(); // Unregister mods to start from a clean state
+
+  switch (unicode_config.input_mode) {
   case UC_OSX:
-    register_code(KC_LALT);
-    break;
-  case UC_OSX_RALT:
-    register_code(KC_RALT);
+    register_code(UNICODE_OSX_KEY);
     break;
   case UC_LNX:
     register_code(KC_LCTL);
     register_code(KC_LSFT);
-    register_code(KC_U);
-    unregister_code(KC_U);
+    tap_code(KC_U); // TODO: Replace with tap_code16(LCTL(LSFT(KC_U))); and test
     unregister_code(KC_LSFT);
     unregister_code(KC_LCTL);
     break;
   case UC_WIN:
     register_code(KC_LALT);
-    register_code(KC_PPLS);
-    unregister_code(KC_PPLS);
+    tap_code(KC_PPLS);
     break;
   case UC_WINC:
-    register_code(KC_RALT);
-    unregister_code(KC_RALT);
-    register_code(KC_U);
-    unregister_code(KC_U);
+    tap_code(UNICODE_WINC_KEY);
+    tap_code(KC_U);
+    break;
   }
+
   wait_ms(UNICODE_TYPE_DELAY);
 }
 
 __attribute__((weak))
-void unicode_input_finish (void) {
-  switch(input_mode) {
-    case UC_OSX:
-    case UC_WIN:
-      unregister_code(KC_LALT);
-      break;
-    case UC_OSX_RALT:
-      unregister_code(KC_RALT);
-      break;
-    case UC_LNX:
-      register_code(KC_SPC);
-      unregister_code(KC_SPC);
-      break;
+void unicode_input_finish(void) {
+  switch (unicode_config.input_mode) {
+  case UC_OSX:
+    unregister_code(UNICODE_OSX_KEY);
+    break;
+  case UC_LNX:
+    tap_code(KC_SPC);
+    break;
+  case UC_WIN:
+    unregister_code(KC_LALT);
+    break;
   }
 
-  // reregister previously set mods
-  if (mods & MOD_BIT(KC_LSFT)) register_code(KC_LSFT);
-  if (mods & MOD_BIT(KC_RSFT)) register_code(KC_RSFT);
-  if (mods & MOD_BIT(KC_LCTL)) register_code(KC_LCTL);
-  if (mods & MOD_BIT(KC_RCTL)) register_code(KC_RCTL);
-  if (mods & MOD_BIT(KC_LALT)) register_code(KC_LALT);
-  if (mods & MOD_BIT(KC_RALT)) register_code(KC_RALT);
-  if (mods & MOD_BIT(KC_LGUI)) register_code(KC_LGUI);
-  if (mods & MOD_BIT(KC_RGUI)) register_code(KC_RGUI);
+  set_mods(saved_mods); // Reregister previously set mods
 }
 
 __attribute__((weak))
-uint16_t hex_to_keycode(uint8_t hex)
-{
+uint16_t hex_to_keycode(uint8_t hex) {
   if (hex == 0x0) {
     return KC_0;
   } else if (hex < 0xA) {
@@ -116,7 +137,89 @@ uint16_t hex_to_keycode(uint8_t hex)
 void register_hex(uint16_t hex) {
   for(int i = 3; i >= 0; i--) {
     uint8_t digit = ((hex >> (i*4)) & 0xF);
-    register_code(hex_to_keycode(digit));
-    unregister_code(hex_to_keycode(digit));
+    tap_code(hex_to_keycode(digit));
+  }
+}
+
+void send_unicode_hex_string(const char *str) {
+  if (!str) { return; }
+
+  while (*str) {
+    // Find the next code point (token) in the string
+    for (; *str == ' '; str++);
+    size_t n = strcspn(str, " "); // Length of the current token
+    char code_point[n+1];
+    strncpy(code_point, str, n);
+    code_point[n] = '\0'; // Make sure it's null-terminated
+
+    // Normalize the code point: make all hex digits lowercase
+    for (char *p = code_point; *p; p++) {
+      *p = tolower((unsigned char)*p);
+    }
+
+    // Send the code point as a Unicode input string
+    unicode_input_start();
+    send_string(code_point);
+    unicode_input_finish();
+
+    str += n; // Move to the first ' ' (or '\0') after the current token
+  }
+}
+
+bool process_unicode_common(uint16_t keycode, keyrecord_t *record) {
+  if (record->event.pressed) {
+    switch (keycode) {
+    case UNICODE_MODE_FORWARD:
+      cycle_unicode_input_mode(+1);
+      break;
+    case UNICODE_MODE_REVERSE:
+      cycle_unicode_input_mode(-1);
+      break;
+
+    case UNICODE_MODE_OSX:
+      set_unicode_input_mode(UC_OSX);
+#if defined(AUDIO_ENABLE) && defined(UNICODE_SONG_OSX)
+      static float song_osx[][2] = UNICODE_SONG_OSX;
+      PLAY_SONG(song_osx);
+#endif
+      break;
+    case UNICODE_MODE_LNX:
+      set_unicode_input_mode(UC_LNX);
+#if defined(AUDIO_ENABLE) && defined(UNICODE_SONG_LNX)
+      static float song_lnx[][2] = UNICODE_SONG_LNX;
+      PLAY_SONG(song_lnx);
+#endif
+      break;
+    case UNICODE_MODE_WIN:
+      set_unicode_input_mode(UC_WIN);
+#if defined(AUDIO_ENABLE) && defined(UNICODE_SONG_WIN)
+      static float song_win[][2] = UNICODE_SONG_WIN;
+      PLAY_SONG(song_win);
+#endif
+      break;
+    case UNICODE_MODE_BSD:
+      set_unicode_input_mode(UC_BSD);
+#if defined(AUDIO_ENABLE) && defined(UNICODE_SONG_BSD)
+      static float song_bsd[][2] = UNICODE_SONG_BSD;
+      PLAY_SONG(song_bsd);
+#endif
+      break;
+    case UNICODE_MODE_WINC:
+      set_unicode_input_mode(UC_WINC);
+#if defined(AUDIO_ENABLE) && defined(UNICODE_SONG_WINC)
+      static float song_winc[][2] = UNICODE_SONG_WINC;
+      PLAY_SONG(song_winc);
+#endif
+      break;
+    }
   }
+#if   defined(UNICODE_ENABLE)
+  return process_unicode(keycode, record);
+#elif defined(UNICODEMAP_ENABLE)
+  return process_unicode_map(keycode, record);
+#elif defined(UCIS_ENABLE)
+  return process_ucis(keycode, record);
+#else
+  return true;
+#endif
 }
diff --git a/quantum/process_keycode/process_unicode_common.h b/quantum/process_keycode/process_unicode_common.h
index 4d2b04fb39..e608ab76be 100644
--- a/quantum/process_keycode/process_unicode_common.h
+++ b/quantum/process_keycode/process_unicode_common.h
@@ -14,33 +14,71 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PROCESS_UNICODE_COMMON_H
-#define PROCESS_UNICODE_COMMON_H
+#pragma once
 
 #include "quantum.h"
 
-#ifndef UNICODE_TYPE_DELAY
-#define UNICODE_TYPE_DELAY 10
+#if defined(UNICODE_ENABLE) + defined(UNICODEMAP_ENABLE) + defined(UCIS_ENABLE) > 1
+  #error "Cannot enable more than one Unicode method (UNICODE, UNICODEMAP, UCIS) at the same time"
+#endif
+
+// Keycodes used for starting Unicode input on different platforms
+#ifndef UNICODE_OSX_KEY
+  #define UNICODE_OSX_KEY  KC_LALT
+#endif
+#ifndef UNICODE_WINC_KEY
+  #define UNICODE_WINC_KEY KC_RALT
 #endif
 
-__attribute__ ((unused))
-static uint8_t input_mode;
+// Comma-delimited, ordered list of input modes selected for use (e.g. in cycle)
+// Example: #define UNICODE_SELECTED_MODES UC_WINC, UC_LNX
+#ifndef UNICODE_SELECTED_MODES
+  #define UNICODE_SELECTED_MODES -1
+#endif
+
+// Whether input mode changes in cycle should be written to EEPROM
+#ifndef UNICODE_CYCLE_PERSIST
+  #define UNICODE_CYCLE_PERSIST true
+#endif
 
-void set_unicode_input_mode(uint8_t os_target);
+// Delay between starting Unicode input and sending a sequence, in ms
+#ifndef UNICODE_TYPE_DELAY
+  #define UNICODE_TYPE_DELAY 10
+#endif
+
+enum unicode_input_modes {
+  UC_OSX,   // Mac OS X using Unicode Hex Input
+  UC_LNX,   // Linux using IBus
+  UC_WIN,   // Windows using EnableHexNumpad
+  UC_BSD,   // BSD (not implemented)
+  UC_WINC,  // Windows using WinCompose (https://github.com/samhocevar/wincompose)
+  UC__COUNT // Number of available input modes (always leave at the end)
+};
+
+typedef union {
+  uint32_t raw;
+  struct {
+    uint8_t input_mode : 8;
+  };
+} unicode_config_t;
+
+extern unicode_config_t unicode_config;
+
+void unicode_input_mode_init(void);
 uint8_t get_unicode_input_mode(void);
+void set_unicode_input_mode(uint8_t mode);
+void cycle_unicode_input_mode(uint8_t offset);
+void persist_unicode_input_mode(void);
+
 void unicode_input_start(void);
 void unicode_input_finish(void);
+
 void register_hex(uint16_t hex);
+void send_unicode_hex_string(const char *str);
 
-#define UC_OSX 0  // Mac OS X
-#define UC_LNX 1  // Linux
-#define UC_WIN 2  // Windows 'HexNumpad'
-#define UC_BSD 3  // BSD (not implemented)
-#define UC_WINC 4 // WinCompose https://github.com/samhocevar/wincompose
-#define UC_OSX_RALT 5 // Mac OS X using Right Alt key for Unicode Compose
+bool process_unicode_common(uint16_t keycode, keyrecord_t *record);
 
 #define UC_BSPC	UC(0x0008)
-
 #define UC_SPC	UC(0x0020)
 
 #define UC_EXLM	UC(0x0021)
@@ -145,5 +183,3 @@ void register_hex(uint16_t hex);
 #define UC_RCBR	UC(0x007D)
 #define UC_TILD	UC(0x007E)
 #define UC_DEL	UC(0x007F)
-
-#endif
diff --git a/quantum/process_keycode/process_unicodemap.c b/quantum/process_keycode/process_unicodemap.c
index 47c27b9117..75f35112b1 100644
--- a/quantum/process_keycode/process_unicodemap.c
+++ b/quantum/process_keycode/process_unicodemap.c
@@ -50,7 +50,7 @@ bool process_unicode_map(uint16_t keycode, keyrecord_t *record) {
     const uint32_t* map = unicode_map;
     uint16_t index = keycode - QK_UNICODE_MAP;
     uint32_t code = pgm_read_dword(&map[index]);
-    if (code > 0xFFFF && code <= 0x10ffff && (input_mode == UC_OSX || input_mode == UC_OSX_RALT)) {
+    if (code > 0xFFFF && code <= 0x10ffff && input_mode == UC_OSX) {
       // Convert to UTF-16 surrogate pair
       code -= 0x10000;
       uint32_t lo = code & 0x3ff;
@@ -59,7 +59,7 @@ bool process_unicode_map(uint16_t keycode, keyrecord_t *record) {
       register_hex32(hi + 0xd800);
       register_hex32(lo + 0xdc00);
       unicode_input_finish();
-    } else if ((code > 0x10ffff && (input_mode == UC_OSX || input_mode == UC_OSX_RALT)) || (code > 0xFFFFF && input_mode == UC_LNX)) {
+    } else if ((code > 0x10ffff && input_mode == UC_OSX) || (code > 0xFFFFF && input_mode == UC_LNX)) {
       // when character is out of range supported by the OS
       unicode_map_input_error();
     } else {
diff --git a/quantum/process_keycode/process_unicodemap.h b/quantum/process_keycode/process_unicodemap.h
index 929c88c0b6..f6d64bb86b 100644
--- a/quantum/process_keycode/process_unicodemap.h
+++ b/quantum/process_keycode/process_unicodemap.h
@@ -14,12 +14,10 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef PROCESS_UNICODEMAP_H
-#define PROCESS_UNICODEMAP_H
+#pragma once
 
 #include "quantum.h"
 #include "process_unicode_common.h"
 
 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 ab47fa48ff..85db100ab4 100644
--- a/quantum/quantum.c
+++ b/quantum/quantum.c
@@ -42,6 +42,11 @@ extern backlight_config_t backlight_config;
 #include "process_midi.h"
 #endif
 
+
+#ifdef ENCODER_ENABLE
+#include "encoder.h"
+#endif
+
 #ifdef AUDIO_ENABLE
   #ifndef GOODBYE_SONG
     #define GOODBYE_SONG SONG(GOODBYE_SOUND)
@@ -127,6 +132,14 @@ void unregister_code16 (uint16_t code) {
   }
 }
 
+void tap_code16(uint16_t code) {
+  register_code16(code);
+  #if TAP_CODE_DELAY > 0
+    wait_ms(TAP_CODE_DELAY);
+  #endif
+  unregister_code16(code);
+}
+
 __attribute__ ((weak))
 bool process_action_kb(keyrecord_t *record) {
   return true;
@@ -196,7 +209,7 @@ bool process_record_quantum(keyrecord_t *record) {
   keypos_t key = record->event.key;
   uint16_t keycode;
 
-  #if !defined(NO_ACTION_LAYER) && defined(PREVENT_STUCK_MODIFIERS)
+  #if !defined(NO_ACTION_LAYER) && !defined(STRICT_LAYER_RELEASE)
     /* TODO: Use store_or_get_action() or a similar function. */
     if (!disable_action_cache) {
       uint8_t layer;
@@ -230,7 +243,7 @@ bool process_record_quantum(keyrecord_t *record) {
     process_key_lock(&keycode, record) &&
   #endif
   #if defined(AUDIO_ENABLE) && defined(AUDIO_CLICKY)
-      process_clicky(keycode, record) &&
+    process_clicky(keycode, record) &&
   #endif //AUDIO_CLICKY
     process_record_kb(keycode, record) &&
   #if defined(RGB_MATRIX_ENABLE) && defined(RGB_MATRIX_KEYPRESSES)
@@ -245,36 +258,27 @@ bool process_record_quantum(keyrecord_t *record) {
   #ifdef STENO_ENABLE
     process_steno(keycode, record) &&
   #endif
-  #if ( defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))) && !defined(NO_MUSIC_MODE)
+  #if (defined(AUDIO_ENABLE) || (defined(MIDI_ENABLE) && defined(MIDI_BASIC))) && !defined(NO_MUSIC_MODE)
     process_music(keycode, record) &&
   #endif
   #ifdef TAP_DANCE_ENABLE
     process_tap_dance(keycode, record) &&
   #endif
-  #ifndef DISABLE_LEADER
-    process_leader(keycode, record) &&
+  #if defined(UNICODE_ENABLE) || defined(UNICODEMAP_ENABLE) || defined(UCIS_ENABLE)
+    process_unicode_common(keycode, record) &&
   #endif
-  #ifndef DISABLE_CHORDING
-    process_chording(keycode, record) &&
+  #ifdef LEADER_ENABLE
+    process_leader(keycode, record) &&
   #endif
   #ifdef COMBO_ENABLE
     process_combo(keycode, record) &&
   #endif
-  #ifdef UNICODE_ENABLE
-    process_unicode(keycode, record) &&
-  #endif
-  #ifdef UCIS_ENABLE
-    process_ucis(keycode, record) &&
-  #endif
   #ifdef PRINTING_ENABLE
     process_printer(keycode, record) &&
   #endif
   #ifdef AUTO_SHIFT_ENABLE
     process_auto_shift(keycode, record) &&
   #endif
-  #ifdef UNICODEMAP_ENABLE
-    process_unicode_map(keycode, record) &&
-  #endif
   #ifdef TERMINAL_ENABLE
     process_terminal(keycode, record) &&
   #endif
@@ -296,6 +300,11 @@ bool process_record_quantum(keyrecord_t *record) {
           print("DEBUG: enabled.\n");
       }
     return false;
+    case EEPROM_RESET:
+      if (record->event.pressed) {
+          eeconfig_init();
+      }
+    return false;
   #ifdef FAUXCLICKY_ENABLE
   case FC_TOG:
     if (record->event.pressed) {
@@ -445,75 +454,97 @@ bool process_record_quantum(keyrecord_t *record) {
     return false;
   case RGB_MODE_PLAIN:
     if (record->event.pressed) {
-      rgblight_mode(1);
+      rgblight_mode(RGBLIGHT_MODE_STATIC_LIGHT);
       #ifdef SPLIT_KEYBOARD
           RGB_DIRTY = true;
       #endif
     }
     return false;
   case RGB_MODE_BREATHE:
+  #ifdef RGBLIGHT_EFFECT_BREATHING
     if (record->event.pressed) {
-      if ((2 <= rgblight_get_mode()) && (rgblight_get_mode() < 5)) {
+      if ((RGBLIGHT_MODE_BREATHING <= rgblight_get_mode()) &&
+          (rgblight_get_mode() < RGBLIGHT_MODE_BREATHING_end)) {
         rgblight_step();
       } else {
-        rgblight_mode(2);
+        rgblight_mode(RGBLIGHT_MODE_BREATHING);
       }
     }
+  #endif
     return false;
   case RGB_MODE_RAINBOW:
+  #ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
     if (record->event.pressed) {
-      if ((6 <= rgblight_get_mode()) && (rgblight_get_mode() < 8)) {
+      if ((RGBLIGHT_MODE_RAINBOW_MOOD <= rgblight_get_mode()) &&
+          (rgblight_get_mode() < RGBLIGHT_MODE_RAINBOW_MOOD_end)) {
         rgblight_step();
       } else {
-        rgblight_mode(6);
+        rgblight_mode(RGBLIGHT_MODE_RAINBOW_MOOD);
       }
     }
+  #endif
     return false;
   case RGB_MODE_SWIRL:
+  #ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
     if (record->event.pressed) {
-      if ((9 <= rgblight_get_mode()) && (rgblight_get_mode() < 14)) {
+      if ((RGBLIGHT_MODE_RAINBOW_SWIRL <= rgblight_get_mode()) &&
+          (rgblight_get_mode() < RGBLIGHT_MODE_RAINBOW_SWIRL_end)) {
         rgblight_step();
       } else {
-        rgblight_mode(9);
+        rgblight_mode(RGBLIGHT_MODE_RAINBOW_SWIRL);
       }
     }
+  #endif
     return false;
   case RGB_MODE_SNAKE:
+  #ifdef RGBLIGHT_EFFECT_SNAKE
     if (record->event.pressed) {
-      if ((15 <= rgblight_get_mode()) && (rgblight_get_mode() < 20)) {
+      if ((RGBLIGHT_MODE_SNAKE <= rgblight_get_mode()) &&
+          (rgblight_get_mode() < RGBLIGHT_MODE_SNAKE_end)) {
         rgblight_step();
       } else {
-        rgblight_mode(15);
+        rgblight_mode(RGBLIGHT_MODE_SNAKE);
       }
     }
+  #endif
     return false;
   case RGB_MODE_KNIGHT:
+  #ifdef RGBLIGHT_EFFECT_KNIGHT
     if (record->event.pressed) {
-      if ((21 <= rgblight_get_mode()) && (rgblight_get_mode() < 23)) {
+      if ((RGBLIGHT_MODE_KNIGHT <= rgblight_get_mode()) &&
+          (rgblight_get_mode() < RGBLIGHT_MODE_KNIGHT_end)) {
         rgblight_step();
       } else {
-        rgblight_mode(21);
+        rgblight_mode(RGBLIGHT_MODE_KNIGHT);
       }
     }
+  #endif
     return false;
   case RGB_MODE_XMAS:
+  #ifdef RGBLIGHT_EFFECT_CHRISTMAS
     if (record->event.pressed) {
-      rgblight_mode(24);
+      rgblight_mode(RGBLIGHT_MODE_CHRISTMAS);
     }
+  #endif
     return false;
   case RGB_MODE_GRADIENT:
+  #ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
     if (record->event.pressed) {
-      if ((25 <= rgblight_get_mode()) && (rgblight_get_mode() < 34)) {
+      if ((RGBLIGHT_MODE_STATIC_GRADIENT <= rgblight_get_mode()) &&
+          (rgblight_get_mode() < RGBLIGHT_MODE_STATIC_GRADIENT_end)) {
         rgblight_step();
       } else {
-        rgblight_mode(25);
+        rgblight_mode(RGBLIGHT_MODE_STATIC_GRADIENT);
       }
     }
+  #endif
     return false;
   case RGB_MODE_RGBTEST:
+  #ifdef RGBLIGHT_EFFECT_RGB_TEST
     if (record->event.pressed) {
-      rgblight_mode(35);
+      rgblight_mode(RGBLIGHT_MODE_RGB_TEST);
     }
+  #endif
     return false;
   #endif // defined(RGBLIGHT_ENABLE) || defined(RGB_MATRIX_ENABLE)
     #ifdef PROTOCOL_LUFA
@@ -607,6 +638,17 @@ bool process_record_quantum(keyrecord_t *record) {
               PLAY_SONG(ag_norm_song);
             #endif
             break;
+          case MAGIC_TOGGLE_ALT_GUI:
+            keymap_config.swap_lalt_lgui = !keymap_config.swap_lalt_lgui;
+            keymap_config.swap_ralt_rgui = !keymap_config.swap_ralt_rgui;
+            #ifdef AUDIO_ENABLE
+              if (keymap_config.swap_ralt_rgui) {
+                PLAY_SONG(ag_swap_song);
+              } else {
+                PLAY_SONG(ag_norm_song);
+              }
+            #endif
+            break;
           case MAGIC_TOGGLE_NKRO:
             keymap_config.nkro = !keymap_config.nkro;
             break;
@@ -914,7 +956,42 @@ void tap_random_base64(void) {
   }
 }
 
+__attribute__((weak))
+void bootmagic_lite(void) {
+  // The lite version of TMK's bootmagic based on Wilba.
+  // 100% less potential for accidentally making the
+  // keyboard do stupid things.
+
+  // We need multiple scans because debouncing can't be turned off.
+  matrix_scan();
+  #if defined(DEBOUNCING_DELAY) && DEBOUNCING_DELAY > 0
+    wait_ms(DEBOUNCING_DELAY * 2);
+  #elif defined(DEBOUNCE) && DEBOUNCE > 0
+    wait_ms(DEBOUNCE * 2);
+  #else
+    wait_ms(30);
+  #endif
+  matrix_scan();
+
+  // If the Esc and space bar are held down on power up,
+  // reset the EEPROM valid state and jump to bootloader.
+  // Assumes Esc is at [0,0].
+  // This isn't very generalized, but we need something that doesn't
+  // rely on user's keymaps in firmware or EEPROM.
+  if (matrix_get_row(BOOTMAGIC_LITE_ROW) & (1 << BOOTMAGIC_LITE_COLUMN)) {
+    eeconfig_disable();
+    // Jump to bootloader.
+    bootloader_jump();
+  }
+}
+
 void matrix_init_quantum() {
+  #ifdef BOOTMAGIC_LITE
+    bootmagic_lite();
+  #endif
+  if (!eeconfig_is_enabled()) {
+    eeconfig_init();
+  }
   #ifdef BACKLIGHT_ENABLE
     backlight_init_ports();
   #endif
@@ -924,6 +1001,12 @@ void matrix_init_quantum() {
   #ifdef RGB_MATRIX_ENABLE
     rgb_matrix_init();
   #endif
+  #ifdef ENCODER_ENABLE
+    encoder_init();
+  #endif
+  #if defined(UNICODE_ENABLE) || defined(UNICODEMAP_ENABLE) || defined(UCIS_ENABLE)
+    unicode_input_mode_init();
+  #endif
   matrix_init_kb();
 }
 
@@ -958,6 +1041,10 @@ void matrix_scan_quantum() {
     rgb_matrix_task_counter = ((rgb_matrix_task_counter + 1) % (RGB_MATRIX_SKIP_FRAMES + 1));
   #endif
 
+  #ifdef ENCODER_ENABLE
+    encoder_read();
+  #endif
+
   matrix_scan_kb();
 }
 #if defined(BACKLIGHT_ENABLE) && defined(BACKLIGHT_PIN)
diff --git a/quantum/quantum.h b/quantum/quantum.h
index b4e4de1743..f78915fdfb 100644
--- a/quantum/quantum.h
+++ b/quantum/quantum.h
@@ -1,4 +1,4 @@
-/* Copyright 2016-2017 Erez Zukerman, Jack Humbert
+/* Copyright 2016-2018 Erez Zukerman, Jack Humbert, Yiancar
  *
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,9 +17,12 @@
 #define QUANTUM_H
 
 #if defined(__AVR__)
-#include <avr/pgmspace.h>
-#include <avr/io.h>
-#include <avr/interrupt.h>
+    #include <avr/pgmspace.h>
+    #include <avr/io.h>
+    #include <avr/interrupt.h>
+#endif
+#if defined(PROTOCOL_CHIBIOS)
+    #include "hal.h"
 #endif
 #include "wait.h"
 #include "matrix.h"
@@ -28,10 +31,16 @@
     #include "backlight.h"
 #endif
 #if !defined(RGBLIGHT_ENABLE) && !defined(RGB_MATRIX_ENABLE)
-    #include "rgb.h"
+	#include "rgb.h"
 #endif
 #ifdef RGBLIGHT_ENABLE
   #include "rgblight.h"
+#else
+    #ifdef RGB_MATRIX_ENABLE
+        /* dummy define RGBLIGHT_MODE_xxxx */
+        #define RGBLIGHT_H_DUMMY_DEFINE
+        #include "rgblight.h"
+    #endif
 #endif
 
 #ifdef SPLIT_KEYBOARD
@@ -70,9 +79,9 @@ extern uint32_t default_layer_state;
 #ifdef AUDIO_ENABLE
     #include "audio.h"
     #include "process_audio.h"
-  #ifdef AUDIO_CLICKY
-    #include "process_clicky.h"
-  #endif // AUDIO_CLICKY
+    #ifdef AUDIO_CLICKY
+        #include "process_clicky.h"
+    #endif // AUDIO_CLICKY
 #endif
 
 #ifdef STENO_ENABLE
@@ -83,15 +92,10 @@ extern uint32_t default_layer_state;
     #include "process_music.h"
 #endif
 
-#ifndef DISABLE_LEADER
+#ifdef LEADER_ENABLE
     #include "process_leader.h"
 #endif
 
-#define DISABLE_CHORDING
-#ifndef DISABLE_CHORDING
-    #include "process_chording.h"
-#endif
-
 #ifdef UNICODE_ENABLE
     #include "process_unicode.h"
 #endif
@@ -104,7 +108,9 @@ extern uint32_t default_layer_state;
     #include "process_unicodemap.h"
 #endif
 
-#include "process_tap_dance.h"
+#ifdef TAP_DANCE_ENABLE
+  #include "process_tap_dance.h"
+#endif
 
 #ifdef PRINTING_ENABLE
     #include "process_printer.h"
@@ -132,6 +138,50 @@ extern uint32_t default_layer_state;
     #include "hd44780.h"
 #endif
 
+//Function substitutions to ease GPIO manipulation
+#ifdef __AVR__
+    #define PIN_ADDRESS(p, offset) _SFR_IO8(ADDRESS_BASE + (p >> PORT_SHIFTER) + offset)
+
+    #define pin_t uint8_t
+    #define setPinInput(pin) PIN_ADDRESS(pin, 1) &= ~ _BV(pin & 0xF)
+    #define setPinInputHigh(pin) ({\
+            PIN_ADDRESS(pin, 1) &= ~ _BV(pin & 0xF);\
+            PIN_ADDRESS(pin, 2) |=   _BV(pin & 0xF);\
+            })
+    #define setPinInputLow(pin) _Static_assert(0, "AVR Processors cannot impliment an input as pull low")
+    #define setPinOutput(pin) PIN_ADDRESS(pin, 1) |= _BV(pin & 0xF)
+
+    #define writePinHigh(pin) PIN_ADDRESS(pin, 2) |=  _BV(pin & 0xF)
+    #define writePinLow(pin) PIN_ADDRESS(pin, 2) &= ~_BV(pin & 0xF)
+    static inline void writePin(pin_t pin, uint8_t level){
+        if (level){
+            PIN_ADDRESS(pin, 2) |=  _BV(pin & 0xF);
+        } else {
+            PIN_ADDRESS(pin, 2) &= ~_BV(pin & 0xF);
+        }
+    }
+
+    #define readPin(pin) ((bool)(PIN_ADDRESS(pin, 0) & _BV(pin & 0xF)))
+#elif defined(PROTOCOL_CHIBIOS)
+    #define pin_t ioline_t
+    #define setPinInput(pin) palSetLineMode(pin, PAL_MODE_INPUT)
+    #define setPinInputHigh(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLUP)
+    #define setPinInputLow(pin) palSetLineMode(pin, PAL_MODE_INPUT_PULLDOWN)
+    #define setPinOutput(pin) palSetLineMode(pin, PAL_MODE_OUTPUT_PUSHPULL)
+
+    #define writePinHigh(pin) palSetLine(pin)
+    #define writePinLow(pin) palClearLine(pin)
+    static inline void writePin(pin_t pin, uint8_t level){
+        if (level){
+            palSetLine(pin);
+        } else {
+            palClearLine(pin);
+        }
+    }
+
+    #define readPin(pin) palReadLine(pin)
+#endif
+
 #define STRINGIZE(z) #z
 #define ADD_SLASH_X(y) STRINGIZE(\x ## y)
 #define SYMBOL_STR(x) ADD_SLASH_X(x)
@@ -147,6 +197,7 @@ extern uint32_t default_layer_state;
 #define SS_LALT(string) SS_DOWN(X_LALT) string SS_UP(X_LALT)
 #define SS_LSFT(string) SS_DOWN(X_LSHIFT) string SS_UP(X_LSHIFT)
 #define SS_RALT(string) SS_DOWN(X_RALT) string SS_UP(X_RALT)
+#define SS_ALGR(string) SS_RALT(string)
 
 #define SEND_STRING(str) send_string_P(PSTR(str))
 extern const bool ascii_to_shift_lut[0x80];
@@ -176,13 +227,23 @@ bool process_action_kb(keyrecord_t *record);
 bool process_record_kb(uint16_t keycode, keyrecord_t *record);
 bool process_record_user(uint16_t keycode, keyrecord_t *record);
 
+#ifndef BOOTMAGIC_LITE_COLUMN
+  #define BOOTMAGIC_LITE_COLUMN 0
+#endif
+#ifndef BOOTMAGIC_LITE_ROW
+  #define BOOTMAGIC_LITE_ROW 0
+#endif
+
+void bootmagic_lite(void);
+
 void reset_keyboard(void);
 
 void startup_user(void);
 void shutdown_user(void);
 
-void register_code16 (uint16_t code);
-void unregister_code16 (uint16_t code);
+void register_code16(uint16_t code);
+void unregister_code16(uint16_t code);
+void tap_code16(uint16_t code);
 
 #ifdef BACKLIGHT_ENABLE
 void backlight_init_ports(void);
diff --git a/quantum/quantum_keycodes.h b/quantum/quantum_keycodes.h
index f2cdb8a3bf..2b309f4d52 100644
--- a/quantum/quantum_keycodes.h
+++ b/quantum/quantum_keycodes.h
@@ -63,10 +63,6 @@ enum quantum_keycodes {
     QK_ONE_SHOT_LAYER_MAX = 0x54FF,
     QK_ONE_SHOT_MOD       = 0x5500,
     QK_ONE_SHOT_MOD_MAX   = 0x55FF,
-#ifndef DISABLE_CHORDING
-    QK_CHORDING           = 0x5600,
-    QK_CHORDING_MAX       = 0x56FF,
-#endif
     QK_TAP_DANCE          = 0x5700,
     QK_TAP_DANCE_MAX      = 0x57FF,
     QK_LAYER_TAP_TOGGLE   = 0x5800,
@@ -85,9 +81,6 @@ enum quantum_keycodes {
 #endif
     QK_MOD_TAP            = 0x6000,
     QK_MOD_TAP_MAX        = 0x7FFF,
-#if defined(UNICODEMAP_ENABLE) && defined(UNICODE_ENABLE)
-    #error "Cannot enable both UNICODEMAP && UNICODE"
-#endif
 #ifdef UNICODE_ENABLE
     QK_UNICODE            = 0x8000,
     QK_UNICODE_MAX        = 0xFFFF,
@@ -120,10 +113,11 @@ enum quantum_keycodes {
     MAGIC_UNHOST_NKRO,
     MAGIC_UNSWAP_ALT_GUI,
     MAGIC_TOGGLE_NKRO,
+    MAGIC_TOGGLE_ALT_GUI,
     GRAVE_ESC,
 
     // Leader key
-#ifndef DISABLE_LEADER
+#ifdef LEADER_ENABLE
     KC_LEAD,
 #endif
 
@@ -142,10 +136,13 @@ enum quantum_keycodes {
 
     // Faux clicky as part of main audio feature
     CLICKY_TOGGLE,
+    CLICKY_ENABLE,
+    CLICKY_DISABLE,
     CLICKY_UP,
     CLICKY_DOWN,
     CLICKY_RESET,
 
+
 #ifdef FAUXCLICKY_ENABLE
     // Faux clicky
     FC_ON,
@@ -454,32 +451,43 @@ enum quantum_keycodes {
     TERM_OFF,
 #endif
 
+    EEPROM_RESET,
+
+    UNICODE_MODE_FORWARD,
+    UNICODE_MODE_REVERSE,
+
+    UNICODE_MODE_OSX,
+    UNICODE_MODE_LNX,
+    UNICODE_MODE_WIN,
+    UNICODE_MODE_BSD,
+    UNICODE_MODE_WINC,
+
     // always leave at the end
     SAFE_RANGE
 };
 
 // Ability to use mods in layouts
-#define LCTL(kc) (kc | QK_LCTL)
-#define LSFT(kc) (kc | QK_LSFT)
-#define LALT(kc) (kc | QK_LALT)
-#define LGUI(kc) (kc | QK_LGUI)
+#define LCTL(kc) (QK_LCTL | (kc))
+#define LSFT(kc) (QK_LSFT | (kc))
+#define LALT(kc) (QK_LALT | (kc))
+#define LGUI(kc) (QK_LGUI | (kc))
 #define LCMD(kc) LGUI(kc)
 #define LWIN(kc) LGUI(kc)
-#define RCTL(kc) (kc | QK_RCTL)
-#define RSFT(kc) (kc | QK_RSFT)
-#define RALT(kc) (kc | QK_RALT)
-#define RGUI(kc) (kc | QK_RGUI)
+#define RCTL(kc) (QK_RCTL | (kc))
+#define RSFT(kc) (QK_RSFT | (kc))
+#define RALT(kc) (QK_RALT | (kc))
+#define ALGR(kc) RALT(kc)
+#define RGUI(kc) (QK_RGUI | (kc))
 #define RCMD(kc) RGUI(kc)
 #define RWIN(kc) RGUI(kc)
 
-#define HYPR(kc) (kc | QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI)
-#define MEH(kc)  (kc | QK_LCTL | QK_LSFT | QK_LALT)
-#define LCAG(kc) (kc | QK_LCTL | QK_LALT | QK_LGUI)
-#define ALTG(kc) (kc | QK_RCTL | QK_RALT)
-#define SGUI(kc) (kc | QK_LGUI | QK_LSFT)
+#define HYPR(kc) (QK_LCTL | QK_LSFT | QK_LALT | QK_LGUI | (kc))
+#define MEH(kc)  (QK_LCTL | QK_LSFT | QK_LALT | (kc))
+#define LCAG(kc) (QK_LCTL | QK_LALT | QK_LGUI | (kc))
+#define SGUI(kc) (QK_LGUI | QK_LSFT | (kc))
 #define SCMD(kc) SGUI(kc)
 #define SWIN(kc) SGUI(kc)
-#define LCA(kc) (kc | QK_LCTL | QK_LALT)
+#define LCA(kc)  (QK_LCTL | QK_LALT | (kc))
 
 #define MOD_HYPR 0xf
 #define MOD_MEH 0x7
@@ -557,26 +565,29 @@ enum quantum_keycodes {
 #define KC_DELT KC_DELETE // Del key (four letter code)
 
 // Alias for function layers than expand past FN31
-#define FUNC(kc) (kc | QK_FUNCTION)
+#define FUNC(kc) (QK_FUNCTION | (kc))
 
 // Aliases
 #define S(kc) LSFT(kc)
 #define F(kc) FUNC(kc)
 
-#define M(kc) (kc | QK_MACRO)
+#define M(kc) (QK_MACRO | (kc))
 
-#define MACROTAP(kc) (kc | QK_MACRO | FUNC_TAP<<8)
+#define MACROTAP(kc) (QK_MACRO | (FUNC_TAP << 8) | (kc))
 #define MACRODOWN(...) (record->event.pressed ? MACRO(__VA_ARGS__) : MACRO_NONE)
 
 #define KC_GESC GRAVE_ESC
 
+#define EEP_RST EEPROM_RESET
+
 #define CK_TOGG CLICKY_TOGGLE
 #define CK_RST CLICKY_RESET
 #define CK_UP CLICKY_UP
 #define CK_DOWN CLICKY_DOWN
+#define CK_ON CLICKY_ENABLE
+#define CK_OFF CLICKY_DISABLE
 
 #define RGB_MOD RGB_MODE_FORWARD
-#define RGB_SMOD RGB_MODE_FORWARD
 #define RGB_RMOD RGB_MODE_REVERSE
 
 #define RGB_M_P RGB_MODE_PLAIN
@@ -590,10 +601,11 @@ enum quantum_keycodes {
 #define RGB_M_T RGB_MODE_RGBTEST
 
 // L-ayer, T-ap - 256 keycode max, 16 layer max
-#define LT(layer, kc) (kc | QK_LAYER_TAP | ((layer & 0xF) << 8))
+#define LT(layer, kc) (QK_LAYER_TAP | ((layer & 0xF) << 8) | ((kc) & 0xFF))
 
 #define AG_SWAP MAGIC_SWAP_ALT_GUI
 #define AG_NORM MAGIC_UNSWAP_ALT_GUI
+#define AG_TOGG MAGIC_TOGGLE_ALT_GUI
 
 // GOTO layer - 16 layers max
 // when:
@@ -602,32 +614,32 @@ enum quantum_keycodes {
 // Unless you have a good reason not to do so, prefer  ON_PRESS (1) as your default.
 // In fact, we changed it to assume ON_PRESS for sanity/simplicity. If needed, you can add your own
 // keycode modeled after the old version, kept below for this.
-/* #define TO(layer, when) (layer | QK_TO | (when << 0x4)) */
-#define TO(layer) (layer | QK_TO | (ON_PRESS << 0x4))
+/* #define TO(layer, when) (QK_TO | (when << 0x4) | (layer & 0xFF)) */
+#define TO(layer) (QK_TO | (ON_PRESS << 0x4) | (layer & 0xFF))
 
 // Momentary switch layer - 256 layer max
-#define MO(layer) (layer | QK_MOMENTARY)
+#define MO(layer) (QK_MOMENTARY | (layer & 0xFF))
 
 // Set default layer - 256 layer max
-#define DF(layer) (layer | QK_DEF_LAYER)
+#define DF(layer) (QK_DEF_LAYER | (layer & 0xFF))
 
 // Toggle to layer - 256 layer max
-#define TG(layer) (layer | QK_TOGGLE_LAYER)
+#define TG(layer) (QK_TOGGLE_LAYER | (layer & 0xFF))
 
 // One-shot layer - 256 layer max
-#define OSL(layer) (layer | QK_ONE_SHOT_LAYER)
+#define OSL(layer) (QK_ONE_SHOT_LAYER | (layer & 0xFF))
 
 // L-ayer M-od: Momentary switch layer with modifiers active - 16 layer max, left mods only
-#define LM(layer, mod) (QK_LAYER_MOD | (((layer) & 0xF) << 4) | ((mod) & 0xF))
+#define LM(layer, mod) (QK_LAYER_MOD | ((layer & 0xF) << 4) | ((mod) & 0xF))
 
 // One-shot mod
-#define OSM(mod) ((mod) | QK_ONE_SHOT_MOD)
+#define OSM(mod) (QK_ONE_SHOT_MOD | ((mod) & 0xFF))
 
 // Layer tap-toggle
-#define TT(layer) (layer | QK_LAYER_TAP_TOGGLE)
+#define TT(layer) (QK_LAYER_TAP_TOGGLE | (layer & 0xFF))
 
 // M-od, T-ap - 256 keycode max
-#define MT(mod, kc) (kc | QK_MOD_TAP | (((mod) & 0x1F) << 8))
+#define MT(mod, kc) (QK_MOD_TAP | (((mod) & 0x1F) << 8) | ((kc) & 0xFF))
 
 #define CTL_T(kc) MT(MOD_LCTL, kc)
 #define LCTL_T(kc) MT(MOD_LCTL, kc)
@@ -640,7 +652,7 @@ enum quantum_keycodes {
 #define ALT_T(kc) MT(MOD_LALT, kc)
 #define LALT_T(kc) MT(MOD_LALT, kc)
 #define RALT_T(kc) MT(MOD_RALT, kc)
-#define ALGR_T(kc) MT(MOD_RALT, kc) // dual-function AltGR
+#define ALGR_T(kc) RALT_T(kc)
 
 #define GUI_T(kc) MT(MOD_LGUI, kc)
 #define CMD_T(kc) GUI_T(kc)
@@ -652,15 +664,15 @@ enum quantum_keycodes {
 #define RCMD_T(kc) RGUI_T(kc)
 #define RWIN_T(kc) RGUI_T(kc)
 
-#define C_S_T(kc) MT((MOD_LCTL | MOD_LSFT), kc) // Control + Shift e.g. for gnome-terminal
-#define MEH_T(kc) MT((MOD_LCTL | MOD_LSFT | MOD_LALT), kc) // Meh is a less hyper version of the Hyper key -- doesn't include Win or Cmd, so just alt+shift+ctrl
-#define LCAG_T(kc) MT((MOD_LCTL | MOD_LALT | MOD_LGUI), kc) // Left control alt and gui
-#define RCAG_T(kc) MT((MOD_RCTL | MOD_RALT | MOD_RGUI), kc) // Right control alt and gui
-#define ALL_T(kc) MT((MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI), kc) // see http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/
-#define SGUI_T(kc) MT((MOD_LGUI | MOD_LSFT), kc)
+#define C_S_T(kc)  MT(MOD_LCTL | MOD_LSFT, kc) // Control + Shift e.g. for gnome-terminal
+#define MEH_T(kc)  MT(MOD_LCTL | MOD_LSFT | MOD_LALT, kc) // Meh is a less hyper version of the Hyper key -- doesn't include Win or Cmd, so just alt+shift+ctrl
+#define LCAG_T(kc) MT(MOD_LCTL | MOD_LALT | MOD_LGUI, kc) // Left control alt and gui
+#define RCAG_T(kc) MT(MOD_RCTL | MOD_RALT | MOD_RGUI, kc) // Right control alt and gui
+#define ALL_T(kc)  MT(MOD_LCTL | MOD_LSFT | MOD_LALT | MOD_LGUI, kc) // see http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/
+#define SGUI_T(kc) MT(MOD_LGUI | MOD_LSFT, kc)
 #define SCMD_T(kc) SGUI_T(kc)
 #define SWIN_T(kc) SGUI_T(kc)
-#define LCA_T(kc) MT((MOD_LCTL | MOD_LALT), kc) // Left control and left alt
+#define LCA_T(kc)  MT(MOD_LCTL | MOD_LALT, kc) // Left control and left alt
 
 // Dedicated keycode versions for Hyper and Meh, if you want to use them as standalone keys rather than mod-tap
 #define KC_HYPR HYPR(KC_NO)
@@ -670,22 +682,31 @@ enum quantum_keycodes {
     // For sending unicode codes.
     // You may not send codes over 7FFF -- this supports most of UTF8.
     // To have a key that sends out Œ, go UC(0x0152)
-    #define UNICODE(n) (n | QK_UNICODE)
+    #define UNICODE(n) (QK_UNICODE | (n))
     #define UC(n) UNICODE(n)
 #endif
 
 #ifdef UNICODEMAP_ENABLE
-    #define X(n) (n | QK_UNICODE_MAP)
+    #define X(n) (QK_UNICODE_MAP | (n))
 #endif
 
+#define UC_MOD  UNICODE_MODE_FORWARD
+#define UC_RMOD UNICODE_MODE_REVERSE
+
+#define UC_M_OS UNICODE_MODE_OSX
+#define UC_M_LN UNICODE_MODE_LNX
+#define UC_M_WI UNICODE_MODE_WIN
+#define UC_M_BS UNICODE_MODE_BSD
+#define UC_M_WC UNICODE_MODE_WINC
+
 #ifdef SWAP_HANDS_ENABLE
-  #define SH_T(key)  (QK_SWAP_HANDS | key)
-  #define SH_TG      (QK_SWAP_HANDS | OP_SH_TOGGLE)
-  #define SH_TT      (QK_SWAP_HANDS | OP_SH_TAP_TOGGLE)
-  #define SH_MON     (QK_SWAP_HANDS | OP_SH_ON_OFF)
-  #define SH_MOFF    (QK_SWAP_HANDS | OP_SH_OFF_ON)
-  #define SH_ON      (QK_SWAP_HANDS | OP_SH_ON)
-  #define SH_OFF     (QK_SWAP_HANDS | OP_SH_OFF)
+  #define SH_T(kc) (QK_SWAP_HANDS | (kc))
+  #define SH_TG    (QK_SWAP_HANDS | OP_SH_TOGGLE)
+  #define SH_TT    (QK_SWAP_HANDS | OP_SH_TAP_TOGGLE)
+  #define SH_MON   (QK_SWAP_HANDS | OP_SH_ON_OFF)
+  #define SH_MOFF  (QK_SWAP_HANDS | OP_SH_OFF_ON)
+  #define SH_ON    (QK_SWAP_HANDS | OP_SH_ON)
+  #define SH_OFF   (QK_SWAP_HANDS | OP_SH_OFF)
 #endif
 
 #endif // QUANTUM_KEYCODES_H
diff --git a/quantum/rgb_matrix.c b/quantum/rgb_matrix.c
index 197bc1ac5e..2ed36304dc 100644
--- a/quantum/rgb_matrix.c
+++ b/quantum/rgb_matrix.c
@@ -18,10 +18,10 @@
 
 
 #include "rgb_matrix.h"
-#include "i2c_master.h"
 #include "progmem.h"
 #include "config.h"
 #include "eeprom.h"
+#include <string.h>
 #include <math.h>
 
 rgb_config_t rgb_matrix_config;
@@ -50,6 +50,15 @@ rgb_config_t rgb_matrix_config;
     #define RGB_MATRIX_MAXIMUM_BRIGHTNESS 255
 #endif
 
+#ifndef RGB_DIGITAL_RAIN_DROPS
+    // lower the number for denser effect/wider keyboard
+    #define RGB_DIGITAL_RAIN_DROPS 24
+#endif
+
+#if !defined(DISABLE_RGB_MATRIX_RAINDROPS) || !defined(DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS) || !defined(DISABLE_RGB_MATRIX_DIGITAL_RAIN)
+    #define TRACK_PREVIOUS_EFFECT
+#endif
+
 bool g_suspend_state = false;
 
 // Global tick at 20 Hz
@@ -74,7 +83,12 @@ void eeconfig_update_rgb_matrix(uint32_t val) {
 void eeconfig_update_rgb_matrix_default(void) {
   dprintf("eeconfig_update_rgb_matrix_default\n");
   rgb_matrix_config.enable = 1;
+#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
   rgb_matrix_config.mode = RGB_MATRIX_CYCLE_LEFT_RIGHT;
+#else
+  // fallback to solid colors if RGB_MATRIX_CYCLE_LEFT_RIGHT is disabled in userspace
+  rgb_matrix_config.mode = RGB_MATRIX_SOLID_COLOR;
+#endif
   rgb_matrix_config.hue = 0;
   rgb_matrix_config.sat = 255;
   rgb_matrix_config.val = RGB_MATRIX_MAXIMUM_BRIGHTNESS;
@@ -111,29 +125,15 @@ void map_row_column_to_led( uint8_t row, uint8_t column, uint8_t *led_i, uint8_t
 }
 
 void rgb_matrix_update_pwm_buffers(void) {
-#ifdef IS31FL3731
-    IS31FL3731_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
-    IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
-#elif defined(IS31FL3733)
-    IS31FL3733_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
-    IS31FL3733_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
-#endif
+    rgb_matrix_driver.flush();
 }
 
 void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue ) {
-#ifdef IS31FL3731
-    IS31FL3731_set_color( index, red, green, blue );
-#elif defined(IS31FL3733)
-    IS31FL3733_set_color( index, red, green, blue );
-#endif
+    rgb_matrix_driver.set_color(index, red, green, blue);
 }
 
 void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue ) {
-#ifdef IS31FL3731
-    IS31FL3731_set_color_all( red, green, blue );
-#elif defined(IS31FL3733)
-    IS31FL3733_set_color_all( red, green, blue );
-#endif
+    rgb_matrix_driver.set_color_all(red, green, blue);
 }
 
 bool process_rgb_matrix(uint16_t keycode, keyrecord_t *record) {
@@ -196,47 +196,6 @@ void rgb_matrix_test(void) {
     }
 }
 
-// This tests the LEDs
-// Note that it will change the LED control registers
-// in the LED drivers, and leave them in an invalid
-// state for other backlight effects.
-// ONLY USE THIS FOR TESTING LEDS!
-void rgb_matrix_single_LED_test(void) {
-    static uint8_t color = 0; // 0,1,2 for R,G,B
-    static uint8_t row = 0;
-    static uint8_t column = 0;
-
-    static uint8_t tick = 0;
-    tick++;
-
-    if ( tick > 2 )
-    {
-        tick = 0;
-        column++;
-    }
-    if ( column > MATRIX_COLS )
-    {
-        column = 0;
-        row++;
-    }
-    if ( row > MATRIX_ROWS )
-    {
-        row = 0;
-        color++;
-    }
-    if ( color > 2 )
-    {
-        color = 0;
-    }
-
-    uint8_t led[8], led_count;
-    map_row_column_to_led(row,column,led,&led_count);
-    for(uint8_t i = 0; i < led_count; i++) {
-        rgb_matrix_set_color_all( 40, 40, 40 );
-        rgb_matrix_test_led( led[i], color==0, color==1, color==2 );
-    }
-}
-
 // All LEDs off
 void rgb_matrix_all_off(void) {
     rgb_matrix_set_color_all( 0, 0, 0 );
@@ -438,10 +397,12 @@ void rgb_matrix_cycle_up_down(void) {
 void rgb_matrix_dual_beacon(void) {
     HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
     RGB rgb;
-    rgb_led led;
+    Point point;
+    double cos_value = cos(g_tick * PI / 128) / 32;
+    double sin_value =  sin(g_tick * PI / 128) / 112;
     for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        led = g_rgb_leds[i];
-        hsv.h = ((led.point.y - 32.0)* cos(g_tick * PI / 128) / 32 + (led.point.x - 112.0) * sin(g_tick * PI / 128) / (112)) * (180) + rgb_matrix_config.hue;
+        point = g_rgb_leds[i].point;
+        hsv.h = ((point.y - 32.0)* cos_value + (point.x - 112.0) * sin_value) * (180) + rgb_matrix_config.hue;
         rgb = hsv_to_rgb( hsv );
         rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
     }
@@ -450,10 +411,12 @@ void rgb_matrix_dual_beacon(void) {
 void rgb_matrix_rainbow_beacon(void) {
     HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
     RGB rgb;
-    rgb_led led;
+    Point point;
+    double cos_value = cos(g_tick * PI / 128);
+    double sin_value =  sin(g_tick * PI / 128);
     for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        led = g_rgb_leds[i];
-        hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (led.point.y - 32.0)* cos(g_tick * PI / 128) + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (led.point.x - 112.0) * sin(g_tick * PI / 128) + rgb_matrix_config.hue;
+        point = g_rgb_leds[i].point;
+        hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.y - 32.0)* cos_value + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.x - 112.0) * sin_value + rgb_matrix_config.hue;
         rgb = hsv_to_rgb( hsv );
         rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
     }
@@ -462,10 +425,12 @@ void rgb_matrix_rainbow_beacon(void) {
 void rgb_matrix_rainbow_pinwheels(void) {
     HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
     RGB rgb;
-    rgb_led led;
+    Point point;
+    double cos_value = cos(g_tick * PI / 128);
+    double sin_value =  sin(g_tick * PI / 128);
     for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        led = g_rgb_leds[i];
-        hsv.h = (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (led.point.y - 32.0)* cos(g_tick * PI / 128) + (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (66 - abs(led.point.x - 112.0)) * sin(g_tick * PI / 128) + rgb_matrix_config.hue;
+        point = g_rgb_leds[i].point;
+        hsv.h = (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.y - 32.0)* cos_value + (2 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (66 - abs(point.x - 112.0)) * sin_value + rgb_matrix_config.hue;
         rgb = hsv_to_rgb( hsv );
         rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
     }
@@ -474,12 +439,14 @@ void rgb_matrix_rainbow_pinwheels(void) {
 void rgb_matrix_rainbow_moving_chevron(void) {
     HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
     RGB rgb;
-    rgb_led led;
+    Point point;
+    uint8_t r = 128;
+    double cos_value = cos(r * PI / 128);
+    double sin_value =  sin(r * PI / 128);
+    double multiplier = (g_tick / 256.0 * 224);
     for (uint8_t i = 0; i < DRIVER_LED_TOTAL; i++) {
-        led = g_rgb_leds[i];
-        // uint8_t r = g_tick;
-        uint8_t r = 128;
-        hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * abs(led.point.y - 32.0)* sin(r * PI / 128) + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (led.point.x - (g_tick / 256.0 * 224)) * cos(r * PI / 128) + rgb_matrix_config.hue;
+        point = g_rgb_leds[i].point;
+        hsv.h = (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * abs(point.y - 32.0)* sin_value + (1.5 * (rgb_matrix_config.speed == 0 ? 1 : rgb_matrix_config.speed)) * (point.x - multiplier) * cos_value + rgb_matrix_config.hue;
         rgb = hsv_to_rgb( hsv );
         rgb_matrix_set_color( i, rgb.r, rgb.g, rgb.b );
     }
@@ -510,6 +477,68 @@ void rgb_matrix_jellybean_raindrops( bool initialize ) {
     }
 }
 
+void rgb_matrix_digital_rain( const bool initialize ) {
+    // algorithm ported from https://github.com/tremby/Kaleidoscope-LEDEffect-DigitalRain
+    const uint8_t drop_ticks           = 28;
+    const uint8_t pure_green_intensity = 0xd0;
+    const uint8_t max_brightness_boost = 0xc0;
+    const uint8_t max_intensity        = 0xff;
+
+    static uint8_t map[MATRIX_COLS][MATRIX_ROWS] = {{0}};
+    static uint8_t drop = 0;
+
+    if (initialize) {
+        rgb_matrix_set_color_all(0, 0, 0);
+        memset(map, 0, sizeof map);
+        drop = 0;
+    }
+    for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+        for (uint8_t row = 0; row < MATRIX_ROWS; row++) {
+            if (row == 0 && drop == 0 && rand() < RAND_MAX / RGB_DIGITAL_RAIN_DROPS) {
+                // top row, pixels have just fallen and we're
+                // making a new rain drop in this column
+                map[col][row] = max_intensity;
+            }
+            else if (map[col][row] > 0 && map[col][row] < max_intensity) {
+                // neither fully bright nor dark, decay it
+                map[col][row]--;
+            }
+            // set the pixel colour
+            uint8_t led, led_count;
+            map_row_column_to_led(row, col, &led, &led_count);
+
+            if (map[col][row] > pure_green_intensity) {
+                const uint8_t boost = (uint8_t) ((uint16_t) max_brightness_boost
+                        * (map[col][row] - pure_green_intensity) / (max_intensity - pure_green_intensity));
+                rgb_matrix_set_color(led, boost, max_intensity, boost);
+            }
+            else {
+                const uint8_t green = (uint8_t) ((uint16_t) max_intensity * map[col][row] / pure_green_intensity);
+                rgb_matrix_set_color(led, 0, green, 0);
+            }
+        }
+    }
+    if (++drop > drop_ticks) {
+        // reset drop timer
+        drop = 0;
+        for (uint8_t row = MATRIX_ROWS - 1; row > 0; row--) {
+            for (uint8_t col = 0; col < MATRIX_COLS; col++) {
+                // if ths is on the bottom row and bright allow decay
+                if (row == MATRIX_ROWS - 1 && map[col][row] == max_intensity) {
+                    map[col][row]--;
+                }
+                // check if the pixel above is bright
+                if (map[col][row - 1] == max_intensity) {
+                    // allow old bright pixel to decay
+                    map[col][row - 1]--;
+                    // make this pixel bright
+                    map[col][row] = max_intensity;
+                }
+            }
+        }
+    }
+}
+
 void rgb_matrix_multisplash(void) {
     // if (g_any_key_hit < 0xFF) {
         HSV hsv = { .h = rgb_matrix_config.hue, .s = rgb_matrix_config.sat, .v = rgb_matrix_config.val };
@@ -598,11 +627,16 @@ void rgb_matrix_custom(void) {
 }
 
 void rgb_matrix_task(void) {
-    static uint8_t toggle_enable_last = 255;
+  #ifdef TRACK_PREVIOUS_EFFECT
+      static uint8_t toggle_enable_last = 255;
+  #endif
 	if (!rgb_matrix_config.enable) {
-    	rgb_matrix_all_off();
-        toggle_enable_last = rgb_matrix_config.enable;
-    	return;
+     rgb_matrix_all_off();
+     rgb_matrix_indicators();
+     #ifdef TRACK_PREVIOUS_EFFECT
+         toggle_enable_last = rgb_matrix_config.enable;
+     #endif
+     return;
     }
     // delay 1 second before driving LEDs or doing anything else
     static uint8_t startup_tick = 0;
@@ -637,13 +671,16 @@ void rgb_matrix_task(void) {
             (RGB_DISABLE_AFTER_TIMEOUT > 0 && g_any_key_hit > RGB_DISABLE_AFTER_TIMEOUT * 60 * 20));
     uint8_t effect = suspend_backlight ? 0 : rgb_matrix_config.mode;
 
-    // Keep track of the effect used last time,
-    // detect change in effect, so each effect can
-    // have an optional initialization.
-    static uint8_t effect_last = 255;
-    bool initialize = (effect != effect_last) || (rgb_matrix_config.enable != toggle_enable_last);
-    effect_last = effect;
-    toggle_enable_last = rgb_matrix_config.enable;
+    #ifdef TRACK_PREVIOUS_EFFECT
+        // Keep track of the effect used last time,
+        // detect change in effect, so each effect can
+        // have an optional initialization.
+
+        static uint8_t effect_last = 255;
+        bool initialize = (effect != effect_last) || (rgb_matrix_config.enable != toggle_enable_last);
+        effect_last = effect;
+        toggle_enable_last = rgb_matrix_config.enable;
+    #endif
 
     // this gets ticked at 20 Hz.
     // each effect can opt to do calculations
@@ -652,56 +689,93 @@ void rgb_matrix_task(void) {
         case RGB_MATRIX_SOLID_COLOR:
             rgb_matrix_solid_color();
             break;
-        case RGB_MATRIX_ALPHAS_MODS:
-            rgb_matrix_alphas_mods();
-            break;
-        case RGB_MATRIX_DUAL_BEACON:
-            rgb_matrix_dual_beacon();
-            break;
-        case RGB_MATRIX_GRADIENT_UP_DOWN:
-            rgb_matrix_gradient_up_down();
-            break;
-        case RGB_MATRIX_RAINDROPS:
-            rgb_matrix_raindrops( initialize );
-            break;
-        case RGB_MATRIX_CYCLE_ALL:
-            rgb_matrix_cycle_all();
-            break;
-        case RGB_MATRIX_CYCLE_LEFT_RIGHT:
-            rgb_matrix_cycle_left_right();
-            break;
-        case RGB_MATRIX_CYCLE_UP_DOWN:
-            rgb_matrix_cycle_up_down();
-            break;
-        case RGB_MATRIX_RAINBOW_BEACON:
-            rgb_matrix_rainbow_beacon();
-            break;
-        case RGB_MATRIX_RAINBOW_PINWHEELS:
-            rgb_matrix_rainbow_pinwheels();
-            break;
-        case RGB_MATRIX_RAINBOW_MOVING_CHEVRON:
-            rgb_matrix_rainbow_moving_chevron();
-            break;
-        case RGB_MATRIX_JELLYBEAN_RAINDROPS:
-            rgb_matrix_jellybean_raindrops( initialize );
-            break;
-        #ifdef RGB_MATRIX_KEYPRESSES
-            case RGB_MATRIX_SOLID_REACTIVE:
-                rgb_matrix_solid_reactive();
+        #ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
+            case RGB_MATRIX_ALPHAS_MODS:
+                rgb_matrix_alphas_mods();
+                break;
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
+            case RGB_MATRIX_DUAL_BEACON:
+                rgb_matrix_dual_beacon();
                 break;
-            case RGB_MATRIX_SPLASH:
-                rgb_matrix_splash();
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
+            case RGB_MATRIX_GRADIENT_UP_DOWN:
+                rgb_matrix_gradient_up_down();
+                break;
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_RAINDROPS
+            case RGB_MATRIX_RAINDROPS:
+                rgb_matrix_raindrops( initialize );
+                break;
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
+            case RGB_MATRIX_CYCLE_ALL:
+                rgb_matrix_cycle_all();
                 break;
-            case RGB_MATRIX_MULTISPLASH:
-                rgb_matrix_multisplash();
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
+            case RGB_MATRIX_CYCLE_LEFT_RIGHT:
+                rgb_matrix_cycle_left_right();
                 break;
-            case RGB_MATRIX_SOLID_SPLASH:
-                rgb_matrix_solid_splash();
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
+            case RGB_MATRIX_CYCLE_UP_DOWN:
+                rgb_matrix_cycle_up_down();
                 break;
-            case RGB_MATRIX_SOLID_MULTISPLASH:
-                rgb_matrix_solid_multisplash();
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
+            case RGB_MATRIX_RAINBOW_BEACON:
+                rgb_matrix_rainbow_beacon();
                 break;
         #endif
+        #ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
+            case RGB_MATRIX_RAINBOW_PINWHEELS:
+                rgb_matrix_rainbow_pinwheels();
+                break;
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
+            case RGB_MATRIX_RAINBOW_MOVING_CHEVRON:
+                rgb_matrix_rainbow_moving_chevron();
+                break;
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
+            case RGB_MATRIX_JELLYBEAN_RAINDROPS:
+                rgb_matrix_jellybean_raindrops( initialize );
+                break;
+        #endif
+        #ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
+            case RGB_MATRIX_DIGITAL_RAIN:
+                rgb_matrix_digital_rain( initialize );
+                break;
+        #endif
+        #ifdef RGB_MATRIX_KEYPRESSES
+            #ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+                case RGB_MATRIX_SOLID_REACTIVE:
+                    rgb_matrix_solid_reactive();
+                    break;
+            #endif
+            #ifndef DISABLE_RGB_MATRIX_SPLASH
+                case RGB_MATRIX_SPLASH:
+                    rgb_matrix_splash();
+                    break;
+            #endif
+            #ifndef DISABLE_RGB_MATRIX_MULTISPLASH
+                case RGB_MATRIX_MULTISPLASH:
+                    rgb_matrix_multisplash();
+                    break;
+            #endif
+            #ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
+                case RGB_MATRIX_SOLID_SPLASH:
+                    rgb_matrix_solid_splash();
+                    break;
+            #endif
+            #ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+                case RGB_MATRIX_SOLID_MULTISPLASH:
+                    rgb_matrix_solid_multisplash();
+                    break;
+            #endif
+        #endif
         default:
             rgb_matrix_custom();
             break;
@@ -743,7 +817,7 @@ void rgb_matrix_indicators_user(void) {}
 // }
 
 void rgb_matrix_init(void) {
-  rgb_matrix_setup_drivers();
+  rgb_matrix_driver.init();
 
   // TODO: put the 1 second startup delay here?
 
@@ -767,41 +841,14 @@ void rgb_matrix_init(void) {
   eeconfig_debug_rgb_matrix(); // display current eeprom values
 }
 
-void rgb_matrix_setup_drivers(void) {
-  // Initialize TWI
-  i2c_init();
-#ifdef IS31FL3731
-  IS31FL3731_init( DRIVER_ADDR_1 );
-  IS31FL3731_init( DRIVER_ADDR_2 );
-#elif defined (IS31FL3733)
-  IS31FL3733_init( DRIVER_ADDR_1 );
-#endif
-
-  for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) {
-    bool enabled = true;
-    // This only caches it for later
-#ifdef IS31FL3731
-    IS31FL3731_set_led_control_register( index, enabled, enabled, enabled );
-#elif defined (IS31FL3733)
-    IS31FL3733_set_led_control_register( index, enabled, enabled, enabled );
-#endif
-  }
-  // This actually updates the LED drivers
-#ifdef IS31FL3731
-  IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
-#elif defined (IS31FL3733)
-  IS31FL3733_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
-#endif
-}
-
 // Deals with the messy details of incrementing an integer
-uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
+static uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
     int16_t new_value = value;
     new_value += step;
     return MIN( MAX( new_value, min ), max );
 }
 
-uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
+static uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
     int16_t new_value = value;
     new_value -= step;
     return MIN( MAX( new_value, min ), max );
@@ -836,96 +883,109 @@ uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
 //     }
 // }
 
-void rgb_matrix_test_led( uint8_t index, bool red, bool green, bool blue ) {
-    for ( int i=0; i<DRIVER_LED_TOTAL; i++ )
-    {
-        if ( i == index )
-        {
-#ifdef IS31FL3731
-            IS31FL3731_set_led_control_register( i, red, green, blue );
-#elif defined (IS31FL3733)
-            IS31FL3733_set_led_control_register( i, red, green, blue );
-#endif
-        }
-        else
-        {
-#ifdef IS31FL3731
-            IS31FL3731_set_led_control_register( i, false, false, false );
-#elif defined (IS31FL3733)
-            IS31FL3733_set_led_control_register( i, false, false, false );
-#endif
-        }
-    }
-}
-
 uint32_t rgb_matrix_get_tick(void) {
     return g_tick;
 }
 
-void rgblight_toggle(void) {
+void rgb_matrix_toggle(void) {
 	rgb_matrix_config.enable ^= 1;
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_step(void) {
+void rgb_matrix_enable(void) {
+	rgb_matrix_config.enable = 1;
+    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+}
+
+void rgb_matrix_enable_noeeprom(void) {
+	rgb_matrix_config.enable = 1;
+}
+
+void rgb_matrix_disable(void) {
+	rgb_matrix_config.enable = 0;
+    eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+}
+
+void rgb_matrix_disable_noeeprom(void) {
+	rgb_matrix_config.enable = 0;
+}
+
+void rgb_matrix_step(void) {
     rgb_matrix_config.mode++;
     if (rgb_matrix_config.mode >= RGB_MATRIX_EFFECT_MAX)
         rgb_matrix_config.mode = 1;
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_step_reverse(void) {
+void rgb_matrix_step_reverse(void) {
     rgb_matrix_config.mode--;
     if (rgb_matrix_config.mode < 1)
         rgb_matrix_config.mode = RGB_MATRIX_EFFECT_MAX - 1;
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_increase_hue(void) {
+void rgb_matrix_increase_hue(void) {
     rgb_matrix_config.hue = increment( rgb_matrix_config.hue, 8, 0, 255 );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_decrease_hue(void) {
+void rgb_matrix_decrease_hue(void) {
     rgb_matrix_config.hue = decrement( rgb_matrix_config.hue, 8, 0, 255 );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_increase_sat(void) {
+void rgb_matrix_increase_sat(void) {
     rgb_matrix_config.sat = increment( rgb_matrix_config.sat, 8, 0, 255 );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_decrease_sat(void) {
+void rgb_matrix_decrease_sat(void) {
     rgb_matrix_config.sat = decrement( rgb_matrix_config.sat, 8, 0, 255 );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_increase_val(void) {
+void rgb_matrix_increase_val(void) {
     rgb_matrix_config.val = increment( rgb_matrix_config.val, 8, 0, RGB_MATRIX_MAXIMUM_BRIGHTNESS );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_decrease_val(void) {
+void rgb_matrix_decrease_val(void) {
     rgb_matrix_config.val = decrement( rgb_matrix_config.val, 8, 0, RGB_MATRIX_MAXIMUM_BRIGHTNESS );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-void rgblight_increase_speed(void) {
+void rgb_matrix_increase_speed(void) {
     rgb_matrix_config.speed = increment( rgb_matrix_config.speed, 1, 0, 3 );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
 }
 
-void rgblight_decrease_speed(void) {
+void rgb_matrix_decrease_speed(void) {
     rgb_matrix_config.speed = decrement( rgb_matrix_config.speed, 1, 0, 3 );
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);//EECONFIG needs to be increased to support this
 }
 
-void rgblight_mode(uint8_t mode) {
+void rgb_matrix_mode(uint8_t mode) {
     rgb_matrix_config.mode = mode;
     eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
 }
 
-uint32_t rgblight_get_mode(void) {
+void rgb_matrix_mode_noeeprom(uint8_t mode) {
+    rgb_matrix_config.mode = mode;
+}
+
+uint8_t rgb_matrix_get_mode(void) {
     return rgb_matrix_config.mode;
 }
+
+void rgb_matrix_sethsv(uint16_t hue, uint8_t sat, uint8_t val) {
+  rgb_matrix_config.hue = hue;
+  rgb_matrix_config.sat = sat;
+  rgb_matrix_config.val = val;
+  eeconfig_update_rgb_matrix(rgb_matrix_config.raw);
+}
+
+void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val) {
+  rgb_matrix_config.hue = hue;
+  rgb_matrix_config.sat = sat;
+  rgb_matrix_config.val = val;
+}
diff --git a/quantum/rgb_matrix.h b/quantum/rgb_matrix.h
index b91c9fba59..e43532d11e 100644
--- a/quantum/rgb_matrix.h
+++ b/quantum/rgb_matrix.h
@@ -70,28 +70,64 @@ typedef union {
 
 enum rgb_matrix_effects {
 	RGB_MATRIX_SOLID_COLOR = 1,
+#ifndef DISABLE_RGB_MATRIX_ALPHAS_MODS
     RGB_MATRIX_ALPHAS_MODS,
+#endif
+#ifndef DISABLE_RGB_MATRIX_DUAL_BEACON
     RGB_MATRIX_DUAL_BEACON,
+#endif
+#ifndef DISABLE_RGB_MATRIX_GRADIENT_UP_DOWN
     RGB_MATRIX_GRADIENT_UP_DOWN,
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINDROPS
     RGB_MATRIX_RAINDROPS,
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_ALL
     RGB_MATRIX_CYCLE_ALL,
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_LEFT_RIGHT
     RGB_MATRIX_CYCLE_LEFT_RIGHT,
+#endif
+#ifndef DISABLE_RGB_MATRIX_CYCLE_UP_DOWN
     RGB_MATRIX_CYCLE_UP_DOWN,
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_BEACON
     RGB_MATRIX_RAINBOW_BEACON,
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_PINWHEELS
     RGB_MATRIX_RAINBOW_PINWHEELS,
+#endif
+#ifndef DISABLE_RGB_MATRIX_RAINBOW_MOVING_CHEVRON
     RGB_MATRIX_RAINBOW_MOVING_CHEVRON,
+#endif
+#ifndef DISABLE_RGB_MATRIX_JELLYBEAN_RAINDROPS
     RGB_MATRIX_JELLYBEAN_RAINDROPS,
+#endif
+#ifndef DISABLE_RGB_MATRIX_DIGITAL_RAIN
+    RGB_MATRIX_DIGITAL_RAIN,
+#endif
 #ifdef RGB_MATRIX_KEYPRESSES
-    RGB_MATRIX_SOLID_REACTIVE,
-    RGB_MATRIX_SPLASH,
-    RGB_MATRIX_MULTISPLASH,
-    RGB_MATRIX_SOLID_SPLASH,
-    RGB_MATRIX_SOLID_MULTISPLASH,
+   #ifndef DISABLE_RGB_MATRIX_SOLID_REACTIVE
+       RGB_MATRIX_SOLID_REACTIVE,
+   #endif
+   #ifndef DISABLE_RGB_MATRIX_SPLASH
+       RGB_MATRIX_SPLASH,
+   #endif
+   #ifndef DISABLE_RGB_MATRIX_MULTISPLASH
+       RGB_MATRIX_MULTISPLASH,
+   #endif
+   #ifndef DISABLE_RGB_MATRIX_SOLID_SPLASH
+       RGB_MATRIX_SOLID_SPLASH,
+   #endif
+   #ifndef DISABLE_RGB_MATRIX_SOLID_MULTISPLASH
+       RGB_MATRIX_SOLID_MULTISPLASH,
+   #endif
 #endif
     RGB_MATRIX_EFFECT_MAX
 };
 
 void rgb_matrix_set_color( int index, uint8_t red, uint8_t green, uint8_t blue );
+void rgb_matrix_set_color_all( uint8_t red, uint8_t green, uint8_t blue );
 
 // This runs after another backlight effect and replaces
 // colors already set
@@ -99,8 +135,6 @@ void rgb_matrix_indicators(void);
 void rgb_matrix_indicators_kb(void);
 void rgb_matrix_indicators_user(void);
 
-void rgb_matrix_single_LED_test(void);
-
 void rgb_matrix_init(void);
 void rgb_matrix_setup_drivers(void);
 
@@ -125,21 +159,65 @@ void rgb_matrix_decrease(void);
 // void backlight_get_key_color( uint8_t led, HSV *hsv );
 // void backlight_set_key_color( uint8_t row, uint8_t column, HSV hsv );
 
-void rgb_matrix_test_led( uint8_t index, bool red, bool green, bool blue );
 uint32_t rgb_matrix_get_tick(void);
 
-void rgblight_toggle(void);
-void rgblight_step(void);
-void rgblight_step_reverse(void);
-void rgblight_increase_hue(void);
-void rgblight_decrease_hue(void);
-void rgblight_increase_sat(void);
-void rgblight_decrease_sat(void);
-void rgblight_increase_val(void);
-void rgblight_decrease_val(void);
-void rgblight_increase_speed(void);
-void rgblight_decrease_speed(void);
-void rgblight_mode(uint8_t mode);
-uint32_t rgblight_get_mode(void);
+void rgb_matrix_toggle(void);
+void rgb_matrix_enable(void);
+void rgb_matrix_enable_noeeprom(void);
+void rgb_matrix_disable(void);
+void rgb_matrix_disable_noeeprom(void);
+void rgb_matrix_step(void);
+void rgb_matrix_sethsv(uint16_t hue, uint8_t sat, uint8_t val);
+void rgb_matrix_sethsv_noeeprom(uint16_t hue, uint8_t sat, uint8_t val);
+void rgb_matrix_step_reverse(void);
+void rgb_matrix_increase_hue(void);
+void rgb_matrix_decrease_hue(void);
+void rgb_matrix_increase_sat(void);
+void rgb_matrix_decrease_sat(void);
+void rgb_matrix_increase_val(void);
+void rgb_matrix_decrease_val(void);
+void rgb_matrix_increase_speed(void);
+void rgb_matrix_decrease_speed(void);
+void rgb_matrix_mode(uint8_t mode);
+void rgb_matrix_mode_noeeprom(uint8_t mode);
+uint8_t rgb_matrix_get_mode(void);
+
+#ifndef RGBLIGHT_ENABLE
+#define rgblight_toggle() rgb_matrix_toggle()
+#define rgblight_enable() rgb_matrix_enable()
+#define rgblight_enable_noeeprom() rgb_matrix_enable_noeeprom()
+#define rgblight_disable() rgb_matrix_disable()
+#define rgblight_disable_noeeprom() rgb_matrix_disable_noeeprom()
+#define rgblight_step() rgb_matrix_step()
+#define rgblight_sethsv(hue, sat, val) rgb_matrix_sethsv(hue, sat, val)
+#define rgblight_sethsv_noeeprom(hue, sat, val) rgb_matrix_sethsv_noeeprom(hue, sat, val)
+#define rgblight_step_reverse() rgb_matrix_step_reverse()
+#define rgblight_increase_hue() rgb_matrix_increase_hue()
+#define rgblight_decrease_hue() rgb_matrix_decrease_hue()
+#define rgblight_increase_sat() rgb_matrix_increase_sat()
+#define rgblight_decrease_sat() rgb_matrix_decrease_sat()
+#define rgblight_increase_val() rgb_matrix_increase_val()
+#define rgblight_decrease_val() rgb_matrix_decrease_val()
+#define rgblight_increase_speed() rgb_matrix_increase_speed()
+#define rgblight_decrease_speed() rgb_matrix_decrease_speed()
+#define rgblight_mode(mode) rgb_matrix_mode(mode)
+#define rgblight_mode_noeeprom(mode) rgb_matrix_mode_noeeprom(mode)
+#define rgblight_get_mode() rgb_matrix_get_mode()
+
+#endif
+
+typedef struct {
+    /* Perform any initialisation required for the other driver functions to work. */
+    void (*init)(void);
+
+    /* Set the colour of a single LED in the buffer. */
+    void (*set_color)(int index, uint8_t r, uint8_t g, uint8_t b);
+    /* Set the colour of all LEDS on the keyboard in the buffer. */
+    void (*set_color_all)(uint8_t r, uint8_t g, uint8_t b);
+    /* Flush any buffered changes to the hardware. */
+    void (*flush)(void);
+} rgb_matrix_driver_t;
+
+extern const rgb_matrix_driver_t rgb_matrix_driver;
 
 #endif
diff --git a/quantum/rgb_matrix_drivers.c b/quantum/rgb_matrix_drivers.c
new file mode 100644
index 0000000000..70b80293dd
--- /dev/null
+++ b/quantum/rgb_matrix_drivers.c
@@ -0,0 +1,82 @@
+/* Copyright 2018 James Laird-Wah
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "rgb_matrix.h"
+
+/* Each driver needs to define the struct
+ *    const rgb_matrix_driver_t rgb_matrix_driver;
+ * All members must be provided.
+ * Keyboard custom drivers can define this in their own files, it should only
+ * be here if shared between boards.
+ */
+
+#if defined(IS31FL3731) || defined(IS31FL3733)
+
+#include "i2c_master.h"
+
+static void init( void )
+{
+    i2c_init();
+#ifdef IS31FL3731
+    IS31FL3731_init( DRIVER_ADDR_1 );
+    IS31FL3731_init( DRIVER_ADDR_2 );
+#else
+    IS31FL3733_init( DRIVER_ADDR_1 );
+#endif
+    for ( int index = 0; index < DRIVER_LED_TOTAL; index++ ) {
+        bool enabled = true;
+        // This only caches it for later
+#ifdef IS31FL3731
+        IS31FL3731_set_led_control_register( index, enabled, enabled, enabled );
+#else
+        IS31FL3733_set_led_control_register( index, enabled, enabled, enabled );
+#endif
+    }
+    // This actually updates the LED drivers
+#ifdef IS31FL3731
+    IS31FL3731_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
+#else
+    IS31FL3733_update_led_control_registers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
+#endif
+}
+
+#ifdef IS31FL3731
+static void flush( void )
+{
+    IS31FL3731_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
+}
+
+const rgb_matrix_driver_t rgb_matrix_driver = {
+    .init = init,
+    .flush = flush,
+    .set_color = IS31FL3731_set_color,
+    .set_color_all = IS31FL3731_set_color_all,
+};
+#else
+static void flush( void )
+{
+    IS31FL3733_update_pwm_buffers( DRIVER_ADDR_1, DRIVER_ADDR_2 );
+}
+
+const rgb_matrix_driver_t rgb_matrix_driver = {
+    .init = init,
+    .flush = flush,
+    .set_color = IS31FL3733_set_color,
+    .set_color_all = IS31FL3733_set_color_all,
+};
+#endif
+
+#endif
diff --git a/quantum/rgblight.c b/quantum/rgblight.c
index 4919ae4abf..23420ddd87 100644
--- a/quantum/rgblight.c
+++ b/quantum/rgblight.c
@@ -14,6 +14,7 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include <math.h>
+#include <string.h>
 #ifdef __AVR__
   #include <avr/eeprom.h>
   #include <avr/interrupt.h>
@@ -29,23 +30,27 @@
 #define RGBLIGHT_LIMIT_VAL 255
 #endif
 
+#define _RGBM_SINGLE_STATIC(sym)   RGBLIGHT_MODE_ ## sym,
+#define _RGBM_SINGLE_DYNAMIC(sym)
+#define _RGBM_MULTI_STATIC(sym)    RGBLIGHT_MODE_ ## sym,
+#define _RGBM_MULTI_DYNAMIC(sym)
+#define _RGBM_TMP_STATIC(sym)      RGBLIGHT_MODE_ ## sym,
+#define _RGBM_TMP_DYNAMIC(sym)
+static uint8_t static_effect_table [] = {
+#include "rgblight.h"
+};
+
+static inline int is_static_effect(uint8_t mode) {
+    return memchr(static_effect_table, mode, sizeof(static_effect_table)) != NULL;
+}
+
 #define MIN(a,b) (((a)<(b))?(a):(b))
 #define MAX(a,b) (((a)>(b))?(a):(b))
 
-__attribute__ ((weak))
-const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};
-__attribute__ ((weak))
-const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30};
-__attribute__ ((weak))
-const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20};
-__attribute__ ((weak))
-const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};
-__attribute__ ((weak))
-const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};
+#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
 __attribute__ ((weak))
 const uint16_t RGBLED_GRADIENT_RANGES[] PROGMEM = {360, 240, 180, 120, 90};
-__attribute__ ((weak))
-const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024};
+#endif
 
 rgblight_config_t rgblight_config;
 
@@ -129,7 +134,7 @@ void eeconfig_update_rgblight(uint32_t val) {
 void eeconfig_update_rgblight_default(void) {
   //dprintf("eeconfig_update_rgblight_default\n");
   rgblight_config.enable = 1;
-  rgblight_config.mode = 1;
+  rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;
   rgblight_config.hue = 0;
   rgblight_config.sat = 255;
   rgblight_config.val = RGBLIGHT_LIMIT_VAL;
@@ -163,9 +168,9 @@ void rgblight_init(void) {
   }
   eeconfig_debug_rgblight(); // display current eeprom values
 
-  #ifdef RGBLIGHT_ANIMATIONS
+#ifdef RGBLIGHT_USE_TIMER
     rgblight_timer_init(); // setup the timer
-  #endif
+#endif
 
   if (rgblight_config.enable) {
     rgblight_mode_noeeprom(rgblight_config.mode);
@@ -178,9 +183,9 @@ void rgblight_update_dword(uint32_t dword) {
   if (rgblight_config.enable)
     rgblight_mode(rgblight_config.mode);
   else {
-    #ifdef RGBLIGHT_ANIMATIONS
+#ifdef RGBLIGHT_USE_TIMER
       rgblight_timer_disable();
-    #endif
+#endif
       rgblight_set();
   }
 }
@@ -195,29 +200,41 @@ void rgblight_increase(void) {
 void rgblight_decrease(void) {
   uint8_t mode = 0;
   // Mode will never be < 1. If it ever is, eeprom needs to be initialized.
-  if (rgblight_config.mode > 1) {
+  if (rgblight_config.mode > RGBLIGHT_MODE_STATIC_LIGHT) {
     mode = rgblight_config.mode - 1;
   }
   rgblight_mode(mode);
 }
-void rgblight_step(void) {
+void rgblight_step_helper(bool write_to_eeprom) {
   uint8_t mode = 0;
   mode = rgblight_config.mode + 1;
   if (mode > RGBLIGHT_MODES) {
     mode = 1;
   }
-  rgblight_mode(mode);
+  rgblight_mode_eeprom_helper(mode, write_to_eeprom);
 }
-void rgblight_step_reverse(void) {
+void rgblight_step_noeeprom(void) {
+  rgblight_step_helper(false);
+}
+void rgblight_step(void) {
+  rgblight_step_helper(true);
+}
+void rgblight_step_reverse_helper(bool write_to_eeprom) {
   uint8_t mode = 0;
   mode = rgblight_config.mode - 1;
   if (mode < 1) {
     mode = RGBLIGHT_MODES;
   }
-  rgblight_mode(mode);
+  rgblight_mode_eeprom_helper(mode, write_to_eeprom);
+}
+void rgblight_step_reverse_noeeprom(void) {
+  rgblight_step_reverse_helper(false);
+}
+void rgblight_step_reverse(void) {
+  rgblight_step_reverse_helper(true);
 }
 
-uint32_t rgblight_get_mode(void) {
+uint8_t rgblight_get_mode(void) {
   if (!rgblight_config.enable) {
     return false;
   }
@@ -229,8 +246,8 @@ void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
   if (!rgblight_config.enable) {
     return;
   }
-  if (mode < 1) {
-    rgblight_config.mode = 1;
+  if (mode < RGBLIGHT_MODE_STATIC_LIGHT) {
+    rgblight_config.mode = RGBLIGHT_MODE_STATIC_LIGHT;
   } else if (mode > RGBLIGHT_MODES) {
     rgblight_config.mode = RGBLIGHT_MODES;
   } else {
@@ -242,30 +259,14 @@ void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom) {
   } else {
     xprintf("rgblight mode [NOEEPROM]: %u\n", rgblight_config.mode);
   }
-  if (rgblight_config.mode == 1) {
-    #ifdef RGBLIGHT_ANIMATIONS
+  if( is_static_effect(rgblight_config.mode) ) {
+#ifdef RGBLIGHT_USE_TIMER
       rgblight_timer_disable();
-    #endif
-  } else if ((rgblight_config.mode >= 2 && rgblight_config.mode <= 24) ||
-	     rgblight_config.mode == 35 || rgblight_config.mode == 36) {
-    // MODE 2-5, breathing
-    // MODE 6-8, rainbow mood
-    // MODE 9-14, rainbow swirl
-    // MODE 15-20, snake
-    // MODE 21-23, knight
-    // MODE 24, xmas
-    // MODE 35  RGB test
-    // MODE 36, alterating
-
-    #ifdef RGBLIGHT_ANIMATIONS
+#endif
+  } else {
+#ifdef RGBLIGHT_USE_TIMER
       rgblight_timer_enable();
-    #endif
-  } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) {
-    // MODE 25-34, static gradient
-
-    #ifdef RGBLIGHT_ANIMATIONS
-      rgblight_timer_disable();
-    #endif
+#endif
   }
   rgblight_sethsv_noeeprom(rgblight_config.hue, rgblight_config.sat, rgblight_config.val);
 }
@@ -317,9 +318,9 @@ void rgblight_disable(void) {
   rgblight_config.enable = 0;
   eeconfig_update_rgblight(rgblight_config.raw);
   xprintf("rgblight disable [EEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
-  #ifdef RGBLIGHT_ANIMATIONS
-    rgblight_timer_disable();
-  #endif
+#ifdef RGBLIGHT_USE_TIMER
+      rgblight_timer_disable();
+#endif
   wait_ms(50);
   rgblight_set();
 }
@@ -327,76 +328,112 @@ void rgblight_disable(void) {
 void rgblight_disable_noeeprom(void) {
   rgblight_config.enable = 0;
   xprintf("rgblight disable [noEEPROM]: rgblight_config.enable = %u\n", rgblight_config.enable);
-  #ifdef RGBLIGHT_ANIMATIONS
+#ifdef RGBLIGHT_USE_TIMER
     rgblight_timer_disable();
-  #endif
+#endif
   _delay_ms(50);
   rgblight_set();
 }
 
 
 // Deals with the messy details of incrementing an integer
-uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
+static uint8_t increment( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
     int16_t new_value = value;
     new_value += step;
     return MIN( MAX( new_value, min ), max );
 }
 
-uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
+static uint8_t decrement( uint8_t value, uint8_t step, uint8_t min, uint8_t max ) {
     int16_t new_value = value;
     new_value -= step;
     return MIN( MAX( new_value, min ), max );
 }
 
-void rgblight_increase_hue(void) {
+void rgblight_increase_hue_helper(bool write_to_eeprom) {
   uint16_t hue;
   hue = (rgblight_config.hue+RGBLIGHT_HUE_STEP) % 360;
-  rgblight_sethsv(hue, rgblight_config.sat, rgblight_config.val);
+  rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);
 }
-void rgblight_decrease_hue(void) {
+void rgblight_increase_hue_noeeprom(void) {
+  rgblight_increase_hue_helper(false);
+}
+void rgblight_increase_hue(void) {
+  rgblight_increase_hue_helper(true);
+}
+void rgblight_decrease_hue_helper(bool write_to_eeprom) {
   uint16_t hue;
   if (rgblight_config.hue-RGBLIGHT_HUE_STEP < 0) {
     hue = (rgblight_config.hue + 360 - RGBLIGHT_HUE_STEP) % 360;
   } else {
     hue = (rgblight_config.hue - RGBLIGHT_HUE_STEP) % 360;
   }
-  rgblight_sethsv(hue, rgblight_config.sat, rgblight_config.val);
+  rgblight_sethsv_eeprom_helper(hue, rgblight_config.sat, rgblight_config.val, write_to_eeprom);
 }
-void rgblight_increase_sat(void) {
+void rgblight_decrease_hue_noeeprom(void) {
+  rgblight_decrease_hue_helper(false);
+}
+void rgblight_decrease_hue(void) {
+  rgblight_decrease_hue_helper(true);
+}
+void rgblight_increase_sat_helper(bool write_to_eeprom) {
   uint8_t sat;
   if (rgblight_config.sat + RGBLIGHT_SAT_STEP > 255) {
     sat = 255;
   } else {
     sat = rgblight_config.sat + RGBLIGHT_SAT_STEP;
   }
-  rgblight_sethsv(rgblight_config.hue, sat, rgblight_config.val);
+  rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);
 }
-void rgblight_decrease_sat(void) {
+void rgblight_increase_sat_noeeprom(void) {
+  rgblight_increase_sat_helper(false);
+}
+void rgblight_increase_sat(void) {
+  rgblight_increase_sat_helper(true);
+}
+void rgblight_decrease_sat_helper(bool write_to_eeprom) {
   uint8_t sat;
   if (rgblight_config.sat - RGBLIGHT_SAT_STEP < 0) {
     sat = 0;
   } else {
     sat = rgblight_config.sat - RGBLIGHT_SAT_STEP;
   }
-  rgblight_sethsv(rgblight_config.hue, sat, rgblight_config.val);
+  rgblight_sethsv_eeprom_helper(rgblight_config.hue, sat, rgblight_config.val, write_to_eeprom);
 }
-void rgblight_increase_val(void) {
+void rgblight_decrease_sat_noeeprom(void) {
+  rgblight_decrease_sat_helper(false);
+}
+void rgblight_decrease_sat(void) {
+  rgblight_decrease_sat_helper(true);
+}
+void rgblight_increase_val_helper(bool write_to_eeprom) {
   uint8_t val;
   if (rgblight_config.val + RGBLIGHT_VAL_STEP > RGBLIGHT_LIMIT_VAL) {
     val = RGBLIGHT_LIMIT_VAL;
   } else {
     val = rgblight_config.val + RGBLIGHT_VAL_STEP;
   }
-  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, val);
+  rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);
 }
-void rgblight_decrease_val(void) {
+void rgblight_increase_val_noeeprom(void) {
+  rgblight_increase_val_helper(false);
+}
+void rgblight_increase_val(void) {
+  rgblight_increase_val_helper(true);
+}
+void rgblight_decrease_val_helper(bool write_to_eeprom) {
   uint8_t val;
   if (rgblight_config.val - RGBLIGHT_VAL_STEP < 0) {
     val = 0;
   } else {
     val = rgblight_config.val - RGBLIGHT_VAL_STEP;
   }
-  rgblight_sethsv(rgblight_config.hue, rgblight_config.sat, val);
+  rgblight_sethsv_eeprom_helper(rgblight_config.hue, rgblight_config.sat, val, write_to_eeprom);
+}
+void rgblight_decrease_val_noeeprom(void) {
+  rgblight_decrease_val_helper(false);
+}
+void rgblight_decrease_val(void) {
+  rgblight_decrease_val_helper(true);
 }
 void rgblight_increase_speed(void) {
     rgblight_config.speed = increment( rgblight_config.speed, 1, 0, 3 );
@@ -419,24 +456,43 @@ void rgblight_sethsv_noeeprom_old(uint16_t hue, uint8_t sat, uint8_t val) {
 
 void rgblight_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom) {
   if (rgblight_config.enable) {
-    if (rgblight_config.mode == 1) {
+    if (rgblight_config.mode == RGBLIGHT_MODE_STATIC_LIGHT) {
       // same static color
       LED_TYPE tmp_led;
       sethsv(hue, sat, val, &tmp_led);
       rgblight_setrgb(tmp_led.r, tmp_led.g, tmp_led.b);
     } else {
       // all LEDs in same color
-      if (rgblight_config.mode >= 2 && rgblight_config.mode <= 5) {
+      if ( 1 == 0 ) { //dummy
+      }
+#ifdef RGBLIGHT_EFFECT_BREATHING
+      else if (rgblight_config.mode >= RGBLIGHT_MODE_BREATHING &&
+          rgblight_config.mode <= RGBLIGHT_MODE_BREATHING_end) {
         // breathing mode, ignore the change of val, use in memory value instead
         val = rgblight_config.val;
-      } else if (rgblight_config.mode >= 6 && rgblight_config.mode <= 14) {
-        // rainbow mood and rainbow swirl, ignore the change of hue
+      }
+#endif
+#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
+      else if (rgblight_config.mode >= RGBLIGHT_MODE_RAINBOW_MOOD &&
+                  rgblight_config.mode <= RGBLIGHT_MODE_RAINBOW_MOOD_end) {
+        // rainbow mood, ignore the change of hue
         hue = rgblight_config.hue;
-      } else if (rgblight_config.mode >= 25 && rgblight_config.mode <= 34) {
+      }
+#endif
+#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
+      else if (rgblight_config.mode >= RGBLIGHT_MODE_RAINBOW_SWIRL &&
+               rgblight_config.mode <= RGBLIGHT_MODE_RAINBOW_SWIRL_end) {
+        // rainbow swirl, ignore the change of hue
+        hue = rgblight_config.hue;
+      }
+#endif
+#ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
+      else if (rgblight_config.mode >= RGBLIGHT_MODE_STATIC_GRADIENT &&
+               rgblight_config.mode <= RGBLIGHT_MODE_STATIC_GRADIENT_end) {
         // static gradient
         uint16_t _hue;
-        int8_t direction = ((rgblight_config.mode - 25) % 2) ? -1 : 1;
-        uint16_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[(rgblight_config.mode - 25) / 2]);
+        int8_t direction = ((rgblight_config.mode - RGBLIGHT_MODE_STATIC_GRADIENT) % 2) ? -1 : 1;
+        uint16_t range = pgm_read_word(&RGBLED_GRADIENT_RANGES[(rgblight_config.mode - RGBLIGHT_MODE_STATIC_GRADIENT) / 2]);
         for (uint8_t i = 0; i < RGBLED_NUM; i++) {
           _hue = (range / RGBLED_NUM * i * direction + hue + 360) % 360;
           dprintf("rgblight rainbow set hsv: %u,%u,%d,%u\n", i, _hue, direction, range);
@@ -444,6 +500,7 @@ void rgblight_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, bool
         }
         rgblight_set();
       }
+#endif
     }
     rgblight_config.hue = hue;
     rgblight_config.sat = sat;
@@ -528,7 +585,7 @@ void rgblight_set(void) {
 }
 #endif
 
-#ifdef RGBLIGHT_ANIMATIONS
+#ifdef RGBLIGHT_USE_TIMER
 
 // Animation timer -- AVR Timer3
 void rgblight_timer_init(void) {
@@ -564,41 +621,77 @@ void rgblight_timer_toggle(void) {
 
 void rgblight_show_solid_color(uint8_t r, uint8_t g, uint8_t b) {
   rgblight_enable();
-  rgblight_mode(1);
+  rgblight_mode(RGBLIGHT_MODE_STATIC_LIGHT);
   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);
-    } else if (rgblight_config.mode == 24) {
-      // mode = 24, christmas mode
+    // static light mode, do nothing here
+    if ( 1 == 0 ) { //dummy
+    }
+#ifdef RGBLIGHT_EFFECT_BREATHING
+    else if (rgblight_config.mode >= RGBLIGHT_MODE_BREATHING  &&
+        rgblight_config.mode <= RGBLIGHT_MODE_BREATHING_end) {
+      // breathing mode
+      rgblight_effect_breathing(rgblight_config.mode - RGBLIGHT_MODE_BREATHING );
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
+    else if (rgblight_config.mode >= RGBLIGHT_MODE_RAINBOW_MOOD &&
+               rgblight_config.mode <= RGBLIGHT_MODE_RAINBOW_MOOD_end) {
+      // rainbow mood mode
+      rgblight_effect_rainbow_mood(rgblight_config.mode - RGBLIGHT_MODE_RAINBOW_MOOD);
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
+    else if (rgblight_config.mode >= RGBLIGHT_MODE_RAINBOW_SWIRL &&
+               rgblight_config.mode <= RGBLIGHT_MODE_RAINBOW_SWIRL_end) {
+      // rainbow swirl mode
+      rgblight_effect_rainbow_swirl(rgblight_config.mode - RGBLIGHT_MODE_RAINBOW_SWIRL);
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_SNAKE
+    else if (rgblight_config.mode >= RGBLIGHT_MODE_SNAKE &&
+               rgblight_config.mode <= RGBLIGHT_MODE_SNAKE_end) {
+      // snake mode
+      rgblight_effect_snake(rgblight_config.mode - RGBLIGHT_MODE_SNAKE);
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_KNIGHT
+    else if (rgblight_config.mode >= RGBLIGHT_MODE_KNIGHT &&
+               rgblight_config.mode <= RGBLIGHT_MODE_KNIGHT_end) {
+      // knight mode
+      rgblight_effect_knight(rgblight_config.mode - RGBLIGHT_MODE_KNIGHT);
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_CHRISTMAS
+    else if (rgblight_config.mode == RGBLIGHT_MODE_CHRISTMAS) {
+      // christmas mode
       rgblight_effect_christmas();
-    } else if (rgblight_config.mode == 35) {
-      // mode = 35, RGB test
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_RGB_TEST
+    else if (rgblight_config.mode == RGBLIGHT_MODE_RGB_TEST) {
+      // RGB test mode
       rgblight_effect_rgbtest();
-    } else if (rgblight_config.mode == 36){
+    }
+#endif
+#ifdef RGBLIGHT_EFFECT_ALTERNATING
+    else if (rgblight_config.mode == RGBLIGHT_MODE_ALTERNATING){
       rgblight_effect_alternating();
     }
+#endif
   }
 }
 
+#endif /* RGBLIGHT_USE_TIMER */
+
 // Effects
+#ifdef RGBLIGHT_EFFECT_BREATHING
+__attribute__ ((weak))
+const uint8_t RGBLED_BREATHING_INTERVALS[] PROGMEM = {30, 20, 10, 5};
+
 void rgblight_effect_breathing(uint8_t interval) {
   static uint8_t pos = 0;
   static uint16_t last_timer = 0;
@@ -609,12 +702,17 @@ void rgblight_effect_breathing(uint8_t interval) {
   }
   last_timer = timer_read();
 
-
   // http://sean.voisen.org/blog/2011/10/breathing-led-with-arduino/
   val = (exp(sin((pos/255.0)*M_PI)) - RGBLIGHT_EFFECT_BREATHE_CENTER/M_E)*(RGBLIGHT_EFFECT_BREATHE_MAX/(M_E-1/M_E));
   rgblight_sethsv_noeeprom_old(rgblight_config.hue, rgblight_config.sat, val);
   pos = (pos + 1) % 256;
 }
+#endif
+
+#ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
+__attribute__ ((weak))
+const uint8_t RGBLED_RAINBOW_MOOD_INTERVALS[] PROGMEM = {120, 60, 30};
+
 void rgblight_effect_rainbow_mood(uint8_t interval) {
   static uint16_t current_hue = 0;
   static uint16_t last_timer = 0;
@@ -626,6 +724,16 @@ void rgblight_effect_rainbow_mood(uint8_t interval) {
   rgblight_sethsv_noeeprom_old(current_hue, rgblight_config.sat, rgblight_config.val);
   current_hue = (current_hue + 1) % 360;
 }
+#endif
+
+#ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
+#ifndef RGBLIGHT_RAINBOW_SWIRL_RANGE
+  #define RGBLIGHT_RAINBOW_SWIRL_RANGE 360
+#endif
+
+__attribute__ ((weak))
+const uint8_t RGBLED_RAINBOW_SWIRL_INTERVALS[] PROGMEM = {100, 50, 20};
+
 void rgblight_effect_rainbow_swirl(uint8_t interval) {
   static uint16_t current_hue = 0;
   static uint16_t last_timer = 0;
@@ -636,7 +744,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;
+    hue = (RGBLIGHT_RAINBOW_SWIRL_RANGE / RGBLED_NUM * i + current_hue) % 360;
     sethsv(hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
   }
   rgblight_set();
@@ -651,6 +759,12 @@ void rgblight_effect_rainbow_swirl(uint8_t interval) {
     }
   }
 }
+#endif
+
+#ifdef RGBLIGHT_EFFECT_SNAKE
+__attribute__ ((weak))
+const uint8_t RGBLED_SNAKE_INTERVALS[] PROGMEM = {100, 50, 20};
+
 void rgblight_effect_snake(uint8_t interval) {
   static uint8_t pos = 0;
   static uint16_t last_timer = 0;
@@ -689,6 +803,12 @@ void rgblight_effect_snake(uint8_t interval) {
     pos = (pos + 1) % RGBLED_NUM;
   }
 }
+#endif
+
+#ifdef RGBLIGHT_EFFECT_KNIGHT
+__attribute__ ((weak))
+const uint8_t RGBLED_KNIGHT_INTERVALS[] PROGMEM = {127, 63, 31};
+
 void rgblight_effect_knight(uint8_t interval) {
   static uint16_t last_timer = 0;
   if (timer_elapsed(last_timer) < pgm_read_byte(&RGBLED_KNIGHT_INTERVALS[interval])) {
@@ -730,8 +850,9 @@ void rgblight_effect_knight(uint8_t interval) {
     increment = -increment;
   }
 }
+#endif
 
-
+#ifdef RGBLIGHT_EFFECT_CHRISTMAS
 void rgblight_effect_christmas(void) {
   static uint16_t current_offset = 0;
   static uint16_t last_timer = 0;
@@ -748,6 +869,11 @@ void rgblight_effect_christmas(void) {
   }
   rgblight_set();
 }
+#endif
+
+#ifdef RGBLIGHT_EFFECT_RGB_TEST
+__attribute__ ((weak))
+const uint16_t RGBLED_RGBTEST_INTERVALS[] PROGMEM = {1024};
 
 void rgblight_effect_rgbtest(void) {
   static uint8_t pos = 0;
@@ -774,7 +900,9 @@ void rgblight_effect_rgbtest(void) {
   rgblight_setrgb(r, g, b);
   pos = (pos + 1) % 3;
 }
+#endif
 
+#ifdef RGBLIGHT_EFFECT_ALTERNATING
 void rgblight_effect_alternating(void){
   static uint16_t last_timer = 0;
   static uint16_t pos = 0;
@@ -784,16 +912,15 @@ void rgblight_effect_alternating(void){
   last_timer = timer_read();
 
   for(int i = 0; i<RGBLED_NUM; i++){
-		  if(i<RGBLED_NUM/2 && pos){
-			  rgblight_sethsv_at(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, i);
-		  }else if (i>=RGBLED_NUM/2 && !pos){
-			  rgblight_sethsv_at(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, i);
-		  }else{
-			  rgblight_sethsv_at(rgblight_config.hue, rgblight_config.sat, 0, i);
-		  }
+      if(i<RGBLED_NUM/2 && pos){
+          sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
+      }else if (i>=RGBLED_NUM/2 && !pos){
+          sethsv(rgblight_config.hue, rgblight_config.sat, rgblight_config.val, (LED_TYPE *)&led[i]);
+      }else{
+          sethsv(rgblight_config.hue, rgblight_config.sat, 0, (LED_TYPE *)&led[i]);
+      }
   }
   rgblight_set();
   pos = (pos + 1) % 2;
 }
-
-#endif /* RGBLIGHT_ANIMATIONS */
+#endif
diff --git a/quantum/rgblight.h b/quantum/rgblight.h
index ba010dfae3..65dda3f521 100644
--- a/quantum/rgblight.h
+++ b/quantum/rgblight.h
@@ -16,11 +16,69 @@
 #ifndef RGBLIGHT_H
 #define RGBLIGHT_H
 
-#ifdef RGBLIGHT_ANIMATIONS
-	#define RGBLIGHT_MODES 36
-#else
-	#define RGBLIGHT_MODES 1
-#endif
+#include "rgblight_reconfig.h"
+
+/***** rgblight_mode(mode)/rgblight_mode_noeeprom(mode) ****
+
+ old mode number (before 0.6.117) to new mode name table
+
+|-----------------|-----------------------------------|
+| old mode number | new mode name                     |
+|-----------------|-----------------------------------|
+|        1        | RGBLIGHT_MODE_STATIC_LIGHT        |
+|        2        | RGBLIGHT_MODE_BREATHING           |
+|        3        | RGBLIGHT_MODE_BREATHING + 1       |
+|        4        | RGBLIGHT_MODE_BREATHING + 2       |
+|        5        | RGBLIGHT_MODE_BREATHING + 3       |
+|        6        | RGBLIGHT_MODE_RAINBOW_MOOD        |
+|        7        | RGBLIGHT_MODE_RAINBOW_MOOD + 1    |
+|        8        | RGBLIGHT_MODE_RAINBOW_MOOD + 2    |
+|        9        | RGBLIGHT_MODE_RAINBOW_SWIRL       |
+|       10        | RGBLIGHT_MODE_RAINBOW_SWIRL + 1   |
+|       11        | RGBLIGHT_MODE_RAINBOW_SWIRL + 2   |
+|       12        | RGBLIGHT_MODE_RAINBOW_SWIRL + 3   |
+|       13        | RGBLIGHT_MODE_RAINBOW_SWIRL + 4   |
+|       14        | RGBLIGHT_MODE_RAINBOW_SWIRL + 5   |
+|       15        | RGBLIGHT_MODE_SNAKE               |
+|       16        | RGBLIGHT_MODE_SNAKE + 1           |
+|       17        | RGBLIGHT_MODE_SNAKE + 2           |
+|       18        | RGBLIGHT_MODE_SNAKE + 3           |
+|       19        | RGBLIGHT_MODE_SNAKE + 4           |
+|       20        | RGBLIGHT_MODE_SNAKE + 5           |
+|       21        | RGBLIGHT_MODE_KNIGHT              |
+|       22        | RGBLIGHT_MODE_KNIGHT + 1          |
+|       23        | RGBLIGHT_MODE_KNIGHT + 2          |
+|       24        | RGBLIGHT_MODE_CHRISTMAS           |
+|       25        | RGBLIGHT_MODE_STATIC_GRADIENT     |
+|       26        | RGBLIGHT_MODE_STATIC_GRADIENT + 1 |
+|       27        | RGBLIGHT_MODE_STATIC_GRADIENT + 2 |
+|       28        | RGBLIGHT_MODE_STATIC_GRADIENT + 3 |
+|       29        | RGBLIGHT_MODE_STATIC_GRADIENT + 4 |
+|       30        | RGBLIGHT_MODE_STATIC_GRADIENT + 5 |
+|       31        | RGBLIGHT_MODE_STATIC_GRADIENT + 6 |
+|       32        | RGBLIGHT_MODE_STATIC_GRADIENT + 7 |
+|       33        | RGBLIGHT_MODE_STATIC_GRADIENT + 8 |
+|       34        | RGBLIGHT_MODE_STATIC_GRADIENT + 9 |
+|       35        | RGBLIGHT_MODE_RGB_TEST            |
+|       36        | RGBLIGHT_MODE_ALTERNATING         |
+|-----------------|-----------------------------------|
+ *****/
+
+#define _RGBM_SINGLE_STATIC(sym)   RGBLIGHT_MODE_ ## sym,
+#define _RGBM_SINGLE_DYNAMIC(sym)  RGBLIGHT_MODE_ ## sym,
+#define _RGBM_MULTI_STATIC(sym)    RGBLIGHT_MODE_ ## sym,
+#define _RGBM_MULTI_DYNAMIC(sym)   RGBLIGHT_MODE_ ## sym,
+#define _RGBM_TMP_STATIC(sym)      RGBLIGHT_MODE_ ## sym,
+#define _RGBM_TMP_DYNAMIC(sym)     RGBLIGHT_MODE_ ## sym,
+enum RGBLIGHT_EFFECT_MODE {
+    RGBLIGHT_MODE_zero = 0,
+#include "rgblight.h"
+    RGBLIGHT_MODE_last
+};
+
+#ifndef RGBLIGHT_H_DUMMY_DEFINE
+
+#define RGBLIGHT_MODES (RGBLIGHT_MODE_last-1)
 
 #ifndef RGBLIGHT_EFFECT_BREATHE_CENTER
 #define RGBLIGHT_EFFECT_BREATHE_CENTER 1.85  // 1-2.7
@@ -109,7 +167,7 @@ void rgblight_enable(void);
 void rgblight_disable(void);
 void rgblight_step(void);
 void rgblight_step_reverse(void);
-uint32_t rgblight_get_mode(void);
+uint8_t rgblight_get_mode(void);
 void rgblight_mode(uint8_t mode);
 void rgblight_set(void);
 void rgblight_update_dword(uint32_t dword);
@@ -145,6 +203,14 @@ void rgblight_mode_noeeprom(uint8_t mode);
 void rgblight_toggle_noeeprom(void);
 void rgblight_enable_noeeprom(void);
 void rgblight_disable_noeeprom(void);
+void rgblight_step_noeeprom(void);
+void rgblight_step_reverse_noeeprom(void);
+void rgblight_increase_hue_noeeprom(void);
+void rgblight_decrease_hue_noeeprom(void);
+void rgblight_increase_sat_noeeprom(void);
+void rgblight_decrease_sat_noeeprom(void);
+void rgblight_increase_val_noeeprom(void);
+void rgblight_decrease_val_noeeprom(void);
 
 void rgblight_sethsv_eeprom_helper(uint16_t hue, uint8_t sat, uint8_t val, bool write_to_eeprom);
 void rgblight_mode_eeprom_helper(uint8_t mode, bool write_to_eeprom);
@@ -168,4 +234,73 @@ void rgblight_effect_christmas(void);
 void rgblight_effect_rgbtest(void);
 void rgblight_effect_alternating(void);
 
+#endif // #ifndef RGBLIGHT_H_DUMMY_DEFINE
+#endif // RGBLIGHT_H
+
+#ifdef _RGBM_SINGLE_STATIC
+  _RGBM_SINGLE_STATIC( STATIC_LIGHT )
+  #ifdef RGBLIGHT_EFFECT_BREATHING
+    _RGBM_MULTI_DYNAMIC( BREATHING )
+    _RGBM_TMP_DYNAMIC( breathing_3 )
+    _RGBM_TMP_DYNAMIC( breathing_4 )
+    _RGBM_TMP_DYNAMIC( BREATHING_end )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_RAINBOW_MOOD
+    _RGBM_MULTI_DYNAMIC( RAINBOW_MOOD )
+    _RGBM_TMP_DYNAMIC( rainbow_mood_7 )
+    _RGBM_TMP_DYNAMIC( RAINBOW_MOOD_end )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_RAINBOW_SWIRL
+    _RGBM_MULTI_DYNAMIC( RAINBOW_SWIRL )
+    _RGBM_TMP_DYNAMIC( rainbow_swirl_10 )
+    _RGBM_TMP_DYNAMIC( rainbow_swirl_11 )
+    _RGBM_TMP_DYNAMIC( rainbow_swirl_12 )
+    _RGBM_TMP_DYNAMIC( rainbow_swirl_13 )
+    _RGBM_TMP_DYNAMIC( RAINBOW_SWIRL_end )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_SNAKE
+    _RGBM_MULTI_DYNAMIC( SNAKE )
+    _RGBM_TMP_DYNAMIC( snake_16 )
+    _RGBM_TMP_DYNAMIC( snake_17 )
+    _RGBM_TMP_DYNAMIC( snake_18 )
+    _RGBM_TMP_DYNAMIC( snake_19 )
+    _RGBM_TMP_DYNAMIC( SNAKE_end )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_KNIGHT
+    _RGBM_MULTI_DYNAMIC( KNIGHT )
+    _RGBM_TMP_DYNAMIC( knight_22 )
+    _RGBM_TMP_DYNAMIC( KNIGHT_end )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_CHRISTMAS
+    _RGBM_SINGLE_DYNAMIC( CHRISTMAS )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_STATIC_GRADIENT
+    _RGBM_MULTI_STATIC( STATIC_GRADIENT )
+    _RGBM_TMP_STATIC( static_gradient_26 )
+    _RGBM_TMP_STATIC( static_gradient_27 )
+    _RGBM_TMP_STATIC( static_gradient_28 )
+    _RGBM_TMP_STATIC( static_gradient_29 )
+    _RGBM_TMP_STATIC( static_gradient_30 )
+    _RGBM_TMP_STATIC( static_gradient_31 )
+    _RGBM_TMP_STATIC( static_gradient_32 )
+    _RGBM_TMP_STATIC( static_gradient_33 )
+    _RGBM_TMP_STATIC( STATIC_GRADIENT_end )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_RGB_TEST
+    _RGBM_SINGLE_DYNAMIC( RGB_TEST )
+  #endif
+  #ifdef RGBLIGHT_EFFECT_ALTERNATING
+    _RGBM_SINGLE_DYNAMIC( ALTERNATING )
+  #endif
+  ////  Add a new mode here.
+  // #ifdef RGBLIGHT_EFFECT_<name>
+  //    _RGBM_<SINGLE|MULTI>_<STATIC|DYNAMIC>( <name> )
+  // #endif
 #endif
+
+#undef _RGBM_SINGLE_STATIC
+#undef _RGBM_SINGLE_DYNAMIC
+#undef _RGBM_MULTI_STATIC
+#undef _RGBM_MULTI_DYNAMIC
+#undef _RGBM_TMP_STATIC
+#undef _RGBM_TMP_DYNAMIC
diff --git a/quantum/rgblight_reconfig.h b/quantum/rgblight_reconfig.h
new file mode 100644
index 0000000000..11bd4fd118
--- /dev/null
+++ b/quantum/rgblight_reconfig.h
@@ -0,0 +1,36 @@
+#ifndef RGBLIGHT_RECONFIG_H
+#define RGBLIGHT_RECONFIG_H
+
+#ifdef RGBLIGHT_ANIMATIONS
+   // for backward compatibility
+   #define RGBLIGHT_EFFECT_BREATHING
+   #define RGBLIGHT_EFFECT_RAINBOW_MOOD
+   #define RGBLIGHT_EFFECT_RAINBOW_SWIRL
+   #define RGBLIGHT_EFFECT_SNAKE
+   #define RGBLIGHT_EFFECT_KNIGHT
+   #define RGBLIGHT_EFFECT_CHRISTMAS
+   #define RGBLIGHT_EFFECT_STATIC_GRADIENT
+   #define RGBLIGHT_EFFECT_RGB_TEST
+   #define RGBLIGHT_EFFECT_ALTERNATING
+#endif
+
+#ifdef RGBLIGHT_STATIC_PATTERNS
+   #define RGBLIGHT_EFFECT_STATIC_GRADIENT
+#endif
+
+// check dynamic animation effects chose ?
+#if defined(RGBLIGHT_EFFECT_BREATHING) || \
+    defined(RGBLIGHT_EFFECT_RAINBOW_MOOD) || \
+    defined(RGBLIGHT_EFFECT_RAINBOW_SWIRL) ||	\
+    defined(RGBLIGHT_EFFECT_SNAKE) ||		\
+    defined(RGBLIGHT_EFFECT_KNIGHT) ||		\
+    defined(RGBLIGHT_EFFECT_CHRISTMAS) ||	\
+    defined(RGBLIGHT_EFFECT_RGB_TEST) ||	\
+    defined(RGBLIGHT_EFFECT_ALTERNATING)
+  #define RGBLIGHT_USE_TIMER
+  #ifndef RGBLIGHT_ANIMATIONS
+    #define RGBLIGHT_ANIMATIONS  // for backward compatibility
+  #endif
+#endif
+
+#endif // RGBLIGHT_RECONFIG_H
diff --git a/quantum/split_common/i2c.c b/quantum/split_common/i2c.c
index b3d7fcc681..45e958b395 100644
--- a/quantum/split_common/i2c.c
+++ b/quantum/split_common/i2c.c
@@ -7,8 +7,6 @@
 #include "i2c.h"
 #include "split_flags.h"
 
-#if defined(USE_I2C) || defined(EH)
-
 // Limits the amount of we wait for any one i2c transaction.
 // Since were running SCL line 100kHz (=> 10μs/bit), and each transactions is
 // 9 bits, a single transaction will take around 90μs to complete.
@@ -184,4 +182,3 @@ ISR(TWI_vect) {
   // Reset everything, so we are ready for the next TWI interrupt
   TWCR |= (1<<TWIE) | (1<<TWINT) | (ack<<TWEA) | (1<<TWEN);
 }
-#endif
diff --git a/quantum/split_common/matrix.c b/quantum/split_common/matrix.c
index 4af90209f6..2c37053f88 100644
--- a/quantum/split_common/matrix.c
+++ b/quantum/split_common/matrix.c
@@ -20,21 +20,15 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 #include <stdint.h>
 #include <stdbool.h>
-#include <avr/io.h>
 #include "wait.h"
-#include "print.h"
-#include "debug.h"
 #include "util.h"
 #include "matrix.h"
 #include "split_util.h"
-#include "pro_micro.h"
 #include "config.h"
 #include "timer.h"
 #include "split_flags.h"
+#include "quantum.h"
 
-#ifdef RGBLIGHT_ENABLE
-#   include "rgblight.h"
-#endif
 #ifdef BACKLIGHT_ENABLE
 #   include "backlight.h"
     extern backlight_config_t backlight_config;
@@ -55,6 +49,8 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
     static bool debouncing = false;
 #endif
 
+#if defined(USE_I2C) || defined(EH)
+
 #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))
@@ -63,6 +59,27 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #else
 #    error "Currently only supports 8 COLS"
 #endif
+
+#else // USE_SERIAL
+
+#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
+
+#endif
 static matrix_row_t matrix_debouncing[MATRIX_ROWS];
 
 #define ERROR_DISCONNECT_COUNT 5
@@ -71,8 +88,8 @@ static matrix_row_t matrix_debouncing[MATRIX_ROWS];
 
 static uint8_t error_count = 0;
 
-static const uint8_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
-static const uint8_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
+static pin_t row_pins[MATRIX_ROWS] = MATRIX_ROW_PINS;
+static pin_t col_pins[MATRIX_COLS] = MATRIX_COL_PINS;
 
 /* matrix state(1:on, 0:off) */
 static matrix_row_t matrix[MATRIX_ROWS];
@@ -128,15 +145,24 @@ uint8_t matrix_cols(void)
 
 void matrix_init(void)
 {
-#ifdef DISABLE_JTAG
-  // JTAG disable for PORT F. write JTD bit twice within four cycles.
-  MCUCR |= (1<<JTD);
-  MCUCR |= (1<<JTD);
-#endif
-
     debug_enable = true;
     debug_matrix = true;
     debug_mouse = true;
+
+    // Set pinout for right half if pinout for that half is defined
+    if (!isLeftHand) {
+#ifdef MATRIX_ROW_PINS_RIGHT
+        const uint8_t row_pins_right[MATRIX_ROWS] = MATRIX_ROW_PINS_RIGHT;
+        for (uint8_t i = 0; i < MATRIX_ROWS; i++)
+            row_pins[i] = row_pins_right[i];
+#endif
+#ifdef MATRIX_COL_PINS_RIGHT
+        const uint8_t col_pins_right[MATRIX_COLS] = MATRIX_COL_PINS_RIGHT;
+        for (uint8_t i = 0; i < MATRIX_COLS; i++)
+            col_pins[i] = col_pins_right[i];
+#endif
+    }
+
     // initialize row and col
 #if (DIODE_DIRECTION == COL2ROW)
     unselect_rows();
@@ -277,24 +303,48 @@ i2c_error: // the cable is disconnceted, or something else went wrong
 
 #else // USE_SERIAL
 
+
+typedef struct _Serial_s2m_buffer_t {
+    // TODO: if MATRIX_COLS > 8 change to uint8_t packed_matrix[] for pack/unpack
+    matrix_row_t smatrix[ROWS_PER_HAND];
+} Serial_s2m_buffer_t;
+
+volatile Serial_s2m_buffer_t serial_s2m_buffer = {};
+volatile Serial_m2s_buffer_t serial_m2s_buffer = {};
+uint8_t volatile status0 = 0;
+
+SSTD_t transactions[] = {
+    { (uint8_t *)&status0,
+      sizeof(serial_m2s_buffer), (uint8_t *)&serial_m2s_buffer,
+      sizeof(serial_s2m_buffer), (uint8_t *)&serial_s2m_buffer
+  }
+};
+
+void serial_master_init(void)
+{ soft_serial_initiator_init(transactions, TID_LIMIT(transactions)); }
+
+void serial_slave_init(void)
+{ soft_serial_target_init(transactions, TID_LIMIT(transactions)); }
+
 int serial_transaction(void) {
     int slaveOffset = (isLeftHand) ? (ROWS_PER_HAND) : 0;
 
-    if (serial_update_buffers()) {
+    if (soft_serial_transaction()) {
         return 1;
     }
 
+    // TODO:  if MATRIX_COLS > 8 change to unpack()
     for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        matrix[slaveOffset+i] = serial_slave_buffer[i];
+        matrix[slaveOffset+i] = serial_s2m_buffer.smatrix[i];
     }
     
-    #ifdef RGBLIGHT_ENABLE
+    #if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
         // Code to send RGB over serial goes here (not implemented yet)
     #endif
     
     #ifdef BACKLIGHT_ENABLE
         // Write backlight level for slave to read
-        serial_master_buffer[SERIAL_BACKLIT_START] = backlight_config.enable ? backlight_config.level : 0;
+        serial_m2s_buffer.backlight_level = backlight_config.enable ? backlight_config.level : 0;
     #endif
 
     return 0;
@@ -337,27 +387,10 @@ void matrix_slave_scan(void) {
         i2c_slave_buffer[I2C_KEYMAP_START+i] = matrix[offset+i];
     }   
 #else // USE_SERIAL
+    // TODO: if MATRIX_COLS > 8 change to pack()
     for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        serial_slave_buffer[i] = matrix[offset+i];
-    }
-#endif
-#ifdef USE_I2C
-#ifdef BACKLIGHT_ENABLE
-    // Read backlight level sent from master and update level on slave
-    backlight_set(i2c_slave_buffer[0]);
-#endif
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        i2c_slave_buffer[i+1] = matrix[offset+i];
-    }
-#else // USE_SERIAL
-    for (int i = 0; i < ROWS_PER_HAND; ++i) {
-        serial_slave_buffer[i] = matrix[offset+i];
+        serial_s2m_buffer.smatrix[i] = matrix[offset+i];
     }
-
-#ifdef BACKLIGHT_ENABLE
-    // Read backlight level sent from master and update level on slave
-    backlight_set(serial_master_buffer[SERIAL_BACKLIT_START]);
-#endif
 #endif
     matrix_slave_scan_user();
 }
@@ -404,9 +437,7 @@ uint8_t matrix_key_count(void)
 static void init_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
+        setPinInputHigh(col_pins[x]);
     }
 }
 
@@ -424,13 +455,8 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t current_row)
 
     // 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);
+        current_matrix[current_row] |=  readPin(col_pins[col_index]) ? 0 : (ROW_SHIFTER << col_index);
     }
 
     // Unselect row
@@ -441,24 +467,19 @@ static bool read_cols_on_row(matrix_row_t current_matrix[], uint8_t 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
+    writePinLow(row_pins[row]);
+    setPinOutput(row_pins[row]);
 }
 
 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
+    setPinInputHigh(row_pins[row]);
 }
 
 static void unselect_rows(void)
 {
     for(uint8_t x = 0; x < ROWS_PER_HAND; 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
+        setPinInputHigh(row_pins[x]);
     }
 }
 
@@ -467,9 +488,7 @@ static void unselect_rows(void)
 static void init_rows(void)
 {
     for(uint8_t x = 0; x < ROWS_PER_HAND; 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
+        setPinInputHigh(row_pins[x]);
     }
 }
 
@@ -489,15 +508,15 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
         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)
+        if (readPin(row_pins[row_index]))
         {
-            // Pin LO, set col bit
-            current_matrix[row_index] |= (ROW_SHIFTER << current_col);
+            // Pin HI, clear col bit
+            current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
         }
         else
         {
-            // Pin HI, clear col bit
-            current_matrix[row_index] &= ~(ROW_SHIFTER << current_col);
+            // Pin LO, set col bit
+            current_matrix[row_index] |= (ROW_SHIFTER << current_col);
         }
 
         // Determine if the matrix changed state
@@ -515,24 +534,19 @@ static bool read_rows_on_col(matrix_row_t current_matrix[], uint8_t current_col)
 
 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
+    writePinLow(col_pins[col]);
+    setPinOutput(col_pins[col]);
 }
 
 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
+    setPinInputHigh(col_pins[col]);
 }
 
 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
+        setPinInputHigh(col_pins[x]);
     }
 }
 
diff --git a/quantum/split_common/matrix.h b/quantum/split_common/matrix.h
new file mode 100644
index 0000000000..b5cb45baed
--- /dev/null
+++ b/quantum/split_common/matrix.h
@@ -0,0 +1,31 @@
+#ifndef SPLIT_COMMON_MATRIX_H
+#define SPLIT_COMMON_MATRIX_H
+
+#include <common/matrix.h>
+
+#ifdef RGBLIGHT_ENABLE
+#   include "rgblight.h"
+#endif
+
+typedef struct _Serial_m2s_buffer_t {
+#ifdef BACKLIGHT_ENABLE
+    uint8_t backlight_level;
+#endif
+#if defined(RGBLIGHT_ENABLE) && defined(RGBLIGHT_SPLIT)
+    rgblight_config_t rgblight_config; //not yet use
+    //
+    // When MCUs on both sides drive their respective RGB LED chains,
+    // it is necessary to synchronize, so it is necessary to communicate RGB information.
+    // In that case, define the RGBLIGHT_SPLIT macro.
+    //
+    // Otherwise, if the master side MCU drives both sides RGB LED chains,
+    // there is no need to communicate.
+#endif
+} Serial_m2s_buffer_t;
+
+extern volatile Serial_m2s_buffer_t serial_m2s_buffer;
+
+void serial_master_init(void);
+void serial_slave_init(void);
+
+#endif
diff --git a/quantum/split_common/serial.c b/quantum/split_common/serial.c
index 74bcbb6bf6..1315377a34 100644
--- a/quantum/split_common/serial.c
+++ b/quantum/split_common/serial.c
@@ -1,5 +1,10 @@
 /*
  * WARNING: be careful changing this code, it is very timing dependent
+ *
+ * 2018-10-28 checked
+ *  avr-gcc 4.9.2
+ *  avr-gcc 5.4.0
+ *  avr-gcc 7.3.0
  */
 
 #ifndef F_CPU
@@ -9,220 +14,533 @@
 #include <avr/io.h>
 #include <avr/interrupt.h>
 #include <util/delay.h>
+#include <stddef.h>
 #include <stdbool.h>
 #include "serial.h"
+//#include <pro_micro.h>
+
+#ifdef SOFT_SERIAL_PIN
+
+#ifdef __AVR_ATmega32U4__
+  // if using ATmega32U4 I2C, can not use PD0 and PD1 in soft serial.
+  #ifdef USE_AVR_I2C
+    #if SOFT_SERIAL_PIN == D0 || SOFT_SERIAL_PIN == D1
+      #error Using ATmega32U4 I2C, so can not use PD0, PD1
+    #endif
+  #endif
+
+  #if SOFT_SERIAL_PIN >= D0 && SOFT_SERIAL_PIN <= D3
+    #define SERIAL_PIN_DDR   DDRD
+    #define SERIAL_PIN_PORT  PORTD
+    #define SERIAL_PIN_INPUT PIND
+    #if SOFT_SERIAL_PIN == D0
+      #define SERIAL_PIN_MASK _BV(PD0)
+      #define EIMSK_BIT       _BV(INT0)
+      #define EICRx_BIT       (~(_BV(ISC00) | _BV(ISC01)))
+      #define SERIAL_PIN_INTERRUPT INT0_vect
+    #elif  SOFT_SERIAL_PIN == D1
+      #define SERIAL_PIN_MASK _BV(PD1)
+      #define EIMSK_BIT       _BV(INT1)
+      #define EICRx_BIT       (~(_BV(ISC10) | _BV(ISC11)))
+      #define SERIAL_PIN_INTERRUPT INT1_vect
+    #elif  SOFT_SERIAL_PIN == D2
+      #define SERIAL_PIN_MASK _BV(PD2)
+      #define EIMSK_BIT       _BV(INT2)
+      #define EICRx_BIT       (~(_BV(ISC20) | _BV(ISC21)))
+      #define SERIAL_PIN_INTERRUPT INT2_vect
+    #elif  SOFT_SERIAL_PIN == D3
+      #define SERIAL_PIN_MASK _BV(PD3)
+      #define EIMSK_BIT       _BV(INT3)
+      #define EICRx_BIT       (~(_BV(ISC30) | _BV(ISC31)))
+      #define SERIAL_PIN_INTERRUPT INT3_vect
+    #endif
+  #elif  SOFT_SERIAL_PIN == E6
+    #define SERIAL_PIN_DDR   DDRE
+    #define SERIAL_PIN_PORT  PORTE
+    #define SERIAL_PIN_INPUT PINE
+    #define SERIAL_PIN_MASK  _BV(PE6)
+    #define EIMSK_BIT        _BV(INT6)
+    #define EICRx_BIT        (~(_BV(ISC60) | _BV(ISC61)))
+    #define SERIAL_PIN_INTERRUPT INT6_vect
+  #else
+  #error invalid SOFT_SERIAL_PIN value
+  #endif
+
+#else
+ #error serial.c now support ATmega32U4 only
+#endif
 
-#ifndef USE_I2C
+#define ALWAYS_INLINE __attribute__((always_inline))
+#define NO_INLINE __attribute__((noinline))
+#define _delay_sub_us(x)    __builtin_avr_delay_cycles(x)
+
+// parity check
+#define ODD_PARITY 1
+#define EVEN_PARITY 0
+#define PARITY EVEN_PARITY
+
+#ifdef SERIAL_DELAY
+  // custom setup in config.h
+  // #define TID_SEND_ADJUST 2
+  // #define SERIAL_DELAY 6             // micro sec
+  // #define READ_WRITE_START_ADJUST 30 // cycles
+  // #define READ_WRITE_WIDTH_ADJUST 8 // cycles
+#else
+// ============ Standard setups ============
+
+#ifndef SELECT_SOFT_SERIAL_SPEED
+#define SELECT_SOFT_SERIAL_SPEED 1
+//  0: about 189kbps (Experimental only)
+//  1: about 137kbps (default)
+//  2: about 75kbps
+//  3: about 39kbps
+//  4: about 26kbps
+//  5: about 20kbps
+#endif
 
-// Serial pulse period in microseconds. Its probably a bad idea to lower this
-// value.
-#define SERIAL_DELAY 24
+#if __GNUC__ < 6
+  #define TID_SEND_ADJUST 14
+#else
+  #define TID_SEND_ADJUST 2
+#endif
 
-uint8_t volatile serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH] = {0};
-uint8_t volatile serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH] = {0};
+#if SELECT_SOFT_SERIAL_SPEED == 0
+  // Very High speed
+  #define SERIAL_DELAY 4             // micro sec
+  #if __GNUC__ < 6
+    #define READ_WRITE_START_ADJUST 33 // cycles
+    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
+  #else
+    #define READ_WRITE_START_ADJUST 34 // cycles
+    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
+  #endif
+#elif SELECT_SOFT_SERIAL_SPEED == 1
+  // High speed
+  #define SERIAL_DELAY 6             // micro sec
+  #if __GNUC__ < 6
+    #define READ_WRITE_START_ADJUST 30 // cycles
+    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
+  #else
+    #define READ_WRITE_START_ADJUST 33 // cycles
+    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
+  #endif
+#elif SELECT_SOFT_SERIAL_SPEED == 2
+  // Middle speed
+  #define SERIAL_DELAY 12            // micro sec
+  #define READ_WRITE_START_ADJUST 30 // cycles
+  #if __GNUC__ < 6
+    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
+  #else
+    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
+  #endif
+#elif SELECT_SOFT_SERIAL_SPEED == 3
+  // Low speed
+  #define SERIAL_DELAY 24            // micro sec
+  #define READ_WRITE_START_ADJUST 30 // cycles
+  #if __GNUC__ < 6
+    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
+  #else
+    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
+  #endif
+#elif SELECT_SOFT_SERIAL_SPEED == 4
+  // Very Low speed
+  #define SERIAL_DELAY 36            // micro sec
+  #define READ_WRITE_START_ADJUST 30 // cycles
+  #if __GNUC__ < 6
+    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
+  #else
+    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
+  #endif
+#elif SELECT_SOFT_SERIAL_SPEED == 5
+  // Ultra Low speed
+  #define SERIAL_DELAY 48            // micro sec
+  #define READ_WRITE_START_ADJUST 30 // cycles
+  #if __GNUC__ < 6
+    #define READ_WRITE_WIDTH_ADJUST 3 // cycles
+  #else
+    #define READ_WRITE_WIDTH_ADJUST 7 // cycles
+  #endif
+#else
+#error invalid SELECT_SOFT_SERIAL_SPEED value
+#endif /* SELECT_SOFT_SERIAL_SPEED */
+#endif /* SERIAL_DELAY */
+
+#define SERIAL_DELAY_HALF1 (SERIAL_DELAY/2)
+#define SERIAL_DELAY_HALF2 (SERIAL_DELAY - SERIAL_DELAY/2)
+
+#define SLAVE_INT_WIDTH_US 1
+#ifndef SERIAL_USE_MULTI_TRANSACTION
+  #define SLAVE_INT_RESPONSE_TIME SERIAL_DELAY
+#else
+  #define SLAVE_INT_ACK_WIDTH_UNIT 2
+  #define SLAVE_INT_ACK_WIDTH 4
+#endif
 
-#define SLAVE_DATA_CORRUPT (1<<0)
-volatile uint8_t status = 0;
+static SSTD_t *Transaction_table = NULL;
+static uint8_t Transaction_table_size = 0;
 
+inline static void serial_delay(void) ALWAYS_INLINE;
 inline static
 void serial_delay(void) {
   _delay_us(SERIAL_DELAY);
 }
 
+inline static void serial_delay_half1(void) ALWAYS_INLINE;
+inline static
+void serial_delay_half1(void) {
+  _delay_us(SERIAL_DELAY_HALF1);
+}
+
+inline static void serial_delay_half2(void) ALWAYS_INLINE;
+inline static
+void serial_delay_half2(void) {
+  _delay_us(SERIAL_DELAY_HALF2);
+}
+
+inline static void serial_output(void) ALWAYS_INLINE;
 inline static
 void serial_output(void) {
   SERIAL_PIN_DDR |= SERIAL_PIN_MASK;
 }
 
 // make the serial pin an input with pull-up resistor
+inline static void serial_input_with_pullup(void) ALWAYS_INLINE;
 inline static
-void serial_input(void) {
+void serial_input_with_pullup(void) {
   SERIAL_PIN_DDR  &= ~SERIAL_PIN_MASK;
   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
 }
 
+inline static uint8_t serial_read_pin(void) ALWAYS_INLINE;
 inline static
 uint8_t serial_read_pin(void) {
   return !!(SERIAL_PIN_INPUT & SERIAL_PIN_MASK);
 }
 
+inline static void serial_low(void) ALWAYS_INLINE;
 inline static
 void serial_low(void) {
   SERIAL_PIN_PORT &= ~SERIAL_PIN_MASK;
 }
 
+inline static void serial_high(void) ALWAYS_INLINE;
 inline static
 void serial_high(void) {
   SERIAL_PIN_PORT |= SERIAL_PIN_MASK;
 }
 
-void serial_master_init(void) {
-  serial_output();
-  serial_high();
+void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size)
+{
+    Transaction_table = sstd_table;
+    Transaction_table_size = (uint8_t)sstd_table_size;
+    serial_output();
+    serial_high();
 }
 
-void serial_slave_init(void) {
-  serial_input();
-
-  // Enable INT0
-  EIMSK |= _BV(INT0);
-  // Trigger on falling edge of INT0
-  EICRA &= ~(_BV(ISC00) | _BV(ISC01));
+void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size)
+{
+    Transaction_table = sstd_table;
+    Transaction_table_size = (uint8_t)sstd_table_size;
+    serial_input_with_pullup();
+
+    // Enable INT0-INT3,INT6
+    EIMSK |= EIMSK_BIT;
+#if SERIAL_PIN_MASK == _BV(PE6)
+    // Trigger on falling edge of INT6
+    EICRB &= EICRx_BIT;
+#else
+    // Trigger on falling edge of INT0-INT3
+    EICRA &= EICRx_BIT;
+#endif
 }
 
-// Used by the master to synchronize timing with the slave.
+// Used by the sender to synchronize timing with the reciver.
+static void sync_recv(void) NO_INLINE;
 static
 void sync_recv(void) {
-  serial_input();
-  // This shouldn't hang if the slave disconnects because the
-  // serial line will float to high if the slave does disconnect.
+  for (uint8_t i = 0; i < SERIAL_DELAY*5 && serial_read_pin(); i++ ) {
+  }
+  // This shouldn't hang if the target disconnects because the
+  // serial line will float to high if the target does disconnect.
   while (!serial_read_pin());
-  serial_delay();
 }
 
-// Used by the slave to send a synchronization signal to the master.
+// Used by the reciver to send a synchronization signal to the sender.
+static void sync_send(void) NO_INLINE;
 static
 void sync_send(void) {
-  serial_output();
-
   serial_low();
   serial_delay();
-
   serial_high();
 }
 
 // Reads a byte from the serial line
-static
-uint8_t serial_read_byte(void) {
-  uint8_t byte = 0;
-  serial_input();
-  for ( uint8_t i = 0; i < 8; ++i) {
-    byte = (byte << 1) | serial_read_pin();
-    serial_delay();
-    _delay_us(1);
+static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) NO_INLINE;
+static uint8_t serial_read_chunk(uint8_t *pterrcount, uint8_t bit) {
+    uint8_t byte, i, p, pb;
+
+  _delay_sub_us(READ_WRITE_START_ADJUST);
+  for( i = 0, byte = 0, p = PARITY; i < bit; i++ ) {
+      serial_delay_half1();   // read the middle of pulses
+      if( serial_read_pin() ) {
+          byte = (byte << 1) | 1; p ^= 1;
+      } else {
+          byte = (byte << 1) | 0; p ^= 0;
+      }
+      _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
+      serial_delay_half2();
   }
+  /* recive parity bit */
+  serial_delay_half1();   // read the middle of pulses
+  pb = serial_read_pin();
+  _delay_sub_us(READ_WRITE_WIDTH_ADJUST);
+  serial_delay_half2();
+
+  *pterrcount += (p != pb)? 1 : 0;
 
   return byte;
 }
 
 // Sends a byte with MSB ordering
-static
-void serial_write_byte(uint8_t data) {
-  uint8_t b = 8;
-  serial_output();
-  while( b-- ) {
-    if(data & (1 << b)) {
-      serial_high();
-    } else {
-      serial_low();
+void serial_write_chunk(uint8_t data, uint8_t bit) NO_INLINE;
+void serial_write_chunk(uint8_t data, uint8_t bit) {
+    uint8_t b, p;
+    for( p = PARITY, b = 1<<(bit-1); b ; b >>= 1) {
+        if(data & b) {
+            serial_high(); p ^= 1;
+        } else {
+            serial_low();  p ^= 0;
+        }
+        serial_delay();
     }
+    /* send parity bit */
+    if(p & 1) { serial_high(); }
+    else      { serial_low(); }
     serial_delay();
-  }
-}
 
-// interrupt handle to be used by the slave device
-ISR(SERIAL_PIN_INTERRUPT) {
-  sync_send();
+    serial_low(); // sync_send() / senc_recv() need raise edge
+}
 
-  uint8_t checksum = 0;
-  for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
-    serial_write_byte(serial_slave_buffer[i]);
+static void serial_send_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
+static
+void serial_send_packet(uint8_t *buffer, uint8_t size) {
+  for (uint8_t i = 0; i < size; ++i) {
+    uint8_t data;
+    data = buffer[i];
     sync_send();
-    checksum += serial_slave_buffer[i];
+    serial_write_chunk(data,8);
   }
-  serial_write_byte(checksum);
-  sync_send();
+}
 
-  // wait for the sync to finish sending
-  serial_delay();
+static uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) NO_INLINE;
+static
+uint8_t serial_recive_packet(uint8_t *buffer, uint8_t size) {
+  uint8_t pecount = 0;
+  for (uint8_t i = 0; i < size; ++i) {
+    uint8_t data;
+    sync_recv();
+    data = serial_read_chunk(&pecount, 8);
+    buffer[i] = data;
+  }
+  return pecount == 0;
+}
 
-  // read the middle of pulses
-  _delay_us(SERIAL_DELAY/2);
+inline static
+void change_sender2reciver(void) {
+    sync_send();          //0
+    serial_delay_half1(); //1
+    serial_low();         //2
+    serial_input_with_pullup(); //2
+    serial_delay_half1(); //3
+}
 
-  uint8_t checksum_computed = 0;
-  for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
-    serial_master_buffer[i] = serial_read_byte();
-    sync_send();
-    checksum_computed += serial_master_buffer[i];
+inline static
+void change_reciver2sender(void) {
+    sync_recv();     //0
+    serial_delay();  //1
+    serial_low();    //3
+    serial_output(); //3
+    serial_delay_half1(); //4
+}
+
+static inline uint8_t nibble_bits_count(uint8_t bits)
+{
+    bits = (bits & 0x5) + (bits >> 1 & 0x5);
+    bits = (bits & 0x3) + (bits >> 2 & 0x3);
+    return bits;
+}
+
+// interrupt handle to be used by the target device
+ISR(SERIAL_PIN_INTERRUPT) {
+
+#ifndef SERIAL_USE_MULTI_TRANSACTION
+  serial_low();
+  serial_output();
+  SSTD_t *trans = Transaction_table;
+#else
+  // recive transaction table index
+  uint8_t tid, bits;
+  uint8_t pecount = 0;
+  sync_recv();
+  bits = serial_read_chunk(&pecount,7);
+  tid = bits>>3;
+  bits = (bits&7) != nibble_bits_count(tid);
+  if( bits || pecount> 0 || tid > Transaction_table_size ) {
+      return;
   }
-  uint8_t checksum_received = serial_read_byte();
-  sync_send();
+  serial_delay_half1();
 
-  serial_input(); // end transaction
+  serial_high(); // response step1 low->high
+  serial_output();
+  _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT*SLAVE_INT_ACK_WIDTH);
+  SSTD_t *trans = &Transaction_table[tid];
+  serial_low(); // response step2 ack high->low
+#endif
 
-  if ( checksum_computed != checksum_received ) {
-    status |= SLAVE_DATA_CORRUPT;
+  // target send phase
+  if( trans->target2initiator_buffer_size > 0 )
+      serial_send_packet((uint8_t *)trans->target2initiator_buffer,
+                         trans->target2initiator_buffer_size);
+  // target switch to input
+  change_sender2reciver();
+
+  // target recive phase
+  if( trans->initiator2target_buffer_size > 0 ) {
+      if (serial_recive_packet((uint8_t *)trans->initiator2target_buffer,
+                               trans->initiator2target_buffer_size) ) {
+          *trans->status = TRANSACTION_ACCEPTED;
+      } else {
+          *trans->status = TRANSACTION_DATA_ERROR;
+      }
   } else {
-    status &= ~SLAVE_DATA_CORRUPT;
+      *trans->status = TRANSACTION_ACCEPTED;
   }
-}
 
-inline
-bool serial_slave_DATA_CORRUPT(void) {
-  return status & SLAVE_DATA_CORRUPT;
+  sync_recv(); //weit initiator output to high
 }
 
-// Copies the serial_slave_buffer to the master and sends the
-// serial_master_buffer to the slave.
+/////////
+//  start transaction by initiator
+//
+// int  soft_serial_transaction(int sstd_index)
 //
 // Returns:
-// 0 => no error
-// 1 => slave did not respond
-int serial_update_buffers(void) {
-  // this code is very time dependent, so we need to disable interrupts
+//    TRANSACTION_END
+//    TRANSACTION_NO_RESPONSE
+//    TRANSACTION_DATA_ERROR
+// this code is very time dependent, so we need to disable interrupts
+#ifndef SERIAL_USE_MULTI_TRANSACTION
+int  soft_serial_transaction(void) {
+  SSTD_t *trans = Transaction_table;
+#else
+int  soft_serial_transaction(int sstd_index) {
+  if( sstd_index > Transaction_table_size )
+      return TRANSACTION_TYPE_ERROR;
+  SSTD_t *trans = &Transaction_table[sstd_index];
+#endif
   cli();
 
-  // signal to the slave that we want to start a transaction
+  // signal to the target that we want to start a transaction
   serial_output();
   serial_low();
-  _delay_us(1);
+  _delay_us(SLAVE_INT_WIDTH_US);
 
-  // wait for the slaves response
-  serial_input();
-  serial_high();
-  _delay_us(SERIAL_DELAY);
+#ifndef SERIAL_USE_MULTI_TRANSACTION
+  // wait for the target response
+  serial_input_with_pullup();
+  _delay_us(SLAVE_INT_RESPONSE_TIME);
 
-  // check if the slave is present
+  // check if the target is present
   if (serial_read_pin()) {
-    // slave failed to pull the line low, assume not present
+    // target failed to pull the line low, assume not present
+    serial_output();
+    serial_high();
+    *trans->status = TRANSACTION_NO_RESPONSE;
     sei();
-    return 1;
+    return TRANSACTION_NO_RESPONSE;
   }
 
-  // if the slave is present syncronize with it
-  sync_recv();
-
-  uint8_t checksum_computed = 0;
-  // receive data from the slave
-  for (int i = 0; i < SERIAL_SLAVE_BUFFER_LENGTH; ++i) {
-    serial_slave_buffer[i] = serial_read_byte();
-    sync_recv();
-    checksum_computed += serial_slave_buffer[i];
+#else
+  // send transaction table index
+  int tid = (sstd_index<<3) | (7 & nibble_bits_count(sstd_index));
+  sync_send();
+  _delay_sub_us(TID_SEND_ADJUST);
+  serial_write_chunk(tid, 7);
+  serial_delay_half1();
+
+  // wait for the target response (step1 low->high)
+  serial_input_with_pullup();
+  while( !serial_read_pin() ) {
+      _delay_sub_us(2);
   }
-  uint8_t checksum_received = serial_read_byte();
-  sync_recv();
 
-  if (checksum_computed != checksum_received) {
-    sei();
-    return 1;
+  // check if the target is present (step2 high->low)
+  for( int i = 0; serial_read_pin(); i++ ) {
+      if (i > SLAVE_INT_ACK_WIDTH + 1) {
+          // slave failed to pull the line low, assume not present
+          serial_output();
+          serial_high();
+          *trans->status = TRANSACTION_NO_RESPONSE;
+          sei();
+          return TRANSACTION_NO_RESPONSE;
+      }
+      _delay_sub_us(SLAVE_INT_ACK_WIDTH_UNIT);
   }
+#endif
 
-  uint8_t checksum = 0;
-  // send data to the slave
-  for (int i = 0; i < SERIAL_MASTER_BUFFER_LENGTH; ++i) {
-    serial_write_byte(serial_master_buffer[i]);
-    sync_recv();
-    checksum += serial_master_buffer[i];
+  // initiator recive phase
+  // if the target is present syncronize with it
+  if( trans->target2initiator_buffer_size > 0 ) {
+      if (!serial_recive_packet((uint8_t *)trans->target2initiator_buffer,
+                                trans->target2initiator_buffer_size) ) {
+          serial_output();
+          serial_high();
+          *trans->status = TRANSACTION_DATA_ERROR;
+          sei();
+          return TRANSACTION_DATA_ERROR;
+      }
+   }
+
+  // initiator switch to output
+  change_reciver2sender();
+
+  // initiator send phase
+  if( trans->initiator2target_buffer_size > 0 ) {
+      serial_send_packet((uint8_t *)trans->initiator2target_buffer,
+                         trans->initiator2target_buffer_size);
   }
-  serial_write_byte(checksum);
-  sync_recv();
 
   // always, release the line when not in use
-  serial_output();
-  serial_high();
+  sync_send();
 
+  *trans->status = TRANSACTION_END;
   sei();
-  return 0;
+  return TRANSACTION_END;
 }
 
+#ifdef SERIAL_USE_MULTI_TRANSACTION
+int soft_serial_get_and_clean_status(int sstd_index) {
+    SSTD_t *trans = &Transaction_table[sstd_index];
+    cli();
+    int retval = *trans->status;
+    *trans->status = 0;;
+    sei();
+    return retval;
+}
+#endif
+
 #endif
+
+// Helix serial.c history
+//   2018-1-29 fork from let's split and add PD2, modify sync_recv() (#2308, bceffdefc)
+//   2018-6-28 bug fix master to slave comm and speed up (#3255, 1038bbef4)
+//             (adjusted with avr-gcc 4.9.2)
+//   2018-7-13 remove USE_SERIAL_PD2 macro (#3374, f30d6dd78)
+//             (adjusted with avr-gcc 4.9.2)
+//   2018-8-11 add support multi-type transaction (#3608, feb5e4aae)
+//             (adjusted with avr-gcc 4.9.2)
+//   2018-10-21 fix serial and RGB animation conflict (#4191, 4665e4fff)
+//             (adjusted with avr-gcc 7.3.0)
+//   2018-10-28 re-adjust compiler depend value of delay (#4269, 8517f8a66)
+//             (adjusted with avr-gcc 5.4.0, 7.3.0)
+//   2018-12-17 copy to TOP/quantum/split_common/ and remove backward compatibility code (#4669)
diff --git a/quantum/split_common/serial.h b/quantum/split_common/serial.h
index e566eb8a06..b6638b3bde 100644
--- a/quantum/split_common/serial.h
+++ b/quantum/split_common/serial.h
@@ -1,29 +1,65 @@
-#ifndef MY_SERIAL_H
-#define MY_SERIAL_H
+#ifndef SOFT_SERIAL_H
+#define SOFT_SERIAL_H
 
-#include "config.h"
 #include <stdbool.h>
 
-/* TODO:  some defines for interrupt setup */
-#define SERIAL_PIN_DDR DDRD
-#define SERIAL_PIN_PORT PORTD
-#define SERIAL_PIN_INPUT PIND
-#define SERIAL_PIN_MASK _BV(PD0)
-#define SERIAL_PIN_INTERRUPT INT0_vect
+// /////////////////////////////////////////////////////////////////
+// Need Soft Serial defines in config.h
+// /////////////////////////////////////////////////////////////////
+// ex.
+//  #define SOFT_SERIAL_PIN ??   // ?? = D0,D1,D2,D3,E6
+//  OPTIONAL: #define SELECT_SOFT_SERIAL_SPEED ? // ? = 1,2,3,4,5
+//                                               //  1: about 137kbps (default)
+//                                               //  2: about 75kbps
+//                                               //  3: about 39kbps
+//                                               //  4: about 26kbps
+//                                               //  5: about 20kbps
+//
+// //// USE simple API (using signle-type transaction function)
+//   /* nothing */
+// //// USE flexible API (using multi-type transaction function)
+//   #define SERIAL_USE_MULTI_TRANSACTION
+//
+// /////////////////////////////////////////////////////////////////
 
-#define SERIAL_SLAVE_BUFFER_LENGTH MATRIX_ROWS/2
-#define SERIAL_MASTER_BUFFER_LENGTH 1
+// Soft Serial Transaction Descriptor
+typedef struct _SSTD_t  {
+    uint8_t *status;
+    uint8_t initiator2target_buffer_size;
+    uint8_t *initiator2target_buffer;
+    uint8_t target2initiator_buffer_size;
+    uint8_t *target2initiator_buffer;
+} SSTD_t;
+#define TID_LIMIT( table ) (sizeof(table) / sizeof(SSTD_t))
 
-// Address location defines 
-#define SERIAL_BACKLIT_START   0x00
+// initiator is transaction start side
+void soft_serial_initiator_init(SSTD_t *sstd_table, int sstd_table_size);
+// target is interrupt accept side
+void soft_serial_target_init(SSTD_t *sstd_table, int sstd_table_size);
 
-// Buffers for master - slave communication
-extern volatile uint8_t serial_slave_buffer[SERIAL_SLAVE_BUFFER_LENGTH];
-extern volatile uint8_t serial_master_buffer[SERIAL_MASTER_BUFFER_LENGTH];
-
-void serial_master_init(void);
-void serial_slave_init(void);
-int serial_update_buffers(void);
-bool serial_slave_data_corrupt(void);
+// initiator resullt
+#define TRANSACTION_END 0
+#define TRANSACTION_NO_RESPONSE 0x1
+#define TRANSACTION_DATA_ERROR  0x2
+#define TRANSACTION_TYPE_ERROR  0x4
+#ifndef SERIAL_USE_MULTI_TRANSACTION
+int  soft_serial_transaction(void);
+#else
+int  soft_serial_transaction(int sstd_index);
+#endif
 
+// target status
+// *SSTD_t.status has
+//   initiator:
+//       TRANSACTION_END
+//    or TRANSACTION_NO_RESPONSE
+//    or TRANSACTION_DATA_ERROR
+//   target:
+//       TRANSACTION_DATA_ERROR
+//    or TRANSACTION_ACCEPTED
+#define TRANSACTION_ACCEPTED 0x8
+#ifdef SERIAL_USE_MULTI_TRANSACTION
+int  soft_serial_get_and_clean_status(int sstd_index);
 #endif
+
+#endif /* SOFT_SERIAL_H */
diff --git a/quantum/split_common/split_util.c b/quantum/split_common/split_util.c
index 13b09d5b81..e41b6f6386 100644
--- a/quantum/split_common/split_util.c
+++ b/quantum/split_common/split_util.c
@@ -1,31 +1,21 @@
-#include <avr/io.h>
-#include <avr/wdt.h>
-#include <avr/power.h>
-#include <avr/interrupt.h>
-#include <util/delay.h>
-#include <avr/eeprom.h>
 #include "split_util.h"
 #include "matrix.h"
 #include "keyboard.h"
 #include "config.h"
 #include "timer.h"
 #include "split_flags.h"
+#include "quantum.h"
 
-#ifdef RGBLIGHT_ENABLE
-#   include "rgblight.h"
+#ifdef EE_HANDS
+#   include "tmk_core/common/eeprom.h"
 #endif
+
 #ifdef BACKLIGHT_ENABLE
 #   include "backlight.h"
 #endif
 
-#ifdef SPLIT_HAND_PIN
-#   include "pincontrol.h"
-#endif
-
 #if defined(USE_I2C) || defined(EH)
 #  include "i2c.h"
-#else
-#  include "serial.h"
 #endif
 
 volatile bool isLeftHand = true;
@@ -35,14 +25,13 @@ volatile uint8_t setTries = 0;
 static void setup_handedness(void) {
   #ifdef SPLIT_HAND_PIN
     // Test pin SPLIT_HAND_PIN for High/Low, if low it's right hand
-    pinMode(SPLIT_HAND_PIN, PinDirectionInput);
-    isLeftHand = digitalRead(SPLIT_HAND_PIN);
+    setPinInput(SPLIT_HAND_PIN);
+    isLeftHand = readPin(SPLIT_HAND_PIN);
   #else
     #ifdef EE_HANDS
       isLeftHand = eeprom_read_byte(EECONFIG_HANDEDNESS);
     #else
-      // I2C_MASTER_RIGHT is deprecated, use MASTER_RIGHT instead, since this works for both serial and i2c
-      #if defined(I2C_MASTER_RIGHT) || defined(MASTER_RIGHT)
+      #ifdef MASTER_RIGHT
         isLeftHand = !has_usb();
       #else
         isLeftHand = has_usb();
@@ -94,7 +83,7 @@ void split_keyboard_setup(void) {
 
 void keyboard_slave_loop(void) {
    matrix_init();
-   
+
    //Init RGB
    #ifdef RGBLIGHT_ENABLE
       rgblight_init();
@@ -103,17 +92,17 @@ void keyboard_slave_loop(void) {
    while (1) {
     // Matrix Slave Scan
     matrix_slave_scan();
-    
+
     // Read Backlight Info
     #ifdef BACKLIGHT_ENABLE
-        if (BACKLIT_DIRTY) {
-            #ifdef USE_I2C
+        #ifdef USE_I2C
+            if (BACKLIT_DIRTY) {
                 backlight_set(i2c_slave_buffer[I2C_BACKLIT_START]);
-            #else // USE_SERIAL
-                backlight_set(serial_master_buffer[SERIAL_BACKLIT_START]);
-            #endif
-            BACKLIT_DIRTY = false;
-        }
+                BACKLIT_DIRTY = false;
+            }
+        #else // USE_SERIAL
+            backlight_set(serial_m2s_buffer.backlight_level);
+        #endif
     #endif
     // Read RGB Info
     #ifdef RGBLIGHT_ENABLE
@@ -122,14 +111,14 @@ void keyboard_slave_loop(void) {
                 // Disable interupts (RGB data is big)
                 cli();
                 // Create new DWORD for RGB data
-                uint32_t dword; 
-                
+                uint32_t dword;
+
                 // Fill the new DWORD with the data that was sent over
                 uint8_t *dword_dat = (uint8_t *)(&dword);
                 for (int i = 0; i < 4; i++) {
                     dword_dat[i] = i2c_slave_buffer[I2C_RGB_START+i];
                 }
-                
+
                 // Update the RGB now with the new data and set RGB_DIRTY to false
                 rgblight_update_dword(dword);
                 RGB_DIRTY = false;
@@ -137,7 +126,9 @@ void keyboard_slave_loop(void) {
                 sei();
             }
         #else // USE_SERIAL
+          #ifdef RGBLIGHT_SPLIT
             // Add serial implementation for RGB here
+          #endif
         #endif
     #endif
    }
diff --git a/quantum/template/avr/config.h b/quantum/template/avr/config.h
index caa72af0c2..eed50e5c04 100644
--- a/quantum/template/avr/config.h
+++ b/quantum/template/avr/config.h
@@ -48,17 +48,35 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 /* COL2ROW, ROW2COL, or CUSTOM_MATRIX */
 #define DIODE_DIRECTION COL2ROW
 
+/*
+ * Split Keyboard specific options, make sure you have 'SPLIT_KEYBOARD = yes' in your rules.mk, and define SOFT_SERIAL_PIN.
+ */
+#define SOFT_SERIAL_PIN D0 // or D1, D2, D3, E6
+
 // #define BACKLIGHT_PIN B7
 // #define BACKLIGHT_BREATHING
 // #define BACKLIGHT_LEVELS 3
 
 // #define RGB_DI_PIN E2
 // #ifdef RGB_DI_PIN
-// #define RGBLIGHT_ANIMATIONS
-// #define RGBLED_NUM 16
-// #define RGBLIGHT_HUE_STEP 8
-// #define RGBLIGHT_SAT_STEP 8
-// #define RGBLIGHT_VAL_STEP 8
+//   #define RGBLED_NUM 16
+//   #define RGBLIGHT_HUE_STEP 8
+//   #define RGBLIGHT_SAT_STEP 8
+//   #define RGBLIGHT_VAL_STEP 8
+//   #define RGBLIGHT_LIMIT_VAL 255 /* The maximum brightness level */
+//   #define RGBLIGHT_SLEEP  /* If defined, the RGB lighting will be switched off when the host goes to sleep */
+// /*== all animations enable ==*/
+//   #define RGBLIGHT_ANIMATIONS
+// /*== or choose animations ==*/
+//   #define RGBLIGHT_EFFECT_BREATHING
+//   #define RGBLIGHT_EFFECT_RAINBOW_MOOD
+//   #define RGBLIGHT_EFFECT_RAINBOW_SWIRL
+//   #define RGBLIGHT_EFFECT_SNAKE
+//   #define RGBLIGHT_EFFECT_KNIGHT
+//   #define RGBLIGHT_EFFECT_CHRISTMAS
+//   #define RGBLIGHT_EFFECT_STATIC_GRADIENT
+//   #define RGBLIGHT_EFFECT_RGB_TEST
+//   #define RGBLIGHT_EFFECT_ALTERNATING
 // #endif
 
 /* Debounce reduces chatter (unintended double-presses) - set 0 if debouncing is not needed */
@@ -222,3 +240,6 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #endif
 */
 
+/* Bootmagic Lite key configuration */
+// #define BOOTMAGIC_LITE_ROW 0
+// #define BOOTMAGIC_LITE_COLUMN 0
diff --git a/quantum/template/avr/readme.md b/quantum/template/avr/readme.md
index d9349811d0..d243c66a94 100644
--- a/quantum/template/avr/readme.md
+++ b/quantum/template/avr/readme.md
@@ -12,4 +12,4 @@ Make example for this keyboard (after setting up your build environment):
 
     make %KEYBOARD%:default
 
-See [build environment setup](https://docs.qmk.fm/build_environment_setup.html) then the [make instructions](https://docs.qmk.fm/make_instructions.html) for more information.
\ No newline at end of file
+See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
diff --git a/quantum/template/avr/rules.mk b/quantum/template/avr/rules.mk
index 92f3a03a92..383a3594b4 100644
--- a/quantum/template/avr/rules.mk
+++ b/quantum/template/avr/rules.mk
@@ -39,13 +39,24 @@ F_USB = $(F_CPU)
 OPT_DEFS += -DINTERRUPT_CONTROL_ENDPOINT
 
 
-# Boot Section Size in *bytes*
-#   Teensy halfKay   512
-#   Teensy++ halfKay 1024
-#   Atmel DFU loader 4096
-#   LUFA bootloader  4096
-#   USBaspLoader     2048
-OPT_DEFS += -DBOOTLOADER_SIZE=4096
+# Bootloader selection
+#   Teensy       halfkay
+#   Pro Micro    caterina
+#   Atmel DFU    atmel-dfu
+#   LUFA DFU     lufa-dfu
+#   QMK DFU      qmk-dfu
+#   atmega32a    bootloadHID
+BOOTLOADER = atmel-dfu
+
+
+# If you don't know the bootloader type, then you can specify the
+# Boot Section Size in *bytes* by uncommenting out the OPT_DEFS line
+#   Teensy halfKay      512
+#   Teensy++ halfKay    1024
+#   Atmel DFU loader    4096
+#   LUFA bootloader     4096
+#   USBaspLoader        2048
+# OPT_DEFS += -DBOOTLOADER_SIZE=4096
 
 
 # Build Options
diff --git a/quantum/template/avr/template.h b/quantum/template/avr/template.h
index 031efc9529..0d626ed50b 100644
--- a/quantum/template/avr/template.h
+++ b/quantum/template/avr/template.h
@@ -18,12 +18,14 @@
 
 #include "quantum.h"
 
-// This a shortcut to help you visually see your layout.
-// The following is an example using the Planck MIT layout
-// The first section contains all of the arguments representing the physical
-// layout of the board and position of the keys
-// The second converts the arguments into a two-dimensional array which 
-// represents the switch matrix. 
+/* This a shortcut to help you visually see your layout.
+ *
+ * The first section contains all of the arguments representing the physical
+ * layout of the board and position of the keys.
+ *
+ * The second converts the arguments into a two-dimensional array which
+ * represents the switch matrix.
+ */
 #define LAYOUT( \
     K00, K01, K02, \
       K10,  K11   \
diff --git a/quantum/template/base/keymaps/default/keymap.c b/quantum/template/base/keymaps/default/keymap.c
index 14a8bc0f28..5f0730c8a9 100644
--- a/quantum/template/base/keymaps/default/keymap.c
+++ b/quantum/template/base/keymaps/default/keymap.c
@@ -15,32 +15,40 @@
  */
 #include QMK_KEYBOARD_H
 
-const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
-[0] = LAYOUT( /* Base */
-  KC_A,  KC_1,  KC_H, \
-    KC_TAB,  KC_SPC   \
-),
+// Defines the keycodes used by our macros in process_record_user
+enum custom_keycodes {
+  QMKBEST = SAFE_RANGE,
+  QMKURL
 };
 
-const uint16_t PROGMEM fn_actions[] = {
-
+const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
+  [0] = LAYOUT( /* Base */
+    KC_A,  KC_1,  KC_H, \
+      KC_TAB,  KC_SPC   \
+  ),
 };
 
-const macro_t *action_get_macro(keyrecord_t *record, uint8_t id, uint8_t opt)
-{
-  // MACRODOWN only works in this function
-      switch(id) {
-        case 0:
-          if (record->event.pressed) {
-            register_code(KC_RSFT);
-          } else {
-            unregister_code(KC_RSFT);
-          }
-        break;
+bool process_record_user(uint16_t keycode, keyrecord_t *record) {
+  switch (keycode) {
+    case QMKBEST:
+      if (record->event.pressed) {
+        // when keycode QMKBEST is pressed
+        SEND_STRING("QMK is the best thing ever!");
+      } else {
+        // when keycode QMKBEST is released
       }
-    return MACRO_NONE;
-};
-
+      break;
+    case QMKURL:
+      if (record->event.pressed) {
+        // when keycode QMKURL is pressed
+        SEND_STRING("https://qmk.fm/" SS_TAP(X_ENTER));
+      } else {
+        // when keycode QMKURL is released
+      }
+      break;
+  }
+  return true;
+}
 
 void matrix_init_user(void) {
 
@@ -50,10 +58,6 @@ void matrix_scan_user(void) {
 
 }
 
-bool process_record_user(uint16_t keycode, keyrecord_t *record) {
-  return true;
-}
-
 void led_set_user(uint8_t usb_led) {
 
 }
diff --git a/quantum/template/ps2avrgb/config.h b/quantum/template/ps2avrgb/config.h
index d2c83781fa..4ff3513bc7 100644
--- a/quantum/template/ps2avrgb/config.h
+++ b/quantum/template/ps2avrgb/config.h
@@ -44,3 +44,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 /* key combination for command */
 #define IS_COMMAND() (keyboard_report->mods == (MOD_BIT(KC_LSHIFT) | MOD_BIT(KC_RSHIFT)))
+
+/* Bootmagic Lite key configuration */
+// #define BOOTMAGIC_LITE_ROW 0
+// #define BOOTMAGIC_LITE_COLUMN 0
diff --git a/quantum/template/ps2avrgb/readme.md b/quantum/template/ps2avrgb/readme.md
index 1dcbe6e755..feec722a52 100644
--- a/quantum/template/ps2avrgb/readme.md
+++ b/quantum/template/ps2avrgb/readme.md
@@ -41,4 +41,4 @@ macOS:
 4. Place your keyboard into reset. 
 5. Flash the board by typing `bootloadHID -r` followed by the path to your `.hex` file. 
 
-See [build environment setup](https://docs.qmk.fm/build_environment_setup.html) then the [make instructions](https://docs.qmk.fm/make_instructions.html) for more information.
+See the [build environment setup](https://docs.qmk.fm/#/getting_started_build_tools) and the [make instructions](https://docs.qmk.fm/#/getting_started_make_guide) for more information. Brand new to QMK? Start with our [Complete Newbs Guide](https://docs.qmk.fm/#/newbs).
diff --git a/quantum/template/ps2avrgb/rules.mk b/quantum/template/ps2avrgb/rules.mk
index 68d50aec67..85603f955c 100644
--- a/quantum/template/ps2avrgb/rules.mk
+++ b/quantum/template/ps2avrgb/rules.mk
@@ -31,8 +31,8 @@ F_CPU = 12000000
 BOOTLOADER = bootloadHID
 
 # build options
-BOOTMAGIC_ENABLE = yes
-MOUSEKEY_ENABLE = yes
+BOOTMAGIC_ENABLE = full
+MOUSEKEY_ENABLE = no
 EXTRAKEY_ENABLE = yes
 CONSOLE_ENABLE = yes
 COMMAND_ENABLE = yes
diff --git a/quantum/template/ps2avrgb/template.h b/quantum/template/ps2avrgb/template.h
index c3924ee71f..b4d6f46624 100644
--- a/quantum/template/ps2avrgb/template.h
+++ b/quantum/template/ps2avrgb/template.h
@@ -18,10 +18,14 @@
 
 #include "quantum.h"
 
-// This a shortcut to help you visually see your layout.
-// The following is an example using the Planck MIT layout
-// The first section contains all of the arguments
-// The second converts the arguments into a two-dimensional array
+/* This a shortcut to help you visually see your layout.
+ *
+ * The first section contains all of the arguments representing the physical
+ * layout of the board and position of the keys.
+ *
+ * The second converts the arguments into a two-dimensional array which
+ * represents the switch matrix.
+ */
 #define LAYOUT( \
     k00, k01, k02, \
       k10,  k11   \