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