This commit is contained in:
e2002
2022-10-14 11:00:49 +03:00
parent dbe0f32e14
commit 1c4b1dec6e
94 changed files with 5320 additions and 6613 deletions

View File

@@ -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)
---
<img src="images/img0.jpg" width="830" height="443">
@@ -293,11 +294,26 @@ download _http://\<yoradioip\>/data/playlist.csv_ and _http://\<yoradioip\>/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 dont 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

View File

@@ -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) */

View File

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

View File

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

View File

@@ -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 <WiFiClient.h>
#include <Ticker.h>
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

View File

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

View File

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

View File

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

View File

@@ -1,203 +0,0 @@
#ifndef display_h
#define display_h
#include "options.h"
#include "Arduino.h"
#include <Ticker.h>
#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

View File

@@ -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(measL<dimension) measL+=VU_FS;
if(measR<dimension) measR+=VU_FS;
}
if(measL>dimension) 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; i<dimension; i++){
if(i%(dimension/VU_NB)==0){
if(VU_HOR){
#ifndef BOOMBOX_STYLE
bandColor = (i>VU_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

View File

@@ -0,0 +1,59 @@
#ifndef dsp_full_loc
#define dsp_full_loc
#include <pgmspace.h>
/*************************************************************************************
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

View File

@@ -0,0 +1,59 @@
#ifndef dsp_full_loc
#define dsp_full_loc
#include <pgmspace.h>
/*************************************************************************************
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

View File

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

View File

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

View File

@@ -1,4 +1,4 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_ILI9225
#include "TFT_22_ILI9225Fix.h"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

113
yoRadio/src/core/display.h Normal file
View File

@@ -0,0 +1,113 @@
#ifndef display_h
#define display_h
#include "options.h"
#include "Arduino.h"
#include <Ticker.h>
#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

View File

@@ -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 <AsyncMqttClient.h>

View File

@@ -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\",");
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
#if defined(LCD_I2C) || defined(DSP_OLED)
act += F("\"group_oled\",");
#endif
if (VU_READY == 1 || dbgact) act += F("\"group_vu\",");
#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; }

View File

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

View File

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

View File

@@ -7,6 +7,7 @@
#define apPassword "12345987"
//#define TSYNC_DELAY 10800000 // 1000*60*60*3 = 3 hours
#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();
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,135 +1,33 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_GC9106
#include "displayGC9106.h"
#include <SPI.h>
#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 */
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(txt);
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor(swidth - w - TFT_FRAMEWDT, y);
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
}
void DspCore::setClockBounds(){
setFont(&DS_DIGI28pt7b);
void DspCore::_clockSeconds(){
setTextSize(1);
getTextBounds("88:88", 0, 0, &x, &y, &cwidth, &cheight);
uint16_t header = TFT_FRAMEWDT + 4 * TFT_LINEHGHT;
uint16_t footer = TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 5;
clockY = header + (sheight - header - footer) / 2 - cheight / 2 - 6;
setFont(&DS_DIGI28pt7b);
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::printClock(const char* timestr) {
void DspCore::_clockDate(){ }
}
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;
uint16_t clockdelta=config.store.vumeter?CLOCK_DELTA:0;
strftime(timeBuf, sizeof(timeBuf), "%H %M", &timeinfo);
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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(":");
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
yield();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
}
#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::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::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);
setFont();
}
void DspCore::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
}
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

View File

@@ -1,118 +1,34 @@
#ifndef displayGC9106_h
#define displayGC9106_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
/* 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

View File

@@ -1,15 +1,13 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_ILI9225
#include "displayILI9225.h"
#include <SPI.h>
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
if(y==90) y=(sheight-64)/2 + 64 + TFT_LINEHGHT;
if(y==110) y=(sheight-64)/2 + 64 + TFT_LINEHGHT*3;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg, bg);
setCursor((swidth - w) / 2, y);
fillRect((swidth-w)/2-5, y, w+10, h, bg);
print(txt);
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, bool fliprect, uint16_t delta) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
setCursor(swidth - w - TFT_FRAMEWDT - delta, y);
print(text);
}
void DspCore::displayHeapForDebug() {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2;
char buf[50];
setTextSize(1);
setTextColor(config.theme.heap, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, config.theme.background);
sprintf(buf, "%d / %d", ESP.getFreeHeap(), ESP.getMaxAllocHeap());
print(buf);
// audio buffer;
fillRect(0, sheight - 2, swidth, 2, config.theme.background);
int astored = player.inBufferFilled();
int afree = player.inBufferFree();
int aprcnt = 100 * astored / (astored + afree);
uint16_t sbw = map(aprcnt, 0, 100 , 0, swidth);
fillRect(0, sheight - 2, sbw, 2, config.theme.buffer);
}
void DspCore::printClock(const char* timestr) {
}
uint16_t cltop = 0;
uint8_t clsp = 24;
uint16_t clleft = 0;
uint16_t clwidth = 0;
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){
char timeBuf[50] = { 0 };
strftime(timeBuf, sizeof(timeBuf), "%H:%M", &timeinfo);
if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
int16_t x1, y1;
gFont=true;
setGFXFont(&DS_DIGI28pt7b);
getTextBounds(oldTimeBuf, 0, 0, &x1, &y1, &wot, &hot);
if(cltop==0){
cltop=sheight-(TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 38) - hot;
}
clwidth = wot+clsp+34;
clleft=swidth-TFT_FRAMEWDT-clwidth;
setCursor(clleft, cltop);
setTextColor(config.theme.background, config.theme.background);
print(oldTimeBuf);
strlcpy(oldTimeBuf, timeBuf, 20);
getTextBounds(timeBuf, 0, 0, &x1, &y1, &wot, &hot);
clwidth = wot+clsp+34;
clleft=swidth-TFT_FRAMEWDT-clwidth;
setTextColor(config.theme.clock, config.theme.background);
setCursor(clleft, cltop);
print(timeBuf);
gFont=false;
setTextSize(2);
setTextColor(config.theme.dow, config.theme.background);
setCursor(clleft+wot+clsp, cltop-hot+22);
print(utf8Rus(dow[timeinfo.tm_wday], false));
TAKE_MUTEX();
drawLine(clleft+wot+clsp/2, cltop-34, clleft+wot+clsp/2, cltop+1, config.theme.div); //vert
drawLine(clleft+wot+clsp/2, cltop-hot+20, clleft+wot+clsp/2+35, cltop-hot+20, config.theme.div); //hor
drawLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, TITLE_TOP1-8, config.theme.div);
GIVE_MUTEX();
sprintf(timeBuf, "%2d %s %d", timeinfo.tm_mday,mnths[timeinfo.tm_mon], timeinfo.tm_year+1900);
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);
setTextSize(1);
rightText(utf8Rus(timeBuf,true), cltop+10, config.theme.date, config.theme.background, false, 12);
}
void DspCore::_clockSeconds(){
setTextSize(2);
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*2*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;
(void)volTop;
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);
drawRectangle(TFT_FRAMEWDT, vTop - 2, TFT_FRAMEWDT+vWidth, 6+vTop - 2, config.theme.volbarout);
void DspCore::_clockDate(){
if(_olddateleft>0)
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 */
}
if (withNumber) {
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 volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
/*setTextColor(TFT_BG);
sprintf(volstr, "%d", oldVolume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(volstr);*/
sprintf(volstr, "%d", oldVolume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect((swidth - wv) / 2 - 12, (sheight-hv)/2, wv+24, hv, config.theme.background);
setTextColor(config.theme.vol, config.theme.background);
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(volstr);
oldVolume=config.store.volume;
}
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::drawNextStationNum(uint16_t num) {
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);
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::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::clearClock(){
fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
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::startWrite(void) {
//TAKE_MUTEX();
TFT_22_ILI9225::startWrite();
}
void DspCore::ip(const char* str) {
if(!started){
started = true;
clear(config.theme.background);
}
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::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::endWrite(void) {
TFT_22_ILI9225::endWrite();
//GIVE_MUTEX();
}
void DspCore::loop(bool force) {
//delay(5);
}
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

View File

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

View File

@@ -1,15 +1,11 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_ILI9341
#include "displayILI9341.h"
#include <SPI.h>
#include "fonts/bootlogo.h"
#include "../../player.h"
#include "../../config.h"
#include "../../network.h"
const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
const char *mnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"};
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
fillRect(0, texttop, swidth, textheight, bg);
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
if(y==90) y=(sheight-64)/2 + 64 + TFT_LINEHGHT;
if(y==110) y=(sheight-64)/2 + 64 + TFT_LINEHGHT*3;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect((swidth-w)/2-5, y, w+10, h, bg);
print(txt);
}
void DspCore::rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, bool fliprect, uint16_t delta) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
setCursor(swidth - w - TFT_FRAMEWDT - delta, y);
//fillRect(swidth - w - TFT_FRAMEWDT, fliprect?y-h:y, w, h, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2;
setTextSize(1);
setTextColor(config.theme.heap, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, config.theme.background);
print(ESP.getFreeHeap());
print(" / ");
print(ESP.getMaxAllocHeap());
// audio buffer;
fillRect(0, sheight - 2, swidth, 2, config.theme.background);
int astored = player.inBufferFilled();
int afree = player.inBufferFree();
int aprcnt = 100 * astored / (astored + afree);
uint16_t sbw = map(aprcnt, 0, 100 , 0, swidth);
fillRect(0, sheight - 2, sbw, 2, config.theme.buffer);
}
void DspCore::printClock(const char* timestr) {
}
uint16_t cltop = 0;
uint8_t clsp = 24;
uint16_t clleft = 0;
uint16_t clwidth = 0;
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){
char timeBuf[50] = { 0 };
strftime(timeBuf, sizeof(timeBuf), "%H:%M", &timeinfo);
if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
int16_t x1, y1;
setTextSize(1);
setFont(&DS_DIGI42pt7b);
getTextBounds(oldTimeBuf, 0, 0, &x1, &y1, &wot, &hot);
if(cltop==0){
cltop=sheight-(TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 38) - hot;
}
clwidth = wot+clsp+(swidth>240?46:34);
clleft=swidth-TFT_FRAMEWDT-clwidth;
//fillRect(swidth-TFT_FRAMEWDT-clwidth, cltop-hot, clwidth, hot+3, 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);
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 */
}
if (withNumber) {
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 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;
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::drawNextStationNum(uint16_t num) {
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);
setFont();
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::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::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

View File

@@ -1,76 +1,32 @@
#ifndef displayILI9341_h
#define displayILI9341_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
// 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;

View File

@@ -1,40 +1,39 @@
#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 };
#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();
@@ -45,184 +44,111 @@ void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
begin(16, 2);
#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; x<swidth-(fillSpaces?controlspaces[texttop]:0); x++){
setCursor(x, texttop);
print(" ");
}
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
byte x=(strlen(text)>swidth)?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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
if(x<0) {
xOffset=-x;
x=0;
}else{
xOffset=0;
}
nextX=0;
yOffset = y;
setCursor(x, y);
void DspCore::_getTimeBounds() { }
void DspCore::_clockSeconds(){
setCursor(_timeleft+_dotsLeft, clockTop);
print((network.timeinfo.tm_sec % 2 == 0)?":":" ");
}
void DspCore::printText(const char* txt) {
char tmp[swidth+1] = {0};
int16_t numchars = fillSpaces?swidth-controlspaces[yOffset]:swidth;
strlcpy(tmp, txt+xOffset, numchars+1-nextX);
print(tmp);
xOffset=(int16_t)(strlen(txt)-xOffset)<=0?xOffset-strlen(txt):0;
nextX=nextX+strlen(tmp);
if(nextX>numchars) nextX=numchars;
setCursor(nextX, yOffset);
void DspCore::_clockDate(){ }
void DspCore::_clockTime(){ }
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw) {
clockTop = 0;
_timeleft = width()-5;
_dotsLeft = 2;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
setCursor(_timeleft, clockTop);
print(_timeBuf);
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
}
_clockSeconds();
}
void DspCore::clearClock() { }
void DspCore::loop(bool force) {
if (checkdelay(SCROLLTIME, loopdelay)) {
//display();
}
// delay(100);
}
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 = 1;
height = 1;
}
void DspCore::flip(){ }
void DspCore::invert(){ }
void DspCore::sleep(void) {
noDisplay();
#ifdef LCD_I2C
@@ -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) { }
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;
}
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::clearClipping(){
_clipping = false;
setClipping({0, 0, width(), height()});
}
void DspCore::setNumFont(){ }
#endif

