diff --git a/README.md b/README.md
index a41d439..a57ed06 100644
--- a/README.md
+++ b/README.md
@@ -17,6 +17,7 @@
- [More features](#more-features)
- [Plugins](#plugins)
- [Version history](#version-history)
+- [Описание на 4PDA](https://4pda.to/forum/index.php?s=&showtopic=1010378&view=findpost&p=112992611)
---
@@ -293,11 +294,26 @@ download _http://\/data/playlist.csv_ and _http://\/data
---
## Plugins
-At the moment, you can display additional information on the display by writing a few additional functions. There is no documentation yet, you will have to deal with the examples, which is in directory [exsamples/plugins/](https://github.com/e2002/yoradio/tree/main/exsamples/plugins).\
+There is no documentation yet, you will have to deal with the examples, which is in directory [exsamples/plugins/](https://github.com/e2002/yoradio/tree/main/exsamples/plugins).\
Work is in progress...
---
## Version history
+#### v0.8.00b
+- rewritten the display engine
+- added the ability to position widgets on the display using configuration files. More info in yoRadio/src/displays/conf/ and here https://github.com/e2002/yoradio/wiki/Widgets
+- the VU_PARAMS3 parameter is deprecated. VUmeter configuration is done through yoRadio/src/displays/conf/ configs
+- added bitrate display on displays
+- added the ability to display the weather on all displays except LCD1602
+- examples of plug-ins related to displaying information on the display are outdated and no longer work. The examples have been removed from the exsamples/plugins folder.
+- the structure of the project files has been changed so that I don’t know what.
+- localization of information displayed on the display (rus, en). Option L10N_LANGUAGE (EN by default. see exsamples/myoptions.h for details)
+- changes in mytheme.h . Added colors COLOR_STATION_BG, COLOR_STATION_FILL, COLOR_BITRATE
+- optimization, refactoring
+- bugs fixes
+- bugs adding
+- probably something else that I forgot .__.
+
#### v0.7.540
- fixed compilation error when using NEXTION display with DUMMY display
diff --git a/exsamples/myoptions.h b/exsamples/myoptions.h
index dc5bb8b..f429d60 100644
--- a/exsamples/myoptions.h
+++ b/exsamples/myoptions.h
@@ -9,8 +9,10 @@ Uncomment the lines you need, to override the default value and set the values a
The connection tables are located here https://github.com/e2002/yoradio#connection-tables
********************************************************/
+#define LED_BUILTIN 2 /* Onboard LED Pin */
+#define L10N_LANGUAGE EN /* Language (EN, RU). More info in yoRadio/locale/displayL10n_(en|ru).h
-/* DSP_MODEL. See description/available values in the options.h file */
+/* DSP_MODEL. See description/available values in https://github.com/e2002/yoradio/wiki/Available-display-models */
/* This option is required. Use DSP_DUMMY if no display is connected */
#define DSP_MODEL DSP_DUMMY
/*
@@ -58,7 +60,7 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
//#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_INTERNALPULLUP true /* Enabl//#define LED_BUILTIN 2 /* LED Pin */e the weak pull up resistors */
//#define ENC_HALFQUARD true /* Experiment with it */
/******************************************/
@@ -108,7 +110,6 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
/* INITR_MINI160x80 // 0.96' 160x80 ST7735S https://???? */
/* INITR_GREENTAB */
/* INITR_REDTAB */
-//#define LED_BUILTIN 2 /* LED Pin */
//#define MUTE_PIN 255 /* MUTE Pin */
//#define MUTE_VAL HIGH /* Write this to MUTE_PIN when player is stopped */
//#define BRIGHTNESS_PIN 255 /* Pin for adjusting the brightness of the display (output 0 - 3v3) */
diff --git a/exsamples/mytheme.h b/exsamples/mytheme.h
index cdb7fcd..b8ac569 100644
--- a/exsamples/mytheme.h
+++ b/exsamples/mytheme.h
@@ -17,7 +17,9 @@
/* | color name | R G B | */
/*----------------------------------------------------------------------------------------------------------------*/
//#define COLOR_BACKGROUND 255, 255, 0 /* background */
-//#define COLOR_STATION_NAME 91, 118, 255 /* station name */
+//#define COLOR_STATION_NAME 0, 0, 0 /* station name */
+//#define COLOR_STATION_BG 91, 118, 255 /* station name background */
+//#define COLOR_STATION_FILL 231, 211, 90 /* station name fill background */
//#define COLOR_SNG_TITLE_1 255, 0, 0 /* first title */
//#define COLOR_SNG_TITLE_2 0, 0, 0 /* second title */
//#define COLOR_WEATHER 255, 0, 216 /* weather string */
@@ -36,6 +38,7 @@
//#define COLOR_VOLBAR_IN 189, 189, 189 /* volume bar fill */
//#define COLOR_DIGITS 100, 100, 255 /* volume / station number */
//#define COLOR_DIVIDER 0, 255, 0 /* divider color (DSP_ST7789, DSP_ILI9341, DSP_ILI9225) */
+//#define COLOR_BITRATE 231, 211, 90 /* bitrate */
//#define COLOR_PLAYLIST_0 255, 0, 0 /* playlist string 0 */
//#define COLOR_PLAYLIST_1 0, 255, 0 /* playlist string 1 */
//#define COLOR_PLAYLIST_2 255, 0, 255 /* playlist string 2 */
diff --git a/exsamples/plugins/analogvolume.ino b/exsamples/plugins/analogvolume.ino
deleted file mode 100644
index 02c1fb0..0000000
--- a/exsamples/plugins/analogvolume.ino
+++ /dev/null
@@ -1,16 +0,0 @@
-/**************************************************************
-
- An example of volume control using an analog resistor.
- This file must be in the root directory of the sketch.
-
-**************************************************************/
-const uint8_t volume_pin = 34;
-
-void ctrls_on_loop() {
- static uint32_t prevVolPinMillis;
- uint16_t volPinVal = map(analogRead(volume_pin), 0, 4095, 0, 254);
- if((abs(volPinVal-config.store.volume)>2) && (millis()-prevVolPinMillis>300)){ /* simple debounce */
- player.setVol(volPinVal, false);
- prevVolPinMillis=millis();
- }
-}
diff --git a/exsamples/plugins/displayhandlers.ino b/exsamples/plugins/displayhandlers.ino
deleted file mode 100644
index 9e410b2..0000000
--- a/exsamples/plugins/displayhandlers.ino
+++ /dev/null
@@ -1,202 +0,0 @@
-/**************************************************************
-
- An example of displaying user information on the display.
- This file must be in the root directory of the sketch.
-
-**************************************************************/
-
-#if (DSP_MODEL==DSP_ST7735) || (DSP_MODEL==DSP_ST7789) || (DSP_MODEL==DSP_SSD1327) || (DSP_MODEL==DSP_ILI9341) || (DSP_MODEL==DSP_ILI9225)
-
-#define WEATHER_REQUEST_INTERVAL 1800 //30min
-#define WEATHER_REQUEST_INTERVAL_FAULTY 30
-
-#include
-#include
-
-const char* host = "api.openweathermap.org";
-const char* lat = "55.7512";
-const char* lon = "37.6184";
-const char* key = "********************************";
-
-Ticker ticker;
-
-/***********************************************
- scrolled line
- ***********************************************/
-Scroll hello;
-
-char weather[254] = { 0 };
-bool weatherRequest = false;
-TaskHandle_t weatherUpdateTaskHandle;
-
-bool getForecast() {
- WiFiClient client;
- if (!client.connect(host, 80)) {
- Serial.println("## OPENWEATHERMAP ###: connection failed");
- return false;
- }
- char httpget[250] = {0};
- sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=metric&lang=ru&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", lat, lon, key, host);
- client.print(httpget);
- unsigned long timeout = millis();
- while (client.available() == 0) {
- if (millis() - timeout > 2000UL) {
- Serial.println("## OPENWEATHERMAP ###: client available timeout !");
- client.stop();
- return false;
- }
- }
- timeout = millis();
- String line = "";
- if (client.connected()) {
- while (client.available())
- {
- line = client.readStringUntil('\n');
- if (strstr(line.c_str(), "\"temp\"") != NULL) {
- client.stop();
- break;
- }
- if ((millis() - timeout) > 500)
- {
- client.stop();
- Serial.println("## OPENWEATHERMAP ###: client read timeout !");
- return false;
- }
- }
- }
- if (strstr(line.c_str(), "\"temp\"") == NULL) {
- Serial.println("## OPENWEATHERMAP ###: weather not found !");
- return false;
- }
- char *tmpe;
- char *tmps;
- const char* cursor = line.c_str();
- char desc[120], temp[20], hum[20], press[20];
-
- tmps = strstr(cursor, "\"description\":\"");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
- tmps += 15;
- tmpe = strstr(tmps, "\",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
- strlcpy(desc, tmps, tmpe - tmps + 1);
- cursor = tmpe + 3;
-
- tmps = strstr(cursor, "\"temp\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
- tmps += 7;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
- strlcpy(temp, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
- float tempf = atof(temp);
-
- tmps = strstr(cursor, "\"pressure\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
- tmps += 11;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
- strlcpy(press, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
- int pressi = (float)atoi(press) / 1.333;
-
- tmps = strstr(cursor, "humidity\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
- tmps += 10;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
- strlcpy(hum, tmps, tmpe - tmps + 1);
-
- Serial.printf("## OPENWEATHERMAP ###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
- sprintf(weather, "%s, %.1f C * давление: %d мм * влажность: %s%%", desc, tempf, pressi, hum);
-
- return true;
-
-}
-
-void getWeather( void * pvParameters ) {
- delay(200);
- if (getForecast()) {
- weatherRequest = true;
- ticker.detach();
- ticker.attach(WEATHER_REQUEST_INTERVAL, updateWeather);
- } else {
- ticker.detach();
- ticker.attach(WEATHER_REQUEST_INTERVAL_FAULTY, updateWeather);
- }
- vTaskDelete( NULL );
-}
-
-void updateWeather() {
- xTaskCreatePinnedToCore(
- getWeather, /* Task function. */
- "getWeather1", /* name of task. */
- 1024 * 4, /* Stack size of task */
- NULL, /* parameter of the task */
- 0, /* priority of the task */
- &weatherUpdateTaskHandle, /* Task handle to keep track of created task */
- 0); /* pin task to core CORE_FOR_LOOP_CONTROLS */
-}
-
-/***********************************************
- Occurs when the network is connected
- ***********************************************/
-void network_on_connect() {
- updateWeather();
-}
-
-/*********************************************************************************************
- The display has initialized, the network is connected, the player is ready to play.
- DspCore class documentation is missing :^( See the source in src/displays
- *********************************************************************************************/
-void dsp_on_start(DspCore *dsp) {
-
-}
-
-/***********************************************
- Occurs when the display is initialized
- ***********************************************/
-void dsp_on_init() {
- if (DSP_MODEL == DSP_ST7735 || (DSP_MODEL == DSP_SSD1327)) {
- hello.init(5, " * ", 1, TFT_LINEHGHT * 4 + 6, 0, config.theme.weather, config.theme.background);
- }else if(DSP_MODEL == DSP_ILI9225){
- hello.init(5, " * ", 1, TFT_LINEHGHT * 6 + 5, 0, config.theme.weather, config.theme.background);
- } else {
- hello.init(5, " * ", 2, TFT_LINEHGHT * 9 + 5, 0, config.theme.weather, config.theme.background);
- }
-}
-
-/************************
- The loop cycle
- ************************/
-void dsp_on_loop(DspCore *dsp) {
- if (weatherRequest) {
- weatherRequest = false;
- hello.setText(dsp->utf8Rus(weather, true));
- }
- if (display.mode == PLAYER) hello.loop();
-}
-
-/***********************************************
- Occurs when the display changes mode
- ***********************************************/
-void dsp_on_newmode(displayMode_e newmode) {
- if (newmode == PLAYER) {
- hello.reset();
- } else {
- hello.lock();
- }
-}
-
-/************************
- Before print the clock
- ************************/
-bool dsp_before_clock(DspCore *dsp, bool dots) {
- if (display.mode == PLAYER) {
- dsp->setFont();
- dsp->setTextSize(1);
- display.centerText(dsp->utf8Rus("Hello from plugin!", true), display.screenheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2, 0xF97F, config.theme.background);
- }
- return true; // false, if you need to disable the drawing of the clock
-}
-
-#endif
diff --git a/exsamples/plugins/rssibitrate.ino b/exsamples/plugins/rssibitrate.ino
deleted file mode 100644
index 37b1b2e..0000000
--- a/exsamples/plugins/rssibitrate.ino
+++ /dev/null
@@ -1,34 +0,0 @@
-/**************************************************************
-
- An example of replase a RSSI to bitrate information alternately.
- This file must be in the root directory of the sketch.
-
-**************************************************************/
-
-#if DSP_MODEL==DSP_ILI9225 /* your DSP_MODEL */
-
-bool dsp_before_rssi(DspCore *dsp){
- static int8_t cnt;
- int16_t x1, y1;
- char buf[20]; /* buffer for the bitrate string */
- uint16_t w, h; /* width & height of the bitrate string */
- int16_t vTop = dsp->height() - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2; /* vTop - Y cordnate of the bitrate string */;
- sprintf(buf, "RSSI:000dBm");
- dsp->setTextSize(1);
- dsp->getTextBounds(buf, 0, 0, &x1, &y1, &w, &h);
- dsp->fillRect(dsp->width() - w - TFT_FRAMEWDT /* left */, vTop /* top */, w /* width */, TFT_LINEHGHT-2 /* height */, config.theme.background /* background color */);
- sprintf(buf, "%dkBits", config.station.bitrate);
- dsp->getTextBounds(buf, 0, 0, &x1, &y1, &w, &h);
- if(cnt<2){
- cnt++;
- return true; /* print RSSI and retrn */
- }
- cnt++;
- if(cnt>3) cnt=0;
- dsp->setTextColor(config.theme.rssi,config.theme.background);
- dsp->setCursor(dsp->width() - w - TFT_FRAMEWDT, vTop);
- dsp->print(buf); /* print bitrate */
- return false; /* disable to print RSSI */
-}
-
-#endif
diff --git a/yoRadio/audiohandlers.ino b/yoRadio/audiohandlers.ino
deleted file mode 100644
index 54a7d11..0000000
--- a/yoRadio/audiohandlers.ino
+++ /dev/null
@@ -1,78 +0,0 @@
-void audio_info(const char *info) {
- if(config.store.audioinfo) telnet.printf("##AUDIO.INFO#: %s\n", info);
-#ifdef USE_NEXTION
- if (strstr(info, "format is aac") != NULL) nextion.bitratePic(ICON_AAC);
- if (strstr(info, "format is flac") != NULL) nextion.bitratePic(ICON_FLAC);
- if (strstr(info, "format is mp3") != NULL) nextion.bitratePic(ICON_MP3);
- if (strstr(info, "format is wav") != NULL) nextion.bitratePic(ICON_WAV);
-#endif
- if (strstr(info, "failed!") != NULL || strstr(info, " 404") != NULL || strstr(info, " 403") != NULL || strstr(info, "address is empty") != NULL) {
- config.setTitle("[request failed]");
- netserver.requestOnChange(TITLE, 0);
- player.setOutputPins(false);
- player.setDefaults();
- if (player_on_stop_play) player_on_stop_play();
- player.mode = STOPPED;
- player.stopInfo();
- }
- if (strstr(info, "not supported") != NULL || strstr(info, "Account already in use") != NULL){
- config.setTitle(info);
- netserver.requestOnChange(TITLE, 0);
- player.setOutputPins(false);
- player.setDefaults();
- if (player_on_stop_play) player_on_stop_play();
- player.mode = STOPPED;
- player.stopInfo();
- }
-}
-
-void audio_bitrate(const char *info)
-{
- if(config.store.audioinfo) telnet.printf("%s %s\n", "##AUDIO.BITRATE#:", info);
- config.station.bitrate = atoi(info) / 1000;
-#ifdef USE_NEXTION
- nextion.bitrate(config.station.bitrate);
-#endif
- netserver.requestOnChange(BITRATE, 0);
-}
-
-bool printable(const char *info) {
- bool p = true;
- for (int c = 0; c < strlen(info); c++)
- {
- if ((uint8_t)info[c] > 0x7e || (uint8_t)info[c] < 0x20) p = false;
- }
- if (!p) p = (uint8_t)info[0] >= 0xC2 && (uint8_t)info[1] >= 0x80 && (uint8_t)info[1] <= 0xBF;
- return p;
-}
-
-void audio_showstation(const char *info) {
- if (strlen(info) > 0) {
- bool p = printable(info);
- config.setTitle(p?info:config.station.name);
- netserver.requestOnChange(TITLE, 0);
- }
-}
-
-void audio_showstreamtitle(const char *info) {
- DBGH();
- if (strstr(info, "Account already in use") != NULL){
- config.setTitle(info);
- netserver.requestOnChange(TITLE, 0);
- player.setOutputPins(false);
- player.setDefaults();
- if (player_on_stop_play) player_on_stop_play();
- player.mode = STOPPED;
- player.stopInfo();
- return;
- }
- if (strlen(info) > 0) {
- bool p = printable(info);
-#ifdef DEBUG_TITLES
- config.setTitle(DEBUG_TITLES);
-#else
- config.setTitle(p?info:config.station.name);
-#endif
- netserver.requestOnChange(TITLE, 0);
- }
-}
diff --git a/yoRadio/display.cpp b/yoRadio/display.cpp
deleted file mode 100644
index 62aa41f..0000000
--- a/yoRadio/display.cpp
+++ /dev/null
@@ -1,863 +0,0 @@
-#include "options.h"
-
-#include "WiFi.h"
-#include "time.h"
-#include "display.h"
-#if VU_READY==1
-#include "display_vu.h"
-#endif
-#include "player.h"
-#include "netserver.h"
-#include "network.h"
-
-DspCore dsp;
-Display display;
-#ifdef USE_NEXTION
-Nextion nextion;
-#endif
-
-#ifndef DUMMYDISPLAY
-/******************************************************************************************************************/
-void ticks() {
- network.timeinfo.tm_sec ++;
- mktime(&network.timeinfo);
- display.putRequest({CLOCK,0});
-}
-
-#ifndef STARTTIME
-#define STARTTIME 5000
-#endif
-#ifndef STARTTIME_PL
-#define STARTTIME_PL 0
-#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
-
-#ifndef CORE_STACK_SIZE
-#define CORE_STACK_SIZE 1024*3
-#endif
-#ifndef DSP_TASK_DELAY
-#define DSP_TASK_DELAY 5
-#endif
-byte currentScrollId = 0; /* one scroll on one time */
-
-#if WEATHER_READY==1
-bool weatherRequest = false;
-TaskHandle_t weatherUpdateTaskHandle;
-Ticker weatherTicker;
-char weatherText[254] = { 0 };
-#endif
-
-void Scroll::init(byte ScrollId, const char *sep, byte tsize, byte top, uint16_t dlay, uint16_t fgcolor, uint16_t bgcolor) {
- textsize = tsize;
- id = ScrollId;
- if (textsize == 0) return;
- texttop = top;
- fg = fgcolor;
- bg = bgcolor;
- delayStartScroll = dlay;
- memset(separator, 0, 4);
- strlcpy(separator, sep, 4);
- locked = false;
-}
-
-void Scroll::setText(const char *txt) {
- if (textsize == 0) return;
- memset(text, 0, BUFLEN / 2);
- strlcpy(text, txt, BUFLEN / 2 - 1);
- getbounds(textwidth, textheight, sepwidth);
- if (!locked) {
- clearscrolls();
- reset();
- }
- lockRequest = false;
-}
-
-void Scroll::lock() {
- locked = true;
-}
-
-void Scroll::unlock() {
- locked = false;
-}
-
-void Scroll::reset() {
- if (textsize == 0) return;
- locked = false;
- clear();
- setTextParams();
- dsp.set_Cursor(TFT_FRAMEWDT, texttop);
- dsp.printText(text);
- drawFrame();
- if (currentScrollId == id) currentScrollId = 0;
-}
-
-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 (lockRequest) {
- return;
- }
- if (textsize == 0) return;
- if (currentScrollId != 0 && currentScrollId != id) {
- return;
- }
- if (checkdelay(x == TFT_FRAMEWDT ? delayStartScroll : SCROLLTIME, scrolldelay)) {
- scroll();
- sticks();
- }
-}
-
-boolean Scroll::checkdelay(int m, unsigned long &tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
-}
-
-void Scroll::drawFrame() {
- if (textsize == 0) return;
- dsp.drawScrollFrame(texttop, textheight, bg);
-}
-
-void Scroll::sticks() {
- if (!doscroll || locked || textsize == 0) return;
- setTextParams();
- dsp.set_Cursor(x, texttop);
- dsp.printText(text);
- dsp.printText(separator);
- dsp.printText(text);
- if (x == TFT_FRAMEWDT) drawFrame();
-}
-
-void Scroll::scroll() {
- if (!doscroll || textsize == 0) return;
-
- //if (textwidth > display.screenwidth) {
- x -= SCROLLDELTA;
- if (-x > textwidth + sepwidth - TFT_FRAMEWDT) {
- x = TFT_FRAMEWDT;
- drawFrame();
- currentScrollId = 0;
- } else {
- currentScrollId = id;
- }
- //}
-}
-
-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 = DO_SCROLL;
-}
-
-
-TaskHandle_t TaskCore0;
-QueueHandle_t displayQueue;
-
-void Display::createCore0Task(){
- xTaskCreatePinnedToCore(
- loopCore0, /* Task function. */
- "TaskCore0", /* name of task. */
- CORE_STACK_SIZE, /* Stack size of task */
- NULL, /* parameter of the task */
- 4, /* no one flies higher than the Toruk */
- &TaskCore0, /* Task handle to keep track of created task */
- !xPortGetCoreID()); /* pin task to core 0 */
-}
-
-void loopCore0( void * pvParameters ){
- delay(500);
- while(!display.busy){
- if(displayQueue==NULL) break;
- display.loop();
- vTaskDelay(DSP_TASK_DELAY);
- }
- vTaskDelete( NULL );
- TaskCore0=NULL;
-}
-
-void Display::init() {
-#ifdef USE_NEXTION
- nextion.begin();
-#endif
- dsp.initD(screenwidth, screenheight);
- dsp.drawLogo();
- meta.init(1, " * ", META_SIZE, TFT_FRAMEWDT, STARTTIME, config.theme.meta, config.theme.background);
- title1.init(2, " * ", TITLE_SIZE1, TITLE_TOP1, STARTTIME, config.theme.title1, config.theme.background);
- title2.init(3, " * ", TITLE_SIZE2, TITLE_TOP2, STARTTIME, config.theme.title2, config.theme.background);
- int yStart = (screenheight / 2 - PLMITEMHEIGHT / 2) + 3;
-#ifdef PL_TOP
- yStart = PL_TOP;
-#endif
- plCurrent.init(4, " * ", PLCURRENT_SIZE, yStart, STARTTIME_PL, config.theme.background, config.theme.meta);
- plCurrent.lock();
-#if WEATHER_READY==1
- if (DSP_MODEL == DSP_ST7735 || (DSP_MODEL == DSP_SSD1327)) {
- weatherScroll.init(5, " * ", 1, TFT_LINEHGHT * 4 + 6, 0, config.theme.weather, config.theme.background);
- }else if(DSP_MODEL == DSP_ILI9225){
- weatherScroll.init(5, " * ", 1, TFT_LINEHGHT * 6 + 5, 0, config.theme.weather, config.theme.background);
- } else {
- weatherScroll.init(5, " * ", 2, TFT_LINEHGHT * 9 + 5, 0, config.theme.weather, config.theme.background);
- }
-#endif
-#ifdef USE_NEXTION
- nextion.wake();
-#endif
- if (dsp_on_init) dsp_on_init();
-}
-
-void Display::apScreen() {
-#ifndef PL_TOP
- meta.setText(dsp.utf8Rus("ёRADIO * ёRADIO * ёRADIO", false));
-#endif
- dsp.apScreen();
-}
-
-void Display::stop() {
- busy = true;
- swichMode(PLAYER);
- timer.detach();
- resetQueue();
- vQueueDelete(displayQueue);
- displayQueue=NULL;
- delay(100);
- bootLogo();
- bootString("reloading", 1);
- busy = false;
-}
-
-void Display::start(bool reboot) {
- busy = false;
- displayQueue = xQueueCreate( 5, sizeof( requestParams_t ) );
- createCore0Task();
-
- clear();
- if (network.status != CONNECTED) {
- apScreen();
-#ifdef USE_NEXTION
- nextion.apScreen();
-#endif
- return;
- }
- mode = PLAYER;
-#ifdef USE_NEXTION
- nextion.putcmd("page player");
-#endif
- if(!reboot){
- config.setTitle("[READY]");
- //loop();
- }
- ip();
- volume();
- station();
- rssi();
- time(reboot);
- if(reboot) title();
- timer.attach_ms(1000, ticks);
- if (dsp_on_start) dsp_on_start(&dsp);
- // Экстреминатус секвестирован /*дважды*/ /*трижды*/ /*четырежды*/ пятирежды
-}
-
-void Display::clear() {
- dsp.clearDsp();
-}
-
-void Display::swichMode(displayMode_e newmode) {
-#ifdef USE_NEXTION
- nextion.swichMode(newmode);
-#endif
- if (newmode == VOL) {
- volDelay = millis();
- }
- if (newmode == mode || network.status != CONNECTED) return;
- clear();
- mode = newmode;
- if (newmode != STATIONS) {
- ip();
- volume();
- }
- if (newmode == PLAYER) {
- currentScrollId = 0;
- meta.reset();
- title1.reset();
- if (TITLE_SIZE2 != 0) title2.reset();
- //player.loop();
- plCurrent.lock();
- time(true);
-#ifdef CLOCK_SPACE // if set space for clock in 1602 displays
- dsp.fillSpaces = true;
- ip();
- rssi();
- volume();
-#endif
- dsp.loop(true);
- } else {
- if (newmode != NUMBERS) {
- meta.lock();
- }
- title1.lock();
- if (TITLE_SIZE2 != 0) title2.lock();
-#ifdef CLOCK_SPACE
- dsp.fillSpaces = false;
-#endif
- }
- if (newmode == VOL) {
-#ifdef IP_INST_VOL
- dsp.frameTitle(WiFi.localIP().toString().c_str());
-#else
- dsp.frameTitle("VOLUME");
-#endif
- }
- if (newmode == LOST) {
- dsp.frameTitle("* LOST *");
- }
- if (newmode == UPDATING) {
- dsp.frameTitle("* UPDATING *");
- }
- if (newmode == INFO || newmode == SETTINGS || newmode == TIMEZONE || newmode == WIFI) {
- dsp.frameTitle("* NEXTION *");
- }
- if (newmode == NUMBERS) {
- //dsp.frameTitle("STATION");
- meta.reset();
- }
- if (newmode == STATIONS) {
- currentPlItem = config.store.lastStation;
- plCurrent.reset();
- currentScrollId = 0;
- drawPlaylist();
- }
- if (dsp_on_newmode) dsp_on_newmode(newmode);
-}
-
-void Display::drawPlayer() {
- if (clockRequest) {
- //getLocalTime(&network.timeinfo);
- //network.timeinfo.tm_sec ++;
- //mktime(&network.timeinfo);
- time();
- clockRequest = false;
- }
- meta.loop();
- title1.loop();
- if (TITLE_SIZE2 != 0) title2.loop();
-}
-
-void Display::sendInfo(){
- if (clockRequest) {
-#ifdef USE_NEXTION
- if(mode==TIMEZONE) nextion.localTime(network.timeinfo);
- if(mode==INFO) nextion.rssi();
-#endif
- clockRequest = false;
- }
-}
-
-void Display::drawVolume() {
- if (millis() - volDelay > 3000) {
- volDelay = millis();
- swichMode(PLAYER);
- }
-}
-
-void Display::resetQueue(){
- xQueueReset(displayQueue);
-}
-
-void Display::drawPlaylist() {
- char buf[PLMITEMLENGHT];
- dsp.drawPlaylist(currentPlItem, buf);
- plCurrent.setText(dsp.utf8Rus(buf, true));
-#ifdef USE_NEXTION
- nextion.drawPlaylist(currentPlItem);
-#endif
-}
-
-void Display::drawNextStationNum(uint16_t num) {
- char plMenu[1][40];
- char currentItemText[40] = {0};
- config.fillPlMenu(plMenu, num, 1, true);
- strlcpy(currentItemText, plMenu[0], 39);
- meta.setText(dsp.utf8Rus(currentItemText, true));
- dsp.drawNextStationNum(num);
-#ifdef USE_NEXTION
- nextion.drawNextStationNum(num);
-#endif
-}
-
-void Display::putRequest(requestParams_t request){
- if(displayQueue==NULL) return;
- xQueueSend(displayQueue, &request, portMAX_DELAY);
-}
-
-void Display::loop() {
- if(displayQueue==NULL) return;
-#ifdef USE_NEXTION
- nextion.loop();
-#endif
- requestParams_t request;
- if(xQueueReceive(displayQueue, &request, 20)){
- switch (request.type){
- case NEWMODE: {
- swichMode((displayMode_e)request.payload);
- break;
- }
- case CLOCK: {
- clockRequest = true;
- break;
- }
- case NEWTITLE: {
- title();
- break;
- }
- case RETURNTITLE: {
- returnTile();
- break;
- }
- case NEWSTATION: {
- station();
- break;
- }
- case NEXTSTATION: {
- drawNextStationNum((displayMode_e)request.payload);
- break;
- }
- case DRAWPLAYLIST: {
- int p = request.payload ? currentPlItem + 1 : currentPlItem - 1;
- if (p < 1) p = config.store.countStation;
- if (p > config.store.countStation) p = 1;
- currentPlItem = p;
- drawPlaylist();
- break;
- }
- case DRAWVOL: {
- volume();
- break;
- }
- }
- }
-
- switch (mode) {
- case PLAYER: {
- drawPlayer();
-#if WEATHER_READY==1
- weatherScroll.loop();
-#endif
- break;
- }
- case INFO:
- case TIMEZONE: {
- sendInfo();
- break;
- }
- case VOL: {
- drawVolume();
- break;
- }
- case NUMBERS: {
- meta.loop();
- break;
- }
- case STATIONS: {
- plCurrent.loop();
- break;
- }
- default:
- break;
- }
- dsp.loop();
- if (dsp_on_loop) dsp_on_loop(&dsp);
-#if VU_READY==1
- drawVU(&dsp);
-#endif
-#if WEATHER_READY==1
- if (weatherRequest) {
- weatherRequest = false;
- weatherScroll.setText(dsp.utf8Rus(weatherText, true));
- }
-#endif
-}
-
-void Display::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
- dsp.centerText(text, y, fg, bg);
-}
-
-void Display::bootString(const char* text, byte y) {
- dsp.set_TextSize(1);
- dsp.centerText(text, y == 1 ? BOOTSTR_TOP1 : BOOTSTR_TOP2, DSP_OLED?1:0xE68B, 0x0000);
- dsp.loop(true);
-#ifdef USE_NEXTION
- if(y==2) nextion.bootString(text);
-#endif
-}
-
-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);
-}
-
-void Display::station() {
- meta.setText(dsp.utf8Rus(config.station.name, true));
-#ifdef USE_NEXTION
- nextion.newNameset(config.station.name);
- nextion.bitrate(config.station.bitrate);
- nextion.bitratePic(ICON_NA);
-#endif
- //dsp.loop(true);
- //netserver.requestOnChange(STATION, 0);
-}
-
-void Display::returnTile() {
- meta.setText(dsp.utf8Rus(config.station.name, true));
-#ifdef USE_NEXTION
- nextion.newNameset(config.station.name);
-#endif
- meta.reset();
- //dsp.loop(true);
-}
-
-char *split(char *str, const char *delim) {
- char *dmp = strstr(str, delim);
- if (dmp == NULL) return NULL;
- *dmp = '\0';
- return dmp + strlen(delim);
-}
-
-void Display::title() {
- if (strlen(config.station.title) > 0) {
- char tmpbuf[strlen(config.station.title)+1];
- strlcpy(tmpbuf, config.station.title, strlen(config.station.title)+1);
- char *stitle = split(tmpbuf, " - ");
- if(stitle && TITLE_SIZE2 != 0){
- title1.setText(dsp.utf8Rus(tmpbuf, true));
- title2.setText(dsp.utf8Rus(stitle, true));
- }else{
- title1.setText(dsp.utf8Rus(config.station.title, true));
- title2.setText(dsp.utf8Rus("", true));
- }
-#ifdef USE_NEXTION
- nextion.newTitle(config.station.title);
-#endif
- if (player_on_track_change) player_on_track_change();
- }
-}
-
-void Display::heap() {
- if (config.store.audioinfo) dsp.displayHeapForDebug();
-}
-
-void Display::rssi() {
- int rssi = WiFi.RSSI();
- netserver.setRSSI(rssi);
- if (dsp_before_rssi) if (!dsp_before_rssi(&dsp)) return;
- char buf[20];
- sprintf(buf, "%ddBm", rssi);
- dsp.rssi(buf);
-}
-
-void Display::ip() {
- if (dsp_before_ip) if (!dsp_before_ip(&dsp)) return;
- dsp.ip(network.status == CONNECTED?WiFi.localIP().toString().c_str():WiFi.softAPIP().toString().c_str());
-}
-
-void Display::time(bool redraw) {
- if (dsp_before_clock) if (!dsp_before_clock(&dsp, dt)) return;
- char timeStringBuff[40] = { 0 };
- (void)timeStringBuff;
- 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
-#ifdef USE_NEXTION
- nextion.printClock(network.timeinfo);
-#endif
- dt = !dt;
- if (dsp_after_clock) dsp_after_clock(&dsp, dt);
-}
-
-void Display::volume() {
- dsp.drawVolumeBar(mode == VOL);
-#ifdef USE_NEXTION
- nextion.setVol(config.store.volume, mode == VOL);
-#endif
- //netserver.requestOnChange(VOLUME, 0);
-}
-
-void Display::flip(){
- dsp.flip();
-}
-
-void Display::invert(){
- dsp.invert();
-}
-#if DSP_MODEL==DSP_NOKIA5110
-void Display::setContrast(){
- dsp.setContrast(config.store.contrast);
-}
-#endif // DSP_MODEL==DSP_NOKIA5110
-
-bool Display::deepsleep(){
-//#ifdef DSP_CAN_SLEEP
-#if defined(LCD_I2C) || DSP_OLED || BRIGHTNESS_PIN!=255
- dsp.sleep();
- return true;
-#endif
- return false;
-}
-
-void Display::wakeup(){
-//#ifdef DSP_CAN_SLEEP
-#if defined(LCD_I2C) || DSP_OLED || BRIGHTNESS_PIN!=255
- dsp.wake();
-#endif
-}
-/******************************************************************************************************************/
-#endif // !DUMMYDISPLAY
-
-#ifdef DUMMYDISPLAY
-/******************************************************************************************************************/
-#ifndef USE_NEXTION
-void ticks() {
- static bool divrssi;
- network.timeinfo.tm_sec ++;
- mktime(&network.timeinfo);
- if(divrssi) {
- netserver.setRSSI(WiFi.RSSI());
- divrssi=false;
- }else{
- divrssi=true;
- }
-}
-#endif
-void Display::bootString(const char* text, byte y) {
- #ifdef USE_NEXTION
- if(y==2) nextion.bootString(text);
- #endif
-}
-void Display::init(){
- #ifdef USE_NEXTION
- nextion.begin(true);
- #endif
-}
-void Display::start(bool reboot){
- #ifdef USE_NEXTION
- nextion.start();
- #else
- timer.attach_ms(1000, ticks);
- #endif
-}
-void Display::putRequest(requestParams_t request){
- #ifdef USE_NEXTION
- nextion.putRequest(request);
- #endif
-}
-/******************************************************************************************************************/
-#endif // DUMMYDISPLAY
-
-#ifndef DUMMYDISPLAY
-#if WEATHER_READY==1
-
-bool getForecast() {
- WiFiClient client;
- const char* host = "api.openweathermap.org";
-
- if (!client.connect(host, 80)) {
- Serial.println("## OPENWEATHERMAP ###: connection failed");
- return false;
- }
- char httpget[250] = {0};
- sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=metric&lang=ru&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", config.store.weatherlat, config.store.weatherlon, config.store.weatherkey, host);
- client.print(httpget);
- unsigned long timeout = millis();
- while (client.available() == 0) {
- if (millis() - timeout > 2000UL) {
- Serial.println("## OPENWEATHERMAP ###: client available timeout !");
- client.stop();
- return false;
- }
- }
- timeout = millis();
- String line = "";
- if (client.connected()) {
- while (client.available())
- {
- line = client.readStringUntil('\n');
- if (strstr(line.c_str(), "\"temp\"") != NULL) {
- client.stop();
- break;
- }
- if ((millis() - timeout) > 500)
- {
- client.stop();
- Serial.println("## OPENWEATHERMAP ###: client read timeout !");
- return false;
- }
- }
- }
- if (strstr(line.c_str(), "\"temp\"") == NULL) {
- Serial.println("## OPENWEATHERMAP ###: weather not found !");
- return false;
- }
- char *tmpe;
- char *tmps;
- const char* cursor = line.c_str();
- char desc[120], temp[20], hum[20], press[20], icon[5];
-
- tmps = strstr(cursor, "\"description\":\"");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
- tmps += 15;
- tmpe = strstr(tmps, "\",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
- strlcpy(desc, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
-
- // "ясно","icon":"01d"}],
- tmps = strstr(cursor, "\"icon\":\"");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
- tmps += 8;
- tmpe = strstr(tmps, "\"}");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
- strlcpy(icon, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
-
- tmps = strstr(cursor, "\"temp\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
- tmps += 7;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
- strlcpy(temp, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
- float tempf = atof(temp);
-
- tmps = strstr(cursor, "\"pressure\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
- tmps += 11;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
- strlcpy(press, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
- int pressi = (float)atoi(press) / 1.333;
-
- tmps = strstr(cursor, "humidity\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
- tmps += 10;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
- strlcpy(hum, tmps, tmpe - tmps + 1);
-
- Serial.printf("## OPENWEATHERMAP ###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
- sprintf(weatherText, "%s, %.1f C * давление: %d мм * влажность: %s%%", desc, tempf, pressi, hum);
- return true;
-
-}
-
-void Display::getWeather( void * pvParameters ) {
- delay(200);
- if (getForecast()) {
- weatherRequest = true;
- weatherTicker.detach();
- weatherTicker.attach(WEATHER_REQUEST_INTERVAL, display.updateWeather);
- } else {
- weatherTicker.detach();
- weatherTicker.attach(WEATHER_REQUEST_INTERVAL_FAULTY, display.updateWeather);
- }
- vTaskDelete( NULL );
-}
-#endif // WEATHER_READY==1
-
-void Display::updateWeather(){
-#if WEATHER_READY==1
- if(!config.store.showweather || strlen(config.store.weatherkey)==0) return;
- xTaskCreatePinnedToCore(
- getWeather, /* Task function. */
- "dspGetWeather1", /* name of task. */
- 1024 * 4, /* Stack size of task */
- NULL, /* parameter of the task */
- 0, /* priority of the task */
- &weatherUpdateTaskHandle, /* Task handle to keep track of created task */
- 0); /* pin task to core CORE_FOR_LOOP_CONTROLS */
-#endif // WEATHER_READY==1
-}
-
-void Display::showWeather(){
-#if WEATHER_READY==1
- if(strlen(config.store.weatherkey)!=0 && config.store.showweather) display.updateWeather();
- if(!config.store.showweather){
- memset(weatherText, 0, sizeof(weatherText));
- weatherScroll.setText(weatherText);
- }
-#endif // WEATHER_READY==1
-}
-/******************************************************************************************************************/
-#endif // !DUMMYDISPLAY
diff --git a/yoRadio/display.h b/yoRadio/display.h
deleted file mode 100644
index 896127f..0000000
--- a/yoRadio/display.h
+++ /dev/null
@@ -1,203 +0,0 @@
-#ifndef display_h
-#define display_h
-#include "options.h"
-
-#include "Arduino.h"
-#include
-#include "config.h"
-
-#if DSP_MODEL==DSP_DUMMY
-#define DUMMYDISPLAY
-#include "src/displays/displayDummy.h"
-#elif DSP_MODEL==DSP_ST7735
-#include "src/displays/displayST7735.h"
-#elif DSP_MODEL==DSP_SSD1306 || DSP_MODEL==DSP_SSD1306x32
-#include "src/displays/displaySSD1306.h"
-#elif DSP_MODEL==DSP_NOKIA5110
-#include "src/displays/displayN5110.h"
-#elif DSP_MODEL==DSP_ST7789 || DSP_MODEL==DSP_ST7789_240
-#include "src/displays/displayST7789.h"
-#elif DSP_MODEL==DSP_SH1106
-#include "src/displays/displaySH1106.h"
-#elif DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_2004I2C
-#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 || DSP_MODEL==DSP_SSD1305I2C
-#include "src/displays/displaySSD1305.h"
-#elif DSP_MODEL==DSP_SH1107
-#include "src/displays/displaySH1106.h"
-#elif DSP_MODEL==DSP_1602 || DSP_MODEL==DSP_2004
-#include "src/displays/displayLC1602.h"
-#elif DSP_MODEL==DSP_GC9106
-#include "src/displays/displayGC9106.h"
-#elif DSP_MODEL==DSP_CUSTOM
-#include "src/displays/displayCustom.h"
-#elif DSP_MODEL==DSP_ILI9225
-#include "src/displays/displayILI9225.h"
-#endif
-#ifndef VU_READY
-#define VU_READY 0
-#endif
-#ifndef DSP_FLIPPED
-#define DSP_FLIPPED 1
-#endif
-#ifndef DSP_OLED
-#define DSP_OLED false
-#endif
-#ifndef WEATHER_READY
-#define WEATHER_READY 0
-#else
-#define WEATHER_REQUEST_INTERVAL 1800 //30min
-#define WEATHER_REQUEST_INTERVAL_FAULTY 30
-#endif
-
-enum displayMode_e { PLAYER, VOL, STATIONS, NUMBERS, LOST, UPDATING, INFO, SETTINGS, TIMEZONE, WIFI, CLEAR };
-
-enum displayRequestType_e { NEWMODE, CLOCK, NEWTITLE, RETURNTITLE, NEWSTATION, NEXTSTATION, DRAWPLAYLIST, DRAWVOL };
-struct requestParams_t
-{
- displayRequestType_e type;
- int payload;
-};
-
-#if NEXTION_RX!=255 && NEXTION_TX!=255
-#define USE_NEXTION
-#include "src/displays/nextion.h"
-#endif
-
-#ifndef DUMMYDISPLAY
-void loopCore0( void * pvParameters );
-
-class Scroll {
- public:
- Scroll() { };
- void init(byte ScrollId, const char *sep, byte tsize, byte top, uint16_t dlay, uint16_t fgcolor, uint16_t bgcolor);
- void setText(const char *txt);
- void loop();
- void reset();
- void lock();
- void unlock();
- bool lockRequest;
- private:
- byte textsize, texttop, id;
- char text[BUFLEN/2];
- char separator[4];
- uint16_t fg, bg;
- uint16_t delayStartScroll;
- uint16_t textwidth, textheight, sepwidth, startticks, scrollticks;
- int x;
- bool doscroll, locked;
- unsigned long scrolldelay;
- void clearscrolls();
- void getbounds(uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
- boolean checkdelay(int m, unsigned long &tstamp);
- void scroll();
- void sticks();
- void clear();
- void setTextParams();
- void drawFrame();
-};
-#endif
-
-class Display {
- public:
- uint16_t screenwidth, screenheight;
- displayMode_e mode;
- uint16_t currentPlItem;
- uint16_t numOfNextStation;
-#ifndef DUMMYDISPLAY
- Scroll plCurrent;
-#endif
- bool busy;
- bool dt; // dots
- public:
- Display() {};
-#ifndef DUMMYDISPLAY
- void init();
- void loop();
- void start(bool reboot=false);
- void stop();
- void resetQueue();
- 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 putRequest(requestParams_t request);
- void flip();
- void invert();
- static void updateWeather();
- void showWeather();
- bool deepsleep();
- void wakeup();
-#if DSP_MODEL==DSP_NOKIA5110
- void setContrast();
-#else
- void setContrast(){};
-#endif // DSP_MODEL==DSP_NOKIA5110
-#else
- void init();
- void loop(){};
- void start(bool reboot=false);
- void stop(){};
- void resetQueue(){};
- 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 putRequest(requestParams_t request);
- void flip(){};
- void invert(){};
- void setContrast(){};
- static void updateWeather(){};
- void showWeather(){};
- bool deepsleep(){return true;};
- void wakeup(){};
-#endif
-#ifndef DUMMYDISPLAY
- private:
-#if WEATHER_READY==1
- Scroll weatherScroll;
- static void getWeather( void * pvParameters );
-#endif
- Ticker timer;
- Scroll meta, title1, title2;
- bool clockRequest;
- unsigned long volDelay;
- void clear();
- void heap();
- void rssi();
- void ip();
- void time(bool redraw = false);
- void apScreen();
- void drawPlayer();
- void sendInfo();
- void drawVolume();
- void swichMode(displayMode_e newmode);
- void drawPlaylist();
- void volume();
- void title();
- void station();
- void drawNextStationNum(uint16_t num);
- void returnTile();
- void createCore0Task();
-#else
- Ticker timer;
-#endif
-};
-
-extern Display display;
-
-extern __attribute__((weak)) bool dsp_before_clock(DspCore *dsp, bool dots);
-extern __attribute__((weak)) void dsp_after_clock(DspCore *dsp, bool dots);
-extern __attribute__((weak)) bool dsp_before_ip(DspCore *dsp);
-extern __attribute__((weak)) bool dsp_before_rssi(DspCore *dsp);
-extern __attribute__((weak)) void dsp_on_init();
-extern __attribute__((weak)) void dsp_on_loop(DspCore *dsp);
-extern __attribute__((weak)) void dsp_on_start(DspCore *dsp);
-extern __attribute__((weak)) void dsp_on_newmode(displayMode_e newmode);
-extern __attribute__((weak)) void player_on_station_change();
-
-#endif
diff --git a/yoRadio/display_vu.h b/yoRadio/display_vu.h
deleted file mode 100644
index 66b9bb9..0000000
--- a/yoRadio/display_vu.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef display_vu_h
-#define display_vu_h
-#include "player.h"
-#include "network.h"
-
-#ifdef VU_PARAMS3
-enum : uint16_t VU_PARAMS3;
-#else
-/*
- * vu left - left position
- * vu top - top position
- * band width - width of band
- * band height - height of band
- * band space - space between bands
- * num of bands - num of bands
- * fade speed - fade speed
- * horisontal - bands orientation
- * Max/Min Bands Color - color of bands
- */
-
-/**********************************************************************************************************************************************************************************/
-/* vu left | vu top | band width | band height | band space | num of bands | fade speed | horisontal | Max Bands Color | Min Bands Color */
-/**********************************************************************************************************************************************************************************/
-
-#if DSP_MODEL==DSP_ST7735 && DTYPE==INITR_BLACKTAB /* ST7735 160x128 */
-enum : uint16_t { VU_X = 4, VU_Y = 50, VU_BW = 10, VU_BH = 44, VU_BS = 2, VU_NB = 8, VU_FS = 3, VU_HOR = 0 };
-
-#elif DSP_MODEL==DSP_ST7735 && DTYPE==INITR_144GREENTAB /* ST7735 128x128 */
-enum : uint16_t { VU_X = 4, VU_Y = 97, VU_BW = 60, VU_BH = 8, VU_BS = 0, VU_NB = 10, VU_FS = 3, VU_HOR = 1 };
-#define GREENTAB128
-
-#elif DSP_MODEL==DSP_ILI9341 /* ILI9341 320x240 */
-enum : uint16_t { VU_X = 4, VU_Y = 116, VU_BW = 24, VU_BH = 80, VU_BS = 4, VU_NB = 8, VU_FS = 5, VU_HOR = 0 };
-
-#elif DSP_MODEL==DSP_ST7789 /* ST7789 320x240 */
-enum : uint16_t { VU_X = 4, VU_Y = 116, VU_BW = 24, VU_BH = 80, VU_BS = 4, VU_NB = 8, VU_FS = 5, VU_HOR = 0 };
-
-#elif DSP_MODEL==DSP_ST7789_240 /* ST7789 240x240 */
-enum : uint16_t { VU_X = 4, VU_Y = 90, VU_BW = 120, VU_BH = 20, VU_BS = 0, VU_NB = 12, VU_FS = 3, VU_HOR = 1 };
-
-#elif DSP_MODEL==DSP_ILI9225 /* ILI9225 220x176 */
-enum : uint16_t { VU_X = 4, VU_Y = 80, VU_BW = 13, VU_BH = 56, VU_BS = 2, VU_NB = 8, VU_FS = 4, VU_HOR = 0 };
-
-#elif (DSP_MODEL==DSP_ST7735 && DTYPE==INITR_MINI160x80) || (DSP_MODEL==DSP_GC9106) /* ST7735 160x80, GC9106 160x80 */
-enum : uint16_t { VU_X = 1, VU_Y = 30, VU_BW = 12, VU_BH = 36, VU_BS = 4, VU_NB = 8, VU_FS = 3, VU_HOR = 0 };
-
-#endif
-#endif //VU_PARAMS
-/**********************************************************************************************************************************************************************************/
-
-void drawVU(DspCore *dsp);
-
-GFXcanvas16 gfxc(VU_BW*2+VU_BS,VU_BH);
-
-void drawVU(DspCore *dsp){
- if(!config.store.vumeter || network.status!=CONNECTED) return;
- if(display.mode!=PLAYER && display.mode!=VOL) return;
-#ifdef GREENTAB128
- if(display.mode==VOL) return;
-#endif
-#if !defined(USE_NEXTION) && I2S_DOUT==255
- player.getVUlevel();
-#endif
- static uint16_t /*samples_cnt, */measL, measR;
- uint16_t bandColor;
- uint16_t dimension = VU_HOR?VU_BW:VU_BH;
- uint8_t L = map(player.vuLeft, 255, 0, 0, dimension);
- uint8_t R = map(player.vuRight, 255, 0, 0, dimension);
- if(player.isRunning()){
- measL=(L>=measL)?measL+VU_FS:L;
- measR=(R>=measR)?measR+VU_FS:R;
- }else{
- if(measLdimension) measL=dimension;
- if(measR>dimension) measR=dimension;
- uint8_t h=(dimension/VU_NB)-2;
- gfxc.fillRect(0,0,VU_BW*2+VU_BS,VU_BH, config.theme.background);
- for(int i=0; iVU_BW-(VU_BW/VU_NB)*4)?config.theme.vumax:config.theme.vumin;
- gfxc.fillRect(i, 0, h, VU_BH, bandColor);
- gfxc.fillRect(i+VU_BW+VU_BS, 0, h, VU_BH, bandColor);
- #else
- bandColor = (i>(VU_BW/VU_NB))?VU_COLOR_MIN:config.theme.vumax;
- gfxc.fillRect(i, 0, h, VU_BH, bandColor);
- bandColor = (i>VU_BW-(VU_BW/VU_NB)*3)?config.theme.vumax:config.theme.vumin;
- gfxc.fillRect(i+VU_BW+VU_BS, 0, h, VU_BH, bandColor);
- #endif
- }else{
- bandColor = (i<(VU_BH/VU_NB)*3)?config.theme.vumax:config.theme.vumin;
- gfxc.fillRect(0, i, VU_BW, h, bandColor);
- gfxc.fillRect(VU_BW+VU_BS, i, VU_BW, h, bandColor);
- }
- }
- }
- if(VU_HOR){
- #ifndef BOOMBOX_STYLE
- gfxc.fillRect(VU_BW-measL, 0, measL, VU_BW, config.theme.background);
- gfxc.fillRect(VU_BW*2+VU_BS-measR, 0, measR, VU_BW, config.theme.background);
- dsp->drawRGBBitmap(VU_X, (display.mode==VOL && DSP_MODEL==DSP_ST7789_240)?VU_Y-40:VU_Y, gfxc.getBuffer(), VU_BW*2+VU_BS, VU_BH);
- #else
- gfxc.fillRect(0, 0, VU_BW-(VU_BW-measL), VU_BW, config.theme.background);
- gfxc.fillRect(VU_BW*2+VU_BS-measR, 0, measR, VU_BW, config.theme.background);
- dsp->drawRGBBitmap(VU_X, (display.mode==VOL && DSP_MODEL==DSP_ST7789_240)?VU_Y-40:VU_Y, gfxc.getBuffer(), VU_BW*2+VU_BS, VU_BH);
- #endif
- }else{
- gfxc.fillRect(0, 0, VU_BW, measL, config.theme.background);
- gfxc.fillRect(VU_BW+VU_BS, 0, VU_BW, measR, config.theme.background);
- dsp->drawRGBBitmap(VU_X, VU_Y, gfxc.getBuffer(), VU_BW*2+VU_BS, VU_BH);
- }
-}
-#endif
diff --git a/yoRadio/locale/displayL10n_en.h b/yoRadio/locale/displayL10n_en.h
new file mode 100644
index 0000000..6cf95d4
--- /dev/null
+++ b/yoRadio/locale/displayL10n_en.h
@@ -0,0 +1,59 @@
+#ifndef dsp_full_loc
+#define dsp_full_loc
+#include
+/*************************************************************************************
+ HOWTO:
+ Copy this file to yoRadio/locale/displayL10n_custom.h
+ and modify it
+*************************************************************************************/
+const char mon[] PROGMEM = "mo";
+const char tue[] PROGMEM = "tu";
+const char wed[] PROGMEM = "we";
+const char thu[] PROGMEM = "th";
+const char fri[] PROGMEM = "fr";
+const char sat[] PROGMEM = "sa";
+const char sun[] PROGMEM = "su";
+
+const char monf[] PROGMEM = "monday";
+const char tuef[] PROGMEM = "tuesday";
+const char wedf[] PROGMEM = "wednesday";
+const char thuf[] PROGMEM = "thursday";
+const char frif[] PROGMEM = "friday";
+const char satf[] PROGMEM = "saturday";
+const char sunf[] PROGMEM = "sunday";
+
+const char jan[] PROGMEM = "january";
+const char feb[] PROGMEM = "february";
+const char mar[] PROGMEM = "march";
+const char apr[] PROGMEM = "april";
+const char may[] PROGMEM = "may";
+const char jun[] PROGMEM = "june";
+const char jul[] PROGMEM = "july";
+const char aug[] PROGMEM = "august";
+const char sep[] PROGMEM = "september";
+const char oct[] PROGMEM = "october";
+const char nov[] PROGMEM = "november";
+const char dec[] PROGMEM = "december";
+
+const char* const dow[] PROGMEM = { sun, mon, tue, wed, thu, fri, sat };
+const char* const dowf[] PROGMEM = { sunf, monf, tuef, wedf, thuf, frif, satf };
+const char* const mnths[] PROGMEM = { jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec };
+
+const char const_PlReady[] PROGMEM = "[ready]";
+const char const_PlStopped[] PROGMEM = "[stopped]";
+const char const_PlConnect[] PROGMEM = "[connecting]";
+const char const_DlgVolume[] PROGMEM = "VOLUME";
+const char const_DlgLost[] PROGMEM = "* LOST *";
+const char const_DlgUpdate[] PROGMEM = "* UPDATING *";
+const char const_DlgNextion[] PROGMEM = "* NEXTION *";
+const char const_getWeather[] PROGMEM = "";
+
+const char apNameTxt[] PROGMEM = "AP NAME";
+const char apPassTxt[] PROGMEM = "PASSWORD";
+const char bootstrFmt[] PROGMEM = "Trying to %s";
+const char apSettFmt[] PROGMEM = "SETTINGS PAGE ON: HTTP://%s/";
+const char weatherFmt[] PROGMEM = "%s, %.1f C * pressure: %d mm * humidity: %s%%";
+const char weatherUnits[] PROGMEM = "metric"; /* standard, metric, imperial */
+const char weatherLang[] PROGMEM = "en"; /* https://openweathermap.org/current#multi */
+
+#endif
diff --git a/yoRadio/locale/displayL10n_ru.h b/yoRadio/locale/displayL10n_ru.h
new file mode 100644
index 0000000..e57dc73
--- /dev/null
+++ b/yoRadio/locale/displayL10n_ru.h
@@ -0,0 +1,59 @@
+#ifndef dsp_full_loc
+#define dsp_full_loc
+#include
+/*************************************************************************************
+ HOWTO:
+ Copy this file to yoRadio/locale/displayL10n_custom.h
+ and modify it
+*************************************************************************************/
+const char mon[] PROGMEM = "пн";
+const char tue[] PROGMEM = "вт";
+const char wed[] PROGMEM = "ср";
+const char thu[] PROGMEM = "чт";
+const char fri[] PROGMEM = "пт";
+const char sat[] PROGMEM = "сб";
+const char sun[] PROGMEM = "вс";
+
+const char monf[] PROGMEM = "понедельник";
+const char tuef[] PROGMEM = "вторник";
+const char wedf[] PROGMEM = "среда";
+const char thuf[] PROGMEM = "четверг";
+const char frif[] PROGMEM = "пятница";
+const char satf[] PROGMEM = "суббота";
+const char sunf[] PROGMEM = "воскресенье";
+
+const char jan[] PROGMEM = "января";
+const char feb[] PROGMEM = "февраля";
+const char mar[] PROGMEM = "марта";
+const char apr[] PROGMEM = "апреля";
+const char may[] PROGMEM = "мая";
+const char jun[] PROGMEM = "июня";
+const char jul[] PROGMEM = "июля";
+const char aug[] PROGMEM = "августа";
+const char sep[] PROGMEM = "сентября";
+const char oct[] PROGMEM = "октября";
+const char nov[] PROGMEM = "ноября";
+const char dec[] PROGMEM = "декабря";
+
+const char* const dow[] PROGMEM = { sun, mon, tue, wed, thu, fri, sat };
+const char* const dowf[] PROGMEM = { sunf, monf, tuef, wedf, thuf, frif, satf };
+const char* const mnths[] PROGMEM = { jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec };
+
+const char const_PlReady[] PROGMEM = "[готов]";
+const char const_PlStopped[] PROGMEM = "[остановлено]";
+const char const_PlConnect[] PROGMEM = "[соединение]";
+const char const_DlgVolume[] PROGMEM = "ГРОМКОСТЬ";
+const char const_DlgLost[] PROGMEM = "ОТКЛЮЧЕНО";
+const char const_DlgUpdate[] PROGMEM = "ОБНОВЛЕНИЕ";
+const char const_DlgNextion[] PROGMEM = "NEXTION";
+const char const_getWeather[] PROGMEM = "";
+
+const char apNameTxt[] PROGMEM = "ТОЧКА ДОСТУПА";
+const char apPassTxt[] PROGMEM = "ПАРОЛЬ";
+const char bootstrFmt[] PROGMEM = "Соединяюсь с %s";
+const char apSettFmt[] PROGMEM = "НАСТРОЙКИ: HTTP://%s/";
+const char weatherFmt[] PROGMEM = "%s, %.1f C * давление: %d мм * влажность: %s%%";
+const char weatherUnits[] PROGMEM = "metric"; /* standard, metric, imperial */
+const char weatherLang[] PROGMEM = "ru"; /* https://openweathermap.org/current#multi */
+
+#endif
diff --git a/yoRadio/network.cpp b/yoRadio/network.cpp
deleted file mode 100644
index 7551f10..0000000
--- a/yoRadio/network.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "network.h"
-#include "WiFi.h"
-#include "display.h"
-#include "options.h"
-#include "config.h"
-#include "telnet.h"
-
-Network network;
-
-void syncTime() {
- network.requestTimeSync(true);
-}
-
-void getFirstTime() {
- getLocalTime(&network.timeinfo);
-}
-
-void rebootTime() {
- ESP.restart();
-}
-
-void Network::begin() {
- config.initNetwork();
- if (config.ssidsCount == 0) {
- raiseSoftAP();
- return;
- }
- byte ls = (config.store.lastSSID == 0 || config.store.lastSSID > config.ssidsCount) ? 0 : config.store.lastSSID - 1;
- byte startedls = ls;
- byte errcnt = 0;
- 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, 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, 1);
- errcnt++;
- if (errcnt > 16) {
- errcnt = 0;
- ls++;
- if (ls > config.ssidsCount - 1) ls = 0;
- break;
- }
- }
- if (WiFi.status() != WL_CONNECTED && ls == startedls) {
- raiseSoftAP();
- return;
- }
- if (WiFi.status() == WL_CONNECTED) {
- config.setLastSSID(ls + 1);
- break;
- }
- }
- digitalWrite(LED_BUILTIN, LOW);
- status = CONNECTED;
- WiFi.setSleep(false);
- if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){
- configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
- }else if(strlen(config.store.sntp1)>0){
- configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
- }
- stimer.once_ms(200,getFirstTime);
- ntimer.attach_ms(TSYNC_DELAY, syncTime);
-#ifdef USE_NEXTION
- nextion.startWeather();
-#endif
- display.updateWeather();
- if (network_on_connect) network_on_connect();
-}
-
-void Network::requestTimeSync(bool withTelnetOutput, uint8_t clientId) {
- if (withTelnetOutput) {
- getLocalTime(&timeinfo);
- char timeStringBuff[50];
- strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S", &timeinfo);
- if (config.store.tzHour < 0) {
- telnet.printf(clientId, "##SYS.DATE#: %s%03d:%02d\n> ", timeStringBuff, config.store.tzHour, config.store.tzMin);
- } else {
- telnet.printf(clientId, "##SYS.DATE#: %s+%02d:%02d\n> ", timeStringBuff, config.store.tzHour, config.store.tzMin);
- }
- }
-}
-
-void Network::raiseSoftAP() {
- WiFi.mode(WIFI_AP);
- WiFi.softAP(apSsid, apPassword);
- Serial.printf("\n\nRunning in AP mode.\nConnect to AP %s with password %s for settings.\n\n", apSsid, apPassword);
- status = SOFT_AP;
- if(config.store.softapdelay>0)
- rtimer.attach_ms(config.store.softapdelay*1000*60, rebootTime);
-}
diff --git a/yoRadio/src/Adafruit_GC9106Ex/Adafruit_GC9106Ex.cpp b/yoRadio/src/Adafruit_GC9106Ex/Adafruit_GC9106Ex.cpp
index daba8ae..2e8d281 100644
--- a/yoRadio/src/Adafruit_GC9106Ex/Adafruit_GC9106Ex.cpp
+++ b/yoRadio/src/Adafruit_GC9106Ex/Adafruit_GC9106Ex.cpp
@@ -21,7 +21,7 @@
* BSD license, all text here must be included in any redistribution.
*
*/
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_GC9106
#include "Adafruit_GC9106Ex.h"
diff --git a/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.cpp b/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.cpp
index 545e5ea..59e170b 100644
--- a/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.cpp
+++ b/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.cpp
@@ -1,4 +1,4 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_ILI9225
#include "TFT_22_ILI9225Fix.h"
diff --git a/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.h b/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.h
index 8d41a52..ed4f950 100644
--- a/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.h
+++ b/yoRadio/src/ILI9225Fix/TFT_22_ILI9225Fix.h
@@ -220,12 +220,10 @@ class TFT_22_ILI9225 {
/// @return horizontal size of the screen, in pixels
/// @note 240 means 240 pixels and thus 0..239 coordinates (decimal)
uint16_t maxX(void);
-
/// Screen size, y-axis
/// @return vertical size of the screen, in pixels
/// @note 220 means 220 pixels and thus 0..219 coordinates (decimal)
uint16_t maxY(void);
-
/// Draw circle
/// @param x0 center, point coordinate, x-axis
/// @param y0 center, point coordinate, y-axis
@@ -333,7 +331,7 @@ class TFT_22_ILI9225 {
/// @param ch ASCII character
/// @param color 16-bit color, default=white
/// @return width of character in display pixels
- uint16_t drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color = COLOR_WHITE);
+ virtual uint16_t drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color = COLOR_WHITE);
/// width of an ASCII character (pixel )
/// @param ch ASCII character
@@ -400,8 +398,8 @@ class TFT_22_ILI9225 {
/// @return width of character in display pixels
uint16_t drawGFXChar(int16_t x, int16_t y, unsigned char c, uint16_t color);
- void startWrite(void);
- void endWrite(void);
+ virtual void startWrite(void);
+ virtual void endWrite(void);
private:
@@ -458,7 +456,7 @@ class TFT_22_ILI9225 {
bool hwSPI, blState;
- _currentFont cfont;
+ //_currentFont cfont;
#ifdef ESP32
SPIClass _spi;
@@ -471,6 +469,8 @@ class TFT_22_ILI9225 {
void getGFXCharExtent(uint8_t c, int16_t *gw, int16_t *gh, int16_t *xa);
GFXfont *gfxFont;
+
+ _currentFont cfont;
};
class GFXcanvas16 {
diff --git a/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp b/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp
index 6a76554..aaf0ea4 100644
--- a/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp
+++ b/yoRadio/src/LiquidCrystalI2C/LiquidCrystalI2CEx.cpp
@@ -1,6 +1,6 @@
// Based on the work by DFRobot
// Based on the LiquidCrystal_I2C library https://github.com/johnrickman/LiquidCrystal_I2C
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_2004I2C
#include "LiquidCrystalI2CEx.h"
diff --git a/yoRadio/src/audioI2S/Audio.cpp b/yoRadio/src/audioI2S/Audio.cpp
index 518ecbc..df60170 100644
--- a/yoRadio/src/audioI2S/Audio.cpp
+++ b/yoRadio/src/audioI2S/Audio.cpp
@@ -1,4 +1,4 @@
-#include "../../options.h"
+#include "../core/options.h"
#if VS1053_CS==255
/*
* Audio.cpp
@@ -3892,26 +3892,32 @@ bool Audio::parseContentType(char* ct) {
case CT_MP3:
m_codec = CODEC_MP3;
if(m_f_Log) { log_i("ContentType %s, format is mp3", ct); } //ok is likely mp3
+ if(audio_info) audio_info("format is mp3");
break;
case CT_AAC:
m_codec = CODEC_AAC;
if(m_f_Log) { log_i("ContentType %s, format is aac", ct); }
+ if(audio_info) audio_info("format is aac");
break;
case CT_M4A:
m_codec = CODEC_M4A;
if(m_f_Log) { log_i("ContentType %s, format is aac", ct); }
+ if(audio_info) audio_info("format is aac");
break;
case CT_FLAC:
m_codec = CODEC_FLAC;
if(m_f_Log) { log_i("ContentType %s, format is flac", ct); }
+ if(audio_info) audio_info("format is flac");
break;
case CT_WAV:
m_codec = CODEC_WAV;
if(m_f_Log) { log_i("ContentType %s, format is wav", ct); }
+ if(audio_info) audio_info("format is wav");
break;
case CT_OGG:
m_codec = CODEC_OGG;
if(m_f_Log) { log_i("ContentType %s found", ct); }
+ if(audio_info) audio_info("format is ogg");
break;
case CT_PLS:
diff --git a/yoRadio/src/audioVS1053/audioVS1053Ex.cpp b/yoRadio/src/audioVS1053/audioVS1053Ex.cpp
index f9ff3ef..8ed90f4 100644
--- a/yoRadio/src/audioVS1053/audioVS1053Ex.cpp
+++ b/yoRadio/src/audioVS1053/audioVS1053Ex.cpp
@@ -1,4 +1,4 @@
-#include "../../options.h"
+#include "../core/options.h"
#if I2S_DOUT==255
/*
* vs1053_ext.cpp
@@ -1501,26 +1501,32 @@ bool Audio::parseContentType(char* ct) {
case CT_MP3:
m_codec = CODEC_MP3;
if(m_f_Log) { log_i("ContentType %s, format is mp3", ct); } //ok is likely mp3
+ if(audio_info) audio_info("format is mp3");
break;
case CT_AAC:
m_codec = CODEC_AAC;
if(m_f_Log) { log_i("ContentType %s, format is aac", ct); }
+ if(audio_info) audio_info("format is aac");
break;
case CT_M4A:
m_codec = CODEC_M4A;
if(m_f_Log) { log_i("ContentType %s, format is aac", ct); }
+ if(audio_info) audio_info("format is aac");
break;
case CT_FLAC:
m_codec = CODEC_FLAC;
if(m_f_Log) { log_i("ContentType %s, format is flac", ct); }
+ if(audio_info) audio_info("format is flac");
break;
case CT_WAV:
m_codec = CODEC_WAV;
if(m_f_Log) { log_i("ContentType %s, format is wav", ct); }
+ if(audio_info) audio_info("format is wav");
break;
case CT_OGG:
m_codec = CODEC_OGG;
if(m_f_Log) { log_i("ContentType %s found", ct); }
+ if(audio_info) audio_info("format is ogg");
break;
case CT_PLS:
diff --git a/yoRadio/config.cpp b/yoRadio/src/core/config.cpp
similarity index 98%
rename from yoRadio/config.cpp
rename to yoRadio/src/core/config.cpp
index 77e1226..417b25b 100644
--- a/yoRadio/config.cpp
+++ b/yoRadio/src/core/config.cpp
@@ -51,6 +51,8 @@ uint16_t Config::color565(uint8_t r, uint8_t g, uint8_t b)
void Config::loadTheme(){
theme.background = color565(COLOR_BACKGROUND);
theme.meta = color565(COLOR_STATION_NAME);
+ theme.metabg = color565(COLOR_STATION_BG);
+ theme.metafill = color565(COLOR_STATION_FILL);
theme.title1 = color565(COLOR_SNG_TITLE_1);
theme.title2 = color565(COLOR_SNG_TITLE_2);
theme.digit = color565(COLOR_DIGITS);
@@ -67,6 +69,7 @@ void Config::loadTheme(){
theme.ip = color565(COLOR_IP);
theme.vol = color565(COLOR_VOLUME_VALUE);
theme.rssi = color565(COLOR_RSSI);
+ theme.bitrate = color565(COLOR_BITRATE);
theme.volbarout = color565(COLOR_VOLBAR_OUT);
theme.volbarin = color565(COLOR_VOLBAR_IN);
theme.playlist[0] = color565(COLOR_PLAYLIST_0);
@@ -119,7 +122,7 @@ void Config::setDefaults() {
store.dspon=true;
store.brightness=100;
store.contrast=55;
- strlcpy(store.sntp1,"0.ru.pool.ntp.org", 35);
+ strlcpy(store.sntp1,"pool.ntp.org", 35);
strlcpy(store.sntp2,"1.ru.pool.ntp.org", 35);
store.showweather=false;
strlcpy(store.weatherlat,"55.7512", 10);
@@ -218,7 +221,7 @@ void Config::setTitle(const char* title) {
memset(config.station.title, 0, BUFLEN);
strlcpy(config.station.title, title, BUFLEN);
u8fix(config.station.title);
- display.putRequest({NEWTITLE, 0});
+ display.putRequest(NEWTITLE);
}
void Config::setStation(const char* station) {
diff --git a/yoRadio/config.h b/yoRadio/src/core/config.h
similarity index 98%
rename from yoRadio/config.h
rename to yoRadio/src/core/config.h
index d762ffb..67bcc94 100644
--- a/yoRadio/config.h
+++ b/yoRadio/src/core/config.h
@@ -27,6 +27,8 @@ void u8fix(char *src);
struct theme_t {
uint16_t background;
uint16_t meta;
+ uint16_t metabg;
+ uint16_t metafill;
uint16_t title1;
uint16_t title2;
uint16_t digit;
@@ -43,6 +45,7 @@ struct theme_t {
uint16_t ip;
uint16_t vol;
uint16_t rssi;
+ uint16_t bitrate;
uint16_t volbarout;
uint16_t volbarin;
uint16_t playlist[5];
diff --git a/yoRadio/controls.cpp b/yoRadio/src/core/controls.cpp
similarity index 82%
rename from yoRadio/controls.cpp
rename to yoRadio/src/core/controls.cpp
index 70f4139..ce590a7 100644
--- a/yoRadio/controls.cpp
+++ b/yoRadio/src/core/controls.cpp
@@ -33,7 +33,7 @@ constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]);
#endif
#if (ENC_BTNL!=255 && ENC_BTNR!=255) || (ENC2_BTNL!=255 && ENC2_BTNR!=255)
-#include "src/yoEncoder/yoEncoder.h"
+#include "../yoEncoder/yoEncoder.h"
#if (ENC_BTNL!=255 && ENC_BTNR!=255)
yoEncoder encoder = yoEncoder(ENC_BTNL, ENC_BTNR, ENCODER_STEPS, ENC_INTERNALPULLUP);
#endif
@@ -129,7 +129,7 @@ void initControls() {
}
void loopControls() {
- if(display.mode==LOST || display.mode==UPDATING) return;
+ if(display.mode()==LOST || display.mode()==UPDATING) return;
if (ctrls_on_loop) ctrls_on_loop();
#if ENC_BTNL!=255
encoderLoop();
@@ -178,9 +178,9 @@ void encoder2Loop() {
if (ENC2_BTNB != 255) {
bp = digitalRead(ENC2_BTNB);
}
- if (bp == HIGH && display.mode == PLAYER) {
- display.putRequest({NEWMODE, STATIONS});
- while(display.mode != STATIONS) {delay(10);}
+ if (bp == HIGH && display.mode() == PLAYER) {
+ display.putRequest(NEWMODE, STATIONS);
+ while(display.mode() != STATIONS) {delay(10);}
}
controlsEvent(encoderDelta > 0, encoderDelta);
}
@@ -200,12 +200,12 @@ void irBlink() {
void irNum(byte num) {
uint16_t s;
if (display.numOfNextStation == 0 && num == 0) return;
- display.putRequest({NEWMODE, NUMBERS});
+ display.putRequest(NEWMODE, NUMBERS);
if (display.numOfNextStation > UINT16_MAX / 10) return;
s = display.numOfNextStation * 10 + num;
if (s > config.store.countStation) return;
display.numOfNextStation = s;
- display.putRequest({NEXTSTATION, s});
+ display.putRequest(NEXTSTATION, s);
}
void irLoop() {
@@ -223,11 +223,11 @@ void irLoop() {
}
switch (irVolRepeat) {
case 1: {
- controlsEvent(display.mode == STATIONS ? false : true);
+ controlsEvent(display.mode() == STATIONS ? false : true);
break;
}
case 2: {
- controlsEvent(display.mode == STATIONS ? true : false);
+ controlsEvent(display.mode() == STATIONS ? true : false);
break;
}
}
@@ -237,8 +237,8 @@ void irLoop() {
switch (target){
case IR_PLAY: {
irBlink();
- if (display.mode == NUMBERS) {
- display.putRequest({NEWMODE, PLAYER});
+ if (display.mode() == NUMBERS) {
+ display.putRequest(NEWMODE, PLAYER);
player.play(display.numOfNextStation);
display.numOfNextStation = 0;
break;
@@ -255,23 +255,22 @@ void irLoop() {
break;
}
case IR_UP: {
- controlsEvent(display.mode == STATIONS ? false : true);
+ controlsEvent(display.mode() == STATIONS ? false : true);
irVolRepeat = 1;
break;
}
case IR_DOWN: {
- controlsEvent(display.mode == STATIONS ? true : false);
+ controlsEvent(display.mode() == STATIONS ? true : false);
irVolRepeat = 2;
break;
}
case IR_HASH: {
- if (display.mode == NUMBERS) {
- display.putRequest({RETURNTITLE, 0});
- display.putRequest({NEWMODE, PLAYER});
+ if (display.mode() == NUMBERS) {
+ display.putRequest(NEWMODE, PLAYER);
display.numOfNextStation = 0;
break;
}
- display.putRequest({NEWMODE, display.mode == PLAYER ? STATIONS : PLAYER});
+ display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER);
break;
}
case IR_0: {
@@ -378,8 +377,8 @@ void touchLoop() {
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);
+ uint16_t touchX = map(p.x, TS_X_MIN, TS_X_MAX, 0, dsp.width());
+ uint16_t touchY = map(p.y, TS_Y_MIN, TS_Y_MAX, 0, dsp.height());
if (!wastouched) { /* START TOUCH */
oldTouchP[0] = touchX;
oldTouchP[1] = touchY;
@@ -393,9 +392,9 @@ void touchLoop() {
case TSD_LEFT:
case TSD_RIGHT: {
touchLongPress=millis();
- if(display.mode==PLAYER || display.mode==VOL){
- int16_t xDelta = map(abs(touchVol - touchX), 0, display.screenwidth, 0, TS_STEPS);
- display.putRequest({NEWMODE, VOL});
+ if(display.mode()==PLAYER || display.mode()==VOL){
+ int16_t xDelta = map(abs(touchVol - touchX), 0, dsp.width(), 0, TS_STEPS);
+ display.putRequest(NEWMODE, VOL);
if (xDelta>1) {
controlsEvent((touchVol - touchX)<0);
touchVol = touchX;
@@ -406,9 +405,9 @@ void touchLoop() {
case TSD_UP:
case TSD_DOWN: {
touchLongPress=millis();
- if(display.mode==PLAYER || display.mode==STATIONS){
- int16_t yDelta = map(abs(touchStation - touchY), 0, display.screenheight, 0, TS_STEPS);
- display.putRequest({NEWMODE, STATIONS});
+ if(display.mode()==PLAYER || display.mode()==STATIONS){
+ int16_t yDelta = map(abs(touchStation - touchY), 0, dsp.height(), 0, TS_STEPS);
+ display.putRequest(NEWMODE, STATIONS);
if (yDelta>1) {
controlsEvent((touchStation - touchY)<0);
touchStation = touchY;
@@ -429,10 +428,11 @@ void touchLoop() {
} else {
if (wastouched) {/* END TOUCH */
if (direct == TDS_REQUEST) {
- if(millis()-touchLongPress < BTN_PRESS_TICKS*2){
- onBtnClick(EVT_BTNCENTER);
+ uint32_t pressTicks = millis()-touchLongPress;
+ if( pressTicks < BTN_PRESS_TICKS*2){
+ if(pressTicks > 50) onBtnClick(EVT_BTNCENTER);
}else{
- display.putRequest({NEWMODE, display.mode == PLAYER ? STATIONS : PLAYER});
+ display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER);
}
}
direct = TSD_STAY;
@@ -453,11 +453,11 @@ void onBtnLongPressStart(int id) {
}
case EVT_BTNCENTER:
case EVT_ENCBTNB: {
- display.putRequest({NEWMODE, display.mode == PLAYER ? STATIONS : PLAYER});
+ display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER);
break;
}
case EVT_ENC2BTNB: {
- display.putRequest({NEWMODE, display.mode == PLAYER ? VOL : PLAYER});
+ display.putRequest(NEWMODE, display.mode() == PLAYER ? VOL : PLAYER);
break;
}
default:
@@ -502,10 +502,10 @@ void onBtnDuringLongPress(int id) {
}
case EVT_BTNUP:
case EVT_BTNDOWN: {
- if (display.mode == PLAYER) {
- display.putRequest({NEWMODE, STATIONS});
+ if (display.mode() == PLAYER) {
+ display.putRequest(NEWMODE, STATIONS);
}
- if (display.mode == STATIONS) {
+ if (display.mode() == STATIONS) {
controlsEvent(id == EVT_BTNDOWN);
}
break;
@@ -517,12 +517,12 @@ void onBtnDuringLongPress(int id) {
}
void controlsEvent(bool toRight, int8_t volDelta) {
- if (display.mode == NUMBERS) {
+ if (display.mode() == NUMBERS) {
display.numOfNextStation = 0;
- display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, PLAYER);
}
- if (display.mode != STATIONS) {
- display.putRequest({NEWMODE, VOL});
+ if (display.mode() != STATIONS) {
+ display.putRequest(NEWMODE, VOL);
if(volDelta!=0){
int nv = config.store.volume+volDelta;
if(nv<0) nv=0;
@@ -532,10 +532,13 @@ void controlsEvent(bool toRight, int8_t volDelta) {
player.stepVol(toRight);
}
}
- if (display.mode == STATIONS) {
+ if (display.mode() == STATIONS) {
display.resetQueue();
- display.putRequest({DRAWPLAYLIST, toRight});
-
+ int p = toRight ? display.currentPlItem + 1 : display.currentPlItem - 1;
+ if (p < 1) p = config.store.countStation;
+ if (p > config.store.countStation) p = 1;
+ display.currentPlItem = p;
+ display.putRequest(DRAWPLAYLIST, p);
}
}
@@ -548,15 +551,15 @@ void onBtnClick(int id) {
case EVT_BTNCENTER:
case EVT_ENCBTNB:
case EVT_ENC2BTNB: {
- if (display.mode == NUMBERS) {
+ if (display.mode() == NUMBERS) {
display.numOfNextStation = 0;
- display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, PLAYER);
}
- if (display.mode == PLAYER) {
+ if (display.mode() == PLAYER) {
player.toggle();
}
- if (display.mode == STATIONS) {
- display.putRequest({NEWMODE, PLAYER});
+ if (display.mode() == STATIONS) {
+ display.putRequest(NEWMODE, PLAYER);
player.play(display.currentPlItem);
}
break;
@@ -574,10 +577,10 @@ void onBtnClick(int id) {
player.prev();
}
} else {
- if (display.mode == PLAYER) {
- display.putRequest({NEWMODE, STATIONS});
+ if (display.mode() == PLAYER) {
+ display.putRequest(NEWMODE, STATIONS);
}
- if (display.mode == STATIONS) {
+ if (display.mode() == STATIONS) {
controlsEvent(id == EVT_BTNDOWN);
}
}
@@ -589,18 +592,18 @@ void onBtnClick(int id) {
void onBtnDoubleClick(int id) {
switch ((controlEvt_e)id) {
case EVT_BTNLEFT: {
- if (display.mode != PLAYER) return;
+ if (display.mode() != PLAYER) return;
player.prev();
break;
}
case EVT_BTNCENTER:
case EVT_ENCBTNB:
case EVT_ENC2BTNB: {
- display.putRequest({NEWMODE, display.mode == PLAYER ? VOL : PLAYER});
+ display.putRequest(NEWMODE, display.mode() == PLAYER ? VOL : PLAYER);
break;
}
case EVT_BTNRIGHT: {
- if (display.mode != PLAYER) return;
+ if (display.mode() != PLAYER) return;
player.next();
break;
}
diff --git a/yoRadio/controls.h b/yoRadio/src/core/controls.h
similarity index 100%
rename from yoRadio/controls.h
rename to yoRadio/src/core/controls.h
diff --git a/yoRadio/src/core/display.cpp b/yoRadio/src/core/display.cpp
new file mode 100644
index 0000000..15a1191
--- /dev/null
+++ b/yoRadio/src/core/display.cpp
@@ -0,0 +1,511 @@
+#include "options.h"
+
+#include "WiFi.h"
+#include "time.h"
+#include "display.h"
+#include "player.h"
+#include "network.h"
+
+
+Display display;
+#ifdef USE_NEXTION
+Nextion nextion;
+#endif
+
+#ifndef DUMMYDISPLAY
+//============================================================================================================================
+DspCore dsp;
+
+Page *pages[] = { new Page(), new Page(), new Page() };
+
+#ifndef CORE_STACK_SIZE
+ #define CORE_STACK_SIZE 1024*3
+#endif
+#ifndef DSP_TASK_DELAY
+ #define DSP_TASK_DELAY 2
+#endif
+
+TaskHandle_t DspTask;
+QueueHandle_t displayQueue;
+
+void returnPlayer(){
+ display.putRequest(NEWMODE, PLAYER);
+}
+
+void Display::_createDspTask(){
+ xTaskCreatePinnedToCore(loopDspTask, "DspTask", CORE_STACK_SIZE, NULL, 4, &DspTask, !xPortGetCoreID());
+}
+
+void loopDspTask(void * pvParameters){
+ while(true){
+ if(displayQueue==NULL) break;
+ display.loop();
+ vTaskDelay(DSP_TASK_DELAY);
+ }
+ vTaskDelete( NULL );
+ DspTask=NULL;
+}
+
+void Display::init() {
+#ifdef USE_NEXTION
+ nextion.begin();
+#endif
+ _bootStep = 0;
+ dsp.initDisplay();
+ displayQueue=NULL;
+ displayQueue = xQueueCreate( 5, sizeof( requestParams_t ) );
+ while(displayQueue==NULL){;}
+ _createDspTask();
+ while(!_bootStep==0) { delay(10); }
+ //_pager.begin();
+ //_bootScreen();
+}
+
+void Display::_bootScreen(){
+ _boot = new Page();
+ _boot->addWidget(new ProgressWidget(bootWdtConf, bootPrgConf, BOOT_PRG_COLOR, 0));
+ _bootstring = (TextWidget*) &_boot->addWidget(new TextWidget(bootstrConf, 50, true, BOOT_TXT_COLOR, 0));
+ _pager.addPage(_boot);
+ _pager.setPage(_boot, true);
+ dsp.drawLogo(bootLogoTop);
+ _bootStep = 1;
+}
+
+void Display::_buildPager(){
+ _meta.init("*", metaConf, config.theme.meta, config.theme.metabg);
+ _title1.init("*", title1Conf, config.theme.title1, config.theme.background);
+ _clock.init(clockConf, 0, 0);
+ #if DSP_MODEL==DSP_NOKIA5110
+ _plcurrent.init("*", playlistConf, 0, 1);
+ #else
+ _plcurrent.init("*", playlistConf, config.theme.meta, config.theme.metabg);
+ #endif
+ #ifndef HIDE_TITLE2
+ _title2 = new ScrollWidget("*", title2Conf, config.theme.title2, config.theme.background);
+ #endif
+ #if !defined(DSP_LCD) && DSP_MODEL!=DSP_NOKIA5110
+ _plbackground = new FillWidget(playlBGConf, config.theme.metafill);
+ _metabackground = new FillWidget(metaBGConf, config.theme.metafill);
+ #endif
+ #if DSP_MODEL==DSP_NOKIA5110
+ _plbackground = new FillWidget(playlBGConf, 1);
+ //_metabackground = new FillWidget(metaBGConf, 1);
+ #endif
+ #ifndef HIDE_VU
+ _vuwidget = new VuWidget(vuConf, bandsConf, config.theme.vumax, config.theme.vumin, config.theme.background);
+ #endif
+ #ifndef HIDE_VOLBAR
+ _volbar = new SliderWidget(volbarConf, config.theme.volbarin, config.theme.background, 254, config.theme.volbarout);
+ #endif
+ #ifndef HIDE_HEAPBAR
+ _heapbar = new SliderWidget(heapbarConf, config.theme.buffer, config.theme.background, psramInit()?300000:1600 * AUDIOBUFFER_MULTIPLIER2);
+ #endif
+ #ifndef HIDE_VOL
+ _voltxt = new TextWidget(voltxtConf, 10, false, config.theme.vol, config.theme.background);
+ #endif
+ #ifndef HIDE_IP
+ _volip = new TextWidget(iptxtConf, 30, false, config.theme.ip, config.theme.background);
+ #endif
+ #ifndef HIDE_RSSI
+ _rssi = new TextWidget(rssiConf, 20, false, config.theme.rssi, config.theme.background);
+ #endif
+ _nums.init(numConf, 10, false, config.theme.digit, config.theme.background);
+ #ifndef HIDE_WEATHER
+ _weather = new ScrollWidget("*", weatherConf, config.theme.weather, config.theme.background);
+ #endif
+
+ if(_volbar) _footer.addWidget( _volbar);
+ if(_voltxt) _footer.addWidget( _voltxt);
+ if(_volip) _footer.addWidget( _volip);
+ if(_rssi) _footer.addWidget( _rssi);
+ if(_heapbar) _footer.addWidget( _heapbar);
+
+ if(_metabackground) pages[PG_PLAYER]->addWidget( _metabackground);
+ pages[PG_PLAYER]->addWidget(&_meta);
+ pages[PG_PLAYER]->addWidget(&_title1);
+ if(_title2) pages[PG_PLAYER]->addWidget(_title2);
+ if(_weather) pages[PG_PLAYER]->addWidget(_weather);
+ #ifdef BITRATE_FULL
+ _fullbitrate = new BitrateWidget(fullbitrateConf, config.theme.bitrate, config.theme.background);
+ pages[PG_PLAYER]->addWidget( _fullbitrate);
+ #else
+ _bitrate = new TextWidget(bitrateConf, 30, false, config.theme.bitrate, config.theme.background);
+ pages[PG_PLAYER]->addWidget( _bitrate);
+ #endif
+ if(_vuwidget) pages[PG_PLAYER]->addWidget( _vuwidget);
+ pages[PG_PLAYER]->addWidget(&_clock);
+ pages[PG_PLAYER]->addPage(&_footer);
+
+ if(_metabackground) pages[PG_DIALOG]->addWidget( _metabackground);
+ pages[PG_DIALOG]->addWidget(&_meta);
+ pages[PG_DIALOG]->addWidget(&_nums);
+
+ #if !defined(DSP_LCD) && DSP_MODEL!=DSP_NOKIA5110
+ pages[PG_DIALOG]->addPage(&_footer);
+ #endif
+
+ if(_plbackground) pages[PG_PLAYLIST]->addWidget( _plbackground);
+ pages[PG_PLAYLIST]->addWidget(&_plcurrent);
+
+ for(const auto& p: pages) _pager.addPage(p);
+}
+
+void Display::_apScreen() {
+ if(_boot) _pager.removePage(_boot);
+ #ifndef DSP_LCD
+ _boot = new Page();
+ #if DSP_MODEL!=DSP_NOKIA5110
+ _boot->addWidget(new FillWidget(metaBGConf, config.theme.metafill));
+ #endif
+ ScrollWidget *bootTitle = (ScrollWidget*) &_boot->addWidget(new ScrollWidget("*", apTitleConf, config.theme.meta, config.theme.metabg));
+ bootTitle->setText("ёRadio AP Mode");
+ TextWidget *apname = (TextWidget*) &_boot->addWidget(new TextWidget(apNameConf, 30, false, config.theme.title1, config.theme.background));
+ apname->setText(apNameTxt);
+ TextWidget *apname2 = (TextWidget*) &_boot->addWidget(new TextWidget(apName2Conf, 30, false, config.theme.metabg, config.theme.background));
+ apname2->setText(apSsid);
+ TextWidget *appass = (TextWidget*) &_boot->addWidget(new TextWidget(apPassConf, 30, false, config.theme.title1, config.theme.background));
+ appass->setText(apPassTxt);
+ TextWidget *appass2 = (TextWidget*) &_boot->addWidget(new TextWidget(apPass2Conf, 30, false, config.theme.metabg, config.theme.background));
+ appass2->setText(apPassword);
+ ScrollWidget *bootSett = (ScrollWidget*) &_boot->addWidget(new ScrollWidget("*", apSettConf, config.theme.title2, config.theme.background));
+ bootSett->setText(WiFi.softAPIP().toString().c_str(), apSettFmt);
+ _pager.addPage(_boot);
+ _pager.setPage(_boot);
+ #else
+ dsp.apScreen();
+ #endif
+}
+
+void Display::_start() {
+ if(_boot) _pager.removePage(_boot);
+ #ifdef USE_NEXTION
+ nextion.wake();
+ #endif
+ if (network.status != CONNECTED) {
+ _apScreen();
+ #ifdef USE_NEXTION
+ nextion.apScreen();
+ #endif
+ _bootStep = 2;
+ return;
+ }
+ #ifdef USE_NEXTION
+ nextion.putcmd("page player");
+ nextion.start();
+ #endif
+ _buildPager();
+ _mode = PLAYER;
+ config.setTitle(const_PlReady);
+
+ if(_heapbar) _heapbar->lock(!config.store.audioinfo);
+
+ if(_weather) _weather->lock(!config.store.showweather);
+ if(_weather && config.store.showweather) _weather->setText(const_getWeather);
+
+ if(_vuwidget) _vuwidget->lock();
+ if(_rssi) _rssi->setText(WiFi.RSSI(), rssiFmt);
+ #ifndef HIDE_IP
+ if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt);
+ #endif
+ _pager.setPage( pages[PG_PLAYER]);
+ _volume();
+ _station();
+ _time(false);
+ _bootStep = 2;
+}
+
+void Display::_showDialog(const char *title){
+ dsp.setScrollId(NULL);
+ _pager.setPage( pages[PG_DIALOG]);
+ #ifdef META_MOVE
+ _meta.moveTo(metaMove);
+ #endif
+ _meta.setAlign(WA_CENTER);
+ _meta.setText(title);
+}
+
+void Display::_setReturnTicker(uint8_t time_s){
+ _returnTicker.detach();
+ _returnTicker.once(time_s, returnPlayer);
+}
+
+void Display::_swichMode(displayMode_e newmode) {
+ #ifdef USE_NEXTION
+ nextion.swichMode(newmode);
+ #endif
+ if (newmode == _mode || network.status != CONNECTED) return;
+ _mode = newmode;
+ dsp.setScrollId(NULL);
+ if (newmode == PLAYER) {
+ numOfNextStation = 0;
+ _returnTicker.detach();
+ #ifdef META_MOVE
+ _meta.moveBack();
+ #endif
+ _meta.setAlign(WA_LEFT);
+ _meta.setText(config.station.name);
+ _nums.setText("");
+ _pager.setPage( pages[PG_PLAYER]);
+ }
+ if (newmode == VOL) {
+ #ifndef HIDE_IP
+ _showDialog(const_DlgVolume);
+ #else
+ _showDialog(WiFi.localIP().toString().c_str());
+ #endif
+ }
+ if (newmode == LOST) _showDialog(const_DlgLost);
+ if (newmode == UPDATING) _showDialog(const_DlgUpdate);
+ if (newmode == INFO || newmode == SETTINGS || newmode == TIMEZONE || newmode == WIFI) _showDialog(const_DlgNextion);
+ if (newmode == NUMBERS) _showDialog("");
+ if (newmode == STATIONS) {
+ _pager.setPage( pages[PG_PLAYLIST]);
+ _plcurrent.setText("");
+ currentPlItem = config.store.lastStation;
+ _drawPlaylist();
+ }
+}
+
+void Display::resetQueue(){
+ xQueueReset(displayQueue);
+}
+
+void Display::_drawPlaylist() {
+ char buf[PLMITEMLENGHT];
+ dsp.drawPlaylist(currentPlItem, buf);
+ _plcurrent.setText(buf);
+/*#ifdef USE_NEXTION
+ nextion.drawPlaylist(currentPlItem);
+#endif*/
+ _setReturnTicker(30);
+}
+
+void Display::_drawNextStationNum(uint16_t num) {
+ char plMenu[1][40];
+ char currentItemText[40] = {0};
+ config.fillPlMenu(plMenu, num, 1, true);
+ strlcpy(currentItemText, plMenu[0], 39);
+ _setReturnTicker(30);
+ _meta.setText(currentItemText);
+ _nums.setText(num, "%d");
+/*#ifdef USE_NEXTION
+ nextion.drawNextStationNum(num);
+#endif*/
+}
+
+void Display::putRequest(displayRequestType_e type, int payload){
+ if(displayQueue==NULL) return;
+ requestParams_t request;
+ request.type = type;
+ request.payload = payload;
+ xQueueSend(displayQueue, &request, portMAX_DELAY);
+ #ifdef USE_NEXTION
+ nextion.putRequest(request);
+ #endif
+}
+
+void Display::_layoutChange(bool played){
+ if(config.store.vumeter){
+ if(played){
+ if(_vuwidget) _vuwidget->unlock();
+ _clock.moveTo(clockMove);
+ if(_weather) _weather->moveTo(weatherMoveVU);
+ }else{
+ if(_vuwidget) if(!_vuwidget->locked()) _vuwidget->lock();
+ _clock.moveBack();
+ if(_weather) _weather->moveBack();
+ }
+ }else{
+ if(played){
+ if(_weather) _weather->moveTo(weatherMove);
+ _clock.moveBack();
+ }else{
+ if(_weather) _weather->moveBack();
+ _clock.moveBack();
+ }
+ }
+}
+
+void Display::loop() {
+ if(_bootStep==0) {
+ _pager.begin();
+ _bootScreen();
+ return;
+ }
+ if(displayQueue==NULL) return;
+ _pager.loop();
+#ifdef USE_NEXTION
+ nextion.loop();
+#endif
+ requestParams_t request;
+ if(xQueueReceive(displayQueue, &request, 20)){
+ switch (request.type){
+ case NEWMODE: _swichMode((displayMode_e)request.payload); break;
+ case CLOCK:
+ if(_mode==PLAYER) _time();
+ /*#ifdef USE_NEXTION
+ if(_mode==TIMEZONE) nextion.localTime(network.timeinfo);
+ if(_mode==INFO) nextion.rssi();
+ #endif*/
+ break;
+ case NEWTITLE: _title(); break;
+ case NEWSTATION: _station(); break;
+ case NEXTSTATION: _drawNextStationNum(request.payload); break;
+ case DRAWPLAYLIST: _drawPlaylist(); break;
+ case DRAWVOL: _volume(); break;
+ case DBITRATE: {
+ char buf[20];
+ snprintf(buf, 20, bitrateFmt, config.station.bitrate);
+ if(_bitrate) { _bitrate->setText(config.station.bitrate==0?"":buf); }
+ if(_fullbitrate) {
+ _fullbitrate->setBitrate(config.station.bitrate);
+ _fullbitrate->setFormat(BF_MP3);
+ }
+ }
+ break;
+ case AUDIOINFO: if(_heapbar) { _heapbar->lock(!config.store.audioinfo); _heapbar->setValue(player.inBufferFilled()); } break;
+ case SHOWVUMETER: {
+ if(_vuwidget){
+ _vuwidget->lock(!config.store.vumeter);
+ _layoutChange(player.isRunning());
+ }
+ break;
+ }
+ case SHOWWEATHER: {
+ if(_weather) _weather->lock(!config.store.showweather);
+ if(!config.store.showweather){
+ #ifndef HIDE_IP
+ if(_volip) _volip->setText(WiFi.localIP().toString().c_str(), iptxtFmt);
+ #endif
+ }else{
+ if(_weather) _weather->setText(const_getWeather);
+ }
+ break;
+ }
+ case NEWWEATHER: {
+ if(_weather && network.weatherBuf) _weather->setText(network.weatherBuf);
+ break;
+ }
+ case BOOTSTRING: {
+ if(_bootstring) _bootstring->setText(config.ssids[request.payload].ssid, bootstrFmt);
+ /*#ifdef USE_NEXTION
+ char buf[50];
+ snprintf(buf, 50, bootstrFmt, config.ssids[request.payload].ssid);
+ nextion.bootString(buf);
+ #endif*/
+ break;
+ }
+ case DSPRSSI: if(_rssi){ _rssi->setText(request.payload, rssiFmt); } if (_heapbar && config.store.audioinfo) _heapbar->setValue(player.inBufferFilled()); break;
+ case PSTART: _layoutChange(true); break;
+ case PSTOP: _layoutChange(false); break;
+ case DSP_START: _start(); break;
+ default: break;
+ }
+ }
+ dsp.loop();
+}
+
+void Display::_station() {
+ _meta.setAlign(WA_LEFT);
+ _meta.setText(config.station.name);
+/*#ifdef USE_NEXTION
+ nextion.newNameset(config.station.name);
+ nextion.bitrate(config.station.bitrate);
+ nextion.bitratePic(ICON_NA);
+#endif*/
+}
+
+char *split(char *str, const char *delim) {
+ char *dmp = strstr(str, delim);
+ if (dmp == NULL) return NULL;
+ *dmp = '\0';
+ return dmp + strlen(delim);
+}
+
+void Display::_title() {
+ if (strlen(config.station.title) > 0) {
+ char tmpbuf[strlen(config.station.title)+1];
+ strlcpy(tmpbuf, config.station.title, strlen(config.station.title)+1);
+ char *stitle = split(tmpbuf, " - ");
+ if(stitle && _title2){
+ _title1.setText(tmpbuf);
+ _title2->setText(stitle);
+ }else{
+ _title1.setText(config.station.title);
+ if(_title2) _title2->setText("");
+ }
+ /*#ifdef USE_NEXTION
+ nextion.newTitle(config.station.title);
+ #endif*/
+ if (player_on_track_change) player_on_track_change();
+ }
+}
+
+void Display::_time(bool redraw) {
+ _clock.draw();
+ /*#ifdef USE_NEXTION
+ nextion.printClock(network.timeinfo);
+ #endif*/
+}
+
+void Display::_volume() {
+ if(_volbar) _volbar->setValue(config.store.volume);
+ #ifndef HIDE_VOL
+ if(_voltxt) _voltxt->setText(config.store.volume, voltxtFmt);
+ #endif
+ if(_mode==VOL) {
+ _setReturnTicker(3);
+ _nums.setText(config.store.volume, numtxtFmt);
+ }
+ /*#ifdef USE_NEXTION
+ nextion.setVol(config.store.volume, _mode == VOL);
+ #endif*/
+}
+
+void Display::flip(){ dsp.flip(); }
+
+void Display::invert(){ dsp.invert(); }
+
+void Display::setContrast(){
+ #if DSP_MODEL==DSP_NOKIA5110
+ dsp.setContrast(config.store.contrast);
+ #endif
+}
+
+bool Display::deepsleep(){
+#if defined(LCD_I2C) || defined(DSP_OLED) || BRIGHTNESS_PIN!=255
+ dsp.sleep();
+ return true;
+#endif
+ return false;
+}
+
+void Display::wakeup(){
+#if defined(LCD_I2C) || defined(DSP_OLED) || BRIGHTNESS_PIN!=255
+ dsp.wake();
+#endif
+}
+//============================================================================================================================
+#else // !DUMMYDISPLAY
+//============================================================================================================================
+void Display::init(){
+ #ifdef USE_NEXTION
+ nextion.begin(true);
+ #endif
+}
+void Display::_start(){
+ #ifdef USE_NEXTION
+ nextion.start();
+ #endif
+}
+void Display::putRequest(displayRequestType_e type, int payload){
+ if(type==DSP_START) _start();
+ #ifdef USE_NEXTION
+ requestParams_t request;
+ request.type = type;
+ request.payload = payload;
+ nextion.putRequest(request);
+ #endif
+}
+//============================================================================================================================
+#endif // DUMMYDISPLAY
diff --git a/yoRadio/src/core/display.h b/yoRadio/src/core/display.h
new file mode 100644
index 0000000..f98aa53
--- /dev/null
+++ b/yoRadio/src/core/display.h
@@ -0,0 +1,113 @@
+#ifndef display_h
+#define display_h
+#include "options.h"
+
+#include "Arduino.h"
+#include
+#include "config.h"
+
+#include "../displays/dspcore.h"
+
+enum displayMode_e { PLAYER, VOL, STATIONS, NUMBERS, LOST, UPDATING, INFO, SETTINGS, TIMEZONE, WIFI, CLEAR };
+enum pages_e : uint8_t { PG_PLAYER=0, PG_DIALOG=1, PG_PLAYLIST=2 };
+//enum dialogType_e : uint8_t { DG_NONE=0, DG_VOLUME=1, DG_LOST=2, DG_UPDATING=3, DG_NEXTION=4 };
+
+enum displayRequestType_e { BOOTSTRING, NEWMODE, CLOCK, NEWTITLE, NEWSTATION, NEXTSTATION, DRAWPLAYLIST, DRAWVOL, DBITRATE, AUDIOINFO, SHOWVUMETER, DSPRSSI, SHOWWEATHER, NEWWEATHER, PSTOP, PSTART, DSP_START };
+struct requestParams_t
+{
+ displayRequestType_e type;
+ int payload;
+};
+
+#if NEXTION_RX!=255 && NEXTION_TX!=255
+ #define USE_NEXTION
+ #include "../displays/nextion.h"
+#endif
+
+#ifndef DUMMYDISPLAY
+ void loopDspTask(void * pvParameters);
+
+class Display {
+ public:
+ uint16_t currentPlItem;
+ uint16_t numOfNextStation;
+ displayMode_e _mode;
+ public:
+ Display() {};
+ displayMode_e mode() { return _mode; }
+ void mode(displayMode_e m) { _mode=m; }
+ void init();
+ void loop();
+ void _start();
+ bool ready() { return _bootStep==2; }
+ void resetQueue();
+ void putRequest(displayRequestType_e type, int payload=0);
+ void flip();
+ void invert();
+ bool deepsleep();
+ void wakeup();
+ void setContrast();
+ private:
+ ScrollWidget _meta, _title1, _plcurrent;
+ ScrollWidget *_weather;
+ ScrollWidget *_title2;
+ BitrateWidget *_fullbitrate;
+ FillWidget *_metabackground, *_plbackground;
+ SliderWidget *_volbar, *_heapbar;
+ Pager _pager;
+ Page _footer;
+ VuWidget *_vuwidget;
+ NumWidget _nums;
+ ProgressWidget _testprogress;
+ ClockWidget _clock;
+ Page *_boot;
+ TextWidget *_bootstring, *_volip, *_voltxt, *_rssi, *_bitrate;
+ Ticker _returnTicker;
+ uint8_t _bootStep;
+ void _time(bool redraw = false);
+ void _apScreen();
+ void _swichMode(displayMode_e newmode);
+ void _drawPlaylist();
+ void _volume();
+ void _title();
+ void _station();
+ void _drawNextStationNum(uint16_t num);
+ void _createDspTask();
+ void _showDialog(const char *title);
+ void _buildPager();
+ void _bootScreen();
+ void _setReturnTicker(uint8_t time_s);
+ void _layoutChange(bool played);
+};
+
+#else
+
+class Display {
+ public:
+ uint16_t currentPlItem;
+ uint16_t numOfNextStation;
+ displayMode_e _mode;
+ public:
+ Display() {};
+ displayMode_e mode() { return _mode; }
+ void mode(displayMode_e m) { _mode=m; }
+ void init();
+ void _start();
+ void putRequest(displayRequestType_e type, int payload=0);
+ void loop(){}
+ bool ready() { return true; }
+ void resetQueue(){}
+ 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 flip(){}
+ void invert(){}
+ void setContrast(){}
+ bool deepsleep(){return true;}
+ void wakeup(){}
+};
+
+#endif
+
+extern Display display;
+
+#endif
diff --git a/yoRadio/mqtt.cpp b/yoRadio/src/core/mqtt.cpp
similarity index 100%
rename from yoRadio/mqtt.cpp
rename to yoRadio/src/core/mqtt.cpp
diff --git a/yoRadio/mqtt.h b/yoRadio/src/core/mqtt.h
similarity index 86%
rename from yoRadio/mqtt.h
rename to yoRadio/src/core/mqtt.h
index 224870c..d4a3e07 100644
--- a/yoRadio/mqtt.h
+++ b/yoRadio/src/core/mqtt.h
@@ -1,8 +1,8 @@
#ifndef mqtt_h
#define mqtt_h
-#if __has_include("mqttoptions.h")
-#include "mqttoptions.h"
+#if __has_include("../../mqttoptions.h")
+#include "../../mqttoptions.h"
#include
diff --git a/yoRadio/netserver.cpp b/yoRadio/src/core/netserver.cpp
similarity index 90%
rename from yoRadio/netserver.cpp
rename to yoRadio/src/core/netserver.cpp
index d27f3a4..4e0f099 100644
--- a/yoRadio/netserver.cpp
+++ b/yoRadio/src/core/netserver.cpp
@@ -90,7 +90,7 @@ void handleUpdate(AsyncWebServerRequest *request, String filename, size_t index,
int target = (request->getParam("updatetarget", true)->value() == "spiffs") ? U_SPIFFS : U_FLASH;
Serial.printf("Update Start: %s\n", filename.c_str());
player.mode = STOPPED;
- display.putRequest({NEWMODE, UPDATING});
+ display.putRequest(NEWMODE, UPDATING);
if (!Update.begin(UPDATE_SIZE_UNKNOWN, target)) {
Update.printError(Serial);
request->send(200, "text/html", updateError());
@@ -142,38 +142,12 @@ size_t NetServer::chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t
void NetServer::chunkedHtmlPage(const String& contentType, AsyncWebServerRequest *request, const char * path, bool gzip) {
memset(chunkedPathBuffer, 0, sizeof(chunkedPathBuffer));
strlcpy(chunkedPathBuffer, path, sizeof(chunkedPathBuffer)-1);
- PLOW();
AsyncWebServerResponse *response = request->beginChunkedResponse(contentType, chunkedHtmlPageCallback, processor);
- xSemaphoreTake(player.playmutex, portMAX_DELAY);
- request->send(response);
- xSemaphoreGive(player.playmutex); PHIG();
-}
-/*
-void NetServer::chunkedHtmlPage(const String& contentType, AsyncWebServerRequest *request, const char * path, bool gzip) {
- static char pathbuffer[40] = { 0 };
- strlcpy(pathbuffer, path, 39);
- PLOW();
- AsyncWebServerResponse *response = request->beginChunkedResponse(contentType, [](uint8_t *buffer, size_t maxLen, size_t index) -> size_t {
- File requiredfile = SPIFFS.open(pathbuffer, "r");
- if (!requiredfile) return 0;
- size_t filesize = requiredfile.size();
- size_t needread = filesize - index;
- if (!needread) return 0;
- size_t canread = (needread > maxLen) ? maxLen : needread;
- DBGVB("[%s] seek to %d in %s and read %d bytes with maxLen=%d", __func__, index, pathbuffer, canread, maxLen);
- requiredfile.seek(index, SeekSet);
- requiredfile.read(buffer, canread);
- index += canread;
- if (requiredfile) requiredfile.close();
- return canread;
- }, processor); // AsyncWebServerResponse
-
xSemaphoreTake(player.playmutex, portMAX_DELAY);
request->send(response);
xSemaphoreGive(player.playmutex);
- PHIG();
}
- */
+
void NetServer::loop() {
if (shouldReboot) {
Serial.println("Rebooting...");
@@ -181,7 +155,6 @@ void NetServer::loop() {
ESP.restart();
}
websocket.cleanupClients();
- //if (playlistrequest > 0) { requestOnChange(PLAYLIST, playlistrequest); playlistrequest = 0; } /* Cleanup this */
switch (importRequest) {
case IMPL: importPlaylist(); importRequest = IMDONE; break;
case IMWIFI: config.saveWifi(); importRequest = IMDONE; break;
@@ -229,14 +202,14 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
byte valb = atoi(val);
config.store.audioinfo = valb;
config.save();
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(AUDIOINFO);
return;
}
if (strcmp(cmd, "vumeter") == 0) {
byte valb = atoi(val);
config.store.vumeter = valb;
config.save();
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(SHOWVUMETER);
return;
}
if (strcmp(cmd, "softap") == 0) {
@@ -256,7 +229,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
byte valb = atoi(val);
config.store.numplaylist = valb;
config.save();
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
return;
}
if (strcmp(cmd, "fliptouch") == 0) {
@@ -277,7 +250,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
config.store.flipscreen = valb;
config.save();
display.flip();
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
return;
}
if (strcmp(cmd, "brightness") == 0) {
@@ -324,7 +297,8 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
tzdone = true;
}
if (tzdone) {
- network.requestTimeSync(true);
+ //network.requestTimeSync(true);
+ network.forceTimeSync = true;
config.save();
}
return;
@@ -351,11 +325,9 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
uint8_t valb = atoi(val);
config.store.showweather = valb == 1;
config.save();
- display.showWeather();
-#ifdef USE_NEXTION
- nextion.startWeather();
-#endif
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ network.trueWeather=false;
+ network.forceWeather = true;
+ display.putRequest(SHOWWEATHER);
return;
}
if (strcmp(cmd, "lat") == 0) {
@@ -369,11 +341,8 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
if (strcmp(cmd, "key") == 0) {
strlcpy(config.store.weatherkey, val, 64);
config.save();
- display.showWeather();
-#ifdef USE_NEXTION
- nextion.startWeather();
-#endif
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ network.trueWeather=false;
+ display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
return;
}
/* RESETS */
@@ -384,7 +353,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
config.store.vumeter = false;
config.store.softapdelay = 0;
config.save();
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
requestOnChange(GETSYSTEM, clientId);
return;
}
@@ -400,7 +369,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
display.setContrast();
config.store.numplaylist = false;
config.save();
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
requestOnChange(GETSCREEN, clientId);
return;
}
@@ -411,7 +380,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
strlcpy(config.store.sntp2, "0.ru.pool.ntp.org", 35);
config.save();
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
- network.requestTimeSync(true);
+ network.forceTimeSync = true;
requestOnChange(GETTIMEZONE, clientId);
return;
}
@@ -421,11 +390,8 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
strlcpy(config.store.weatherlon, "37.6184", 10);
strlcpy(config.store.weatherkey, "", 64);
config.save();
- display.showWeather();
-#ifdef USE_NEXTION
- nextion.startWeather();
-#endif
- display.putRequest({NEWMODE, CLEAR}); display.putRequest({NEWMODE, PLAYER});
+ network.trueWeather=false;
+ display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
requestOnChange(GETWEATHER, clientId);
return;
}
@@ -557,7 +523,16 @@ bool NetServer::importPlaylist() {
SPIFFS.remove(TMP_PATH);
return false;
}
-
+#ifndef DSP_NOT_FLIPPED
+ #define DSP_CAN_FLIPPED true
+#else
+ #define DSP_CAN_FLIPPED false
+#endif
+#if !defined(HIDE_WEATHER) && (!defined(DUMMYDISPLAY) && !defined(USE_NEXTION))
+ #define SHOW_WEATHER true
+#else
+ #define SHOW_WEATHER false
+#endif
void NetServer::requestOnChange(requestType_e request, uint8_t clientId) {
char buf[BUFLEN * 2] = { 0 };
switch (request) {
@@ -568,22 +543,24 @@ void NetServer::requestOnChange(requestType_e request, uint8_t clientId) {
String act = F("\"group_wifi\",");
if (network.status == CONNECTED) {
act += F("\"group_system\",");
- if (BRIGHTNESS_PIN != 255 || DSP_FLIPPED == 1 || DSP_MODEL == DSP_NOKIA5110 || dbgact) act += F("\"group_display\",");
-#ifdef USE_NEXTION
+ if (BRIGHTNESS_PIN != 255 || DSP_CAN_FLIPPED || DSP_MODEL == DSP_NOKIA5110 || dbgact) act += F("\"group_display\",");
+ #ifdef USE_NEXTION
act += F("\"group_nextion\",");
- if (WEATHER_READY == 0 || dbgact) act += F("\"group_weather\",");
+ if (!SHOW_WEATHER || dbgact) act += F("\"group_weather\",");
nxtn=true;
-#endif
-#if defined(LCD_I2C) || DSP_OLED
+ #endif
+ #if defined(LCD_I2C) || defined(DSP_OLED)
act += F("\"group_oled\",");
-#endif
- if (VU_READY == 1 || dbgact) act += F("\"group_vu\",");
+ #endif
+ #ifndef HIDE_VU
+ act += F("\"group_vu\",");
+ #endif
if (BRIGHTNESS_PIN != 255 || nxtn || dbgact) act += F("\"group_brightness\",");
- if (DSP_FLIPPED == 1 || dbgact) act += F("\"group_tft\",");
+ if (DSP_CAN_FLIPPED || dbgact) act += F("\"group_tft\",");
if (TS_CS != 255 || dbgact) act += F("\"group_touch\",");
if (DSP_MODEL == DSP_NOKIA5110) act += F("\"group_nokia\",");
act += F("\"group_timezone\",");
- if (WEATHER_READY == 1 || dbgact) act += F("\"group_weather\",");
+ if (SHOW_WEATHER || dbgact) act += F("\"group_weather\",");
act += F("\"group_controls\",");
if (ENC_BTNL != 255 || ENC2_BTNL != 255 || dbgact) act += F("\"group_encoder\",");
if (IR_PIN != 255 || dbgact) act += F("\"group_ir\",");
@@ -626,7 +603,6 @@ String processor(const String& var) { // %Templates%
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
- PLOW();
request->_tempFile = SPIFFS.open(TMP_PATH , "w");
}
if (len) {
@@ -635,7 +611,6 @@ void handleUpload(AsyncWebServerRequest *request, String filename, size_t index,
}
if (final) {
request->_tempFile.close();
- PHIG();
}
}
@@ -674,7 +649,7 @@ void handleHTTPArgs(AsyncWebServerRequest * request) {
if (network.status == CONNECTED) {
bool commandFound=false;
if (request->hasArg("start")) { player.request.station = config.store.lastStation; commandFound=true; }
- if (request->hasArg("stop")) { player.mode = STOPPED; config.setTitle("[stopped]"); commandFound=true; }
+ if (request->hasArg("stop")) { player.mode = STOPPED; config.setTitle(const_PlStopped); commandFound=true; }
if (request->hasArg("toggle")) { player.toggle(); commandFound=true; }
if (request->hasArg("prev")) { player.prev(); commandFound=true; }
if (request->hasArg("next")) { player.next(); commandFound=true; }
diff --git a/yoRadio/netserver.h b/yoRadio/src/core/netserver.h
similarity index 86%
rename from yoRadio/netserver.h
rename to yoRadio/src/core/netserver.h
index 914faf6..171c212 100644
--- a/yoRadio/netserver.h
+++ b/yoRadio/src/core/netserver.h
@@ -8,12 +8,8 @@
enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, ITEM=3, TITLE=4, VOLUME=5, NRSSI=6, BITRATE=7, MODE=8, EQUALIZER=9, BALANCE=10, PLAYLISTSAVED=11, GETMODE=12, GETINDEX=13, GETACTIVE=14, GETSYSTEM=15, GETSCREEN=16, GETTIMEZONE=17, GETWEATHER=18, GETCONTROLS=19, DSPON=20 };
enum import_e : uint8_t { IMDONE=0, IMPL=1, IMWIFI=2 };
-#define PLOW() //player.setBufsize(1600*2, -1); vTaskDelay(150)
-#define PHIG() //vTaskDelay(150); player.setBufsize(1600*AUDIOBUFFER_MULTIPLIER2, -1)
-
class NetServer {
public:
- //uint8_t playlistrequest; // ClientId want the playlist/* Cleanup this */
import_e importRequest;
bool resumePlay;
char chunkedPathBuffer[40];
diff --git a/yoRadio/src/core/network.cpp b/yoRadio/src/core/network.cpp
new file mode 100644
index 0000000..ea13e1e
--- /dev/null
+++ b/yoRadio/src/core/network.cpp
@@ -0,0 +1,288 @@
+#include "network.h"
+#include "WiFi.h"
+#include "display.h"
+#include "options.h"
+#include "config.h"
+#include "telnet.h"
+#include "netserver.h"
+
+Network network;
+
+TaskHandle_t syncTaskHandle;
+bool getWeather(char *wstr);
+void doSync(void * pvParameters);
+
+void ticks() {
+ static const uint16_t weatherSyncInterval=1800;
+ //static const uint16_t weatherSyncIntervalFail=10;
+ static const uint16_t timeSyncInterval=3600;
+ static uint16_t timeSyncTicks = 0;
+ static uint16_t weatherSyncTicks = 0;
+ static bool divrssi;
+ timeSyncTicks++;
+ weatherSyncTicks++;
+ divrssi = !divrssi;
+ if(network.forceTimeSync || network.forceWeather){
+ xTaskCreatePinnedToCore(doSync, "doSync", 1024 * 4, NULL, 0, &syncTaskHandle, 0);
+ }
+ if(timeSyncTicks >= timeSyncInterval){
+ timeSyncTicks=0;
+ network.forceTimeSync = true;
+ }
+ if(weatherSyncTicks >= weatherSyncInterval){
+ weatherSyncTicks=0;
+ network.forceWeather = true;
+ }
+
+ if(network.timeinfo.tm_year>100) {
+ network.timeinfo.tm_sec++;
+ mktime(&network.timeinfo);
+ display.putRequest(CLOCK);
+ }
+
+ if(divrssi) {
+ int rs = WiFi.RSSI();
+ netserver.setRSSI(rs);
+ display.putRequest(DSPRSSI, rs);
+ }
+}
+
+#define DBGAP false
+
+void Network::begin() {
+ config.initNetwork();
+ ctimer.detach();
+ forceTimeSync = forceWeather = true;
+ if (config.ssidsCount == 0 || DBGAP) {
+ raiseSoftAP();
+ return;
+ }
+ byte ls = (config.store.lastSSID == 0 || config.store.lastSSID > config.ssidsCount) ? 0 : config.store.lastSSID - 1;
+ byte startedls = ls;
+ byte errcnt = 0;
+ WiFi.mode(WIFI_STA);
+ while (true) {
+ Serial.printf("[BOOT]\tAttempt to connect to %s...\n", config.ssids[ls].ssid);
+ display.putRequest(BOOTSTRING, ls);
+ WiFi.begin(config.ssids[ls].ssid, config.ssids[ls].password);
+ while (WiFi.status() != WL_CONNECTED) {
+ Serial.print(".");
+ delay(500);
+ digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
+ errcnt++;
+ if (errcnt > 16) {
+ errcnt = 0;
+ ls++;
+ if (ls > config.ssidsCount - 1) ls = 0;
+ break;
+ }
+ }
+ if (WiFi.status() != WL_CONNECTED && ls == startedls) {
+ raiseSoftAP();
+ return;
+ }
+ if (WiFi.status() == WL_CONNECTED) {
+ config.setLastSSID(ls + 1);
+ break; // отстрелялись
+ }
+ }
+ Serial.println(".");
+ digitalWrite(LED_BUILTIN, LOW);
+ status = CONNECTED;
+ WiFi.setSleep(false);
+
+ weatherBuf=NULL;
+ trueWeather = false;
+ #if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
+ weatherBuf = (char *) malloc(sizeof(char) * WEATHER_STRING_L);
+ memset(weatherBuf, 0, WEATHER_STRING_L);
+ #endif
+
+ if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){
+ configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
+ }else if(strlen(config.store.sntp1)>0){
+ configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
+ }
+ ctimer.attach(1, ticks);
+ if (network_on_connect) network_on_connect();
+}
+
+void Network::requestTimeSync(bool withTelnetOutput, uint8_t clientId) {
+ if (withTelnetOutput) {
+ char timeStringBuff[50];
+ strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S", &timeinfo);
+ if (config.store.tzHour < 0) {
+ telnet.printf(clientId, "##SYS.DATE#: %s%03d:%02d\n> ", timeStringBuff, config.store.tzHour, config.store.tzMin);
+ } else {
+ telnet.printf(clientId, "##SYS.DATE#: %s+%02d:%02d\n> ", timeStringBuff, config.store.tzHour, config.store.tzMin);
+ }
+ }
+}
+
+void rebootTime() {
+ ESP.restart();
+}
+
+void Network::raiseSoftAP() {
+ WiFi.mode(WIFI_AP);
+ WiFi.softAP(apSsid, apPassword);
+ Serial.printf("\n\nRunning in AP mode.\nConnect to AP %s with password %s for settings.\n\n", apSsid, apPassword);
+ status = SOFT_AP;
+ if(config.store.softapdelay>0)
+ rtimer.once(config.store.softapdelay*60, rebootTime);
+}
+
+void Network::requestWeatherSync(){
+ display.putRequest(NEWWEATHER);
+}
+
+
+void doSync( void * pvParameters ) {
+ static uint8_t tsFailCnt = 0;
+ //static uint8_t wsFailCnt = 0;
+ if(network.forceTimeSync){
+ network.forceTimeSync = false;
+ if(getLocalTime(&network.timeinfo)){
+ tsFailCnt = 0;
+ network.forceTimeSync = false;
+ mktime(&network.timeinfo);
+ display.putRequest(CLOCK);
+ network.requestTimeSync(true);
+ }else{
+ if(tsFailCnt<4){
+ network.forceTimeSync = true;
+ tsFailCnt++;
+ }else{
+ network.forceTimeSync = false;
+ tsFailCnt=0;
+ }
+ }
+ }
+ if(network.weatherBuf && (strlen(config.store.weatherkey)!=0 && config.store.showweather) && network.forceWeather){
+ network.forceWeather = false;
+ network.trueWeather=getWeather(network.weatherBuf);
+ }
+ vTaskDelete( NULL );
+}
+
+bool getWeather(char *wstr) {
+#if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
+ WiFiClient client;
+ const char* host = "api.openweathermap.org";
+
+ if (!client.connect(host, 80)) {
+ Serial.println("## OPENWEATHERMAP ###: connection failed");
+ return false;
+ }
+ char httpget[250] = {0};
+ sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=%s&lang=%s&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", config.store.weatherlat, config.store.weatherlon, weatherUnits, weatherLang, config.store.weatherkey, host);
+ client.print(httpget);
+ unsigned long timeout = millis();
+ while (client.available() == 0) {
+ if (millis() - timeout > 2000UL) {
+ Serial.println("## OPENWEATHERMAP ###: client available timeout !");
+ client.stop();
+ return false;
+ }
+ }
+ timeout = millis();
+ String line = "";
+ if (client.connected()) {
+ while (client.available())
+ {
+ line = client.readStringUntil('\n');
+ if (strstr(line.c_str(), "\"temp\"") != NULL) {
+ client.stop();
+ break;
+ }
+ if ((millis() - timeout) > 500)
+ {
+ client.stop();
+ Serial.println("## OPENWEATHERMAP ###: client read timeout !");
+ return false;
+ }
+ }
+ }
+ if (strstr(line.c_str(), "\"temp\"") == NULL) {
+ Serial.println("## OPENWEATHERMAP ###: weather not found !");
+ return false;
+ }
+ char *tmpe;
+ char *tmps;
+ const char* cursor = line.c_str();
+ char desc[120], temp[20], hum[20], press[20], icon[5];
+
+ tmps = strstr(cursor, "\"description\":\"");
+ if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
+ tmps += 15;
+ tmpe = strstr(tmps, "\",\"");
+ if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
+ strlcpy(desc, tmps, tmpe - tmps + 1);
+ cursor = tmpe + 2;
+
+ // "ясно","icon":"01d"}],
+ tmps = strstr(cursor, "\"icon\":\"");
+ if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
+ tmps += 8;
+ tmpe = strstr(tmps, "\"}");
+ if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
+ strlcpy(icon, tmps, tmpe - tmps + 1);
+ cursor = tmpe + 2;
+
+ tmps = strstr(cursor, "\"temp\":");
+ if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
+ tmps += 7;
+ tmpe = strstr(tmps, ",\"");
+ if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
+ strlcpy(temp, tmps, tmpe - tmps + 1);
+ cursor = tmpe + 2;
+ float tempf = atof(temp);
+
+ tmps = strstr(cursor, "\"pressure\":");
+ if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
+ tmps += 11;
+ tmpe = strstr(tmps, ",\"");
+ if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
+ strlcpy(press, tmps, tmpe - tmps + 1);
+ cursor = tmpe + 2;
+ int pressi = (float)atoi(press) / 1.333;
+
+ tmps = strstr(cursor, "humidity\":");
+ if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
+ tmps += 10;
+ tmpe = strstr(tmps, ",\"");
+ if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
+ strlcpy(hum, tmps, tmpe - tmps + 1);
+
+ #ifdef USE_NEXTION
+ nextion.putcmdf("press_txt.txt=\"%dmm\"", pressi);
+ nextion.putcmdf("hum_txt.txt=\"%d%%\"", atoi(hum));
+ char cmd[30];
+ snprintf(cmd, sizeof(cmd)-1,"temp_txt.txt=\"%.1f\"", tempf);
+ nextion.putcmd(cmd);
+ int iconofset;
+ if(strstr(icon,"01")!=NULL) iconofset = 0;
+ else if(strstr(icon,"02")!=NULL) iconofset = 1;
+ else if(strstr(icon,"03")!=NULL) iconofset = 2;
+ else if(strstr(icon,"04")!=NULL) iconofset = 3;
+ else if(strstr(icon,"09")!=NULL) iconofset = 4;
+ else if(strstr(icon,"10")!=NULL) iconofset = 5;
+ else if(strstr(icon,"11")!=NULL) iconofset = 6;
+ else if(strstr(icon,"13")!=NULL) iconofset = 7;
+ else if(strstr(icon,"50")!=NULL) iconofset = 8;
+ else iconofset = 9;
+ nextion.putcmd("cond_img.pic", 50+iconofset);
+ nextion.weatherVisible(1);
+ #endif
+
+ Serial.printf("## OPENWEATHERMAP ###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
+ #ifdef WEATHER_FMT_SHORT
+ sprintf(wstr, weatherFmt, tempf, pressi, hum);
+ #else
+ sprintf(wstr, weatherFmt, desc, tempf, pressi, hum);
+ #endif
+ network.requestWeatherSync();
+ return true;
+#endif // if (DSP_MODEL!=DSP_DUMMY || defined(USE_NEXTION)) && !defined(HIDE_WEATHER)
+ return false;
+}
diff --git a/yoRadio/network.h b/yoRadio/src/core/network.h
similarity index 66%
rename from yoRadio/network.h
rename to yoRadio/src/core/network.h
index 1522b7e..2d70d72 100644
--- a/yoRadio/network.h
+++ b/yoRadio/src/core/network.h
@@ -6,7 +6,8 @@
#define apSsid "yoRadioAP"
#define apPassword "12345987"
//#define TSYNC_DELAY 10800000 // 1000*60*60*3 = 3 hours
-#define TSYNC_DELAY 3600000 // 1000*60*60 = 1 hour
+#define TSYNC_DELAY 3600000 // 1000*60*60 = 1 hour
+#define WEATHER_STRING_L 254
enum n_Status_e { CONNECTED, SOFT_AP, FAILED };
@@ -14,12 +15,18 @@ class Network {
public:
n_Status_e status;
struct tm timeinfo;
+ bool firstRun, forceTimeSync, forceWeather;
+ //uint8_t tsFailCnt, wsFailCnt;
public:
Network() {};
void begin();
void requestTimeSync(bool withTelnetOutput=false, uint8_t clientId=0);
+ void requestWeatherSync();
+ Ticker ctimer;
+ char *weatherBuf;
+ bool trueWeather;
private:
- Ticker ntimer, stimer, rtimer;
+ Ticker rtimer;
void raiseSoftAP();
};
diff --git a/yoRadio/options.h b/yoRadio/src/core/options.h
similarity index 71%
rename from yoRadio/options.h
rename to yoRadio/src/core/options.h
index 3771e3c..8103f73 100644
--- a/yoRadio/options.h
+++ b/yoRadio/src/core/options.h
@@ -1,7 +1,7 @@
#ifndef options_h
#define options_h
-#define VERSION "0.7.540"
+#define VERSION "0.8.00b"
/*******************************************************
DO NOT EDIT THIS FILE.
@@ -9,12 +9,13 @@ ALL YOUR SETTINGS WILL BE OVERWRITTEN DURING THE UPDATE.
STORE YOUR SETTINGS IN THE *** myoptions.h *** FILE.
********************************************************/
-#if __has_include("myoptions.h")
-#include "myoptions.h" /* <- write your variable values here */
+#if __has_include("../../myoptions.h")
+ #include "../../myoptions.h" /* <- write your variable values here */
#endif
-#if __has_include("mytheme.h")
-#include "mytheme.h" /* <- Theme file */
+#if __has_include("../../mytheme.h")
+ #include "../../mytheme.h" /* <- Theme file */
#endif
+
/*******************************************************
The connection tables are located here https://github.com/e2002/yoradio#connection-tables
@@ -22,24 +23,24 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
********************************************************/
#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
-#define DSP_SSD1306x32 7 // https://aliexpress.com/item/32798439084.html
-#define DSP_SSD1327 8 // https://aliexpress.com/item/1005001414175498.html
-#define DSP_ILI9341 9 // 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_GC9106 13 // 0.96' 160x80 (looks like ST7735S, but it's not him) https://aliexpress.com/item/32947890530.html
-#define DSP_2004I2C 14 // https://aliexpress.com/item/32783128355.html
-#define DSP_2004 15 // https://aliexpress.com/item/32783128355.html
-#define DSP_SSD1305I2C 16 // SSD1305 and SSD1309 128x64 I2C https://aliexpress.com/item/32950307344.html
-#define DSP_ILI9225 17 // 2.0' 220x176 SPI https://aliexpress.com/item/32952021835.html
-#define DSP_ST7789_240 18 // 1.3' 240x240 SPI https://aliexpress.com/item/32996979276.html
+#define DSP_ST7735 1 // 160x128 1.8' or 128x128 1.44' or 160x80 0.96' https://aliexpress.com/item/1005002822797745.html
+#define DSP_SSD1306 2 // 128x64 0.96' https://aliexpress.com/item/1005001621806398.html
+#define DSP_NOKIA5110 3 // 84x48 1.6' https://aliexpress.com/item/1005001621837569.html
+#define DSP_ST7789 4 // 320x240 2.4' https://aliexpress.com/item/32960241206.html
+#define DSP_SH1106 5 // 128x64 1.3' https://aliexpress.com/item/32683094040.html
+#define DSP_1602I2C 6 // 16x2 https://aliexpress.com/item/32305776560.html
+#define DSP_SSD1306x32 7 // 128x32 0.91' https://aliexpress.com/item/32798439084.html
+#define DSP_SSD1327 8 // 128x128 1.5' https://aliexpress.com/item/1005001414175498.html
+#define DSP_ILI9341 9 // 320x240 3.2' https://aliexpress.com/item/33048191074.html
+#define DSP_SSD1305 10 // 128x64 2.4' SSD1305 and SSD1309 SPI https://aliexpress.com/item/32950307344.html
+#define DSP_SH1107 11 // 128x64 1.3' https://aliexpress.com/item/4000551696674.html
+#define DSP_1602 12 // 16x2 https://aliexpress.com/item/32685016568.html
+#define DSP_GC9106 13 // 160x80 0.96' (looks like ST7735S, but it's not him) https://aliexpress.com/item/32947890530.html
+#define DSP_2004I2C 14 // 20x4 https://aliexpress.com/item/32783128355.html
+#define DSP_2004 15 // 20x4 https://aliexpress.com/item/32783128355.html
+#define DSP_SSD1305I2C 16 // 128x64 2.4' SSD1305 and SSD1309 I2C https://aliexpress.com/item/32950307344.html
+#define DSP_ILI9225 17 // 220x176 2.0' https://aliexpress.com/item/32952021835.html
+#define DSP_ST7789_240 18 // 240x240 1.3' https://aliexpress.com/item/32996979276.html
/* !!! DSP_ST7789_240 requires further development when used in conjunction with the VS1053 module !!! See the link https://www.instructables.com/Adding-CS-Pin-to-13-LCD/ */
#define DSP_CUSTOM 101 // your display
@@ -219,12 +220,12 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#endif
/*
*** ST7735 display submodel ***
-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_MINI160x80 // 0.96' 160x80 ST7735S https://????
-INITR_GREENTAB
-INITR_REDTAB
+ 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_MINI160x80 // 0.96' 160x80 ST7735S https://????
+ INITR_GREENTAB
+ INITR_REDTAB
*/
#ifndef DTYPE
#define DTYPE INITR_BLACKTAB
@@ -244,7 +245,13 @@ INITR_REDTAB
#define COLOR_BACKGROUND 0, 0, 0
#endif
#ifndef COLOR_STATION_NAME
- #define COLOR_STATION_NAME 231, 211, 90
+ #define COLOR_STATION_NAME 0, 0, 0
+#endif
+#ifndef COLOR_STATION_BG
+ #define COLOR_STATION_BG 231, 211, 90
+#endif
+#ifndef COLOR_STATION_FILL
+ #define COLOR_STATION_FILL 231, 211, 90
#endif
#ifndef COLOR_SNG_TITLE_1
#define COLOR_SNG_TITLE_1 255, 255, 255
@@ -315,6 +322,14 @@ INITR_REDTAB
#ifndef COLOR_PLAYLIST_4
#define COLOR_PLAYLIST_4 25, 25, 25
#endif
+#ifndef COLOR_BITRATE
+ #define COLOR_BITRATE 231, 211, 90
+#endif
+#define EN 1
+#define RU 2
+#ifndef L10N_LANGUAGE
+ #define L10N_LANGUAGE EN
+#endif
#endif
diff --git a/yoRadio/player.cpp b/yoRadio/src/core/player.cpp
similarity index 74%
rename from yoRadio/player.cpp
rename to yoRadio/src/core/player.cpp
index 3ccece8..c812178 100644
--- a/yoRadio/player.cpp
+++ b/yoRadio/src/core/player.cpp
@@ -11,36 +11,36 @@
Player player;
#if VS1053_CS!=255 && !I2S_INTERNAL
-Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ) {
+ Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ) {
-}
-void ResetChip(){
- pinMode(VS1053_RST, OUTPUT);
- digitalWrite(VS1053_RST, LOW);
- delay(30);
- digitalWrite(VS1053_RST, HIGH);
- delay(100);
-}
+ }
+ void ResetChip(){
+ pinMode(VS1053_RST, OUTPUT);
+ digitalWrite(VS1053_RST, LOW);
+ delay(30);
+ digitalWrite(VS1053_RST, HIGH);
+ delay(100);
+ }
#else
-#if !I2S_INTERNAL
-Player::Player() {}
-#else
-Player::Player(): Audio(true, I2S_DAC_CHANNEL_BOTH_EN) {}
-#endif
+ #if !I2S_INTERNAL
+ Player::Player() {}
+ #else
+ Player::Player(): Audio(true, I2S_DAC_CHANNEL_BOTH_EN) {}
+ #endif
#endif
void Player::init() {
if(MUTE_PIN!=255) pinMode(MUTE_PIN, OUTPUT);
-#if I2S_DOUT!=255
-#if !I2S_INTERNAL
- setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
-#endif
-#else
- SPI.begin();
- if(VS1053_RST>0) ResetChip();
- begin();
-#endif
+ #if I2S_DOUT!=255
+ #if !I2S_INTERNAL
+ setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
+ #endif
+ #else
+ SPI.begin();
+ if(VS1053_RST>0) ResetChip();
+ begin();
+ #endif
setBalance(config.store.balance);
setTone(config.store.bass, config.store.middle, config.store.trebble);
setVolume(0);
@@ -59,23 +59,31 @@ void Player::stopInfo() {
requestToStart = true;
}
+void Player::stop(const char *nttl){
+ mode = STOPPED;
+ setOutputPins(false);
+ if(nttl) config.setTitle(nttl);
+ else config.setTitle((display.mode()==LOST || display.mode()==UPDATING)?"":const_PlStopped);
+ netserver.requestOnChange(TITLE, 0);
+ config.station.bitrate = 0;
+ #ifdef USE_NEXTION
+ nextion.bitrate(config.station.bitrate);
+ #endif
+ netserver.requestOnChange(BITRATE, 0);
+ display.putRequest(DBITRATE);
+ display.putRequest(PSTOP);
+ setDefaults();
+ stopInfo();
+ if (player_on_stop_play) player_on_stop_play();
+}
+
void Player::loop() {
if (mode == PLAYING) {
xSemaphoreTake(playmutex, portMAX_DELAY);
Audio::loop();
xSemaphoreGive(playmutex);
- //vTaskDelay(2);
} else {
- if (isRunning()) {
- //digitalWrite(LED_BUILTIN, LOW);
- setOutputPins(false);
- config.setTitle((display.mode==LOST || display.mode==UPDATING)?"":"[stopped]");
- netserver.requestOnChange(TITLE, 0);
- //stopSong();
- setDefaults();
- stopInfo();
- if (player_on_stop_play) player_on_stop_play();
- }
+ if (isRunning()) stop();
}
if (request.station > 0) {
if (request.doSave) {
@@ -90,7 +98,7 @@ void Player::loop() {
telnet.printf("##CLI.VOL#: %d\n", config.store.volume);
Audio::setVolume(volToI2S(request.volume));
zeroRequest();
- display.putRequest({DRAWVOL, 0});
+ display.putRequest(DRAWVOL);
netserver.requestOnChange(VOLUME, 0);
}
if(volTimer){
@@ -113,16 +121,15 @@ void Player::setOutputPins(bool isPlaying) {
}
void Player::play(uint16_t stationId) {
- //stopSong();
+ display.putRequest(PSTOP);
setDefaults();
setOutputPins(false);
- config.setTitle("[connecting]");
+ config.setTitle(const_PlConnect);
config.station.bitrate=0;
netserver.requestOnChange(TITLE, 0);
- //telnet.printf("##CLI.META#: %s\n", config.station.title);
config.loadStation(stationId);
setVol(config.store.volume, true);
- display.putRequest({NEWSTATION, 0});
+ display.putRequest(NEWSTATION);
netserver.requestOnChange(STATION, 0);
telnet.printf("##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
if (connecttohost(config.station.url)) {
@@ -131,6 +138,7 @@ void Player::play(uint16_t stationId) {
netserver.requestOnChange(MODE, 0);
setOutputPins(true);
requestToStart = true;
+ display.putRequest(PSTART);
if (player_on_start_play) player_on_start_play();
}else{
Serial.println("some unknown bug...");
@@ -152,7 +160,6 @@ void Player::next() {
void Player::toggle() {
if (mode == PLAYING) {
mode = STOPPED;
- //display.title("[stopped]");
} else {
request.station = config.store.lastStation;
}
diff --git a/yoRadio/player.h b/yoRadio/src/core/player.h
similarity index 85%
rename from yoRadio/player.h
rename to yoRadio/src/core/player.h
index 84bac1c..ada85c6 100644
--- a/yoRadio/player.h
+++ b/yoRadio/src/core/player.h
@@ -3,9 +3,9 @@
#include "options.h"
#if I2S_DOUT!=255 || I2S_INTERNAL
-#include "src/audioI2S/AudioEx.h"
+ #include "../audioI2S/AudioEx.h"
#else
-#include "src/audioVS1053/audioVS1053Ex.h"
+ #include "../audioVS1053/audioVS1053Ex.h"
#endif
enum audioMode_e { PLAYING, STOPPED };
@@ -30,6 +30,7 @@ class Player: public Audio {
void init();
void loop();
void play(uint16_t stationId);
+ void stop(const char *nttl = NULL);
void prev();
void next();
void toggle();
@@ -45,5 +46,6 @@ extern Player player;
extern __attribute__((weak)) void player_on_start_play();
extern __attribute__((weak)) void player_on_stop_play();
extern __attribute__((weak)) void player_on_track_change();
+extern __attribute__((weak)) void player_on_station_change();
#endif
diff --git a/yoRadio/telnet.cpp b/yoRadio/src/core/telnet.cpp
similarity index 100%
rename from yoRadio/telnet.cpp
rename to yoRadio/src/core/telnet.cpp
diff --git a/yoRadio/telnet.h b/yoRadio/src/core/telnet.h
similarity index 100%
rename from yoRadio/telnet.h
rename to yoRadio/src/core/telnet.h
diff --git a/yoRadio/src/displays/conf/displayGC9106conf.h b/yoRadio/src/displays/conf/displayGC9106conf.h
new file mode 100644
index 0000000..2106cbb
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayGC9106conf.h
@@ -0,0 +1,71 @@
+/*************************************************************************************
+ GC9106 160x80 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayGC9106conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayGC9106conf_h
+#define displayGC9106conf_h
+
+#define DSP_WIDTH 160
+#define TFT_FRAMEWDT 1
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 19
+
+#define HIDE_IP
+#define HIDE_TITLE2
+#define HIDE_VOL
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 19, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-4, 5000, 3, 30 };
+//const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 36, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 2, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 33, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 80-TFT_FRAMEWDT-8, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 80-13, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-4, 0, 3, 30 }; // ПОГОДА!!
+
+/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 16, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 80-1-1-2, 0, WA_LEFT }, MAX_WIDTH, 2, false };
+const FillConfig playlBGConf PROGMEM = {{ 0, 30, 0, WA_LEFT }, DSP_WIDTH, 20, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 79, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 65, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 19, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+//const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 80-13, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 29+32, 35, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 20, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 32, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 46, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 58, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 29+34, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 50, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+//const char iptxtFmt[] PROGMEM = "%s";
+//const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 14, 29+34, 0};
+const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 80-13, MAX_WIDTH-6*3-4-30 };
+const MoveConfig weatherMoveVU PROGMEM = { 30, 80-13, MAX_WIDTH-6*3-4-30 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayILI9225conf.h b/yoRadio/src/displays/conf/displayILI9225conf.h
new file mode 100644
index 0000000..f899cd5
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayILI9225conf.h
@@ -0,0 +1,67 @@
+/*************************************************************************************
+ ILI9225 220x176 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayILI9225conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayILI9225conf_h
+#define displayILI9225conf_h
+
+#define DSP_WIDTH 220
+#define DSP_HEIGHT 176
+#define TFT_FRAMEWDT 4
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 9
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 22
+
+#define bootLogoTop 28
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, DSP_WIDTH+10, 5000, 4, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 28, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 3, 25 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 40, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 3, 25 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 80, 2, WA_LEFT }, 140, true, DSP_WIDTH+10, 1000, 4, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, DSP_HEIGHT-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, DSP_WIDTH+10, 0, 3, 25 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 50, 2, WA_LEFT }, 140, true, DSP_WIDTH+10, 0, 4, 30 };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, DSP_HEIGHT-TFT_FRAMEWDT-4, 0, WA_LEFT }, MAX_WIDTH, 4, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 76, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, DSP_HEIGHT-1, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 150, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT+6, DSP_HEIGHT-TFT_FRAMEWDT-14-14, 1, WA_RIGHT };
+const WidgetConfig voltxtConf PROGMEM = { 80, DSP_HEIGHT-TFT_FRAMEWDT-14, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, DSP_HEIGHT-TFT_FRAMEWDT-14, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, DSP_HEIGHT-TFT_FRAMEWDT-14, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 110, 35, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { TFT_FRAMEWDT, 38, 2, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { TFT_FRAMEWDT, 62, 2, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { TFT_FRAMEWDT, 102, 2, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { TFT_FRAMEWDT, 126, 2, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 12, 120, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 58, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 130, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 19, 90, 2, 2, 10, 4 };
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "WiFi %d";
+const char iptxtFmt[] PROGMEM = "IP %s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d kBs";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 2, 120, 0 };
+const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 58, DSP_WIDTH+10 };
+const MoveConfig weatherMoveVU PROGMEM = { TFT_FRAMEWDT+46, 58, DSP_WIDTH+10-46 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayILI9341conf.h b/yoRadio/src/displays/conf/displayILI9341conf.h
new file mode 100644
index 0000000..80ec5ea
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayILI9341conf.h
@@ -0,0 +1,67 @@
+/*************************************************************************************
+ ILI9341 320x240 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayILI9341conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayILI9341conf_h
+#define displayILI9341conf_h
+
+#define DSP_WIDTH 320
+#define TFT_FRAMEWDT 8
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 11
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 22
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 5, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 50, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 70, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 112, 2, WA_LEFT }, 140, true, MAX_WIDTH, 1000, 4, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 20 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ 8, 87, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 4, 30 };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 38, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-6, 0, WA_LEFT }, MAX_WIDTH, 6, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 107, 0, WA_LEFT }, DSP_WIDTH, 24, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 239, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 182, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 70, 191, 1, WA_LEFT };
+const WidgetConfig voltxtConf PROGMEM = { 0, 214, 1, WA_CENTER };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 120+30, 52, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { TFT_FRAMEWDT, 66, 2, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { TFT_FRAMEWDT, 90, 2, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { TFT_FRAMEWDT, 130, 2, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { TFT_FRAMEWDT, 154, 2, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 12, 176, 52, WA_RIGHT }; /* 52 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 100, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 162, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 24, 100, 4, 2, 10, 5 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "WiFi %d";
+const char iptxtFmt[] PROGMEM = "IP %s";
+const char voltxtFmt[] PROGMEM = "vol %d";
+const char bitrateFmt[] PROGMEM = "%d kBs";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 176, -1 };
+const MoveConfig weatherMove PROGMEM = { 8, 97, MAX_WIDTH };
+const MoveConfig weatherMoveVU PROGMEM = { 70, 97, 250 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayLCD1602conf.h b/yoRadio/src/displays/conf/displayLCD1602conf.h
new file mode 100644
index 0000000..de4e8fc
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayLCD1602conf.h
@@ -0,0 +1,52 @@
+/*************************************************************************************
+ LCD1602 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayLCD1602conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayLCD1602conf_h
+#define displayLCD1602conf_h
+
+#define DSP_WIDTH 16
+#define TFT_FRAMEWDT 0
+#define MAX_WIDTH 16
+#define PLMITEMS 2
+#define PLMITEMLENGHT 40
+
+#define HIDE_IP
+#define HIDE_TITLE2
+#define HIDE_VOL
+#define HIDE_VOLBAR
+#define HIDE_HEAPBAR
+#define HIDE_RSSI
+#define HIDE_VU
+#define HIDE_WEATHER
+#define META_MOVE
+
+//#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+#define SDELTA 2
+#define STIME 400
+const ScrollConfig metaConf PROGMEM = {{ 0, 0, 1, WA_LEFT }, 140, true, MAX_WIDTH-6, 2000, SDELTA, STIME };
+const ScrollConfig title1Conf PROGMEM = {{ 0, 1, 1, WA_LEFT }, 140, true, MAX_WIDTH-4, 2000, SDELTA, STIME };
+const ScrollConfig playlistConf PROGMEM = {{ 1, 1, 1, WA_LEFT }, 140, true, MAX_WIDTH-1, 2000, SDELTA, STIME };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 0, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 0, 1, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 1, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 0, 1, WA_RIGHT };
+const WidgetConfig bootWdtConf PROGMEM = { 0, 1, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 250, 10, 4 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+//const char bootstrFmt[] PROGMEM = "Trying to %s";
+
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig metaMove PROGMEM = { 0, 0, MAX_WIDTH };
+#endif
diff --git a/yoRadio/src/displays/conf/displayLCD2004conf.h b/yoRadio/src/displays/conf/displayLCD2004conf.h
new file mode 100644
index 0000000..e08470a
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayLCD2004conf.h
@@ -0,0 +1,52 @@
+/*************************************************************************************
+ LCD2004 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayLCD2004conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayLCD1602conf_h
+#define displayLCD1602conf_h
+
+#define DSP_WIDTH 20
+#define TFT_FRAMEWDT 0
+#define MAX_WIDTH 20
+#define PLMITEMS 4
+#define PLMITEMLENGHT 40
+
+#define HIDE_IP
+#define HIDE_VOLBAR
+#define HIDE_HEAPBAR
+#define HIDE_RSSI
+#define HIDE_VU
+#define META_MOVE
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+#define SDELTA 2
+#define STIME 300
+const ScrollConfig metaConf PROGMEM = {{ 0, 0, 1, WA_LEFT }, 140, true, MAX_WIDTH-6, 2000, SDELTA, STIME };
+const ScrollConfig title1Conf PROGMEM = {{ 0, 1, 1, WA_LEFT }, 140, true, MAX_WIDTH-4, 2000, SDELTA, STIME };
+const ScrollConfig title2Conf PROGMEM = {{ 0, 2, 1, WA_LEFT }, 140, true, MAX_WIDTH, 2000, SDELTA, STIME };
+const ScrollConfig playlistConf PROGMEM = {{ 1, 1, 1, WA_LEFT }, 140, true, MAX_WIDTH-1, 2000, SDELTA, STIME };
+const ScrollConfig weatherConf PROGMEM = {{ 0, 3, 1, WA_LEFT }, 140, false, MAX_WIDTH-4, 2000, SDELTA, STIME };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig voltxtConf PROGMEM = { 0, 3, 1, WA_RIGHT };
+const char voltxtFmt[] PROGMEM = "%d";
+
+const WidgetConfig bootstrConf PROGMEM = { 0, 1, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 0, 1, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 2, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 0, 1, WA_RIGHT };
+const WidgetConfig bootWdtConf PROGMEM = { 0, 2, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 250, 10, 4 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+//#define WEATHER_FMT_SHORT
+//const char weatherFmt[] PROGMEM = "%.1fC %dmm %s%%";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig metaMove PROGMEM = { 0, 0, MAX_WIDTH };
+#endif
diff --git a/yoRadio/src/displays/conf/displayN5110conf.h b/yoRadio/src/displays/conf/displayN5110conf.h
new file mode 100644
index 0000000..4175d90
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayN5110conf.h
@@ -0,0 +1,68 @@
+/*************************************************************************************
+ Nokia 5110 84x48 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayN5110conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayN5110conf_h
+#define displayN5110conf_h
+
+#define DSP_WIDTH 84
+#define TFT_FRAMEWDT 0
+#define MAX_WIDTH DSP_WIDTH
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 10
+#define SCROLLDELAY 180
+
+#define HIDE_TITLE2
+#define HIDE_HEAPBAR
+#define HIDE_IP
+//#define HIDE_VOLBAR
+#define HIDE_RSSI
+#define HIDE_VU
+
+#define bootLogoTop 0
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 5, SCROLLDELAY };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 8, 1, WA_LEFT }, 140, true, MAX_WIDTH-24, 5000, 5, SCROLLDELAY };
+const ScrollConfig playlistConf PROGMEM = {{ 2, 22, 1, WA_LEFT }, 140, true, MAX_WIDTH-4, 1000, 5, SCROLLDELAY };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 1, WA_CENTER }, 140, false, MAX_WIDTH, 0, 5, SCROLLDELAY };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 48-7, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 5, SCROLLDELAY };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 48-11, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-2, 1000, 5, SCROLLDELAY };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig playlBGConf PROGMEM = {{ 0, 20, 0, WA_LEFT }, DSP_WIDTH, 11, false };
+//const FillConfig metaBGConf PROGMEM = {{ MAX_WIDTH-22, 9, 0, WA_LEFT }, 1, 5, false };
+const FillConfig volbarConf PROGMEM = {{ 0, 45, 0, WA_LEFT }, MAX_WIDTH, 3, true };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 48-7, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 8, 1, WA_RIGHT };
+const WidgetConfig voltxtConf PROGMEM = { 0, 48-11, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 34, 19, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 8, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 16, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 24, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 32, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 4, 35, 19, WA_RIGHT }; /* 19 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 50, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 48-7-10, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 10, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width (0 - auto, -1 - lock } */
+const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displaySH1106conf.h b/yoRadio/src/displays/conf/displaySH1106conf.h
new file mode 100644
index 0000000..472ece5
--- /dev/null
+++ b/yoRadio/src/displays/conf/displaySH1106conf.h
@@ -0,0 +1,71 @@
+/*************************************************************************************
+ SH1106 128x64 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displaySH1106conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displaySH1106conf_h
+#define displaySH1106conf_h
+
+#define DSP_WIDTH 128
+#define TFT_FRAMEWDT 1
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 10
+
+#define HIDE_HEAPBAR
+#define HIDE_VOL
+#define HIDE_VU
+
+#define bootLogoTop 8
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 3, 25 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 19, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*4, 5000, 2, 25 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 28, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 2, 25 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 30, 1, WA_LEFT }, 140, true, MAX_WIDTH, 500, 2, 25 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, 0, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 2, 25 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 64-7, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 25 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 64-9, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*4, 0, 2, 25 }; // ПОГОДА!!
+
+/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 17, false };
+const FillConfig volbarConf PROGMEM = {{ 0, 64-1, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+const FillConfig playlBGConf PROGMEM = {{ 0, 26, 0, WA_LEFT }, DSP_WIDTH, 12, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 63, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 64-8, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 0, 19, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 64-9, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { 0, 64-9, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 28, 2, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 18, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 26, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 37, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 45, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 6, 38, 2, WA_CENTER };
+const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 64-8*2-5, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+//const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displaySSD1305conf.h b/yoRadio/src/displays/conf/displaySSD1305conf.h
new file mode 100644
index 0000000..93b1c2e
--- /dev/null
+++ b/yoRadio/src/displays/conf/displaySSD1305conf.h
@@ -0,0 +1,71 @@
+/*************************************************************************************
+ SSD1305 128x64 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displaySSD1305conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displaySSD1305conf_h
+#define displaySSD1305conf_h
+
+#define DSP_WIDTH 128
+#define TFT_FRAMEWDT 1
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 10
+
+#define HIDE_HEAPBAR
+#define HIDE_VOL
+#define HIDE_VU
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT+1, TFT_FRAMEWDT+1, 1, WA_LEFT }, 140, true, MAX_WIDTH-2, 5000, 2, 25 };
+const ScrollConfig title1Conf PROGMEM = {{ 0, 13, 1, WA_LEFT }, 140, true, DSP_WIDTH-6*4, 5000, 2, 25 };
+const ScrollConfig title2Conf PROGMEM = {{ 0, 22, 1, WA_LEFT }, 140, true, DSP_WIDTH, 5000, 2, 25 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 30, 1, WA_LEFT }, 140, true, MAX_WIDTH, 500, 2, 25 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT+1, TFT_FRAMEWDT+1, 1, WA_CENTER }, 140, false, MAX_WIDTH-2, 0, 2, 25 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 64-7, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 25 };
+const ScrollConfig weatherConf PROGMEM = {{ 0, 64-11, 1, WA_LEFT }, 140, true, DSP_WIDTH-6*4, 0, 2, 25 }; // ПОГОДА!!
+
+/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 11, false };
+const FillConfig volbarConf PROGMEM = {{ 0, 64-1-1-1, 0, WA_LEFT }, DSP_WIDTH, 3, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 26, 0, WA_LEFT }, DSP_WIDTH, 12, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 63, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 64-8, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 0, 13, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { 0, 64-11, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { 0, 64-11, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 26, 2, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 18, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 26, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 37, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 45, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 6, 34, 2, WA_CENTER };
+const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 64-8*2-5, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 10, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+//const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displaySSD1306conf.h b/yoRadio/src/displays/conf/displaySSD1306conf.h
new file mode 100644
index 0000000..fce90e3
--- /dev/null
+++ b/yoRadio/src/displays/conf/displaySSD1306conf.h
@@ -0,0 +1,71 @@
+/*************************************************************************************
+ SSD1306 128x64 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displaySSD1306conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displaySSD1306conf_h
+#define displaySSD1306conf_h
+
+#define DSP_WIDTH 128
+#define TFT_FRAMEWDT 1
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 5
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 18
+
+#define HIDE_HEAPBAR
+#define HIDE_VOL
+#define HIDE_VU
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, 0, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 3, 15 };
+const ScrollConfig title1Conf PROGMEM = {{ 0, 17, 1, WA_LEFT }, 140, true, DSP_WIDTH-6*4, 5000, 2, 35 };
+const ScrollConfig title2Conf PROGMEM = {{ 0, 26, 1, WA_LEFT }, 140, true, DSP_WIDTH, 5000, 2, 35 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 26, 2, WA_LEFT }, 140, true, MAX_WIDTH, 500, 2, 25 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, 0, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 2, 35 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 64-7, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 15 };
+const ScrollConfig weatherConf PROGMEM = {{ 0, 64-11, 1, WA_LEFT }, 140, true, DSP_WIDTH-6*4, 0, 2, 25 }; // ПОГОДА!!
+
+/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 16, false };
+const FillConfig volbarConf PROGMEM = {{ 0, 64-1-1-1, 0, WA_LEFT }, DSP_WIDTH, 3, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 23, 0, WA_LEFT }, DSP_WIDTH, 19, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 63, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 64-8, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 0, 17, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { 0, 64-11, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { 0, 64-11, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 28, 2, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 18, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 26, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 37, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 45, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 6, 36, 2, WA_CENTER };
+const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 64-8*2-5, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+//const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displaySSD1306x32conf.h b/yoRadio/src/displays/conf/displaySSD1306x32conf.h
new file mode 100644
index 0000000..c94af31
--- /dev/null
+++ b/yoRadio/src/displays/conf/displaySSD1306x32conf.h
@@ -0,0 +1,72 @@
+/*************************************************************************************
+ SSD1306 128x32 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displaySSD1306conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displaySSD1306conf_h
+#define displaySSD1306conf_h
+
+#define DSP_WIDTH 128
+#define TFT_FRAMEWDT 1
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 5
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 10
+
+#define HIDE_IP
+#define HIDE_TITLE2
+#define HIDE_HEAPBAR
+#define HIDE_VU
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*5-2, 5000, 2, 25 };
+const ScrollConfig title1Conf PROGMEM = {{ 0, 11, 1, WA_LEFT }, 140, true, DSP_WIDTH-6*4, 5000, 2, 25 };
+//const ScrollConfig title2Conf PROGMEM = {{ 0, 26, 1, WA_LEFT }, 140, true, DSP_WIDTH, 5000, 2, 35 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 14, 1, WA_LEFT }, 140, true, MAX_WIDTH, 500, 2, 25 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, 1, 1, WA_CENTER }, 140, false, MAX_WIDTH, 0, 2, 25 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 32-7, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 25 };
+const ScrollConfig weatherConf PROGMEM = {{ 0, 20, 1, WA_LEFT }, 140, true, DSP_WIDTH-6*4, 0, 2, 25 }; // ПОГОДА!!
+
+/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH/*-6*5-3*/, 9, false };
+const FillConfig volbarConf PROGMEM = {{ 0, 32-1-1-1, 0, WA_LEFT }, DSP_WIDTH, 3, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 13, 0, WA_LEFT }, DSP_WIDTH, 9, false };
+//const FillConfig heapbarConf PROGMEM = {{ 0, 63, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 32-8, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 0, 11, 1, WA_RIGHT };
+const WidgetConfig voltxtConf PROGMEM = { 0, 20, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { 0, 64-11, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { 0, 64-11, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 12, 2, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 9, 1, WA_LEFT };
+const WidgetConfig apName2Conf PROGMEM = { 0, 9, 1, WA_RIGHT };
+const WidgetConfig apPassConf PROGMEM = { 0, 17, 1, WA_LEFT };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 17, 1, WA_RIGHT };
+const WidgetConfig clockConf PROGMEM = { 0, 1, 1, WA_RIGHT };
+//const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 32-8*2-5, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 10, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displaySSD1327conf.h b/yoRadio/src/displays/conf/displaySSD1327conf.h
new file mode 100644
index 0000000..7e7b696
--- /dev/null
+++ b/yoRadio/src/displays/conf/displaySSD1327conf.h
@@ -0,0 +1,71 @@
+/*************************************************************************************
+ SSD1327 128x128 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displaySSD1327conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displaySSD1327conf_h
+#define displaySSD1327conf_h
+
+#define DSP_WIDTH 128
+#define TFT_FRAMEWDT 4
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 21
+
+#define HIDE_HEAPBAR
+#define HIDE_VU
+
+#define bootLogoTop 34
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 26, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 2, 30 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 36, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-4, 5000, 2, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 56, 2, WA_LEFT }, 140, true, MAX_WIDTH, 1000, 4, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 128-TFT_FRAMEWDT-8, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 42, 1, WA_LEFT }, 140, true, MAX_WIDTH, 0, 3, 30 };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 118, 0, WA_LEFT }, MAX_WIDTH-6*3-4, 5, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 52, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 127, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 110, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 36, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+const WidgetConfig voltxtConf PROGMEM = { TFT_FRAMEWDT, 128-10, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 86, 35, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 40, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 54, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 74, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 88, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 94, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 99, 1, WA_CENTER };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 90, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 56, 7, 2, 1, 8, 2 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width (0 - auto, -1 - lock } */
+const MoveConfig clockMove PROGMEM = { 0, 94, -1 };
+const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 48, 122 };
+const MoveConfig weatherMoveVU PROGMEM = { TFT_FRAMEWDT, 48, 122 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayST7735_144conf.h b/yoRadio/src/displays/conf/displayST7735_144conf.h
new file mode 100644
index 0000000..96644c3
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayST7735_144conf.h
@@ -0,0 +1,68 @@
+/*************************************************************************************
+ ST7735 128x128 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayST7735conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayST7789conf_h
+#define displayST7789conf_h
+
+#define DSP_WIDTH 128
+#define TFT_FRAMEWDT 4
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 21
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 26, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 2, 30 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 36, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-4, 5000, 2, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 56, 2, WA_LEFT }, 140, true, MAX_WIDTH, 1000, 4, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 128-TFT_FRAMEWDT-8, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 2, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 42, 1, WA_LEFT }, 140, true, MAX_WIDTH, 0, 3, 30 };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 118, 0, WA_LEFT }, MAX_WIDTH-6*3-4, 5, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 52, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 127, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 110, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 36, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+const WidgetConfig voltxtConf PROGMEM = { TFT_FRAMEWDT, 128-10, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 86, 35, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 40, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 54, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 74, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 88, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 94, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 99, 1, WA_CENTER };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 90, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 56, 7, 2, 1, 8, 2 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+
+/* MOVES */ /* { left, top, width (0 - auto, -1 - lock } */
+const MoveConfig clockMove PROGMEM = { 0, 94, -1 };
+const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 48, 122 };
+const MoveConfig weatherMoveVU PROGMEM = { TFT_FRAMEWDT, 48, 122 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayST7735_blackconf.h b/yoRadio/src/displays/conf/displayST7735_blackconf.h
new file mode 100644
index 0000000..4a70b08
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayST7735_blackconf.h
@@ -0,0 +1,67 @@
+/*************************************************************************************
+ ST7735 160x128 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayST7735conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+#ifndef displayST7735conf_h
+#define displayST7735conf_h
+
+#define DSP_WIDTH 160
+#define TFT_FRAMEWDT 4
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 21
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 26, 1, WA_LEFT }, 140, true, MAX_WIDTH-24, 5000, 3, 30 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 36, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 3, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 56, 2, WA_LEFT }, 140, true, MAX_WIDTH, 1000, 4, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 128-TFT_FRAMEWDT-8, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 42, 1, WA_LEFT }, 140, true, MAX_WIDTH, 0, 3, 30 };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 118, 0, WA_LEFT }, MAX_WIDTH, 5, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 52, 0, WA_LEFT }, DSP_WIDTH, 22, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 127, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 110, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 26, 1, WA_RIGHT };
+//const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 99, 1, WA_LEFT };
+const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 86, 35, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 40, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 54, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 74, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 88, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 94, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 54, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 90, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 50, 2, 1, 10, 3 };
+
+const BitrateConfig fullbitrateConf PROGMEM = {{DSP_WIDTH-TFT_FRAMEWDT-19, 23, 1, WA_LEFT}, 22 };
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+const char iptxtFmt[] PROGMEM = "%s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+/* MOVES */ /* { left, top, width (0 - auto, -1 - lock } */
+const MoveConfig clockMove PROGMEM = { 16, 94, 0};
+const MoveConfig weatherMove PROGMEM = {TFT_FRAMEWDT, 48, MAX_WIDTH};
+const MoveConfig weatherMoveVU PROGMEM = { 34, 48, 122 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayST7735_miniconf.h b/yoRadio/src/displays/conf/displayST7735_miniconf.h
new file mode 100644
index 0000000..be7a363
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayST7735_miniconf.h
@@ -0,0 +1,71 @@
+/*************************************************************************************
+ ST7735 160x80 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayST7735conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayST7789conf_h
+#define displayST7789conf_h
+
+#define DSP_WIDTH 160
+#define TFT_FRAMEWDT 1
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 7
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 19
+
+#define HIDE_IP
+#define HIDE_TITLE2
+#define HIDE_VOL
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 19, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-4, 5000, 3, 30 };
+//const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 36, 1, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 2, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 33, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 2, WA_CENTER }, 140, false, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 80-TFT_FRAMEWDT-8, 1, WA_LEFT }, 140, false, MAX_WIDTH, 0, 3, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 80-13, 1, WA_LEFT }, 140, true, MAX_WIDTH-6*3-4, 0, 3, 30 }; // ПОГОДА!!
+
+/* BACKGROUNGC9106DS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 16, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 80-1-1-2, 0, WA_LEFT }, MAX_WIDTH, 2, false };
+const FillConfig playlBGConf PROGMEM = {{ 0, 30, 0, WA_LEFT }, DSP_WIDTH, 20, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 79, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 65, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 19, 1, WA_RIGHT };
+//const WidgetConfig voltxtConf PROGMEM = { 32, 108, 1, WA_RIGHT };
+//const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 108, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 80-13, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 29+32, 35, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { 0, 20, 1, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { 0, 32, 1, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { 0, 46, 1, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { 0, 58, 1, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 29+34, 35, WA_RIGHT }; /* 35 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { 1, 28, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 50, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 12, 48, 2, 1, 8, 3 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "%d";
+//const char iptxtFmt[] PROGMEM = "%s";
+//const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d";
+ */
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 14, 29+34, 0};
+const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 80-13, MAX_WIDTH-6*3-4-30 };
+const MoveConfig weatherMoveVU PROGMEM = { 30, 80-13, MAX_WIDTH-6*3-4-30 };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayST7789_240conf.h b/yoRadio/src/displays/conf/displayST7789_240conf.h
new file mode 100644
index 0000000..8613081
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayST7789_240conf.h
@@ -0,0 +1,66 @@
+/*************************************************************************************
+ ST7789 240x240 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayST7789conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayST7789conf_h
+#define displayST7789conf_h
+
+#define DSP_WIDTH 240
+#define TFT_FRAMEWDT 8
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 11
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 22
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 5, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 50, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 70, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 112, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 2, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 20 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ TFT_FRAMEWDT, 198, 1, WA_LEFT }, 140, true, MAX_WIDTH, 0, 3, 30 };
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 38, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-6, 0, WA_LEFT }, MAX_WIDTH, 6, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 107, 0, WA_LEFT }, DSP_WIDTH, 24, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 239, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 182, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { TFT_FRAMEWDT, 188, 1, WA_LEFT };
+const WidgetConfig voltxtConf PROGMEM = { 80, 214, 1, WA_RIGHT };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 120+30, 52, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { TFT_FRAMEWDT, 66, 2, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { TFT_FRAMEWDT, 90, 2, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { TFT_FRAMEWDT, 130, 2, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { TFT_FRAMEWDT, 154, 2, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 0, 168, 52, WA_RIGHT }; /* 52 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 94, 1, WA_CENTER };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 162, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 100, 20, 10, 2, 10, 5 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "WiFi %d";
+const char iptxtFmt[] PROGMEM = "IP %s";
+const char voltxtFmt[] PROGMEM = "%d";
+const char bitrateFmt[] PROGMEM = "%d kBs";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 176, 0 };
+const MoveConfig weatherMove PROGMEM = { TFT_FRAMEWDT, 202, MAX_WIDTH };
+const MoveConfig weatherMoveVU PROGMEM = { TFT_FRAMEWDT, 202, MAX_WIDTH };
+
+#endif
diff --git a/yoRadio/src/displays/conf/displayST7789conf.h b/yoRadio/src/displays/conf/displayST7789conf.h
new file mode 100644
index 0000000..657b133
--- /dev/null
+++ b/yoRadio/src/displays/conf/displayST7789conf.h
@@ -0,0 +1,67 @@
+/*************************************************************************************
+ ST7789 320x240 displays configuration file.
+ Copy this file to yoRadio/src/displays/conf/displayST7789conf_custom.h
+ and modify it
+ More info on https://github.com/e2002/yoradio/wiki/Widgets#widgets-description
+*************************************************************************************/
+
+#ifndef displayST7789conf_h
+#define displayST7789conf_h
+
+#define DSP_WIDTH 320
+#define TFT_FRAMEWDT 8
+#define MAX_WIDTH DSP_WIDTH-TFT_FRAMEWDT*2
+#define PLMITEMS 11
+#define PLMITEMLENGHT 40
+#define PLMITEMHEIGHT 22
+
+#define bootLogoTop 68
+
+/* SROLLS */ /* {{ left, top, fontsize, align }, buffsize, uppercase, width, scrolldelay, scrolldelta, scrolltime } */
+const ScrollConfig metaConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 5, 30 };
+const ScrollConfig title1Conf PROGMEM = {{ TFT_FRAMEWDT, 50, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig title2Conf PROGMEM = {{ TFT_FRAMEWDT, 70, 2, WA_LEFT }, 140, true, MAX_WIDTH, 5000, 4, 30 };
+const ScrollConfig playlistConf PROGMEM = {{ TFT_FRAMEWDT, 112, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 2, 30 };
+const ScrollConfig apTitleConf PROGMEM = {{ TFT_FRAMEWDT, TFT_FRAMEWDT, 3, WA_CENTER }, 140, false, MAX_WIDTH, 0, 4, 20 };
+const ScrollConfig apSettConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-16, 2, WA_LEFT }, 140, false, MAX_WIDTH, 0, 4, 30 };
+const ScrollConfig weatherConf PROGMEM = {{ 8, 87, 2, WA_LEFT }, 140, true, MAX_WIDTH, 0, 4, 30 };
+
+/* BACKGROUNDS */ /* {{ left, top, fontsize, align }, width, height, outlined } */
+const FillConfig metaBGConf PROGMEM = {{ 0, 0, 0, WA_LEFT }, DSP_WIDTH, 38, false };
+const FillConfig volbarConf PROGMEM = {{ TFT_FRAMEWDT, 240-TFT_FRAMEWDT-6, 0, WA_LEFT }, MAX_WIDTH, 6, true };
+const FillConfig playlBGConf PROGMEM = {{ 0, 107, 0, WA_LEFT }, DSP_WIDTH, 24, false };
+const FillConfig heapbarConf PROGMEM = {{ 0, 239, 0, WA_LEFT }, DSP_WIDTH, 1, false };
+
+/* WIDGETS */ /* { left, top, fontsize, align } */
+const WidgetConfig bootstrConf PROGMEM = { 0, 182, 1, WA_CENTER };
+const WidgetConfig bitrateConf PROGMEM = { 70, 191, 1, WA_LEFT };
+const WidgetConfig voltxtConf PROGMEM = { 0, 214, 1, WA_CENTER };
+const WidgetConfig iptxtConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_LEFT };
+const WidgetConfig rssiConf PROGMEM = { TFT_FRAMEWDT, 214, 1, WA_RIGHT };
+const WidgetConfig numConf PROGMEM = { 0, 120+30, 52, WA_CENTER };
+const WidgetConfig apNameConf PROGMEM = { TFT_FRAMEWDT, 66, 2, WA_CENTER };
+const WidgetConfig apName2Conf PROGMEM = { TFT_FRAMEWDT, 90, 2, WA_CENTER };
+const WidgetConfig apPassConf PROGMEM = { TFT_FRAMEWDT, 130, 2, WA_CENTER };
+const WidgetConfig apPass2Conf PROGMEM = { TFT_FRAMEWDT, 154, 2, WA_CENTER };
+const WidgetConfig clockConf PROGMEM = { 12, 176, 52, WA_RIGHT }; /* 52 is a fixed font size. do not change */
+const WidgetConfig vuConf PROGMEM = { TFT_FRAMEWDT, 100, 1, WA_LEFT };
+
+const WidgetConfig bootWdtConf PROGMEM = { 0, 162, 1, WA_CENTER };
+const ProgressConfig bootPrgConf PROGMEM = { 90, 14, 4 };
+
+/* BANDS */ /* { onebandwidth, onebandheight, bandsHspace, bandsVspace, numofbands, fadespeed } */
+const VUBandsConfig bandsConf PROGMEM = { 24, 100, 4, 2, 10, 5 };
+
+/* STRINGS */
+const char numtxtFmt[] PROGMEM = "%d";
+const char rssiFmt[] PROGMEM = "WiFi %d";
+const char iptxtFmt[] PROGMEM = "IP %s";
+const char voltxtFmt[] PROGMEM = "vol %d";
+const char bitrateFmt[] PROGMEM = "%d kBs";
+
+/* MOVES */ /* { left, top, width } */
+const MoveConfig clockMove PROGMEM = { 0, 176, -1 };
+const MoveConfig weatherMove PROGMEM = { 8, 97, MAX_WIDTH };
+const MoveConfig weatherMoveVU PROGMEM = { 70, 97, 250 };
+
+#endif
diff --git a/yoRadio/src/displays/displayDummy.h b/yoRadio/src/displays/displayDummy.h
deleted file mode 100644
index 47cdd98..0000000
--- a/yoRadio/src/displays/displayDummy.h
+++ /dev/null
@@ -1,87 +0,0 @@
-#ifndef displayDummy_h
-#define displayDummy_h
-
-#include "Arduino.h"
-
-#define TFT_LINEHGHT 10
-#define TFT_FRAMEWDT 4
-
-#define PLMITEMS 7
-#define PLMITEMLENGHT 40
-#define PLMITEMHEIGHT 22
-
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 3
-#define SCROLLTIME 60
-#endif
-
-#define DSP_FLIPPED 0
-
-class DspCore {
- 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 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(bool force=false);
- private:
- uint16_t swidth, sheight;
-
-};
-
-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/displayGC9106.cpp b/yoRadio/src/displays/displayGC9106.cpp
index fa917e7..368fe53 100644
--- a/yoRadio/src/displays/displayGC9106.cpp
+++ b/yoRadio/src/displays/displayGC9106.cpp
@@ -1,135 +1,33 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_GC9106
#include "displayGC9106.h"
-#include
#include "fonts/bootlogo40.h"
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/network.h"
#ifndef DEF_SPI_FREQ
-#define DEF_SPI_FREQ 24000000 /* set it to 0 for system default */
+ #define DEF_SPI_FREQ 24000000 /* set it to 0 for system default */
#endif
-#define CLOCK_DELTA 16
-
#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY)
#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl)
-DspCore::DspCore(): Adafruit_GC9106Ex(TFT_CS, TFT_DC, TFT_RST) {
+DspCore::DspCore(): Adafruit_GC9106Ex(TFT_CS, TFT_DC, TFT_RST) { }
-}
+#include "tools/utf8RusGFX.h"
-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(config.theme.title1, config.theme.background);
- 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(config.theme.title2, config.theme.background);
- setCursor(TFT_FRAMEWDT, 107);
- print("SETTINGS PAGE ON: ");
- setCursor(TFT_FRAMEWDT, 117);
- print("http://");
- print(WiFi.softAPIP().toString().c_str());
- print("/");
-}
-
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
begin(DEF_SPI_FREQ);
cp437(true);
invert();
-// fillScreen(TFT_BG);
flip();
setTextWrap(false);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
- setClockBounds();
}
-void DspCore::drawLogo() {
- fillScreen(0x0000);
- drawRGBBitmap((swidth - 62) / 2, 5, bootlogo40, 62, 40);
+void DspCore::drawLogo(uint16_t top) {
+ drawRGBBitmap((width() - 62) / 2, 5, bootlogo40, 62, 40);
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -138,8 +36,7 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
for (byte i = 0; i < PLMITEMS; i++) {
if (abs(i - 3) == 3) setTextColor(config.theme.playlist[2], config.theme.background);
if (abs(i - 3) == 2) setTextColor(config.theme.playlist[1], config.theme.background);
@@ -148,181 +45,77 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT - 1, swidth, PLMITEMHEIGHT - 4, config.theme.background);
+ fillRect(0, yStart + i * PLMITEMHEIGHT - 1, width(), PLMITEMHEIGHT - 4, config.theme.background);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
- fillScreen(config.theme.background);
+void DspCore::clearDsp(bool black) {
+ fillScreen(black?0:config.theme.background);
}
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
- fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-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;
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI28pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop-2, swidth, textheight+3, bg);
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = (width()/2 - _timewidth/2)+clockRightSpace;
setTextSize(1);
- setFont(&DS_DIGI28pt7b);
- if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
- getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot);
- setCursor((swidth - wot) / 2 - 4 + clockdelta, clockY+28+6);
- setTextColor(config.theme.background);
- print(oldTimeBuf);
- dot = (swidth - wot) / 2 - 4 + clockdelta;
- /* dots */
- strlcpy(tmpBuf, oldTimeBuf, 3);
- getTextBounds(tmpBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- dot = dot + ncwidth + getPw(ncwidth);
- setCursor(dot, clockY+28+6);
- print(":");
- /* dots */
-
- strlcpy(oldTimeBuf, timeBuf, 20);
- setTextSize(1);
- getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- setTextColor(config.theme.clock);
- setCursor((swidth - ncwidth) / 2 - 4 + clockdelta, clockY+28+6);
- dot = (swidth - ncwidth) / 2 - 4 + clockdelta;
- setTextSize(1);
- print(timeBuf);
- /* dots */
- strftime(timeBuf, sizeof(timeBuf), "%H", &timeinfo);
- getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- dot = dot + ncwidth + getPw(ncwidth);
- /* dots */
- }
- setCursor(dot, clockY+28+6);
- setTextColor(dots?config.theme.background:config.theme.clock);
- print(":");
- setFont();
- yield();
-}
-
-#define VTOP TITLE_TOP1+6
-
-void DspCore::drawVolumeBar(bool withNumber) {
- int16_t vTop = sheight - TFT_FRAMEWDT - 2;
- int16_t vWidth = swidth - TFT_FRAMEWDT * 2;
- uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth);
- fillRect(TFT_FRAMEWDT, vTop, vWidth, 2, config.theme.background);
- fillRect(TFT_FRAMEWDT, vTop, ww, 2, config.theme.volbarout);
- if (withNumber) {
- setTextSize(1);
- setTextColor(config.theme.digit);
- 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, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
- setCursor((swidth - wv) / 2, VTOP + hv);
- print(volstr);
- setFont();
- }
-}
-
-void DspCore::drawNextStationNum(uint16_t num) {
- setTextSize(1);
- setTextColor(config.theme.digit);
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, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
- setCursor((swidth - wv) / 2, VTOP + hv);
- print(numstr);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-void DspCore::frameTitle(const char* str) {
- setTextSize(2);
- centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ }
+ _clockSeconds();
}
-void DspCore::rssi(const char* str) {
- int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
- setTextSize(1);
- rightText(str, vTop, config.theme.rssi, config.theme.background);
-}
-
-void DspCore::ip(const char* str) {
- int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
- setTextSize(1);
- setTextColor(config.theme.ip, config.theme.background);
- setCursor(4, vTop);
- print(str);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -335,45 +128,17 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
-/*
-void DspCore::sendCommand(uint8_t commandByte, uint8_t *dataBytes, uint8_t numDataBytes) {
- TAKE_MUTEX();
- Adafruit_GC9106Ex::sendCommand(commandByte, dataBytes, numDataBytes);
- GIVE_MUTEX();
+void DspCore::loop(bool force) { }
+
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
-void DspCore::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) {
- TAKE_MUTEX();
- Adafruit_GC9106Ex::sendCommand(commandByte, dataBytes, numDataBytes);
- GIVE_MUTEX();
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
}
-void DspCore::sendCommand16(uint16_t commandWord, const uint8_t *dataBytes, uint8_t numDataBytes) {
- TAKE_MUTEX();
- Adafruit_GC9106Ex::sendCommand16(commandWord, dataBytes, numDataBytes);
- GIVE_MUTEX();
-}*/
-
-
-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(bool force) {
-
-}
void DspCore::flip(){
setRotation(config.store.flipscreen?1:3);
}
@@ -385,4 +150,33 @@ void DspCore::invert(){
void DspCore::sleep(void) { sendCommand(GC9106_SLPIN); delay(150); sendCommand(GC9106_DISPOFF); delay(150); }
void DspCore::wake(void) { sendCommand(GC9106_DISPON); delay(150); sendCommand(GC9106_SLPOUT); delay(150); }
+
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_GC9106Ex::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_GC9106Ex::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setFont(&DS_DIGI28pt7b);
+ setTextSize(1);
+}
+
#endif
diff --git a/yoRadio/src/displays/displayGC9106.h b/yoRadio/src/displays/displayGC9106.h
index f02bb2f..4cfd6c7 100644
--- a/yoRadio/src/displays/displayGC9106.h
+++ b/yoRadio/src/displays/displayGC9106.h
@@ -1,118 +1,34 @@
#ifndef displayGC9106_h
#define displayGC9106_h
+#include "../core/options.h"
#include "Arduino.h"
#include
-/* https://github.com/prenticedavid/Adafruit_GC9102_kbv */
-#include "../Adafruit_GC9106Ex/Adafruit_GC9106Ex.h"
-#include "fonts/DS_DIGI28pt7b.h"
+#include "../Adafruit_GC9106Ex/Adafruit_GC9106Ex.h" // https://github.com/prenticedavid/Adafruit_GC9102_kbv
+#include "fonts/DS_DIGI28pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
+#include "tools/l10n.h"
-#define VU_READY 1
-#define DSP_CAN_SLEEP true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 10
-#define TFT_FRAMEWDT 0
+typedef GFXcanvas16 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
-#define PLMITEMS 7
-#define PLMITEMLENGHT 40
-#define PLMITEMHEIGHT 19
-
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 3
-#define SCROLLTIME 30
+#if __has_include("conf/displayGC9106conf_custom.h")
+ #include "conf/displayGC9106conf_custom.h"
+#else
+ #include "conf/displayGC9106conf.h"
#endif
-#define TFT_FULLTIME 1
-
-#define TITLE_SIZE2 0
-#define TITLE_TOP1 TFT_FRAMEWDT + 2 * TFT_LINEHGHT-3
-
-#define BOOTSTR_TOP1 50
-#define BOOTSTR_TOP2 65
+#define BOOT_PRG_COLOR 0xE68B
+#define BOOT_TXT_COLOR 0xFFFF
+#define PINK 0xF97F
class DspCore: public Adafruit_GC9106Ex {
- 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(bool force=false);
- virtual void startWrite(void);
- virtual void endWrite(void);
- void flip();
- void invert();
- void sleep();
- void wake();
- /*virtual void sendCommand(uint8_t commandByte, uint8_t *dataBytes,
- uint8_t numDataBytes);
- virtual void sendCommand(uint8_t commandByte, const uint8_t *dataBytes = NULL,
- uint8_t numDataBytes = 0);
- virtual void sendCommand16(uint16_t commandWord, const uint8_t *dataBytes = NULL,
- uint8_t numDataBytes = 0);*/
- private:
- uint16_t swidth, sheight;
- char oldTimeBuf[20];
- uint16_t wot, hot, dot;
- int16_t x, y;
- uint16_t cwidth, cheight;
- void setClockBounds();
- byte getPw(uint16_t ncwidth);
+#include "tools/commongfx.h"
};
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/displayILI9225.cpp b/yoRadio/src/displays/displayILI9225.cpp
index f8d6774..0b0c6f1 100644
--- a/yoRadio/src/displays/displayILI9225.cpp
+++ b/yoRadio/src/displays/displayILI9225.cpp
@@ -1,15 +1,13 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_ILI9225
#include "displayILI9225.h"
#include
#include "fonts/bootlogo.h"
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/network.h"
-const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
-const char *mnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"};
extern unsigned char yofont5x7[];
extern unsigned char yofont10x14[];
@@ -21,102 +19,7 @@ DspCore::DspCore(): TFT_22_ILI9225(TFT_RST, TFT_DC, TFT_CS, 0) {
}
-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() {
- started = true;
- clearDsp();
- setTextSize(TITLE_SIZE1);
- setTextColor(config.theme.title1, config.theme.background);
- setCursor(TFT_FRAMEWDT, TITLE_TOP1);
- print("AP NAME: ");
- print(apSsid);
- setCursor(TFT_FRAMEWDT, TITLE_TOP2);
- print("PASSWORD: ");
- print(apPassword);
- setTextColor(config.theme.title2, config.theme.background);
- 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("/");
- TAKE_MUTEX();
- drawLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, TITLE_TOP1-8, config.theme.div);
- GIVE_MUTEX();
-}
+#include "tools/utf8RusGFX.h"
void DspCore::setTextSize(uint8_t s){
if(s==2){
@@ -127,59 +30,44 @@ void DspCore::setTextSize(uint8_t s){
}
void DspCore::setTextColor(uint16_t fg, uint16_t bg){
- bgcolor=bg;
- setBackgroundColor(bgcolor);
- fgcolor=fg;
+ _bgcolor=bg;
+ setBackgroundColor(_bgcolor);
+ _fgcolor=fg;
}
void DspCore::setCursor(int16_t x, int16_t y){
- cursorx=x;
- cursory=y;
+ _cursorx=x;
+ _cursory=y;
}
uint16_t DspCore::print(const char* s){
TAKE_MUTEX();
- if(gFont){
- drawGFXText(cursorx, cursory, s, fgcolor);
+ if(_gFont){
+ drawGFXText(_cursorx, _cursory, s, _fgcolor);
GIVE_MUTEX();
return 0;
}else{
- cursorx=drawText(cursorx, cursory, s, fgcolor);
+ _cursorx=drawText(_cursorx, _cursory, s, _fgcolor);
GIVE_MUTEX();
- return cursorx;
- }
-}
-
-void DspCore::getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1,
- int16_t *y1, uint16_t *w, uint16_t *h){
- if(!gFont){
- _currentFont cfont = getFont();
- *w = getTextWidth(string);
- *h = cfont.height;
- }else{
- int16_t iw, ih;
- getGFXTextExtent(string, x, y, &iw, &ih);
- *w = (uint16_t)iw;
- *h = (uint16_t)ih;
+ return _cursorx;
}
}
void DspCore::setFont(uint8_t* font, bool monoSp) {
- gFont = false;
+ _gFont = false;
TFT_22_ILI9225::setFont(font, monoSp);
}
void DspCore::setFont(const GFXfont *f) {
if (f) {
- gFont = true;
+ _gFont = true;
setGFXFont(f);
} else {
setFont(yofont5x7, false);
}
}
-void DspCore::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
- uint16_t color) {
+void DspCore::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(y<0){
h=h+y;
y=0;
@@ -189,24 +77,37 @@ void DspCore::fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
GIVE_MUTEX();
}
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
- //hspi.begin();
- started = false;
- begin();
- invert();
-// clear(0x0000);
- flip();
- setTextSize(1);
- screenwidth = maxX();
- screenheight = maxY();
- swidth = screenwidth;
- sheight = screenheight;
+void DspCore::drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){
+ TAKE_MUTEX();
+ drawRectangle(x, y, x+w, y+h, color);
+ GIVE_MUTEX();
}
-void DspCore::drawLogo() {
-// setBackgroundColor(0x0000);
- drawBitmap((swidth - 99) / 2, (sheight-64)/2 - TFT_LINEHGHT*2, bootlogo2, 99, 64);
-// setBackgroundColor(config.theme.background);
+void DspCore::drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color){
+ TAKE_MUTEX();
+ drawLine(x, y, x, y+h, color);
+ GIVE_MUTEX();
+}
+
+void DspCore::drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color){
+ TAKE_MUTEX();
+ drawLine(x, y, x+w, y, color);
+ GIVE_MUTEX();
+}
+
+void DspCore::initDisplay() {
+ TAKE_MUTEX();
+ begin();
+ invert();
+ flip();
+ setTextSize(1);
+ GIVE_MUTEX();
+}
+
+void DspCore::drawLogo(uint16_t top) {
+ TAKE_MUTEX();
+ drawBitmap((width() - 99) / 2, top, bootlogo2, 99, 64);
+ GIVE_MUTEX();
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -215,242 +116,124 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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, config.theme.meta);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 4) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setTextColor(config.theme.playlist[abs(i - 4)-1], config.theme.background);
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT-1, swidth, PLMITEMHEIGHT-7, config.theme.background);
+ fillRect(0, yStart + i * PLMITEMHEIGHT-1, width(), PLMITEMHEIGHT-7, config.theme.background);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
- //fillRect(0, 0, swidth, sheight, config.theme.background);
+void DspCore::clearDsp(bool black) {
TAKE_MUTEX();
- clear(started?config.theme.background:0x0000);
+ clear(black?0x0000:config.theme.background);
GIVE_MUTEX();
}
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- if (TFT_FRAMEWDT==0) return;
- fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
- fillRect(0, texttop, TFT_FRAMEWDT-1, textheight, bg);
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-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;
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI28pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop, swidth, textheight, bg);
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0)
+ fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background);
+ setTextColor(config.theme.date, config.theme.background);
+ setCursor(_dateleft, clockTop+10);
+ print(_dateBuf); /* print date */
+ strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf));
+ _olddatewidth = _datewidth;
+ _olddateleft = _dateleft;
+ setTextSize(2);
+ setTextColor(config.theme.dow, config.theme.background);
+ setCursor(width() - 8 - clockRightSpace - CHARWIDTH*2*2, clockTop-CHARHEIGHT*2+4);
+ print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */
}
-void DspCore::drawNextStationNum(uint16_t num) {
+void DspCore::_clockTime(){
+ if(_oldtimeleft>0) fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = width()-clockRightSpace-CHARWIDTH*2*2-24-_timewidth;
setTextSize(1);
- setTextColor(config.theme.digit, config.theme.background);
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, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
- setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
- print(numstr);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
+ setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
+ drawFastVLine(width()-clockRightSpace-CHARWIDTH*2*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/
+ drawFastHLine(width()-clockRightSpace-CHARWIDTH*2*2-18, clockTop-clockTimeHeight+21, 32, config.theme.div); /*divider hor*/
+ sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900);
+ strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf));
+ _datewidth = strlen(_dateBuf) * CHARWIDTH;
+ _dateleft = width() - 8 - clockRightSpace - _datewidth;
}
-void DspCore::frameTitle(const char* str) {
- setTextSize(META_SIZE);
- centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
- TAKE_MUTEX();
- drawLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, TITLE_TOP1-8, config.theme.div);
- GIVE_MUTEX();
-}
-
-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, config.theme.rssi, config.theme.background);
-}
-
-void DspCore::ip(const char* str) {
- if(!started){
- started = true;
- clear(config.theme.background);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate();
}
- int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
- char buf[30];
- sprintf(buf, "IP: %s", str);
- setTextSize(1);
- setTextColor(config.theme.ip, config.theme.background);
- setCursor(TFT_FRAMEWDT, vTop);
- print(buf);
+ _clockSeconds();
}
-void DspCore::set_TextSize(uint8_t s) {
- setTextSize(s);
+void DspCore::clearClock(){
+ fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
- setTextColor(fg, bg);
+void DspCore::startWrite(void) {
+ //TAKE_MUTEX();
+ TFT_22_ILI9225::startWrite();
}
-void DspCore::set_Cursor(int16_t x, int16_t y) {
- setCursor(x, y);
+void DspCore::endWrite(void) {
+ TFT_22_ILI9225::endWrite();
+ //GIVE_MUTEX();
}
-void DspCore::printText(const char* txt) {
- print(txt);
+void DspCore::loop(bool force) {
+ //delay(5);
}
-void DspCore::loop(bool force) {
-
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
void DspCore::drawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_t w, int16_t h) {
@@ -458,14 +241,46 @@ void DspCore::drawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_
drawBitmap(x, y, bitmap, w, h);
GIVE_MUTEX();
}
+
void DspCore::flip(){
+ TAKE_MUTEX();
setOrientation(config.store.flipscreen?3:1);
+ GIVE_MUTEX();
}
void DspCore::invert(){
+ TAKE_MUTEX();
invertDisplay(config.store.invertdisplay);
+ GIVE_MUTEX();
}
-void DspCore::sleep(void) { setDisplay(false); }
-void DspCore::wake(void) { setDisplay(true); }
+void DspCore::sleep(void) { TAKE_MUTEX(); setDisplay(false); GIVE_MUTEX(); }
+void DspCore::wake(void) { TAKE_MUTEX(); setDisplay(true); GIVE_MUTEX(); }
+
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) { }
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { }
+
+uint16_t DspCore::drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) {
+ return cfont.width;
+ }
+ }
+ return TFT_22_ILI9225::drawChar(x, y, ch, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setTextSize(1);
+ setFont(&DS_DIGI28pt7b);
+}
#endif
diff --git a/yoRadio/src/displays/displayILI9225.h b/yoRadio/src/displays/displayILI9225.h
index 177dcbb..aeea31d 100644
--- a/yoRadio/src/displays/displayILI9225.h
+++ b/yoRadio/src/displays/displayILI9225.h
@@ -1,94 +1,31 @@
#ifndef displayILI9225_h
#define displayILI9225_h
-
+#include "../core/options.h"
+//==================================================
#include "Arduino.h"
#include "../ILI9225Fix/TFT_22_ILI9225Fix.h"
-#include "fonts/DS_DIGI28pt7b.h"
+#include "fonts/DS_DIGI28pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
+#include "tools/l10n.h"
-#define VU_READY 1
-#define WEATHER_READY 1
-#define DSP_CAN_SLEEP true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 10
-#define TFT_FRAMEWDT 4
-#define META_SIZE 2
-#ifndef TITLE_SIZE1
-#define TITLE_SIZE1 1
-#endif
-#ifndef TITLE_SIZE2
-#define TITLE_SIZE2 1
-#endif
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 3
-#define SCROLLTIME 30
+typedef GFXcanvas16 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
+
+#if __has_include("conf/displayILI9225conf_custom.h")
+ #include "conf/displayILI9225conf_custom.h"
+#else
+ #include "conf/displayILI9225conf.h"
#endif
-#define PLMITEMS 9
-#define PLMITEMLENGHT 40
-#define PLMITEMHEIGHT 22
-#define TFT_FULLTIME 1
-#ifndef TITLE_TOP1
-#define TITLE_TOP1 TFT_FRAMEWDT + META_SIZE * TFT_LINEHGHT + 8
-#endif
-#ifndef TITLE_TOP2
-#define TITLE_TOP2 TFT_FRAMEWDT + (META_SIZE+2) * TFT_LINEHGHT
-#endif
+#define BOOT_PRG_COLOR 0xE68B
+#define BOOT_TXT_COLOR 0xFFFF
+#define PINK 0xF97F
class DspCore: public TFT_22_ILI9225 {
- 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(bool force=false);
- void setFont(uint8_t* font, bool monoSp=false );
- void setFont(const GFXfont *f = NULL);
- void setTextSize(uint8_t s);
- void setTextColor(uint16_t fg, uint16_t bg);
- void setCursor(int16_t x, int16_t y);
- uint16_t print(const char* s);
- void getTextBounds(const char *string, int16_t x, int16_t y, int16_t *x1,
- int16_t *y1, uint16_t *w, uint16_t *h);
- void fillRect(int16_t x, int16_t y, int16_t w, int16_t h,
- uint16_t color);
- int16_t width(void) { return (int16_t)maxX(); }
- int16_t height(void) { return (int16_t)maxY(); }
- void drawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_t w, int16_t h);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- uint16_t bgcolor, fgcolor;
- int16_t cursorx, cursory;
- bool gFont, started;
- char oldTimeBuf[20];
- uint8_t oldVolume;
- uint16_t wot, hot;
-
+#include "tools/commongfx.h"
};
extern DspCore dsp;
diff --git a/yoRadio/src/displays/displayILI9341.cpp b/yoRadio/src/displays/displayILI9341.cpp
index c9080a8..c75af0f 100644
--- a/yoRadio/src/displays/displayILI9341.cpp
+++ b/yoRadio/src/displays/displayILI9341.cpp
@@ -1,15 +1,11 @@
-#include "../../options.h"
+#include "../core/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] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"};
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/network.h"
#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY)
#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl)
@@ -18,116 +14,18 @@ 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;
-}
+#include "tools/utf8RusGFX.h"
-void DspCore::apScreen() {
- setTextSize(TITLE_SIZE1);
- setTextColor(config.theme.title1, config.theme.background);
- setCursor(TFT_FRAMEWDT, TITLE_TOP1);
- print("AP NAME: ");
- print(apSsid);
- setCursor(TFT_FRAMEWDT, TITLE_TOP2);
- print("PASSWORD: ");
- print(apPassword);
- setTextColor(config.theme.title2, config.theme.background);
- 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, config.theme.div);
-}
-
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
begin(); /* SPI_DEFAULT_FREQ 40000000 */
invert();
cp437(true);
-// fillScreen(config.theme.background);
flip();
setTextWrap(false);
- setTextSize(1);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
}
-void DspCore::drawLogo() {
- fillScreen(0x0000);
- drawRGBBitmap((swidth - 99) / 2, (sheight-64)/2 - TFT_LINEHGHT*2, bootlogo2, 99, 64);
+void DspCore::drawLogo(uint16_t top) {
+ drawRGBBitmap((width() - 99) / 2, top, bootlogo2, 99, 64);
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -136,223 +34,102 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 5, PLMITEMS);
setTextSize(2);
- int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
- fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) - 1, swidth, PLMITEMHEIGHT + 2, config.theme.meta);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
+ //fillRect(0, (height() / 2 - PLMITEMHEIGHT / 2) - 1, width(), PLMITEMHEIGHT + 2, config.theme.meta);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 5) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setTextColor(config.theme.playlist[abs(i - 5)-1], config.theme.background);
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT-1, swidth, PLMITEMHEIGHT-4, config.theme.background);
+ fillRect(0, yStart + i * PLMITEMHEIGHT-1, width(), PLMITEMHEIGHT-4, config.theme.background);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
- fillScreen(config.theme.background);
+void DspCore::clearDsp(bool black) { fillScreen(black?0:config.theme.background); }
+
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- if (TFT_FRAMEWDT==0) return;
- fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
- fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI42pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-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;
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c240?46:34);
- clleft=swidth-TFT_FRAMEWDT-clwidth;
- //fillRect(swidth-TFT_FRAMEWDT-clwidth, cltop-hot, clwidth, hot+3, config.theme.background);
- setCursor(clleft, cltop);
- setTextColor(config.theme.background);
- 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(config.theme.clock, config.theme.background);
- setCursor(clleft, cltop);
- print(timeBuf);
-
- setFont();
- setTextSize(3);
- setTextColor(config.theme.dow, config.theme.background);
- 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);
- uint16_t wdate, hdate;
- getTextBounds(timeBuf, 0, 0, &x1, &y1, &wdate, &hdate);
- fillRect(swidth - wdate - TFT_FRAMEWDT-20, cltop+10, wdate+20, hdate, config.theme.background);
- rightText(utf8Rus(timeBuf,true), cltop+10, config.theme.date, config.theme.background, false, swidth>240?12:0);
- drawFastVLine(clleft+wot+clsp/2+3, cltop-hot, hot+3, config.theme.div);
- drawFastHLine(clleft+wot+clsp/2+3, cltop-hot+29, 42, config.theme.div);
-
- drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, config.theme.div);
- }
+void DspCore::_clockSeconds(){
setTextSize(3);
setTextColor(config.theme.seconds, config.theme.background);
- setCursor(clleft+wot+clsp, cltop-hot+1);
- sprintf(timeBuf, "%02d", timeinfo.tm_sec);
- print(timeBuf);
+ setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-clockTimeHeight+1);
+ sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
+ print(_bufforseconds); /* print seconds */
}
-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, config.theme.background);
- fillRect(TFT_FRAMEWDT + 1, vTop - 1, ww, 5, config.theme.volbarin);
- drawRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, config.theme.volbarout);
- if(swidth>240){
- char buf[20];
- sprintf(buf, "VOL %d", config.store.volume);
- setTextSize(1);
- centerText(buf, volTop, config.theme.vol, config.theme.background);
- }
- if (withNumber) {
- setTextSize(1);
- setTextColor(config.theme.digit);
- setFont(&DS_DIGI42pt7b);
- char volstr[4];
- uint16_t wv, hv;
- int16_t x1, y1;
-
- /*setTextColor(config.theme.background);
- sprintf(volstr, "%d", oldVolume);
- getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
- setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
- print(volstr);*/
- sprintf(volstr, "%d", oldVolume);
- getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
- fillRect((swidth - wv) / 2 - 12, (sheight-hv)/2, wv+24, hv+4, config.theme.background);
-
- setTextColor(config.theme.vol);
- 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, config.theme.background);
- setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
- print(volstr);
- oldVolume=config.store.volume;
- setFont();
- }
+void DspCore::_clockDate(){
+ if(_olddateleft>0)
+ dsp.fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background);
+ setTextColor(config.theme.date, config.theme.background);
+ setCursor(_dateleft, clockTop+10);
+ print(_dateBuf); /* print date */
+ strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf));
+ _olddatewidth = _datewidth;
+ _olddateleft = _dateleft;
+ setTextSize(3);
+ setTextColor(config.theme.dow, config.theme.background);
+ setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-CHARHEIGHT*3+4);
+ print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */
}
-void DspCore::drawNextStationNum(uint16_t num) {
+void DspCore::_clockTime(){
+ if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = width()-clockRightSpace-CHARWIDTH*3*2-24-_timewidth;
setTextSize(1);
- setTextColor(config.theme.digit);
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, config.theme.background);
- setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
- print(numstr);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
+ drawFastVLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/
+ drawFastHLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight+29, 44, config.theme.div); /*divider hor*/
+ sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900);
+ strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf));
+ _datewidth = strlen(_dateBuf) * CHARWIDTH;
+ _dateleft = width() - 8 - clockRightSpace - _datewidth;
}
-void DspCore::frameTitle(const char* str) {
- setTextSize(META_SIZE);
- centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
- drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, config.theme.div);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate();
+ }
+ _clockSeconds();
}
-void DspCore::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, config.theme.rssi, config.theme.background);
-}
-
-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(config.theme.ip, config.theme.background);
- setCursor(TFT_FRAMEWDT, vTop);
- print(buf);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -365,25 +142,17 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
-void DspCore::set_TextSize(uint8_t s) {
- setTextSize(s);
+void DspCore::loop(bool force) { }
+
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
- setTextColor(fg, bg);
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
}
-void DspCore::set_Cursor(int16_t x, int16_t y) {
- setCursor(x, y);
-}
-
-void DspCore::printText(const char* txt) {
- print(txt);
-}
-
-void DspCore::loop(bool force) {
-
-}
void DspCore::flip(){
setRotation(config.store.flipscreen?1:3);
}
@@ -395,4 +164,31 @@ void DspCore::invert(){
void DspCore::sleep(void) { sendCommand(ILI9341_SLPIN); delay(150); sendCommand(ILI9341_DISPOFF); delay(150);}
void DspCore::wake(void) { sendCommand(ILI9341_DISPON); delay(150); sendCommand(ILI9341_SLPOUT); delay(150);}
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_ILI9341::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_ILI9341::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setFont(&DS_DIGI42pt7b);
+ setTextSize(1);
+}
#endif
diff --git a/yoRadio/src/displays/displayILI9341.h b/yoRadio/src/displays/displayILI9341.h
index f807765..a52287d 100644
--- a/yoRadio/src/displays/displayILI9341.h
+++ b/yoRadio/src/displays/displayILI9341.h
@@ -1,76 +1,32 @@
#ifndef displayILI9341_h
#define displayILI9341_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
-// https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
-#include "fonts/DS_DIGI42pt7b.h"
+#include "fonts/DS_DIGI42pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
+#include "tools/l10n.h"
-#define VU_READY 1
-#define WEATHER_READY 1
-#define DSP_CAN_SLEEP true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 10
-#define TFT_FRAMEWDT 8
-#define META_SIZE 3
-#define TITLE_SIZE1 2
-#define TITLE_SIZE2 2
+typedef GFXcanvas16 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 4
-#define SCROLLTIME 30
+#if __has_include("conf/displayILI9341conf_custom.h")
+ #include "conf/displayILI9341conf_custom.h"
+#else
+ #include "conf/displayILI9341conf.h"
#endif
-#define PLMITEMS 11
-#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 BOOT_PRG_COLOR 0xE68B
+#define BOOT_TXT_COLOR 0xFFFF
+#define PINK 0xF97F
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(bool force=false);
- virtual void startWrite(void);
- virtual void endWrite(void);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- char oldTimeBuf[20];
- uint8_t oldVolume;
- uint16_t wot, hot;
-
+#include "tools/commongfx.h"
};
extern DspCore dsp;
diff --git a/yoRadio/src/displays/displayLC1602.cpp b/yoRadio/src/displays/displayLC1602.cpp
index 6697ca6..98a3e74 100644
--- a/yoRadio/src/displays/displayLC1602.cpp
+++ b/yoRadio/src/displays/displayLC1602.cpp
@@ -1,227 +1,153 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_1602 || DSP_MODEL==DSP_2004 || DSP_MODEL==DSP_2004I2C
#include "displayLC1602.h"
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/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
-
-#ifdef LCD_2004
-const byte controlspaces[] = { CLOCK_SPACE, 0, 0, VOL_SPACE };
-#else
-const byte controlspaces[] = { CLOCK_SPACE, VOL_SPACE };
+ #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
DspCore::DspCore(): DSP_INIT {}
+#include "tools/utf8RusLCD.h"
+
void DspCore::apScreen() {
+ clear();
setCursor(0,0);
- print("YORADIO AP MODE");
+ print(utf8Rus(const_lcdApMode, false));
setCursor(0,1);
print(WiFi.softAPIP().toString().c_str());
#ifdef LCD_2004
setCursor(0, 2);
- print("AP NAME: ");
+ print(utf8Rus(const_lcdApName, false));
print(apSsid);
setCursor(0, 3);
- print("PASSWORD: ");
+ print(utf8Rus(const_lcdApPass, false));
print(apPassword);
#endif
}
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+byte Arrow[] PROGMEM = { B00000, B00100, B00010, B01001, B00010, B00100, B00000, B00000 };
+
+void DspCore::initDisplay() {
#ifdef LCD_I2C
init();
backlight();
#else
-#ifdef LCD_2004
- begin(20, 4);
-#else
- begin(16, 2);
+ #ifdef LCD_2004
+ begin(20, 4);
+ #else
+ begin(16, 2);
+ #endif
#endif
-#endif
-
-#ifdef LCD_2004
- screenwidth = 20;
- screenheight = 4;
-#else
- screenwidth = 16;
- screenheight = 2;
-#endif
-
- swidth = screenwidth;
- sheight = screenheight;
- fillSpaces = true;
+ clearClipping();
+ createChar(0, Arrow);
}
-void DspCore::drawLogo() {
+void DspCore::drawLogo(uint16_t top) { }
-}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
- centerText("NEXT STATION", 0, 0, 0);
+ clear();
for (byte i = 0; i < PLMITEMS; i++) {
plMenu[i][0] = '\0';
}
-#ifdef LCD_2004
config.fillPlMenu(plMenu, currentItem-1, PLMITEMS);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 1) {
- strlcpy(currentItemText, ">", 2);
- //strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
- strlcat(currentItemText, plMenu[i], PLMITEMLENGHT - 2);
+ strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
- char tmp[swidth+1] = {0};
- strlcpy(tmp, utf8Rus(plMenu[i], true), swidth);
- clearScroll(1 + i, 0, 0);
- setCursor(1, 1 + i);
+ char tmp[width()] = {0};
+ strlcpy(tmp, utf8Rus(plMenu[i], true), width());
+ setCursor(1, i);
print(tmp);
}
}
-#else
- config.fillPlMenu(plMenu, currentItem, PLMITEMS);
- for (byte i = 0; i < PLMITEMS; i++) {
- strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
- }
-#endif
+ setCursor(0,1);
+ write(byte(0));
}
-void DspCore::clearDsp() {
+void DspCore::clearDsp(bool black) {
clear();
}
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
-
-}
-
-void DspCore::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 DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- for(uint16_t x=0; xswidth)?0:(swidth-strlen(text))/2;
+void DspCore::fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){
+ if(w<2) return;
+ char buf[width()+1] = { 0 };
+ snprintf(buf, sizeof(buf), "%*s%s", w-1, "", " ");
setCursor(x, y);
- print(text);
-}
-
-void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
- byte x=swidth-strlen(text);
- setCursor(x-1, y);
- print(" ");
+ print(buf);
setCursor(x, y);
- print(text);
}
-void DspCore::displayHeapForDebug() {
-
-}
-
-void DspCore::printClock(const char* timestr) {
- rightText(timestr, 0, 0, 0);
-}
-
-void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw) {
-
-}
-
-void DspCore::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{
+uint16_t DspCore::width(){
#ifdef LCD_2004
- rightText(" ", 3, 0, 0);
- rightText(volstr, 3, TFT_LOGO, TFT_BG);
+ return 20;
#else
- rightText(" ", 1, 0, 0);
- rightText(volstr, 1, TFT_LOGO, TFT_BG);
+ return 16;
#endif
- }
}
-void DspCore::drawNextStationNum(uint16_t num) {
- char numstr[7];
- sprintf(numstr, "%d", num);
- clearScroll(1, 0, 0);
- centerText(numstr, 1, TFT_LOGO, TFT_BG);
-}
-
-void DspCore::frameTitle(const char* str) {
- centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
-}
-
-void DspCore::rssi(const char* str) {
-
-}
-
-void DspCore::ip(const char* str) {
+uint16_t DspCore::height(){
#ifdef LCD_2004
- setCursor(0, 3);
- print(str);
+ return 4;
+#else
+ return 2;
#endif
}
-void DspCore::set_TextSize(uint8_t s) {
-
+uint8_t DspCore::_charWidth(unsigned char c){
+ return 1;
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
-
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;cnumchars) nextX=numchars;
- setCursor(nextX, yOffset);
+void DspCore::clearClock() { }
+
+void DspCore::loop(bool force) {
+// delay(100);
}
-void DspCore::loop(bool force) {
- if (checkdelay(SCROLLTIME, loopdelay)) {
- //display();
- }
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = 1;
+ height = 1;
}
-boolean DspCore::checkdelay(int m, unsigned long & tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
-}
+void DspCore::flip(){ }
+
+void DspCore::invert(){ }
void DspCore::sleep(void) {
noDisplay();
@@ -236,111 +162,20 @@ void DspCore::wake(void) {
#endif
}
-char* DspCore::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);
- }
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) { }
- 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;
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { }
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
}
+void DspCore::clearClipping(){
+ _clipping = false;
+ setClipping({0, 0, width(), height()});
+}
+
+void DspCore::setNumFont(){ }
+
#endif
diff --git a/yoRadio/src/displays/displayLC1602.h b/yoRadio/src/displays/displayLC1602.h
index 99598ec..27b699d 100644
--- a/yoRadio/src/displays/displayLC1602.h
+++ b/yoRadio/src/displays/displayLC1602.h
@@ -1,121 +1,76 @@
#ifndef displayLC1602_h
#define displayLC1602_h
+#include "../core/options.h"
#include "Arduino.h"
+#include "tools/l10n.h"
-#define DSP_FLIPPED 0
+#define DSP_NOT_FLIPPED
+#define DSP_LCD
+
+#define CHARWIDTH 1
+#define CHARHEIGHT 1
+
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
#if DSP_MODEL==DSP_2004 || DSP_MODEL==DSP_2004I2C
-#define LCD_2004
+ #define LCD_2004
#endif
#if DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_2004I2C
-#define LCD_I2C
-#include "../LiquidCrystalI2C/LiquidCrystalI2CEx.h"
+ #define LCD_I2C
+ #include "../LiquidCrystalI2C/LiquidCrystalI2CEx.h"
#else
-#include
+ #include
#endif
#ifdef LCD_I2C
+ #ifdef LCD_2004
+ #define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 20, 4, I2C_SDA, I2C_SCL)
+ #else
+ #define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 16, 2, I2C_SDA, I2C_SCL)
+ #endif
+#else
+ #define DSP_INIT LiquidCrystal(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7)
+#endif
+
#ifdef LCD_2004
-#define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 20, 4, I2C_SDA, I2C_SCL)
+ #if __has_include("conf/displayLCD2004conf_custom.h")
+ #include "conf/displayLCD2004conf_custom.h"
+ #else
+ #include "conf/displayLCD2004conf.h"
+ #endif
#else
-#define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 16, 2, I2C_SDA, I2C_SCL)
-#endif
-#else
-#define DSP_INIT LiquidCrystal(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7)
+ #if __has_include("conf/displayLCD1602conf_custom.h")
+ #include "conf/displayLCD1602conf_custom.h"
+ #else
+ #include "conf/displayLCD1602conf.h"
+ #endif
#endif
+#define BOOT_PRG_COLOR 0x1
+#define BOOT_TXT_COLOR 0x1
+#define PINK 0x1
-#define TFT_LINEHGHT 1
-#define TFT_FRAMEWDT 0
-#define DSP_CAN_SLEEP true
-
-#define PLMITEMLENGHT 40
-#define PLMITEMHEIGHT 9
-#define TITLE_TOP1 1
-#ifdef LCD_2004
-#define PLMITEMS 3
-#define TITLE_TOP2 2
-#define PL_TOP 2
-#define BOOTSTR_TOP2 1
-#define BOOTSTR_TOP1 2
-#else
-#define PLMITEMS 1
-#define TITLE_SIZE2 0
-#define IP_INST_VOL
-#define PL_TOP 1
-#define BOOTSTR_TOP2 0
-#define BOOTSTR_TOP1 1
-#endif
-
-#define PLCURRENT_SIZE 1
-
-#define SCROLLDELTA 1
-#define SCROLLTIME 250
-
-
-#define STARTTIME_PL 2000
+/* not used required */
+#define bootLogoTop 0
+const char rssiFmt[] PROGMEM = "";
+const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
+const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
+const char const_lcdApMode[] PROGMEM = "YORADIO AP MODE";
+const char const_lcdApName[] PROGMEM = "AP NAME: ";
+const char const_lcdApPass[] PROGMEM = "PASSWORD: ";
#ifdef LCD_I2C
-class DspCore: public LiquidCrystal_I2C {
+ class DspCore: public LiquidCrystal_I2C {
#else
-class DspCore: public LiquidCrystal {
+ class DspCore: public LiquidCrystal {
#endif
- 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 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(bool force=false);
- void flip(){};
- void invert(){};
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight, xOffset, yOffset;
- int16_t nextX;
- unsigned long loopdelay;
- boolean checkdelay(int m, unsigned long &tstamp);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
-/*
- * TFT COLORS
- */
-#define CLOCK_SPACE 6
-#ifdef LCD_2004
-#define VOL_SPACE 0
-#else
-#define VOL_SPACE 3
-#endif
-#define SILVER 0
-#define TFT_BG 0
-#define TFT_FG CLOCK_SPACE
-#define TFT_LOGO VOL_SPACE
-
#endif
diff --git a/yoRadio/src/displays/displayN5110.cpp b/yoRadio/src/displays/displayN5110.cpp
index bbfa8e8..935dd3b 100644
--- a/yoRadio/src/displays/displayN5110.cpp
+++ b/yoRadio/src/displays/displayN5110.cpp
@@ -1,11 +1,11 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==3
#include "displayN5110.h"
#include
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/network.h"
#define LOGO_WIDTH 21
#define LOGO_HEIGHT 28
@@ -27,98 +27,7 @@ DspCore::DspCore(): Adafruit_PCD8544(TFT_DC, TFT_CS, 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] = 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);
- setFont(&TinyFont6);
- setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 1 * TFT_LINEHGHT+6);
- print("AP NAME: ");
- print(apSsid);
- setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 2 * TFT_LINEHGHT+6);
- print("PASSWD: ");
- print(apPassword);
- setTextColor(SILVER, TFT_BG);
- setCursor(TFT_FRAMEWDT, sheight - 10);
- print("SETTINGS PAGE ON: ");
- setCursor(TFT_FRAMEWDT, sheight-2);
- print("http://");
- print(WiFi.softAPIP().toString().c_str());
- print("/");
- setFont();
-}
+#include "tools/utf8RusGFX.h"
void DspCore::command(uint8_t c) {
TAKE_MUTEX();
@@ -132,30 +41,35 @@ void DspCore::data(uint8_t c) {
GIVE_MUTEX();
}
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
begin();
setReinitInterval(255);
config.theme.background = TFT_BG;
config.theme.meta = TFT_FG;
+ config.theme.clock = TFT_FG;
+ config.theme.weather = TFT_FG;
+ config.theme.metabg = TFT_BG;
+ config.theme.metafill = TFT_BG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
+ config.theme.ip = TFT_FG;
+ config.theme.bitrate = TFT_FG;
+ config.theme.digit = TFT_FG;
+ config.theme.buffer = TFT_FG;
+ config.theme.volbarout = TFT_FG;
+ config.theme.volbarin = TFT_FG;
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
+
setContrast(config.store.contrast);
cp437(true);
invert();
- fillScreen(TFT_BG);
flip();
setTextWrap(false);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
}
-void DspCore::drawLogo() {
- clearDisplay();
- drawBitmap((width() - LOGO_WIDTH ) / 2, 0, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
+void DspCore::drawLogo(uint16_t top) {
+ drawBitmap((width() - LOGO_WIDTH ) / 2, top, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
display();
}
@@ -165,185 +79,108 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
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);
- fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
+ fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
+void DspCore::clearDsp(bool black) {
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);
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-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;
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI15pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop, swidth, textheight, bg);
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth+CHARWIDTH*2+2, clockTimeHeight, config.theme.background);
+ //if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = (width()/2 - _timewidth/2)-clockRightSpace;
+ setTextSize(1);
+ setFont(&DS_DIGI15pt7b);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-void DspCore::set_TextSize(uint8_t s) {
- setTextSize(s);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ }
+ _clockSeconds();
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
- setTextColor(fg, bg);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*2+2, clockTimeHeight, config.theme.background);
+ //dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
-void DspCore::set_Cursor(int16_t x, int16_t y) {
- setCursor(x, y);
-}
-
-void DspCore::printText(const char* txt) {
- print(txt);
-}
void DspCore::loop(bool force) {
- if (checkdelay(SCROLLTIME, loopdelay) || force) {
- display();
- }
+ display();
+ delay(5);
}
-boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
+
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
+}
+
void DspCore::flip(){
setRotation(config.store.flipscreen?2:0);
}
@@ -355,4 +192,32 @@ void DspCore::invert(){
void DspCore::sleep(void) { command( PCD8544_FUNCTIONSET | PCD8544_POWERDOWN); }
void DspCore::wake(void) { initDisplay(); }
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_PCD8544::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_PCD8544::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setFont(&DS_DIGI15pt7b);
+ setTextSize(1);
+}
+
#endif
diff --git a/yoRadio/src/displays/displayN5110.h b/yoRadio/src/displays/displayN5110.h
index 4e58423..752d4cf 100644
--- a/yoRadio/src/displays/displayN5110.h
+++ b/yoRadio/src/displays/displayN5110.h
@@ -1,5 +1,6 @@
#ifndef displayN5110_h
#define displayN5110_h
+#include "../core/options.h"
#include "Arduino.h"
#include
@@ -7,68 +8,25 @@
#include "fonts/TinyFont5.h"
#include "fonts/TinyFont6.h"
#include "fonts/DS_DIGI15pt7b.h"
+#include "tools/l10n.h"
-#define TFT_LINEHGHT 8
-#define TFT_FRAMEWDT 0
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define DSP_CAN_SLEEP true
-#define DSP_OLED true
+#define DSP_OLED
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-//#define SCROLLDELTA 8
-//#define SCROLLTIME 332
-#define SCROLLDELTA 4
-#define SCROLLTIME 250
+typedef GFXcanvas1 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
+
+#if __has_include("conf/displayN5110conf_custom.h")
+ #include "conf/displayN5110conf_custom.h"
+#else
+ #include "conf/displayN5110conf.h"
#endif
-#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
-
class DspCore: public Adafruit_PCD8544 {
- 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 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(bool force=false);
- virtual void command(uint8_t c);
- virtual void data(uint8_t c);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- unsigned long loopdelay;
- boolean checkdelay(int m, unsigned long &tstamp);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
@@ -76,9 +34,11 @@ extern DspCore dsp;
/*
* TFT COLORS
*/
-#define SILVER BLACK
-#define TFT_BG WHITE
-#define TFT_FG BLACK
-#define TFT_LOGO BLACK
+#define BOOT_PRG_COLOR BLACK
+#define BOOT_TXT_COLOR BLACK
+#define SILVER BLACK
+#define TFT_BG WHITE
+#define TFT_FG BLACK
+#define TFT_LOGO BLACK
#endif
diff --git a/yoRadio/src/displays/displaySH1106.cpp b/yoRadio/src/displays/displaySH1106.cpp
index 0a0a3c7..5282d7b 100644
--- a/yoRadio/src/displays/displaySH1106.cpp
+++ b/yoRadio/src/displays/displaySH1106.cpp
@@ -1,25 +1,23 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_SH1106 || DSP_MODEL==DSP_SH1107
#include "displaySH1106.h"
#include
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/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
+ #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
#ifndef I2CFREQ_HZ
-#define I2CFREQ_HZ 4000000UL
+ #define I2CFREQ_HZ 4000000UL
#endif
-const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
-
const unsigned char logo [] PROGMEM=
{
0x06, 0x03, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x8f, 0xc0, 0x1f, 0x8f, 0xc0,
@@ -42,128 +40,42 @@ DspCore::DspCore(): Adafruit_SH1107(64, 128, &I2CSH1106, -1) {
}
#endif
-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("/");
-}
+#include "tools/utf8RusGFX.h"
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
I2CSH1106.begin(I2C_SDA, I2C_SCL);
if (!begin(SCREEN_ADDRESS, true)) {
Serial.println(F("SH110X allocation failed"));
for (;;); // Don't proceed, loop forever
}
config.theme.background = TFT_BG;
- config.theme.meta = TFT_FG;
+ config.theme.meta = TFT_BG;
+ config.theme.clock = TFT_FG;
+ config.theme.weather = TFT_FG;
+ config.theme.metabg = TFT_FG;
+ config.theme.metafill = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
+ config.theme.ip = TFT_FG;
+ config.theme.vol = TFT_FG;
+ config.theme.bitrate = TFT_FG;
+ config.theme.digit = TFT_FG;
+ config.theme.buffer = TFT_FG;
+ config.theme.volbarout = TFT_FG;
+ config.theme.volbarin = TFT_FG;
+
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
+
cp437(true);
- fillScreen(TFT_BG);
flip();
invert();
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::drawLogo(uint16_t top) {
+ drawBitmap((width() - LOGO_WIDTH ) / 2, top, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -172,184 +84,100 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
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);
- fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
+ fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
- display();
}
-void DspCore::clearDsp() {
+void DspCore::clearDsp(bool black) {
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);
+uint8_t DspCore::_charWidth(unsigned char c){
+ return CHARWIDTH*clockTimeHeight;
}
-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;
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop, _oldtimewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
+ _timeleft = (width()/2 - _timewidth/2)+clockRightSpace;
+ setTextColor(config.theme.clock, config.theme.background);
+ setTextSize(clockTimeHeight);
+
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-void DspCore::set_TextSize(uint8_t s) {
- setTextSize(s);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ }
+ _clockSeconds();
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
- setTextColor(fg, bg);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop, _timewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
}
-void DspCore::set_Cursor(int16_t x, int16_t y) {
- setCursor(x, y);
-}
+void DspCore::startWrite(void) { }
-void DspCore::printText(const char* txt) {
- print(txt);
- display();
-}
+void DspCore::endWrite(void) { }
void DspCore::loop(bool force) {
- if (checkdelay(SCROLLTIME, loopdelay) || force) {
- //display();
- }
+ display();
+ delay(5);
}
-boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
+
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
+}
+
void DspCore::flip(){
#if DSP_MODEL==DSP_SH1107
setRotation(config.store.flipscreen?3:1);
@@ -366,4 +194,39 @@ void DspCore::invert(){
void DspCore::sleep(void) { oled_command(SH110X_DISPLAYOFF); }
void DspCore::wake(void) { oled_command(SH110X_DISPLAYON); }
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+#if DSP_MODEL==DSP_SH1107
+ Adafruit_SH1107::writePixel(x, y, color);
+#else
+ Adafruit_SH1106G::writePixel(x, y, color);
+#endif
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+#if DSP_MODEL==DSP_SH1107
+ Adafruit_SH1107::writeFillRect(x, y, w, h, color);
+#else
+ Adafruit_SH1106G::writeFillRect(x, y, w, h, color);
+#endif
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setTextSize(2);
+}
+
#endif
diff --git a/yoRadio/src/displays/displaySH1106.h b/yoRadio/src/displays/displaySH1106.h
index 8051a20..aab5baf 100644
--- a/yoRadio/src/displays/displaySH1106.h
+++ b/yoRadio/src/displays/displaySH1106.h
@@ -1,29 +1,25 @@
#ifndef displaySH1106_h
#define displaySH1106_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
+#include "tools/l10n.h"
-#define TFT_LINEHGHT 8
-#define TFT_FRAMEWDT 0
-#define DSP_CAN_SLEEP true
-#define DSP_OLED true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define PLMITEMS 7
-#define PLMITEMLENGHT 40
-#define PLMITEMHEIGHT 10
-#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT
-#define PLCURRENT_SIZE 1
-#define TFT_FULLTIME 1
+#define DSP_OLED
-#if DSP_MODEL==DSP_SH1107
-#define TITLE_SIZE2 0
-#endif
+typedef GFXcanvas1 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 2
-#define SCROLLTIME 35
+#if __has_include("conf/displaySH1106conf_custom.h")
+ #include "conf/displaySH1106conf_custom.h"
+#else
+ #include "conf/displaySH1106conf.h"
#endif
#if DSP_MODEL==DSP_SH1106
@@ -31,52 +27,20 @@ class DspCore: public Adafruit_SH1106G {
#else
class DspCore: public Adafruit_SH1107 {
#endif
- 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(bool force=false);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- unsigned long loopdelay;
- boolean checkdelay(int m, unsigned long &tstamp);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
/*
- * TFT COLORS
+ * OLED COLORS
*/
-#define SILVER SH110X_WHITE
-#define TFT_BG SH110X_BLACK
-#define TFT_FG SH110X_WHITE
-#define TFT_LOGO SH110X_WHITE
+#define BOOT_PRG_COLOR SH110X_WHITE
+#define BOOT_TXT_COLOR SH110X_WHITE
+#define PINK SH110X_WHITE
+#define SILVER SH110X_WHITE
+#define TFT_BG SH110X_BLACK
+#define TFT_FG SH110X_WHITE
+#define TFT_LOGO SH110X_WHITE
#endif
diff --git a/yoRadio/src/displays/displaySSD1305.cpp b/yoRadio/src/displays/displaySSD1305.cpp
index 71f977d..f5a80fd 100644
--- a/yoRadio/src/displays/displaySSD1305.cpp
+++ b/yoRadio/src/displays/displaySSD1305.cpp
@@ -1,24 +1,22 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_SSD1305 || DSP_MODEL==DSP_SSD1305I2C
#include "displaySSD1305.h"
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/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
+ #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
#ifndef DEF_SPI_FREQ
-#define DEF_SPI_FREQ 8000000UL /* set it to 0 for system default */
+ #define DEF_SPI_FREQ 8000000UL /* set it to 0 for system default */
#endif
-const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
-
const unsigned char logo [] PROGMEM=
{
0x06, 0x03, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x8f, 0xc0, 0x1f, 0x8f, 0xc0,
@@ -42,99 +40,10 @@ DspCore::DspCore(): Adafruit_SSD1305(128, 64, &I2CSSD1305, -1){
}
#endif
-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("/");
-}
+#include "tools/utf8RusGFX.h"
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
#if DSP_MODEL==DSP_SSD1305I2C
I2CSSD1305.begin(I2C_SDA, I2C_SCL);
#endif
@@ -143,28 +52,32 @@ void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
for (;;); // Don't proceed, loop forever
}
config.theme.background = TFT_BG;
- config.theme.meta = TFT_FG;
+ config.theme.meta = TFT_BG;
+ config.theme.clock = TFT_FG;
+ config.theme.weather = TFT_FG;
+ config.theme.metabg = TFT_FG;
+ config.theme.metafill = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
+ config.theme.ip = TFT_FG;
+ config.theme.vol = TFT_FG;
+ config.theme.bitrate = TFT_FG;
+ config.theme.digit = TFT_FG;
+ config.theme.buffer = TFT_FG;
+ config.theme.volbarout = TFT_FG;
+ config.theme.volbarin = TFT_FG;
+
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
+
cp437(true);
- fillScreen(TFT_BG);
flip();
invert();
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);
+void DspCore::drawLogo(uint16_t top) {
+ drawBitmap( (width() - LOGO_WIDTH ) / 2, 8, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
display();
}
@@ -174,171 +87,103 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
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);
- fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
+ fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
+void DspCore::clearDsp(bool black) {
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);
+uint8_t DspCore::_charWidth(unsigned char c){
+ return CHARWIDTH*clockTimeHeight;
}
-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;
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop, _oldtimewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
+ _timeleft = (width()/2 - _timewidth/2)+clockRightSpace;
+ setTextColor(config.theme.clock, config.theme.background);
+ setTextSize(clockTimeHeight);
+
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
+ //setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-void DspCore::set_TextSize(uint8_t s) {
- setTextSize(s);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ }
+ _clockSeconds();
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
- setTextColor(fg, bg);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop, _timewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
}
-void DspCore::set_Cursor(int16_t x, int16_t y) {
- setCursor(x, y);
-}
+void DspCore::startWrite(void) { }
-void DspCore::printText(const char* txt) {
- print(txt);
-}
+void DspCore::endWrite(void) { }
void DspCore::loop(bool force) {
- if (checkdelay(SCROLLTIME, loopdelay) || force) {
- display();
- }
+ display();
+ delay(5);
}
-boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
+
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
+}
+
void DspCore::flip(){
setRotation(config.store.flipscreen?2:0);
}
@@ -346,4 +191,35 @@ void DspCore::flip(){
void DspCore::invert(){
invertDisplay(config.store.invertdisplay);
}
+
+void DspCore::sleep(void) { oled_command(SSD1305_DISPLAYOFF); }
+void DspCore::wake(void) { oled_command(SSD1305_DISPLAYON); }
+
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_SSD1305::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_SSD1305::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setTextSize(2);
+}
+
#endif
diff --git a/yoRadio/src/displays/displaySSD1305.h b/yoRadio/src/displays/displaySSD1305.h
index 42bf06d..14b5118 100644
--- a/yoRadio/src/displays/displaySSD1305.h
+++ b/yoRadio/src/displays/displaySSD1305.h
@@ -1,76 +1,42 @@
#ifndef displaySSD1305_h
#define displaySSD1305_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
+#include "tools/l10n.h"
-#define WEATHER_READY 0
-#define DSP_CAN_SLEEP true
-#define DSP_OLED true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 8
-#define TFT_FRAMEWDT 0
+#define DSP_OLED
-#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
+typedef GFXcanvas1 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 2
-#define SCROLLTIME 35
+#if __has_include("conf/displaySSD1305conf_custom.h")
+ #include "conf/displaySSD1305conf_custom.h"
+#else
+ #include "conf/displaySSD1305conf.h"
#endif
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(bool force=false);
- void flip();
- void invert();
- private:
- uint16_t swidth, sheight;
- unsigned long loopdelay;
- boolean checkdelay(int m, unsigned long &tstamp);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
/*
- * TFT COLORS
+ * OLED COLORS
*/
-#define SILVER WHITE
-#define TFT_BG BLACK
-#define TFT_FG WHITE
-#define TFT_LOGO WHITE
+#define BOOT_PRG_COLOR WHITE
+#define BOOT_TXT_COLOR WHITE
+#define PINK WHITE
+#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 b15663e..087f179 100644
--- a/yoRadio/src/displays/displaySSD1306.cpp
+++ b/yoRadio/src/displays/displaySSD1306.cpp
@@ -1,14 +1,14 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_SSD1306 || DSP_MODEL==DSP_SSD1306x32
#include "displaySSD1306.h"
#include
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/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
+ #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
#if DSP_MODEL==DSP_SSD1306
@@ -30,144 +30,53 @@ const unsigned char logo [] PROGMEM=
#endif
#ifndef I2CFREQ_HZ
-#define I2CFREQ_HZ 4000000UL
+ #define I2CFREQ_HZ 4000000UL
#endif
TwoWire I2CSSD1306 = TwoWire(0);
-DspCore::DspCore(): Adafruit_SSD1306(128, ((DSP_MODEL==DSP_SSD1306)?64:32), &I2CSSD1306, I2C_RST, I2CFREQ_HZ) {
+DspCore::DspCore(): Adafruit_SSD1306(128, ((DSP_MODEL==DSP_SSD1306)?64:32), &I2CSSD1306, I2C_RST, I2CFREQ_HZ) { }
-}
+#include "tools/utf8RusGFX.h"
-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);
-#if DSP_MODEL==DSP_SSD1306
- setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT * 2);
- print("SETTINGS PAGE ON: ");
-#endif
- setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT);
- print("http://");
- print(WiFi.softAPIP().toString().c_str());
- print("/");
-}
-
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
I2CSSD1306.begin(I2C_SDA, I2C_SCL);
if (!begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
- maxcontrast = DSP_MODEL==DSP_SSD1306?0xCF:0x8F;
config.theme.background = TFT_BG;
- config.theme.meta = TFT_FG;
+ config.theme.meta = TFT_BG;
+ config.theme.clock = TFT_FG;
+ config.theme.weather = TFT_FG;
+ config.theme.metabg = TFT_FG;
+ config.theme.metafill = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
+ config.theme.ip = TFT_FG;
+ config.theme.vol = TFT_FG;
+ config.theme.bitrate = TFT_FG;
+ config.theme.digit = TFT_FG;
+ config.theme.buffer = TFT_FG;
+ config.theme.volbarout = TFT_FG;
+ config.theme.volbarin = TFT_FG;
+
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
cp437(true);
- fillScreen(TFT_BG);
flip();
invert();
setTextWrap(false);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
- fillSpaces = true;
}
-void DspCore::drawLogo() {
- clearDisplay();
+void DspCore::drawLogo(uint16_t top) {
#if DSP_MODEL==DSP_SSD1306
- drawBitmap(
- (width() - LOGO_WIDTH ) / 2,
- 8,
- logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
+ drawBitmap((width() - LOGO_WIDTH ) / 2, 8, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
#else
- setTextSize(2);
- centerText(utf8Rus("ёRadio", false), 0, TFT_FG, TFT_BG);
+ setTextSize(1);
+ setCursor((width() - 6*CHARWIDTH) / 2, 0);
+ setTextColor(TFT_FG, TFT_BG);
+ print(utf8Rus("ёRadio", false));
setTextSize(1);
#endif
display();
@@ -179,187 +88,114 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 2, PLMITEMS);
setTextSize((DSP_MODEL==DSP_SSD1306)?2:1);
- int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
- fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) + 1, swidth, PLMITEMHEIGHT, TFT_LOGO);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
setTextColor(TFT_FG, TFT_BG);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 2) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
+ fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
+void DspCore::clearDsp(bool black) {
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);
+uint8_t DspCore::_charWidth(unsigned char c){
+ return CHARWIDTH*clockTimeHeight;
}
-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;
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop, _oldtimewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
+ #if DSP_MODEL!=DSP_SSD1306x32
+ _timeleft = (width()/2 - _timewidth/2)+clockRightSpace;
+ setTextColor(config.theme.clock, config.theme.background);
+ #else
+ _timeleft = (width() - _timewidth)-clockRightSpace;
+ setTextColor(0, 1);
+ #endif
+ setTextSize(clockTimeHeight);
+
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
+ //setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
- setTextColor(fg, bg);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ }
+ _clockSeconds();
}
-void DspCore::set_Cursor(int16_t x, int16_t y) {
- setCursor(x, y);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop, _timewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
}
-void DspCore::printText(const char* txt) {
- print(txt);
-}
+void DspCore::startWrite(void) { }
+
+void DspCore::endWrite(void) { }
void DspCore::loop(bool force) {
- if (checkdelay(SCROLLTIME, loopdelay) || force) {
-#if DSP_MODEL==DSP_SSD1306x32
- if(fillSpaces) printClock(insideClc);
-#endif
- display();
- }
- yield();
+ display();
+ delay(5);
}
-boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
+
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
+}
+
void DspCore::flip(){
setRotation(config.store.flipscreen?2:0);
}
@@ -371,4 +207,31 @@ void DspCore::invert(){
void DspCore::sleep(void) { ssd1306_command(SSD1306_DISPLAYOFF); }
void DspCore::wake(void) { ssd1306_command(SSD1306_DISPLAYON); }
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_SSD1306::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_SSD1306::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setTextSize(2);
+}
+
#endif
diff --git a/yoRadio/src/displays/displaySSD1306.h b/yoRadio/src/displays/displaySSD1306.h
index 876c92e..9b4dcb1 100644
--- a/yoRadio/src/displays/displaySSD1306.h
+++ b/yoRadio/src/displays/displaySSD1306.h
@@ -1,98 +1,47 @@
#ifndef displaySSD1306_h
#define displaySSD1306_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
+#include "tools/l10n.h"
-#define TFT_LINEHGHT 8
-#define TFT_FRAMEWDT 0
-#define PLMITEMLENGHT 40
-#define DSP_CAN_SLEEP true
-#define DSP_OLED true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 2
-#define SCROLLTIME 35
-#endif
+#define DSP_OLED
-#if DSP_MODEL==DSP_SSD1306
-
-#define PLMITEMS 5
-#define PLMITEMHEIGHT 18
-#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT
-#define PLCURRENT_SIZE 2
-#define BOOTSTR_TOP1 64-TFT_LINEHGHT*2-5
-#define BOOTSTR_TOP2 64-TFT_LINEHGHT
-#define VOL_TOP 24
+typedef GFXcanvas1 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
+#if __has_include("conf/displaySSD1306conf_custom.h")
+ #include "conf/displaySSD1306conf_custom.h"
#else
-
-#define PLMITEMS 5
-#define PLMITEMHEIGHT 10
-#define TITLE_TOP2 TFT_FRAMEWDT + 2 * TFT_LINEHGHT
-#define PLCURRENT_SIZE 1
-#define META_SIZE 1
-#define TITLE_SIZE2 0
-#define TITLE_TOP1 TFT_FRAMEWDT + META_SIZE * TFT_LINEHGHT + 3
-#define TFT_FULLTIME 1
-#define BOOTSTR_TOP1 14
-#define BOOTSTR_TOP2 24
-#define CLOCK_SPACE 38
-#define VOL_SPACE 0
-#define VOL_TOP 16
-
+ #if DSP_MODEL==DSP_SSD1306
+ #include "conf/displaySSD1306conf.h"
+ #else
+ #include "conf/displaySSD1306x32conf.h"
+ #endif
#endif
+
+
class DspCore: public Adafruit_SSD1306 {
- 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 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(bool force=false);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- unsigned long loopdelay;
- uint8_t maxcontrast;
- char insideClc[10];
- boolean checkdelay(int m, unsigned long &tstamp);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
/*
- * TFT COLORS
+ * OLED COLORS
*/
-#define SILVER WHITE
-#define TFT_BG BLACK
-#define TFT_FG WHITE
-#define TFT_LOGO WHITE
+#define BOOT_PRG_COLOR WHITE
+#define BOOT_TXT_COLOR WHITE
+#define PINK WHITE
+#define SILVER WHITE
+#define TFT_BG BLACK
+#define TFT_FG WHITE
+#define TFT_LOGO WHITE
#endif
diff --git a/yoRadio/src/displays/displaySSD1327.cpp b/yoRadio/src/displays/displaySSD1327.cpp
index c3405a7..4ef9351 100644
--- a/yoRadio/src/displays/displaySSD1327.cpp
+++ b/yoRadio/src/displays/displaySSD1327.cpp
@@ -1,155 +1,65 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_SSD1327
#include "displaySSD1327.h"
#include
-#include "fonts/bootlogobw.h"
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "fonts/bootlogo40.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/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
+ #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 6000000UL
+ #define I2CFREQ_HZ 6000000UL
#endif
TwoWire tw = TwoWire(0);
-DspCore::DspCore(): Adafruit_SSD1327(128, 128, &tw, I2C_RST, I2CFREQ_HZ) {
+DspCore::DspCore(): Adafruit_SSD1327(128, 128, &tw, I2C_RST/*, I2CFREQ_HZ*/) {}
-}
+#include "tools/utf8RusGFX.h"
-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("/");
-}
#define CLR_ITEM1 0xA
#define CLR_ITEM2 0x8
#define CLR_ITEM3 0x5
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
tw.begin(I2C_SDA, I2C_SCL);
if (!begin(SCREEN_ADDRESS)) {
Serial.println(F("SSD1327 allocation failed"));
for (;;);
}
config.theme.background = TFT_BG;
- config.theme.meta = TFT_FG;
- config.theme.title1 = TFT_FG;
+ config.theme.meta = TFT_BG;
+ config.theme.metabg = TFT_LOGO;
+ config.theme.metafill = TFT_LOGO;
+ config.theme.title1 = TFT_LOGO;
config.theme.title2 = SILVER;
+ config.theme.clock = TFT_LOGO;
config.theme.rssi = TFT_FG;
config.theme.weather = ORANGE;
- config.theme.heap = SILVER;
config.theme.ip = SILVER;
+ config.theme.vol = SILVER;
+ config.theme.bitrate = TFT_LOGO;
+ config.theme.digit = TFT_LOGO;
+ config.theme.buffer = TFT_FG;
+ config.theme.volbarout = TFT_FG;
+ config.theme.volbarin = SILVER;
config.theme.playlist[0] = CLR_ITEM1;
config.theme.playlist[1] = CLR_ITEM2;
config.theme.playlist[2] = CLR_ITEM3;
config.theme.playlist[3] = CLR_ITEM3;
config.theme.playlist[4] = CLR_ITEM3;
cp437(true);
- fillScreen(TFT_BG);
flip();
invert();
setTextWrap(false);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
- setClockBounds();
}
-void DspCore::drawLogo() {
- drawGrayscaleBitmap((swidth - 99) / 2, 18, bootlogobw, 99, 64);
+void DspCore::drawLogo(uint16_t top) {
+ drawRGBBitmap((DSP_WIDTH - 62) / 2, top, bootlogo40, 62, 40);
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -158,8 +68,7 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
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);
@@ -168,252 +77,97 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT - 1, swidth, PLMITEMHEIGHT - 4, TFT_BG);
+ fillRect(0, yStart + i * PLMITEMHEIGHT - 1, width(), PLMITEMHEIGHT - 4, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
- //display();
}
-void DspCore::clearDsp() {
- fillScreen(TFT_BG);
- //clearDisplay();
+void DspCore::clearDsp(bool black) {
+ fillScreen(black?0:config.theme.background);
}
-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);
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-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;
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI28pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop, swidth, textheight, bg);
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = (width()/2 - _timewidth/2)+clockRightSpace;
+ setTextSize(1);
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);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
setFont();
- //display();*/
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-byte DspCore::getPw(uint16_t ncwidth){
- byte pw = 6;
- if(ncwidth<35) pw = 7;
- if(ncwidth<20) pw = 8;
- return pw;
-}
-
-void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){
- char timeBuf[50] = { 0 };
- char tmpBuf[4] = { 0 };
- uint16_t ncwidth, ncheight;
- strftime(timeBuf, sizeof(timeBuf), "%H %M", &timeinfo);
- setTextSize(1);
- setFont(&DS_DIGI28pt7b);
- if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
- getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot);
- setCursor((swidth - wot) / 2 - 4, clockY+28+6);
- setTextColor(TFT_BG);
- print(oldTimeBuf);
- dot = (swidth - wot) / 2 - 4;
- /* dots */
- strlcpy(tmpBuf, oldTimeBuf, 3);
- getTextBounds(tmpBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- dot = dot + ncwidth + getPw(ncwidth);
- setCursor(dot, clockY+28+6);
- print(":");
- /* dots */
-
- strlcpy(oldTimeBuf, timeBuf, 20);
- setTextSize(1);
- getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- setTextColor(TFT_LOGO);
- setCursor((swidth - ncwidth) / 2 - 4, clockY+28+6);
- dot = (swidth - ncwidth) / 2 - 4;
- setTextSize(1);
- print(timeBuf);
- /* dots */
- strftime(timeBuf, sizeof(timeBuf), "%H", &timeinfo);
- getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- dot = dot + ncwidth + getPw(ncwidth);
- /* dots */
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
}
- setCursor(dot, clockY+28+6);
- setTextColor(dots?TFT_BG:TFT_LOGO);
- print(":");
- setFont();
- display();
+ //_clockSeconds();
}
-void DspCore::drawVolumeBar(bool withNumber) {
- //if (withNumber) delay(150); /* buuuut iiiiit's toooooo faaaast 0000__oooo !!111 ommm____nomm____nomm */
- 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);
- //fillRect(24, 48, 80, hv + 3, TFT_BG);
- setCursor((swidth - wv) / 2, 48 + hv);
- print(volstr);
- setFont();
- }
- //display();
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth, clockTimeHeight, config.theme.background);
}
-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();
- //display();
-}
+void DspCore::startWrite(void) { }
-void DspCore::frameTitle(const char* str) {
- setTextSize(2);
- centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
- //display();
-}
-
-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);
- //display();
-}
+void DspCore::endWrite(void) { }
void DspCore::loop(bool force) {
- if (checkdelay(LOOP_DELAY, loopdelay) || force) {
- display();
- }
- yield();
+ display();
+ delay(5);
}
-boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
- if (millis() - tstamp > m) {
- tstamp = millis();
- return true;
- } else {
- return false;
- }
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
+
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
+}
+
void DspCore::flip(){
if(ROTATE_90){
setRotation(config.store.flipscreen?3:1);
@@ -429,4 +183,32 @@ void DspCore::invert(){
void DspCore::sleep(void) { oled_command(SSD1327_DISPLAYOFF); }
void DspCore::wake(void) { oled_command(SSD1327_DISPLAYON); }
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_SSD1327::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_SSD1327::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setFont(&DS_DIGI28pt7b);
+ setTextSize(1);
+}
+
#endif
diff --git a/yoRadio/src/displays/displaySSD1327.h b/yoRadio/src/displays/displaySSD1327.h
index f4c046b..c027902 100644
--- a/yoRadio/src/displays/displaySSD1327.h
+++ b/yoRadio/src/displays/displaySSD1327.h
@@ -1,75 +1,30 @@
#ifndef displaySSD1327_h
#define displaySSD1327_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
-#include "fonts/DS_DIGI28pt7b.h"
+#include "fonts/DS_DIGI28pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
+#include "tools/l10n.h"
-#define WEATHER_READY 1
-#define DSP_CAN_SLEEP true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 10
-#define TFT_FRAMEWDT 4
+#define DSP_OLED
-#define PLMITEMS 7
-#define PLMITEMLENGHT 40
-#define PLMITEMHEIGHT 22
-#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT
+typedef GFXcanvas1 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-
-#define SCROLLDELTA 2
-#define SCROLLTIME 30
-#define LOOP_DELAY 33
+#if __has_include("conf/displaySSD1327conf_custom.h")
+ #include "conf/displaySSD1327conf_custom.h"
+#else
+ #include "conf/displaySSD1327conf.h"
#endif
-#define TFT_FULLTIME 1
-
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 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(bool force=false);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- int16_t x, y;
- uint16_t cwidth, cheight;
- unsigned long loopdelay;
- char oldTimeBuf[20];
- uint16_t wot, hot, dot;
- boolean checkdelay(int m, unsigned long &tstamp);
- void setClockBounds();
- byte getPw(uint16_t ncwidth);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
@@ -83,12 +38,14 @@ extern DspCore dsp;
/*
* TFT COLORS
*/
+#define BOOT_PRG_COLOR 0x07
+#define BOOT_TXT_COLOR 0x3f
#define DARK_GRAY 0x01
#define SILVER 0x07
#define TFT_BG 0x00
#define TFT_FG 0x08
#define TFT_LOGO 0x3f
-#define ORANGE 0x02
+#define ORANGE 0x05
#define PINK 0x02
#endif
diff --git a/yoRadio/src/displays/displayST7735.cpp b/yoRadio/src/displays/displayST7735.cpp
index 2740e9d..0179604 100644
--- a/yoRadio/src/displays/displayST7735.cpp
+++ b/yoRadio/src/displays/displayST7735.cpp
@@ -1,29 +1,16 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_ST7735
#include "displayST7735.h"
-#include
-#ifdef DSP_MINI
#include "fonts/bootlogo40.h"
-#else
-#include "fonts/bootlogo.h"
-#endif
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/network.h"
#ifndef DEF_SPI_FREQ
#define DEF_SPI_FREQ 26000000UL /* set it to 0 for system default */
#endif
-#if DTYPE==INITR_BLACKTAB
-#define CLOCK_DELTA 12
-#elif DTYPE==INITR_MINI160x80
-#define CLOCK_DELTA 16
-#else
-#define CLOCK_DELTA 0
-#endif
-
#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY)
#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl)
@@ -31,119 +18,23 @@ DspCore::DspCore(): Adafruit_ST7735(&SPI, 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;
-}
+#include "tools/utf8RusGFX.h"
-void DspCore::apScreen() {
- setTextSize(1);
- setTextColor(config.theme.title1, config.theme.background);
- 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(config.theme.title2, config.theme.background);
- setCursor(TFT_FRAMEWDT, 107);
- print("SETTINGS PAGE ON: ");
- setCursor(TFT_FRAMEWDT, 117);
- print("http://");
- print(WiFi.softAPIP().toString().c_str());
- print("/");
-}
-
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
initR(DTYPE);
if(DEF_SPI_FREQ > 0) setSPISpeed(DEF_SPI_FREQ);
cp437(true);
invert();
-// fillScreen(0x0000);
flip();
setTextWrap(false);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
- setClockBounds();
}
-void DspCore::drawLogo() {
- fillScreen(0x0000);
+void DspCore::drawLogo(uint16_t top) {
#ifdef DSP_MINI
- drawRGBBitmap((swidth - 62) / 2, 5, bootlogo40, 62, 40);
+ drawRGBBitmap((DSP_WIDTH - 62) / 2, 5, bootlogo40, 62, 40);
#else
- drawRGBBitmap((swidth - 99) / 2, 18, bootlogo2, 99, 64);
+ //drawRGBBitmap((DSP_WIDTH - 99) / 2, 18, bootlogo2, 99, 64);
+ drawRGBBitmap((DSP_WIDTH - 62) / 2, 34, bootlogo40, 62, 40);
#endif
}
@@ -153,8 +44,7 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
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);
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
for (byte i = 0; i < PLMITEMS; i++) {
if (abs(i - 3) == 3) setTextColor(config.theme.playlist[2], config.theme.background);
if (abs(i - 3) == 2) setTextColor(config.theme.playlist[1], config.theme.background);
@@ -163,209 +53,77 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT - 1, swidth, PLMITEMHEIGHT - 4, config.theme.background);
+ fillRect(0, yStart + i * PLMITEMHEIGHT - 1, DSP_WIDTH, PLMITEMHEIGHT - 4, config.theme.background);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
- fillScreen(config.theme.background);
+void DspCore::clearDsp(bool black) {
+ fillScreen(black?0:config.theme.background);
}
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
- fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-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;
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI28pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- fillRect(0, texttop-2, swidth, textheight+3, bg);
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = (width()/2 - _timewidth/2)+clockRightSpace;
setTextSize(1);
- setFont(&DS_DIGI28pt7b);
- if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
- getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot);
- setCursor((swidth - wot) / 2 - 4 + clockdelta, clockY+28+6);
- setTextColor(config.theme.background);
- print(oldTimeBuf);
- dot = (swidth - wot) / 2 - 4 + clockdelta;
- /* dots */
- strlcpy(tmpBuf, oldTimeBuf, 3);
- getTextBounds(tmpBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- dot = dot + ncwidth + getPw(ncwidth);
- setCursor(dot, clockY+28+6);
- print(":");
- /* dots */
-
- strlcpy(oldTimeBuf, timeBuf, 20);
- setTextSize(1);
- getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- setTextColor(config.theme.clock);
- setCursor((swidth - ncwidth) / 2 - 4 + clockdelta, clockY+28+6);
- dot = (swidth - ncwidth) / 2 - 4 + clockdelta;
- setTextSize(1);
- print(timeBuf);
- /* dots */
- strftime(timeBuf, sizeof(timeBuf), "%H", &timeinfo);
- getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
- dot = dot + ncwidth + getPw(ncwidth);
- /* dots */
- }
- setCursor(dot, clockY+28+6);
- setTextColor(dots?config.theme.background:config.theme.clock);
- print(":");
- setFont();
- yield();
-}
-#ifdef DSP_MINI
-#define VTOP TITLE_TOP1+6
-#else
-#define VTOP 48
-#endif
-void DspCore::drawVolumeBar(bool withNumber) {
-#ifdef DSP_MINI
- int16_t vTop = sheight - TFT_FRAMEWDT - 2;
- int16_t vWidth = swidth - TFT_FRAMEWDT * 2;
- uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth);
- fillRect(TFT_FRAMEWDT, vTop, vWidth, 2, config.theme.background);
- fillRect(TFT_FRAMEWDT, vTop, ww, 2, config.theme.volbarin);
-#else
- int16_t vTop = sheight - TFT_FRAMEWDT - 6;
- int16_t vWidth = swidth - TFT_FRAMEWDT * 2;
- uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
- fillRect(TFT_FRAMEWDT, vTop - 2 + 3, vWidth, 5, config.theme.background);
- fillRect(TFT_FRAMEWDT + 1, vTop - 1 + 3, ww, 3, config.theme.volbarin);
- drawRect(TFT_FRAMEWDT, vTop - 2 + 3, vWidth, 5, config.theme.volbarout);
-#endif
- if (withNumber) {
- setTextSize(1);
- setTextColor(config.theme.digit);
- 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, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
- setCursor((swidth - wv) / 2, VTOP + hv);
- print(volstr);
- setFont();
- }
-}
-
-void DspCore::drawNextStationNum(uint16_t num) {
- setTextSize(1);
- setTextColor(config.theme.digit);
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, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
- setCursor((swidth - wv) / 2, VTOP + hv);
- print(numstr);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
}
-void DspCore::frameTitle(const char* str) {
- setTextSize(2);
- centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ }
+ _clockSeconds();
}
-void DspCore::rssi(const char* str) {
- int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
- setTextSize(1);
- rightText(str, vTop, config.theme.rssi, config.theme.background);
-}
-
-void DspCore::ip(const char* str) {
- int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
- setTextSize(1);
- setTextColor(config.theme.ip, config.theme.background);
- setCursor(4, vTop);
- print(str);
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth, clockTimeHeight, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -378,24 +136,15 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
-void DspCore::set_TextSize(uint8_t s) {
- setTextSize(s);
+void DspCore::loop(bool force) { }
+
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
}
-void DspCore::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(bool force) {
-
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
}
void DspCore::flip(){
@@ -417,4 +166,32 @@ void DspCore::wake(void) {
enableDisplay(true); delay(150); enableSleep(false); delay(150);
}
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_ST7735::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_ST7735::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setFont(&DS_DIGI28pt7b);
+ setTextSize(1);
+}
+
#endif
diff --git a/yoRadio/src/displays/displayST7735.h b/yoRadio/src/displays/displayST7735.h
index 66d68b6..9e59e4f 100644
--- a/yoRadio/src/displays/displayST7735.h
+++ b/yoRadio/src/displays/displayST7735.h
@@ -1,89 +1,39 @@
#ifndef displayST7735_h
#define displayST7735_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
-#include "fonts/DS_DIGI28pt7b.h"
+#include "fonts/DS_DIGI28pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
+#include "tools/l10n.h"
-#define VU_READY 1
-#define WEATHER_READY 1
-#define DSP_CAN_SLEEP true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 10
-#if DTYPE==INITR_MINI160x80
-#define TFT_FRAMEWDT 0
-#define DSP_MINI
+typedef GFXcanvas16 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
+
+
+#if __has_include("conf/displayST7735conf_custom.h")
+ #include "conf/displayST7735conf_custom.h"
#else
-#define TFT_FRAMEWDT 4
+ #if DTYPE==INITR_MINI160x80
+ #include "conf/displayST7735_miniconf.h"
+ #elif DTYPE==INITR_144GREENTAB
+ #include "conf/displayST7735_144conf.h"
+ #else
+ #include "conf/displayST7735_blackconf.h"
+ #endif
#endif
-#define PLMITEMS 7
-#define PLMITEMLENGHT 40
-#if DTYPE==INITR_MINI160x80
-#define PLMITEMHEIGHT 19
-#else
-#define PLMITEMHEIGHT 21
-#endif
-#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT
-
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 2
-#define SCROLLTIME 30
-#endif
-
-#define TFT_FULLTIME 1
-
-#if DTYPE==INITR_MINI160x80
-#define TITLE_SIZE2 0
-#define TITLE_TOP1 TFT_FRAMEWDT + 2 * TFT_LINEHGHT-3
-#define BOOTSTR_TOP1 50
-#define BOOTSTR_TOP2 65
-#endif
+#define BOOT_PRG_COLOR 0xE68B
+#define BOOT_TXT_COLOR 0xFFFF
+#define PINK 0xF97F
class DspCore: public Adafruit_ST7735 {
- 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(bool force=false);
- virtual void startWrite(void);
- virtual void endWrite(void);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- char oldTimeBuf[20];
- uint16_t wot, hot, dot;
- int16_t x, y;
- uint16_t cwidth, cheight;
- void setClockBounds();
- byte getPw(uint16_t ncwidth);
+#include "tools/commongfx.h"
};
extern DspCore dsp;
diff --git a/yoRadio/src/displays/displayST7789.cpp b/yoRadio/src/displays/displayST7789.cpp
index e9b6ed3..8da1a0b 100644
--- a/yoRadio/src/displays/displayST7789.cpp
+++ b/yoRadio/src/displays/displayST7789.cpp
@@ -1,138 +1,36 @@
-#include "../../options.h"
+#include "../core/options.h"
#if DSP_MODEL==DSP_ST7789 || DSP_MODEL==DSP_ST7789_240
#include "displayST7789.h"
-#include
+//#include
#include "fonts/bootlogo.h"
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/config.h"
+#include "../core/network.h"
#ifndef DEF_SPI_FREQ
-#define DEF_SPI_FREQ 40000000UL /* set it to 0 for system default */
+ #define DEF_SPI_FREQ 40000000UL /* set it to 0 for system default */
#endif
-const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
-const char *mnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"};
#define TAKE_MUTEX() if(player.mutex_pl) xSemaphoreTake(player.mutex_pl, portMAX_DELAY)
#define GIVE_MUTEX() if(player.mutex_pl) xSemaphoreGive(player.mutex_pl)
-DspCore::DspCore(): Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST) {
+DspCore::DspCore(): Adafruit_ST7789(TFT_CS, TFT_DC, TFT_RST) {}
-}
+#include "tools/utf8RusGFX.h"
-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(config.theme.title1, config.theme.background);
- setCursor(TFT_FRAMEWDT, TITLE_TOP1);
- print("AP NAME: ");
- print(apSsid);
- setCursor(TFT_FRAMEWDT, TITLE_TOP2);
- print("PASSWORD: ");
- print(apPassword);
- setTextColor(config.theme.title2, config.theme.background);
- 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, config.theme.div);
-}
-
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
+void DspCore::initDisplay() {
init(240,(DSP_MODEL==DSP_ST7789)?320:240);
if(DEF_SPI_FREQ > 0) setSPISpeed(DEF_SPI_FREQ);
invert();
cp437(true);
-// fillScreen(0x0000);
flip();
setTextWrap(false);
setTextSize(1);
- screenwidth = width();
- screenheight = height();
- swidth = screenwidth;
- sheight = screenheight;
+ fillScreen(0x0000);
}
-void DspCore::drawLogo() {
- fillScreen(0x0000);
- drawRGBBitmap((swidth - 99) / 2, (sheight-64)/2 - TFT_LINEHGHT*2, bootlogo2, 99, 64);
-}
+void DspCore::drawLogo(uint16_t top) { drawRGBBitmap((width() - 99) / 2, top, bootlogo2, 99, 64); }
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
for (byte i = 0; i < PLMITEMS; i++) {
@@ -140,216 +38,108 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 5, PLMITEMS);
setTextSize(2);
- int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
+ int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 5) {
- //fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) - 1, swidth, PLMITEMHEIGHT + 2, TFT_LOGO);
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setTextColor(config.theme.playlist[abs(i - 5)-1], config.theme.background);
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
- fillRect(0, yStart + i * PLMITEMHEIGHT - 1, swidth, PLMITEMHEIGHT - 2, config.theme.background);
+ fillRect(0, yStart + i * PLMITEMHEIGHT - 1, width(), PLMITEMHEIGHT - 2, config.theme.background);
print(utf8Rus(plMenu[i], true));
}
}
}
-void DspCore::clearDsp() {
- fillScreen(config.theme.background);
+void DspCore::clearDsp(bool black) { fillScreen(black?0:config.theme.background); }
+
+GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
+ return gfxFont->glyph + c;
}
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
- if (TFT_FRAMEWDT==0) return;
- fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
- fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
+uint8_t DspCore::_charWidth(unsigned char c){
+ GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI42pt7b, c - 0x20);
+ return pgm_read_byte(&glyph->xAdvance);
}
-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;
+uint16_t DspCore::textWidth(const char *txt){
+ uint16_t w = 0, l=strlen(txt);
+ for(uint16_t c=0;c240?46:34);
- fillRect(swidth-TFT_FRAMEWDT-clwidth, cltop-hot, clwidth, hot+3, config.theme.background);
- 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(config.theme.clock, config.theme.background);
- setCursor(clleft, cltop);
- setTextSize(1);
- print(timeBuf);
-
- setFont();
- setTextSize(3);
- setTextColor(config.theme.dow, config.theme.background);
- 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);
- uint16_t wdate, hdate;
- getTextBounds(timeBuf, 0, 0, &x1, &y1, &wdate, &hdate);
- fillRect(swidth - wdate - TFT_FRAMEWDT-20, cltop+10, wdate+20, hdate, config.theme.background);
- rightText(utf8Rus(timeBuf,true), cltop+10, config.theme.date, config.theme.background, false, swidth>240?12:0);
- drawFastVLine(clleft+wot+clsp/2+3, cltop-hot, hot+3, config.theme.div);
- drawFastHLine(clleft+wot+clsp/2+3, cltop-hot+29, 42, config.theme.div);
-
- drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, config.theme.div);
- }
+void DspCore::_clockSeconds(){
setTextSize(3);
setTextColor(config.theme.seconds, config.theme.background);
- 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;
- uint16_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
- fillRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, config.theme.background);
- fillRect(TFT_FRAMEWDT + 1, vTop - 1, ww, 5, config.theme.volbarin);
- drawRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, config.theme.volbarout);
- if(swidth>240){
- char buf[20];
- sprintf(buf, "VOL %d", config.store.volume);
- setTextSize(1);
- centerText(buf, volTop, config.theme.vol, config.theme.background);
- }
- if (withNumber) {
- setTextSize(1);
- setTextColor(config.theme.digit);
- 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, config.theme.background);
- setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
- print(volstr);
- setFont();
- }
- yield();
-}
-
-void DspCore::drawNextStationNum(uint16_t num) {
+ setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-clockTimeHeight+1);
+ sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
+ print(_bufforseconds); /* print seconds */
setTextSize(1);
- setTextColor(config.theme.digit);
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, config.theme.background);
- setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
- print(numstr);
+ setTextColor((network.timeinfo.tm_sec % 2 == 0) ? config.theme.clock : config.theme.background, config.theme.background);
+ setCursor(_timeleft+_dotsLeft, clockTop);
+ print(":"); /* print dots */
setFont();
}
-void DspCore::frameTitle(const char* str) {
- setTextSize(META_SIZE);
- centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
- drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, config.theme.div);
+void DspCore::_clockDate(){
+ if(_olddateleft>0)
+ dsp.fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background);
+ setTextColor(config.theme.date, config.theme.background);
+ setCursor(_dateleft, clockTop+10);
+ print(_dateBuf); /* print date */
+ strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf));
+ _olddatewidth = _datewidth;
+ _olddateleft = _dateleft;
+ setTextSize(3);
+ setTextColor(config.theme.dow, config.theme.background);
+ setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-CHARHEIGHT*3+4);
+ print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */
}
-void DspCore::rssi(const char* str) {
- int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
- char buf[20];
- sprintf(buf, "RSSI:%s", str);
+void DspCore::_clockTime(){
+ if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
+ _timeleft = width()-clockRightSpace-CHARWIDTH*3*2-24-_timewidth;
setTextSize(1);
- rightText(buf, vTop, config.theme.rssi, config.theme.background);
+ setFont(&DS_DIGI42pt7b);
+ setTextColor(config.theme.clock, config.theme.background);
+ setCursor(_timeleft, clockTop);
+ print(_timeBuf);
+ setFont();
+ strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
+ _oldtimewidth = _timewidth;
+ _oldtimeleft = _timeleft;
+ drawFastVLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/
+ drawFastHLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight+29, 44, config.theme.div); /*divider hor*/
+ sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900);
+ strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf));
+ _datewidth = strlen(_dateBuf) * CHARWIDTH;
+ _dateleft = width() - 8 - clockRightSpace - _datewidth;
}
-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(config.theme.ip, config.theme.background);
- setCursor(TFT_FRAMEWDT, vTop);
- print(buf);
+void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
+ clockTop = top;
+ clockRightSpace = rightspace;
+ clockTimeHeight = timeheight;
+ strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
+ if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
+ _getTimeBounds();
+ _clockTime();
+ if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate();
+ }
+ _clockSeconds();
+}
+
+void DspCore::clearClock(){
+ dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -362,25 +152,19 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
-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(bool force) {
}
+
+void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
+ width = textsize * CHARWIDTH;
+ height = textsize * CHARHEIGHT;
+}
+
+void DspCore::setTextSize(uint8_t s){
+ Adafruit_GFX::setTextSize(s);
+}
+
void DspCore::flip(){
#if DSP_MODEL==DSP_ST7789
setRotation(config.store.flipscreen?3:1);
@@ -405,4 +189,32 @@ void DspCore::wake(void) {
enableDisplay(true); delay(150); enableSleep(false); delay(150);
}
+
+void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_ST7789::writePixel(x, y, color);
+}
+
+void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
+ if(_clipping){
+ if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
+ }
+ Adafruit_ST7789::writeFillRect(x, y, w, h, color);
+}
+
+void DspCore::setClipping(clipArea ca){
+ _cliparea = ca;
+ _clipping = true;
+}
+
+void DspCore::clearClipping(){
+ _clipping = false;
+}
+
+void DspCore::setNumFont(){
+ setFont(&DS_DIGI42pt7b);
+ setTextSize(1);
+}
#endif
diff --git a/yoRadio/src/displays/displayST7789.h b/yoRadio/src/displays/displayST7789.h
index f6a9873..795635b 100644
--- a/yoRadio/src/displays/displayST7789.h
+++ b/yoRadio/src/displays/displayST7789.h
@@ -1,75 +1,36 @@
#ifndef displayST7789_h
#define displayST7789_h
+#include "../core/options.h"
#include "Arduino.h"
#include
#include
-// https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
-#include "fonts/DS_DIGI42pt7b.h"
+#include "fonts/DS_DIGI42pt7b.h" // https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
+#include "tools/l10n.h"
-#define VU_READY 1
-#define WEATHER_READY 1
-#define DSP_CAN_SLEEP true
+#define CHARWIDTH 6
+#define CHARHEIGHT 8
-#define TFT_LINEHGHT 10
-#define TFT_FRAMEWDT 8
-#define META_SIZE 3
-#define TITLE_SIZE1 2
-#define TITLE_SIZE2 2
+typedef GFXcanvas16 Canvas;
+#include "widgets/widgets.h"
+#include "widgets/pages.h"
-#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
-#define SCROLLDELTA 4
-#define SCROLLTIME 30
+#if __has_include("conf/displayST7789conf_custom.h")
+ #include "conf/displayST7789conf_custom.h"
+#else
+ #if DSP_MODEL==DSP_ST7789
+ #include "conf/displayST7789conf.h"
+ #else
+ #include "conf/displayST7789_240conf.h"
+ #endif
#endif
-#define PLMITEMS 11
-#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 BOOT_PRG_COLOR 0xE68B
+#define BOOT_TXT_COLOR 0xFFFF
+#define PINK 0xF97F
class DspCore: public Adafruit_ST7789 {
- 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(bool force=false);
- virtual void startWrite(void);
- virtual void endWrite(void);
- void flip();
- void invert();
- void sleep();
- void wake();
- private:
- uint16_t swidth, sheight;
- char oldTimeBuf[20];
- uint16_t wot, hot;
-
+#include "tools/commongfx.h"
};
extern DspCore dsp;
diff --git a/yoRadio/src/displays/dspcore.h b/yoRadio/src/displays/dspcore.h
new file mode 100644
index 0000000..18b7fa6
--- /dev/null
+++ b/yoRadio/src/displays/dspcore.h
@@ -0,0 +1,41 @@
+#ifndef dspcore_h
+#define dspcore_h
+#include "../core/options.h"
+
+#if DSP_MODEL==DSP_DUMMY
+ #define DUMMYDISPLAY
+ #define DSP_NOT_FLIPPED
+ #include "tools/l10n.h"
+#elif DSP_MODEL==DSP_ST7735
+ #include "displayST7735.h"
+#elif DSP_MODEL==DSP_SSD1306 || DSP_MODEL==DSP_SSD1306x32
+ #include "displaySSD1306.h"
+#elif DSP_MODEL==DSP_NOKIA5110
+ #include "displayN5110.h"
+#elif DSP_MODEL==DSP_ST7789 || DSP_MODEL==DSP_ST7789_240
+ #include "displayST7789.h"
+#elif DSP_MODEL==DSP_SH1106
+ #include "displaySH1106.h"
+#elif DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_2004I2C
+ #include "displayLC1602.h"
+#elif DSP_MODEL==DSP_SSD1327
+ #include "displaySSD1327.h"
+#elif DSP_MODEL==DSP_ILI9341
+ #include "displayILI9341.h"
+#elif DSP_MODEL==DSP_SSD1305 || DSP_MODEL==DSP_SSD1305I2C
+ #include "displaySSD1305.h"
+#elif DSP_MODEL==DSP_SH1107
+ #include "displaySH1106.h"
+#elif DSP_MODEL==DSP_1602 || DSP_MODEL==DSP_2004
+ #include "displayLC1602.h"
+#elif DSP_MODEL==DSP_GC9106
+ #include "displayGC9106.h"
+#elif DSP_MODEL==DSP_CUSTOM
+ #include "displayCustom.h"
+#elif DSP_MODEL==DSP_ILI9225
+ #include "displayILI9225.h"
+#endif
+
+//extern DspCore dsp;
+
+#endif
diff --git a/yoRadio/src/displays/fonts/DS_DIGI15pt7b.h b/yoRadio/src/displays/fonts/DS_DIGI15pt7b.h
index 2bfba50..bc3f4b2 100644
--- a/yoRadio/src/displays/fonts/DS_DIGI15pt7b.h
+++ b/yoRadio/src/displays/fonts/DS_DIGI15pt7b.h
@@ -16,7 +16,7 @@ const uint8_t DS_DIGI15pt7bBitmaps[] PROGMEM = {
0xF8, 0x3F, 0x07, 0xDF, 0x77, 0xF5, 0xFF, 0x00, 0x7F, 0xC7, 0xF4, 0x7D,
0x80, 0x70, 0x0E, 0x01, 0xC0, 0x38, 0x03, 0x00, 0x20, 0x00, 0x00, 0x80,
0x30, 0x0E, 0x01, 0xC0, 0x38, 0x07, 0x00, 0x60, 0x04, 0x00, 0x00, 0x7F,
- 0xD7, 0xF7, 0x7D, 0xF0, 0xFE, 0x0F, 0xC1, 0xF8, 0x3E, 0x03, 0xBF, 0xAF,
+ 0xD7, 0xF7, 0x7D, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3E, 0x03, 0xBF, 0xAF,
0xFA, 0xFE, 0xE0, 0x3E, 0x0F, 0xC1, 0xF8, 0x3F, 0x07, 0xCE, 0x77, 0xF5,
0xFF, 0x00, 0x7F, 0xD7, 0xF7, 0x7D, 0xF0, 0x7E, 0x0F, 0xC1, 0xF8, 0x3E,
0x03, 0xBF, 0xAF, 0xF8, 0xFE, 0x80, 0x30, 0x0E, 0x01, 0xC0, 0x38, 0x07,
@@ -54,7 +54,7 @@ const GFXglyph DS_DIGI15pt7bGlyphs[] PROGMEM = {
};
const GFXfont DS_DIGI15pt7b PROGMEM = {
- (uint8_t *)DS_DIGI15pt7bBitmaps,
- (GFXglyph *)DS_DIGI15pt7bGlyphs, 0x20, 0x3A, 29 };
+ (uint8_t *)DS_DIGI15pt7bBitmaps,
+ (GFXglyph *)DS_DIGI15pt7bGlyphs, 0x20, 0x3A, 29 };
// Approx. 664 bytes
diff --git a/yoRadio/src/displays/fonts/DS_DIGI28pt7b.h b/yoRadio/src/displays/fonts/DS_DIGI28pt7b.h
index f29219d..d7e0cd6 100644
--- a/yoRadio/src/displays/fonts/DS_DIGI28pt7b.h
+++ b/yoRadio/src/displays/fonts/DS_DIGI28pt7b.h
@@ -92,16 +92,16 @@ const GFXglyph DS_DIGI28pt7bGlyphs[] PROGMEM = {
{ 0, 0, 0, 0, 0, 0 }, // 0x2E '.'
{ 0, 0, 0, 0, 0, 0 }, // 0x2F '/'
{ 20, 21, 35, 27, 3, -34 }, // 0x30 '0'
- { 113, 4, 35, 14, 5, -34 }, // 0x31 '1'
+ { 113, 4, 35, 12, 5, -34 }, // 0x31 '1'
{ 131, 21, 35, 27, 3, -34 }, // 0x32 '2'
- { 224, 20, 35, 27, 4, -34 }, // 0x33 '3'
+ { 224, 20, 35, 26, 3, -34 }, // 0x33 '3'
{ 312, 21, 34, 27, 3, -34 }, // 0x34 '4'
{ 402, 21, 35, 27, 3, -34 }, // 0x35 '5'
{ 495, 21, 35, 27, 3, -34 }, // 0x36 '6'
- { 588, 20, 34, 27, 4, -34 }, // 0x37 '7'
+ { 588, 20, 34, 26, 3, -34 }, // 0x37 '7'
{ 673, 21, 35, 27, 3, -34 }, // 0x38 '8'
{ 766, 21, 35, 27, 3, -34 }, // 0x39 '9'
- { 859, 4, 29, 12, 4, -28 } // 0x3A ':'
+ { 859, 4, 29, 11, 4, -28 } // 0x3A ':'
};
const GFXfont DS_DIGI28pt7b PROGMEM = {
diff --git a/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h b/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h
index a57b3a6..8887df9 100644
--- a/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h
+++ b/yoRadio/src/displays/fonts/DS_DIGI42pt7b.h
@@ -163,7 +163,7 @@ const uint8_t DS_DIGI42pt7bBitmaps[] PROGMEM = {
};
const GFXglyph DS_DIGI42pt7bGlyphs[] PROGMEM = {
- { 0, 0, 0, 19, 0, 1 }, // 0x20 ' '
+ { 0, 0, 0, 15, 0, 1 }, // 0x20 ' '
{ 0, 0, 0, 0, 0, 0 }, // 0x21 '!'
{ 0, 0, 0, 0, 0, 0 }, // 0x22 '"'
{ 0, 0, 0, 0, 0, 0 }, // 0x23 '#'
@@ -180,7 +180,7 @@ const GFXglyph DS_DIGI42pt7bGlyphs[] PROGMEM = {
{ 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'
+ { 247, 6, 51, 15, 4, -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'
diff --git a/yoRadio/src/displays/fonts/TinyFont5.h b/yoRadio/src/displays/fonts/TinyFont5.h
index 17343de..a0abff8 100644
--- a/yoRadio/src/displays/fonts/TinyFont5.h
+++ b/yoRadio/src/displays/fonts/TinyFont5.h
@@ -2,50 +2,64 @@ const uint8_t TinyFont5_Bitmaps[] PROGMEM = {
0x00, 0x00, 0x00, 0xE8, 0x00, 0xB4, 0x00, 0x57, 0xD5, 0xF5, 0x00, 0x7A,
0x65, 0xE0, 0xA5, 0x4A, 0x00, 0x33, 0xE9, 0x70, 0xC0, 0x6A, 0x40, 0x95,
0x80, 0xAA, 0x80, 0x5D, 0x00, 0x60, 0xE0, 0x80, 0x5E, 0x80, 0x69, 0x99,
- 0x60, 0x59, 0x2E, 0x00, 0x69, 0x24, 0xF0, 0x69, 0x29, 0x60, 0x99, 0xF1,
- 0x10, 0xF8, 0xE1, 0xE0, 0x68, 0xE9, 0x60, 0xF1, 0x24, 0x40, 0x69, 0x69,
- 0x60, 0x69, 0x71, 0x60, 0xA0, 0x46, 0x64, 0x00, 0xE3, 0x80, 0x98, 0x00,
- 0x69, 0x20, 0x20, 0x6B, 0xB8, 0x70, 0x69, 0x9F, 0x90, 0xE9, 0xE9, 0xE0,
+ 0x60, 0x59, 0x2E, 0x00, 0xF1, 0xF8, 0xF0, 0xF1, 0x61, 0xF0, 0x99, 0xF1,
+ 0x10, 0xF8, 0xF1, 0xF0, 0xF8, 0xF9, 0xF0, 0xF1, 0x24, 0x40, 0xF9, 0x69,
+ 0xF0, 0xF9, 0xF1, 0xF0, 0xA0, 0x46, 0x64, 0x00, 0xE3, 0x80, 0x98, 0x00,
+ 0xF9, 0x30, 0x20, 0x6B, 0xB8, 0x70, 0x69, 0x9F, 0x90, 0xE9, 0xE9, 0xE0,
0x69, 0x89, 0x60, 0xE9, 0x99, 0xE0, 0xF8, 0xC8, 0xF0, 0xF8, 0xC8, 0x80,
0x69, 0x8B, 0x60, 0x99, 0xF9, 0x90, 0xF8, 0x00, 0x11, 0x19, 0x60, 0x99,
0xE9, 0x90, 0x88, 0x88, 0xF0, 0x8C, 0x63, 0xBA, 0x80, 0x99, 0xDB, 0x90,
0x69, 0x99, 0x60, 0xE9, 0x9E, 0x80, 0x64, 0xA5, 0x26, 0x80, 0xE9, 0x9E,
- 0x90, 0x68, 0x61, 0xE0, 0xE9, 0x24, 0x00, 0x99, 0x99, 0x60, 0x8C, 0x54,
+ 0x90, 0xE8, 0xF1, 0xF0, 0xE9, 0x24, 0x00, 0x99, 0x99, 0x60, 0x8C, 0x54,
0xA2, 0x00, 0x8C, 0x6A, 0xE5, 0x00, 0x99, 0x69, 0x90, 0xB6, 0xA4, 0x00,
0xF1, 0x68, 0xF0, 0xEA, 0xC0, 0xAD, 0x40, 0xD5, 0xC0, 0x54, 0x00, 0xE0,
- 0x90, 0x17, 0x99, 0x70, 0x07, 0xEB, 0x5A, 0x80, 0x2B, 0x22, 0x00, 0xF0,
- 0x89, 0xA8, 0x00, 0x16, 0x80, 0xF9, 0x99, 0xF0, 0x69, 0x9F, 0x90, 0xE8,
- 0xF9, 0xE0, 0xE9, 0xE9, 0xE0, 0xF8, 0x88, 0x80, 0x32, 0x94, 0xAF, 0x80,
- 0xF8, 0xC8, 0xF0, 0xAD, 0x5D, 0x5A, 0x80, 0x69, 0x29, 0x60, 0x99, 0xBD,
- 0x90, 0xD1, 0xBD, 0x90, 0x99, 0xE9, 0x90, 0x79, 0x99, 0x90, 0x8C, 0x63,
- 0xBA, 0x80, 0x99, 0xF9, 0x90, 0x69, 0x99, 0x60, 0xF9, 0x99, 0x90, 0xE9,
- 0x9E, 0x80, 0x69, 0x89, 0x60, 0xE9, 0x24, 0x00, 0x99, 0x71, 0x60, 0x75,
- 0x6A, 0xE2, 0x00, 0x99, 0x69, 0x90, 0x94, 0xA5, 0x2F, 0x84, 0x00, 0x99,
- 0x9F, 0x10, 0x8C, 0x6B, 0x5F, 0x80, 0x8A, 0x2A, 0xAA, 0xFC, 0x10, 0xC3,
- 0x92, 0x97, 0x00, 0x87, 0x99, 0x65, 0xE4, 0x00, 0x8E, 0x99, 0xE0, 0x69,
- 0x39, 0x60, 0x95, 0x7B, 0x59, 0x00, 0x79, 0x97, 0x90, 0x80, 0x96, 0x9F,
- 0x86, 0x96, 0x9F, 0x86
+ 0x90, 0x69, 0x9F, 0x90, 0xE9, 0xE9, 0xE0, 0x69, 0x89, 0x60, 0xE9, 0x99,
+ 0xE0, 0xF8, 0xC8, 0xF0, 0xF8, 0xC8, 0x80, 0x69, 0x8B, 0x60, 0x99, 0x9F,
+ 0x90, 0x44, 0x44, 0x40, 0x11, 0x19, 0x60, 0x99, 0xE9, 0x90, 0x88, 0x88,
+ 0xF0, 0x8C, 0x63, 0xBA, 0x80, 0x99, 0xDB, 0x90, 0x69, 0x99, 0x60, 0xE9,
+ 0x9E, 0x80, 0x64, 0xA5, 0x26, 0x80, 0xE9, 0x9E, 0x90, 0xE8, 0xF1, 0xF0,
+ 0xE4, 0x44, 0x40, 0x99, 0x99, 0x60, 0x8C, 0x54, 0xA2, 0x00, 0x8C, 0x6A,
+ 0xE5, 0x00, 0x99, 0x69, 0x90, 0x55, 0x52, 0x20, 0xF1, 0x68, 0xF0, 0x2B,
+ 0x22, 0x00, 0xF0, 0x89, 0xA8, 0x00, 0x16, 0x80, 0xF9, 0x99, 0xF0, 0xE9,
+ 0x9E, 0x80, 0x69, 0x89, 0x60, 0xE9, 0x24, 0x00, 0x99, 0x71, 0xE0, 0x75,
+ 0x6A, 0xE2, 0x00, 0x99, 0x69, 0x90, 0xAA, 0xAA, 0xF0, 0x99, 0x97, 0x10,
+ 0x8C, 0x6B, 0x5F, 0x80, 0x8C, 0x6B, 0x5F, 0x00, 0xC6, 0x55, 0x60, 0x8E,
+ 0x6B, 0x5C, 0x80, 0x8E, 0x99, 0xE0, 0x69, 0x39, 0x60, 0x95, 0x7B, 0x59,
+ 0x00, 0x79, 0x97, 0x90, 0x69, 0x9F, 0x90, 0xE8, 0xF9, 0xE0, 0xE9, 0xE9,
+ 0xE0, 0xF8, 0x88, 0x80, 0x35, 0x55, 0xF0, 0xF8, 0xC8, 0xF0, 0xAD, 0x5D,
+ 0x5A, 0x80, 0x69, 0x29, 0x60, 0x99, 0xBD, 0x90, 0xD1, 0xBD, 0x90, 0x99,
+ 0xE9, 0x90, 0x79, 0x99, 0x90, 0x8C, 0x63, 0xBA, 0x80, 0x99, 0xF9, 0x90,
+ 0x69, 0x99, 0x60, 0xF9, 0x99, 0x90, 0xE9, 0x9E, 0x80, 0x69, 0x89, 0x60,
+ 0xE9, 0x24, 0x00, 0x99, 0x71, 0x60, 0x75, 0x6A, 0xE2, 0x00, 0x99, 0x69,
+ 0x90, 0xAA, 0xAA, 0xF0, 0x99, 0x9F, 0x10, 0x8C, 0x6B, 0x5F, 0x80, 0x8C,
+ 0x6B, 0x5F, 0x00, 0x00, 0xC6, 0x55, 0x60, 0x8E, 0x6B, 0x5C, 0x80, 0x8E,
+ 0x99, 0xE0, 0x69, 0x39, 0x60, 0x95, 0x7B, 0x59, 0x00, 0x79, 0x97, 0x90,
+ 0x69, 0x9F, 0x90, 0xE8, 0xF9, 0xE0, 0xE9, 0xE9, 0xE0, 0xF8, 0x88, 0x80,
+ 0x35, 0x55, 0xF0, 0xF8, 0xE8, 0xF0, 0xAD, 0x5D, 0x5A, 0x80, 0x69, 0x29,
+ 0x60, 0x99, 0xBD, 0x90, 0xB9, 0xBD, 0x90, 0x99, 0xE9, 0x90, 0x79, 0x99,
+ 0x90, 0x8C, 0x63, 0xBA, 0x80, 0x99, 0x9F, 0x90, 0x69, 0x99, 0x60, 0xF9,
+ 0x99, 0x90, 0x96, 0x9F, 0x86, 0x96, 0x9F, 0x86
};
const GFXglyph TinyFont5_Glyphs[] PROGMEM = {
- { 0, 3, 5, 3, 0, -5 }, // 0x20 ' '
- { 3, 1, 5, 2, 0, -5 }, // 0x21 '!'
- { 5, 3, 2, 4, 0, -5 }, // 0x22 '"'
- { 7, 5, 5, 6, 0, -4 }, // 0x23 '#'
+ { 0, 4, 5, 5, 0, -5 }, // 0x20 ' '
+ { 3, 1, 5, 5, 1, -5 }, // 0x21 '!'
+ { 5, 3, 2, 5, 1, -5 }, // 0x22 '"'
+ { 7, 5, 5, 5, 0, -5 }, // 0x23 '#'
{ 11, 4, 5, 5, 0, -5 }, // 0x24 '$'
- { 14, 3, 5, 4, 0, -5 }, // 0x25 '%'
+ { 14, 3, 5, 5, 1, -5 }, // 0x25 '%'
{ 17, 4, 5, 5, 0, -5 }, // 0x26 '&'
- { 20, 1, 2, 2, 0, -5 }, // 0x27 '''
- { 21, 2, 5, 4, 0, -5 }, // 0x28 '('
- { 23, 2, 5, 4, 0, -5 }, // 0x29 ')'
- { 25, 3, 3, 4, 0, -4 }, // 0x2A '*'
- { 27, 3, 3, 4, 0, -4 }, // 0x2B '+'
- { 29, 2, 2, 3, 0, -1 }, // 0x2C ','
- { 30, 3, 1, 4, 0, -3 }, // 0x2D '-'
- { 31, 1, 1, 2, 0, -1 }, // 0x2E '.'
- { 32, 2, 5, 4, 0, -5 }, // 0x2F '/'
+ { 20, 1, 2, 5, 1, -5 }, // 0x27 '''
+ { 21, 2, 5, 5, 1, -5 }, // 0x28 '('
+ { 23, 2, 5, 5, 1, -5 }, // 0x29 ')'
+ { 25, 3, 3, 5, 1, -4 }, // 0x2A '*'
+ { 27, 3, 3, 5, 1, -4 }, // 0x2B '+'
+ { 29, 2, 2, 5, 1, -1 }, // 0x2C ','
+ { 30, 3, 1, 5, 1, -3 }, // 0x2D '-'
+ { 31, 1, 1, 5, 2, -1 }, // 0x2E '.'
+ { 32, 2, 5, 5, 1, -5 }, // 0x2F '/'
{ 34, 4, 5, 5, 0, -5 }, // 0x30 '0'
- { 37, 3, 5, 4, 0, -5 }, // 0x31 '1'
+ { 37, 3, 5, 5, 1, -5 }, // 0x31 '1'
{ 40, 4, 5, 5, 0, -5 }, // 0x32 '2'
{ 43, 4, 5, 5, 0, -5 }, // 0x33 '3'
{ 46, 4, 5, 5, 0, -5 }, // 0x34 '4'
@@ -54,11 +68,11 @@ const GFXglyph TinyFont5_Glyphs[] PROGMEM = {
{ 55, 4, 5, 5, 0, -5 }, // 0x37 '7'
{ 58, 4, 5, 5, 0, -5 }, // 0x38 '8'
{ 61, 4, 5, 5, 0, -5 }, // 0x39 '9'
- { 64, 1, 3, 2, 0, -4 }, // 0x3A ':'
- { 65, 2, 4, 3, 0, -4 }, // 0x3B ';'
- { 66, 2, 3, 3, 0, -4 }, // 0x3C '<'
- { 68, 3, 3, 4, 0, -4 }, // 0x3D '='
- { 70, 2, 3, 3, 0, -4 }, // 0x3E '>'
+ { 64, 1, 3, 5, 2, -4 }, // 0x3A ':'
+ { 65, 2, 4, 5, 1, -4 }, // 0x3B ';'
+ { 66, 2, 3, 5, 1, -4 }, // 0x3C '<'
+ { 68, 3, 3, 5, 1, -3 }, // 0x3D '='
+ { 70, 2, 3, 5, 1, -4 }, // 0x3E '>'
{ 72, 4, 5, 5, 0, -5 }, // 0x3F '?'
{ 75, 4, 5, 5, 0, -5 }, // 0x40 '@'
{ 78, 4, 5, 5, 0, -5 }, // 0x41 'A'
@@ -69,127 +83,127 @@ const GFXglyph TinyFont5_Glyphs[] PROGMEM = {
{ 93, 4, 5, 5, 0, -5 }, // 0x46 'F'
{ 96, 4, 5, 5, 0, -5 }, // 0x47 'G'
{ 99, 4, 5, 5, 0, -5 }, // 0x48 'H'
- { 102, 1, 5, 2, 0, -5 }, // 0x49 'I'
+ { 102, 1, 5, 5, 1, -5 }, // 0x49 'I'
{ 104, 4, 5, 5, 0, -5 }, // 0x4A 'J'
{ 107, 4, 5, 5, 0, -5 }, // 0x4B 'K'
{ 110, 4, 5, 5, 0, -5 }, // 0x4C 'L'
- { 113, 5, 5, 6, 0, -5 }, // 0x4D 'M'
+ { 113, 5, 5, 5, 0, -5 }, // 0x4D 'M'
{ 117, 4, 5, 5, 0, -5 }, // 0x4E 'N'
{ 120, 4, 5, 5, 0, -5 }, // 0x4F 'O'
{ 123, 4, 5, 5, 0, -5 }, // 0x50 'P'
- { 126, 5, 5, 6, 0, -5 }, // 0x51 'Q'
+ { 126, 5, 5, 5, 0, -5 }, // 0x51 'Q'
{ 130, 4, 5, 5, 0, -5 }, // 0x52 'R'
{ 133, 4, 5, 5, 0, -5 }, // 0x53 'S'
- { 136, 3, 5, 4, 0, -5 }, // 0x54 'T'
+ { 136, 3, 5, 5, 1, -5 }, // 0x54 'T'
{ 139, 4, 5, 5, 0, -5 }, // 0x55 'U'
- { 142, 5, 5, 6, 0, -5 }, // 0x56 'V'
- { 146, 5, 5, 6, 0, -5 }, // 0x57 'W'
+ { 142, 5, 5, 5, 0, -5 }, // 0x56 'V'
+ { 146, 5, 5, 5, 0, -5 }, // 0x57 'W'
{ 150, 4, 5, 5, 0, -5 }, // 0x58 'X'
- { 153, 3, 5, 4, 0, -5 }, // 0x59 'Y'
+ { 153, 3, 5, 5, 1, -5 }, // 0x59 'Y'
{ 156, 4, 5, 5, 0, -5 }, // 0x5A 'Z'
- { 159, 2, 5, 3, 0, -5 }, // 0x5B '['
- { 161, 2, 5, 3, 0, -5 }, // 0x5C '\'
- { 163, 2, 5, 4, 0, -5 }, // 0x5D ']'
- { 165, 3, 2, 4, 0, -5 }, // 0x5E '^'
- { 167, 3, 1, 4, 0, -1 }, // 0x5F '_'
- { 168, 2, 2, 3, 0, -5 }, // 0x60 '`'
- { 0, 0, 0, 0, 0, 0 }, // 0x61 'a'
- { 0, 0, 0, 0, 0, 0 }, // 0x62 'b'
- { 0, 0, 0, 0, 0, 0 }, // 0x63 'c'
- { 169, 4, 5, 5, 0, -5 }, // 0x64 'd'
- { 0, 0, 0, 0, 0, 0 }, // 0x65 'e'
- { 0, 0, 0, 0, 0, 0 }, // 0x66 'f'
- { 0, 0, 0, 0, 0, 0 }, // 0x67 'g'
- { 0, 0, 0, 0, 0, 0 }, // 0x68 'h'
- { 0, 0, 0, 0, 0, 0 }, // 0x69 'i'
- { 0, 0, 0, 0, 0, 0 }, // 0x6A 'j'
- { 0, 0, 0, 0, 0, 0 }, // 0x6B 'k'
- { 0, 0, 0, 0, 0, 0 }, // 0x6C 'l'
- { 172, 5, 5, 6, 0, -5 }, // 0x6D 'm'
- { 0, 0, 0, 0, 0, 0 }, // 0x6E 'n'
- { 0, 0, 0, 0, 0, 0 }, // 0x6F 'o'
- { 0, 0, 0, 0, 0, 0 }, // 0x70 'p'
- { 0, 0, 0, 0, 0, 0 }, // 0x71 'q'
- { 0, 0, 0, 0, 0, 0 }, // 0x72 'r'
- { 0, 0, 0, 0, 0, 0 }, // 0x73 's'
- { 0, 0, 0, 0, 0, 0 }, // 0x74 't'
- { 0, 0, 0, 0, 0, 0 }, // 0x75 'u'
- { 0, 0, 0, 0, 0, 0 }, // 0x76 'v'
- { 0, 0, 0, 0, 0, 0 }, // 0x77 'w'
- { 0, 0, 0, 0, 0, 0 }, // 0x78 'x'
- { 0, 0, 0, 0, 0, 0 }, // 0x79 'y'
- { 0, 0, 0, 0, 0, 0 }, // 0x7A 'z'
- { 176, 3, 5, 4, 0, -5 }, // 0x7B '{'
- { 179, 1, 4, 4, 0, -4 }, // 0x7C '|'
- { 180, 3, 5, 4, 0, -5 }, // 0x7D '}'
- { 183, 4, 3, 5, 0, -4 }, // 0x7E '~'
- { 185, 4, 5, 5, 0, -5 }, // 0x7F 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x80 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x81 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x82 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x83 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x84 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x85 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x86 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x87 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x88 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x89 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x8A 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x8B 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x8C 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x8D 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x8E 'non-printable'
- { 0, 0, 0, 0, 0, 0 }, // 0x8F 'non-printable'
- { 188, 4, 5, 5, 0, -5 }, // 0x90 'non-printable'
- { 191, 4, 5, 5, 0, -5 }, // 0x91 'non-printable'
- { 194, 4, 5, 5, 0, -5 }, // 0x92 'non-printable'
- { 197, 4, 5, 5, 0, -5 }, // 0x93 'non-printable'
- { 200, 5, 5, 6, 0, -5 }, // 0x94 'non-printable'
- { 204, 4, 5, 5, 0, -5 }, // 0x95 'non-printable'
- { 207, 5, 5, 6, 0, -5 }, // 0x96 'non-printable'
- { 211, 4, 5, 5, 0, -5 }, // 0x97 'non-printable'
- { 214, 4, 5, 5, 0, -5 }, // 0x98 'non-printable'
- { 217, 4, 5, 5, 0, -5 }, // 0x99 'non-printable'
- { 220, 4, 5, 4, 0, -5 }, // 0x9A 'non-printable'
- { 223, 4, 5, 4, 0, -5 }, // 0x9B 'non-printable'
- { 226, 5, 5, 6, 0, -5 }, // 0x9C 'non-printable'
- { 230, 4, 5, 5, 0, -5 }, // 0x9D 'non-printable'
- { 233, 4, 5, 5, 0, -5 }, // 0x9E 'non-printable'
- { 236, 4, 5, 5, 0, -5 }, // 0x9F 'non-printable'
- { 239, 4, 5, 5, 0, -5 }, // 0xA0 ' '
- { 242, 4, 5, 5, 0, -5 }, // 0xA1 '¡'
- { 245, 3, 5, 4, 0, -5 }, // 0xA2 '¢'
- { 248, 4, 5, 5, 0, -5 }, // 0xA3 '£'
- { 251, 5, 5, 6, 0, -5 }, // 0xA4 '¤'
- { 255, 4, 5, 5, 0, -5 }, // 0xA5 '¥'
- { 258, 5, 6, 6, 0, -5 }, // 0xA6 '¦'
- { 263, 4, 5, 5, 0, -5 }, // 0xA7 '§'
- { 266, 5, 5, 6, 0, -5 }, // 0xA8 '¨'
- { 270, 6, 6, 7, 0, -5 }, // 0xA9 '©'
- { 275, 5, 5, 6, 0, -5 }, // 0xAA 'ª'
- { 279, 6, 5, 7, 0, -5 }, // 0xAB '«'
- { 284, 4, 5, 5, 0, -5 }, // 0xAC '¬'
- { 287, 4, 5, 5, 0, -5 }, // 0xAD 'non-printable'
- { 290, 5, 5, 6, 0, -5 }, // 0xAE '®'
- { 294, 4, 5, 5, 0, -5 }, // 0xAF '¯'
- { 0, 0, 0, 0, 0, 0 }, // 0xB0 '°'
- { 0, 0, 0, 0, 0, 0 }, // 0xB1 '±'
- { 0, 0, 0, 0, 0, 0 }, // 0xB2 '²'
- { 0, 0, 0, 0, 0, 0 }, // 0xB3 '³'
- { 0, 0, 0, 0, 0, 0 }, // 0xB4 '´'
- { 0, 0, 0, 0, 0, 0 }, // 0xB5 'µ'
- { 0, 0, 0, 0, 0, 0 }, // 0xB6 '¶'
- { 297, 1, 1, 2, 0, -3 }, // 0xB7 '·'
- { 0, 0, 0, 0, 0, 0 }, // 0xB8 '¸'
- { 0, 0, 0, 0, 0, 0 }, // 0xB9 '¹'
- { 0, 0, 0, 0, 0, 0 }, // 0xBA 'º'
- { 0, 0, 0, 0, 0, 0 }, // 0xBB '»'
- { 0, 0, 0, 0, 0, 0 }, // 0xBC '¼'
- { 0, 0, 0, 0, 0, 0 }, // 0xBD '½'
- { 0, 0, 0, 0, 0, 0 }, // 0xBE '¾'
- { 0, 0, 0, 0, 0, 0 }, // 0xBF '¿'
- { 298, 4, 6, 5, 0, -5 }, // 0xC0 'À'
- { 301, 4, 6, 5, 0, -5 } // 0xC1 'Á'
+ { 159, 2, 5, 5, 1, -5 }, // 0x5B '['
+ { 161, 2, 5, 5, 1, -5 }, // 0x5C '\'
+ { 163, 2, 5, 5, 1, -5 }, // 0x5D ']'
+ { 165, 3, 2, 5, 1, -5 }, // 0x5E '^'
+ { 167, 3, 1, 5, 1, -1 }, // 0x5F '_'
+ { 168, 2, 2, 5, 1, -5 }, // 0x60 '`'
+ { 169, 4, 5, 5, 0, -5 }, // 0x61 'a'
+ { 172, 4, 5, 5, 0, -5 }, // 0x62 'b'
+ { 175, 4, 5, 5, 0, -5 }, // 0x63 'c'
+ { 178, 4, 5, 5, 0, -5 }, // 0x64 'd'
+ { 181, 4, 5, 5, 0, -5 }, // 0x65 'e'
+ { 184, 4, 5, 5, 0, -5 }, // 0x66 'f'
+ { 187, 4, 5, 5, 0, -5 }, // 0x67 'g'
+ { 190, 4, 5, 5, 0, -5 }, // 0x68 'h'
+ { 193, 4, 5, 5, 0, -5 }, // 0x69 'i'
+ { 196, 4, 5, 5, 0, -5 }, // 0x6A 'j'
+ { 199, 4, 5, 5, 0, -5 }, // 0x6B 'k'
+ { 202, 4, 5, 5, 0, -5 }, // 0x6C 'l'
+ { 205, 5, 5, 5, 0, -5 }, // 0x6D 'm'
+ { 209, 4, 5, 5, 0, -5 }, // 0x6E 'n'
+ { 212, 4, 5, 5, 0, -5 }, // 0x6F 'o'
+ { 215, 4, 5, 5, 0, -5 }, // 0x70 'p'
+ { 218, 5, 5, 5, 0, -5 }, // 0x71 'q'
+ { 222, 4, 5, 5, 0, -5 }, // 0x72 'r'
+ { 225, 4, 5, 5, 0, -5 }, // 0x73 's'
+ { 228, 4, 5, 5, 0, -5 }, // 0x74 't'
+ { 231, 4, 5, 5, 0, -5 }, // 0x75 'u'
+ { 234, 5, 5, 5, 0, -5 }, // 0x76 'v'
+ { 238, 5, 5, 5, 0, -5 }, // 0x77 'w'
+ { 242, 4, 5, 5, 0, -5 }, // 0x78 'x'
+ { 245, 4, 5, 5, 0, -5 }, // 0x79 'y'
+ { 248, 4, 5, 5, 0, -5 }, // 0x7A 'z'
+ { 251, 3, 5, 5, 1, -5 }, // 0x7B '{'
+ { 254, 1, 4, 5, 1, -4 }, // 0x7C '|'
+ { 255, 3, 5, 5, 1, -5 }, // 0x7D '}'
+ { 258, 4, 3, 5, 0, -4 }, // 0x7E '~'
+ { 260, 4, 5, 5, 0, -5 }, // 0x7F 'non-printable'
+ { 263, 4, 5, 5, 0, -5 }, // 0x80 'non-printable'
+ { 266, 4, 5, 5, 0, -5 }, // 0x81 'non-printable'
+ { 269, 3, 5, 5, 1, -5 }, // 0x82 'non-printable'
+ { 272, 4, 5, 5, 0, -5 }, // 0x83 'non-printable'
+ { 275, 5, 5, 5, 0, -5 }, // 0x84 'non-printable'
+ { 279, 4, 5, 5, 0, -5 }, // 0x85 'non-printable'
+ { 282, 4, 5, 5, 0, -5 }, // 0x86 'non-printable'
+ { 285, 4, 5, 5, 0, -5 }, // 0x87 'non-printable'
+ { 288, 5, 5, 5, 0, -5 }, // 0x88 'non-printable'
+ { 292, 5, 5, 5, 0, -5 }, // 0x89 'non-printable'
+ { 296, 4, 5, 5, 0, -5 }, // 0x8A 'non-printable'
+ { 299, 5, 5, 5, 0, -5 }, // 0x8B 'non-printable'
+ { 303, 4, 5, 5, 0, -5 }, // 0x8C 'non-printable'
+ { 306, 4, 5, 5, 0, -5 }, // 0x8D 'non-printable'
+ { 309, 5, 5, 5, 0, -5 }, // 0x8E 'non-printable'
+ { 313, 4, 5, 5, 0, -5 }, // 0x8F 'non-printable'
+ { 316, 4, 5, 5, 0, -5 }, // 0x90 'non-printable'
+ { 319, 4, 5, 5, 0, -5 }, // 0x91 'non-printable'
+ { 322, 4, 5, 5, 0, -5 }, // 0x92 'non-printable'
+ { 325, 4, 5, 5, 0, -5 }, // 0x93 'non-printable'
+ { 328, 4, 5, 5, 0, -5 }, // 0x94 'non-printable'
+ { 331, 4, 5, 5, 0, -5 }, // 0x95 'non-printable'
+ { 334, 5, 5, 5, 0, -5 }, // 0x96 'non-printable'
+ { 338, 4, 5, 5, 0, -5 }, // 0x97 'non-printable'
+ { 341, 4, 5, 5, 0, -5 }, // 0x98 'non-printable'
+ { 344, 4, 5, 5, 0, -5 }, // 0x99 'non-printable'
+ { 347, 4, 5, 4, 0, -5 }, // 0x9A 'non-printable'
+ { 350, 4, 5, 4, 0, -5 }, // 0x9B 'non-printable'
+ { 353, 5, 5, 5, 0, -5 }, // 0x9C 'non-printable'
+ { 357, 4, 5, 5, 0, -5 }, // 0x9D 'non-printable'
+ { 360, 4, 5, 5, 0, -5 }, // 0x9E 'non-printable'
+ { 363, 4, 5, 5, 0, -5 }, // 0x9F 'non-printable'
+ { 366, 4, 5, 5, 0, -5 }, // 0xA0 ' '
+ { 369, 4, 5, 5, 0, -5 }, // 0xA1 '¡'
+ { 372, 3, 5, 5, 1, -5 }, // 0xA2 '¢'
+ { 375, 4, 5, 5, 0, -5 }, // 0xA3 '£'
+ { 378, 5, 5, 5, 0, -5 }, // 0xA4 '¤'
+ { 382, 4, 5, 5, 0, -5 }, // 0xA5 '¥'
+ { 385, 4, 5, 5, 0, -5 }, // 0xA6 '¦'
+ { 388, 4, 5, 5, 0, -5 }, // 0xA7 '§'
+ { 391, 5, 5, 5, 0, -5 }, // 0xA8 '¨'
+ { 395, 5, 6, 5, 0, -5 }, // 0xA9 '©'
+ { 400, 4, 5, 5, 0, -5 }, // 0xAA 'ª'
+ { 403, 5, 5, 5, 0, -5 }, // 0xAB '«'
+ { 407, 4, 5, 5, 0, -5 }, // 0xAC '¬'
+ { 410, 4, 5, 5, 0, -5 }, // 0xAD 'non-printable'
+ { 413, 5, 5, 5, 0, -5 }, // 0xAE '®'
+ { 417, 4, 5, 5, 0, -5 }, // 0xAF '¯'
+ { 420, 4, 5, 5, 0, -5 }, // 0xB0 '°'
+ { 423, 4, 5, 5, 0, -5 }, // 0xB1 '±'
+ { 426, 4, 5, 5, 0, -5 }, // 0xB2 '²'
+ { 429, 4, 5, 5, 0, -5 }, // 0xB3 '³'
+ { 432, 4, 5, 5, 0, -5 }, // 0xB4 '´'
+ { 435, 4, 5, 5, 0, -5 }, // 0xB5 'µ'
+ { 438, 5, 5, 5, 0, -5 }, // 0xB6 '¶'
+ { 442, 4, 5, 5, 0, -5 }, // 0xB7 '·'
+ { 445, 4, 5, 5, 0, -5 }, // 0xB8 '¸'
+ { 448, 4, 5, 5, 0, -5 }, // 0xB9 '¹'
+ { 451, 4, 5, 5, 0, -5 }, // 0xBA 'º'
+ { 454, 4, 5, 5, 0, -5 }, // 0xBB '»'
+ { 457, 5, 5, 5, 0, -5 }, // 0xBC '¼'
+ { 461, 4, 5, 5, 0, -5 }, // 0xBD '½'
+ { 464, 4, 5, 5, 0, -5 }, // 0xBE '¾'
+ { 467, 4, 5, 5, 0, -5 }, // 0xBF '¿'
+ { 470, 4, 6, 5, 0, -5 }, // 0xC0 'À'
+ { 473, 4, 6, 5, 0, -5 } // 0xC1 'Á'
};
-const GFXfont TinyFont5 PROGMEM = {(uint8_t *) TinyFont5_Bitmaps, (GFXglyph *)TinyFont5_Glyphs, 0x20, 0xC1, 5};
+const GFXfont TinyFont5 PROGMEM = {(uint8_t *) TinyFont5_Bitmaps, (GFXglyph *)TinyFont5_Glyphs, 0x20, 0xC1, 5};
diff --git a/yoRadio/src/displays/fonts/bootlogobw.h b/yoRadio/src/displays/fonts/bootlogobw.h
deleted file mode 100644
index 557d11b..0000000
--- a/yoRadio/src/displays/fonts/bootlogobw.h
+++ /dev/null
@@ -1,163 +0,0 @@
-
-/*******************************************************************************
-* generated by lcd-image-converter rev.030b30d from 2019-03-17 01:38:34 +0500
-* image
-* filename: unsaved
-* name: bl40
-*
-* preset name: Grayscale 8
-* data block size: 8 bit(s), uint8_t
-* RLE compression enabled: no
-* conversion type: Grayscale, not_used not_used
-* split to rows: yes
-* bits per pixel: 8
-*
-* preprocess:
-* main scan direction: top_to_bottom
-* line scan direction: forward
-* inverse: no
-*******************************************************************************/
-
-/*
- typedef struct {
- const uint8_t *data;
- uint16_t width;
- uint16_t height;
- uint8_t dataSize;
- } tImage;
-*/
-#include
-
-
-
-static const uint8_t bootlogobw[6336] PROGMEM = {
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙░▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓█∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙░▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙█▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙░█▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙░███▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░░∙∙▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒░░∙∙∙▒▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒∙∙∙∙∙∙∙∙∙░∙∙▓▒∙∙∙∙░▓▓▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙▒▒░░░░▒▒░∙∙∙∙∙∙∙∙∙∙▒∙∙∙░▓▒▒▓▓▓▒▒▒▒▒▒▒▒▒▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒░∙░▒∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙▒░∙∙∙∙∙░░░▒∙∙∙∙∙∙∙∙∙▓∙∙∙∙∙▒░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▓░∙∙∙▓∙∙∙
- // ∙∙∙∙∙∙∙∙░▒∙∙∙∙∙∙▒∙∙∙▓∙∙∙∙∙∙∙∙▓░∙∙∙░█░░▒▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░∙∙∙∙∙▒▓░∙∙∙∙▓∙∙∙
- // ∙∙∙∙∙∙∙▒∙∙∙∙∙∙▒∙∙∙∙∙▓░∙∙∙∙∙∙▓▒∙∙∙▒▓▓░░▒▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓█∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓∙∙∙∙∙▒▓░∙∙∙∙∙▓∙∙∙
- // ∙∙∙∙∙∙▓∙∙∙∙∙∙▒∙∙∙∙∙∙▒▒∙∙∙∙∙▒▓∙∙∙█▓▓▓░░▒▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙░░∙∙∙∙▒▓▒∙∙∙∙▒▓▒∙∙∙∙∙▒░∙∙░
- // ∙∙∙∙∙▒∙∙∙∙∙∙░∙∙∙∙∙∙∙▒▒∙∙∙∙∙▓░∙∙▓▓▓▓▓░░▒▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓∙∙∙∙∙∙∙∙∙∙▓▓∙∙∙∙░▓▓∙∙∙∙░▓▓∙∙∙∙∙∙▓∙∙▒∙
- // ∙∙∙∙▒∙∙∙∙∙∙░∙∙∙∙∙∙∙∙▓▒∙∙∙∙▓▓∙∙▒█▓▓▓▓░░▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓▓∙∙∙∙∙∙∙∙▓▓░∙∙∙∙▓▓░∙∙∙∙▓▓▒∙∙∙∙∙░░∙░∙∙
- // ∙∙∙▒∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙▓░∙∙∙▒▓░∙░█▓▓▓▓▒░░▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▓░▒▒∙∙∙∙▒▓▓∙∙∙∙▒▓▒∙∙∙∙▓▓▓∙∙∙∙∙∙▒∙▒∙∙∙
- // ∙∙∙∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙░▓∙∙∙∙▓▓∙∙████▓▓░░▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒░░▒▒░▓∙∙∙∙░▒▓∙∙∙∙∙▓▓∙∙∙∙▒░▓▓∙∙∙∙∙▒▓░∙∙∙∙
- // ∙∙▒∙∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙▒▓∙∙∙▒▓▒∙▓█████▒░▒▓▓▓▓▓▓█▒░∙░▒█▓▓▓▓▒▒▒░▒▒▒▒░░░∙∙∙▒░▓▓∙∙∙∙▓▓▒∙∙∙▒∙▒▓░∙∙∙∙░∙∙∙∙∙∙∙
- // ∙░∙∙∙∙∙▓∙∙∙∙∙∙∙∙∙∙∙▓▒∙∙░▓▓∙∙█████▓░▒█▓▓▓▓█░∙∙∙∙∙∙∙∙▓▓▓▒░░▒▒▒▒▒░░▓∙∙∙∙▓▓∙∙∙∙▒▓▓∙∙∙∙∙∙▒▓∙∙∙∙∙░∙∙∙∙∙∙∙
- // ∙▒∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙▒▓∙∙∙▓▓▒∙▓█████░▒████▓█∙∙∙∙∙░▒∙∙∙∙▓▒░░▓▓▒▒▒░░▒▓░∙░▒▓▒∙∙∙░▓▓░∙∙∙░∙∙░▓∙∙∙░░∙∙∙∙∙∙∙∙
- // ░∙∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙∙▓▒∙∙░▓▓∙∙████▓░▒██████∙∙∙∙∙▒▓∙∙∙∙∙░░░▓▓▓▓▓░░░▒▒▓▒∙▓▓∙∙∙∙▒▓▓∙∙∙▒∙∙∙∙▓░∙▒░∙∙∙∙∙∙∙∙∙
- // ░∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙∙▒▓∙∙∙▓▓▒∙░███▒░███████▓▒░░░░▓▒∙∙∙∙∙▓▒▒▓▓▓▓▒░░▒▒▒▓∙▒▓░∙∙∙▒∙▓▒∙∙▒∙∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙░∙∙∙∙∙∙∙∙∙∙∙▓░∙∙░▓▓∙∙▒▒▒▓████████░░∙∙∙░▓▓∙∙∙∙∙▓▓░▓▓▓▓▓░░░▓▓▒▒∙▓▓∙∙∙▒∙∙▓∙∙▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙∙▓▓∙∙∙▓▓▒∙∙██████████▓░▒∙∙∙∙▒▓▒∙∙∙∙▓▓▓∙▓▓▓▓▒░░▒▓▓▒▓∙▓░∙∙▒∙∙∙▓▒▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙░∙∙∙∙∙∙∙∙∙∙▒▓∙∙∙░▓▓∙∙∙█████████▓░▒▒∙∙∙░▓▓∙∙∙∙▒▓▓░∙▒▓▓▓▒░░▓▓▒▓▓░▓∙∙▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙░∙∙∙∙∙∙∙∙∙░▓∙∙∙∙▓▓▒∙∙░████████▓░░█▒░░░▒▒▒░░░░▒▒▒░░▒▓▓▒░░▒▓▒▓▓▓▒▒▒▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙░∙∙∙∙∙∙∙∙∙░▓░∙∙∙░▓▓∙∙∙░████████░░▓████▓░░████▓▓░░▓▓▓▓▒▓░░▓▒▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙░∙∙∙∙∙∙∙∙░▓∙∙∙∙∙▒▓▒∙∙∙▒▓██████░░▒█████░░▓███▒█▓░▓███▒▓▒░▓▒▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙░∙∙∙∙∙∙▒▒∙∙∙∙∙░▓▓∙∙∙∙▒▓▓▓███▒░░▓████▒░░███▒██▓░▓██▒▓▓░▒▒▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙▒∙∙∙∙░▒░∙∙∙∙∙∙▓▓░∙∙∙∙▒▓▓▓▓██░░▒████▓░░▓██▓████░▒▒▒▓█▓░▒▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙░░▒▒░∙∙∙∙∙∙∙░▓▒∙∙∙∙∙▒▓▓▓▓▓▓░░▓████▓▒░██▓▓█████▓▓███░░▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▓∙∙∙∙∙∙▒▓▓▓▓▓▓░░▓███▒█▒░█▓▓█████████▓▒░▓███▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▓∙∙∙∙∙∙∙▒▓▓▓▓▓▓░░█▓█▒██▓░░██████████▓▓▒░██████▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓░∙∙∙∙∙∙∙░▓▓▓▓▓▓▒░▓▓▒▓██████████████▒██░▓█████████▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒∙∙∙∙∙∙∙∙░▓▓▓▓▓▓▓▒▒▒▓▓░∙∙∙∙∙∙∙∙∙∙∙∙▒∙∙▒▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒∙∙∙∙∙∙∙∙∙∙▓▓▓▓▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙▒∙∙∙▓░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙░▓∙∙∙∙∙∙∙∙∙∙∙▓▒▓▓▓▓▓▓▓▓▓▓█∙∙∙∙∙∙∙∙∙∙▒∙∙∙▒▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▓▓▓▓▓▓▓▓▓░∙∙∙∙∙∙∙∙▓∙∙∙∙▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙▓▓▒∙∙∙∙∙∙∙░▒∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▓▓▓▓▓▓▓█∙∙∙∙∙∙∙▓∙∙∙∙▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ░▓▓▓∙∙∙∙∙∙▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▒▒▓▓▓▓▓▓▓∙∙∙∙∙▒∙∙∙∙∙▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ▒▓▓░∙∙∙∙∙▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▓▒▒▒▒▒▒▒▒▓▓▓▓▓▓░∙∙░▒∙∙∙∙▓∙∙∙∙∙∙∙∙∙▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙▓▓▒∙∙░▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓█░▓∙∙∙∙░∙∙∙∙∙∙∙▒███▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙▒▒▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▒▒███▒▒▓███████████∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▒▒▓▓▓▒▓▓▓▓▓▓▓██████∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▒░▓▒▒▓▓▓▓▓▓▓▓▓█████∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓███░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓█▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓▓▓█∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▓▓█∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▓▓▓▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▓▓▓▓▓▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- // ∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░▒▒▒▒▒▒▒▒▒▓▓▓▒░∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x68, 0xb0, 0xcb, 0xc9, 0xc4, 0x83, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x70, 0xa7, 0xa7, 0xa4, 0x8f, 0x50, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, 0xcb, 0xc4, 0xc4, 0xc4, 0xc3, 0xc2, 0xc0, 0xca, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0xb4, 0xa6, 0xa3, 0xa1, 0x9e, 0x9b, 0x9a, 0xa2, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0xc4, 0xbb, 0xbe, 0xc0, 0xc2, 0xc4, 0xc4, 0xc2, 0xc2, 0xc4, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0xb6, 0xad, 0xab, 0xa8, 0xa7, 0xa3, 0xa0, 0x9e, 0x9b, 0xa6, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa4, 0xb5, 0xb7, 0xb9, 0xbb, 0xbe, 0xc0, 0xc2, 0xc4, 0xc5, 0xc3, 0xcd, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0xc4, 0xb5, 0xb3, 0xb0, 0xae, 0xaa, 0xa8, 0xa6, 0xa3, 0xa1, 0xa0, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xbf, 0xb1, 0xb2, 0xb4, 0xb7, 0xba, 0xbc, 0xbe, 0xc0, 0xc3, 0xc4, 0xc5, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xbe, 0xb9, 0xb6, 0xb5, 0xb2, 0xaf, 0xad, 0xaa, 0xa8, 0xa5, 0xa2, 0xac, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0xb4, 0xac, 0xaf, 0xb1, 0xb3, 0xb5, 0xb8, 0xba, 0xbc, 0xbe, 0xc1, 0xc3, 0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0xbb, 0xbb, 0xba, 0xb8, 0xb6, 0xb4, 0xb2, 0xaf, 0xac, 0xaa, 0xa8, 0xac, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0xad, 0xa7, 0xaa, 0xac, 0xaf, 0xb2, 0xb3, 0xb5, 0xb8, 0xba, 0xbc, 0xbe, 0x89, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0xbe, 0xbd, 0xbc, 0xbb, 0xb9, 0xb8, 0xb6, 0xb4, 0xb1, 0xaf, 0xac, 0xae, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0xae, 0xa3, 0xa5, 0xa8, 0xaa, 0xad, 0xaf, 0xb2, 0xb3, 0xb6, 0xb8, 0xb9, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0xc0, 0xc0, 0xbe, 0xbd, 0xbc, 0xbb, 0xb9, 0xb8, 0xb6, 0xb4, 0xb1, 0xb7, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0xb6, 0x9e, 0xa1, 0xa3, 0xa6, 0xa8, 0xaa, 0xad, 0xb0, 0xb2, 0xb3, 0xbb, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xcb, 0xc2, 0xc1, 0xbf, 0xbe, 0xbe, 0xbc, 0xba, 0xb9, 0xb8, 0xb6, 0xc4, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x9c, 0x9c, 0x9e, 0xa1, 0xa3, 0xa6, 0xa8, 0xab, 0xad, 0xb0, 0xca, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd2, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, 0xbe, 0xbd, 0xbc, 0xba, 0xbd, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xa6, 0x9a, 0x99, 0x9c, 0x9f, 0xa1, 0xa4, 0xa6, 0xa8, 0xbd, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0xd1, 0xc4, 0xc4, 0xc3, 0xc2, 0xc0, 0xc0, 0xbe, 0xc0, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x95, 0xa3, 0x9a, 0x9a, 0x9c, 0xa1, 0xa9, 0xb9, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xcb, 0xc9, 0xc6, 0xc4, 0xc2, 0xc4, 0xcb, 0xb2, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x90, 0xb4, 0xb6, 0xb9, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0xd1, 0xd5, 0xd6, 0xb6, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x19, 0x26, 0x36, 0x3b, 0x38, 0x2a, 0x2b, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x05, 0x41, 0x94, 0x95, 0x64, 0x36, 0x1e, 0x15, 0x2c, 0x76, 0xad, 0xad, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x85, 0x31, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x61, 0x00, 0x2e, 0xb2, 0x97, 0x00, 0x00, 0x00, 0x0b, 0x5d, 0xb2, 0xb6, 0xab, 0xa6, 0xa4, 0xaa, 0xb2, 0xbe, 0xbf, 0xb1, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x4e, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x72, 0x7d, 0x5e, 0x4e, 0x49, 0x55, 0x86, 0x8f, 0x41, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x95, 0x00, 0x00, 0x00, 0x3e, 0xb5, 0x81, 0x80, 0xb1, 0xab, 0x9d, 0x95, 0x8f, 0x8e, 0x8d, 0x8c, 0x8d, 0x90, 0x93, 0x97, 0x9f, 0xa8, 0xbb, 0xaf, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x8e, 0x60, 0x1a, 0x4e, 0x86, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x6f, 0x63, 0x16, 0x00, 0x00, 0x00, 0x02, 0x5e, 0x52, 0x5e, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xae, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x73, 0x47, 0x98, 0x96, 0x96, 0x96, 0x94, 0x94, 0x92, 0x90, 0x8f, 0x8e, 0x8d, 0x8e, 0x92, 0x95, 0x97, 0x99, 0x9d, 0xb3, 0xc2, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, 0xb0, 0x33, 0x00, 0x00, 0x0f, 0xad, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x78, 0x00, 0x00, 0x00, 0xac, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xb0, 0x39, 0x00, 0x00, 0x00, 0x42, 0xd1, 0x59, 0x4c, 0x78, 0x9d, 0x9a, 0x98, 0x97, 0x95, 0x95, 0x93, 0x92, 0x90, 0x8f, 0x8e, 0x8d, 0x8f, 0x92, 0x95, 0x98, 0x9b, 0x9b, 0xba, 0x9f, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x30, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0xb4, 0x37, 0x00, 0x00, 0x00, 0x15, 0xac, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x8b, 0x11, 0x00, 0x00, 0x00, 0x00, 0x01, 0x6f, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x7d, 0x00, 0x00, 0x01, 0x92, 0xc7, 0xac, 0x5f, 0x4c, 0x67, 0xa3, 0x9e, 0x9b, 0x99, 0x98, 0x98, 0x96, 0x95, 0x93, 0x91, 0x90, 0x8f, 0x8d, 0x8d, 0x8f, 0x92, 0x95, 0x99, 0x9c, 0xa0, 0xce, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0xb4, 0xb3, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xb4, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x9c, 0x00, 0x00, 0x04,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x9c, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0xb4, 0x13, 0x00, 0x00, 0xce, 0xc2, 0xb5, 0xb5, 0x51, 0x4c, 0x67, 0xa9, 0xa2, 0xa0, 0x9d, 0x9b, 0x99, 0x99, 0x97, 0x96, 0x95, 0x93, 0x92, 0x90, 0x8f, 0x8d, 0x8e, 0x90, 0x93, 0x96, 0x99, 0x9b, 0xc8, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x57, 0x50, 0x00, 0x00, 0x00, 0x00, 0x95, 0xb3, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x83, 0xb3, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x47, 0x00, 0x00, 0x56,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x22, 0xb3, 0x5e, 0x00, 0x00, 0xc3, 0xc7, 0xbd, 0xbb, 0xb5, 0x48, 0x4c, 0x7c, 0xad, 0xa8, 0xa5, 0xa2, 0x9f, 0x9d, 0x9b, 0x99, 0x99, 0x97, 0x95, 0x95, 0x93, 0x92, 0x90, 0x8e, 0x8d, 0x8e, 0x91, 0x93, 0x96, 0x9a, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0xb1, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x48, 0xb4, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x53, 0xb3, 0xb4, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x74, 0x0b,
- 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa8, 0x85, 0x00, 0x00, 0x00, 0x07, 0xb0, 0xb3, 0x0a, 0x00, 0x8d, 0xd1, 0xc5, 0xc2, 0xbf, 0xa9, 0x4a, 0x4b, 0xa0, 0xb1, 0xae, 0xaa, 0xa8, 0xa5, 0xa1, 0x9f, 0x9c, 0x9b, 0x99, 0x98, 0x96, 0x95, 0x94, 0x93, 0x91, 0x8f, 0x8e, 0x8d, 0x8e, 0x91, 0x94, 0x99, 0xbd, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x14, 0xab, 0xb3, 0x42, 0x00, 0x00, 0x00, 0x00, 0xa8, 0xb4, 0x4c, 0x00, 0x00, 0x00, 0x2f, 0xb3, 0xb3, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x52, 0x00, 0x62, 0x18, 0x00,
- 0x00, 0x00, 0x00, 0x73, 0x16, 0x00, 0x00, 0x00, 0x00, 0x25, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xb4, 0x5b, 0x00, 0x00, 0x00, 0x6a, 0xb3, 0x61, 0x00, 0x53, 0xdf, 0xcb, 0xc9, 0xc6, 0xc6, 0x85, 0x4c, 0x51, 0xba, 0xb7, 0xb4, 0xb1, 0xac, 0xaa, 0xa7, 0xa4, 0xa1, 0x9f, 0x9c, 0x9a, 0x98, 0x98, 0x96, 0x95, 0x94, 0x92, 0x91, 0x8f, 0x8f, 0x8e, 0x90, 0x91, 0xa7, 0x5c, 0x97, 0x7a, 0x00, 0x00, 0x00, 0x07, 0x7f, 0xb2, 0xad, 0x01, 0x00, 0x00, 0x00, 0x79, 0xb4, 0x95, 0x00, 0x00, 0x00, 0x14, 0xa2, 0xb3, 0xb2, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x16, 0x6a, 0x0e, 0x00, 0x00,
- 0x00, 0x00, 0x19, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0xb4, 0x21, 0x00, 0x00, 0x28, 0xb4, 0xad, 0x02, 0x00, 0xe0, 0xd0, 0xd0, 0xcd, 0xcb, 0xc8, 0x46, 0x4c, 0xaf, 0xbf, 0xbd, 0xba, 0xb6, 0xb3, 0xb1, 0xaf, 0xad, 0xab, 0xa7, 0xa4, 0xa0, 0x9c, 0x9a, 0x99, 0x97, 0x96, 0x95, 0x93, 0x8f, 0x72, 0x5a, 0x63, 0x8e, 0x8b, 0x41, 0xac, 0x00, 0x00, 0x00, 0x00, 0x60, 0x6e, 0xb3, 0x23, 0x00, 0x00, 0x00, 0x14, 0xb2, 0xb3, 0x25, 0x00, 0x00, 0x00, 0x73, 0x48, 0xb3, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0xa6, 0x46, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x04, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xa8, 0x00, 0x00, 0x00, 0x96, 0xb3, 0x71, 0x00, 0xb0, 0xdd, 0xd6, 0xd4, 0xd2, 0xd0, 0x83, 0x4a, 0x73, 0xc6, 0xc3, 0xc1, 0xbf, 0xbc, 0xca, 0xdb, 0x88, 0x64, 0x21, 0x52, 0x69, 0xce, 0xbe, 0x9d, 0x9c, 0x9a, 0x98, 0x98, 0x72, 0x52, 0x8d, 0x92, 0x83, 0x81, 0x57, 0x4b, 0x3a, 0x00, 0x00, 0x00, 0x69, 0x41, 0xb4, 0xa4, 0x00, 0x00, 0x00, 0x03, 0xb2, 0xb3, 0x6f, 0x00, 0x00, 0x00, 0x7f, 0x04, 0x6e, 0xb3, 0x55, 0x00, 0x00, 0x00, 0x00, 0x35, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x47, 0x06, 0x00, 0x00, 0x00, 0x00, 0x99, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb1, 0x7d, 0x00, 0x00, 0x53, 0xb4, 0xae, 0x00, 0x00, 0xeb, 0xdc, 0xda, 0xd8, 0xd6, 0xb9, 0x49, 0x69, 0xce, 0xca, 0xc8, 0xc5, 0xc5, 0xdf, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0xc7, 0xa5, 0x9e, 0x96, 0x55, 0x5c, 0x97, 0x96, 0x95, 0x86, 0x7f, 0x4c, 0x4a, 0xb3, 0x00, 0x00, 0x2e, 0x28, 0xaf, 0xb1, 0x0d, 0x00, 0x00, 0x00, 0x6a, 0xb3, 0xb3, 0x0e, 0x00, 0x00, 0x2c, 0x1d, 0x00, 0x6e, 0xb3, 0x0e, 0x00, 0x00, 0x00, 0x28, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x09, 0x83, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0xb3, 0x1a, 0x00, 0x00, 0xa9, 0xb3, 0x72, 0x00, 0xaf, 0xe5, 0xe1, 0xdf, 0xdc, 0xd3, 0x4e, 0x74, 0xd4, 0xd0, 0xcf, 0xcc, 0xca, 0xd3, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x35, 0x73, 0x00, 0x00, 0x00, 0x00, 0xb0, 0x93, 0x4e, 0x5b, 0x9e, 0x9a, 0x98, 0x98, 0x74, 0x58, 0x4c, 0x77, 0xa5, 0x33, 0x05, 0x52, 0x73, 0xb4, 0x7e, 0x00, 0x00, 0x00, 0x35, 0xb4, 0xb3, 0x58, 0x00, 0x00, 0x10, 0x56, 0x00, 0x00, 0x53, 0xb2, 0x00, 0x00, 0x00, 0x4b, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x4b, 0x04, 0x00, 0x00, 0x00, 0x1b, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x80, 0x00, 0x00, 0x59, 0xb3, 0xb2, 0x09, 0x08, 0xed, 0xe3, 0xe4, 0xe5, 0xc7, 0x47, 0x8e, 0xde, 0xd7, 0xd5, 0xd3, 0xd0, 0xe2, 0x1c, 0x25, 0x31, 0x01, 0x00, 0x96, 0xb4, 0x17, 0x00, 0x00, 0x00, 0x00, 0x33, 0x54, 0x53, 0xa5, 0xa0, 0x9d, 0x9b, 0x9a, 0x4b, 0x4c, 0x4d, 0x93, 0x98, 0x99, 0x8a, 0x09, 0xb1, 0xb1, 0x01, 0x00, 0x00, 0x0d, 0x76, 0xb4, 0xb2, 0x06, 0x00, 0x05, 0x73, 0x00, 0x00, 0x00, 0x1b, 0xb1, 0x4f, 0x2f, 0x77, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x5d, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0xb3, 0x1d, 0x00, 0x06, 0xb1, 0xb3, 0x6a, 0x00, 0x40, 0xde, 0xde, 0xd8, 0x95, 0x5e, 0xdd, 0xe1, 0xde, 0xdc, 0xd9, 0xd7, 0xd8, 0xa7, 0x92, 0x38, 0x43, 0x37, 0x45, 0xb4, 0x92, 0x00, 0x00, 0x00, 0x00, 0x2a, 0xb3, 0x7e, 0x98, 0xa8, 0xa5, 0xa2, 0x9f, 0x7c, 0x4b, 0x4c, 0x82, 0x97, 0x94, 0xa8, 0x0b, 0x7d, 0xb4, 0x5e, 0x00, 0x00, 0x00, 0x83, 0x19, 0xb3, 0x71, 0x00, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x65, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x1b, 0x00, 0x00, 0x00, 0x2b, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xb3, 0x58, 0x00, 0x00, 0x46, 0xb3, 0xb4, 0x16, 0x00, 0x90, 0x92, 0x84, 0xa8, 0xdf, 0xe3, 0xe4, 0xe4, 0xe2, 0xe0, 0xde, 0xdc, 0x5f, 0x4e, 0x00, 0x00, 0x00, 0x4d, 0x9b, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x1b, 0xb3, 0xb3, 0x58, 0xca, 0xac, 0xaa, 0xa7, 0xa9, 0x4f, 0x4c, 0x4e, 0x99, 0x99, 0x8f, 0x85, 0x00, 0xa9, 0xae, 0x00, 0x00, 0x00, 0x97, 0x0f, 0x1c, 0xb3, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xab, 0xac, 0x01, 0x00, 0x04, 0xb2, 0xb3, 0x6d, 0x00, 0x00, 0xea, 0xdd, 0xde, 0xdf, 0xdf, 0xe0, 0xe1, 0xe3, 0xe4, 0xe5, 0xc9, 0x51, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x95, 0xb4, 0x6d, 0x00, 0x00, 0x00, 0x02, 0xae, 0xb3, 0xb3, 0x02, 0xa5, 0xb2, 0xb0, 0xad, 0x70, 0x55, 0x4a, 0x8a, 0x9f, 0xa1, 0x71, 0xa6, 0x22, 0xaf, 0x4d, 0x00, 0x00, 0x77, 0x19, 0x00, 0x00, 0x9e, 0x87, 0x70, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0xb0, 0x0a, 0x00, 0x00, 0x3e, 0xb3, 0xb4, 0x11, 0x00, 0x1f, 0xe5, 0xd9, 0xda, 0xdc, 0xdd, 0xdf, 0xdf, 0xe1, 0xe4, 0xbb, 0x49, 0x83, 0x6d, 0x00, 0x00, 0x00, 0x4f, 0xb4, 0xae, 0x00, 0x00, 0x00, 0x00, 0x79, 0xb4, 0xb3, 0x5a, 0x00, 0x72, 0xba, 0xb7, 0xa5, 0x7a, 0x48, 0x57, 0xa9, 0xa7, 0x73, 0x9e, 0xa2, 0x5e, 0xb1, 0x23, 0x00, 0x6c, 0x27, 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x19, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0xb3, 0x2e, 0x00, 0x00, 0x00, 0xa5, 0xb3, 0x80, 0x00, 0x00, 0x3a, 0xde, 0xd8, 0xd9, 0xd9, 0xdb, 0xdc, 0xdd, 0xdf, 0xbc, 0x46, 0x56, 0xe6, 0x74, 0x51, 0x51, 0x50, 0x8d, 0x8e, 0x7f, 0x4e, 0x4f, 0x4f, 0x52, 0x73, 0x90, 0x90, 0x4e, 0x4c, 0x91, 0xc1, 0xbc, 0x6e, 0x64, 0x48, 0x92, 0xad, 0x7c, 0x9f, 0xa3, 0xa5, 0x6b, 0x95, 0x77, 0x77, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x3f, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0xb3, 0x37, 0x00, 0x00, 0x00, 0x42, 0xb4, 0xb0, 0x02, 0x00, 0x00, 0x47, 0xd3, 0xd4, 0xd6, 0xd8, 0xd9, 0xda, 0xdb, 0xd0, 0x4a, 0x49, 0xc5, 0xe1, 0xe2, 0xe3, 0xe4, 0xc1, 0x4c, 0x4c, 0xdf, 0xd9, 0xd7, 0xcd, 0xae, 0xaa, 0x48, 0x5e, 0xca, 0xc6, 0xc3, 0xc4, 0x73, 0xba, 0x4a, 0x61, 0xbb, 0x8e, 0xa8, 0xab, 0xa9, 0xa9, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x3d, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0xb2, 0x20, 0x00, 0x00, 0x00, 0x00, 0x98, 0xb3, 0x72, 0x00, 0x00, 0x00, 0x68, 0xca, 0xce, 0xd1, 0xd5, 0xd7, 0xd8, 0xdb, 0x59, 0x4c, 0x6e, 0xde, 0xdf, 0xe0, 0xe1, 0xdf, 0x48, 0x4c, 0xb3, 0xe1, 0xde, 0xda, 0x81, 0xda, 0xac, 0x46, 0x9b, 0xce, 0xcc, 0xcd, 0x89, 0xc4, 0x6a, 0x48, 0xa1, 0x89, 0xb4, 0xb5, 0xb2, 0xaf, 0xac, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x05, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x89, 0x94, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x3f, 0xb3, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x8b, 0xc4, 0xc9, 0xcb, 0xce, 0xd2, 0xd5, 0x6d, 0x4b, 0x44, 0xc7, 0xdb, 0xdc, 0xde, 0xe3, 0x94, 0x4c, 0x47, 0xe1, 0xe4, 0xe5, 0x7a, 0xdc, 0xdc, 0xc1, 0x42, 0xbe, 0xd3, 0xd8, 0x7c, 0xc0, 0xc7, 0x4f, 0x6e, 0x7d, 0xb9, 0xbe, 0xbb, 0xb8, 0xb4, 0xb2, 0xc2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x72, 0x1e, 0x00, 0x00, 0x00, 0x5b, 0x95, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa2, 0xb4, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x8e, 0xbe, 0xc2, 0xc5, 0xc9, 0xcc, 0xcc, 0x50, 0x4c, 0x6d, 0xde, 0xd9, 0xda, 0xdb, 0xb4, 0x5e, 0x4b, 0xa8, 0xe3, 0xe8, 0xa5, 0xdb, 0xe1, 0xdf, 0xe2, 0x61, 0x71, 0x98, 0x81, 0xc5, 0xd1, 0x9c, 0x4b, 0x7f, 0xc3, 0xc4, 0xc2, 0xbf, 0xbd, 0xbb, 0xb8, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x63, 0x67, 0x7b, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0xb3, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8c, 0xb8, 0xbc, 0xbf, 0xc3, 0xc6, 0xc7, 0x50, 0x4a, 0xa3, 0xd5, 0xd7, 0xd9, 0xcf, 0x9a, 0x87, 0x50, 0xdb, 0xe3, 0xb1, 0xbf, 0xe2, 0xe4, 0xe4, 0xe2, 0xdd, 0xb0, 0xc2, 0xde, 0xd8, 0xce, 0x4c, 0x4b, 0xcb, 0xcb, 0xc9, 0xc6, 0xc4, 0xc1, 0xbf, 0xbd, 0xc9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0xb3, 0xb1, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87, 0xb1, 0xb6, 0xba, 0xbc, 0xc0, 0xc0, 0x4f, 0x45, 0xc2, 0xd0, 0xd4, 0xd5, 0x87, 0xe2, 0x94, 0x57, 0xdc, 0xac, 0xb9, 0xdf, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe1, 0xe0, 0xde, 0xc8, 0x70, 0x4b, 0xa1, 0xd3, 0xcf, 0xcd, 0xcb, 0xc9, 0xc6, 0xc4, 0xc2, 0xca, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x69, 0xb3, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0xab, 0xb0, 0xb3, 0xb7, 0xba, 0xbc, 0x50, 0x48, 0xce, 0xca, 0xcc, 0x70, 0xd4, 0xd7, 0xc9, 0x51, 0x5c, 0xd2, 0xdb, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe3, 0xe5, 0xe3, 0xb1, 0xba, 0x77, 0x50, 0xd4, 0xd6, 0xd4, 0xd1, 0xcf, 0xcd, 0xca, 0xc9, 0xc8, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xb3, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xa6, 0xaa, 0xac, 0xb0, 0xb4, 0xb8, 0x6a, 0x45, 0xc6, 0xb8, 0x78, 0xc8, 0xd6, 0xd9, 0xdc, 0xde, 0xe1, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe9, 0x91, 0xd6, 0xd7, 0x3f, 0xac, 0xe3, 0xe2, 0xdf, 0xde, 0xdb, 0xda, 0xd8, 0xd6, 0xd5, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa3, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0xa8, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb2, 0x72, 0x72, 0x93, 0xc4, 0xca, 0x4d, 0x25, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x25, 0x26, 0x24, 0x95, 0x1e, 0x25, 0x75, 0x91, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x24, 0x24, 0x24, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0xad, 0x9c, 0xa0, 0xa3, 0xa7, 0xaa, 0xae, 0xb4, 0xb9, 0xb9, 0xbc, 0xbe, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x74, 0x00, 0x00, 0x00, 0xb0, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4d, 0x9c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xb5, 0x95, 0x99, 0x9c, 0xa0, 0xa3, 0xa7, 0xab, 0xae, 0xb2, 0xb4, 0xb8, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x84, 0x00, 0x00, 0x00, 0x71, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x01, 0x22, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x27, 0x94, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb3, 0x8e, 0x92, 0x96, 0x9a, 0x9d, 0xa1, 0xa4, 0xa8, 0xab, 0xae, 0xb2, 0xc5, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x01, 0x00, 0x00, 0x00, 0xb1, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x1a, 0xb1, 0xb2, 0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x8a, 0x8b, 0x8f, 0x92, 0x96, 0x9a, 0x9d, 0xa1, 0xa4, 0xa8, 0xac, 0xae, 0xd0, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x0f, 0x00, 0x00, 0x00, 0x8c, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x60, 0xb3, 0xb3, 0x9f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x9c, 0x85, 0x88, 0x8c, 0x8f, 0x92, 0x97, 0x9b, 0x9e, 0xa2, 0xa5, 0xa9, 0xae, 0xca, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x90, 0x20, 0x00, 0x00, 0x00, 0x14, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x6a, 0xb3, 0xb3, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x6e, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xac, 0x81, 0x83, 0x86, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9a, 0x9e, 0xa2, 0xa5, 0xaa, 0xcb, 0x57, 0x00, 0x00, 0x4d, 0x73, 0x00, 0x00, 0x00, 0x01, 0xa2, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x67, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x2e, 0xb2, 0xb3, 0x8b, 0x15, 0x1b, 0x34, 0x7a, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x88, 0x7d, 0x81, 0x84, 0x86, 0x89, 0x8d, 0x90, 0x93, 0x98, 0x9b, 0x9f, 0xa3, 0xa4, 0xba, 0xcd, 0x51, 0xa1, 0x05, 0x00, 0x00, 0x00, 0x56, 0x32, 0x00, 0x00, 0x00, 0x12, 0x17, 0x2c, 0x80, 0xd8, 0xe9, 0xe2, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x19, 0x82, 0x97, 0x88, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x94, 0x77, 0x7b, 0x7e, 0x81, 0x84, 0x87, 0x89, 0x8d, 0x91, 0x94, 0x98, 0x9b, 0x9f, 0xa3, 0xa6, 0x7f, 0x6b, 0xd5, 0xd6, 0xd0, 0x89, 0x98, 0xb0, 0xd6, 0xe1, 0xe4, 0xe4, 0xdf, 0xdb, 0xd7, 0xd7, 0xd9, 0xda, 0xeb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x89, 0x75, 0x79, 0x7b, 0x7e, 0x82, 0x85, 0x88, 0x89, 0x8d, 0x91, 0x94, 0x99, 0x9c, 0xa1, 0x6a, 0x72, 0xaa, 0xaf, 0xa4, 0x7d, 0xbb, 0xbd, 0xbf, 0xc2, 0xc6, 0xc8, 0xcb, 0xce, 0xd2, 0xd5, 0xd7, 0xd8, 0xe9, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x77, 0x73, 0x76, 0x79, 0x7c, 0x7f, 0x82, 0x85, 0x88, 0x8a, 0x8e, 0x92, 0x95, 0x9a, 0x6c, 0x64, 0xa8, 0x7d, 0x83, 0xb0, 0xb2, 0xb6, 0xb9, 0xbc, 0xbf, 0xc2, 0xc6, 0xc9, 0xcc, 0xcf, 0xd3, 0xd5, 0xe2, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x88, 0x6f, 0x70, 0x73, 0x76, 0x79, 0x7c, 0x7f, 0x82, 0x85, 0x88, 0x8b, 0x8f, 0x92, 0x8f, 0x68, 0x6c, 0x93, 0xa6, 0xa8, 0xac, 0xaf, 0xb2, 0xb6, 0xb9, 0xbc, 0xc0, 0xc2, 0xc6, 0xca, 0xcc, 0xd0, 0xd9, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x8c, 0x6d, 0x6e, 0x71, 0x74, 0x76, 0x7a, 0x7d, 0x80, 0x83, 0x85, 0x88, 0x8b, 0x8f, 0x93, 0x97, 0x9a, 0x9e, 0xa1, 0xa5, 0xa8, 0xac, 0xaf, 0xb3, 0xb7, 0xb9, 0xbd, 0xc0, 0xc3, 0xc7, 0xca, 0xcc, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x89, 0x6c, 0x6b, 0x6e, 0x71, 0x75, 0x77, 0x7a, 0x7d, 0x80, 0x83, 0x85, 0x89, 0x8b, 0x90, 0x93, 0x96, 0x9b, 0x9e, 0xa2, 0xa6, 0xa9, 0xac, 0xb0, 0xb4, 0xb7, 0xba, 0xbd, 0xc0, 0xc4, 0xc6, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x78, 0x77, 0x68, 0x6c, 0x6e, 0x72, 0x75, 0x77, 0x7b, 0x7d, 0x80, 0x83, 0x86, 0x89, 0x8c, 0x90, 0x93, 0x97, 0x9b, 0x9e, 0xa2, 0xa6, 0xa9, 0xad, 0xb0, 0xb4, 0xb7, 0xba, 0xbd, 0xc1, 0xd3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x82, 0x6f, 0x68, 0x6c, 0x6f, 0x73, 0x75, 0x78, 0x7a, 0x7e, 0x80, 0x84, 0x86, 0x8a, 0x8c, 0x90, 0x94, 0x98, 0x9c, 0x9f, 0xa3, 0xa6, 0xaa, 0xad, 0xb1, 0xb5, 0xb7, 0xba, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4b, 0x7d, 0x73, 0x69, 0x6b, 0x70, 0x72, 0x75, 0x78, 0x7b, 0x7e, 0x81, 0x84, 0x87, 0x8a, 0x8d, 0x91, 0x94, 0x98, 0x9c, 0xa0, 0xa3, 0xa6, 0xa9, 0xb1, 0xbd, 0xc4, 0x85, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x70, 0x7a, 0x72, 0x71, 0x71, 0x73, 0x75, 0x78, 0x7c, 0x7e, 0x81, 0x84, 0x86, 0x89, 0x8d, 0x93, 0x99, 0x9e, 0xa6, 0xae, 0xa5, 0x8c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x50, 0x7f, 0x85, 0x88, 0x87, 0x86, 0x89, 0x8d, 0x90, 0x97, 0x9e, 0x9d, 0x9b, 0x78, 0x4d, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
diff --git a/yoRadio/src/displays/fonts/yofont10x14.c b/yoRadio/src/displays/fonts/yofont10x14.c
index 89f8b11..88a1700 100644
--- a/yoRadio/src/displays/fonts/yofont10x14.c
+++ b/yoRadio/src/displays/fonts/yofont10x14.c
@@ -1,5 +1,3 @@
-#include "../../../options.h"
-#if DSP_MODEL==DSP_ILI9225
#ifndef YOFONT10X14_H
#define YOFONT10X14_H
@@ -274,5 +272,3 @@ const unsigned char yofont10x14[] PROGMEM = {
0x0A, 0xC0, 0x30, 0xC0, 0x30, 0x30, 0x0F, 0x30, 0x0F, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0x30, 0x03, 0xF0, 0x3F, 0xF0, 0x3F
};
#endif // YOFONT10X14_H
-
-#endif //DSP_ILI9225
diff --git a/yoRadio/src/displays/fonts/yofont5x7.c b/yoRadio/src/displays/fonts/yofont5x7.c
index 28162be..3d04795 100644
--- a/yoRadio/src/displays/fonts/yofont5x7.c
+++ b/yoRadio/src/displays/fonts/yofont5x7.c
@@ -1,5 +1,3 @@
-#include "../../../options.h"
-#if DSP_MODEL==DSP_ILI9225
#ifndef YOFONTFONT5X7_H
#define YOFONTFONT5X7_H
@@ -272,4 +270,3 @@ const unsigned char yofont5x7[] PROGMEM = {
0x05, 0x48, 0x34, 0x14, 0x14, 0x7C
};
#endif // YOFONTFONT5X7_H
-#endif //DSP_ILI9225
diff --git a/yoRadio/src/displays/nextion.cpp b/yoRadio/src/displays/nextion.cpp
index 2d8b0ce..26f1d1a 100644
--- a/yoRadio/src/displays/nextion.cpp
+++ b/yoRadio/src/displays/nextion.cpp
@@ -1,41 +1,19 @@
-#include "../../options.h"
+#include "../core/options.h"
#if NEXTION_RX!=255 && NEXTION_TX!=255
#include "nextion.h"
-#include "../../config.h"
+#include "../core/config.h"
-#include "../../player.h"
-#include "../../controls.h"
-#include "../../netserver.h"
-#include "../../network.h"
+#include "../core/player.h"
+#include "../core/controls.h"
+#include "../core/netserver.h"
+#include "../core/network.h"
#ifndef CORE_STACK_SIZE
-#define CORE_STACK_SIZE 1024*3
+ #define CORE_STACK_SIZE 1024*3
#endif
HardwareSerial hSerial(1); // use UART1
-Ticker weatherticker;
-//char weather[254] = { 0 };
-//bool weatherRequest = false;
-
-
-const char *ndow[7] = {"воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"};
-const char *nmnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"};
-
-#ifdef DUMMYDISPLAY
-void ticks() {
- network.timeinfo.tm_sec ++;
- mktime(&network.timeinfo);
- nextion.putRequest({CLOCK,0});
- if(nextion.mode==TIMEZONE) nextion.localTime(network.timeinfo);
- if(nextion.mode==INFO) nextion.rssi();
- if(nextion.dt){
- int rssi = WiFi.RSSI();
- netserver.setRSSI(rssi);
- }
- nextion.dt=!nextion.dt;
-}
-#endif
Nextion::Nextion() {
@@ -44,24 +22,12 @@ Nextion::Nextion() {
void nextionCore0( void * pvParameters ){
delay(500);
while(true){
-// if(displayQueue==NULL) break;
nextion.loop();
vTaskDelay(5);
}
vTaskDelete( NULL );
}
-void Nextion::createCore0Task(){
- xTaskCreatePinnedToCore(
- nextionCore0, /* Task function. */
- "TaskCore0", /* name of task. */
- CORE_STACK_SIZE, /* Stack size of task */
- NULL, /* parameter of the task */
- 4, /* no one flies higher than the Toruk */
- &_TaskCore0, /* Task handle to keep track of created task */
- !xPortGetCoreID()); /* pin task to core 0 */
-}
-
void Nextion::begin(bool dummy) {
_dummyDisplay=dummy;
hSerial.begin(NEXTION_BAUD, SERIAL_8N1, NEXTION_RX, NEXTION_TX);
@@ -80,9 +46,10 @@ void Nextion::begin(bool dummy) {
putcmd("");
putcmd("bkcmd=0");
// putcmd("page boot");
- if(dummy) {
+
_displayQueue = xQueueCreate( 5, sizeof( requestParams_t ) );
- createCore0Task();
+ if(dummy) {
+ xTaskCreatePinnedToCore(nextionCore0, "TaskCore0", CORE_STACK_SIZE, NULL, 4, &_TaskCore0, !xPortGetCoreID());
}
}
@@ -92,30 +59,20 @@ void Nextion::start(){
return;
}
#ifdef DUMMYDISPLAY
- if(_dummyDisplay) _timer.attach_ms(1000, ticks);
- display.mode = PLAYER;
- config.setTitle("[READY]");
+ display.mode(PLAYER);
+ config.setTitle(const_PlReady);
#endif
mode = PLAYER;
putcmd("page player");
delay(100);
-#ifdef DUMMYDISPLAY
newNameset(config.station.name);
newTitle(config.station.title);
-#endif
setVol(config.store.volume, mode == VOL);
}
void Nextion::apScreen() {
putcmd("apscreenlock=1");
putcmd("page settings_wifi");
- //char cmd[20];
- /*for(int i=0;i<5;i++){
- snprintf(cmd, sizeof(cmd)-1, "vis b%d,%d", i, 0);
- putcmd(cmd);
- }*/
- //putcmd("vis btnBack,0");
-
}
void Nextion::putRequest(requestParams_t request){
@@ -128,20 +85,14 @@ void Nextion::processQueue(){
requestParams_t request;
if(xQueueReceive(_displayQueue, &request, 20)){
switch (request.type){
- case NEWMODE: {
- swichMode((displayMode_e)request.payload);
- break;
- }
- case CLOCK: {
- printClock(network.timeinfo);
- break;
- }
- case NEWTITLE: {
- newTitle(config.station.title);
- break;
- }
- case RETURNTITLE: {
- //returnTile();
+ case NEWMODE: swichMode((displayMode_e)request.payload); break;
+ case CLOCK: printClock(network.timeinfo); break;
+ case DSPRSSI: rssi(); break;
+ case NEWTITLE: newTitle(config.station.title); break;
+ case BOOTSTRING: {
+ char buf[50];
+ snprintf(buf, 50, bootstrFmt, config.ssids[request.payload].ssid);
+ bootString(buf);
break;
}
case NEWSTATION: {
@@ -150,18 +101,9 @@ void Nextion::processQueue(){
bitratePic(ICON_NA);
break;
}
- case NEXTSTATION: {
- drawNextStationNum((displayMode_e)request.payload);
- break;
- }
- case DRAWPLAYLIST: {
- int p = request.payload ? display.currentPlItem + 1 : display.currentPlItem - 1;
- if (p < 1) p = config.store.countStation;
- if (p > config.store.countStation) p = 1;
- display.currentPlItem = p;
- drawPlaylist(display.currentPlItem);
- break;
- }
+ case SHOWWEATHER: weatherVisible(strlen(config.store.weatherkey)>0 && config.store.showweather); break;
+ case NEXTSTATION: drawNextStationNum(request.payload); break;
+ case DRAWPLAYLIST: drawPlaylist(request.payload); break;
case DRAWVOL: {
if(!_volInside){
setVol(config.store.volume, mode == VOL);
@@ -169,36 +111,17 @@ void Nextion::processQueue(){
_volInside=false;
break;
}
+ default: break;
}
}
- switch (mode) {
- case PLAYER: {
- //drawPlayer();
- break;
- }
- case INFO:
- case TIMEZONE: {
- //sendInfo();
- break;
- }
- case VOL: {
- if (millis() - _volDelay > 3000) {
- _volDelay = millis();
- swichMode(PLAYER);
- }
- break;
- }
- case NUMBERS: {
- //meta.loop();
- break;
- }
- case STATIONS: {
- //plCurrent.loop();
- break;
- }
- default:
- break;
+#ifdef DUMMYDISPLAY
+ if(mode==VOL || mode==STATIONS || mode==NUMBERS ){
+ if (millis() - _volDelay > (mode==VOL?3000:30000)) {
+ _volDelay = millis();
+ swichMode(PLAYER);
+ }
}
+#endif
}
void Nextion::loop() {
@@ -230,14 +153,14 @@ void Nextion::loop() {
rxbuf[rx_pos] = '\0';
rx_pos = 0;
if (sscanf(rxbuf, "page=%s", scanBuf) == 1){
- if(strcmp(scanBuf, "player") == 0) display.putRequest({NEWMODE, PLAYER});
- if(strcmp(scanBuf, "playlist") == 0) display.putRequest({NEWMODE, STATIONS});
+ if(strcmp(scanBuf, "player") == 0) display.putRequest(NEWMODE, PLAYER);
+ if(strcmp(scanBuf, "playlist") == 0) display.putRequest(NEWMODE, STATIONS);
if(strcmp(scanBuf, "info") == 0) {
putcmd("yoversion.txt", VERSION);
putcmd("espcore.txt", _espcoreversion);
putcmd("ipaddr.txt", WiFi.localIP().toString().c_str());
putcmd("ssid.txt", WiFi.SSID().c_str());
- display.putRequest({NEWMODE, INFO});
+ display.putRequest(NEWMODE, INFO);
}
if(strcmp(scanBuf, "eq") == 0) {
putcmd("t4.txt", config.store.balance, true);
@@ -248,7 +171,7 @@ void Nextion::loop() {
putcmd("h2.val", config.store.middle+16);
putcmd("t7.txt", config.store.bass, true);
putcmd("h3.val", config.store.bass+16);
- display.putRequest({NEWMODE, SETTINGS});
+ display.putRequest(NEWMODE, SETTINGS);
}
if(strcmp(scanBuf, "wifi") == 0) {
if(mode != WIFI){
@@ -260,7 +183,7 @@ void Nextion::loop() {
snprintf(cell, sizeof(cell) - 1, "t%d.txt", i*2+1);
putcmd(cell, config.ssids[i].password);
}
- display.putRequest({NEWMODE, WIFI});
+ display.putRequest(NEWMODE, WIFI);
}
}
if(strcmp(scanBuf, "time") == 0) {
@@ -268,25 +191,31 @@ void Nextion::loop() {
putcmd("tzHour.val", config.store.tzHour);
putcmdf("tzMinText.txt=\"%02d\"", config.store.tzMin);
putcmd("tzMin.val", config.store.tzMin);
- display.putRequest({NEWMODE, TIMEZONE});
+ display.putRequest(NEWMODE, TIMEZONE);
}
if(strcmp(scanBuf, "sys") == 0) {
putcmd("smartstart.val", config.store.smartstart==2?0:1);
putcmd("audioinfo.val", config.store.audioinfo);
- display.putRequest({NEWMODE, SETTINGS});
+ display.putRequest(NEWMODE, SETTINGS);
}
}
if (sscanf(rxbuf, "ctrls=%s", scanBuf) == 1){
if(strcmp(scanBuf, "up") == 0) {
display.resetQueue();
- display.putRequest({DRAWPLAYLIST, false});
+ int p = display.currentPlItem - 1;
+ if (p < 1) p = config.store.countStation;
+ display.currentPlItem = p;
+ display.putRequest(DRAWPLAYLIST, p);
}
if(strcmp(scanBuf, "dn") == 0) {
display.resetQueue();
- display.putRequest({DRAWPLAYLIST, true});
+ int p = display.currentPlItem + 1;
+ if (p > config.store.countStation) p = 1;
+ display.currentPlItem = p;
+ display.putRequest(DRAWPLAYLIST, p);
}
if(strcmp(scanBuf, "go") == 0) {
- display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, PLAYER);
player.request.station=display.currentPlItem;
}
if(strcmp(scanBuf, "toggle") == 0) {
@@ -320,23 +249,21 @@ void Nextion::loop() {
}
if (sscanf(rxbuf, "tzhour=%d", &scanDigit) == 1){
config.setTimezone((int8_t)scanDigit, config.store.tzMin);
- //configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), SNTP_SERVER);
if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
}else if(strlen(config.store.sntp1)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
}
- network.requestTimeSync(true);
+ network.forceTimeSync = true;
}
if (sscanf(rxbuf, "tzmin=%d", &scanDigit) == 1){
config.setTimezone(config.store.tzHour, (int8_t)scanDigit);
- //configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), SNTP_SERVER);
if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
}else if(strlen(config.store.sntp1)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
}
- network.requestTimeSync(true);
+ network.forceTimeSync = true;
}
if (sscanf(rxbuf, "audioinfo=%d", &scanDigit) == 1){
config.store.audioinfo = scanDigit;
@@ -411,7 +338,7 @@ void Nextion::bitrate(int bpm){
if(bpm>0){
putcmd("player.bitrate.txt", bpm, true);
}else{
- putcmd("player.bitrate.txt=\"und\"");
+ putcmd("player.bitrate.txt=\" \"");
}
}
@@ -434,6 +361,13 @@ void Nextion::bitratePic(uint8_t pic){
putcmd("player.bitrate.pic", pic);
}
+void Nextion::audioinfo(const char* info){
+ if (strstr(info, "format is aac") != NULL) bitratePic(ICON_AAC);
+ if (strstr(info, "format is flac") != NULL) bitratePic(ICON_FLAC);
+ if (strstr(info, "format is mp3") != NULL) bitratePic(ICON_MP3);
+ if (strstr(info, "format is wav") != NULL) bitratePic(ICON_WAV);
+}
+
void Nextion::bootString(const char* bs) {
char buf[50] = { 0 };
strlcpy(buf, bs, 50);
@@ -449,10 +383,7 @@ void Nextion::newNameset(const char* meta){
void Nextion::setVol(uint8_t vol, bool dialog){
if(dialog){
putcmd("dialog.text.txt", vol, true);
- }/*else{
- putcmd("player.volText.txt", vol, true);
- putcmd("player.volumeSlider.val", vol);
- }*/
+ }
putcmd("player.volText.txt", vol, true);
putcmd("player.volumeSlider.val", vol);
}
@@ -480,12 +411,14 @@ void Nextion::newTitle(const char* title){
}
void Nextion::printClock(struct tm timeinfo){
- char timeStringBuff[70] = { 0 };
+ char timeStringBuff[100] = { 0 };
strftime(timeStringBuff, sizeof(timeStringBuff), "player.clock.txt=\"%H:%M\"", &timeinfo);
putcmd(timeStringBuff);
putcmdf("player.secText.txt=\"%02d\"", timeinfo.tm_sec);
- snprintf(timeStringBuff, sizeof(timeStringBuff), "player.dateText.txt=\"%s, %d %s %d\"", ndow[timeinfo.tm_wday], timeinfo.tm_mday, nmnths[timeinfo.tm_mon], timeinfo.tm_year+1900);
+ snprintf(timeStringBuff, sizeof(timeStringBuff), "player.dateText.txt=\"%s, %d %s %d\"", dowf[timeinfo.tm_wday], timeinfo.tm_mday, mnths[timeinfo.tm_mon], timeinfo.tm_year+1900);
putcmd(utf8Rus(timeStringBuff, false));
+ if(mode==TIMEZONE) localTime(network.timeinfo);
+ if(mode==INFO) rssi();
}
void Nextion::localTime(struct tm timeinfo){
@@ -505,6 +438,7 @@ void Nextion::drawPlaylist(uint16_t currentPlItem){
snprintf(cmd, sizeof(cmd) - 1, "t%d.txt=\"%s\"", i, nextion.utf8Rus(plMenu[i], true));
putcmd(cmd);
}
+ _volDelay = millis();
}
void Nextion::drawNextStationNum(uint16_t num) {//dialog
@@ -512,10 +446,9 @@ void Nextion::drawNextStationNum(uint16_t num) {//dialog
char currentItemText[40] = {0};
config.fillPlMenu(plMenu, num, 1, true);
strlcpy(currentItemText, plMenu[0], 39);
- //meta.setText(dsp.utf8Rus(currentItemText, true));
putcmd("dialog.title.txt", utf8Rus(currentItemText, true));
putcmd("dialog.text.txt", num, true);
- //dsp.drawNextStationNum(num);
+ _volDelay = millis();
}
void Nextion::swichMode(displayMode_e newmode){
@@ -525,12 +458,8 @@ void Nextion::swichMode(displayMode_e newmode){
if (newmode == mode) return;
mode = newmode;
#ifdef DUMMYDISPLAY
- display.mode = newmode;
+ display.mode(newmode);
#endif
-/* if (newmode != STATIONS) {
- ip();
- volume();
- }*/
if (newmode == PLAYER) {
putcmd("page player");
putcmd("dialog.title.txt", "");
@@ -560,158 +489,6 @@ void Nextion::swichMode(displayMode_e newmode){
}
}
-bool Nextion::getForecast(){
- WiFiClient client;
- const char* host = "api.openweathermap.org";
- if (!client.connect(host, 80)) {
- Serial.println("## OPENWEATHERMAP ###: connection failed");
- return false;
- }
- char httpget[250] = {0};
- sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=metric&lang=ru&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", config.store.weatherlat, config.store.weatherlon, config.store.weatherkey, host);
- client.print(httpget);
- unsigned long timeout = millis();
- while (client.available() == 0) {
- if (millis() - timeout > 2000UL) {
- Serial.println("## OPENWEATHERMAP ###: client available timeout !");
- client.stop();
- return false;
- }
- }
- timeout = millis();
- String line = "";
- if (client.connected()) {
- while (client.available())
- {
- line = client.readStringUntil('\n');
- if (strstr(line.c_str(), "\"temp\"") != NULL) {
- client.stop();
- break;
- }
- if ((millis() - timeout) > 500)
- {
- client.stop();
- Serial.println("## OPENWEATHERMAP ###: client read timeout !");
- return false;
- }
- }
- }
- if (strstr(line.c_str(), "\"temp\"") == NULL) {
- Serial.println("## OPENWEATHERMAP ###: weather not found !");
- return false;
- }
- char *tmpe;
- char *tmps;
- const char* cursor = line.c_str();
- char desc[120], temp[20], hum[20], press[20], icon[5];
-
- tmps = strstr(cursor, "\"description\":\"");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
- tmps += 15;
- tmpe = strstr(tmps, "\",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
- strlcpy(desc, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
-
- // "ясно","icon":"01d"}],
- tmps = strstr(cursor, "\"icon\":\"");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
- tmps += 8;
- tmpe = strstr(tmps, "\"}");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
- strlcpy(icon, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
-
- tmps = strstr(cursor, "\"temp\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
- tmps += 7;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
- strlcpy(temp, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
- float tempf = atof(temp);
- tmps = strstr(cursor, "\"pressure\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
- tmps += 11;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
- strlcpy(press, tmps, tmpe - tmps + 1);
- cursor = tmpe + 2;
- int pressi = (float)atoi(press) / 1.333;
-
- tmps = strstr(cursor, "humidity\":");
- if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
- tmps += 10;
- tmpe = strstr(tmps, ",\"");
- if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
- strlcpy(hum, tmps, tmpe - tmps + 1);
-
- if(config.store.audioinfo) Serial.printf("## OPENWEATHERMAP ###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
-
- putcmdf("press_txt.txt=\"%dmm\"", pressi);
- putcmdf("hum_txt.txt=\"%d%%\"", atoi(hum));
- char cmd[30];
- snprintf(cmd, sizeof(cmd)-1,"temp_txt.txt=\"%.1f\"", tempf);
- putcmd(cmd);
- int iconofset;
- if(strstr(icon,"01")!=NULL){
- iconofset = 0;
- }else if(strstr(icon,"02")!=NULL){
- iconofset = 1;
- }else if(strstr(icon,"03")!=NULL){
- iconofset = 2;
- }else if(strstr(icon,"04")!=NULL){
- iconofset = 3;
- }else if(strstr(icon,"09")!=NULL){
- iconofset = 4;
- }else if(strstr(icon,"10")!=NULL){
- iconofset = 5;
- }else if(strstr(icon,"11")!=NULL){
- iconofset = 6;
- }else if(strstr(icon,"13")!=NULL){
- iconofset = 7;
- }else if(strstr(icon,"50")!=NULL){
- iconofset = 8;
- }else{
- iconofset = 9;
- }
- putcmd("cond_img.pic", 50+iconofset);
- weatherVisible(1);
- return true;
-}
-
-void Nextion::getWeather(void * pvParameters){
- delay(200);
- if (nextion.getForecast()) {
-// nextion.weatherRequest = true;
- weatherticker.detach();
- weatherticker.attach(WEATHER_REQUEST_INTERVAL, nextion.updateWeather);
- } else {
- weatherticker.detach();
- weatherticker.attach(WEATHER_REQUEST_INTERVAL_FAULTY, nextion.updateWeather);
- }
- vTaskDelete( NULL );
-}
-
-void Nextion::updateWeather() {
- if(strlen(config.store.weatherkey)==0 || !config.store.showweather) {
- nextion.weatherVisible(0);
- return;
- }
- xTaskCreatePinnedToCore(
- nextion.getWeather, /* Task function. */
- "nextiongetWeather", /* name of task. */
- 1024 * 4, /* Stack size of task */
- NULL, /* parameter of the task */
- 0, /* priority of the task */
- &nextion.weatherUpdateTaskHandle, /* Task handle to keep track of created task */
- 0); /* pin task to core 0 */
-}
-
-void Nextion::startWeather(){
- updateWeather();
-}
-
void Nextion::sleep(void) {
putcmd("sleep=1");
}
diff --git a/yoRadio/src/displays/nextion.h b/yoRadio/src/displays/nextion.h
index af22cdf..b514aa8 100644
--- a/yoRadio/src/displays/nextion.h
+++ b/yoRadio/src/displays/nextion.h
@@ -1,9 +1,8 @@
#ifndef NEXTION_H
#define NEXTION_H
-//#include
#include
-#include "../../display.h"
+#include "../core/display.h"
#define TXBUFLEN 255
#define RXBUFLEN 50
@@ -17,9 +16,6 @@
#define ICON_MP3 ICON_NA+3
#define ICON_WAV ICON_NA+4
-#define WEATHER_REQUEST_INTERVAL 1800 //30min
-#define WEATHER_REQUEST_INTERVAL_FAULTY 30
-
class Nextion {
private:
char txbuf[TXBUFLEN];
@@ -31,16 +27,12 @@ class Nextion {
QueueHandle_t _displayQueue=NULL;
bool _dummyDisplay;
bool _volInside;
- Ticker _timer;
unsigned long _volDelay;
- void createCore0Task();
void processQueue();
void drawVU();
public:
displayMode_e mode;
bool dt;
- TaskHandle_t weatherUpdateTaskHandle;
-// bool weatherRequest;
public:
Nextion();
void begin(bool dummy=false);
@@ -60,6 +52,7 @@ class Nextion {
void printClock(struct tm timeinfo);
void bitrate(int bpm);
void bitratePic(uint8_t pic);
+ void audioinfo(const char* info);
void rssi();
void weatherVisible(uint8_t vis);
void localTime(struct tm timeinfo);
@@ -67,12 +60,8 @@ class Nextion {
void swichMode(displayMode_e newmode);
void drawNextStationNum(uint16_t num);
void putRequest(requestParams_t request);
- void startWeather();
- bool getForecast();
- static void updateWeather();
- static void getWeather(void * pvParameters);
- void sleep();
- void wake();
+ void sleep();
+ void wake();
};
extern Nextion nextion;
diff --git a/yoRadio/src/displays/tools/commongfx.h b/yoRadio/src/displays/tools/commongfx.h
new file mode 100644
index 0000000..64b976f
--- /dev/null
+++ b/yoRadio/src/displays/tools/commongfx.h
@@ -0,0 +1,82 @@
+#ifndef common_gfx_h
+#define common_gfx_h
+
+ public:
+ DspCore();
+ char plMenu[PLMITEMS][PLMITEMLENGHT];
+ void initDisplay();
+ void drawLogo(uint16_t top);
+ void clearDsp(bool black=false);
+ void printClock(){}
+ void printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw);
+ void clearClock();
+ char* utf8Rus(const char* str, bool uppercase);
+ void drawPlaylist(uint16_t currentItem, char* currentItemText);
+ void loop(bool force=false);
+ void charSize(uint8_t textsize, uint8_t& width, uint16_t& height);
+ #ifndef DSP_LCD
+ #if DSP_MODEL==DSP_NOKIA5110
+ virtual void command(uint8_t c);
+ virtual void data(uint8_t c);
+ #else
+ virtual void startWrite(void);
+ virtual void endWrite(void);
+ #endif
+ void setTextSize(uint8_t s);
+ #else
+ uint16_t width();
+ uint16_t height();
+ void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
+ void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color){}
+ void setTextSize(uint8_t s){}
+ void setTextSize(uint8_t sx, uint8_t sy){}
+ void setTextColor(uint16_t c, uint16_t bg){}
+ void setFont(){}
+ void apScreen();
+ #endif
+
+ void flip();
+ void invert();
+ void sleep();
+ void wake();
+ void writePixel(int16_t x, int16_t y, uint16_t color);
+ void writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
+ void setClipping(clipArea ca);
+ void clearClipping();
+ void setScrollId(void * scrollid) { _scrollid = scrollid; }
+ void * getScrollId() { return _scrollid; }
+ void setNumFont();
+ uint16_t textWidth(const char *txt);
+ #if DSP_MODEL==DSP_ILI9225
+ uint16_t width(void) { return (int16_t)maxX(); }
+ uint16_t height(void) { return (int16_t)maxY(); }
+ void drawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_t w, int16_t h);
+ uint16_t print(const char* s);
+ void fillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
+ void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color);
+ void setFont(const GFXfont *f = NULL);
+ void setFont(uint8_t* font, bool monoSp=false );
+ void setTextColor(uint16_t fg, uint16_t bg);
+ void setCursor(int16_t x, int16_t y);
+ void drawFastVLine(int16_t x, int16_t y, int16_t h, uint16_t color);
+ void drawFastHLine(int16_t x, int16_t y, int16_t w, uint16_t color);
+ uint16_t drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color = COLOR_WHITE);
+ #endif
+ private:
+ char _timeBuf[20], _dateBuf[20], _oldTimeBuf[20], _oldDateBuf[20], _bufforseconds[4], _buffordate[40];
+ uint16_t _timewidth, _timeleft, _datewidth, _dateleft, _oldtimeleft, _oldtimewidth, _olddateleft, _olddatewidth, clockTop, clockRightSpace, clockTimeHeight, _dotsLeft;
+ bool _clipping, _printdots;
+ clipArea _cliparea;
+ void * _scrollid;
+ void _getTimeBounds();
+ void _clockSeconds();
+ void _clockDate();
+ void _clockTime();
+ uint8_t _charWidth(unsigned char c);
+ #if DSP_MODEL==DSP_ILI9225
+ uint16_t _bgcolor, _fgcolor;
+ int16_t _cursorx, _cursory;
+ bool _gFont/*, _started*/;
+ #endif
+
+#endif
diff --git a/yoRadio/src/displays/tools/l10n.h b/yoRadio/src/displays/tools/l10n.h
new file mode 100644
index 0000000..1a5b245
--- /dev/null
+++ b/yoRadio/src/displays/tools/l10n.h
@@ -0,0 +1,18 @@
+#ifndef _display_l10n_h
+#define _display_l10n_h
+
+//==================================================
+#if L10N_LANGUAGE==RU
+ #define L10N_PATH "../../../locale/displayL10n_ru.h"
+#else
+ #define L10N_PATH "../../../locale/displayL10n_en.h"
+#endif
+
+#if __has_include("../../../locale/displayL10n_custom.h")
+ #include "../../../locale/displayL10n_custom.h"
+#else
+ #include L10N_PATH
+#endif
+//==================================================
+
+#endif
diff --git a/yoRadio/src/displays/displayDummy.cpp b/yoRadio/src/displays/tools/utf8RusGFX.h
similarity index 55%
rename from yoRadio/src/displays/displayDummy.cpp
rename to yoRadio/src/displays/tools/utf8RusGFX.h
index 74b4c2f..f3f779a 100644
--- a/yoRadio/src/displays/displayDummy.cpp
+++ b/yoRadio/src/displays/tools/utf8RusGFX.h
@@ -1,15 +1,5 @@
-#include "../../options.h"
-#if DSP_MODEL==0
-
-#include "displayDummy.h"
-#include
-#include "../../player.h"
-#include "../../config.h"
-#include "../../network.h"
-
-DspCore::DspCore() {
-
-}
+#ifndef utf8RusGFX_h
+#define utf8RusGFX_h
char* DspCore::utf8Rus(const char* str, bool uppercase) {
int index = 0;
@@ -85,92 +75,4 @@ char* DspCore::utf8Rus(const char* str, bool uppercase) {
return strn;
}
-void DspCore::apScreen() {
-
-}
-
-void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
-
-}
-
-void DspCore::drawLogo() {
-
-}
-
-void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
-
-}
-
-void DspCore::clearDsp() {
-
-}
-
-void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
-
-}
-
-void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
-
-}
-
-void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
-
-}
-
-void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
-
-}
-
-void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
-
-}
-
-void DspCore::displayHeapForDebug() {
-
-}
-
-void DspCore::printClock(const char* timestr) {
-
-}
-
-void DspCore::drawVolumeBar(bool withNumber) {
-
-}
-
-void DspCore::drawNextStationNum(uint16_t num) {
-
-}
-
-void DspCore::frameTitle(const char* str) {
-
-}
-
-void DspCore::rssi(const char* str) {
-;
-}
-
-void DspCore::ip(const char* str) {
-
-}
-
-void DspCore::set_TextSize(uint8_t s) {
-
-}
-
-void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
-
-}
-
-void DspCore::set_Cursor(int16_t x, int16_t y) {
-
-}
-
-void DspCore::printText(const char* txt) {
-
-}
-
-void DspCore::loop(bool force) {
-
-}
-
#endif
diff --git a/yoRadio/src/displays/tools/utf8RusLCD.h b/yoRadio/src/displays/tools/utf8RusLCD.h
new file mode 100644
index 0000000..f97086f
--- /dev/null
+++ b/yoRadio/src/displays/tools/utf8RusLCD.h
@@ -0,0 +1,111 @@
+#ifndef utf8RusLCD_h
+#define utf8RusLCD_h
+
+char* DspCore::utf8Rus(const char* str, bool uppercase) {
+ int index = 0;
+ static char strn[BUFLEN];
+ static char newStr[BUFLEN];
+ bool E = false;
+ strlcpy(strn, str, BUFLEN);
+ newStr[0] = '\0';
+ bool next = false;
+ for (char *iter = strn; *iter != '\0'; ++iter)
+ {
+ if (E) {
+ E = false;
+ continue;
+ }
+ byte rus = (byte) * iter;
+ if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
+ *iter = (char)209;
+ *(iter + 1) = (char)145;
+ E = true;
+ continue;
+ }
+ if (rus == 209 && (byte) * (iter + 1) == 145) {
+ *iter = (char)209;
+ *(iter + 1) = (char)145;
+ E = true;
+ continue;
+ }
+ if (next) {
+ if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
+ if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
+ next = false;
+ }
+ if (rus == 208) next = true;
+ if (rus == 209) {
+ *iter = (char)208;
+ next = true;
+ }
+ *iter = toupper(*iter);
+ }
+
+ while (strn[index])
+ {
+ if (strlen(newStr) > BUFLEN - 2) break;
+ if (strn[index] >= 0xBF)
+ {
+ switch (strn[index]) {
+ case 0xD0: {
+ switch (strn[index + 1])
+ {
+ case 0x90: strcat(newStr, "A"); break;
+ case 0x91: strcat(newStr, "B"); break;
+ case 0x92: strcat(newStr, "V"); break;
+ case 0x93: strcat(newStr, "G"); break;
+ case 0x94: strcat(newStr, "D"); break;
+ case 0x95: strcat(newStr, "E"); break;
+ case 0x96: strcat(newStr, "ZH"); break;
+ case 0x97: strcat(newStr, "Z"); break;
+ case 0x98: strcat(newStr, "I"); break;
+ case 0x99: strcat(newStr, "Y"); break;
+ case 0x9A: strcat(newStr, "K"); break;
+ case 0x9B: strcat(newStr, "L"); break;
+ case 0x9C: strcat(newStr, "M"); break;
+ case 0x9D: strcat(newStr, "N"); break;
+ case 0x9E: strcat(newStr, "O"); break;
+ case 0x9F: strcat(newStr, "P"); break;
+ case 0xA0: strcat(newStr, "R"); break;
+ case 0xA1: strcat(newStr, "S"); break;
+ case 0xA2: strcat(newStr, "T"); break;
+ case 0xA3: strcat(newStr, "U"); break;
+ case 0xA4: strcat(newStr, "F"); break;
+ case 0xA5: strcat(newStr, "H"); break;
+ case 0xA6: strcat(newStr, "TS"); break;
+ case 0xA7: strcat(newStr, "CH"); break;
+ case 0xA8: strcat(newStr, "SH"); break;
+ case 0xA9: strcat(newStr, "SHCH"); break;
+ case 0xAA: strcat(newStr, "'"); break;
+ case 0xAB: strcat(newStr, "YU"); break;
+ case 0xAC: strcat(newStr, "'"); break;
+ case 0xAD: strcat(newStr, "E"); break;
+ case 0xAE: strcat(newStr, "YU"); break;
+ case 0xAF: strcat(newStr, "YA"); break;
+ }
+ break;
+ }
+ case 0xD1: {
+ if (strn[index + 1] == 0x91) {
+ strcat(newStr, "YO"); break;
+ break;
+ }
+ break;
+ }
+ }
+ int sind = index + 2;
+ while (strn[sind]) {
+ strn[sind - 1] = strn[sind];
+ sind++;
+ }
+ strn[sind - 1] = 0;
+ } else {
+ char Temp[2] = {(char) strn[index] , 0 } ;
+ strcat(newStr, Temp);
+ }
+ index++;
+ }
+ return newStr;
+}
+
+#endif
diff --git a/yoRadio/src/displays/widgets/pages.cpp b/yoRadio/src/displays/widgets/pages.cpp
new file mode 100644
index 0000000..61f0bc8
--- /dev/null
+++ b/yoRadio/src/displays/widgets/pages.cpp
@@ -0,0 +1,78 @@
+#include "../dspcore.h"
+#if DSP_MODEL!=DSP_DUMMY
+
+#include "pages.h"
+
+void Pager::begin(){
+
+}
+
+void Pager::loop(){
+ for(const auto& p: _pages)
+ if(p->isActive()) p->loop();
+}
+
+Page& Pager::addPage(Page* page, bool setNow){
+ _pages.add(page);
+ if(setNow) setPage(page);
+ return *page;
+}
+
+bool Pager::removePage(Page* page){
+ page->setActive(false);
+ dsp.clearDsp();
+ return _pages.remove(page);
+}
+
+void Pager::setPage(Page* page, bool black){
+ for(const auto& p: _pages) p->setActive(false);
+ dsp.clearDsp(black);
+ page->setActive(true);
+}
+
+
+/*******************************************************/
+
+Page::Page() : _widgets(LinkedList([](Widget * wd) { delete wd;})), _pages(LinkedList([](Page* pg){ delete pg; })) {
+ _active = false;
+}
+
+Page::~Page() {
+ for (const auto& w : _widgets) removeWidget(w);
+}
+
+void Page::loop() {
+ if(_active) for (const auto& w : _widgets) w->loop();
+}
+
+Widget& Page::addWidget(Widget* widget) {
+ _widgets.add(widget);
+ widget->setActive(_active, _active);
+ return *widget;
+}
+
+bool Page::removeWidget(Widget* widget){
+ widget->setActive(false, _active);
+ return _widgets.remove(widget);
+}
+
+Page& Page::addPage(Page* page){
+ _pages.add(page);
+ return *page;
+}
+
+bool Page::removePage(Page* page){
+ return _pages.remove(page);
+}
+
+void Page::setActive(bool act) {
+ for(const auto& w: _widgets) w->setActive(act);
+ for(const auto& p: _pages) p->setActive(act);
+ _active = act;
+}
+
+bool Page::isActive() {
+ return _active;
+}
+
+#endif // #if DSP_MODEL!=DSP_DUMMY
diff --git a/yoRadio/src/displays/widgets/pages.h b/yoRadio/src/displays/widgets/pages.h
new file mode 100644
index 0000000..7fa364c
--- /dev/null
+++ b/yoRadio/src/displays/widgets/pages.h
@@ -0,0 +1,38 @@
+#ifndef pages_h
+#define pages_h
+
+#include "Arduino.h"
+#include "StringArray.h"
+
+class Page {
+ protected:
+ LinkedList _widgets;
+ LinkedList _pages;
+ bool _active;
+ public:
+ Page();
+ ~Page();
+ void loop();
+ Widget& addWidget(Widget* widget);
+ bool removeWidget(Widget* widget);
+ Page& addPage(Page* page);
+ bool removePage(Page* page);
+ void setActive(bool act);
+ bool isActive();
+};
+
+class Pager{
+ public:
+ Pager() : _pages(LinkedList([](Page* pg){ delete pg; })) {}
+ void begin();
+ void loop();
+ Page& addPage(Page* page, bool setNow = false);
+ bool removePage(Page* page);
+ void setPage(Page* page, bool black=false);
+ private:
+ LinkedList _pages;
+
+};
+
+
+#endif
diff --git a/yoRadio/src/displays/widgets/widgets.cpp b/yoRadio/src/displays/widgets/widgets.cpp
new file mode 100644
index 0000000..fc29ecb
--- /dev/null
+++ b/yoRadio/src/displays/widgets/widgets.cpp
@@ -0,0 +1,494 @@
+#include "../dspcore.h"
+#if DSP_MODEL!=DSP_DUMMY
+
+#include "widgets.h"
+#include "../../core/player.h" // for VU widget
+
+/************************
+ FILL WIDGET
+ ************************/
+void FillWidget::init(FillConfig conf, uint16_t bgcolor){
+ Widget::init(conf.widget, bgcolor, bgcolor);
+ _width = conf.width;
+ _height = conf.height;
+
+}
+
+void FillWidget::_draw(){
+ if(!_active) return;
+ dsp.fillRect(_config.left, _config.top, _width, _height, _bgcolor);
+}
+
+/************************
+ TEXT WIDGET
+ ************************/
+TextWidget::~TextWidget() {
+ free(_text);
+ free(_oldtext);
+}
+
+void TextWidget::init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor) {
+ Widget::init(wconf, fgcolor, bgcolor);
+ _buffsize = buffsize;
+ _text = (char *) malloc(sizeof(char) * _buffsize);
+ memset(_text, 0, _buffsize);
+ _oldtext = (char *) malloc(sizeof(char) * _buffsize);
+ memset(_oldtext, 0, _buffsize);
+ //_charWidth = wconf.textsize * CHARWIDTH; // default GFX font
+ //_textheight = wconf.textsize * CHARHEIGHT; // default GFX font
+ dsp.charSize(_config.textsize, _charWidth, _textheight);
+ _textwidth = _oldtextwidth = _oldleft = 0;
+ _uppercase = uppercase;
+}
+
+void TextWidget::setText(const char* txt) {
+ strlcpy(_text, dsp.utf8Rus(txt, _uppercase), _buffsize);
+ _textwidth = strlen(_text) * _charWidth;
+ if (strcmp(_oldtext, _text) == 0) return;
+ if (_active) dsp.fillRect(_oldleft == 0 ? _realLeft() : min(_oldleft, _realLeft()), _config.top, max(_oldtextwidth, _textwidth), _textheight, _bgcolor);
+ _oldtextwidth = _textwidth;
+ _oldleft = _realLeft();
+ if (_active) _draw();
+}
+
+void TextWidget::setText(int val, const char *format){
+ char buf[_buffsize];
+ snprintf(buf, _buffsize, format, val);
+ setText(buf);
+}
+
+void TextWidget::setText(const char* txt, const char *format){
+ char buf[_buffsize];
+ snprintf(buf, _buffsize, format, txt);
+ setText(buf);
+}
+
+uint16_t TextWidget::_realLeft() {
+ switch (_config.align) {
+ case WA_CENTER: return (dsp.width() - _textwidth) / 2; break;
+ case WA_RIGHT: return (dsp.width() - _textwidth - _config.left); break;
+ default: return _config.left; break;
+ }
+}
+
+void TextWidget::_draw() {
+ if(!_active) return;
+ dsp.setTextColor(_fgcolor, _bgcolor);
+ dsp.setCursor(_realLeft(), _config.top);
+ dsp.setFont();
+ dsp.setTextSize(_config.textsize);
+ dsp.print(_text);
+ strlcpy(_oldtext, _text, _buffsize);
+}
+
+/************************
+ SCROLL WIDGET
+ ************************/
+ScrollWidget::ScrollWidget(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor) {
+ init(separator, conf, fgcolor, bgcolor);
+}
+
+ScrollWidget::~ScrollWidget() {
+ free(_sep);
+ free(_window);
+}
+
+void ScrollWidget::init(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor) {
+ TextWidget::init(conf.widget, conf.buffsize, conf.uppercase, fgcolor, bgcolor);
+ _sep = (char *) malloc(sizeof(char) * 4);
+ memset(_sep, 0, 4);
+ snprintf(_sep, 4, " %.*s ", 1, separator);
+ _x = conf.widget.left;
+ _startscrolldelay = conf.startscrolldelay;
+ _scrolldelta = conf.scrolldelta;
+ _scrolltime = conf.scrolltime;
+ //_charWidth = CHARWIDTH * _config.textsize; // default GFX font
+ //_textheight = CHARHEIGHT * _config.textsize; // default GFX font
+ dsp.charSize(_config.textsize, _charWidth, _textheight);
+ _sepwidth = strlen(_sep) * _charWidth;
+ _width = conf.width;
+ _backMove.width = _width;
+ _window = (char *) malloc(sizeof(char) * (MAX_WIDTH / _charWidth + 1));
+ memset(_window, 0, (MAX_WIDTH / _charWidth + 1)); // +1?
+ _doscroll = false;
+}
+
+void ScrollWidget::_setTextParams() {
+ if (_config.textsize == 0) return;
+ dsp.setTextSize(_config.textsize);
+ dsp.setTextColor(_fgcolor, _bgcolor);
+}
+
+bool ScrollWidget::_checkIsScrollNeeded() {
+ return _textwidth > _width;
+}
+
+void ScrollWidget::setText(const char* txt) {
+ strlcpy(_text, dsp.utf8Rus(txt, _uppercase), _buffsize - 1);
+ if (strcmp(_oldtext, _text) == 0) return;
+ _textwidth = strlen(_text) * _charWidth;
+ _x = _config.left;
+ _doscroll = _checkIsScrollNeeded();
+ if (dsp.getScrollId() == this) dsp.setScrollId(NULL);
+ _scrolldelay = millis();
+ if (_active) {
+ _setTextParams();
+ if (_doscroll) {
+ dsp.fillRect(_config.left, _config.top, _width, _textheight, _bgcolor);
+ dsp.setCursor(_config.left, _config.top);
+ snprintf(_window, _width / _charWidth + 1, "%s", _text); //TODO
+ dsp.setClipping({_config.left, _config.top, _width, _textheight});
+ dsp.print(_window);
+ dsp.clearClipping();
+ } else {
+ dsp.fillRect(_config.left, _config.top, _width, _textheight, _bgcolor);
+ dsp.setCursor(_realLeft(), _config.top);
+ //dsp.setClipping({_config.left, _config.top, _width, _textheight});
+ dsp.print(_text);
+ //dsp.clearClipping();
+ }
+ strlcpy(_oldtext, _text, _buffsize);
+ }
+}
+
+void ScrollWidget::setText(const char* txt, const char *format){
+ char buf[_buffsize];
+ snprintf(buf, _buffsize, format, txt);
+ setText(buf);
+}
+
+void ScrollWidget::loop() {
+ if(_locked) return;
+ if (!_doscroll || _config.textsize == 0 || (dsp.getScrollId() != NULL && dsp.getScrollId() != this)) return;
+ if (_checkDelay(_x == _config.left ? _startscrolldelay : _scrolltime, _scrolldelay)) {
+ _calcX();
+ if (_active) _draw();
+ }
+}
+
+void ScrollWidget::_clear(){
+ dsp.fillRect(_config.left, _config.top, _width, _textheight, _bgcolor);
+}
+
+void ScrollWidget::_draw() {
+ if(!_active || _locked) return;
+ _setTextParams();
+ if (_doscroll) {
+ uint16_t _newx = _config.left - _x;
+ const char* _cursor = _text + _newx / _charWidth;
+ uint16_t hiddenChars = _cursor - _text;
+ if (hiddenChars < strlen(_text)) {
+ snprintf(_window, _width / _charWidth + 1, "%s%s%s", _cursor, _sep, _text);
+ } else {
+ const char* _scursor = _sep + (_cursor - (_text + strlen(_text)));
+ snprintf(_window, _width / _charWidth + 1, "%s%s", _scursor, _text);
+ }
+ dsp.setCursor(_x + hiddenChars * _charWidth, _config.top);
+ dsp.setClipping({_config.left, _config.top, _width, _textheight});
+ dsp.print(_window);
+ #ifndef DSP_LCD
+ dsp.print(" ");
+ #endif
+ dsp.clearClipping();
+ } else {
+ dsp.fillRect(_config.left, _config.top, _width, _textheight, _bgcolor);
+ dsp.setCursor(_realLeft(), _config.top);
+ dsp.setClipping({_realLeft(), _config.top, _width, _textheight});
+ dsp.print(_text);
+ dsp.clearClipping();
+ }
+}
+
+void ScrollWidget::_calcX() {
+ if (!_doscroll || _config.textsize == 0) return;
+ _x -= _scrolldelta;
+ if (-_x > _textwidth + _sepwidth - _config.left) {
+ _x = _config.left;
+ dsp.setScrollId(NULL);
+ } else {
+ dsp.setScrollId(this);
+ }
+}
+
+bool ScrollWidget::_checkDelay(int m, uint32_t &tstamp) {
+ if (millis() - tstamp > m) {
+ tstamp = millis();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ScrollWidget::_reset(){
+ dsp.setScrollId(NULL);
+ _x = _config.left;
+ _scrolldelay = millis();
+ _doscroll = _checkIsScrollNeeded();
+}
+
+/************************
+ SLIDER WIDGET
+ ************************/
+void SliderWidget::init(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor) {
+ Widget::init(conf.widget, fgcolor, bgcolor);
+ _width = conf.width; _height = conf.height; _outlined = conf.outlined; _oucolor = oucolor, _max = maxval;
+ _oldvalwidth = _value = 0;
+}
+
+void SliderWidget::setValue(uint32_t val) {
+ _value = val;
+ if (_active && !_locked) _drawslider();
+
+}
+
+void SliderWidget::_drawslider() {
+ uint16_t valwidth = map(_value, 0, _max, 0, _width - _outlined * 2);
+ if (_oldvalwidth == valwidth) return;
+ dsp.fillRect(_config.left + _outlined + min(valwidth, _oldvalwidth), _config.top + _outlined, abs(_oldvalwidth - valwidth), _height - _outlined * 2, _oldvalwidth > valwidth ? _bgcolor : _fgcolor);
+ _oldvalwidth = valwidth;
+}
+
+void SliderWidget::_draw() {
+ if(_locked) return;
+ _clear();
+ if(!_active) return;
+ if (_outlined) dsp.drawRect(_config.left, _config.top, _width, _height, _oucolor);
+ uint16_t valwidth = map(_value, 0, _max, 0, _width - _outlined * 2);
+ dsp.fillRect(_config.left + _outlined, _config.top + _outlined, valwidth, _height - _outlined * 2, _fgcolor);
+}
+
+void SliderWidget::_clear() {
+ _oldvalwidth = 0;
+ dsp.fillRect(_config.left, _config.top, _width, _height, _bgcolor);
+}
+/************************
+ VU WIDGET
+ ************************/
+#if !defined(DSP_LCD) && !defined(DSP_OLED)
+VuWidget::~VuWidget() {
+ if(_canvas) free(_canvas);
+}
+
+void VuWidget::init(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor) {
+ Widget::init(wconf, bgcolor, bgcolor);
+ _vumaxcolor = vumaxcolor;
+ _vumincolor = vumincolor;
+ _bands = bands;
+ _canvas = new Canvas(_bands.width * 2 + _bands.space, _bands.height);
+}
+
+void VuWidget::_draw(){
+ if(!_active || _locked) return;
+#if !defined(USE_NEXTION) && I2S_DOUT==255
+ static uint8_t cc = 0;
+ cc++;
+ if(cc>1){
+ player.getVUlevel();
+ cc=0;
+ }
+#endif
+ static uint16_t measL, measR;
+ uint16_t bandColor;
+ uint16_t dimension = _config.align?_bands.width:_bands.height;
+ uint8_t L = map(player.vuLeft, 255, 0, 0, dimension);
+ uint8_t R = map(player.vuRight, 255, 0, 0, dimension);
+ bool played = player.isRunning();
+ if(played){
+ measL=(L>=measL)?measL + _bands.fadespeed:L;
+ measR=(R>=measR)?measR + _bands.fadespeed:R;
+ }else{
+ if(measLdimension) measL=dimension;
+ if(measR>dimension) measR=dimension;
+ uint8_t h=(dimension/_bands.perheight)-_bands.vspace;
+ _canvas->fillRect(0,0,_bands.width * 2 + _bands.space,_bands.height, _bgcolor);
+ for(int i=0; i_bands.width-(_bands.width/_bands.perheight)*4)?_vumaxcolor:_vumincolor;
+ _canvas->fillRect(i, 0, h, _bands.height, bandColor);
+ _canvas->fillRect(i + _bands.width + _bands.space, 0, h, _bands.height, bandColor);
+ #else
+ bandColor = (i>(_bands.width/_bands.perheight))?_vumincolor:_vumaxcolor;
+ _canvas->fillRect(i, 0, h, _bands.height, bandColor);
+ bandColor = (i>_bands.width-(_bands.width/_bands.perheight)*3)?_vumaxcolor:_vumincolor;
+ _canvas->fillRect(i + _bands.width + _bands.space, 0, h, _bands.height, bandColor);
+ #endif
+ }else{
+ bandColor = (i<(_bands.height/_bands.perheight)*3)?_vumaxcolor:_vumincolor;
+ _canvas->fillRect(0, i, _bands.width, h, bandColor);
+ _canvas->fillRect(_bands.width + _bands.space, i, _bands.width, h, bandColor);
+ }
+ }
+ }
+ if(_config.align){
+ #ifndef BOOMBOX_STYLE
+ _canvas->fillRect(_bands.width-measL, 0, measL, _bands.width, _bgcolor);
+ _canvas->fillRect(_bands.width * 2 + _bands.space - measR, 0, measR, _bands.width, _bgcolor);
+ dsp.drawRGBBitmap(_config.left, _config.top, _canvas->getBuffer(), _bands.width * 2 + _bands.space, _bands.height);
+ #else
+ _canvas->fillRect(0, 0, _bands.width-(_bands.width-measL), _bands.width, _bgcolor);
+ _canvas->fillRect(_bands.width * 2 + _bands.space - measR, 0, measR, _bands.width, _bgcolor);
+ dsp->drawRGBBitmap(_config.left, _config.top, _canvas->getBuffer(), _bands.width * 2 + _bands.space, _bands.height);
+ #endif
+ }else{
+ _canvas->fillRect(0, 0, _bands.width, measL, _bgcolor);
+ _canvas->fillRect(_bands.width + _bands.space, 0, _bands.width, measR, _bgcolor);
+ dsp.drawRGBBitmap(_config.left, _config.top, _canvas->getBuffer(), _bands.width * 2 + _bands.space, _bands.height);
+ }
+}
+
+void VuWidget::loop(){
+ if(_active || !_locked) _draw();
+}
+
+void VuWidget::_clear(){
+ dsp.fillRect(_config.left, _config.top, _bands.width * 2 + _bands.space, _bands.height, _bgcolor);
+}
+#else // DSP_LCD
+VuWidget::~VuWidget() { }
+void VuWidget::init(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor) {
+ Widget::init(wconf, bgcolor, bgcolor);
+}
+void VuWidget::_draw(){ }
+void VuWidget::loop(){ }
+void VuWidget::_clear(){ }
+#endif
+/************************
+ NUM WIDGET
+ ************************/
+void NumWidget::init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor) {
+ Widget::init(wconf, fgcolor, bgcolor);
+ _buffsize = buffsize;
+ _text = (char *) malloc(sizeof(char) * _buffsize);
+ memset(_text, 0, _buffsize);
+ _oldtext = (char *) malloc(sizeof(char) * _buffsize);
+ memset(_oldtext, 0, _buffsize);
+ _textwidth = _oldtextwidth = _oldleft = 0;
+ _uppercase = uppercase;
+ _textheight = wconf.textsize;
+}
+
+void NumWidget::setText(const char* txt) {
+ strlcpy(_text, txt, _buffsize);
+ _getBounds();
+ if (strcmp(_oldtext, _text) == 0) return;
+ if (_active) dsp.fillRect(_oldleft == 0 ? _realLeft() : min(_oldleft, _realLeft()), _config.top-_textheight+1, max(_oldtextwidth, _textwidth), _textheight, _bgcolor);
+ _oldtextwidth = _textwidth;
+ _oldleft = _realLeft();
+ if (_active) _draw();
+}
+
+void NumWidget::setText(int val, const char *format){
+ char buf[_buffsize];
+ snprintf(buf, _buffsize, format, val);
+ setText(buf);
+}
+
+void NumWidget::_getBounds() {
+ _textwidth= dsp.textWidth(_text);
+}
+
+void NumWidget::_draw() {
+ if(!_active) return;
+ dsp.setNumFont(); // --------------SetBigFont
+ //dsp.setTextSize(1);
+ dsp.setTextColor(_fgcolor, _bgcolor);
+ dsp.setCursor(_realLeft(), _config.top);
+ dsp.print(_text);
+ strlcpy(_oldtext, _text, _buffsize);
+ dsp.setFont();
+}
+
+/**************************
+ PROGRESS WIDGET
+ **************************/
+void ProgressWidget::_progress() {
+ char buf[_width + 1];
+ snprintf(buf, _width, "%*s%.*s%*s", _pg <= _barwidth ? 0 : _pg - _barwidth, "", _pg <= _barwidth ? _pg : 5, ".....", _width - _pg, "");
+ _pg++; if (_pg >= _width + _barwidth) _pg = 0;
+ setText(buf);
+}
+
+bool ProgressWidget::_checkDelay(int m, uint32_t &tstamp) {
+ if (millis() - tstamp > m) {
+ tstamp = millis();
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ProgressWidget::loop() {
+ if (_checkDelay(_speed, _scrolldelay)) {
+ _progress();
+ }
+}
+
+/**************************
+ CLOCK WIDGET
+ **************************/
+void ClockWidget::draw(){
+ if(!_active) return;
+ dsp.printClock(_config.top, _config.left, _config.textsize, false);
+}
+
+void ClockWidget::_draw(){
+ if(!_active) return;
+ dsp.printClock(_config.top, _config.left, _config.textsize, true);
+}
+
+void ClockWidget::_clear(){
+ dsp.clearClock();
+}
+
+void BitrateWidget::init(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor){
+ Widget::init(bconf.widget, fgcolor, bgcolor);
+ _dimension = bconf.dimension;
+ _bitrate = 0;
+ _format = BF_UNCNOWN;
+ dsp.charSize(bconf.widget.textsize, _charWidth, _textheight);
+ memset(_buf, 0, 6);
+}
+
+void BitrateWidget::setBitrate(uint16_t bitrate){
+ _bitrate = bitrate;
+ _draw();
+}
+
+void BitrateWidget::setFormat(BitrateFormat format){
+ _format = format;
+ _draw();
+}
+
+void BitrateWidget::_draw(){
+ _clear();
+ if(!_active || _format == BF_UNCNOWN || _bitrate==0) return;
+ dsp.drawRect(_config.left, _config.top, _dimension, _dimension, _fgcolor);
+ dsp.fillRect(_config.left, _config.top + _dimension/2, _dimension, _dimension/2, _fgcolor);
+ dsp.setFont();
+ dsp.setTextSize(_config.textsize);
+ dsp.setTextColor(_fgcolor, _bgcolor);
+ snprintf(_buf, 6, "%d", _bitrate);
+ dsp.setCursor(_config.left + _dimension/2 - _charWidth*strlen(_buf)/2, _config.top + _dimension/4 - _textheight/2+1);
+ dsp.print(_buf);
+ dsp.setTextColor(_bgcolor, _fgcolor);
+ dsp.setCursor(_config.left + _dimension/2 - _charWidth*3/2, _config.top + _dimension - _dimension/4 - _textheight/2);
+ switch(_format){
+ case BF_MP3: dsp.print("MP3"); break;
+ case BF_AAC: dsp.print("AAC"); break;
+ case BF_FLAC: dsp.print("FLC"); break;
+ case BF_OGG: dsp.print("OGG"); break;
+ case BF_WAV: dsp.print("WAV"); break;
+ default: break;
+ }
+}
+
+void BitrateWidget::_clear() {
+ dsp.fillRect(_config.left, _config.top, _dimension, _dimension, _bgcolor);
+}
+
+#endif // #if DSP_MODEL!=DSP_DUMMY
diff --git a/yoRadio/src/displays/widgets/widgets.h b/yoRadio/src/displays/widgets/widgets.h
new file mode 100644
index 0000000..bb27dd2
--- /dev/null
+++ b/yoRadio/src/displays/widgets/widgets.h
@@ -0,0 +1,267 @@
+#ifndef widgets_h
+#define widgets_h
+
+#include "Arduino.h"
+//#include "../../core/config.h"
+enum WidgetAlign { WA_LEFT, WA_CENTER, WA_RIGHT };
+enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV };
+
+typedef struct clipArea {
+ uint16_t left;
+ uint16_t top;
+ uint16_t width;
+ uint16_t height;
+} clipArea;
+
+struct WidgetConfig {
+ uint16_t left;
+ uint16_t top;
+ uint16_t textsize;
+ WidgetAlign align;
+};
+
+struct ScrollConfig {
+ WidgetConfig widget;
+ uint16_t buffsize;
+ bool uppercase;
+ uint16_t width;
+ uint16_t startscrolldelay;
+ uint8_t scrolldelta;
+ uint16_t scrolltime;
+};
+
+struct FillConfig {
+ WidgetConfig widget;
+ uint16_t width;
+ uint16_t height;
+ bool outlined;
+};
+
+struct ProgressConfig {
+ uint16_t speed;
+ uint16_t width;
+ uint16_t barwidth;
+};
+
+struct VUBandsConfig {
+ uint16_t width;
+ uint16_t height;
+ uint8_t space;
+ uint8_t vspace;
+ uint8_t perheight;
+ uint8_t fadespeed;
+};
+
+struct MoveConfig {
+ uint16_t x;
+ uint16_t y;
+ int16_t width;
+};
+
+struct BitrateConfig {
+ WidgetConfig widget;
+ uint16_t dimension;
+};
+
+class Widget{
+ public:
+ Widget(){ _active = false; }
+ virtual ~Widget(){}
+ virtual void loop(){}
+ virtual void init(WidgetConfig conf, uint16_t fgcolor, uint16_t bgcolor){
+ _config = conf;
+ _fgcolor = fgcolor;
+ _bgcolor = bgcolor;
+ _width = _backMove.width = 0;
+ _backMove.x = _config.left;
+ _backMove.y = _config.top;
+ _moved = _locked = false;
+ }
+ void setAlign(WidgetAlign align){
+ _config.align = align;
+ }
+ void setActive(bool act, bool clr=false) { _active = act; if(_active && !_locked) _draw(); if(clr && !_locked) _clear(); }
+ void lock(bool lck=true) { _locked = lck; if(_locked) _reset(); if(_locked && _active) _clear(); }
+ void unlock() { _locked = false; }
+ bool locked() { return _locked; }
+ void moveTo(MoveConfig mv){
+ if(mv.width<0) return;
+ _moved = true;
+ if(_active && !_locked) _clear();
+ _config.left = mv.x;
+ _config.top = mv.y;
+ if(mv.width>0) _width = mv.width;
+ _reset();
+ _draw();
+ }
+ void moveBack(){
+ if(!_moved) return;
+ if(_active && !_locked) _clear();
+ _config.left = _backMove.x;
+ _config.top = _backMove.y;
+ _width = _backMove.width;
+ _moved = false;
+ _reset();
+ _draw();
+ }
+ protected:
+ bool _active, _moved, _locked;
+ uint16_t _fgcolor, _bgcolor, _width;
+ WidgetConfig _config;
+ MoveConfig _backMove;
+ virtual void _draw() {}
+ virtual void _clear() {}
+ virtual void _reset() {}
+};
+
+class TextWidget: public Widget {
+ public:
+ TextWidget() {}
+ TextWidget(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor) { init(wconf, buffsize, uppercase, fgcolor, bgcolor); }
+ ~TextWidget();
+ void init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor);
+ void setText(const char* txt);
+ void setText(int val, const char *format);
+ void setText(const char* txt, const char *format);
+ bool uppercase() { return _uppercase; }
+ protected:
+ char *_text;
+ char *_oldtext;
+ bool _uppercase;
+ uint16_t _buffsize, _textwidth, _oldtextwidth, _oldleft, _textheight;
+ uint8_t _charWidth;
+ protected:
+ void _draw();
+ uint16_t _realLeft();
+};
+
+class FillWidget: public Widget {
+ public:
+ FillWidget() {}
+ FillWidget(FillConfig conf, uint16_t bgcolor) { init(conf, bgcolor); }
+ void init(FillConfig conf, uint16_t bgcolor);
+ protected:
+ uint16_t _height;
+ void _draw();
+};
+
+class ScrollWidget: public TextWidget {
+ public:
+ ScrollWidget(){}
+ ScrollWidget(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor);
+ ~ScrollWidget();
+ void init(const char* separator, ScrollConfig conf, uint16_t fgcolor, uint16_t bgcolor);
+ void loop();
+ void setText(const char* txt);
+ void setText(const char* txt, const char *format);
+ private:
+ char *_sep;
+ char *_window;
+ int16_t _x;
+ bool _doscroll;
+ uint8_t _scrolldelta;
+ uint16_t _scrolltime;
+ uint32_t _scrolldelay;
+ uint16_t _sepwidth, _startscrolldelay;
+ uint8_t _charWidth;
+ private:
+ void _setTextParams();
+ void _calcX();
+ void _drawFrame();
+ void _draw();
+ bool _checkIsScrollNeeded();
+ bool _checkDelay(int m, uint32_t &tstamp);
+ void _clear();
+ void _reset();
+};
+
+class SliderWidget: public Widget {
+ public:
+ SliderWidget(){}
+ SliderWidget(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor=0){
+ init(conf, fgcolor, bgcolor, maxval, oucolor);
+ }
+ void init(FillConfig conf, uint16_t fgcolor, uint16_t bgcolor, uint32_t maxval, uint16_t oucolor=0);
+ void setValue(uint32_t val);
+ protected:
+ uint16_t _height, _oucolor, _oldvalwidth;
+ uint32_t _max, _value;
+ bool _outlined;
+ void _draw();
+ void _drawslider();
+ void _clear();
+};
+
+class VuWidget: public Widget {
+ public:
+ VuWidget() {}
+ VuWidget(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor) { init(wconf, bands, vumaxcolor, vumincolor, bgcolor); }
+ ~VuWidget();
+ void init(WidgetConfig wconf, VUBandsConfig bands, uint16_t vumaxcolor, uint16_t vumincolor, uint16_t bgcolor);
+ void loop();
+ protected:
+ #if !defined(DSP_LCD) && !defined(DSP_OLED)
+ Canvas *_canvas;
+ #endif
+ VUBandsConfig _bands;
+ uint16_t _vumaxcolor, _vumincolor;
+ void _draw();
+ void _clear();
+};
+
+class NumWidget: public TextWidget {
+ public:
+ void init(WidgetConfig wconf, uint16_t buffsize, bool uppercase, uint16_t fgcolor, uint16_t bgcolor);
+ void setText(const char* txt);
+ void setText(int val, const char *format);
+ protected:
+ void _getBounds();
+ void _draw();
+};
+
+class ProgressWidget: public TextWidget {
+ public:
+ ProgressWidget() {}
+ ProgressWidget(WidgetConfig conf, ProgressConfig pconf, uint16_t fgcolor, uint16_t bgcolor) {
+ init(conf, pconf, fgcolor, bgcolor);
+ }
+ void init(WidgetConfig conf, ProgressConfig pconf, uint16_t fgcolor, uint16_t bgcolor){
+ TextWidget::init(conf, pconf.width, false, fgcolor, bgcolor);
+ _speed = pconf.speed; _width = pconf.width; _barwidth = pconf.barwidth;
+ _pg = 0;
+ }
+ void loop();
+ private:
+ uint8_t _pg;
+ uint16_t _speed, _barwidth;
+ uint32_t _scrolldelay;
+ void _progress();
+ bool _checkDelay(int m, uint32_t &tstamp);
+};
+
+class ClockWidget: public Widget {
+ public:
+ void draw();
+ protected:
+ void _draw();
+ void _clear();
+};
+
+class BitrateWidget: public Widget {
+ public:
+ BitrateWidget() {}
+ BitrateWidget(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor) { init(bconf, fgcolor, bgcolor); }
+ ~BitrateWidget(){}
+ void init(BitrateConfig bconf, uint16_t fgcolor, uint16_t bgcolor);
+ void setBitrate(uint16_t bitrate);
+ void setFormat(BitrateFormat format);
+ protected:
+ BitrateFormat _format;
+ char _buf[6];
+ uint8_t _charWidth;
+ uint16_t _dimension, _bitrate, _textheight;
+ void _draw();
+ void _clear();
+};
+
+#endif
diff --git a/yoRadio/yoRadio.ino b/yoRadio/yoRadio.ino
index 7c0183a..7816eed 100644
--- a/yoRadio/yoRadio.ino
+++ b/yoRadio/yoRadio.ino
@@ -1,23 +1,33 @@
+/* ============================================================================================================
+ * ёRadio
+ * ============================================================================================================
+ * Web-radio based on
+ * ESP32-audioI2S https://github.com/schreibfaul1/ESP32-audioI2S
+ * or/and
+ * ESP32-vs1053_ext https://github.com/schreibfaul1/ESP32-vs1053_ext
+ * libraries
+ * ============================================================================================================
+ * Project home https://github.com/e2002/yoradio
+ * Wiki https://github.com/e2002/yoradio/wiki
+ * Описание на 4PDA https://4pda.to/forum/index.php?s=&showtopic=1010378&view=findpost&p=112992611
+ * Как это прошить? https://4pda.to/forum/index.php?act=findpost&pid=112992611&anchor=Spoil-112992611-2
+ * ============================================================================================================
+ * Here goes!
+ * ============================================================================================================
+ */
#include "Arduino.h"
-
-#include "options.h"
-#if DSP_MODEL==DSP_DUMMY
-#define DUMMYDISPLAY
-#endif
-#include "config.h"
-#include "telnet.h"
-#include "player.h"
-#include "display.h"
-#include "player.h"
-#include "network.h"
-#include "netserver.h"
-#include "controls.h"
-#include "mqtt.h"
-
-unsigned long checkMillis = 0;
-unsigned long checkInterval = 3000;
+#include "src/core/options.h"
+#include "src/core/config.h"
+#include "src/core/telnet.h"
+#include "src/core/player.h"
+#include "src/core/display.h"
+#include "src/core/network.h"
+#include "src/core/netserver.h"
+#include "src/core/controls.h"
+#include "src/core/mqtt.h"
extern __attribute__((weak)) void yoradio_on_setup();
+
void setup() {
Serial.begin(115200);
pinMode(LED_BUILTIN, OUTPUT);
@@ -28,22 +38,22 @@ void setup() {
network.begin();
if (network.status != CONNECTED) {
netserver.begin();
- display.start();
+ display.putRequest(DSP_START);
+ while(!display.ready()) delay(10);
return;
}
netserver.begin();
telnet.begin();
-#if PLAYER_FORCE_MONO
- player.forceMono(true);
-#endif
+ #if PLAYER_FORCE_MONO
+ player.forceMono(true);
+ #endif
player.setVol(config.store.volume, true);
initControls();
- display.start();
-
-#ifdef MQTT_HOST
- mqttInit();
-#endif
-
+ display.putRequest(DSP_START);
+ while(!display.ready()) delay(10);
+ #ifdef MQTT_HOST
+ mqttInit();
+ #endif
if (config.store.smartstart == 1) player.play(config.store.lastStation);
}
@@ -54,15 +64,16 @@ void loop() {
loopControls();
checkConnection();
}
-// display.loop();
netserver.loop();
}
void checkConnection(){
+ static uint32_t checkMillis = 0;
+ static uint32_t checkInterval = 3000;
if ((WiFi.status() != WL_CONNECTED) && (millis() - checkMillis >=checkInterval)) {
bool playing = player.isRunning();
if (playing) player.mode = STOPPED;
- display.putRequest({NEWMODE, LOST});
+ display.putRequest(NEWMODE, LOST);
Serial.println("Lost connection, reconnecting...");
while(true){
if (WiFi.status() == WL_CONNECTED) break;
@@ -70,11 +81,70 @@ void checkConnection(){
WiFi.reconnect();
delay(3000);
}
- display.putRequest({NEWMODE, PLAYER});
+ display.putRequest(NEWMODE, PLAYER);
if (playing) player.request.station = config.store.lastStation;
checkMillis = millis();
-#ifdef MQTT_HOST
- connectToMqtt();
-#endif
+ #ifdef MQTT_HOST
+ connectToMqtt();
+ #endif
+ }
+}
+
+//=============================================//
+// Audio handlers //
+//=============================================//
+
+void audio_info(const char *info) {
+ if(config.store.audioinfo) telnet.printf("##AUDIO.INFO#: %s\n", info);
+ #ifdef USE_NEXTION
+ nextion.audioinfo(info);
+ #endif
+ if (strstr(info, "failed!") != NULL || strstr(info, " 404") != NULL || strstr(info, " 403") != NULL || strstr(info, "address is empty") != NULL) player.stop(info);
+ if (strstr(info, "not supported") != NULL || strstr(info, "Account already in use") != NULL) player.stop(info);
+}
+
+void audio_bitrate(const char *info)
+{
+ if(config.store.audioinfo) telnet.printf("%s %s\n", "##AUDIO.BITRATE#:", info);
+ config.station.bitrate = atoi(info) / 1000;
+ display.putRequest(DBITRATE);
+ #ifdef USE_NEXTION
+ nextion.bitrate(config.station.bitrate);
+ #endif
+ netserver.requestOnChange(BITRATE, 0);
+}
+
+bool printable(const char *info) {
+ bool p = true;
+ for (int c = 0; c < strlen(info); c++)
+ {
+ if ((uint8_t)info[c] > 0x7e || (uint8_t)info[c] < 0x20) p = false;
+ }
+ if (!p) p = (uint8_t)info[0] >= 0xC2 && (uint8_t)info[1] >= 0x80 && (uint8_t)info[1] <= 0xBF;
+ return p;
+}
+
+void audio_showstation(const char *info) {
+ if (strlen(info) > 0) {
+ bool p = printable(info);
+ config.setTitle(p?info:config.station.name);
+ netserver.requestOnChange(TITLE, 0);
+ }
+}
+
+void audio_showstreamtitle(const char *info) {
+ DBGH();
+ if (strstr(info, "Account already in use") != NULL){
+ player.stop(info);
+ return;
+ }
+ if (strlen(info) > 0) {
+ bool p = printable(info);
+ #ifdef DEBUG_TITLES
+ config.setTitle(DEBUG_TITLES);
+ #else
+ config.setTitle(p?info:config.station.name);
+ #endif
+ netserver.requestOnChange(TITLE, 0);
}
}