diff --git a/Images.md b/Images.md index 36c634f..5213c3a 100644 --- a/Images.md +++ b/Images.md @@ -57,4 +57,6 @@ \ ![ёRadio](images/img27.jpg)\ \ -![ёRadio](images/img28.jpg) +![ёRadio](images/img28.jpg)\ +\ +![ёRadio](images/img29.jpg) diff --git a/README.md b/README.md index ff2d72d..6a363cf 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,8 @@ https://aliexpress.com/item/32965676064.html - or **LCD1602** 16x2 I2C https://aliexpress.com/item/32305776560.html - or **LCD1602** 16x2 without I2C https://aliexpress.com/item/32305776560.html - or **SSD1327** 1.5' 128x128 I2C https://aliexpress.com/item/1005001414175498.html -- or **ILI9341** 3.2'320x240 SPI https://aliexpress.com/item/33048191074.html +- or **ILI9341** 3.2' 320x240 SPI https://aliexpress.com/item/33048191074.html +- or **ILI9341** 2.8' 320x240 SPI https://aliexpress.com/item/1005004502250619.html - or **SSD1305 (SSD1309)** 2.4' 128x64 SPI/I2C https://aliexpress.com/item/32950307344.html - or **SH1107** 0.96' 128x64 I2C https://aliexpress.com/item/4000551696674.html - or **GC9106** 0.96' 160x80 SPI (looks like ST7735S, but it's not him) https://aliexpress.com/item/32947890530.html @@ -53,6 +54,7 @@ https://aliexpress.com/item/32965676064.html - or **LCD2004** 20x4 without I2C https://aliexpress.com/item/32783128355.html - or **ILI9225** 2.0' 220x176 SPI https://aliexpress.com/item/32952021835.html - or **Nextion displays** - [more info](https://github.com/e2002/yoradio/tree/main/nextion) +- or **ST7796** 3.5' 480x320 SPI https://aliexpress.com/item/1005004632953455.html?sku_id=12000029911293172 ##### Controls - Three tact buttons https://www.aliexpress.com/item/32907144687.html @@ -299,6 +301,16 @@ Work is in progress... --- ## Version history +#### v0.8.03b +- added support for ST7796 display +- added support for capacitive touch GT911 +- HSPI bus support added - DSP_HSPI, VS_HSPI, TS_HSPI options More details in examples/myoptions.h +- changed the method of connecting the touchscreen in myoptions.h Now instead of specifying TS_CS, you must specify TS_MODEL (by default TS_MODEL_UNDEFINED) More details in examples/myoptions.h +- new parameters TS_SDA, TS_SCL, TS_INT, TS_RST for GT911 touchscreen +- new parameters LIGHT_SENSOR and AUTOBACKLIGHT - to automatically adjust the brightness of the display. More details in examples/myoptions.h +- new parameter LED_INVERT (true/false) - to invert the behavior of the built-in LED +- fixed bug with extra sign } in humidity value + #### v0.8.02b - fixed artifacts when displaying the volume level - changes in mytheme.h . Added colors COLOR_PL_CURRENT, COLOR_PL_CURRENT_BG, COLOR_PL_CURRENT_FILL. Details in [exsamples/mytheme.h](exsamples/mytheme.h) diff --git a/exsamples/myoptions.h b/exsamples/myoptions.h index 7b14a93..36eee34 100644 --- a/exsamples/myoptions.h +++ b/exsamples/myoptions.h @@ -10,6 +10,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti ********************************************************/ #define LED_BUILTIN 2 /* Onboard LED Pin */ +//#define LED_INVERT false /* Invert Onboard LED? */ #define L10N_LANGUAGE EN /* Language (EN, RU). More info in yoRadio/locale/displayL10n_(en|ru).h /* DSP_MODEL. See description/available values in https://github.com/e2002/yoradio/wiki/Available-display-models */ @@ -25,11 +26,14 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti */ /******************************************/ -/* SPI PINS. SCL(SCK, CLK) must be connected to pin 18 - SDA(MOSI, DIN, SDI) must be connected to pin 23 */ +/* VSPI PINS. SCL(SCK, CLK) must be connected to pin 18 + SDA(MOSI, DIN, SDI) must be connected to pin 23 */ //#define TFT_CS 5 /* SPI CS pin */ //#define TFT_RST 15 /* SPI RST pin. set to -1 and connect to Esp EN pin */ //#define TFT_DC 4 /* SPI DC/RS pin */ +/* HSPI PINS. SCL(SCK, CLK) must be connected to pin 14 + SDA(MOSI, DIN, SDI) must be connected to pin 13 */ +//#define DSP_HSPI false /* Use HSPI for display */ /******************************************/ /* NEXTION */ @@ -47,13 +51,17 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti //#define I2S_LRC 25 /* WSEL Left Right Clock */ /******************************************/ -/* VS1053 PINS. VS1053 SCK must be connected to pin 18 - VS1053 MISO must be connected to pin 19 - VS1053 MOSI must be connected to pin 23 */ +/* VS1053 VSPI PINS. VS1053 SCK must be connected to pin 18 + VS1053 MISO must be connected to pin 19 + VS1053 MOSI must be connected to pin 23 */ //#define VS1053_CS 255 /* XCS pin. Should be set to 255 if the board is not used */ //#define VS1053_DCS 25 /* XDCS pin. */ //#define VS1053_DREQ 26 /* DREQ pin. */ //#define VS1053_RST -1 /* XRESET pin. Set to -1 if connected to Esp EN pin */ +/* VS1053 HSPI PINS. VS1053 SCK must be connected to pin 14 + VS1053 MISO must be connected to pin 12 + VS1053 MOSI must be connected to pin 13 */ +//#define VS_HSPI false /* Use HSPI for VS */ /******************************************/ /* ENCODER */ @@ -94,11 +102,25 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti /******************************************/ /* TOUCHSCREEN */ -/* SPI PINS. CLK must be connected to pin 18 - DIN must be connected to pin 23 - DO must be connected to pin 19 - IRQ - not connected */ -//#define TS_CS 255 /* Touch screen CS pin +//#define TS_MODEL TS_MODEL_UNDEFINED /* See description/available values in yoRadio/src/core/options.h */ + +/* Resistive SPI touch screen */ +/* TS VSPI PINS. CLK must be connected to pin 18 + DIN must be connected to pin 23 + DO must be connected to pin 19 + IRQ - not connected */ +//#define TS_CS 255 /* Touch screen CS pin */ +/* TS HSPI PINS. CLK must be connected to pin 14 + DIN must be connected to pin 13 + DO must be connected to pin 12 + IRQ - not connected */ +//#define TS_HSPI false /* Use HSPI for Touch screen */ + +/* Capacitive I2C touch screen */ +//#define TS_SDA 33 +//#define TS_SCL 32 +//#define TS_INT 21 +//#define TS_RST 25 /******************************************/ /* Other settings. */ @@ -118,12 +140,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti //#define ROTATE_90 false /* Optional 90 degree rotation for square displays */ //#define WAKE_PIN 255 /* Wake Pin (for manual wakeup from sleep mode. can match with BTN_XXXX, ENC_BTNB, ENC2_BTNB. must be one of: 0,2,4,12,13,14,15,25,26,27,32,33,34,35,36,39) */ /* For sample #define ENC_BTNB 36 - next line - #define WAKE_PIN ENC_BTNB */ -/* VU settings. See the default settings for your display in file yoRadio/display_vu.h */ -/************************************************************************************************************************************************************************************/ -/* vu left | vu top | band width | band height | band space | num of bands | fade speed | horisontal | Max Bands Color | Min Bands Color */ -/************************************************************************************************************************************************************************************/ -//#define VU_PARAMS2 { VU_X = 4, VU_Y = 60, VU_BW = 10, VU_BH = 34, VU_BS = 2, VU_NB = 8, VU_FS = 2, VU_HOR = 0, VU_COLOR_MAX = TFT_LOGO, VU_COLOR_MIN = SILVER } -/************************************************************************************************************************************************************************************/ +//#define LIGHT_SENSOR 255 /* Light sensor */ +//#define AUTOBACKLIGHT(x) *function* /* Autobacklight function. See options.h for exsample */ +/******************************************/ /* IR control */ //#define IR_PIN 255 diff --git a/images/img29.jpg b/images/img29.jpg new file mode 100644 index 0000000..be19392 Binary files /dev/null and b/images/img29.jpg differ diff --git a/yoRadio/src/Adafruit_ST7796S/Adafruit_ST7796S_kbv.cpp b/yoRadio/src/Adafruit_ST7796S/Adafruit_ST7796S_kbv.cpp new file mode 100644 index 0000000..536884a --- /dev/null +++ b/yoRadio/src/Adafruit_ST7796S/Adafruit_ST7796S_kbv.cpp @@ -0,0 +1,341 @@ +#include "../core/options.h" +#if DSP_MODEL==DSP_ST7796 +/*! + * These displays use SPI to communicate, 4 or 5 pins are required + * to interface (RST is optional). + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * @section dependencies Dependencies + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * @section author Author + * + * Written by Limor "ladyada" Fried for Adafruit Industries. + * + * @section license License + * + * BSD license, all text here must be included in any redistribution. + * + */ + +#include "Adafruit_ST7796S_kbv.h" +#ifndef ARDUINO_STM32_FEATHER +#include "pins_arduino.h" +#ifndef RASPI +#include "wiring_private.h" +#endif +#endif +#include + +#if defined(ARDUINO_ARCH_ARC32) || defined(ARDUINO_MAXIM) +#define SPI_DEFAULT_FREQ 16000000 +// Teensy 3.0, 3.1/3.2, 3.5, 3.6 +#elif defined(__MK20DX128__) || defined(__MK20DX256__) || \ + defined(__MK64FX512__) || defined(__MK66FX1M0__) +#define SPI_DEFAULT_FREQ 40000000 +#elif defined(__AVR__) || defined(TEENSYDUINO) +#define SPI_DEFAULT_FREQ 8000000 +#elif defined(ESP8266) || defined(ESP32) +#define SPI_DEFAULT_FREQ 40000000 +#elif defined(RASPI) +#define SPI_DEFAULT_FREQ 80000000 +#elif defined(ARDUINO_ARCH_STM32F1) +#define SPI_DEFAULT_FREQ 36000000 +#else +#define SPI_DEFAULT_FREQ 24000000 ///< Default SPI data clock frequency +#endif + +#define MADCTL_MY 0x80 ///< Bottom to top +#define MADCTL_MX 0x40 ///< Right to left +#define MADCTL_MV 0x20 ///< Reverse Mode +#define MADCTL_ML 0x10 ///< LCD refresh Bottom to top +#define MADCTL_RGB 0x00 ///< Red-Green-Blue pixel order +#define MADCTL_BGR 0x08 ///< Blue-Green-Red pixel order +#define MADCTL_MH 0x04 ///< LCD refresh right to left + +/**************************************************************************/ +/*! + @brief Instantiate Adafruit ST7796S driver with software SPI + @param cs Chip select pin # + @param dc Data/Command pin # + @param mosi SPI MOSI pin # + @param sclk SPI Clock pin # + @param rst Reset pin # (optional, pass -1 if unused) + @param miso SPI MISO pin # (optional, pass -1 if unused) +*/ +/**************************************************************************/ +Adafruit_ST7796S_kbv::Adafruit_ST7796S_kbv(int8_t cs, int8_t dc, int8_t mosi, + int8_t sclk, int8_t rst, int8_t miso) + : Adafruit_SPITFT(ST7796S_TFTWIDTH, ST7796S_TFTHEIGHT, cs, dc, mosi, sclk, + rst, miso) {} + +/**************************************************************************/ +/*! + @brief Instantiate Adafruit ST7796S driver with hardware SPI using the + default SPI peripheral. + @param cs Chip select pin # (OK to pass -1 if CS tied to GND). + @param dc Data/Command pin # (required). + @param rst Reset pin # (optional, pass -1 if unused). +*/ +/**************************************************************************/ +Adafruit_ST7796S_kbv::Adafruit_ST7796S_kbv(int8_t cs, int8_t dc, int8_t rst) + : Adafruit_SPITFT(ST7796S_TFTWIDTH, ST7796S_TFTHEIGHT, cs, dc, rst) {} + +#if !defined(ESP8266) +/**************************************************************************/ +/*! + @brief Instantiate Adafruit ST7796S driver with hardware SPI using + a specific SPI peripheral (not necessarily default). + @param spiClass Pointer to SPI peripheral (e.g. &SPI or &SPI1). + @param dc Data/Command pin # (required). + @param cs Chip select pin # (optional, pass -1 if unused and + CS is tied to GND). + @param rst Reset pin # (optional, pass -1 if unused). +*/ +/**************************************************************************/ +Adafruit_ST7796S_kbv::Adafruit_ST7796S_kbv(SPIClass *spiClass, int8_t dc, int8_t cs, + int8_t rst) + : Adafruit_SPITFT(ST7796S_TFTWIDTH, ST7796S_TFTHEIGHT, spiClass, cs, dc, + rst) {} +#endif // end !ESP8266 + +/**************************************************************************/ +/*! + @brief Instantiate Adafruit ST7796S driver using parallel interface. + @param busWidth If tft16 (enumeration in Adafruit_SPITFT.h), is a + 16-bit interface, else 8-bit. + @param d0 Data pin 0 (MUST be a byte- or word-aligned LSB of a + PORT register -- pins 1-n are extrapolated from this). + @param wr Write strobe pin # (required). + @param dc Data/Command pin # (required). + @param cs Chip select pin # (optional, pass -1 if unused and CS + is tied to GND). + @param rst Reset pin # (optional, pass -1 if unused). + @param rd Read strobe pin # (optional, pass -1 if unused). +*/ +/**************************************************************************/ +Adafruit_ST7796S_kbv::Adafruit_ST7796S_kbv(tftBusWidth busWidth, int8_t d0, int8_t wr, + int8_t dc, int8_t cs, int8_t rst, int8_t rd) + : Adafruit_SPITFT(ST7796S_TFTWIDTH, ST7796S_TFTHEIGHT, busWidth, d0, wr, dc, + cs, rst, rd) {} + +// clang-format off +static const uint8_t PROGMEM initcmd[] = { + // (COMMAND_BYTE), n, data_bytes.... + 0x01, 0x80, // Soft reset, then delay 150 ms + 0xF0, 1, 0xC3, // ?? Unlock Manufacturer + 0xF0, 1, 0x96, +#if 0 +#elif 0 //LCDWIKI + 0x36, 1, 0x68, + 0x3A, 1, 0x05, + 0 0xB0, 1, 0x80, + 0xB6, 2, 0x00, 0x02, + 0xB5, 4, 0x02, 0x03, 0x00, 0x04, + 0xB1, 2, 0x80, 0x10, + 0xB4, 1, 0x00, + 0xB7, 1, 0xC6, + 0xC5, 1, 0x24, + 0xE4, 1, 0x31, + 0xE8, 8, 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33, + 0xC2, 1, 0xA7, + 0xE0, 14, 0xF0, 0x09, 0x13, 0x12, 0x12, 0x2B, 0x3C, 0x44, 0x4B, 0x1B, 0x18, 0x17, 0x1D, 0x21, + 0xE1, 14, 0xF0, 0x09, 0x13, 0x0C, 0x0D, 0x27, 0x3B, 0x44, 0x4D, 0x0B, 0x17 ,0x17, 0x1D, 0x21, + 0x36, 1, 0x48, + +#elif 0 //TFT_eSPI + 0x36, 1, 0x48, + 0x3A, 1, 0x05, //Interlace Pixel Format [XX] + 0xB4, 1, 0x01, //Inversion Control [01] + 0xB6, 3, 0x80, 0x02, 0x3B, // Display Function Control [80 02 3B] + 0xE8, 8, 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33, //Adjustment Control 3 [40 8A 00 00 25 0A 38 33] + 0xC1, 1, 0x06, //Power Control 2 [13] + 0xC2, 1, 0xA7, //Power Control 3 [A?] + 0xC5, 1, 0x18, //VCOM=0.9 [1C] + //0x11, 0x80, //delay 150 ms + (0xE0), 14, 0xF0, 0x09, 0x0B, 0x06, 0x04, 0x15, 0x2F, 0x54, 0x42, 0x3C, 0x17, 0x14, 0x18, 0x1B, //PVGAMCTRL: Positive Voltage Gamma control + (0xE1), 14, 0xE0, 0x09, 0x0B, 0x06, 0x04, 0x03, 0x2B, 0x43, 0x42, 0x3B, 0x16, 0x14, 0x17, 0x1B, //NVGAMCTRL: Negative Voltage Gamma control +#else +// 0xC0, 2, 0x10, 0x10, //Power Control 1 [80 25] +// 0xC1, 1, 0x41, //Power Control 2 [13] + 0xC5, 1, 0x1C, //VCOM Control 1 [1C] + 0x36, 1, 0x48, //Memory Access [00] + 0x3A, 1, 0x55, //565 + 0xB0, 1, 0x80, //Interface [00] + //0xB1, 2, 0xB0, 0x11, //Frame Rate Control [A0 10] + 0xB4, 1, 0x01, //Inversion Control [01] + 0xB6, 3, 0x80, 0x02, 0x3B, // Display Function Control [80 02 3B] .kbv SS=1, NL=480 + 0xB7, 1, 0xC6, //Entry Mode [06] +// 0x3A, 1, 0x66, //Interlace Pixel Format [XX] +// 0xF7, 4, 0xA9, 0x51, 0x2C, 0x82, //Adjustment Control 3 [A9 51 2C 82] +#endif + 0xF0, 1, 0x69, //?? lock manufacturer commands + 0xF0, 1, 0x3C, // + 0x11, 0x80, // Exit Sleep, then delay 150 ms + 0x29, 0x80, // Main screen turn on, delay 150 ms + 0x00 // End of list +}; +// clang-format on + +/**************************************************************************/ +/*! + @brief Initialize ST7796S chip + Connects to the ST7796S over SPI and sends initialization procedure commands + @param freq Desired SPI clock frequency +*/ +/**************************************************************************/ +void Adafruit_ST7796S_kbv::begin(uint32_t freq) { + + if (!freq) + freq = SPI_DEFAULT_FREQ; + initSPI(freq); + + if (_rst < 0) { // If no hardware reset pin... + sendCommand(ST7796S_SWRESET); // Engage software reset + delay(150); + } + + uint8_t cmd, x, numArgs; + const uint8_t *addr = initcmd; + while ((cmd = pgm_read_byte(addr++)) > 0) { + x = pgm_read_byte(addr++); + numArgs = x & 0x7F; + sendCommand(cmd, addr, numArgs); + addr += numArgs; + if (x & 0x80) + delay(150); + } + + _width = ST7796S_TFTWIDTH; + _height = ST7796S_TFTHEIGHT; +} + +/**************************************************************************/ +/*! + @brief Set origin of (0,0) and orientation of TFT display + @param m The index for rotation, from 0-3 inclusive +*/ +/**************************************************************************/ +void Adafruit_ST7796S_kbv::setRotation(uint8_t m) { + rotation = m % 4; // can't be higher than 3 + switch (rotation) { + case 0: + m = (MADCTL_MX | MADCTL_BGR); + _width = ST7796S_TFTWIDTH; + _height = ST7796S_TFTHEIGHT; + break; + case 1: + m = (MADCTL_MV | MADCTL_BGR); + _width = ST7796S_TFTHEIGHT; + _height = ST7796S_TFTWIDTH; + break; + case 2: + m = (MADCTL_MY | MADCTL_ML | MADCTL_BGR); + _width = ST7796S_TFTWIDTH; + _height = ST7796S_TFTHEIGHT; + break; + case 3: + m = (MADCTL_MX | MADCTL_MY | MADCTL_MV | MADCTL_ML | MADCTL_BGR); + _width = ST7796S_TFTHEIGHT; + _height = ST7796S_TFTWIDTH; + break; + } + + sendCommand(ST7796S_MADCTL, &m, 1); + setScrollMargins(0, 0); //.kbv + scrollTo(0); +} + +/**************************************************************************/ +/*! + @brief Enable/Disable display color inversion + @param invert True to invert, False to have normal color +*/ +/**************************************************************************/ +void Adafruit_ST7796S_kbv::invertDisplay(bool invert) { + sendCommand(invert ? ST7796S_INVON : ST7796S_INVOFF); +} + +/**************************************************************************/ +/*! + @brief Scroll display memory + @param y How many pixels to scroll display by +*/ +/**************************************************************************/ +void Adafruit_ST7796S_kbv::scrollTo(uint16_t y) { + uint8_t data[2]; + data[0] = y >> 8; + data[1] = y & 0xff; + sendCommand(ST7796S_VSCRSADD, (uint8_t *)data, 2); +} + +/**************************************************************************/ +/*! + @brief Set the height of the Top and Bottom Scroll Margins + @param top The height of the Top scroll margin + @param bottom The height of the Bottom scroll margin + */ +/**************************************************************************/ +void Adafruit_ST7796S_kbv::setScrollMargins(uint16_t top, uint16_t bottom) { + // TFA+VSA+BFA must equal 480 + if (top + bottom <= ST7796S_TFTHEIGHT) { + uint16_t middle = ST7796S_TFTHEIGHT - top - bottom; + uint8_t data[6]; + data[0] = top >> 8; + data[1] = top & 0xff; + data[2] = middle >> 8; + data[3] = middle & 0xff; + data[4] = bottom >> 8; + data[5] = bottom & 0xff; + sendCommand(ST7796S_VSCRDEF, (uint8_t *)data, 6); + } +} + +/**************************************************************************/ +/*! + @brief Set the "address window" - the rectangle we will write to RAM with + the next chunk of SPI data writes. The ST7796S will automatically wrap + the data as each row is filled + @param x1 TFT memory 'x' origin + @param y1 TFT memory 'y' origin + @param w Width of rectangle + @param h Height of rectangle +*/ +/**************************************************************************/ +void Adafruit_ST7796S_kbv::setAddrWindow(uint16_t x1, uint16_t y1, uint16_t w, + uint16_t h) { + uint16_t x2 = (x1 + w - 1), y2 = (y1 + h - 1); + writeCommand(ST7796S_CASET); // Column address set + SPI_WRITE16(x1); + SPI_WRITE16(x2); + writeCommand(ST7796S_PASET); // Row address set + SPI_WRITE16(y1); + SPI_WRITE16(y2); + writeCommand(ST7796S_RAMWR); // Write to RAM +} + +/**************************************************************************/ +/*! + @brief Read 8 bits of data from ST7796S configuration memory. NOT from RAM! + This is highly undocumented/supported, it's really a hack but kinda + works? + @param commandByte The command register to read data from + @param index The byte index into the command to read from + @return Unsigned 8-bit data read from ST7796S register + */ +/**************************************************************************/ +uint8_t Adafruit_ST7796S_kbv::readcommand8(uint8_t commandByte, uint8_t index) { + uint8_t data = 0x10 + index, ret; + sendCommand(0xFB, &data, 1); // Set Index Register + ret = Adafruit_SPITFT::readcommand8(commandByte); + data = 0x00; + sendCommand(0xFB, &data, 1); // Set Index Register + return ret; +} +#endif diff --git a/yoRadio/src/Adafruit_ST7796S/Adafruit_ST7796S_kbv.h b/yoRadio/src/Adafruit_ST7796S/Adafruit_ST7796S_kbv.h new file mode 100644 index 0000000..baf210b --- /dev/null +++ b/yoRadio/src/Adafruit_ST7796S/Adafruit_ST7796S_kbv.h @@ -0,0 +1,129 @@ +/* + * Adafruit_ST7796S_kbv class inherits from Adafruit_GFX, Adafruit_SPITFT class and the Arduino Print class. + * Adafruit_ST7796S_kbv written by David Prentice + * + * Any use of Adafruit_ST7796S_kbv class and examples is dependent on Adafruit and Arduino licenses + * The license texts are in the accompanying license.txt file + */ + +/*! + * @file Adafruit_ST7796S_kbv.h + * + * These displays use SPI to communicate, 4 or 5 pins are required + * to interface (RST is optional IF YOU ADD A PULLUP RESISTOR). + * + * Adafruit invests time and resources providing this open source code, + * please support Adafruit and open-source hardware by purchasing + * products from Adafruit! + * + * + * This library depends on + * Adafruit_GFX being present on your system. Please make sure you have + * installed the latest version before using this library. + * + * Adafruit_GFX, Adafruit_SPITFT written by Limor "ladyada" Fried for Adafruit Industries. + * + * BSD license, all text here must be included in any redistribution. + * + */ + + +#ifndef _ADAFRUIT_ST7796S_KBV_H_ +#define _ADAFRUIT_ST7796S_KBV_H_ + +#include "Adafruit_GFX.h" +#include "Arduino.h" +#include "Print.h" +#include +#include +#include + +#define ST7796S_TFTWIDTH 320 ///< ST7796S max TFT width +#define ST7796S_TFTHEIGHT 480 ///< ST7796S max TFT height + +#define ST7796S_NOP 0x00 ///< No-op register +#define ST7796S_SWRESET 0x01 ///< Software reset register +#define ST7796S_RDDID 0x04 ///< Read display identification information +#define ST7796S_RDDST 0x09 ///< Read Display Status + +#define ST7796S_SLPIN 0x10 ///< Enter Sleep Mode +#define ST7796S_SLPOUT 0x11 ///< Sleep Out +#define ST7796S_PTLON 0x12 ///< Partial Mode ON +#define ST7796S_NORON 0x13 ///< Normal Display Mode ON + +#define ST7796S_RDMODE 0x0A ///< Read Display Power Mode +#define ST7796S_RDMADCTL 0x0B ///< Read Display MADCTL +#define ST7796S_RDPIXFMT 0x0C ///< Read Display Pixel Format +#define ST7796S_RDIMGFMT 0x0D ///< Read Display Image Format +#define ST7796S_RDSELFDIAG 0x0F ///< Read Display Self-Diagnostic Result + +#define ST7796S_INVOFF 0x20 ///< Display Inversion OFF +#define ST7796S_INVON 0x21 ///< Display Inversion ON +#define ST7796S_GAMMASET 0x26 ///< Gamma Set +#define ST7796S_DISPOFF 0x28 ///< Display OFF +#define ST7796S_DISPON 0x29 ///< Display ON + +#define ST7796S_CASET 0x2A ///< Column Address Set +#define ST7796S_PASET 0x2B ///< Page Address Set +#define ST7796S_RAMWR 0x2C ///< Memory Write +#define ST7796S_RAMRD 0x2E ///< Memory Read + +#define ST7796S_PTLAR 0x30 ///< Partial Area +#define ST7796S_VSCRDEF 0x33 ///< Vertical Scrolling Definition +#define ST7796S_MADCTL 0x36 ///< Memory Access Control +#define ST7796S_VSCRSADD 0x37 ///< Vertical Scrolling Start Address +#define ST7796S_PIXFMT 0x3A ///< COLMOD: Pixel Format Set + + +// Color definitions +#define ST7796S_BLACK 0x0000 ///< 0, 0, 0 +#define ST7796S_NAVY 0x000F ///< 0, 0, 123 +#define ST7796S_DARKGREEN 0x03E0 ///< 0, 125, 0 +#define ST7796S_DARKCYAN 0x03EF ///< 0, 125, 123 +#define ST7796S_MAROON 0x7800 ///< 123, 0, 0 +#define ST7796S_PURPLE 0x780F ///< 123, 0, 123 +#define ST7796S_OLIVE 0x7BE0 ///< 123, 125, 0 +#define ST7796S_LIGHTGREY 0xC618 ///< 198, 195, 198 +#define ST7796S_DARKGREY 0x7BEF ///< 123, 125, 123 +#define ST7796S_BLUE 0x001F ///< 0, 0, 255 +#define ST7796S_GREEN 0x07E0 ///< 0, 255, 0 +#define ST7796S_CYAN 0x07FF ///< 0, 255, 255 +#define ST7796S_RED 0xF800 ///< 255, 0, 0 +#define ST7796S_MAGENTA 0xF81F ///< 255, 0, 255 +#define ST7796S_YELLOW 0xFFE0 ///< 255, 255, 0 +#define ST7796S_WHITE 0xFFFF ///< 255, 255, 255 +#define ST7796S_ORANGE 0xFD20 ///< 255, 165, 0 +#define ST7796S_GREENYELLOW 0xAFE5 ///< 173, 255, 41 +#define ST7796S_PINK 0xFC18 ///< 255, 130, 198 + +/**************************************************************************/ +/*! +@brief Class to manage hardware interface with ST7796S chipset +*/ +/**************************************************************************/ + +class Adafruit_ST7796S_kbv : public Adafruit_SPITFT { +public: + Adafruit_ST7796S_kbv(int8_t _CS, int8_t _DC, int8_t _MOSI, int8_t _SCLK, + int8_t _RST = -1, int8_t _MISO = -1); + Adafruit_ST7796S_kbv(int8_t _CS, int8_t _DC, int8_t _RST = -1); +#if !defined(ESP8266) + Adafruit_ST7796S_kbv(SPIClass *spiClass, int8_t dc, int8_t cs = -1, + int8_t rst = -1); +#endif // end !ESP8266 + Adafruit_ST7796S_kbv(tftBusWidth busWidth, int8_t d0, int8_t wr, int8_t dc, + int8_t cs = -1, int8_t rst = -1, int8_t rd = -1); + + void begin(uint32_t freq = 0); + void setRotation(uint8_t r); + void invertDisplay(bool i); + void scrollTo(uint16_t y); + void setScrollMargins(uint16_t top, uint16_t bottom); + + // Transaction API not used by GFX + void setAddrWindow(uint16_t x, uint16_t y, uint16_t w, uint16_t h); + + uint8_t readcommand8(uint8_t reg, uint8_t index = 0); +}; + +#endif // _ADAFRUIT_ST7796SH_ diff --git a/yoRadio/src/GT911_Touchscreen/TAMC_GT911.cpp b/yoRadio/src/GT911_Touchscreen/TAMC_GT911.cpp new file mode 100644 index 0000000..38892a4 --- /dev/null +++ b/yoRadio/src/GT911_Touchscreen/TAMC_GT911.cpp @@ -0,0 +1,168 @@ +#if TS_MODEL==TS_MODEL_GT911 +#include "Arduino.h" +#include "TAMC_GT911.h" +#include + +TAMC_GT911::TAMC_GT911(uint8_t _sda, uint8_t _scl, uint8_t _int, uint8_t _rst, uint16_t _width, uint16_t _height) : + pinSda(_sda), pinScl(_scl), pinInt(_int), pinRst(_rst), width(_width), height(_height) { + +} + +void TAMC_GT911::begin(uint8_t _addr) { + addr = _addr; + Wire.begin(pinSda, pinScl); + reset(); +} +void TAMC_GT911::reset() { + pinMode(pinInt, OUTPUT); + pinMode(pinRst, OUTPUT); + digitalWrite(pinInt, 0); + digitalWrite(pinRst, 0); + delay(10); + digitalWrite(pinInt, addr==GT911_ADDR2); + delay(1); + digitalWrite(pinRst, 1); + delay(5); + digitalWrite(pinInt, 0); + delay(50); + pinMode(pinInt, INPUT); + // attachInterrupt(pinInt, TAMC_GT911::onInterrupt, RISING); + delay(50); + readBlockData(configBuf, GT911_CONFIG_START, GT911_CONFIG_SIZE); + setResolution(width, height); +} +void TAMC_GT911::calculateChecksum() { + uint8_t checksum = 0; + for (uint8_t i=0; i> 7 & 1; + isLargeDetect = pointInfo >> 6 & 1; + touches = pointInfo & 0xF; + // Serial.print("bufferStatus: ");Serial.println(bufferStatus); + // Serial.print("largeDetect: ");Serial.println(isLargeDetect); + // Serial.print("proximityValid: ");Serial.println(proximityValid); + // Serial.print("haveKey: ");Serial.println(haveKey); + // Serial.print("touches: ");Serial.println(touches); + isTouched = touches > 0; + if (bufferStatus == 1 && isTouched) { + for (uint8_t i=0; i + +#define GT911_ADDR1 (uint8_t)0x5D +#define GT911_ADDR2 (uint8_t)0x14 + +#define ROTATION_LEFT (uint8_t)0 +#define ROTATION_INVERTED (uint8_t)1 +#define ROTATION_RIGHT (uint8_t)2 +#define ROTATION_NORMAL (uint8_t)3 + + +// Real-time command (Write only) +#define GT911_COMMAND (uint16_t)0x8040 +#define GT911_ESD_CHECK (uint16_t)0x8041 +#define GT911_COMMAND_CHECK (uint16_t)0x8046 + +// Configuration information (R/W) +#define GT911_CONFIG_START (uint16_t)0x8047 +#define GT911_CONFIG_VERSION (uint16_t)0x8047 +#define GT911_X_OUTPUT_MAX_LOW (uint16_t)0x8048 +#define GT911_X_OUTPUT_MAX_HIGH (uint16_t)0x8049 +#define GT911_Y_OUTPUT_MAX_LOW (uint16_t)0x804A +#define GT911_Y_OUTPUT_MAX_HIGH (uint16_t)0x804B +#define GT911_TOUCH_NUMBER (uint16_t)0x804C +#define GT911_MODULE_SWITCH_1 (uint16_t)0x804D +#define GT911_MODULE_SWITCH_2 (uint16_t)0x804E +#define GT911_SHAKE_COUNT (uint16_t)0x804F +#define GT911_FILTER (uint16_t)0x8050 +#define GT911_LARGE_TOUCH (uint16_t)0x8051 +#define GT911_NOISE_REDUCTION (uint16_t)0x8052 +#define GT911_SCREEN_TOUCH_LEVEL (uint16_t)0x8053 +#define GT911_SCREEN_RELEASE_LEVEL (uint16_t)0x8054 +#define GT911_LOW_POWER_CONTROL (uint16_t)0x8055 +#define GT911_REFRESH_RATE (uint16_t)0x8056 +#define GT911_X_THRESHOLD (uint16_t)0x8057 +#define GT911_Y_THRESHOLD (uint16_t)0x8058 +#define GT911_X_SPEED_LIMIT (uint16_t)0x8059 //Reserve +#define GT911_Y_SPEED_LIMIT (uint16_t)0x805A //Reserve +#define GT911_SPACE_TOP_BOTTOM (uint16_t)0x805B +#define GT911_SPACE_LEFT_RIGHT (uint16_t)0x805C +#define GT911_MINI_FILTER (uint16_t)0x805D +#define GT911_STRETCH_R0 (uint16_t)0x805E +#define GT911_STRETCH_R1 (uint16_t)0x805F +#define GT911_STRETCH_R2 (uint16_t)0x8060 +#define GT911_STRETCH_RM (uint16_t)0x8061 +#define GT911_DRV_GROUPA_NUM (uint16_t)0x8062 +#define GT911_DRV_GROUPB_NUM (uint16_t)0x8063 +#define GT911_SENSOR_NUM (uint16_t)0x8064 +#define GT911_FREQ_A_FACTOR (uint16_t)0x8065 +#define GT911_FREQ_B_FACTOR (uint16_t)0x8066 +#define GT911_PANEL_BIT_FREQ_L (uint16_t)0x8067 +#define GT911_PANEL_BIT_FREQ_H (uint16_t)0x8068 +#define GT911_PANEL_SENSOR_TIME_L (uint16_t)0x8069 //Reserve +#define GT911_PANEL_SENSOR_TIME_H (uint16_t)0x806A +#define GT911_PANEL_TX_GAIN (uint16_t)0x806B +#define GT911_PANEL_RX_GAIN (uint16_t)0x806C +#define GT911_PANEL_DUMP_SHIFT (uint16_t)0x806D +#define GT911_DRV_FRAME_CONTROL (uint16_t)0x806E +#define GT911_CHARGING_LEVEL_UP (uint16_t)0x806F +#define GT911_MODULE_SWITCH3 (uint16_t)0x8070 +#define GT911_GESTURE_DIS (uint16_t)0X8071 +#define GT911_GESTURE_LONG_PRESS_TIME (uint16_t)0x8072 +#define GT911_X_Y_SLOPE_ADJUST (uint16_t)0X8073 +#define GT911_GESTURE_CONTROL (uint16_t)0X8074 +#define GT911_GESTURE_SWITCH1 (uint16_t)0X8075 +#define GT911_GESTURE_SWITCH2 (uint16_t)0X8076 +#define GT911_GESTURE_REFRESH_RATE (uint16_t)0x8077 +#define GT911_GESTURE_TOUCH_LEVEL (uint16_t)0x8078 +#define GT911_NEWGREENWAKEUPLEVEL (uint16_t)0x8079 +#define GT911_FREQ_HOPPING_START (uint16_t)0x807A +#define GT911_FREQ_HOPPING_END (uint16_t)0X807B +#define GT911_NOISE_DETECT_TIMES (uint16_t)0x807C +#define GT911_HOPPING_FLAG (uint16_t)0X807D +#define GT911_HOPPING_THRESHOLD (uint16_t)0X807E +#define GT911_NOISE_THRESHOLD (uint16_t)0X807F //Reserve +#define GT911_NOISE_MIN_THRESHOLD (uint16_t)0X8080 +#define GT911_HOPPING_SENSOR_GROUP (uint16_t)0X8082 +#define GT911_HOPPING_SEG1_NORMALIZE (uint16_t)0X8083 +#define GT911_HOPPING_SEG1_FACTOR (uint16_t)0X8084 +#define GT911_MAIN_CLOCK_AJDUST (uint16_t)0X8085 +#define GT911_HOPPING_SEG2_NORMALIZE (uint16_t)0X8086 +#define GT911_HOPPING_SEG2_FACTOR (uint16_t)0X8087 +#define GT911_HOPPING_SEG3_NORMALIZE (uint16_t)0X8089 +#define GT911_HOPPING_SEG3_FACTOR (uint16_t)0X808A +#define GT911_HOPPING_SEG4_NORMALIZE (uint16_t)0X808C +#define GT911_HOPPING_SEG4_FACTOR (uint16_t)0X808D +#define GT911_HOPPING_SEG5_NORMALIZE (uint16_t)0X808F +#define GT911_HOPPING_SEG5_FACTOR (uint16_t)0X8090 +#define GT911_HOPPING_SEG6_NORMALIZE (uint16_t)0X8092 +#define GT911_KEY_1 (uint16_t)0X8093 +#define GT911_KEY_2 (uint16_t)0X8094 +#define GT911_KEY_3 (uint16_t)0X8095 +#define GT911_KEY_4 (uint16_t)0X8096 +#define GT911_KEY_AREA (uint16_t)0X8097 +#define GT911_KEY_TOUCH_LEVEL (uint16_t)0X8098 +#define GT911_KEY_LEAVE_LEVEL (uint16_t)0X8099 +#define GT911_KEY_SENS_1_2 (uint16_t)0X809A +#define GT911_KEY_SENS_3_4 (uint16_t)0X809B +#define GT911_KEY_RESTRAIN (uint16_t)0X809C +#define GT911_KEY_RESTRAIN_TIME (uint16_t)0X809D +#define GT911_GESTURE_LARGE_TOUCH (uint16_t)0X809E +#define GT911_HOTKNOT_NOISE_MAP (uint16_t)0X80A1 +#define GT911_LINK_THRESHOLD (uint16_t)0X80A2 +#define GT911_PXY_THRESHOLD (uint16_t)0X80A3 +#define GT911_GHOT_DUMP_SHIFT (uint16_t)0X80A4 +#define GT911_GHOT_RX_GAIN (uint16_t)0X80A5 +#define GT911_FREQ_GAIN0 (uint16_t)0X80A6 +#define GT911_FREQ_GAIN1 (uint16_t)0X80A7 +#define GT911_FREQ_GAIN2 (uint16_t)0X80A8 +#define GT911_FREQ_GAIN3 (uint16_t)0X80A9 +#define GT911_COMBINE_DIS (uint16_t)0X80B3 +#define GT911_SPLIT_SET (uint16_t)0X80B4 +#define GT911_SENSOR_CH0 (uint16_t)0X80B7 +#define GT911_DRIVER_CH0 (uint16_t)0X80D5 +#define GT911_CONFIG_CHKSUM (uint16_t)0X80FF +#define GT911_CONFIG_FRESH (uint16_t)0X8100 +#define GT911_CONFIG_SIZE (uint16_t)0xFF-0x46 +// Coordinate information +#define GT911_PRODUCT_ID (uint16_t)0X8140 +#define GT911_FIRMWARE_VERSION (uint16_t)0X8140 +#define GT911_RESOLUTION (uint16_t)0X8140 +#define GT911_VENDOR_ID (uint16_t)0X8140 +#define GT911_IMFORMATION (uint16_t)0X8140 +#define GT911_POINT_INFO (uint16_t)0X814E +#define GT911_POINT_1 (uint16_t)0X814F +#define GT911_POINT_2 (uint16_t)0X8157 +#define GT911_POINT_3 (uint16_t)0X815F +#define GT911_POINT_4 (uint16_t)0X8167 +#define GT911_POINT_5 (uint16_t)0X816F +#define GT911_POINTS_REG {GT911_POINT_1, GT911_POINT_2, GT911_POINT_3, GT911_POINT_4, GT911_POINT_5} + +class TP_Point { + public: + TP_Point(void); + TP_Point(uint8_t id, uint16_t x, uint16_t y, uint16_t size); + + bool operator==(TP_Point); + bool operator!=(TP_Point); + + uint8_t id; + uint16_t x; + uint16_t y; + uint8_t size; +}; + +class TAMC_GT911 { + public: + TAMC_GT911(uint8_t _sda, uint8_t _scl, uint8_t _int, uint8_t _rst, uint16_t _width, uint16_t _height); + void begin(uint8_t _addr=GT911_ADDR1); + void reset(); + void setRotation(uint8_t rot); + void setResolution(uint16_t _width, uint16_t _height); + // void setOnRead(void (*isr)()); + uint8_t getGesture(void); + void read(void); + uint8_t isLargeDetect; + uint8_t touches = 0; + bool isTouched = false; + // uint8_t gesture = NO_GESTURE; + TP_Point points[5]; + + private: + void calculateChecksum(); + void reflashConfig(); + // static void ARDUINO_ISR_ATTR onInterrupt(); + TP_Point readPoint(uint8_t *data); + // void (*onRead)(); + void writeByteData(uint16_t reg, uint8_t val); + uint8_t readByteData(uint16_t reg); + void writeBlockData(uint16_t reg, uint8_t *val, uint8_t size); + void readBlockData(uint8_t *buf, uint16_t reg, uint8_t size); + uint8_t rotation = ROTATION_NORMAL; + uint8_t addr; + uint8_t pinSda; + uint8_t pinScl; + uint8_t pinInt; + uint8_t pinRst; + uint16_t width; + uint16_t height; + uint8_t configBuf[GT911_CONFIG_SIZE]; + // uint8_t *configBuf; +}; + +#endif // TAMC_GT911_H diff --git a/yoRadio/src/core/config.cpp b/yoRadio/src/core/config.cpp index 30ecee8..714c0ef 100644 --- a/yoRadio/src/core/config.cpp +++ b/yoRadio/src/core/config.cpp @@ -6,6 +6,10 @@ Config config; +#if DSP_HSPI || TS_HSPI || VS_HSPI +SPIClass SPI2(HSPI); +#endif + void u8fix(char *src){ char last = src[strlen(src)-1]; if ((uint8_t)last >= 0xC2) src[strlen(src)-1]='\0'; diff --git a/yoRadio/src/core/config.h b/yoRadio/src/core/config.h index 10f1a48..671b1f3 100644 --- a/yoRadio/src/core/config.h +++ b/yoRadio/src/core/config.h @@ -2,6 +2,7 @@ #define config_h #include "Arduino.h" #include +#include #include "options.h" #define EEPROM_SIZE 768 @@ -184,5 +185,9 @@ class Config { }; extern Config config; +#if DSP_HSPI || TS_HSPI || VS_HSPI +extern SPIClass SPI2; +#endif + #endif diff --git a/yoRadio/src/core/controls.cpp b/yoRadio/src/core/controls.cpp index ce590a7..77ff705 100644 --- a/yoRadio/src/core/controls.cpp +++ b/yoRadio/src/core/controls.cpp @@ -42,9 +42,9 @@ yoEncoder encoder2 = yoEncoder(ENC2_BTNL, ENC2_BTNR, ENCODER2_STEPS, ENC2_INTERN #endif #endif -#if TS_CS!=255 -#include -XPT2046_Touchscreen ts(TS_CS); +#if TS_MODEL!=TS_MODEL_UNDEFINED + #include "touchscreen.h" + TouchScreen touchscreen; #endif #if IR_PIN!=255 @@ -113,9 +113,8 @@ void initControls() { button[i].setPressTicks(BTN_PRESS_TICKS); } #endif -#if TS_CS!=255 - ts.begin(); - ts.setRotation(config.store.fliptouch?3:1); +#if TS_MODEL!=TS_MODEL_UNDEFINED + touchscreen.init(); #endif #if IR_PIN!=255 pinMode(IR_PIN, INPUT); @@ -145,18 +144,15 @@ void loopControls() { if (lpId >= 0) { if (DSP_MODEL == DSP_DUMMY && (lpId == 4 || lpId == 5)) continue; onBtnDuringLongPress(lpId); - yield(); } - yield(); } #endif #if IR_PIN!=255 irLoop(); #endif -#if TS_CS!=255 - touchLoop(); +#if TS_MODEL!=TS_MODEL_UNDEFINED + touchscreen.loop(); #endif - yield(); } #if ENC_BTNL!=255 @@ -326,122 +322,6 @@ void irLoop() { } #endif // if IR_PIN!=255 -#if TS_CS!=255 -#ifndef TS_X_MIN - #define TS_X_MIN 400 -#endif -#ifndef TS_X_MAX - #define TS_X_MAX 3800 -#endif -#ifndef TS_Y_MIN - #define TS_Y_MIN 260 -#endif -#ifndef TS_Y_MAX - #define TS_Y_MAX 3800 -#endif -#ifndef TS_STEPS - #define TS_STEPS 40 -#endif - -boolean wastouched = true; -unsigned long touchdelay; -uint16_t touchVol, touchStation; -uint16_t oldTouchP[2]; -tsDirection_e direct; -unsigned long touchLongPress; - -tsDirection_e tsDirection(uint16_t x, uint16_t y) { - int16_t dX = x - oldTouchP[0]; - int16_t dY = y - oldTouchP[1]; - if (abs(dX) > 20 || abs(dY) > 20) { - if (abs(dX) > abs(dY)) { - if (dX > 0) { - return TSD_RIGHT; - } else { - return TSD_LEFT; - } - } else { - if (dY > 0) { - return TSD_DOWN; - } else { - return TSD_UP; - } - } - } else { - return TDS_REQUEST; - } -} - -void touchLoop() { - if (!checklpdelay(100, touchdelay)) return; - boolean istouched = ts.touched(); - if (istouched) { - TS_Point p = ts.getPoint(); - uint16_t touchX = map(p.x, TS_X_MIN, TS_X_MAX, 0, dsp.width()); - uint16_t touchY = map(p.y, TS_Y_MIN, TS_Y_MAX, 0, dsp.height()); - if (!wastouched) { /* START TOUCH */ - oldTouchP[0] = touchX; - oldTouchP[1] = touchY; - touchVol = touchX; - touchStation = touchY; - direct = TDS_REQUEST; - touchLongPress=millis(); - } else { /* SWIPE TOUCH */ - direct = tsDirection(touchX, touchY); - switch (direct) { - case TSD_LEFT: - case TSD_RIGHT: { - touchLongPress=millis(); - if(display.mode()==PLAYER || display.mode()==VOL){ - int16_t xDelta = map(abs(touchVol - touchX), 0, dsp.width(), 0, TS_STEPS); - display.putRequest(NEWMODE, VOL); - if (xDelta>1) { - controlsEvent((touchVol - touchX)<0); - touchVol = touchX; - } - } - break; - } - case TSD_UP: - case TSD_DOWN: { - touchLongPress=millis(); - if(display.mode()==PLAYER || display.mode()==STATIONS){ - int16_t yDelta = map(abs(touchStation - touchY), 0, dsp.height(), 0, TS_STEPS); - display.putRequest(NEWMODE, STATIONS); - if (yDelta>1) { - controlsEvent((touchStation - touchY)<0); - touchStation = touchY; - } - } - break; - } - default: - break; - } - } - if (config.store.dbgtouch) { - Serial.print(", x = "); - Serial.print(p.x); - Serial.print(", y = "); - Serial.println(p.y); - } - } else { - if (wastouched) {/* END TOUCH */ - if (direct == TDS_REQUEST) { - uint32_t pressTicks = millis()-touchLongPress; - if( pressTicks < BTN_PRESS_TICKS*2){ - if(pressTicks > 50) onBtnClick(EVT_BTNCENTER); - }else{ - display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER); - } - } - direct = TSD_STAY; - } - } - wastouched = istouched; -} -#endif // if TS_CS!=255 - void onBtnLongPressStart(int id) { switch ((controlEvt_e)id) { case EVT_BTNLEFT: @@ -629,7 +509,7 @@ void setEncAcceleration(uint16_t acc){ #endif } void flipTS(){ -#if TS_CS!=255 - ts.setRotation(config.store.fliptouch?3:1); +#if TS_MODEL!=TS_MODEL_UNDEFINED + touchscreen.flip(); #endif } diff --git a/yoRadio/src/core/controls.h b/yoRadio/src/core/controls.h index 84a76d1..c44bb92 100644 --- a/yoRadio/src/core/controls.h +++ b/yoRadio/src/core/controls.h @@ -4,7 +4,7 @@ enum controlEvt_e { EVT_BTNLEFT, EVT_BTNCENTER, EVT_BTNRIGHT, EVT_ENCBTNB, EVT_BTNUP, EVT_BTNDOWN, EVT_ENC2BTNB }; -enum tsDirection_e { TSD_STAY, TSD_LEFT, TSD_RIGHT, TSD_UP, TSD_DOWN, TDS_REQUEST }; +//enum tsDirection_e { TSD_STAY, TSD_LEFT, TSD_RIGHT, TSD_UP, TSD_DOWN, TDS_REQUEST }; #if IR_PIN!=255 enum : uint8_t { IR_UP=0, IR_PREV=1, IR_PLAY=2, IR_NEXT=3, IR_DOWN=4, IR_1=5, IR_2=6, IR_3=7, IR_4=8, IR_5=9, IR_6=10, IR_7=11, IR_8=12, IR_9=13, IR_AST=14, IR_0=15, IR_HASH=16 }; @@ -17,7 +17,7 @@ void loopControls(); void encoderLoop(); void encoder2Loop(); void irLoop(); -void touchLoop(); +//void touchLoop(); void irNum(byte num); void irBlink(); void controlsEvent(bool toRight, int8_t volDelta = 0); @@ -27,7 +27,7 @@ void onBtnDoubleClick(int id); void onBtnDuringLongPress(int id); void onBtnLongPressStart(int id); void onBtnLongPressStop(int id); -tsDirection_e tsDirection(uint16_t x, uint16_t y); +//tsDirection_e tsDirection(uint16_t x, uint16_t y); void setIRTolerance(uint8_t tl); void setEncAcceleration(uint16_t acc); diff --git a/yoRadio/src/core/display.cpp b/yoRadio/src/core/display.cpp index db5e3fa..d381b9c 100644 --- a/yoRadio/src/core/display.cpp +++ b/yoRadio/src/core/display.cpp @@ -49,6 +49,9 @@ void loopDspTask(void * pvParameters){ void Display::init() { #ifdef USE_NEXTION nextion.begin(); +#endif +#if LIGHT_SENSOR!=255 + analogSetAttenuation(ADC_0db); #endif _bootStep = 0; dsp.initDisplay(); @@ -442,6 +445,10 @@ void Display::_title() { } void Display::_time(bool redraw) { +#if LIGHT_SENSOR!=255 + config.store.brightness = AUTOBACKLIGHT(analogRead(LIGHT_SENSOR)); + config.setBrightness(); +#endif _clock.draw(); /*#ifdef USE_NEXTION nextion.printClock(network.timeinfo); diff --git a/yoRadio/src/core/display.h b/yoRadio/src/core/display.h index f98aa53..18c9f0a 100644 --- a/yoRadio/src/core/display.h +++ b/yoRadio/src/core/display.h @@ -110,4 +110,5 @@ class Display { extern Display display; + #endif diff --git a/yoRadio/src/core/netserver.cpp b/yoRadio/src/core/netserver.cpp index 4e0f099..a9257d5 100644 --- a/yoRadio/src/core/netserver.cpp +++ b/yoRadio/src/core/netserver.cpp @@ -557,7 +557,7 @@ void NetServer::requestOnChange(requestType_e request, uint8_t clientId) { #endif if (BRIGHTNESS_PIN != 255 || nxtn || dbgact) act += F("\"group_brightness\","); if (DSP_CAN_FLIPPED || dbgact) act += F("\"group_tft\","); - if (TS_CS != 255 || dbgact) act += F("\"group_touch\","); + if (TS_MODEL != TS_MODEL_UNDEFINED || dbgact) act += F("\"group_touch\","); if (DSP_MODEL == DSP_NOKIA5110) act += F("\"group_nokia\","); act += F("\"group_timezone\","); if (SHOW_WEATHER || dbgact) act += F("\"group_weather\","); diff --git a/yoRadio/src/core/network.cpp b/yoRadio/src/core/network.cpp index ea13e1e..6122c60 100644 --- a/yoRadio/src/core/network.cpp +++ b/yoRadio/src/core/network.cpp @@ -209,6 +209,7 @@ bool getWeather(char *wstr) { } char *tmpe; char *tmps; + char *tmpc; const char* cursor = line.c_str(); char desc[120], temp[20], hum[20], press[20], icon[5]; @@ -251,8 +252,9 @@ bool getWeather(char *wstr) { if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;} tmps += 10; tmpe = strstr(tmps, ",\""); + tmpc = strstr(tmps, "}"); if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;} - strlcpy(hum, tmps, tmpe - tmps + 1); + strlcpy(hum, tmps, tmpe - tmps + (tmpc>tmpe?1:0)); #ifdef USE_NEXTION nextion.putcmdf("press_txt.txt=\"%dmm\"", pressi); diff --git a/yoRadio/src/core/options.h b/yoRadio/src/core/options.h index cb4b9f9..2d18829 100644 --- a/yoRadio/src/core/options.h +++ b/yoRadio/src/core/options.h @@ -1,7 +1,7 @@ #ifndef options_h #define options_h -#define VERSION "0.8.02b" +#define VERSION "0.8.03b" /******************************************************* DO NOT EDIT THIS FILE. @@ -41,6 +41,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #define DSP_SSD1305I2C 16 // 128x64 2.4' SSD1305 and SSD1309 I2C https://aliexpress.com/item/32950307344.html #define DSP_ILI9225 17 // 220x176 2.0' https://aliexpress.com/item/32952021835.html #define DSP_ST7789_240 18 // 240x240 1.3' https://aliexpress.com/item/32996979276.html +#define DSP_ST7796 19 /* !!! DSP_ST7789_240 requires further development when used in conjunction with the VS1053 module !!! See the link https://www.instructables.com/Adding-CS-Pin-to-13-LCD/ */ #define DSP_CUSTOM 101 // your display @@ -48,6 +49,12 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #ifndef DSP_MODEL #define DSP_MODEL DSP_DUMMY #endif +#ifndef DSP_HSPI + #define DSP_HSPI false // use HSPI for displays (miso=12, mosi=13, clk=14) instead of VSPI (by default) +#endif +#ifndef LED_INVERT + #define LED_INVERT false // invert onboard LED? +#endif /* TFT DISPLAY */ #ifndef TFT_CS @@ -92,6 +99,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #ifndef VS1053_RST #define VS1053_RST -1 // set to -1 if connected to Esp EN pin #endif +#ifndef VS_HSPI + #define VS_HSPI false // use HSPI for VS1053 (miso=12, mosi=13, clk=14) instead of VSPI (by default) +#endif /* I2S DAC */ #ifndef I2S_DOUT @@ -167,8 +177,32 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #endif /* TOUCH SCREEN */ +#define TS_MODEL_UNDEFINED 0 +#define TS_MODEL_XPT2046 1 +#define TS_MODEL_GT911 2 + +#ifndef TS_MODEL + #define TS_MODEL TS_MODEL_UNDEFINED +#endif + #ifndef TS_CS - #define TS_CS 255 + #define TS_CS 13 +#endif +#ifndef TS_SDA + #define TS_SDA 33 +#endif +#ifndef TS_SCL + #define TS_SCL 32 +#endif +#ifndef TS_INT + #define TS_INT 21 +#endif +#ifndef TS_RST + #define TS_RST 25 +#endif + +#ifndef TS_HSPI + #define TS_HSPI false // use HSPI for touchscreen (miso=12, mosi=13, clk=14) instead of VSPI (by default) #endif /* LCD DISPLAY */ @@ -218,6 +252,18 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #ifndef WAKE_PIN #define WAKE_PIN 255 // Wake Pin (for manual wakeup from sleep mode. can match with BTN_XXXX, ENC_BTNB, ENC2_BTNB. must be one of: 0,2,4,12,13,14,15,25,26,27,32,33,34,35,36,39) #endif +#ifndef LIGHT_SENSOR + #define LIGHT_SENSOR 255 // Light sensor +#endif +#ifndef AUTOBACKLIGHT + #ifndef AUTOBACKLIGHT_MAX + #define AUTOBACKLIGHT_MAX 2500 + #endif + #ifndef AUTOBACKLIGHT_MIN + #define AUTOBACKLIGHT_MIN 12 + #endif + #define AUTOBACKLIGHT(x) ({uint16_t _lh=(x>AUTOBACKLIGHT_MAX?AUTOBACKLIGHT_MAX:x); map(_lh, AUTOBACKLIGHT_MAX, 0, AUTOBACKLIGHT_MIN, 100);}) // autobacklight function +#endif /* *** ST7735 display submodel *** INITR_BLACKTAB // 1.8' https://aliexpress.ru/item/1005002822797745.html diff --git a/yoRadio/src/core/player.cpp b/yoRadio/src/core/player.cpp index c812178..728a34f 100644 --- a/yoRadio/src/core/player.cpp +++ b/yoRadio/src/core/player.cpp @@ -11,9 +11,11 @@ Player player; #if VS1053_CS!=255 && !I2S_INTERNAL - Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ) { - - } + #if VS_HSPI + Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ, HSPI, 13, 12, 14) {} + #else + Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ) {} + #endif void ResetChip(){ pinMode(VS1053_RST, OUTPUT); digitalWrite(VS1053_RST, LOW); @@ -116,7 +118,7 @@ void Player::zeroRequest() { } void Player::setOutputPins(bool isPlaying) { - digitalWrite(LED_BUILTIN, isPlaying); + digitalWrite(LED_BUILTIN, LED_INVERT?!isPlaying:isPlaying); if(MUTE_PIN!=255) digitalWrite(MUTE_PIN, isPlaying?!MUTE_VAL:MUTE_VAL); } diff --git a/yoRadio/src/core/touchscreen.cpp b/yoRadio/src/core/touchscreen.cpp new file mode 100644 index 0000000..b401d99 --- /dev/null +++ b/yoRadio/src/core/touchscreen.cpp @@ -0,0 +1,196 @@ +#include "options.h" +#if TS_MODEL!=TS_MODEL_UNDEFINED + +#include "touchscreen.h" +#include "config.h" +#include "controls.h" +#include "display.h" +#include "player.h" + +#ifndef TS_X_MIN + #define TS_X_MIN 400 +#endif +#ifndef TS_X_MAX + #define TS_X_MAX 3800 +#endif +#ifndef TS_Y_MIN + #define TS_Y_MIN 260 +#endif +#ifndef TS_Y_MAX + #define TS_Y_MAX 3800 +#endif +#ifndef TS_STEPS + #define TS_STEPS 40 +#endif + +#if TS_MODEL==TS_MODEL_XPT2046 + #ifdef TS_SPIPINS + SPIClass TSSPI(HSPI); + #endif + #include + XPT2046_Touchscreen ts(TS_CS); + typedef TS_Point TSPoint; +#elif TS_MODEL==TS_MODEL_GT911 + #include "../GT911_Touchscreen/TAMC_GT911.h" + TAMC_GT911 ts = TAMC_GT911(TS_SDA, TS_SCL, TS_INT, TS_RST, 0, 0); + typedef TP_Point TSPoint; +#endif + +void TouchScreen::init(){ + +#if TS_MODEL==TS_MODEL_XPT2046 + #ifdef TS_SPIPINS + TSSPI.begin(TS_SPIPINS); + ts.begin(TSSPI); + #else + #if TS_HSPI + ts.begin(SPI2); + #else + ts.begin(); + #endif + #endif + ts.setRotation(config.store.fliptouch?3:1); +#endif +#if TS_MODEL==TS_MODEL_GT911 + ts.begin(); + ts.setRotation(config.store.fliptouch?0:2); +#endif + _width = dsp.width(); + _height = dsp.height(); +#if TS_MODEL==TS_MODEL_GT911 + ts.setResolution(_width, _height); +#endif +} + +tsDirection_e TouchScreen::_tsDirection(uint16_t x, uint16_t y) { + int16_t dX = x - _oldTouchX; + int16_t dY = y - _oldTouchY; + if (abs(dX) > 20 || abs(dY) > 20) { + if (abs(dX) > abs(dY)) { + if (dX > 0) { + return TSD_RIGHT; + } else { + return TSD_LEFT; + } + } else { + if (dY > 0) { + return TSD_DOWN; + } else { + return TSD_UP; + } + } + } else { + return TDS_REQUEST; + } +} + +void TouchScreen::flip(){ +#if TS_MODEL==TS_MODEL_XPT2046 + ts.setRotation(config.store.fliptouch?3:1); +#endif +#if TS_MODEL==TS_MODEL_GT911 + ts.setRotation(config.store.fliptouch?0:2); +#endif +} + +void TouchScreen::loop(){ + uint16_t touchX, touchY; + static bool wastouched = true; + static uint32_t touchLongPress; + static tsDirection_e direct; + static uint16_t touchVol, touchStation; + if (!_checklpdelay(20, _touchdelay)) return; +#if TS_MODEL==TS_MODEL_GT911 + ts.read(); +#endif + bool istouched = _istouched(); + if(istouched){ + #if TS_MODEL==TS_MODEL_XPT2046 + TSPoint p = ts.getPoint(); + touchX = map(p.x, TS_X_MIN, TS_X_MAX, 0, _width); + touchY = map(p.y, TS_Y_MIN, TS_Y_MAX, 0, _height); + #elif TS_MODEL==TS_MODEL_GT911 + TSPoint p = ts.points[0]; + touchX = p.x; + touchY = p.y; + #endif + if (!wastouched) { /* START TOUCH */ + _oldTouchX = touchX; + _oldTouchY = touchY; + touchVol = touchX; + touchStation = touchY; + direct = TDS_REQUEST; + touchLongPress=millis(); + } else { /* SWIPE TOUCH */ + direct = _tsDirection(touchX, touchY); + switch (direct) { + case TSD_LEFT: + case TSD_RIGHT: { + touchLongPress=millis(); + if(display.mode()==PLAYER || display.mode()==VOL){ + int16_t xDelta = map(abs(touchVol - touchX), 0, _width, 0, TS_STEPS); + display.putRequest(NEWMODE, VOL); + if (xDelta>1) { + controlsEvent((touchVol - touchX)<0); + touchVol = touchX; + } + } + break; + } + case TSD_UP: + case TSD_DOWN: { + touchLongPress=millis(); + if(display.mode()==PLAYER || display.mode()==STATIONS){ + int16_t yDelta = map(abs(touchStation - touchY), 0, _height, 0, TS_STEPS); + display.putRequest(NEWMODE, STATIONS); + if (yDelta>1) { + controlsEvent((touchStation - touchY)<0); + touchStation = touchY; + } + } + break; + } + default: + break; + } + } + if (config.store.dbgtouch) { + Serial.print(", x = "); + Serial.print(p.x); + Serial.print(", y = "); + Serial.println(p.y); + } + }else{ + if (wastouched) {/* END TOUCH */ + if (direct == TDS_REQUEST) { + uint32_t pressTicks = millis()-touchLongPress; + if( pressTicks < BTN_PRESS_TICKS*2){ + if(pressTicks > 50) onBtnClick(EVT_BTNCENTER); + }else{ + display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER); + } + } + direct = TSD_STAY; + } + } + wastouched = istouched; +} + +bool TouchScreen::_checklpdelay(int m, uint32_t &tstamp) { + if (millis() - tstamp > m) { + tstamp = millis(); + return true; + } else { + return false; + } +} + +bool TouchScreen::_istouched(){ +#if TS_MODEL==TS_MODEL_XPT2046 + return ts.touched(); +#elif TS_MODEL==TS_MODEL_GT911 + return ts.isTouched; +#endif +} + +#endif // TS_MODEL!=TS_MODEL_UNDEFINED diff --git a/yoRadio/src/core/touchscreen.h b/yoRadio/src/core/touchscreen.h new file mode 100644 index 0000000..23233cd --- /dev/null +++ b/yoRadio/src/core/touchscreen.h @@ -0,0 +1,23 @@ +#ifndef touchscreen_h +#define touchscreen_h +#include "Arduino.h" + +enum tsDirection_e { TSD_STAY, TSD_LEFT, TSD_RIGHT, TSD_UP, TSD_DOWN, TDS_REQUEST }; + +class TouchScreen { + public: + TouchScreen() {} + void init(); + void loop(); + void flip(); + private: + uint16_t _oldTouchX, _oldTouchY, _width, _height; + uint32_t _touchdelay; + bool _checklpdelay(int m, uint32_t &tstamp); + tsDirection_e _tsDirection(uint16_t x, uint16_t y); + bool _istouched(); +}; + +extern TouchScreen touchscreen; + +#endif diff --git a/yoRadio/src/displays/conf/displayST7789conf.h b/yoRadio/src/displays/conf/displayST7789conf.h index 657b133..fa7bcd3 100644 --- a/yoRadio/src/displays/conf/displayST7789conf.h +++ b/yoRadio/src/displays/conf/displayST7789conf.h @@ -21,7 +21,7 @@ const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 5, 30 }; const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 50, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 }; const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 70, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 }; -const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 112, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 2, 30 }; +const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 112, 2, WA_LEFT }, 140, true, MAX_WIDTH, 1000, 2, 30 }; const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 20 }; const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, MAX_WIDTH, 0, 4, 30 }; const ScrollConfig weatherConf PROGMEM = {{ 8, 87, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 4, 30 }; diff --git a/yoRadio/src/displays/conf/displayST7796conf.h b/yoRadio/src/displays/conf/displayST7796conf.h new file mode 100644 index 0000000..ccdd8dd --- /dev/null +++ b/yoRadio/src/displays/conf/displayST7796conf.h @@ -0,0 +1,68 @@ +/************************************************************************************* + ST7796 480X320 displays configuration file. + Copy this file to yoRadio/src/displays/conf/displayST7789conf_custom.h + and modify it + More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description +*************************************************************************************/ + +#ifndef displayST7789conf_h +#define displayST7789conf_h + +#define DSP_WIDTH 480 +#define DSP_HEIGHT 320 +#define TFT_FRAMEWDT 10 +#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2 +#define PLMITEMS 11 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 32 + +#define bootLogoTop 110 + +/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */ +const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 4, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 7, 40 }; +const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 62, 2, WA_LEFT }, 140, true, MAX_WIDTH-6*2*7-6, 5000, 7, 40 }; +const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 86, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 7, 40 }; +const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 146, 3, WA_LEFT }, 140, true, MAX_WIDTH, 1000, 7, 40 }; +const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 4, WA_CENTER }, 140, false, MAX_WIDTH, 0, 7, 40 }; +const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 320-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, MAX_WIDTH, 0, 7, 40 }; +const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 116, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 7, 40 }; + +/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */ +const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 50, false }; +const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, DSP_HEIGHT-TFT_FRAMEWDT-8, 0, WA_LEFT }, MAX_WIDTH, 8, true }; +const FillConfig playlBGConf PROGMEM = {{ 0, 138, 0, WA_LEFT }, DSP_WIDTH, 36, false }; +const FillConfig heapbarConf PROGMEM = {{ 0, DSP_HEIGHT-2, 0, WA_LEFT }, DSP_WIDTH, 2, false }; + +/* WIDGETS */ /* { left, top, fontsize, align } */ +const WidgetConfig bootstrConf PROGMEM = { 0, 243, 1, WA_CENTER }; +const WidgetConfig bitrateConf PROGMEM = { 6, 62, 2, WA_RIGHT }; +const WidgetConfig voltxtConf PROGMEM = { 0, DSP_HEIGHT-38, 2, WA_CENTER }; +const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, DSP_HEIGHT-38, 2, WA_LEFT }; +const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, DSP_HEIGHT-38, 2, WA_RIGHT }; +const WidgetConfig numConf PROGMEM = { 0, 200, 70, WA_CENTER }; +const WidgetConfig apNameConf PROGMEM = { TFT_FRAMEWDT, 88, 3, WA_CENTER }; +const WidgetConfig apName2Conf PROGMEM = { TFT_FRAMEWDT, 120, 3, WA_CENTER }; +const WidgetConfig apPassConf PROGMEM = { TFT_FRAMEWDT, 173, 3, WA_CENTER }; +const WidgetConfig apPass2Conf PROGMEM = { TFT_FRAMEWDT, 205, 3, WA_CENTER }; +const WidgetConfig clockConf PROGMEM = { 16, 224, 70, WA_RIGHT }; /* 52 is a fixed font size. do not change */ +const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 136, 1, WA_LEFT }; + +const WidgetConfig bootWdtConf PROGMEM = { 0, 216, 1, WA_CENTER }; +const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 }; + +/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */ +const VUBandsConfig bandsConf PROGMEM = { 32, 130, 4, 2, 10, 6 }; + +/* STRINGS */ +const char numtxtFmt[] PROGMEM = "%d"; +const char rssiFmt[] PROGMEM = "WiFi %d"; +const char iptxtFmt[] PROGMEM = "%s"; +const char voltxtFmt[] PROGMEM = "%d"; +const char bitrateFmt[] PROGMEM = "%d kBs"; + +/* MOVES */ /* { left, top, width } */ +const MoveConfig clockMove PROGMEM = { 0, 176, -1 }; +const MoveConfig weatherMove PROGMEM = { 8, 120, MAX_WIDTH }; +const MoveConfig weatherMoveVU PROGMEM = { 89, 120, 381 }; + +#endif diff --git a/yoRadio/src/displays/displayGC9106.cpp b/yoRadio/src/displays/displayGC9106.cpp index 368fe53..e8d26d4 100644 --- a/yoRadio/src/displays/displayGC9106.cpp +++ b/yoRadio/src/displays/displayGC9106.cpp @@ -14,7 +14,11 @@ #define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) #define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) -DspCore::DspCore(): Adafruit_GC9106Ex(TFT_CS, TFT_DC, TFT_RST) { } +#if DSP_HSPI +DspCore::DspCore(): Adafruit_GC9106Ex(&SPI2, TFT_DC, TFT_CS, TFT_RST) {} +#else +DspCore::DspCore(): Adafruit_GC9106Ex(TFT_CS, TFT_DC, TFT_RST) {} +#endif #include "tools/utf8RusGFX.h" diff --git a/yoRadio/src/displays/displayILI9225.cpp b/yoRadio/src/displays/displayILI9225.cpp index 0b0c6f1..1662291 100644 --- a/yoRadio/src/displays/displayILI9225.cpp +++ b/yoRadio/src/displays/displayILI9225.cpp @@ -13,11 +13,8 @@ extern unsigned char yofont10x14[]; #define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) #define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) -//SPIClass hspi(VSPI); -DspCore::DspCore(): TFT_22_ILI9225(TFT_RST, TFT_DC, TFT_CS, 0) { - -} +DspCore::DspCore(): TFT_22_ILI9225(TFT_RST, TFT_DC, TFT_CS, 0) {} #include "tools/utf8RusGFX.h" @@ -97,7 +94,11 @@ void DspCore::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){ void DspCore::initDisplay() { TAKE_MUTEX(); +#if DSP_HSPI + begin(SPI2); +#else begin(); +#endif invert(); flip(); setTextSize(1); diff --git a/yoRadio/src/displays/displayILI9341.cpp b/yoRadio/src/displays/displayILI9341.cpp index c75af0f..71ded06 100644 --- a/yoRadio/src/displays/displayILI9341.cpp +++ b/yoRadio/src/displays/displayILI9341.cpp @@ -10,9 +10,11 @@ #define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) #define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) -DspCore::DspCore(): Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST) { - -} +#if DSP_HSPI +DspCore::DspCore(): Adafruit_ILI9341(&SPI2, TFT_DC, TFT_CS, TFT_RST) {} +#else +DspCore::DspCore(): Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST) {} +#endif #include "tools/utf8RusGFX.h" diff --git a/yoRadio/src/displays/displayN5110.cpp b/yoRadio/src/displays/displayN5110.cpp index 935dd3b..01aeead 100644 --- a/yoRadio/src/displays/displayN5110.cpp +++ b/yoRadio/src/displays/displayN5110.cpp @@ -23,9 +23,11 @@ const unsigned char logo [] PROGMEM= 0xe0, 0x01, 0xff, 0xc0 }; -DspCore::DspCore(): Adafruit_PCD8544(TFT_DC, TFT_CS, TFT_RST) { - -} +#if DSP_HSPI +DspCore::DspCore(): Adafruit_PCD8544(TFT_DC, TFT_CS, TFT_RST, &SPI2) {} +#else +DspCore::DspCore(): Adafruit_PCD8544(TFT_DC, TFT_CS, TFT_RST) {} +#endif #include "tools/utf8RusGFX.h" diff --git a/yoRadio/src/displays/displaySSD1305.cpp b/yoRadio/src/displays/displaySSD1305.cpp index f5a80fd..90981f8 100644 --- a/yoRadio/src/displays/displaySSD1305.cpp +++ b/yoRadio/src/displays/displaySSD1305.cpp @@ -30,9 +30,11 @@ const unsigned char logo [] PROGMEM= }; #if DSP_MODEL==DSP_SSD1305 -DspCore::DspCore(): Adafruit_SSD1305(128, 64, &SPI, TFT_DC, TFT_RST, TFT_CS, DEF_SPI_FREQ) { - -} + #if DSP_HSPI + DspCore::DspCore(): Adafruit_SSD1305(128, 64, &SPI2, TFT_DC, TFT_RST, TFT_CS, DEF_SPI_FREQ) {} + #else + DspCore::DspCore(): Adafruit_SSD1305(128, 64, &SPI, TFT_DC, TFT_RST, TFT_CS, DEF_SPI_FREQ) {} + #endif #else #include TwoWire I2CSSD1305 = TwoWire(0); diff --git a/yoRadio/src/displays/displayST7735.cpp b/yoRadio/src/displays/displayST7735.cpp index 0179604..ca2ed6d 100644 --- a/yoRadio/src/displays/displayST7735.cpp +++ b/yoRadio/src/displays/displayST7735.cpp @@ -14,9 +14,11 @@ #define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) #define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) -DspCore::DspCore(): Adafruit_ST7735(&SPI, TFT_CS, TFT_DC, TFT_RST) { - -} +#if DSP_HSPI +DspCore::DspCore(): Adafruit_ST7735(&SPI2, TFT_CS, TFT_DC, TFT_RST) {} +#else +DspCore::DspCore(): Adafruit_ST7735(&SPI, TFT_CS, TFT_DC, TFT_RST) {} +#endif #include "tools/utf8RusGFX.h" diff --git a/yoRadio/src/displays/displayST7789.cpp b/yoRadio/src/displays/displayST7789.cpp index 8da1a0b..baf8d97 100644 --- a/yoRadio/src/displays/displayST7789.cpp +++ b/yoRadio/src/displays/displayST7789.cpp @@ -15,7 +15,11 @@ #define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) #define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) +#if DSP_HSPI +DspCore::DspCore(): Adafruit_ST7789(&SPI2, TFT_CS, TFT_DC, TFT_RST) {} +#else DspCore::DspCore(): Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST) {} +#endif #include "tools/utf8RusGFX.h" diff --git a/yoRadio/src/displays/displayST7796.cpp b/yoRadio/src/displays/displayST7796.cpp new file mode 100644 index 0000000..7aff7ad --- /dev/null +++ b/yoRadio/src/displays/displayST7796.cpp @@ -0,0 +1,216 @@ +#include "../core/options.h" +#if DSP_MODEL==DSP_ST7796 + +#include "displayST7796.h" +//#include +#include "fonts/bootlogo.h" +#include "../core/player.h" +#include "../core/config.h" +#include "../core/network.h" + +#ifndef DEF_SPI_FREQ + #define DEF_SPI_FREQ 40000000UL /* set it to 0 for system default */ +#endif + +#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY) +#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl) + +#if DSP_HSPI +DspCore::DspCore(): Adafruit_ST7796S_kbv(&SPI2, TFT_DC, TFT_CS, TFT_RST) {} +#else +DspCore::DspCore(): Adafruit_ST7796S_kbv(TFT_CS, TFT_DC, TFT_RST) {} +#endif + +#include "tools/utf8RusGFX.h" + +void DspCore::initDisplay() { + begin(); + if(DEF_SPI_FREQ > 0) setSPISpeed(DEF_SPI_FREQ); + invert(); + cp437(true); + flip(); + setTextWrap(false); + setTextSize(1); + fillScreen(0x0000); +} + +void DspCore::drawLogo(uint16_t top) { drawRGBBitmap((width() - 99) / 2, top, bootlogo2, 99, 64); } + +void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) { + for (byte i = 0; i < PLMITEMS; i++) { + plMenu[i][0] = '\0'; + } + config.fillPlMenu(plMenu, currentItem - 5, PLMITEMS); + setTextSize(3); + int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3; + + for (byte i = 0; i < PLMITEMS; i++) { + if (i == 5) { + strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); + } else { + setTextColor(config.theme.playlist[abs(i - 5)-1], config.theme.background); + setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); + fillRect(0, yStart + i * PLMITEMHEIGHT - 1, width(), PLMITEMHEIGHT - 6, config.theme.background); + print(utf8Rus(plMenu[i], true)); + } + } +} + +void DspCore::clearDsp(bool black) { fillScreen(black?0:config.theme.background); } + +GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) { + return gfxFont->glyph + c; +} + +uint8_t DspCore::_charWidth(unsigned char c){ + GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI56pt7b, c - 0x20); + return pgm_read_byte(&glyph->xAdvance); +} + +uint16_t DspCore::textWidth(const char *txt){ + uint16_t w = 0, l=strlen(txt); + for(uint16_t c=0;c0) + dsp.fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background); + setTextColor(config.theme.date, config.theme.background); + setCursor(_dateleft, clockTop+15); + setTextSize(2); + print(_dateBuf); /* print date */ + strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf)); + _olddatewidth = _datewidth; + _olddateleft = _dateleft; + setTextSize(4); + setTextColor(config.theme.dow, config.theme.background); + setCursor(width() - 8 - clockRightSpace - CHARWIDTH*4*2, clockTop-CHARHEIGHT*4+4); + print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */ +} + +void DspCore::_clockTime(){ + if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background); + _timeleft = width()-clockRightSpace-CHARWIDTH*4*2-24-_timewidth; + setTextSize(1); + setFont(&DS_DIGI56pt7b); + setTextColor(config.theme.clock, config.theme.background); + setCursor(_timeleft, clockTop); + print(_timeBuf); + setFont(); + strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf)); + _oldtimewidth = _timewidth; + _oldtimeleft = _timeleft; + drawFastVLine(width()-clockRightSpace-CHARWIDTH*4*2-18, clockTop-clockTimeHeight, clockTimeHeight+4, config.theme.div); /*divider vert*/ + drawFastHLine(width()-clockRightSpace-CHARWIDTH*4*2-18, clockTop-clockTimeHeight+37, 59, config.theme.div); /*divider hor*/ + sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900); + strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf)); + _datewidth = strlen(_dateBuf) * CHARWIDTH*2; + _dateleft = width() - 10 - clockRightSpace - _datewidth; +} + +void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){ + clockTop = top; + clockRightSpace = rightspace; + clockTimeHeight = timeheight; + strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo); + if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){ + _getTimeBounds(); + _clockTime(); + if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate(); + } + _clockSeconds(); +} + +void DspCore::clearClock(){ + dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+12+CHARHEIGHT, config.theme.background); +} + +void DspCore::startWrite(void) { + TAKE_MUTEX(); + Adafruit_ST7796S_kbv::startWrite(); +} + +void DspCore::endWrite(void) { + Adafruit_ST7796S_kbv::endWrite(); + GIVE_MUTEX(); +} + +void DspCore::loop(bool force) { + +} + +void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){ + width = textsize * CHARWIDTH; + height = textsize * CHARHEIGHT; +} + +void DspCore::setTextSize(uint8_t s){ + Adafruit_GFX::setTextSize(s); +} + +void DspCore::flip(){ + setRotation(config.store.flipscreen?3:1); +} + +void DspCore::invert(){ + invertDisplay(config.store.invertdisplay); +} + +void DspCore::sleep(void) { + sendCommand(ST7796S_SLPIN); delay(150); sendCommand(ST7796S_DISPOFF); delay(150); +} + +void DspCore::wake(void) { + sendCommand(ST7796S_DISPON); delay(150); sendCommand(ST7796S_SLPOUT); delay(150); +} + +void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) { + if(_clipping){ + if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return; + } + Adafruit_ST7796S_kbv::writePixel(x, y, color); +} + +void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { + if(_clipping){ + if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return; + } + Adafruit_ST7796S_kbv::writeFillRect(x, y, w, h, color); +} + +void DspCore::setClipping(clipArea ca){ + _cliparea = ca; + _clipping = true; +} + +void DspCore::clearClipping(){ + _clipping = false; +} + +void DspCore::setNumFont(){ + setFont(&DS_DIGI56pt7b); + setTextSize(1); +} +#endif diff --git a/yoRadio/src/displays/displayST7796.h b/yoRadio/src/displays/displayST7796.h new file mode 100644 index 0000000..586f85b --- /dev/null +++ b/yoRadio/src/displays/displayST7796.h @@ -0,0 +1,35 @@ +#ifndef displayST7789_h +#define displayST7789_h +#include "../core/options.h" + +#include "Arduino.h" +#include +//#include +#include "../Adafruit_ST7796S/Adafruit_ST7796S_kbv.h" +#include "fonts/DS_DIGI56pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/ +#include "tools/l10n.h" + +#define CHARWIDTH 6 +#define CHARHEIGHT 8 + +typedef GFXcanvas16 Canvas; +#include "widgets/widgets.h" +#include "widgets/pages.h" + +#if __has_include("conf/displayST7796conf_custom.h") + #include "conf/displayST7796conf_custom.h" +#else + #include "conf/displayST7796conf.h" +#endif + +#define BOOT_PRG_COLOR 0xE68B +#define BOOT_TXT_COLOR 0xFFFF +#define PINK 0xF97F + +class DspCore: public Adafruit_ST7796S_kbv { +#include "tools/commongfx.h" +}; + +extern DspCore dsp; + +#endif diff --git a/yoRadio/src/displays/dspcore.h b/yoRadio/src/displays/dspcore.h index 18b7fa6..769ae03 100644 --- a/yoRadio/src/displays/dspcore.h +++ b/yoRadio/src/displays/dspcore.h @@ -34,6 +34,8 @@ #include "displayCustom.h" #elif DSP_MODEL==DSP_ILI9225 #include "displayILI9225.h" +#elif DSP_MODEL==DSP_ST7796 + #include "displayST7796.h" #endif //extern DspCore dsp; diff --git a/yoRadio/src/displays/fonts/DS_DIGI56pt7b.h b/yoRadio/src/displays/fonts/DS_DIGI56pt7b.h new file mode 100644 index 0000000..cd19b37 --- /dev/null +++ b/yoRadio/src/displays/fonts/DS_DIGI56pt7b.h @@ -0,0 +1,320 @@ +const uint8_t DS_DIGI56pt7bBitmaps[] PROGMEM = { + 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC7, 0x1F, 0xFF, 0xFF, 0xFF, 0xE3, 0xE3, 0xFF, 0xFF, + 0xFF, 0xF1, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0xFF, 0x8F, 0xFF, 0xFF, 0xFC, + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, + 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7D, 0xC0, 0x00, 0x00, 0x00, 0x0E, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, + 0x00, 0x01, 0x1C, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x80, 0x00, 0x00, 0x00, + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0x1F, + 0xF1, 0xFF, 0xFF, 0xFF, 0xE3, 0xF8, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C, 0x7F, + 0xFF, 0xFF, 0xFF, 0x8E, 0x3F, 0xFF, 0xFF, 0xFF, 0xF1, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x04, 0x18, 0x71, + 0xE7, 0xDF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xEF, 0xCF, 0x8E, + 0x08, 0x00, 0x00, 0x00, 0x83, 0x8F, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xEF, 0xCF, 0x8F, 0x0E, 0x0C, 0x08, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, + 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xC4, + 0x1F, 0xFF, 0xFF, 0xFF, 0xE3, 0x03, 0xFF, 0xFF, 0xFF, 0xF1, 0xC0, 0x7F, + 0xFF, 0xFF, 0xF8, 0xF0, 0x0F, 0xFF, 0xFF, 0xFC, 0x7C, 0x00, 0x00, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, + 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, + 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x07, 0xFF, 0xFF, 0xFF, 0x1F, 0x03, 0xFF, 0xFF, 0xFF, + 0xE3, 0x81, 0xFF, 0xFF, 0xFF, 0xFC, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0x80, + 0x1F, 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xE0, 0x08, 0x7F, + 0xFF, 0xFF, 0xF0, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, + 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, + 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0x00, 0x03, 0xE3, 0xFF, 0xFF, 0xFF, 0x00, 0xF1, 0xFF, 0xFF, 0xFF, + 0xE0, 0x38, 0xFF, 0xFF, 0xFF, 0xFC, 0x0C, 0x7F, 0xFF, 0xFF, 0xFF, 0x82, + 0x3F, 0xFF, 0xFF, 0xFF, 0xF0, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x7F, 0xFF, 0xFF, 0xFF, 0xFC, 0x3F, 0xFF, 0xFF, + 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xFF, 0xE1, + 0x07, 0xFF, 0xFF, 0xFF, 0xC3, 0x03, 0xFF, 0xFF, 0xFF, 0x87, 0x01, 0xFF, + 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x03, 0xFF, 0xFF, 0xFF, 0x1F, 0x07, 0xFF, 0xFF, 0xFF, + 0x8E, 0x0F, 0xFF, 0xFF, 0xFF, 0xC4, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x0F, + 0xFF, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0xFF, 0x80, 0x03, 0xFF, 0xFF, + 0xFF, 0x04, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x1F, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x01, 0xFF, 0xFF, 0xFF, 0x1F, + 0x03, 0xFF, 0xFF, 0xFF, 0x8F, 0x07, 0xFF, 0xFF, 0xFF, 0xC7, 0x0F, 0xFF, + 0xFF, 0xFF, 0xE3, 0x1F, 0xFF, 0xFF, 0xFF, 0xF1, 0x3F, 0xFF, 0xFF, 0xFF, + 0xF8, 0x7F, 0xFF, 0xFF, 0xFF, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xE0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, + 0x00, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x3F, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x9F, 0xFF, 0xFF, 0xFE, + 0x7D, 0xCF, 0xFF, 0xFF, 0xFF, 0xCE, 0x27, 0xFF, 0xFF, 0xFF, 0xF9, 0x03, + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x0F, 0xFF, + 0xFF, 0xFF, 0xC0, 0x01, 0xFF, 0xFF, 0xFF, 0xE1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, + 0x7C, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x01, 0xC0, + 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, + 0xFF, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xC3, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, + 0xE3, 0xFF, 0xFF, 0xFF, 0xF0, 0x3C, 0x7F, 0xFF, 0xFF, 0xF8, 0x0F, 0x8F, + 0xFF, 0xFF, 0xFC, 0x03, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, + 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, + 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, + 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, + 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, + 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, + 0x80, 0x00, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xE7, 0xFF, + 0xFF, 0xFF, 0x80, 0x73, 0xFF, 0xFF, 0xFF, 0xF0, 0x09, 0xFF, 0xFF, 0xFF, + 0xFE, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, + 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xF8, 0x40, 0x00, + 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x03, 0xFF, + 0xFF, 0xFF, 0x1F, 0x01, 0xFF, 0xFF, 0xFF, 0xE3, 0xC0, 0xFF, 0xFF, 0xFF, + 0xFC, 0x70, 0x7F, 0xFF, 0xFF, 0xFF, 0x8C, 0x3F, 0xFF, 0xFF, 0xFF, 0xF1, + 0x1F, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, + 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC3, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0xE3, 0xFF, 0xFF, 0xFF, + 0xF0, 0x3C, 0x7F, 0xFF, 0xFF, 0xF8, 0x0F, 0x8F, 0xFF, 0xFF, 0xFC, 0x03, + 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, + 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, + 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, + 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, + 0xF8, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, + 0x00, 0x00, 0x00, 0x0F, 0xE0, 0x00, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, + 0x00, 0x00, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x80, 0x00, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x00, 0x03, 0xE7, 0xFF, 0xFF, 0xFF, 0x80, 0x73, + 0xFF, 0xFF, 0xFF, 0xF0, 0x09, 0xFF, 0xFF, 0xFF, 0xFE, 0x00, 0xFF, 0xFF, + 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xFF, + 0xF0, 0x08, 0x7F, 0xFF, 0xFF, 0xF8, 0x47, 0x00, 0x00, 0x00, 0x00, 0x3B, + 0xE0, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0x80, + 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, + 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, + 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, + 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, + 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, + 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, + 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, + 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, + 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xE3, 0xFF, 0xFF, 0xFF, 0x1F, 0xF1, + 0xFF, 0xFF, 0xFF, 0xE3, 0xF8, 0xFF, 0xFF, 0xFF, 0xFC, 0x7C, 0x7F, 0xFF, + 0xFF, 0xFF, 0x8E, 0x3F, 0xFF, 0xFF, 0xFF, 0xF1, 0x1F, 0xFF, 0xFF, 0xFF, + 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xFF, 0xFC, + 0x1F, 0xFF, 0xFF, 0xFF, 0xF8, 0x0F, 0xFF, 0xFF, 0xFF, 0xF1, 0x07, 0xFF, + 0xFF, 0xFF, 0xE3, 0x03, 0xFF, 0xFF, 0xFF, 0xC7, 0x01, 0xFF, 0xFF, 0xFF, + 0x8F, 0x00, 0xFF, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, + 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, + 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, + 0xFF, 0xFF, 0x88, 0xFF, 0xFF, 0xFF, 0xFF, 0xC7, 0x1F, 0xFF, 0xFF, 0xFF, + 0xE3, 0xE3, 0xFF, 0xFF, 0xFF, 0xF1, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0xFF, + 0x8F, 0xFF, 0xFF, 0xFC, 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, + 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, + 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, + 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, + 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xE7, + 0xFF, 0xFF, 0xFF, 0x9F, 0x73, 0xFF, 0xFF, 0xFF, 0xF3, 0x89, 0xFF, 0xFF, + 0xFF, 0xFE, 0x40, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, + 0xE0, 0x03, 0xFF, 0xFF, 0xFF, 0xF0, 0x08, 0x7F, 0xFF, 0xFF, 0xF8, 0x47, + 0x00, 0x00, 0x00, 0x00, 0x3B, 0xE0, 0x00, 0x00, 0x00, 0x1F, 0xFC, 0x00, + 0x00, 0x00, 0x0F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, + 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, + 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, + 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, + 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, + 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, + 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, + 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, + 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xE3, + 0xFF, 0xFF, 0xFF, 0x1F, 0xF1, 0xFF, 0xFF, 0xFF, 0xE3, 0xF8, 0xFF, 0xFF, + 0xFF, 0xFC, 0x7C, 0x7F, 0xFF, 0xFF, 0xFF, 0x8E, 0x3F, 0xFF, 0xFF, 0xFF, + 0xF1, 0x1F, 0xFF, 0xFF, 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0x07, 0xFF, 0xFF, 0xFF, 0xFF, 0x88, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC7, 0x1F, 0xFF, 0xFF, 0xFF, 0xE3, 0xE3, 0xFF, 0xFF, + 0xFF, 0xF1, 0xFC, 0x7F, 0xFF, 0xFF, 0xF8, 0xFF, 0x8F, 0xFF, 0xFF, 0xFC, + 0x7F, 0xF0, 0x00, 0x00, 0x00, 0x3F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, + 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, + 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, + 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, + 0xFF, 0xF8, 0x00, 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, + 0x80, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x01, 0xFF, 0xF8, 0x00, + 0x00, 0x00, 0x7F, 0xFE, 0x00, 0x00, 0x00, 0x1F, 0xFF, 0x80, 0x00, 0x00, + 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0xFF, 0xE7, 0xFF, 0xFF, 0xFF, 0x9F, + 0x73, 0xFF, 0xFF, 0xFF, 0xF3, 0x89, 0xFF, 0xFF, 0xFF, 0xFE, 0x40, 0xFF, + 0xFF, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, + 0xFF, 0xF0, 0x00, 0x7F, 0xFF, 0xFF, 0xF8, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xC0, + 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, + 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, + 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, + 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, + 0xFC, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, + 0x00, 0x00, 0x00, 0x07, 0xF0, 0x00, 0x00, 0x00, 0x01, 0xFC, 0x00, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC0, 0x00, 0x00, 0x00, + 0x07, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x03, 0xFF, 0xFF, 0xFF, 0x1F, + 0x01, 0xFF, 0xFF, 0xFF, 0xE3, 0xC0, 0xFF, 0xFF, 0xFF, 0xFC, 0x70, 0x7F, + 0xFF, 0xFF, 0xFF, 0x8C, 0x3F, 0xFF, 0xFF, 0xFF, 0xF1, 0x1F, 0xFF, 0xFF, + 0xFF, 0xFE, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; + +const GFXglyph DS_DIGI56pt7bGlyphs[] PROGMEM = { + { 0, 0, 0, 24, 0, 1 }, // 0x20 ' ' + { 0, 0, 0, 0, 0, 0 }, // 0x21 '!' + { 0, 0, 0, 0, 0, 0 }, // 0x22 '"' + { 0, 0, 0, 0, 0, 0 }, // 0x23 '#' + { 0, 0, 0, 0, 0, 0 }, // 0x24 '$' + { 0, 0, 0, 0, 0, 0 }, // 0x25 '%' + { 0, 0, 0, 0, 0, 0 }, // 0x26 '&' + { 0, 0, 0, 0, 0, 0 }, // 0x27 ''' + { 0, 0, 0, 0, 0, 0 }, // 0x28 '(' + { 0, 0, 0, 0, 0, 0 }, // 0x29 ')' + { 0, 0, 0, 0, 0, 0 }, // 0x2A '*' + { 0, 0, 0, 0, 0, 0 }, // 0x2B '+' + { 0, 0, 0, 0, 0, 0 }, // 0x2C ',' + { 0, 0, 0, 0, 0, 0 }, // 0x2D '-' + { 0, 0, 0, 0, 0, 0 }, // 0x2E '.' + { 0, 0, 0, 0, 0, 0 }, // 0x2F '/' + { 0, 42, 70, 54, 6, -69 }, // 0x30 '0' + { 368, 7, 68, 29, 10, -68 }, // 0x31 '1' + { 428, 42, 70, 54, 6, -69 }, // 0x32 '2' + { 796, 40, 70, 54, 7, -69 }, // 0x33 '3' + { 1146, 42, 68, 54, 6, -68 }, // 0x34 '4' + { 1503, 42, 70, 54, 6, -69 }, // 0x35 '5' + { 1871, 42, 70, 54, 6, -69 }, // 0x36 '6' + { 2239, 40, 69, 54, 7, -69 }, // 0x37 '7' + { 2584, 42, 70, 54, 6, -69 }, // 0x38 '8' + { 2952, 42, 70, 54, 6, -69 }, // 0x39 '9' + { 3320, 7, 59, 24, 8, -58 } // 0x3A ':' +}; + +const GFXfont DS_DIGI56pt7b PROGMEM = { + (uint8_t *)DS_DIGI56pt7bBitmaps, + (GFXglyph *)DS_DIGI56pt7bGlyphs, 0x20, 0x3A, 110 }; + +// Approx. 6564 bytes