View File

@@ -1,9 +1,18 @@
#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
@@ -26,96 +35,42 @@
#define DSP_INIT LiquidCrystal(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7)
#endif
#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
#if __has_include("conf/displayLCD2004conf_custom.h")
#include "conf/displayLCD2004conf_custom.h"
#else
#define PLMITEMS 1
#define TITLE_SIZE2 0
#define IP_INST_VOL
#define PL_TOP 1
#define BOOTSTR_TOP2 0
#define BOOTSTR_TOP1 1
#include "conf/displayLCD2004conf.h"
#endif
#else
#if __has_include("conf/displayLCD1602conf_custom.h")
#include "conf/displayLCD1602conf_custom.h"
#else
#include "conf/displayLCD1602conf.h"
#endif
#endif
#define PLCURRENT_SIZE 1
#define BOOT_PRG_COLOR 0x1
#define BOOT_TXT_COLOR 0x1
#define PINK 0x1
#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 {
#else
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

View File

@@ -1,11 +1,11 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==3
#include "displayN5110.h"
#include <Wire.h>
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
if(y==90) {
y=sheight-TFT_LINEHGHT*2;
}
if(y==110) {
y=sheight;
setFont(&TinyFont5);
}
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(utf8Rus(txt, true));
setFont();
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor(swidth - w - TFT_FRAMEWDT, y+h+1);
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h+1, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
}
void DspCore::printClock(const char* timestr) {
int16_t x1, y1;
uint16_t w, h;
setTextSize(1);
setFont(&DS_DIGI15pt7b);
getTextBounds(timestr, 0, 0, &x1, &y1, &w, &h);
setTextColor(TFT_FG);
setCursor((swidth - w) / 2, 19+17);
fillRect(0, 18, swidth, h+2, TFT_BG);
print(timestr);
setFont();
}
void DspCore::drawVolumeBar(bool withNumber) {
int16_t vTop = sheight - 3;
int16_t vWidth = swidth;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
fillRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_BG);
drawRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_LOGO);
fillRect(TFT_FRAMEWDT + 1, vTop + 1, ww, 1, TFT_LOGO);
if (withNumber) {
setTextSize(1);
setTextColor(TFT_FG);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
setFont(&DS_DIGI15pt7b);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 24-10, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 24+8);
print(volstr);
setFont();
}
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(1);
setTextColor(TFT_FG);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
setFont(&DS_DIGI15pt7b);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 24-10, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 24+8);
print(numstr);
setFont();
}
void DspCore::frameTitle(const char* str) {
setTextSize(1);
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
}
void DspCore::rssi(const char* str) {
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strlcpy(buf, str, strlen(str)-2);
int16_t vTop = sheight - TFT_LINEHGHT - 2;
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::_clockSeconds(){
setTextSize(1);
setFont(&TinyFont5);
rightText(buf, vTop, SILVER, TFT_BG);
setFont();
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_LINEHGHT - 2;
setFont(&DS_DIGI15pt7b);
setTextColor((network.timeinfo.tm_sec % 2 == 0) ? config.theme.clock : config.theme.background, config.theme.background);
setCursor(_timeleft+_dotsLeft, clockTop);
print(":");
setFont(); /* print dots */
setTextSize(1);
setTextColor(SILVER, TFT_BG);
setCursor(0, vTop);
setFont(&TinyFont5);
print(str);
setCursor(_timeleft+_timewidth+2, clockTop-CHARHEIGHT*2-1);
setTextColor(config.theme.clock, config.theme.background);
sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
print(_bufforseconds);
}
void DspCore::_clockDate(){ }
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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();
}
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

