diff --git a/Images.md b/Images.md index b8fb726..f9ba986 100644 --- a/Images.md +++ b/Images.md @@ -18,3 +18,9 @@ ![ёRadio](images/img7.jpg)\ \ ![ёRadio](images/img8.jpg)\ +\ +![ёRadio](images/img9.jpg)\ +\ +![ёRadio](images/img10.jpg)\ +\ +![ёRadio](images/img11.jpg)\ diff --git a/README.md b/README.md index 190b123..d3fc2d0 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,9 @@ https://aliexpress.com/item/32965676064.html - **ST7735** 1.8' or 1.44' https://aliexpress.com/item/1005002822797745.html - or **SSD1306** 0.96' I2C https://aliexpress.com/item/1005001621806398.html - or **Nokia5110** 84x48 SPI https://aliexpress.com/item/1005001621837569.htmlz +- or **ST7789** 320x240 SPI https://aliexpress.com/item/32960241206.html +- or **SH1106** 128x64 I2C https://aliexpress.com/item/32683094040.html +- or **1602** 16x2 I2C https://aliexpress.com/item/32305776560.html ##### Controls - Three tact buttons https://www.aliexpress.com/item/32907144687.html @@ -239,6 +242,15 @@ download _http://\/data/playlist.csv_ and _http://\/data --- ## Version history +#### v0.5.010 +- added support for ST7789 320x240 SPI displays +- added support for SH1106 I2C displays +- added support for 1602 16x2 I2C displays +- a little modified control logic +- added buttons long press feature +- small changes in options.h, check the correctness of your myoptions.h file +- bugs fixes + #### v0.4.323 - fixed bug [Equalizer not come visible after go to playlist](https://github.com/e2002/yoradio/issues/1) \ (a [full update](#update) is required) diff --git a/exsamples/myoptions.h b/exsamples/myoptions.h index 0d6950c..60d4876 100644 --- a/exsamples/myoptions.h +++ b/exsamples/myoptions.h @@ -1,68 +1,101 @@ #ifndef myoptions_h #define myoptions_h -/* - * HWID's: - * 0: with yellow-blue SSD1306 - * 1: white SSD1306 without controls - * 2: ST7735 with encoder - * 3: Nokia 5110 dev board - * 4: VS1053 dev - * 5: VS1053 UNO3 Shield - */ -#define HWID 2 +/******************************************************* +Copy this file in the project root directory (next to the yoRadio.ino file) +Uncomment the lines you need, to override the default value and set the values according to the connected equipment. + +The connection tables are located here https://github.com/e2002/yoradio#connection-tables + +********************************************************/ + +/* DSP_MODEL. See description/available values in the options.h file */ +/* This option is required. Use DSP_DUMMY if no display is connected */ +#define DSP_MODEL DSP_DUMMY /******************************************/ -#if HWID==0 -#define DSP_MODEL 2 -#define TFT_ROTATE 0 -#define I2S_DOUT 22 -#define BTN_LEFT 16 -#define BTN_CENTER 5 -#define BTN_RIGHT 17 - -#elif HWID==1 -#define DSP_MODEL 2 -#define TFT_ROTATE 0 -#define I2S_DOUT 22 - -#elif HWID==2 -#define DSP_MODEL 1 -#define TFT_ROTATE 3 -#define ENC_BTNL 13 -#define ENC_BTNB 12 -#define ENC_BTNR 14 - -#elif HWID==3 -#define DSP_MODEL 3 -#define TFT_ROTATE 0 -#define BTN_LEFT 13 -#define BTN_CENTER 12 -#define BTN_RIGHT 14 - -#elif HWID==4 -#define DSP_MODEL 3 -#define TFT_ROTATE 0 -#define VS1053_CS 27 -#define I2S_DOUT 255 -//#define VS1053_RST 14 - -#elif HWID==5 -#define DSP_MODEL 3 -#define TFT_RST -1 -#define TFT_DC 13 -#define I2S_DOUT 255 -#define VS1053_CS 27 -#define VS1053_DCS 14 -#define VS1053_DREQ 26 -#define VS1053_RST 12 -#define ENC_BTNL 25 -#define ENC_BTNB 17 -#define ENC_BTNR 16 - -#endif - +/* SPI 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 pin */ /******************************************/ +/* I2C PINS */ +//#define I2C_SDA 21 /* I2C SDA pin. It is best to connect to pin 21. */ +//#define I2C_SCL 22 /* I2C SCL pin. It is best to connect to pin 22. */ +//#define I2C_RST -1 /* I2C RST pin. Set to -1 if not used */ + +/* I2S DAC */ +//#define I2S_DOUT 27 /* DIN connection. Should be set to 255 if the board is not used */ +//#define I2S_BCLK 26 /* BCLK Bit clock */ +//#define I2S_LRC 25 /* WSEL Left Right Clock */ +/******************************************/ + +/* VS1053 PINS. VS1053 SCK must be connected to pin 18 + VS1053 MOSI must be connected to pin 23 */ +//#define VS1053_CS 255 /* Should be set to 255 if the board is not used */ +//#define VS1053_DCS 25 +//#define VS1053_DREQ 26 +//#define VS1053_RST -1 /* Set to -1 if connected to Esp EN pin */ +/******************************************/ + +/* ENCODER */ +//#define ENC_BTNL 255 /* Left rotation */ +//#define ENC_BTNB 255 /* Encoder button */ +//#define ENC_BTNR 255 /* Right rotation */ +//#define ENC_INTERNALPULLUP true /* Enable the weak pull up resistors */ +//#define ENC_HALFQUARD true /* Experiment with it */ +/******************************************/ + +/* BUTTONS */ +//#define BTN_LEFT 255 /* VolDown, Prev */ +//#define BTN_CENTER 255 /* Play, Stop, Show playlist */ +//#define BTN_RIGHT 255 /* VolUp, Next */ +//#define BTN_INTERNALPULLUP true /* Enable the weak pull up resistors */ +//#define BTN_LONGPRESS_LOOP_DELAY 200 /* Delay between calling DuringLongPress event */ +//#define BTN_CLICK_TICKS 300 /* Event Timing https://github.com/mathertel/OneButton#event-timing */ +//#define BTN_PRESS_TICKS 500 /* Event Timing https://github.com/mathertel/OneButton#event-timing */ +/******************************************/ + +/* Other settings. */ +//#define DTYPE INITR_BLACKTAB /* ST7735 display submodel */ + /* Could be one of: */ + /* INITR_BLACKTAB 1.8' https://aliexpress.ru/item/1005002822797745.html */ + /* (See this note If INITR_BLACKTAB have a noisy line on one side of the screen https://github.com/e2002/yoradio#note-if-initr_blacktab-dsp-have-a-noisy-line-on-one-side-of-the-screen-then-in-adafruit_st7735cpp ) */ + /* INITR_144GREENTAB // 1.44' https://aliexpress.ru/item/1005002822797745.html */ + /* INITR_GREENTAB */ + /* INITR_REDTAB */ +//#define LED_BUILTIN 2 /* LED Pin */ +//#define TFT_ROTATE 3 /* Display rotation. 0 - 0, 1 - 90, 2 - 180, 3 - 270 degrees */ +//#define TFT_CONTRAST 55 /* Nokia 5110 contrast */ +//#define TFT_INVERT true /* Invert the display colors (usually true) */ +//#define VOL_STEP 1 /* Volume control step */ +//#define MUTE_PIN 255 /* MUTE Pin */ +//#define MUTE_VAL HIGH /* Write this to MUTE_PIN when player is stopped */ +/******************************************/ + +/* IR control */ +//#define IR_PIN 255 +//#define IR_DEBUG 0 /* Set this to 1, capture ir codes from serial, insert the captured codes below and set this back to 0 */ +//#define IR_TIMEOUT 80 /* see kTimeout description in IRremoteESP8266 exsample https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/IRrecvDumpV2/IRrecvDumpV2.ino */ +//#define IR_TLP 40 /* see kTolerancePercentage description in IRremoteESP8266 exsample https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/IRrecvDumpV2/IRrecvDumpV2.ino */ +//#define IR_CODE_PLAY 0xFF02FD +//#define IR_CODE_PREV 0xFF22DD +//#define IR_CODE_NEXT 0xFFC23DP +//#define IR_CODE_VOLUP 0xFF629D +//#define IR_CODE_VOLDN 0xFFA857 +//#define IR_CODE_NUM0 0xFF4AB5 +//#define IR_CODE_NUM1 0xFF6897 +//#define IR_CODE_NUM2 0xFF9867 +//#define IR_CODE_NUM3 0xFFB04F +//#define IR_CODE_NUM4 0xFF30CF +//#define IR_CODE_NUM5 0xFF18E7 +//#define IR_CODE_NUM6 0xFF7A85 +//#define IR_CODE_NUM7 0xFF10EF +//#define IR_CODE_NUM8 0xFF38C7 +//#define IR_CODE_NUM9 0xFF5AA5 +//#define IR_CODE_HASH 0xFF52AD /* Toggle playlist mode */ +//#define IR_CODE_AST 0xFF42BD /* Not used */ #endif diff --git a/images/img10.jpg b/images/img10.jpg new file mode 100644 index 0000000..e013272 Binary files /dev/null and b/images/img10.jpg differ diff --git a/images/img11.jpg b/images/img11.jpg new file mode 100644 index 0000000..496efc4 Binary files /dev/null and b/images/img11.jpg differ diff --git a/images/img9.jpg b/images/img9.jpg new file mode 100644 index 0000000..1ac0516 Binary files /dev/null and b/images/img9.jpg differ diff --git a/yoRadio/controls.cpp b/yoRadio/controls.cpp index 7929844..41b8ea9 100644 --- a/yoRadio/controls.cpp +++ b/yoRadio/controls.cpp @@ -6,25 +6,18 @@ #include "display.h" long encOldPosition = 0; +int lpId = -1; -#if BTN_LEFT!=255 || BTN_LEFT!=255 || BTN_RIGHT!=255 || ENC_BTNL!=255 +#define ISPUSHBUTTONS BTN_LEFT!=255 || BTN_LEFT!=255 || BTN_RIGHT!=255 || ENC_BTNB!=255 +#if ISPUSHBUTTONS #include "OneButton.h" +OneButton button[] {{BTN_LEFT, true, BTN_INTERNALPULLUP}, {BTN_CENTER, true, BTN_INTERNALPULLUP}, {BTN_RIGHT, true, BTN_INTERNALPULLUP}, {ENC_BTNB, true, ENC_INTERNALPULLUP}}; +constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]); #endif -#if ENC_BTNL!=255 +#if ENC_BTNL!=255 && ENC_BTNR!=255 #include ESP32Encoder encoder; -OneButton encbutton(ENC_BTNB, true); -#endif - -#if BTN_LEFT!=255 -OneButton btnleft(BTN_LEFT, true, BTN_INTERNALPULLUP); -#endif -#if BTN_CENTER!=255 -OneButton btncenter(BTN_CENTER, true, BTN_INTERNALPULLUP); -#endif -#if BTN_RIGHT!=255 -OneButton btnright(BTN_RIGHT, true, BTN_INTERNALPULLUP); #endif #if IR_PIN!=255 @@ -54,23 +47,28 @@ void initControls() { } else { encoder.attachFullQuad(ENC_BTNL, ENC_BTNR); } - encbutton.attachClick(onEncClick); - encbutton.attachDoubleClick(onEncDoubleClick); - encbutton.attachLongPressStart(onEncLPStart); #endif -#if BTN_LEFT!=255 - btnleft.attachClick(onLeftClick); - btnleft.attachDoubleClick(onLeftDoubleClick); -#endif -#if BTN_CENTER!=255 - btncenter.attachClick(onEncClick); - btncenter.attachDoubleClick(onEncDoubleClick); - btncenter.attachLongPressStart(onEncLPStart); -#endif -#if BTN_RIGHT!=255 - btnright.attachClick(onRightClick); - btnright.attachDoubleClick(onRightDoubleClick); +#if ISPUSHBUTTONS + for (int i = 0; i < nrOfButtons; i++) + { + if ((i == 0 && BTN_LEFT == 255) || (i == 1 && BTN_CENTER == 255) || (i == 2 && BTN_RIGHT == 255) || (i == 3 && ENC_BTNB == 255)) continue; + button[i].attachClick([](void* p) { + onBtnClick((int)p); + }, (void*)i); + button[i].attachDoubleClick([](void* p) { + onBtnDoubleClick((int)p); + }, (void*)i); + button[i].attachLongPressStart([](void* p) { + onBtnLongPressStart((int)p); + }, (void*)i); + button[i].attachLongPressStop([](void* p) { + onBtnLongPressStop((int)p); + }, (void*)i); + button[i].setClickTicks(BTN_CLICK_TICKS); + button[i].setPressTicks(BTN_PRESS_TICKS); + } #endif + #if IR_PIN!=255 pinMode(IR_PIN, INPUT); assert(irutils::lowLevelSanityCheck() == 0); @@ -84,17 +82,17 @@ void initControls() { void loopControls() { #if ENC_BTNL!=255 - encbutton.tick(); encoderLoop(); #endif -#if BTN_LEFT!=255 - btnleft.tick(); -#endif -#if BTN_CENTER!=255 - btncenter.tick(); -#endif -#if BTN_RIGHT!=255 - btnright.tick(); +#if ISPUSHBUTTONS + for (unsigned i = 0; i < nrOfButtons; i++) + { + if ((i == 0 && BTN_LEFT == 255) || (i == 1 && BTN_CENTER == 255) || (i == 2 && BTN_RIGHT == 255) || (i == 3 && ENC_BTNB == 255)) continue; + button[i].tick(); + if (lpId >= 0) { + onBtnDuringLongPress(lpId); + } + } #endif #if IR_PIN!=255 irLoop(); @@ -114,10 +112,9 @@ void encoderLoop() { #endif #if IR_PIN!=255 - void irBlink() { if (player.mode == STOPPED) { - for(byte i=0; i<7; i++) { + for (byte i = 0; i < 7; i++) { digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); delay(100); } @@ -163,7 +160,7 @@ void irLoop() { display.numOfNextStation = 0; break; } - onEncClick(); + onBtnClick(1); break; } case IR_CODE_PREV: { @@ -236,29 +233,56 @@ void irLoop() { } } } -#endif +#endif // if IR_PIN!=255 -void onEncClick() { - if (display.mode == NUMBERS) { - display.numOfNextStation = 0; - display.swichMode(PLAYER); - } - if (display.mode == PLAYER) { - player.toggle(); - } - if (display.mode == STATIONS) { - display.swichMode(PLAYER); - player.play(display.currentPlItem); +void onBtnLongPressStart(int id) { + switch (id) { + case 0: + case 2: { + lpId = id; + break; + } + case 1: + case 3: { + display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER); + break; + } } } - -void onEncDoubleClick() { - display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER); +void onBtnLongPressStop(int id) { + switch (id) { + case 0: + case 2: { + lpId = -1; + break; + } + } } -void onEncLPStart() { - display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER); +unsigned long lpdelay; +boolean checklpdelay(int m, unsigned long &tstamp) { + if (millis() - tstamp > m) { + tstamp = millis(); + return true; + } else { + return false; + } +} + +void onBtnDuringLongPress(int id) { + if (checklpdelay(BTN_LONGPRESS_LOOP_DELAY, lpdelay)) { + switch (id) { + case 0: { + controlsEvent(false); + break; + } + case 2: { + controlsEvent(true); + break; + } + } + } } void controlsEvent(bool toRight) { @@ -280,20 +304,50 @@ void controlsEvent(bool toRight) { } } -void onLeftClick() { - controlsEvent(false); +void onBtnClick(int id) { + switch (id) { + case 0: { + controlsEvent(false); + break; + } + case 1: + case 3: { + if (display.mode == NUMBERS) { + display.numOfNextStation = 0; + display.swichMode(PLAYER); + } + if (display.mode == PLAYER) { + player.toggle(); + } + if (display.mode == STATIONS) { + display.swichMode(PLAYER); + player.play(display.currentPlItem); + } + break; + } + case 2: { + controlsEvent(true); + break; + } + } } -void onLeftDoubleClick() { - display.swichMode(PLAYER); - player.prev(); -} - -void onRightClick() { - controlsEvent(true); -} - -void onRightDoubleClick() { - display.swichMode(PLAYER); - player.next(); +void onBtnDoubleClick(int id) { + switch (id) { + case 0: { + if (display.mode != PLAYER) return; + player.prev(); + break; + } + case 1: + case 3: { + display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER); + break; + } + case 2: { + if (display.mode != PLAYER) return; + player.next(); + break; + } + } } diff --git a/yoRadio/controls.h b/yoRadio/controls.h index 19bd38b..0d15e67 100644 --- a/yoRadio/controls.h +++ b/yoRadio/controls.h @@ -1,21 +1,20 @@ #ifndef controls_h #define controls_h +boolean checklpdelay(int m, unsigned long &tstamp); void initControls(); void loopControls(); -void onEncClick(); -void onEncDoubleClick(); -void onEncLPStart(); void encoderLoop(); void irLoop(); void irNum(byte num); void irBlink(); void controlsEvent(bool toRight); -void onLeftClick(); -void onLeftDoubleClick(); -void onRightClick(); -void onRightDoubleClick(); +void onBtnClick(int id); +void onBtnDoubleClick(int id); +void onBtnDuringLongPress(int id); +void onBtnLongPressStart(int id); +void onBtnLongPressStop(int id); #endif diff --git a/yoRadio/display.cpp b/yoRadio/display.cpp index 91cc0f9..218a576 100644 --- a/yoRadio/display.cpp +++ b/yoRadio/display.cpp @@ -8,18 +8,27 @@ #include "netserver.h" #include "network.h" -#if DSP_MODEL==0 +#if DSP_MODEL==DSP_DUMMY #include "src/displays/displayDummy.h" DisplayDummy dsp; -#elif DSP_MODEL==1 +#elif DSP_MODEL==DSP_ST7735 #include "src/displays/displayST7735.h" DisplayST7735 dsp; -#elif DSP_MODEL==2 +#elif DSP_MODEL==DSP_SSD1306 #include "src/displays/displaySSD1306.h" DisplaySSD1306 dsp; -#elif DSP_MODEL==3 +#elif DSP_MODEL==DSP_NOKIA5110 #include "src/displays/displayN5110.h" DisplayN5110 dsp; +#elif DSP_MODEL==DSP_ST7789 +#include "src/displays/displayST7789.h" +DisplayST7789 dsp; +#elif DSP_MODEL==DSP_SH1106 +#include "src/displays/displaySH1106.h" +DisplaySH1106 dsp; +#elif DSP_MODEL==DSP_1602I2C +#include "src/displays/displayLC1602.h" +DisplayLC1602 dsp; #endif Display display; @@ -28,28 +37,67 @@ void ticks() { display.clockRequest = true; } +#ifndef STARTTIME #define STARTTIME 5000 - -#if DSP_MODEL==3 -#define SCROLLDELTA 8 -#define SCROLLTIME 332 -#else +#endif +#ifndef STARTTIME_PL +#define STARTTIME_PL 0 +#endif +#ifndef SCROLLDELTA #define SCROLLDELTA 3 #define SCROLLTIME 83 #endif +#ifndef META_SIZE +#define META_SIZE 2 +#endif +#ifndef TITLE_SIZE1 +#define TITLE_SIZE1 1 +#endif +#ifndef TITLE_SIZE2 +#define TITLE_SIZE2 1 +#endif +#ifndef TITLE_TOP1 +#define TITLE_TOP1 TFT_FRAMEWDT + META_SIZE * TFT_LINEHGHT +#endif +#ifndef TITLE_TOP2 +#define TITLE_TOP2 TFT_FRAMEWDT + (META_SIZE+2) * TFT_LINEHGHT +#endif +#ifndef TITLE_FG1 +#define TITLE_FG1 TFT_FG +#endif +#ifndef TITLE_FG2 +#define TITLE_FG2 TFT_FG +#endif +#ifndef BOOTSTR_TOP1 +#define BOOTSTR_TOP1 90 +#endif +#ifndef BOOTSTR_TOP2 +#define BOOTSTR_TOP2 110 +#endif +#ifndef PLCURRENT_SIZE +#define PLCURRENT_SIZE 2 +#endif + +#ifndef CLOCK_SPACE +#define DO_SCROLL (tWidth > display.screenwidth - TFT_FRAMEWDT * 2) +#else +#define DO_SCROLL (tWidth > (display.screenwidth - (dsp.fillSpaces?((texttop==0)?CLOCK_SPACE:VOL_SPACE):0))) +#endif void Scroll::init(const char *sep, byte tsize, byte top, uint16_t dlay, uint16_t fgcolor, uint16_t bgcolor) { textsize = tsize; + if (textsize == 0) return; texttop = top; fg = fgcolor; bg = bgcolor; - delayStartScroll=dlay; + delayStartScroll = dlay; memset(separator, 0, 4); strlcpy(separator, sep, 4); locked = false; } void Scroll::setText(const char *txt) { + if (textsize == 0) return; memset(text, 0, BUFLEN / 2); strlcpy(text, txt, BUFLEN / 2); getbounds(textwidth, textheight, sepwidth); @@ -59,11 +107,16 @@ void Scroll::setText(const char *txt) { } } -void Scroll::lock() { locked = true; } +void Scroll::lock() { + locked = true; +} -void Scroll::unlock() { locked = false; } +void Scroll::unlock() { + locked = false; +} void Scroll::reset() { + if (textsize == 0) return; locked = false; clear(); setTextParams(); @@ -73,17 +126,20 @@ void Scroll::reset() { } void Scroll::setTextParams() { + if (textsize == 0) return; dsp.set_TextSize(textsize); dsp.set_TextColor(fg, bg); } void Scroll::clearscrolls() { + if (textsize == 0) return; x = TFT_FRAMEWDT; scrolldelay = millis(); clear(); } void Scroll::loop() { + if (textsize == 0) return; if (checkdelay(x == TFT_FRAMEWDT ? delayStartScroll : SCROLLTIME, scrolldelay)) { scroll(); sticks(); @@ -101,11 +157,12 @@ boolean Scroll::checkdelay(int m, unsigned long &tstamp) { } void Scroll::drawFrame() { + if (textsize == 0) return; dsp.drawScrollFrame(texttop, textheight, bg); } void Scroll::sticks() { - if (!doscroll || locked) return; + if (!doscroll || locked || textsize == 0) return; setTextParams(); dsp.set_Cursor(x, texttop); dsp.printText(text); @@ -115,46 +172,46 @@ void Scroll::sticks() { } void Scroll::scroll() { - if (!doscroll) return; - if (textwidth > display.screenwidth) { + if (!doscroll || textsize == 0) return; + //if (textwidth > display.screenwidth) { x -= SCROLLDELTA; - if (-x >= textwidth + sepwidth - TFT_FRAMEWDT) x = TFT_FRAMEWDT; - } + if (-x > textwidth + sepwidth - TFT_FRAMEWDT) x = TFT_FRAMEWDT; + //} } void Scroll::clear() { + if (textsize == 0) return; dsp.clearScroll(texttop, textheight, bg); } void Scroll::getbounds(uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) { + if (textsize == 0) return; if (strlen(text) == 0) { dsp.getScrolBbounds("EMPTY", separator, textsize, tWidth, tHeight, sWidth); } else { dsp.getScrolBbounds(text, separator, textsize, tWidth, tHeight, sWidth); } - doscroll = (tWidth > display.screenwidth); + doscroll = DO_SCROLL; } void Display::init() { dsp.initD(screenwidth, screenheight); dsp.drawLogo(); - if(DSP_MODEL==3){ - meta.init(" * ", 1, TFT_FRAMEWDT, STARTTIME, TFT_LOGO, TFT_BG); - title1.init(" * ", 1, TFT_FRAMEWDT + TFT_LINEHGHT+1, STARTTIME, TFT_FG, TFT_BG); - //title2.init(" * ", 1, TFT_FRAMEWDT + 2 * TFT_LINEHGHT, STARTTIME, TFT_FG, TFT_BG); - }else{ - meta.init(" * ", 2, TFT_FRAMEWDT, STARTTIME, TFT_LOGO, TFT_BG); - title1.init(" * ", 1, TFT_FRAMEWDT + 2 * TFT_LINEHGHT, STARTTIME, TFT_FG, TFT_BG); - title2.init(" * ", 1, TFT_FRAMEWDT + 3 * TFT_LINEHGHT, STARTTIME, TFT_FG, TFT_BG); - } + meta.init(" * ", META_SIZE, TFT_FRAMEWDT, STARTTIME, TFT_LOGO, TFT_BG); + title1.init(" * ", TITLE_SIZE1, TITLE_TOP1, STARTTIME, TITLE_FG1, TFT_BG); + title2.init(" * ", TITLE_SIZE2, TITLE_TOP2, STARTTIME, TITLE_FG2, TFT_BG); int yStart = (screenheight / 2 - PLMITEMHEIGHT / 2) + 3; - //plCurrent.init(" * ", 2, 57, 0, TFT_BG, TFT_LOGO); - plCurrent.init(" * ", DSP_MODEL==3?1:2, yStart, 0, TFT_BG, TFT_LOGO); +#ifdef PL_TOP + yStart=PL_TOP; +#endif + plCurrent.init(" * ", PLCURRENT_SIZE, yStart, STARTTIME_PL, TFT_BG, TFT_LOGO); plCurrent.lock(); } void Display::apScreen() { +#ifndef PL_TOP meta.setText(dsp.utf8Rus("ёRADIO * ёRADIO * ёRADIO", false)); +#endif dsp.apScreen(); } @@ -193,13 +250,20 @@ void Display::swichMode(displayMode_e newmode) { if (newmode == PLAYER) { meta.reset(); title1.reset(); - if(DSP_MODEL!=3) title2.reset(); + if (TITLE_SIZE2 != 0) title2.reset(); plCurrent.lock(); - time(); + time(true); +#ifdef CLOCK_SPACE // if set space for clock in 1602 displays + dsp.fillSpaces=true; + volume(); +#endif } else { meta.lock(); title1.lock(); - if(DSP_MODEL!=3) title2.lock(); + if (TITLE_SIZE2 != 0) title2.lock(); +#ifdef CLOCK_SPACE + dsp.fillSpaces=false; +#endif } if (newmode == VOL) { dsp.frameTitle("VOLUME"); @@ -222,7 +286,7 @@ void Display::drawPlayer() { } meta.loop(); title1.loop(); - if(DSP_MODEL!=3) title2.loop(); + if (TITLE_SIZE2 != 0) title2.loop(); } void Display::drawVolume() { @@ -269,10 +333,15 @@ void Display::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) { } void Display::bootString(const char* text, byte y) { - dsp.centerText(text, y, TFT_LOGO, TFT_BG); + dsp.centerText(text, y==1?BOOTSTR_TOP1:BOOTSTR_TOP2, TFT_LOGO, TFT_BG); dsp.loop(); } +void Display::bootLogo() { + clear(); + dsp.drawLogo(); +} + void Display::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) { dsp.rightText(text, y, fg, bg); } @@ -291,7 +360,7 @@ void Display::title(const char *str) { strlcpy(config.station.title, title, BUFLEN); if (strlen(config.station.title) > 0) { char* ici; - if ((ici = strstr(config.station.title, " - ")) != NULL && DSP_MODEL!=3) { + if ((ici = strstr(config.station.title, " - ")) != NULL && TITLE_SIZE2 != 0) { strlcpy(sng, ici + 3, BUFLEN / 2); strlcpy(ttl, config.station.title, strlen(config.station.title) - strlen(ici) + 1); } else { @@ -299,14 +368,14 @@ void Display::title(const char *str) { sng[0] = '\0'; } title1.setText(dsp.utf8Rus(ttl, true)); - if(DSP_MODEL!=3) title2.setText(dsp.utf8Rus(sng, true)); + if (TITLE_SIZE2 != 0) title2.setText(dsp.utf8Rus(sng, true)); dsp.loop(); } netserver.requestOnChange(TITLE, 0); } void Display::heap() { - if(config.store.audioinfo) dsp.displayHeapForDebug(); + if (config.store.audioinfo) dsp.displayHeapForDebug(); } void Display::rssi() { @@ -321,16 +390,22 @@ void Display::ip() { dsp.ip(WiFi.localIP().toString().c_str()); } -void Display::time() { +void Display::time(bool redraw) { char timeStringBuff[20] = { 0 }; if (!dt) { heap(); rssi(); + } +#ifndef TFT_FULLTIME + if (!dt) { strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M", &network.timeinfo); } else { strftime(timeStringBuff, sizeof(timeStringBuff), "%H %M", &network.timeinfo); } dsp.printClock(timeStringBuff); +#else + dsp.printClock(network.timeinfo, dt, redraw); +#endif dt = !dt; } diff --git a/yoRadio/display.h b/yoRadio/display.h index 38327e1..67e3580 100644 --- a/yoRadio/display.h +++ b/yoRadio/display.h @@ -52,9 +52,10 @@ class Display { void centerText(const char* text, byte y, uint16_t fg, uint16_t bg); void rightText(const char* text, byte y, uint16_t fg, uint16_t bg); void bootString(const char* text, byte y); + void bootLogo(); void station(); void title(const char *str); - void time(); + void time(bool redraw = false); void volume(); void ip(); void swichMode(displayMode_e newmode); diff --git a/yoRadio/network.cpp b/yoRadio/network.cpp index c91cf63..c5a7ce3 100644 --- a/yoRadio/network.cpp +++ b/yoRadio/network.cpp @@ -23,16 +23,17 @@ void Network::begin() { WiFi.mode(WIFI_STA); char buf[40] = { 0 }; while (true) { + display.bootLogo(); Serial.printf("Attempt to connect to %s\n", config.ssids[ls].ssid); snprintf(buf, sizeof(buf) - 1, "ATTEMPT TO %s", config.ssids[ls].ssid); - display.bootString(buf, 110); + display.bootString(buf, 2); WiFi.begin(config.ssids[ls].ssid, config.ssids[ls].password); strcpy(buf, "."); while (WiFi.status() != WL_CONNECTED) { delay(500); digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); strcat(buf, "."); - display.bootString(buf, 90); + display.bootString(buf, 1); errcnt++; if (errcnt > 16) { errcnt = 0; diff --git a/yoRadio/options.h b/yoRadio/options.h index 75449d2..a2835e9 100644 --- a/yoRadio/options.h +++ b/yoRadio/options.h @@ -1,26 +1,36 @@ #ifndef options_h #define options_h -#define VERSION "0.4.323" +#define VERSION "0.5.010" + +/******************************************************* +DO NOT EDIT THIS FILE. +ALL YOUR SETTINGS WILL BE OVERWRITTEN DURING THE UPDATE. +STORE YOUR SETTINGS IN A myoptions.h FILE. +********************************************************/ #if __has_include("myoptions.h") -#include "myoptions.h" // <- write your variable values here +#include "myoptions.h" /* <- write your variable values here */ #endif -/* DISPLAY MODEL - * 0 - DUMMY - * 1 - ST7735 - * 2 - SSD1306 - * 3 - NOKIA5110 - */ +/******************************************************* + +The connection tables are located here https://github.com/e2002/yoradio#connection-tables + +********************************************************/ + +#define DSP_DUMMY 0 // without display +#define DSP_ST7735 1 // https://aliexpress.com/item/1005002822797745.html +#define DSP_SSD1306 2 // https://aliexpress.com/item/1005001621806398.html +#define DSP_NOKIA5110 3 // https://aliexpress.com/item/1005001621837569.html +#define DSP_ST7789 4 // use it with the [#define TFT_INVERT false] option https://aliexpress.com/item/32960241206.html +#define DSP_SH1106 5 // https://aliexpress.com/item/32683094040.html +#define DSP_1602I2C 6 // https://aliexpress.com/item/32305776560.html + #ifndef DSP_MODEL - #define DSP_MODEL 1 + #define DSP_MODEL DSP_DUMMY #endif -/* - * The connection tables are located here https://github.com/e2002/yoradio#connection-tables - */ - /* TFT DISPLAY */ #ifndef TFT_CS #define TFT_CS 5 @@ -98,6 +108,15 @@ #ifndef BTN_INTERNALPULLUP #define BTN_INTERNALPULLUP true #endif +#ifndef BTN_LONGPRESS_LOOP_DELAY + #define BTN_LONGPRESS_LOOP_DELAY 200 // delay between calling DuringLongPress event +#endif +#ifndef BTN_CLICK_TICKS + #define BTN_CLICK_TICKS 300 +#endif +#ifndef BTN_PRESS_TICKS + #define BTN_PRESS_TICKS 500 +#endif /* ESP DEVBOARD */ #ifndef LED_BUILTIN @@ -111,10 +130,12 @@ #ifndef TFT_CONTRAST #define TFT_CONTRAST 55 // Nokia 5110 contrast #endif +#ifndef TFT_INVERT + #define TFT_INVERT true // invert the display colors (usually true) +#endif #ifndef VOL_STEP #define VOL_STEP 1 // Encoder vol step #endif - #ifndef MUTE_PIN #define MUTE_PIN 255 // MUTE Pin #endif @@ -199,5 +220,4 @@ INITR_REDTAB #define IR_CODE_AST 0xFF42BD #endif - #endif diff --git a/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp b/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp new file mode 100644 index 0000000..6a5a091 --- /dev/null +++ b/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp @@ -0,0 +1,323 @@ +// Based on the work by DFRobot +// Based on the LiquidCrystal_I2C library https://github.com/johnrickman/LiquidCrystal_I2C +#include "../../options.h" +#if DSP_MODEL==DSP_1602I2C + +#include "LiquidCrystalI2CEx.h" +#include +#if defined(ARDUINO) && ARDUINO >= 100 + +#include "Arduino.h" + +#define printIIC(args) Wire.write(args) +inline size_t LiquidCrystal_I2C::write(uint8_t value) { + send(value, Rs); + return 1; +} + +#else +#include "WProgram.h" + +#define printIIC(args) Wire.send(args) +inline void LiquidCrystal_I2C::write(uint8_t value) { + send(value, Rs); +} + +#endif +#include "Wire.h" + +// When the display powers up, it is configured as follows: +// +// 1. Display clear +// 2. Function set: +// DL = 1; 8-bit interface data +// N = 0; 1-line display +// F = 0; 5x8 dot character font +// 3. Display on/off control: +// D = 0; Display off +// C = 0; Cursor off +// B = 0; Blinking off +// 4. Entry mode set: +// I/D = 1; Increment by 1 +// S = 0; No shift +// +// Note, however, that resetting the Arduino doesn't reset the LCD, so we +// can't assume that its in that state when a sketch starts (and the +// LiquidCrystal constructor is called). + +LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows, uint8_t sda_pin, uint8_t scl_pin) +{ + _Addr = lcd_Addr; + _cols = lcd_cols; + _rows = lcd_rows; + _sda_pin = sda_pin; + _scl_pin = scl_pin; + _backlightval = LCD_NOBACKLIGHT; +} + +void LiquidCrystal_I2C::init(){ + init_priv(); +} + +void LiquidCrystal_I2C::init_priv() +{ + if((_sda_pin==255 && _scl_pin==255) || (_sda_pin==21 && _scl_pin==22)){ + Wire.begin(); + }else{ + Wire.begin(_sda_pin, _scl_pin, (uint32_t)100000); + } + _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; + begin(_cols, _rows); +} + +void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) { + if (lines > 1) { + _displayfunction |= LCD_2LINE; + } + _numlines = lines; + + // for some 1 line displays you can select a 10 pixel high font + if ((dotsize != 0) && (lines == 1)) { + _displayfunction |= LCD_5x10DOTS; + } + + // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! + // according to datasheet, we need at least 40ms after power rises above 2.7V + // before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50 + delay(50); + + // Now we pull both RS and R/W low to begin commands + expanderWrite(_backlightval); // reset expanderand turn backlight off (Bit 8 =1) + delay(1000); + + //put the LCD into 4 bit mode + // this is according to the hitachi HD44780 datasheet + // figure 24, pg 46 + + // we start in 8bit mode, try to set 4 bit mode + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // second try + write4bits(0x03 << 4); + delayMicroseconds(4500); // wait min 4.1ms + + // third go! + write4bits(0x03 << 4); + delayMicroseconds(150); + + // finally, set to 4-bit interface + write4bits(0x02 << 4); + + + // set # lines, font size, etc. + command(LCD_FUNCTIONSET | _displayfunction); + + // turn the display on with no cursor or blinking default + _displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF; + display(); + + // clear it off + clear(); + + // Initialize to default text direction (for roman languages) + _displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT; + + // set the entry mode + command(LCD_ENTRYMODESET | _displaymode); + + home(); + +} + +/********** high level commands, for the user! */ +void LiquidCrystal_I2C::clear(){ + command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal_I2C::home(){ + command(LCD_RETURNHOME); // set cursor position to zero + delayMicroseconds(2000); // this command takes a long time! +} + +void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row){ + int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 }; + if ( row > _numlines ) { + row = _numlines-1; // we count rows starting w/0 + } + command(LCD_SETDDRAMADDR | (col + row_offsets[row])); +} + +// Turn the display on/off (quickly) +void LiquidCrystal_I2C::noDisplay() { + _displaycontrol &= ~LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::display() { + _displaycontrol |= LCD_DISPLAYON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turns the underline cursor on/off +void LiquidCrystal_I2C::noCursor() { + _displaycontrol &= ~LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::cursor() { + _displaycontrol |= LCD_CURSORON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// Turn on and off the blinking cursor +void LiquidCrystal_I2C::noBlink() { + _displaycontrol &= ~LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} +void LiquidCrystal_I2C::blink() { + _displaycontrol |= LCD_BLINKON; + command(LCD_DISPLAYCONTROL | _displaycontrol); +} + +// These commands scroll the display without changing the RAM +void LiquidCrystal_I2C::scrollDisplayLeft(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT); +} +void LiquidCrystal_I2C::scrollDisplayRight(void) { + command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT); +} + +// This is for text that flows Left to Right +void LiquidCrystal_I2C::leftToRight(void) { + _displaymode |= LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This is for text that flows Right to Left +void LiquidCrystal_I2C::rightToLeft(void) { + _displaymode &= ~LCD_ENTRYLEFT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'right justify' text from the cursor +void LiquidCrystal_I2C::autoscroll(void) { + _displaymode |= LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// This will 'left justify' text from the cursor +void LiquidCrystal_I2C::noAutoscroll(void) { + _displaymode &= ~LCD_ENTRYSHIFTINCREMENT; + command(LCD_ENTRYMODESET | _displaymode); +} + +// Allows us to fill the first 8 CGRAM locations +// with custom characters +void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) { + location &= 0x7; // we only have 8 locations 0-7 + command(LCD_SETCGRAMADDR | (location << 3)); + for (int i=0; i<8; i++) { + write(charmap[i]); + } +} + +// Turn the (optional) backlight off/on +void LiquidCrystal_I2C::noBacklight(void) { + _backlightval=LCD_NOBACKLIGHT; + expanderWrite(0); +} + +void LiquidCrystal_I2C::backlight(void) { + _backlightval=LCD_BACKLIGHT; + expanderWrite(0); +} + + + +/*********** mid level commands, for sending data/cmds */ + +inline void LiquidCrystal_I2C::command(uint8_t value) { + send(value, 0); +} + + +/************ low level data pushing commands **********/ + +// write either command or data +void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) { + uint8_t highnib=value&0xf0; + uint8_t lownib=(value<<4)&0xf0; + write4bits((highnib)|mode); + write4bits((lownib)|mode); +} + +void LiquidCrystal_I2C::write4bits(uint8_t value) { + expanderWrite(value); + pulseEnable(value); +} + +void LiquidCrystal_I2C::expanderWrite(uint8_t _data){ + Wire.beginTransmission(_Addr); + printIIC((int)(_data) | _backlightval); + Wire.endTransmission(); +} + +void LiquidCrystal_I2C::pulseEnable(uint8_t _data){ + expanderWrite(_data | En); // En high + delayMicroseconds(1); // enable pulse must be >450ns + + expanderWrite(_data & ~En); // En low + delayMicroseconds(50); // commands need > 37us to settle +} + + +// Alias functions + +void LiquidCrystal_I2C::cursor_on(){ + cursor(); +} + +void LiquidCrystal_I2C::cursor_off(){ + noCursor(); +} + +void LiquidCrystal_I2C::blink_on(){ + blink(); +} + +void LiquidCrystal_I2C::blink_off(){ + noBlink(); +} + +void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows){ + createChar(char_num, rows); +} + +void LiquidCrystal_I2C::setBacklight(uint8_t new_val){ + if(new_val){ + backlight(); // turn backlight on + }else{ + noBacklight(); // turn backlight off + } +} + +void LiquidCrystal_I2C::printstr(const char c[]){ + //This function is not identical to the function used for "real" I2C displays + //it's here so the user sketch doesn't have to be changed + print(c); +} + + +// unsupported API functions +void LiquidCrystal_I2C::off(){} +void LiquidCrystal_I2C::on(){} +void LiquidCrystal_I2C::setDelay (int cmdDelay,int charDelay) {} +uint8_t LiquidCrystal_I2C::status(){return 0;} +uint8_t LiquidCrystal_I2C::keypad (){return 0;} +uint8_t LiquidCrystal_I2C::init_bargraph(uint8_t graphtype){return 0;} +void LiquidCrystal_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end){} +void LiquidCrystal_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_row_end){} +void LiquidCrystal_I2C::setContrast(uint8_t new_val){} + +#endif // if DSP_MODEL==DSP_1602I2C + diff --git a/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.h b/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.h new file mode 100644 index 0000000..ae27685 --- /dev/null +++ b/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.h @@ -0,0 +1,127 @@ +//YWROBOT +#ifndef LiquidCrystalI2CEx_h +#define LiquidCrystalI2CEx_h + +#include +#include "Print.h" +#include + +// commands +#define LCD_CLEARDISPLAY 0x01 +#define LCD_RETURNHOME 0x02 +#define LCD_ENTRYMODESET 0x04 +#define LCD_DISPLAYCONTROL 0x08 +#define LCD_CURSORSHIFT 0x10 +#define LCD_FUNCTIONSET 0x20 +#define LCD_SETCGRAMADDR 0x40 +#define LCD_SETDDRAMADDR 0x80 + +// flags for display entry mode +#define LCD_ENTRYRIGHT 0x00 +#define LCD_ENTRYLEFT 0x02 +#define LCD_ENTRYSHIFTINCREMENT 0x01 +#define LCD_ENTRYSHIFTDECREMENT 0x00 + +// flags for display on/off control +#define LCD_DISPLAYON 0x04 +#define LCD_DISPLAYOFF 0x00 +#define LCD_CURSORON 0x02 +#define LCD_CURSOROFF 0x00 +#define LCD_BLINKON 0x01 +#define LCD_BLINKOFF 0x00 + +// flags for display/cursor shift +#define LCD_DISPLAYMOVE 0x08 +#define LCD_CURSORMOVE 0x00 +#define LCD_MOVERIGHT 0x04 +#define LCD_MOVELEFT 0x00 + +// flags for function set +#define LCD_8BITMODE 0x10 +#define LCD_4BITMODE 0x00 +#define LCD_2LINE 0x08 +#define LCD_1LINE 0x00 +#define LCD_5x10DOTS 0x04 +#define LCD_5x8DOTS 0x00 + +// flags for backlight control +#define LCD_BACKLIGHT 0x08 +#define LCD_NOBACKLIGHT 0x00 + +#define En B00000100 // Enable bit +#define Rw B00000010 // Read/Write bit +#define Rs B00000001 // Register select bit + +class LiquidCrystal_I2C : public Print { +public: + LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows, uint8_t sda_pin=255, uint8_t scl_pin=255); + void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS ); + void clear(); + void home(); + void noDisplay(); + void display(); + void noBlink(); + void blink(); + void noCursor(); + void cursor(); + void scrollDisplayLeft(); + void scrollDisplayRight(); + void printLeft(); + void printRight(); + void leftToRight(); + void rightToLeft(); + void shiftIncrement(); + void shiftDecrement(); + void noBacklight(); + void backlight(); + void autoscroll(); + void noAutoscroll(); + void createChar(uint8_t, uint8_t[]); + void setCursor(uint8_t, uint8_t); +#if defined(ARDUINO) && ARDUINO >= 100 + virtual size_t write(uint8_t); +#else + virtual void write(uint8_t); +#endif + void command(uint8_t); + void init(); + +////compatibility API function aliases +void blink_on(); // alias for blink() +void blink_off(); // alias for noBlink() +void cursor_on(); // alias for cursor() +void cursor_off(); // alias for noCursor() +void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight() +void load_custom_character(uint8_t char_num, uint8_t *rows); // alias for createChar() +void printstr(const char[]); + +////Unsupported API functions (not implemented in this library) +uint8_t status(); +void setContrast(uint8_t new_val); +uint8_t keypad(); +void setDelay(int,int); +void on(); +void off(); +uint8_t init_bargraph(uint8_t graphtype); +void draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); +void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len, uint8_t pixel_col_end); + + +private: + void init_priv(); + void send(uint8_t, uint8_t); + void write4bits(uint8_t); + void expanderWrite(uint8_t); + void pulseEnable(uint8_t); + uint8_t _Addr; + uint8_t _displayfunction; + uint8_t _displaycontrol; + uint8_t _displaymode; + uint8_t _numlines; + uint8_t _cols; + uint8_t _rows; + uint8_t _backlightval; + uint8_t _sda_pin, _scl_pin; +}; + +#endif diff --git a/yoRadio/src/audioI2S/Audio.cpp b/yoRadio/src/audioI2S/Audio.cpp index 962b59a..7db7eb1 100644 --- a/yoRadio/src/audioI2S/Audio.cpp +++ b/yoRadio/src/audioI2S/Audio.cpp @@ -522,7 +522,7 @@ bool Audio::connecttohost(const char* host, const char* user, const char* pwd) { // strcat(resp, "Transfer-Encoding: \r\n"); // otherwise the server assumes gzip compression strcat(resp, "Connection: keep-alive\r\n\r\n"); - const uint32_t TIMEOUT_MS{250}; + const uint32_t TIMEOUT_MS{350}; uint32_t wtf; if(m_f_ssl == false) { uint32_t t = millis(); diff --git a/yoRadio/src/displays/displayLC1602.cpp b/yoRadio/src/displays/displayLC1602.cpp new file mode 100644 index 0000000..ad72d2b --- /dev/null +++ b/yoRadio/src/displays/displayLC1602.cpp @@ -0,0 +1,284 @@ +#include "../../options.h" + +#if DSP_MODEL==DSP_1602I2C + +#include "displayLC1602.h" +#include "../../player.h" +#include "../../config.h" +#include "../../network.h" + +#ifndef SCREEN_ADDRESS +#define SCREEN_ADDRESS 0x27 ///< See datasheet for Address or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda +#endif + +const byte controlspaces[] = { CLOCK_SPACE, VOL_SPACE }; + +DisplayLC1602::DisplayLC1602(): LiquidCrystal_I2C(SCREEN_ADDRESS, 16, 2, I2C_SDA, I2C_SCL) { + +} + +void DisplayLC1602::apScreen() { + setCursor(0,0); + print("YORADIO AP MODE"); + setCursor(0,1); + print(WiFi.softAPIP().toString().c_str()); +} + +void DisplayLC1602::initD(uint16_t &screenwidth, uint16_t &screenheight) { + init(); + backlight(); + screenwidth = 16; + screenheight = 2; + swidth = screenwidth; + sheight = screenheight; + fillSpaces = true; +} + +void DisplayLC1602::drawLogo() { + +} + +void DisplayLC1602::drawPlaylist(uint16_t currentItem, char* currentItemText) { + centerText("NEXT STATION", 0, 0, 0); + for (byte i = 0; i < PLMITEMS; i++) { + plMenu[i][0] = '\0'; + } + config.fillPlMenu(plMenu, currentItem, PLMITEMS); + for (byte i = 0; i < PLMITEMS; i++) { + strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); + } +} + +void DisplayLC1602::clearDsp() { + clear(); +} + +void DisplayLC1602::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) { + +} + +void DisplayLC1602::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) { + tWidth = strlen(text); + tHeight = 1; + sWidth = strlen(separator); +} + +void DisplayLC1602::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { + for(uint16_t x=0; xswidth)?0:(swidth-strlen(text))/2; + setCursor(x, y); + print(text); +} + +void DisplayLC1602::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) { + byte x=swidth-strlen(text); + setCursor(x-1, y); + print(" "); + setCursor(x, y); + print(text); +} + +void DisplayLC1602::displayHeapForDebug() { + +} + +void DisplayLC1602::printClock(const char* timestr) { + rightText(timestr, 0, 0, 0); +} + +void DisplayLC1602::printClock(struct tm timeinfo, bool dots, bool redraw) { + +} + +void DisplayLC1602::drawVolumeBar(bool withNumber) { + char volstr[4]; + sprintf(volstr, "%02d", config.store.volume); + if (withNumber) {; + centerText(" ", 1, 0, 0); + centerText(volstr, 1, TFT_LOGO, TFT_BG); + }else{ + rightText(" ", 1, 0, 0); + rightText(volstr, 1, TFT_LOGO, TFT_BG); + } +} + +void DisplayLC1602::drawNextStationNum(uint16_t num) { + char numstr[7]; + sprintf(numstr, "%d", num); + centerText(" ", 1, 0, 0); + centerText(numstr, 1, TFT_LOGO, TFT_BG); +} + +void DisplayLC1602::frameTitle(const char* str) { + centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG); +} + +void DisplayLC1602::rssi(const char* str) { + +} + +void DisplayLC1602::ip(const char* str) { + +} + +void DisplayLC1602::set_TextSize(uint8_t s) { + +} + +void DisplayLC1602::set_TextColor(uint16_t fg, uint16_t bg) { + +} + +void DisplayLC1602::set_Cursor(int16_t x, int16_t y) { + if(x<0) { + xOffset=-x; + x=0; + }else{ + xOffset=0; + } + nextX=0; + yOffset = y; + setCursor(x, y); +} + +void DisplayLC1602::printText(const char* txt) { + char tmp[swidth+1] = {0}; + int16_t numchars = fillSpaces?swidth-controlspaces[yOffset]:swidth; + strlcpy(tmp, txt+xOffset, numchars+1-nextX); + print(tmp); + xOffset=(int16_t)(strlen(txt)-xOffset)<=0?xOffset-strlen(txt):0; + nextX=nextX+strlen(tmp); + if(nextX>numchars) nextX=numchars; + setCursor(nextX, yOffset); +} + +void DisplayLC1602::loop() { + if (checkdelay(SCROLLTIME, loopdelay)) { + //display(); + } + yield(); +} + +boolean DisplayLC1602::checkdelay(int m, unsigned long & tstamp) { + if (millis() - tstamp > m) { + tstamp = millis(); + return true; + } else { + return false; + } +} + +char* DisplayLC1602::utf8Rus(const char* str, bool uppercase) { + int index = 0; + static char strn[BUFLEN]; + static char newStr[BUFLEN]; + bool E = false; + strlcpy(strn, str, BUFLEN); + newStr[0] = '\0'; + bool next = false; + for (char *iter = strn; *iter != '\0'; ++iter) + { + if (E) { + E = false; + continue; + } + byte rus = (byte) * iter; + if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли + *iter = (char)209; + *(iter + 1) = (char)145; + E = true; + continue; + } + if (rus == 209 && (byte) * (iter + 1) == 145) { + *iter = (char)209; + *(iter + 1) = (char)145; + E = true; + continue; + } + if (next) { + if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32); + if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32); + next = false; + } + if (rus == 208) next = true; + if (rus == 209) { + *iter = (char)208; + next = true; + } + *iter = toupper(*iter); + } + + while (strn[index]) + { + if (strlen(newStr) > BUFLEN - 2) break; + if (strn[index] >= 0xBF) + { + switch (strn[index]) { + case 0xD0: { + switch (strn[index + 1]) + { + case 0x90: strcat(newStr, "A"); break; + case 0x91: strcat(newStr, "B"); break; + case 0x92: strcat(newStr, "V"); break; + case 0x93: strcat(newStr, "G"); break; + case 0x94: strcat(newStr, "D"); break; + case 0x95: strcat(newStr, "E"); break; + case 0x96: strcat(newStr, "ZH"); break; + case 0x97: strcat(newStr, "Z"); break; + case 0x98: strcat(newStr, "I"); break; + case 0x99: strcat(newStr, "Y"); break; + case 0x9A: strcat(newStr, "K"); break; + case 0x9B: strcat(newStr, "L"); break; + case 0x9C: strcat(newStr, "M"); break; + case 0x9D: strcat(newStr, "N"); break; + case 0x9E: strcat(newStr, "O"); break; + case 0x9F: strcat(newStr, "P"); break; + case 0xA0: strcat(newStr, "R"); break; + case 0xA1: strcat(newStr, "S"); break; + case 0xA2: strcat(newStr, "T"); break; + case 0xA3: strcat(newStr, "U"); break; + case 0xA4: strcat(newStr, "F"); break; + case 0xA5: strcat(newStr, "H"); break; + case 0xA6: strcat(newStr, "TS"); break; + case 0xA7: strcat(newStr, "CH"); break; + case 0xA8: strcat(newStr, "SH"); break; + case 0xA9: strcat(newStr, "SHCH"); break; + case 0xAA: strcat(newStr, "'"); break; + case 0xAB: strcat(newStr, "YU"); break; + case 0xAC: strcat(newStr, "'"); break; + case 0xAD: strcat(newStr, "E"); break; + case 0xAE: strcat(newStr, "YU"); break; + case 0xAF: strcat(newStr, "YA"); break; + } + break; + } + case 0xD1: { + if (strn[index + 1] == 0x91) { + strcat(newStr, "YO"); break; + break; + } + break; + } + } + int sind = index + 2; + while (strn[sind]) { + strn[sind - 1] = strn[sind]; + sind++; + } + strn[sind - 1] = 0; + } else { + char Temp[2] = {(char) strn[index] , 0 } ; + strcat(newStr, Temp); + } + index++; + } + return newStr; +} + +#endif diff --git a/yoRadio/src/displays/displayLC1602.h b/yoRadio/src/displays/displayLC1602.h new file mode 100644 index 0000000..f2c0eb5 --- /dev/null +++ b/yoRadio/src/displays/displayLC1602.h @@ -0,0 +1,73 @@ +#ifndef displayLC1602_h +#define displayLC1602_h + +#include "Arduino.h" +#include "../LiquidCrystalI2C/LiquidCrystalI2CEx.h" + +#define TFT_LINEHGHT 1 +#define TFT_FRAMEWDT 0 + +#define PLMITEMS 1 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 9 +#define TITLE_TOP1 1 +#define TITLE_SIZE2 0 +#define PL_TOP 1 +#define PLCURRENT_SIZE 1 + +#define SCROLLDELTA 1 +#define SCROLLTIME 250 +#define BOOTSTR_TOP2 0 +#define BOOTSTR_TOP1 1 +#define STARTTIME_PL 2000 + +class DisplayLC1602: public LiquidCrystal_I2C { + public: + bool fillSpaces; + DisplayLC1602(); + char plMenu[PLMITEMS][PLMITEMLENGHT]; + uint16_t clockY; + void initD(uint16_t &screenwidth, uint16_t &screenheight); + void apScreen(); + void drawLogo(); + void clearDsp(); + void centerText(const char* text, byte y, uint16_t fg, uint16_t bg); + void rightText(const char* text, byte y, uint16_t fg, uint16_t bg); + void set_TextSize(uint8_t s); + void set_TextColor(uint16_t fg, uint16_t bg); + void set_Cursor(int16_t x, int16_t y); + void printText(const char* txt); + void printClock(const char* timestr); + void printClock(struct tm timeinfo, bool dots, bool redraw = false); + void displayHeapForDebug(); + void drawVolumeBar(bool withNumber); + void drawNextStationNum(uint16_t num); + char* utf8Rus(const char* str, bool uppercase=true); + void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg); + void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth); + void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg); + void frameTitle(const char* str); + void rssi(const char* str); + void ip(const char* str); + void drawPlaylist(uint16_t currentItem, char* currentItemText); + void loop(); + private: + uint16_t swidth, sheight, xOffset, yOffset; + int16_t nextX; + unsigned long loopdelay; + boolean checkdelay(int m, unsigned long &tstamp); +}; + +extern DisplayLC1602 dsp; + +/* + * TFT COLORS + */ +#define CLOCK_SPACE 6 +#define VOL_SPACE 3 +#define SILVER 0 +#define TFT_BG 0 +#define TFT_FG CLOCK_SPACE +#define TFT_LOGO VOL_SPACE + +#endif diff --git a/yoRadio/src/displays/displayN5110.cpp b/yoRadio/src/displays/displayN5110.cpp index 337cdc5..6b68002 100644 --- a/yoRadio/src/displays/displayN5110.cpp +++ b/yoRadio/src/displays/displayN5110.cpp @@ -122,7 +122,10 @@ void DisplayN5110::initD(uint16_t &screenwidth, uint16_t &screenheight) { setContrast(TFT_CONTRAST); cp437(true); fillScreen(TFT_BG); - setRotation(TFT_ROTATE); + byte tftRotate = TFT_ROTATE; + if(tftRotate>2) tftRotate=2; + if(tftRotate==1) tftRotate=0; + setRotation(tftRotate); setTextWrap(false); screenwidth = width(); screenheight = height(); diff --git a/yoRadio/src/displays/displayN5110.h b/yoRadio/src/displays/displayN5110.h index cbc32d8..6922ddd 100644 --- a/yoRadio/src/displays/displayN5110.h +++ b/yoRadio/src/displays/displayN5110.h @@ -11,6 +11,14 @@ #define TFT_LINEHGHT 8 #define TFT_FRAMEWDT 0 +#define SCROLLDELTA 8 +#define SCROLLTIME 332 +#define META_SIZE 1 +#define TITLE_TOP1 TFT_FRAMEWDT + TFT_LINEHGHT+1 +#define TITLE_SIZE2 0 +#define PLCURRENT_SIZE 1 + + #define PLMITEMS 7 #define PLMITEMLENGHT 40 #define PLMITEMHEIGHT 10 diff --git a/yoRadio/src/displays/displaySH1106.cpp b/yoRadio/src/displays/displaySH1106.cpp new file mode 100644 index 0000000..c432df1 --- /dev/null +++ b/yoRadio/src/displays/displaySH1106.cpp @@ -0,0 +1,333 @@ +#include "../../options.h" +#if DSP_MODEL==5 + +#include "displaySH1106.h" +#include +#include "../../player.h" +#include "../../config.h" +#include "../../network.h" + +#ifndef SCREEN_ADDRESS +#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda +#endif + +#define LOGO_WIDTH 21 +#define LOGO_HEIGHT 32 + +const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"}; + +const unsigned char logo [] PROGMEM= +{ + 0x06, 0x03, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x8f, 0xc0, 0x1f, 0x8f, 0xc0, + 0x0f, 0x07, 0x80, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x03, 0xff, 0x00, 0x0f, 0xff, 0x80, + 0x1f, 0xff, 0xc0, 0x1f, 0xff, 0xc0, 0x3f, 0x8f, 0xe0, 0x7e, 0x03, 0xf0, + 0x7c, 0x01, 0xf0, 0x7c, 0x01, 0xf0, 0x7f, 0xff, 0xf0, 0xff, 0xff, 0xf8, + 0xff, 0xff, 0xf8, 0xff, 0xff, 0xf8, 0x7c, 0x00, 0x00, 0x7c, 0x00, 0x00, + 0x7e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x3f, 0xc0, 0xe0, 0x3f, 0xff, 0xe0, + 0x1f, 0xff, 0xe0, 0x0f, 0xff, 0xe0, 0x03, 0xff, 0xc0, 0x00, 0xfe, 0x00 +}; + +TwoWire I2CSH1106 = TwoWire(0); + +DisplaySH1106::DisplaySH1106(): Adafruit_SH1106G(128, 64, &I2CSH1106, -1) { + +} + +char* DisplaySH1106::utf8Rus(const char* str, bool uppercase) { + int index = 0; + static char strn[BUFLEN]; + bool E = false; + strlcpy(strn, str, BUFLEN); + if (uppercase) { + bool next = false; + for (char *iter = strn; *iter != '\0'; ++iter) + { + if (E) { + E = false; + continue; + } + byte rus = (byte) * iter; + if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли + *iter = (char)209; + *(iter + 1) = (char)145; + E = true; + continue; + } + if (rus == 209 && (byte) * (iter + 1) == 145) { + *iter = (char)209; + *(iter + 1) = (char)145; + E = true; + continue; + } + if (next) { + if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32); + if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32); + next = false; + } + if (rus == 208) next = true; + if (rus == 209) { + *iter = (char)208; + next = true; + } + *iter = toupper(*iter); + } + } + while (strn[index]) + { + if (strn[index] >= 0xBF) + { + switch (strn[index]) { + case 0xD0: { + if (strn[index + 1] == 0x81) { + strn[index] = 0xA8; + break; + } + if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30; + break; + } + case 0xD1: { + if (strn[index + 1] == 0x91) { + //strn[index] = 0xB7; + strn[index] = 0xB8; + break; + } + if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70; + break; + } + } + int sind = index + 2; + while (strn[sind]) { + strn[sind - 1] = strn[sind]; + sind++; + } + strn[sind - 1] = 0; + } + index++; + } + return strn; +} + +void DisplaySH1106::apScreen() { + setTextSize(1); + setTextColor(TFT_FG, TFT_BG); + setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 2 * TFT_LINEHGHT); + print("AP NAME: "); + print(apSsid); + setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 3 * TFT_LINEHGHT); + print("PASSWORD: "); + print(apPassword); + setTextColor(SILVER, TFT_BG); + setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT * 2); + print("SETTINGS PAGE ON: "); + setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT); + print("http://"); + print(WiFi.softAPIP().toString().c_str()); + print("/"); +} + +void DisplaySH1106::initD(uint16_t &screenwidth, uint16_t &screenheight) { + I2CSH1106.begin(I2C_SDA, I2C_SCL, (uint32_t)100000); + if (!begin(SCREEN_ADDRESS, true)) { + Serial.println(F("SH1106 allocation failed")); + for (;;); // Don't proceed, loop forever + } + cp437(true); + fillScreen(TFT_BG); + setRotation(TFT_ROTATE); + setTextWrap(false); + screenwidth = width(); + screenheight = height(); + swidth = screenwidth; + sheight = screenheight; +} + +void DisplaySH1106::drawLogo() { + clearDisplay(); + drawBitmap( + (width() - LOGO_WIDTH ) / 2, + 8, + logo, LOGO_WIDTH, LOGO_HEIGHT, 1); + display(); +} + +void DisplaySH1106::drawPlaylist(uint16_t currentItem, char* currentItemText) { + for (byte i = 0; i < PLMITEMS; i++) { + plMenu[i][0] = '\0'; + } + config.fillPlMenu(plMenu, currentItem - 3, PLMITEMS); + setTextSize(1); + int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3; + fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) + 1, swidth, PLMITEMHEIGHT, TFT_LOGO); + setTextColor(TFT_FG, TFT_BG); + for (byte i = 0; i < PLMITEMS; i++) { + if (i == 3) { + strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); + } else { + setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); + print(utf8Rus(plMenu[i], true)); + } + } +} + +void DisplaySH1106::clearDsp() { + fillScreen(TFT_BG); +} + +void DisplaySH1106::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) { + if (TFT_FRAMEWDT == 0) return; + fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg); + fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg); +} + +void DisplaySH1106::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) { + int16_t x1, y1; + uint16_t w, h; + setTextSize(textsize); + getTextBounds(text, 0, 0, &x1, &y1, &w, &h); + tWidth = w; + tHeight = h; + getTextBounds(separator, 0, 0, &x1, &y1, &w, &h); + sWidth = w; +} + +void DisplaySH1106::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { + fillRect(0, texttop, swidth, textheight, bg); +} + +void DisplaySH1106::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) { + int16_t x1, y1; + uint16_t w, h; + const char* txt = text; + getTextBounds(txt, 0, 0, &x1, &y1, &w, &h); + setTextColor(fg,bg); + if(y==90) y=sheight-TFT_LINEHGHT*2-5; + if(y==110) y=sheight-TFT_LINEHGHT; + setCursor((swidth - w) / 2, y); + fillRect(0, y, swidth, h, bg); + print(txt); +} + +void DisplaySH1106::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) { + int16_t x1, y1; + uint16_t w, h; + getTextBounds(text, 0, 0, &x1, &y1, &w, &h); + setTextColor(fg,bg); + setCursor(swidth - w - TFT_FRAMEWDT, y); + fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg); + print(text); +} + +void DisplaySH1106::displayHeapForDebug() { + +} + +void DisplaySH1106::printClock(const char* timestr) { + setTextSize(2); + centerText(timestr, 34, TFT_FG, TFT_BG); + setTextSize(1); +} + +#define CLCLF 34 + +void DisplaySH1106::printClock(struct tm timeinfo, bool dots, bool redraw) { + char timeStringBuff[20] = { 0 }; + strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M", &timeinfo); + setTextSize(2); + setCursor(CLCLF, 34); + setTextColor(TFT_FG, TFT_BG); + print(timeStringBuff); + setTextSize(1); + setCursor(CLCLF + 6*2*5+1, 34+1); + sprintf(timeStringBuff, "%02d", timeinfo.tm_sec); + print(timeStringBuff); +} + +void DisplaySH1106::drawVolumeBar(bool withNumber) { + int16_t vTop = sheight - 4; + int16_t vWidth = swidth; + uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2); + fillRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_BG); + drawRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_LOGO); + fillRect(TFT_FRAMEWDT + 1, vTop + 1, ww, 1, TFT_LOGO); + if (withNumber) { + setTextSize(2); + setTextColor(TFT_FG); + char volstr[4]; + uint16_t wv, hv; + int16_t x1, y1; + sprintf(volstr, "%d", config.store.volume); + getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv); + fillRect(TFT_FRAMEWDT, 24, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, 24); + print(volstr); + } +} + +void DisplaySH1106::drawNextStationNum(uint16_t num) { + setTextSize(2); + setTextColor(TFT_FG); + char numstr[7]; + uint16_t wv, hv; + int16_t x1, y1; + sprintf(numstr, "%d", num); + getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv); + fillRect(TFT_FRAMEWDT, 24, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, 24); + print(numstr); +} + +void DisplaySH1106::frameTitle(const char* str) { + setTextSize(2); + centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG); +} + +void DisplaySH1106::rssi(const char* str) { + char buf[4]; + strlcpy(buf, str, strlen(str)-2); + int16_t vTop = sheight - TFT_LINEHGHT - 4; + setTextSize(1); + rightText(buf, vTop, SILVER, TFT_BG); +} + +void DisplaySH1106::ip(const char* str) { + int16_t vTop = sheight - TFT_LINEHGHT - 4; + setTextSize(1); + setTextColor(SILVER, TFT_BG); + setCursor(0, vTop); + print(str); +} + +void DisplaySH1106::set_TextSize(uint8_t s) { + setTextSize(s); +} + +void DisplaySH1106::set_TextColor(uint16_t fg, uint16_t bg) { + setTextColor(fg, bg); +} + +void DisplaySH1106::set_Cursor(int16_t x, int16_t y) { + setCursor(x, y); +} + +void DisplaySH1106::printText(const char* txt) { + print(txt); +} + +void DisplaySH1106::loop() { + if (checkdelay(SCROLLTIME, loopdelay)) { + display(); + } + yield(); +} + +boolean DisplaySH1106::checkdelay(int m, unsigned long &tstamp) { + if (millis() - tstamp > m) { + tstamp = millis(); + return true; + } else { + return false; + } +} + +#endif diff --git a/yoRadio/src/displays/displaySH1106.h b/yoRadio/src/displays/displaySH1106.h new file mode 100644 index 0000000..f9e0f8d --- /dev/null +++ b/yoRadio/src/displays/displaySH1106.h @@ -0,0 +1,65 @@ +#ifndef displaySH1106_h +#define displaySH1106_h + +#include "Arduino.h" +#include +#include + +#define TFT_LINEHGHT 8 +#define TFT_FRAMEWDT 0 + +#define PLMITEMS 7 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 9 +#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT +#define PLCURRENT_SIZE 1 +#define TFT_FULLTIME 1 +#define SCROLLDELTA 5 +#define SCROLLTIME 110 + +class DisplaySH1106: public Adafruit_SH1106G { + public: + DisplaySH1106(); + char plMenu[PLMITEMS][PLMITEMLENGHT]; + uint16_t clockY; + void initD(uint16_t &screenwidth, uint16_t &screenheight); + void apScreen(); + void drawLogo(); + void clearDsp(); + void centerText(const char* text, byte y, uint16_t fg, uint16_t bg); + void rightText(const char* text, byte y, uint16_t fg, uint16_t bg); + void set_TextSize(uint8_t s); + void set_TextColor(uint16_t fg, uint16_t bg); + void set_Cursor(int16_t x, int16_t y); + void printText(const char* txt); + void printClock(const char* timestr); + void printClock(struct tm timeinfo, bool dots, bool redraw = false); + void displayHeapForDebug(); + void drawVolumeBar(bool withNumber); + void drawNextStationNum(uint16_t num); + char* utf8Rus(const char* str, bool uppercase); + void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg); + void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth); + void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg); + void frameTitle(const char* str); + void rssi(const char* str); + void ip(const char* str); + void drawPlaylist(uint16_t currentItem, char* currentItemText); + void loop(); + private: + uint16_t swidth, sheight; + unsigned long loopdelay; + boolean checkdelay(int m, unsigned long &tstamp); +}; + +extern DisplaySH1106 dsp; + +/* + * TFT COLORS + */ +#define SILVER SH110X_WHITE +#define TFT_BG SH110X_BLACK +#define TFT_FG SH110X_WHITE +#define TFT_LOGO SH110X_WHITE + +#endif diff --git a/yoRadio/src/displays/displaySSD1306.cpp b/yoRadio/src/displays/displaySSD1306.cpp index 83f3166..21ad9b7 100644 --- a/yoRadio/src/displays/displaySSD1306.cpp +++ b/yoRadio/src/displays/displaySSD1306.cpp @@ -7,7 +7,9 @@ #include "../../config.h" #include "../../network.h" -#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 +#ifndef SCREEN_ADDRESS +#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda +#endif #define LOGO_WIDTH 21 #define LOGO_HEIGHT 32 @@ -123,7 +125,7 @@ void DisplaySSD1306::apScreen() { } void DisplaySSD1306::initD(uint16_t &screenwidth, uint16_t &screenheight) { - I2CSSD1306.begin(I2C_SDA, I2C_SCL, (uint32_t)400000); + I2CSSD1306.begin(I2C_SDA, I2C_SCL, (uint32_t)100000); if (!begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for (;;); // Don't proceed, loop forever @@ -151,13 +153,13 @@ void DisplaySSD1306::drawPlaylist(uint16_t currentItem, char* currentItemText) { for (byte i = 0; i < PLMITEMS; i++) { plMenu[i][0] = '\0'; } - config.fillPlMenu(plMenu, currentItem - 2, PLMITEMS); - setTextSize(2); + config.fillPlMenu(plMenu, currentItem - 3, PLMITEMS); + setTextSize(1); int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3; fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) + 1, swidth, PLMITEMHEIGHT, TFT_LOGO); setTextColor(TFT_FG, TFT_BG); for (byte i = 0; i < PLMITEMS; i++) { - if (i == 2) { + if (i == 3) { strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); } else { setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); diff --git a/yoRadio/src/displays/displaySSD1306.h b/yoRadio/src/displays/displaySSD1306.h index db30f9c..a84a10b 100644 --- a/yoRadio/src/displays/displaySSD1306.h +++ b/yoRadio/src/displays/displaySSD1306.h @@ -8,9 +8,11 @@ #define TFT_LINEHGHT 8 #define TFT_FRAMEWDT 0 -#define PLMITEMS 5 +#define PLMITEMS 7 #define PLMITEMLENGHT 40 -#define PLMITEMHEIGHT 18 +#define PLMITEMHEIGHT 9 +#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT +#define PLCURRENT_SIZE 1 class DisplaySSD1306: public Adafruit_SSD1306 { public: diff --git a/yoRadio/src/displays/displayST7735.h b/yoRadio/src/displays/displayST7735.h index de45f66..d8079c8 100644 --- a/yoRadio/src/displays/displayST7735.h +++ b/yoRadio/src/displays/displayST7735.h @@ -12,6 +12,7 @@ #define PLMITEMS 7 #define PLMITEMLENGHT 40 #define PLMITEMHEIGHT 22 +#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT class DisplayST7735: public Adafruit_ST7735 { public: diff --git a/yoRadio/src/displays/displayST7789.cpp b/yoRadio/src/displays/displayST7789.cpp new file mode 100644 index 0000000..aff82c2 --- /dev/null +++ b/yoRadio/src/displays/displayST7789.cpp @@ -0,0 +1,358 @@ +#include "../../options.h" +#if DSP_MODEL==4 + +#include "displayST7789.h" +#include +#include "fonts/bootlogo.h" +#include "../../player.h" +#include "../../config.h" +#include "../../network.h" + +const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"}; +const char *mnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"}; + +DisplayST7789::DisplayST7789(): Adafruit_ST7789(&SPI, TFT_CS, TFT_DC, TFT_RST) { + +} + +char* DisplayST7789::utf8Rus(const char* str, bool uppercase) { + int index = 0; + static char strn[BUFLEN]; + bool E = false; + strlcpy(strn, str, BUFLEN); + if (uppercase) { + bool next = false; + for (char *iter = strn; *iter != '\0'; ++iter) + { + if (E) { + E = false; + continue; + } + byte rus = (byte) * iter; + if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли + *iter = (char)209; + *(iter + 1) = (char)145; + E = true; + continue; + } + if (rus == 209 && (byte) * (iter + 1) == 145) { + *iter = (char)209; + *(iter + 1) = (char)145; + E = true; + continue; + } + if (next) { + if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32); + if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32); + next = false; + } + if (rus == 208) next = true; + if (rus == 209) { + *iter = (char)208; + next = true; + } + *iter = toupper(*iter); + } + } + while (strn[index]) + { + if (strn[index] >= 0xBF) + { + switch (strn[index]) { + case 0xD0: { + if (strn[index + 1] == 0x81) { + strn[index] = 0xA8; + break; + } + if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30; + break; + } + case 0xD1: { + if (strn[index + 1] == 0x91) { + //strn[index] = 0xB7; + strn[index] = 0xB8; + break; + } + if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70; + break; + } + } + int sind = index + 2; + while (strn[sind]) { + strn[sind - 1] = strn[sind]; + sind++; + } + strn[sind - 1] = 0; + } + index++; + } + return strn; +} + +void DisplayST7789::apScreen() { + setTextSize(TITLE_SIZE1); + setTextColor(TFT_FG, TFT_BG); + setCursor(TFT_FRAMEWDT, TITLE_TOP1); + print("AP NAME: "); + print(apSsid); + setCursor(TFT_FRAMEWDT, TITLE_TOP2); + print("PASSWORD: "); + print(apPassword); + setTextColor(SILVER, TFT_BG); + setCursor(TFT_FRAMEWDT, sheight-TFT_FRAMEWDT-TFT_LINEHGHT*4); + print("SETTINGS PAGE ON: "); + setCursor(TFT_FRAMEWDT, sheight-TFT_FRAMEWDT-TFT_LINEHGHT*2); + print("http://"); + print(WiFi.softAPIP().toString().c_str()); + print("/"); + drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, SILVER); +} + +void DisplayST7789::initD(uint16_t &screenwidth, uint16_t &screenheight) { + init(240,320); + invertDisplay(TFT_INVERT); + cp437(true); + fillScreen(TFT_BG); + setRotation(TFT_ROTATE); + setTextWrap(false); + setTextSize(1); + screenwidth = width(); + screenheight = height(); + swidth = screenwidth; + sheight = screenheight; +} + +void DisplayST7789::drawLogo() { + drawRGBBitmap((swidth - 99) / 2, (sheight-64)/2 - TFT_LINEHGHT*2, bootlogo2, 99, 64); +} + +// http://greekgeeks.net/#maker-tools_convertColor +uint16_t iclrs[] = { 0x738E /*707070*/, 0x52AA /*575757*/, 0x39C7, 0x18E3 }; +void DisplayST7789::drawPlaylist(uint16_t currentItem, char* currentItemText) { + for (byte i = 0; i < PLMITEMS; i++) { + plMenu[i][0] = '\0'; + } + config.fillPlMenu(plMenu, currentItem - 4, PLMITEMS); + setTextSize(2); + int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3; + fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) - 1, swidth, PLMITEMHEIGHT + 2, TFT_LOGO); + for (byte i = 0; i < PLMITEMS; i++) { + if (i == 4) { + strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); + } else { + setTextColor(iclrs[abs(i - 4)-1], TFT_BG); + setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); + print(utf8Rus(plMenu[i], true)); + } + } +} + +void DisplayST7789::clearDsp() { + fillScreen(TFT_BG); +} + +void DisplayST7789::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) { + if (TFT_FRAMEWDT==0) return; + fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg); + fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg); +} + +void DisplayST7789::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) { + int16_t x1, y1; + uint16_t w, h; + setTextSize(textsize); + getTextBounds(text, 0, 0, &x1, &y1, &w, &h); + tWidth = w; + tHeight = h; + getTextBounds(separator, 0, 0, &x1, &y1, &w, &h); + sWidth = w; +} + +void DisplayST7789::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { + fillRect(0, texttop, swidth, textheight, bg); +} + +void DisplayST7789::centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg) { + int16_t x1, y1; + uint16_t w, h; + const char* txt = text; + if(y==90) y=(sheight-64)/2 + 64 + TFT_LINEHGHT; + if(y==110) y=(sheight-64)/2 + 64 + TFT_LINEHGHT*3; + getTextBounds(txt, 0, 0, &x1, &y1, &w, &h); + setTextColor(fg); + setCursor((swidth - w) / 2, y); + fillRect((swidth-w)/2-5, y, w+10, h, bg); + print(txt); +} + +void DisplayST7789::rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, bool fliprect, uint16_t delta) { + int16_t x1, y1; + uint16_t w, h; + getTextBounds(text, 0, 0, &x1, &y1, &w, &h); + setTextColor(fg,bg); + setCursor(swidth - w - TFT_FRAMEWDT - delta, y); + fillRect(swidth - w - TFT_FRAMEWDT, fliprect?y-h:y, w, h, bg); + print(text); +} + +void DisplayST7789::displayHeapForDebug() { + int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2; + setTextSize(1); + setTextColor(DARK_GRAY, TFT_BG); + setCursor(TFT_FRAMEWDT, vTop); + fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, TFT_BG); + print(ESP.getFreeHeap()); + print(" / "); + print(ESP.getMaxAllocHeap()); +#if VS1053_CS==255 + // audio buffer; + fillRect(0, sheight - 2, swidth, 2, TFT_BG); + int astored = player.inBufferFilled(); + int afree = player.inBufferFree(); + int aprcnt = 100 * astored / (astored + afree); + byte sbw = map(aprcnt, 0, 100 , 0, swidth); + fillRect(0, sheight - 2, sbw, 2, SILVER); +#endif +} + +void DisplayST7789::printClock(const char* timestr) { + +} + +uint16_t cltop = 0; +uint8_t clsp = 24; +uint16_t clleft = 0; +uint16_t clwidth = 0; + +void DisplayST7789::printClock(struct tm timeinfo, bool dots, bool redraw){ + char timeBuf[50] = { 0 }; + strftime(timeBuf, sizeof(timeBuf), "%H:%M", &timeinfo); + if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){ + int16_t x1, y1; + setTextSize(1); + setFont(&DS_DIGI42pt7b); + getTextBounds(oldTimeBuf, 0, 0, &x1, &y1, &wot, &hot); + if(cltop==0){ + cltop=sheight-(TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 38) - hot; + } + clwidth = wot+clsp+(swidth>240?46:34); + fillRect(swidth-TFT_FRAMEWDT-clwidth, cltop-hot, clwidth, hot+3, TFT_BG); + strlcpy(oldTimeBuf, timeBuf, 20); + getTextBounds(timeBuf, 0, 0, &x1, &y1, &wot, &hot); + clwidth = wot+clsp+(swidth>240?46:34); + clleft=swidth-TFT_FRAMEWDT-clwidth; + setTextColor(TFT_LOGO, TFT_BG); + setCursor(clleft, cltop); + print(timeBuf); + + setFont(); + setTextSize(3); + setTextColor(TFT_FG, TFT_BG); + setCursor(clleft+wot+clsp, cltop-hot+32); + print(utf8Rus(dow[timeinfo.tm_wday], false)); + + sprintf(timeBuf, "%2d %s %d", timeinfo.tm_mday,mnths[timeinfo.tm_mon], timeinfo.tm_year+1900); + setTextSize(1); + rightText(utf8Rus(timeBuf,true), cltop+10, TFT_FG, TFT_BG, false, swidth>240?12:0); + drawFastVLine(clleft+wot+clsp/2+3, cltop-hot, hot+3, SILVER); + drawFastHLine(clleft+wot+clsp/2+3, cltop-hot+29, 42, SILVER); + + drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, SILVER); + } + setTextSize(3); + setTextColor(TFT_LOGO, TFT_BG); + setCursor(clleft+wot+clsp, cltop-hot+1); + sprintf(timeBuf, "%02d", timeinfo.tm_sec); + print(timeBuf); +} + +void DisplayST7789::drawVolumeBar(bool withNumber) { + int16_t vTop = sheight - TFT_FRAMEWDT * 2; + int16_t volTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2; + int16_t vWidth = swidth - TFT_FRAMEWDT *2; + uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2); + fillRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, TFT_BG); + drawRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, TFT_LOGO); + fillRect(TFT_FRAMEWDT + 1, vTop - 1, ww, 5, TFT_LOGO); + if(swidth>240){ + char buf[20]; + sprintf(buf, "VOL %d", config.store.volume); + setTextSize(1); + centerText(buf, volTop, SILVER, TFT_BG); + } + if (withNumber) { + setTextSize(1); + setTextColor(TFT_FG); + setFont(&DS_DIGI42pt7b); + char volstr[4]; + uint16_t wv, hv; + int16_t x1, y1; + sprintf(volstr, "%d", config.store.volume); + getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv); + fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv); + print(volstr); + setFont(); + } +} + +void DisplayST7789::drawNextStationNum(uint16_t num) { + setTextSize(1); + setTextColor(TFT_FG); + setFont(&DS_DIGI42pt7b); + char numstr[7]; + uint16_t wv, hv; + int16_t x1, y1; + sprintf(numstr, "%d", num); + getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv); + fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv); + print(numstr); + setFont(); +} + +void DisplayST7789::frameTitle(const char* str) { + setTextSize(META_SIZE); + centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG); + drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, SILVER); +} + +void DisplayST7789::rssi(const char* str) { + int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2; + char buf[20]; + sprintf(buf, "RSSI:%s", str); + setTextSize(1); + rightText(buf, vTop, SILVER, TFT_BG); +} + +void DisplayST7789::ip(const char* str) { + int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2; + char buf[30]; + sprintf(buf, "IP: %s", str); + setTextSize(1); + setTextColor(SILVER, TFT_BG); + setCursor(TFT_FRAMEWDT, vTop); + print(buf); +} + +void DisplayST7789::set_TextSize(uint8_t s) { + setTextSize(s); +} + +void DisplayST7789::set_TextColor(uint16_t fg, uint16_t bg) { + setTextColor(fg, bg); +} + +void DisplayST7789::set_Cursor(int16_t x, int16_t y) { + setCursor(x, y); +} + +void DisplayST7789::printText(const char* txt) { + print(txt); +} + +void DisplayST7789::loop() { + +} + +#endif diff --git a/yoRadio/src/displays/displayST7789.h b/yoRadio/src/displays/displayST7789.h new file mode 100644 index 0000000..1e18a05 --- /dev/null +++ b/yoRadio/src/displays/displayST7789.h @@ -0,0 +1,98 @@ +#ifndef displayST7789_h +#define displayST7789_h + +#include "Arduino.h" +#include +#include +// https://tchapi.github.io/Adafruit-GFX-Font-Customiser/ +#include "fonts/DS_DIGI42pt7b.h" + +#define TFT_LINEHGHT 10 +#define TFT_FRAMEWDT 8 +#define META_SIZE 3 +#define TITLE_SIZE1 2 +#define TITLE_SIZE2 2 + +#define SCROLLDELTA 6 +#define SCROLLTIME 83 + +#define PLMITEMS 9 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 22 +#define TFT_FULLTIME 1 + +#define TITLE_TOP1 TFT_FRAMEWDT + META_SIZE * TFT_LINEHGHT + 8 +#define TITLE_TOP2 TFT_FRAMEWDT + (META_SIZE+2) * TFT_LINEHGHT + 8 +#define TITLE_FG2 SILVER + +class DisplayST7789: public Adafruit_ST7789 { + public: + DisplayST7789(); + char plMenu[PLMITEMS][PLMITEMLENGHT]; + uint16_t clockY; + void initD(uint16_t &screenwidth, uint16_t &screenheight); + void apScreen(); + void drawLogo(); + void clearDsp(); + void centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg); + void rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, bool fliprect=false, uint16_t delta = 0); + void set_TextSize(uint8_t s); + void set_TextColor(uint16_t fg, uint16_t bg); + void set_Cursor(int16_t x, int16_t y); + void printText(const char* txt); + void printClock(const char* timestr); + void printClock(struct tm timeinfo, bool dots, bool redraw = false); + void displayHeapForDebug(); + void drawVolumeBar(bool withNumber); + void drawNextStationNum(uint16_t num); + char* utf8Rus(const char* str, bool uppercase); + void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg); + void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth); + void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg); + void frameTitle(const char* str); + void rssi(const char* str); + void ip(const char* str); + void drawPlaylist(uint16_t currentItem, char* currentItemText); + void loop(); + private: + uint16_t swidth, sheight; + char oldTimeBuf[20]; + uint16_t wot, hot; + +}; + +extern DisplayST7789 dsp; + +/* + * TFT COLORS + */ +#define BLACK 0x0000 +#define BLUE 0x001F +#define RED 0xF800 +#define GREEN 0x07E0 +#define MAGENTA 0xF81F +#define YELLOW 0xFFE0 +#define WHITE 0xFFFF +#define GRAY 0x7BEF +#define DARK_GRAY 0x2945 +#define LIGHT_GRAY 0xC618 +#define LIME 0x87E0 +#define AQUA 0x5D1C +#define CYAN 0x07FF +#define DARK_CYAN 0x03EF +#define ORANGE 0xFCA0 +#define PINK 0xF97F +#define BROWN 0x8200 +#define VIOLET 0x9199 +#define SILVER 0xA510 +#define GOLD 0xA508 +#define NAVY 0x000F +#define MAROON 0x7800 +#define PURPLE 0x780F +#define OLIVE 0x7BE0 + +#define TFT_BG BLACK +#define TFT_FG WHITE +#define TFT_LOGO 0xE68B // 224, 209, 92 + +#endif diff --git a/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h b/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h new file mode 100644 index 0000000..a57b3a6 --- /dev/null +++ b/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h @@ -0,0 +1,199 @@ +const uint8_t DS_DIGI42pt7bBitmaps[] PROGMEM = { + 0x04, 0x31, 0xCF, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xCE, 0x10, 0x00, 0x04, 0x39, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF7, 0xCF, + 0x1C, 0x30, 0x40, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, + 0xF9, 0x3F, 0xFF, 0xFF, 0xE7, 0x3F, 0xFF, 0xFF, 0x9F, 0x3F, 0xFF, 0xFE, + 0x7F, 0x3F, 0xFF, 0xF9, 0xFF, 0x3F, 0xFF, 0xE7, 0xFF, 0x00, 0x00, 0x1F, + 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, + 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, + 0x80, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, + 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, + 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0x00, + 0x00, 0x0F, 0xB8, 0x00, 0x00, 0x0E, 0x20, 0x00, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, + 0x01, 0xDF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, + 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, + 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, + 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, + 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, + 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0x7F, 0xFF, 0xCF, 0xF9, + 0xFF, 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0x9F, 0xFF, 0xFF, 0xCE, 0x7F, + 0xFF, 0xFF, 0xC9, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x10, 0xC7, 0x3D, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF3, + 0x84, 0x00, 0x01, 0x0E, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xF3, 0xC7, 0x0C, 0x10, 0x00, 0x3F, 0xFF, + 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xE4, 0x3F, 0xFF, 0xFF, 0x98, 0x3F, 0xFF, + 0xFE, 0x70, 0x3F, 0xFF, 0xF9, 0xE0, 0x3F, 0xFF, 0xE7, 0xC0, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3F, + 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, + 0x00, 0x00, 0x0F, 0x81, 0xFF, 0xFF, 0xCE, 0x07, 0xFF, 0xFF, 0xC8, 0x1F, + 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x81, 0x3F, 0xFF, 0xFE, 0x07, 0x3F, + 0xFF, 0xF8, 0x1F, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3E, 0x7F, 0xFF, 0xC0, + 0x79, 0xFF, 0xFF, 0xC0, 0xE7, 0xFF, 0xFF, 0xC1, 0x9F, 0xFF, 0xFF, 0xC2, + 0x7F, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF0, + 0x7F, 0xFF, 0xFF, 0x90, 0xFF, 0xFF, 0xFC, 0xC1, 0xFF, 0xFF, 0xE7, 0x03, + 0xFF, 0xFF, 0x3C, 0x07, 0xFF, 0xF9, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0xFF, + 0xFF, 0xCE, 0x07, 0xFF, 0xFF, 0x90, 0x3F, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, + 0xFC, 0x01, 0xFF, 0xFF, 0xE4, 0x03, 0xFF, 0xFF, 0x38, 0x00, 0x00, 0x01, + 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, + 0xF0, 0x1F, 0xFF, 0xE7, 0xC0, 0xFF, 0xFF, 0xCF, 0x07, 0xFF, 0xFF, 0x9C, + 0x3F, 0xFF, 0xFF, 0x31, 0xFF, 0xFF, 0xFE, 0x4F, 0xFF, 0xFF, 0xFC, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x1F, 0x00, + 0x00, 0x00, 0x7F, 0x00, 0x00, 0x01, 0xFF, 0x00, 0x00, 0x07, 0xFF, 0x00, + 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, + 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, + 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, + 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, + 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, + 0xFE, 0x00, 0x00, 0x0F, 0xB9, 0xFF, 0xFF, 0xCE, 0x27, 0xFF, 0xFF, 0xC8, + 0x1F, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFE, 0x40, + 0x3F, 0xFF, 0xF9, 0xC0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x80, + 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, + 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, + 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, + 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, + 0x0F, 0x80, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x00, + 0x0C, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, + 0xF9, 0x3F, 0xFF, 0xFF, 0xE3, 0x3F, 0xFF, 0xFF, 0x87, 0x3F, 0xFF, 0xFE, + 0x0F, 0x3F, 0xFF, 0xF8, 0x1F, 0x3F, 0xFF, 0xE0, 0x3F, 0x00, 0x00, 0x00, + 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, + 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, + 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3E, 0x00, + 0x00, 0x00, 0x39, 0xFF, 0xFF, 0xC0, 0x27, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, + 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFE, 0x40, 0x3F, 0xFF, + 0xF9, 0xC0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, + 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, + 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, + 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, + 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, + 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x7F, 0xFF, 0xCF, 0x81, + 0xFF, 0xFF, 0xCF, 0x07, 0xFF, 0xFF, 0xCE, 0x1F, 0xFF, 0xFF, 0xCC, 0x7F, + 0xFF, 0xFF, 0xC9, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF9, 0x3F, + 0xFF, 0xFF, 0xE3, 0x3F, 0xFF, 0xFF, 0x87, 0x3F, 0xFF, 0xFE, 0x0F, 0x3F, + 0xFF, 0xF8, 0x1F, 0x3F, 0xFF, 0xE0, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, + 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, + 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, + 0x39, 0xFF, 0xFF, 0xC0, 0x27, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xC0, + 0x3F, 0xFF, 0xFF, 0x81, 0x3F, 0xFF, 0xFE, 0x47, 0x3F, 0xFF, 0xF9, 0xDF, + 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, + 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, + 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFF, 0x00, + 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, + 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, + 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0x7F, 0xFF, 0xCF, 0xF9, 0xFF, 0xFF, + 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0x9F, 0xFF, 0xFF, 0xCE, 0x7F, 0xFF, 0xFF, + 0xC9, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF0, 0x7F, 0xFF, 0xFF, + 0x90, 0xFF, 0xFF, 0xFC, 0xC1, 0xFF, 0xFF, 0xE7, 0x03, 0xFF, 0xFF, 0x3C, + 0x07, 0xFF, 0xF9, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, 0xC0, 0x00, 0x00, 0x0E, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, + 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, + 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, + 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, 0x3F, 0x00, + 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x0F, 0xC0, 0x00, + 0x00, 0x3F, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, + 0x07, 0xC0, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1C, 0x00, 0x00, 0x00, + 0x30, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x3F, 0xFF, 0xFF, 0xF9, + 0x3F, 0xFF, 0xFF, 0xE7, 0x3F, 0xFF, 0xFF, 0x9F, 0x3F, 0xFF, 0xFE, 0x7F, + 0x3F, 0xFF, 0xF9, 0xFF, 0x3F, 0xFF, 0xE7, 0xFF, 0x00, 0x00, 0x1F, 0xFE, + 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, + 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, + 0x00, 0x0F, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, + 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, + 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0x00, 0x00, + 0x0F, 0xB9, 0xFF, 0xFF, 0xCE, 0x27, 0xFF, 0xFF, 0xC8, 0x1F, 0xFF, 0xFF, + 0xC0, 0x3F, 0xFF, 0xFF, 0x83, 0x3F, 0xFF, 0xFE, 0x4F, 0x3F, 0xFF, 0xF9, + 0xDF, 0x00, 0x00, 0x07, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, + 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, + 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFF, + 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, + 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, + 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0x7F, 0xFF, 0xCF, 0xF9, 0xFF, + 0xFF, 0xCF, 0xE7, 0xFF, 0xFF, 0xCF, 0x9F, 0xFF, 0xFF, 0xCE, 0x7F, 0xFF, + 0xFF, 0xC9, 0xFF, 0xFF, 0xFF, 0xC0, 0x3F, 0xFF, 0xFF, 0xF9, 0x3F, 0xFF, + 0xFF, 0xE7, 0x3F, 0xFF, 0xFF, 0x9F, 0x3F, 0xFF, 0xFE, 0x7F, 0x3F, 0xFF, + 0xF9, 0xFF, 0x3F, 0xFF, 0xE7, 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, + 0x3F, 0xFC, 0x00, 0x00, 0x7F, 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, + 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, + 0xFF, 0x00, 0x00, 0x1F, 0xFE, 0x00, 0x00, 0x3F, 0xFC, 0x00, 0x00, 0x7F, + 0xF8, 0x00, 0x00, 0xFF, 0xF0, 0x00, 0x01, 0xFF, 0xE0, 0x00, 0x03, 0xFF, + 0xC0, 0x00, 0x07, 0xFF, 0x80, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x0F, 0xB9, + 0xFF, 0xFF, 0xCE, 0x27, 0xFF, 0xFF, 0xC8, 0x1F, 0xFF, 0xFF, 0xC0, 0x3F, + 0xFF, 0xFF, 0x80, 0x3F, 0xFF, 0xFE, 0x40, 0x3F, 0xFF, 0xF9, 0xC0, 0x00, + 0x00, 0x07, 0xC0, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, + 0x00, 0x7E, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, + 0x03, 0xF0, 0x00, 0x00, 0x07, 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x00, 0x00, + 0x1F, 0x80, 0x00, 0x00, 0x3F, 0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, + 0xFC, 0x00, 0x00, 0x01, 0xF8, 0x00, 0x00, 0x03, 0xF0, 0x00, 0x00, 0x07, + 0xE0, 0x00, 0x00, 0x0F, 0xC0, 0x7F, 0xFF, 0xCF, 0x81, 0xFF, 0xFF, 0xCF, + 0x07, 0xFF, 0xFF, 0xCE, 0x1F, 0xFF, 0xFF, 0xCC, 0x7F, 0xFF, 0xFF, 0xC9, + 0xFF, 0xFF, 0xFF, 0xC0, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xC0, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00 +}; + +const GFXglyph DS_DIGI42pt7bGlyphs[] PROGMEM = { + { 0, 0, 0, 19, 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 '/' + { 45, 31, 52, 40, 4, -51 }, // 0x30 '0' + { 247, 6, 51, 17, 6, -50 }, // 0x31 '1' + { 286, 31, 52, 40, 4, -51 }, // 0x32 '2' + { 488, 30, 52, 39, 4, -51 }, // 0x33 '3' + { 683, 31, 52, 40, 4, -51 }, // 0x34 '4' + { 885, 31, 52, 40, 4, -51 }, // 0x35 '5' + { 1087, 31, 52, 40, 4, -51 }, // 0x36 '6' + { 1289, 30, 52, 39, 4, -51 }, // 0x37 '7' + { 1484, 31, 52, 40, 4, -51 }, // 0x38 '8' + { 1686, 31, 52, 40, 4, -51 }, // 0x39 '9' + { 1888, 6, 43, 15, 4, -42 } // 0x3A ':' +}; + +const GFXfont DS_DIGI42pt7b PROGMEM = { + (uint8_t *)DS_DIGI42pt7bBitmaps, + (GFXglyph *)DS_DIGI42pt7bGlyphs, 0x20, 0x3A, 82 }; + +// Approx. 3774 bytes