diff --git a/components/badge23/CMakeLists.txt b/components/badge23/CMakeLists.txt
index 60c1009dc4d3c9cb7e88047872d1703be1b64ee1..d72c6ee2f838a701fdffc4dbbb6447a6f41a3b99 100644
--- a/components/badge23/CMakeLists.txt
+++ b/components/badge23/CMakeLists.txt
@@ -8,6 +8,7 @@ idf_component_register(
         leds.c
         scope.c
         synth.c
+        spio.c
     INCLUDE_DIRS
         include
     REQUIRES
diff --git a/components/badge23/captouch.c b/components/badge23/captouch.c
index f82e97897f235c88a02597dd1bfc9aa6304c7bb2..8c248c3d88a58f9f72dde7abed9b403d2b141bb6 100644
--- a/components/badge23/captouch.c
+++ b/components/badge23/captouch.c
@@ -168,27 +168,33 @@ static void IRAM_ATTR gpio_isr_handler(void* arg)
     xQueueSendFromISR(gpio_evt_queue, &chip, NULL);
 }
 
+static uint16_t pressed_top, pressed_bot;
+
+static void captouch_chip_readout(struct ad714x_chip * chip){
+    uint16_t pressed;
+    ad714x_i2c_read(chip, 9, &pressed, 1);
+    ESP_LOGI(TAG, "Addr %x, High interrupt %X", chip->addr, pressed);
+
+    pressed &= ((1 << chip->stages) - 1);
+
+    if(chip == &chip_top) pressed_top = pressed;
+    if(chip == &chip_bot) pressed_bot = pressed;
+}
+
 void manual_captouch_readout(uint8_t top)
 {
     struct ad714x_chip* chip = top ? (&chip_top) : (&chip_bot);
-    xQueueSend(gpio_evt_queue, &chip, NULL);
+    captouch_chip_readout(chip);
+    //xQueueSend(gpio_evt_queue, &chip, NULL);
 }
 
-static uint16_t pressed_top, pressed_bot;
 void gpio_event_handler(void* arg)
 {
     static unsigned long counter = 0;
     struct ad714x_chip* chip;
-    uint16_t pressed;
     while(true) {
         if(xQueueReceive(gpio_evt_queue, &chip, portMAX_DELAY)) {
-            ad714x_i2c_read(chip, 9, &pressed, 1);
-            ESP_LOGI(TAG, "Addr %x, High interrupt %X", chip->addr, pressed);
-
-            pressed &= ((1 << chip->stages) - 1);
-
-            if(chip == &chip_top) pressed_top = pressed;
-            if(chip == &chip_bot) pressed_bot = pressed;
+            captouch_chip_readout(chip);
         }
     }
 }
@@ -306,7 +312,7 @@ void captouch_init(void)
                                                  });
 
     gpio_evt_queue = xQueueCreate(10, sizeof(const struct ad714x_chip*));
-    xTaskCreate(gpio_event_handler, "gpio_event_handler", 2048 * 2, NULL, configMAX_PRIORITIES - 2, NULL);
+    //xTaskCreate(gpio_event_handler, "gpio_event_handler", 2048 * 2, NULL, configMAX_PRIORITIES - 2, NULL);
 }
 
 static void captouch_print_debug_info_chip(const struct ad714x_chip* chip)
diff --git a/components/badge23/espan.c b/components/badge23/espan.c
index 7d7afcf88976e501c154a71aa967b29d704761fd..c10c44c632b9615127c22efa6f0ddc3579c0e79b 100644
--- a/components/badge23/espan.c
+++ b/components/badge23/espan.c
@@ -2,6 +2,7 @@
 #include "badge23/audio.h"
 #include "badge23/leds.h"
 #include "badge23/display.h"
+#include "badge23/spio.h"
 #include "../../../revision_config.h"
 
 #include "esp_log.h"