View File

@@ -1,5 +1,6 @@
#ifndef displayN5110_h
#define displayN5110_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
@@ -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,6 +34,8 @@ extern DspCore dsp;
/*
* TFT COLORS
*/
#define BOOT_PRG_COLOR BLACK
#define BOOT_TXT_COLOR BLACK
#define SILVER BLACK
#define TFT_BG WHITE
#define TFT_FG BLACK

View File

@@ -1,11 +1,11 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_SH1106 || DSP_MODEL==DSP_SH1107
#include "displaySH1106.h"
#include <Wire.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
@@ -18,8 +18,6 @@
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
fillRect(0, texttop, swidth, textheight, bg);
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
if(y==90) y=sheight-TFT_LINEHGHT*2-5;
if(y==110) y=sheight-TFT_LINEHGHT;
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(txt);
display();
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
setCursor(swidth - w - TFT_FRAMEWDT, y);
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
}
void DspCore::printClock(const char* timestr) {
setTextSize(2);
centerText(timestr, 34, TFT_FG, TFT_BG);
setTextSize(1);
}
#define CLCLF 34
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw) {
char timeStringBuff[20] = { 0 };
strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M", &timeinfo);
setTextSize(2);
setCursor(CLCLF, (DSP_MODEL==DSP_SH1107)?30:34);
setTextColor(TFT_FG, TFT_BG);
print(timeStringBuff);
setTextSize(1);
setCursor(CLCLF + 6*2*5+1, (DSP_MODEL==DSP_SH1107)?30+1:34+1);
sprintf(timeStringBuff, "%02d", timeinfo.tm_sec);
print(timeStringBuff);
display();
}
void DspCore::drawVolumeBar(bool withNumber) {
int16_t vTop = sheight - 4;
int16_t vWidth = swidth;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
fillRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_BG);
drawRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_LOGO);
fillRect(TFT_FRAMEWDT + 1, vTop + 1, ww, 1, TFT_LOGO);
if (withNumber) {
setTextSize(2);
setTextColor(TFT_FG);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 24, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);;
setCursor((swidth - wv) / 2, 24);
print(volstr);
}
display();
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(2);
setTextColor(TFT_FG);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 24, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 24);
print(numstr);
display();
}
void DspCore::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
}
void DspCore::rssi(const char* str) {
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strlcpy(buf, str, strlen(str)-2);
int16_t vTop = sheight - TFT_LINEHGHT - 4;
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::_clockSeconds(){
setTextSize(clockTimeHeight);
setTextColor((network.timeinfo.tm_sec % 2 == 0) ? config.theme.clock : config.theme.background, config.theme.background);
setCursor(_timeleft+_dotsLeft, clockTop);
print(":"); /* print dots */
setTextSize(1);
rightText(buf, vTop, SILVER, TFT_BG);
setCursor(_timeleft+_timewidth+1, clockTop);
setTextColor(config.theme.clock, config.theme.background);
sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
print(_bufforseconds);
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_LINEHGHT - 4;
setTextSize(1);
setTextColor(SILVER, TFT_BG);
setCursor(0, vTop);
print(str);
void DspCore::_clockDate(){ }
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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

View File

@@ -1,29 +1,25 @@
#ifndef displaySH1106_h
#define displaySH1106_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#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,49 +27,17 @@ 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 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

View File

@@ -1,10 +1,10 @@
#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
@@ -17,8 +17,6 @@
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
fillRect(0, texttop, swidth, textheight, bg);
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
if(y==90) y=sheight-TFT_LINEHGHT*2-5;
if(y==110) y=sheight-TFT_LINEHGHT;
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(txt);
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
setCursor(swidth - w - TFT_FRAMEWDT, y);
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
}
void DspCore::printClock(const char* timestr) {
setTextSize(2);
centerText(timestr, 34, TFT_FG, TFT_BG);
setTextSize(1);
}
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw) {
char timeStringBuff[20] = { 0 };
strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M:%S", &timeinfo);
setTextSize(2);
centerText(timeStringBuff, 30, TFT_FG, TFT_BG);
}
void DspCore::drawVolumeBar(bool withNumber) {
int16_t vTop = sheight - 4;
int16_t vWidth = swidth;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
fillRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_BG);
drawRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_LOGO);
fillRect(TFT_FRAMEWDT + 1, vTop + 1, ww, 1, TFT_LOGO);
if (withNumber) {
setTextSize(2);
setTextColor(TFT_FG);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 22, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 22);
print(volstr);
}
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(2);
setTextColor(TFT_FG);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 22, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 22);
print(numstr);
}
void DspCore::frameTitle(const char* str) {
setTextSize(1);
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
}
void DspCore::rssi(const char* str) {
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strlcpy(buf, str, strlen(str)-2);
int16_t vTop = sheight - TFT_LINEHGHT - 4;
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::_clockSeconds(){
setTextSize(clockTimeHeight);
setTextColor((network.timeinfo.tm_sec % 2 == 0) ? config.theme.clock : config.theme.background, config.theme.background);
setCursor(_timeleft+_dotsLeft, clockTop);
print(":"); /* print dots */
setTextSize(1);
rightText(buf, vTop, SILVER, TFT_BG);
setCursor(_timeleft+_timewidth+1, clockTop);
setTextColor(config.theme.clock, config.theme.background);
sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
print(_bufforseconds);
//setFont();
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_LINEHGHT - 4;
setTextSize(1);
setTextColor(SILVER, TFT_BG);
setCursor(0, vTop);
print(str);
void DspCore::_clockDate(){ }
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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();
}
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

View File

@@ -1,73 +1,39 @@
#ifndef displaySSD1305_h
#define displaySSD1305_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1305.h>
#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 BOOT_PRG_COLOR WHITE
#define BOOT_TXT_COLOR WHITE
#define PINK WHITE
#define SILVER WHITE
#define TFT_BG BLACK
#define TFT_FG WHITE

View File

@@ -1,11 +1,11 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_SSD1306 || DSP_MODEL==DSP_SSD1306x32
#include "displaySSD1306.h"
#include <Wire.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
@@ -35,139 +35,48 @@ const unsigned char logo [] PROGMEM=
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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
fillRect(0, texttop, swidth, textheight, bg);
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(txt);
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor(swidth - w - TFT_FRAMEWDT, y);
fillRect(swidth - w - TFT_FRAMEWDT-((DSP_MODEL==DSP_SSD1306)?0:2), y, w+((DSP_MODEL==DSP_SSD1306)?0:2), h, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
}
void DspCore::printClock(const char* timestr) {
#if DSP_MODEL==DSP_SSD1306
setTextSize(2);
centerText(timestr, 34, TFT_FG, TFT_BG);
setTextSize(1);
#else
setTextSize(1);
rightText(timestr, 0, TFT_FG, TFT_BG);
#endif
}
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw) {
#if DSP_MODEL==DSP_SSD1306x32
strftime(insideClc, sizeof(insideClc), dots?" %H %M":" %H:%M", &timeinfo);
#endif
}
void DspCore::drawVolumeBar(bool withNumber) {
int16_t vTop = sheight - 4;
(void)vTop;
int16_t vWidth = swidth-TFT_FRAMEWDT*2;
#if DSP_MODEL==DSP_SSD1306
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
fillRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_BG);
drawRect(TFT_FRAMEWDT, vTop, vWidth, 3, TFT_LOGO);
fillRect(TFT_FRAMEWDT + 1, vTop + 1, ww, 1, TFT_LOGO);
#else
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth);
if(fillSpaces) {
drawFastHLine(TFT_FRAMEWDT, sheight-1, swidth-TFT_FRAMEWDT*2, TFT_BG);
drawFastHLine(TFT_FRAMEWDT, sheight-1, ww, TFT_LOGO);
}
#endif
if (withNumber) {
setTextSize(2);
setTextColor(TFT_FG);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, VOL_TOP, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, VOL_TOP);
print(volstr);
}
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(2);
setTextColor(TFT_FG);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, VOL_TOP, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, VOL_TOP);
print(numstr);
}
void DspCore::frameTitle(const char* str) {
setTextSize((DSP_MODEL==DSP_SSD1306?2:1));
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
}
void DspCore::rssi(const char* str) {
if(!fillSpaces && DSP_MODEL==DSP_SSD1306x32) return;
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strlcpy(buf, str, strlen(str)-2);
int16_t vTop = sheight - TFT_LINEHGHT - ((DSP_MODEL==DSP_SSD1306)?4:2);
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::_clockSeconds(){
setTextSize(clockTimeHeight);
#if DSP_MODEL!=DSP_SSD1306x32
setTextColor((network.timeinfo.tm_sec % 2 == 0) ? config.theme.clock : config.theme.background, config.theme.background);
#else
setTextColor((network.timeinfo.tm_sec % 2 == 0) ? 0 : 1, 1);
#endif
setCursor(_timeleft+_dotsLeft, clockTop);
print(":"); /* print dots */
#if DSP_MODEL!=DSP_SSD1306x32
setTextSize(1);
rightText(buf, vTop, SILVER, TFT_BG);
setCursor(_timeleft+_timewidth+1, clockTop);
setTextColor(config.theme.clock, config.theme.background);
sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
print(_bufforseconds);
#endif
//setFont();
}
void DspCore::ip(const char* str) {
if(!fillSpaces && DSP_MODEL==DSP_SSD1306x32) return;
int16_t vTop = sheight - TFT_LINEHGHT - ((DSP_MODEL==DSP_SSD1306)?4:2);
setTextSize(1);
setTextColor(SILVER, TFT_BG);
setCursor(0, vTop);
print(str);
void DspCore::_clockDate(){ }
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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_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) {
#if DSP_MODEL==DSP_SSD1306x32
if(fillSpaces) printClock(insideClc);
#endif
display();
}
yield();
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

View File

@@ -1,95 +1,44 @@
#ifndef displaySSD1306_h
#define displaySSD1306_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#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 BOOT_PRG_COLOR WHITE
#define BOOT_TXT_COLOR WHITE
#define PINK WHITE
#define SILVER WHITE
#define TFT_BG BLACK
#define TFT_FG WHITE

View File

@@ -1,155 +1,65 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_SSD1327
#include "displaySSD1327.h"
#include <Wire.h>
#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
#endif
#ifndef I2CFREQ_HZ
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(txt);
//display();
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg, bg);
setCursor(swidth - w - TFT_FRAMEWDT, y);
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg);
print(text);
//display();
}
void DspCore::displayHeapForDebug() {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2;
void DspCore::_clockSeconds(){
setTextSize(1);
setTextColor(DARK_GRAY, TFT_BG);
setCursor(TFT_FRAMEWDT, vTop);
fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, TFT_BG);
print(ESP.getFreeHeap());
print(" / ");
print(ESP.getMaxAllocHeap());
// audio buffer;
fillRect(0, sheight - 2, swidth, 2, TFT_BG);
int astored = player.inBufferFilled();
int afree = player.inBufferFree();
int aprcnt = 100 * astored / (astored + afree);
byte sbw = map(aprcnt, 0, 100 , 0, swidth);
fillRect(0, sheight - 2, sbw, 2, DARK_GRAY);
}
void DspCore::setClockBounds(){
setFont(&DS_DIGI28pt7b);
setTextSize(1);
getTextBounds("88:88", 0, 0, &x, &y, &cwidth, &cheight);
uint16_t header = TFT_FRAMEWDT + 4 * TFT_LINEHGHT;
uint16_t footer = TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 5;
clockY = header + (sheight - header - footer) / 2 - cheight / 2;
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::printClock(const char* timestr) {
/* uint16_t ncwidth, ncheight;
setFont(&DS_DIGI28pt7b);
setTextSize(1);
getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot);
setCursor((swidth - wot) / 2 - 4, clockY+28+6);
setTextColor(TFT_BG);
print(oldTimeBuf);
strlcpy(oldTimeBuf, timestr, 20);
setTextColor(TFT_LOGO);
//fillRect(0, clockY, swidth, cheight + 3, TFT_BG);
getTextBounds(timestr, 0, 0, &x, &y, &ncwidth, &ncheight);
setCursor((swidth - ncwidth) / 2 - 4, clockY+28+6);
print(timestr);
setFont();
//display();*/
}
void DspCore::_clockDate(){ }
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);
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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, 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 */
}
setCursor(dot, clockY+28+6);
setTextColor(dots?TFT_BG:TFT_LOGO);
print(":");
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
display();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
}
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();
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();
}
//display();
//_clockSeconds();
}
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::clearClock(){
dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth, clockTimeHeight, config.theme.background);
}
void DspCore::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
//display();
}
void DspCore::startWrite(void) { }
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();
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

