diff --git a/.cproject b/.cproject
index b30f521..3add275 100644
--- a/.cproject
+++ b/.cproject
@@ -1,17 +1,34 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/clock/CMakeLists.txt b/components/clock/CMakeLists.txt
new file mode 100644
index 0000000..34a99b2
--- /dev/null
+++ b/components/clock/CMakeLists.txt
@@ -0,0 +1,3 @@
+idf_component_register(SRCS "clock.c"
+ INCLUDE_DIRS "."
+ REQUIRES )
\ No newline at end of file
diff --git a/components/clock/clock.c b/components/clock/clock.c
new file mode 100644
index 0000000..6ec8348
--- /dev/null
+++ b/components/clock/clock.c
@@ -0,0 +1,58 @@
+/*
+ * clock.c
+ *
+ * Created on: Apr 23, 2020
+ * Author: chris
+ */
+
+#include "clock.h"
+
+const char* TAG = "clock";
+
+void time_sync_notification_cb(struct timeval *tv)
+{
+ ESP_LOGI(TAG, "Synchronized time to PTB!");
+}
+
+esp_err_t init_clock()
+{
+ ESP_LOGI(TAG, "Initializing SNTP");
+ esp_event_loop_create_default();
+ sntp_setoperatingmode(SNTP_OPMODE_POLL);
+ sntp_setservername(0, "ptbtime1.ptb.de");
+ sntp_set_time_sync_notification_cb(time_sync_notification_cb);
+ sntp_set_sync_mode(SNTP_SYNC_MODE_IMMED);
+ sntp_init();
+
+ // wait for time to be set
+ time_t now = 0;
+ struct tm timeinfo = { 0 };
+ int retry = 0;
+ const int retry_count = 10;
+ while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
+ ESP_LOGI(TAG, "Waiting for system time to be set... (%d/%d)", retry, retry_count);
+ vTaskDelay(2000 / portTICK_PERIOD_MS);
+ }
+ time(&now);
+ localtime_r(&now, &timeinfo);
+
+ setenv("TZ", "CET-1CEST-2,M3.5.0/02:00:00,M10.5.0/03:00:00", 1);
+ tzset();
+
+ return ESP_OK;
+}
+
+void get_time(time_str_t* res)
+{
+ time_t now;
+ struct tm timeinfo;
+ time(&now);
+ localtime_r(&now, &timeinfo);
+
+ char date_str[64];
+ char time_str[64];
+ strftime(date_str, sizeof(date_str), "%d.%m.%Y", &timeinfo);
+ strftime(time_str, sizeof(time_str), "%H:%M:%S", &timeinfo);
+ strcpy(res->date_str, date_str);
+ strcpy(res->time_str, time_str);
+}
diff --git a/components/clock/clock.h b/components/clock/clock.h
new file mode 100644
index 0000000..e395bdf
--- /dev/null
+++ b/components/clock/clock.h
@@ -0,0 +1,28 @@
+/*
+ * clock.h
+ *
+ * Created on: Apr 23, 2020
+ * Author: chris
+ */
+
+#ifndef COMPONENTS_CLOCK_CLOCK_H_
+#define COMPONENTS_CLOCK_CLOCK_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include "esp_event.h"
+
+typedef struct {
+ char date_str[64];
+ char time_str[64];
+} time_str_t;
+
+esp_err_t init_clock();
+
+void get_time(time_str_t* res);
+
+#endif /* COMPONENTS_CLOCK_CLOCK_H_ */
diff --git a/components/display/CMakeLists.txt b/components/display/CMakeLists.txt
new file mode 100644
index 0000000..c471279
--- /dev/null
+++ b/components/display/CMakeLists.txt
@@ -0,0 +1,3 @@
+idf_component_register(SRCS "display.c"
+ INCLUDE_DIRS "."
+ REQUIRES tft clock)
\ No newline at end of file
diff --git a/components/display/display.c b/components/display/display.c
new file mode 100644
index 0000000..31923e5
--- /dev/null
+++ b/components/display/display.c
@@ -0,0 +1,347 @@
+/*
+ * display.c
+ *
+ * Created on: 19 Apr 2020
+ * Author: Chris
+ */
+
+#include "display.h"
+
+#include "tftspi.h"
+#include "tft.h"
+
+#define SPI_BUS TFT_VSPI_HOST
+
+typedef struct {
+ int datetimeW;
+ int datetimeA;
+ int dateTimeLeft;
+ int datetimeBaseline;
+ int innenW;
+ int aussenW;
+ int locA;
+ int tempW;
+ int degCW;
+ int tempA;
+ int humW;
+ int percentW;
+ int humA;
+ int pressW;
+ int pressA;
+ int hpaW;
+ int datetimebarH;
+ int margin;
+ int borderVMargin;
+ int innenBaseline;
+ int aussenBaseline;
+ int pressBaseline;
+ int humBaseline;
+ int tempBaseline;
+ int unitMaxW;
+ int unitW;
+ int ioW;
+ int unitLeft;
+ int innenLeft;
+ int aussenLeft;
+} layout_t;
+
+static layout_t layout;
+
+layout_t create_layout()
+{
+ layout_t layout;
+ int screenW = tft_width;
+ int screenH = tft_height;
+ layout.datetimebarH = 20; // Can be calculated too
+ layout.margin = 5;
+ layout.borderVMargin = 10;
+
+ TFT_setFont(SMALL_FONT, NULL);
+ layout.datetimebarH = 20;
+ layout.datetimeW = TFT_getStringWidth("25.12.2031 08:31:11");
+ layout.datetimeA = TFT_getfontheight();
+ layout.dateTimeLeft = (screenW - layout.datetimeW) / 2;
+ layout.datetimeBaseline = (layout.datetimebarH - layout.datetimeA) / 2;
+
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ layout.innenW = TFT_getStringWidth("Innen");
+ layout.aussenW = TFT_getStringWidth("Aussen");
+ layout.locA = TFT_getfontheight();
+
+ TFT_setFont(DEJAVU18_FONT, NULL);
+ layout.tempW = TFT_getStringWidth("-35.2");
+ layout.degCW = TFT_getStringWidth(" C");
+ layout.tempA = TFT_getfontheight();
+
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ layout.humW = TFT_getStringWidth("25.2");
+ layout.percentW = TFT_getStringWidth("%");
+ layout.humA = TFT_getfontheight();
+
+ layout.pressW = TFT_getStringWidth("1281");
+ layout.pressA = TFT_getfontheight();
+ layout.hpaW = TFT_getStringWidth("hPa");
+
+ layout.innenBaseline = layout.datetimebarH + layout.borderVMargin;
+ layout.aussenBaseline = layout.innenBaseline;
+ layout.pressBaseline = screenH - layout.borderVMargin - layout.pressA;
+ layout.humBaseline = layout.pressBaseline - layout.pressA - layout.margin;
+ layout.tempBaseline = (layout.humBaseline - layout.humA + layout.innenBaseline) / 2
+ + 0.5*layout.tempA - 1;
+
+ int unitMaxW = layout.degCW;
+ if (layout.percentW > unitMaxW) unitMaxW = layout.percentW;
+ if (layout.hpaW > unitMaxW) unitMaxW = layout.hpaW;
+ layout.unitW = 2*layout.margin + unitMaxW;
+ layout.ioW = (screenW - layout.unitW) / 2;
+ layout.unitLeft = screenW - layout.unitW + layout.margin;
+ layout.innenLeft = layout.margin;
+ layout.aussenLeft = layout.innenLeft + layout.ioW;
+
+ ESP_LOGI("main", "innen: %d x %d, aussen: %d x %d", layout.innenW,
+ layout.locA, layout.aussenW, layout.locA);
+ ESP_LOGI("main", "temps: %d x %d, unit: %d x %d", layout.tempW,
+ layout.tempA, layout.degCW, layout.tempA);
+ ESP_LOGI("main", "hum: %d x %d, unit: %d x %d", layout.humW, layout.humA,
+ layout.percentW, layout.humA);
+ ESP_LOGI("main", "press: %d x %d, unit: %d x %d", layout.pressW, layout.pressA,
+ layout.hpaW, layout.pressA);
+
+ ESP_LOGI("main", "Baselines - innen: %d, aussen: %d, press: %d, hum: %d, temp: %d",
+ layout.innenBaseline, layout.aussenBaseline,
+ layout.pressBaseline, layout.humBaseline, layout.tempBaseline);
+ ESP_LOGI("main", "Width - units: %d, i/o: %d", layout.unitW, layout.ioW);
+ ESP_LOGI("main", "Left - units: %d", layout.unitLeft);
+ return layout;
+}
+
+uint8_t interpolation(int32_t arg) {
+ int32_t i32_val = arg * 256 / 1000;
+ uint8_t u8_val = (uint8_t)i32_val;
+ return u8_val;
+}
+
+color_t temp_color(int32_t temp_raw) {
+ color_t col = { 0 };
+ if (temp_raw >= 3500)
+ col.r = 255;
+ else if (temp_raw < 3500 && temp_raw > 2500) {
+ int32_t diff = 3500 - temp_raw;
+ col.r = 255;
+ col.g = interpolation(diff);
+ }
+ else if (temp_raw == 2500) {
+ col.r = 255;
+ col.g = 255;
+ }
+ else if (temp_raw < 2500 && temp_raw > 1500) {
+ int32_t diff = temp_raw - 1500;
+ col.r = interpolation(diff);
+ col.g = 255;
+ }
+ else if (temp_raw == 1500)
+ col.g = 255;
+ else if (temp_raw < 1500 && temp_raw > 500) {
+ int32_t diff = 1500 - temp_raw;
+ col.g = 255;
+ col.b = interpolation(diff);
+ }
+ else if (temp_raw == 500) {
+ col.g = 255;
+ col.b = 255;
+ }
+ else if (temp_raw < 500 && temp_raw > -500) {
+ int32_t diff = temp_raw + 500;
+ col.g = interpolation(diff);
+ col.b = 255;
+ }
+ else if (temp_raw == -500)
+ col.b = 255;
+ else if (temp_raw < -500 && temp_raw > -1500) {
+ int32_t diff = -500 - temp_raw;
+ col.r = interpolation(diff);
+ col.b = 255;
+ }
+ else if (temp_raw <= -1500) {
+ col.r = 255;
+ col.b = 255;
+ }
+ return col;
+}
+
+void print_temp1(int32_t temp_raw)
+{
+ // Calc temperature pre and post comma values
+ int32_t temp_pre = temp_raw / 100;
+ int32_t temp_post = (abs(temp_raw) % 100) / 10;
+ char temp_str[12];
+ sprintf(temp_str, "% 2.2d,%.1d", temp_pre, temp_post);
+
+ TFT_setFont(DEJAVU18_FONT, NULL);
+ tft_fg = temp_color(temp_raw);
+ TFT_print(temp_str, layout.innenLeft, layout.tempBaseline);
+}
+
+void print_temp2(int32_t temp_raw)
+{
+ // Calc temperature pre and post comma values
+ int32_t temp_pre = temp_raw / 100;
+ int32_t temp_post = (abs(temp_raw) % 100) / 10;
+ char temp_str[12];
+ sprintf(temp_str, "% 2.2d,%.1d", temp_pre, temp_post);
+
+ TFT_setFont(DEJAVU18_FONT, NULL);
+ tft_fg = temp_color(temp_raw);
+ TFT_print(temp_str, layout.aussenLeft, layout.tempBaseline);
+}
+
+void print_press1(uint32_t pressure_raw)
+{
+ // Calc pressure values
+ uint32_t press = pressure_raw / 100;
+ char press_str[12];
+ sprintf(press_str, "%d", press);
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ tft_fg = TFT_WHITE;
+ TFT_print(press_str, layout.innenLeft, layout.pressBaseline);
+}
+
+void print_press2(uint32_t pressure_raw)
+{
+ // Calc pressure values
+ uint32_t press = pressure_raw / 100;
+ char press_str[12];
+ sprintf(press_str, "%d", press);
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ tft_fg = TFT_WHITE;
+ TFT_print(press_str, layout.aussenLeft, layout.pressBaseline);
+}
+
+void print_humid1(uint32_t humidity_raw)
+{
+ // Calc humidity pre and post comma values
+ uint32_t humid_pre = humidity_raw / 1024;
+ uint32_t humid_post = (humidity_raw - humid_pre*1024) * 10 / 1024;
+ char humid_str[12];
+ sprintf(humid_str, "%2.2d,%.1d", humid_pre, humid_post);
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ tft_fg = TFT_WHITE;
+ TFT_print(humid_str, layout.innenLeft, layout.humBaseline);
+}
+
+void print_humid2(uint32_t humidity_raw)
+{
+ // Calc humidity pre and post comma values
+ uint32_t humid_pre = humidity_raw / 1024;
+ uint32_t humid_post = (humidity_raw - humid_pre*1024) * 10 / 1024;
+ char humid_str[12];
+ sprintf(humid_str, "%2.2d,%.1d", humid_pre, humid_post);
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ tft_fg = TFT_WHITE;
+ TFT_print(humid_str, layout.aussenLeft, layout.humBaseline);
+}
+
+void print_time(time_str_t time)
+{
+ TFT_setFont(SMALL_FONT, NULL);
+ char datetime_str[129];
+ sprintf(datetime_str, "%s %s", time.date_str, time.time_str);
+ TFT_print(datetime_str, layout.dateTimeLeft, layout.datetimeBaseline);
+}
+
+void display_data(int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
+ int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw,
+ time_str_t time)
+{
+ // HEADER
+ tft_fg = TFT_WHITE;
+ TFT_drawFastHLine(0, 20, 160, TFT_WHITE);
+
+ // IN OUT LABEL
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ TFT_print("Innen", layout.innenLeft, layout.innenBaseline);
+ TFT_print("Aussen", layout.aussenLeft, layout.aussenBaseline);
+
+ // VALUES
+ print_temp1(temp_raw);
+ print_temp2(temp2_raw);
+ print_humid1(humidity_raw);
+ print_humid2(humidity2_raw);
+ print_press1(pressure_raw);
+ print_press2(pressure2_raw);
+ print_time(time);
+
+ // UNIT LABELS
+ TFT_setFont(DEJAVU18_FONT, NULL);
+ tft_fg = TFT_WHITE;
+ TFT_print(" C", layout.unitLeft, layout.tempBaseline);
+ TFT_drawCircle(layout.unitLeft+3, layout.tempBaseline+3, 3, TFT_WHITE);
+ TFT_setFont(UBUNTU16_FONT, NULL);
+ TFT_print("%", layout.unitLeft, layout.humBaseline);
+ TFT_print("hPa", layout.unitLeft, layout.pressBaseline);
+}
+
+void update_data(int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
+ int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw,
+ time_str_t time)
+{
+ // VALUES
+ print_temp1(temp_raw);
+ print_temp2(temp2_raw);
+ print_humid1(humidity_raw);
+ print_humid2(humidity2_raw);
+ print_press1(pressure_raw);
+ print_press2(pressure2_raw);
+ print_time(time);
+}
+
+esp_err_t init_display()
+{
+ tft_max_rdclock = 4000000;
+ TFT_PinsInit();
+ spi_lobo_device_handle_t spi;
+
+ spi_lobo_bus_config_t buscfg={
+ .miso_io_num=PIN_NUM_MISO,
+ .mosi_io_num=PIN_NUM_MOSI,
+ .sclk_io_num=PIN_NUM_CLK,
+ .quadwp_io_num=-1,
+ .quadhd_io_num=-1,
+ .max_transfer_sz = 6*1024,
+ };
+ spi_lobo_device_interface_config_t devcfg={
+ .clock_speed_hz=8000000,
+ .mode=0,
+ .spics_io_num=-1,
+ .spics_ext_io_num=PIN_NUM_CS,
+ .flags=LB_SPI_DEVICE_HALFDUPLEX,
+ };
+ esp_err_t ret;
+ ret=spi_lobo_bus_add_device(SPI_BUS, &buscfg, &devcfg, &spi);
+ if (ret != ESP_OK)
+ {
+ return ret;
+ }
+ tft_disp_spi = spi;
+
+ TFT_display_init();
+
+ tft_max_rdclock = find_rd_speed();
+
+ spi_lobo_set_speed(spi, DEFAULT_SPI_CLOCK);
+
+ tft_font_rotate = 0;
+ tft_text_wrap = 0;
+ tft_font_transparent = 0;
+ tft_font_forceFixed = 0;
+ tft_gray_scale = 0;
+ TFT_setGammaCurve(DEFAULT_GAMMA_CURVE);
+ TFT_setRotation(LANDSCAPE_FLIP);
+ TFT_setFont(DEFAULT_FONT, NULL);
+ TFT_resetclipwin();
+ tft_image_debug = 0;
+
+ layout = create_layout();
+
+ return ESP_OK;
+}
diff --git a/components/display/display.h b/components/display/display.h
new file mode 100644
index 0000000..8e19dbf
--- /dev/null
+++ b/components/display/display.h
@@ -0,0 +1,25 @@
+/*
+ * display.h
+ *
+ * Created on: 19 Apr 2020
+ * Author: Chris
+ */
+
+#ifndef COMPONENTS_DISPLAY_DISPLAY_H_
+#define COMPONENTS_DISPLAY_DISPLAY_H_
+
+#include
+#include
+#include
+#include
+#include "clock.h"
+
+esp_err_t init_display();
+void display_data(int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
+ int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw,
+ time_str_t time);
+void update_data(int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
+ int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw,
+ time_str_t time);
+
+#endif /* COMPONENTS_DISPLAY_DISPLAY_H_ */
diff --git a/components/sensors/CMakeLists.txt b/components/sensors/CMakeLists.txt
new file mode 100644
index 0000000..68a867f
--- /dev/null
+++ b/components/sensors/CMakeLists.txt
@@ -0,0 +1,3 @@
+idf_component_register(SRCS "bme280.c" "sensors.c"
+ INCLUDE_DIRS "."
+ REQUIRES driver )
\ No newline at end of file
diff --git a/main/bme280.c b/components/sensors/bme280.c
similarity index 100%
rename from main/bme280.c
rename to components/sensors/bme280.c
diff --git a/main/bme280.h b/components/sensors/bme280.h
similarity index 100%
rename from main/bme280.h
rename to components/sensors/bme280.h
diff --git a/main/bme280_defs.h b/components/sensors/bme280_defs.h
similarity index 100%
rename from main/bme280_defs.h
rename to components/sensors/bme280_defs.h
diff --git a/components/sensors/sensors.c b/components/sensors/sensors.c
new file mode 100644
index 0000000..ee251c7
--- /dev/null
+++ b/components/sensors/sensors.c
@@ -0,0 +1,145 @@
+/*
+ * sensors.c
+ *
+ * Created on: 20 Apr 2020
+ * Author: Chris
+ */
+
+#include "sensors.h"
+
+static struct bme280_dev* dev;
+static struct bme280_dev* dev2;
+
+void i2c_setup()
+{
+ printf("Setting up I2C driver on port 1... ");
+ i2c_config_t config;
+ config.mode = I2C_MODE_MASTER;
+ config.sda_io_num = 33;
+ config.sda_pullup_en = GPIO_PULLUP_ENABLE;
+ config.scl_io_num = 32;
+ config.scl_pullup_en = GPIO_PULLUP_ENABLE;
+ config.master.clk_speed = 100000;
+ i2c_param_config(I2C_NUM_0, &config);
+ printf("Set driver parameters... ");
+ esp_err_t err = i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
+ if (err == ESP_OK)
+ printf("Driver installed!\n");
+ else if (err == ESP_ERR_INVALID_ARG)
+ printf("Driver install failed, invalid arguments!\n");
+ else
+ printf("Driver install failed!\n");
+}
+
+int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len) {
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, dev_id << 1 | I2C_MASTER_WRITE, 1);
+ i2c_master_write_byte(cmd, reg_addr, 1);
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, dev_id << 1 | I2C_MASTER_READ, 1);
+ if (len > 1) {
+ i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
+ }
+ i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
+ i2c_master_stop(cmd);
+ i2c_master_cmd_begin(I2C_NUM_0, cmd, 500 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+ return 0;
+}
+
+int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len) {
+ //printf("Writing to bus: dev_id=%x, reg_addr=%x, data=%p, length=%u\n", dev_id, reg_addr, data, len);
+ i2c_cmd_handle_t cmd = i2c_cmd_link_create();
+ i2c_master_start(cmd);
+ i2c_master_write_byte(cmd, (dev_id << 1) | I2C_MASTER_WRITE, 1);
+ i2c_master_write_byte(cmd, reg_addr, 1);
+ i2c_master_write(cmd, data, len, 1);
+ i2c_master_stop(cmd);
+ i2c_master_cmd_begin(I2C_NUM_0, cmd, 500 / portTICK_RATE_MS);
+ i2c_cmd_link_delete(cmd);
+ return 0;
+}
+
+void i2c_delay(uint32_t period) {
+ vTaskDelay(period / portTICK_PERIOD_MS);
+}
+
+void i2c_shutdown()
+{
+ printf("Shutting down I�C bus... ");
+ esp_err_t err = i2c_driver_delete(I2C_NUM_0);
+ if (err == ESP_ERR_INVALID_ARG)
+ printf("Failed, invalid arguments!\n");
+ else
+ printf("Success!\n");
+}
+
+void read_sensor(int32_t* temp, uint32_t* pressure, uint32_t* humidity) {
+
+ uint8_t settings_sel;
+ uint32_t req_delay;
+ struct bme280_data comp_data;
+ dev->settings.osr_h = BME280_OVERSAMPLING_16X;
+ dev->settings.osr_p = BME280_OVERSAMPLING_16X;
+ dev->settings.osr_t = BME280_OVERSAMPLING_16X;
+ dev->settings.filter = BME280_FILTER_COEFF_16;
+
+ settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
+ bme280_set_sensor_settings(settings_sel, dev);
+
+ req_delay = 12*bme280_cal_meas_delay(&(dev->settings));
+
+ bme280_set_sensor_mode(BME280_FORCED_MODE, dev);
+ dev->delay_ms(req_delay / portTICK_PERIOD_MS);
+ bme280_get_sensor_data(BME280_ALL, &comp_data, dev);
+ *temp = comp_data.temperature;
+ *pressure = comp_data.pressure;
+ *humidity = comp_data.humidity;
+}
+
+void read_sensor2(int32_t* temp, uint32_t* pressure, uint32_t* humidity) {
+
+ uint8_t settings_sel;
+ uint32_t req_delay;
+ struct bme280_data comp_data;
+
+ dev2->settings.osr_h = BME280_OVERSAMPLING_16X;
+ dev2->settings.osr_p = BME280_OVERSAMPLING_16X;
+ dev2->settings.osr_t = BME280_OVERSAMPLING_16X;
+ dev2->settings.filter = BME280_FILTER_COEFF_16;
+
+ settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
+ bme280_set_sensor_settings(settings_sel, dev2);
+
+ req_delay = 12*bme280_cal_meas_delay(&(dev2->settings));
+
+ bme280_set_sensor_mode(BME280_FORCED_MODE, dev2);
+ dev2->delay_ms(req_delay / portTICK_PERIOD_MS);
+ bme280_get_sensor_data(BME280_ALL, &comp_data, dev2);
+ *temp = comp_data.temperature;
+ *pressure = comp_data.pressure;
+ *humidity = comp_data.humidity;
+}
+
+void init_sensors()
+{
+ // INIT SENSOR
+ i2c_setup();
+ dev = malloc(sizeof(struct bme280_dev));
+ dev->dev_id = 0x76;
+ dev->intf = BME280_I2C_INTF;
+ dev->read = i2c_read;
+ dev->write = i2c_write;
+ dev->delay_ms = i2c_delay;
+ bme280_init(dev);
+
+ // INIT SENSOR2
+ dev2 = malloc(sizeof(struct bme280_dev));
+ dev2->dev_id = 0x77;
+ dev2->intf = BME280_I2C_INTF;
+ dev2->read = i2c_read;
+ dev2->write = i2c_write;
+ dev2->delay_ms = i2c_delay;
+ bme280_init(dev2);
+}
diff --git a/components/sensors/sensors.h b/components/sensors/sensors.h
new file mode 100644
index 0000000..b23870d
--- /dev/null
+++ b/components/sensors/sensors.h
@@ -0,0 +1,19 @@
+/*
+ * sensors.h
+ *
+ * Created on: 20 Apr 2020
+ * Author: Chris
+ */
+
+#ifndef COMPONENTS_SENSORS_SENSORS_H_
+#define COMPONENTS_SENSORS_SENSORS_H_
+
+#include "bme280.h"
+#include "bme280_defs.h"
+#include "driver/i2c.h"
+
+void init_sensors();
+void read_sensor2(int32_t* temp, uint32_t* pressure, uint32_t* humidity);
+void read_sensor(int32_t* temp, uint32_t* pressure, uint32_t* humidity);
+
+#endif /* COMPONENTS_SENSORS_SENSORS_H_ */
diff --git a/components/server/CMakeLists.txt b/components/server/CMakeLists.txt
new file mode 100644
index 0000000..3b99613
--- /dev/null
+++ b/components/server/CMakeLists.txt
@@ -0,0 +1,3 @@
+idf_component_register(SRCS "server.c"
+ INCLUDE_DIRS "."
+ REQUIRES esp_http_server freertos)
\ No newline at end of file
diff --git a/components/server/server.c b/components/server/server.c
new file mode 100644
index 0000000..745e996
--- /dev/null
+++ b/components/server/server.c
@@ -0,0 +1,70 @@
+/*
+ * server.c
+ *
+ * Created on: 21 Apr 2020
+ * Author: Chris
+ */
+
+#include "server.h"
+
+static const char *TAG = "server";
+char cur_value_str[255] = "No measurements yet!";
+
+void server_set_values(int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
+ int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw)
+{
+ sprintf(cur_value_str, "%d %d %d
%d %d %d
",
+ temp_raw, pressure_raw, humidity_raw, temp2_raw, pressure2_raw, humidity2_raw);
+}
+
+static esp_err_t get_handler(httpd_req_t *req)
+{
+ char* buf;
+ size_t buf_len;
+
+ /* Get header value string length and allocate memory for length + 1,
+ * extra byte for null termination */
+ buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
+ if (buf_len > 1) {
+ buf = malloc(buf_len);
+ /* Copy null terminated value string into buffer */
+ if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
+ ESP_LOGI(TAG, "Found header => Host: %s", buf);
+ }
+ free(buf);
+ }
+
+ const char* resp_str = cur_value_str;
+ httpd_resp_send(req, resp_str, strlen(resp_str));
+
+ return ESP_OK;
+}
+
+static httpd_uri_t uri = {
+ .uri = "/",
+ .method = HTTP_GET,
+ .handler = get_handler,
+};
+
+static httpd_handle_t start_webserver(void)
+{
+ httpd_handle_t server = NULL;
+ httpd_config_t config = HTTPD_DEFAULT_CONFIG();
+
+ // Start the httpd server
+ ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
+ if (httpd_start(&server, &config) == ESP_OK) {
+ // Set URI handlers
+ ESP_LOGI(TAG, "Registering URI handlers");
+ httpd_register_uri_handler(server, &uri);
+ return server;
+ }
+
+ ESP_LOGI(TAG, "Error starting server!");
+ return NULL;
+}
+
+void init_server()
+{
+ start_webserver();
+}
diff --git a/components/server/server.h b/components/server/server.h
new file mode 100644
index 0000000..af7e702
--- /dev/null
+++ b/components/server/server.h
@@ -0,0 +1,23 @@
+/*
+ * server.h
+ *
+ * Created on: 21 Apr 2020
+ * Author: Chris
+ */
+
+#ifndef COMPONENTS_SERVER_SERVER_H_
+#define COMPONENTS_SERVER_SERVER_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void init_server();
+void server_set_values(int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
+ int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw);
+
+#endif /* COMPONENTS_SERVER_SERVER_H_ */
diff --git a/components/spidriver/CMakeLists.txt b/components/spidriver/CMakeLists.txt
new file mode 100644
index 0000000..2422429
--- /dev/null
+++ b/components/spidriver/CMakeLists.txt
@@ -0,0 +1,2 @@
+FILE(GLOB SOURCES *.c)
+idf_component_register(SRCS ${SOURCES} INCLUDE_DIRS ".")
diff --git a/components/spidriver/component.mk b/components/spidriver/component.mk
new file mode 100644
index 0000000..0f76ee5
--- /dev/null
+++ b/components/spidriver/component.mk
@@ -0,0 +1,7 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+COMPONENT_SRCDIRS := .
+COMPONENT_ADD_INCLUDEDIRS := .
diff --git a/components/spidriver/spi_master_lobo.c b/components/spidriver/spi_master_lobo.c
new file mode 100644
index 0000000..6aa7725
--- /dev/null
+++ b/components/spidriver/spi_master_lobo.c
@@ -0,0 +1,1137 @@
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/*
+----------------------------------------
+Non DMA version of the spi_master driver
+----------------------------------------
+------------------------------------------------------------------------------------
+Based on esp-idf 'spi_master', modified by LoBo (https://github.com/loboris) 03/2017
+------------------------------------------------------------------------------------
+
+* Transfers data to SPI device in direct mode, not using DMA
+* All configuration options (bus, device, transaction) are the same as in spi_master driver
+* Transfers uses the semaphore (taken in select function & given in deselect function) to protect the transfer
+* Number of the devices attached to the bus which uses hardware CS can be 3 ('NO_CS')
+* Additional devices which uses software CS can be attached to the bus, up to 'NO_DEV'
+* 'spi_bus_initialize' & 'spi_bus_remove' functions are removed, spi bus is initiated/removed in spi_lobo_bus_add_device/spi_lobo_bus_remove_device when needed
+* 'spi_lobo_bus_add_device' function has added parameter 'bus_config' and automatically initializes spi bus device if not already initialized
+* 'spi_lobo_bus_remove_device' automatically removes spi bus device if no other devices are attached to it.
+* Devices can have individual bus_configs, so different mosi, miso, sck pins can be configured for each device
+ Reconfiguring the bus is done automaticaly in 'spi_lobo_device_select' function
+* 'spi_lobo_device_select' & 'spi_lobo_device_deselect' functions handles devices configuration changes and software CS
+* Some helper functions are added ('spi_lobo_get_speed', 'spi_lobo_set_speed', ...)
+* All structures are available in header file for easy creation of user low level spi functions. See **tftfunc.c** source for examples.
+* Transimt and receive lenghts are limited only by available memory
+
+
+Main driver's function is 'spi_lobo_transfer_data()'
+
+ * TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes)
+ * and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes)
+ * Lengths must be 8-bit multiples!
+ * If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data
+ * If trans->tx_buffer is NULL or trans->length is 0, only receives data
+ * If the device is in duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously.
+ * If the device is in half duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission
+ * 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration
+ * and IF 'trans->length' and 'trans->rx_length' are NOT both 0
+ * If configured, devices 'pre_cb' callback is called before and 'post_cb' after the transmission
+ * If device was not previously selected, it will be selected before transmission and deselected after transmission.
+
+*/
+
+/*
+ Replace this include with
+ #include "driver/spi_master_lobo.h"
+ if the driver is located in esp-isf/components
+*/
+#include "freertos/FreeRTOS.h"
+#include
+#include "soc/spi_reg.h"
+#include "soc/dport_reg.h"
+#include "esp_log.h"
+#include "freertos/semphr.h"
+#include "driver/uart.h"
+#include "driver/gpio.h"
+#include "spi_master_lobo.h"
+#include "driver/periph_ctrl.h"
+
+static spi_lobo_host_t *spihost[3] = {NULL};
+
+
+static const char *SPI_TAG = "spi_lobo_master";
+#define SPI_CHECK(a, str, ret_val) \
+ if (!(a)) { \
+ ESP_LOGE(SPI_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
+ return (ret_val); \
+ }
+
+/*
+ Stores a bunch of per-spi-peripheral data.
+*/
+typedef struct {
+ const uint8_t spiclk_out; //GPIO mux output signals
+ const uint8_t spid_out;
+ const uint8_t spiq_out;
+ const uint8_t spiwp_out;
+ const uint8_t spihd_out;
+ const uint8_t spid_in; //GPIO mux input signals
+ const uint8_t spiq_in;
+ const uint8_t spiwp_in;
+ const uint8_t spihd_in;
+ const uint8_t spics_out[3]; // /CS GPIO output mux signals
+ const uint8_t spiclk_native; //IO pins of IO_MUX muxed signals
+ const uint8_t spid_native;
+ const uint8_t spiq_native;
+ const uint8_t spiwp_native;
+ const uint8_t spihd_native;
+ const uint8_t spics0_native;
+ const uint8_t irq; //irq source for interrupt mux
+ const uint8_t irq_dma; //dma irq source for interrupt mux
+ const periph_module_t module; //peripheral module, for enabling clock etc
+ spi_dev_t *hw; //Pointer to the hardware registers
+} spi_signal_conn_t;
+
+/*
+ Bunch of constants for every SPI peripheral: GPIO signals, irqs, hw addr of registers etc
+*/
+static const spi_signal_conn_t io_signal[3]={
+ {
+ .spiclk_out=SPICLK_OUT_IDX,
+ .spid_out=SPID_OUT_IDX,
+ .spiq_out=SPIQ_OUT_IDX,
+ .spiwp_out=SPIWP_OUT_IDX,
+ .spihd_out=SPIHD_OUT_IDX,
+ .spid_in=SPID_IN_IDX,
+ .spiq_in=SPIQ_IN_IDX,
+ .spiwp_in=SPIWP_IN_IDX,
+ .spihd_in=SPIHD_IN_IDX,
+ .spics_out={SPICS0_OUT_IDX, SPICS1_OUT_IDX, SPICS2_OUT_IDX},
+ .spiclk_native=6,
+ .spid_native=8,
+ .spiq_native=7,
+ .spiwp_native=10,
+ .spihd_native=9,
+ .spics0_native=11,
+ .irq=ETS_SPI1_INTR_SOURCE,
+ .irq_dma=ETS_SPI1_DMA_INTR_SOURCE,
+ .module=PERIPH_SPI_MODULE,
+ .hw=&SPI1
+ }, {
+ .spiclk_out=HSPICLK_OUT_IDX,
+ .spid_out=HSPID_OUT_IDX,
+ .spiq_out=HSPIQ_OUT_IDX,
+ .spiwp_out=HSPIWP_OUT_IDX,
+ .spihd_out=HSPIHD_OUT_IDX,
+ .spid_in=HSPID_IN_IDX,
+ .spiq_in=HSPIQ_IN_IDX,
+ .spiwp_in=HSPIWP_IN_IDX,
+ .spihd_in=HSPIHD_IN_IDX,
+ .spics_out={HSPICS0_OUT_IDX, HSPICS1_OUT_IDX, HSPICS2_OUT_IDX},
+ .spiclk_native=14,
+ .spid_native=13,
+ .spiq_native=12,
+ .spiwp_native=2,
+ .spihd_native=4,
+ .spics0_native=15,
+ .irq=ETS_SPI2_INTR_SOURCE,
+ .irq_dma=ETS_SPI2_DMA_INTR_SOURCE,
+ .module=PERIPH_HSPI_MODULE,
+ .hw=&SPI2
+ }, {
+ .spiclk_out=VSPICLK_OUT_IDX,
+ .spid_out=VSPID_OUT_IDX,
+ .spiq_out=VSPIQ_OUT_IDX,
+ .spiwp_out=VSPIWP_OUT_IDX,
+ .spihd_out=VSPIHD_OUT_IDX,
+ .spid_in=VSPID_IN_IDX,
+ .spiq_in=VSPIQ_IN_IDX,
+ .spiwp_in=VSPIWP_IN_IDX,
+ .spihd_in=VSPIHD_IN_IDX,
+ .spics_out={VSPICS0_OUT_IDX, VSPICS1_OUT_IDX, VSPICS2_OUT_IDX},
+ .spiclk_native=18,
+ .spid_native=23,
+ .spiq_native=19,
+ .spiwp_native=22,
+ .spihd_native=21,
+ .spics0_native=5,
+ .irq=ETS_SPI3_INTR_SOURCE,
+ .irq_dma=ETS_SPI3_DMA_INTR_SOURCE,
+ .module=PERIPH_VSPI_MODULE,
+ .hw=&SPI3
+ }
+};
+
+
+//======================================================================================================
+
+#define DMA_CHANNEL_ENABLED(dma_chan) (BIT(dma_chan-1))
+
+typedef void(*dmaworkaround_cb_t)(void *arg);
+
+//Set up a list of dma descriptors. dmadesc is an array of descriptors. Data is the buffer to point to.
+//--------------------------------------------------------------------------------------------
+void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx)
+{
+ int n = 0;
+ while (len) {
+ int dmachunklen = len;
+ if (dmachunklen > SPI_MAX_DMA_LEN) dmachunklen = SPI_MAX_DMA_LEN;
+ if (isrx) {
+ //Receive needs DMA length rounded to next 32-bit boundary
+ dmadesc[n].size = (dmachunklen + 3) & (~3);
+ dmadesc[n].length = (dmachunklen + 3) & (~3);
+ } else {
+ dmadesc[n].size = dmachunklen;
+ dmadesc[n].length = dmachunklen;
+ }
+ dmadesc[n].buf = (uint8_t *)data;
+ dmadesc[n].eof = 0;
+ dmadesc[n].sosf = 0;
+ dmadesc[n].owner = 1;
+ dmadesc[n].qe.stqe_next = &dmadesc[n + 1];
+ len -= dmachunklen;
+ data += dmachunklen;
+ n++;
+ }
+ dmadesc[n - 1].eof = 1; //Mark last DMA desc as end of stream.
+ dmadesc[n - 1].qe.stqe_next = NULL;
+}
+
+
+/*
+Code for workaround for DMA issue in ESP32 v0/v1 silicon
+*/
+
+
+static volatile int dmaworkaround_channels_busy[2] = {0, 0};
+static dmaworkaround_cb_t dmaworkaround_cb;
+static void *dmaworkaround_cb_arg;
+static portMUX_TYPE dmaworkaround_mux = portMUX_INITIALIZER_UNLOCKED;
+static int dmaworkaround_waiting_for_chan = 0;
+static bool spi_periph_claimed[3] = {true, false, false};
+static uint8_t spi_dma_chan_enabled = 0;
+static portMUX_TYPE spi_dma_spinlock = portMUX_INITIALIZER_UNLOCKED;
+
+//--------------------------------------------------------------------------------------------
+bool IRAM_ATTR spi_lobo_dmaworkaround_req_reset(int dmachan, dmaworkaround_cb_t cb, void *arg)
+{
+ int otherchan = (dmachan == 1) ? 2 : 1;
+ bool ret;
+ portENTER_CRITICAL(&dmaworkaround_mux);
+ if (dmaworkaround_channels_busy[otherchan-1]) {
+ //Other channel is busy. Call back when it's done.
+ dmaworkaround_cb = cb;
+ dmaworkaround_cb_arg = arg;
+ dmaworkaround_waiting_for_chan = otherchan;
+ ret = false;
+ } else {
+ //Reset DMA
+ DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
+ DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
+ ret = true;
+ }
+ portEXIT_CRITICAL(&dmaworkaround_mux);
+ return ret;
+}
+
+//-------------------------------------------------------
+bool IRAM_ATTR spi_lobo_dmaworkaround_reset_in_progress()
+{
+ return (dmaworkaround_waiting_for_chan != 0);
+}
+
+//-----------------------------------------------------
+void IRAM_ATTR spi_lobo_dmaworkaround_idle(int dmachan)
+{
+ portENTER_CRITICAL(&dmaworkaround_mux);
+ dmaworkaround_channels_busy[dmachan-1] = 0;
+ if (dmaworkaround_waiting_for_chan == dmachan) {
+ //Reset DMA
+ DPORT_SET_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
+ DPORT_CLEAR_PERI_REG_MASK(DPORT_PERIP_RST_EN_REG, DPORT_SPI_DMA_RST);
+ dmaworkaround_waiting_for_chan = 0;
+ //Call callback
+ dmaworkaround_cb(dmaworkaround_cb_arg);
+
+ }
+ portEXIT_CRITICAL(&dmaworkaround_mux);
+}
+
+//----------------------------------------------------------------
+void IRAM_ATTR spi_lobo_dmaworkaround_transfer_active(int dmachan)
+{
+ portENTER_CRITICAL(&dmaworkaround_mux);
+ dmaworkaround_channels_busy[dmachan-1] = 1;
+ portEXIT_CRITICAL(&dmaworkaround_mux);
+}
+
+//Returns true if this peripheral is successfully claimed, false if otherwise.
+//-----------------------------------------------------
+bool spi_lobo_periph_claim(spi_lobo_host_device_t host)
+{
+ bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], false, true);
+ if (ret) periph_module_enable(io_signal[host].module);
+ return ret;
+}
+
+//Returns true if this peripheral is successfully freed, false if otherwise.
+//-----------------------------------------------
+bool spi_lobo_periph_free(spi_lobo_host_device_t host)
+{
+ bool ret = __sync_bool_compare_and_swap(&spi_periph_claimed[host], true, false);
+ if (ret) periph_module_disable(io_signal[host].module);
+ return ret;
+}
+
+//-----------------------------------------
+bool spi_lobo_dma_chan_claim (int dma_chan)
+{
+ bool ret = false;
+ assert( dma_chan == 1 || dma_chan == 2 );
+
+ portENTER_CRITICAL(&spi_dma_spinlock);
+ if ( !(spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan)) ) {
+ // get the channel only when it's not claimed yet.
+ spi_dma_chan_enabled |= DMA_CHANNEL_ENABLED(dma_chan);
+ ret = true;
+ }
+ periph_module_enable( PERIPH_SPI_DMA_MODULE );
+ portEXIT_CRITICAL(&spi_dma_spinlock);
+
+ return ret;
+}
+
+//---------------------------------------
+bool spi_lobo_dma_chan_free(int dma_chan)
+{
+ assert( dma_chan == 1 || dma_chan == 2 );
+ assert( spi_dma_chan_enabled & DMA_CHANNEL_ENABLED(dma_chan) );
+
+ portENTER_CRITICAL(&spi_dma_spinlock);
+ spi_dma_chan_enabled &= ~DMA_CHANNEL_ENABLED(dma_chan);
+ if ( spi_dma_chan_enabled == 0 ) {
+ //disable the DMA only when all the channels are freed.
+ periph_module_disable( PERIPH_SPI_DMA_MODULE );
+ }
+ portEXIT_CRITICAL(&spi_dma_spinlock);
+
+ return true;
+}
+
+
+//======================================================================================================
+
+
+//----------------------------------------------------------------------------------------------------------------
+static esp_err_t spi_lobo_bus_initialize(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, int init)
+{
+ bool native=true, spi_chan_claimed, dma_chan_claimed;
+
+ if (init > 0) {
+ /* ToDo: remove this when we have flash operations cooperating with this */
+ SPI_CHECK(host!=TFT_SPI_HOST, "SPI1 is not supported", ESP_ERR_NOT_SUPPORTED);
+
+ SPI_CHECK(host>=TFT_SPI_HOST && host<=TFT_VSPI_HOST, "invalid host", ESP_ERR_INVALID_ARG);
+ SPI_CHECK(spihost[host]==NULL, "host already in use", ESP_ERR_INVALID_STATE);
+ }
+ else {
+ SPI_CHECK(spihost[host]!=NULL, "host not in use", ESP_ERR_INVALID_STATE);
+ }
+
+ SPI_CHECK(bus_config->mosi_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->mosi_io_num), "spid pin invalid", ESP_ERR_INVALID_ARG);
+ SPI_CHECK(bus_config->sclk_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->sclk_io_num), "spiclk pin invalid", ESP_ERR_INVALID_ARG);
+ SPI_CHECK(bus_config->miso_io_num<0 || GPIO_IS_VALID_GPIO(bus_config->miso_io_num), "spiq pin invalid", ESP_ERR_INVALID_ARG);
+ SPI_CHECK(bus_config->quadwp_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadwp_io_num), "spiwp pin invalid", ESP_ERR_INVALID_ARG);
+ SPI_CHECK(bus_config->quadhd_io_num<0 || GPIO_IS_VALID_OUTPUT_GPIO(bus_config->quadhd_io_num), "spihd pin invalid", ESP_ERR_INVALID_ARG);
+
+ if (init > 0) {
+ spi_chan_claimed=spi_lobo_periph_claim(host);
+ SPI_CHECK(spi_chan_claimed, "host already in use", ESP_ERR_INVALID_STATE);
+
+ //spihost[host]=malloc(sizeof(spi_lobo_host_t));
+ spihost[host]=heap_caps_malloc(sizeof(spi_lobo_host_t), MALLOC_CAP_DMA);
+ if (spihost[host]==NULL) return ESP_ERR_NO_MEM;
+ memset(spihost[host], 0, sizeof(spi_lobo_host_t));
+ // Create semaphore
+ spihost[host]->spi_lobo_bus_mutex = xSemaphoreCreateMutex();
+ if (!spihost[host]->spi_lobo_bus_mutex) return ESP_ERR_NO_MEM;
+ }
+
+ spihost[host]->cur_device = -1;
+ memcpy(&spihost[host]->cur_bus_config, bus_config, sizeof(spi_lobo_bus_config_t));
+
+ //Check if the selected pins correspond to the native pins of the peripheral
+ if (bus_config->mosi_io_num >= 0 && bus_config->mosi_io_num!=io_signal[host].spid_native) native=false;
+ if (bus_config->miso_io_num >= 0 && bus_config->miso_io_num!=io_signal[host].spiq_native) native=false;
+ if (bus_config->sclk_io_num >= 0 && bus_config->sclk_io_num!=io_signal[host].spiclk_native) native=false;
+ if (bus_config->quadwp_io_num >= 0 && bus_config->quadwp_io_num!=io_signal[host].spiwp_native) native=false;
+ if (bus_config->quadhd_io_num >= 0 && bus_config->quadhd_io_num!=io_signal[host].spihd_native) native=false;
+
+ spihost[host]->no_gpio_matrix=native;
+ if (native) {
+ //All SPI native pin selections resolve to 1, so we put that here instead of trying to figure
+ //out which FUNC_GPIOx_xSPIxx to grab; they all are defined to 1 anyway.
+ if (bus_config->mosi_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], 1);
+ if (bus_config->miso_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], 1);
+ if (bus_config->quadwp_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], 1);
+ if (bus_config->quadhd_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], 1);
+ if (bus_config->sclk_io_num > 0) PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], 1);
+ } else {
+ //Use GPIO
+ if (bus_config->mosi_io_num>0) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->mosi_io_num], PIN_FUNC_GPIO);
+ gpio_set_direction(bus_config->mosi_io_num, GPIO_MODE_OUTPUT);
+ gpio_matrix_out(bus_config->mosi_io_num, io_signal[host].spid_out, false, false);
+ gpio_matrix_in(bus_config->mosi_io_num, io_signal[host].spid_in, false);
+ }
+ if (bus_config->miso_io_num>0) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->miso_io_num], PIN_FUNC_GPIO);
+ gpio_set_direction(bus_config->miso_io_num, GPIO_MODE_INPUT);
+ gpio_matrix_out(bus_config->miso_io_num, io_signal[host].spiq_out, false, false);
+ gpio_matrix_in(bus_config->miso_io_num, io_signal[host].spiq_in, false);
+ }
+ if (bus_config->quadwp_io_num>0) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadwp_io_num], PIN_FUNC_GPIO);
+ gpio_set_direction(bus_config->quadwp_io_num, GPIO_MODE_OUTPUT);
+ gpio_matrix_out(bus_config->quadwp_io_num, io_signal[host].spiwp_out, false, false);
+ gpio_matrix_in(bus_config->quadwp_io_num, io_signal[host].spiwp_in, false);
+ }
+ if (bus_config->quadhd_io_num>0) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->quadhd_io_num], PIN_FUNC_GPIO);
+ gpio_set_direction(bus_config->quadhd_io_num, GPIO_MODE_OUTPUT);
+ gpio_matrix_out(bus_config->quadhd_io_num, io_signal[host].spihd_out, false, false);
+ gpio_matrix_in(bus_config->quadhd_io_num, io_signal[host].spihd_in, false);
+ }
+ if (bus_config->sclk_io_num>0) {
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[bus_config->sclk_io_num], PIN_FUNC_GPIO);
+ gpio_set_direction(bus_config->sclk_io_num, GPIO_MODE_OUTPUT);
+ gpio_matrix_out(bus_config->sclk_io_num, io_signal[host].spiclk_out, false, false);
+ }
+ }
+ periph_module_enable(io_signal[host].module);
+ spihost[host]->hw=io_signal[host].hw;
+
+ if (init > 0) {
+ dma_chan_claimed=spi_lobo_dma_chan_claim(init);
+ if ( !dma_chan_claimed ) {
+ spi_lobo_periph_free( host );
+ SPI_CHECK(dma_chan_claimed, "dma channel already in use", ESP_ERR_INVALID_STATE);
+ }
+ spihost[host]->dma_chan = init;
+ //See how many dma descriptors we need and allocate them
+ int dma_desc_ct=(bus_config->max_transfer_sz+SPI_MAX_DMA_LEN-1)/SPI_MAX_DMA_LEN;
+ if (dma_desc_ct==0) dma_desc_ct=1; //default to 4k when max is not given
+ spihost[host]->max_transfer_sz = dma_desc_ct*SPI_MAX_DMA_LEN;
+
+ spihost[host]->dmadesc_tx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
+ spihost[host]->dmadesc_rx=heap_caps_malloc(sizeof(lldesc_t)*dma_desc_ct, MALLOC_CAP_DMA);
+ if (!spihost[host]->dmadesc_tx || !spihost[host]->dmadesc_rx) goto nomem;
+
+ //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
+ spi_lobo_dmaworkaround_idle(spihost[host]->dma_chan);
+
+ // Reset DMA
+ spihost[host]->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST;
+ spihost[host]->hw->dma_out_link.start=0;
+ spihost[host]->hw->dma_in_link.start=0;
+ spihost[host]->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
+ spihost[host]->hw->dma_conf.out_data_burst_en=1;
+
+ //Reset timing
+ spihost[host]->hw->ctrl2.val=0;
+
+ //Disable unneeded ints
+ spihost[host]->hw->slave.rd_buf_done=0;
+ spihost[host]->hw->slave.wr_buf_done=0;
+ spihost[host]->hw->slave.rd_sta_done=0;
+ spihost[host]->hw->slave.wr_sta_done=0;
+ spihost[host]->hw->slave.rd_buf_inten=0;
+ spihost[host]->hw->slave.wr_buf_inten=0;
+ spihost[host]->hw->slave.rd_sta_inten=0;
+ spihost[host]->hw->slave.wr_sta_inten=0;
+
+ //Force a transaction done interrupt. This interrupt won't fire yet because we initialized the SPI interrupt as
+ //disabled. This way, we can just enable the SPI interrupt and the interrupt handler will kick in, handling
+ //any transactions that are queued.
+ spihost[host]->hw->slave.trans_inten=1;
+ spihost[host]->hw->slave.trans_done=1;
+
+ //Select DMA channel.
+ DPORT_SET_PERI_REG_BITS(DPORT_SPI_DMA_CHAN_SEL_REG, 3, init, (host * 2));
+ }
+ return ESP_OK;
+
+nomem:
+ if (spihost[host]) {
+ free(spihost[host]->dmadesc_tx);
+ free(spihost[host]->dmadesc_rx);
+ }
+ free(spihost[host]);
+ spi_lobo_periph_free(host);
+ return ESP_ERR_NO_MEM;
+}
+
+//---------------------------------------------------------------------------
+static esp_err_t spi_lobo_bus_free(spi_lobo_host_device_t host, int dofree)
+{
+ if ((host == TFT_SPI_HOST) || (host > TFT_VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host
+
+ if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE; // host not in use
+
+ if (dofree) {
+ for (int x=0; xdevice[x] != NULL) return ESP_ERR_INVALID_STATE; // not all devices freed
+ }
+ }
+ if ( spihost[host]->dma_chan > 0 ) {
+ spi_lobo_dma_chan_free ( spihost[host]->dma_chan );
+ }
+ spihost[host]->hw->slave.trans_inten=0;
+ spihost[host]->hw->slave.trans_done=0;
+ spi_lobo_periph_free(host);
+
+ if (dofree) {
+ vSemaphoreDelete(spihost[host]->spi_lobo_bus_mutex);
+ free(spihost[host]->dmadesc_tx);
+ free(spihost[host]->dmadesc_rx);
+ free(spihost[host]);
+ spihost[host] = NULL;
+ }
+ return ESP_OK;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle)
+{
+ if ((host == TFT_SPI_HOST) || (host > TFT_VSPI_HOST)) return ESP_ERR_NOT_SUPPORTED; // invalid host
+
+ if (spihost[host] == NULL) {
+ esp_err_t ret = spi_lobo_bus_initialize(host, bus_config, 1);
+ if (ret) return ret;
+ }
+
+ int freecs, maxdev;
+ int apbclk=APB_CLK_FREQ;
+
+ if (spihost[host] == NULL) return ESP_ERR_INVALID_STATE;
+
+ if (dev_config->spics_io_num >= 0) {
+ if (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_io_num)) return ESP_ERR_INVALID_ARG;
+ if (dev_config->spics_ext_io_num > 0) dev_config->spics_ext_io_num = -1;
+ }
+ else {
+ //if ((dev_config->spics_ext_io_num <= 0) || (!GPIO_IS_VALID_OUTPUT_GPIO(dev_config->spics_ext_io_num))) return ESP_ERR_INVALID_ARG;
+ }
+
+ //ToDo: Check if some other device uses the same 'spics_ext_io_num'
+
+ if (dev_config->clock_speed_hz == 0) return ESP_ERR_INVALID_ARG;
+ if (dev_config->spics_io_num > 0) maxdev = NO_CS;
+ else maxdev = NO_DEV;
+
+ for (freecs=0; freecsdevice[freecs], NULL, (spi_lobo_device_t *)1)) break;
+ }
+ if (freecs == maxdev) return ESP_ERR_NOT_FOUND;
+
+ // The hardware looks like it would support this, but actually setting cs_ena_pretrans when transferring in full
+ // duplex mode does absolutely nothing on the ESP32.
+ if ((dev_config->cs_ena_pretrans != 0) && (dev_config->flags & LB_SPI_DEVICE_HALFDUPLEX)) return ESP_ERR_INVALID_ARG;
+
+ // Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
+ if (((dev_config->flags & LB_SPI_DEVICE_HALFDUPLEX)==0) && (dev_config->clock_speed_hz > ((apbclk*2)/5)) && (!spihost[host]->no_gpio_matrix)) return ESP_ERR_INVALID_ARG;
+
+ //Allocate memory for device
+ spi_lobo_device_t *dev=malloc(sizeof(spi_lobo_device_t));
+ if (dev==NULL) return ESP_ERR_NO_MEM;
+
+ memset(dev, 0, sizeof(spi_lobo_device_t));
+ spihost[host]->device[freecs]=dev;
+
+ if (dev_config->duty_cycle_pos==0) dev_config->duty_cycle_pos=128;
+ dev->host=spihost[host];
+ dev->host_dev = host;
+
+ //We want to save a copy of the dev config in the dev struct.
+ memcpy(&dev->cfg, dev_config, sizeof(spi_lobo_device_interface_config_t));
+ //We want to save a copy of the bus config in the dev struct.
+ memcpy(&dev->bus_config, bus_config, sizeof(spi_lobo_bus_config_t));
+
+ //Set CS pin, CS options
+ if (dev_config->spics_io_num > 0) {
+ if (spihost[host]->no_gpio_matrix &&dev_config->spics_io_num == io_signal[host].spics0_native && freecs==0) {
+ //Again, the cs0s for all SPI peripherals map to pin mux source 1, so we use that instead of a define.
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], 1);
+ } else {
+ //Use GPIO matrix
+ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[dev_config->spics_io_num], PIN_FUNC_GPIO);
+ gpio_set_direction(dev_config->spics_io_num, GPIO_MODE_OUTPUT);
+ gpio_matrix_out(dev_config->spics_io_num, io_signal[host].spics_out[freecs], false, false);
+ }
+ }
+ else if (dev_config->spics_ext_io_num >= 0) {
+ gpio_set_direction(dev_config->spics_ext_io_num, GPIO_MODE_OUTPUT);
+ gpio_set_level(dev_config->spics_ext_io_num, 1);
+ }
+ if (dev_config->flags & LB_SPI_DEVICE_CLK_AS_CS) {
+ spihost[host]->hw->pin.master_ck_sel |= (1<hw->pin.master_ck_sel &= (1<flags & LB_SPI_DEVICE_POSITIVE_CS) {
+ spihost[host]->hw->pin.master_cs_pol |= (1<hw->pin.master_cs_pol &= (1<host->device[x] == handle) handle->host->device[x]=NULL;
+ }
+
+ // Check if all devices are removed from this host and free the bus if yes
+ for (x=0; xhost_dev]->device[x] !=NULL) break;
+ }
+ if (x == NO_DEV) {
+ free(handle);
+ spi_lobo_bus_free(handle->host_dev, 1);
+ }
+ else free(handle);
+
+ return ESP_OK;
+}
+
+//-----------------------------------------------------------------
+static int IRAM_ATTR spi_freq_for_pre_n(int fapb, int pre, int n) {
+ return (fapb / (pre * n));
+}
+
+/*
+ * Set the SPI clock to a certain frequency. Returns the effective frequency set, which may be slightly
+ * different from the requested frequency.
+ */
+//-----------------------------------------------------------------------------------
+static int IRAM_ATTR spi_set_clock(spi_dev_t *hw, int fapb, int hz, int duty_cycle) {
+ int pre, n, h, l, eff_clk;
+
+ //In hw, n, h and l are 1-64, pre is 1-8K. Value written to register is one lower than used value.
+ if (hz>((fapb/4)*3)) {
+ //Using Fapb directly will give us the best result here.
+ hw->clock.clkcnt_l=0;
+ hw->clock.clkcnt_h=0;
+ hw->clock.clkcnt_n=0;
+ hw->clock.clkdiv_pre=0;
+ hw->clock.clk_equ_sysclk=1;
+ eff_clk=fapb;
+ } else {
+ //For best duty cycle resolution, we want n to be as close to 32 as possible, but
+ //we also need a pre/n combo that gets us as close as possible to the intended freq.
+ //To do this, we bruteforce n and calculate the best pre to go along with that.
+ //If there's a choice between pre/n combos that give the same result, use the one
+ //with the higher n.
+ int bestn=-1;
+ int bestpre=-1;
+ int besterr=0;
+ int errval;
+ for (n=1; n<=64; n++) {
+ //Effectively, this does pre=round((fapb/n)/hz).
+ pre=((fapb/n)+(hz/2))/hz;
+ if (pre<=0) pre=1;
+ if (pre>8192) pre=8192;
+ errval=abs(spi_freq_for_pre_n(fapb, pre, n)-hz);
+ if (bestn==-1 || errval<=besterr) {
+ besterr=errval;
+ bestn=n;
+ bestpre=pre;
+ }
+ }
+
+ n=bestn;
+ pre=bestpre;
+ l=n;
+ //This effectively does round((duty_cycle*n)/256)
+ h=(duty_cycle*n+127)/256;
+ if (h<=0) h=1;
+
+ hw->clock.clk_equ_sysclk=0;
+ hw->clock.clkcnt_n=n-1;
+ hw->clock.clkdiv_pre=pre-1;
+ hw->clock.clkcnt_h=h-1;
+ hw->clock.clkcnt_l=l-1;
+ eff_clk=spi_freq_for_pre_n(fapb, pre, n);
+ }
+ return eff_clk;
+}
+
+
+
+//------------------------------------------------------------------------------------
+esp_err_t IRAM_ATTR spi_lobo_device_select(spi_lobo_device_handle_t handle, int force)
+{
+ if (handle == NULL) return ESP_ERR_INVALID_ARG;
+
+ if ((handle->cfg.selected == 1) && (!force)) return ESP_OK; // already selected
+
+ int i;
+ spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
+
+ // find device's host bus
+ for (i=0; idevice[i] == handle) break;
+ }
+ if (i == NO_DEV) return ESP_ERR_INVALID_ARG;
+
+ if (!(xSemaphoreTake(host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE;
+
+ // Check if previously used device's bus device is the same
+ if (memcmp(&host->cur_bus_config, &handle->bus_config, sizeof(spi_lobo_bus_config_t)) != 0) {
+ // device has different bus configuration, we need to reconfigure the bus
+ esp_err_t err = spi_lobo_bus_free(1, 0);
+ if (err) {
+ xSemaphoreGive(host->spi_lobo_bus_mutex);
+ return err;
+ }
+ err = spi_lobo_bus_initialize(i, &handle->bus_config, -1);
+ if (err) {
+ xSemaphoreGive(host->spi_lobo_bus_mutex);
+ return err;
+ }
+ }
+
+ //Reconfigure according to device settings, but only if the device changed or forced.
+ if ((force) || (host->device[host->cur_device] != handle)) {
+ //Assumes a hardcoded 80MHz Fapb for now. ToDo: figure out something better once we have clock scaling working.
+ int apbclk=APB_CLK_FREQ;
+
+ //Speeds >=40MHz over GPIO matrix needs a dummy cycle, but these don't work for full-duplex connections.
+ if (((handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX) == 0) && (handle->cfg.clock_speed_hz > ((apbclk*2)/5)) && (!host->no_gpio_matrix)) {
+ // set speed to 32 MHz
+ handle->cfg.clock_speed_hz = (apbclk*2)/5;
+ }
+
+ int effclk=spi_set_clock(host->hw, apbclk, handle->cfg.clock_speed_hz, handle->cfg.duty_cycle_pos);
+ //Configure bit order
+ host->hw->ctrl.rd_bit_order=(handle->cfg.flags & LB_SPI_DEVICE_RXBIT_LSBFIRST)?1:0;
+ host->hw->ctrl.wr_bit_order=(handle->cfg.flags & LB_SPI_DEVICE_TXBIT_LSBFIRST)?1:0;
+
+ //Configure polarity
+ //SPI iface needs to be configured for a delay in some cases.
+ int nodelay=0;
+ int extra_dummy=0;
+ if (host->no_gpio_matrix) {
+ if (effclk >= apbclk/2) {
+ nodelay=1;
+ }
+ } else {
+ if (effclk >= apbclk/2) {
+ nodelay=1;
+ extra_dummy=1; //Note: This only works on half-duplex connections. spi_lobo_bus_add_device checks for this.
+ } else if (effclk >= apbclk/4) {
+ nodelay=1;
+ }
+ }
+ if (handle->cfg.mode==0) {
+ host->hw->pin.ck_idle_edge=0;
+ host->hw->user.ck_out_edge=0;
+ host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
+ } else if (handle->cfg.mode==1) {
+ host->hw->pin.ck_idle_edge=0;
+ host->hw->user.ck_out_edge=1;
+ host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
+ } else if (handle->cfg.mode==2) {
+ host->hw->pin.ck_idle_edge=1;
+ host->hw->user.ck_out_edge=1;
+ host->hw->ctrl2.miso_delay_mode=nodelay?0:1;
+ } else if (handle->cfg.mode==3) {
+ host->hw->pin.ck_idle_edge=1;
+ host->hw->user.ck_out_edge=0;
+ host->hw->ctrl2.miso_delay_mode=nodelay?0:2;
+ }
+
+ //Configure bit sizes, load addr and command
+ host->hw->user.usr_dummy=(handle->cfg.dummy_bits+extra_dummy)?1:0;
+ host->hw->user.usr_addr=(handle->cfg.address_bits)?1:0;
+ host->hw->user.usr_command=(handle->cfg.command_bits)?1:0;
+ host->hw->user1.usr_addr_bitlen=handle->cfg.address_bits-1;
+ host->hw->user1.usr_dummy_cyclelen=handle->cfg.dummy_bits+extra_dummy-1;
+ host->hw->user2.usr_command_bitlen=handle->cfg.command_bits-1;
+ //Configure misc stuff
+ host->hw->user.doutdin=(handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)?0:1;
+ host->hw->user.sio=(handle->cfg.flags & LB_SPI_DEVICE_3WIRE)?1:0;
+
+ host->hw->ctrl2.setup_time=handle->cfg.cs_ena_pretrans-1;
+ host->hw->user.cs_setup=handle->cfg.cs_ena_pretrans?1:0;
+ host->hw->ctrl2.hold_time=handle->cfg.cs_ena_posttrans-1;
+ host->hw->user.cs_hold=(handle->cfg.cs_ena_posttrans)?1:0;
+
+ //Configure CS pin
+ host->hw->pin.cs0_dis=(i==0)?0:1;
+ host->hw->pin.cs1_dis=(i==1)?0:1;
+ host->hw->pin.cs2_dis=(i==2)?0:1;
+
+ host->cur_device = i;
+ }
+
+ if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) {
+ gpio_set_level(handle->cfg.spics_ext_io_num, 0);
+ }
+
+ handle->cfg.selected = 1;
+
+ return ESP_OK;
+}
+
+//---------------------------------------------------------------------------
+esp_err_t IRAM_ATTR spi_lobo_device_deselect(spi_lobo_device_handle_t handle)
+{
+ if (handle == NULL) return ESP_ERR_INVALID_ARG;
+
+ if (handle->cfg.selected == 0) return ESP_OK; // already deselected
+
+ int i;
+ spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
+
+ for (i=0; idevice[i] == handle) break;
+ }
+ if (i == NO_DEV) return ESP_ERR_INVALID_ARG;
+
+ if (host->device[host->cur_device] == handle) {
+ if ((handle->cfg.spics_io_num < 0) && (handle->cfg.spics_ext_io_num > 0)) {
+ gpio_set_level(handle->cfg.spics_ext_io_num, 1);
+ }
+ }
+
+ handle->cfg.selected = 0;
+ xSemaphoreGive(host->spi_lobo_bus_mutex);
+
+ return ESP_OK;
+}
+
+//--------------------------------------------------------------------------------
+esp_err_t IRAM_ATTR spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle)
+{
+ if (!(xSemaphoreTake(handle->host->spi_lobo_bus_mutex, SPI_SEMAPHORE_WAIT))) return ESP_ERR_INVALID_STATE;
+ else return ESP_OK;
+}
+
+//---------------------------------------------------------------------------
+void IRAM_ATTR spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle)
+{
+ xSemaphoreTake(handle->host->spi_lobo_bus_mutex, portMAX_DELAY);
+}
+
+//----------------------------------------------------------
+uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle)
+{
+ spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
+ uint32_t speed = 0;
+ if (spi_lobo_device_select(handle, 0) == ESP_OK) {
+ if (host->hw->clock.clk_equ_sysclk == 1) speed = 80000000;
+ else speed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1);
+ }
+ spi_lobo_device_deselect(handle);
+ return speed;
+}
+
+//--------------------------------------------------------------------------
+uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed)
+{
+ spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
+ uint32_t newspeed = 0;
+ if (spi_lobo_device_select(handle, 0) == ESP_OK) {
+ spi_lobo_device_deselect(handle);
+ handle->cfg.clock_speed_hz = speed;
+ if (spi_lobo_device_select(handle, 1) == ESP_OK) {
+ if (host->hw->clock.clk_equ_sysclk == 1) newspeed = 80000000;
+ else newspeed = 80000000/(host->hw->clock.clkdiv_pre+1)/(host->hw->clock.clkcnt_n+1);
+ }
+ }
+ spi_lobo_device_deselect(handle);
+
+ return newspeed;
+}
+
+//-------------------------------------------------------------
+bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle)
+{
+ return handle->host->no_gpio_matrix;
+}
+
+//-------------------------------------------------------------------
+void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck)
+{
+ *sdo = io_signal[host].spid_native;
+ *sdi = io_signal[host].spiq_native;
+ *sck = io_signal[host].spiclk_native;
+}
+
+/*
+When using 'spi_lobo_transfer_data' function we can have several scenarios:
+
+A: Send only (trans->rxlength = 0)
+B: Receive only (trans->txlength = 0)
+C: Send & receive (trans->txlength > 0 & trans->rxlength > 0)
+D: No operation (trans->txlength = 0 & trans->rxlength = 0)
+
+*/
+//----------------------------------------------------------------------------------------------------------
+esp_err_t IRAM_ATTR spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans) {
+ if (!handle) return ESP_ERR_INVALID_ARG;
+
+ // *** For now we can only handle 8-bit bytes transmission
+ if (((trans->length % 8) != 0) || ((trans->rxlength % 8) != 0)) return ESP_ERR_INVALID_ARG;
+
+ spi_lobo_host_t *host=(spi_lobo_host_t*)handle->host;
+ esp_err_t ret;
+ uint8_t do_deselect = 0;
+ const uint8_t *txbuffer = NULL;
+ uint8_t *rxbuffer = NULL;
+
+ if (trans->flags & LB_SPI_TRANS_USE_TXDATA) {
+ // Send data from 'trans->tx_data'
+ txbuffer=(uint8_t*)&trans->tx_data[0];
+ } else {
+ // Send data from 'trans->tx_buffer'
+ txbuffer=(uint8_t*)trans->tx_buffer;
+ }
+ if (trans->flags & LB_SPI_TRANS_USE_RXDATA) {
+ // Receive data to 'trans->rx_data'
+ rxbuffer=(uint8_t*)&trans->rx_data[0];
+ } else {
+ // Receive data to 'trans->rx_buffer'
+ rxbuffer=(uint8_t*)trans->rx_buffer;
+ }
+
+ // ** Set transmit & receive length in bytes
+ uint32_t txlen = trans->length / 8;
+ uint32_t rxlen = trans->rxlength / 8;
+
+ if (txbuffer == NULL) txlen = 0;
+ if (rxbuffer == NULL) rxlen = 0;
+ if ((rxlen == 0) && (txlen == 0)) {
+ // ** NOTHING TO SEND or RECEIVE, return
+ return ESP_ERR_INVALID_ARG;
+ }
+
+ // If using 'trans->tx_data' and/or 'trans->rx_data', maximum 4 bytes can be sent/received
+ if ((txbuffer == &trans->tx_data[0]) && (txlen > 4)) return ESP_ERR_INVALID_ARG;
+ if ((rxbuffer == &trans->rx_data[0]) && (rxlen > 4)) return ESP_ERR_INVALID_ARG;
+
+ // --- Wait for SPI bus ready ---
+ while (host->hw->cmd.usr);
+
+ // ** If the device was not selected, select it
+ if (handle->cfg.selected == 0) {
+ ret = spi_lobo_device_select(handle, 0);
+ if (ret) return ret;
+ do_deselect = 1; // We will deselect the device after the operation !
+ }
+
+ // ** Call pre-transmission callback, if any
+ if (handle->cfg.pre_cb) handle->cfg.pre_cb(trans);
+
+ // Test if operating in full duplex mode
+ uint8_t duplex = 1;
+ if (handle->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX) duplex = 0; // Half duplex mode !
+
+ uint32_t bits, rdbits;
+ uint32_t wd;
+ uint8_t bc, rdidx;
+ uint32_t rdcount = rxlen; // Total number of bytes to read
+ uint32_t count = 0; // number of bytes transmitted
+ uint32_t rd_read = 0; // Number of bytes read so far
+
+ host->hw->user.usr_mosi_highpart = 0; // use the whole spi buffer
+
+ // ** Check if address phase will be used
+ host->hw->user2.usr_command_value=trans->command;
+ if (handle->cfg.address_bits>32) {
+ host->hw->addr=trans->address >> 32;
+ host->hw->slv_wr_status=trans->address & 0xffffffff;
+ } else {
+ host->hw->addr=trans->address & 0xffffffff;
+ }
+
+ // Check if we have to transmit some data
+ if (txlen > 0) {
+ host->hw->user.usr_mosi = 1;
+ uint8_t idx;
+ bits = 0; // remaining bits to send
+ idx = 0; // index to spi hw data_buf (16 32-bit words, 64 bytes, 512 bits)
+
+ // ** Transmit 'txlen' bytes
+ while (count < txlen) {
+ wd = 0;
+ for (bc=0;bc<32;bc+=8) {
+ wd |= (uint32_t)txbuffer[count] << bc;
+ count++; // Increment sent data count
+ bits += 8; // Increment bits count
+ if (count == txlen) break; // If all transmit data pushed to hw spi buffer break from the loop
+ }
+ host->hw->data_buf[idx] = wd;
+ idx++;
+ if (idx == 16) {
+ // hw SPI buffer full (all 64 bytes filled, START THE TRANSSACTION
+ host->hw->mosi_dlen.usr_mosi_dbitlen=bits-1; // Set mosi dbitlen
+
+ if ((duplex) && (rdcount > 0)) {
+ // In full duplex mode we are receiving while sending !
+ host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen
+ host->hw->user.usr_miso = 1;
+ }
+ else {
+ host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received
+ host->hw->user.usr_miso = 0;
+ }
+
+ // ** Start the transaction ***
+ host->hw->cmd.usr=1;
+ // Wait the transaction to finish
+ while (host->hw->cmd.usr);
+
+ if ((duplex) && (rdcount > 0)) {
+ // *** in full duplex mode transfer received data to input buffer ***
+ rdidx = 0;
+ while (bits > 0) {
+ wd = host->hw->data_buf[rdidx];
+ rdidx++;
+ for (bc=0;bc<32;bc+=8) { // get max 4 bytes
+ rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
+ rdcount--;
+ bits -= 8;
+ if (rdcount == 0) {
+ bits = 0;
+ break; // Finished reading data
+ }
+ }
+ }
+ }
+ bits = 0; // nothing in hw spi buffer yet
+ idx = 0; // start from the beginning of the hw spi buffer
+ }
+ }
+ // *** All transmit data are sent or pushed to hw spi buffer
+ // bits > 0 IF THERE ARE SOME DATA STILL WAITING IN THE HW SPI TRANSMIT BUFFER
+ if (bits > 0) {
+ // ** WE HAVE SOME DATA IN THE HW SPI TRANSMIT BUFFER
+ host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // Set mosi dbitlen
+
+ if ((duplex) && (rdcount > 0)) {
+ // In full duplex mode we are receiving while sending !
+ host->hw->miso_dlen.usr_miso_dbitlen = bits-1; // Set miso dbitlen
+ host->hw->user.usr_miso = 1;
+ }
+ else {
+ host->hw->miso_dlen.usr_miso_dbitlen = 0; // In half duplex mode nothing will be received
+ host->hw->user.usr_miso = 0;
+ }
+
+ // ** Start the transaction ***
+ host->hw->cmd.usr=1;
+ // Wait the transaction to finish
+ while (host->hw->cmd.usr);
+
+ if ((duplex) && (rdcount > 0)) {
+ // *** in full duplex mode transfer received data to input buffer ***
+ rdidx = 0;
+ while (bits > 0) {
+ wd = host->hw->data_buf[rdidx];
+ rdidx++;
+ for (bc=0;bc<32;bc+=8) { // get max 4 bytes
+ rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
+ rdcount--;
+ bits -= 8;
+ if (bits == 0) break;
+ if (rdcount == 0) {
+ bits = 0;
+ break; // Finished reading data
+ }
+ }
+ }
+ }
+ }
+ //if (duplex) rdcount = 0; // In duplex mode receive only as many bytes as was transmitted
+ }
+
+ // ------------------------------------------------------------------------
+ // *** If rdcount = 0 we have nothing to receive and we exit the function
+ // This is true if no data receive was requested,
+ // or all the data was received in Full duplex mode during the transmission
+ // ------------------------------------------------------------------------
+ if (rdcount > 0) {
+ // ----------------------------------------------------------------------------------------------------------------
+ // *** rdcount > 0, we have to receive some data
+ // This is true if we operate in Half duplex mode when receiving after transmission is done,
+ // or not all data was received in Full duplex mode during the transmission (trans->rxlength > trans->txlength)
+ // ----------------------------------------------------------------------------------------------------------------
+ host->hw->user.usr_mosi = 0; // do not send
+ host->hw->user.usr_miso = 1; // do receive
+ while (rdcount > 0) {
+ if (rdcount <= 64) rdbits = rdcount * 8;
+ else rdbits = 64 * 8;
+
+ // Load receive buffer
+ host->hw->mosi_dlen.usr_mosi_dbitlen=0;
+ host->hw->miso_dlen.usr_miso_dbitlen=rdbits-1;
+
+ // ** Start the transaction ***
+ host->hw->cmd.usr=1;
+ // Wait the transaction to finish
+ while (host->hw->cmd.usr);
+
+ // *** transfer received data to input buffer ***
+ rdidx = 0;
+ while (rdbits > 0) {
+ wd = host->hw->data_buf[rdidx];
+ rdidx++;
+ for (bc=0;bc<32;bc+=8) {
+ rxbuffer[rd_read++] = (uint8_t)((wd >> bc) & 0xFF);
+ rdcount--;
+ rdbits -= 8;
+ if (rdcount == 0) {
+ rdbits = 0;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // ** Call post-transmission callback, if any
+ if (handle->cfg.post_cb) handle->cfg.post_cb(trans);
+
+ if (do_deselect) {
+ // Spi device was selected in this function, we have to deselect it now
+ ret = spi_lobo_device_deselect(handle);
+ if (ret) return ret;
+ }
+
+ return ESP_OK;
+}
diff --git a/components/spidriver/spi_master_lobo.h b/components/spidriver/spi_master_lobo.h
new file mode 100644
index 0000000..bca00ca
--- /dev/null
+++ b/components/spidriver/spi_master_lobo.h
@@ -0,0 +1,354 @@
+// Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#ifndef _DRIVER_SPI_MASTER_LOBO_H_
+#define _DRIVER_SPI_MASTER_LOBO_H_
+
+#include "esp_err.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/semphr.h"
+#include "soc/spi_struct.h"
+
+#include "esp_intr_alloc.h"
+#include "esp32/rom/lldesc.h"
+
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+//Maximum amount of bytes that can be put in one DMA descriptor
+#define SPI_MAX_DMA_LEN (4096-4)
+
+/**
+ * @brief Enum with the three SPI peripherals that are software-accessible in it
+ */
+typedef enum {
+ TFT_SPI_HOST=0, ///< SPI1, SPI; Cannot be used in this driver!
+ TFT_HSPI_HOST=1, ///< SPI2, HSPI
+ TFT_VSPI_HOST=2 ///< SPI3, VSPI
+} spi_lobo_host_device_t;
+
+
+/**
+ * @brief This is a configuration structure for a SPI bus.
+ *
+ * You can use this structure to specify the GPIO pins of the bus. Normally, the driver will use the
+ * GPIO matrix to route the signals. An exception is made when all signals either can be routed through
+ * the IO_MUX or are -1. In that case, the IO_MUX is used, allowing for >40MHz speeds.
+ */
+typedef struct {
+ int mosi_io_num; ///< GPIO pin for Master Out Slave In (=spi_d) signal, or -1 if not used.
+ int miso_io_num; ///< GPIO pin for Master In Slave Out (=spi_q) signal, or -1 if not used.
+ int sclk_io_num; ///< GPIO pin for Spi CLocK signal, or -1 if not used.
+ int quadwp_io_num; ///< GPIO pin for WP (Write Protect) signal which is used as D2 in 4-bit communication modes, or -1 if not used.
+ int quadhd_io_num; ///< GPIO pin for HD (HolD) signal which is used as D3 in 4-bit communication modes, or -1 if not used.
+ int max_transfer_sz; ///< Maximum transfer size, in bytes. Defaults to 4094 if 0.
+} spi_lobo_bus_config_t;
+
+
+#define LB_SPI_DEVICE_TXBIT_LSBFIRST (1<<0) ///< Transmit command/address/data LSB first instead of the default MSB first
+#define LB_SPI_DEVICE_RXBIT_LSBFIRST (1<<1) ///< Receive data LSB first instead of the default MSB first
+#define LB_SPI_DEVICE_BIT_LSBFIRST (SPI_TXBIT_LSBFIRST|SPI_RXBIT_LSBFIRST); ///< Transmit and receive LSB first
+#define LB_SPI_DEVICE_3WIRE (1<<2) ///< Use spiq for both sending and receiving data
+#define LB_SPI_DEVICE_POSITIVE_CS (1<<3) ///< Make CS positive during a transaction instead of negative
+#define LB_SPI_DEVICE_HALFDUPLEX (1<<4) ///< Transmit data before receiving it, instead of simultaneously
+#define LB_SPI_DEVICE_CLK_AS_CS (1<<5) ///< Output clock on CS line if CS is active
+
+#define SPI_ERR_OTHER_CONFIG 7001
+
+typedef struct spi_lobo_transaction_t spi_lobo_transaction_t;
+typedef void(*spi_lobo_transaction_cb_t)(spi_lobo_transaction_t *trans);
+
+/**
+ * @brief This is a configuration for a SPI slave device that is connected to one of the SPI buses.
+ */
+typedef struct {
+ uint8_t command_bits; ///< Amount of bits in command phase (0-16)
+ uint8_t address_bits; ///< Amount of bits in address phase (0-64)
+ uint8_t dummy_bits; ///< Amount of dummy bits to insert between address and data phase
+ uint8_t mode; ///< SPI mode (0-3)
+ uint8_t duty_cycle_pos; ///< Duty cycle of positive clock, in 1/256th increments (128 = 50%/50% duty). Setting this to 0 (=not setting it) is equivalent to setting this to 128.
+ uint8_t cs_ena_pretrans; ///< Amount of SPI bit-cycles the cs should be activated before the transmission (0-16). This only works on half-duplex transactions.
+ uint8_t cs_ena_posttrans; ///< Amount of SPI bit-cycles the cs should stay active after the transmission (0-16)
+ int clock_speed_hz; ///< Clock speed, in Hz
+ int spics_io_num; ///< CS GPIO pin for this device, handled by hardware; set to -1 if not used
+ int spics_ext_io_num; ///< CS GPIO pin for this device, handled by software (spi_lobo_device_select/spi_lobo_device_deselect); only used if spics_io_num=-1
+ uint32_t flags; ///< Bitwise OR of LB_SPI_DEVICE_* flags
+ spi_lobo_transaction_cb_t pre_cb; ///< Callback to be called before a transmission is started. This callback from 'spi_lobo_transfer_data' function.
+ spi_lobo_transaction_cb_t post_cb; ///< Callback to be called after a transmission has completed. This callback from 'spi_lobo_transfer_data' function.
+ uint8_t selected; ///< **INTERNAL** 1 if the device's CS pin is active
+} spi_lobo_device_interface_config_t;
+
+
+#define LB_SPI_TRANS_MODE_DIO (1<<0) ///< Transmit/receive data in 2-bit mode
+#define LB_SPI_TRANS_MODE_QIO (1<<1) ///< Transmit/receive data in 4-bit mode
+#define LB_SPI_TRANS_MODE_DIOQIO_ADDR (1<<2) ///< Also transmit address in mode selected by SPI_MODE_DIO/SPI_MODE_QIO
+#define LB_SPI_TRANS_USE_RXDATA (1<<3) ///< Receive into rx_data member of spi_lobo_transaction_t instead into memory at rx_buffer.
+#define LB_SPI_TRANS_USE_TXDATA (1<<4) ///< Transmit tx_data member of spi_lobo_transaction_t instead of data at tx_buffer. Do not set tx_buffer when using this.
+
+/**
+ * This structure describes one SPI transmission
+ */
+struct spi_lobo_transaction_t {
+ uint32_t flags; ///< Bitwise OR of LB_SPI_TRANS_* flags
+ uint16_t command; ///< Command data. Specific length was given when device was added to the bus.
+ uint64_t address; ///< Address. Specific length was given when device was added to the bus.
+ size_t length; ///< Total data length to be transmitted to the device, in bits; if 0, no data is transmitted
+ size_t rxlength; ///< Total data length to be received from the device, in bits; if 0, no data is received
+ void *user; ///< User-defined variable. Can be used to store eg transaction ID or data to be used by pre_cb and/or post_cb callbacks.
+ union {
+ const void *tx_buffer; ///< Pointer to transmit buffer, or NULL for no MOSI phase
+ uint8_t tx_data[4]; ///< If SPI_USE_TXDATA is set, data set here is sent directly from this variable.
+ };
+ union {
+ void *rx_buffer; ///< Pointer to receive buffer, or NULL for no MISO phase
+ uint8_t rx_data[4]; ///< If SPI_USE_RXDATA is set, data is received directly to this variable
+ };
+};
+
+#define NO_CS 3 // Number of CS pins per SPI host
+#define NO_DEV 6 // Number of spi devices per SPI host; more than 3 devices can be attached to the same bus if using software CS's
+#define SPI_SEMAPHORE_WAIT 2000 // Time in ms to wait for SPI mutex
+
+typedef struct spi_lobo_device_t spi_lobo_device_t;
+
+typedef struct {
+ spi_lobo_device_t *device[NO_DEV];
+ intr_handle_t intr;
+ spi_dev_t *hw;
+ //spi_lobo_transaction_t *cur_trans;
+ int cur_device;
+ lldesc_t *dmadesc_tx;
+ lldesc_t *dmadesc_rx;
+ bool no_gpio_matrix;
+ int dma_chan;
+ int max_transfer_sz;
+ QueueHandle_t spi_lobo_bus_mutex;
+ spi_lobo_bus_config_t cur_bus_config;
+} spi_lobo_host_t;
+
+struct spi_lobo_device_t {
+ spi_lobo_device_interface_config_t cfg;
+ spi_lobo_host_t *host;
+ spi_lobo_bus_config_t bus_config;
+ spi_lobo_host_device_t host_dev;
+};
+
+typedef spi_lobo_device_t* spi_lobo_device_handle_t; ///< Handle for a device on a SPI bus
+typedef spi_lobo_host_t* spi_lobo_host_handle_t;
+typedef spi_lobo_device_interface_config_t* spi_lobo_device_interface_config_handle_t;
+
+
+/**
+ * @brief Add a device. This allocates a CS line for the device, allocates memory for the device structure and hooks
+ * up the CS pin to whatever is specified.
+ *
+ * This initializes the internal structures for a device, plus allocates a CS pin on the indicated SPI master
+ * peripheral and routes it to the indicated GPIO. All SPI master devices have three hw CS pins and can thus control
+ * up to three devices. Software handled CS pin can also be used for additional devices on the same SPI bus.
+ *
+ * ### If selected SPI host device bus is not yet initialized, it is initialized first with 'bus_config' function ###
+ *
+ * @note While in general, speeds up to 80MHz on the dedicated SPI pins and 40MHz on GPIO-matrix-routed pins are
+ * supported, full-duplex transfers routed over the GPIO matrix only support speeds up to 26MHz.
+ *
+ * @param host SPI peripheral to allocate device on (HSPI or VSPI)
+ * @param dev_config SPI interface protocol config for the device
+ * @param bus_config Pointer to a spi_lobo_bus_config_t struct specifying how the host device bus should be initialized
+ * @param handle Pointer to variable to hold the device handle
+ * @return
+ * - ESP_ERR_INVALID_ARG if parameter is invalid
+ * - ESP_ERR_NOT_FOUND if host doesn't have any free CS slots
+ * - ESP_ERR_NO_MEM if out of memory
+ * - ESP_OK on success
+ */
+esp_err_t spi_lobo_bus_add_device(spi_lobo_host_device_t host, spi_lobo_bus_config_t *bus_config, spi_lobo_device_interface_config_t *dev_config, spi_lobo_device_handle_t *handle);
+
+/**
+ * @brief Remove a device from the SPI bus. If after removal no other device is attached to the spi bus device, it is freed.
+ *
+ * @param handle Device handle to free
+ * @return
+ * - ESP_ERR_INVALID_ARG if parameter is invalid
+ * - ESP_ERR_INVALID_STATE if device already is freed
+ * - ESP_OK on success
+ */
+esp_err_t spi_lobo_bus_remove_device(spi_lobo_device_handle_t handle);
+
+
+/**
+ * @brief Return the actuall SPI bus speed for the spi device in Hz
+ *
+ * Some frequencies cannot be set, for example 30000000 will actually set SPI clock to 26666666 Hz
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ *
+ * @return
+ * - actuall SPI clock
+ */
+uint32_t spi_lobo_get_speed(spi_lobo_device_handle_t handle);
+
+/**
+ * @brief Set the new clock speed for the device, return the actuall SPI bus speed set, in Hz
+ * This function can be used after the device is initialized
+ *
+ * Some frequencies cannot be set, for example 30000000 will actually set SPI clock to 26666666 Hz
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ * @param speed New device spi clock to be set in Hz
+ *
+ * @return
+ * - actuall SPI clock
+ * - 0 if speed cannot be set
+ */
+uint32_t spi_lobo_set_speed(spi_lobo_device_handle_t handle, uint32_t speed);
+
+/**
+ * @brief Select spi device for transmission
+ *
+ * It configures spi bus with selected spi device parameters if previously selected device was different than the current
+ * If device's spics_io_num=-1 and spics_ext_io_num > 0 'spics_ext_io_num' pin is set to active state (low)
+ *
+ * spi bus device's semaphore is taken before selecting the device
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ * @param force configure spi bus even if the previous device was the same
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG if parameter is invalid
+ * - ESP_OK on success
+ */
+esp_err_t spi_lobo_device_select(spi_lobo_device_handle_t handle, int force);
+
+/**
+ * @brief De-select spi device
+ *
+ * If device's spics_io_num=-1 and spics_ext_io_num > 0 'spics_ext_io_num' pin is set to inactive state (high)
+ *
+ * spi bus device's semaphore is given after selecting the device
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG if parameter is invalid
+ * - ESP_OK on success
+ */
+esp_err_t spi_lobo_device_deselect(spi_lobo_device_handle_t handle);
+
+
+/**
+ * @brief Check if spi bus uses native spi pins
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ *
+ * @return
+ * - true if native spi pins are used
+ * - false if spi pins are routed through gpio matrix
+ */
+bool spi_lobo_uses_native_pins(spi_lobo_device_handle_t handle);
+
+/**
+ * @brief Get spi bus native spi pins
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ *
+ * @return
+ * places spi bus native pins in provided pointers
+ */
+void spi_lobo_get_native_pins(int host, int *sdi, int *sdo, int *sck);
+
+/**
+ * @brief Transimit and receive data to/from spi device based on transaction data
+ *
+ * TRANSMIT 8-bit data to spi device from 'trans->tx_buffer' or 'trans->tx_data' (trans->lenght/8 bytes)
+ * and RECEIVE data to 'trans->rx_buffer' or 'trans->rx_data' (trans->rx_length/8 bytes)
+ * Lengths must be 8-bit multiples!
+ * If trans->rx_buffer is NULL or trans->rx_length is 0, only transmits data
+ * If trans->tx_buffer is NULL or trans->length is 0, only receives data
+ * If the device is in duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag NOT set), data are transmitted and received simultaneously.
+ * If the device is in half duplex mode (LB_SPI_DEVICE_HALFDUPLEX flag IS set), data are received after transmission
+ * 'address', 'command' and 'dummy bits' are transmitted before data phase IF set in device's configuration
+ * and IF 'trans->length' and 'trans->rx_length' are NOT both 0
+ * If device was not previously selected, it will be selected before transmission and deselected after transmission.
+ *
+ * @param handle Device handle obtained using spi_lobo_bus_add_device
+ *
+ * @param trans Pointer to variable containing the description of the transaction that is executed
+ *
+ * @return
+ * - ESP_ERR_INVALID_ARG if parameter is invalid
+ * - ESP error code if device cannot be selected
+ * - ESP_OK on success
+ *
+ */
+esp_err_t spi_lobo_transfer_data(spi_lobo_device_handle_t handle, spi_lobo_transaction_t *trans);
+
+
+/*
+ * SPI transactions uses the semaphore (taken in select function) to protect the transfer
+ */
+esp_err_t spi_lobo_device_TakeSemaphore(spi_lobo_device_handle_t handle);
+void spi_lobo_device_GiveSemaphore(spi_lobo_device_handle_t handle);
+
+
+/**
+ * @brief Setup a DMA link chain
+ *
+ * This routine will set up a chain of linked DMA descriptors in the array pointed to by
+ * ``dmadesc``. Enough DMA descriptors will be used to fit the buffer of ``len`` bytes in, and the
+ * descriptors will point to the corresponding positions in ``buffer`` and linked together. The
+ * end result is that feeding ``dmadesc[0]`` into DMA hardware results in the entirety ``len`` bytes
+ * of ``data`` being read or written.
+ *
+ * @param dmadesc Pointer to array of DMA descriptors big enough to be able to convey ``len`` bytes
+ * @param len Length of buffer
+ * @param data Data buffer to use for DMA transfer
+ * @param isrx True if data is to be written into ``data``, false if it's to be read from ``data``.
+ */
+void spi_lobo_setup_dma_desc_links(lldesc_t *dmadesc, int len, const uint8_t *data, bool isrx);
+
+/**
+ * @brief Check if a DMA reset is requested but has not completed yet
+ *
+ * @return True when a DMA reset is requested but hasn't completed yet. False otherwise.
+ */
+bool spi_lobo_dmaworkaround_reset_in_progress();
+
+
+/**
+ * @brief Mark a DMA channel as idle.
+ *
+ * A call to this function tells the workaround logic that this channel will
+ * not be affected by a global SPI DMA reset.
+ */
+void spi_lobo_dmaworkaround_idle(int dmachan);
+
+/**
+ * @brief Mark a DMA channel as active.
+ *
+ * A call to this function tells the workaround logic that this channel will
+ * be affected by a global SPI DMA reset, and a reset like that should not be attempted.
+ */
+void spi_lobo_dmaworkaround_transfer_active(int dmachan);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/components/tft/CMakeLists.txt b/components/tft/CMakeLists.txt
new file mode 100644
index 0000000..a5f8712
--- /dev/null
+++ b/components/tft/CMakeLists.txt
@@ -0,0 +1,4 @@
+FILE(GLOB SOURCES *.c)
+idf_component_register(SRCS ${SOURCES}
+ INCLUDE_DIRS "."
+ REQUIRES spidriver)
diff --git a/components/tft/DefaultFont.c b/components/tft/DefaultFont.c
new file mode 100644
index 0000000..3e3c2a0
--- /dev/null
+++ b/components/tft/DefaultFont.c
@@ -0,0 +1,333 @@
+// Default font
+
+// ========================================================================
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// Header Format:
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset (start Y of visible pixels)
+// Width (width of the visible pixels)
+// Height (height of the visible pixels)
+// xOffset (start X of visible pixels)
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+// ========================================================================
+
+// dejavu
+// Point Size : 12
+// Memory usage : 1158 bytes
+// # characters : 95
+
+const unsigned char tft_DefaultFont[] =
+{
+0x00, 0x0B, 0x86, 0x04,
+
+// ' '
+0x20,0x0A,0x00,0x00,0x00,0x04,
+
+// '!'
+0x21,0x01,0x01,0x09,0x02,0x05,
+0xFD,0x80,
+// '"'
+0x22,0x01,0x03,0x03,0x01,0x05,
+0xB6,0x80,
+// '#'
+0x23,0x02,0x08,0x08,0x01,0x0A,
+0x12,0x14,0x7F,0x24,0x24,0xFE,0x28,0x48,
+// '$'
+0x24,0x01,0x06,0x0B,0x02,0x08,
+0x21,0xCA,0xA8,0xE0,0xE2,0xAA,0x70,0x82,0x00,
+// '%'
+0x25,0x01,0x0A,0x09,0x00,0x0B,
+0x61,0x24,0x89,0x22,0x50,0x6D,0x82,0x91,0x24,0x49,0x21,0x80,
+// '&'
+0x26,0x01,0x09,0x09,0x01,0x0A,
+0x30,0x24,0x10,0x0C,0x05,0x14,0x4A,0x19,0x8C,0x7B,0x00,
+// '''
+0x27,0x01,0x01,0x03,0x01,0x03,
+0xE0,
+// '('
+0x28,0x00,0x03,0x0B,0x01,0x05,
+0x69,0x49,0x24,0x48,0x80,
+// ')'
+0x29,0x00,0x03,0x0B,0x01,0x05,
+0x89,0x12,0x49,0x4A,0x00,
+// '*'
+0x2A,0x01,0x05,0x06,0x01,0x06,
+0x25,0x5C,0xEA,0x90,
+// '+'
+0x2B,0x03,0x07,0x07,0x01,0x0A,
+0x10,0x20,0x47,0xF1,0x02,0x04,0x00,
+// ','
+0x2C,0x08,0x01,0x03,0x01,0x04,
+0xE0,
+// '-'
+0x2D,0x06,0x03,0x01,0x01,0x04,
+0xE0,
+// '.'
+0x2E,0x08,0x01,0x02,0x01,0x04,
+0xC0,
+// '/'
+0x2F,0x01,0x04,0x0A,0x00,0x04,
+0x11,0x22,0x24,0x44,0x88,
+// '0'
+0x30,0x01,0x06,0x09,0x01,0x08,
+0x79,0x28,0x61,0x86,0x18,0x52,0x78,
+// '1'
+0x31,0x01,0x05,0x09,0x01,0x08,
+0xE1,0x08,0x42,0x10,0x84,0xF8,
+// '2'
+0x32,0x01,0x07,0x09,0x01,0x08,
+0x79,0x18,0x10,0x20,0x82,0x08,0x20,0xFC,
+// '3'
+0x33,0x01,0x06,0x09,0x01,0x08,
+0x7A,0x10,0x41,0x38,0x30,0x63,0x78,
+// '4'
+0x34,0x01,0x06,0x09,0x01,0x08,
+0x18,0x62,0x92,0x4A,0x2F,0xC2,0x08,
+// '5'
+0x35,0x01,0x06,0x09,0x01,0x08,
+0xFA,0x08,0x3C,0x0C,0x10,0x63,0x78,
+// '6'
+0x36,0x01,0x06,0x09,0x01,0x08,
+0x39,0x18,0x3E,0xCE,0x18,0x53,0x78,
+// '7'
+0x37,0x01,0x06,0x09,0x01,0x08,
+0xFC,0x10,0x82,0x10,0x42,0x08,0x40,
+// '8'
+0x38,0x01,0x06,0x09,0x01,0x08,
+0x7B,0x38,0x73,0x7B,0x38,0x73,0x78,
+// '9'
+0x39,0x01,0x06,0x09,0x01,0x08,
+0x7B,0x28,0x61,0xCD,0xD0,0x62,0x70,
+// ':'
+0x3A,0x04,0x01,0x06,0x01,0x04,
+0xCC,
+// ';'
+0x3B,0x04,0x01,0x07,0x01,0x04,
+0xCE,
+// '<'
+0x3C,0x03,0x08,0x06,0x01,0x0A,
+0x03,0x1E,0xE0,0xE0,0x1E,0x03,
+// '='
+0x3D,0x05,0x08,0x03,0x01,0x0A,
+0xFF,0x00,0xFF,
+// '>'
+0x3E,0x03,0x08,0x06,0x01,0x0A,
+0xC0,0x78,0x07,0x07,0x78,0xC0,
+// '?'
+0x3F,0x01,0x05,0x09,0x00,0x06,
+0x74,0x42,0x22,0x10,0x04,0x20,
+// '@'
+0x40,0x01,0x0B,0x0B,0x01,0x0D,
+0x1F,0x06,0x19,0x01,0x46,0x99,0x13,0x22,0x64,0x54,0x6C,0x40,0x04,0x10,0x7C,0x00,
+// 'A'
+0x41,0x01,0x08,0x09,0x00,0x08,
+0x18,0x18,0x24,0x24,0x24,0x42,0x7E,0x42,0x81,
+// 'B'
+0x42,0x01,0x06,0x09,0x01,0x08,
+0xFA,0x18,0x61,0xFA,0x18,0x61,0xF8,
+// 'C'
+0x43,0x01,0x06,0x09,0x01,0x08,
+0x39,0x18,0x20,0x82,0x08,0x11,0x38,
+// 'D'
+0x44,0x01,0x07,0x09,0x01,0x09,
+0xF9,0x0A,0x0C,0x18,0x30,0x60,0xC2,0xF8,
+// 'E'
+0x45,0x01,0x06,0x09,0x01,0x08,
+0xFE,0x08,0x20,0xFE,0x08,0x20,0xFC,
+// 'F'
+0x46,0x01,0x05,0x09,0x01,0x07,
+0xFC,0x21,0x0F,0xC2,0x10,0x80,
+// 'G'
+0x47,0x01,0x07,0x09,0x01,0x09,
+0x3C,0x86,0x04,0x08,0xF0,0x60,0xA1,0x3C,
+// 'H'
+0x48,0x01,0x07,0x09,0x01,0x09,
+0x83,0x06,0x0C,0x1F,0xF0,0x60,0xC1,0x82,
+// 'I'
+0x49,0x01,0x01,0x09,0x01,0x03,
+0xFF,0x80,
+// 'J'
+0x4A,0x01,0x03,0x0B,0xFF,0x03,
+0x24,0x92,0x49,0x27,0x00,
+// 'K'
+0x4B,0x01,0x07,0x09,0x01,0x07,
+0x85,0x12,0x45,0x0C,0x14,0x24,0x44,0x84,
+// 'L'
+0x4C,0x01,0x05,0x09,0x01,0x06,
+0x84,0x21,0x08,0x42,0x10,0xF8,
+// 'M'
+0x4D,0x01,0x08,0x09,0x01,0x0A,
+0x81,0xC3,0xC3,0xA5,0xA5,0x99,0x99,0x81,0x81,
+// 'N'
+0x4E,0x01,0x07,0x09,0x01,0x09,
+0xC3,0x86,0x8D,0x19,0x31,0x62,0xC3,0x86,
+// 'O'
+0x4F,0x01,0x07,0x09,0x01,0x09,
+0x38,0x8A,0x0C,0x18,0x30,0x60,0xA2,0x38,
+// 'P'
+0x50,0x01,0x06,0x09,0x01,0x08,
+0xFA,0x38,0x63,0xFA,0x08,0x20,0x80,
+// 'Q'
+0x51,0x01,0x07,0x0B,0x01,0x09,
+0x38,0x8A,0x0C,0x18,0x30,0x60,0xA2,0x38,0x10,0x10,
+// 'R'
+0x52,0x01,0x07,0x09,0x01,0x08,
+0xF9,0x1A,0x14,0x6F,0x91,0x21,0x42,0x82,
+// 'S'
+0x53,0x01,0x06,0x09,0x01,0x08,
+0x7B,0x18,0x30,0x78,0x30,0x63,0x78,
+// 'T'
+0x54,0x01,0x07,0x09,0x00,0x07,
+0xFE,0x20,0x40,0x81,0x02,0x04,0x08,0x10,
+// 'U'
+0x55,0x01,0x07,0x09,0x01,0x09,
+0x83,0x06,0x0C,0x18,0x30,0x60,0xA2,0x38,
+// 'V'
+0x56,0x01,0x0A,0x09,0xFF,0x08,
+0x40,0x90,0x22,0x10,0x84,0x21,0x04,0x81,0x20,0x30,0x0C,0x00,
+// 'W'
+0x57,0x01,0x0B,0x09,0x00,0x0B,
+0x84,0x28,0x89,0x11,0x27,0x22,0xA8,0x55,0x0E,0xE0,0x88,0x11,0x00,
+// 'X'
+0x58,0x01,0x07,0x09,0x00,0x07,
+0xC6,0x88,0xA1,0xC1,0x07,0x0A,0x22,0x82,
+// 'Y'
+0x59,0x01,0x07,0x09,0x00,0x07,
+0x82,0x89,0x11,0x43,0x82,0x04,0x08,0x10,
+// 'Z'
+0x5A,0x01,0x07,0x09,0x01,0x09,
+0xFE,0x04,0x10,0x41,0x04,0x10,0x40,0xFE,
+// '['
+0x5B,0x01,0x02,0x0B,0x02,0x05,
+0xEA,0xAA,0xAC,
+// '\'
+0x5C,0x01,0x04,0x0A,0x00,0x04,
+0x88,0x44,0x42,0x22,0x11,
+// ']'
+0x5D,0x01,0x02,0x0B,0x01,0x05,
+0xD5,0x55,0x5C,
+// '^'
+0x5E,0x01,0x08,0x03,0x01,0x0A,
+0x18,0x24,0x42,
+// '_'
+0x5F,0x0C,0x06,0x01,0x00,0x06,
+0xFC,
+// '`'
+0x60,0x00,0x03,0x02,0x01,0x06,
+0x44,
+// 'a'
+0x61,0x03,0x06,0x07,0x01,0x08,
+0x7A,0x30,0x5F,0x86,0x37,0x40,
+// 'b'
+0x62,0x00,0x06,0x0A,0x01,0x08,
+0x82,0x08,0x2E,0xCA,0x18,0x61,0xCE,0xE0,
+// 'c'
+0x63,0x03,0x05,0x07,0x01,0x07,
+0x72,0x61,0x08,0x25,0xC0,
+// 'd'
+0x64,0x00,0x06,0x0A,0x01,0x08,
+0x04,0x10,0x5D,0xCE,0x18,0x61,0xCD,0xD0,
+// 'e'
+0x65,0x03,0x06,0x07,0x01,0x08,
+0x39,0x38,0x7F,0x81,0x13,0x80,
+// 'f'
+0x66,0x00,0x04,0x0A,0x00,0x04,
+0x34,0x4F,0x44,0x44,0x44,
+// 'g'
+0x67,0x03,0x06,0x0A,0x01,0x08,
+0x77,0x38,0x61,0x87,0x37,0x41,0x4C,0xE0,
+// 'h'
+0x68,0x00,0x06,0x0A,0x01,0x08,
+0x82,0x08,0x2E,0xC6,0x18,0x61,0x86,0x10,
+// 'i'
+0x69,0x01,0x01,0x09,0x01,0x03,
+0xBF,0x80,
+// 'j'
+0x6A,0x01,0x02,0x0C,0x00,0x03,
+0x45,0x55,0x56,
+// 'k'
+0x6B,0x00,0x06,0x0A,0x01,0x07,
+0x82,0x08,0x22,0x92,0x8E,0x28,0x92,0x20,
+// 'l'
+0x6C,0x00,0x01,0x0A,0x01,0x03,
+0xFF,0xC0,
+// 'm'
+0x6D,0x03,0x09,0x07,0x01,0x0B,
+0xB3,0x66,0x62,0x31,0x18,0x8C,0x46,0x22,
+// 'n'
+0x6E,0x03,0x06,0x07,0x01,0x08,
+0xBB,0x18,0x61,0x86,0x18,0x40,
+// 'o'
+0x6F,0x03,0x06,0x07,0x01,0x08,
+0x7B,0x38,0x61,0x87,0x37,0x80,
+// 'p'
+0x70,0x03,0x06,0x0A,0x01,0x08,
+0xBB,0x28,0x61,0x87,0x3B,0xA0,0x82,0x00,
+// 'q'
+0x71,0x03,0x06,0x0A,0x01,0x08,
+0x77,0x38,0x61,0x87,0x37,0x41,0x04,0x10,
+// 'r'
+0x72,0x03,0x04,0x07,0x01,0x05,
+0xBC,0x88,0x88,0x80,
+// 's'
+0x73,0x03,0x06,0x07,0x01,0x07,
+0x72,0x28,0x1C,0x0A,0x27,0x00,
+// 't'
+0x74,0x01,0x04,0x09,0x00,0x05,
+0x44,0xF4,0x44,0x44,0x30,
+// 'u'
+0x75,0x03,0x06,0x07,0x01,0x08,
+0x86,0x18,0x61,0x86,0x37,0x40,
+// 'v'
+0x76,0x03,0x08,0x07,0xFF,0x06,
+0x42,0x42,0x24,0x24,0x24,0x18,0x18,
+// 'w'
+0x77,0x03,0x09,0x07,0x00,0x09,
+0x88,0xC4,0x57,0x4A,0xA5,0x51,0x10,0x88,
+// 'x'
+0x78,0x03,0x06,0x07,0x00,0x06,
+0x85,0x24,0x8C,0x49,0x28,0x40,
+// 'y'
+0x79,0x03,0x08,0x0A,0xFF,0x06,
+0x42,0x42,0x24,0x24,0x14,0x18,0x08,0x08,0x10,0x60,
+// 'z'
+0x7A,0x03,0x05,0x07,0x00,0x05,
+0xF8,0x44,0x44,0x43,0xE0,
+// '{'
+0x7B,0x01,0x05,0x0B,0x02,0x08,
+0x19,0x08,0x42,0x60,0x84,0x21,0x06,
+// '|'
+0x7C,0x01,0x01,0x0C,0x02,0x04,
+0xFF,0xF0,
+// '}'
+0x7D,0x01,0x05,0x0B,0x01,0x08,
+0xC1,0x08,0x42,0x0C,0x84,0x21,0x30,
+// '~'
+0x7E,0x04,0x08,0x03,0x01,0x0A,
+0x00,0x71,0x8E,
+
+// Terminator
+0xFF
+};
diff --git a/components/tft/DejaVuSans18.c b/components/tft/DejaVuSans18.c
new file mode 100644
index 0000000..b9e6ba9
--- /dev/null
+++ b/components/tft/DejaVuSans18.c
@@ -0,0 +1,322 @@
+// ============================================================================
+// Proportional font Header Format:
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+// ============================================================================
+
+// DejaVuSans
+// Point Size : 18
+// Memory usage : 1828 bytes
+// # characters : 95
+
+const unsigned char tft_Dejavu18[] =
+{
+0x00, 0x12, 0x00, 0x00,
+
+// ' '
+0x20,0x0E,0x00,0x00,0x00,0x06,
+
+// '!'
+0x21,0x01,0x02,0x0D,0x03,0x07,
+0xFF,0xFF,0xC3,0xC0,
+// '"'
+0x22,0x01,0x06,0x05,0x01,0x08,
+0xCF,0x3C,0xF3,0xCC,
+// '#'
+0x23,0x00,0x0C,0x0E,0x01,0x0F,
+0x04,0x40,0x44,0x0C,0xC0,0xC8,0x7F,0xF7,0xFF,0x09,0x81,0x90,0xFF,0xEF,0xFE,0x13,0x03,0x30,0x32,0x02,0x20,
+// '$'
+0x24,0x00,0x0A,0x11,0x01,0x0B,
+0x08,0x02,0x03,0xE1,0xFC,0xE9,0x32,0x0F,0x81,0xF8,0x0F,0x02,0x60,0x9A,0x2E,0xFF,0x1F,0x80,0x80,0x20,0x08,0x00,
+// '%'
+0x25,0x01,0x0F,0x0D,0x01,0x11,
+0x78,0x10,0x90,0x43,0x31,0x86,0x62,0x0C,0xC8,0x19,0x10,0x1E,0x4F,0x01,0x12,0x02,0x66,0x08,0xCC,0x31,0x98,0x41,0x21,0x03,0xC0,
+// '&'
+0x26,0x01,0x0C,0x0D,0x01,0x0D,
+0x0F,0x01,0xF8,0x30,0x83,0x00,0x38,0x03,0xC0,0x7E,0x6C,0x76,0xC3,0xCC,0x18,0xE1,0xC7,0xFE,0x3E,0x70,
+// '''
+0x27,0x01,0x02,0x05,0x01,0x04,
+0xFF,0xC0,
+// '('
+0x28,0x00,0x04,0x10,0x02,0x07,
+0x32,0x66,0x4C,0xCC,0xCC,0xC4,0x66,0x23,
+// ')'
+0x29,0x00,0x04,0x10,0x01,0x07,
+0xC4,0x66,0x23,0x33,0x33,0x32,0x66,0x4C,
+// '*'
+0x2A,0x01,0x07,0x08,0x01,0x09,
+0x11,0x25,0x51,0xC3,0x8A,0xA4,0x88,
+// '+'
+0x2B,0x02,0x0C,0x0C,0x02,0x0F,
+0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x0F,0xFF,0xFF,0xF0,0x60,0x06,0x00,0x60,0x06,0x00,0x60,
+// ','
+0x2C,0x0C,0x03,0x04,0x01,0x06,
+0x6D,0x40,
+// '-'
+0x2D,0x08,0x05,0x02,0x01,0x07,
+0xFF,0xC0,
+// '.'
+0x2E,0x0C,0x02,0x02,0x02,0x06,
+0xF0,
+// '/'
+0x2F,0x01,0x06,0x0F,0x00,0x06,
+0x0C,0x31,0x86,0x18,0xE3,0x0C,0x31,0xC6,0x18,0x63,0x0C,0x00,
+// '0'
+0x30,0x01,0x09,0x0D,0x01,0x0B,
+0x3E,0x3F,0x98,0xD8,0x3C,0x1E,0x0F,0x07,0x83,0xC1,0xE0,0xD8,0xCF,0xE3,0xE0,
+// '1'
+0x31,0x01,0x08,0x0D,0x02,0x0B,
+0x38,0xF8,0xD8,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0xFF,0xFF,
+// '2'
+0x32,0x01,0x09,0x0D,0x01,0x0B,
+0x7C,0x7F,0x21,0xC0,0x60,0x30,0x30,0x18,0x18,0x18,0x18,0x18,0x1F,0xEF,0xF0,
+// '3'
+0x33,0x01,0x09,0x0D,0x01,0x0B,
+0x7E,0x7F,0xA0,0xE0,0x30,0x39,0xF0,0xFC,0x07,0x01,0x80,0xE0,0xFF,0xE7,0xE0,
+// '4'
+0x34,0x01,0x0A,0x0D,0x01,0x0B,
+0x07,0x01,0xC0,0xB0,0x6C,0x13,0x08,0xC6,0x31,0x0C,0xFF,0xFF,0xF0,0x30,0x0C,0x03,0x00,
+// '5'
+0x35,0x01,0x08,0x0D,0x01,0x0B,
+0x7E,0x7E,0x60,0x60,0x7C,0x7E,0x47,0x03,0x03,0x03,0x87,0xFE,0x7C,
+// '6'
+0x36,0x01,0x09,0x0D,0x01,0x0B,
+0x1E,0x1F,0x9C,0x5C,0x0C,0x06,0xF3,0xFD,0xC7,0xC1,0xE0,0xD8,0xEF,0xE1,0xE0,
+// '7'
+0x37,0x01,0x08,0x0D,0x01,0x0B,
+0xFF,0xFF,0x06,0x06,0x06,0x0E,0x0C,0x0C,0x1C,0x18,0x18,0x38,0x30,
+// '8'
+0x38,0x01,0x09,0x0D,0x01,0x0B,
+0x3E,0x3F,0xB8,0xF8,0x3E,0x39,0xF1,0xFD,0xC7,0xC1,0xE0,0xF8,0xEF,0xE3,0xE0,
+// '9'
+0x39,0x01,0x09,0x0D,0x01,0x0B,
+0x3C,0x3F,0xB8,0xD8,0x3C,0x1F,0x1D,0xFE,0x7B,0x01,0x81,0xD1,0xCF,0xC3,0xC0,
+// ':'
+0x3A,0x05,0x02,0x09,0x02,0x06,
+0xF0,0x03,0xC0,
+// ';'
+0x3B,0x05,0x03,0x0B,0x01,0x06,
+0x6C,0x00,0x03,0x6A,0x00,
+// '<'
+0x3C,0x04,0x0B,0x0A,0x02,0x0F,
+0x00,0x20,0x3C,0x1F,0x1F,0x0F,0x81,0xF0,0x0F,0x80,0x3E,0x01,0xE0,0x04,
+// '='
+0x3D,0x05,0x0B,0x06,0x02,0x0F,
+0xFF,0xFF,0xFC,0x00,0x00,0x0F,0xFF,0xFF,0xC0,
+// '>'
+0x3E,0x04,0x0B,0x0A,0x02,0x0F,
+0x80,0x1E,0x01,0xF0,0x07,0xC0,0x3E,0x07,0xC3,0xE3,0xE0,0xF0,0x10,0x00,
+// '?'
+0x3F,0x01,0x07,0x0D,0x01,0x0A,
+0x79,0xFA,0x38,0x30,0x61,0x86,0x18,0x30,0x60,0x01,0x83,0x00,
+// '@'
+0x40,0x01,0x10,0x10,0x01,0x12,
+0x07,0xE0,0x1F,0xF8,0x3C,0x1C,0x70,0x06,0x60,0x07,0xE3,0x63,0xC7,0xE3,0xC6,0x63,0xC6,0x66,0xC7,0xFC,0xE3,0x70,0x60,0x00,0x70,0x00,0x3C,0x30,0x1F,0xF0,0x07,0xC0,
+// 'A'
+0x41,0x01,0x0C,0x0D,0x00,0x0C,
+0x06,0x00,0x60,0x0F,0x00,0xF0,0x19,0x81,0x98,0x19,0x83,0x0C,0x3F,0xC7,0xFE,0x60,0x66,0x06,0xC0,0x30,
+// 'B'
+0x42,0x01,0x09,0x0D,0x02,0x0C,
+0xFC,0x7F,0xB0,0xD8,0x6C,0x37,0xF3,0xF9,0x86,0xC1,0xE0,0xF0,0xFF,0xEF,0xE0,
+// 'C'
+0x43,0x01,0x0B,0x0D,0x01,0x0D,
+0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x0C,0x01,0xC0,0x9F,0xF0,0xFC,
+// 'D'
+0x44,0x01,0x0B,0x0D,0x02,0x0E,
+0xFE,0x1F,0xF3,0x07,0x60,0x6C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1B,0x07,0x7F,0xCF,0xE0,
+// 'E'
+0x45,0x01,0x08,0x0D,0x02,0x0B,
+0xFF,0xFF,0xC0,0xC0,0xC0,0xFF,0xFF,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF,
+// 'F'
+0x46,0x01,0x08,0x0D,0x02,0x0A,
+0xFF,0xFF,0xC0,0xC0,0xC0,0xFE,0xFE,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,
+// 'G'
+0x47,0x01,0x0B,0x0D,0x01,0x0E,
+0x0F,0xC7,0xFD,0xC0,0xB0,0x0C,0x01,0x87,0xF0,0xFE,0x03,0xC0,0x6C,0x0D,0xC1,0x9F,0xE0,0xF8,
+// 'H'
+0x48,0x01,0x0A,0x0D,0x02,0x0E,
+0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xFF,0xFF,0xFF,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xC0,
+// 'I'
+0x49,0x01,0x02,0x0D,0x02,0x06,
+0xFF,0xFF,0xFF,0xC0,
+// 'J'
+0x4A,0x01,0x05,0x11,0xFF,0x06,
+0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0xFE,0xE0,
+// 'K'
+0x4B,0x01,0x0B,0x0D,0x02,0x0C,
+0xC1,0x98,0x63,0x18,0x66,0x0D,0x81,0xE0,0x3C,0x06,0xC0,0xCC,0x18,0xC3,0x0C,0x60,0xCC,0x0C,
+// 'L'
+0x4C,0x01,0x08,0x0D,0x02,0x0A,
+0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xFF,0xFF,
+// 'M'
+0x4D,0x01,0x0C,0x0D,0x02,0x10,
+0xE0,0x7F,0x0F,0xF0,0xFD,0x8B,0xD9,0xBD,0x9B,0xCF,0x3C,0xF3,0xC6,0x3C,0x63,0xC0,0x3C,0x03,0xC0,0x30,
+// 'N'
+0x4E,0x01,0x0A,0x0D,0x02,0x0E,
+0xE0,0xF8,0x3F,0x0F,0xC3,0xD8,0xF6,0x3C,0xCF,0x1B,0xC6,0xF0,0xFC,0x3F,0x07,0xC1,0xC0,
+// 'O'
+0x4F,0x01,0x0C,0x0D,0x01,0x0E,
+0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,
+// 'P'
+0x50,0x01,0x08,0x0D,0x02,0x0B,
+0xFC,0xFE,0xC7,0xC3,0xC3,0xC7,0xFE,0xFC,0xC0,0xC0,0xC0,0xC0,0xC0,
+// 'Q'
+0x51,0x01,0x0C,0x0F,0x01,0x0E,
+0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,0x18,0x00,0xC0,
+// 'R'
+0x52,0x01,0x0A,0x0D,0x02,0x0D,
+0xFC,0x3F,0x8C,0x73,0x0C,0xC3,0x31,0xCF,0xE3,0xF0,0xC6,0x30,0xCC,0x33,0x06,0xC1,0xC0,
+// 'S'
+0x53,0x01,0x0A,0x0D,0x01,0x0B,
+0x3E,0x1F,0xCE,0x13,0x00,0xC0,0x1F,0x03,0xF0,0x0E,0x01,0x80,0x68,0x3B,0xFC,0x7E,0x00,
+// 'T'
+0x54,0x01,0x0C,0x0D,0x00,0x0C,
+0xFF,0xFF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,
+// 'U'
+0x55,0x01,0x0A,0x0D,0x02,0x0E,
+0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x36,0x19,0xFE,0x1E,0x00,
+// 'V'
+0x56,0x01,0x0C,0x0D,0x00,0x0C,
+0xC0,0x36,0x06,0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60,0x06,0x00,
+// 'W'
+0x57,0x01,0x11,0x0D,0x01,0x13,
+0xC1,0xC1,0xE0,0xE0,0xD8,0xF8,0xCC,0x6C,0x66,0x36,0x33,0x1B,0x18,0xD8,0xD8,0x6C,0x6C,0x36,0x36,0x1F,0x1F,0x07,0x07,0x03,0x83,0x81,0xC1,0xC0,
+// 'X'
+0x58,0x01,0x0B,0x0D,0x01,0x0D,
+0x70,0xE6,0x18,0xE6,0x0D,0xC0,0xF0,0x1C,0x03,0x80,0x78,0x1B,0x07,0x30,0xC7,0x30,0x6E,0x0E,
+// 'Y'
+0x59,0x01,0x0C,0x0D,0x00,0x0C,
+0xE0,0x76,0x06,0x30,0xC1,0x98,0x19,0x80,0xF0,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,
+// 'Z'
+0x5A,0x01,0x0B,0x0D,0x01,0x0D,
+0xFF,0xFF,0xFC,0x07,0x01,0xC0,0x30,0x0E,0x03,0x80,0xE0,0x18,0x06,0x01,0xC0,0x7F,0xFF,0xFE,
+// '['
+0x5B,0x00,0x04,0x10,0x01,0x07,
+0xFF,0xCC,0xCC,0xCC,0xCC,0xCC,0xCC,0xFF,
+// '\'
+0x5C,0x01,0x06,0x0F,0x00,0x06,
+0xC3,0x06,0x18,0x61,0xC3,0x0C,0x30,0xE1,0x86,0x18,0x30,0xC0,
+// ']'
+0x5D,0x00,0x04,0x10,0x02,0x07,
+0xFF,0x33,0x33,0x33,0x33,0x33,0x33,0xFF,
+// '^'
+0x5E,0x01,0x0B,0x05,0x02,0x0F,
+0x0E,0x03,0xE0,0xC6,0x30,0x6C,0x06,
+// '_'
+0x5F,0x10,0x09,0x02,0x00,0x09,
+0xFF,0xFF,0xC0,
+// '`'
+0x60,0x00,0x04,0x03,0x02,0x09,
+0xC6,0x30,
+// 'a'
+0x61,0x04,0x08,0x0A,0x01,0x0A,
+0x3C,0x7E,0x47,0x03,0x3F,0xFF,0xC3,0xC7,0xFF,0x7B,
+// 'b'
+0x62,0x00,0x09,0x0E,0x02,0x0B,
+0xC0,0x60,0x30,0x18,0x0D,0xE7,0xFB,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x7F,0xF6,0xF0,
+// 'c'
+0x63,0x04,0x08,0x0A,0x01,0x09,
+0x1E,0x7F,0x61,0xC0,0xC0,0xC0,0xC0,0x61,0x7F,0x1E,
+// 'd'
+0x64,0x00,0x09,0x0E,0x01,0x0B,
+0x01,0x80,0xC0,0x60,0x33,0xDB,0xFF,0x8F,0x83,0xC1,0xE0,0xF0,0x7C,0x77,0xF9,0xEC,
+// 'e'
+0x65,0x04,0x0A,0x0A,0x01,0x0B,
+0x1F,0x1F,0xE6,0x1F,0x03,0xFF,0xFF,0xFC,0x01,0x81,0x7F,0xC7,0xE0,
+// 'f'
+0x66,0x00,0x07,0x0E,0x00,0x06,
+0x1E,0x7C,0xC1,0x8F,0xFF,0xCC,0x18,0x30,0x60,0xC1,0x83,0x06,0x00,
+// 'g'
+0x67,0x04,0x09,0x0E,0x01,0x0B,
+0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x68,0x67,0xF1,0xF0,
+// 'h'
+0x68,0x00,0x08,0x0E,0x02,0x0B,
+0xC0,0xC0,0xC0,0xC0,0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,
+// 'i'
+0x69,0x00,0x02,0x0E,0x02,0x05,
+0xF0,0xFF,0xFF,0xF0,
+// 'j'
+0x6A,0x00,0x04,0x12,0x00,0x05,
+0x33,0x00,0x33,0x33,0x33,0x33,0x33,0x33,0xEC,
+// 'k'
+0x6B,0x00,0x09,0x0E,0x02,0x0A,
+0xC0,0x60,0x30,0x18,0x0C,0x36,0x33,0x31,0xB0,0xF0,0x78,0x36,0x19,0x8C,0x66,0x18,
+// 'l'
+0x6C,0x00,0x02,0x0E,0x02,0x05,
+0xFF,0xFF,0xFF,0xF0,
+// 'm'
+0x6D,0x04,0x0E,0x0A,0x02,0x11,
+0xDC,0x7B,0xFB,0xEE,0x79,0xF0,0xC3,0xC3,0x0F,0x0C,0x3C,0x30,0xF0,0xC3,0xC3,0x0F,0x0C,0x30,
+// 'n'
+0x6E,0x04,0x08,0x0A,0x02,0x0B,
+0xDE,0xFE,0xE7,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,
+// 'o'
+0x6F,0x04,0x0A,0x0A,0x01,0x0B,
+0x1E,0x1F,0xE6,0x1B,0x03,0xC0,0xF0,0x3C,0x0D,0x86,0x7F,0x87,0x80,
+// 'p'
+0x70,0x04,0x09,0x0E,0x02,0x0B,
+0xDE,0x7F,0xB8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0xFF,0x6F,0x30,0x18,0x0C,0x06,0x00,
+// 'q'
+0x71,0x04,0x09,0x0E,0x01,0x0B,
+0x3D,0xBF,0xF8,0xF8,0x3C,0x1E,0x0F,0x07,0xC7,0x7F,0x9E,0xC0,0x60,0x30,0x18,0x0C,
+// 'r'
+0x72,0x04,0x06,0x0A,0x02,0x08,
+0xDF,0xFE,0x30,0xC3,0x0C,0x30,0xC3,0x00,
+// 's'
+0x73,0x04,0x08,0x0A,0x01,0x08,
+0x7C,0xFE,0xC2,0xE0,0x7C,0x1E,0x06,0x86,0xFE,0x78,
+// 't'
+0x74,0x01,0x06,0x0D,0x01,0x07,
+0x61,0x86,0x3F,0xFD,0x86,0x18,0x61,0x86,0x1F,0x3C,
+// 'u'
+0x75,0x04,0x08,0x0A,0x02,0x0B,
+0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xC3,0xE7,0x7F,0x7B,
+// 'v'
+0x76,0x04,0x0C,0x0A,0x00,0x0B,
+0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x19,0x80,0xF0,0x0F,0x00,0x60,
+// 'w'
+0x77,0x04,0x0F,0x0A,0x01,0x10,
+0x63,0x8C,0xC7,0x19,0x8E,0x31,0xB6,0xC3,0x6D,0x86,0xDB,0x0F,0x1E,0x0E,0x38,0x1C,0x70,0x38,0xE0,
+// 'x'
+0x78,0x04,0x0A,0x0A,0x01,0x0B,
+0xE1,0xD8,0x63,0x30,0xCC,0x1E,0x07,0x83,0x30,0xCC,0x61,0xB8,0x70,
+// 'y'
+0x79,0x04,0x0C,0x0E,0x00,0x0B,
+0x60,0x66,0x06,0x30,0xC3,0x0C,0x19,0x81,0x98,0x0F,0x00,0xF0,0x06,0x00,0x60,0x06,0x00,0xC0,0x3C,0x03,0x80,
+// 'z'
+0x7A,0x04,0x08,0x0A,0x01,0x09,
+0xFF,0xFF,0x06,0x0C,0x1C,0x38,0x30,0x70,0xFF,0xFF,
+// '{'
+0x7B,0x00,0x08,0x11,0x02,0x0B,
+0x0F,0x1F,0x18,0x18,0x18,0x18,0x38,0xF0,0xF0,0x38,0x18,0x18,0x18,0x18,0x18,0x1F,0x0F,
+// '|'
+0x7C,0x00,0x02,0x12,0x02,0x06,
+0xFF,0xFF,0xFF,0xFF,0xF0,
+// '}'
+0x7D,0x00,0x08,0x11,0x02,0x0B,
+0xF0,0xF8,0x18,0x18,0x18,0x18,0x1C,0x0F,0x0F,0x1C,0x18,0x18,0x18,0x18,0x18,0xF8,0xF0,
+// '~'
+0x7E,0x05,0x0B,0x05,0x02,0x0F,
+0x00,0x0F,0x87,0xFF,0xC3,0xE0,0x00,
+
+// Terminator
+0xFF
+};
diff --git a/components/tft/DejaVuSans24.c b/components/tft/DejaVuSans24.c
new file mode 100644
index 0000000..aa7e2f1
--- /dev/null
+++ b/components/tft/DejaVuSans24.c
@@ -0,0 +1,331 @@
+// ========================================================================
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// Header Format:
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+// ========================================================================
+
+// dejavu
+// Point Size : 24
+// Memory usage : 2724 bytes
+// # characters : 95
+
+const unsigned char tft_Dejavu24[] =
+{
+0x00, 0x17, 0x00, 0x00,
+
+// ' '
+0x20,0x13,0x00,0x00,0x00,0x08,
+
+// '!'
+0x21,0x01,0x02,0x12,0x04,0x0A,
+0xFF,0xFF,0xFF,0x03,0xF0,
+// '"'
+0x22,0x01,0x06,0x07,0x02,0x0B,
+0xCF,0x3C,0xF3,0xCF,0x3C,0xC0,
+// '#'
+0x23,0x01,0x10,0x12,0x02,0x14,
+0x03,0x08,0x03,0x18,0x03,0x18,0x03,0x18,0x02,0x18,0x7F,0xFF,0x7F,0xFF,0x06,0x30,0x04,0x30,0x0C,0x20,0x0C,0x60,0xFF,0xFE,0xFF,0xFE,0x18,0x40,0x18,0xC0,0x18,0xC0,0x18,0xC0,0x10,0xC0,
+// '$'
+0x24,0x01,0x0B,0x16,0x02,0x0F,
+0x04,0x00,0x80,0x10,0x0F,0xC7,0xFD,0xC8,0xB1,0x06,0x20,0xE4,0x0F,0x80,0xFE,0x03,0xE0,0x4E,0x08,0xC1,0x1E,0x27,0xFF,0xC7,0xE0,0x10,0x02,0x00,0x40,0x08,0x00,
+// '%'
+0x25,0x01,0x14,0x12,0x01,0x17,
+0x3C,0x03,0x06,0x60,0x60,0xC3,0x06,0x0C,0x30,0xC0,0xC3,0x1C,0x0C,0x31,0x80,0xC3,0x38,0x0C,0x33,0x00,0x66,0x63,0xC3,0xC6,0x66,0x00,0xCC,0x30,0x1C,0xC3,0x01,0x8C,0x30,0x38,0xC3,0x03,0x0C,0x30,0x60,0xC3,0x06,0x06,0x60,0xC0,0x3C,
+// '&'
+0x26,0x01,0x10,0x12,0x01,0x13,
+0x07,0xC0,0x1F,0xE0,0x38,0x20,0x30,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x1C,0x00,0x3E,0x00,0x77,0x06,0xE3,0x86,0xC1,0xCC,0xC0,0xFC,0xC0,0x78,0xE0,0x78,0x70,0xFC,0x3F,0xCE,0x0F,0x87,
+// '''
+0x27,0x01,0x02,0x07,0x02,0x07,
+0xFF,0xFC,
+// '('
+0x28,0x01,0x05,0x15,0x02,0x09,
+0x19,0x8C,0xC6,0x31,0x18,0xC6,0x31,0x8C,0x61,0x0C,0x63,0x0C,0x61,0x80,
+// ')'
+0x29,0x01,0x05,0x15,0x02,0x09,
+0xC3,0x18,0x63,0x18,0x43,0x18,0xC6,0x31,0x8C,0x46,0x31,0x98,0xCC,0x00,
+// '*'
+0x2A,0x01,0x0B,0x0A,0x00,0x0C,
+0x04,0x00,0x83,0x11,0xBA,0xE1,0xF0,0x3E,0x1D,0x76,0x23,0x04,0x00,0x80,
+// '+'
+0x2B,0x03,0x10,0x10,0x03,0x14,
+0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0xFF,0xFF,0xFF,0xFF,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,0x01,0x80,
+// ','
+0x2C,0x10,0x03,0x06,0x02,0x08,
+0x6D,0xBD,0x80,
+// '-'
+0x2D,0x0B,0x06,0x02,0x01,0x09,
+0xFF,0xF0,
+// '.'
+0x2E,0x10,0x02,0x03,0x03,0x08,
+0xFC,
+// '/'
+0x2F,0x01,0x08,0x14,0x00,0x08,
+0x03,0x07,0x06,0x06,0x06,0x0C,0x0C,0x0C,0x18,0x18,0x18,0x18,0x30,0x30,0x30,0x60,0x60,0x60,0xE0,0xC0,
+// '0'
+0x30,0x01,0x0C,0x12,0x02,0x0F,
+0x0F,0x03,0xFC,0x70,0xE6,0x06,0x60,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x60,0x67,0x0E,0x3F,0xC0,0xF0,
+// '1'
+0x31,0x01,0x0A,0x12,0x03,0x0F,
+0x3C,0x3F,0x0C,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,
+// '2'
+0x32,0x01,0x0C,0x12,0x02,0x0F,
+0x3F,0x0F,0xF8,0xC1,0xC0,0x0E,0x00,0x60,0x06,0x00,0x60,0x0C,0x01,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xEF,0xFE,
+// '3'
+0x33,0x01,0x0C,0x12,0x02,0x0F,
+0x3F,0x07,0xFC,0x41,0xC0,0x06,0x00,0x60,0x06,0x00,0x60,0x0C,0x1F,0x81,0xFC,0x00,0xE0,0x07,0x00,0x30,0x03,0x00,0x78,0x0E,0xFF,0xC3,0xF0,
+// '4'
+0x34,0x01,0x0D,0x12,0x01,0x0F,
+0x01,0xC0,0x1E,0x00,0xB0,0x0D,0x80,0xCC,0x06,0x60,0x63,0x03,0x18,0x30,0xC3,0x06,0x18,0x31,0x81,0x8F,0xFF,0xFF,0xFC,0x03,0x00,0x18,0x00,0xC0,0x06,0x00,
+// '5'
+0x35,0x01,0x0B,0x12,0x02,0x0F,
+0x7F,0xCF,0xF9,0x80,0x30,0x06,0x00,0xC0,0x1F,0xC3,0xFC,0x41,0xC0,0x1C,0x01,0x80,0x30,0x06,0x00,0xC0,0x3C,0x0E,0xFF,0x8F,0xC0,
+// '6'
+0x36,0x01,0x0C,0x12,0x02,0x0F,
+0x07,0xC1,0xFE,0x38,0x27,0x00,0x60,0x0C,0x00,0xCF,0x8D,0xFC,0xF8,0xEF,0x07,0xE0,0x3E,0x03,0xE0,0x36,0x03,0x70,0x77,0x8E,0x3F,0xC0,0xF8,
+// '7'
+0x37,0x01,0x0B,0x12,0x02,0x0F,
+0xFF,0xFF,0xFC,0x03,0x00,0x60,0x1C,0x03,0x00,0x60,0x18,0x03,0x00,0xE0,0x18,0x03,0x00,0xC0,0x18,0x07,0x00,0xC0,0x18,0x06,0x00,
+// '8'
+0x38,0x01,0x0C,0x12,0x02,0x0F,
+0x1F,0x87,0xFE,0x70,0xEC,0x03,0xC0,0x3C,0x03,0xC0,0x37,0x0E,0x3F,0xC3,0xFC,0x70,0xEC,0x03,0xC0,0x3C,0x03,0xC0,0x37,0x0E,0x7F,0xE1,0xF8,
+// '9'
+0x39,0x01,0x0C,0x12,0x02,0x0F,
+0x1F,0x03,0xFC,0x71,0xCE,0x0E,0xC0,0x6C,0x07,0xC0,0x7C,0x07,0xE0,0xF7,0x1F,0x3F,0xB1,0xF3,0x00,0x30,0x06,0x00,0xE4,0x1C,0x7F,0x83,0xE0,
+// ':'
+0x3A,0x07,0x02,0x0C,0x03,0x08,
+0xFC,0x00,0x3F,
+// ';'
+0x3B,0x07,0x03,0x0F,0x02,0x08,
+0x6D,0x80,0x00,0x0D,0xB7,0xB0,
+// '<'
+0x3C,0x05,0x0F,0x0D,0x03,0x14,
+0x00,0x02,0x00,0x3C,0x03,0xF0,0x3F,0x01,0xF8,0x1F,0x80,0x3C,0x00,0x7E,0x00,0x1F,0x80,0x0F,0xC0,0x03,0xF0,0x00,0xF0,0x00,0x20,
+// '='
+0x3D,0x08,0x0F,0x07,0x03,0x14,
+0xFF,0xFF,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xFF,0x80,
+// '>'
+0x3E,0x05,0x0F,0x0D,0x03,0x14,
+0x80,0x01,0xE0,0x01,0xF8,0x00,0x7E,0x00,0x3F,0x00,0x0F,0xC0,0x07,0x80,0x3F,0x03,0xF0,0x1F,0x81,0xF8,0x07,0x80,0x08,0x00,0x00,
+// '?'
+0x3F,0x01,0x09,0x12,0x02,0x0D,
+0x3E,0x3F,0xB0,0xF0,0x30,0x18,0x0C,0x0C,0x0E,0x0E,0x0E,0x06,0x03,0x01,0x80,0x00,0x00,0x30,0x18,0x0C,0x00,
+// '@'
+0x40,0x02,0x15,0x15,0x02,0x18,
+0x00,0xFC,0x00,0x3F,0xF8,0x03,0xC0,0xF0,0x38,0x01,0xC3,0x80,0x07,0x38,0x79,0x99,0x8F,0xEC,0xFC,0x71,0xE3,0xC7,0x07,0x1E,0x30,0x18,0xF1,0x80,0xC7,0x8C,0x06,0x3C,0x70,0x73,0x71,0xC7,0xB9,0x8F,0xEF,0x8E,0x1E,0x70,0x38,0x00,0x00,0xE0,0x04,0x03,0xC0,0xE0,0x0F,0xFE,0x00,0x0F,0x80,0x00,
+// 'A'
+0x41,0x01,0x10,0x12,0x00,0x10,
+0x03,0xC0,0x03,0xC0,0x03,0xC0,0x07,0xE0,0x06,0x60,0x06,0x60,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x18,0x18,0x18,0x18,0x38,0x1C,0x3F,0xFC,0x3F,0xFC,0x60,0x06,0x60,0x06,0x60,0x06,0xC0,0x03,
+// 'B'
+0x42,0x01,0x0C,0x12,0x02,0x10,
+0xFF,0x0F,0xFC,0xC0,0xEC,0x06,0xC0,0x6C,0x06,0xC0,0x6C,0x0C,0xFF,0x8F,0xFC,0xC0,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x06,0xFF,0xEF,0xF8,
+// 'C'
+0x43,0x01,0x0E,0x12,0x01,0x11,
+0x07,0xE0,0x7F,0xE3,0xC1,0xDC,0x01,0x60,0x01,0x80,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0x60,0x01,0x80,0x07,0x00,0x4F,0x07,0x1F,0xF8,0x1F,0x80,
+// 'D'
+0x44,0x01,0x0F,0x12,0x02,0x12,
+0xFF,0x81,0xFF,0xE3,0x01,0xE6,0x00,0xEC,0x00,0xD8,0x01,0xF0,0x01,0xE0,0x03,0xC0,0x07,0x80,0x0F,0x00,0x1E,0x00,0x3C,0x00,0xF8,0x01,0xB0,0x07,0x60,0x3C,0xFF,0xF1,0xFF,0x00,
+// 'E'
+0x45,0x01,0x0B,0x12,0x02,0x0F,
+0xFF,0xFF,0xFF,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xDF,0xFB,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xFF,0xFC,
+// 'F'
+0x46,0x01,0x0A,0x12,0x02,0x0E,
+0xFF,0xFF,0xFC,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xFF,0xBF,0xEC,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x00,
+// 'G'
+0x47,0x01,0x0F,0x12,0x01,0x13,
+0x07,0xE0,0x3F,0xF0,0xE0,0x73,0x80,0x26,0x00,0x1C,0x00,0x30,0x00,0x60,0x00,0xC0,0x7F,0x80,0xFF,0x00,0x1E,0x00,0x36,0x00,0x6C,0x00,0xDC,0x01,0x9E,0x07,0x1F,0xFC,0x0F,0xE0,
+// 'H'
+0x48,0x01,0x0D,0x12,0x02,0x12,
+0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xFF,0xFF,0xFF,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xC0,
+// 'I'
+0x49,0x01,0x02,0x12,0x02,0x07,
+0xFF,0xFF,0xFF,0xFF,0xF0,
+// 'J'
+0x4A,0x01,0x06,0x17,0xFE,0x07,
+0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x1B,0xEF,0x00,
+// 'K'
+0x4B,0x01,0x0F,0x12,0x02,0x10,
+0xC0,0x71,0x81,0xC3,0x07,0x06,0x1C,0x0C,0x70,0x19,0xC0,0x37,0x00,0x7C,0x00,0xF8,0x01,0xB0,0x03,0x38,0x06,0x38,0x0C,0x38,0x18,0x38,0x30,0x38,0x60,0x38,0xC0,0x39,0x80,0x38,
+// 'L'
+0x4C,0x01,0x0B,0x12,0x02,0x0D,
+0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xFF,0xFC,
+// 'M'
+0x4D,0x01,0x10,0x12,0x02,0x15,
+0xE0,0x07,0xF0,0x0F,0xF0,0x0F,0xF8,0x1F,0xD8,0x1B,0xD8,0x1B,0xCC,0x33,0xCC,0x33,0xCC,0x33,0xC6,0x63,0xC6,0x63,0xC7,0xE3,0xC3,0xC3,0xC3,0xC3,0xC1,0x83,0xC0,0x03,0xC0,0x03,0xC0,0x03,
+// 'N'
+0x4E,0x01,0x0D,0x12,0x02,0x12,
+0xE0,0x1F,0x80,0xFC,0x07,0xF0,0x3D,0x81,0xE6,0x0F,0x30,0x78,0xC3,0xC6,0x1E,0x18,0xF0,0xC7,0x83,0x3C,0x19,0xE0,0x6F,0x03,0x78,0x0F,0xC0,0x7E,0x01,0xC0,
+// 'O'
+0x4F,0x01,0x10,0x12,0x01,0x13,
+0x07,0xE0,0x1F,0xF8,0x3C,0x3C,0x70,0x0E,0x60,0x06,0x60,0x06,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x60,0x06,0x60,0x06,0x70,0x0E,0x3C,0x3C,0x1F,0xF8,0x07,0xE0,
+// 'P'
+0x50,0x01,0x0B,0x12,0x02,0x0E,
+0xFF,0x1F,0xFB,0x07,0x60,0x3C,0x07,0x80,0xF0,0x1E,0x0E,0xFF,0xDF,0xE3,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x00,
+// 'Q'
+0x51,0x01,0x10,0x15,0x01,0x13,
+0x07,0xE0,0x1F,0xF8,0x3C,0x3C,0x70,0x0E,0x60,0x06,0x60,0x06,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0xC0,0x03,0x60,0x07,0x60,0x06,0x70,0x0E,0x3C,0x3C,0x1F,0xF8,0x07,0xF0,0x00,0x38,0x00,0x18,0x00,0x0C,
+// 'R'
+0x52,0x01,0x0D,0x12,0x02,0x11,
+0xFF,0x07,0xFE,0x30,0x31,0x80,0xCC,0x06,0x60,0x33,0x01,0x98,0x18,0xFF,0xC7,0xFC,0x30,0x71,0x81,0x8C,0x06,0x60,0x33,0x01,0xD8,0x06,0xC0,0x36,0x00,0xC0,
+// 'S'
+0x53,0x01,0x0C,0x12,0x02,0x0F,
+0x1F,0x87,0xFE,0x70,0x6C,0x00,0xC0,0x0C,0x00,0xC0,0x07,0x00,0x7F,0x01,0xFC,0x00,0xE0,0x07,0x00,0x30,0x03,0x00,0x3C,0x0E,0xFF,0xE3,0xF8,
+// 'T'
+0x54,0x01,0x0E,0x12,0x00,0x0F,
+0xFF,0xFF,0xFF,0xF0,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,
+// 'U'
+0x55,0x01,0x0D,0x12,0x02,0x12,
+0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x0D,0x80,0xCE,0x0E,0x3F,0xE0,0x7C,0x00,
+// 'V'
+0x56,0x01,0x10,0x12,0x00,0x10,
+0xC0,0x03,0x60,0x06,0x60,0x06,0x60,0x06,0x30,0x0C,0x30,0x0C,0x38,0x1C,0x18,0x18,0x18,0x18,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x06,0x60,0x06,0x60,0x07,0x60,0x03,0xC0,0x03,0xC0,0x03,0xC0,
+// 'W'
+0x57,0x01,0x16,0x12,0x01,0x18,
+0xC0,0x78,0x0F,0x01,0xE0,0x36,0x07,0x81,0x98,0x1E,0x06,0x60,0xEC,0x19,0x83,0x30,0x63,0x0C,0xC3,0x0C,0x33,0x0C,0x30,0xCE,0x30,0xC6,0x18,0xC1,0x98,0x66,0x06,0x61,0x98,0x19,0x86,0x60,0x6C,0x0D,0x80,0xF0,0x3C,0x03,0xC0,0xF0,0x0F,0x03,0xC0,0x38,0x07,0x00,
+// 'X'
+0x58,0x01,0x0F,0x12,0x01,0x11,
+0x70,0x0E,0x60,0x18,0x60,0x60,0xE1,0xC0,0xC7,0x00,0xCC,0x01,0xF0,0x01,0xE0,0x03,0x80,0x07,0x80,0x1F,0x00,0x37,0x00,0xC6,0x03,0x86,0x0E,0x0E,0x18,0x0C,0x60,0x0D,0xC0,0x1C,
+// 'Y'
+0x59,0x01,0x0E,0x12,0x00,0x0F,
+0xE0,0x1D,0x80,0x63,0x03,0x0E,0x1C,0x18,0x60,0x33,0x00,0xFC,0x01,0xE0,0x07,0x80,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,
+// 'Z'
+0x5A,0x01,0x0E,0x12,0x01,0x10,
+0xFF,0xFF,0xFF,0xF0,0x01,0x80,0x0E,0x00,0x70,0x01,0x80,0x0C,0x00,0x60,0x03,0x80,0x1C,0x00,0x60,0x03,0x00,0x18,0x00,0xE0,0x07,0x00,0x18,0x00,0xFF,0xFF,0xFF,0xF0,
+// '['
+0x5B,0x01,0x05,0x15,0x02,0x09,
+0xFF,0xF1,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0xFF,0x80,
+// '\'
+0x5C,0x01,0x08,0x14,0x00,0x08,
+0xC0,0xE0,0x60,0x60,0x60,0x30,0x30,0x30,0x18,0x18,0x18,0x18,0x0C,0x0C,0x0C,0x06,0x06,0x06,0x07,0x03,
+// ']'
+0x5D,0x01,0x05,0x15,0x02,0x09,
+0xFF,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC7,0xFF,0x80,
+// '^'
+0x5E,0x01,0x0F,0x07,0x03,0x14,
+0x03,0x80,0x0F,0x80,0x3B,0x80,0xE3,0x83,0x83,0x8E,0x03,0xB8,0x03,0x80,
+// '_'
+0x5F,0x17,0x0C,0x02,0x00,0x0C,
+0xFF,0xFF,0xFF,
+// '`'
+0x60,0x00,0x06,0x04,0x02,0x0C,
+0x60,0xC1,0x83,
+// 'a'
+0x61,0x06,0x0B,0x0D,0x01,0x0E,
+0x3F,0x0F,0xF9,0x03,0x00,0x30,0x06,0x3F,0xDF,0xFF,0x03,0xC0,0x78,0x1F,0x87,0xBF,0xF3,0xE6,
+// 'b'
+0x62,0x01,0x0C,0x12,0x02,0x0F,
+0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0xF8,0xFF,0xCF,0x0E,0xE0,0x6C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xE0,0x6F,0x0E,0xFF,0xCC,0xF8,
+// 'c'
+0x63,0x06,0x0A,0x0D,0x01,0x0D,
+0x0F,0x8F,0xF7,0x05,0x80,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x18,0x07,0x04,0xFF,0x0F,0x80,
+// 'd'
+0x64,0x01,0x0C,0x12,0x01,0x0F,
+0x00,0x30,0x03,0x00,0x30,0x03,0x00,0x31,0xF3,0x3F,0xF7,0x0F,0x60,0x7C,0x03,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x77,0x0F,0x3F,0xF1,0xF3,
+// 'e'
+0x65,0x06,0x0C,0x0D,0x01,0x0E,
+0x0F,0x83,0xFC,0x70,0xE6,0x07,0xC0,0x3F,0xFF,0xFF,0xFC,0x00,0xC0,0x06,0x00,0x70,0x23,0xFE,0x0F,0xC0,
+// 'f'
+0x66,0x01,0x08,0x12,0x01,0x08,
+0x0F,0x1F,0x38,0x30,0x30,0xFF,0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,
+// 'g'
+0x67,0x06,0x0C,0x12,0x01,0x0F,
+0x1F,0x33,0xFF,0x70,0xF6,0x07,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x07,0x70,0xF3,0xFF,0x1F,0x30,0x03,0x00,0x72,0x0E,0x3F,0xC1,0xF8,
+// 'h'
+0x68,0x01,0x0B,0x12,0x02,0x0F,
+0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x9F,0x3F,0xF7,0x87,0xE0,0x78,0x0F,0x01,0xE0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x0C,
+// 'i'
+0x69,0x01,0x02,0x12,0x02,0x07,
+0xFC,0x3F,0xFF,0xFF,0xF0,
+// 'j'
+0x6A,0x01,0x05,0x17,0xFF,0x07,
+0x18,0xC6,0x00,0x0C,0x63,0x18,0xC6,0x31,0x8C,0x63,0x18,0xC6,0x33,0xFB,0x80,
+// 'k'
+0x6B,0x01,0x0C,0x12,0x02,0x0E,
+0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x1C,0xC3,0x8C,0x70,0xCE,0x0D,0xC0,0xF8,0x0F,0x80,0xDC,0x0C,0xE0,0xC7,0x0C,0x38,0xC1,0xCC,0x0E,
+// 'l'
+0x6C,0x01,0x02,0x12,0x02,0x06,
+0xFF,0xFF,0xFF,0xFF,0xF0,
+// 'm'
+0x6D,0x06,0x14,0x0D,0x02,0x18,
+0xCF,0x87,0xCF,0xFC,0xFE,0xF0,0xF8,0x7E,0x07,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x3C,0x06,0x03,0xC0,0x60,0x30,
+// 'n'
+0x6E,0x06,0x0B,0x0D,0x02,0x0F,
+0xCF,0x9F,0xFB,0xC3,0xF0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x0F,0x01,0xE0,0x3C,0x06,
+// 'o'
+0x6F,0x06,0x0C,0x0D,0x01,0x0E,
+0x1F,0x83,0xFC,0x70,0xE6,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x06,0x70,0xE3,0xFC,0x1F,0x80,
+// 'p'
+0x70,0x06,0x0C,0x12,0x02,0x0F,
+0xCF,0x8F,0xFC,0xF0,0xEE,0x06,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x3E,0x06,0xF0,0xEF,0xFC,0xCF,0x8C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,
+// 'q'
+0x71,0x06,0x0C,0x12,0x01,0x0F,
+0x1F,0x33,0xFF,0x70,0xF6,0x07,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0xC0,0x36,0x07,0x70,0xF3,0xFF,0x1F,0x30,0x03,0x00,0x30,0x03,0x00,0x30,0x03,
+// 'r'
+0x72,0x06,0x08,0x0D,0x02,0x0A,
+0xCF,0xFF,0xF0,0xE0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,0xC0,
+// 's'
+0x73,0x06,0x0B,0x0D,0x01,0x0C,
+0x3F,0x0F,0xF3,0x82,0x60,0x0C,0x00,0xF0,0x0F,0xC0,0x3C,0x00,0xC0,0x1A,0x07,0x7F,0xC7,0xF0,
+// 't'
+0x74,0x02,0x08,0x11,0x00,0x09,
+0x30,0x30,0x30,0x30,0xFF,0xFF,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x1F,0x0F,
+// 'u'
+0x75,0x06,0x0B,0x0D,0x02,0x0F,
+0xC0,0x78,0x0F,0x01,0xE0,0x3C,0x07,0x80,0xF0,0x1E,0x03,0xC0,0x78,0x1F,0x87,0xBF,0xF3,0xE6,
+// 'v'
+0x76,0x06,0x0D,0x0D,0x01,0x0F,
+0xC0,0x1B,0x01,0x98,0x0C,0xC0,0x63,0x06,0x18,0x30,0x63,0x03,0x18,0x18,0xC0,0x6C,0x03,0x60,0x1F,0x00,0x70,0x00,
+// 'w'
+0x77,0x06,0x12,0x0D,0x01,0x14,
+0xC1,0xE0,0xF0,0x78,0x36,0x1E,0x19,0x87,0x86,0x63,0x31,0x9C,0xCC,0xE3,0x33,0x30,0xCC,0xCC,0x36,0x1B,0x07,0x87,0x81,0xE1,0xE0,0x78,0x78,0x1C,0x0E,0x00,
+// 'x'
+0x78,0x06,0x0D,0x0D,0x01,0x0F,
+0xE0,0x3B,0x83,0x8E,0x38,0x31,0x80,0xD8,0x07,0xC0,0x1C,0x01,0xF0,0x1D,0xC0,0xC6,0x0C,0x18,0xE0,0xEE,0x03,0x80,
+// 'y'
+0x79,0x06,0x0D,0x12,0x01,0x0F,
+0xC0,0x1B,0x01,0x98,0x0C,0xE0,0xE3,0x06,0x18,0x70,0x63,0x03,0x18,0x0D,0x80,0x6C,0x03,0xE0,0x0E,0x00,0x70,0x03,0x00,0x18,0x01,0x80,0x7C,0x03,0xC0,0x00,
+// 'z'
+0x7A,0x06,0x0B,0x0D,0x01,0x0D,
+0xFF,0xFF,0xFC,0x03,0x00,0xE0,0x38,0x0E,0x03,0x80,0xE0,0x38,0x0E,0x01,0x80,0x7F,0xFF,0xFE,
+// '{'
+0x7B,0x01,0x09,0x16,0x03,0x0F,
+0x03,0x83,0xC3,0x81,0x80,0xC0,0x60,0x30,0x18,0x0C,0x0E,0x3E,0x1F,0x01,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0xC0,0x78,0x1C,
+// '|'
+0x7C,0x01,0x02,0x18,0x03,0x08,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+// '}'
+0x7D,0x01,0x09,0x16,0x03,0x0F,
+0xE0,0x78,0x0E,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0E,0x03,0xE1,0xF1,0xC0,0xC0,0x60,0x30,0x18,0x0C,0x06,0x07,0x0F,0x07,0x00,
+// '~'
+0x7E,0x09,0x0F,0x05,0x03,0x14,
+0x00,0x00,0x7C,0x05,0xFE,0x1E,0x1F,0xE0,0x0F,0x80,
+
+// Terminator
+0xFF
+};
diff --git a/components/tft/Kconfig b/components/tft/Kconfig
new file mode 100644
index 0000000..cdea9bd
--- /dev/null
+++ b/components/tft/Kconfig
@@ -0,0 +1,157 @@
+menu "TFT Display"
+
+config TFT_PREDEFINED_DISPLAY_TYPE
+ int
+ default 0 if TFT_PREDEFINED_DISPLAY_TYPE0
+ default 1 if TFT_PREDEFINED_DISPLAY_TYPE1
+ default 2 if TFT_PREDEFINED_DISPLAY_TYPE2
+ default 3 if TFT_PREDEFINED_DISPLAY_TYPE3
+ default 4 if TFT_PREDEFINED_DISPLAY_TYPE4
+ default 5 if TFT_PREDEFINED_DISPLAY_TYPE5
+
+ choice
+ prompt "Select predefined display configuration"
+ default TFT_PREDEFINED_DISPLAY_TYPE0
+ help
+ Select predefined display configuration
+
+ config TFT_PREDEFINED_DISPLAY_TYPE0
+ bool "None"
+ config TFT_PREDEFINED_DISPLAY_TYPE1
+ bool "ESP-WROVER-KIT v3 Display (ST7789V)"
+ config TFT_PREDEFINED_DISPLAY_TYPE4
+ bool "ESP-WROVER-KIT v4.1 Display (ILI9341)"
+ config TFT_PREDEFINED_DISPLAY_TYPE2
+ bool "Adafruit TFT Feather Display"
+ config TFT_PREDEFINED_DISPLAY_TYPE3
+ bool "M5Stack TFT Display"
+ config TFT_PREDEFINED_DISPLAY_TYPE5
+ bool "TTGO T-DISPLAY (ST7789V)"
+ endchoice
+
+if TFT_PREDEFINED_DISPLAY_TYPE0
+
+config TFT_DISPLAY_CONTROLLER_MODEL
+ int
+ default 0 if TFT_DISPLAY_CONTROLLER_ILI9341
+ default 1 if TFT_DISPLAY_CONTROLLER_ILI9488
+ default 2 if TFT_DISPLAY_CONTROLLER_ST7789V
+ default 3 if TFT_DISPLAY_CONTROLLER_ST7735
+ default 4 if TFT_DISPLAY_CONTROLLER_ST7735R
+ default 5 if TFT_DISPLAY_CONTROLLER_ST7735B
+
+ choice
+ prompt "Select a display controller model."
+ default TFT_DISPLAY_CONTROLLER_ILI9341
+ help
+ Select the controller for your display. If an TFT_PREDEFINED_DISPLAY_TYPE is set, this will be overridden.
+
+ config TFT_DISPLAY_CONTROLLER_ILI9341
+ bool "ILI9341"
+ config TFT_DISPLAY_CONTROLLER_ILI9488
+ bool "ILI9488"
+ config TFT_DISPLAY_CONTROLLER_ST7789V
+ bool "ST7789V"
+ config TFT_DISPLAY_CONTROLLER_ST7735
+ bool "ST7735"
+ config TFT_DISPLAY_CONTROLLER_ST7735R
+ bool "ST7735R"
+ config TFT_DISPLAY_CONTROLLER_ST7735B
+ bool "ST7735B"
+ endchoice
+
+config TFT_DISPLAY_WIDTH
+ int "TFT display width in pixels."
+ default 240
+ help
+ The smaller dimension (in portrait).
+
+config TFT_DISPLAY_HEIGHT
+ int "TFT display height in pixels."
+ default 320
+ help
+ The smaller dimension (in portrait).
+
+config TFT_RGB_BGR
+ bool "RGB (Red Green Blue)"
+ default n
+ help
+ Is the display RGB rather than GBR?
+
+config TFT_TOUCH_CONTROLLER
+ int
+ default 0 if TFT_TOUCH_CONTROLLER0
+ default 1 if TFT_TOUCH_CONTROLLER1
+ default 2 if TFT_TOUCH_CONTROLLER2
+
+ choice
+ prompt "Select touch controller."
+ default TFT_TOUCH_CONTROLLER0
+ help
+ Select predefined display configuration
+
+ config TFT_TOUCH_CONTROLLER0
+ bool "None"
+ config TFT_TOUCH_CONTROLLER1
+ bool "XPT2046"
+ config TFT_TOUCH_CONTROLLER2
+ bool "STMPE610"
+ endchoice
+
+config TFT_INVERT_ROTATION1
+ bool "Invert rotation1."
+ default n
+ help
+ If text is backwards on your display, try enabling this.
+
+config TFT_PIN_NUM_MOSI
+ int "GPIO for MOSI (Master Out Slave In)"
+ default 23
+ help
+ If not using a predefined display type, configure the MOSI pin here.
+
+config TFT_PIN_NUM_MISO
+ int "GPIO for MISO (Master In Slave Out)"
+ default 19
+ help
+ If not using a predefined display type, configure the MISO pin here.
+
+config TFT_PIN_NUM_CLK
+ int "GPIO for CLK (SCK / Serial Clock)"
+ default 18
+ help
+ If not using a predefined display type, configure the SCK pin here.
+
+config TFT_PIN_NUM_CS
+ int "GPIO for CS (Slave Select)"
+ default 5
+ help
+ If not using a predefined display type, configure the CS pin here.
+
+config TFT_PIN_NUM_DC
+ int "GPIO for DC (Data \ Command)"
+ default 26
+ help
+ If not using a predefined display type, configure the DC pin here.
+
+config TFT_PIN_NUM_TCS
+ int "GPIO for TCS (Touchscreen)"
+ default 25
+ help
+ Optional. If not using a predefined display type, configure the TCS (touch screen) pin here.
+
+config TFT_PIN_NUM_RST
+ int "GPIO for Reset"
+ default 0
+ help
+ Optional. If not using a predefined display type, configure the reset pin here.
+
+config TFT_PIN_NUM_BCKL
+ int "GPIO for Back-light control"
+ default 0
+ help
+ Optional. If not using a predefined display type, configure the blacklight pin here.
+
+endif
+
+endmenu
diff --git a/components/tft/SmallFont.c b/components/tft/SmallFont.c
new file mode 100644
index 0000000..663f59c
--- /dev/null
+++ b/components/tft/SmallFont.c
@@ -0,0 +1,120 @@
+// SmallFont.c
+// Font type : Full (95 characters)
+// Font size : 8x12 pixels
+// Memory usage : 1144 bytes
+
+#if defined(__AVR__)
+ #include
+ #define fontdatatype const uint8_t
+#elif defined(__PIC32MX__)
+ #define PROGMEM
+ #define fontdatatype const unsigned char
+#elif defined(__arm__)
+ #define PROGMEM
+ #define fontdatatype const unsigned char
+#endif
+
+const unsigned char tft_SmallFont[1144] =
+{
+0x08,0x0C,0x20,0x5F,
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, //
+0x00,0x00,0x20,0x20,0x20,0x20,0x20,0x20,0x00,0x20,0x00,0x00, // !
+0x00,0x28,0x50,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // "
+0x00,0x00,0x28,0x28,0xFC,0x28,0x50,0xFC,0x50,0x50,0x00,0x00, // #
+0x00,0x20,0x78,0xA8,0xA0,0x60,0x30,0x28,0xA8,0xF0,0x20,0x00, // $
+0x00,0x00,0x48,0xA8,0xB0,0x50,0x28,0x34,0x54,0x48,0x00,0x00, // %
+0x00,0x00,0x20,0x50,0x50,0x78,0xA8,0xA8,0x90,0x6C,0x00,0x00, // &
+0x00,0x40,0x40,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // '
+0x00,0x04,0x08,0x10,0x10,0x10,0x10,0x10,0x10,0x08,0x04,0x00, // (
+0x00,0x40,0x20,0x10,0x10,0x10,0x10,0x10,0x10,0x20,0x40,0x00, // )
+0x00,0x00,0x00,0x20,0xA8,0x70,0x70,0xA8,0x20,0x00,0x00,0x00, // *
+0x00,0x00,0x20,0x20,0x20,0xF8,0x20,0x20,0x20,0x00,0x00,0x00, // +
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x40,0x80, // ,
+0x00,0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00, // -
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00, // .
+0x00,0x08,0x10,0x10,0x10,0x20,0x20,0x40,0x40,0x40,0x80,0x00, // /
+
+0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // 0
+0x00,0x00,0x20,0x60,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // 1
+0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x40,0x80,0xF8,0x00,0x00, // 2
+0x00,0x00,0x70,0x88,0x08,0x30,0x08,0x08,0x88,0x70,0x00,0x00, // 3
+0x00,0x00,0x10,0x30,0x50,0x50,0x90,0x78,0x10,0x18,0x00,0x00, // 4
+0x00,0x00,0xF8,0x80,0x80,0xF0,0x08,0x08,0x88,0x70,0x00,0x00, // 5
+0x00,0x00,0x70,0x90,0x80,0xF0,0x88,0x88,0x88,0x70,0x00,0x00, // 6
+0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x20,0x20,0x20,0x00,0x00, // 7
+0x00,0x00,0x70,0x88,0x88,0x70,0x88,0x88,0x88,0x70,0x00,0x00, // 8
+0x00,0x00,0x70,0x88,0x88,0x88,0x78,0x08,0x48,0x70,0x00,0x00, // 9
+0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x20,0x00,0x00, // :
+0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x20,0x20,0x00, // ;
+0x00,0x04,0x08,0x10,0x20,0x40,0x20,0x10,0x08,0x04,0x00,0x00, // <
+0x00,0x00,0x00,0x00,0xF8,0x00,0x00,0xF8,0x00,0x00,0x00,0x00, // =
+0x00,0x40,0x20,0x10,0x08,0x04,0x08,0x10,0x20,0x40,0x00,0x00, // >
+0x00,0x00,0x70,0x88,0x88,0x10,0x20,0x20,0x00,0x20,0x00,0x00, // ?
+
+0x00,0x00,0x70,0x88,0x98,0xA8,0xA8,0xB8,0x80,0x78,0x00,0x00, // @
+0x00,0x00,0x20,0x20,0x30,0x50,0x50,0x78,0x48,0xCC,0x00,0x00, // A
+0x00,0x00,0xF0,0x48,0x48,0x70,0x48,0x48,0x48,0xF0,0x00,0x00, // B
+0x00,0x00,0x78,0x88,0x80,0x80,0x80,0x80,0x88,0x70,0x00,0x00, // C
+0x00,0x00,0xF0,0x48,0x48,0x48,0x48,0x48,0x48,0xF0,0x00,0x00, // D
+0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x48,0xF8,0x00,0x00, // E
+0x00,0x00,0xF8,0x48,0x50,0x70,0x50,0x40,0x40,0xE0,0x00,0x00, // F
+0x00,0x00,0x38,0x48,0x80,0x80,0x9C,0x88,0x48,0x30,0x00,0x00, // G
+0x00,0x00,0xCC,0x48,0x48,0x78,0x48,0x48,0x48,0xCC,0x00,0x00, // H
+0x00,0x00,0xF8,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // I
+0x00,0x00,0x7C,0x10,0x10,0x10,0x10,0x10,0x10,0x90,0xE0,0x00, // J
+0x00,0x00,0xEC,0x48,0x50,0x60,0x50,0x50,0x48,0xEC,0x00,0x00, // K
+0x00,0x00,0xE0,0x40,0x40,0x40,0x40,0x40,0x44,0xFC,0x00,0x00, // L
+0x00,0x00,0xD8,0xD8,0xD8,0xD8,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // M
+0x00,0x00,0xDC,0x48,0x68,0x68,0x58,0x58,0x48,0xE8,0x00,0x00, // N
+0x00,0x00,0x70,0x88,0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00, // O
+
+0x00,0x00,0xF0,0x48,0x48,0x70,0x40,0x40,0x40,0xE0,0x00,0x00, // P
+0x00,0x00,0x70,0x88,0x88,0x88,0x88,0xE8,0x98,0x70,0x18,0x00, // Q
+0x00,0x00,0xF0,0x48,0x48,0x70,0x50,0x48,0x48,0xEC,0x00,0x00, // R
+0x00,0x00,0x78,0x88,0x80,0x60,0x10,0x08,0x88,0xF0,0x00,0x00, // S
+0x00,0x00,0xF8,0xA8,0x20,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // T
+0x00,0x00,0xCC,0x48,0x48,0x48,0x48,0x48,0x48,0x30,0x00,0x00, // U
+0x00,0x00,0xCC,0x48,0x48,0x50,0x50,0x30,0x20,0x20,0x00,0x00, // V
+0x00,0x00,0xA8,0xA8,0xA8,0x70,0x50,0x50,0x50,0x50,0x00,0x00, // W
+0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x50,0x50,0xD8,0x00,0x00, // X
+0x00,0x00,0xD8,0x50,0x50,0x20,0x20,0x20,0x20,0x70,0x00,0x00, // Y
+0x00,0x00,0xF8,0x90,0x10,0x20,0x20,0x40,0x48,0xF8,0x00,0x00, // Z
+0x00,0x38,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x38,0x00, // [
+0x00,0x40,0x40,0x40,0x20,0x20,0x10,0x10,0x10,0x08,0x00,0x00, //
+0x00,0x70,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x70,0x00, // ]
+0x00,0x20,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ^
+0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFC, // _
+
+0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // `
+0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x38,0x48,0x3C,0x00,0x00, // a
+0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0x70,0x00,0x00, // b
+0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x40,0x40,0x38,0x00,0x00, // c
+0x00,0x00,0x18,0x08,0x08,0x38,0x48,0x48,0x48,0x3C,0x00,0x00, // d
+0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x78,0x40,0x38,0x00,0x00, // e
+0x00,0x00,0x1C,0x20,0x20,0x78,0x20,0x20,0x20,0x78,0x00,0x00, // f
+0x00,0x00,0x00,0x00,0x00,0x3C,0x48,0x30,0x40,0x78,0x44,0x38, // g
+0x00,0x00,0xC0,0x40,0x40,0x70,0x48,0x48,0x48,0xEC,0x00,0x00, // h
+0x00,0x00,0x20,0x00,0x00,0x60,0x20,0x20,0x20,0x70,0x00,0x00, // i
+0x00,0x00,0x10,0x00,0x00,0x30,0x10,0x10,0x10,0x10,0x10,0xE0, // j
+0x00,0x00,0xC0,0x40,0x40,0x5C,0x50,0x70,0x48,0xEC,0x00,0x00, // k
+0x00,0x00,0xE0,0x20,0x20,0x20,0x20,0x20,0x20,0xF8,0x00,0x00, // l
+0x00,0x00,0x00,0x00,0x00,0xF0,0xA8,0xA8,0xA8,0xA8,0x00,0x00, // m
+0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0xEC,0x00,0x00, // n
+0x00,0x00,0x00,0x00,0x00,0x30,0x48,0x48,0x48,0x30,0x00,0x00, // o
+
+0x00,0x00,0x00,0x00,0x00,0xF0,0x48,0x48,0x48,0x70,0x40,0xE0, // p
+0x00,0x00,0x00,0x00,0x00,0x38,0x48,0x48,0x48,0x38,0x08,0x1C, // q
+0x00,0x00,0x00,0x00,0x00,0xD8,0x60,0x40,0x40,0xE0,0x00,0x00, // r
+0x00,0x00,0x00,0x00,0x00,0x78,0x40,0x30,0x08,0x78,0x00,0x00, // s
+0x00,0x00,0x00,0x20,0x20,0x70,0x20,0x20,0x20,0x18,0x00,0x00, // t
+0x00,0x00,0x00,0x00,0x00,0xD8,0x48,0x48,0x48,0x3C,0x00,0x00, // u
+0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x00,0x00, // v
+0x00,0x00,0x00,0x00,0x00,0xA8,0xA8,0x70,0x50,0x50,0x00,0x00, // w
+0x00,0x00,0x00,0x00,0x00,0xD8,0x50,0x20,0x50,0xD8,0x00,0x00, // x
+0x00,0x00,0x00,0x00,0x00,0xEC,0x48,0x50,0x30,0x20,0x20,0xC0, // y
+0x00,0x00,0x00,0x00,0x00,0x78,0x10,0x20,0x20,0x78,0x00,0x00, // z
+0x00,0x18,0x10,0x10,0x10,0x20,0x10,0x10,0x10,0x10,0x18,0x00, // {
+0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, // |
+0x00,0x60,0x20,0x20,0x20,0x10,0x20,0x20,0x20,0x20,0x60,0x00, // }
+0x40,0xA4,0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // ~
+};
diff --git a/components/tft/Ubuntu16.c b/components/tft/Ubuntu16.c
new file mode 100644
index 0000000..4f75855
--- /dev/null
+++ b/components/tft/Ubuntu16.c
@@ -0,0 +1,331 @@
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// on Arduinos. It can however handle any ttf font that has been converted
+// using the conversion program. These could be fixed width or proportional
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// Ubuntu16.c
+// Point Size : 16
+// Memory usage : 1433 bytes
+// # characters : 95
+
+// Header Format (to make Arduino UTFT Compatible):
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+const unsigned char tft_Ubuntu16[] =
+{
+0x00, 0x10, 0x00, 0x00,
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+
+// ' '
+0x20,0x0D,0x00,0x00,0x00,0x04,
+
+// '!'
+0x21,0x02,0x01,0x0B,0x01,0x04,
+0xFC,0x60,
+// '"'
+0x22,0x00,0x04,0x04,0x01,0x07,
+0x99,0x99,
+// '#'
+0x23,0x02,0x09,0x0B,0x01,0x0B,
+0x11,0x08,0x84,0x5F,0xF2,0x21,0x10,0x89,0xFF,0x44,0x22,0x11,0x00,
+// '$'
+0x24,0x00,0x07,0x10,0x01,0x09,
+0x10,0x20,0xF6,0x08,0x10,0x18,0x08,0x0C,0x0C,0x08,0x3F,0xC2,0x04,0x00,
+// '%'
+0x25,0x02,0x0C,0x0B,0x01,0x0E,
+0x70,0x4D,0x88,0x89,0x08,0x90,0xDA,0x07,0x4E,0x05,0xB0,0x91,0x09,0x11,0x1B,0x20,0xE0,
+// '&'
+0x26,0x02,0x0A,0x0B,0x01,0x0B,
+0x3C,0x18,0x84,0x21,0x08,0x2C,0x0C,0x04,0x8A,0x10,0x83,0x30,0xC7,0xC8,
+// '''
+0x27,0x00,0x01,0x04,0x01,0x04,
+0xF0,
+// '('
+0x28,0x00,0x04,0x10,0x01,0x05,
+0x02,0x44,0x48,0x88,0x88,0x84,0x44,0x20,
+// ')'
+0x29,0x00,0x04,0x10,0x00,0x05,
+0x04,0x22,0x21,0x11,0x11,0x12,0x22,0x40,
+// '*'
+0x2A,0x02,0x09,0x06,0x00,0x08,
+0x08,0x24,0x8F,0x83,0x81,0x41,0x10,
+// '+'
+0x2B,0x05,0x07,0x07,0x01,0x09,
+0x10,0x20,0x47,0xF1,0x02,0x04,0x00,
+// ','
+0x2C,0x0B,0x02,0x05,0x00,0x04,
+0x54,0x80,
+// '-'
+0x2D,0x08,0x04,0x01,0x01,0x06,
+0xF0,
+// '.'
+0x2E,0x0B,0x01,0x02,0x01,0x04,
+0xC0,
+// '/'
+0x2F,0x00,0x07,0x10,0x00,0x06,
+0x02,0x08,0x10,0x20,0x81,0x02,0x08,0x10,0x40,0x81,0x04,0x08,0x10,0x40,
+// '0'
+0x30,0x02,0x07,0x0B,0x01,0x09,
+0x38,0x8B,0x1C,0x18,0x30,0x60,0xC1,0x86,0x88,0xE0,
+// '1'
+0x31,0x02,0x04,0x0B,0x01,0x09,
+0x13,0x59,0x11,0x11,0x11,0x10,
+// '2'
+0x32,0x02,0x06,0x0B,0x01,0x09,
+0x7A,0x30,0x41,0x08,0x21,0x08,0x42,0x0F,0xC0,
+// '3'
+0x33,0x02,0x07,0x0B,0x01,0x09,
+0x78,0x08,0x08,0x10,0x47,0x01,0x01,0x02,0x0B,0xE0,
+// '4'
+0x34,0x02,0x07,0x0B,0x01,0x09,
+0x04,0x18,0x51,0x22,0x48,0xA1,0x7F,0x04,0x08,0x10,
+// '5'
+0x35,0x02,0x07,0x0B,0x01,0x09,
+0x7E,0x81,0x02,0x07,0x81,0x80,0x81,0x02,0x0B,0xE0,
+// '6'
+0x36,0x02,0x07,0x0B,0x01,0x09,
+0x1C,0x61,0x00,0x0F,0x90,0xA0,0xC1,0x82,0x88,0xE0,
+// '7'
+0x37,0x02,0x07,0x0B,0x01,0x09,
+0xFE,0x04,0x10,0x40,0x82,0x04,0x08,0x20,0x40,0x80,
+// '8'
+0x38,0x02,0x07,0x0B,0x01,0x09,
+0x39,0x8A,0x0C,0x14,0x47,0x11,0x41,0x83,0x89,0xE0,
+// '9'
+0x39,0x02,0x07,0x0B,0x01,0x09,
+0x38,0x8A,0x0C,0x18,0x28,0x4F,0x81,0x04,0x11,0xC0,
+// ':'
+0x3A,0x05,0x01,0x08,0x01,0x04,
+0xC3,
+// ';'
+0x3B,0x05,0x02,0x0B,0x00,0x04,
+0x50,0x05,0x48,
+// '<'
+0x3C,0x05,0x08,0x07,0x01,0x09,
+0x02,0x0C,0x30,0x60,0x30,0x0C,0x02,
+// '='
+0x3D,0x06,0x07,0x04,0x01,0x09,
+0xFE,0x00,0x07,0xF0,
+// '>'
+0x3E,0x05,0x09,0x07,0x00,0x09,
+0x40,0x1C,0x01,0x80,0x70,0x61,0xC1,0x00,
+// '?'
+0x3F,0x02,0x06,0x0B,0x01,0x07,
+0x78,0x30,0x41,0x18,0xC2,0x00,0x00,0x82,0x00,
+// '@'
+0x40,0x02,0x0D,0x0D,0x01,0x0F,
+0x0F,0x81,0x83,0x10,0x0C,0x8F,0xA8,0x84,0xC8,0x26,0x41,0x32,0x09,0x88,0x5A,0x3F,0x90,0x00,0x60,0x00,0xFC,0x00,
+// 'A'
+0x41,0x02,0x0B,0x0B,0x00,0x0B,
+0x04,0x01,0xC0,0x28,0x08,0x81,0x10,0x61,0x08,0x21,0xFC,0x60,0x48,0x0B,0x00,0x80,
+// 'B'
+0x42,0x02,0x08,0x0B,0x01,0x0A,
+0xF8,0x86,0x82,0x82,0x86,0xFC,0x82,0x81,0x81,0x82,0xFC,
+// 'C'
+0x43,0x02,0x09,0x0B,0x01,0x0B,
+0x1F,0x10,0x10,0x10,0x08,0x04,0x02,0x01,0x00,0x40,0x30,0x07,0xC0,
+// 'D'
+0x44,0x02,0x09,0x0B,0x01,0x0B,
+0xFC,0x41,0x20,0x50,0x18,0x0C,0x06,0x03,0x01,0x81,0x41,0x3F,0x00,
+// 'E'
+0x45,0x02,0x07,0x0B,0x01,0x09,
+0xFF,0x02,0x04,0x08,0x1F,0xA0,0x40,0x81,0x03,0xF8,
+// 'F'
+0x46,0x02,0x07,0x0B,0x01,0x09,
+0xFF,0x02,0x04,0x08,0x1F,0xA0,0x40,0x81,0x02,0x00,
+// 'G'
+0x47,0x02,0x09,0x0B,0x01,0x0B,
+0x1F,0x10,0x10,0x10,0x08,0x04,0x02,0x03,0x01,0x40,0xB0,0x47,0xE0,
+// 'H'
+0x48,0x02,0x09,0x0B,0x01,0x0B,
+0x80,0xC0,0x60,0x30,0x18,0x0F,0xFE,0x03,0x01,0x80,0xC0,0x60,0x20,
+// 'I'
+0x49,0x02,0x01,0x0B,0x01,0x03,
+0xFF,0xE0,
+// 'J'
+0x4A,0x02,0x07,0x0B,0x00,0x08,
+0x02,0x04,0x08,0x10,0x20,0x40,0x81,0x02,0x09,0xE0,
+// 'K'
+0x4B,0x02,0x09,0x0B,0x01,0x0A,
+0x81,0x41,0x23,0x12,0x0A,0x06,0x02,0xC1,0x10,0x86,0x40,0xA0,0x20,
+// 'L'
+0x4C,0x02,0x07,0x0B,0x01,0x08,
+0x81,0x02,0x04,0x08,0x10,0x20,0x40,0x81,0x03,0xF8,
+// 'M'
+0x4D,0x02,0x0B,0x0B,0x01,0x0D,
+0x40,0x4C,0x19,0x01,0x28,0xA5,0x14,0x94,0xB2,0x9C,0x33,0x84,0x30,0x06,0x00,0x80,
+// 'N'
+0x4E,0x02,0x09,0x0B,0x01,0x0B,
+0x80,0xE0,0x68,0x32,0x19,0x0C,0x46,0x13,0x05,0x82,0xC0,0xE0,0x20,
+// 'O'
+0x4F,0x02,0x0B,0x0B,0x01,0x0D,
+0x1F,0x04,0x11,0x01,0x40,0x18,0x03,0x00,0x60,0x0C,0x01,0x40,0x44,0x10,0x7C,0x00,
+// 'P'
+0x50,0x02,0x08,0x0B,0x01,0x0A,
+0xFC,0x82,0x81,0x81,0x81,0x82,0xFC,0x80,0x80,0x80,0x80,
+// 'Q'
+0x51,0x02,0x0B,0x0E,0x01,0x0D,
+0x1F,0x04,0x11,0x01,0x40,0x18,0x03,0x00,0x60,0x0C,0x01,0x40,0x44,0x10,0x78,0x02,0x00,0x30,0x01,0x80,
+// 'R'
+0x52,0x02,0x09,0x0B,0x01,0x0A,
+0xFC,0x41,0x20,0x50,0x28,0x14,0x13,0xF1,0x08,0x82,0x40,0xA0,0x20,
+// 'S'
+0x53,0x02,0x08,0x0B,0x01,0x09,
+0x3C,0xC2,0x80,0x80,0x40,0x1C,0x06,0x02,0x02,0x06,0x78,
+// 'T'
+0x54,0x02,0x09,0x0B,0x00,0x09,
+0xFF,0x84,0x02,0x01,0x00,0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x00,
+// 'U'
+0x55,0x02,0x09,0x0B,0x01,0x0B,
+0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x80,0xA0,0x8F,0x80,
+// 'V'
+0x56,0x02,0x09,0x0B,0x00,0x09,
+0x80,0xE0,0xD0,0x48,0x26,0x21,0x10,0x88,0x28,0x14,0x0E,0x02,0x00,
+// 'W'
+0x57,0x02,0x0D,0x0B,0x00,0x0D,
+0x80,0x0E,0x10,0xD0,0x84,0x8E,0x24,0x51,0x22,0x88,0xA2,0x85,0x14,0x38,0xE0,0xC2,0x04,0x10,
+// 'X'
+0x58,0x02,0x09,0x0B,0x00,0x09,
+0xC1,0xA0,0x88,0x86,0xC1,0x40,0x60,0x70,0x6C,0x22,0x20,0xB0,0x60,
+// 'Y'
+0x59,0x02,0x09,0x0B,0x00,0x09,
+0x80,0xA0,0x90,0x44,0x41,0x40,0xA0,0x20,0x10,0x08,0x04,0x02,0x00,
+// 'Z'
+0x5A,0x02,0x07,0x0B,0x01,0x09,
+0xFE,0x04,0x10,0x41,0x02,0x08,0x00,0x41,0x03,0xF8,
+// '['
+0x5B,0x00,0x03,0x10,0x02,0x05,
+0xF2,0x49,0x24,0x92,0x49,0x27,
+// '\'
+0x5C,0x00,0x07,0x10,0x00,0x06,
+0x80,0x81,0x02,0x02,0x04,0x08,0x08,0x10,0x10,0x20,0x40,0x40,0x81,0x01,
+// ']'
+0x5D,0x00,0x03,0x10,0x00,0x05,
+0xE4,0x92,0x49,0x24,0x92,0x4F,
+// '^'
+0x5E,0x02,0x07,0x06,0x01,0x09,
+0x10,0x70,0xA2,0x24,0x50,0x40,
+// '_'
+0x5F,0x0F,0x08,0x01,0x00,0x08,
+0xFF,
+// '`'
+0x60,0x01,0x04,0x03,0x01,0x06,
+0x86,0x10,
+// 'a'
+0x61,0x05,0x06,0x08,0x01,0x08,
+0x78,0x30,0x5F,0xC6,0x18,0x5F,
+// 'b'
+0x62,0x01,0x07,0x0C,0x01,0x09,
+0x81,0x02,0x04,0x0F,0x90,0xA0,0xC1,0x83,0x06,0x17,0xC0,
+// 'c'
+0x63,0x05,0x06,0x08,0x01,0x08,
+0x3D,0x08,0x20,0x82,0x04,0x0F,
+// 'd'
+0x64,0x01,0x07,0x0C,0x01,0x09,
+0x02,0x04,0x08,0x13,0xE8,0x60,0xC1,0x83,0x05,0x09,0xF0,
+// 'e'
+0x65,0x05,0x07,0x08,0x01,0x09,
+0x3C,0x8A,0x0F,0xF8,0x10,0x10,0x1E,
+// 'f'
+0x66,0x01,0x05,0x0C,0x01,0x06,
+0x7E,0x21,0x0F,0xC2,0x10,0x84,0x21,0x00,
+// 'g'
+0x67,0x05,0x07,0x0B,0x01,0x09,
+0x3E,0x86,0x0C,0x18,0x30,0x50,0x9F,0x02,0x0B,0xE0,
+// 'h'
+0x68,0x01,0x07,0x0C,0x01,0x09,
+0x81,0x02,0x04,0x0F,0x90,0xE0,0xC1,0x83,0x06,0x0C,0x10,
+// 'i'
+0x69,0x01,0x03,0x0C,0x00,0x03,
+0x48,0x04,0x92,0x49,0x20,
+// 'j'
+0x6A,0x01,0x04,0x0F,0xFF,0x03,
+0x22,0x00,0x22,0x22,0x22,0x22,0x22,0xC0,
+// 'k'
+0x6B,0x01,0x06,0x0C,0x01,0x08,
+0x82,0x08,0x20,0x8A,0x4A,0x30,0xA2,0x48,0xA1,
+// 'l'
+0x6C,0x01,0x04,0x0C,0x01,0x04,
+0x88,0x88,0x88,0x88,0x88,0x86,
+// 'm'
+0x6D,0x05,0x0B,0x08,0x01,0x0D,
+0xFB,0xD1,0x8E,0x10,0xC2,0x18,0x43,0x08,0x61,0x0C,0x21,
+// 'n'
+0x6E,0x05,0x07,0x08,0x01,0x09,
+0xFD,0x0E,0x0C,0x18,0x30,0x60,0xC1,
+// 'o'
+0x6F,0x05,0x08,0x08,0x01,0x0A,
+0x3C,0x42,0x81,0x81,0x81,0x81,0x42,0x3C,
+// 'p'
+0x70,0x05,0x07,0x0B,0x01,0x09,
+0xF9,0x0A,0x0C,0x18,0x30,0x61,0x7C,0x81,0x02,0x00,
+// 'q'
+0x71,0x05,0x07,0x0B,0x01,0x09,
+0x3E,0x86,0x0C,0x18,0x30,0x50,0x9F,0x02,0x04,0x08,
+// 'r'
+0x72,0x05,0x05,0x08,0x01,0x06,
+0xFC,0x21,0x08,0x42,0x10,
+// 's'
+0x73,0x05,0x05,0x08,0x01,0x07,
+0x7C,0x20,0xC3,0x04,0x3E,
+// 't'
+0x74,0x02,0x05,0x0B,0x01,0x07,
+0x84,0x21,0xF8,0x42,0x10,0x84,0x1E,
+// 'u'
+0x75,0x05,0x07,0x08,0x01,0x09,
+0x83,0x06,0x0C,0x18,0x30,0x50,0xBF,
+// 'v'
+0x76,0x05,0x07,0x08,0x00,0x07,
+0x83,0x05,0x12,0x22,0x85,0x0E,0x08,
+// 'w'
+0x77,0x05,0x0D,0x08,0x00,0x0D,
+0x82,0x0C,0x10,0x51,0xC4,0x8A,0x26,0x5B,0x14,0x50,0xE3,0x82,0x08,
+// 'x'
+0x78,0x05,0x08,0x08,0x00,0x08,
+0xC3,0x66,0x24,0x18,0x18,0x24,0x42,0xC3,
+// 'y'
+0x79,0x05,0x07,0x0B,0x00,0x07,
+0x82,0x89,0x12,0x22,0x85,0x04,0x08,0x10,0x43,0x00,
+// 'z'
+0x7A,0x05,0x06,0x08,0x01,0x08,
+0xFC,0x10,0x84,0x21,0x08,0x3F,
+// '{'
+0x7B,0x00,0x05,0x10,0x00,0x05,
+0x19,0x08,0x42,0x10,0x98,0x61,0x08,0x42,0x10,0x83,
+// '|'
+0x7C,0x00,0x01,0x10,0x02,0x05,
+0xFF,0xFF,
+// '}'
+0x7D,0x00,0x05,0x10,0x00,0x05,
+0xC1,0x08,0x42,0x10,0x83,0x31,0x08,0x42,0x10,0x98,
+// '~'
+0x7E,0x07,0x07,0x02,0x01,0x09,
+0x73,0x18,
+
+// Terminator
+0xFF
+};
diff --git a/components/tft/comic24.c b/components/tft/comic24.c
new file mode 100644
index 0000000..2e534c9
--- /dev/null
+++ b/components/tft/comic24.c
@@ -0,0 +1,331 @@
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// on Arduinos. It can however handle any ttf font that has been converted
+// using the conversion program. These could be fixed width or proportional
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// comic.c
+// Point Size : 24
+// Memory usage : 2814 bytes
+// # characters : 95
+
+// Header Format (to make Arduino UTFT Compatible):
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+unsigned char tft_Comic24[] =
+{
+0x00, 0x19, 0x00, 0x00,
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+
+// ' '
+0x20,0x15,0x00,0x00,0x00,0x07,
+
+// '!'
+0x21,0x02,0x02,0x14,0x01,0x06,
+0xFF,0xFF,0xFF,0xFC,0x2D,
+// '"'
+0x22,0x03,0x06,0x08,0x02,0x0A,
+0xCF,0x3C,0xF3,0xCF,0x3C,0xF3,
+// '#'
+0x23,0x03,0x14,0x12,0x01,0x14,
+0x01,0x81,0x80,0x18,0x18,0x01,0x81,0x80,0x30,0x30,0x03,0x03,0x07,0xFF,0xFF,0x7F,0xFF,0xF0,0x60,0x60,0x06,0x06,0x00,0xC0,0xC0,0x0C,0x0C,0x0F,0xFF,0xFE,0xFF,0xFF,0xE1,0x81,0x80,0x18,0x18,0x03,0x83,0x00,0x30,0x30,0x03,0x03,0x00,
+// '$'
+0x24,0x00,0x0B,0x19,0x02,0x11,
+0x0C,0x01,0x80,0x30,0x0F,0x83,0xFC,0xD9,0xBB,0x06,0x60,0xCC,0x19,0x83,0xB0,0x3F,0x83,0xFC,0x1B,0x83,0x18,0x63,0x0C,0x71,0x9F,0x37,0x7F,0xC3,0xF0,0x18,0x03,0x00,0x60,0x0C,0x00,
+// '%'
+0x25,0x01,0x11,0x14,0x02,0x14,
+0x00,0x00,0x00,0x0C,0x0E,0x0E,0x0F,0x86,0x0C,0x67,0x06,0x33,0x03,0x19,0x80,0xF9,0x80,0x38,0xC0,0x00,0xE0,0x00,0x60,0x00,0x70,0x00,0x31,0xE0,0x39,0xF8,0x19,0xCE,0x1C,0xC3,0x0C,0x61,0x86,0x39,0xC6,0x0F,0xC3,0x03,0xC0,
+// '&'
+0x26,0x03,0x0F,0x13,0x01,0x10,
+0x01,0xC0,0x07,0xC0,0x19,0x80,0x33,0x00,0x6E,0x00,0xF8,0x01,0xE0,0x07,0x80,0x1F,0x8C,0x73,0x19,0xC3,0x37,0x07,0xEC,0x07,0xD8,0x07,0x30,0x0E,0x38,0x7E,0x3F,0xEC,0x3F,0x0C,0x00,0x18,
+// '''
+0x27,0x03,0x02,0x06,0x03,0x09,
+0xFF,0xF0,
+// '('
+0x28,0x02,0x07,0x18,0x01,0x09,
+0x06,0x1C,0x71,0xC3,0x0E,0x18,0x30,0xE1,0x83,0x06,0x0C,0x18,0x30,0x60,0xE0,0xC1,0x83,0x83,0x83,0x87,0x83,
+// ')'
+0x29,0x02,0x06,0x18,0x02,0x09,
+0xC3,0x86,0x0C,0x30,0x61,0x86,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0x61,0x86,0x31,0xCE,0x30,
+// '*'
+0x2A,0x03,0x0B,0x09,0x01,0x0D,
+0x0C,0x01,0x83,0xBF,0xFF,0xF3,0xFC,0x3C,0x0F,0xC3,0x9C,0x61,0x80,
+// '+'
+0x2B,0x09,0x0A,0x0A,0x00,0x0C,
+0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,0xC0,0x30,0x0C,0x03,0x00,
+// ','
+0x2C,0x13,0x04,0x06,0x02,0x07,
+0x37,0x66,0xEC,
+// '-'
+0x2D,0x0E,0x08,0x02,0x01,0x0A,
+0xFF,0xFF,
+// '.'
+0x2E,0x12,0x03,0x03,0x02,0x06,
+0xFF,0x80,
+// '/'
+0x2F,0x01,0x0A,0x15,0x01,0x0C,
+0x00,0x00,0x30,0x0C,0x06,0x01,0x80,0x60,0x30,0x0C,0x06,0x01,0x80,0xC0,0x30,0x18,0x06,0x03,0x00,0xC0,0x60,0x18,0x0E,0x03,0x00,0xC0,0x00,
+// '0'
+0x30,0x03,0x0D,0x12,0x01,0x0F,
+0x0F,0x80,0xFF,0x0E,0x18,0xE0,0x66,0x03,0x70,0x0F,0x00,0x78,0x03,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x03,0xB0,0x19,0x81,0xC7,0x1C,0x3F,0xC0,0x7C,0x00,
+// '1'
+0x31,0x03,0x06,0x12,0x03,0x0B,
+0x10,0xC7,0x3C,0xB0,0xC3,0x0C,0x30,0xC3,0x0C,0x30,0xC3,0x0C,0xFF,0xF0,
+// '2'
+0x32,0x03,0x0B,0x12,0x02,0x0F,
+0x1F,0x07,0xFB,0xC3,0xE0,0x30,0x06,0x00,0xC0,0x38,0x0E,0x07,0x81,0xE0,0xF8,0x3C,0x07,0x01,0xC0,0x30,0x06,0x00,0xFF,0xDF,0xFC,
+// '3'
+0x33,0x03,0x0B,0x12,0x02,0x0F,
+0x1F,0x0F,0xF9,0xC3,0x80,0x30,0x06,0x00,0xC0,0x78,0x7E,0x0F,0x80,0x78,0x03,0x80,0x30,0x06,0x00,0xF0,0x1F,0x0E,0x7F,0x83,0xE0,
+// '4'
+0x34,0x03,0x0D,0x12,0x02,0x0F,
+0x01,0xC0,0x0E,0x00,0xF0,0x0F,0x80,0x6C,0x07,0x60,0x33,0x03,0x98,0x38,0xC1,0x86,0x1C,0x31,0xFF,0xFF,0xFF,0x80,0x60,0x03,0x00,0x18,0x00,0xC0,0x06,0x00,
+// '5'
+0x35,0x02,0x0C,0x13,0x02,0x0F,
+0x00,0x0F,0xFE,0xFF,0xE6,0x00,0x60,0x0E,0x00,0xEF,0x8F,0xFC,0xF8,0x6E,0x07,0xC0,0x30,0x03,0x00,0x30,0x03,0x00,0x7C,0x06,0xE1,0xE7,0xFC,0x3F,0x00,
+// '6'
+0x36,0x03,0x0C,0x12,0x01,0x0F,
+0x03,0x00,0x70,0x0E,0x01,0xC0,0x38,0x03,0x00,0x60,0x06,0xF8,0xFF,0xEE,0x0E,0xC0,0x3C,0x03,0xC0,0x3C,0x03,0x60,0x77,0x0E,0x3F,0xC1,0xF8,
+// '7'
+0x37,0x02,0x0D,0x13,0x01,0x0F,
+0x00,0x07,0xFF,0xFF,0xFE,0x00,0xE0,0x0E,0x00,0x60,0x06,0x00,0x30,0x03,0x80,0x18,0x01,0xC0,0x0C,0x00,0x60,0x07,0x00,0x30,0x03,0x80,0x18,0x00,0xC0,0x04,0x00,
+// '8'
+0x38,0x02,0x0C,0x13,0x01,0x0F,
+0x00,0x00,0xFC,0x3F,0xE3,0x07,0x60,0x36,0x03,0x60,0x37,0x8F,0x3F,0xE1,0xFE,0x38,0xE7,0x07,0x60,0x36,0x03,0x60,0x36,0x03,0x30,0x63,0xFE,0x0F,0x80,
+// '9'
+0x39,0x03,0x0D,0x13,0x01,0x0F,
+0x0F,0x01,0xFE,0x1C,0x38,0xC0,0xCC,0x07,0x60,0x1B,0x00,0xD8,0x06,0xE0,0x73,0x87,0x8F,0xF8,0x3E,0xC0,0x0E,0x00,0x60,0x07,0x00,0xF0,0x1F,0x03,0xE0,0x1C,0x00,
+// ':'
+0x3A,0x09,0x03,0x0B,0x02,0x07,
+0xFF,0x80,0x00,0xFF,0x80,
+// ';'
+0x3B,0x09,0x04,0x0E,0x02,0x07,
+0xEE,0xE0,0x00,0x00,0x03,0x7E,0xCC,
+// '<'
+0x3C,0x09,0x07,0x0A,0x01,0x09,
+0x06,0x1C,0x71,0xC7,0x1E,0x1E,0x0E,0x0E,0x0C,
+// '='
+0x3D,0x0A,0x09,0x09,0x01,0x0C,
+0xFF,0xFF,0xC0,0x00,0x00,0x00,0x03,0xFF,0xFF,0x00,0x00,
+// '>'
+0x3E,0x08,0x08,0x0B,0x01,0x0A,
+0x60,0x70,0x38,0x3C,0x1E,0x0F,0x06,0x0C,0x38,0x70,0xC0,
+// '?'
+0x3F,0x04,0x0B,0x12,0x01,0x0D,
+0x1E,0x0F,0xE3,0xC6,0x60,0x60,0x06,0x00,0xC0,0x18,0x07,0x01,0xE0,0xF8,0x3E,0x0F,0x01,0x80,0x00,0x00,0x01,0x80,0x30,0x06,0x00,
+// '@'
+0x40,0x02,0x13,0x14,0x01,0x16,
+0x03,0xF8,0x01,0xFF,0xC0,0x78,0x3C,0x1C,0x01,0xC3,0x00,0x1C,0xC1,0xC1,0x98,0xF8,0x1E,0x3C,0x03,0xC6,0x30,0x79,0x8E,0x0F,0x31,0xC1,0xE6,0x78,0x6C,0x7F,0xFC,0xC7,0x3E,0x18,0x00,0x01,0x80,0x00,0x38,0x00,0x03,0xC0,0xE0,0x1F,0xFC,0x00,0xFE,0x00,
+// 'A'
+0x41,0x03,0x0E,0x12,0x01,0x11,
+0x00,0x80,0x07,0x00,0x1C,0x00,0xF0,0x03,0xC0,0x1D,0x80,0x76,0x03,0x98,0x0E,0x20,0x70,0xC1,0xFF,0x0F,0xFC,0x7C,0x19,0xC0,0x67,0x01,0xB8,0x07,0xE0,0x0F,0x00,0x30,
+// 'B'
+0x42,0x03,0x0B,0x13,0x03,0x0F,
+0x7C,0x1F,0xE3,0x0E,0x60,0xEC,0x0D,0x81,0xB0,0x36,0x0E,0xC3,0x9F,0xE3,0xFC,0x61,0xEC,0x0F,0x80,0xF0,0x1E,0x0E,0xC7,0xDF,0xE3,0xF0,0x00,
+// 'C'
+0x43,0x03,0x0D,0x12,0x01,0x0E,
+0x01,0xF8,0x3F,0xC3,0xC6,0x38,0x31,0x80,0x1C,0x01,0xC0,0x0C,0x00,0x60,0x06,0x00,0x30,0x01,0x80,0x0C,0x00,0x60,0x19,0x81,0xCE,0x3C,0x3F,0xC0,0xF8,0x00,
+// 'D'
+0x44,0x03,0x0D,0x12,0x02,0x11,
+0x60,0x07,0xC0,0x37,0x81,0x8F,0x0C,0x1C,0x60,0x73,0x01,0xD8,0x06,0xC0,0x1E,0x00,0xF0,0x07,0x80,0x3C,0x01,0xE0,0x1B,0x01,0xDC,0x1C,0xFF,0xC1,0xF8,0x00,
+// 'E'
+0x45,0x03,0x0D,0x12,0x02,0x0F,
+0xFF,0xF7,0xFF,0xF0,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x7E,0xFF,0xF7,0xE0,0x30,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x00,0x7F,0xF1,0xFF,0x80,
+// 'F'
+0x46,0x03,0x0C,0x12,0x02,0x0F,
+0xFF,0xCF,0xFF,0xC0,0x7C,0x00,0xC0,0x0C,0x00,0xC0,0x0D,0xFE,0xFF,0xEF,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,0xC0,0x0C,0x00,
+// 'G'
+0x47,0x03,0x0F,0x12,0x01,0x10,
+0x03,0xE0,0x0F,0xF0,0x38,0xE0,0xE0,0x03,0x80,0x06,0x00,0x18,0x00,0x30,0x00,0x61,0xFF,0x9F,0xFF,0x3C,0x36,0x00,0x6C,0x01,0x98,0x07,0x30,0x0C,0x30,0x70,0x7F,0xC0,0x3E,0x00,
+// 'H'
+0x48,0x03,0x0F,0x12,0x02,0x12,
+0xC0,0x03,0x80,0x0F,0x00,0x1E,0x00,0x3C,0x00,0x78,0x00,0xF0,0x01,0xE0,0x03,0xC0,0xFF,0xFF,0xFF,0xFC,0x1E,0x00,0x3C,0x00,0x78,0x00,0xF0,0x01,0xE0,0x03,0xC0,0x07,0x80,0x0C,
+// 'I'
+0x49,0x03,0x0C,0x12,0x00,0x0D,
+0xFF,0xEF,0xFF,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0xFF,0xFF,0xFF,
+// 'J'
+0x4A,0x03,0x0E,0x12,0x01,0x10,
+0x1F,0xFC,0x7F,0xF0,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0x00,0xC0,0x03,0x00,0x0C,0x00,0x30,0xC0,0xC3,0x06,0x0E,0x18,0x1C,0x60,0x3F,0x80,0x3C,0x00,
+// 'K'
+0x4B,0x03,0x0C,0x12,0x03,0x0F,
+0xC0,0x6C,0x0E,0xC1,0xCC,0x38,0xC7,0x0C,0xE0,0xDC,0x0F,0x80,0xF0,0x0F,0x00,0xF8,0x0F,0xC0,0xDE,0x0C,0xF0,0xC7,0x8C,0x1E,0xC0,0xFC,0x07,
+// 'L'
+0x4C,0x03,0x0B,0x12,0x01,0x0D,
+0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xFF,0xEF,0xFC,
+// 'M'
+0x4D,0x03,0x13,0x13,0x01,0x15,
+0x0C,0x06,0x01,0x80,0xC0,0x78,0x3C,0x0F,0x07,0x81,0xE0,0xF0,0x3C,0x1E,0x07,0x83,0xC1,0xD8,0xEC,0x3B,0x1D,0x87,0x63,0xB0,0xCC,0xE6,0x38,0xDC,0x47,0x1B,0x8C,0xE3,0xF1,0xB8,0x3C,0x37,0x07,0x86,0xE0,0xF0,0x7C,0x1E,0x0F,0x01,0x81,0x80,
+// 'N'
+0x4E,0x03,0x11,0x12,0x01,0x13,
+0x60,0x01,0x38,0x00,0xDE,0x00,0x6F,0x00,0x37,0xC0,0x1B,0x70,0x0D,0x9C,0x06,0xCF,0x03,0x63,0x81,0xB0,0xE0,0xD8,0x38,0x6C,0x0E,0x36,0x03,0x9B,0x00,0xED,0x80,0x3E,0xC0,0x0F,0x60,0x03,0xB0,0x00,0xC0,
+// 'O'
+0x4F,0x03,0x11,0x12,0x01,0x13,
+0x01,0xF8,0x03,0xFF,0x07,0x81,0xC3,0x00,0x63,0x00,0x1B,0x80,0x0D,0x80,0x07,0xC0,0x03,0xC0,0x01,0xE0,0x00,0xF0,0x00,0xF8,0x00,0x6C,0x00,0x33,0x00,0x31,0xC0,0x38,0x70,0x78,0x1F,0xF8,0x03,0xF0,0x00,
+// 'P'
+0x50,0x03,0x0B,0x12,0x01,0x0D,
+0xFE,0x1F,0xF3,0x0F,0x60,0x7C,0x07,0x80,0xF0,0x1E,0x06,0xC3,0xDF,0xF3,0xF8,0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x00,
+// 'Q'
+0x51,0x03,0x14,0x17,0x01,0x15,
+0x01,0xF8,0x00,0x7F,0xE0,0x1E,0x07,0x03,0x80,0x18,0x30,0x01,0xC6,0x00,0x0C,0x60,0x00,0xEC,0x00,0x06,0xC0,0x00,0x6C,0x00,0x06,0xC0,0x00,0x6C,0x00,0x06,0x60,0xE0,0xE7,0x0F,0x0C,0x38,0x79,0xC1,0xC3,0xF8,0x0F,0xFF,0x00,0x3F,0x78,0x00,0x03,0xC0,0x00,0x1E,0x00,0x00,0xF0,0x00,0x07,0x00,0x00,0x20,
+// 'R'
+0x52,0x02,0x0D,0x13,0x01,0x0F,
+0x00,0x03,0xE0,0x3F,0xC1,0x8F,0x0C,0x0E,0x60,0x33,0x00,0xD8,0x06,0xC0,0x36,0x03,0xB0,0x79,0xFF,0x8F,0xF0,0x7F,0x83,0x1F,0x18,0x3C,0xC0,0xF6,0x01,0xF0,0x06,
+// 'S'
+0x53,0x03,0x0F,0x13,0x01,0x11,
+0x01,0xF0,0x07,0xF8,0x18,0x70,0x60,0x01,0x80,0x03,0x00,0x06,0x00,0x0E,0x00,0x0F,0xF0,0x07,0xF0,0x00,0xF0,0x00,0x70,0x00,0x60,0x00,0xD8,0x01,0xB8,0x06,0x78,0x3C,0x7F,0xE0,0x3F,0x00,
+// 'T'
+0x54,0x02,0x0F,0x13,0x01,0x10,
+0x00,0x01,0xFF,0xFD,0xFF,0xF8,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,
+// 'U'
+0x55,0x03,0x11,0x12,0x01,0x12,
+0x60,0x03,0x30,0x01,0x98,0x00,0xCC,0x00,0x66,0x00,0x33,0x00,0x19,0x80,0x0C,0xC0,0x06,0x60,0x03,0x30,0x01,0x98,0x01,0xCC,0x00,0xC7,0x00,0x61,0x80,0x70,0xE0,0x30,0x38,0x38,0x0F,0xF8,0x01,0xF0,0x00,
+// 'V'
+0x56,0x03,0x0E,0x13,0x02,0x10,
+0x80,0x0F,0x00,0x3C,0x01,0xB0,0x06,0x60,0x31,0x80,0xC6,0x03,0x0C,0x18,0x30,0x60,0xC1,0x81,0x8C,0x06,0x30,0x0D,0x80,0x36,0x00,0xF8,0x01,0xC0,0x07,0x00,0x08,0x00,0x00,0x00,
+// 'W'
+0x57,0x03,0x17,0x12,0x01,0x19,
+0xC0,0x20,0x0F,0xC0,0x60,0x19,0x81,0xC0,0x23,0x03,0x80,0xC6,0x07,0x01,0x86,0x1E,0x03,0x0C,0x36,0x0C,0x18,0x6C,0x18,0x11,0x98,0x60,0x33,0x30,0xC0,0x66,0x61,0x80,0xD8,0x66,0x01,0xB0,0xCC,0x01,0xC1,0xB0,0x03,0x83,0x60,0x07,0x07,0x80,0x0C,0x07,0x00,0x08,0x0E,0x00,
+// 'X'
+0x58,0x03,0x10,0x12,0x01,0x11,
+0x60,0x03,0x70,0x07,0x38,0x0E,0x1C,0x1C,0x0C,0x1C,0x0E,0x38,0x07,0x70,0x03,0xE0,0x01,0xC0,0x03,0xC0,0x07,0xE0,0x07,0x70,0x0E,0x38,0x1C,0x18,0x38,0x1C,0x70,0x0E,0xE0,0x07,0xC0,0x03,
+// 'Y'
+0x59,0x03,0x0F,0x13,0x00,0x10,
+0x60,0x06,0xE0,0x1D,0xC0,0x31,0xC0,0xE1,0xC1,0x83,0x83,0x03,0x8C,0x07,0x18,0x07,0x70,0x0F,0xC0,0x0F,0x80,0x0F,0x00,0x1C,0x00,0x38,0x00,0x60,0x01,0xC0,0x03,0x00,0x06,0x00,0x08,0x00,
+// 'Z'
+0x5A,0x03,0x0F,0x12,0x01,0x11,
+0xFF,0xFF,0xFF,0xFC,0x00,0xF0,0x03,0x80,0x0E,0x00,0x3C,0x00,0xF0,0x03,0xC0,0x07,0x00,0x1E,0x00,0x38,0x00,0xE0,0x03,0xC0,0x07,0x00,0x1C,0x00,0x70,0x00,0xFF,0xFF,0xFF,0xFC,
+// '['
+0x5B,0x01,0x07,0x1A,0x01,0x09,
+0x00,0xFD,0xFB,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x3F,0x7E,0x00,
+// '\'
+0x5C,0x03,0x0B,0x14,0x02,0x0D,
+0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x60,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x20,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x60,
+// ']'
+0x5D,0x01,0x07,0x1A,0x02,0x09,
+0x01,0xFB,0xF0,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x06,0x0C,0x18,0x30,0x60,0xC1,0x83,0x7E,0xFC,0x00,
+// '^'
+0x5E,0x02,0x0A,0x06,0x02,0x0E,
+0x0C,0x07,0x83,0xF1,0xCE,0xE1,0xF0,0x30,
+// '_'
+0x5F,0x16,0x0F,0x04,0x00,0x0F,
+0x00,0x01,0xFF,0xFF,0xFF,0xF8,0x00,0x00,
+// '`'
+0x60,0x02,0x05,0x06,0x02,0x0D,
+0xC7,0x1C,0x63,0x8C,
+// 'a'
+0x61,0x09,0x0B,0x0C,0x01,0x0C,
+0x0F,0x87,0xF9,0xE3,0x30,0x6E,0x0D,0x81,0xB0,0x36,0x06,0xC0,0xCC,0x39,0xFF,0x9F,0x30,
+// 'b'
+0x62,0x02,0x0C,0x13,0x01,0x0E,
+0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x78,0x7F,0xC7,0x8E,0x60,0x76,0x03,0x60,0x36,0x03,0x60,0x36,0x06,0x70,0xE7,0xFC,0x7F,0x00,
+// 'c'
+0x63,0x09,0x0A,0x0C,0x01,0x0C,
+0x0F,0x07,0xF3,0x0D,0x80,0x60,0x30,0x0C,0x03,0x00,0xC0,0x1C,0x33,0xFC,0x7C,
+// 'd'
+0x64,0x02,0x0C,0x13,0x01,0x0E,
+0x00,0x20,0x06,0x00,0x60,0x06,0x00,0x60,0x06,0x00,0x61,0xF6,0x3F,0xE7,0x0E,0x60,0x6C,0x06,0xC0,0x6C,0x06,0xC0,0x6E,0x06,0x70,0xE3,0xFE,0x1F,0x60,
+// 'e'
+0x65,0x09,0x0B,0x0C,0x01,0x0D,
+0x1F,0x07,0xF9,0xC7,0x30,0xEC,0x79,0xBE,0x3E,0x07,0x00,0xC0,0x6E,0x1D,0xFF,0x0F,0x80,
+// 'f'
+0x66,0x02,0x0A,0x14,0x01,0x0C,
+0x03,0x83,0xE0,0xE0,0x70,0x18,0x06,0x01,0x83,0xFF,0xFF,0xC6,0x01,0x80,0x60,0x18,0x06,0x01,0x80,0x60,0x18,0x06,0x01,0x80,0x60,
+// 'g'
+0x67,0x09,0x0A,0x13,0x02,0x0D,
+0x0F,0x0F,0xF7,0x0D,0x83,0xC0,0xF0,0x3C,0x1F,0x07,0xC1,0xD8,0xF7,0xEC,0xF3,0x00,0xC0,0x30,0x18,0x06,0x03,0xBF,0xC7,0xE0,
+// 'h'
+0x68,0x02,0x0B,0x13,0x01,0x0E,
+0x60,0x0C,0x01,0x80,0x30,0x06,0x00,0xC0,0x18,0x03,0x1E,0x6F,0xEF,0x8D,0xE1,0xB8,0x36,0x06,0xC0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0x80,
+// 'i'
+0x69,0x04,0x02,0x11,0x03,0x07,
+0xF0,0x3F,0xFF,0xFF,0xC0,
+// 'j'
+0x6A,0x04,0x08,0x18,0x00,0x0A,
+0x03,0x03,0x00,0x00,0x00,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0x03,0xC3,0xE3,0x77,0x7E,0x1C,
+// 'k'
+0x6B,0x03,0x0B,0x13,0x02,0x0E,
+0xC0,0x18,0x03,0x00,0x60,0x0C,0x01,0x80,0x30,0x36,0x0E,0xC7,0x99,0xE3,0x70,0x7E,0x0F,0xE1,0xCE,0x30,0xE6,0x0E,0xC0,0xF8,0x08,0x00,0x00,
+// 'l'
+0x6C,0x02,0x02,0x13,0x03,0x07,
+0xFF,0xFF,0xFF,0xFF,0xFC,
+// 'm'
+0x6D,0x09,0x10,0x0C,0x01,0x12,
+0x67,0x3C,0x6F,0xFE,0x7D,0xEE,0x79,0x86,0x71,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,0x61,0x86,
+// 'n'
+0x6E,0x09,0x0B,0x0C,0x01,0x0D,
+0x63,0x8D,0xF9,0xF1,0xBC,0x37,0x06,0xE0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0xB0,0x30,
+// 'o'
+0x6F,0x09,0x0C,0x0C,0x01,0x0D,
+0x0F,0x81,0xFC,0x38,0xC3,0x06,0x60,0x66,0x06,0x60,0x66,0x06,0x60,0xE3,0x1C,0x1F,0x80,0xF0,
+// 'p'
+0x70,0x08,0x0A,0x14,0x02,0x0D,
+0xC0,0x33,0xCF,0xFB,0xC6,0xC0,0xF0,0x3C,0x0F,0x03,0xC0,0xF0,0x7C,0x1B,0xFC,0xFE,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,
+// 'q'
+0x71,0x08,0x0A,0x14,0x01,0x0C,
+0x00,0x03,0xF3,0xFD,0xE3,0x60,0xF8,0x3C,0x0F,0x03,0xC0,0xF0,0x76,0x1D,0xFF,0x1F,0x80,0x60,0x18,0x06,0x01,0x80,0x60,0x18,0x06,
+// 'r'
+0x72,0x09,0x09,0x0C,0x01,0x0B,
+0xCF,0x6F,0xFE,0x7C,0x3C,0x1E,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x00,
+// 's'
+0x73,0x09,0x09,0x0C,0x02,0x0C,
+0x03,0x9F,0xDE,0x7C,0x3E,0x07,0xF0,0xFC,0x07,0x01,0xE0,0xFF,0xC7,0xC0,
+// 't'
+0x74,0x05,0x0A,0x10,0x00,0x0A,
+0x0C,0x03,0x00,0xC0,0x30,0xFF,0xFF,0xF0,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,
+// 'u'
+0x75,0x09,0x0B,0x0C,0x01,0x0C,
+0xC0,0xD8,0x1B,0x03,0x60,0x6C,0x0D,0x81,0xB0,0x36,0x06,0xC0,0xD8,0x19,0xFF,0x1F,0x60,
+// 'v'
+0x76,0x09,0x0B,0x0D,0x01,0x0C,
+0xC0,0x78,0x1F,0x83,0x30,0x67,0x1C,0x63,0x0C,0xE0,0xD8,0x1E,0x03,0xC0,0x30,0x06,0x00,0x00,
+// 'w'
+0x77,0x09,0x0F,0x0D,0x01,0x11,
+0xC1,0x87,0x83,0x0F,0x0E,0x1E,0x1C,0x66,0x7C,0xCC,0xD9,0x99,0x36,0x36,0x6C,0x7C,0xD8,0x70,0xE0,0xE1,0xC0,0x83,0x80,0x00,0x00,
+// 'x'
+0x78,0x09,0x0D,0x0D,0x01,0x0E,
+0x60,0x1B,0x81,0xCE,0x1C,0x39,0xC0,0xFC,0x03,0xC0,0x3C,0x03,0xF0,0x39,0xC3,0x87,0x38,0x1D,0x80,0x70,0x01,0x80,
+// 'y'
+0x79,0x09,0x0C,0x13,0x00,0x0D,
+0xC0,0x3E,0x07,0x60,0x67,0x0C,0x30,0xC3,0x98,0x19,0x81,0xD8,0x0F,0x00,0xF0,0x06,0x00,0x60,0x0C,0x00,0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x30,0x00,
+// 'z'
+0x7A,0x09,0x0B,0x0C,0x01,0x0D,
+0xFF,0xFF,0xFC,0x07,0x00,0xC0,0x30,0x0C,0x03,0x80,0xE0,0x38,0x0E,0x03,0xFF,0xFF,0xF0,
+// '{'
+0x7B,0x02,0x08,0x18,0x01,0x09,
+0x0F,0x1F,0x38,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x30,0x60,0xE0,0xE0,0x70,0x30,0x30,0x30,0x30,0x30,0x38,0x18,0x1F,0x07,
+// '|'
+0x7C,0x01,0x02,0x18,0x04,0x0A,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+// '}'
+0x7D,0x02,0x08,0x18,0x01,0x09,
+0x70,0xF8,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x06,0x07,0x07,0x0E,0x0C,0x0C,0x0C,0x0C,0x0C,0x1C,0x18,0xF8,0xE0,
+// '~'
+0x7E,0x0B,0x0C,0x05,0x01,0x0E,
+0x38,0x37,0xE3,0xE7,0x7C,0x3E,0x01,0xC0,
+
+// Terminator
+0xFF
+};
diff --git a/components/tft/component.mk b/components/tft/component.mk
new file mode 100644
index 0000000..0f76ee5
--- /dev/null
+++ b/components/tft/component.mk
@@ -0,0 +1,7 @@
+#
+# Main Makefile. This is basically the same as a component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
+
+COMPONENT_SRCDIRS := .
+COMPONENT_ADD_INCLUDEDIRS := .
diff --git a/components/tft/def_small.c b/components/tft/def_small.c
new file mode 100644
index 0000000..54101cd
--- /dev/null
+++ b/components/tft/def_small.c
@@ -0,0 +1,332 @@
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// on Arduinos. It can however handle any ttf font that has been converted
+// using the conversion program. These could be fixed width or proportional
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// def_small.c
+// Point Size : 9
+// Memory usage : 928 bytes
+// # characters : 95
+
+// Header Format (to make Arduino UTFT Compatible):
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+
+unsigned char tft_def_small[] =
+{
+0x00, 0x08, 0x00, 0x00,
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+
+// ' '
+0x20,0x08,0x00,0x00,0x00,0x03,
+
+// '!'
+0x21,0x01,0x01,0x07,0x01,0x03,
+0xFA,
+// '"'
+0x22,0x01,0x03,0x02,0x01,0x04,
+0xB4,
+// '#'
+0x23,0x01,0x06,0x07,0x01,0x08,
+0x28,0xAF,0xCA,0xFD,0x45,0x00,
+// '$'
+0x24,0x01,0x06,0x08,0x00,0x06,
+0x21,0xEA,0x38,0x38,0xAF,0x08,
+// '%'
+0x25,0x01,0x08,0x07,0x00,0x09,
+0x44,0xA4,0xA8,0x5A,0x15,0x25,0x22,
+// '&'
+0x26,0x01,0x06,0x07,0x01,0x08,
+0x31,0x04,0x19,0x9E,0x66,0xC0,
+// '''
+0x27,0x01,0x01,0x02,0x01,0x02,
+0xC0,
+// '('
+0x28,0x00,0x02,0x08,0x01,0x04,
+0x4A,0xA1,
+// ')'
+0x29,0x00,0x02,0x08,0x01,0x04,
+0x85,0x52,
+// '*'
+0x2A,0x01,0x05,0x04,0x00,0x05,
+0xAB,0x9D,0x50,
+// '+'
+0x2B,0x03,0x05,0x05,0x01,0x08,
+0x21,0x3E,0x42,0x00,
+// ','
+0x2C,0x07,0x01,0x02,0x01,0x03,
+0xC0,
+// '-'
+0x2D,0x05,0x02,0x01,0x01,0x03,
+0xC0,
+// '.'
+0x2E,0x07,0x01,0x01,0x01,0x03,
+0x80,
+// '/'
+0x2F,0x01,0x03,0x07,0x00,0x03,
+0x25,0x25,0x20,
+// '0'
+0x30,0x01,0x04,0x07,0x01,0x06,
+0x69,0x99,0x99,0x60,
+// '1'
+0x31,0x01,0x03,0x07,0x02,0x06,
+0xC9,0x24,0xB8,
+// '2'
+0x32,0x01,0x05,0x07,0x01,0x06,
+0x64,0x84,0x44,0x43,0xC0,
+// '3'
+0x33,0x01,0x04,0x07,0x01,0x06,
+0x69,0x16,0x11,0x60,
+// '4'
+0x34,0x01,0x05,0x07,0x01,0x06,
+0x11,0x94,0xA9,0x7C,0x40,
+// '5'
+0x35,0x01,0x04,0x07,0x01,0x06,
+0xF8,0x8E,0x11,0xE0,
+// '6'
+0x36,0x01,0x04,0x07,0x01,0x06,
+0x7C,0x8E,0x99,0x60,
+// '7'
+0x37,0x01,0x04,0x07,0x01,0x06,
+0xF1,0x22,0x24,0x40,
+// '8'
+0x38,0x01,0x04,0x07,0x01,0x06,
+0x69,0x96,0x99,0x60,
+// '9'
+0x39,0x01,0x04,0x07,0x01,0x06,
+0x69,0x97,0x13,0xE0,
+// ':'
+0x3A,0x03,0x01,0x05,0x01,0x03,
+0x88,
+// ';'
+0x3B,0x03,0x01,0x06,0x01,0x03,
+0x8C,
+// '<'
+0x3C,0x03,0x06,0x05,0x01,0x08,
+0x04,0xEE,0x0E,0x04,
+// '='
+0x3D,0x04,0x06,0x03,0x01,0x08,
+0xFC,0x0F,0xC0,
+// '>'
+0x3E,0x03,0x06,0x05,0x01,0x08,
+0x81,0xC1,0xDC,0x80,
+// '?'
+0x3F,0x01,0x04,0x07,0x01,0x05,
+0xE1,0x24,0x40,0x40,
+// '@'
+0x40,0x01,0x08,0x08,0x01,0x0A,
+0x3C,0x42,0x9D,0xA5,0xA5,0x9E,0x40,0x38,
+// 'A'
+0x41,0x01,0x06,0x07,0x00,0x06,
+0x30,0xC4,0x92,0x7A,0x18,0x40,
+// 'B'
+0x42,0x01,0x05,0x07,0x01,0x07,
+0xF4,0x63,0xE8,0xC7,0xC0,
+// 'C'
+0x43,0x01,0x05,0x07,0x01,0x07,
+0x72,0x61,0x08,0x25,0xC0,
+// 'D'
+0x44,0x01,0x05,0x07,0x01,0x07,
+0xF4,0xE3,0x18,0xCF,0xC0,
+// 'E'
+0x45,0x01,0x04,0x07,0x01,0x06,
+0xF8,0x8F,0x88,0xF0,
+// 'F'
+0x46,0x01,0x04,0x07,0x01,0x06,
+0xF8,0x8F,0x88,0x80,
+// 'G'
+0x47,0x01,0x05,0x07,0x01,0x07,
+0x76,0x61,0x38,0xE5,0xC0,
+// 'H'
+0x48,0x01,0x05,0x07,0x01,0x07,
+0x8C,0x63,0xF8,0xC6,0x20,
+// 'I'
+0x49,0x01,0x01,0x07,0x01,0x03,
+0xFE,
+// 'J'
+0x4A,0x01,0x02,0x09,0x00,0x03,
+0x55,0x55,0x80,
+// 'K'
+0x4B,0x01,0x05,0x07,0x01,0x06,
+0x8C,0xA9,0x8A,0x4A,0x20,
+// 'L'
+0x4C,0x01,0x04,0x07,0x01,0x05,
+0x88,0x88,0x88,0xF0,
+// 'M'
+0x4D,0x01,0x06,0x07,0x01,0x08,
+0x87,0x3C,0xED,0xB6,0x18,0x40,
+// 'N'
+0x4E,0x01,0x05,0x07,0x01,0x07,
+0x8E,0x73,0x59,0xCE,0x20,
+// 'O'
+0x4F,0x01,0x05,0x07,0x01,0x07,
+0x76,0xE3,0x18,0xED,0xC0,
+// 'P'
+0x50,0x01,0x04,0x07,0x01,0x06,
+0xE9,0x9E,0x88,0x80,
+// 'Q'
+0x51,0x01,0x05,0x08,0x01,0x07,
+0x76,0xE3,0x18,0xE9,0xC2,
+// 'R'
+0x52,0x01,0x05,0x07,0x01,0x06,
+0xE4,0xA5,0xCA,0x4A,0x20,
+// 'S'
+0x53,0x01,0x06,0x07,0x01,0x07,
+0x72,0x28,0x1C,0x0A,0x27,0x00,
+// 'T'
+0x54,0x01,0x05,0x07,0x00,0x05,
+0xF9,0x08,0x42,0x10,0x80,
+// 'U'
+0x55,0x01,0x05,0x07,0x01,0x07,
+0x8C,0x63,0x18,0xC5,0xC0,
+// 'V'
+0x56,0x01,0x06,0x07,0x00,0x06,
+0x86,0x14,0x92,0x48,0xC3,0x00,
+// 'W'
+0x57,0x01,0x09,0x07,0xFF,0x07,
+0x49,0x24,0x8A,0x85,0x43,0xE0,0xA0,0x50,
+// 'X'
+0x58,0x01,0x06,0x07,0x00,0x06,
+0xCD,0x23,0x0C,0x31,0x28,0xC0,
+// 'Y'
+0x59,0x01,0x05,0x07,0x00,0x05,
+0x8A,0x9C,0x42,0x10,0x80,
+// 'Z'
+0x5A,0x01,0x05,0x07,0x00,0x05,
+0xF8,0x44,0x44,0x43,0xE0,
+// '['
+0x5B,0x01,0x02,0x08,0x01,0x04,
+0xEA,0xAB,
+// '\'
+0x5C,0x01,0x03,0x07,0x00,0x03,
+0x91,0x24,0x48,
+// ']'
+0x5D,0x01,0x02,0x08,0x01,0x04,
+0xD5,0x57,
+// '^'
+0x5E,0x01,0x06,0x02,0x01,0x08,
+0x31,0x20,
+// '_'
+0x5F,0x09,0x05,0x01,0x00,0x05,
+0xF8,
+// '`'
+0x60,0x00,0x02,0x02,0x01,0x05,
+0x90,
+// 'a'
+0x61,0x03,0x04,0x05,0x01,0x06,
+0x61,0x79,0xF0,
+// 'b'
+0x62,0x00,0x04,0x08,0x01,0x06,
+0x88,0x8E,0x99,0x9E,
+// 'c'
+0x63,0x03,0x04,0x05,0x01,0x06,
+0x78,0x88,0x70,
+// 'd'
+0x64,0x00,0x04,0x08,0x01,0x06,
+0x11,0x17,0x99,0x97,
+// 'e'
+0x65,0x03,0x04,0x05,0x01,0x06,
+0x69,0xF8,0x70,
+// 'f'
+0x66,0x00,0x04,0x08,0x00,0x03,
+0x34,0x4E,0x44,0x44,
+// 'g'
+0x67,0x03,0x04,0x07,0x01,0x06,
+0x79,0x99,0x71,0x60,
+// 'h'
+0x68,0x00,0x04,0x08,0x01,0x06,
+0x88,0x8E,0x99,0x99,
+// 'i'
+0x69,0x01,0x01,0x07,0x01,0x03,
+0xBE,
+// 'j'
+0x6A,0x01,0x02,0x09,0x00,0x03,
+0x45,0x55,0x80,
+// 'k'
+0x6B,0x00,0x04,0x08,0x01,0x05,
+0x88,0x89,0xAC,0xA9,
+// 'l'
+0x6C,0x00,0x01,0x08,0x01,0x03,
+0xFF,
+// 'm'
+0x6D,0x03,0x07,0x05,0x01,0x09,
+0xED,0x26,0x4C,0x99,0x20,
+// 'n'
+0x6E,0x03,0x04,0x05,0x01,0x06,
+0xE9,0x99,0x90,
+// 'o'
+0x6F,0x03,0x04,0x05,0x01,0x06,
+0x69,0x99,0x60,
+// 'p'
+0x70,0x03,0x04,0x07,0x01,0x06,
+0xE9,0x99,0xE8,0x80,
+// 'q'
+0x71,0x03,0x04,0x07,0x01,0x06,
+0x79,0x99,0x71,0x10,
+// 'r'
+0x72,0x03,0x03,0x05,0x01,0x04,
+0xF2,0x48,
+// 's'
+0x73,0x03,0x04,0x05,0x01,0x05,
+0x68,0x62,0xE0,
+// 't'
+0x74,0x02,0x04,0x06,0x00,0x04,
+0x4F,0x44,0x47,
+// 'u'
+0x75,0x03,0x04,0x05,0x01,0x06,
+0x99,0x99,0x70,
+// 'v'
+0x76,0x03,0x07,0x05,0xFF,0x05,
+0x44,0x98,0xA1,0xC1,0x00,
+// 'w'
+0x77,0x03,0x07,0x05,0x00,0x07,
+0x93,0x76,0xBA,0x24,0x40,
+// 'x'
+0x78,0x03,0x05,0x05,0x00,0x05,
+0x8A,0x88,0xA8,0x80,
+// 'y'
+0x79,0x03,0x07,0x07,0xFF,0x05,
+0x44,0x88,0xA1,0xC1,0x02,0x18,0x00,
+// 'z'
+0x7A,0x03,0x04,0x05,0x01,0x06,
+0xF1,0x24,0xF0,
+// '{'
+0x7B,0x01,0x03,0x08,0x01,0x05,
+0x69,0x64,0x93,
+// '|'
+0x7C,0x01,0x01,0x09,0x01,0x03,
+0xFF,0x80,
+// '}'
+0x7D,0x01,0x03,0x08,0x01,0x05,
+0xC9,0x34,0x96,
+// '~'
+0x7E,0x03,0x06,0x03,0x01,0x08,
+0x01,0x91,0x80,
+
+// Terminator
+0xFF
+};
diff --git a/components/tft/minya24.c b/components/tft/minya24.c
new file mode 100644
index 0000000..6bab953
--- /dev/null
+++ b/components/tft/minya24.c
@@ -0,0 +1,331 @@
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// on Arduinos. It can however handle any ttf font that has been converted
+// using the conversion program. These could be fixed width or proportional
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// minya24.c
+// Point Size : 24
+// Memory usage : 2807 bytes
+// # characters : 95
+
+// Header Format (to make Arduino UTFT Compatible):
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+unsigned char tft_minya24[] =
+{
+0x00, 0x15, 0x00, 0x00,
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+
+// ' '
+0x20,0x13,0x00,0x00,0x00,0x07,
+
+// '!'
+0x21,0x02,0x05,0x12,0x00,0x05,
+0x11,0x8C,0x63,0x18,0xC6,0x31,0x8C,0x42,0x01,0xCE,0x73,0x80,
+// '"'
+0x22,0x01,0x06,0x08,0x00,0x06,
+0x01,0xA6,0xDB,0x6D,0xB6,0xC0,
+// '#'
+0x23,0x04,0x0C,0x0E,0x00,0x0B,
+0x04,0x80,0x6C,0x0C,0x80,0xD8,0x7F,0xE7,0xFE,0x1B,0x01,0xB0,0x7F,0xE7,0xFC,0x12,0x03,0x20,0x32,0x00,0x00,
+// '$'
+0x24,0x02,0x0A,0x11,0x00,0x0B,
+0x04,0x01,0x61,0xF8,0xFE,0x65,0x19,0x06,0x40,0xF0,0x1F,0x01,0xE0,0x4C,0x93,0x7C,0xCF,0xE0,0x60,0x10,0x04,0x00,
+// '%'
+0x25,0x01,0x0D,0x14,0x01,0x0F,
+0x00,0x01,0xC1,0x1F,0x19,0x8C,0xCC,0x6C,0x63,0x61,0x36,0x0F,0xB0,0x1B,0x00,0x18,0x01,0x80,0x0C,0x00,0xCE,0x06,0xF8,0x6C,0x66,0x63,0x33,0x13,0x0F,0x98,0x38,0x00,0x00,
+// '&'
+0x26,0x02,0x0E,0x11,0x00,0x0D,
+0x0E,0x00,0x7C,0x01,0xB0,0x06,0xC0,0x1E,0x00,0x38,0x00,0xC3,0x07,0x8C,0x37,0x60,0xCD,0x86,0x1E,0x18,0x70,0x41,0xE1,0x07,0xC6,0x33,0x9F,0x86,0x38,0x00,
+// '''
+0x27,0x02,0x03,0x08,0x00,0x03,
+0x6D,0xB6,0xD8,
+// '('
+0x28,0x01,0x05,0x14,0x02,0x07,
+0x00,0x8E,0x66,0x33,0x18,0xC6,0x31,0x8C,0x61,0x0C,0x63,0x8C,0x00,
+// ')'
+0x29,0x01,0x06,0x15,0x00,0x07,
+0x01,0x86,0x0C,0x18,0x61,0x82,0x08,0x30,0xC2,0x08,0x61,0x86,0x30,0xC6,0x10,0x00,
+// '*'
+0x2A,0x04,0x0A,0x0D,0x01,0x0B,
+0x08,0x03,0x04,0xC1,0xF6,0x7F,0x07,0x81,0xC0,0xF8,0x3F,0x1B,0xCC,0xD8,0x30,0x0C,0x00,
+// '+'
+0x2B,0x06,0x0A,0x0A,0x01,0x0B,
+0x00,0x03,0x00,0xC0,0x30,0x7F,0xBF,0xE0,0xC0,0x30,0x0C,0x00,0x00,
+// ','
+0x2C,0x10,0x05,0x07,0x00,0x05,
+0x33,0x9C,0x63,0x20,0x00,
+// '-'
+0x2D,0x09,0x07,0x02,0x00,0x07,
+0x7D,0xF8,
+// '.'
+0x2E,0x10,0x04,0x04,0x01,0x05,
+0x6E,0xE6,
+// '/'
+0x2F,0x01,0x0C,0x13,0x00,0x0B,
+0x00,0x00,0x06,0x00,0x60,0x0C,0x00,0xC0,0x18,0x01,0x80,0x30,0x03,0x00,0x60,0x06,0x00,0xC0,0x0C,0x01,0x80,0x18,0x03,0x00,0x30,0x06,0x00,0x40,0x00,
+// '0'
+0x30,0x08,0x0B,0x0B,0x00,0x0B,
+0x0E,0x03,0xE0,0xC6,0x30,0x66,0x0C,0xC0,0x98,0x33,0x06,0x61,0xC7,0xF0,0x7C,0x00,
+// '1'
+0x31,0x08,0x0A,0x0D,0x00,0x09,
+0x04,0x1F,0x03,0xC0,0x30,0x0C,0x03,0x00,0xC0,0x30,0x0C,0x03,0x07,0xF9,0xFE,0x00,0x00,
+// '2'
+0x32,0x06,0x0A,0x0E,0x00,0x0A,
+0x0E,0x07,0xC3,0x30,0xCC,0x03,0x01,0x80,0x60,0x30,0x18,0x0C,0x03,0x01,0xF8,0x7F,0xC0,0x20,
+// '3'
+0x33,0x08,0x09,0x10,0x00,0x08,
+0x78,0x3F,0x81,0x81,0x81,0xC0,0xC0,0xE0,0x78,0x06,0x01,0x01,0x80,0xC0,0xE1,0xE0,0xC0,0x00,
+// '4'
+0x34,0x03,0x0B,0x13,0x00,0x0A,
+0x00,0x00,0xC0,0x18,0x03,0x00,0x60,0x08,0x03,0x00,0x60,0x0D,0x83,0x20,0x64,0x18,0x83,0x10,0xC6,0x1F,0xF3,0xFE,0x03,0x00,0x60,0x0C,0x00,
+// '5'
+0x35,0x09,0x0A,0x0E,0x00,0x0A,
+0x3F,0x8F,0xE3,0x00,0x80,0x60,0x1F,0x87,0xF0,0x06,0x01,0x80,0x60,0x19,0x86,0x7F,0x07,0x80,
+// '6'
+0x36,0x03,0x0A,0x12,0x00,0x0A,
+0x00,0x01,0x80,0xE0,0x30,0x18,0x06,0x03,0x00,0xC0,0x60,0x1B,0x87,0xF1,0x86,0x61,0x90,0x66,0x19,0x86,0x3F,0x07,0x80,
+// '7'
+0x37,0x09,0x0A,0x0E,0x00,0x0A,
+0x7F,0x9F,0xF0,0x18,0x0C,0x06,0x01,0x80,0xC0,0x30,0x18,0x06,0x01,0x80,0x40,0x10,0x0C,0x00,
+// '8'
+0x38,0x03,0x0B,0x11,0x00,0x0B,
+0x0F,0x07,0xF0,0xC3,0x30,0x66,0x0C,0x63,0x87,0xE0,0xF8,0x39,0xCE,0x19,0x81,0x30,0x34,0x06,0xC1,0x98,0x71,0xFC,0x1F,0x00,
+// '9'
+0x39,0x07,0x0A,0x11,0x00,0x0A,
+0x1E,0x0F,0xC6,0x19,0x86,0x41,0x98,0x66,0x18,0xFE,0x1D,0x80,0x40,0x30,0x0C,0x06,0x03,0x01,0xC0,0x20,0x00,0x00,
+// ':'
+0x3A,0x0A,0x05,0x0A,0x00,0x05,
+0x33,0x9C,0x60,0x00,0xCE,0x71,0x80,
+// ';'
+0x3B,0x09,0x05,0x0D,0x00,0x06,
+0x33,0xDE,0x60,0x00,0x0E,0x73,0x8C,0xC0,0x00,
+// '<'
+0x3C,0x06,0x0A,0x0C,0x00,0x09,
+0x00,0x00,0xE0,0x70,0x38,0x1C,0x1C,0x03,0x00,0x70,0x0E,0x01,0xC0,0x10,0x00,
+// '='
+0x3D,0x09,0x08,0x06,0x01,0x0A,
+0xFF,0xFE,0x00,0x00,0x7F,0xFF,
+// '>'
+0x3E,0x06,0x0A,0x0C,0x00,0x09,
+0x00,0x18,0x03,0x80,0x70,0x0E,0x00,0xE0,0x30,0x38,0x1C,0x0E,0x02,0x00,0x00,
+// '?'
+0x3F,0x02,0x09,0x12,0x00,0x09,
+0x1E,0x1F,0x98,0x6C,0x30,0x18,0x18,0x38,0x30,0x30,0x18,0x0C,0x02,0x01,0x00,0x00,0x60,0x78,0x3C,0x0C,0x00,
+// '@'
+0x40,0x02,0x11,0x11,0x00,0x11,
+0x01,0xF0,0x03,0xFE,0x03,0x03,0x83,0x00,0xC3,0x04,0x31,0x1F,0x19,0x9F,0x0C,0xCC,0x86,0x4C,0x43,0x24,0x21,0x12,0x39,0x89,0xF7,0x84,0x71,0x83,0x00,0x00,0xC1,0x80,0x3F,0xC0,0x0F,0x80,0x00,
+// 'A'
+0x41,0x02,0x12,0x13,0x00,0x10,
+0x00,0x00,0x01,0xF0,0x00,0x7E,0x00,0x03,0x80,0x01,0xA0,0x00,0x6C,0x00,0x1B,0x00,0x0C,0x40,0x03,0x18,0x01,0x86,0x00,0x61,0x80,0x1F,0xE0,0x0F,0xFC,0x03,0x03,0x01,0x80,0xC0,0x60,0x10,0x18,0x1F,0x9F,0xC7,0xEF,0xF8,0x00,
+// 'B'
+0x42,0x02,0x0E,0x13,0x00,0x0E,
+0x7F,0x81,0xFF,0x81,0x86,0x06,0x18,0x18,0xC0,0x66,0x01,0xFC,0x07,0xFC,0x1C,0x30,0x60,0x61,0x81,0x86,0x06,0x18,0x18,0x60,0x61,0x81,0x06,0x0C,0x18,0xE0,0xFE,0x03,0xE0,0x00,
+// 'C'
+0x43,0x02,0x0F,0x12,0x00,0x0F,
+0x03,0xF0,0x1F,0xE0,0x70,0xC1,0x80,0x83,0x00,0x04,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x03,0x00,0x06,0x00,0x06,0x00,0x0E,0x01,0x0E,0x0E,0x0F,0xF8,0x07,0xE0,
+// 'D'
+0x44,0x02,0x0F,0x13,0x00,0x0F,
+0x3F,0xC0,0x7F,0xE0,0x60,0xE0,0xC0,0xC1,0x80,0xC3,0x01,0x86,0x03,0x0C,0x02,0x18,0x04,0x30,0x08,0x60,0x30,0xC0,0x61,0x80,0x83,0x03,0x06,0x0C,0x0C,0x38,0x18,0xE0,0xFF,0x81,0xF8,0x00,
+// 'E'
+0x45,0x02,0x0D,0x13,0x00,0x0D,
+0x3F,0xF1,0xFF,0x86,0x0C,0x30,0x61,0x80,0x0C,0x40,0x62,0x03,0xF0,0x1F,0x80,0xC4,0x06,0x20,0x30,0x01,0x80,0x0C,0x00,0x60,0xC3,0x06,0x7F,0xF3,0xFF,0x80,0x00,
+// 'F'
+0x46,0x01,0x0E,0x13,0x00,0x0E,
+0x00,0x01,0xFF,0xE7,0xFF,0x86,0x06,0x18,0x18,0x63,0x01,0x8C,0x03,0xF0,0x0F,0xC0,0x63,0x01,0x8C,0x06,0x30,0x18,0x00,0x60,0x01,0x80,0x06,0x00,0x3F,0x81,0xFE,0x00,0x00,0x00,
+// 'G'
+0x47,0x01,0x11,0x14,0x00,0x11,
+0x00,0x00,0x03,0xC8,0x03,0xFC,0x03,0x0E,0x03,0x03,0x01,0x00,0x01,0x80,0x00,0xC0,0x00,0x60,0x03,0x30,0x3F,0xD8,0x1F,0x0C,0x01,0x86,0x00,0xC3,0x80,0x60,0xC0,0x30,0x70,0x38,0x1C,0x3C,0x07,0xF6,0x01,0xF3,0x80,0x01,0xE0,
+// 'H'
+0x48,0x01,0x10,0x13,0x00,0x10,
+0x00,0x00,0x7C,0x0F,0x1C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x08,0x0C,0x08,0x0F,0xF8,0x0F,0xF8,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x18,0x0C,0x1A,0x7C,0x1F,0x7C,0x10,
+// 'I'
+0x49,0x02,0x08,0x13,0x00,0x08,
+0x7E,0x7E,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,
+// 'J'
+0x4A,0x01,0x0C,0x14,0x00,0x0B,
+0x00,0x00,0x7E,0x07,0xE0,0x18,0x01,0x80,0x18,0x01,0x80,0x18,0x00,0x80,0x08,0x00,0x80,0x18,0x61,0x84,0x18,0x41,0x8C,0x18,0xC3,0x06,0x70,0x7E,0x01,0x80,
+// 'K'
+0x4B,0x02,0x11,0x13,0x00,0x10,
+0x7E,0x7E,0x3F,0x3E,0x06,0x06,0x03,0x06,0x01,0x86,0x00,0xC6,0x00,0x66,0x00,0x37,0x00,0x1F,0x80,0x0E,0x60,0x06,0x10,0x03,0x0C,0x01,0x06,0x00,0x81,0x00,0x60,0x80,0x30,0x66,0x7E,0x1B,0x3F,0x0F,0x80,0x01,0x80,
+// 'L'
+0x4C,0x02,0x0D,0x11,0x00,0x0D,
+0x7E,0x03,0xF0,0x06,0x00,0x30,0x01,0x80,0x0C,0x00,0x60,0x03,0x00,0x18,0x00,0xC0,0x06,0x00,0x30,0x01,0x81,0x0C,0x08,0x60,0x4F,0xFF,0x7F,0xF8,
+// 'M'
+0x4D,0x02,0x14,0x12,0x00,0x13,
+0x7C,0x07,0x87,0xC0,0x7C,0x0C,0x06,0x00,0xE0,0xE0,0x0E,0x0E,0x00,0xE0,0xE0,0x1B,0x1E,0x01,0xB1,0x60,0x0B,0x16,0x00,0x9B,0x60,0x09,0xA6,0x00,0x9E,0x60,0x08,0xE6,0x00,0x8C,0x60,0x08,0xC6,0x03,0x8C,0x7E,0x78,0x47,0xE0,0x00,0x00,
+// 'N'
+0x4E,0x01,0x12,0x13,0x00,0x12,
+0x00,0x00,0x1F,0x03,0xF3,0xC0,0x7C,0x38,0x10,0x0E,0x04,0x03,0xC1,0x00,0xB0,0x40,0x26,0x10,0x09,0x84,0x02,0x31,0x00,0x8C,0x40,0x21,0x90,0x08,0x74,0x06,0x0F,0x01,0x81,0xC0,0x60,0x70,0x78,0x0C,0x3E,0x03,0x00,0x00,0x40,
+// 'O'
+0x4F,0x02,0x11,0x12,0x00,0x11,
+0x03,0xE0,0x07,0xFC,0x07,0x07,0x06,0x01,0x82,0x00,0x63,0x00,0x11,0x80,0x0C,0x80,0x06,0x40,0x03,0x20,0x01,0x98,0x00,0xCC,0x00,0x66,0x00,0x31,0x80,0x30,0xE0,0x18,0x38,0x18,0x0F,0xF8,0x01,0xF0,0x00,
+// 'P'
+0x50,0x02,0x0C,0x12,0x00,0x0C,
+0x7F,0x07,0xFC,0x10,0x61,0x03,0x10,0x31,0x03,0x10,0x31,0x06,0x10,0xE1,0xFC,0x1E,0x01,0x00,0x18,0x01,0x80,0x18,0x01,0x80,0x7E,0x07,0xF0,
+// 'Q'
+0x51,0x02,0x13,0x15,0x00,0x11,
+0x07,0xE0,0x01,0xFE,0x00,0x60,0x70,0x18,0x06,0x06,0x00,0x60,0xC0,0x0C,0x18,0x00,0xC2,0x00,0x18,0x40,0x03,0x08,0x0C,0x61,0x83,0xCC,0x30,0xCD,0x83,0x09,0xE0,0x60,0x3C,0x06,0x07,0x00,0x7F,0xE3,0x07,0xEC,0x40,0x01,0x98,0x00,0x33,0x00,0x03,0xC0,0x00,0x30,
+// 'R'
+0x52,0x02,0x0F,0x13,0x00,0x0F,
+0x7F,0x80,0xFF,0xE0,0x60,0xE0,0xC0,0x61,0x80,0xC3,0x01,0x86,0x07,0x0C,0x1C,0x1F,0xF0,0x3F,0x80,0x66,0x00,0xC4,0x01,0x8C,0x63,0x18,0xC6,0x11,0x8C,0x33,0x7E,0x7C,0xFC,0x70,0x00,0x00,
+// 'S'
+0x53,0x01,0x0D,0x13,0x00,0x0D,
+0x00,0x60,0x7B,0x07,0xF8,0x71,0xC3,0x06,0x18,0x00,0xC0,0x03,0x80,0x0E,0x00,0x1C,0x00,0x78,0x00,0xC0,0x03,0x30,0x19,0x80,0xCC,0x06,0x30,0x70,0xFF,0x03,0xE0,
+// 'T'
+0x54,0x01,0x0F,0x12,0x00,0x0F,
+0x00,0x00,0xFF,0xF9,0xFF,0xF3,0x18,0x66,0x30,0xCC,0x61,0x90,0xC3,0x01,0x80,0x03,0x00,0x06,0x00,0x0C,0x00,0x18,0x00,0x30,0x00,0x60,0x00,0xC0,0x01,0x80,0x0F,0xE0,0x1F,0xC0,
+// 'U'
+0x55,0x02,0x10,0x11,0x00,0x0F,
+0x7C,0x7E,0x7C,0x7E,0x30,0x08,0x30,0x08,0x30,0x08,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x30,0x0C,0x10,0x08,0x18,0x18,0x18,0x18,0x0C,0x30,0x0F,0xF0,0x03,0xC0,
+// 'V'
+0x56,0x01,0x10,0x12,0x00,0x10,
+0x00,0x7E,0xFE,0x7E,0x7E,0x18,0x18,0x30,0x18,0x30,0x18,0x20,0x08,0x60,0x0C,0x60,0x0C,0x60,0x0C,0x40,0x0C,0xC0,0x04,0xC0,0x04,0xC0,0x06,0x80,0x07,0x80,0x07,0x80,0x03,0x00,0x03,0x00,
+// 'W'
+0x57,0x01,0x14,0x13,0x00,0x14,
+0x00,0x00,0x0F,0xC4,0xFF,0xF0,0x4F,0xE3,0x06,0x10,0x30,0xE3,0x01,0x8E,0x30,0x18,0xE3,0x01,0x8E,0x30,0x19,0xA3,0x01,0x9A,0x20,0x09,0xA6,0x00,0xD2,0x60,0x0D,0x36,0x00,0xF3,0x40,0x0F,0x34,0x00,0x63,0xC0,0x06,0x1C,0x00,0x61,0x80,0x00,0x18,0x00,
+// 'X'
+0x58,0x01,0x10,0x13,0x00,0x10,
+0x00,0x00,0x7F,0x7F,0x7E,0x7E,0x0C,0x30,0x06,0x20,0x06,0x60,0x03,0xC0,0x01,0x80,0x01,0x80,0x03,0x80,0x02,0xC0,0x06,0x40,0x04,0x60,0x0C,0x60,0x18,0x30,0x18,0x30,0x7C,0x30,0xFC,0xFE,0x00,0xFF,
+// 'Y'
+0x59,0x01,0x11,0x13,0x00,0x10,
+0x00,0x00,0x7F,0x3F,0xBF,0x07,0x03,0x03,0x00,0xC1,0x00,0x31,0x80,0x18,0x80,0x06,0xC0,0x01,0xC0,0x00,0xE0,0x00,0x30,0x00,0x30,0x00,0x18,0x00,0x0C,0x00,0x06,0x00,0x03,0x00,0x07,0x80,0x03,0xF8,0x00,0x7C,0x00,
+// 'Z'
+0x5A,0x02,0x0E,0x12,0x00,0x0D,
+0x7F,0xF9,0xFF,0xE6,0x03,0x18,0x18,0x60,0xC1,0x07,0x00,0x18,0x00,0xC0,0x06,0x00,0x18,0x00,0xC0,0x06,0x00,0x18,0x30,0xC0,0xC3,0x03,0x1F,0xFE,0x7F,0xF8,0x00,0x60,
+// '['
+0x5B,0x01,0x08,0x14,0x01,0x08,
+0x00,0x7C,0x7C,0x40,0x40,0x40,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x60,0x7E,0x7E,0x00,
+// '\'
+0x5C,0x01,0x0A,0x13,0x00,0x0A,
+0x00,0x10,0x06,0x01,0x80,0x30,0x0C,0x01,0x80,0x60,0x0C,0x03,0x00,0x60,0x18,0x06,0x00,0xC0,0x30,0x06,0x01,0x80,0x60,0x08,
+// ']'
+0x5D,0x01,0x08,0x14,0x00,0x08,
+0x00,0x7C,0x7E,0x04,0x04,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x0C,0x7C,0xFC,
+// '^'
+0x5E,0x03,0x0B,0x0B,0x00,0x0B,
+0x00,0x00,0x40,0x18,0x03,0x00,0xF0,0x32,0x0C,0x63,0x86,0x60,0xC0,0x0C,0x00,0x00,
+// '_'
+0x5F,0x15,0x11,0x02,0xFF,0x0F,
+0x7F,0xFF,0x3F,0xFF,0x80,
+// '`'
+0x60,0x00,0x06,0x07,0x00,0x06,
+0x01,0x87,0x0C,0x18,0x20,0x00,
+// 'a'
+0x61,0x06,0x0C,0x0D,0x00,0x0B,
+0x0E,0x03,0xF0,0x31,0x06,0x10,0x23,0x80,0xF8,0x3D,0x87,0x10,0x61,0x0C,0x18,0x63,0xA7,0xFE,0x1C,0xC0,
+// 'b'
+0x62,0x01,0x0C,0x14,0x00,0x0C,
+0x00,0x07,0xC0,0x3C,0x01,0x80,0x18,0x01,0x80,0x18,0x01,0x98,0x1F,0xC1,0xC6,0x18,0x21,0x83,0x10,0x31,0x03,0x18,0x31,0x83,0x18,0x6F,0xC6,0xF7,0xC0,0x30,
+// 'c'
+0x63,0x06,0x0B,0x0D,0x00,0x0B,
+0x06,0x03,0xF0,0xC7,0x31,0xE4,0x1C,0x80,0x30,0x06,0x00,0x60,0x0C,0x01,0xC3,0x1F,0xC0,0xF0,
+// 'd'
+0x64,0x01,0x0D,0x13,0x00,0x0D,
+0x00,0x00,0x1E,0x00,0xF0,0x01,0x80,0x0C,0x00,0x60,0x03,0x01,0x98,0x3E,0x81,0x9C,0x18,0x60,0xC3,0x04,0x08,0x60,0x41,0x02,0x0C,0x30,0x71,0x91,0xFF,0x87,0x1C,
+// 'e'
+0x65,0x07,0x0B,0x0C,0x00,0x0B,
+0x0F,0x03,0xF0,0xC3,0x30,0x66,0x1C,0xDE,0x1E,0x03,0x00,0x60,0x06,0x18,0x7E,0x07,0x80,
+// 'f'
+0x66,0x02,0x0A,0x12,0x00,0x08,
+0x07,0x07,0xE1,0xB8,0x4E,0x10,0x04,0x07,0xE1,0xF8,0x18,0x06,0x01,0x80,0x60,0x18,0x06,0x01,0x81,0xF8,0x7E,0x00,0x00,
+// 'g'
+0x67,0x05,0x0A,0x12,0x00,0x0A,
+0x00,0x00,0x20,0x18,0x7E,0x3F,0x18,0x64,0x09,0x02,0x61,0x9F,0xC1,0xE0,0x0C,0x01,0x84,0x67,0x19,0x8C,0x7E,0x0F,0x00,
+// 'h'
+0x68,0x02,0x0E,0x12,0x00,0x0D,
+0x78,0x01,0xE0,0x01,0x80,0x06,0x00,0x19,0xC0,0x2F,0x80,0xE6,0x07,0x08,0x18,0x20,0x60,0x81,0x82,0x06,0x08,0x18,0x20,0x60,0x81,0x82,0x1F,0x9E,0x7E,0x78,0x00,0x00,
+// 'i'
+0x69,0x02,0x08,0x11,0x00,0x08,
+0x30,0x78,0x78,0x30,0x00,0x78,0x78,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7F,
+// 'j'
+0x6A,0x02,0x09,0x15,0xFE,0x07,
+0x06,0x07,0x03,0x81,0xC0,0x00,0xF8,0x7C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x18,0x0C,0x06,0x03,0x01,0x99,0x8F,0xC3,0xC0,
+// 'k'
+0x6B,0x02,0x0D,0x12,0x00,0x0D,
+0x78,0x03,0xC0,0x06,0x00,0x37,0xC1,0xBC,0x0C,0xC0,0x6C,0x03,0xC0,0x1C,0x00,0xC0,0x07,0x00,0x3E,0x01,0xB8,0x08,0x61,0xC1,0xCF,0x07,0x00,0x10,0x00,0x00,
+// 'l'
+0x6C,0x02,0x08,0x12,0x00,0x08,
+0x78,0x78,0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x18,0x18,0x18,0x18,0x18,0x18,0x7E,0x7E,0x00,
+// 'm'
+0x6D,0x08,0x14,0x0D,0x00,0x14,
+0x40,0xC3,0x0F,0xBE,0xF8,0x1E,0x78,0xC1,0xC7,0x0C,0x18,0x20,0xC1,0x82,0x0C,0x10,0x20,0xC1,0x82,0x08,0x18,0x60,0x81,0x84,0x18,0x7E,0x71,0xE7,0xE7,0x9E,0x00,0x00,0x00,
+// 'n'
+0x6E,0x06,0x0E,0x0E,0x00,0x0D,
+0x79,0xC1,0xEF,0x81,0xE6,0x07,0x08,0x18,0x30,0x60,0xC1,0x82,0x06,0x08,0x18,0x20,0x60,0x81,0x82,0x1F,0x1E,0x7E,0x78,0x00,0x00,
+// 'o'
+0x6F,0x08,0x0B,0x0C,0x00,0x0C,
+0x0F,0x03,0xF8,0xC3,0x30,0x36,0x06,0x80,0xD0,0x1B,0x03,0x60,0x66,0x18,0xFE,0x07,0x80,
+// 'p'
+0x70,0x07,0x0B,0x10,0x00,0x0B,
+0x07,0x1F,0xF1,0xE3,0x18,0x23,0x06,0x60,0xCC,0x19,0x82,0x30,0xC6,0x18,0xFE,0x1B,0x83,0x00,0x60,0x1C,0x07,0x80,
+// 'q'
+0x71,0x06,0x0D,0x11,0x00,0x0B,
+0x0C,0x01,0xF7,0x0C,0xF8,0xC3,0x04,0x18,0x20,0x41,0x02,0x08,0x10,0x60,0x83,0x04,0x0C,0x60,0x7F,0x01,0xE8,0x00,0xC0,0x06,0xC0,0x1E,0x00,0xE0,
+// 'r'
+0x72,0x08,0x0A,0x0D,0x00,0x09,
+0x77,0x3F,0xE3,0xB8,0xCE,0x33,0x0C,0x03,0x00,0xC0,0x30,0x04,0x07,0xC1,0xF0,0x00,0x00,
+// 's'
+0x73,0x05,0x0A,0x0E,0x00,0x0A,
+0x03,0x00,0xC1,0xF0,0xFC,0x63,0x18,0x47,0x00,0xF8,0x07,0x80,0x64,0x19,0x86,0x7F,0x07,0x80,
+// 't'
+0x74,0x02,0x09,0x13,0x00,0x09,
+0x30,0x18,0x0C,0x06,0x02,0x07,0xE3,0xF8,0x40,0x20,0x10,0x08,0x04,0x02,0x11,0x18,0x84,0x62,0x33,0x0F,0x83,0x00,
+// 'u'
+0x75,0x06,0x0E,0x0E,0x00,0x0D,
+0x00,0x03,0xE7,0xC7,0x9F,0x0C,0x30,0x30,0xC0,0xC3,0x03,0x0C,0x0C,0x30,0x30,0xC0,0xC3,0x03,0x0C,0x0E,0x7C,0x1F,0xF8,0x3C,0x00,
+// 'v'
+0x76,0x07,0x0E,0x0D,0x00,0x0D,
+0x01,0xFB,0xF3,0xEF,0xC6,0x06,0x18,0x18,0x60,0x31,0x00,0xCC,0x01,0x30,0x06,0x80,0x0A,0x00,0x38,0x00,0xE0,0x01,0x00,
+// 'w'
+0x77,0x08,0x11,0x0D,0x00,0x11,
+0x04,0x1F,0xFF,0x6F,0xBE,0x31,0x83,0x1C,0xC1,0x9E,0x40,0x4D,0x20,0x34,0xB0,0x1A,0x78,0x05,0x3C,0x03,0x8E,0x01,0x86,0x00,0x43,0x00,0x00,0x00,
+// 'x'
+0x78,0x08,0x0D,0x0D,0x00,0x0D,
+0x0C,0xFB,0xE7,0xDE,0x18,0x31,0x80,0xD8,0x03,0x80,0x18,0x01,0xE0,0x19,0x81,0x8C,0x0E,0x7D,0xF3,0xE4,0x00,0x00,
+// 'y'
+0x79,0x06,0x0F,0x11,0x00,0x0D,
+0x7C,0x01,0xF8,0x00,0xC3,0xF0,0xC7,0xE1,0x82,0x01,0x0C,0x03,0x18,0x06,0x20,0x06,0xC0,0x0D,0x00,0x0E,0x00,0x18,0x00,0x30,0x06,0xC0,0x1F,0x00,0x3E,0x00,0x30,0x00,
+// 'z'
+0x7A,0x08,0x0B,0x0B,0x00,0x0B,
+0x7F,0xCF,0xF9,0x86,0x31,0x86,0x60,0x1C,0x07,0x00,0xC6,0x30,0xCF,0xF9,0xFF,0x00,
+// '{'
+0x7B,0x02,0x09,0x12,0x00,0x08,
+0x07,0x07,0x83,0x01,0x01,0x80,0xC0,0x60,0xE0,0x70,0x0C,0x06,0x03,0x01,0x80,0xC0,0x60,0x30,0x1F,0x03,0x80,
+// '|'
+0x7C,0x02,0x03,0x12,0x01,0x05,
+0x49,0x24,0x92,0x6D,0xB6,0xDB,0x6C,
+// '}'
+0x7D,0x02,0x07,0x11,0x00,0x07,
+0x30,0xF0,0x20,0x41,0x83,0x02,0x06,0x0E,0x18,0x60,0xC0,0x81,0x83,0x3C,0x78,
+// '~'
+0x7E,0x09,0x0D,0x04,0x00,0x0D,
+0x18,0x03,0xF1,0x98,0xFC,0x83,0xC0,
+
+// Terminator
+0xFF
+};
\ No newline at end of file
diff --git a/components/tft/stmpe610.h b/components/tft/stmpe610.h
new file mode 100644
index 0000000..6a52454
--- /dev/null
+++ b/components/tft/stmpe610.h
@@ -0,0 +1,71 @@
+/*
+ STMPE610 Touch controller constants
+*/
+
+#ifndef _STMPE610_H
+#define _STMPE610_H
+
+#include
+
+#define STMPE610_SPI_MODE 1
+
+// Identification registers
+#define STMPE610_REG_CHP_ID 0x00 // 16-bit
+#define STMPE610_REG_ID_VER 0x02
+
+// System registers
+#define STMPE610_REG_SYS_CTRL1 0x03
+#define STMPE610_REG_SYS_CTRL2 0x04
+#define STMPE610_REG_SPI_CFG 0x08
+
+// Interrupt control registers
+#define STMPE610_REG_INT_CTRL 0x09
+#define STMPE610_REG_INT_EN 0x0A
+#define STMPE610_REG_INT_STA 0x0B
+#define STMPE610_REG_GPIO_INT_EN 0x0C
+#define STMPE610_REG_GPIO_INT_STA 0x0D
+#define STMPE610_REG_ADC_INT_EN 0x0E
+#define STMPE610_REG_ADC_INT_STA 0x0F
+
+// GPIO registers
+#define STMPE610_REG_GPIO_SET_PIN 0x10
+#define STMPE610_REG_GPIO_CLR_PIN 0x11
+#define STMPE610_REG_GPIO_MP_STA 0x12
+#define STMPE610_REG_GPIO_DIR 0x13
+#define STMPE610_REG_GPIO_ED 0x14
+#define STMPE610_REG_GPIO_RE 0x15
+#define STMPE610_REG_GPIO_FE 0x16
+#define STMPE610_REG_GPIO_AF 0x17
+
+// ADC registers
+#define STMPE610_REG_ADC_CTRL1 0x20
+#define STMPE610_REG_ADC_CTRL2 0x21
+#define STMPE610_REG_ADC_CAPT 0x22
+#define STMPE610_REG_ADC_DATA_CH0 0x30 // 16-bit
+#define STMPE610_REG_ADC_DATA_CH1 0x32 // 16-bit
+#define STMPE610_REG_ADC_DATA_CH4 0x38 // 16-bit
+#define STMPE610_REG_ADC_DATA_CH5 0x3A // 16-bit
+#define STMPE610_REG_ADC_DATA_CH6 0x3C // 16-bit
+#define STMPE610_REG_ADC_DATA_CH7 0x3E // 16-bit
+
+// Touchscreen registers
+#define STMPE610_REG_TSC_CTRL 0x40
+#define STMPE610_REG_TSC_CFG 0x41
+#define STMPE610_REG_WDW_TR_X 0x42 // 16-bit
+#define STMPE610_REG_WDW_TR_Y 0x44 // 16-bit
+#define STMPE610_REG_WDW_BL_X 0x46 // 16-bit
+#define STMPE610_REG_WDW_BL_Y 0x48 // 16-bit
+#define STMPE610_REG_FIFO_TH 0x4A
+#define STMPE610_REG_FIFO_STA 0x4B
+#define STMPE610_REG_FIFO_SIZE 0x4C
+#define STMPE610_REG_TSC_DATA_X 0x4D // 16-bit
+#define STMPE610_REG_TSC_DATA_Y 0x4F // 16-bit
+#define STMPE610_REG_TSC_DATA_Z 0x51
+#define STMPE610_REG_TSC_DATA_XYZ 0x52 // 32-bit
+#define STMPE610_REG_TSC_FRACT_XYZ 0x56
+#define STMPE610_REG_TSC_DATA 0x57
+#define STMPE610_REG_TSC_I_DRIVE 0x58
+#define STMPE610_REG_TSC_SHIELD 0x59
+
+
+#endif /* _STMPE610_H */
diff --git a/components/tft/tft.c b/components/tft/tft.c
new file mode 100644
index 0000000..e4498d5
--- /dev/null
+++ b/components/tft/tft.c
@@ -0,0 +1,2950 @@
+/* TFT module
+ *
+ * Author: LoBo (loboris@gmail.com, loboris.github)
+ *
+ * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers
+*/
+
+#include
+#include
+#include
+#include
+#include "freertos/FreeRTOS.h"
+#include "tft.h"
+#include
+#include "esp32/rom/tjpgd.h"
+
+
+#define DEG_TO_RAD 0.01745329252
+#define deg_to_rad 0.01745329252 + 3.14159265359
+#define swap(a, b) { int16_t t = a; a = b; b = t; }
+
+#if !defined(max)
+#define max(A,B) ( (A) > (B) ? (A):(B))
+#endif
+#if !defined(min)
+#define min(A,B) ( (A) < (B) ? (A):(B))
+#endif
+
+// Embedded fonts
+extern uint8_t tft_SmallFont[];
+extern uint8_t tft_DefaultFont[];
+extern uint8_t tft_Dejavu18[];
+extern uint8_t tft_Dejavu24[];
+extern uint8_t tft_Ubuntu16[];
+extern uint8_t tft_Comic24[];
+extern uint8_t tft_minya24[];
+extern uint8_t tft_tooney32[];
+extern uint8_t tft_def_small[];
+
+// ==== Color definitions constants ==============
+const color_t TFT_BLACK = { 0, 0, 0 };
+const color_t TFT_NAVY = { 0, 0, 128 };
+const color_t TFT_DARKGREEN = { 0, 128, 0 };
+const color_t TFT_DARKCYAN = { 0, 128, 128 };
+const color_t TFT_MAROON = { 128, 0, 0 };
+const color_t TFT_PURPLE = { 128, 0, 128 };
+const color_t TFT_OLIVE = { 128, 128, 0 };
+const color_t TFT_LIGHTGREY = { 192, 192, 192 };
+const color_t TFT_DARKGREY = { 128, 128, 128 };
+const color_t TFT_BLUE = { 0, 0, 255 };
+const color_t TFT_GREEN = { 0, 255, 0 };
+const color_t TFT_CYAN = { 0, 255, 255 };
+const color_t TFT_RED = { 252, 0, 0 };
+const color_t TFT_MAGENTA = { 252, 0, 255 };
+const color_t TFT_YELLOW = { 252, 252, 0 };
+const color_t TFT_WHITE = { 252, 252, 252 };
+const color_t TFT_ORANGE = { 252, 164, 0 };
+const color_t TFT_GREENYELLOW = { 172, 252, 44 };
+const color_t TFT_PINK = { 252, 192, 202 };
+// ===============================================
+
+// ==============================================================
+// ==== Set default values of global variables ==================
+uint8_t tft_orientation = LANDSCAPE;// screen tft_orientation
+uint16_t tft_font_rotate = 0; // font rotation
+uint8_t tft_font_transparent = 0;
+uint8_t tft_font_forceFixed = 0;
+uint8_t tft_text_wrap = 0; // character wrapping to new line
+color_t tft_fg = { 0, 255, 0};
+color_t tft_bg = { 0, 0, 0};
+uint8_t tft_image_debug = 0;
+
+float tft_angleOffset = DEFAULT_ANGLE_OFFSET;
+
+int tft_x = 0;
+int tft_y = 0;
+
+uint32_t tft_tp_calx = 7472920;
+uint32_t tft_tp_caly = 122224794;
+
+dispWin_t tft_dispWin = {
+ .x1 = TFT_STATIC_WIDTH_OFFSET,
+ .y1 = TFT_STATIC_HEIGHT_OFFSET,
+ .x2 = DEFAULT_TFT_DISPLAY_WIDTH + TFT_STATIC_WIDTH_OFFSET,
+ .y2 = DEFAULT_TFT_DISPLAY_HEIGHT + TFT_STATIC_HEIGHT_OFFSET,
+};
+
+Font tft_cfont = {
+ .font = tft_DefaultFont,
+ .x_size = 0,
+ .y_size = 0x0B,
+ .offset = 0,
+ .numchars = 95,
+ .bitmap = 1,
+};
+
+uint8_t tft_font_buffered_char = 1;
+uint8_t tft_font_line_space = 0;
+// ==============================================================
+
+
+typedef struct {
+ uint8_t charCode;
+ int adjYOffset;
+ int width;
+ int height;
+ int xOffset;
+ int xDelta;
+ uint16_t dataPtr;
+} propFont;
+
+static dispWin_t dispWinTemp;
+
+static uint8_t *userfont = NULL;
+static int TFT_OFFSET = 0;
+static propFont fontChar;
+static float _arcAngleMax = DEFAULT_ARC_ANGLE_MAX;
+
+
+// =========================================================================
+// ** All drawings are clipped to 'tft_dispWin' **
+// ** All x,y coordinates in public functions are relative to clip window **
+// =========== : Public functions
+// ----------- : Local functions
+// =========================================================================
+
+
+// Compare two colors; return 0 if equal
+//============================================
+int TFT_compare_colors(color_t c1, color_t c2)
+{
+ if ((c1.r & 0xFC) != (c2.r & 0xFC)) return 1;
+ if ((c1.g & 0xFC) != (c2.g & 0xFC)) return 1;
+ if ((c1.b & 0xFC) != (c2.b & 0xFC)) return 1;
+
+ return 0;
+}
+
+// draw color pixel on screen
+//------------------------------------------------------------------------
+static void _drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
+
+ if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
+ drawPixel(x, y, color, sel);
+}
+
+//====================================================================
+void TFT_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel) {
+
+ _drawPixel(x+tft_dispWin.x1, y+tft_dispWin.y1, color, sel);
+}
+
+//===========================================
+color_t TFT_readPixel(int16_t x, int16_t y) {
+
+ if ((x < tft_dispWin.x1) || (y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return TFT_BLACK;
+
+ return readPixel(x, y);
+}
+
+//--------------------------------------------------------------------------
+static void _drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
+ // clipping
+ if ((x < tft_dispWin.x1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
+ if (y < tft_dispWin.y1) {
+ h -= (tft_dispWin.y1 - y);
+ y = tft_dispWin.y1;
+ }
+ if (h < 0) h = 0;
+ if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1;
+ if (h == 0) h = 1;
+ TFT_pushColorRep(x, y, x, y+h-1, color, (uint32_t)h);
+}
+
+//--------------------------------------------------------------------------
+static void _drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
+ // clipping
+ if ((y < tft_dispWin.y1) || (x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
+ if (x < tft_dispWin.x1) {
+ w -= (tft_dispWin.x1 - x);
+ x = tft_dispWin.x1;
+ }
+ if (w < 0) w = 0;
+ if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1;
+ if (w == 0) w = 1;
+
+ TFT_pushColorRep(x, y, x+w-1, y, color, (uint32_t)w);
+}
+
+//======================================================================
+void TFT_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color) {
+ _drawFastVLine(x+tft_dispWin.x1, y+tft_dispWin.y1, h, color);
+}
+
+//======================================================================
+void TFT_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color) {
+ _drawFastHLine(x+tft_dispWin.x1, y+tft_dispWin.y1, w, color);
+}
+
+// Bresenham's algorithm - thx wikipedia - speed enhanced by Bodmer this uses
+// the eficient FastH/V Line draw routine for segments of 2 pixels or more
+//----------------------------------------------------------------------------------
+static void _drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
+{
+ if (x0 == x1) {
+ if (y0 <= y1) _drawFastVLine(x0, y0, y1-y0, color);
+ else _drawFastVLine(x0, y1, y0-y1, color);
+ return;
+ }
+ if (y0 == y1) {
+ if (x0 <= x1) _drawFastHLine(x0, y0, x1-x0, color);
+ else _drawFastHLine(x1, y0, x0-x1, color);
+ return;
+ }
+
+ int steep = 0;
+ if (abs(y1 - y0) > abs(x1 - x0)) steep = 1;
+ if (steep) {
+ swap(x0, y0);
+ swap(x1, y1);
+ }
+ if (x0 > x1) {
+ swap(x0, x1);
+ swap(y0, y1);
+ }
+
+ int16_t dx = x1 - x0, dy = abs(y1 - y0);
+ int16_t err = dx >> 1, ystep = -1, xs = x0, dlen = 0;
+
+ if (y0 < y1) ystep = 1;
+
+ // Split into steep and not steep for FastH/V separation
+ if (steep) {
+ for (; x0 <= x1; x0++) {
+ dlen++;
+ err -= dy;
+ if (err < 0) {
+ err += dx;
+ if (dlen == 1) _drawPixel(y0, xs, color, 1);
+ else _drawFastVLine(y0, xs, dlen, color);
+ dlen = 0; y0 += ystep; xs = x0 + 1;
+ }
+ }
+ if (dlen) _drawFastVLine(y0, xs, dlen, color);
+ }
+ else
+ {
+ for (; x0 <= x1; x0++) {
+ dlen++;
+ err -= dy;
+ if (err < 0) {
+ err += dx;
+ if (dlen == 1) _drawPixel(xs, y0, color, 1);
+ else _drawFastHLine(xs, y0, dlen, color);
+ dlen = 0; y0 += ystep; xs = x0 + 1;
+ }
+ }
+ if (dlen) _drawFastHLine(xs, y0, dlen, color);
+ }
+}
+
+//==============================================================================
+void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color)
+{
+ _drawLine(x0+tft_dispWin.x1, y0+tft_dispWin.y1, x1+tft_dispWin.x1, y1+tft_dispWin.y1, color);
+}
+
+// fill a rectangle
+//--------------------------------------------------------------------------------
+static void _fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
+ // clipping
+ if ((x >= tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
+
+ if (x < tft_dispWin.x1) {
+ w -= (tft_dispWin.x1 - x);
+ x = tft_dispWin.x1;
+ }
+ if (y < tft_dispWin.y1) {
+ h -= (tft_dispWin.y1 - y);
+ y = tft_dispWin.y1;
+ }
+ if (w < 0) w = 0;
+ if (h < 0) h = 0;
+
+ if ((x + w) > (tft_dispWin.x2+1)) w = tft_dispWin.x2 - x + 1;
+ if ((y + h) > (tft_dispWin.y2+1)) h = tft_dispWin.y2 - y + 1;
+ if (w == 0) w = 1;
+ if (h == 0) h = 1;
+ TFT_pushColorRep(x, y, x+w-1, y+h-1, color, (uint32_t)(h*w));
+}
+
+//============================================================================
+void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color) {
+ _fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, color);
+}
+
+//==================================
+void TFT_fillScreen(color_t color) {
+ TFT_pushColorRep(TFT_STATIC_X_OFFSET, TFT_STATIC_Y_OFFSET, tft_width + TFT_STATIC_X_OFFSET -1, tft_height + TFT_STATIC_Y_OFFSET -1, color, (uint32_t)(tft_height*tft_width));
+}
+
+//==================================
+void TFT_fillWindow(color_t color) {
+ TFT_pushColorRep(tft_dispWin.x1, tft_dispWin.y1, tft_dispWin.x2, tft_dispWin.y2,
+ color, (uint32_t)((tft_dispWin.x2-tft_dispWin.x1+1) * (tft_dispWin.y2-tft_dispWin.y1+1)));
+}
+
+// ^^^============= Basics drawing functions ================================^^^
+
+
+// ================ Graphics drawing functions ==================================
+
+//-----------------------------------------------------------------------------------
+static void _drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
+ _drawFastHLine(x1,y1,w, color);
+ _drawFastVLine(x1+w-1,y1,h, color);
+ _drawFastHLine(x1,y1+h-1,w, color);
+ _drawFastVLine(x1,y1,h, color);
+}
+
+//===============================================================================
+void TFT_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color) {
+ _drawRect(x1+tft_dispWin.x1, y1+tft_dispWin.y1, w, h, color);
+}
+
+//-------------------------------------------------------------------------------------------------
+static void drawCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, color_t color)
+{
+ int16_t f = 1 - r;
+ int16_t ddF_x = 1;
+ int16_t ddF_y = -2 * r;
+ int16_t x = 0;
+ int16_t y = r;
+
+ disp_select();
+ while (x < y) {
+ if (f >= 0) {
+ y--;
+ ddF_y += 2;
+ f += ddF_y;
+ }
+ x++;
+ ddF_x += 2;
+ f += ddF_x;
+ if (cornername & 0x4) {
+ _drawPixel(x0 + x, y0 + y, color, 0);
+ _drawPixel(x0 + y, y0 + x, color, 0);
+ }
+ if (cornername & 0x2) {
+ _drawPixel(x0 + x, y0 - y, color, 0);
+ _drawPixel(x0 + y, y0 - x, color, 0);
+ }
+ if (cornername & 0x8) {
+ _drawPixel(x0 - y, y0 + x, color, 0);
+ _drawPixel(x0 - x, y0 + y, color, 0);
+ }
+ if (cornername & 0x1) {
+ _drawPixel(x0 - y, y0 - x, color, 0);
+ _drawPixel(x0 - x, y0 - y, color, 0);
+ }
+ }
+ disp_deselect();
+}
+
+// Used to do circles and roundrects
+//----------------------------------------------------------------------------------------------------------------
+static void fillCircleHelper(int16_t x0, int16_t y0, int16_t r, uint8_t cornername, int16_t delta, color_t color)
+{
+ int16_t f = 1 - r;
+ int16_t ddF_x = 1;
+ int16_t ddF_y = -2 * r;
+ int16_t x = 0;
+ int16_t y = r;
+ int16_t ylm = x0 - r;
+
+ while (x < y) {
+ if (f >= 0) {
+ if (cornername & 0x1) _drawFastVLine(x0 + y, y0 - x, 2 * x + 1 + delta, color);
+ if (cornername & 0x2) _drawFastVLine(x0 - y, y0 - x, 2 * x + 1 + delta, color);
+ ylm = x0 - y;
+ y--;
+ ddF_y += 2;
+ f += ddF_y;
+ }
+ x++;
+ ddF_x += 2;
+ f += ddF_x;
+
+ if ((x0 - x) > ylm) {
+ if (cornername & 0x1) _drawFastVLine(x0 + x, y0 - y, 2 * y + 1 + delta, color);
+ if (cornername & 0x2) _drawFastVLine(x0 - x, y0 - y, 2 * y + 1 + delta, color);
+ }
+ }
+}
+
+// Draw a rounded rectangle
+//=============================================================================================
+void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
+{
+ x += tft_dispWin.x1;
+ y += tft_dispWin.y1;
+
+ // smarter version
+ _drawFastHLine(x + r, y, w - 2 * r, color); // Top
+ _drawFastHLine(x + r, y + h - 1, w - 2 * r, color); // Bottom
+ _drawFastVLine(x, y + r, h - 2 * r, color); // Left
+ _drawFastVLine(x + w - 1, y + r, h - 2 * r, color); // Right
+
+ // draw four corners
+ drawCircleHelper(x + r, y + r, r, 1, color);
+ drawCircleHelper(x + w - r - 1, y + r, r, 2, color);
+ drawCircleHelper(x + w - r - 1, y + h - r - 1, r, 4, color);
+ drawCircleHelper(x + r, y + h - r - 1, r, 8, color);
+}
+
+// Fill a rounded rectangle
+//=============================================================================================
+void TFT_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color)
+{
+ x += tft_dispWin.x1;
+ y += tft_dispWin.y1;
+
+ // smarter version
+ _fillRect(x + r, y, w - 2 * r, h, color);
+
+ // draw four corners
+ fillCircleHelper(x + w - r - 1, y + r, r, 1, h - 2 * r - 1, color);
+ fillCircleHelper(x + r, y + r, r, 2, h - 2 * r - 1, color);
+}
+
+
+
+
+//-----------------------------------------------------------------------------------------------
+static void _drawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t length, color_t color)
+{
+ _drawLine(
+ x,
+ y,
+ x + length * cos((angle + tft_angleOffset) * DEG_TO_RAD),
+ y + length * sin((angle + tft_angleOffset) * DEG_TO_RAD), color);
+}
+
+//---------------------------------------------------------------------------------------------------------------
+static void _DrawLineByAngle(int16_t x, int16_t y, int16_t angle, uint16_t start, uint16_t length, color_t color)
+{
+ _drawLine(
+ x + start * cos((angle + tft_angleOffset) * DEG_TO_RAD),
+ y + start * sin((angle + tft_angleOffset) * DEG_TO_RAD),
+ x + (start + length) * cos((angle + tft_angleOffset) * DEG_TO_RAD),
+ y + (start + length) * sin((angle + tft_angleOffset) * DEG_TO_RAD), color);
+}
+
+//===========================================================================================================
+void TFT_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color)
+{
+ x += tft_dispWin.x1;
+ y += tft_dispWin.y1;
+
+ if (start == 0) _drawLineByAngle(x, y, angle, len, color);
+ else _DrawLineByAngle(x, y, angle, start, len, color);
+}
+
+
+// Draw a triangle
+//--------------------------------------------------------------------------------------------------------------------
+static void _drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
+{
+ _drawLine(x0, y0, x1, y1, color);
+ _drawLine(x1, y1, x2, y2, color);
+ _drawLine(x2, y2, x0, y0, color);
+}
+
+//================================================================================================================
+void TFT_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
+{
+ x0 += tft_dispWin.x1;
+ y0 += tft_dispWin.y1;
+ x1 += tft_dispWin.x1;
+ y1 += tft_dispWin.y1;
+ x2 += tft_dispWin.x1;
+ y2 += tft_dispWin.y1;
+
+ _drawLine(x0, y0, x1, y1, color);
+ _drawLine(x1, y1, x2, y2, color);
+ _drawLine(x2, y2, x0, y0, color);
+}
+
+// Fill a triangle
+//--------------------------------------------------------------------------------------------------------------------
+static void _fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
+{
+ int16_t a, b, y, last;
+
+ // Sort coordinates by Y order (y2 >= y1 >= y0)
+ if (y0 > y1) {
+ swap(y0, y1); swap(x0, x1);
+ }
+ if (y1 > y2) {
+ swap(y2, y1); swap(x2, x1);
+ }
+ if (y0 > y1) {
+ swap(y0, y1); swap(x0, x1);
+ }
+
+ if(y0 == y2) { // Handle awkward all-on-same-line case as its own thing
+ a = b = x0;
+ if(x1 < a) a = x1;
+ else if(x1 > b) b = x1;
+ if(x2 < a) a = x2;
+ else if(x2 > b) b = x2;
+ _drawFastHLine(a, y0, b-a+1, color);
+ return;
+ }
+
+ int16_t
+ dx01 = x1 - x0,
+ dy01 = y1 - y0,
+ dx02 = x2 - x0,
+ dy02 = y2 - y0,
+ dx12 = x2 - x1,
+ dy12 = y2 - y1;
+ int32_t
+ sa = 0,
+ sb = 0;
+
+ // For upper part of triangle, find scanline crossings for segments
+ // 0-1 and 0-2. If y1=y2 (flat-bottomed triangle), the scanline y1
+ // is included here (and second loop will be skipped, avoiding a /0
+ // error there), otherwise scanline y1 is skipped here and handled
+ // in the second loop...which also avoids a /0 error here if y0=y1
+ // (flat-topped triangle).
+ if(y1 == y2) last = y1; // Include y1 scanline
+ else last = y1-1; // Skip it
+
+ for(y=y0; y<=last; y++) {
+ a = x0 + sa / dy01;
+ b = x0 + sb / dy02;
+ sa += dx01;
+ sb += dx02;
+ /* longhand:
+ a = x0 + (x1 - x0) * (y - y0) / (y1 - y0);
+ b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
+ */
+ if(a > b) swap(a,b);
+ _drawFastHLine(a, y, b-a+1, color);
+ }
+
+ // For lower part of triangle, find scanline crossings for segments
+ // 0-2 and 1-2. This loop is skipped if y1=y2.
+ sa = dx12 * (y - y1);
+ sb = dx02 * (y - y0);
+ for(; y<=y2; y++) {
+ a = x1 + sa / dy12;
+ b = x0 + sb / dy02;
+ sa += dx12;
+ sb += dx02;
+ /* longhand:
+ a = x1 + (x2 - x1) * (y - y1) / (y2 - y1);
+ b = x0 + (x2 - x0) * (y - y0) / (y2 - y0);
+ */
+ if(a > b) swap(a,b);
+ _drawFastHLine(a, y, b-a+1, color);
+ }
+}
+
+//================================================================================================================
+void TFT_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color)
+{
+ _fillTriangle(
+ x0 + tft_dispWin.x1, y0 + tft_dispWin.y1,
+ x1 + tft_dispWin.x1, y1 + tft_dispWin.y1,
+ x2 + tft_dispWin.x1, y2 + tft_dispWin.y1,
+ color);
+}
+
+//====================================================================
+void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color) {
+ x += tft_dispWin.x1;
+ y += tft_dispWin.y1;
+ int f = 1 - radius;
+ int ddF_x = 1;
+ int ddF_y = -2 * radius;
+ int x1 = 0;
+ int y1 = radius;
+
+ disp_select();
+ _drawPixel(x, y + radius, color, 0);
+ _drawPixel(x, y - radius, color, 0);
+ _drawPixel(x + radius, y, color, 0);
+ _drawPixel(x - radius, y, color, 0);
+ while(x1 < y1) {
+ if (f >= 0) {
+ y1--;
+ ddF_y += 2;
+ f += ddF_y;
+ }
+ x1++;
+ ddF_x += 2;
+ f += ddF_x;
+ _drawPixel(x + x1, y + y1, color, 0);
+ _drawPixel(x - x1, y + y1, color, 0);
+ _drawPixel(x + x1, y - y1, color, 0);
+ _drawPixel(x - x1, y - y1, color, 0);
+ _drawPixel(x + y1, y + x1, color, 0);
+ _drawPixel(x - y1, y + x1, color, 0);
+ _drawPixel(x + y1, y - x1, color, 0);
+ _drawPixel(x - y1, y - x1, color, 0);
+ }
+ disp_deselect();
+}
+
+//====================================================================
+void TFT_fillCircle(int16_t x, int16_t y, int radius, color_t color) {
+ x += tft_dispWin.x1;
+ y += tft_dispWin.y1;
+
+ _drawFastVLine(x, y-radius, 2*radius+1, color);
+ fillCircleHelper(x, y, radius, 3, 0, color);
+}
+
+//----------------------------------------------------------------------------------------------------------------
+static void _draw_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
+{
+ disp_select();
+ // upper right
+ if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawPixel(x0 + x, y0 - y, color, 0);
+ // upper left
+ if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawPixel(x0 - x, y0 - y, color, 0);
+ // lower right
+ if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawPixel(x0 + x, y0 + y, color, 0);
+ // lower left
+ if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawPixel(x0 - x, y0 + y, color, 0);
+ disp_deselect();
+}
+
+//=====================================================================================================
+void TFT_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
+{
+ x0 += tft_dispWin.x1;
+ y0 += tft_dispWin.y1;
+
+ uint16_t x, y;
+ int32_t xchg, ychg;
+ int32_t err;
+ int32_t rxrx2;
+ int32_t ryry2;
+ int32_t stopx, stopy;
+
+ rxrx2 = rx;
+ rxrx2 *= rx;
+ rxrx2 *= 2;
+
+ ryry2 = ry;
+ ryry2 *= ry;
+ ryry2 *= 2;
+
+ x = rx;
+ y = 0;
+
+ xchg = 1;
+ xchg -= rx;
+ xchg -= rx;
+ xchg *= ry;
+ xchg *= ry;
+
+ ychg = rx;
+ ychg *= rx;
+
+ err = 0;
+
+ stopx = ryry2;
+ stopx *= rx;
+ stopy = 0;
+
+ while( stopx >= stopy ) {
+ _draw_ellipse_section(x, y, x0, y0, color, option);
+ y++;
+ stopy += rxrx2;
+ err += ychg;
+ ychg += rxrx2;
+ if ( 2*err+xchg > 0 ) {
+ x--;
+ stopx -= ryry2;
+ err += xchg;
+ xchg += ryry2;
+ }
+ }
+
+ x = 0;
+ y = ry;
+
+ xchg = ry;
+ xchg *= ry;
+
+ ychg = 1;
+ ychg -= ry;
+ ychg -= ry;
+ ychg *= rx;
+ ychg *= rx;
+
+ err = 0;
+
+ stopx = 0;
+
+ stopy = rxrx2;
+ stopy *= ry;
+
+ while( stopx <= stopy ) {
+ _draw_ellipse_section(x, y, x0, y0, color, option);
+ x++;
+ stopx += ryry2;
+ err += xchg;
+ xchg += ryry2;
+ if ( 2*err+ychg > 0 ) {
+ y--;
+ stopy -= rxrx2;
+ err += ychg;
+ ychg += rxrx2;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------------------------------------------------
+static void _draw_filled_ellipse_section(uint16_t x, uint16_t y, uint16_t x0, uint16_t y0, color_t color, uint8_t option)
+{
+ // upper right
+ if ( option & TFT_ELLIPSE_UPPER_RIGHT ) _drawFastVLine(x0+x, y0-y, y+1, color);
+ // upper left
+ if ( option & TFT_ELLIPSE_UPPER_LEFT ) _drawFastVLine(x0-x, y0-y, y+1, color);
+ // lower right
+ if ( option & TFT_ELLIPSE_LOWER_RIGHT ) _drawFastVLine(x0+x, y0, y+1, color);
+ // lower left
+ if ( option & TFT_ELLIPSE_LOWER_LEFT ) _drawFastVLine(x0-x, y0, y+1, color);
+}
+
+//=====================================================================================================
+void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option)
+{
+ x0 += tft_dispWin.x1;
+ y0 += tft_dispWin.y1;
+
+ uint16_t x, y;
+ int32_t xchg, ychg;
+ int32_t err;
+ int32_t rxrx2;
+ int32_t ryry2;
+ int32_t stopx, stopy;
+
+ rxrx2 = rx;
+ rxrx2 *= rx;
+ rxrx2 *= 2;
+
+ ryry2 = ry;
+ ryry2 *= ry;
+ ryry2 *= 2;
+
+ x = rx;
+ y = 0;
+
+ xchg = 1;
+ xchg -= rx;
+ xchg -= rx;
+ xchg *= ry;
+ xchg *= ry;
+
+ ychg = rx;
+ ychg *= rx;
+
+ err = 0;
+
+ stopx = ryry2;
+ stopx *= rx;
+ stopy = 0;
+
+ while( stopx >= stopy ) {
+ _draw_filled_ellipse_section(x, y, x0, y0, color, option);
+ y++;
+ stopy += rxrx2;
+ err += ychg;
+ ychg += rxrx2;
+ if ( 2*err+xchg > 0 ) {
+ x--;
+ stopx -= ryry2;
+ err += xchg;
+ xchg += ryry2;
+ }
+ }
+
+ x = 0;
+ y = ry;
+
+ xchg = ry;
+ xchg *= ry;
+
+ ychg = 1;
+ ychg -= ry;
+ ychg -= ry;
+ ychg *= rx;
+ ychg *= rx;
+
+ err = 0;
+
+ stopx = 0;
+
+ stopy = rxrx2;
+ stopy *= ry;
+
+ while( stopx <= stopy ) {
+ _draw_filled_ellipse_section(x, y, x0, y0, color, option);
+ x++;
+ stopx += ryry2;
+ err += xchg;
+ xchg += ryry2;
+ if ( 2*err+ychg > 0 ) {
+ y--;
+ stopy -= rxrx2;
+ err += ychg;
+ ychg += rxrx2;
+ }
+ }
+}
+
+
+// ==== ARC DRAWING ===================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------------
+static void _fillArcOffsetted(uint16_t cx, uint16_t cy, uint16_t radius, uint16_t thickness, float start, float end, color_t color)
+{
+ //float sslope = (float)cos_lookup(start) / (float)sin_lookup(start);
+ //float eslope = (float)cos_lookup(end) / (float)sin_lookup(end);
+ float sslope = (cos(start/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(start/_arcAngleMax * 2 * PI) * _arcAngleMax) ;
+ float eslope = (cos(end/_arcAngleMax * 2 * PI) * _arcAngleMax) / (sin(end/_arcAngleMax * 2 * PI) * _arcAngleMax);
+
+ if (end == 360) eslope = -1000000;
+
+ int ir2 = (radius - thickness) * (radius - thickness);
+ int or2 = radius * radius;
+
+ disp_select();
+ for (int x = -radius; x <= radius; x++) {
+ for (int y = -radius; y <= radius; y++) {
+ int x2 = x * x;
+ int y2 = y * y;
+
+ if (
+ (x2 + y2 < or2 && x2 + y2 >= ir2) &&
+ (
+ (y > 0 && start < 180 && x <= y * sslope) ||
+ (y < 0 && start > 180 && x >= y * sslope) ||
+ (y < 0 && start <= 180) ||
+ (y == 0 && start <= 180 && x < 0) ||
+ (y == 0 && start == 0 && x > 0)
+ ) &&
+ (
+ (y > 0 && end < 180 && x >= y * eslope) ||
+ (y < 0 && end > 180 && x <= y * eslope) ||
+ (y > 0 && end >= 180) ||
+ (y == 0 && end >= 180 && x < 0) ||
+ (y == 0 && start == 0 && x > 0)
+ )
+ )
+ _drawPixel(cx+x, cy+y, color, 0);
+ }
+ }
+ disp_deselect();
+}
+
+
+//===========================================================================================================================
+void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor)
+{
+ cx += tft_dispWin.x1;
+ cy += tft_dispWin.y1;
+
+ if (th < 1) th = 1;
+ if (th > r) th = r;
+
+ int f = TFT_compare_colors(fillcolor, color);
+
+ float astart = fmodf(start, _arcAngleMax);
+ float aend = fmodf(end, _arcAngleMax);
+
+ astart += tft_angleOffset;
+ aend += tft_angleOffset;
+
+ if (astart < 0) astart += (float)360;
+ if (aend < 0) aend += (float)360;
+
+ if (aend == 0) aend = (float)360;
+
+ if (astart > aend) {
+ _fillArcOffsetted(cx, cy, r, th, astart, _arcAngleMax, fillcolor);
+ _fillArcOffsetted(cx, cy, r, th, 0, aend, fillcolor);
+ if (f) {
+ _fillArcOffsetted(cx, cy, r, 1, astart, _arcAngleMax, color);
+ _fillArcOffsetted(cx, cy, r, 1, 0, aend, color);
+ _fillArcOffsetted(cx, cy, r-th, 1, astart, _arcAngleMax, color);
+ _fillArcOffsetted(cx, cy, r-th, 1, 0, aend, color);
+ }
+ }
+ else {
+ _fillArcOffsetted(cx, cy, r, th, astart, aend, fillcolor);
+ if (f) {
+ _fillArcOffsetted(cx, cy, r, 1, astart, aend, color);
+ _fillArcOffsetted(cx, cy, r-th, 1, astart, aend, color);
+ }
+ }
+ if (f) {
+ _drawLine(cx + (r-th) * cos(astart * DEG_TO_RAD), cy + (r-th) * sin(astart * DEG_TO_RAD),
+ cx + (r-1) * cos(astart * DEG_TO_RAD), cy + (r-1) * sin(astart * DEG_TO_RAD), color);
+ _drawLine(cx + (r-th) * cos(aend * DEG_TO_RAD), cy + (r-th) * sin(aend * DEG_TO_RAD),
+ cx + (r-1) * cos(aend * DEG_TO_RAD), cy + (r-1) * sin(aend * DEG_TO_RAD), color);
+ }
+}
+
+//=============================================================================================================
+void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int rot, uint8_t th)
+{
+ cx += tft_dispWin.x1;
+ cy += tft_dispWin.y1;
+
+ int deg = rot - tft_angleOffset;
+ int f = TFT_compare_colors(fill, color);
+
+ if (sides < MIN_POLIGON_SIDES) sides = MIN_POLIGON_SIDES; // This ensures the minimum side number
+ if (sides > MAX_POLIGON_SIDES) sides = MAX_POLIGON_SIDES; // This ensures the maximum side number
+
+ int Xpoints[sides], Ypoints[sides]; // Set the arrays based on the number of sides entered
+ int rads = 360 / sides; // This equally spaces the points.
+
+ for (int idx = 0; idx < sides; idx++) {
+ Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * diameter;
+ Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * diameter;
+ }
+
+ // Draw the polygon on the screen.
+ if (f) {
+ for(int idx = 0; idx < sides; idx++) {
+ if((idx+1) < sides) _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], fill);
+ else _fillTriangle(cx,cy,Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], fill);
+ }
+ }
+
+ if (th) {
+ for (int n=0; n 0) {
+ for (int idx = 0; idx < sides; idx++) {
+ Xpoints[idx] = cx + sin((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
+ Ypoints[idx] = cy + cos((float)(idx*rads + deg) * deg_to_rad) * (diameter-n);
+ }
+ }
+ for(int idx = 0; idx < sides; idx++) {
+ if( (idx+1) < sides)
+ _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[idx+1],Ypoints[idx+1], color); // draw the lines
+ else
+ _drawLine(Xpoints[idx],Ypoints[idx],Xpoints[0],Ypoints[0], color); // finishes the last line to close up the polygon.
+ }
+ }
+ }
+}
+
+/*
+// Similar to the Polygon function.
+//=====================================================================================
+void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor)
+{
+ cx += tft_dispWin.x1;
+ cy += tft_dispWin.y1;
+
+ factor = constrain(factor, 1.0, 4.0);
+ uint8_t sides = 5;
+ uint8_t rads = 360 / sides;
+
+ int Xpoints_O[sides], Ypoints_O[sides], Xpoints_I[sides], Ypoints_I[sides];//Xpoints_T[5], Ypoints_T[5];
+
+ for(int idx = 0; idx < sides; idx++) {
+ // makes the outer points
+ Xpoints_O[idx] = cx + sin((float)(idx*rads + 72) * deg_to_rad) * diameter;
+ Ypoints_O[idx] = cy + cos((float)(idx*rads + 72) * deg_to_rad) * diameter;
+ // makes the inner points
+ Xpoints_I[idx] = cx + sin((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
+ // 36 is half of 72, and this will allow the inner and outer points to line up like a triangle.
+ Ypoints_I[idx] = cy + cos((float)(idx*rads + 36) * deg_to_rad) * ((float)(diameter)/factor);
+ }
+
+ for(int idx = 0; idx < sides; idx++) {
+ if((idx+1) < sides) {
+ if(fill) {// this part below should be self explanatory. It fills in the star.
+ _fillTriangle(cx,cy,Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
+ _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
+ }
+ else {
+ _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx+1],Ypoints_I[idx+1], color);
+ _drawLine(Xpoints_I[idx],Ypoints_I[idx],Xpoints_O[idx],Ypoints_O[idx], color);
+ }
+ }
+ else {
+ if(fill) {
+ _fillTriangle(cx,cy,Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
+ _fillTriangle(cx,cy,Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
+ }
+ else {
+ _drawLine(Xpoints_O[idx],Ypoints_O[idx],Xpoints_I[idx],Ypoints_I[idx], color);
+ _drawLine(Xpoints_I[0],Ypoints_I[0],Xpoints_O[idx],Ypoints_O[idx], color);
+ }
+ }
+ }
+}
+*/
+
+// ================ Font and string functions ==================================
+
+//--------------------------------------------------------
+static int load_file_font(const char * fontfile, int info)
+{
+ int err = 0;
+ char err_msg[256] = {'\0'};
+
+ if (userfont != NULL) {
+ free(userfont);
+ userfont = NULL;
+ }
+
+ struct stat sb;
+
+ // Open the file
+ FILE *fhndl = fopen(fontfile, "r");
+ if (!fhndl) {
+ sprintf(err_msg, "Error opening font file '%s'", fontfile);
+ err = 1;
+ goto exit;
+ }
+
+ // Get file size
+ if (stat(fontfile, &sb) != 0) {
+ sprintf(err_msg, "Error getting font file size");
+ err = 2;
+ goto exit;
+ }
+ int fsize = sb.st_size;
+ if (fsize < 30) {
+ sprintf(err_msg, "Error getting font file size");
+ err = 3;
+ goto exit;
+ }
+
+ userfont = malloc(fsize+4);
+ if (userfont == NULL) {
+ sprintf(err_msg, "Font memory allocation error");
+ fclose(fhndl);
+ err = 4;
+ goto exit;
+ }
+
+ int read = fread(userfont, 1, fsize, fhndl);
+
+ fclose(fhndl);
+
+ if (read != fsize) {
+ sprintf(err_msg, "Font read error");
+ err = 5;
+ goto exit;
+ }
+
+ userfont[read] = 0;
+ if (strstr((char *)(userfont+read-8), "RPH_font") == NULL) {
+ sprintf(err_msg, "Font ID not found");
+ err = 6;
+ goto exit;
+ }
+
+ // Check size
+ int size = 0;
+ int numchar = 0;
+ int width = userfont[0];
+ int height = userfont[1];
+ uint8_t first = 255;
+ uint8_t last = 0;
+ //int offst = 0;
+ int pminwidth = 255;
+ int pmaxwidth = 0;
+
+ if (width != 0) {
+ // Fixed font
+ numchar = userfont[3];
+ first = userfont[2];
+ last = first + numchar - 1;
+ size = ((width * height * numchar) / 8) + 4;
+ }
+ else {
+ // Proportional font
+ size = 4; // point at first char data
+ uint8_t charCode;
+ int charwidth;
+
+ do {
+ charCode = userfont[size];
+ charwidth = userfont[size+2];
+
+ if (charCode != 0xFF) {
+ numchar++;
+ if (charwidth != 0) size += ((((charwidth * userfont[size+3])-1) / 8) + 7);
+ else size += 6;
+
+ if (info) {
+ if (charwidth > pmaxwidth) pmaxwidth = charwidth;
+ if (charwidth < pminwidth) pminwidth = charwidth;
+ if (charCode < first) first = charCode;
+ if (charCode > last) last = charCode;
+ }
+ }
+ else size++;
+ } while ((size < (read-8)) && (charCode != 0xFF));
+ }
+
+ if (size != (read-8)) {
+ sprintf(err_msg, "Font size error: found %d expected %d)", size, (read-8));
+ err = 7;
+ goto exit;
+ }
+
+ if (info) {
+ if (width != 0) {
+ printf("Fixed width font:\r\n size: %d width: %d height: %d characters: %d (%d~%d)\n",
+ size, width, height, numchar, first, last);
+ }
+ else {
+ printf("Proportional font:\r\n size: %d width: %d~%d height: %d characters: %d (%d~%d)\n",
+ size, pminwidth, pmaxwidth, height, numchar, first, last);
+ }
+ }
+
+exit:
+ if (err) {
+ if (userfont) {
+ free(userfont);
+ userfont = NULL;
+ }
+ if (info) printf("Error: %d [%s]\r\n", err, err_msg);
+ }
+ return err;
+}
+
+//------------------------------------------------
+int compile_font_file(char *fontfile, uint8_t dbg)
+{
+ int err = 0;
+ char err_msg[128] = {'\0'};
+ char outfile[128] = {'\0'};
+ size_t len;
+ struct stat sb;
+ FILE *ffd = NULL;
+ FILE *ffd_out = NULL;
+ char *sourcebuf = NULL;
+
+ len = strlen(fontfile);
+
+ // check here that filename end with ".c".
+ if ((len < 3) || (len > 125) || (strcmp(fontfile + len - 2, ".c") != 0)) {
+ sprintf(err_msg, "not a .c file");
+ err = 1;
+ goto exit;
+ }
+
+ sprintf(outfile, "%s", fontfile);
+ sprintf(outfile+strlen(outfile)-1, "fon");
+
+ // Open the source file
+ if (stat(fontfile, &sb) != 0) {
+ sprintf(err_msg, "Error opening source file '%s'", fontfile);
+ err = 2;
+ goto exit;
+ }
+ // Open the file
+ ffd = fopen(fontfile, "rb");
+ if (!ffd) {
+ sprintf(err_msg, "Error opening source file '%s'", fontfile);
+ err = 3;
+ goto exit;
+ }
+
+ // Open the font file
+ ffd_out= fopen(outfile, "wb");
+ if (!ffd_out) {
+ sprintf(err_msg, "error opening destination file");
+ err = 4;
+ goto exit;
+ }
+
+ // Get file size
+ int fsize = sb.st_size;
+ if (fsize <= 0) {
+ sprintf(err_msg, "source file size error");
+ err = 5;
+ goto exit;
+ }
+
+ sourcebuf = malloc(fsize+4);
+ if (sourcebuf == NULL) {
+ sprintf(err_msg, "memory allocation error");
+ err = 6;
+ goto exit;
+ }
+ char *fbuf = sourcebuf;
+
+ int rdsize = fread(fbuf, 1, fsize, ffd);
+ fclose(ffd);
+ ffd = NULL;
+
+ if (rdsize != fsize) {
+ sprintf(err_msg, "error reading from source file");
+ err = 7;
+ goto exit;
+ }
+
+ *(fbuf+rdsize) = '\0';
+
+ fbuf = strchr(fbuf, '{'); // beginning of font data
+ char *fend = strstr(fbuf, "};"); // end of font data
+
+ if ((fbuf == NULL) || (fend == NULL) || ((fend-fbuf) < 22)) {
+ sprintf(err_msg, "wrong source file format");
+ err = 8;
+ goto exit;
+ }
+
+ fbuf++;
+ *fend = '\0';
+ char hexstr[5] = {'\0'};
+ int lastline = 0;
+
+ fbuf = strstr(fbuf, "0x");
+ int size = 0;
+ char *nextline;
+ char *numptr;
+
+ int bptr = 0;
+
+ while ((fbuf != NULL) && (fbuf < fend) && (lastline == 0)) {
+ nextline = strchr(fbuf, '\n'); // beginning of the next line
+ if (nextline == NULL) {
+ nextline = fend-1;
+ lastline++;
+ }
+ else nextline++;
+
+ while (fbuf < nextline) {
+ numptr = strstr(fbuf, "0x");
+ if ((numptr == NULL) || ((fbuf+4) > nextline)) numptr = strstr(fbuf, "0X");
+ if ((numptr != NULL) && ((numptr+4) <= nextline)) {
+ fbuf = numptr;
+ if (bptr >= 128) {
+ // buffer full, write to file
+ if (fwrite(outfile, 1, 128, ffd_out) != 128) goto error;
+ bptr = 0;
+ size += 128;
+ }
+ memcpy(hexstr, fbuf, 4);
+ hexstr[4] = 0;
+ outfile[bptr++] = (uint8_t)strtol(hexstr, NULL, 0);
+ fbuf += 4;
+ }
+ else fbuf = nextline;
+ }
+ fbuf = nextline;
+ }
+
+ if (bptr > 0) {
+ size += bptr;
+ if (fwrite(outfile, 1, bptr, ffd_out) != bptr) goto error;
+ }
+
+ // write font ID
+ sprintf(outfile, "RPH_font");
+ if (fwrite(outfile, 1, 8, ffd_out) != 8) goto error;
+
+ fclose(ffd_out);
+ ffd_out = NULL;
+
+ // === Test compiled font ===
+ sprintf(outfile, "%s", fontfile);
+ sprintf(outfile+strlen(outfile)-1, "fon");
+
+ uint8_t *uf = userfont; // save userfont pointer
+ userfont = NULL;
+ if (load_file_font(outfile, 1) != 0) {
+ sprintf(err_msg, "Error compiling file!");
+ err = 10;
+ }
+ else {
+ free(userfont);
+ sprintf(err_msg, "File compiled successfully.");
+ }
+ userfont = uf; // restore userfont
+
+ goto exit;
+
+error:
+ sprintf(err_msg, "error writing to destination file");
+ err = 9;
+
+exit:
+ if (sourcebuf) free(sourcebuf);
+ if (ffd) fclose(ffd);
+ if (ffd_out) fclose(ffd_out);
+
+ if (dbg) printf("%s\r\n", err_msg);
+
+ return err;
+}
+
+
+// -----------------------------------------------------------------------------------------
+// Individual Proportional Font Character Format:
+// -----------------------------------------------------------------------------------------
+// Character Code
+// yOffset (start Y of visible pixels)
+// Width (width of the visible pixels)
+// Height (height of the visible pixels)
+// xOffset (start X of visible pixels)
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+// -----------------------------------------------------------------------------------------
+
+//---------------------------------------------------------------------------------------------
+// Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1)
+// Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
+//---------------------------------------------------------------------------------------------
+
+//----------------------------------
+void getFontCharacters(uint8_t *buf)
+{
+ if (tft_cfont.bitmap == 2) {
+ //For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
+ for (uint8_t n=0; n < 11; n++) {
+ buf[n] = n + 0x30;
+ }
+ buf[11] = '.';
+ buf[12] = '-';
+ buf[13] = '/';
+ buf[14] = '\0';
+ return;
+ }
+
+ if (tft_cfont.x_size > 0) {
+ for (uint8_t n=0; n < tft_cfont.numchars; n++) {
+ buf[n] = tft_cfont.offset + n;
+ }
+ buf[tft_cfont.numchars] = '\0';
+ return;
+ }
+
+ uint16_t tempPtr = 4; // point at first char data
+ uint8_t cc, cw, ch, n;
+
+ n = 0;
+ cc = tft_cfont.font[tempPtr++];
+ while (cc != 0xFF) {
+ tft_cfont.numchars++;
+ tempPtr++;
+ cw = tft_cfont.font[tempPtr++];
+ ch = tft_cfont.font[tempPtr++];
+ tempPtr++;
+ tempPtr++;
+ if (cw != 0) {
+ // packed bits
+ tempPtr += (((cw * ch)-1) / 8) + 1;
+ }
+ buf[n++] = cc;
+ cc = tft_cfont.font[tempPtr++];
+ }
+ buf[n] = '\0';
+}
+
+// Set max width & height of the proportional font
+//-----------------------------
+static void getMaxWidthHeight()
+{
+ uint16_t tempPtr = 4; // point at first char data
+ uint8_t cc, cw, ch, cd, cy;
+
+ tft_cfont.numchars = 0;
+ tft_cfont.max_x_size = 0;
+
+ cc = tft_cfont.font[tempPtr++];
+ while (cc != 0xFF) {
+ tft_cfont.numchars++;
+ cy = tft_cfont.font[tempPtr++];
+ cw = tft_cfont.font[tempPtr++];
+ ch = tft_cfont.font[tempPtr++];
+ tempPtr++;
+ cd = tft_cfont.font[tempPtr++];
+ cy += ch;
+ if (cw > tft_cfont.max_x_size) tft_cfont.max_x_size = cw;
+ if (cd > tft_cfont.max_x_size) tft_cfont.max_x_size = cd;
+ if (ch > tft_cfont.y_size) tft_cfont.y_size = ch;
+ if (cy > tft_cfont.y_size) tft_cfont.y_size = cy;
+ if (cw != 0) {
+ // packed bits
+ tempPtr += (((cw * ch)-1) / 8) + 1;
+ }
+ cc = tft_cfont.font[tempPtr++];
+ }
+ tft_cfont.size = tempPtr;
+}
+
+// Return the Glyph data for an individual character in the proportional font
+//------------------------------------
+static uint8_t getCharPtr(uint8_t c) {
+ uint16_t tempPtr = 4; // point at first char data
+
+ do {
+ fontChar.charCode = tft_cfont.font[tempPtr++];
+ if (fontChar.charCode == 0xFF) return 0;
+
+ fontChar.adjYOffset = tft_cfont.font[tempPtr++];
+ fontChar.width = tft_cfont.font[tempPtr++];
+ fontChar.height = tft_cfont.font[tempPtr++];
+ fontChar.xOffset = tft_cfont.font[tempPtr++];
+ fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
+ fontChar.xDelta = tft_cfont.font[tempPtr++];
+
+ if (c != fontChar.charCode && fontChar.charCode != 0xFF) {
+ if (fontChar.width != 0) {
+ // packed bits
+ tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
+ }
+ }
+ } while ((c != fontChar.charCode) && (fontChar.charCode != 0xFF));
+
+ fontChar.dataPtr = tempPtr;
+ if (c == fontChar.charCode) {
+ if (tft_font_forceFixed > 0) {
+ // fix width & offset for forced fixed width
+ fontChar.xDelta = tft_cfont.max_x_size;
+ fontChar.xOffset = (fontChar.xDelta - fontChar.width) / 2;
+ }
+ }
+ else return 0;
+
+ return 1;
+}
+
+/*
+//-----------------------
+static void _testFont() {
+ if (tft_cfont.x_size) {
+ printf("FONT TEST: fixed font\r\n");
+ return;
+ }
+ uint16_t tempPtr = 4; // point at first char data
+ uint8_t c = 0x20;
+ for (c=0x20; c <0xFF; c++) {
+ fontChar.charCode = tft_cfont.font[tempPtr++];
+ if (fontChar.charCode == 0xFF) break;
+ if (fontChar.charCode != c) {
+ printf("FONT TEST: last sequential char: %d, expected %d\r\n", fontChar.charCode, c);
+ break;
+ }
+ c = fontChar.charCode;
+ fontChar.adjYOffset = tft_cfont.font[tempPtr++];
+ fontChar.width = tft_cfont.font[tempPtr++];
+ fontChar.height = tft_cfont.font[tempPtr++];
+ fontChar.xOffset = tft_cfont.font[tempPtr++];
+ fontChar.xOffset = fontChar.xOffset < 0x80 ? fontChar.xOffset : -(0xFF - fontChar.xOffset);
+ fontChar.xDelta = tft_cfont.font[tempPtr++];
+
+ if (fontChar.charCode != 0xFF) {
+ if (fontChar.width != 0) {
+ // packed bits
+ tempPtr += (((fontChar.width * fontChar.height)-1) / 8) + 1;
+ }
+ }
+ }
+ printf("FONT TEST: W=%d H=%d last char: %d [%c]; length: %d\r\n", tft_cfont.max_x_size, tft_cfont.y_size, c, c, tempPtr);
+}
+*/
+
+//===================================================
+void TFT_setFont(uint8_t font, const char *font_file)
+{
+ tft_cfont.font = NULL;
+
+ if (font == FONT_7SEG) {
+ tft_cfont.bitmap = 2;
+ tft_cfont.x_size = 24;
+ tft_cfont.y_size = 6;
+ tft_cfont.offset = 0;
+ tft_cfont.color = tft_fg;
+ }
+ else {
+ if (font == USER_FONT) {
+ if (load_file_font(font_file, 0) != 0) tft_cfont.font = tft_DefaultFont;
+ else tft_cfont.font = userfont;
+ }
+ else if (font == DEJAVU18_FONT) tft_cfont.font = tft_Dejavu18;
+ else if (font == DEJAVU24_FONT) tft_cfont.font = tft_Dejavu24;
+ else if (font == UBUNTU16_FONT) tft_cfont.font = tft_Ubuntu16;
+ else if (font == COMIC24_FONT) tft_cfont.font = tft_Comic24;
+ else if (font == MINYA24_FONT) tft_cfont.font = tft_minya24;
+ else if (font == TOONEY32_FONT) tft_cfont.font = tft_tooney32;
+ else if (font == SMALL_FONT) tft_cfont.font = tft_SmallFont;
+ else if (font == DEF_SMALL_FONT) tft_cfont.font = tft_def_small;
+ else tft_cfont.font = tft_DefaultFont;
+
+ tft_cfont.bitmap = 1;
+ tft_cfont.x_size = tft_cfont.font[0];
+ tft_cfont.y_size = tft_cfont.font[1];
+ if (tft_cfont.x_size > 0) {
+ tft_cfont.offset = tft_cfont.font[2];
+ tft_cfont.numchars = tft_cfont.font[3];
+ tft_cfont.size = tft_cfont.x_size * tft_cfont.y_size * tft_cfont.numchars;
+ }
+ else {
+ tft_cfont.offset = 4;
+ getMaxWidthHeight();
+ }
+ //_testFont();
+ }
+}
+
+// -----------------------------------------------------------------------------------------
+// Individual Proportional Font Character Format:
+// -----------------------------------------------------------------------------------------
+// Character Code
+// yOffset (start Y of visible pixels)
+// Width (width of the visible pixels)
+// Height (height of the visible pixels)
+// xOffset (start X of visible pixels)
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+// -----------------------------------------------------------------------------------------
+//---------------------------------------------------------------------------------------------
+// Character drawing rectangle is (0, 0) (xDelta-1, tft_cfont.y_size-1)
+// Character visible pixels rectangle is (xOffset, yOffset) (xOffset+Width-1, yOffset+Height-1)
+//---------------------------------------------------------------------------------------------
+
+// print non-rotated proportional character
+// character is already in fontChar
+//----------------------------------------------
+static int printProportionalChar(int x, int y) {
+ uint8_t ch = 0;
+ int i, j, char_width;
+
+ char_width = ((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta);
+
+ if ((tft_font_buffered_char) && (!tft_font_transparent)) {
+ int len, bufPos;
+
+ // === buffer Glyph data for faster sending ===
+ len = char_width * tft_cfont.y_size;
+ color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
+ if (color_line) {
+ // fill with background color
+ for (int n = 0; n < len; n++) {
+ color_line[n] = tft_bg;
+ }
+ // set character pixels to foreground color
+ uint8_t mask = 0x80;
+ for (j=0; j < fontChar.height; j++) {
+ for (i=0; i < fontChar.width; i++) {
+ if (((i + (j*fontChar.width)) % 8) == 0) {
+ mask = 0x80;
+ ch = tft_cfont.font[fontChar.dataPtr++];
+ }
+ if ((ch & mask) != 0) {
+ // visible pixel
+ bufPos = ((j + fontChar.adjYOffset) * char_width) + (fontChar.xOffset + i); // bufY + bufX
+ color_line[bufPos] = tft_fg;
+ /*
+ bufY = (j + fontChar.adjYOffset) * char_width;
+ bufX = fontChar.xOffset + i;
+ if ((bufX < 0) || (bufX > char_width)) {
+ printf("[%c] X ERR: %d\r\n", fontChar.charCode, bufX);
+ }
+ bufPos = bufY + bufX;
+ if ((bufPos < len) && (bufPos > 0)) color_line[bufPos] = tft_fg;
+ else printf("[%c] ERR: %d > %d W=%d H=%d bufX=%d bufY=%d X=%d Y=%d\r\n",
+ fontChar.charCode, bufPos, len, char_width, tft_cfont.y_size, bufX, bufY, fontChar.xOffset + i, j + fontChar.adjYOffset);
+ */
+ }
+ mask >>= 1;
+ }
+ }
+ // send to display in one transaction
+ disp_select();
+ send_data(x, y, x+char_width-1, y+tft_cfont.y_size-1, len, color_line);
+ disp_deselect();
+ free(color_line);
+
+ return char_width;
+ }
+ }
+
+ int cx, cy;
+
+ if (!tft_font_transparent) _fillRect(x, y, char_width+1, tft_cfont.y_size, tft_bg);
+
+ // draw Glyph
+ uint8_t mask = 0x80;
+ disp_select();
+ for (j=0; j < fontChar.height; j++) {
+ for (i=0; i < fontChar.width; i++) {
+ if (((i + (j*fontChar.width)) % 8) == 0) {
+ mask = 0x80;
+ ch = tft_cfont.font[fontChar.dataPtr++];
+ }
+
+ if ((ch & mask) !=0) {
+ cx = (uint16_t)(x+fontChar.xOffset+i);
+ cy = (uint16_t)(y+j+fontChar.adjYOffset);
+ _drawPixel(cx, cy, tft_fg, 0);
+ }
+ mask >>= 1;
+ }
+ }
+ disp_deselect();
+
+ return char_width;
+}
+
+// non-rotated fixed width character
+//----------------------------------------------
+static void printChar(uint8_t c, int x, int y) {
+ uint8_t i, j, ch, fz, mask;
+ uint16_t k, temp, cx, cy, len;
+
+ // fz = bytes per char row
+ fz = tft_cfont.x_size/8;
+ if (tft_cfont.x_size % 8) fz++;
+
+ // get character position in buffer
+ temp = ((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4;
+
+ if ((tft_font_buffered_char) && (!tft_font_transparent)) {
+ // === buffer Glyph data for faster sending ===
+ len = tft_cfont.x_size * tft_cfont.y_size;
+ color_t *color_line = heap_caps_malloc(len*3, MALLOC_CAP_DMA);
+ if (color_line) {
+ // fill with background color
+ for (int n = 0; n < len; n++) {
+ color_line[n] = tft_bg;
+ }
+ // set character pixels to foreground color
+ for (j=0; j>= 1;
+ }
+ }
+ temp += (fz);
+ }
+ // send to display in one transaction
+ disp_select();
+ send_data(x, y, x+tft_cfont.x_size-1, y+tft_cfont.y_size-1, len, color_line);
+ disp_deselect();
+ free(color_line);
+
+ return;
+ }
+ }
+
+ if (!tft_font_transparent) _fillRect(x, y, tft_cfont.x_size, tft_cfont.y_size, tft_bg);
+
+ disp_select();
+ for (j=0; j>= 1;
+ }
+ }
+ temp += (fz);
+ }
+ disp_deselect();
+}
+
+// print rotated proportional character
+// character is already in fontChar
+//---------------------------------------------------
+static int rotatePropChar(int x, int y, int offset) {
+ uint8_t ch = 0;
+ double radian = tft_font_rotate * DEG_TO_RAD;
+ float cos_radian = cos(radian);
+ float sin_radian = sin(radian);
+
+ uint8_t mask = 0x80;
+ disp_select();
+ for (int j=0; j < fontChar.height; j++) {
+ for (int i=0; i < fontChar.width; i++) {
+ if (((i + (j*fontChar.width)) % 8) == 0) {
+ mask = 0x80;
+ ch = tft_cfont.font[fontChar.dataPtr++];
+ }
+
+ int newX = (int)(x + (((offset + i) * cos_radian) - ((j+fontChar.adjYOffset)*sin_radian)));
+ int newY = (int)(y + (((j+fontChar.adjYOffset) * cos_radian) + ((offset + i) * sin_radian)));
+
+ if ((ch & mask) != 0) _drawPixel(newX,newY,tft_fg, 0);
+ else if (!tft_font_transparent) _drawPixel(newX,newY,tft_bg, 0);
+
+ mask >>= 1;
+ }
+ }
+ disp_deselect();
+
+ return fontChar.xDelta+1;
+}
+
+// rotated fixed width character
+//--------------------------------------------------------
+static void rotateChar(uint8_t c, int x, int y, int pos) {
+ uint8_t i,j,ch,fz,mask;
+ uint16_t temp;
+ int newx,newy;
+ double radian = tft_font_rotate*0.0175;
+ float cos_radian = cos(radian);
+ float sin_radian = sin(radian);
+ int zz;
+
+ if( tft_cfont.x_size < 8 ) fz = tft_cfont.x_size;
+ else fz = tft_cfont.x_size/8;
+ temp=((c-tft_cfont.offset)*((fz)*tft_cfont.y_size))+4;
+
+ disp_select();
+ for (j=0; j>= 1;
+ }
+ }
+ temp+=(fz);
+ }
+ disp_deselect();
+ // calculate x,y for the next char
+ tft_x = (int)(x + ((pos+1) * tft_cfont.x_size * cos_radian));
+ tft_y = (int)(y + ((pos+1) * tft_cfont.x_size * sin_radian));
+}
+
+//----------------------
+static int _7seg_width()
+{
+ return (2 * (2 * tft_cfont.y_size + 1)) + tft_cfont.x_size;
+}
+
+//-----------------------
+static int _7seg_height()
+{
+ return (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size);
+}
+
+// Returns the string width in pixels.
+// Useful for positions strings on the screen.
+//===============================
+int TFT_getStringWidth(char* str)
+{
+ int strWidth = 0;
+
+ if (tft_cfont.bitmap == 2) strWidth = ((_7seg_width()+2) * strlen(str)) - 2; // 7-segment font
+ else if (tft_cfont.x_size != 0) strWidth = strlen(str) * tft_cfont.x_size; // fixed width font
+ else {
+ // calculate the width of the string of proportional characters
+ char* tempStrptr = str;
+ while (*tempStrptr != 0) {
+ if (getCharPtr(*tempStrptr++)) {
+ strWidth += (((fontChar.width > fontChar.xDelta) ? fontChar.width : fontChar.xDelta) + 1);
+ }
+ }
+ strWidth--;
+ }
+ return strWidth;
+}
+
+//===============================================
+void TFT_clearStringRect(int x, int y, char *str)
+{
+ int w = TFT_getStringWidth(str);
+ int h = TFT_getfontheight();
+ TFT_fillRect(x+tft_dispWin.x1, y+tft_dispWin.y1, w, h, tft_bg);
+}
+
+//==============================================================================
+/**
+ * bit-encoded bar position of all digits' bcd segments
+ *
+ * 6
+ * +-----+
+ * 3 | . | 2
+ * +--5--+
+ * 1 | . | 0
+ * +--.--+
+ * 4
+ */
+static const uint16_t font_bcd[] = {
+ 0x200, // 0010 0000 0000 // -
+ 0x080, // 0000 1000 0000 // .
+ 0x06C, // 0100 0110 1100 // /, degree
+ 0x05f, // 0000 0101 1111, // 0
+ 0x005, // 0000 0000 0101, // 1
+ 0x076, // 0000 0111 0110, // 2
+ 0x075, // 0000 0111 0101, // 3
+ 0x02d, // 0000 0010 1101, // 4
+ 0x079, // 0000 0111 1001, // 5
+ 0x07b, // 0000 0111 1011, // 6
+ 0x045, // 0000 0100 0101, // 7
+ 0x07f, // 0000 0111 1111, // 8
+ 0x07d, // 0000 0111 1101 // 9
+ 0x900 // 1001 0000 0000 // :
+};
+
+//-----------------------------------------------------------------------------------------------
+static void barVert(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
+ _fillTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, color);
+ _fillTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, color);
+ _fillRect(x, y+2*w+1, 2*w+1, l, color);
+ if (tft_cfont.offset) {
+ _drawTriangle(x+1, y+2*w, x+w, y+w+1, x+2*w-1, y+2*w, outline);
+ _drawTriangle(x+1, y+2*w+l+1, x+w, y+3*w+l, x+2*w-1, y+2*w+l+1, outline);
+ _drawRect(x, y+2*w+1, 2*w+1, l, outline);
+ }
+}
+
+//----------------------------------------------------------------------------------------------
+static void barHor(int16_t x, int16_t y, int16_t w, int16_t l, color_t color, color_t outline) {
+ _fillTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, color);
+ _fillTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, color);
+ _fillRect(x+2*w+1, y, l, 2*w+1, color);
+ if (tft_cfont.offset) {
+ _drawTriangle(x+2*w, y+2*w-1, x+w+1, y+w, x+2*w, y+1, outline);
+ _drawTriangle(x+2*w+l+1, y+2*w-1, x+3*w+l, y+w, x+2*w+l+1, y+1, outline);
+ _drawRect(x+2*w+1, y, l, 2*w+1, outline);
+ }
+}
+
+//--------------------------------------------------------------------------------------------
+static void _draw7seg(int16_t x, int16_t y, int8_t num, int16_t w, int16_t l, color_t color) {
+ /* TODO: clipping */
+ if (num < 0x2D || num > 0x3A) return;
+
+ int16_t c = font_bcd[num-0x2D];
+ int16_t d = 2*w+l+1;
+
+ // === Clear unused segments ===
+ if (!(c & 0x001)) barVert(x+d, y+d, w, l, tft_bg, tft_bg);
+ if (!(c & 0x002)) barVert(x, y+d, w, l, tft_bg, tft_bg);
+ if (!(c & 0x004)) barVert(x+d, y, w, l, tft_bg, tft_bg);
+ if (!(c & 0x008)) barVert(x, y, w, l, tft_bg, tft_bg);
+ if (!(c & 0x010)) barHor(x, y+2*d, w, l, tft_bg, tft_bg);
+ if (!(c & 0x020)) barHor(x, y+d, w, l, tft_bg, tft_bg);
+ if (!(c & 0x040)) barHor(x, y, w, l, tft_bg, tft_bg);
+
+ if (!(c & 0x080)) {
+ // low point
+ _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg);
+ if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_bg);
+ }
+ if (!(c & 0x100)) {
+ // down middle point
+ _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg);
+ if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_bg);
+ }
+ if (!(c & 0x800)) {
+ // up middle point
+ _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg);
+ if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_bg);
+ }
+ if (!(c & 0x200)) {
+ // middle, minus
+ _fillRect(x+2*w+1, y+d, l, 2*w+1, tft_bg);
+ if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_bg);
+ }
+
+ // === Draw used segments ===
+ if (c & 0x001) barVert(x+d, y+d, w, l, color, tft_cfont.color); // down right
+ if (c & 0x002) barVert(x, y+d, w, l, color, tft_cfont.color); // down left
+ if (c & 0x004) barVert(x+d, y, w, l, color, tft_cfont.color); // up right
+ if (c & 0x008) barVert(x, y, w, l, color, tft_cfont.color); // up left
+ if (c & 0x010) barHor(x, y+2*d, w, l, color, tft_cfont.color); // down
+ if (c & 0x020) barHor(x, y+d, w, l, color, tft_cfont.color); // middle
+ if (c & 0x040) barHor(x, y, w, l, color, tft_cfont.color); // up
+
+ if (c & 0x080) {
+ // low point
+ _fillRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, color);
+ if (tft_cfont.offset) _drawRect(x+(d/2), y+2*d, 2*w+1, 2*w+1, tft_cfont.color);
+ }
+ if (c & 0x100) {
+ // down middle point
+ _fillRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, color);
+ if (tft_cfont.offset) _drawRect(x+(d/2), y+d+2*w+1, 2*w+1, l/2, tft_cfont.color);
+ }
+ if (c & 0x800) {
+ // up middle point
+ _fillRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, color);
+ if (tft_cfont.offset) _drawRect(x+(d/2), y+(2*w)+1+(l/2), 2*w+1, l/2, tft_cfont.color);
+ }
+ if (c & 0x200) {
+ // middle, minus
+ _fillRect(x+2*w+1, y+d, l, 2*w+1, color);
+ if (tft_cfont.offset) _drawRect(x+2*w+1, y+d, l, 2*w+1, tft_cfont.color);
+ }
+}
+//==============================================================================
+
+//======================================
+void TFT_print(char *st, int x, int y) {
+ int stl, i, tmpw, tmph, fh;
+ uint8_t ch;
+
+ if (tft_cfont.bitmap == 0) return; // wrong font selected
+
+ // ** Rotated strings cannot be aligned
+ if ((tft_font_rotate != 0) && ((x <= CENTER) || (y <= CENTER))) return;
+
+ if ((x < LASTX) || (tft_font_rotate == 0)) TFT_OFFSET = 0;
+
+ if ((x >= LASTX) && (x < LASTY)) x = tft_x + (x-LASTX);
+ else if (x > CENTER) x += tft_dispWin.x1;
+
+ if (y >= LASTY) y = tft_y + (y-LASTY);
+ else if (y > CENTER) y += tft_dispWin.y1;
+
+ // ** Get number of characters in string to print
+ stl = strlen(st);
+
+ // ** Calculate CENTER, RIGHT or BOTTOM position
+ tmpw = TFT_getStringWidth(st); // string width in pixels
+ fh = tft_cfont.y_size; // font height
+ if ((tft_cfont.x_size != 0) && (tft_cfont.bitmap == 2)) {
+ // 7-segment font
+ fh = (3 * (2 * tft_cfont.y_size + 1)) + (2 * tft_cfont.x_size); // 7-seg character height
+ }
+
+ if (x == RIGHT) x = tft_dispWin.x2 - tmpw + tft_dispWin.x1;
+ else if (x == CENTER) x = (((tft_dispWin.x2 - tft_dispWin.x1 + 1) - tmpw) / 2) + tft_dispWin.x1;
+
+ if (y == BOTTOM) y = tft_dispWin.y2 - fh + tft_dispWin.y1;
+ else if (y==CENTER) y = (((tft_dispWin.y2 - tft_dispWin.y1 + 1) - (fh/2)) / 2) + tft_dispWin.y1;
+
+ if (x < tft_dispWin.x1) x = tft_dispWin.x1;
+ if (y < tft_dispWin.y1) y = tft_dispWin.y1;
+ if ((x > tft_dispWin.x2) || (y > tft_dispWin.y2)) return;
+
+ tft_x = x;
+ tft_y = y;
+
+ // ** Adjust y position
+ tmph = tft_cfont.y_size; // font height
+ // for non-proportional fonts, char width is the same for all chars
+ tmpw = tft_cfont.x_size;
+ if (tft_cfont.x_size != 0) {
+ if (tft_cfont.bitmap == 2) { // 7-segment font
+ tmpw = _7seg_width(); // character width
+ tmph = _7seg_height(); // character height
+ }
+ }
+ else TFT_OFFSET = 0; // fixed font; offset not needed
+
+ if ((tft_y + tmph - 1) > tft_dispWin.y2) return;
+
+ int offset = TFT_OFFSET;
+
+ for (i=0; i (tft_dispWin.y2-tmph)) break;
+ tft_x = tft_dispWin.x1;
+ }
+ }
+
+ else { // ==== other characters ====
+ if (tft_cfont.x_size == 0) {
+ // for proportional font get character data to 'fontChar'
+ if (getCharPtr(ch)) tmpw = fontChar.xDelta;
+ else continue;
+ }
+
+ // check if character can be displayed in the current line
+ if ((tft_x+tmpw) > (tft_dispWin.x2)) {
+ if (tft_text_wrap == 0) break;
+ tft_y += tmph + tft_font_line_space;
+ if (tft_y > (tft_dispWin.y2-tmph)) break;
+ tft_x = tft_dispWin.x1;
+ }
+
+ // Let's print the character
+ if (tft_cfont.x_size == 0) {
+ // == proportional font
+ if (tft_font_rotate == 0) tft_x += printProportionalChar(tft_x, tft_y) + 1;
+ else {
+ // rotated proportional font
+ offset += rotatePropChar(x, y, offset);
+ TFT_OFFSET = offset;
+ }
+ }
+ else {
+ if (tft_cfont.bitmap == 1) {
+ // == fixed font
+ if ((ch < tft_cfont.offset) || ((ch-tft_cfont.offset) > tft_cfont.numchars)) ch = tft_cfont.offset;
+ if (tft_font_rotate == 0) {
+ printChar(ch, tft_x, tft_y);
+ tft_x += tmpw;
+ }
+ else rotateChar(ch, x, y, i);
+ }
+ else if (tft_cfont.bitmap == 2) {
+ // == 7-segment font ==
+ _draw7seg(tft_x, tft_y, ch, tft_cfont.y_size, tft_cfont.x_size, tft_fg);
+ tft_x += (tmpw + 2);
+ }
+ }
+ }
+ }
+}
+
+
+// ================ Service functions ==========================================
+
+// Change the screen rotation.
+// Input: m new rotation value (0 to 3)
+//=================================
+void TFT_setRotation(uint8_t rot) {
+ if (rot > 3) {
+ uint8_t madctl = (rot & 0xF8); // for testing, manually set MADCTL register
+ if (disp_select() == ESP_OK) {
+ disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
+ disp_deselect();
+ }
+ }
+ else {
+ tft_orientation = rot;
+ _tft_setRotation(rot);
+ }
+
+ tft_dispWin.x1 = TFT_STATIC_X_OFFSET;
+ tft_dispWin.y1 = TFT_STATIC_Y_OFFSET;
+ tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
+ tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
+
+ TFT_fillScreen(tft_bg);
+}
+
+// Send the command to invert all of the colors.
+// Input: i 0 to disable inversion; non-zero to enable inversion
+//==========================================
+void TFT_invertDisplay(const uint8_t mode) {
+ if (disp_select() == ESP_OK) {
+ if ( mode == INVERT_ON ) disp_spi_transfer_cmd(TFT_INVONN);
+ else disp_spi_transfer_cmd(TFT_INVOFF);
+ disp_deselect();
+ }
+}
+
+// Select gamma curve
+// Input: gamma = 0~3
+//==================================
+void TFT_setGammaCurve(uint8_t gm) {
+ uint8_t gamma_curve = (uint8_t)1 << (gm & (uint8_t)0x03);
+ if (disp_select() == ESP_OK) {
+ disp_spi_transfer_cmd_data(TFT_CMD_GAMMASET, &gamma_curve, 1);
+ disp_deselect();
+ }
+}
+
+//===========================================================
+color_t HSBtoRGB(float _hue, float _sat, float _brightness) {
+ float red = 0.0;
+ float green = 0.0;
+ float blue = 0.0;
+
+ if (_sat == 0.0) {
+ red = _brightness;
+ green = _brightness;
+ blue = _brightness;
+ } else {
+ if (_hue == 360.0) {
+ _hue = 0;
+ }
+
+ int slice = (int)(_hue / 60.0);
+ float hue_frac = (_hue / 60.0) - slice;
+
+ float aa = _brightness * (1.0 - _sat);
+ float bb = _brightness * (1.0 - _sat * hue_frac);
+ float cc = _brightness * (1.0 - _sat * (1.0 - hue_frac));
+
+ switch(slice) {
+ case 0:
+ red = _brightness;
+ green = cc;
+ blue = aa;
+ break;
+ case 1:
+ red = bb;
+ green = _brightness;
+ blue = aa;
+ break;
+ case 2:
+ red = aa;
+ green = _brightness;
+ blue = cc;
+ break;
+ case 3:
+ red = aa;
+ green = bb;
+ blue = _brightness;
+ break;
+ case 4:
+ red = cc;
+ green = aa;
+ blue = _brightness;
+ break;
+ case 5:
+ red = _brightness;
+ green = aa;
+ blue = bb;
+ break;
+ default:
+ red = 0.0;
+ green = 0.0;
+ blue = 0.0;
+ break;
+ }
+ }
+
+ color_t color;
+ color.r = ((uint8_t)(red * 255.0)) & 0xFC;
+ color.g = ((uint8_t)(green * 255.0)) & 0xFC;
+ color.b = ((uint8_t)(blue * 255.0)) & 0xFC;
+
+ return color;
+}
+//=====================================================================
+void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
+{
+ tft_dispWin.x1 = x1 + TFT_STATIC_X_OFFSET;
+ tft_dispWin.y1 = y1 + TFT_STATIC_Y_OFFSET;
+ tft_dispWin.x2 = x2 + TFT_STATIC_X_OFFSET;
+ tft_dispWin.y2 = y2 + TFT_STATIC_Y_OFFSET;
+
+ if (tft_dispWin.x2 >= tft_width + TFT_STATIC_X_OFFSET) tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
+ if (tft_dispWin.y2 >= tft_height + TFT_STATIC_Y_OFFSET) tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
+ if (tft_dispWin.x1 > tft_dispWin.x2) tft_dispWin.x1 = tft_dispWin.x2;
+ if (tft_dispWin.y1 > tft_dispWin.y2) tft_dispWin.y1 = tft_dispWin.y2;
+}
+
+//=====================
+void TFT_resetclipwin()
+{
+ tft_dispWin.x2 = tft_width + TFT_STATIC_X_OFFSET -1;
+ tft_dispWin.y2 = tft_height + TFT_STATIC_Y_OFFSET -1;
+ tft_dispWin.x1 = TFT_STATIC_X_OFFSET;
+ tft_dispWin.y1 = TFT_STATIC_Y_OFFSET;
+}
+
+//==========================================================================
+void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color) {
+ if (tft_cfont.bitmap != 2) return;
+
+ if (l < 6) l = 6;
+ if (l > 40) l = 40;
+ if (w < 1) w = 1;
+ if (w > (l/2)) w = l/2;
+ if (w > 12) w = 12;
+
+ tft_cfont.x_size = l;
+ tft_cfont.y_size = w;
+ tft_cfont.offset = outline;
+ tft_cfont.color = color;
+}
+
+//==========================================
+int TFT_getfontsize(int *width, int* height)
+{
+ if (tft_cfont.bitmap == 1) {
+ if (tft_cfont.x_size != 0) *width = tft_cfont.x_size; // fixed width font
+ else *width = tft_cfont.max_x_size; // proportional font
+ *height = tft_cfont.y_size;
+ }
+ else if (tft_cfont.bitmap == 2) {
+ // 7-segment font
+ *width = _7seg_width();
+ *height = _7seg_height();
+ }
+ else {
+ *width = 0;
+ *height = 0;
+ return 0;
+ }
+ return 1;
+}
+
+//=====================
+int TFT_getfontheight()
+{
+ if (tft_cfont.bitmap == 1) return tft_cfont.y_size; // Bitmap font
+ else if (tft_cfont.bitmap == 2) return _7seg_height(); // 7-segment font
+ return 0;
+}
+
+//====================
+void TFT_saveClipWin()
+{
+ dispWinTemp.x1 = tft_dispWin.x1;
+ dispWinTemp.y1 = tft_dispWin.y1;
+ dispWinTemp.x2 = tft_dispWin.x2;
+ dispWinTemp.y2 = tft_dispWin.y2;
+}
+
+//=======================
+void TFT_restoreClipWin()
+{
+ tft_dispWin.x1 = dispWinTemp.x1;
+ tft_dispWin.y1 = dispWinTemp.y1;
+ tft_dispWin.x2 = dispWinTemp.x2;
+ tft_dispWin.y2 = dispWinTemp.y2;
+}
+
+
+// ================ JPG SUPPORT ================================================
+// User defined device identifier
+typedef struct {
+ FILE *fhndl; // File handler for input function
+ int x; // image top left point X position
+ int y; // image top left point Y position
+ uint8_t *membuff; // memory buffer containing the image
+ uint32_t bufsize; // size of the memory buffer
+ uint32_t bufptr; // memory buffer current position
+ color_t *linbuf[2]; // memory buffer used for display output
+ uint8_t linbuf_idx;
+} JPGIODEV;
+
+
+// User defined call-back function to input JPEG data from file
+//---------------------
+static UINT tjd_input (
+ JDEC* jd, // Decompression object
+ BYTE* buff, // Pointer to the read buffer (NULL:skip)
+ UINT nd // Number of bytes to read/skip from input stream
+)
+{
+ int rb = 0;
+ // Device identifier for the session (5th argument of jd_prepare function)
+ JPGIODEV *dev = (JPGIODEV*)jd->device;
+
+ if (buff) { // Read nd bytes from the input strem
+ rb = fread(buff, 1, nd, dev->fhndl);
+ return rb; // Returns actual number of bytes read
+ }
+ else { // Remove nd bytes from the input stream
+ if (fseek(dev->fhndl, nd, SEEK_CUR) >= 0) return nd;
+ else return 0;
+ }
+}
+
+// User defined call-back function to input JPEG data from memory buffer
+//-------------------------
+static UINT tjd_buf_input (
+ JDEC* jd, // Decompression object
+ BYTE* buff, // Pointer to the read buffer (NULL:skip)
+ UINT nd // Number of bytes to read/skip from input stream
+)
+{
+ // Device identifier for the session (5th argument of jd_prepare function)
+ JPGIODEV *dev = (JPGIODEV*)jd->device;
+ if (!dev->membuff) return 0;
+ if (dev->bufptr >= (dev->bufsize + 2)) return 0; // end of stream
+
+ if ((dev->bufptr + nd) > (dev->bufsize + 2)) nd = (dev->bufsize + 2) - dev->bufptr;
+
+ if (buff) { // Read nd bytes from the input strem
+ memcpy(buff, dev->membuff + dev->bufptr, nd);
+ dev->bufptr += nd;
+ return nd; // Returns number of bytes read
+ }
+ else { // Remove nd bytes from the input stream
+ dev->bufptr += nd;
+ return nd;
+ }
+}
+
+// User defined call-back function to output RGB bitmap to display device
+//----------------------
+static UINT tjd_output (
+ JDEC* jd, // Decompression object of current session
+ void* bitmap, // Bitmap data to be output
+ JRECT* rect // Rectangular region to output
+)
+{
+ // Device identifier for the session (5th argument of jd_prepare function)
+ JPGIODEV *dev = (JPGIODEV*)jd->device;
+
+ // ** Put the rectangular into the display device **
+ int x;
+ int y;
+ int dleft, dtop, dright, dbottom;
+ BYTE *src = (BYTE*)bitmap;
+
+ int left = rect->left + dev->x;
+ int top = rect->top + dev->y;
+ int right = rect->right + dev->x;
+ int bottom = rect->bottom + dev->y;
+
+ if ((left > tft_dispWin.x2) || (top > tft_dispWin.y2)) return 1; // out of screen area, return
+ if ((right < tft_dispWin.x1) || (bottom < tft_dispWin.y1)) return 1;// out of screen area, return
+
+ if (left < tft_dispWin.x1) dleft = tft_dispWin.x1;
+ else dleft = left;
+ if (top < tft_dispWin.y1) dtop = tft_dispWin.y1;
+ else dtop = top;
+ if (right > tft_dispWin.x2) dright = tft_dispWin.x2;
+ else dright = right;
+ if (bottom > tft_dispWin.y2) dbottom = tft_dispWin.y2;
+ else dbottom = bottom;
+
+ if ((dleft > tft_dispWin.x2) || (dtop > tft_dispWin.y2)) return 1; // out of screen area, return
+ if ((dright < tft_dispWin.x1) || (dbottom < tft_dispWin.y1)) return 1; // out of screen area, return
+
+ uint32_t len = ((dright-dleft+1) * (dbottom-dtop+1)); // calculate length of data
+
+
+ if ((len > 0) && (len <= JPG_IMAGE_LINE_BUF_SIZE)) {
+ uint8_t *dest = (uint8_t *)(dev->linbuf[dev->linbuf_idx]);
+
+ for (y = top; y <= bottom; y++) {
+ for (x = left; x <= right; x++) {
+ // Clip to display area
+ if ((x >= dleft) && (y >= dtop) && (x <= dright) && (y <= dbottom)) {
+ *dest++ = (*src++) & 0xFC;
+ *dest++ = (*src++) & 0xFC;
+ *dest++ = (*src++) & 0xFC;
+ }
+ else src += 3; // skip
+ }
+ }
+ wait_trans_finish(1);
+ send_data(dleft, dtop, dright, dbottom, len, dev->linbuf[dev->linbuf_idx]);
+ dev->linbuf_idx = ((dev->linbuf_idx + 1) & 1);
+ }
+ else {
+ wait_trans_finish(1);
+ printf("Data size error: %d jpg: (%d,%d,%d,%d) disp: (%d,%d,%d,%d)\r\n", len, left,top,right,bottom, dleft,dtop,dright,dbottom);
+ return 0; // stop decompression
+ }
+
+ return 1; // Continue to decompression
+}
+
+// tft.jpgimage(X, Y, scale, file_name, buf, size]
+// X & Y can be < 0 !
+//==================================================================================
+void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size)
+{
+ JPGIODEV dev;
+ struct stat sb;
+ char *work = NULL; // Pointer to the working buffer (must be 4-byte aligned)
+ UINT sz_work = 3800; // Size of the working buffer (must be power of 2)
+ JDEC jd; // Decompression object (70 bytes)
+ JRESULT rc;
+
+ dev.linbuf[0] = NULL;
+ dev.linbuf[1] = NULL;
+ dev.linbuf_idx = 0;
+
+ dev.fhndl = NULL;
+ if (fname == NULL) {
+ // image from buffer
+ dev.membuff = buf;
+ dev.bufsize = size;
+ dev.bufptr = 0;
+ }
+ else {
+ // image from file
+ dev.membuff = NULL;
+ dev.bufsize = 0;
+ dev.bufptr = 0;
+
+ if (stat(fname, &sb) != 0) {
+ if (tft_image_debug) printf("File error: %ss\r\n", strerror(errno));
+ goto exit;
+ }
+
+ dev.fhndl = fopen(fname, "r");
+ if (!dev.fhndl) {
+ if (tft_image_debug) printf("Error opening file: %s\r\n", strerror(errno));
+ goto exit;
+ }
+ }
+
+ if (scale > 3) scale = 3;
+
+ work = malloc(sz_work);
+ if (work) {
+ if (dev.membuff) rc = jd_prepare(&jd, tjd_buf_input, (void *)work, sz_work, &dev);
+ else rc = jd_prepare(&jd, tjd_input, (void *)work, sz_work, &dev);
+ if (rc == JDR_OK) {
+ if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - (int)(jd.width >> scale)) / 2) + tft_dispWin.x1;
+ else if (x == RIGHT) x = tft_dispWin.x2 + 1 - (int)(jd.width >> scale);
+
+ if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - (int)(jd.height >> scale)) / 2) + tft_dispWin.y1;
+ else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - (int)(jd.height >> scale);
+
+ if (x < ((tft_dispWin.x2-1) * -1)) x = (tft_dispWin.x2-1) * -1;
+ if (y < ((tft_dispWin.y2-1)) * -1) y = (tft_dispWin.y2-1) * -1;
+ if (x > (tft_dispWin.x2-1)) x = tft_dispWin.x2 - 1;
+ if (y > (tft_dispWin.y2-1)) y = tft_dispWin.y2-1;
+
+ dev.x = x;
+ dev.y = y;
+
+ dev.linbuf[0] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
+ if (dev.linbuf[0] == NULL) {
+ if (tft_image_debug) printf("Error allocating line buffer #0\r\n");
+ goto exit;
+ }
+ dev.linbuf[1] = heap_caps_malloc(JPG_IMAGE_LINE_BUF_SIZE*3, MALLOC_CAP_DMA);
+ if (dev.linbuf[1] == NULL) {
+ if (tft_image_debug) printf("Error allocating line buffer #1\r\n");
+ goto exit;
+ }
+
+ // Start to decode the JPEG file
+ disp_select();
+ rc = jd_decomp(&jd, tjd_output, scale);
+ disp_deselect();
+
+ if (rc != JDR_OK) {
+ if (tft_image_debug) printf("jpg decompression error %d\r\n", rc);
+ }
+ if (tft_image_debug) printf("Jpg size: %dx%d, position; %d,%d, scale: %d, bytes used: %d\r\n", jd.width, jd.height, x, y, scale, jd.sz_pool);
+ }
+ else {
+ if (tft_image_debug) printf("jpg prepare error %d\r\n", rc);
+ }
+ }
+ else {
+ if (tft_image_debug) printf("work buffer allocation error\r\n");
+ }
+
+exit:
+ if (work) free(work); // free work buffer
+ if (dev.linbuf[0]) free(dev.linbuf[0]);
+ if (dev.linbuf[1]) free(dev.linbuf[1]);
+ if (dev.fhndl) fclose(dev.fhndl); // close input file
+}
+
+
+//====================================================================================
+int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size)
+{
+ FILE *fhndl = NULL;
+ struct stat sb;
+ int i, err=0;
+ int img_xsize, img_ysize, img_xstart, img_xlen, img_ystart, img_ylen;
+ int img_pos, img_pix_pos, scan_lines, rd_len;
+ uint8_t tmpc;
+ uint16_t wtemp;
+ uint32_t temp;
+ int disp_xstart, disp_xend, disp_ystart, disp_yend;
+ uint8_t buf[56];
+ char err_buf[64];
+ uint8_t *line_buf[2] = {NULL,NULL};
+ uint8_t lb_idx = 0;
+ uint8_t *scale_buf = NULL;
+ uint8_t scale_pix;
+ uint16_t co[3] = {0,0,0}; // RGB sum
+ uint8_t npix;
+
+ if (scale > 7) scale = 7;
+ scale_pix = scale+1; // scale factor ( 1~8 )
+
+ if (fname) {
+ // * File name is given, reading image from file
+ if (stat(fname, &sb) != 0) {
+ sprintf(err_buf, "opening file");
+ err = -1;
+ goto exit;
+ }
+ size = sb.st_size;
+ fhndl = fopen(fname, "r");
+ if (!fhndl) {
+ sprintf(err_buf, "opening file");
+ err = -2;
+ goto exit;
+ }
+
+ i = fread(buf, 1, 54, fhndl); // read header
+ }
+ else {
+ // * Reading image from buffer
+ if ((imgbuf) && (size > 54)) {
+ memcpy(buf, imgbuf, 54);
+ i = 54;
+ }
+ else i = 0;
+ }
+
+ sprintf(err_buf, "reading header");
+ if (i != 54) {err = -3; goto exit;}
+
+ // ** Check image header and get image properties
+ if ((buf[0] != 'B') || (buf[1] != 'M')) {err=-4; goto exit;} // accept only images with 'BM' id
+
+ memcpy(&temp, buf+2, 4); // file size
+ if (temp != size) {err=-5; goto exit;}
+
+ memcpy(&img_pos, buf+10, 4); // start of pixel data
+
+ memcpy(&temp, buf+14, 4); // BMP header size
+ if (temp != 40) {err=-6; goto exit;}
+
+ memcpy(&wtemp, buf+26, 2); // the number of color planes
+ if (wtemp != 1) {err=-7; goto exit;}
+
+ memcpy(&wtemp, buf+28, 2); // the number of bits per pixel
+ if (wtemp != 24) {err=-8; goto exit;}
+
+ memcpy(&temp, buf+30, 4); // the compression method being used
+ if (temp != 0) {err=-9; goto exit;}
+
+ memcpy(&img_xsize, buf+18, 4); // the bitmap width in pixels
+ memcpy(&img_ysize, buf+22, 4); // the bitmap height in pixels
+
+
+ // * scale image dimensions
+
+ img_xlen = img_xsize / scale_pix; // image display horizontal size
+ img_ylen = img_ysize / scale_pix; // image display vertical size
+
+ if (x == CENTER) x = ((tft_dispWin.x2 - tft_dispWin.x1 + 1 - img_xlen) / 2) + tft_dispWin.x1;
+ else if (x == RIGHT) x = tft_dispWin.x2 + 1 - img_xlen;
+
+ if (y == CENTER) y = ((tft_dispWin.y2 - tft_dispWin.y1 + 1 - img_ylen) / 2) + tft_dispWin.y1;
+ else if (y == BOTTOM) y = tft_dispWin.y2 + 1 - img_ylen;
+
+ if ((x < ((tft_dispWin.x2 + 1) * -1)) || (x > (tft_dispWin.x2 + 1)) || (y < ((tft_dispWin.y2 + 1) * -1)) || (y > (tft_dispWin.y2 + 1))) {
+ sprintf(err_buf, "out of display area (%d,%d", x, y);
+ err = -10;
+ goto exit;
+ }
+
+ // ** set display and image areas
+ if (x < tft_dispWin.x1) {
+ disp_xstart = tft_dispWin.x1;
+ img_xstart = -x; // image pixel line X offset
+ img_xlen += x;
+ }
+ else {
+ disp_xstart = x;
+ img_xstart = 0;
+ }
+ if (y < tft_dispWin.y1) {
+ disp_ystart = tft_dispWin.y1;
+ img_ystart = -y; // image pixel line Y offset
+ img_ylen += y;
+ }
+ else {
+ disp_ystart = y;
+ img_ystart = 0;
+ }
+ disp_xend = disp_xstart + img_xlen - 1;
+ disp_yend = disp_ystart + img_ylen - 1;
+ if (disp_xend > tft_dispWin.x2) {
+ disp_xend = tft_dispWin.x2;
+ img_xlen = disp_xend - disp_xstart + 1;
+ }
+ if (disp_yend > tft_dispWin.y2) {
+ disp_yend = tft_dispWin.y2;
+ img_ylen = disp_yend - disp_ystart + 1;
+ }
+
+ if ((img_xlen < 8) || (img_ylen < 8) || (img_xstart >= (img_xsize-2)) || ((img_ysize - img_ystart) < 2)) {
+ sprintf(err_buf, "image too small");
+ err = -11;
+ goto exit;
+ }
+
+ // ** Allocate memory for 2 lines of image pixels
+ line_buf[0] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
+ if (line_buf[0] == NULL) {
+ sprintf(err_buf, "allocating line buffer #1");
+ err=-12;
+ goto exit;
+ }
+
+ line_buf[1] = heap_caps_malloc(img_xsize*3, MALLOC_CAP_DMA);
+ if (line_buf[1] == NULL) {
+ sprintf(err_buf, "allocating line buffer #2");
+ err=-13;
+ goto exit;
+ }
+
+ if (scale) {
+ // Allocate memory for scale buffer
+ rd_len = img_xlen * 3 * scale_pix;
+ scale_buf = malloc(rd_len*scale_pix);
+ if (scale_buf == NULL) {
+ sprintf(err_buf, "allocating scale buffer");
+ err=-14;
+ goto exit;
+ }
+ }
+ else rd_len = img_xlen * 3;
+
+ // ** ***************************************************** **
+ // ** BMP images are stored in file from LAST to FIRST line **
+ // ** ***************************************************** **
+
+ /* Used variables:
+ img_xsize horizontal image size in pixels
+ img_ysize number of image lines
+ img_xlen image display horizontal scaled size in pixels
+ img_ylen image display vertical scaled size in pixels
+ img_xstart first pixel in line to be displayed
+ img_ystart first image line to be displayed
+ img_xlen number of pixels in image line to be displayed, starting with 'img_xstart'
+ img_ylen number of lines in image to be displayed, starting with 'img_ystart'
+ rd_len length of color data which are read from image line in bytes
+ */
+
+ // Set position in image to the first color data (beginning of the LAST line)
+ img_pos += (img_ystart * (img_xsize*3));
+ if (fhndl) {
+ if (fseek(fhndl, img_pos, SEEK_SET) != 0) {
+ sprintf(err_buf, "file seek at %d", img_pos);
+ err = -15;
+ goto exit;
+ }
+ }
+
+ if (tft_image_debug) printf("BMP: image size: (%d,%d) scale: %d disp size: (%d,%d) img xofs: %d img yofs: %d at: %d,%d; line buf: 2* %d scale buf: %d\r\n",
+ img_xsize, img_ysize, scale_pix, img_xlen, img_ylen, img_xstart, img_ystart, disp_xstart, disp_ystart, img_xsize*3, ((scale) ? (rd_len*scale_pix) : 0));
+
+ // * Select the display
+ disp_select();
+
+ while ((disp_yend >= disp_ystart) && ((img_pos + (img_xsize*3)) <= size)) {
+ if (img_pos > size) {
+ sprintf(err_buf, "EOF reached: %d > %d", img_pos, size);
+ err = -16;
+ goto exit1;
+ }
+ if (scale == 0) {
+ // Read the line of color data into color buffer
+ if (fhndl) {
+ i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
+ if (i != (img_xsize*3)) {
+ sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
+ err = -16;
+ goto exit1;
+ }
+ }
+ else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
+
+ if (img_xstart > 0) memmove(line_buf[lb_idx], line_buf[lb_idx]+(img_xstart*3), rd_len);
+ // Convert colors BGR-888 (BMP) -> RGB-888 (DISPLAY) ===
+ for (i=0; i < rd_len; i += 3) {
+ tmpc = line_buf[lb_idx][i+2] & 0xfc; // save R
+ line_buf[lb_idx][i+2] = line_buf[lb_idx][i] & 0xfc; // B -> R
+ line_buf[lb_idx][i] = tmpc; // R -> B
+ line_buf[lb_idx][i+1] &= 0xfc; // G
+ }
+ img_pos += (img_xsize*3);
+ }
+ else {
+ // scale image, read 'scale_pix' lines and find the average color
+ for (scan_lines=0; scan_lines size) break;
+ if (fhndl) {
+ i = fread(line_buf[lb_idx], 1, img_xsize*3, fhndl); // read line from file
+ if (i != (img_xsize*3)) {
+ sprintf(err_buf, "file read at %d (%d<>%d)", img_pos, i, img_xsize*3);
+ err = -17;
+ goto exit1;
+ }
+ }
+ else memcpy(line_buf[lb_idx], imgbuf+img_pos, img_xsize*3);
+ img_pos += (img_xsize*3);
+
+ // copy only data which are displayed to scale buffer
+ memcpy(scale_buf + (rd_len * scan_lines), line_buf[lb_idx]+img_xstart, rd_len);
+ }
+
+ // Populate display line buffer
+ for (int n=0;n<(img_xlen*3);n += 3) {
+ memset(co, 0, sizeof(co)); // initialize color sum
+ npix = 0; // initialize number of pixels in scale rectangle
+
+ // sum all pixels in scale rectangle
+ for (int sc_line=0; sc_line RGB-888 (DISPLAY)
+ line_buf[lb_idx][n+2] = (uint8_t)(co[0] / npix); // B
+ line_buf[lb_idx][n+1] = (uint8_t)(co[1] / npix); // G
+ line_buf[lb_idx][n] = (uint8_t)(co[2] / npix); // R
+ }
+ }
+
+ wait_trans_finish(1);
+ send_data(disp_xstart, disp_yend, disp_xend, disp_yend, img_xlen, (color_t *)line_buf[lb_idx]);
+ lb_idx = (lb_idx + 1) & 1; // change buffer
+
+ disp_yend--;
+ }
+ err = 0;
+exit1:
+ disp_deselect();
+exit:
+ if (scale_buf) free(scale_buf);
+ if (line_buf[0]) free(line_buf[0]);
+ if (line_buf[1]) free(line_buf[1]);
+ if (fhndl) fclose(fhndl);
+ if ((err) && (tft_image_debug)) printf("Error: %d [%s]\r\n", err, err_buf);
+
+ return err;
+}
+
+
+// ============= Touch panel functions =========================================
+
+#if USE_TOUCH == TOUCH_TYPE_XPT2046
+//-------------------------------------------------------
+static int tp_get_data_xpt2046(uint8_t type, int samples)
+{
+ if (tft_ts_spi == NULL) return 0;
+
+ int n, result, val = 0;
+ uint32_t i = 0;
+ uint32_t vbuf[18];
+ uint32_t minval, maxval, dif;
+
+ if (samples < 3) samples = 1;
+ if (samples > 18) samples = 18;
+
+ // one dummy read
+ result = touch_get_data(type);
+
+ // read data
+ while (i < 10) {
+ minval = 5000;
+ maxval = 0;
+ // get values
+ for (n=0;n maxval) maxval = result;
+ }
+ if (result < 0) break;
+ dif = maxval - minval;
+ if (dif < 40) break;
+ i++;
+ }
+ if (result < 0) return -1;
+
+ if (samples > 2) {
+ // remove one min value
+ for (n = 0; n < samples; n++) {
+ if (vbuf[n] == minval) {
+ vbuf[n] = 5000;
+ break;
+ }
+ }
+ // remove one max value
+ for (n = 0; n < samples; n++) {
+ if (vbuf[n] == maxval) {
+ vbuf[n] = 5000;
+ break;
+ }
+ }
+ for (n = 0; n < samples; n++) {
+ if (vbuf[n] < 5000) val += vbuf[n];
+ }
+ val /= (samples-2);
+ }
+ else val = vbuf[0];
+
+ return val;
+}
+
+//-----------------------------------------------
+static int TFT_read_touch_xpt2046(int *x, int* y)
+{
+ int res = 0, result = -1;
+ if (spi_lobo_device_select(tft_ts_spi, 0) != ESP_OK) return 0;
+
+ result = tp_get_data_xpt2046(0xB0, 3); // Z; pressure; touch detect
+ if (result <= 50) goto exit;
+
+ // touch panel pressed
+ result = tp_get_data_xpt2046(0xD0, 10);
+ if (result < 0) goto exit;
+
+ *x = result;
+
+ result = tp_get_data_xpt2046(0x90, 10);
+ if (result < 0) goto exit;
+
+ *y = result;
+ res = 1;
+exit:
+ spi_lobo_device_deselect(tft_ts_spi);
+ return res;
+}
+#endif
+
+//=============================================
+int TFT_read_touch(int *x, int* y, uint8_t raw)
+{
+ *x = 0;
+ *y = 0;
+ if (tft_ts_spi == NULL) return 0;
+ #if USE_TOUCH == TOUCH_TYPE_NONE
+ return 0;
+ #else
+ int result = -1;
+ int X=0, Y=0;
+
+ #if USE_TOUCH == TOUCH_TYPE_XPT2046
+ uint32_t tft_tp_calx = TP_CALX_XPT2046;
+ uint32_t tft_tp_caly = TP_CALY_XPT2046;
+ result = TFT_read_touch_xpt2046(&X, &Y);
+ if (result == 0) return 0;
+ #elif USE_TOUCH == TOUCH_TYPE_STMPE610
+ uint32_t tft_tp_calx = TP_CALX_STMPE610;
+ uint32_t tft_tp_caly = TP_CALY_STMPE610;
+ uint16_t Xx, Yy, Z=0;
+ result = stmpe610_get_touch(&Xx, &Yy, &Z);
+ if (result == 0) return 0;
+ X = Xx;
+ Y = Yy;
+ #else
+ return 0;
+ #endif
+
+ if (raw) {
+ *x = X;
+ *y = Y;
+ return 1;
+ }
+
+ // Calibrate the result
+ int tmp;
+ int xleft = (tft_tp_calx >> 16) & 0x3FFF;
+ int xright = tft_tp_calx & 0x3FFF;
+ int ytop = (tft_tp_caly >> 16) & 0x3FFF;
+ int ybottom = tft_tp_caly & 0x3FFF;
+
+ if (((xright - xleft) <= 0) || ((ybottom - ytop) <= 0)) return 0;
+
+ #if USE_TOUCH == TOUCH_TYPE_XPT2046
+ int width = tft_width;
+ int height = tft_height;
+ X = ((X - xleft) * height) / (xright - xleft);
+ Y = ((Y - ytop) * width) / (ybottom - ytop);
+
+ if (X < 0) X = 0;
+ if (X > height-1) X = height-1;
+ if (Y < 0) Y = 0;
+ if (Y > width-1) Y = width-1;
+
+ switch (tft_orientation) {
+ case PORTRAIT:
+ tmp = X;
+ X = width - Y - 1;
+ Y = tmp;
+ break;
+ case PORTRAIT_FLIP:
+ tmp = X;
+ X = Y;
+ Y = height - tmp - 1;
+ break;
+ case LANDSCAPE_FLIP:
+ X = height - X - 1;
+ Y = width - Y - 1;
+ break;
+ }
+ #elif USE_TOUCH == TOUCH_TYPE_STMPE610
+ int width = tft_width;
+ int height = tft_height;
+ if (tft_width > tft_height) {
+ width = tft_height;
+ height = tft_width;
+ }
+ X = ((X - xleft) * width) / (xright - xleft);
+ Y = ((Y - ytop) * height) / (ybottom - ytop);
+
+ if (X < 0) X = 0;
+ if (X > width-1) X = width-1;
+ if (Y < 0) Y = 0;
+ if (Y > height-1) Y = height-1;
+
+ switch (tft_orientation) {
+ case PORTRAIT_FLIP:
+ X = width - X - 1;
+ Y = height - Y - 1;
+ break;
+ case LANDSCAPE:
+ tmp = X;
+ X = Y;
+ Y = width - tmp -1;
+ break;
+ case LANDSCAPE_FLIP:
+ tmp = X;
+ X = height - Y -1;
+ Y = tmp;
+ break;
+ }
+ #endif
+ *x = X;
+ *y = Y;
+ return 1;
+ #endif
+}
+
diff --git a/components/tft/tft.h b/components/tft/tft.h
new file mode 100644
index 0000000..8249f20
--- /dev/null
+++ b/components/tft/tft.h
@@ -0,0 +1,688 @@
+/*
+ * High level TFT functions
+ * Author: LoBo 04/2017, https://github/loboris
+ *
+ */
+
+#ifndef _TFT_H_
+#define _TFT_H_
+
+#include
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "tftspi.h"
+
+typedef struct {
+ uint16_t x1;
+ uint16_t y1;
+ uint16_t x2;
+ uint16_t y2;
+} dispWin_t;
+
+typedef struct {
+ uint8_t *font;
+ uint8_t x_size;
+ uint8_t y_size;
+ uint8_t offset;
+ uint16_t numchars;
+ uint16_t size;
+ uint8_t max_x_size;
+ uint8_t bitmap;
+ color_t color;
+} Font;
+
+
+//==========================================================================================
+// ==== Global variables ===================================================================
+//==========================================================================================
+extern uint8_t tft_orientation; // current screen tft_orientation
+extern uint16_t tft_font_rotate; // current font tft_font_rotate angle (0~395)
+extern uint8_t tft_font_transparent; // if not 0 draw fonts transparent
+extern uint8_t tft_font_forceFixed; // if not zero force drawing proportional fonts with fixed width
+extern uint8_t tft_font_buffered_char;
+extern uint8_t tft_font_line_space; // additional spacing between text lines; added to font height
+extern uint8_t tft_text_wrap; // if not 0 wrap long text to the new line, else clip
+extern color_t tft_fg; // current foreground color for fonts
+extern color_t tft_bg; // current background for non transparent fonts
+extern dispWin_t tft_dispWin; // display clip window
+extern float tft_angleOffset; // angle offset for arc, polygon and line by angle functions
+extern uint8_t tft_image_debug; // print debug messages during image decode if set to 1
+
+extern Font tft_cfont; // Current font structure
+
+extern int tft_x; // X position of the next character after TFT_print() function
+extern int tft_y; // Y position of the next character after TFT_print() function
+
+extern uint32_t tft_tp_calx; // touch screen X calibration constant
+extern uint32_t tft_tp_caly; // touch screen Y calibration constant
+// =========================================================================================
+
+
+// Buffer is created during jpeg decode for sending data
+// Total size of the buffer is 2 * (JPG_IMAGE_LINE_BUF_SIZE * 3)
+// The size must be multiple of 256 bytes !!
+#define JPG_IMAGE_LINE_BUF_SIZE 512
+
+// --- Constants for ellipse function ---
+#define TFT_ELLIPSE_UPPER_RIGHT 0x01
+#define TFT_ELLIPSE_UPPER_LEFT 0x02
+#define TFT_ELLIPSE_LOWER_LEFT 0x04
+#define TFT_ELLIPSE_LOWER_RIGHT 0x08
+
+// Constants for Arc function
+// number representing the maximum angle (e.g. if 100, then if you pass in start=0 and end=50, you get a half circle)
+// this can be changed with setArcParams function at runtime
+#define DEFAULT_ARC_ANGLE_MAX 360
+// rotational offset in degrees defining position of value 0 (-90 will put it at the top of circle)
+// this can be changed with setAngleOffset function at runtime
+#define DEFAULT_ANGLE_OFFSET -90
+
+#define PI 3.14159265359
+
+#define MIN_POLIGON_SIDES 3
+#define MAX_POLIGON_SIDES 60
+
+// === Color names constants ===
+extern const color_t TFT_BLACK;
+extern const color_t TFT_NAVY;
+extern const color_t TFT_DARKGREEN;
+extern const color_t TFT_DARKCYAN;
+extern const color_t TFT_MAROON;
+extern const color_t TFT_PURPLE;
+extern const color_t TFT_OLIVE;
+extern const color_t TFT_LIGHTGREY;
+extern const color_t TFT_DARKGREY;
+extern const color_t TFT_BLUE;
+extern const color_t TFT_GREEN;
+extern const color_t TFT_CYAN;
+extern const color_t TFT_RED;
+extern const color_t TFT_MAGENTA;
+extern const color_t TFT_YELLOW;
+extern const color_t TFT_WHITE;
+extern const color_t TFT_ORANGE;
+extern const color_t TFT_GREENYELLOW;
+extern const color_t TFT_PINK;
+
+// === Color invert constants ===
+#define INVERT_ON 1
+#define INVERT_OFF 0
+
+// === Special coordinates constants ===
+#define CENTER -9003
+#define RIGHT -9004
+#define BOTTOM -9004
+
+#define LASTX 7000
+#define LASTY 8000
+
+// === Embedded fonts constants ===
+#define DEFAULT_FONT 0
+#define DEJAVU18_FONT 1
+#define DEJAVU24_FONT 2
+#define UBUNTU16_FONT 3
+#define COMIC24_FONT 4
+#define MINYA24_FONT 5
+#define TOONEY32_FONT 6
+#define SMALL_FONT 7
+#define DEF_SMALL_FONT 8
+#define FONT_7SEG 9
+#define USER_FONT 10 // font will be read from file
+
+
+
+// ===== PUBLIC FUNCTIONS =========================================================================
+
+
+/*
+ * Draw pixel at given x,y coordinates
+ *
+ * Params:
+ * x: horizontal position
+ * y: vertical position
+ * color: pixel color
+ * sel: if not 0 activate CS before and deactivat after sending pixel data to display
+ * when sending multiple pixels it is faster to activate the CS first,
+ * send all pixels an deactivate CS after all pixela was sent
+*/
+//-------------------------------------------------------------------
+void TFT_drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel);
+
+/*
+ * Read pixel color value from display GRAM at given x,y coordinates
+ *
+ * Params:
+ * x: horizontal position
+ * y: vertical position
+ *
+ * Returns:
+ * pixel color at x,y
+*/
+//------------------------------------------
+color_t TFT_readPixel(int16_t x, int16_t y);
+
+/*
+ * Draw vertical line at given x,y coordinates
+ *
+ * Params:
+ * x: horizontal start position
+ * y: vertical start position
+ * h: line height in pixels
+ * color: line color
+*/
+//---------------------------------------------------------------------
+void TFT_drawFastVLine(int16_t x, int16_t y, int16_t h, color_t color);
+
+/*
+ * Draw horizontal line at given x,y coordinates
+ *
+ * Params:
+ * x: horizontal start position
+ * y: vertical start position
+ * w: line width in pixels
+ * color: line color
+*/
+//---------------------------------------------------------------------
+void TFT_drawFastHLine(int16_t x, int16_t y, int16_t w, color_t color);
+
+/*
+ * Draw line on screen
+ *
+ * Params:
+ * x0: horizontal start position
+ * y0: vertical start position
+ * x1: horizontal end position
+ * y1: vertical end position
+ * color: line color
+*/
+//-------------------------------------------------------------------------------
+void TFT_drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1, color_t color);
+
+
+/*
+ * Draw line on screen from (x,y) point at given angle
+ * Line drawing angle starts at lower right quadrant of the screen and is offseted by
+ * 'tft_angleOffset' global variable (default: -90 degrees)
+ *
+ * Params:
+ * x: horizontal start position
+ * y: vertical start position
+ * start: start offset from (x,y)
+ * len: length of the line
+ * angle: line angle in degrees
+ * color: line color
+*/
+//-----------------------------------------------------------------------------------------------------------
+void TFT_drawLineByAngle(uint16_t x, uint16_t y, uint16_t start, uint16_t len, uint16_t angle, color_t color);
+
+/*
+ * Fill given rectangular screen region with color
+ *
+ * Params:
+ * x: horizontal rect start position
+ * y: vertical rect start position
+ * w: rectangle width
+ * h: rectangle height
+ * color: fill color
+*/
+//---------------------------------------------------------------------------
+void TFT_fillRect(int16_t x, int16_t y, int16_t w, int16_t h, color_t color);
+
+/*
+ * Draw rectangle on screen
+ *
+ * Params:
+ * x: horizontal rect start position
+ * y: vertical rect start position
+ * w: rectangle width
+ * h: rectangle height
+ * color: rect line color
+*/
+//------------------------------------------------------------------------------
+void TFT_drawRect(uint16_t x1,uint16_t y1,uint16_t w,uint16_t h, color_t color);
+
+/*
+ * Draw rectangle with rounded corners on screen
+ *
+ * Params:
+ * x: horizontal rect start position
+ * y: vertical rect start position
+ * w: rectangle width
+ * h: rectangle height
+ * r: corner radius
+ * color: rectangle color
+*/
+//----------------------------------------------------------------------------------------------
+void TFT_drawRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color);
+
+/*
+ * Fill given rectangular screen region with rounded corners with color
+ *
+ * Params:
+ * x: horizontal rect start position
+ * y: vertical rect start position
+ * w: rectangle width
+ * h: rectangle height
+ * r: corner radius
+ * color: fill color
+*/
+//----------------------------------------------------------------------------------------------
+void TFT_fillRoundRect(int16_t x, int16_t y, uint16_t w, uint16_t h, uint16_t r, color_t color);
+
+/*
+ * Fill the whole screen with color
+ *
+ * Params:
+ * color: fill color
+*/
+//--------------------------------
+void TFT_fillScreen(color_t color);
+
+/*
+ * Fill the current clip window with color
+ *
+ * Params:
+ * color: fill color
+*/
+//---------------------------------
+void TFT_fillWindow(color_t color);
+
+/*
+ * Draw triangle on screen
+ *
+ * Params:
+ * x0: first triangle point x position
+ * y0: first triangle point y position
+ * x0: second triangle point x position
+ * y0: second triangle point y position
+ * x0: third triangle point x position
+ * y0: third triangle point y position
+ * color: triangle color
+*/
+//-----------------------------------------------------------------------------------------------------------------
+void TFT_drawTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color);
+
+/*
+ * Fill triangular screen region with color
+ *
+ * Params:
+ * x0: first triangle point x position
+ * y0: first triangle point y position
+ * x0: second triangle point x position
+ * y0: second triangle point y position
+ * x0: third triangle point x position
+ * y0: third triangle point y position
+ * color: fill color
+*/
+//-----------------------------------------------------------------------------------------------------------------
+void TFT_fillTriangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, color_t color);
+
+/*
+ * Draw circle on screen
+ *
+ * Params:
+ * x: circle center x position
+ * y: circle center x position
+ * r: circle radius
+ * color: circle color
+*/
+//-------------------------------------------------------------------
+void TFT_drawCircle(int16_t x, int16_t y, int radius, color_t color);
+
+/*
+ * Fill circle on screen with color
+ *
+ * Params:
+ * x: circle center x position
+ * y: circle center x position
+ * r: circle radius
+ * color: circle fill color
+*/
+//-------------------------------------------------------------------
+void TFT_fillCircle(int16_t x, int16_t y, int radius, color_t color);
+
+/*
+ * Draw ellipse on screen
+ *
+ * Params:
+ * x0: ellipse center x position
+ * y0: ellipse center x position
+ * rx: ellipse horizontal radius
+ * ry: ellipse vertical radius
+ * option: drawing options, multiple options can be combined
+ 1 (TFT_ELLIPSE_UPPER_RIGHT) draw upper right corner
+ 2 (TFT_ELLIPSE_UPPER_LEFT) draw upper left corner
+ 4 (TFT_ELLIPSE_LOWER_LEFT) draw lower left corner
+ 8 (TFT_ELLIPSE_LOWER_RIGHT) draw lower right corner
+ to draw the whole ellipse use option value 15 (1 | 2 | 4 | 8)
+ *
+ * color: circle color
+*/
+//------------------------------------------------------------------------------------------------------
+void TFT_drawEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option);
+
+/*
+ * Fill elliptical region on screen
+ *
+ * Params:
+ * x0: ellipse center x position
+ * y0: ellipse center x position
+ * rx: ellipse horizontal radius
+ * ry: ellipse vertical radius
+ * option: drawing options, multiple options can be combined
+ 1 (TFT_ELLIPSE_UPPER_RIGHT) fill upper right corner
+ 2 (TFT_ELLIPSE_UPPER_LEFT) fill upper left corner
+ 4 (TFT_ELLIPSE_LOWER_LEFT) fill lower left corner
+ 8 (TFT_ELLIPSE_LOWER_RIGHT) fill lower right corner
+ to fill the whole ellipse use option value 15 (1 | 2 | 4 | 8)
+ *
+ * color: fill color
+*/
+//------------------------------------------------------------------------------------------------------
+void TFT_fillEllipse(uint16_t x0, uint16_t y0, uint16_t rx, uint16_t ry, color_t color, uint8_t option);
+
+
+/*
+ * Draw circle arc on screen
+ * Arc drawing angle starts at lower right quadrant of the screen and is offseted by
+ * 'tft_angleOffset' global variable (default: -90 degrees)
+ *
+ * Params:
+ * cx: arc center X position
+ * cy: arc center Y position
+ * th: thickness of the drawn arc
+ * ry: arc vertical radius
+ * start: arc start angle in degrees
+ * end: arc end angle in degrees
+ * color: arc outline color
+ * fillcolor: arc fill color
+*/
+//----------------------------------------------------------------------------------------------------------------------------
+void TFT_drawArc(uint16_t cx, uint16_t cy, uint16_t r, uint16_t th, float start, float end, color_t color, color_t fillcolor);
+
+
+/*
+ * Draw polygon on screen
+ *
+ * Params:
+ * cx: polygon center X position
+ * cy: arc center Y position
+ * sides: number of polygon sides; MAX_POLIGON_SIDES ~ MAX_POLIGON_SIDES (3 ~ 60)
+ * diameter: diameter of the circle inside which the polygon is drawn
+ * color: polygon outline color
+ * fill: polygon fill color; if same as color, polygon is not filled
+ * deg: polygon rotation angle; 0 ~ 360
+ * th: thickness of the polygon outline
+*/
+//--------------------------------------------------------------------------------------------------------------
+void TFT_drawPolygon(int cx, int cy, int sides, int diameter, color_t color, color_t fill, int deg, uint8_t th);
+
+
+//--------------------------------------------------------------------------------------
+//void TFT_drawStar(int cx, int cy, int diameter, color_t color, bool fill, float factor);
+
+
+/*
+ * Set the font used for writing the text to display.
+ *
+ * ------------------------------------------------------------------------------------
+ * For 7 segment font only characters 0,1,2,3,4,5,6,7,8,9, . , - , : , / are available.
+ * Character ‘/‘ draws the degree sign.
+ * ------------------------------------------------------------------------------------
+ *
+ * Params:
+ * font: font number; use defined font names
+ * font_file: pointer to font file name; NULL for embeded fonts
+ */
+//----------------------------------------------------
+void TFT_setFont(uint8_t font, const char *font_file);
+
+/*
+ * Returns current font height & width in pixels.
+ *
+ * Params:
+ * width: pointer to returned font width
+ * height: pointer to returned font height
+ */
+//-------------------------------------------
+int TFT_getfontsize(int *width, int* height);
+
+
+/*
+ * Returns current font height in pixels.
+ *
+ */
+//----------------------
+int TFT_getfontheight();
+
+/*
+ * Write text to display.
+ *
+ * Rotation of the displayed text depends on 'tft_font_rotate' variable (0~360)
+ * if 'tft_font_transparent' variable is set to 1, no background pixels will be printed
+ *
+ * If the text does not fit the screen width it will be clipped (if tft_text_wrap=0),
+ * or continued on next line (if tft_text_wrap=1)
+ *
+ * Two special characters are allowed in strings:
+ * ‘\r’ CR (0x0D), clears the display to EOL
+ * ‘\n’ LF (ox0A), continues to the new line, x=0
+ *
+ * Params:
+ * st: pointer to null terminated string to be printed
+ * x: horizontal position of the upper left point in pixels
+ * Special values can be entered:
+ * CENTER, centers the text
+ * RIGHT, right justifies the text
+ * LASTX, continues from last X position; offset can be used: LASTX+n
+ * y: vertical position of the upper left point in pixels
+ * Special values can be entered:
+ * CENTER, centers the text
+ * BOTTOM, bottom justifies the text
+ * LASTY, continues from last Y position; offset can be used: LASTY+n
+ *
+ */
+//-------------------------------------
+void TFT_print(char *st, int x, int y);
+
+/*
+ * Set atributes for 7 segment vector font
+ * == 7 segment font must be the current font to this function to have effect ==
+ *
+ * Params:
+ * l: 6~40; distance between bars in pixels
+ * w: 1~12, max l/2; bar width in pixels
+ * outline: draw font outline if set to 1
+ * color: font outline color, only used if outline=1
+ *
+ */
+//-------------------------------------------------------------------------
+void set_7seg_font_atrib(uint8_t l, uint8_t w, int outline, color_t color);
+
+/*
+ * Sets the clipping area coordinates.
+ * All writing to screen is clipped to that area.
+ * Starting x & y in all functions will be adjusted to the clipping area.
+ *
+ * Params:
+ * x1,y1: upper left point of the clipping area
+ * x2,y2: bottom right point of the clipping area
+ *
+ */
+//----------------------------------------------------------------------
+void TFT_setclipwin(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2);
+
+/*
+ * Resets the clipping area to full screen (0,0),(_wodth,tft_height)
+ *
+ */
+//----------------------
+void TFT_resetclipwin();
+
+/*
+ * Save current clipping area to temporary variable
+ *
+ */
+//---------------------
+void TFT_saveClipWin();
+
+/*
+ * Restore current clipping area from temporary variable
+ *
+ */
+//------------------------
+void TFT_restoreClipWin();
+
+/*
+ * Set the screen rotation
+ * Also resets the clip window and clears the screen with current background color
+ *
+ * Params:
+ * rot: 0~3; screen rotation; use defined rotation constants:
+ * PORTRAIT, LANDSCAPE, PORTRAIT_FLIP, LANDSCAPE_FLIP
+ *
+ */
+//--------------------------------
+void TFT_setRotation(uint8_t rot);
+
+/*
+ * Set inverted/normal colors
+ *
+ * Params:
+ * mode: 0 or 1; use defined constants: INVERT_ON or INVERT_OFF
+ *
+ */
+//-----------------------------------------
+void TFT_invertDisplay(const uint8_t mode);
+
+/*
+ * Select gamma curve
+ * Params:
+ * gamma: gama curve, values 0~3
+ */
+//=================================
+void TFT_setGammaCurve(uint8_t gm);
+
+/*
+ * Compare two color structures
+ * Returns 0 if equal, 1 if not equal
+ *
+ * Params:
+ * c1, c2: colors to be compared
+ */
+//---------------------------------------------
+int TFT_compare_colors(color_t c1, color_t c2);
+
+/*
+ * returns the string width in pixels.
+ * Useful for positions strings on the screen.
+ */
+//--------------------------------
+int TFT_getStringWidth(char* str);
+
+
+/*
+ * Fills the rectangle occupied by string with current background color
+ */
+void TFT_clearStringRect(int x, int y, char *str);
+
+/*
+ * Converts the components of a color, as specified by the HSB model,
+ * to an equivalent set of values for the default RGB model.
+
+ * The color structure that is returned by HSBtoRGB encodes the value of a color as R, G & B component
+ *
+ * Params:
+ * _hue: float; any number, the floor of this number is subtracted from it to create a fraction between 0 and 1.
+ * This fractional number is then multiplied by 360 to produce the hue angle in the HSB color model.
+ * _sat: float; 0 ~ 1.0
+ * _brightness: float; 0 ~ 1.0
+ *
+*/
+//----------------------------------------------------------
+color_t HSBtoRGB(float _hue, float _sat, float _brightness);
+
+/*
+ * Decodes and displays JPG image
+ * Limits:
+ * Baseline only. Progressive and Lossless JPEG format are not supported.
+ * Image size: Up to 65520 x 65520 pixels
+ * Color space: YCbCr three components only. Gray scale image is not supported.
+ * Sampling factor: 4:4:4, 4:2:2 or 4:2:0.
+ *
+ * Params:
+ * x: image left position; constants CENTER & RIGHT can be used; negative value is accepted
+ * y: image top position; constants CENTER & BOTTOM can be used; negative value is accepted
+ * scale: image scale factor: 0~3; if scale>0, image is scaled by factor 1/(2^scale) (1/2, 1/4 or 1/8)
+ * fname: pointer to the name of the file from which the image will be read
+ * if set to NULL, image will be read from memory buffer pointed to by 'buf'
+ * buf: pointer to the memory buffer from which the image will be read; used if fname=NULL
+ * size: size of the memory buffer from which the image will be read; used if fname=NULL & buf!=NULL
+ *
+ */
+//-----------------------------------------------------------------------------------
+void TFT_jpg_image(int x, int y, uint8_t scale, char *fname, uint8_t *buf, int size);
+
+/*
+ * Decodes and displays BMP image
+ * Only uncompressed RGB 24-bit with no color space information BMP images can be displayed
+ *
+ * Params:
+ * x: image left position; constants CENTER & RIGHT can be used; negative value is accepted
+ * y: image top position; constants CENTER & BOTTOM can be used; negative value is accepted
+ * scale: image scale factor: 0~7; if scale>0, image is scaled by factor 1/(scale+1)
+ * fname: pointer to the name of the file from which the image will be read
+ * if set to NULL, image will be read from memory buffer pointed to by 'imgbuf'
+ * imgbuf: pointer to the memory buffer from which the image will be read; used if fname=NULL
+ * size: size of the memory buffer from which the image will be read; used if fname=NULL & imgbuf!=NULL
+ *
+ */
+//-------------------------------------------------------------------------------------
+int TFT_bmp_image(int x, int y, uint8_t scale, char *fname, uint8_t *imgbuf, int size);
+
+/*
+ * Get the touch panel coordinates.
+ * The coordinates are adjusted to screen tft_orientation if raw=0
+ *
+ * Params:
+ * x: pointer to X coordinate
+ * y: pointer to Y coordinate
+ * raw: if 0 returns calibrated screen coordinates; if 1 returns raw touch controller coordinates
+ *
+ * Returns:
+ * 0 if touch panel is not touched; x=y=0
+ * 1 if touch panel is touched; x&y are the valid coordinates
+ */
+//----------------------------------------------
+int TFT_read_touch(int *x, int* y, uint8_t raw);
+
+
+/*
+ * Compile font c source file to .fnt file
+ * which can be used in TFT_setFont() function to select external font
+ * Created file have the same name as source file and extension .fnt
+ *
+ * Params:
+ * fontfile: pointer to c source font file name; must have .c extension
+ * dbg: if set to 1, prints debug information
+ *
+ * Returns:
+ * 0 on success
+ * err no on error
+ *
+ */
+//------------------------------------------------
+int compile_font_file(char *fontfile, uint8_t dbg);
+
+/*
+ * Get all font's characters to buffer
+ */
+void getFontCharacters(uint8_t *buf);
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/components/tft/tftspi.c b/components/tft/tftspi.c
new file mode 100644
index 0000000..d9e6e2b
--- /dev/null
+++ b/components/tft/tftspi.c
@@ -0,0 +1,947 @@
+/*
+ * Author: LoBo (loboris@gmail.com, loboris.github)
+ *
+ * Module supporting SPI TFT displays based on ILI9341 & ILI9488 controllers
+ *
+ * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS
+ * USING DIRECT or DMA SPI TRANSFER MODEs
+ *
+*/
+
+#include
+#include "tftspi.h"
+#include "freertos/task.h"
+#include "soc/spi_reg.h"
+#include "driver/gpio.h"
+
+
+// ====================================================
+// ==== Global variables, default values ==============
+
+// Converts colors to grayscale if set to 1
+uint8_t tft_gray_scale = 0;
+// Spi clock for reading data from display memory in Hz
+uint32_t tft_max_rdclock = 8000000;
+
+// Default display dimensions
+int tft_width = DEFAULT_TFT_DISPLAY_WIDTH;
+int tft_height = DEFAULT_TFT_DISPLAY_HEIGHT;
+
+// Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341
+uint8_t tft_disp_type = DEFAULT_DISP_TYPE;
+
+// Spi device handles for display and touch screen
+spi_lobo_device_handle_t tft_disp_spi = NULL;
+spi_lobo_device_handle_t tft_ts_spi = NULL;
+
+// ====================================================
+
+
+static color_t *trans_cline = NULL;
+static uint8_t _dma_sending = 0;
+
+// RGB to GRAYSCALE constants
+// 0.2989 0.5870 0.1140
+#define GS_FACT_R 0.2989
+#define GS_FACT_G 0.4870
+#define GS_FACT_B 0.2140
+
+
+
+// ==== Functions =====================
+
+//------------------------------------------------------
+esp_err_t IRAM_ATTR wait_trans_finish(uint8_t free_line)
+{
+ // Wait for SPI bus ready
+ while (tft_disp_spi->host->hw->cmd.usr);
+ if ((free_line) && (trans_cline)) {
+ free(trans_cline);
+ trans_cline = NULL;
+ }
+ if (_dma_sending) {
+ //Tell common code DMA workaround that our DMA channel is idle. If needed, the code will do a DMA reset.
+ if (tft_disp_spi->host->dma_chan) spi_lobo_dmaworkaround_idle(tft_disp_spi->host->dma_chan);
+
+ // Reset DMA
+ tft_disp_spi->host->hw->dma_conf.val |= SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST;
+ tft_disp_spi->host->hw->dma_out_link.start=0;
+ tft_disp_spi->host->hw->dma_in_link.start=0;
+ tft_disp_spi->host->hw->dma_conf.val &= ~(SPI_OUT_RST|SPI_IN_RST|SPI_AHBM_RST|SPI_AHBM_FIFO_RST);
+ tft_disp_spi->host->hw->dma_conf.out_data_burst_en=1;
+ _dma_sending = 0;
+ }
+ return ESP_OK;
+}
+
+//-------------------------------
+esp_err_t IRAM_ATTR disp_select()
+{
+ wait_trans_finish(1);
+ return spi_lobo_device_select(tft_disp_spi, 0);
+}
+
+//---------------------------------
+esp_err_t IRAM_ATTR disp_deselect()
+{
+ wait_trans_finish(1);
+ return spi_lobo_device_deselect(tft_disp_spi);
+}
+
+//---------------------------------------------------------------------------------------------------
+static void IRAM_ATTR _spi_transfer_start(spi_lobo_device_handle_t spi_dev, int wrbits, int rdbits) {
+ // Load send buffer
+ spi_dev->host->hw->user.usr_mosi_highpart = 0;
+ spi_dev->host->hw->mosi_dlen.usr_mosi_dbitlen = wrbits-1;
+ spi_dev->host->hw->user.usr_mosi = 1;
+ if (rdbits) {
+ spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = rdbits;
+ spi_dev->host->hw->user.usr_miso = 1;
+ }
+ else {
+ spi_dev->host->hw->miso_dlen.usr_miso_dbitlen = 0;
+ spi_dev->host->hw->user.usr_miso = 0;
+ }
+ // Start transfer
+ spi_dev->host->hw->cmd.usr = 1;
+ // Wait for SPI bus ready
+ while (spi_dev->host->hw->cmd.usr);
+}
+
+// Send 1 byte display command, display must be selected
+//------------------------------------------------
+void IRAM_ATTR disp_spi_transfer_cmd(int8_t cmd) {
+ // Wait for SPI bus ready
+ while (tft_disp_spi->host->hw->cmd.usr);
+
+ // Set DC to 0 (command mode);
+ gpio_set_level(PIN_NUM_DC, 0);
+
+ tft_disp_spi->host->hw->data_buf[0] = (uint32_t)cmd;
+ _spi_transfer_start(tft_disp_spi, 8, 0);
+}
+
+// Send command with data to display, display must be selected
+//----------------------------------------------------------------------------------
+void IRAM_ATTR disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t len) {
+ // Wait for SPI bus ready
+ while (tft_disp_spi->host->hw->cmd.usr);
+
+ // Set DC to 0 (command mode);
+ gpio_set_level(PIN_NUM_DC, 0);
+
+ tft_disp_spi->host->hw->data_buf[0] = (uint32_t)cmd;
+ _spi_transfer_start(tft_disp_spi, 8, 0);
+
+ if ((len == 0) || (data == NULL)) return;
+
+ // Set DC to 1 (data mode);
+ gpio_set_level(PIN_NUM_DC, 1);
+
+ uint8_t idx=0, bidx=0;
+ uint32_t bits=0;
+ uint32_t count=0;
+ uint32_t wd = 0;
+ while (count < len) {
+ // get data byte from buffer
+ wd |= (uint32_t)data[count] << bidx;
+ count++;
+ bits += 8;
+ bidx += 8;
+ if (count == len) {
+ tft_disp_spi->host->hw->data_buf[idx] = wd;
+ break;
+ }
+ if (bidx == 32) {
+ tft_disp_spi->host->hw->data_buf[idx] = wd;
+ idx++;
+ bidx = 0;
+ wd = 0;
+ }
+ if (idx == 16) {
+ // SPI buffer full, send data
+ _spi_transfer_start(tft_disp_spi, bits, 0);
+
+ bits = 0;
+ idx = 0;
+ bidx = 0;
+ }
+ }
+ if (bits > 0) _spi_transfer_start(tft_disp_spi, bits, 0);
+}
+
+// Set the address window for display write & read commands, display must be selected
+//---------------------------------------------------------------------------------------------------
+static void IRAM_ATTR disp_spi_transfer_addrwin(uint16_t x1, uint16_t x2, uint16_t y1, uint16_t y2) {
+ uint32_t wd;
+
+ taskDISABLE_INTERRUPTS();
+ // Wait for SPI bus ready
+ while (tft_disp_spi->host->hw->cmd.usr);
+ gpio_set_level(PIN_NUM_DC, 0);
+
+ tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_CASET;
+ tft_disp_spi->host->hw->user.usr_mosi_highpart = 0;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7;
+ tft_disp_spi->host->hw->user.usr_mosi = 1;
+ tft_disp_spi->host->hw->miso_dlen.usr_miso_dbitlen = 0;
+ tft_disp_spi->host->hw->user.usr_miso = 0;
+
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+
+ wd = (uint32_t)(x1>>8);
+ wd |= (uint32_t)(x1&0xff) << 8;
+ wd |= (uint32_t)(x2>>8) << 16;
+ wd |= (uint32_t)(x2&0xff) << 24;
+
+ while (tft_disp_spi->host->hw->cmd.usr); // wait transfer end
+ gpio_set_level(PIN_NUM_DC, 1);
+ tft_disp_spi->host->hw->data_buf[0] = wd;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31;
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+
+ while (tft_disp_spi->host->hw->cmd.usr);
+ gpio_set_level(PIN_NUM_DC, 0);
+ tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_PASET;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7;
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+
+ wd = (uint32_t)(y1>>8);
+ wd |= (uint32_t)(y1&0xff) << 8;
+ wd |= (uint32_t)(y2>>8) << 16;
+ wd |= (uint32_t)(y2&0xff) << 24;
+
+ while (tft_disp_spi->host->hw->cmd.usr);
+ gpio_set_level(PIN_NUM_DC, 1);
+
+ tft_disp_spi->host->hw->data_buf[0] = wd;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 31;
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+ while (tft_disp_spi->host->hw->cmd.usr);
+ taskENABLE_INTERRUPTS();
+}
+
+// Convert color to gray scale
+//----------------------------------------------
+static color_t IRAM_ATTR color2gs(color_t color)
+{
+ color_t _color;
+ float gs_clr = GS_FACT_R * color.r + GS_FACT_G * color.g + GS_FACT_B * color.b;
+ if (gs_clr > 255) gs_clr = 255;
+
+ _color.r = (uint8_t)gs_clr;
+ _color.g = (uint8_t)gs_clr;
+ _color.b = (uint8_t)gs_clr;
+
+ return _color;
+}
+
+// Set display pixel at given coordinates to given color
+//------------------------------------------------------------------------
+void IRAM_ATTR drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel)
+{
+ if (!(tft_disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return;
+
+ if (sel) {
+ if (disp_select()) return;
+ }
+ else wait_trans_finish(1);
+
+ uint32_t wd = 0;
+ color_t _color = color;
+ if (tft_gray_scale) _color = color2gs(color);
+
+ taskDISABLE_INTERRUPTS();
+ disp_spi_transfer_addrwin(x, x+1, y, y+1);
+
+ // Send RAM WRITE command
+ gpio_set_level(PIN_NUM_DC, 0);
+ tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7;
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+ while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready
+
+ wd = (uint32_t)_color.r;
+ wd |= (uint32_t)_color.g << 8;
+ wd |= (uint32_t)_color.b << 16;
+
+ // Set DC to 1 (data mode);
+ gpio_set_level(PIN_NUM_DC, 1);
+
+ tft_disp_spi->host->hw->data_buf[0] = wd;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 23;
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+ while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready
+
+ taskENABLE_INTERRUPTS();
+ if (sel) disp_deselect();
+}
+
+//-----------------------------------------------------------
+static void IRAM_ATTR _dma_send(uint8_t *data, uint32_t size)
+{
+ //Fill DMA descriptors
+ spi_lobo_dmaworkaround_transfer_active(tft_disp_spi->host->dma_chan); //mark channel as active
+ spi_lobo_setup_dma_desc_links(tft_disp_spi->host->dmadesc_tx, size, data, false);
+ tft_disp_spi->host->hw->user.usr_mosi_highpart=0;
+ tft_disp_spi->host->hw->dma_out_link.addr=(int)(&tft_disp_spi->host->dmadesc_tx[0]) & 0xFFFFF;
+ tft_disp_spi->host->hw->dma_out_link.start=1;
+ tft_disp_spi->host->hw->user.usr_mosi_highpart=0;
+
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = (size * 8) - 1;
+
+ _dma_sending = 1;
+ // Start transfer
+ tft_disp_spi->host->hw->cmd.usr = 1;
+}
+
+//---------------------------------------------------------------------------
+static void IRAM_ATTR _direct_send(color_t *color, uint32_t len, uint8_t rep)
+{
+ uint32_t cidx = 0; // color buffer index
+ uint32_t wd = 0;
+ int idx = 0;
+ int bits = 0;
+ int wbits = 0;
+
+ taskDISABLE_INTERRUPTS();
+ color_t _color = color[0];
+ if ((rep) && (tft_gray_scale)) _color = color2gs(color[0]);
+
+ while (len) {
+ // ** Get color data from color buffer **
+ if (rep == 0) {
+ if (tft_gray_scale) _color = color2gs(color[cidx]);
+ else _color = color[cidx];
+ }
+
+ wd |= (uint32_t)_color.r << wbits;
+ wbits += 8;
+ if (wbits == 32) {
+ bits += wbits;
+ wbits = 0;
+ tft_disp_spi->host->hw->data_buf[idx++] = wd;
+ wd = 0;
+ }
+ wd |= (uint32_t)_color.g << wbits;
+ wbits += 8;
+ if (wbits == 32) {
+ bits += wbits;
+ wbits = 0;
+ tft_disp_spi->host->hw->data_buf[idx++] = wd;
+ wd = 0;
+ }
+ wd |= (uint32_t)_color.b << wbits;
+ wbits += 8;
+ if (wbits == 32) {
+ bits += wbits;
+ wbits = 0;
+ tft_disp_spi->host->hw->data_buf[idx++] = wd;
+ wd = 0;
+ }
+ len--; // Decrement colors counter
+ if (rep == 0) cidx++; // if not repeating color, increment color buffer index
+ }
+ if (bits) {
+ while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = bits-1; // set number of bits to be sent
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+ }
+ taskENABLE_INTERRUPTS();
+}
+
+// ================================================================
+// === Main function to send data to display ======================
+// If rep==true: repeat sending color data to display 'len' times
+// If rep==false: send 'len' color data from color buffer to display
+// ** Device must already be selected and address window set **
+// ================================================================
+//----------------------------------------------------------------------------------------------
+static void IRAM_ATTR _TFT_pushColorRep(color_t *color, uint32_t len, uint8_t rep, uint8_t wait)
+{
+ if (len == 0) return;
+ if (!(tft_disp_spi->cfg.flags & LB_SPI_DEVICE_HALFDUPLEX)) return;
+
+ // Send RAM WRITE command
+ gpio_set_level(PIN_NUM_DC, 0);
+ tft_disp_spi->host->hw->data_buf[0] = (uint32_t)TFT_RAMWR;
+ tft_disp_spi->host->hw->mosi_dlen.usr_mosi_dbitlen = 7;
+ tft_disp_spi->host->hw->cmd.usr = 1; // Start transfer
+ while (tft_disp_spi->host->hw->cmd.usr); // Wait for SPI bus ready
+
+ gpio_set_level(PIN_NUM_DC, 1); // Set DC to 1 (data mode);
+
+ if ((len*24) <= 512) {
+
+ _direct_send(color, len, rep);
+
+ }
+ else if (rep == 0) {
+ // ==== use DMA transfer ====
+ // ** Prepare data
+ if (tft_gray_scale) {
+ for (int n=0; n 0) {
+ wait_trans_finish(0);
+ _direct_send(color, ((to_send > 21) ? 21 : to_send), rep);
+ to_send -= 21;
+ }
+ */
+
+ buf_colors = ((len > (tft_width*2)) ? (tft_width*2) : len);
+ buf_bytes = buf_colors * 3;
+
+ // Prepare color buffer of maximum 2 color lines
+ trans_cline = heap_caps_malloc(buf_bytes, MALLOC_CAP_DMA);
+ if (trans_cline == NULL) return;
+
+ // Prepare fill color
+ if (tft_gray_scale) _color = color2gs(color[0]);
+ else _color = color[0];
+
+ // Fill color buffer with fill color
+ for (uint32_t i=0; i 0) {
+ wait_trans_finish(0);
+ _dma_send((uint8_t *)trans_cline, ((to_send > buf_colors) ? buf_bytes : (to_send*3)));
+ to_send -= buf_colors;
+ }
+ }
+
+ if (wait) wait_trans_finish(1);
+}
+
+// Write 'len' color data to TFT 'window' (x1,y2),(x2,y2)
+//-------------------------------------------------------------------------------------------
+void IRAM_ATTR TFT_pushColorRep(int x1, int y1, int x2, int y2, color_t color, uint32_t len)
+{
+ if (disp_select() != ESP_OK) return;
+
+ // ** Send address window **
+ disp_spi_transfer_addrwin(x1, x2, y1, y2);
+
+ _TFT_pushColorRep(&color, len, 1, 1);
+
+ disp_deselect();
+}
+
+// Write 'len' color data to TFT 'window' (x1,y2),(x2,y2) from given buffer
+// ** Device must already be selected **
+//-----------------------------------------------------------------------------------
+void IRAM_ATTR send_data(int x1, int y1, int x2, int y2, uint32_t len, color_t *buf)
+{
+ // ** Send address window **
+ disp_spi_transfer_addrwin(x1, x2, y1, y2);
+ _TFT_pushColorRep(buf, len, 0, 0);
+}
+
+// Reads 'len' pixels/colors from the TFT's GRAM 'window'
+// 'buf' is an array of bytes with 1st byte reserved for reading 1 dummy byte
+// and the rest is actually an array of color_t values
+//--------------------------------------------------------------------------------------------
+int IRAM_ATTR read_data(int x1, int y1, int x2, int y2, int len, uint8_t *buf, uint8_t set_sp)
+{
+ spi_lobo_transaction_t t;
+ uint32_t current_clock = 0;
+
+ memset(&t, 0, sizeof(t)); //Zero out the transaction
+ memset(buf, 0, len*sizeof(color_t));
+
+ if (set_sp) {
+ if (disp_deselect() != ESP_OK) return -1;
+ // Change spi clock if needed
+ current_clock = spi_lobo_get_speed(tft_disp_spi);
+ if (tft_max_rdclock < current_clock) spi_lobo_set_speed(tft_disp_spi, tft_max_rdclock);
+ }
+
+ if (disp_select() != ESP_OK) return -2;
+
+ // ** Send address window **
+ disp_spi_transfer_addrwin(x1, x2, y1, y2);
+
+ // ** GET pixels/colors **
+ disp_spi_transfer_cmd(TFT_RAMRD);
+
+ t.length=0; //Send nothing
+ t.tx_buffer=NULL;
+ t.rxlength=8*((len*3)+1); //Receive size in bits
+ t.rx_buffer=buf;
+ //t.user = (void*)1;
+
+ esp_err_t res = spi_lobo_transfer_data(tft_disp_spi, &t); // Receive using direct mode
+
+ disp_deselect();
+
+ if (set_sp) {
+ // Restore spi clock if needed
+ if (tft_max_rdclock < current_clock) spi_lobo_set_speed(tft_disp_spi, current_clock);
+ }
+
+ return res;
+}
+
+// Reads one pixel/color from the TFT's GRAM at position (x,y)
+//-----------------------------------------------
+color_t IRAM_ATTR readPixel(int16_t x, int16_t y)
+{
+ uint8_t color_buf[sizeof(color_t)+1] = {0};
+
+ read_data(x, y, x+1, y+1, 1, color_buf, 1);
+
+ color_t color;
+ color.r = color_buf[1];
+ color.g = color_buf[2];
+ color.b = color_buf[3];
+ return color;
+}
+
+// get 16-bit data from touch controller for specified type
+// ** Touch device must already be selected **
+//----------------------------------------
+int IRAM_ATTR touch_get_data(uint8_t type)
+{
+ /*
+ esp_err_t ret;
+ spi_lobo_transaction_t t;
+ memset(&t, 0, sizeof(t)); //Zero out the transaction
+ uint8_t rxdata[2] = {0};
+
+ // send command byte & receive 2 byte response
+ t.rxlength=8*2;
+ t.rx_buffer=&rxdata;
+ t.command = type;
+
+ ret = spi_lobo_transfer_data(tft_ts_spi, &t); // Transmit using direct mode
+
+ if (ret != ESP_OK) return -1;
+ return (((int)(rxdata[0] << 8) | (int)(rxdata[1])) >> 4);
+ */
+ spi_lobo_device_select(tft_ts_spi, 0);
+
+ tft_ts_spi->host->hw->data_buf[0] = type;
+ _spi_transfer_start(tft_ts_spi, 24, 24);
+ uint16_t res = (uint16_t)(tft_ts_spi->host->hw->data_buf[0] >> 8);
+
+ spi_lobo_device_deselect(tft_ts_spi);
+
+ return res;
+}
+
+// ==== STMPE610 ===============================================================
+
+
+// ----- STMPE610 --------------------------------------------------------------------------
+
+// Send 1 byte display command, display must be selected
+//---------------------------------------------------------
+static void IRAM_ATTR stmpe610_write_reg(uint8_t reg, uint8_t val) {
+
+ spi_lobo_device_select(tft_ts_spi, 0);
+
+ tft_ts_spi->host->hw->data_buf[0] = (val << 8) | reg;
+ _spi_transfer_start(tft_ts_spi, 16, 0);
+
+ spi_lobo_device_deselect(tft_ts_spi);
+}
+
+//-----------------------------------------------
+static uint8_t IRAM_ATTR stmpe610_read_byte(uint8_t reg) {
+ spi_lobo_device_select(tft_ts_spi, 0);
+
+ tft_ts_spi->host->hw->data_buf[0] = (reg << 8) | (reg | 0x80);
+ _spi_transfer_start(tft_ts_spi, 16, 16);
+ uint8_t res = tft_ts_spi->host->hw->data_buf[0] >> 8;
+
+ spi_lobo_device_deselect(tft_ts_spi);
+ return res;
+}
+
+//-----------------------------------------
+static uint16_t IRAM_ATTR stmpe610_read_word(uint8_t reg) {
+ spi_lobo_device_select(tft_ts_spi, 0);
+
+ tft_ts_spi->host->hw->data_buf[0] = ((((reg+1) << 8) | ((reg+1) | 0x80)) << 16) | (reg << 8) | (reg | 0x80);
+ _spi_transfer_start(tft_ts_spi, 32, 32);
+ uint16_t res = (uint16_t)(tft_ts_spi->host->hw->data_buf[0] & 0xFF00);
+ res |= (uint16_t)(tft_ts_spi->host->hw->data_buf[0] >> 24);
+
+ spi_lobo_device_deselect(tft_ts_spi);
+ return res;
+}
+
+//-----------------------
+uint32_t stmpe610_getID()
+{
+ uint16_t tid = stmpe610_read_word(0);
+ uint8_t tver = stmpe610_read_byte(2);
+ return (tid << 8) | tver;
+}
+
+//==================
+void stmpe610_Init()
+{
+ stmpe610_write_reg(STMPE610_REG_SYS_CTRL1, 0x02); // Software chip reset
+ vTaskDelay(10 / portTICK_RATE_MS);
+
+ stmpe610_write_reg(STMPE610_REG_SYS_CTRL2, 0x04); // Temperature sensor clock off, GPIO clock off, touch clock on, ADC clock on
+
+ stmpe610_write_reg(STMPE610_REG_INT_EN, 0x00); // Don't Interrupt on INT pin
+
+ stmpe610_write_reg(STMPE610_REG_ADC_CTRL1, 0x48); // ADC conversion time = 80 clock ticks, 12-bit ADC, internal voltage refernce
+ vTaskDelay(2 / portTICK_RATE_MS);
+ stmpe610_write_reg(STMPE610_REG_ADC_CTRL2, 0x01); // ADC speed 3.25MHz
+ stmpe610_write_reg(STMPE610_REG_GPIO_AF, 0x00); // GPIO alternate function - OFF
+ stmpe610_write_reg(STMPE610_REG_TSC_CFG, 0xE3); // Averaging 8, touch detect delay 1ms, panel driver settling time 1ms
+ stmpe610_write_reg(STMPE610_REG_FIFO_TH, 0x01); // FIFO threshold = 1
+ stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x01); // FIFO reset enable
+ stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x00); // FIFO reset disable
+ stmpe610_write_reg(STMPE610_REG_TSC_FRACT_XYZ, 0x07); // Z axis data format
+ stmpe610_write_reg(STMPE610_REG_TSC_I_DRIVE, 0x01); // max 50mA touchscreen line current
+ stmpe610_write_reg(STMPE610_REG_TSC_CTRL, 0x30); // X&Y&Z, 16 reading window
+ stmpe610_write_reg(STMPE610_REG_TSC_CTRL, 0x31); // X&Y&Z, 16 reading window, TSC enable
+ stmpe610_write_reg(STMPE610_REG_INT_STA, 0xFF); // Clear all interrupts
+ stmpe610_write_reg(STMPE610_REG_INT_CTRL, 0x00); // Level interrupt, disable interrupts
+}
+
+//===========================================================
+int stmpe610_get_touch(uint16_t *x, uint16_t *y, uint16_t *z)
+{
+ if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0;
+
+ // Get touch data
+ uint8_t fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE);
+ while (fifo_size < 2) {
+ if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0;
+ fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE);
+ }
+ while (fifo_size > 120) {
+ if (!(stmpe610_read_byte(STMPE610_REG_TSC_CTRL) & 0x80)) return 0;
+ *x = stmpe610_read_word(STMPE610_REG_TSC_DATA_X);
+ *y = stmpe610_read_word(STMPE610_REG_TSC_DATA_Y);
+ *z = stmpe610_read_byte(STMPE610_REG_TSC_DATA_Z);
+ fifo_size = stmpe610_read_byte(STMPE610_REG_FIFO_SIZE);
+ }
+ for (uint8_t i=0; i < (fifo_size-1); i++) {
+ *x = stmpe610_read_word(STMPE610_REG_TSC_DATA_X);
+ *y = stmpe610_read_word(STMPE610_REG_TSC_DATA_Y);
+ *z = stmpe610_read_byte(STMPE610_REG_TSC_DATA_Z);
+ }
+
+ *x = 4096 - *x;
+ /*
+ // Clear the rest of the fifo
+ {
+ stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x01); // FIFO reset enable
+ stmpe610_write_reg(STMPE610_REG_FIFO_STA, 0x00); // FIFO reset disable
+ }
+ */
+ return 1;
+}
+
+// ==== STMPE610 ===========================================================================
+
+
+// Find maximum spi clock for successful read from display RAM
+// ** Must be used AFTER the display is initialized **
+//======================
+uint32_t find_rd_speed()
+{
+ esp_err_t ret;
+ color_t color;
+ uint32_t max_speed = 1000000;
+ uint32_t change_speed, cur_speed;
+ int line_check;
+ color_t *color_line = NULL;
+ uint8_t *line_rdbuf = NULL;
+ uint8_t gs = tft_gray_scale;
+
+ tft_gray_scale = 0;
+ cur_speed = spi_lobo_get_speed(tft_disp_spi);
+
+ color_line = malloc(tft_width*3);
+ if (color_line == NULL) goto exit;
+
+ line_rdbuf = malloc((tft_width*3)+1);
+ if (line_rdbuf == NULL) goto exit;
+
+ color_t *rdline = (color_t *)(line_rdbuf+1);
+
+ // Fill test line with colors
+ color = (color_t){0xEC,0xA8,0x74};
+ for (int x=0; x height
+ if (tft_width < tft_height) {
+ tmp = tft_width;
+ tft_width = tft_height;
+ tft_height = tmp;
+ }
+ }
+ else {
+ // in portrait modes must be width < height
+ if (tft_width > tft_height) {
+ tmp = tft_width;
+ tft_width = tft_height;
+ tft_height = tmp;
+ }
+ }
+ #if TFT_INVERT_ROTATION
+ switch (rotation) {
+ case PORTRAIT:
+ madctl = (MADCTL_MV | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE:
+ madctl = (MADCTL_MX | TFT_RGB_BGR);
+ break;
+ case PORTRAIT_FLIP:
+ madctl = (MADCTL_MV | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE_FLIP:
+ madctl = (MADCTL_MY | TFT_RGB_BGR);
+ break;
+ }
+ #elif TFT_INVERT_ROTATION1
+ switch (rotation) {
+ case PORTRAIT:
+ madctl = (MADCTL_MY | MADCTL_MX | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE:
+ madctl = (MADCTL_MY | MADCTL_MV | TFT_RGB_BGR);
+ break;
+ case PORTRAIT_FLIP:
+ madctl = (TFT_RGB_BGR);
+ break;
+ case LANDSCAPE_FLIP:
+ madctl = (MADCTL_MX | MADCTL_MV | TFT_RGB_BGR);
+ break;
+ }
+ #elif TFT_INVERT_ROTATION2
+ switch (rotation) {
+ case PORTRAIT:
+ madctl = (MADCTL_MX | MADCTL_MV | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE:
+ madctl = (TFT_RGB_BGR);
+ break;
+ case PORTRAIT_FLIP:
+ madctl = (MADCTL_MY | MADCTL_MV | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE_FLIP:
+ madctl = (MADCTL_MY | MADCTL_MX | TFT_RGB_BGR);
+ break;
+ }
+ #else
+ switch (rotation) {
+ case PORTRAIT:
+ madctl = (MADCTL_MX | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE:
+ madctl = (MADCTL_MV | TFT_RGB_BGR);
+ break;
+ case PORTRAIT_FLIP:
+ madctl = (MADCTL_MY | TFT_RGB_BGR);
+ break;
+ case LANDSCAPE_FLIP:
+ madctl = (MADCTL_MX | MADCTL_MY | MADCTL_MV | TFT_RGB_BGR);
+ break;
+ }
+ #endif
+ if (send) {
+ if (disp_select() == ESP_OK) {
+ disp_spi_transfer_cmd_data(TFT_MADCTL, &madctl, 1);
+ disp_deselect();
+ }
+ }
+
+}
+
+//=================
+void TFT_PinsInit()
+{
+ // Route all used pins to GPIO control
+ gpio_pad_select_gpio(PIN_NUM_CS);
+ gpio_pad_select_gpio(PIN_NUM_MISO);
+ gpio_pad_select_gpio(PIN_NUM_MOSI);
+ gpio_pad_select_gpio(PIN_NUM_CLK);
+ gpio_pad_select_gpio(PIN_NUM_DC);
+
+ gpio_set_direction(PIN_NUM_MISO, GPIO_MODE_INPUT);
+ gpio_set_pull_mode(PIN_NUM_MISO, GPIO_PULLUP_ONLY);
+ gpio_set_direction(PIN_NUM_CS, GPIO_MODE_OUTPUT);
+ gpio_set_direction(PIN_NUM_MOSI, GPIO_MODE_OUTPUT);
+ gpio_set_direction(PIN_NUM_CLK, GPIO_MODE_OUTPUT);
+ gpio_set_direction(PIN_NUM_DC, GPIO_MODE_OUTPUT);
+ gpio_set_level(PIN_NUM_DC, 0);
+#if USE_TOUCH
+ gpio_pad_select_gpio(PIN_NUM_TCS);
+ gpio_set_direction(PIN_NUM_TCS, GPIO_MODE_OUTPUT);
+#endif
+#if PIN_NUM_BCKL
+ gpio_pad_select_gpio(PIN_NUM_BCKL);
+ gpio_set_direction(PIN_NUM_BCKL, GPIO_MODE_OUTPUT);
+ gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_OFF);
+#endif
+
+#if PIN_NUM_RST
+ gpio_pad_select_gpio(PIN_NUM_RST);
+ gpio_set_direction(PIN_NUM_RST, GPIO_MODE_OUTPUT);
+ gpio_set_level(PIN_NUM_RST, 0);
+#endif
+}
+
+// Initialize the display
+// ====================
+void TFT_display_init()
+{
+ esp_err_t ret;
+
+#if PIN_NUM_RST
+ //Reset the display
+ gpio_set_level(PIN_NUM_RST, 0);
+ vTaskDelay(20 / portTICK_RATE_MS);
+ gpio_set_level(PIN_NUM_RST, 1);
+ vTaskDelay(150 / portTICK_RATE_MS);
+#endif
+
+ ret = disp_select();
+ assert(ret==ESP_OK);
+ //Send all the initialization commands
+ if (tft_disp_type == DISP_TYPE_ILI9341) {
+ commandList(tft_disp_spi, ILI9341_init);
+ }
+ else if (tft_disp_type == DISP_TYPE_ILI9488) {
+ commandList(tft_disp_spi, ILI9488_init);
+ }
+ else if (tft_disp_type == DISP_TYPE_ST7789V) {
+ commandList(tft_disp_spi, ST7789V_init);
+ }
+ else if (tft_disp_type == DISP_TYPE_ST7735) {
+ commandList(tft_disp_spi, STP7735_init);
+ }
+ else if (tft_disp_type == DISP_TYPE_ST7735R) {
+ commandList(tft_disp_spi, STP7735R_init);
+ commandList(tft_disp_spi, Rcmd2green);
+ commandList(tft_disp_spi, Rcmd3);
+ }
+ else if (tft_disp_type == DISP_TYPE_ST7735B) {
+ commandList(tft_disp_spi, STP7735R_init);
+ commandList(tft_disp_spi, Rcmd2red);
+ commandList(tft_disp_spi, Rcmd3);
+ uint8_t dt = 0xC0;
+ disp_spi_transfer_cmd_data(TFT_MADCTL, &dt, 1);
+ }
+ else assert(0);
+
+ ret = disp_deselect();
+ assert(ret==ESP_OK);
+
+ // Clear screen
+ _tft_setRotation(PORTRAIT);
+ TFT_pushColorRep(TFT_STATIC_WIDTH_OFFSET, TFT_STATIC_HEIGHT_OFFSET, tft_width + TFT_STATIC_WIDTH_OFFSET -1, tft_height + TFT_STATIC_HEIGHT_OFFSET -1, (color_t){0,0,0}, (uint32_t)(tft_height*tft_width));
+
+ ///Enable backlight
+#if PIN_NUM_BCKL
+ gpio_set_level(PIN_NUM_BCKL, PIN_BCKL_ON);
+#endif
+}
+
+
diff --git a/components/tft/tftspi.h b/components/tft/tftspi.h
new file mode 100644
index 0000000..fb689b0
--- /dev/null
+++ b/components/tft/tftspi.h
@@ -0,0 +1,751 @@
+/*
+ *
+ * HIGH SPEED LOW LEVEL DISPLAY FUNCTIONS USING DIRECT TRANSFER MODE
+ *
+*/
+
+#ifndef _TFTSPI_H_
+#define _TFTSPI_H_
+
+#include "tftspi.h"
+#include "spi_master_lobo.h"
+#include "sdkconfig.h"
+#include "stmpe610.h"
+
+#define TOUCH_TYPE_NONE 0
+#define TOUCH_TYPE_XPT2046 1
+#define TOUCH_TYPE_STMPE610 2
+
+#define TP_CALX_XPT2046 7472920
+#define TP_CALY_XPT2046 122224794
+
+#define TP_CALX_STMPE610 21368532
+#define TP_CALY_STMPE610 11800144
+
+// === Screen tft_orientation constants ===
+#define PORTRAIT 0
+#define LANDSCAPE 1
+#define PORTRAIT_FLIP 2
+#define LANDSCAPE_FLIP 3
+
+#define DISP_TYPE_ILI9341 0
+#define DISP_TYPE_ILI9488 1
+#define DISP_TYPE_ST7789V 2
+#define DISP_TYPE_ST7735 3
+#define DISP_TYPE_ST7735R 4
+#define DISP_TYPE_ST7735B 5
+
+
+
+#if CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 1
+
+// ** Set the correct configuration for ESP-WROVER-KIT v3
+// --------------------------------------------------------
+#define DEFAULT_DISP_TYPE DISP_TYPE_ST7789V
+#define DEFAULT_TFT_DISPLAY_WIDTH 240
+#define DEFAULT_TFT_DISPLAY_HEIGHT 320
+#define DISP_COLOR_BITS_24 0x66
+#define DEFAULT_GAMMA_CURVE 0
+#define DEFAULT_SPI_CLOCK 26000000
+#define TFT_INVERT_ROTATION 0
+#define TFT_INVERT_ROTATION1 1
+#define TFT_RGB_BGR 0x00
+
+#define USE_TOUCH TOUCH_TYPE_NONE
+
+#define PIN_NUM_MISO 25 // SPI MISO
+#define PIN_NUM_MOSI 23 // SPI MOSI
+#define PIN_NUM_CLK 19 // SPI CLOCK pin
+#define PIN_NUM_CS 22 // Display CS pin
+#define PIN_NUM_DC 21 // Display command/data pin
+#define PIN_NUM_TCS 0 // Touch screen CS pin
+
+#define PIN_NUM_RST 18 // GPIO used for RESET control
+#define PIN_NUM_BCKL 5 // GPIO used for backlight control
+#define PIN_BCKL_ON 0 // GPIO value for backlight ON
+#define PIN_BCKL_OFF 1 // GPIO value for backlight OFF
+// --------------------------------------------------------
+
+#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 2
+
+// ** Set the correct configuration for Adafruit TFT Feather
+// ---------------------------------------------------------
+#define DEFAULT_DISP_TYPE DISP_TYPE_ILI9341
+#define DEFAULT_TFT_DISPLAY_WIDTH 240
+#define DEFAULT_TFT_DISPLAY_HEIGHT 320
+#define DISP_COLOR_BITS_24 0x66
+#define DEFAULT_GAMMA_CURVE 0
+#define DEFAULT_SPI_CLOCK 26000000
+#define TFT_INVERT_ROTATION 0
+#define TFT_INVERT_ROTATION1 0
+#define TFT_RGB_BGR 0x08
+
+#define USE_TOUCH TOUCH_TYPE_STMPE610
+
+#define PIN_NUM_MISO 19 // SPI MISO
+#define PIN_NUM_MOSI 18 // SPI MOSI
+#define PIN_NUM_CLK 5 // SPI CLOCK pin
+#define PIN_NUM_CS 15 // Display CS pin
+#define PIN_NUM_DC 33 // Display command/data pin
+#define PIN_NUM_TCS 32 // Touch screen CS pin (NOT used if USE_TOUCH=0)
+
+#define PIN_NUM_RST 0 // GPIO used for RESET control (#16)
+#define PIN_NUM_BCKL 0 // GPIO used for backlight control
+#define PIN_BCKL_ON 0 // GPIO value for backlight ON
+#define PIN_BCKL_OFF 1 // GPIO value for backlight OFF
+// ---------------------------------------------------------
+
+#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 3
+
+// ** Set the correct configuration for M5Stack TFT
+// ---------------------------------------------------------
+#define DEFAULT_DISP_TYPE DISP_TYPE_ILI9341
+#define DEFAULT_TFT_DISPLAY_WIDTH 320
+#define DEFAULT_TFT_DISPLAY_HEIGHT 240
+#define DISP_COLOR_BITS_24 0x66
+#define DEFAULT_GAMMA_CURVE 0
+#define DEFAULT_SPI_CLOCK 26000000
+#define TFT_INVERT_ROTATION 0
+#define TFT_INVERT_ROTATION1 0
+#define TFT_INVERT_ROTATION2 1 // Adapte M5Stack TFT
+#define TFT_RGB_BGR 0x08
+
+#define USE_TOUCH TOUCH_TYPE_NONE
+
+#define PIN_NUM_MISO 19 // SPI MISO
+#define PIN_NUM_MOSI 23 // SPI MOSI
+#define PIN_NUM_CLK 18 // SPI CLOCK pin
+#define PIN_NUM_CS 14 // Display CS pin
+#define PIN_NUM_DC 27 // Display command/data pin
+#define PIN_NUM_TCS 0 // Touch screen CS pin (NOT used if USE_TOUCH=0)
+
+#define PIN_NUM_RST 33 // GPIO used for RESET control (#16)
+#define PIN_NUM_BCKL 32 // GPIO used for backlight control
+#define PIN_BCKL_ON 1 // GPIO value for backlight ON
+#define PIN_BCKL_OFF 0 // GPIO value for backlight OFF
+// ---------------------------------------------------------
+
+#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 4
+
+// ** Set the correct configuration for ESP-WROVER-KIT v4.1
+// --------------------------------------------------------
+#define DEFAULT_DISP_TYPE DISP_TYPE_ILI9341
+#define DEFAULT_TFT_DISPLAY_WIDTH 240
+#define DEFAULT_TFT_DISPLAY_HEIGHT 320
+#define DISP_COLOR_BITS_24 0x66
+#define DEFAULT_GAMMA_CURVE 0
+#define DEFAULT_SPI_CLOCK 26000000
+#define TFT_INVERT_ROTATION 0
+#define TFT_INVERT_ROTATION1 0
+#define TFT_INVERT_ROTATION2 0
+#define TFT_RGB_BGR 0x08
+
+#define USE_TOUCH TOUCH_TYPE_NONE
+
+#define PIN_NUM_MISO 25 // SPI MISO
+#define PIN_NUM_MOSI 23 // SPI MOSI
+#define PIN_NUM_CLK 19 // SPI CLOCK pin
+#define PIN_NUM_CS 22 // Display CS pin
+#define PIN_NUM_DC 21 // Display command/data pin
+#define PIN_NUM_TCS 0 // Touch screen CS pin
+
+#define PIN_NUM_RST 18 // GPIO used for RESET control
+#define PIN_NUM_BCKL 5 // GPIO used for backlight control
+#define PIN_BCKL_ON 0 // GPIO value for backlight ON
+#define PIN_BCKL_OFF 1 // GPIO value for backlight OFF
+// --------------------------------------------------------
+#elif CONFIG_TFT_PREDEFINED_DISPLAY_TYPE == 5
+//CONFIG FOR TTGO T-DISPLAY
+#define DEFAULT_DISP_TYPE DISP_TYPE_ST7789V
+#define DEFAULT_TFT_DISPLAY_WIDTH 135
+#define DEFAULT_TFT_DISPLAY_HEIGHT 240
+
+//Need to be defined together so they can be swapped for x;y when rotating
+#define TFT_STATIC_WIDTH_OFFSET 53
+#define TFT_STATIC_HEIGHT_OFFSET 40
+
+#define DISP_COLOR_BITS_24 0x66
+#define DEFAULT_GAMMA_CURVE 0
+#define DEFAULT_SPI_CLOCK 20000000
+#define TFT_INVERT_ROTATION 0
+#define TFT_INVERT_ROTATION1 1
+#define TFT_RGB_BGR 0x00
+//To be used by user application for initialization
+#define TFT_START_COLORS_INVERTED
+
+#define USE_TOUCH TOUCH_TYPE_NONE
+
+#define PIN_NUM_MISO 0 // SPI MISO
+#define PIN_NUM_MOSI 19 // SPI MOSI
+#define PIN_NUM_CLK 18 // SPI CLOCK pin
+#define PIN_NUM_CS 5 // Display CS pin
+#define PIN_NUM_DC 16 // Display command/data pin
+#define PIN_NUM_TCS 0 // Touch screen CS pin
+
+#define PIN_NUM_RST 23 // GPIO used for RESET control
+#define PIN_NUM_BCKL 4 // GPIO used for backlight control
+#define PIN_BCKL_ON 1 // GPIO value for backlight ON
+#define PIN_BCKL_OFF 0 // GPIO value for backlight OFF
+//END TTGO T_DISPLAY
+
+#else
+
+// Configuration for other boards, set the correct values for the display used
+//----------------------------------------------------------------------------
+#define DISP_COLOR_BITS_24 0x66
+//#define DISP_COLOR_BITS_16 0x55 // Do not use!
+
+#define TFT_INVERT_ROTATION 0
+#define TFT_INVERT_ROTATION1 CONFIG_TFT_INVERT_ROTATION1
+
+// ################################################
+// ### SET TO 0X00 FOR DISPLAYS WITH RGB MATRIX ###
+// ### SET TO 0X08 FOR DISPLAYS WITH BGR MATRIX ###
+// ### For ESP-WROWER-KIT set to 0x00 ###
+// ################################################
+
+#if CONFIG_TFT_RGB_BGR
+#define TFT_RGB_BGR 0x00
+#else
+#define TFT_RGB_BGR 0x08
+#endif
+
+// ##############################################################
+// ### Define ESP32 SPI pins to which the display is attached ###
+// ##############################################################
+
+// The pins configured here are the native spi pins for HSPI interface
+// Any other valid pin combination can be used
+
+#define PIN_NUM_MISO CONFIG_TFT_PIN_NUM_MISO
+#define PIN_NUM_MOSI CONFIG_TFT_PIN_NUM_MOSI
+#define PIN_NUM_CLK CONFIG_TFT_PIN_NUM_CLK
+#define PIN_NUM_CS CONFIG_TFT_PIN_NUM_CS
+#define PIN_NUM_DC CONFIG_TFT_PIN_NUM_DC
+#define PIN_NUM_TCS CONFIG_TFT_PIN_NUM_TCS
+#define PIN_NUM_RST CONFIG_TFT_PIN_NUM_RST
+#define PIN_NUM_BCKL CONFIG_TFT_PIN_NUM_BCKL
+
+#define PIN_BCKL_ON 0 // GPIO value for backlight ON
+#define PIN_BCKL_OFF 1 // GPIO value for backlight OFF
+// --------------------------------------------------------------
+
+#define USE_TOUCH CONFIG_TFT_TOUCH_CONTROLLER
+
+// #######################################################################
+// Default display width (smaller dimension) and height (larger dimension)
+// #######################################################################
+#define DEFAULT_TFT_DISPLAY_WIDTH CONFIG_TFT_DISPLAY_WIDTH
+#define DEFAULT_TFT_DISPLAY_HEIGHT CONFIG_TFT_DISPLAY_HEIGHT
+// #######################################################################
+
+#define DEFAULT_GAMMA_CURVE 0
+#define DEFAULT_SPI_CLOCK 26000000
+
+#if defined(CONFIG_TFT_DISPLAY_CONTROLLER_MODEL)
+#define DEFAULT_DISP_TYPE CONFIG_TFT_DISPLAY_CONTROLLER_MODEL
+#else
+#define DEFAULT_DISP_TYPE DISP_TYPE_ILI9341
+#endif
+//----------------------------------------------------------------------------
+
+#endif // CONFIG_PREDEFINED_DISPLAY_TYPE
+
+// Define offset generation, or ignore offsets if none are needed
+#ifdef TFT_STATIC_WIDTH_OFFSET
+#define TFT_STATIC_X_OFFSET (tft_orientation & 1 ? TFT_STATIC_HEIGHT_OFFSET : TFT_STATIC_WIDTH_OFFSET)
+#define TFT_STATIC_Y_OFFSET (tft_orientation & 1 ? TFT_STATIC_WIDTH_OFFSET : TFT_STATIC_HEIGHT_OFFSET)
+#else
+#define TFT_STATIC_WIDTH_OFFSET 0
+#define TFT_STATIC_X_OFFSET 0
+#define TFT_STATIC_HEIGHT_OFFSET 0
+#define TFT_STATIC_Y_OFFSET 0
+#endif
+
+
+// ##############################################################
+// #### Global variables ####
+// ##############################################################
+
+// ==== Converts colors to grayscale if 1 =======================
+extern uint8_t tft_gray_scale;
+
+// ==== Spi clock for reading data from display memory in Hz ====
+extern uint32_t tft_max_rdclock;
+
+// ==== Display dimensions in pixels ============================
+extern int tft_width;
+extern int tft_height;
+
+// ==== Display type, DISP_TYPE_ILI9488 or DISP_TYPE_ILI9341 ====
+extern uint8_t tft_disp_type;
+
+// ==== Spi device handles for display and touch screen =========
+extern spi_lobo_device_handle_t tft_disp_spi;
+extern spi_lobo_device_handle_t tft_ts_spi;
+
+// ##############################################################
+
+// 24-bit color type structure
+typedef struct __attribute__((__packed__)) {
+//typedef struct {
+ uint8_t r;
+ uint8_t g;
+ uint8_t b;
+} color_t ;
+
+// ==== Display commands constants ====
+#define TFT_INVOFF 0x20
+#define TFT_INVONN 0x21
+#define TFT_DISPOFF 0x28
+#define TFT_DISPON 0x29
+#define TFT_MADCTL 0x36
+#define TFT_PTLAR 0x30
+#define TFT_ENTRYM 0xB7
+
+#define TFT_CMD_NOP 0x00
+#define TFT_CMD_SWRESET 0x01
+#define TFT_CMD_RDDID 0x04
+#define TFT_CMD_RDDST 0x09
+
+#define TFT_CMD_SLPIN 0x10
+#define TFT_CMD_SLPOUT 0x11
+#define TFT_CMD_PTLON 0x12
+#define TFT_CMD_NORON 0x13
+
+#define TFT_CMD_RDMODE 0x0A
+#define TFT_CMD_RDMADCTL 0x0B
+#define TFT_CMD_RDPIXFMT 0x0C
+#define TFT_CMD_RDIMGFMT 0x0D
+#define TFT_CMD_RDSELFDIAG 0x0F
+
+#define TFT_CMD_GAMMASET 0x26
+
+#define TFT_CMD_FRMCTR1 0xB1
+#define TFT_CMD_FRMCTR2 0xB2
+#define TFT_CMD_FRMCTR3 0xB3
+#define TFT_CMD_INVCTR 0xB4
+#define TFT_CMD_DFUNCTR 0xB6
+
+#define TFT_CMD_PWCTR1 0xC0
+#define TFT_CMD_PWCTR2 0xC1
+#define TFT_CMD_PWCTR3 0xC2
+#define TFT_CMD_PWCTR4 0xC3
+#define TFT_CMD_PWCTR5 0xC4
+#define TFT_CMD_VMCTR1 0xC5
+#define TFT_CMD_VMCTR2 0xC7
+
+#define TFT_CMD_RDID1 0xDA
+#define TFT_CMD_RDID2 0xDB
+#define TFT_CMD_RDID3 0xDC
+#define TFT_CMD_RDID4 0xDD
+
+#define TFT_CMD_GMCTRP1 0xE0
+#define TFT_CMD_GMCTRN1 0xE1
+
+#define TFT_CMD_POWERA 0xCB
+#define TFT_CMD_POWERB 0xCF
+#define TFT_CMD_POWER_SEQ 0xED
+#define TFT_CMD_DTCA 0xE8
+#define TFT_CMD_DTCB 0xEA
+#define TFT_CMD_PRC 0xF7
+#define TFT_CMD_3GAMMA_EN 0xF2
+
+#define ST_CMD_VCOMS 0xBB
+#define ST_CMD_FRCTRL2 0xC6
+#define ST_CMD_PWCTR1 0xD0
+
+#define ST7735_FRMCTR1 0xB1
+#define ST7735_FRMCTR2 0xB2
+#define ST7735_FRMCTR3 0xB3
+#define ST7735_INVCTR 0xB4
+#define ST7735_DISSET5 0xB6
+
+#define ST7735_PWCTR1 0xC0
+#define ST7735_PWCTR2 0xC1
+#define ST7735_PWCTR3 0xC2
+#define ST7735_PWCTR4 0xC3
+#define ST7735_PWCTR5 0xC4
+#define ST7735_VMCTR1 0xC5
+
+#define ST7735_RDID1 0xDA
+#define ST7735_RDID2 0xDB
+#define ST7735_RDID3 0xDC
+#define ST7735_RDID4 0xDD
+#define ST7735_NOP 0x00
+#define ST7735_SWRESET 0x01
+#define ST7735_RDDID 0x04
+#define ST7735_RDDST 0x09
+
+#define ST7735_SLPIN 0x10
+#define ST7735_SLPOUT 0x11
+#define ST7735_PTLON 0x12
+#define ST7735_NORON 0x13
+#define ST7735_PWCTR6 0xFC
+#define ST7735_GMCTRP1 0xE0
+#define ST7735_GMCTRN1 0xE1
+
+#define MADCTL_MY 0x80
+#define MADCTL_MX 0x40
+#define MADCTL_MV 0x20
+#define MADCTL_ML 0x10
+#define MADCTL_MH 0x04
+
+#define TFT_CASET 0x2A
+#define TFT_PASET 0x2B
+#define TFT_RAMWR 0x2C
+#define TFT_RAMRD 0x2E
+#define TFT_CMD_PIXFMT 0x3A
+
+#define TFT_CMD_DELAY 0x80
+
+
+// Initialization sequence for ILI7749
+// ====================================
+static const uint8_t ST7789V_init[] = {
+#if PIN_NUM_RST
+ 15, // 15 commands in list
+#else
+ 16, // 16 commands in list
+ TFT_CMD_SWRESET, TFT_CMD_DELAY, // 1: Software reset, no args, w/delay
+ 200, // 200 ms delay
+#endif
+ TFT_CMD_FRMCTR2, 5, 0x0c, 0x0c, 0x00, 0x33, 0x33,
+ TFT_ENTRYM, 1, 0x45,
+ ST_CMD_VCOMS, 1, 0x2B,
+ TFT_CMD_PWCTR1, 1, 0x2C,
+ TFT_CMD_PWCTR3, 2, 0x01, 0xff,
+ TFT_CMD_PWCTR4, 1, 0x11,
+ TFT_CMD_PWCTR5, 1, 0x20,
+ ST_CMD_FRCTRL2, 1, 0x0f,
+ ST_CMD_PWCTR1, 2, 0xA4, 0xA1,
+ TFT_CMD_GMCTRP1, 14, 0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19,
+ TFT_CMD_GMCTRN1, 14, 0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19,
+ TFT_MADCTL, 1, (MADCTL_MX | TFT_RGB_BGR), // Memory Access Control (tft_orientation)
+ TFT_CMD_PIXFMT, 1, DISP_COLOR_BITS_24, // *** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit; 0x55 -> 16 bit
+ TFT_CMD_SLPOUT, TFT_CMD_DELAY, 120, // Sleep out, // 120 ms delay
+ TFT_DISPON, TFT_CMD_DELAY, 120,
+};
+
+// Initialization sequence for ILI7341
+// ====================================
+static const uint8_t ILI9341_init[] = {
+#if PIN_NUM_RST
+ 23, // 24 commands in list
+#else
+ 24, // 24 commands in list
+ TFT_CMD_SWRESET, TFT_CMD_DELAY, // 1: Software reset, no args, w/delay
+ 250, // 200 ms delay
+#endif
+ TFT_CMD_POWERA, 5, 0x39, 0x2C, 0x00, 0x34, 0x02,
+ TFT_CMD_POWERB, 3, 0x00, 0XC1, 0X30,
+ 0xEF, 3, 0x03, 0x80, 0x02,
+ TFT_CMD_DTCA, 3, 0x85, 0x00, 0x78,
+ TFT_CMD_DTCB, 2, 0x00, 0x00,
+ TFT_CMD_POWER_SEQ, 4, 0x64, 0x03, 0X12, 0X81,
+ TFT_CMD_PRC, 1, 0x20,
+ TFT_CMD_PWCTR1, 1, 0x23, //Power control VRH[5:0]
+ TFT_CMD_PWCTR2, 1, 0x10, //Power control SAP[2:0];BT[3:0]
+ TFT_CMD_VMCTR1, 2, 0x3e, 0x28, //VCM control
+ TFT_CMD_VMCTR2, 1, 0x86, //VCM control2
+ TFT_MADCTL, 1, // Memory Access Control (tft_orientation)
+ (MADCTL_MX | TFT_RGB_BGR),
+ // *** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit; 0x55 -> 16 bit
+ TFT_CMD_PIXFMT, 1, DISP_COLOR_BITS_24,
+ TFT_INVOFF, 0,
+ TFT_CMD_FRMCTR1, 2, 0x00, 0x18,
+ TFT_CMD_DFUNCTR, 4, 0x08, 0x82, 0x27, 0x00, // Display Function Control
+ TFT_PTLAR, 4, 0x00, 0x00, 0x01, 0x3F,
+ TFT_CMD_3GAMMA_EN, 1, 0x00, // 3Gamma Function: Disable (0x02), Enable (0x03)
+ TFT_CMD_GAMMASET, 1, 0x01, //Gamma curve selected (0x01, 0x02, 0x04, 0x08)
+ TFT_CMD_GMCTRP1, 15, //Positive Gamma Correction
+ 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00,
+ TFT_CMD_GMCTRN1, 15, //Negative Gamma Correction
+ 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F,
+ TFT_CMD_SLPOUT, TFT_CMD_DELAY, // Sleep out
+ 200, // 120 ms delay
+ TFT_DISPON, TFT_CMD_DELAY, 200,
+};
+
+// Initialization sequence for ILI9488
+// ====================================
+static const uint8_t ILI9488_init[] = {
+#if PIN_NUM_RST
+ 17, // 17 commands in list
+#else
+ 18, // 18 commands in list
+ TFT_CMD_SWRESET, TFT_CMD_DELAY, // 1: Software reset, no args, w/delay
+ 200, // 200 ms delay
+#endif
+ 0xE0, 15, 0x00, 0x03, 0x09, 0x08, 0x16, 0x0A, 0x3F, 0x78, 0x4C, 0x09, 0x0A, 0x08, 0x16, 0x1A, 0x0F,
+ 0xE1, 15, 0x00, 0x16, 0x19, 0x03, 0x0F, 0x05, 0x32, 0x45, 0x46, 0x04, 0x0E, 0x0D, 0x35, 0x37, 0x0F,
+ 0xC0, 2, //Power Control 1
+ 0x17, //Vreg1out
+ 0x15, //Verg2out
+
+ 0xC1, 1, //Power Control 2
+ 0x41, //VGH,VGL
+
+ 0xC5, 3, //Power Control 3
+ 0x00,
+ 0x12, //Vcom
+ 0x80,
+
+#if TFT_INVERT_ROTATION
+ TFT_MADCTL, 1, (MADCTL_MV | TFT_RGB_BGR), // Memory Access Control (tft_orientation), set to portrait
+#else
+ TFT_MADCTL, 1, (MADCTL_MX | TFT_RGB_BGR), // Memory Access Control (tft_orientation), set to portrait
+#endif
+
+ // *** INTERFACE PIXEL FORMAT: 0x66 -> 18 bit;
+ TFT_CMD_PIXFMT, 1, DISP_COLOR_BITS_24,
+
+ 0xB0, 1, // Interface Mode Control
+ 0x00, // 0x80: SDO NOT USE; 0x00 USE SDO
+
+ 0xB1, 1, //Frame rate
+ 0xA0, //60Hz
+
+ 0xB4, 1, //Display Inversion Control
+ 0x02, //2-dot
+
+ 0xB6, 2, //Display Function Control RGB/MCU Interface Control
+ 0x02, //MCU
+ 0x02, //Source,Gate scan direction
+
+ 0xE9, 1, // Set Image Function
+ 0x00, // Disable 24 bit data
+
+ 0x53, 1, // Write CTRL Display Value
+ 0x28, // BCTRL && DD on
+
+ 0x51, 1, // Write Display Brightness Value
+ 0x7F, //
+
+ 0xF7, 4, // Adjust Control
+ 0xA9,
+ 0x51,
+ 0x2C,
+ 0x02, // D7 stream, loose
+
+
+ 0x11, TFT_CMD_DELAY, //Exit Sleep
+ 120,
+ 0x29, 0, //Display on
+
+};
+
+
+// Initialization commands for 7735B screens
+// ------------------------------------
+static const uint8_t STP7735_init[] = {
+#if PIN_NUM_RST
+ 16, // 17 commands in list
+#else
+ 17, // 18 commands in list:
+ ST7735_SLPOUT, TFT_CMD_DELAY, // 2: Out of sleep mode, no args, w/delay
+ 255, // 255 = 500 ms delay
+#endif
+ TFT_CMD_PIXFMT, 1+TFT_CMD_DELAY, // 3: Set color mode, 1 arg + delay:
+ 0x06, // 18-bit color 6-6-6 color format
+ 10, // 10 ms delay
+ ST7735_FRMCTR1, 3+TFT_CMD_DELAY, // 4: Frame rate control, 3 args + delay:
+ 0x00, // fastest refresh
+ 0x06, // 6 lines front porch
+ 0x03, // 3 lines back porch
+ 10, // 10 ms delay
+ TFT_MADCTL , 1 , // 5: Memory access ctrl (directions), 1 arg:
+ 0x08, // Row addr/col addr, bottom to top refresh
+ ST7735_DISSET5, 2 , // 6: Display settings #5, 2 args, no delay:
+ 0x15, // 1 clk cycle nonoverlap, 2 cycle gate
+ // rise, 3 cycle osc equalize
+ 0x02, // Fix on VTL
+ ST7735_INVCTR , 1 , // 7: Display inversion control, 1 arg:
+ 0x0, // Line inversion
+ ST7735_PWCTR1 , 2+TFT_CMD_DELAY, // 8: Power control, 2 args + delay:
+ 0x02, // GVDD = 4.7V
+ 0x70, // 1.0uA
+ 10, // 10 ms delay
+ ST7735_PWCTR2 , 1 , // 9: Power control, 1 arg, no delay:
+ 0x05, // VGH = 14.7V, VGL = -7.35V
+ ST7735_PWCTR3 , 2 , // 10: Power control, 2 args, no delay:
+ 0x01, // Opamp current small
+ 0x02, // Boost frequency
+ ST7735_VMCTR1 , 2+TFT_CMD_DELAY, // 11: Power control, 2 args + delay:
+ 0x3C, // VCOMH = 4V
+ 0x38, // VCOML = -1.1V
+ 10, // 10 ms delay
+ ST7735_PWCTR6 , 2 , // 12: Power control, 2 args, no delay:
+ 0x11, 0x15,
+ ST7735_GMCTRP1,16 , // 13: Magical unicorn dust, 16 args, no delay:
+ 0x09, 0x16, 0x09, 0x20, // (seriously though, not sure what
+ 0x21, 0x1B, 0x13, 0x19, // these config values represent)
+ 0x17, 0x15, 0x1E, 0x2B,
+ 0x04, 0x05, 0x02, 0x0E,
+ ST7735_GMCTRN1,16+TFT_CMD_DELAY, // 14: Sparkles and rainbows, 16 args + delay:
+ 0x0B, 0x14, 0x08, 0x1E, // (ditto)
+ 0x22, 0x1D, 0x18, 0x1E,
+ 0x1B, 0x1A, 0x24, 0x2B,
+ 0x06, 0x06, 0x02, 0x0F,
+ 10, // 10 ms delay
+ TFT_CASET , 4 , // 15: Column addr set, 4 args, no delay:
+ 0x00, 0x02, // XSTART = 2
+ 0x00, 0x81, // XEND = 129
+ TFT_PASET , 4 , // 16: Row addr set, 4 args, no delay:
+ 0x00, 0x02, // XSTART = 1
+ 0x00, 0x81, // XEND = 160
+ ST7735_NORON , TFT_CMD_DELAY, // 17: Normal display on, no args, w/delay
+ 10, // 10 ms delay
+ TFT_DISPON , TFT_CMD_DELAY, // 18: Main screen turn on, no args, w/delay
+ 255 // 255 = 500 ms delay
+};
+
+// Init for 7735R, part 1 (red or green tab)
+// --------------------------------------
+static const uint8_t STP7735R_init[] = {
+#if PIN_NUM_RST
+ 14, // 14 commands in list
+#else
+ 15, // 15 commands in list:
+ ST7735_SWRESET, TFT_CMD_DELAY, // 1: Software reset, 0 args, w/delay
+ 150, // 150 ms delay
+#endif
+ ST7735_SLPOUT , TFT_CMD_DELAY, // 2: Out of sleep mode, 0 args, w/delay
+ 255, // 500 ms delay
+ ST7735_FRMCTR1, 3 , // 3: Frame rate ctrl - normal mode, 3 args:
+ 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
+ ST7735_FRMCTR2, 3 , // 4: Frame rate control - idle mode, 3 args:
+ 0x01, 0x2C, 0x2D, // Rate = fosc/(1x2+40) * (LINE+2C+2D)
+ ST7735_FRMCTR3, 6 , // 5: Frame rate ctrl - partial mode, 6 args:
+ 0x01, 0x2C, 0x2D, // Dot inversion mode
+ 0x01, 0x2C, 0x2D, // Line inversion mode
+ ST7735_INVCTR , 1 , // 6: Display inversion ctrl, 1 arg, no delay:
+ 0x07, // No inversion
+ ST7735_PWCTR1 , 3 , // 7: Power control, 3 args, no delay:
+ 0xA2,
+ 0x02, // -4.6V
+ 0x84, // AUTO mode
+ ST7735_PWCTR2 , 1 , // 8: Power control, 1 arg, no delay:
+ 0xC5, // VGH25 = 2.4C VGSEL = -10 VGH = 3 * AVDD
+ ST7735_PWCTR3 , 2 , // 9: Power control, 2 args, no delay:
+ 0x0A, // Opamp current small
+ 0x00, // Boost frequency
+ ST7735_PWCTR4 , 2 , // 10: Power control, 2 args, no delay:
+ 0x8A, // BCLK/2, Opamp current small & Medium low
+ 0x2A,
+ ST7735_PWCTR5 , 2 , // 11: Power control, 2 args, no delay:
+ 0x8A, 0xEE,
+ ST7735_VMCTR1 , 1 , // 12: Power control, 1 arg, no delay:
+ 0x0E,
+ TFT_INVOFF , 0 , // 13: Don't invert display, no args, no delay
+ TFT_MADCTL , 1 , // 14: Memory access control (directions), 1 arg:
+ 0xC0, // row addr/col addr, bottom to top refresh, RGB order
+ TFT_CMD_PIXFMT , 1+TFT_CMD_DELAY, // 15: Set color mode, 1 arg + delay:
+ 0x06, // 18-bit color 6-6-6 color format
+ 10 // 10 ms delay
+};
+
+// Init for 7735R, part 2 (green tab only)
+// ---------------------------------------
+static const uint8_t Rcmd2green[] = {
+ 2, // 2 commands in list:
+ TFT_CASET , 4 , // 1: Column addr set, 4 args, no delay:
+ 0x00, 0x02, // XSTART = 0
+ 0x00, 0x7F+0x02, // XEND = 129
+ TFT_PASET , 4 , // 2: Row addr set, 4 args, no delay:
+ 0x00, 0x01, // XSTART = 0
+ 0x00, 0x9F+0x01 // XEND = 160
+};
+
+// Init for 7735R, part 2 (red tab only)
+// -------------------------------------
+static const uint8_t Rcmd2red[] = {
+ 2, // 2 commands in list:
+ TFT_CASET , 4 , // 1: Column addr set, 4 args, no delay:
+ 0x00, 0x00, // XSTART = 0
+ 0x00, 0x7F, // XEND = 127
+ TFT_PASET , 4 , // 2: Row addr set, 4 args, no delay:
+ 0x00, 0x00, // XSTART = 0
+ 0x00, 0x9F // XEND = 159
+};
+
+// Init for 7735R, part 3 (red or green tab)
+// -----------------------------------------
+static const uint8_t Rcmd3[] = {
+ 4, // 4 commands in list:
+ ST7735_GMCTRP1, 16 , // 1: Magical unicorn dust, 16 args, no delay:
+ 0x02, 0x1c, 0x07, 0x12,
+ 0x37, 0x32, 0x29, 0x2d,
+ 0x29, 0x25, 0x2B, 0x39,
+ 0x00, 0x01, 0x03, 0x10,
+ ST7735_GMCTRN1, 16 , // 2: Sparkles and rainbows, 16 args, no delay:
+ 0x03, 0x1d, 0x07, 0x06,
+ 0x2E, 0x2C, 0x29, 0x2D,
+ 0x2E, 0x2E, 0x37, 0x3F,
+ 0x00, 0x00, 0x02, 0x10,
+ ST7735_NORON , TFT_CMD_DELAY, // 3: Normal display on, no args, w/delay
+ 10, // 10 ms delay
+ TFT_DISPON , TFT_CMD_DELAY, // 4: Main screen turn on, no args w/delay
+ 100 // 100 ms delay
+};
+
+
+// ==== Public functions =========================================================
+
+// == Low level functions; usually not used directly ==
+esp_err_t wait_trans_finish(uint8_t free_line);
+void disp_spi_transfer_cmd(int8_t cmd);
+void disp_spi_transfer_cmd_data(int8_t cmd, uint8_t *data, uint32_t len);
+void drawPixel(int16_t x, int16_t y, color_t color, uint8_t sel);
+void send_data(int x1, int y1, int x2, int y2, uint32_t len, color_t *buf);
+void TFT_pushColorRep(int x1, int y1, int x2, int y2, color_t data, uint32_t len);
+int read_data(int x1, int y1, int x2, int y2, int len, uint8_t *buf, uint8_t set_sp);
+color_t readPixel(int16_t x, int16_t y);
+int touch_get_data(uint8_t type);
+
+
+// Deactivate display's CS line
+//========================
+esp_err_t disp_deselect();
+
+// Activate display's CS line and configure SPI interface if necessary
+//======================
+esp_err_t disp_select();
+
+
+// Find maximum spi clock for successful read from display RAM
+// ** Must be used AFTER the display is initialized **
+//======================
+uint32_t find_rd_speed();
+
+
+// Change the screen rotation.
+// Input: m new rotation value (0 to 3)
+//=================================
+void _tft_setRotation(uint8_t rot);
+
+// Initialize all pins used by display driver
+// ** MUST be executed before SPI interface initialization
+//=================
+void TFT_PinsInit();
+
+// Perform display initialization sequence
+// Sets tft_orientation to landscape; clears the screen
+// * All pins must be configured
+// * SPI interface must already be setup
+// * 'tft_disp_type', 'COLOR_BITS', 'tft_width', 'tft_height' variables must be set
+//======================
+void TFT_display_init();
+
+//===================
+void stmpe610_Init();
+
+//============================================================
+int stmpe610_get_touch(uint16_t *x, uint16_t *y, uint16_t *z);
+
+//========================
+uint32_t stmpe610_getID();
+
+// ===============================================================================
+
+#endif
diff --git a/components/tft/tooney32.c b/components/tft/tooney32.c
new file mode 100644
index 0000000..753fb2d
--- /dev/null
+++ b/components/tft/tooney32.c
@@ -0,0 +1,331 @@
+// This comes with no warranty, implied or otherwise
+
+// This data structure was designed to support Proportional fonts
+// on Arduinos. It can however handle any ttf font that has been converted
+// using the conversion program. These could be fixed width or proportional
+// fonts. Individual characters do not have to be multiples of 8 bits wide.
+// Any width is fine and does not need to be fixed.
+
+// The data bits are packed to minimize data requirements, but the tradeoff
+// is that a header is required per character.
+
+// tooney32.c
+// Point Size : 32
+// Memory usage : 5470 bytes
+// # characters : 95
+
+// Header Format (to make Arduino UTFT Compatible):
+// ------------------------------------------------
+// Character Width (Used as a marker to indicate use this format. i.e.: = 0x00)
+// Character Height
+// First Character (Reserved. 0x00)
+// Number Of Characters (Reserved. 0x00)
+
+unsigned char tft_tooney32[] =
+{
+0x00, 0x20, 0x00, 0x00,
+
+// Individual Character Format:
+// ----------------------------
+// Character Code
+// Adjusted Y Offset
+// Width
+// Height
+// xOffset
+// xDelta (the distance to move the cursor. Effective width of the character.)
+// Data[n]
+
+// NOTE: You can remove any of these characters if they are not needed in
+// your application. The first character number in each Glyph indicates
+// the ASCII character code. Therefore, these do not have to be sequential.
+// Just remove all the content for a particular character to save space.
+
+// ' '
+0x20,0x1E,0x00,0x00,0x00,0x09,
+
+// '!'
+0x21,0x09,0x0B,0x16,0x00,0x0B,
+0x3F,0xC8,0x07,0x81,0x70,0x27,0x00,0xE0,0x1C,0x21,0x84,0x30,0x86,0x10,0xC2,0x18,0x43,0xF0,0x61,0x0C,0x13,0x02,0x60,0x4E,0x09,0xE2,0x1F,0x81,0xE0,0x00,0x00,
+// '"'
+0x22,0x05,0x0E,0x0A,0xFF,0x0D,
+0x04,0x30,0x2D,0x61,0x8C,0x44,0x71,0x31,0x88,0xCE,0x42,0x72,0x18,0xC8,0x7B,0xC0,0xC6,0x00,
+// '#'
+0x23,0x07,0x18,0x16,0x00,0x18,
+0x00,0xFF,0xF8,0x01,0x83,0x08,0x01,0x82,0x08,0x01,0x82,0x08,0x0F,0x06,0x0F,0x10,0x00,0x01,0x30,0x00,0x01,0x30,0x00,0x00,0x20,0x00,0x02,0x7E,0x0C,0x1E,0x7E,0x0C,0x1E,0x60,0x00,0x02,0x60,0x00,0x00,0x40,0x00,0x04,0x40,0x00,0x04,0xC0,0x00,0x04,0xFC,0x10,0x78,0xFC,0x30,0x78,0x08,0x30,0x40,0x18,0x30,0x40,0x1F,0xFF,0x80,0x1F,0xFF,0x80,
+// '$'
+0x24,0x09,0x0F,0x14,0x00,0x0F,
+0x01,0x80,0x04,0xF8,0x18,0x08,0x20,0x10,0xC0,0x41,0x00,0x86,0x01,0x0C,0x12,0x18,0x38,0x30,0x60,0xA0,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x10,0x30,0x20,0x6E,0x40,0xFF,0x81,0x9E,0x00,0x18,0x00,
+// '%'
+0x25,0x07,0x17,0x16,0x00,0x17,
+0x0F,0x81,0xF8,0x20,0x84,0x10,0x80,0x98,0x42,0x00,0x21,0x0C,0x01,0x82,0x18,0x43,0x08,0x30,0x04,0x10,0x70,0x18,0x40,0xE0,0x21,0x00,0xF1,0x82,0x00,0xFF,0x0F,0xC0,0xFC,0x10,0x40,0x18,0x40,0x40,0x21,0x80,0x40,0x82,0x00,0x83,0x0C,0x21,0x04,0x18,0x02,0x18,0x78,0x04,0x21,0x30,0x10,0xC2,0x78,0xC3,0xF8,0x7F,0x07,0xE0,0x7C,0x00,
+// '&'
+0x26,0x08,0x17,0x17,0x00,0x17,
+0x01,0xF6,0x00,0x04,0x1A,0x00,0x10,0x04,0x00,0x40,0x10,0x01,0x00,0x40,0x06,0x00,0x80,0x0C,0x1A,0x00,0x18,0x1F,0xC0,0x30,0x18,0xF8,0x60,0x08,0x09,0x80,0x00,0x33,0x00,0x00,0xCC,0x0C,0x01,0x18,0x3C,0x04,0x30,0x30,0x07,0x60,0x00,0x01,0xE0,0x00,0x07,0xC0,0x00,0x09,0xC0,0x0C,0x23,0xC0,0x38,0x03,0xE1,0xF8,0x03,0xFF,0x70,0x01,0xF8,0x60,0x00,
+// '''
+0x27,0x05,0x09,0x0A,0xFF,0x08,
+0x06,0x05,0x86,0x23,0x13,0x11,0x90,0x90,0xC8,0x78,0x18,0x00,
+// '('
+0x28,0x05,0x0D,0x1D,0x00,0x0D,
+0x03,0x00,0x34,0x01,0x90,0x08,0x40,0x81,0x88,0x1C,0xC1,0xC4,0x08,0x60,0x83,0x04,0x10,0x41,0x82,0x0C,0x10,0x60,0x83,0x04,0x18,0x20,0xC0,0x06,0x04,0x38,0x20,0xC0,0x87,0x06,0x38,0x1C,0xE0,0x67,0x86,0x1C,0x60,0x76,0x01,0xE0,0x0E,0x00,0x60,0x00,
+// ')'
+0x29,0x05,0x0D,0x1D,0x00,0x0D,
+0x01,0x00,0x10,0x01,0x20,0x11,0x03,0x04,0x30,0x11,0xE0,0x8F,0x82,0x1C,0x10,0x70,0x81,0x82,0x0E,0x10,0x30,0x81,0x84,0x0C,0x20,0x61,0x02,0x08,0x10,0x01,0x84,0x08,0x20,0x81,0x18,0x11,0x80,0x8E,0x08,0x78,0x80,0xE4,0x03,0xE0,0x0E,0x00,0x30,0x00,
+// '*'
+0x2A,0x09,0x0C,0x0D,0x01,0x0D,
+0x07,0x00,0x88,0x18,0xE4,0x11,0xC0,0x1F,0x8E,0xC0,0x1C,0x11,0xC8,0xBF,0x8E,0x7D,0xC1,0xE0,0x0C,0x00,
+// '+'
+0x2B,0x09,0x15,0x14,0x00,0x15,
+0x00,0x7C,0x00,0x04,0x10,0x00,0x60,0x80,0x07,0x04,0x00,0x38,0x20,0x01,0xC1,0x00,0xFE,0x0F,0xCC,0x00,0x01,0xE0,0x00,0x0F,0x00,0x00,0x78,0x00,0x03,0xFF,0x07,0xFF,0xF8,0x3F,0x7F,0xC1,0xF0,0x0E,0x08,0x00,0x70,0x40,0x03,0x82,0x00,0x1F,0xF0,0x00,0xFE,0x00,0x03,0xE0,0x00,
+// ','
+0x2C,0x17,0x09,0x0B,0x00,0x09,
+0x1E,0x10,0x98,0x38,0x1C,0x0F,0x0B,0xC4,0xE4,0x32,0x1E,0x0E,0x00,
+// '-'
+0x2D,0x11,0x09,0x06,0x00,0x09,
+0x1B,0x90,0x50,0x39,0x2F,0xE7,0xF0,
+// '.'
+0x2E,0x16,0x09,0x09,0x00,0x09,
+0x1E,0x10,0x90,0x38,0x1C,0x0F,0x07,0xCC,0xFC,0x3C,0x00,
+// '/'
+0x2F,0x09,0x11,0x19,0x00,0x11,
+0x00,0x3F,0x80,0x30,0x40,0x30,0x20,0x18,0x20,0x18,0x10,0x0C,0x08,0x06,0x08,0x06,0x04,0x03,0x04,0x03,0x02,0x01,0x81,0x00,0xC1,0x00,0xC0,0x80,0x60,0x00,0x60,0x40,0x30,0x20,0x18,0x20,0x18,0x10,0x0C,0x08,0x06,0x08,0x06,0x04,0x03,0x04,0x03,0xFE,0x01,0xFE,0x00,0xFE,0x00,0x00,
+// '0'
+0x30,0x08,0x17,0x17,0x00,0x17,
+0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00,
+// '1'
+0x31,0x09,0x0D,0x16,0x00,0x0D,
+0x00,0x30,0x06,0x40,0xC4,0x18,0x43,0x02,0x20,0x13,0xC0,0x9E,0x04,0x30,0x21,0x81,0x0C,0x08,0x60,0x43,0x02,0x18,0x10,0xC0,0x86,0x04,0x30,0x21,0x81,0x98,0x03,0xFF,0xE7,0xFE,0x00,0x00,
+// '2'
+0x32,0x08,0x12,0x17,0x00,0x12,
+0x00,0xF0,0x00,0x81,0x00,0xC0,0x20,0xE0,0x04,0x40,0x01,0x38,0x00,0x2F,0x84,0x08,0xF9,0x82,0x1F,0xE0,0x81,0xF0,0x00,0x1C,0x10,0x06,0x04,0x01,0x03,0x80,0xC0,0xD0,0x20,0x04,0x18,0x01,0x0C,0x00,0x43,0x00,0x11,0x80,0x04,0x60,0x01,0x3F,0xFF,0x4F,0xFF,0xE0,0x00,0x30,
+// '3'
+0x33,0x08,0x12,0x17,0x00,0x12,
+0x0C,0x00,0x05,0xFF,0xE3,0x00,0x08,0xC0,0x02,0x30,0x01,0x0C,0x00,0x43,0x00,0x10,0xC0,0x0C,0x37,0x01,0x0F,0x80,0x23,0xE0,0x08,0x10,0x01,0x0F,0xE0,0x43,0xF8,0x10,0x4E,0x04,0x23,0x01,0x10,0x00,0x08,0x00,0x24,0x00,0x13,0x00,0x08,0xFC,0x0C,0x1F,0xFE,0x00,0xFE,0x00,
+// '4'
+0x34,0x09,0x12,0x16,0x00,0x12,
+0x00,0x0E,0x00,0x04,0x80,0x06,0x20,0x03,0x08,0x01,0x82,0x00,0xC0,0x80,0x40,0x20,0x20,0x08,0x10,0x03,0x88,0x20,0x94,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0xFE,0x09,0xFF,0x83,0x80,0x60,0xC0,0x30,0x10,0x0F,0xF8,0x03,0xFC,0x00,0x00,0x00,
+// '5'
+0x35,0x08,0x11,0x17,0x00,0x11,
+0x00,0x03,0x00,0xFF,0x40,0x80,0x20,0xC0,0x10,0x60,0x08,0x30,0x04,0x30,0x02,0x18,0x1D,0x0C,0x07,0x06,0x01,0x82,0x00,0x43,0x00,0x11,0xFC,0x08,0xFF,0x04,0x17,0x82,0x18,0x01,0x18,0x00,0x08,0x00,0x8C,0x00,0x8C,0x00,0xC7,0xC1,0xC3,0xFF,0x80,0xBF,0x00,
+// '6'
+0x36,0x08,0x13,0x17,0x00,0x13,
+0x00,0x7F,0xC0,0x30,0x08,0x18,0x01,0x06,0x00,0x41,0x80,0x10,0x60,0x02,0x08,0x0C,0x83,0x00,0x30,0x60,0x01,0x18,0x00,0x13,0x00,0x01,0x60,0x00,0x0C,0x00,0x03,0x80,0xC0,0x78,0x3C,0x0F,0x03,0x01,0x60,0x00,0x0E,0x00,0x08,0xE0,0x02,0x1E,0x00,0x81,0xF0,0x60,0x1F,0xF8,0x00,0xFC,0x00,
+// '7'
+0x37,0x08,0x12,0x17,0x00,0x12,
+0x0C,0x00,0x0D,0xFF,0xF3,0x00,0x04,0xC0,0x02,0x30,0x00,0x8C,0x00,0x43,0x00,0x10,0xDC,0x08,0x3F,0x02,0x0E,0x81,0x00,0x60,0x40,0x10,0x20,0x0C,0x08,0x02,0x04,0x01,0x81,0x00,0x40,0x80,0x30,0x20,0x10,0x10,0x0F,0x04,0x03,0xF1,0x00,0x3F,0x40,0x03,0xE0,0x00,0x30,0x00,
+// '8'
+0x38,0x08,0x12,0x17,0x00,0x12,
+0x01,0xF0,0x00,0x83,0x00,0xC0,0x20,0x20,0x08,0x10,0x01,0x0C,0x18,0x43,0x06,0x10,0xC0,0x04,0x38,0x01,0x0C,0x00,0x42,0x00,0x09,0x80,0x01,0xC0,0x00,0x70,0x3C,0x1C,0x0F,0x07,0x00,0x01,0xE0,0x00,0x9C,0x00,0x27,0x80,0x30,0xF8,0x38,0x1F,0xFC,0x01,0xFC,0x00,0x00,0x00,
+// '9'
+0x39,0x08,0x13,0x17,0x00,0x13,
+0x01,0xF8,0x00,0xC0,0xC0,0x20,0x04,0x08,0x00,0x42,0x00,0x08,0xC0,0x00,0x90,0x18,0x16,0x07,0x81,0xC0,0x60,0x38,0x00,0x07,0x80,0x00,0xF0,0x00,0x17,0x00,0x02,0xF0,0x00,0x0F,0x80,0x10,0xE6,0x02,0x08,0x00,0x83,0x00,0x20,0x40,0x0C,0x10,0x03,0x06,0xC1,0xC0,0xFF,0xE0,0x1F,0xF0,0x00,
+// ':'
+0x3A,0x0E,0x09,0x11,0x00,0x09,
+0x0E,0x10,0x90,0x38,0x1C,0x0F,0x07,0xC4,0xFC,0x3C,0x19,0x98,0x38,0x1C,0x0F,0x07,0xCC,0xFC,0x3C,0x00,
+// ';'
+0x3B,0x0F,0x09,0x13,0x00,0x09,
+0x0E,0x10,0x98,0x38,0x1C,0x0F,0x07,0xC4,0xFC,0x3E,0x19,0x98,0x38,0x1C,0x0F,0x03,0xC4,0xE4,0x32,0x1E,0x0E,0x00,
+// '<'
+0x3C,0x0A,0x13,0x13,0x00,0x13,
+0x00,0x00,0xC0,0x00,0x64,0x00,0x60,0x80,0x30,0x10,0x38,0x02,0x18,0x03,0x8C,0x01,0xE3,0x01,0xF0,0xE0,0xF8,0x1C,0x06,0x03,0x80,0x18,0x7E,0x00,0xCF,0xF0,0x06,0x7F,0xC0,0x43,0xFE,0x08,0x0F,0xF9,0x00,0x7F,0xE0,0x01,0xF8,0x00,0x0C,0x00,
+// '='
+0x3D,0x0D,0x14,0x0E,0x00,0x14,
+0x3F,0xFF,0xE6,0x00,0x01,0xE0,0x00,0x1E,0x00,0x01,0xE0,0x00,0x1F,0xFF,0xFE,0xFF,0xFF,0xE6,0x00,0x01,0x60,0x00,0x1E,0x00,0x01,0xE0,0x00,0x1F,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF8,
+// '>'
+0x3E,0x0A,0x13,0x13,0x00,0x13,
+0x38,0x00,0x0C,0xC0,0x03,0x87,0x00,0x70,0x18,0x0E,0x00,0xE1,0xF8,0x03,0x3F,0xC0,0x19,0xFF,0x01,0x0F,0xF8,0x20,0x3C,0x04,0x0C,0x00,0x86,0x00,0xE3,0x00,0x78,0xC0,0x7C,0x38,0x3E,0x07,0x3E,0x00,0xFF,0x00,0x1F,0x00,0x03,0x80,0x00,0x00,
+// '?'
+0x3F,0x08,0x11,0x16,0x00,0x11,
+0x00,0xF0,0x01,0x82,0x01,0x00,0x81,0x00,0x23,0x00,0x0B,0x00,0x05,0xC0,0x02,0xF8,0xC1,0x3F,0x40,0x87,0xE0,0x80,0xE0,0x40,0x30,0x40,0x18,0x20,0x0F,0xE0,0x07,0x30,0x03,0x04,0x03,0x02,0x01,0x81,0x00,0xE0,0x80,0x78,0x80,0x1F,0x80,0x07,0x80,
+// '@'
+0x40,0x09,0x16,0x16,0x00,0x16,
+0x00,0x3F,0x00,0x06,0x03,0x00,0x23,0xFB,0x01,0x3F,0xFC,0x09,0x81,0xF8,0x48,0x7F,0xE2,0x44,0x13,0xD9,0x23,0x8F,0x41,0x1E,0x1F,0x2C,0x51,0x7C,0xE0,0xC5,0xF3,0x8B,0x06,0xCE,0x28,0x3B,0x38,0xE2,0xEE,0x71,0x4D,0x19,0xE3,0x88,0x73,0xFF,0xD1,0xE7,0x9F,0xA3,0xC7,0xF1,0x07,0xE0,0x38,0x07,0xFF,0x80,0x07,0xF8,0x00,
+// 'A'
+0x41,0x08,0x19,0x17,0x00,0x19,
+0x00,0x0C,0x00,0x00,0x09,0x00,0x00,0x08,0x80,0x00,0x0C,0x20,0x00,0x04,0x08,0x00,0x06,0x04,0x00,0x02,0x01,0x00,0x03,0x00,0xC0,0x03,0x00,0x20,0x01,0x00,0x08,0x01,0x80,0x04,0x00,0x80,0x01,0x00,0xC0,0xC0,0x40,0xC0,0x60,0x20,0x40,0x00,0x08,0x60,0x00,0x06,0x40,0x00,0x00,0xF0,0x00,0x00,0xFE,0x1F,0xE1,0xCF,0xCF,0xF1,0x81,0xF4,0x1B,0x80,0x3E,0x0F,0x00,0x0E,0x06,0x00,
+// 'B'
+0x42,0x09,0x13,0x15,0x00,0x13,
+0x3F,0xFC,0x0C,0x00,0x61,0xC0,0x06,0x38,0x00,0x43,0x00,0x04,0x60,0x60,0x8C,0x0C,0x11,0x81,0x02,0x30,0x00,0x46,0x00,0x08,0xC0,0x00,0x98,0x18,0x13,0x03,0x02,0x60,0x60,0x4C,0x00,0x09,0x80,0x01,0x30,0x00,0x44,0x00,0x11,0x80,0x0E,0x3F,0xFF,0x07,0xFF,0x80,
+// 'C'
+0x43,0x08,0x16,0x17,0x00,0x16,
+0x00,0x7E,0x00,0x06,0x02,0x00,0x60,0x06,0x02,0x00,0x06,0x10,0x00,0x00,0xC0,0x00,0x22,0x00,0x01,0x18,0x0F,0x18,0x40,0x7E,0xC3,0x02,0x3E,0x0C,0x08,0xF0,0x30,0x21,0x80,0xC0,0x83,0x03,0x81,0x1A,0x0E,0x03,0xC4,0x18,0x00,0x08,0x70,0x00,0x19,0xE0,0x00,0x23,0xC0,0x01,0x87,0x80,0x1E,0x0F,0xC1,0xE0,0x0F,0xFE,0x00,0x0F,0xC0,0x00,
+// 'D'
+0x44,0x09,0x16,0x15,0xFF,0x15,
+0x1F,0xFE,0x00,0x80,0x06,0x07,0x00,0x06,0x1C,0x00,0x0C,0x30,0x00,0x10,0xC0,0x00,0x23,0x00,0x00,0x8C,0x0F,0x01,0x30,0x3E,0x04,0xC0,0xF8,0x13,0x03,0xE0,0x4C,0x0F,0x01,0x30,0x00,0x04,0xC0,0x00,0x23,0x00,0x00,0x8C,0x00,0x04,0x30,0x00,0x20,0x80,0x01,0x06,0x00,0x18,0x1F,0xFF,0x80,0x3F,0xF0,0x00,
+// 'E'
+0x45,0x08,0x11,0x17,0x00,0x11,
+0x00,0x01,0x0F,0xFF,0x48,0x00,0x2C,0x00,0x17,0x00,0x0B,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x1A,0x98,0x01,0x8C,0x00,0x86,0x00,0x43,0x00,0x31,0x80,0xD4,0xC0,0x02,0x60,0x01,0x30,0x00,0x98,0x00,0x4C,0x00,0x24,0x00,0x17,0xFF,0xEB,0xFF,0xF8,0x00,0x08,
+// 'F'
+0x46,0x08,0x11,0x16,0xFF,0x10,
+0x00,0x01,0x0F,0xFF,0x48,0x00,0x2E,0x00,0x17,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x3A,0x98,0x01,0x8C,0x00,0xC6,0x00,0x43,0x00,0x21,0x81,0xD0,0xC0,0xF0,0x60,0x70,0x30,0x20,0x18,0x10,0x08,0x02,0x0C,0x01,0x07,0xFF,0x03,0xFF,0x00,
+// 'G'
+0x47,0x08,0x16,0x17,0x00,0x16,
+0x00,0x7F,0x00,0x06,0x03,0x00,0x60,0x03,0x82,0x00,0x02,0x10,0x00,0x08,0xC0,0x00,0xC2,0x00,0x06,0x18,0x07,0x30,0x40,0x3C,0x83,0x01,0x3C,0x0C,0x04,0xFF,0xB0,0x14,0x02,0xC0,0x78,0x1B,0x80,0xE0,0x4E,0x01,0x81,0x18,0x00,0x04,0x70,0x00,0x11,0xE0,0x00,0x43,0xC0,0x01,0x07,0x80,0x18,0x0F,0x81,0xC0,0x1F,0xFC,0x00,0x0F,0xC0,0x00,
+// 'H'
+0x48,0x09,0x16,0x15,0x00,0x16,
+0x3F,0xC7,0xF9,0x00,0xE0,0x1E,0x03,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x80,0xF0,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x00,0x09,0x80,0x00,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x3C,0x09,0x80,0xF0,0x26,0x02,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x00,0x20,0x1F,0xFF,0xFF,0xFF,0xF7,0xFC,0x00,0x00,0x00,
+// 'I'
+0x49,0x09,0x0B,0x15,0x00,0x0B,
+0x3F,0xC8,0x07,0x81,0x70,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x4C,0x09,0x81,0x30,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x48,0x07,0x00,0xFF,0xEF,0xF8,
+// 'J'
+0x4A,0x09,0x0F,0x16,0x00,0x0F,
+0x03,0xFC,0x08,0x04,0x38,0x08,0x70,0x10,0x60,0x20,0xC0,0x41,0x80,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x08,0x30,0x10,0xA0,0x23,0x00,0x44,0x00,0x98,0x01,0x20,0x00,0xC0,0x09,0x00,0x27,0xC1,0x8F,0xFE,0x0F,0xF0,0x00,
+// 'K'
+0x4B,0x08,0x17,0x18,0x00,0x17,
+0x00,0x01,0x80,0x7F,0xEE,0x81,0x80,0x38,0x87,0x81,0xE0,0x8F,0x03,0x80,0x86,0x06,0x00,0x8C,0x08,0x07,0x18,0x00,0x1C,0x30,0x00,0x70,0x60,0x01,0x00,0xC0,0x02,0x01,0x80,0x02,0x03,0x00,0x04,0x06,0x04,0x04,0x0C,0x0C,0x04,0x18,0x18,0x06,0x30,0x38,0x00,0x60,0x78,0x09,0x00,0x30,0x26,0x00,0x71,0x8F,0xFF,0xE4,0x0F,0xFC,0xD0,0x00,0x01,0xC0,0x00,0x00,0x00,
+// 'L'
+0x4C,0x09,0x11,0x16,0x00,0x11,
+0x3F,0xF0,0x30,0x04,0x1C,0x06,0x0E,0x02,0x03,0x01,0x01,0x80,0x80,0xC0,0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x0C,0x04,0xC6,0x03,0xD3,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x00,0xB0,0x00,0x58,0x00,0x2F,0xFF,0xD7,0xFF,0xF0,0x00,0x30,
+// 'M'
+0x4D,0x09,0x20,0x16,0x00,0x1F,
+0x00,0xF8,0x1F,0x00,0x01,0x84,0x30,0x80,0x01,0x82,0x61,0x00,0x01,0x82,0x40,0x00,0x01,0x81,0xC0,0x80,0x01,0x01,0x80,0x80,0x03,0x00,0x80,0x40,0x02,0x00,0x00,0x40,0x06,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x0C,0x00,0x00,0x10,0x08,0x00,0x00,0x10,0x18,0x08,0x08,0x08,0x10,0x18,0x18,0x08,0x30,0x1C,0x1C,0x04,0x60,0x3C,0x3C,0x02,0x70,0x2E,0x2E,0x06,0x7C,0x27,0x4E,0x3C,0x3F,0x27,0xC6,0xF0,0x07,0xC3,0x8F,0x80,0x01,0x83,0x06,0x00,
+// 'N'
+0x4E,0x09,0x17,0x15,0x00,0x17,
+0x3F,0x07,0xFC,0xC1,0x10,0x07,0xC1,0x78,0x1F,0x81,0xF0,0x23,0x01,0xE0,0x46,0x01,0xC0,0x8C,0x01,0x81,0x18,0x01,0x02,0x30,0x00,0x04,0x60,0x00,0x08,0xC0,0x00,0x11,0x80,0x00,0x23,0x02,0x00,0x46,0x06,0x00,0x8C,0x0E,0x01,0x18,0x1E,0x02,0x30,0x3F,0x04,0x60,0x3F,0x0B,0x00,0x2F,0x17,0xFF,0x8F,0xEF,0xFE,0x0F,0x80,
+// 'O'
+0x4F,0x08,0x17,0x17,0x00,0x17,
+0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00,
+// 'P'
+0x50,0x09,0x13,0x15,0x00,0x13,
+0x3F,0xFC,0x08,0x00,0x43,0x80,0x06,0x70,0x00,0x46,0x00,0x04,0xC0,0x00,0x18,0x00,0x0B,0x01,0x81,0x60,0x30,0x2C,0x04,0x05,0x80,0x00,0x30,0x00,0x26,0x00,0x08,0xC0,0x02,0x18,0x01,0x83,0x01,0xE0,0x60,0x30,0x08,0x04,0x03,0x00,0x80,0x7F,0xE0,0x07,0xF8,0x00,
+// 'Q'
+0x51,0x09,0x17,0x1C,0x00,0x17,
+0x00,0x7E,0x00,0x03,0x01,0x00,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x01,0x08,0x00,0x01,0x30,0x1E,0x02,0x40,0x7E,0x03,0x81,0x1E,0x07,0x02,0x1C,0x0E,0x04,0x18,0x1C,0x04,0x20,0x3C,0x07,0x80,0x78,0x00,0x01,0x30,0x00,0x02,0x70,0x00,0x04,0xF0,0x00,0x10,0xF0,0x00,0x70,0xF0,0x00,0x00,0xFC,0x00,0x40,0xFF,0x81,0x00,0x3F,0x84,0x00,0x0F,0x10,0x00,0x06,0x40,0x00,0x0F,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,
+// 'R'
+0x52,0x09,0x18,0x17,0x00,0x18,
+0x3F,0xFE,0x00,0x60,0x01,0x80,0x70,0x00,0x40,0x70,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x10,0x10,0x30,0x18,0x10,0x30,0x10,0x10,0x30,0x00,0x10,0x30,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x1E,0x30,0x00,0x0A,0x30,0x18,0x06,0x30,0x1C,0x0C,0x20,0x0E,0x18,0x7F,0xFF,0x30,0x7F,0xE7,0x20,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x03,0x00,
+// 'S'
+0x53,0x07,0x11,0x19,0x00,0x11,
+0x00,0x06,0x00,0x02,0x80,0x1E,0x40,0x10,0x20,0x30,0x10,0x30,0x08,0x10,0x04,0x18,0x01,0x08,0x00,0x8C,0x0F,0x46,0x07,0xA3,0x01,0xE1,0xC0,0xE1,0xE0,0x41,0x70,0x21,0x80,0x10,0xC0,0x08,0x70,0x08,0x38,0x04,0x0C,0x04,0x06,0x04,0x03,0x3C,0x01,0xFC,0x00,0xF0,0x00,0x30,0x00,0x00,
+// 'T'
+0x54,0x08,0x12,0x16,0x01,0x13,
+0x30,0x01,0x93,0xFF,0xDC,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0x00,0x01,0xCC,0x07,0x7F,0x01,0xFF,0xC0,0x79,0xB0,0x1C,0x0C,0x04,0x03,0x01,0x00,0xC0,0x40,0x30,0x10,0x0C,0x04,0x03,0x01,0x00,0xC0,0x60,0x60,0x08,0x1F,0xFC,0x07,0xFE,0x00,
+// 'U'
+0x55,0x09,0x19,0x16,0xFF,0x18,
+0x1F,0xF1,0xFF,0x10,0x05,0x00,0x9C,0x07,0xC0,0xCE,0x02,0xE0,0x43,0x01,0x30,0x21,0x80,0x98,0x10,0xC0,0x4C,0x08,0x60,0x26,0x04,0x30,0x13,0x02,0x18,0x09,0x81,0x0C,0x04,0xC0,0x86,0x02,0x60,0x43,0x01,0x30,0x21,0xC0,0x70,0x10,0xE0,0x00,0x10,0x30,0x00,0x08,0x1C,0x00,0x08,0x0F,0x00,0x08,0x03,0xC0,0x08,0x00,0xFC,0x18,0x00,0x1F,0xF8,0x00,0x03,0xF0,0x00,
+// 'V'
+0x56,0x07,0x19,0x18,0x00,0x19,
+0x00,0xC1,0xC0,0x01,0x91,0x98,0x03,0x18,0xC3,0x06,0x04,0x60,0x64,0x02,0x20,0x1F,0x00,0xB0,0x1B,0xC0,0x70,0x0C,0xF0,0x10,0x08,0x38,0x00,0x08,0x0E,0x00,0x04,0x07,0x00,0x04,0x01,0xC0,0x06,0x00,0xE0,0x02,0x00,0x38,0x02,0x00,0x1C,0x01,0x00,0x07,0x01,0x00,0x03,0x81,0x80,0x00,0xE0,0x80,0x00,0x30,0x80,0x00,0x1C,0x40,0x00,0x06,0x40,0x00,0x03,0xE0,0x00,0x00,0xE0,0x00,0x00,0x60,0x00,
+// 'W'
+0x57,0x07,0x20,0x18,0x00,0x20,
+0x00,0x60,0x03,0x80,0x01,0x90,0x06,0x60,0x0E,0x10,0xC6,0x1C,0x38,0x11,0xA6,0x07,0x60,0x11,0x26,0x01,0x78,0x12,0x1C,0x06,0x78,0x0E,0x1C,0x04,0x3C,0x0C,0x08,0x08,0x1C,0x00,0x08,0x10,0x0C,0x00,0x00,0x10,0x0E,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x07,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x80,0x00,0x80,0x03,0x80,0x00,0x80,0x01,0xC0,0x81,0x00,0x01,0xC1,0xC0,0x00,0x00,0xE1,0xC2,0x00,0x00,0xE2,0xE4,0x00,0x00,0x74,0xE4,0x00,0x00,0x7C,0x78,0x00,0x00,0x38,0x78,0x00,0x00,0x30,0x30,0x00,
+// 'X'
+0x58,0x05,0x19,0x1D,0x00,0x18,
+0x00,0x01,0x80,0x00,0x01,0x20,0x00,0x61,0x8C,0x00,0x48,0xC3,0x00,0x44,0x40,0x60,0x43,0x60,0x10,0xC0,0xE0,0x18,0xC0,0x30,0x1C,0xC0,0x00,0x18,0x7C,0x00,0x08,0x1F,0x00,0x08,0x03,0x80,0x08,0x00,0xE0,0x04,0x00,0x38,0x02,0x00,0x0C,0x01,0x80,0x04,0x00,0x60,0x06,0x00,0x10,0x02,0x00,0x07,0x03,0x00,0x00,0x41,0x01,0x00,0x63,0x00,0xC0,0x62,0x00,0xF0,0x63,0x80,0x7C,0x61,0xF0,0x4E,0x60,0x7C,0x63,0xE0,0x0F,0x91,0xE0,0x01,0xF0,0x40,0x00,0x70,0x00,0x00,0x00,0x00,0x00,
+// 'Y'
+0x59,0x06,0x19,0x1B,0x00,0x19,
+0x00,0x00,0x60,0x00,0xC0,0x48,0x00,0x80,0x62,0x00,0x88,0x20,0xC0,0x82,0x30,0x11,0x80,0xB0,0x01,0x80,0x30,0x0D,0x80,0x00,0x0C,0xF8,0x00,0x08,0x3E,0x00,0x04,0x07,0x80,0x04,0x01,0xE0,0x04,0x00,0x78,0x04,0x00,0x1C,0x02,0x00,0x0C,0x02,0x00,0x04,0x02,0x00,0x04,0x02,0x00,0x06,0x01,0x00,0x0E,0x01,0x00,0x0C,0x01,0x00,0x07,0x01,0x00,0x03,0xC0,0x80,0x01,0xF0,0x80,0x00,0x3E,0x00,0x00,0x0F,0xC0,0x00,0x03,0xE0,0x00,0x00,0x40,0x00,0x00,
+// 'Z'
+0x5A,0x08,0x13,0x17,0xFF,0x12,
+0x0C,0x00,0x02,0xFF,0xFC,0xC0,0x01,0x98,0x00,0x23,0x00,0x04,0x60,0x01,0x0C,0x00,0x21,0x80,0x08,0x37,0x01,0x07,0xE0,0x60,0xF8,0x08,0x03,0x01,0x00,0x40,0x70,0x18,0x0D,0x02,0x00,0x20,0xC0,0x04,0x18,0x00,0x86,0x00,0x10,0xC0,0x02,0x10,0x00,0x47,0xFF,0xE8,0xFF,0xFE,0x00,0x01,0x80,
+// '['
+0x5B,0x04,0x0B,0x20,0x00,0x0B,
+0x00,0x40,0x19,0xFD,0x60,0x2C,0x05,0x80,0xB0,0x16,0x0E,0xC1,0xD8,0x33,0x04,0x60,0x8C,0x11,0x82,0x30,0x46,0x08,0xC1,0x18,0x23,0x04,0x60,0x8C,0x11,0x82,0xB0,0x76,0x02,0xC0,0x58,0x0B,0x01,0x60,0x2F,0xFD,0xFF,0x80,0x60,0x00,
+// '\'
+0x5C,0x09,0x11,0x18,0x00,0x11,
+0x3F,0x00,0x30,0x40,0x38,0x10,0x1E,0x08,0x0F,0x04,0x03,0x81,0x01,0xE0,0x80,0x70,0x20,0x3C,0x10,0x1E,0x08,0x07,0x02,0x03,0xC1,0x00,0xE0,0x40,0x78,0x20,0x3C,0x08,0x0F,0x04,0x07,0x82,0x01,0xC0,0x80,0xF0,0x40,0x38,0x10,0x1E,0x08,0x0F,0xFC,0x03,0xFC,0x01,0xFC,
+// ']'
+0x5D,0x04,0x0C,0x1F,0xFF,0x0B,
+0x30,0x02,0xFF,0x60,0x16,0x01,0x60,0x16,0x01,0x60,0x16,0xC1,0x7C,0x16,0xC1,0x6C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x10,0xC1,0x0C,0x13,0xC1,0x6C,0x16,0x01,0x60,0x16,0x01,0x60,0x16,0x01,0x7F,0xE7,0xFE,0x60,0x00,
+// '^'
+0x5E,0x1E,0x00,0x00,0x00,0x09,
+
+// '_'
+0x5F,0x20,0x10,0x04,0x00,0x10,
+0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+// '`'
+0x60,0x00,0x0A,0x09,0x00,0x0B,
+0x00,0x06,0x02,0x61,0x8C,0x60,0xBF,0x17,0xFC,0x3E,0x03,0x00,
+// 'a'
+0x61,0x08,0x19,0x17,0x00,0x19,
+0x00,0x0C,0x00,0x00,0x09,0x00,0x00,0x08,0x80,0x00,0x0C,0x20,0x00,0x04,0x08,0x00,0x06,0x04,0x00,0x02,0x01,0x00,0x03,0x00,0xC0,0x03,0x00,0x20,0x01,0x00,0x08,0x01,0x80,0x04,0x00,0x80,0x01,0x00,0xC0,0xC0,0x40,0xC0,0x60,0x20,0x40,0x00,0x08,0x60,0x00,0x06,0x40,0x00,0x00,0xF0,0x00,0x00,0xFE,0x1F,0xE1,0xCF,0xCF,0xF1,0x81,0xF4,0x1B,0x80,0x3E,0x0F,0x00,0x0E,0x06,0x00,
+// 'b'
+0x62,0x09,0x13,0x15,0x00,0x13,
+0x3F,0xFC,0x0C,0x00,0x61,0xC0,0x06,0x38,0x00,0x43,0x00,0x04,0x60,0x60,0x8C,0x0C,0x11,0x81,0x02,0x30,0x00,0x46,0x00,0x08,0xC0,0x00,0x98,0x18,0x13,0x03,0x02,0x60,0x60,0x4C,0x00,0x09,0x80,0x01,0x30,0x00,0x44,0x00,0x11,0x80,0x0E,0x3F,0xFF,0x07,0xFF,0x80,
+// 'c'
+0x63,0x08,0x16,0x17,0x00,0x16,
+0x00,0x7E,0x00,0x06,0x02,0x00,0x60,0x06,0x02,0x00,0x06,0x10,0x00,0x00,0xC0,0x00,0x22,0x00,0x01,0x18,0x0F,0x18,0x40,0x7E,0xC3,0x02,0x3E,0x0C,0x08,0xF0,0x30,0x21,0x80,0xC0,0x83,0x03,0x81,0x1A,0x0E,0x03,0xC4,0x18,0x00,0x08,0x70,0x00,0x19,0xE0,0x00,0x23,0xC0,0x01,0x87,0x80,0x1E,0x0F,0xC1,0xE0,0x0F,0xFE,0x00,0x0F,0xC0,0x00,
+// 'd'
+0x64,0x09,0x16,0x15,0xFF,0x15,
+0x1F,0xFE,0x00,0x80,0x06,0x07,0x00,0x06,0x1C,0x00,0x0C,0x30,0x00,0x10,0xC0,0x00,0x23,0x00,0x00,0x8C,0x0F,0x01,0x30,0x3E,0x04,0xC0,0xF8,0x13,0x03,0xE0,0x4C,0x0F,0x01,0x30,0x00,0x04,0xC0,0x00,0x23,0x00,0x00,0x8C,0x00,0x04,0x30,0x00,0x20,0x80,0x01,0x06,0x00,0x18,0x1F,0xFF,0x80,0x3F,0xF0,0x00,
+// 'e'
+0x65,0x08,0x11,0x17,0x00,0x11,
+0x00,0x01,0x0F,0xFF,0x48,0x00,0x2C,0x00,0x17,0x00,0x0B,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x1A,0x98,0x01,0x8C,0x00,0x86,0x00,0x43,0x00,0x31,0x80,0xD4,0xC0,0x02,0x60,0x01,0x30,0x00,0x98,0x00,0x4C,0x00,0x24,0x00,0x17,0xFF,0xEB,0xFF,0xF8,0x00,0x08,
+// 'f'
+0x66,0x08,0x11,0x16,0xFF,0x10,
+0x00,0x01,0x0F,0xFF,0x48,0x00,0x2E,0x00,0x17,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x3A,0x98,0x01,0x8C,0x00,0xC6,0x00,0x43,0x00,0x21,0x81,0xD0,0xC0,0xF0,0x60,0x70,0x30,0x20,0x18,0x10,0x08,0x02,0x0C,0x01,0x07,0xFF,0x03,0xFF,0x00,
+// 'g'
+0x67,0x08,0x16,0x17,0x00,0x16,
+0x00,0x7F,0x00,0x06,0x03,0x00,0x60,0x03,0x82,0x00,0x02,0x10,0x00,0x08,0xC0,0x00,0xC2,0x00,0x06,0x18,0x07,0x30,0x40,0x3C,0x83,0x01,0x3C,0x0C,0x04,0xFF,0xB0,0x14,0x02,0xC0,0x78,0x1B,0x80,0xE0,0x4E,0x01,0x81,0x18,0x00,0x04,0x70,0x00,0x11,0xE0,0x00,0x43,0xC0,0x01,0x07,0x80,0x18,0x0F,0x81,0xC0,0x1F,0xFC,0x00,0x0F,0xC0,0x00,
+// 'h'
+0x68,0x09,0x16,0x15,0x00,0x16,
+0x3F,0xC7,0xF9,0x00,0xE0,0x1E,0x03,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x80,0xF0,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x00,0x09,0x80,0x00,0x26,0x00,0x00,0x98,0x00,0x02,0x60,0x3C,0x09,0x80,0xF0,0x26,0x02,0xC0,0x98,0x0B,0x02,0x60,0x2C,0x09,0x00,0x20,0x1F,0xFF,0xFF,0xFF,0xF7,0xFC,0x00,0x00,0x00,
+// 'i'
+0x69,0x09,0x0B,0x15,0x00,0x0B,
+0x3F,0xC8,0x07,0x81,0x70,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x4C,0x09,0x81,0x30,0x26,0x04,0xC0,0x98,0x13,0x02,0x60,0x48,0x07,0x00,0xFF,0xEF,0xF8,
+// 'j'
+0x6A,0x09,0x0F,0x16,0x00,0x0F,
+0x03,0xFC,0x08,0x04,0x38,0x08,0x70,0x10,0x60,0x20,0xC0,0x41,0x80,0x83,0x01,0x06,0x02,0x0C,0x04,0x18,0x08,0x30,0x10,0xA0,0x23,0x00,0x44,0x00,0x98,0x01,0x20,0x00,0xC0,0x09,0x00,0x27,0xC1,0x8F,0xFE,0x0F,0xF0,0x00,
+// 'k'
+0x6B,0x08,0x17,0x18,0x00,0x17,
+0x00,0x01,0x80,0x7F,0xEE,0x81,0x80,0x38,0x87,0x81,0xE0,0x8F,0x03,0x80,0x86,0x06,0x00,0x8C,0x08,0x07,0x18,0x00,0x1C,0x30,0x00,0x70,0x60,0x01,0x00,0xC0,0x02,0x01,0x80,0x02,0x03,0x00,0x04,0x06,0x04,0x04,0x0C,0x0C,0x04,0x18,0x18,0x06,0x30,0x38,0x00,0x60,0x78,0x09,0x00,0x30,0x26,0x00,0x71,0x8F,0xFF,0xE4,0x0F,0xFC,0xD0,0x00,0x01,0xC0,0x00,0x00,0x00,
+// 'l'
+0x6C,0x09,0x11,0x16,0x00,0x11,
+0x3F,0xF0,0x30,0x04,0x1C,0x06,0x0E,0x02,0x03,0x01,0x01,0x80,0x80,0xC0,0x40,0x60,0x20,0x30,0x10,0x18,0x08,0x0C,0x04,0xC6,0x03,0xD3,0x00,0x09,0x80,0x04,0xC0,0x02,0x60,0x01,0x30,0x00,0xB0,0x00,0x58,0x00,0x2F,0xFF,0xD7,0xFF,0xF0,0x00,0x30,
+// 'm'
+0x6D,0x09,0x20,0x16,0x00,0x1F,
+0x00,0xF8,0x1F,0x00,0x01,0x84,0x30,0x80,0x01,0x82,0x61,0x00,0x01,0x82,0x40,0x00,0x01,0x81,0xC0,0x80,0x01,0x01,0x80,0x80,0x03,0x00,0x80,0x40,0x02,0x00,0x00,0x40,0x06,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x04,0x00,0x00,0x10,0x0C,0x00,0x00,0x10,0x08,0x00,0x00,0x10,0x18,0x08,0x08,0x08,0x10,0x18,0x18,0x08,0x30,0x1C,0x1C,0x04,0x60,0x3C,0x3C,0x02,0x70,0x2E,0x2E,0x06,0x7C,0x27,0x4E,0x3C,0x3F,0x27,0xC6,0xF0,0x07,0xC3,0x8F,0x80,0x01,0x83,0x06,0x00,
+// 'n'
+0x6E,0x09,0x17,0x15,0x00,0x17,
+0x3F,0x07,0xFC,0xC1,0x10,0x07,0xC1,0x78,0x1F,0x81,0xF0,0x23,0x01,0xE0,0x46,0x01,0xC0,0x8C,0x01,0x81,0x18,0x01,0x02,0x30,0x00,0x04,0x60,0x00,0x08,0xC0,0x00,0x11,0x80,0x00,0x23,0x02,0x00,0x46,0x06,0x00,0x8C,0x0E,0x01,0x18,0x1E,0x02,0x30,0x3F,0x04,0x60,0x3F,0x0B,0x00,0x2F,0x17,0xFF,0x8F,0xEF,0xFE,0x0F,0x80,
+// 'o'
+0x6F,0x08,0x17,0x17,0x00,0x17,
+0x00,0x7E,0x00,0x03,0x01,0x80,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x00,0x08,0x00,0x01,0x30,0x00,0x02,0x40,0x3C,0x03,0x80,0xFC,0x07,0x02,0x3C,0x0E,0x04,0x38,0x1C,0x08,0x30,0x3C,0x08,0x40,0x78,0x0F,0x01,0x30,0x00,0x02,0x70,0x00,0x08,0xF0,0x00,0x10,0xF0,0x00,0xC0,0xF0,0x03,0x00,0xFC,0x1C,0x00,0xFF,0xE0,0x00,0x3F,0x00,0x00,
+// 'p'
+0x70,0x09,0x13,0x15,0x00,0x13,
+0x3F,0xFC,0x08,0x00,0x43,0x80,0x06,0x70,0x00,0x46,0x00,0x04,0xC0,0x00,0x18,0x00,0x0B,0x01,0x81,0x60,0x30,0x2C,0x04,0x05,0x80,0x00,0x30,0x00,0x26,0x00,0x08,0xC0,0x02,0x18,0x01,0x83,0x01,0xE0,0x60,0x30,0x08,0x04,0x03,0x00,0x80,0x7F,0xE0,0x07,0xF8,0x00,
+// 'q'
+0x71,0x09,0x17,0x1C,0x00,0x17,
+0x00,0x7E,0x00,0x03,0x01,0x00,0x18,0x00,0x80,0x40,0x00,0x81,0x00,0x00,0x86,0x00,0x01,0x08,0x00,0x01,0x30,0x1E,0x02,0x40,0x7E,0x03,0x81,0x1E,0x07,0x02,0x1C,0x0E,0x04,0x18,0x1C,0x04,0x20,0x3C,0x07,0x80,0x78,0x00,0x01,0x30,0x00,0x02,0x70,0x00,0x04,0xF0,0x00,0x10,0xF0,0x00,0x70,0xF0,0x00,0x00,0xFC,0x00,0x40,0xFF,0x81,0x00,0x3F,0x84,0x00,0x0F,0x10,0x00,0x06,0x40,0x00,0x0F,0x00,0x00,0x1C,0x00,0x00,0x00,0x00,
+// 'r'
+0x72,0x09,0x18,0x17,0x00,0x18,
+0x3F,0xFE,0x00,0x60,0x01,0x80,0x70,0x00,0x40,0x70,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x10,0x10,0x30,0x18,0x10,0x30,0x10,0x10,0x30,0x00,0x10,0x30,0x00,0x20,0x30,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x1E,0x30,0x00,0x0A,0x30,0x18,0x06,0x30,0x1C,0x0C,0x20,0x0E,0x18,0x7F,0xFF,0x30,0x7F,0xE7,0x20,0x00,0x03,0xC0,0x00,0x03,0x80,0x00,0x03,0x00,
+// 's'
+0x73,0x07,0x11,0x19,0x00,0x11,
+0x00,0x06,0x00,0x02,0x80,0x1E,0x40,0x10,0x20,0x30,0x10,0x30,0x08,0x10,0x04,0x18,0x01,0x08,0x00,0x8C,0x0F,0x46,0x07,0xA3,0x01,0xE1,0xC0,0xE1,0xE0,0x41,0x70,0x21,0x80,0x10,0xC0,0x08,0x70,0x08,0x38,0x04,0x0C,0x04,0x06,0x04,0x03,0x3C,0x01,0xFC,0x00,0xF0,0x00,0x30,0x00,0x00,
+// 't'
+0x74,0x08,0x12,0x16,0x01,0x13,
+0x30,0x01,0x93,0xFF,0xDC,0x00,0x07,0x00,0x01,0xC0,0x00,0x70,0x00,0x1C,0x00,0x07,0x00,0x01,0xCC,0x07,0x7F,0x01,0xFF,0xC0,0x79,0xB0,0x1C,0x0C,0x04,0x03,0x01,0x00,0xC0,0x40,0x30,0x10,0x0C,0x04,0x03,0x01,0x00,0xC0,0x60,0x60,0x08,0x1F,0xFC,0x07,0xFE,0x00,
+// 'u'
+0x75,0x09,0x19,0x16,0xFF,0x18,
+0x1F,0xF1,0xFF,0x10,0x05,0x00,0x9C,0x07,0xC0,0xCE,0x02,0xE0,0x43,0x01,0x30,0x21,0x80,0x98,0x10,0xC0,0x4C,0x08,0x60,0x26,0x04,0x30,0x13,0x02,0x18,0x09,0x81,0x0C,0x04,0xC0,0x86,0x02,0x60,0x43,0x01,0x30,0x21,0xC0,0x70,0x10,0xE0,0x00,0x10,0x30,0x00,0x08,0x1C,0x00,0x08,0x0F,0x00,0x08,0x03,0xC0,0x08,0x00,0xFC,0x18,0x00,0x1F,0xF8,0x00,0x03,0xF0,0x00,
+// 'v'
+0x76,0x07,0x19,0x18,0x00,0x19,
+0x00,0xC1,0xC0,0x01,0x91,0x98,0x03,0x18,0xC3,0x06,0x04,0x60,0x64,0x02,0x20,0x1F,0x00,0xB0,0x1B,0xC0,0x70,0x0C,0xF0,0x10,0x08,0x38,0x00,0x08,0x0E,0x00,0x04,0x07,0x00,0x04,0x01,0xC0,0x06,0x00,0xE0,0x02,0x00,0x38,0x02,0x00,0x1C,0x01,0x00,0x07,0x01,0x00,0x03,0x81,0x80,0x00,0xE0,0x80,0x00,0x30,0x80,0x00,0x1C,0x40,0x00,0x06,0x40,0x00,0x03,0xE0,0x00,0x00,0xE0,0x00,0x00,0x60,0x00,
+// 'w'
+0x77,0x07,0x20,0x18,0x00,0x20,
+0x00,0x60,0x03,0x80,0x01,0x90,0x06,0x60,0x0E,0x10,0xC6,0x1C,0x38,0x11,0xA6,0x07,0x60,0x11,0x26,0x01,0x78,0x12,0x1C,0x06,0x78,0x0E,0x1C,0x04,0x3C,0x0C,0x08,0x08,0x1C,0x00,0x08,0x10,0x0C,0x00,0x00,0x10,0x0E,0x00,0x00,0x20,0x06,0x00,0x00,0x20,0x07,0x00,0x00,0x40,0x03,0x00,0x00,0x40,0x03,0x80,0x00,0x80,0x03,0x80,0x00,0x80,0x01,0xC0,0x81,0x00,0x01,0xC1,0xC0,0x00,0x00,0xE1,0xC2,0x00,0x00,0xE2,0xE4,0x00,0x00,0x74,0xE4,0x00,0x00,0x7C,0x78,0x00,0x00,0x38,0x78,0x00,0x00,0x30,0x30,0x00,
+// 'x'
+0x78,0x05,0x19,0x1D,0x00,0x18,
+0x00,0x01,0x80,0x00,0x01,0x20,0x00,0x61,0x8C,0x00,0x48,0xC3,0x00,0x44,0x40,0x60,0x43,0x60,0x10,0xC0,0xE0,0x18,0xC0,0x30,0x1C,0xC0,0x00,0x18,0x7C,0x00,0x08,0x1F,0x00,0x08,0x03,0x80,0x08,0x00,0xE0,0x04,0x00,0x38,0x02,0x00,0x0C,0x01,0x80,0x04,0x00,0x60,0x06,0x00,0x10,0x02,0x00,0x07,0x03,0x00,0x00,0x41,0x01,0x00,0x63,0x00,0xC0,0x62,0x00,0xF0,0x63,0x80,0x7C,0x61,0xF0,0x4E,0x60,0x7C,0x63,0xE0,0x0F,0x91,0xE0,0x01,0xF0,0x40,0x00,0x70,0x00,0x00,0x00,0x00,0x00,
+// 'y'
+0x79,0x06,0x19,0x1B,0x00,0x19,
+0x00,0x00,0x60,0x00,0xC0,0x48,0x00,0x80,0x62,0x00,0x88,0x20,0xC0,0x82,0x30,0x11,0x80,0xB0,0x01,0x80,0x30,0x0D,0x80,0x00,0x0C,0xF8,0x00,0x08,0x3E,0x00,0x04,0x07,0x80,0x04,0x01,0xE0,0x04,0x00,0x78,0x04,0x00,0x1C,0x02,0x00,0x0C,0x02,0x00,0x04,0x02,0x00,0x04,0x02,0x00,0x06,0x01,0x00,0x0E,0x01,0x00,0x0C,0x01,0x00,0x07,0x01,0x00,0x03,0xC0,0x80,0x01,0xF0,0x80,0x00,0x3E,0x00,0x00,0x0F,0xC0,0x00,0x03,0xE0,0x00,0x00,0x40,0x00,0x00,
+// 'z'
+0x7A,0x08,0x13,0x17,0xFF,0x12,
+0x0C,0x00,0x02,0xFF,0xFC,0xC0,0x01,0x98,0x00,0x23,0x00,0x04,0x60,0x01,0x0C,0x00,0x21,0x80,0x08,0x37,0x01,0x07,0xE0,0x60,0xF8,0x08,0x03,0x01,0x00,0x40,0x70,0x18,0x0D,0x02,0x00,0x20,0xC0,0x04,0x18,0x00,0x86,0x00,0x10,0xC0,0x02,0x10,0x00,0x47,0xFF,0xE8,0xFF,0xFE,0x00,0x01,0x80,
+// '{'
+0x7B,0x05,0x0D,0x1F,0x01,0x0E,
+0x00,0x10,0x0E,0x81,0x04,0x10,0x21,0x81,0x08,0x08,0xC0,0x46,0x0E,0x30,0x71,0x83,0x0C,0x10,0x60,0x84,0x04,0x60,0x23,0x02,0x18,0x08,0xC0,0x47,0x82,0x3C,0x10,0x60,0x83,0x05,0x18,0x38,0xC0,0x46,0x02,0x38,0x11,0xC0,0x87,0x84,0x1F,0xE0,0x7F,0x00,0x30,0x00,0x00,
+// '|'
+0x7C,0x1E,0x00,0x00,0x00,0x09,
+
+// '}'
+0x7D,0x04,0x0E,0x1F,0x00,0x0F,
+0x30,0x00,0xBC,0x06,0x0C,0x18,0x08,0x60,0x21,0x80,0x46,0x01,0x1F,0x04,0x7C,0x11,0xB0,0x44,0xC1,0x03,0x04,0x0C,0x0C,0x30,0x10,0xE0,0x43,0x81,0x04,0x04,0x30,0x70,0xC1,0x03,0x04,0x0C,0x10,0xF0,0x46,0xC1,0x18,0x04,0x60,0x11,0x80,0x86,0x04,0x18,0x60,0x7F,0x81,0xF8,0x06,0x00,0x00,
+// '~'
+0x7E,0x1E,0x00,0x00,0x00,0x09,
+
+
+// Terminator
+0xFF
+};
diff --git a/components/u8g2 b/components/u8g2
deleted file mode 160000
index 5acf0c6..0000000
--- a/components/u8g2
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 5acf0c652d1ab7fe54d897e809bbb8905f32eca7
diff --git a/components/wifi/CMakeLists.txt b/components/wifi/CMakeLists.txt
new file mode 100644
index 0000000..98cbc38
--- /dev/null
+++ b/components/wifi/CMakeLists.txt
@@ -0,0 +1,3 @@
+idf_component_register(SRCS "wifi.c"
+ INCLUDE_DIRS "."
+ REQUIRES nvs_flash)
\ No newline at end of file
diff --git a/main/Kconfig.projbuild b/components/wifi/Kconfig
similarity index 75%
rename from main/Kconfig.projbuild
rename to components/wifi/Kconfig
index 7e23439..1b07f08 100644
--- a/main/Kconfig.projbuild
+++ b/components/wifi/Kconfig
@@ -1,5 +1,5 @@
# put here your custom config value
-menu "Example Configuration"
+menu "WIFI AP Configuration"
config ESP_WIFI_SSID
string "WiFi SSID"
default "myssid"
@@ -11,4 +11,8 @@ config ESP_WIFI_PASSWORD
default "mypassword"
help
WiFi password (WPA or WPA2) for the example to use.
+
+config ESP_WIFI_RETRIES
+ int "WiFi connection retries"
+ default 5
endmenu
diff --git a/components/wifi/wifi.c b/components/wifi/wifi.c
new file mode 100644
index 0000000..3fbdec1
--- /dev/null
+++ b/components/wifi/wifi.c
@@ -0,0 +1,95 @@
+/*
+ * wifi.c
+ *
+ * Created on: 21 Apr 2020
+ * Author: Chris
+ */
+
+#include "wifi.h"
+
+#define WIFI_CONNECTED_BIT BIT0
+#define WIFI_FAIL_BIT BIT1
+
+static EventGroupHandle_t s_wifi_event_group;
+static const char *TAG = "wifi station";
+static int s_retry_num = 0;
+
+static void event_handler(void* arg, esp_event_base_t event_base,
+ int32_t event_id, void* event_data)
+{
+ if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
+ esp_wifi_connect();
+ } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
+ if (s_retry_num < CONFIG_ESP_WIFI_RETRIES) {
+ esp_wifi_connect();
+ s_retry_num++;
+ ESP_LOGI(TAG, "retry to connect to the AP");
+ } else {
+ xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
+ }
+ ESP_LOGI(TAG,"connect to the AP fail");
+ } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
+ ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
+ ESP_LOGI(TAG, "got ip:%s",
+ ip4addr_ntoa(&event->ip_info.ip));
+ s_retry_num = 0;
+ xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
+ }
+}
+
+void init_wifi_sta()
+{
+ //Initialize NVS
+ esp_err_t ret = nvs_flash_init();
+ if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+ ESP_ERROR_CHECK(nvs_flash_erase());
+ ret = nvs_flash_init();
+ }
+ ESP_ERROR_CHECK(ret);
+
+ s_wifi_event_group = xEventGroupCreate();
+
+ tcpip_adapter_init();
+
+ ESP_ERROR_CHECK(esp_event_loop_create_default());
+
+ wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+ ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+ ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
+ ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
+
+ wifi_config_t wifi_config = {
+ .sta = {
+ .ssid = CONFIG_ESP_WIFI_SSID,
+ .password = CONFIG_ESP_WIFI_PASSWORD
+ },
+ };
+ ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+ ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
+ ESP_ERROR_CHECK(esp_wifi_start() );
+
+ ESP_LOGI(TAG, "wifi_init_sta finished.");
+
+ /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
+ * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
+ EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
+ WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
+ pdFALSE,
+ pdFALSE,
+ portMAX_DELAY);
+
+ //tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info);
+ /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
+ * happened. */
+ if (bits & WIFI_CONNECTED_BIT) {
+ ESP_LOGI(TAG, "connected to ap SSID:%s",
+ CONFIG_ESP_WIFI_SSID);
+ } else if (bits & WIFI_FAIL_BIT) {
+ ESP_LOGI(TAG, "Failed to connect to SSID:%s",
+ CONFIG_ESP_WIFI_SSID);
+ } else {
+ ESP_LOGE(TAG, "UNEXPECTED EVENT");
+ }
+
+}
diff --git a/components/wifi/wifi.h b/components/wifi/wifi.h
new file mode 100644
index 0000000..563764c
--- /dev/null
+++ b/components/wifi/wifi.h
@@ -0,0 +1,20 @@
+/*
+ * wifi.h
+ *
+ * Created on: 21 Apr 2020
+ * Author: Chris
+ */
+
+#ifndef COMPONENTS_WIFI_WIFI_H_
+#define COMPONENTS_WIFI_WIFI_H_
+
+#include
+#include
+#include
+#include "freertos/event_groups.h"
+#include
+#include "nvs_flash.h"
+
+void init_wifi_sta();
+
+#endif /* COMPONENTS_WIFI_WIFI_H_ */
diff --git a/doc/display.pdf b/doc/display.pdf
deleted file mode 100644
index bcb1511..0000000
Binary files a/doc/display.pdf and /dev/null differ
diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 60f0e66..e6c20ac 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -2,7 +2,7 @@
set(COMPONENT_REQUIRES )
set(COMPONENT_PRIV_REQUIRES )
-set(COMPONENT_SRCS "main.c" "u8g2_esp32_hal.c" "bme280.c")
+set(COMPONENT_SRCS "main.c")
set(COMPONENT_ADD_INCLUDEDIRS "")
register_component()
diff --git a/main/main.c b/main/main.c
index 50c43ec..65d3c69 100644
--- a/main/main.c
+++ b/main/main.c
@@ -1,516 +1,39 @@
-#include "bme280.h"
-#include "bme280_defs.h"
-#include "driver/i2c.h"
-#include
-#include "u8g2_esp32_hal.h"
+
+#include "display.h"
+#include "sensors.h"
+#include "wifi.h"
+#include "server.h"
+#include "clock.h"
#include
-#include
-#include "freertos/event_groups.h"
-#include "nvs_flash.h"
-#include
-#include
-
-#define WIFI_SSID "Netzknecht"
-#define WIFI_PASS "***REMOVED***"
-#define WIFI_RETRIES 10
-
-static EventGroupHandle_t s_wifi_event_group;
-#define WIFI_CONNECTED_BIT BIT0
-#define WIFI_FAIL_BIT BIT1
-
-static const char *TAG = "wifi station";
-static int s_retry_num = 0;
-
-char cur_value_str[255];
-
-/* An HTTP GET handler */
-static esp_err_t hello_get_handler(httpd_req_t *req)
-{
- char* buf;
- size_t buf_len;
-
- /* Get header value string length and allocate memory for length + 1,
- * extra byte for null termination */
- buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
- if (buf_len > 1) {
- buf = malloc(buf_len);
- /* Copy null terminated value string into buffer */
- if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
- ESP_LOGI(TAG, "Found header => Host: %s", buf);
- }
- free(buf);
- }
-
- buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-2") + 1;
- if (buf_len > 1) {
- buf = malloc(buf_len);
- if (httpd_req_get_hdr_value_str(req, "Test-Header-2", buf, buf_len) == ESP_OK) {
- ESP_LOGI(TAG, "Found header => Test-Header-2: %s", buf);
- }
- free(buf);
- }
-
- buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-1") + 1;
- if (buf_len > 1) {
- buf = malloc(buf_len);
- if (httpd_req_get_hdr_value_str(req, "Test-Header-1", buf, buf_len) == ESP_OK) {
- ESP_LOGI(TAG, "Found header => Test-Header-1: %s", buf);
- }
- free(buf);
- }
-
- /* Read URL query string length and allocate memory for length + 1,
- * extra byte for null termination */
- buf_len = httpd_req_get_url_query_len(req) + 1;
- if (buf_len > 1) {
- buf = malloc(buf_len);
- if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
- ESP_LOGI(TAG, "Found URL query => %s", buf);
- char param[32];
- /* Get value of expected key from query string */
- if (httpd_query_key_value(buf, "query1", param, sizeof(param)) == ESP_OK) {
- ESP_LOGI(TAG, "Found URL query parameter => query1=%s", param);
- }
- if (httpd_query_key_value(buf, "query3", param, sizeof(param)) == ESP_OK) {
- ESP_LOGI(TAG, "Found URL query parameter => query3=%s", param);
- }
- if (httpd_query_key_value(buf, "query2", param, sizeof(param)) == ESP_OK) {
- ESP_LOGI(TAG, "Found URL query parameter => query2=%s", param);
- }
- }
- free(buf);
- }
-
- /* Set some custom headers */
- httpd_resp_set_hdr(req, "Custom-Header-1", "Custom-Value-1");
- httpd_resp_set_hdr(req, "Custom-Header-2", "Custom-Value-2");
-
- /* Send response with custom headers and body set as the
- * string passed in user context*/
- const char* resp_str = cur_value_str;
- httpd_resp_send(req, resp_str, strlen(resp_str));
-
- /* After sending the HTTP response the old HTTP request
- * headers are lost. Check if HTTP request headers can be read now. */
- if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
- ESP_LOGI(TAG, "Request headers lost");
- }
- return ESP_OK;
-}
-
-static httpd_uri_t hello = {
- .uri = "/hello",
- .method = HTTP_GET,
- .handler = hello_get_handler,
- /* Let's pass response string in user
- * context to demonstrate it's usage */
- .user_ctx = "Hello World!"
-};
-
-static httpd_handle_t start_webserver(void)
-{
- httpd_handle_t server = NULL;
- httpd_config_t config = HTTPD_DEFAULT_CONFIG();
-
- // Start the httpd server
- ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
- if (httpd_start(&server, &config) == ESP_OK) {
- // Set URI handlers
- ESP_LOGI(TAG, "Registering URI handlers");
- httpd_register_uri_handler(server, &hello);
- return server;
- }
-
- ESP_LOGI(TAG, "Error starting server!");
- return NULL;
-}
-
-static void stop_webserver(httpd_handle_t server)
-{
- // Stop the httpd server
- httpd_stop(server);
-}
-
-static void disconnect_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data)
-{
- httpd_handle_t* server = (httpd_handle_t*) arg;
- if (*server) {
- ESP_LOGI(TAG, "Stopping webserver");
- stop_webserver(*server);
- *server = NULL;
- }
-}
-
-static void connect_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data)
-{
- httpd_handle_t* server = (httpd_handle_t*) arg;
- if (*server == NULL) {
- ESP_LOGI(TAG, "Starting webserver");
- *server = start_webserver();
- }
-}
-
-static void event_handler(void* arg, esp_event_base_t event_base,
- int32_t event_id, void* event_data)
-{
- if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
- esp_wifi_connect();
- } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
- if (s_retry_num < WIFI_RETRIES) {
- esp_wifi_connect();
- s_retry_num++;
- ESP_LOGI(TAG, "retry to connect to the AP");
- } else {
- xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
- }
- ESP_LOGI(TAG,"connect to the AP fail");
- } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
- ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
- ESP_LOGI(TAG, "got ip:%s",
- ip4addr_ntoa(&event->ip_info.ip));
- s_retry_num = 0;
- xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
- }
-}
-
-void wifi_init_sta()
-{
- s_wifi_event_group = xEventGroupCreate();
-
- tcpip_adapter_init();
-
- ESP_ERROR_CHECK(esp_event_loop_create_default());
-
- wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
- ESP_ERROR_CHECK(esp_wifi_init(&cfg));
-
- //tcpip_adapter_dhcpc_stop(TCPIP_ADAPTER_IF_STA);
- //tcpip_adapter_ip_info_t info;
- //ip4_addr_t gw;
- //gw.addr = ipaddr_addr("192.168.0.1");
- //info.gw = gw;
- //ip4_addr_t ip;
- //ip.addr = ipaddr_addr("192.168.0.110");
- //info.ip = ip;
- //ip4_addr_t netmask;
- //netmask.addr = ipaddr_addr("255.255.255.0");
- //info.netmask = netmask;
- //tcpip_adapter_sta_start(0, &info);
-
- ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
- ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
-
- wifi_config_t wifi_config = {
- .sta = {
- .ssid = WIFI_SSID,
- .password = WIFI_PASS
- },
- };
- ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
- ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
- ESP_ERROR_CHECK(esp_wifi_start() );
-
- ESP_LOGI(TAG, "wifi_init_sta finished.");
-
- /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
- * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
- EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
- WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
- pdFALSE,
- pdFALSE,
- portMAX_DELAY);
-
- //tcpip_adapter_set_ip_info(TCPIP_ADAPTER_IF_STA, &info);
- /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
- * happened. */
- if (bits & WIFI_CONNECTED_BIT) {
- ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
- WIFI_SSID, WIFI_PASS);
- } else if (bits & WIFI_FAIL_BIT) {
- ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
- WIFI_SSID, WIFI_PASS);
- } else {
- ESP_LOGE(TAG, "UNEXPECTED EVENT");
- }
-
- ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler));
- ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler));
- vEventGroupDelete(s_wifi_event_group);
-}
-
-void i2c_setup()
-{
- printf("Setting up I�C driver on port 1... ");
- i2c_config_t config;
- config.mode = I2C_MODE_MASTER;
- config.sda_io_num = 33;
- config.sda_pullup_en = GPIO_PULLUP_ENABLE;
- config.scl_io_num = 32;
- config.scl_pullup_en = GPIO_PULLUP_ENABLE;
- config.master.clk_speed = 100000;
- i2c_param_config(I2C_NUM_0, &config);
- printf("Set driver parameters... ");
- esp_err_t err = i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0);
- if (err == ESP_OK)
- printf("Driver installed!\n");
- else if (err == ESP_ERR_INVALID_ARG)
- printf("Driver install failed, invalid arguments!\n");
- else
- printf("Driver install failed!\n");
-}
-
-int8_t i2c_read(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len) {
- i2c_cmd_handle_t cmd = i2c_cmd_link_create();
- i2c_master_start(cmd);
- i2c_master_write_byte(cmd, dev_id << 1 | I2C_MASTER_WRITE, 1);
- i2c_master_write_byte(cmd, reg_addr, 1);
- i2c_master_start(cmd);
- i2c_master_write_byte(cmd, dev_id << 1 | I2C_MASTER_READ, 1);
- if (len > 1) {
- i2c_master_read(cmd, data, len - 1, I2C_MASTER_ACK);
- }
- i2c_master_read_byte(cmd, data + len - 1, I2C_MASTER_NACK);
- i2c_master_stop(cmd);
- i2c_master_cmd_begin(I2C_NUM_0, cmd, 500 / portTICK_RATE_MS);
- i2c_cmd_link_delete(cmd);
- return 0;
-}
-
-int8_t i2c_write(uint8_t dev_id, uint8_t reg_addr, uint8_t *data, uint16_t len) {
- //printf("Writing to bus: dev_id=%x, reg_addr=%x, data=%p, length=%u\n", dev_id, reg_addr, data, len);
- i2c_cmd_handle_t cmd = i2c_cmd_link_create();
- i2c_master_start(cmd);
- i2c_master_write_byte(cmd, (dev_id << 1) | I2C_MASTER_WRITE, 1);
- i2c_master_write_byte(cmd, reg_addr, 1);
- i2c_master_write(cmd, data, len, 1);
- i2c_master_stop(cmd);
- i2c_master_cmd_begin(I2C_NUM_0, cmd, 500 / portTICK_RATE_MS);
- i2c_cmd_link_delete(cmd);
- return 0;
-}
-
-void i2c_delay(uint32_t period) {
- vTaskDelay(period / portTICK_PERIOD_MS);
-}
-
-void i2c_shutdown()
-{
- printf("Shutting down I�C bus... ");
- esp_err_t err = i2c_driver_delete(I2C_NUM_0);
- if (err == ESP_ERR_INVALID_ARG)
- printf("Failed, invalid arguments!\n");
- else
- printf("Success!\n");
-}
-
-void read_sensor(struct bme280_dev* dev, int32_t* temp, uint32_t* pressure, uint32_t* humidity) {
-
- uint8_t settings_sel;
- uint32_t req_delay;
- struct bme280_data comp_data;
-
- dev->settings.osr_h = BME280_OVERSAMPLING_16X;
- dev->settings.osr_p = BME280_OVERSAMPLING_16X;
- dev->settings.osr_t = BME280_OVERSAMPLING_16X;
- dev->settings.filter = BME280_FILTER_COEFF_16;
-
- settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
- bme280_set_sensor_settings(settings_sel, dev);
-
- req_delay = 12*bme280_cal_meas_delay(&(dev->settings));
-
- /* Continuously stream sensor data */
- bme280_set_sensor_mode(BME280_FORCED_MODE, dev);
- /* Wait for the measurement to complete and print data @25Hz */
- dev->delay_ms(req_delay / portTICK_PERIOD_MS);
- bme280_get_sensor_data(BME280_ALL, &comp_data, dev);
- *temp = comp_data.temperature;
- *pressure = comp_data.pressure;
- *humidity = comp_data.humidity;
-}
-
-void read_sensor2(struct bme280_dev* dev, int32_t* temp, uint32_t* pressure, uint32_t* humidity) {
-
- uint8_t settings_sel;
- uint32_t req_delay;
- struct bme280_data comp_data;
-
- dev->settings.osr_h = BME280_OVERSAMPLING_16X;
- dev->settings.osr_p = BME280_OVERSAMPLING_16X;
- dev->settings.osr_t = BME280_OVERSAMPLING_16X;
- dev->settings.filter = BME280_FILTER_COEFF_16;
-
- settings_sel = BME280_OSR_PRESS_SEL | BME280_OSR_TEMP_SEL | BME280_OSR_HUM_SEL | BME280_FILTER_SEL;
- bme280_set_sensor_settings(settings_sel, dev);
- /*Calculate the minimum delay required between consecutive measurement based upon the sensor enabled
- * and the oversampling configuration. */
- req_delay = 12*bme280_cal_meas_delay(&(dev->settings));
-
- bme280_set_sensor_mode(BME280_FORCED_MODE, dev);
- /* Wait for the measurement to complete and print data @25Hz */
- dev->delay_ms(req_delay / portTICK_PERIOD_MS);
- bme280_get_sensor_data(BME280_ALL, &comp_data, dev);
- *temp = comp_data.temperature;
- *pressure = comp_data.pressure;
- *humidity = comp_data.humidity;
-}
-
-void print_data(u8g2_t* u8g2, int32_t temp_raw, uint32_t pressure_raw, uint32_t humidity_raw,
- int32_t temp2_raw, uint32_t pressure2_raw, uint32_t humidity2_raw) {
- // Calc temperature pre and post comma values
- int32_t temp_pre = temp_raw / 100;
- int32_t temp_post = (abs(temp_raw) % 100) / 10;
- int32_t temp2_pre = temp2_raw / 100;
- int32_t temp2_post = (abs(temp2_raw) % 100) / 10;
-
- // Calc pressure values
- uint32_t press = pressure_raw / 100;
- uint32_t press2 = pressure2_raw / 100;
-
- // Calc humidity pre and post comma values
- uint32_t humid_pre = humidity_raw / 1024;
- uint32_t humid_post = (humidity_raw - humid_pre*1024) * 10 / 1024;
- uint32_t humid2_pre = humidity2_raw / 1024;
- uint32_t humid2_post = (humidity2_raw - humid2_pre*1024) * 10 / 1024;
-
- // Format temperatures
- char temp_str[2*(sizeof(int)*8+1)+5] = ""; // ""
- char temp_pre_str[sizeof(int)*8+1];
- itoa(temp_pre, temp_pre_str, 10);
- char temp_post_str[sizeof(int)*8+1];
- itoa(temp_post, temp_post_str, 10);
- char temp2_pre_str[sizeof(int)*8+1];
- itoa(temp2_pre, temp2_pre_str, 10);
- char temp2_post_str[sizeof(int)*8+1];
- itoa(temp2_post, temp2_post_str, 10);
- if (temp_pre < 10)
- strcat(temp_str, " "); // Add space if first temperatur is just one digit long " "
- strcat(temp_str, temp_pre_str); // " 1"
- strcat(temp_str, ","); // " 1,"
- strcat(temp_str, temp_post_str); // " 1,3"
- strcat(temp_str, " "); // " 1,3 "
- if (temp2_pre >= 0)
- strcat(temp_str, " "); // Add space if there is no minus sign " 1,3 "
- if (temp2_pre < 10)
- strcat(temp_str, " "); // Add space if second temperatur is just one digit long " 1,3 "
- strcat(temp_str, temp2_pre_str); // " 1,3 7"
- strcat(temp_str, ","); // " 1,3 7,"
- strcat(temp_str, temp2_post_str); // " 1,3 7,2"
- strcat(temp_str, " �C"); // " 1,3 7,2 �C"
-
- // Format temperatures
- char humid_str[2*(sizeof(int)*8+1)+5] = ""; // ""
- char humid_pre_str[sizeof(int)*8+1];
- itoa(humid_pre, humid_pre_str, 10);
- char humid_post_str[sizeof(int)*8+1];
- itoa(humid_post, humid_post_str, 10);
- char humid2_pre_str[sizeof(int)*8+1];
- itoa(humid2_pre, humid2_pre_str, 10);
- char humid2_post_str[sizeof(int)*8+1];
- itoa(humid2_post, humid2_post_str, 10);
- strcat(humid_str, humid_pre_str); // "12"
- strcat(humid_str, ","); // "12,"
- strcat(humid_str, humid_post_str); // "12,5"
- strcat(humid_str, " "); // "12,5 "
- strcat(humid_str, humid2_pre_str); // "12,5 45"
- strcat(humid_str, ","); // "12,5 45,"
- strcat(humid_str, humid2_post_str); // "12,5 45,23"
- strcat(humid_str, " %"); // "12,5 45,23 %"
-
- // Format pressure
- char pressure_str[2*(sizeof(int)*8+1)+5] = ""; // ""
- char press1_str[sizeof(int)*8+1];
- itoa(press, press1_str, 10);
- char press2_str[sizeof(int)*8+1];
- itoa(press2, press2_str, 10);
- if (press < 1000)
- strcat(pressure_str, " ");
- strcat(pressure_str, press1_str);
- strcat(pressure_str, " ");
- if (press2 < 1000)
- strcat(pressure_str, " ");
- strcat(pressure_str, press2_str);
- strcat(pressure_str, " hPa");
-
- u8g2_ClearBuffer(u8g2);
- u8g2_SetFont(u8g2, u8g2_font_profont17_mf);
- int8_t fontheight = u8g2_GetAscent(u8g2);
- int8_t fontmargin = abs(u8g2_GetDescent(u8g2))+2;
- u8g2_DrawStr(u8g2, 0, fontheight, " IN OUT ");
- u8g2_DrawStr(u8g2, 0, 2*fontheight + fontmargin, temp_str);
- u8g2_DrawStr(u8g2, 0, 3*fontheight + 2*fontmargin, humid_str);
- u8g2_DrawStr(u8g2, 0, 4*fontheight + 3*fontmargin, pressure_str);
- u8g2_SendBuffer(u8g2);
-}
void app_main(void)
{
- int32_t temp = 0;
+ int32_t temp = -12;
uint32_t pressure = 0;
uint32_t humidity = 0;
int32_t temp2 = 0;
uint32_t pressure2 = 0;
uint32_t humidity2 = 0;
+ time_str_t time;
- // INIT SENSOR
- i2c_setup();
- struct bme280_dev dev;
- dev.dev_id = 0x76;
- dev.intf = BME280_I2C_INTF;
- dev.read = i2c_read;
- dev.write = i2c_write;
- dev.delay_ms = i2c_delay;
- bme280_init(&dev);
-
- // INIT SENSOR2
- struct bme280_dev dev2;
- dev2.dev_id = 0x77;
- dev2.intf = BME280_I2C_INTF;
- dev2.read = i2c_read;
- dev2.write = i2c_write;
- dev2.delay_ms = i2c_delay;
- bme280_init(&dev2);
-
- // INIT DISPLAY
- u8g2_esp32_hal_t u8g2_esp32_hal = U8G2_ESP32_HAL_DEFAULT;
- u8g2_esp32_hal.sda = 18;
- u8g2_esp32_hal.scl = 19;
-
- u8g2_esp32_hal_init(u8g2_esp32_hal);
- u8g2_t u8g2;
- u8g2_Setup_ssd1306_i2c_128x64_vcomh0_f(&u8g2, U8G2_R0, u8g2_esp32_i2c_byte_cb,
- u8g2_esp32_gpio_and_delay_cb);
- u8x8_SetI2CAddress(&u8g2.u8x8,0x3C << 1);
- u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
- u8g2_SetPowerSave(&u8g2, 0); // wake up display
-
- // INIT WIFI
- //Initialize NVS
- esp_err_t ret = nvs_flash_init();
- if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
- ESP_ERROR_CHECK(nvs_flash_erase());
- ret = nvs_flash_init();
- }
- ESP_ERROR_CHECK(ret);
-
- ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
- wifi_init_sta();
-
- // INIT WEBSERVER
- static httpd_handle_t server = NULL;
- ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &server));
- ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &server));
- server = start_webserver();
+ init_sensors();
+ init_display();
+ init_wifi_sta();
+ init_server();
+ init_clock();
+ /* Draw the whole screen one time */
+ get_time(&time);
+ read_sensor(&temp, &pressure, &humidity);
+ read_sensor2(&temp2, &pressure2, &humidity2);
+ display_data(temp, pressure, humidity, temp2, pressure2, humidity2, time);
while (1) {
- read_sensor(&dev, &temp, &pressure, &humidity);
- read_sensor(&dev2, &temp2, &pressure2, &humidity2);
- printf("%i �c, %i hPa, %i %%\r\n", temp, pressure, humidity);
- printf("%i �c, %i hPa, %i %%\r\n", temp2, pressure2, humidity2);
- print_data(&u8g2, temp, pressure, humidity, temp2, pressure2, humidity2);
- vTaskDelay(250 / portTICK_PERIOD_MS);
+ get_time(&time);
+ read_sensor(&temp, &pressure, &humidity);
+ read_sensor2(&temp2, &pressure2, &humidity2);
+ server_set_values(temp, pressure, humidity, temp2, pressure2, humidity2);
+ update_data(temp, pressure, humidity, temp2, pressure2, humidity2, time);
+ vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
diff --git a/main/u8g2_esp32_hal.c b/main/u8g2_esp32_hal.c
deleted file mode 100644
index 72ae016..0000000
--- a/main/u8g2_esp32_hal.c
+++ /dev/null
@@ -1,166 +0,0 @@
-#include
-#include
-
-#include "sdkconfig.h"
-#include "esp_log.h"
-
-#include "freertos/FreeRTOS.h"
-#include "freertos/task.h"
-
-#include "u8g2_esp32_hal.h"
-
-static const char *TAG = "u8g2_hal";
-static const unsigned int I2C_TIMEOUT_MS = 1000;
-
-static i2c_cmd_handle_t handle_i2c; // I2C handle.
-static u8g2_esp32_hal_t u8g2_esp32_hal; // HAL state data.
-
-#undef ESP_ERROR_CHECK
-#define ESP_ERROR_CHECK(x) do { esp_err_t rc = (x); if (rc != ESP_OK) { ESP_LOGE("err", "esp_err_t = %d", rc); assert(0 && #x);} } while(0);
-
-/*
- * Initialze the ESP32 HAL.
- */
-void u8g2_esp32_hal_init(u8g2_esp32_hal_t u8g2_esp32_hal_param) {
- u8g2_esp32_hal = u8g2_esp32_hal_param;
-} // u8g2_esp32_hal_init
-
-/*
- * HAL callback function as prescribed by the U8G2 library. This callback is invoked
- * to handle I2C communications.
- */
-uint8_t u8g2_esp32_i2c_byte_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
- //ESP_LOGD(TAG, "i2c_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);
-
- switch(msg) {
- case U8X8_MSG_BYTE_SET_DC: {
- if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
- gpio_set_level(u8g2_esp32_hal.dc, arg_int);
- }
- break;
- }
-
- case U8X8_MSG_BYTE_INIT: {
- if (u8g2_esp32_hal.sda == U8G2_ESP32_HAL_UNDEFINED ||
- u8g2_esp32_hal.scl == U8G2_ESP32_HAL_UNDEFINED) {
- break;
- }
-
- i2c_config_t conf;
- conf.mode = I2C_MODE_MASTER;
- ESP_LOGI(TAG, "sda_io_num %d", u8g2_esp32_hal.sda);
- conf.sda_io_num = u8g2_esp32_hal.sda;
- conf.sda_pullup_en = GPIO_PULLUP_ENABLE;
- ESP_LOGI(TAG, "scl_io_num %d", u8g2_esp32_hal.scl);
- conf.scl_io_num = u8g2_esp32_hal.scl;
- conf.scl_pullup_en = GPIO_PULLUP_ENABLE;
- ESP_LOGI(TAG, "clk_speed %d", I2C_MASTER_FREQ_HZ);
- conf.master.clk_speed = I2C_MASTER_FREQ_HZ;
- ESP_LOGI(TAG, "i2c_param_config %d", conf.mode);
- ESP_ERROR_CHECK(i2c_param_config(I2C_MASTER_NUM, &conf));
- ESP_LOGI(TAG, "i2c_driver_install %d", I2C_MASTER_NUM);
- ESP_ERROR_CHECK(i2c_driver_install(I2C_MASTER_NUM, conf.mode, I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0));
- break;
- }
-
- case U8X8_MSG_BYTE_SEND: {
- uint8_t* data_ptr = (uint8_t*)arg_ptr;
- ESP_LOG_BUFFER_HEXDUMP(TAG, data_ptr, arg_int, ESP_LOG_VERBOSE);
-
- while( arg_int > 0 ) {
- ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, *data_ptr, ACK_CHECK_EN));
- data_ptr++;
- arg_int--;
- }
- break;
- }
-
- case U8X8_MSG_BYTE_START_TRANSFER: {
- uint8_t i2c_address = u8x8_GetI2CAddress(u8x8);
- handle_i2c = i2c_cmd_link_create();
- ESP_LOGD(TAG, "Start I2C transfer to %02X.", i2c_address>>1);
- ESP_ERROR_CHECK(i2c_master_start(handle_i2c));
- ESP_ERROR_CHECK(i2c_master_write_byte(handle_i2c, i2c_address | I2C_MASTER_WRITE, ACK_CHECK_EN));
- break;
- }
-
- case U8X8_MSG_BYTE_END_TRANSFER: {
- ESP_LOGD(TAG, "End I2C transfer.");
- ESP_ERROR_CHECK(i2c_master_stop(handle_i2c));
- ESP_ERROR_CHECK(i2c_master_cmd_begin(I2C_MASTER_NUM, handle_i2c, I2C_TIMEOUT_MS / portTICK_RATE_MS));
- i2c_cmd_link_delete(handle_i2c);
- break;
- }
- }
- return 0;
-} // u8g2_esp32_i2c_byte_cb
-
-/*
- * HAL callback function as prescribed by the U8G2 library. This callback is invoked
- * to handle callbacks for GPIO and delay functions.
- */
-uint8_t u8g2_esp32_gpio_and_delay_cb(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
- ESP_LOGD(TAG, "gpio_and_delay_cb: Received a msg: %d, arg_int: %d, arg_ptr: %p", msg, arg_int, arg_ptr);
-
- switch(msg) {
- // Initialize the GPIO and DELAY HAL functions. If the pins for DC and RESET have been
- // specified then we define those pins as GPIO outputs.
- case U8X8_MSG_GPIO_AND_DELAY_INIT: {
- uint64_t bitmask = 0;
- if (u8g2_esp32_hal.dc != U8G2_ESP32_HAL_UNDEFINED) {
- bitmask = bitmask | (1ull< |