This commit is contained in:
e2002
2022-03-16 14:27:01 +03:00
parent 0d759b1b4f
commit e3689b8753
27 changed files with 2279 additions and 202 deletions

View File

@@ -18,3 +18,9 @@
![ёRadio](images/img7.jpg)\
\
![ёRadio](images/img8.jpg)\
\
![ёRadio](images/img9.jpg)\
\
![ёRadio](images/img10.jpg)\
\
![ёRadio](images/img11.jpg)\

View File

@@ -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://\<yoradioip\>/data/playlist.csv_ and _http://\<yoradioip\>/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)

View File

@@ -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

BIN
images/img10.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 KiB

BIN
images/img11.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

BIN
images/img9.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

View File

@@ -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.h>
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,7 +112,6 @@ void encoderLoop() {
#endif
#if IR_PIN!=255
void irBlink() {
if (player.mode == STOPPED) {
for (byte i = 0; i < 7; i++) {
@@ -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);
void onBtnLongPressStart(int id) {
switch (id) {
case 0:
case 2: {
lpId = id;
break;
}
if (display.mode == PLAYER) {
player.toggle();
}
if (display.mode == STATIONS) {
display.swichMode(PLAYER);
player.play(display.currentPlItem);
}
}
void onEncDoubleClick() {
case 1:
case 3: {
display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER);
break;
}
}
}
void onEncLPStart() {
display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER);
void onBtnLongPressStop(int id) {
switch (id) {
case 0:
case 2: {
lpId = -1;
break;
}
}
}
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() {
void onBtnClick(int id) {
switch (id) {
case 0: {
controlsEvent(false);
break;
}
void onLeftDoubleClick() {
case 1:
case 3: {
if (display.mode == NUMBERS) {
display.numOfNextStation = 0;
display.swichMode(PLAYER);
player.prev();
}
void onRightClick() {
if (display.mode == PLAYER) {
player.toggle();
}
if (display.mode == STATIONS) {
display.swichMode(PLAYER);
player.play(display.currentPlItem);
}
break;
}
case 2: {
controlsEvent(true);
break;
}
}
}
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;
}
}
}

View File

@@ -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

View File

@@ -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,18 +37,56 @@ 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;
@@ -50,6 +97,7 @@ void Scroll::init(const char *sep, byte tsize, byte top, uint16_t dlay, uint16_
}
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,7 +368,7 @@ 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);
@@ -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;
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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

View File

@@ -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 <inttypes.h>
#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

View File

@@ -0,0 +1,127 @@
//YWROBOT
#ifndef LiquidCrystalI2CEx_h
#define LiquidCrystalI2CEx_h
#include <inttypes.h>
#include "Print.h"
#include <Wire.h>
// 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

View File

@@ -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();

View File

@@ -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; x<swidth-controlspaces[texttop]; x++){
setCursor(x, texttop);
print(" ");
}
}
void DisplayLC1602::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
byte x=(strlen(text)>swidth)?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

View File

@@ -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

View File

@@ -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();

View File

@@ -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

View File

@@ -0,0 +1,333 @@
#include "../../options.h"
#if DSP_MODEL==5
#include "displaySH1106.h"
#include <Wire.h>
#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

View File

@@ -0,0 +1,65 @@
#ifndef displaySH1106_h
#define displaySH1106_h
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#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

View File

@@ -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);

View File

@@ -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:

View File

@@ -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:

View File

@@ -0,0 +1,358 @@
#include "../../options.h"
#if DSP_MODEL==4
#include "displayST7789.h"
#include <SPI.h>
#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

View File

@@ -0,0 +1,98 @@
#ifndef displayST7789_h
#define displayST7789_h
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
// 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

View File

@@ -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