diff --git a/Controls.md b/Controls.md index b7767a8..56907cc 100644 --- a/Controls.md +++ b/Controls.md @@ -6,7 +6,8 @@ - [Buttons](#buttons) - [Encoders](#encoders) - [IR receiver](#ir-receiver) -- [Joystick](#joystic) +- [Joystick](#joystick) +- [Touchscreen](#touchscreen) - [Back to README](README.md) --- @@ -66,3 +67,10 @@ You can use a joystick [like this](https://aliexpress.com/item/4000681560472.htm
--- +### Touchscreen +- Swipe horizontally: volume control +- Swipe vertically: station selection +- Tap: in PLAYER mode - start/stop playback, in PLAYLIST mode - select +- Long tap: in PLAYLIST mode - cancel + +--- diff --git a/Images.md b/Images.md index b7c8d36..d1cb481 100644 --- a/Images.md +++ b/Images.md @@ -31,4 +31,14 @@ \ ![ёRadio](images/img14.jpg)\ \ -![ёRadio](images/img15.jpg) +![ёRadio](images/img15.jpg)\ +\ +![ёRadio](images/img16.jpg)\ +\ +![ёRadio](images/img17.jpg)\ +\ +![ёRadio](images/img18.jpg)\ +\ +![ёRadio](images/img19.jpg)\ +\ +![ёRadio](images/img20.jpg) diff --git a/README.md b/README.md index 7f98609..c821118 100644 --- a/README.md +++ b/README.md @@ -36,15 +36,23 @@ https://aliexpress.com/item/32965676064.html - or **SSD1306** 0.96' 128x64 I2C https://aliexpress.com/item/1005001621806398.html - or **SSD1306** 0,91' 128x32 I2C https://aliexpress.com/item/32798439084.html - or **Nokia5110** 84x48 SPI https://aliexpress.com/item/1005001621837569.html -- or **ST7789** 320x240 SPI https://aliexpress.com/item/32960241206.html -- or **SH1106** 128x64 I2C https://aliexpress.com/item/32683094040.html +- or **ST7789** 2.4' 320x240 SPI https://aliexpress.com/item/32960241206.html +- or **SH1106** 1.3' 128x64 I2C https://aliexpress.com/item/32683094040.html - or **1602** 16x2 I2C https://aliexpress.com/item/32305776560.html +- or **1602** 16x2 without I2C https://aliexpress.com/item/32305776560.html +- or **SSD1327** 1.5' 128x128 I2C https://aliexpress.com/item/1005001414175498.html +- or **ILI9341** 3.2'320x240 SPI https://aliexpress.com/item/33048191074.html +- or **SSD1305 (SSD1309)** 2.4' 128x64 SPI https://aliexpress.com/item/32950307344.html +- or **SH1107** 0.96' 128x64 I2C https://aliexpress.com/item/4000551696674.html ##### Controls - Three tact buttons https://www.aliexpress.com/item/32907144687.html - Encoder https://www.aliexpress.com/item/32873198060.html +- Joystick https://aliexpress.com/item/4000681560472.html \ +https://aliexpress.com/item/4000699838567.html - IR Control https://www.aliexpress.com/item/32562721229.html \ https://www.aliexpress.com/item/33009687492.html +- Touchscreen https://aliexpress.com/item/33048191074.html --- ## Connection tables @@ -75,6 +83,27 @@ https://www.aliexpress.com/item/33009687492.html | SDA | 13* | I2C_SDA | | SCL | 14* | I2C_SCL | +| LCD 1602 | ESP-32 | options.h | +| ------ | ------ | ------ | +| GND | GND | - | +| VCC | +5v | - | +| RS | any* | LCD_RS | +| E | any* | LCD_E | +| D4 | any* | LCD_D4 | +| D5 | any* | LCD_D5 | +| D6 | any* | LCD_D6 | +| D7 | any* | LCD_D7 | + +| Touchscreen | ESP-32 | options.h | +| ------ | ------ | ------ | +| GND | GND | - | +| VCC | +3.3v | - | +| CLK | 18 | - | +| DIN | 23 | - | +| DO | 19 | - | +| IRQ | N/C | - | +| CS | any* | TS_CS | + | I2S DAC | ESP-32 | options.h | | ------ | ------ | ------ | | GND | GND | - | @@ -97,18 +126,19 @@ https://www.aliexpress.com/item/33009687492.html _\#\# Important! You must choose between I2S DAC and VS1053 by disabling the second module in the settings (see below)_ -| Buttons, Encoder, LED, IR | ESP-32 | options.h | +| Buttons, Encoder, LED, IR, Joystick | ESP-32 | options.h | | ------ | ------ | ------ | | GND | GND | - | -| PIN | * | ENC_BTNx, BTN_xxx, LED_BUILTIN, IR_PIN | +| PIN | any* | ENC_BTNx, BTN_xxx, LED_BUILTIN, IR_PIN | -_\* Any free pin, configured in options.h_ \ -_\** GPIOs 34-39 don't have software pullup/down functions. For encoder/buttons use an external pullup resistor, 10 kOhm works here_ +_\* Any free pin, configured in myoptions.h_ \ +_\** GPIOs 34-39 don't have software pullup/down functions. For encoder/buttons use an external pullup resistor, 10 kOhm works here._ \ +_\** GPIO 16 and 17 are used by PSRAM on the WROVER modules._ --- ## Dependencies #### Libraries: -**Library Manager**: Adafruit_GFX, Adafruit_ST7735\*, Adafruit_SSD1306\*, Adafruit_PCD8544\*, (\* depending on display model), ESP32Encoder, OneButton, IRremoteESP8266 \ +**Library Manager**: Adafruit_GFX, Adafruit_ST7735\*, Adafruit_SSD1306\*, Adafruit_PCD8544\*, Adafruit_SH110X\*, Adafruit_SSD1327\*, Adafruit_ILI9341\*, Adafruit_SSD1305\*, (\* depending on display model), ESP32Encoder, OneButton, IRremoteESP8266, XPT2046_Touchscreen \ **Github**: [ESPAsyncWebServer](https://github.com/me-no-dev/ESPAsyncWebServer), [AsyncTCP](https://github.com/me-no-dev/AsyncTCP), [async-mqtt-client](https://github.com/marvinroger/async-mqtt-client)* \ \* _if you need MQTT support_ @@ -250,6 +280,13 @@ Work is in progress... --- ## Version history +#### v0.6.010 +- added displays [SSD1327](https://aliexpress.com/item/1005001414175498.html), [ILI9341](https://aliexpress.com/item/33048191074.html), [SSD1305/SSD1309](https://aliexpress.com/item/32950307344.html), [SH1107](https://aliexpress.com/item/4000551696674.html), [1602](https://aliexpress.com/item/32685016568.html) +- added [touchscreen](Controls.md#touchscreen) support +- tasks are divided into cores, now the sound is not interrupted when selecting stations / volume +- increased speed of some displays +- optimization of algorithms, bugs fixes + #### v0.5.070 - added something similar to plugins diff --git a/images/img16.jpg b/images/img16.jpg new file mode 100644 index 0000000..ccc106c Binary files /dev/null and b/images/img16.jpg differ diff --git a/images/img17.jpg b/images/img17.jpg new file mode 100644 index 0000000..f4b8a3f Binary files /dev/null and b/images/img17.jpg differ diff --git a/images/img18.jpg b/images/img18.jpg new file mode 100644 index 0000000..0b15860 Binary files /dev/null and b/images/img18.jpg differ diff --git a/images/img19.jpg b/images/img19.jpg new file mode 100644 index 0000000..91ef8f5 Binary files /dev/null and b/images/img19.jpg differ diff --git a/images/img20.jpg b/images/img20.jpg new file mode 100644 index 0000000..c1c0f3b Binary files /dev/null and b/images/img20.jpg differ diff --git a/yoRadio/audiohandlers.ino b/yoRadio/audiohandlers.ino index 8af619c..b919e84 100644 --- a/yoRadio/audiohandlers.ino +++ b/yoRadio/audiohandlers.ino @@ -1,7 +1,8 @@ void audio_info(const char *info) { if(config.store.audioinfo) telnet.printf("##AUDIO.INFO#: %s\n", info); - if (strstr(info, "failed!") != NULL) { - display.title("[Request failed!]"); + if (strstr(info, "failed!") != NULL || strstr(info, "404") != NULL) { + config.setTitle("[request failed]"); + //config.setTitle(info); player.mode = STOPPED; player.stopInfo(); } @@ -27,25 +28,17 @@ bool printable(const char *info) { void audio_showstation(const char *info) { if (strlen(info) > 0) { bool p = printable(info); - display.title(p?info:"*****"); - if (player.requesToStart) { - telnet.info(); - player.requesToStart = false; - } else { - telnet.printf("##CLI.ICY0#: %s\n", p?info:"*****"); - } + //display.title(p?info:"*****"); + config.setTitle(p?info:"*****"); + netserver.requestOnChange(TITLE, 0); } } void audio_showstreamtitle(const char *info) { if (strlen(info) > 0) { bool p = printable(info); - display.title(p?info:"*****"); - if (player.requesToStart) { - telnet.info(); - player.requesToStart = false; - } else { - telnet.printf("##CLI.META#: %s\n> ", p?info:"*****"); - } + //display.title(p?info:"*****"); + config.setTitle(p?info:"*****"); + netserver.requestOnChange(TITLE, 0); } } diff --git a/yoRadio/config.cpp b/yoRadio/config.cpp index 82e35aa..67b2132 100644 --- a/yoRadio/config.cpp +++ b/yoRadio/config.cpp @@ -1,7 +1,7 @@ #include "config.h" #include #include - +#include "display.h" Config config; void Config::init() { @@ -128,6 +128,17 @@ byte Config::setLastSSID(byte val) { return store.lastSSID; } +void Config::setTitle(const char* title){ + memset(config.station.title, 0, BUFLEN); + strlcpy(config.station.title, title, BUFLEN); + display.refreshTitle = true; +} + +void Config::setStation(const char* station){ + memset(config.station.name, 0, BUFLEN); + strlcpy(config.station.name, station, BUFLEN); +} + void Config::indexPlaylist() { File playlist = SPIFFS.open(PLAYLIST_PATH, "r"); if (!playlist) { diff --git a/yoRadio/config.h b/yoRadio/config.h index 0fe1119..e4432d8 100644 --- a/yoRadio/config.h +++ b/yoRadio/config.h @@ -60,6 +60,8 @@ class Config { byte setLastStation(byte val); byte setCountStation(byte val); byte setLastSSID(byte val); + void setTitle(const char* title); + void setStation(const char* station); bool parseCSV(const char* line, char* name, char* url, int &ovol); bool parseJSON(const char* line, char* name, char* url, int &ovol); bool parseWsCommand(const char* line, char* cmd, char* val, byte cSize); diff --git a/yoRadio/controls.cpp b/yoRadio/controls.cpp index 40650f4..03de0b0 100644 --- a/yoRadio/controls.cpp +++ b/yoRadio/controls.cpp @@ -26,6 +26,11 @@ ESP32Encoder encoder2; #endif #endif +#if TS_CS!=255 +#include +XPT2046_Touchscreen ts(TS_CS); +#endif + #if IR_PIN!=255 #include #include @@ -82,7 +87,10 @@ void initControls() { button[i].setPressTicks(BTN_PRESS_TICKS); } #endif - +#if TS_CS!=255 + ts.begin(); + ts.setRotation(TS_ROTATE); +#endif #if IR_PIN!=255 pinMode(IR_PIN, INPUT); assert(irutils::lowLevelSanityCheck() == 0); @@ -107,7 +115,7 @@ void loopControls() { if ((i == 0 && BTN_LEFT == 255) || (i == 1 && BTN_CENTER == 255) || (i == 2 && BTN_RIGHT == 255) || (i == 3 && ENC_BTNB == 255) || (i == 4 && BTN_UP == 255) || (i == 5 && BTN_DOWN == 255) || (i == 6 && ENC2_BTNB == 255)) continue; button[i].tick(); if (lpId >= 0) { - if(DSP_MODEL==DSP_DUMMY && (lpId==4 || lpId==5)) continue; + if (DSP_MODEL == DSP_DUMMY && (lpId == 4 || lpId == 5)) continue; onBtnDuringLongPress(lpId); yield(); } @@ -116,6 +124,9 @@ void loopControls() { #endif #if IR_PIN!=255 irLoop(); +#endif +#if TS_CS!=255 + touchLoop(); #endif yield(); } @@ -138,10 +149,10 @@ void encoder2Loop() { enc2OldPosition = encNewPosition; encoder2.setCount(0); uint8_t bp = 2; - if(ENC2_BTNB!=255){ + if (ENC2_BTNB != 255) { bp = digitalRead(ENC2_BTNB); } - if(bp==HIGH && display.mode!=STATIONS) display.swichMode(STATIONS); + if (bp == HIGH && display.mode != STATIONS) display.swichMode(STATIONS); controlsEvent(encNewPosition > 0); } } @@ -160,7 +171,8 @@ void irBlink() { void irNum(byte num) { uint16_t s; if (display.numOfNextStation == 0 && num == 0) return; - if (display.mode == PLAYER) display.swichMode(NUMBERS); + //if (display.mode == PLAYER) display.swichMode(NUMBERS); + display.swichMode(NUMBERS); if (display.numOfNextStation > UINT16_MAX / 10) return; s = display.numOfNextStation * 10 + num; if (s > config.store.countStation) return; @@ -192,7 +204,9 @@ void irLoop() { irBlink(); if (display.mode == NUMBERS) { display.swichMode(PLAYER); - player.play(display.numOfNextStation); + //player.play(display.numOfNextStation); + player.request.station = display.numOfNextStation; + player.request.doSave = true; display.numOfNextStation = 0; break; } @@ -272,12 +286,124 @@ void irLoop() { } #endif // if IR_PIN!=255 +#if TS_CS!=255 +#ifndef TS_X_MIN + #define TS_X_MIN 400 +#endif +#ifndef TS_X_MAX + #define TS_X_MAX 3800 +#endif +#ifndef TS_Y_MIN + #define TS_Y_MIN 260 +#endif +#ifndef TS_Y_MAX + #define TS_Y_MAX 3800 +#endif +#ifndef TS_STEPS + #define TS_STEPS 70 +#endif + +boolean wastouched = true; +unsigned long touchdelay; +uint16_t touchVol, touchStation; +uint16_t oldTouchP[2]; +tsDirection_e direct; +unsigned long touchLongPress; + +tsDirection_e tsDirection(uint16_t x, uint16_t y) { + int16_t dX = x - oldTouchP[0]; + int16_t dY = y - oldTouchP[1]; + if (abs(dX) > 20 || abs(dY) > 20) { + if (abs(dX) > abs(dY)) { + if (dX > 0) { + return TSD_RIGHT; + } else { + return TSD_LEFT; + } + } else { + if (dY > 0) { + return TSD_DOWN; + } else { + return TSD_UP; + } + } + } else { + return TDS_REQUEST; + } +} + +void touchLoop() { + if (!checklpdelay(100, touchdelay)) return; + boolean istouched = ts.touched(); + if (istouched) { + TS_Point p = ts.getPoint(); + uint16_t touchX = map(p.x, TS_X_MIN, TS_X_MAX, 0, display.screenwidth); + uint16_t touchY = map(p.y, TS_Y_MIN, TS_Y_MAX, 0, display.screenheight); + if (!wastouched) { /* START TOUCH */ + oldTouchP[0] = touchX; + oldTouchP[1] = touchY; + touchVol = touchX; + touchStation = touchY; + direct = TDS_REQUEST; + touchLongPress=millis(); + } else { /* SWIPE TOUCH */ + direct = tsDirection(touchX, touchY); + switch (direct) { + case TSD_LEFT: + case TSD_RIGHT: { + if(display.mode==PLAYER || display.mode==VOL){ + int16_t xDelta = map(abs(touchVol - touchX), 0, display.screenwidth, 0, TS_STEPS); + Serial.println(touchVol - touchX); + display.swichMode(VOL); + if (xDelta>1) { + controlsEvent((touchVol - touchX)<0); + touchVol = touchX; + } + } + break; + } + case TSD_UP: + case TSD_DOWN: { + if(display.mode==PLAYER || display.mode==STATIONS){ + int16_t yDelta = map(abs(touchStation - touchY), 0, display.screenheight, 0, TS_STEPS); + display.swichMode(STATIONS); + if (yDelta>1) { + controlsEvent((touchStation - touchY)>0); + touchStation = touchY; + } + } + break; + } + } + } + if (TS_DBG) { + Serial.print(", x = "); + Serial.print(p.x); + Serial.print(", y = "); + Serial.println(p.y); + } + } else { + if (wastouched) {/* END TOUCH */ + if (direct == TDS_REQUEST) { + if(millis()-touchLongPress < BTN_PRESS_TICKS){ + onBtnClick(EVT_BTNCENTER); + }else{ + display.swichMode(display.mode == PLAYER ? STATIONS : PLAYER); + } + } + direct = TSD_STAY; + } + } + wastouched = istouched; +} +#endif // if TS_CS!=255 + void onBtnLongPressStart(int id) { switch ((controlEvt_e)id) { case EVT_BTNLEFT: case EVT_BTNRIGHT: case EVT_BTNUP: - case EVT_BTNDOWN:{ + case EVT_BTNDOWN: { lpId = id; break; } @@ -294,7 +420,7 @@ void onBtnLongPressStop(int id) { case EVT_BTNLEFT: case EVT_BTNRIGHT: case EVT_BTNUP: - case EVT_BTNDOWN:{ + case EVT_BTNDOWN: { lpId = -1; break; } @@ -328,7 +454,7 @@ void onBtnDuringLongPress(int id) { display.swichMode(STATIONS); } if (display.mode == STATIONS) { - controlsEvent(id==EVT_BTNDOWN); + controlsEvent(id == EVT_BTNDOWN); } break; } @@ -373,7 +499,9 @@ void onBtnClick(int id) { } if (display.mode == STATIONS) { display.swichMode(PLAYER); - player.play(display.currentPlItem); + //player.play(display.currentPlItem); + player.request.station = display.currentPlItem; + player.request.doSave = true; } break; } @@ -383,18 +511,18 @@ void onBtnClick(int id) { } case EVT_BTNUP: case EVT_BTNDOWN: { - if(DSP_MODEL==DSP_DUMMY){ - if(id==EVT_BTNUP){ + if (DSP_MODEL == DSP_DUMMY) { + if (id == EVT_BTNUP) { player.next(); - }else{ + } else { player.prev(); } - }else{ + } else { if (display.mode == PLAYER) { display.swichMode(STATIONS); } if (display.mode == STATIONS) { - controlsEvent(id==EVT_BTNDOWN); + controlsEvent(id == EVT_BTNDOWN); } } break; diff --git a/yoRadio/controls.h b/yoRadio/controls.h index 7963c3f..012de08 100644 --- a/yoRadio/controls.h +++ b/yoRadio/controls.h @@ -3,6 +3,9 @@ enum controlEvt_e { EVT_BTNLEFT, EVT_BTNCENTER, EVT_BTNRIGHT, EVT_ENCBTNB, EVT_BTNUP, EVT_BTNDOWN, EVT_ENC2BTNB }; +enum tsDirection_e { TSD_STAY, TSD_LEFT, TSD_RIGHT, TSD_UP, TSD_DOWN, TDS_REQUEST }; + + boolean checklpdelay(int m, unsigned long &tstamp); void initControls(); @@ -10,6 +13,7 @@ void loopControls(); void encoderLoop(); void encoder2Loop(); void irLoop(); +void touchLoop(); void irNum(byte num); void irBlink(); void controlsEvent(bool toRight); @@ -19,5 +23,6 @@ void onBtnDoubleClick(int id); void onBtnDuringLongPress(int id); void onBtnLongPressStart(int id); void onBtnLongPressStop(int id); +tsDirection_e tsDirection(uint16_t x, uint16_t y); #endif diff --git a/yoRadio/display.cpp b/yoRadio/display.cpp index 3741711..5340c16 100644 --- a/yoRadio/display.cpp +++ b/yoRadio/display.cpp @@ -152,8 +152,8 @@ void Scroll::sticks() { void Scroll::scroll() { if (!doscroll || textsize == 0) return; //if (textwidth > display.screenwidth) { - x -= SCROLLDELTA; - if (-x > textwidth + sepwidth - TFT_FRAMEWDT) x = TFT_FRAMEWDT; + x -= SCROLLDELTA; + if (-x > textwidth + sepwidth - TFT_FRAMEWDT) x = TFT_FRAMEWDT; //} } @@ -180,11 +180,11 @@ void Display::init() { title2.init(" * ", TITLE_SIZE2, TITLE_TOP2, STARTTIME, TITLE_FG2, TFT_BG); int yStart = (screenheight / 2 - PLMITEMHEIGHT / 2) + 3; #ifdef PL_TOP - yStart=PL_TOP; + yStart = PL_TOP; #endif plCurrent.init(" * ", PLCURRENT_SIZE, yStart, STARTTIME_PL, TFT_BG, TFT_LOGO); plCurrent.lock(); - if(dsp_on_init) dsp_on_init(); + if (dsp_on_init) dsp_on_init(); } void Display::apScreen() { @@ -196,20 +196,25 @@ void Display::apScreen() { void Display::start() { clear(); + refreshTitle = false; + refreshStation = false; + refreshVolume = false; if (network.status != CONNECTED) { apScreen(); return; } mode = PLAYER; - title("[READY]"); + config.setTitle("[READY]"); + //title("[READY]"); + loop(); ip(); volume(); station(); rssi(); time(); timer.attach_ms(1000, ticks); - if(dsp_on_start) dsp_on_start(&dsp); - // Экстреминатус секвестирован + if (dsp_on_start) dsp_on_start(&dsp); + // Экстреминатус секвестирован /*дважды*/ трижды } void Display::clear() { @@ -234,7 +239,7 @@ void Display::swichMode(displayMode_e newmode) { plCurrent.lock(); time(true); #ifdef CLOCK_SPACE // if set space for clock in 1602 displays - dsp.fillSpaces=true; + dsp.fillSpaces = true; ip(); rssi(); volume(); @@ -246,7 +251,7 @@ void Display::swichMode(displayMode_e newmode) { title1.lock(); if (TITLE_SIZE2 != 0) title2.lock(); #ifdef CLOCK_SPACE - dsp.fillSpaces=false; + dsp.fillSpaces = false; #endif } if (newmode == VOL) { @@ -261,7 +266,7 @@ void Display::swichMode(displayMode_e newmode) { plCurrent.reset(); drawPlaylist(); } - if(dsp_on_newmode) dsp_on_newmode(newmode); + if (dsp_on_newmode) dsp_on_newmode(newmode); } void Display::drawPlayer() { @@ -298,6 +303,18 @@ void Display::drawNextStationNum(uint16_t num) { } void Display::loop() { + if(refreshStation){ + refreshStation = false; + station(); + } + if(refreshTitle){ + refreshTitle = false; + title(); + } + if(refreshVolume){ + refreshVolume = false; + volume(); + } switch (mode) { case PLAYER: { drawPlayer(); @@ -317,7 +334,7 @@ void Display::loop() { } } dsp.loop(); - if(dsp_on_loop) dsp_on_loop(); + if (dsp_on_loop) dsp_on_loop(); yield(); } @@ -326,7 +343,7 @@ 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==1?BOOTSTR_TOP1:BOOTSTR_TOP2, TFT_LOGO, TFT_BG); + dsp.centerText(text, y == 1 ? BOOTSTR_TOP1 : BOOTSTR_TOP2, TFT_LOGO, TFT_BG); dsp.loop(); } @@ -342,21 +359,22 @@ void Display::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) { void Display::station() { meta.setText(dsp.utf8Rus(config.station.name, true)); dsp.loop(); - netserver.requestOnChange(STATION, 0); + //netserver.requestOnChange(STATION, 0); } -void Display::returnTile(){ +void Display::returnTile() { meta.setText(dsp.utf8Rus(config.station.name, true)); meta.reset(); dsp.loop(); } -void Display::title(const char *str) { - const char *title = str; +void Display::title() { + /* + memset(config.station.title, 0, BUFLEN); + strlcpy(config.station.title, str, BUFLEN); + */ char ttl[BUFLEN / 2] = { 0 }; char sng[BUFLEN / 2] = { 0 }; - memset(config.station.title, 0, BUFLEN); - strlcpy(config.station.title, title, BUFLEN); if (strlen(config.station.title) > 0) { char* ici; if ((ici = strstr(config.station.title, " - ")) != NULL && TITLE_SIZE2 != 0) { @@ -368,9 +386,10 @@ void Display::title(const char *str) { } title1.setText(dsp.utf8Rus(ttl, true)); if (TITLE_SIZE2 != 0) title2.setText(dsp.utf8Rus(sng, true)); + dsp.loop(); } - netserver.requestOnChange(TITLE, 0); + //netserver.requestOnChange(TITLE, 0); } void Display::heap() { @@ -390,7 +409,7 @@ void Display::ip() { } void Display::time(bool redraw) { - if(dsp_before_clock) if(!dsp_before_clock(&dsp, dt)) return; + if (dsp_before_clock) if (!dsp_before_clock(&dsp, dt)) return; char timeStringBuff[20] = { 0 }; if (!dt) { heap(); @@ -407,10 +426,11 @@ void Display::time(bool redraw) { dsp.printClock(network.timeinfo, dt, redraw); #endif dt = !dt; - if(dsp_after_clock) dsp_after_clock(&dsp, dt); + if (dsp_after_clock) dsp_after_clock(&dsp, dt); } void Display::volume() { dsp.drawVolumeBar(mode == VOL); - netserver.requestOnChange(VOLUME, 0); + //netserver.requestOnChange(VOLUME, 0); + netserver.volRequest = true; } diff --git a/yoRadio/display.h b/yoRadio/display.h index b2bc1cb..111df9f 100644 --- a/yoRadio/display.h +++ b/yoRadio/display.h @@ -19,6 +19,18 @@ #include "src/displays/displaySH1106.h" #elif DSP_MODEL==DSP_1602I2C #include "src/displays/displayLC1602.h" +#elif DSP_MODEL==DSP_SSD1327 +#include "src/displays/displaySSD1327.h" +#elif DSP_MODEL==DSP_ILI9341 +#include "src/displays/displayILI9341.h" +#elif DSP_MODEL==DSP_SSD1305 +#include "src/displays/displaySSD1305.h" +#elif DSP_MODEL==DSP_SH1107 +#include "src/displays/displaySH1106.h" +#elif DSP_MODEL==DSP_1602 +#include "src/displays/displayLC1602.h" +#elif DSP_MODEL==DSP_CUSTOM +#include "src/displays/displayCustom.h" #endif enum displayMode_e { PLAYER, VOL, STATIONS, NUMBERS }; @@ -59,6 +71,7 @@ class Display { displayMode_e mode; uint16_t currentPlItem; uint16_t numOfNextStation; + bool refreshTitle, refreshStation, refreshVolume; public: Display() {}; void init(); @@ -69,12 +82,7 @@ class Display { 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 returnTile(); - void time(bool redraw = false); - void volume(); - void ip(); void swichMode(displayMode_e newmode); void drawPlaylist(); void drawNextStationNum(uint16_t num); @@ -85,10 +93,14 @@ class Display { unsigned long volDelay; void heap(); void rssi(); + void station(); + void title(); + void volume(); + void ip(); + void time(bool redraw = false); void apScreen(); void drawPlayer(); void drawVolume(); - }; extern Display display; diff --git a/yoRadio/netserver.cpp b/yoRadio/netserver.cpp index ced44fa..7c28b95 100644 --- a/yoRadio/netserver.cpp +++ b/yoRadio/netserver.cpp @@ -3,6 +3,7 @@ #include "config.h" #include "player.h" +#include "telnet.h" #include "display.h" #include "options.h" #include "network.h" @@ -23,6 +24,7 @@ byte ssidCount; bool NetServer::begin() { importRequest = false; + volRequest = false; webserver.on("/", HTTP_GET, [](AsyncWebServerRequest * request) { ssidCount = 0; request->send(SPIFFS, "/www/index.html", String(), false, processor); @@ -46,11 +48,11 @@ bool NetServer::begin() { webserver.begin(); websocket.onEvent(onWsEvent); webserver.addHandler(&websocket); - + //echo -n "helle?" | socat - udp-datagram:255.255.255.255:44490,broadcast if (udp.listen(44490)) { udp.onPacket([](AsyncUDPPacket packet) { - if(strcmp((char*)packet.data(),"helle?")==0) + if (strcmp((char*)packet.data(), "helle?") == 0) packet.println(WiFi.localIP()); }); } @@ -69,6 +71,13 @@ void NetServer::loop() { } importRequest = false; } + if (volRequest) { + requestOnChange(VOLUME, 0); + volRequest = false; + } + if(rssi<255){ + requestOnChange(NRSSI, 0); + } yield(); } @@ -88,7 +97,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len) { void NetServer::setRSSI(int val) { rssi = val; - requestOnChange(NRSSI, 0); + //requestOnChange(NRSSI, 0); } void NetServer::getPlaylist(uint8_t clientId) { @@ -200,6 +209,12 @@ void NetServer::requestOnChange(requestType_e request, uint8_t clientId) { } case TITLE: { sprintf (buf, "{\"meta\": \"%s\"}", config.station.title); + if (player.requesToStart) { + telnet.info(); + player.requesToStart = false; + } else { + telnet.printf("##CLI.META#: %s\n> ", config.station.title); + } break; } case VOLUME: { @@ -211,6 +226,7 @@ void NetServer::requestOnChange(requestType_e request, uint8_t clientId) { } case NRSSI: { sprintf (buf, "{\"rssi\": %d}", rssi); + rssi=255; break; } case BITRATE: { @@ -234,7 +250,7 @@ void NetServer::requestOnChange(requestType_e request, uint8_t clientId) { if (clientId == 0) { websocket.textAll(buf); #ifdef MQTT_HOST - if(request==STATION || request==ITEM || request==TITLE || request==MODE) mqttPublishStatus(); + if (request == STATION || request == ITEM || request == TITLE || request == MODE) mqttPublishStatus(); #endif } else { websocket.text(clientId, buf); @@ -328,7 +344,8 @@ void handleHTTPPost(AsyncWebServerRequest * request) { } if (request->hasParam("stop", true)) { player.mode = STOPPED; - display.title("[stopped]"); + //display.title("[stopped]"); + config.setTitle("[stopped]"); request->send(200); return; } diff --git a/yoRadio/netserver.h b/yoRadio/netserver.h index c87c882..78038e0 100644 --- a/yoRadio/netserver.h +++ b/yoRadio/netserver.h @@ -10,7 +10,7 @@ enum requestType_e { PLAYLIST, STATION, ITEM, TITLE, VOLUME, NRSSI, BITRATE, MOD class NetServer { public: uint8_t playlistrequest; // ClientId want the playlist - bool importRequest; + bool importRequest, volRequest; public: NetServer() {}; bool begin(); diff --git a/yoRadio/options.h b/yoRadio/options.h index da94a9f..6367bef 100644 --- a/yoRadio/options.h +++ b/yoRadio/options.h @@ -1,7 +1,7 @@ #ifndef options_h #define options_h -#define VERSION "0.5.070" +#define VERSION "0.6.010" /******************************************************* DO NOT EDIT THIS FILE. @@ -27,6 +27,12 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #define DSP_SH1106 5 // https://aliexpress.com/item/32683094040.html #define DSP_1602I2C 6 // https://aliexpress.com/item/32305776560.html #define DSP_SSD1306x32 7 // https://aliexpress.com/item/32798439084.html +#define DSP_SSD1327 8 // https://aliexpress.com/item/1005001414175498.html +#define DSP_ILI9341 9 // use it with the [#define TFT_INVERT false] option https://aliexpress.com/item/33048191074.html +#define DSP_SSD1305 10 // SSD1305 and SSD1309 128x64 SPI https://aliexpress.com/item/32950307344.html +#define DSP_SH1107 11 // https://aliexpress.com/item/4000551696674.html +#define DSP_1602 12 // https://aliexpress.com/item/32685016568.html +#define DSP_CUSTOM 101 // your display #ifndef DSP_MODEL #define DSP_MODEL DSP_DUMMY @@ -141,6 +147,37 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #define BTN_PRESS_TICKS 500 #endif +/* TOUCH SCREEN */ +#ifndef TS_CS + #define TS_CS 255 +#endif +#ifndef TS_ROTATE + #define TS_ROTATE 1 +#endif +#ifndef TS_DBG + #define TS_DBG false +#endif + +/* LCD DISPLAY */ +#ifndef LCD_RS + #define LCD_RS 255 +#endif +#ifndef LCD_E + #define LCD_E 255 +#endif +#ifndef LCD_D4 + #define LCD_D4 255 +#endif +#ifndef LCD_D5 + #define LCD_D5 255 +#endif +#ifndef LCD_D6 + #define LCD_D6 255 +#endif +#ifndef LCD_D7 + #define LCD_D7 255 +#endif + /* ESP DEVBOARD */ #ifndef LED_BUILTIN #define LED_BUILTIN 2 @@ -165,6 +202,9 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti #ifndef MUTE_VAL #define MUTE_VAL HIGH // Write this to MUTE_PIN when player is stopped #endif +#ifndef CORE_FOR_LOOP_CONTROLS + #define CORE_FOR_LOOP_CONTROLS 2 // 0 for Core0, 1 for Core1, 2 for Auto, 255 for Not Used +#endif /* *** ST7735 display submodel *** diff --git a/yoRadio/player.cpp b/yoRadio/player.cpp index 0cc3a4d..380e813 100644 --- a/yoRadio/player.cpp +++ b/yoRadio/player.cpp @@ -40,6 +40,7 @@ void Player::init() { setTone(config.store.bass, config.store.middle, config.store.trebble); setVolume(0); mode = STOPPED; + setOutputPins(false); requesToStart = true; zeroRequest(); } @@ -58,7 +59,9 @@ void Player::loop() { if (isRunning()) { //digitalWrite(LED_BUILTIN, LOW); setOutputPins(false); - display.title("[stopped]"); + //display.title("[stopped]"); + config.setTitle("[stopped]"); + netserver.requestOnChange(TITLE, 0); stopSong(); stopInfo(); } @@ -72,7 +75,8 @@ void Player::loop() { } if (request.volume >= 0) { config.setVolume(request.volume, request.doSave); - display.volume(); + //display.volume(); + display.refreshVolume = true; telnet.printf("##CLI.VOL#: %d\n", config.store.volume); Audio::setVolume(volToI2S(request.volume)); zeroRequest(); @@ -95,11 +99,17 @@ void Player::play(uint16_t stationId) { stopSong(); //digitalWrite(LED_BUILTIN, LOW); setOutputPins(false); - display.title("[connecting]"); - telnet.printf("##CLI.META#: %s\n", config.station.title); + //display.title("[connecting]"); + + config.setTitle("[connecting]"); + //display.refreshTitle = true; + netserver.requestOnChange(TITLE, 0); + //telnet.printf("##CLI.META#: %s\n", config.station.title); config.loadStation(stationId); setVol(config.store.volume, true); - display.station(); + //display.station(); + display.refreshStation = true; + netserver.requestOnChange(STATION, 0); telnet.printf("##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name); if (connecttohost(config.station.url)) { mode = PLAYING; @@ -129,7 +139,7 @@ void Player::next() { void Player::toggle() { if (mode == PLAYING) { mode = STOPPED; - display.title("[stopped]"); + //display.title("[stopped]"); } else { request.station = config.store.lastStation; } @@ -162,7 +172,15 @@ void Player::setVol(byte volume, bool inside) { if (inside) { setVolume(volToI2S(volume)); } else { +#if CORE_FOR_LOOP_CONTROLS==255 request.volume = volume; request.doSave = true; +#else + config.setVolume(volume, true); + //display.volume(); + display.refreshVolume = true; + telnet.printf("##CLI.VOL#: %d\n", config.store.volume); + Audio::setVolume(volToI2S(volume)); +#endif } } diff --git a/yoRadio/src/displays/displayILI9341.cpp b/yoRadio/src/displays/displayILI9341.cpp new file mode 100644 index 0000000..227bc2f --- /dev/null +++ b/yoRadio/src/displays/displayILI9341.cpp @@ -0,0 +1,372 @@ +#include "../../options.h" +#if DSP_MODEL==DSP_ILI9341 + +#include "displayILI9341.h" +#include +#include "fonts/bootlogo.h" +#include "../../player.h" +#include "../../config.h" +#include "../../network.h" + +const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"}; +const char *mnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"}; + +DspCore::DspCore(): Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST) { + +} + +char* DspCore::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 DspCore::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 DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { + //begin(26000000L); /*багиловим*/ + begin(); /* SPI_DEFAULT_FREQ 40000000 */ + invertDisplay(TFT_INVERT); + cp437(true); + fillScreen(TFT_BG); + setRotation(TFT_ROTATE); + setTextWrap(false); + setTextSize(1); + screenwidth = width(); + screenheight = height(); + swidth = screenwidth; + sheight = screenheight; +} + +void DspCore::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 DspCore::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 DspCore::clearDsp() { + fillScreen(TFT_BG); +} + +void DspCore::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 DspCore::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 DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { + fillRect(0, texttop, swidth, textheight, bg); +} + +void DspCore::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 DspCore::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 DspCore::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 DspCore::printClock(const char* timestr) { + +} + +uint16_t cltop = 0; +uint8_t clsp = 24; +uint16_t clleft = 0; +uint16_t clwidth = 0; + +void DspCore::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); + clleft=swidth-TFT_FRAMEWDT-clwidth; + //fillRect(swidth-TFT_FRAMEWDT-clwidth, cltop-hot, clwidth, hot+3, TFT_BG); + setCursor(clleft, cltop); + setTextColor(TFT_BG); + print(oldTimeBuf); + 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 DspCore::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; + uint16_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; + + setTextColor(TFT_BG); + sprintf(volstr, "%d", oldVolume); + getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv); + setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv); + print(volstr); + + setTextColor(TFT_FG); + 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); + oldVolume=config.store.volume; + setFont(); + } +} + +void DspCore::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 DspCore::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 DspCore::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 DspCore::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 DspCore::set_TextSize(uint8_t s) { + setTextSize(s); +} + +void DspCore::set_TextColor(uint16_t fg, uint16_t bg) { + setTextColor(fg, bg); +} + +void DspCore::set_Cursor(int16_t x, int16_t y) { + setCursor(x, y); +} + +void DspCore::printText(const char* txt) { + print(txt); +} + +void DspCore::loop() { + +} + +#endif diff --git a/yoRadio/src/displays/displayILI9341.h b/yoRadio/src/displays/displayILI9341.h new file mode 100644 index 0000000..b18bab4 --- /dev/null +++ b/yoRadio/src/displays/displayILI9341.h @@ -0,0 +1,99 @@ +#ifndef displayILI9341_h +#define displayILI9341_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 4 +#define SCROLLTIME 60 + +#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 DspCore: public Adafruit_ILI9341 { + public: + DspCore(); + 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]; + uint8_t oldVolume; + uint16_t wot, hot; + +}; + +extern DspCore 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/displayLC1602.cpp b/yoRadio/src/displays/displayLC1602.cpp index 88e4fca..04b362a 100644 --- a/yoRadio/src/displays/displayLC1602.cpp +++ b/yoRadio/src/displays/displayLC1602.cpp @@ -1,6 +1,6 @@ #include "../../options.h" -#if DSP_MODEL==DSP_1602I2C +#if DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_1602 #include "displayLC1602.h" #include "../../player.h" @@ -13,10 +13,15 @@ const byte controlspaces[] = { CLOCK_SPACE, VOL_SPACE }; +#if DSP_MODEL==DSP_1602I2C DspCore::DspCore(): LiquidCrystal_I2C(SCREEN_ADDRESS, 16, 2, I2C_SDA, I2C_SCL) { } +#else +DspCore::DspCore(): LiquidCrystal(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7) { +} +#endif void DspCore::apScreen() { setCursor(0,0); print("YORADIO AP MODE"); @@ -25,8 +30,12 @@ void DspCore::apScreen() { } void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { +#if DSP_MODEL==DSP_1602I2C init(); backlight(); +#else + begin(16, 2); +#endif screenwidth = 16; screenheight = 2; swidth = screenwidth; diff --git a/yoRadio/src/displays/displayLC1602.h b/yoRadio/src/displays/displayLC1602.h index 322f775..73b4563 100644 --- a/yoRadio/src/displays/displayLC1602.h +++ b/yoRadio/src/displays/displayLC1602.h @@ -2,8 +2,11 @@ #define displayLC1602_h #include "Arduino.h" +#if DSP_MODEL==DSP_1602I2C #include "../LiquidCrystalI2C/LiquidCrystalI2CEx.h" - +#else +#include +#endif #define TFT_LINEHGHT 1 #define TFT_FRAMEWDT 0 @@ -21,7 +24,11 @@ #define BOOTSTR_TOP1 1 #define STARTTIME_PL 2000 +#if DSP_MODEL==DSP_1602I2C class DspCore: public LiquidCrystal_I2C { +#else +class DspCore: public LiquidCrystal { +#endif public: bool fillSpaces; DspCore(); diff --git a/yoRadio/src/displays/displaySH1106.cpp b/yoRadio/src/displays/displaySH1106.cpp index de6b11e..b6c9be7 100644 --- a/yoRadio/src/displays/displaySH1106.cpp +++ b/yoRadio/src/displays/displaySH1106.cpp @@ -1,5 +1,5 @@ #include "../../options.h" -#if DSP_MODEL==5 +#if DSP_MODEL==DSP_SH1106 || DSP_MODEL==DSP_SH1107 #include "displaySH1106.h" #include @@ -29,11 +29,15 @@ const unsigned char logo [] PROGMEM= }; TwoWire I2CSH1106 = TwoWire(0); - +#if DSP_MODEL==DSP_SH1106 DspCore::DspCore(): Adafruit_SH1106G(128, 64, &I2CSH1106, -1) { } +#else +DspCore::DspCore(): Adafruit_SH1107(64, 128, &I2CSH1106, -1) { +} +#endif char* DspCore::utf8Rus(const char* str, bool uppercase) { int index = 0; static char strn[BUFLEN]; @@ -127,14 +131,22 @@ void DspCore::apScreen() { } void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { - I2CSH1106.begin(I2C_SDA, I2C_SCL, (uint32_t)100000); + I2CSH1106.begin(I2C_SDA, I2C_SCL); if (!begin(SCREEN_ADDRESS, true)) { - Serial.println(F("SH1106 allocation failed")); + Serial.println(F("SH110X allocation failed")); for (;;); // Don't proceed, loop forever } cp437(true); fillScreen(TFT_BG); - setRotation(TFT_ROTATE); + byte tftRotate = TFT_ROTATE; +#if DSP_MODEL==DSP_SH1106 + if(tftRotate>2) tftRotate=2; + if(tftRotate==1) tftRotate=0; +#else + if(tftRotate>=2) tftRotate=3; + if(tftRotate==0) tftRotate=1; +#endif + setRotation(tftRotate); setTextWrap(false); screenwidth = width(); screenheight = height(); diff --git a/yoRadio/src/displays/displaySH1106.h b/yoRadio/src/displays/displaySH1106.h index f261260..f1c96ce 100644 --- a/yoRadio/src/displays/displaySH1106.h +++ b/yoRadio/src/displays/displaySH1106.h @@ -10,14 +10,18 @@ #define PLMITEMS 7 #define PLMITEMLENGHT 40 -#define PLMITEMHEIGHT 9 +#define PLMITEMHEIGHT 10 #define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT #define PLCURRENT_SIZE 1 #define TFT_FULLTIME 1 -#define SCROLLDELTA 5 -#define SCROLLTIME 110 +#define SCROLLDELTA 3 +#define SCROLLTIME 83 +#if DSP_MODEL==DSP_SH1106 class DspCore: public Adafruit_SH1106G { +#else +class DspCore: public Adafruit_SH1107 { +#endif public: DspCore(); char plMenu[PLMITEMS][PLMITEMLENGHT]; diff --git a/yoRadio/src/displays/displaySSD1305.cpp b/yoRadio/src/displays/displaySSD1305.cpp new file mode 100644 index 0000000..015f93f --- /dev/null +++ b/yoRadio/src/displays/displaySSD1305.cpp @@ -0,0 +1,322 @@ +#include "../../options.h" +#if DSP_MODEL==DSP_SSD1305 + +#include "displaySSD1305.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 +}; + +DspCore::DspCore(): Adafruit_SSD1305(128, 64, &SPI, TFT_DC, TFT_RST, TFT_CS, 7000000UL) { + +} + +char* DspCore::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 DspCore::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 DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { + if (!begin(SCREEN_ADDRESS)) { + 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 DspCore::drawLogo() { + clearDisplay(); + drawBitmap( + (width() - LOGO_WIDTH ) / 2, + 8, + logo, LOGO_WIDTH, LOGO_HEIGHT, 1); + display(); +} + +void DspCore::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) + 2, swidth, PLMITEMHEIGHT-1, 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 DspCore::clearDsp() { + fillScreen(TFT_BG); +} + +void DspCore::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 DspCore::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 DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { + fillRect(0, texttop, swidth, textheight, bg); +} + +void DspCore::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 DspCore::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 DspCore::displayHeapForDebug() { + +} + +void DspCore::printClock(const char* timestr) { + setTextSize(2); + centerText(timestr, 34, TFT_FG, TFT_BG); + setTextSize(1); +} + +void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw) { + char timeStringBuff[20] = { 0 }; + strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M:%S", &timeinfo); + setTextSize(2); + centerText(timeStringBuff, 30, TFT_FG, TFT_BG); + +} + +void DspCore::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, 22, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, 22); + print(volstr); + } +} + +void DspCore::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, 22, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, 22); + print(numstr); +} + +void DspCore::frameTitle(const char* str) { + setTextSize(1); + centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG); +} + +void DspCore::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 DspCore::ip(const char* str) { + int16_t vTop = sheight - TFT_LINEHGHT - 4; + setTextSize(1); + setTextColor(SILVER, TFT_BG); + setCursor(0, vTop); + print(str); +} + +void DspCore::set_TextSize(uint8_t s) { + setTextSize(s); +} + +void DspCore::set_TextColor(uint16_t fg, uint16_t bg) { + setTextColor(fg, bg); +} + +void DspCore::set_Cursor(int16_t x, int16_t y) { + setCursor(x, y); +} + +void DspCore::printText(const char* txt) { + print(txt); +} + +void DspCore::loop() { + if (checkdelay(FPS, loopdelay)) { + display(); + } + yield(); +} + +boolean DspCore::checkdelay(int m, unsigned long &tstamp) { + if (millis() - tstamp > m) { + tstamp = millis(); + return true; + } else { + return false; + } +} + +#endif diff --git a/yoRadio/src/displays/displaySSD1305.h b/yoRadio/src/displays/displaySSD1305.h new file mode 100644 index 0000000..97796e0 --- /dev/null +++ b/yoRadio/src/displays/displaySSD1305.h @@ -0,0 +1,68 @@ +#ifndef displaySSD1305_h +#define displaySSD1305_h + +#include "Arduino.h" +#include +#include + +#define TFT_LINEHGHT 8 +#define TFT_FRAMEWDT 0 + +#define PLMITEMS 7 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 10 +#define META_SIZE 1 +#define TITLE_TOP1 TFT_FRAMEWDT + TFT_LINEHGHT +#define TITLE_TOP2 TFT_FRAMEWDT + 2 * TFT_LINEHGHT +#define PLCURRENT_SIZE 1 +#define TFT_FULLTIME 1 +#define SCROLLDELTA 3 +#define SCROLLTIME 83 +#define FPS 50 + +class DspCore: public Adafruit_SSD1305 { + public: + DspCore(); + 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 DspCore dsp; + +/* + * TFT COLORS + */ +#define SILVER WHITE +#define TFT_BG BLACK +#define TFT_FG WHITE +#define TFT_LOGO WHITE + +#endif diff --git a/yoRadio/src/displays/displaySSD1306.cpp b/yoRadio/src/displays/displaySSD1306.cpp index d53dd72..776591f 100644 --- a/yoRadio/src/displays/displaySSD1306.cpp +++ b/yoRadio/src/displays/displaySSD1306.cpp @@ -129,7 +129,7 @@ void DspCore::apScreen() { } void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { - I2CSSD1306.begin(I2C_SDA, I2C_SCL, (uint32_t)100000); + I2CSSD1306.begin(I2C_SDA, I2C_SCL); if (!begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) { Serial.println(F("SSD1306 allocation failed")); for (;;); // Don't proceed, loop forever diff --git a/yoRadio/src/displays/displaySSD1327.cpp b/yoRadio/src/displays/displaySSD1327.cpp new file mode 100644 index 0000000..95cda2f --- /dev/null +++ b/yoRadio/src/displays/displaySSD1327.cpp @@ -0,0 +1,334 @@ +#include "../../options.h" +#if DSP_MODEL==DSP_SSD1327 + +#include "displaySSD1327.h" +#include +#include "fonts/bootlogo.h" +#include "../../player.h" +#include "../../config.h" +#include "../../network.h" + +#ifndef SCREEN_ADDRESS +#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda +#endif + + +#ifndef I2CFREQ_HZ +#define I2CFREQ_HZ 4000000UL +#endif + +TwoWire tw = TwoWire(0); + +DspCore::DspCore(): Adafruit_SSD1327(128, 128, &tw, I2C_RST, I2CFREQ_HZ) { + +} + +char* DspCore::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 DspCore::apScreen() { + setTextSize(1); + setTextColor(TFT_FG, TFT_BG); + setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + ((DSP_MODEL==DSP_SSD1306)?2:1) * TFT_LINEHGHT); + print("AP NAME: "); + print(apSsid); + setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + ((DSP_MODEL==DSP_SSD1306)?3:2) * 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 DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { + tw.begin(I2C_SDA, I2C_SCL); + if (!begin(SCREEN_ADDRESS)) { + Serial.println(F("SSD1327 allocation failed")); + for (;;); + } + cp437(true); + fillScreen(TFT_BG); + setRotation(TFT_ROTATE); + setTextWrap(false); + screenwidth = width(); + screenheight = height(); + swidth = screenwidth; + sheight = screenheight; + setClockBounds(); +} + +void DspCore::drawLogo() { + drawRGBBitmap((swidth - 99) / 2, 18, bootlogo2, 99, 64); +} + +#define CLR_ITEM1 0xA +#define CLR_ITEM2 0x8 +#define CLR_ITEM3 0x5 +void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) { + for (byte i = 0; i < PLMITEMS; i++) { + plMenu[i][0] = '\0'; + } + config.fillPlMenu(plMenu, currentItem - 3, 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 (abs(i - 3) == 3) setTextColor(CLR_ITEM3, TFT_BG); + if (abs(i - 3) == 2) setTextColor(CLR_ITEM2, TFT_BG); + if (abs(i - 3) == 1) setTextColor(CLR_ITEM1, TFT_BG); + if (i == 3) { + strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1); + } else { + setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); + print(utf8Rus(plMenu[i], true)); + } + } + //display(); +} + +void DspCore::clearDsp() { + fillScreen(TFT_BG); + //clearDisplay(); +} + +void DspCore::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 DspCore::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 DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { + fillRect(0, texttop, swidth, textheight, bg); +} + +void DspCore::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); + setCursor((swidth - w) / 2, y); + fillRect(0, y, swidth, h, bg); + print(txt); +} + +void DspCore::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); + display(); +} + +void DspCore::displayHeapForDebug() { + +} + +void DspCore::setClockBounds(){ + setFont(&DS_DIGI28pt7b); + setTextSize(1); + getTextBounds("88:88", 0, 0, &x, &y, &cwidth, &cheight); + uint16_t header = TFT_FRAMEWDT + 4 * TFT_LINEHGHT; + uint16_t footer = TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 5; + clockY = header + (sheight - header - footer) / 2 - cheight / 2; + setFont(); +} + +void DspCore::printClock(const char* timestr) { + uint16_t ncwidth, ncheight; + setFont(&DS_DIGI28pt7b); + setTextSize(1); + getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot); + setCursor((swidth - wot) / 2 - 4, clockY+28+6); + setTextColor(TFT_BG); + print(oldTimeBuf); + strlcpy(oldTimeBuf, timestr, 20); + setTextColor(TFT_LOGO); + //fillRect(0, clockY, swidth, cheight + 3, TFT_BG); + getTextBounds(timestr, 0, 0, &x, &y, &ncwidth, &ncheight); + setCursor((swidth - ncwidth) / 2 - 4, clockY+28+6); + print(timestr); + setFont(); + display(); +} + +void DspCore::drawVolumeBar(bool withNumber) { + int16_t vTop = sheight - TFT_FRAMEWDT * 2; + int16_t vWidth = swidth - TFT_FRAMEWDT - 4; + 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 (withNumber) { + setTextSize(1); + setTextColor(TFT_FG); + setFont(&DS_DIGI28pt7b); + 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, 48, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, 48 + hv); + print(volstr); + setFont(); + display(); + } +} + +void DspCore::drawNextStationNum(uint16_t num) { + setTextSize(1); + setTextColor(TFT_FG); + setFont(&DS_DIGI28pt7b); + 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, 48, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG); + setCursor((swidth - wv) / 2, 48 + hv); + print(numstr); + setFont(); +} + +void DspCore::frameTitle(const char* str) { + setTextSize(2); + centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG); +} + +void DspCore::rssi(const char* str) { + int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2; + setTextSize(1); + rightText(str, vTop, SILVER, TFT_BG); +} + +void DspCore::ip(const char* str) { + int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2; + setTextSize(1); + setTextColor(SILVER, TFT_BG); + setCursor(4, vTop); + print(str); +} + +void DspCore::set_TextSize(uint8_t s) { + setTextSize(s); +} + +void DspCore::set_TextColor(uint16_t fg, uint16_t bg) { + setTextColor(fg, bg); +} + +void DspCore::set_Cursor(int16_t x, int16_t y) { + setCursor(x, y); +} + +void DspCore::printText(const char* txt) { + print(txt); +} + +void DspCore::loop() { + if (checkdelay(LOOP_DELAY, loopdelay)) { + display(); + } + yield(); +} + +boolean DspCore::checkdelay(int m, unsigned long &tstamp) { + if (millis() - tstamp > m) { + tstamp = millis(); + return true; + } else { + return false; + } +} + +#endif diff --git a/yoRadio/src/displays/displaySSD1327.h b/yoRadio/src/displays/displaySSD1327.h new file mode 100644 index 0000000..3f72776 --- /dev/null +++ b/yoRadio/src/displays/displaySSD1327.h @@ -0,0 +1,71 @@ +#ifndef displaySSD1327_h +#define displaySSD1327_h + +#include "Arduino.h" +#include +#include +#include "fonts/DS_DIGI28pt7b.h" + +#define TFT_LINEHGHT 10 +#define TFT_FRAMEWDT 4 + +#define PLMITEMS 7 +#define PLMITEMLENGHT 40 +#define PLMITEMHEIGHT 22 +#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT + +#define SCROLLDELTA 4 +#define SCROLLTIME 83 +#define LOOP_DELAY 83 + +class DspCore: public Adafruit_SSD1327 { + public: + bool fillSpaces; + DspCore(); + 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 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; + int16_t x, y; + uint16_t cwidth, cheight; + unsigned long loopdelay; + char oldTimeBuf[20]; + uint16_t wot, hot; + boolean checkdelay(int m, unsigned long &tstamp); + void setClockBounds(); +}; + +extern DspCore dsp; + +/* + * TFT COLORS + */ +#define SILVER 0x7 +#define TFT_BG 0x0 +#define TFT_FG 0x8 +#define TFT_LOGO 0xF + +#endif diff --git a/yoRadio/src/displays/displayST7735.h b/yoRadio/src/displays/displayST7735.h index 7cfb9fa..a31bc20 100644 --- a/yoRadio/src/displays/displayST7735.h +++ b/yoRadio/src/displays/displayST7735.h @@ -13,6 +13,10 @@ #define PLMITEMLENGHT 40 #define PLMITEMHEIGHT 22 #define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT +#define TITLE_FG2 SILVER + +#define SCROLLDELTA 3 +#define SCROLLTIME 67 class DspCore: public Adafruit_ST7735 { public: diff --git a/yoRadio/src/displays/displayST7789.cpp b/yoRadio/src/displays/displayST7789.cpp index 534ea14..f2b7d44 100644 --- a/yoRadio/src/displays/displayST7789.cpp +++ b/yoRadio/src/displays/displayST7789.cpp @@ -8,10 +8,14 @@ #include "../../config.h" #include "../../network.h" +#ifndef DEF_SPI_FREQ +#define DEF_SPI_FREQ 40000000UL /* set it to 0 for system default */ +#endif + const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"}; const char *mnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"}; -DspCore::DspCore(): Adafruit_ST7789(&SPI, TFT_CS, TFT_DC, TFT_RST) { +DspCore::DspCore(): Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST) { } @@ -110,6 +114,7 @@ void DspCore::apScreen() { void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) { init(240,320); + if(DEF_SPI_FREQ > 0) setSPISpeed(DEF_SPI_FREQ); invertDisplay(TFT_INVERT); cp437(true); fillScreen(TFT_BG); @@ -144,6 +149,7 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) { setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT); print(utf8Rus(plMenu[i], true)); } + yield(); } } @@ -155,6 +161,7 @@ void DspCore::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); + yield(); } void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) { @@ -170,6 +177,7 @@ void DspCore::getScrolBbounds(const char* text, const char* separator, byte text void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) { fillRect(0, texttop, swidth, textheight, bg); + yield(); } void DspCore::centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg) { @@ -183,6 +191,7 @@ void DspCore::centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg) setCursor((swidth - w) / 2, y); fillRect((swidth-w)/2-5, y, w+10, h, bg); print(txt); + yield(); } void DspCore::rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, bool fliprect, uint16_t delta) { @@ -193,6 +202,7 @@ void DspCore::rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, setCursor(swidth - w - TFT_FRAMEWDT - delta, y); fillRect(swidth - w - TFT_FRAMEWDT, fliprect?y-h:y, w, h, bg); print(text); + yield(); } void DspCore::displayHeapForDebug() { @@ -213,6 +223,7 @@ void DspCore::displayHeapForDebug() { byte sbw = map(aprcnt, 0, 100 , 0, swidth); fillRect(0, sheight - 2, sbw, 2, SILVER); #endif + yield(); } void DspCore::printClock(const char* timestr) { @@ -238,11 +249,13 @@ void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){ clwidth = wot+clsp+(swidth>240?46:34); fillRect(swidth-TFT_FRAMEWDT-clwidth, cltop-hot, clwidth, hot+3, TFT_BG); strlcpy(oldTimeBuf, timeBuf, 20); + setTextSize(1); 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); + setTextSize(1); print(timeBuf); setFont(); @@ -264,13 +277,14 @@ void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){ setCursor(clleft+wot+clsp, cltop-hot+1); sprintf(timeBuf, "%02d", timeinfo.tm_sec); print(timeBuf); + yield(); } void DspCore::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); + uint16_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); @@ -294,6 +308,7 @@ void DspCore::drawVolumeBar(bool withNumber) { print(volstr); setFont(); } + yield(); } void DspCore::drawNextStationNum(uint16_t num) { @@ -349,6 +364,7 @@ void DspCore::set_Cursor(int16_t x, int16_t y) { void DspCore::printText(const char* txt) { print(txt); + yield(); } void DspCore::loop() { diff --git a/yoRadio/yoRadio.ino b/yoRadio/yoRadio.ino index a408b22..bbefcf2 100644 --- a/yoRadio/yoRadio.ino +++ b/yoRadio/yoRadio.ino @@ -11,6 +11,13 @@ #include "controls.h" #include "mqtt.h" +#if CORE_FOR_LOOP_CONTROLS != 255 +TaskHandle_t TaskCore0; +#ifndef CORE_FOR_LOOP_STACK_SIZE +#define CORE_FOR_LOOP_STACK_SIZE 16384 +#endif +#endif + void setup() { Serial.begin(115200); pinMode(LED_BUILTIN, OUTPUT); @@ -18,30 +25,66 @@ void setup() { config.init(); display.init(); player.init(); - player.setOutputPins(false); network.begin(); if (network.status != CONNECTED) { netserver.begin(); display.start(); return; } - initControls(); netserver.begin(); telnet.begin(); player.setVol(config.store.volume, true); - display.start(); - if(config.store.smartstart==1) player.play(config.store.lastStation); + if(CORE_FOR_LOOP_CONTROLS == 255){ + initControls(); + display.start(); + } + #ifdef MQTT_HOST mqttInit(); #endif + +#if CORE_FOR_LOOP_CONTROLS != 255 + BaseType_t coreId = xPortGetCoreID(); + BaseType_t newCoreId = (CORE_FOR_LOOP_CONTROLS==2)?!coreId:CORE_FOR_LOOP_CONTROLS; + xTaskCreatePinnedToCore( + loopCore0, /* Task function. */ + "TaskCore0", /* name of task. */ + CORE_FOR_LOOP_STACK_SIZE, /* Stack size of task */ + NULL, /* parameter of the task */ + 1, /* priority of the task */ + &TaskCore0, /* Task handle to keep track of created task */ + newCoreId); /* pin task to core CORE_FOR_LOOP_CONTROLS */ +#endif + + delay(500); + if(config.store.smartstart==1) player.play(config.store.lastStation); } +#if CORE_FOR_LOOP_CONTROLS != 255 +void setupCore0(){ + initControls(); + display.start(); +} + +void loopCore0( void * pvParameters ){ + setupCore0(); + for(;;){ + micros(); + if (network.status == CONNECTED) { + loopControls(); + } + display.loop(); + vTaskDelay(10); + } +} +#endif + void loop() { if (network.status == CONNECTED) { telnet.loop(); player.loop(); - loopControls(); + if(CORE_FOR_LOOP_CONTROLS==255) loopControls(); } - display.loop(); + if(CORE_FOR_LOOP_CONTROLS==255 || network.status != CONNECTED) display.loop(); netserver.loop(); }