@@ -58,6 +59,7 @@ void os_app_main(void)
     set_global_vol_dB(-90);
     audio_init();
     leds_init();
+    init_buttons();
     captouch_init();
 
     vTaskDelay(2000 / portTICK_PERIOD_MS);
@@ -69,6 +71,8 @@ void os_app_main(void)
         vTaskDelay((CAPTOUCH_POLLING_PERIOD) / portTICK_PERIOD_MS);
         manual_captouch_readout(0);
         vTaskDelay((CAPTOUCH_POLLING_PERIOD) / portTICK_PERIOD_MS);
+        update_button_state();
+        vTaskDelay((CAPTOUCH_POLLING_PERIOD) / portTICK_PERIOD_MS);
         //display_draw_scope();
     }
 
diff --git a/components/badge23/include/badge23/spio.h b/components/badge23/include/badge23/spio.h
new file mode 100644
index 0000000000000000000000000000000000000000..7dfe394423ea7899e6f4562f298dc04234c8e862
--- /dev/null
+++ b/components/badge23/include/badge23/spio.h
@@ -0,0 +1,6 @@
+#pragma once
+#include <stdint.h>
+
+int8_t get_button_state(bool leftbutton);
+void update_button_state();
+void init_buttons();
diff --git a/components/badge23/spio.c b/components/badge23/spio.c
new file mode 100644
index 0000000000000000000000000000000000000000..4dca5c3612acfc7b934f7c247e4bd57e17cd4f2e
--- /dev/null
+++ b/components/badge23/spio.c
@@ -0,0 +1,140 @@
+//special purpose input outputs
+#include "driver/gpio.h"
+#include "../../../revision_config.h"
+#include "stdint.h"
+
+static int8_t leftbutton = 0;
+static int8_t rightbutton = 0;
+
+typedef
+
+#ifdef HARDWARE_REVISION_01
+
+#define RIGHT_BUTTON_LEFT 37
+#define RIGHT_BUTTON_MID 0
+#define RIGHT_BUTTON_RIGHT 35
+
+#define LEFT_BUTTON_LEFT 7
+#define LEFT_BUTTON_MID 6
+#define LEFT_BUTTON_RIGHT 5
+
+static void _init_buttons(){
+    //configure all buttons as pullup
+    uint64_t mask = 0;
+    mask |= (1 << RIGHT_BUTTON_LEFT);
+    mask |= (1 << RIGHT_BUTTON_RIGHT);
+    mask |= (1 << LEFT_BUTTON_LEFT);
+    mask |= (1 << LEFT_BUTTON_MID);
+    mask |= (1 << LEFT_BUTTON_RIGHT);
+    gpio_config_t cfg = {
+        .pin_bit_mask = mask,
+        .mode = GPIO_MODE_INPUT,
+        .pull_up_en = GPIO_PULLUP_ENABLE,
+        .pull_down_en = GPIO_PULLDOWN_DISABLE,
+        .intr_type = GPIO_INTR_DISABLE
+    };
+    ESP_ERROR_CHECK(gpio_config(&cfg));
+    cfg.pin_bit_mask = 1;
+    cfg.pull_up_en = GPIO_PULLUP_DISABLE;
+    ESP_ERROR_CHECK(gpio_config(&cfg));
+}
+
+void update_button_state(){
+    if(!gpio_get_level(RIGHT_BUTTON_LEFT)){
+        rightbutton = -1;
+    } else if(!gpio_get_level(RIGHT_BUTTON_MID)){
+        rightbutton = 2;
+    } else if(!gpio_get_level(RIGHT_BUTTON_RIGHT)){
+        rightbutton = 1;
+    } else {
+        rightbutton = 0;
+    }
+
+    if(!gpio_get_level(LEFT_BUTTON_LEFT)){
+        leftbutton = -1;
+    } else if(!gpio_get_level(LEFT_BUTTON_MID)){
+        leftbutton = 2;
+    } else if(!gpio_get_level(LEFT_BUTTON_RIGHT)){
+        leftbutton = 1;
+    } else {
+        leftbutton = 0;
+    }
+}
+#endif
+
+#ifdef HARDWARE_REVISION_04
+
+#include "driver/i2c.h"
+#define I2C_MASTER_NUM 0
+#define TIMEOUT_MS 1000
+
+//on ESP32
+#define LEFT_BUTTON_LEFT 3
+#define LEFT_BUTTON_MID 0
+
+//on PORTEXPANDER
+#define LEFT_BUTTON_RIGHT 0
+#define RIGHT_BUTTON_LEFT 6
+#define RIGHT_BUTTON_MID 5
+#define RIGHT_BUTTON_RIGHT 7
+
+static void _init_buttons(){
+    //configure all buttons as pullup
+    gpio_config_t cfg = {
+        .pin_bit_mask = 1 << LEFT_BUTTON_LEFT,
+        .mode = GPIO_MODE_INPUT,
+        .pull_up_en = GPIO_PULLUP_ENABLE,
+        .pull_down_en = GPIO_PULLDOWN_DISABLE,
+        .intr_type = GPIO_INTR_DISABLE
+    };
+    ESP_ERROR_CHECK(gpio_config(&cfg));
+    cfg.pin_bit_mask = 1;
+    cfg.pull_up_en = GPIO_PULLUP_DISABLE;
+    ESP_ERROR_CHECK(gpio_config(&cfg));
+    printf("nya");
+}
+
+void update_button_state(){
+    uint8_t port;
+    esp_err_t ret = i2c_master_read_from_device(I2C_MASTER_NUM, 0b1101101, &port, sizeof(port), TIMEOUT_MS / portTICK_PERIOD_MS);
+    uint8_t rr = port & (1 << RIGHT_BUTTON_RIGHT);
+    uint8_t rm = port & (1 << RIGHT_BUTTON_MID);
+    uint8_t rl = port & (1 << RIGHT_BUTTON_LEFT);
+    uint8_t lr = port & (1 << LEFT_BUTTON_RIGHT);
+    uint8_t ll = gpio_get_level(LEFT_BUTTON_LEFT);
+    uint8_t lm = gpio_get_level(LEFT_BUTTON_MID);
+
+    if(!rl){
+        rightbutton = -1;
+    } else if(!rm){
+        rightbutton = 2;
+    } else if(!rr){
+        rightbutton = 1;
+    } else {
+        rightbutton = 0;
+    }
+
+    if(!ll){
+        leftbutton = -1;
+    } else if(!lm){
+        leftbutton = 2;
+    } else if(!lr){
+        leftbutton = 1;
+    } else {
+        leftbutton = 0;
+    }
+}
+#endif
+
+void init_buttons(){
+    _init_buttons();
+}
+
+//#define ALWAYS_UPDATE_BUTTON
+int8_t get_button_state(bool left){
+#ifdef ALWAYS_UPDATE_BUTTON
+    update_button_state();
+#endif
+    if(left) return leftbutton;
+    return rightbutton;
+}
diff --git a/components/gc9a01/Kconfig b/components/gc9a01/Kconfig
deleted file mode 100644
index 5d8d28900b756d62d0eef53e353ea64717d15b57..0000000000000000000000000000000000000000
--- a/components/gc9a01/Kconfig
+++ /dev/null
@@ -1,121 +0,0 @@
-menu "GC9A01 LCD Config"
-    config GC9A01_Width
-        int "GC9A01 LCD Width"
-        range 64 1024
-        default 240
-
-    config GC9A01_Height
-        int "GC9A01 LCD Height"
-        range 64 1024
-        default 240
-    #---------------------------------------------
-    choice GC9A01_SPI_HOST
-        prompt "GC9A01 SPI HOST"
-        default USE_SPI3_HOST
-        help
-            Hardware SPI , HSPI=SPI2 , VSPI=SPI3
-
-        config USE_SPI1_HOST
-            bool "USE SPI1 HOST"
-        config USE_SPI2_HOST
-            bool "USE SPI2 HOST"
-        config USE_SPI3_HOST
-            bool "USE SPI3 HOST"
-        config USE_SPI4_HOST
-            bool "USE SPI4 HOST"
-    endchoice
-
-    config GC9A01_SPI_HOST
-        int
-        default 0 if USE_SPI1_HOST
-        default 1 if USE_SPI2_HOST
-        default 2 if USE_SPI3_HOST
-        default 3 if USE_SPI4_HOST
-    #---------------------------------------------
-
-    config GC9A01_PIN_NUM_SCK
-        int "LCD SPI SCK Pin"
-        range 0 48
-        default 18
-        help
-            Must Support SPI SCK
-
-    config GC9A01_PIN_NUM_MOSI
-        int "LCD SPI MOSI Pin"
-        range 0 48
-        default 23
-        help
-            Must Support SPI MOSI
-
-    config GC9A01_PIN_NUM_CS
-        int "LCD SPI CS Pin"
-        range 0 48
-        default 05
-
-    config GC9A01_PIN_NUM_DC
-        int "LCD DC(Data or Command) GPIO Pin Number"
-        range 0 48
-        default 21
-
-    config GC9A01_SPI_SCK_FREQ_M
-        int "SPI Clock Freq in MHz"
-        range 1 80
-        default 40
-
-    config GC9A01_CONTROL_BACK_LIGHT_USED
-        bool "LCD Control Back Light"
-        default y
-
-    config GC9A01_PIN_NUM_BCKL
-        int "LCD BL(Back Light) GPIO Pin Number"
-        depends on GC9A01_CONTROL_BACK_LIGHT_USED
-        range 0 48
-        default 19
-
-    #---------------------------------------------
-    choice GC9A01_CONTROL_BACK_LIGHT_MODE
-        prompt "GC9A01b Control Back Light Mode"
-        default GC9A01_BACK_LIGHT_MODE_PWM
-        depends on GC9A01_CONTROL_BACK_LIGHT_USED
-        help
-            PWM use LEDC_TIMER_0 and LEDC_CHANNEL_0
-
-        config GC9A01_BACK_LIGHT_MODE_On_OFF
-            bool "USE GPIO ON/OF"
-        config GC9A01_BACK_LIGHT_MODE_PWM
-            bool "USE PWM"
-    endchoice
-
-    config GC9A01_CONTROL_BACK_LIGHT_MODE
-        int
-        default 0 if GC9A01_BACK_LIGHT_MODE_On_OFF
-        default 1 if GC9A01_BACK_LIGHT_MODE_PWM
-    #---------------------------------------------
-
-    config GC9A01_RESET_USED
-        bool "GC9A01 RESET Pin Used"
-        default y
-        help
-            Use GC9A01 Hard Reset Pin
-
-    config GC9A01_PIN_NUM_RST
-        int "LCD RST GPIO Pin Number"
-        depends on GC9A01_RESET_USED
-        range 0 48
-        default 22
-
-    config GC9A01_BUFFER_MODE
-        bool "Enable Buffer Mode"
-        default y
-        help
-            Disable for Direct Mode
-
-    config GC9A01_BUFFER_SCREEN_FAST_MODE
-        bool "Don't Convert Buffer for Screen_Load() & Screen_Shot()"
-        default n
-        depends on GC9A01_BUFFER_MODE
-        help
-            If Enabled , the Screen_load() & Screen_Shot() don't run SwapBytes() for Buffer
-            & Direct Save Data to SPI Buffer,So the data must manually SwapBytes before Send to LCD.
-
-endmenu
diff --git a/sdkconfig b/sdkconfig
index 17716ee97036e7029d656c61a1370daaae301b92..c742db8e1db36e32b0669d8f2e9c29216aba31bc 100644
--- a/sdkconfig
+++ b/sdkconfig
@@ -1357,28 +1357,6 @@ CONFIG_WPA_MBEDTLS_CRYPTO=y
 # CONFIG_WPA_WPS_STRICT is not set
 # CONFIG_WPA_11KV_SUPPORT is not set
 # end of Supplicant