View File

@@ -1,75 +1,30 @@
#ifndef displaySSD1327_h
#define displaySSD1327_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1327.h>
#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

View File

@@ -1,29 +1,16 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_ST7735
#include "displayST7735.h"
#include <SPI.h>
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::centerText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect(0, y, swidth, h, bg);
print(txt);
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::rightText(const char* text, byte y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor(swidth - w - TFT_FRAMEWDT, y);
fillRect(swidth - w - TFT_FRAMEWDT, y, w, h, bg);
print(text);
}
void DspCore::displayHeapForDebug() {
#ifndef DSP_MINI
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2;
void DspCore::_clockSeconds(){
setTextSize(1);
setTextColor(config.theme.heap, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, config.theme.background);
print(ESP.getFreeHeap());
print(" / ");
print(ESP.getMaxAllocHeap());
// audio buffer;
fillRect(0, sheight - 2, swidth, 2, config.theme.background);
int astored = player.inBufferFilled();
int afree = player.inBufferFree();
int aprcnt = 100 * astored / (astored + afree);
byte sbw = map(aprcnt, 0, 100 , 0, swidth);
fillRect(0, sheight - 2, sbw, 2, config.theme.title2);
#endif
}
void DspCore::setClockBounds(){
setFont(&DS_DIGI28pt7b);
setTextSize(1);
getTextBounds("88:88", 0, 0, &x, &y, &cwidth, &cheight);
uint16_t header = TFT_FRAMEWDT + 4 * TFT_LINEHGHT;
uint16_t footer = TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 5;
clockY = header + (sheight - header - footer) / 2 - cheight / 2;
if(DTYPE==INITR_MINI160x80) clockY = clockY-6;
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::printClock(const char* timestr) {
void DspCore::_clockDate(){ }
}
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;
uint16_t clockdelta=config.store.vumeter?CLOCK_DELTA:0;
strftime(timeBuf, sizeof(timeBuf), "%H %M", &timeinfo);
void DspCore::_clockTime(){
if(_oldtimeleft>0) 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(":");
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
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();
}
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
}
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);
setFont();
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::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
}
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