-
-#
-# GC9A01 LCD Config
-#
-CONFIG_GC9A01_Width=240
-CONFIG_GC9A01_Height=240
-# CONFIG_USE_SPI1_HOST is not set
-# CONFIG_USE_SPI2_HOST is not set
-CONFIG_USE_SPI3_HOST=y
-# CONFIG_USE_SPI4_HOST is not set
-CONFIG_GC9A01_SPI_HOST=2
-CONFIG_GC9A01_PIN_NUM_SCK=39
-CONFIG_GC9A01_PIN_NUM_MOSI=41
-CONFIG_GC9A01_PIN_NUM_CS=40
-CONFIG_GC9A01_PIN_NUM_DC=42
-CONFIG_GC9A01_SPI_SCK_FREQ_M=80
-# CONFIG_GC9A01_CONTROL_BACK_LIGHT_USED is not set
-CONFIG_GC9A01_RESET_USED=y
-CONFIG_GC9A01_PIN_NUM_RST=38
-CONFIG_GC9A01_BUFFER_MODE=y
-# CONFIG_GC9A01_BUFFER_SCREEN_FAST_MODE is not set
-# end of GC9A01 LCD Config
 # end of Component config
 
 #
diff --git a/usermodule/mp_hardware.c b/usermodule/mp_hardware.c
index e0e5c7e4d49e7fa031d1fe8f3021445b2e63e639..2c941b2ceccb0b4f37ef82b8fb63e961fdf232a6 100644
--- a/usermodule/mp_hardware.c
+++ b/usermodule/mp_hardware.c
@@ -15,6 +15,7 @@
 #include "badge23/leds.h"
 #include "badge23/captouch.h"
 #include "badge23/display.h"
+#include "badge23/spio.h"
 
 STATIC mp_obj_t mp_display_update(size_t n_args, const mp_obj_t *args) {
     display_update();
@@ -60,6 +61,13 @@ STATIC mp_obj_t mp_captouch_autocalib(size_t n_args, const mp_obj_t *args) {
 }
 STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_captouch_autocalib_obj, 0, 2, mp_captouch_autocalib);
 
+STATIC mp_obj_t mp_get_button(size_t n_args, const mp_obj_t *args) {
+    uint8_t leftbutton = mp_obj_get_int(args[0]);
+    int8_t ret = get_button_state(leftbutton);
+    return mp_obj_new_int(ret);
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_get_button_obj, 1, 2, mp_get_button);
+
 STATIC mp_obj_t mp_set_global_volume_dB(size_t n_args, const mp_obj_t *args) {
     mp_float_t x = mp_obj_get_float(args[0]);
     int8_t d = x;
@@ -115,6 +123,7 @@ STATIC const mp_rom_map_elem_t mp_module_hardware_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_badge_audio) },
     { MP_ROM_QSTR(MP_QSTR_get_captouch), MP_ROM_PTR(&mp_get_captouch_obj) },
     { MP_ROM_QSTR(MP_QSTR_captouch_autocalib), MP_ROM_PTR(&mp_captouch_autocalib_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_button), MP_ROM_PTR(&mp_get_button_obj) },
     { MP_ROM_QSTR(MP_QSTR_set_global_volume_dB), MP_ROM_PTR(&mp_set_global_volume_dB_obj) },
     { MP_ROM_QSTR(MP_QSTR_count_sources), MP_ROM_PTR(&mp_count_sources_obj) },
     { MP_ROM_QSTR(MP_QSTR_dump_all_sources), MP_ROM_PTR(&mp_dump_all_sources_obj) },