View File

@@ -1,89 +1,39 @@
#ifndef displayST7735_h
#define displayST7735_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.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
#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
#endif
#define PLMITEMS 7
#define PLMITEMLENGHT 40
#if DTYPE==INITR_MINI160x80
#define PLMITEMHEIGHT 19
#include "conf/displayST7735_miniconf.h"
#elif DTYPE==INITR_144GREENTAB
#include "conf/displayST7735_144conf.h"
#else
#define PLMITEMHEIGHT 21
#include "conf/displayST7735_blackconf.h"
#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;

View File

@@ -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 <SPI.h>
//#include <SPI.h>
#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 */
#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;c<l;c++) w+=_charWidth(txt[c]);
return w;
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
fillRect(0, texttop-4, swidth, textheight+7, bg);
yield();
void DspCore::_getTimeBounds() {
_timewidth = textWidth(_timeBuf);
char buf[4];
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
void DspCore::centerText(const char* text, uint16_t y, uint16_t fg, uint16_t bg) {
int16_t x1, y1;
uint16_t w, h;
const char* txt = text;
if(y==90) y=(sheight-64)/2 + 64 + TFT_LINEHGHT;
if(y==110) y=(sheight-64)/2 + 64 + TFT_LINEHGHT*3;
getTextBounds(txt, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg);
setCursor((swidth - w) / 2, y);
fillRect((swidth-w)/2-5, y, w+10, h, bg);
print(txt);
yield();
}
void DspCore::rightText(const char* text, uint16_t y, uint16_t fg, uint16_t bg, bool fliprect, uint16_t delta) {
int16_t x1, y1;
uint16_t w, h;
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
setTextColor(fg,bg);
setCursor(swidth - w - TFT_FRAMEWDT - delta, y);
fillRect(swidth - w - TFT_FRAMEWDT, fliprect?y-h:y, w, h, bg);
print(text);
yield();
}
void DspCore::displayHeapForDebug() {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT * 2 - 2;
setTextSize(1);
setTextColor(config.theme.heap, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
fillRect(TFT_FRAMEWDT, vTop, swidth - TFT_FRAMEWDT / 2, 7, config.theme.background);
print(ESP.getFreeHeap());
print(" / ");
print(ESP.getMaxAllocHeap());
// audio buffer;
fillRect(0, sheight - 2, swidth, 2, config.theme.background);
int astored = player.inBufferFilled();
int afree = player.inBufferFree();
int aprcnt = 100 * astored / (astored + afree);
uint16_t sbw = map(aprcnt, 0, 100 , 0, swidth);
fillRect(0, sheight - 2, sbw, 2, config.theme.buffer);
yield();
}
void DspCore::printClock(const char* timestr) {
}
uint16_t cltop = 0;
uint8_t clsp = 24;
uint16_t clleft = 0;
uint16_t clwidth = 0;
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){
char timeBuf[50] = { 0 };
strftime(timeBuf, sizeof(timeBuf), "%H:%M", &timeinfo);
if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
int16_t x1, y1;
setTextSize(1);
setFont(&DS_DIGI42pt7b);
getTextBounds(oldTimeBuf, 0, 0, &x1, &y1, &wot, &hot);
if(cltop==0){
cltop=sheight-(TFT_FRAMEWDT * 2 + TFT_LINEHGHT + 38) - hot;
}
clwidth = wot+clsp+(swidth>240?46:34);
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);
setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-clockTimeHeight+1);
sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
print(_bufforseconds); /* print seconds */
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) {
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

View File

@@ -1,75 +1,36 @@
#ifndef displayST7789_h
#define displayST7789_h
#include "../core/options.h"
#include "Arduino.h"
#include <Adafruit_GFX.h>
#include <Adafruit_ST7789.h>
// https://tchapi.github.io/Adafruit-GFX-Font-Customiser/
#include "fonts/DS_DIGI42pt7b.h"
#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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 <stdint.h>
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
};

View File

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

View File

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

View File

@@ -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
#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) {
#ifdef DUMMYDISPLAY
if(mode==VOL || mode==STATIONS || mode==NUMBERS ){
if (millis() - _volDelay > (mode==VOL?3000:30000)) {
_volDelay = millis();
swichMode(PLAYER);
}
break;
}
case NUMBERS: {
//meta.loop();
break;
}
case STATIONS: {
//plCurrent.loop();
break;
}
default:
break;
}
#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");
}

View File

@@ -1,9 +1,8 @@
#ifndef NEXTION_H
#define NEXTION_H
//#include <SoftwareSerial.h>
#include <HardwareSerial.h>
#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,10 +60,6 @@ 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();
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 * >([](Widget * wd) { delete wd;})), _pages(LinkedList<Page*>([](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

View File

@@ -0,0 +1,38 @@
#ifndef pages_h
#define pages_h
#include "Arduino.h"
#include "StringArray.h"
class Page {
protected:
LinkedList<Widget*> _widgets;
LinkedList<Page*> _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*>([](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<Page*> _pages;
};
#endif

View File

@@ -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(measL<dimension) measL += _bands.fadespeed;
if(measR<dimension) measR += _bands.fadespeed;
}
if(measL>dimension) 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<dimension; i++){
if(i%(dimension/_bands.perheight)==0){
if(_config.align){
#ifndef BOOMBOX_STYLE
bandColor = (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

View File

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

View File

@@ -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,7 +38,8 @@ void setup() {
network.begin();
if (network.status != CONNECTED) {
netserver.begin();
display.start();
display.putRequest(DSP_START);
while(!display.ready()) delay(10);
return;
}
netserver.begin();
@@ -38,12 +49,11 @@ void setup() {
#endif
player.setVol(config.store.volume, true);
initControls();
display.start();
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,7 +81,7 @@ 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
@@ -78,3 +89,62 @@ void checkConnection(){
#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);
}
}