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

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

534
yoRadio/src/core/config.cpp Normal file
View File

@@ -0,0 +1,534 @@
#include "config.h"
#include <EEPROM.h>
#include <SPIFFS.h>
#include "display.h"
#include "player.h"
Config config;
void u8fix(char *src){
char last = src[strlen(src)-1];
if ((uint8_t)last >= 0xC2) src[strlen(src)-1]='\0';
}
void Config::init() {
EEPROM.begin(EEPROM_SIZE);
#if IR_PIN!=255
irindex=-1;
#endif
eepromRead(EEPROM_START, store);
if (store.config_set != 4262) setDefaults();
//if (!SPIFFS.begin(false, "/spiffs", 30)) {
if (!SPIFFS.begin(false)) {
return;
}
loadTheme();
ssidsCount = 0;
initPlaylist();
if (store.lastStation == 0 && store.countStation > 0) {
store.lastStation = 1;
save();
}
loadStation(store.lastStation);
#if IR_PIN!=255
eepromRead(EEPROM_START_IR, ircodes);
if(ircodes.ir_set!=4224){
ircodes.ir_set=4224;
memset(ircodes.irVals, 0, sizeof(ircodes.irVals));
}
#endif
#if BRIGHTNESS_PIN!=255
pinMode(BRIGHTNESS_PIN, OUTPUT);
setBrightness(false);
#endif
}
uint16_t Config::color565(uint8_t r, uint8_t g, uint8_t b)
{
return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
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);
theme.div = color565(COLOR_DIVIDER);
theme.weather = color565(COLOR_WEATHER);
theme.vumax = color565(COLOR_VU_MAX);
theme.vumin = color565(COLOR_VU_MIN);
theme.clock = color565(COLOR_CLOCK);
theme.seconds = color565(COLOR_SECONDS);
theme.dow = color565(COLOR_DAY_OF_W);
theme.date = color565(COLOR_DATE);
theme.heap = color565(COLOR_HEAP);
theme.buffer = color565(COLOR_BUFFER);
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);
theme.playlist[1] = color565(COLOR_PLAYLIST_1);
theme.playlist[2] = color565(COLOR_PLAYLIST_2);
theme.playlist[3] = color565(COLOR_PLAYLIST_3);
theme.playlist[4] = color565(COLOR_PLAYLIST_4);
}
template <class T> int Config::eepromWrite(int ee, const T& value) {
const byte* p = (const byte*)(const void*)&value;
int i;
for (i = 0; i < sizeof(value); i++)
EEPROM.write(ee++, *p++);
EEPROM.commit();
return i;
}
template <class T> int Config::eepromRead(int ee, T& value) {
byte* p = (byte*)(void*)&value;
int i;;
for (i = 0; i < sizeof(value); i++)
*p++ = EEPROM.read(ee++);
return i;
}
void Config::setDefaults() {
store.config_set = 4262;
store.volume = 12;
store.balance = 0;
store.trebble = 0;
store.middle = 0;
store.bass = 0;
store.lastStation = 0;
store.countStation = 0;
store.lastSSID = 0;
store.audioinfo = false;
store.smartstart = 2;
store.tzHour = 3;
store.tzMin = 0;
store.timezoneOffset = 0;
store.vumeter=false;
store.softapdelay=0;
store.flipscreen=false;
store.invertdisplay=false;
store.numplaylist=false;
store.fliptouch=false;
store.dbgtouch=false;
store.dspon=true;
store.brightness=100;
store.contrast=55;
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);
strlcpy(store.weatherlon,"37.6184", 10);
strlcpy(store.weatherkey,"", 64);
store.volsteps = 1;
store.encacc = 200;
store.irto = 80;
store.irtlp = 35;
store.btnpullup = true;
store.btnlongpress = 200;
store.btnclickticks = 300;
store.btnpressticks = 500;
store.encpullup = false;
store.enchalf = false;
store.enc2pullup = false;
store.enc2half = false;
store.forcemono = false;
store.i2sinternal = false;
store.rotate90 = false;
}
void Config::setTimezone(int8_t tzh, int8_t tzm) {
store.tzHour = tzh;
store.tzMin = tzm;
save();
}
void Config::setTimezoneOffset(uint16_t tzo) {
store.timezoneOffset = tzo;
save();
}
uint16_t Config::getTimezoneOffset() {
return 0; // TODO
}
void Config::save() {
eepromWrite(EEPROM_START, store);
}
#if IR_PIN!=255
void Config::saveIR(){
eepromWrite(EEPROM_START_IR, ircodes);
}
#endif
void Config::saveVolume(){
EEPROM.write(EEPROM_START + sizeof(store.config_set), store.volume);
EEPROM.commit();
}
byte Config::setVolume(byte val) {
store.volume = val;
return store.volume;
}
void Config::setTone(int8_t bass, int8_t middle, int8_t trebble) {
store.bass = bass;
store.middle = middle;
store.trebble = trebble;
save();
}
void Config::setSmartStart(byte ss) {
if (store.smartstart < 2) {
store.smartstart = ss;
save();
}
}
void Config::setBalance(int8_t balance) {
store.balance = balance;
save();
}
byte Config::setLastStation(byte val) {
store.lastStation = val;
save();
return store.lastStation;
}
byte Config::setCountStation(byte val) {
store.countStation = val;
save();
return store.countStation;
}
byte Config::setLastSSID(byte val) {
store.lastSSID = val;
save();
return store.lastSSID;
}
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);
}
void Config::setStation(const char* station) {
memset(config.station.name, 0, BUFLEN);
strlcpy(config.station.name, station, BUFLEN);
u8fix(config.station.title);
}
void Config::indexPlaylist() {
File playlist = SPIFFS.open(PLAYLIST_PATH, "r");
if (!playlist) {
return;
}
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
File index = SPIFFS.open(INDEX_PATH, "w");
while (playlist.available()) {
uint32_t pos = playlist.position();
if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
index.write((byte *) &pos, 4);
}
}
index.close();
playlist.close();
}
void Config::initPlaylist() {
store.countStation = 0;
if (!SPIFFS.exists(INDEX_PATH)) indexPlaylist();
if (SPIFFS.exists(INDEX_PATH)) {
File index = SPIFFS.open(INDEX_PATH, "r");
store.countStation = index.size() / 4;
index.close();
save();
}
}
void Config::loadStation(uint16_t ls) {
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
if (store.countStation == 0) {
memset(station.url, 0, BUFLEN);
memset(station.name, 0, BUFLEN);
strncpy(station.name, "ёRadio", BUFLEN);
station.ovol = 0;
return;
}
if (ls > store.countStation) {
ls = 1;
}
File playlist = SPIFFS.open(PLAYLIST_PATH, "r");
File index = SPIFFS.open(INDEX_PATH, "r");
index.seek((ls - 1) * 4, SeekSet);
uint32_t pos;
index.readBytes((char *) &pos, 4);
index.close();
playlist.seek(pos, SeekSet);
if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
memset(station.url, 0, BUFLEN);
memset(station.name, 0, BUFLEN);
strncpy(station.name, sName, BUFLEN);
strncpy(station.url, sUrl, BUFLEN);
station.ovol = sOvol;
setLastStation(ls);
}
playlist.close();
}
void Config::fillPlMenu(char plmenu[][40], int from, byte count, bool removeNum) {
int ls = from;
byte c = 0;
bool finded = false;
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
if (store.countStation == 0) {
return;
}
File playlist = SPIFFS.open(PLAYLIST_PATH, "r");
File index = SPIFFS.open(INDEX_PATH, "r");
while (true) {
if (ls < 1) {
ls++;
c++;
continue;
}
if (!finded) {
index.seek((ls - 1) * 4, SeekSet);
uint32_t pos;
index.readBytes((char *) &pos, 4);
finded = true;
index.close();
playlist.seek(pos, SeekSet);
}
while (playlist.available()) {
if (parseCSV(playlist.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
if(config.store.numplaylist){
if(removeNum){
strlcpy(plmenu[c], sName, 39);
}else{
char buf[BUFLEN+10];
sprintf(buf, "%d %s", (int)(from+c), sName);
strlcpy(plmenu[c], buf, 39);
}
}else{
strlcpy(plmenu[c], sName, 39);
}
c++;
}
if (c >= count) break;
}
break;
}
playlist.close();
}
bool Config::parseCSV(const char* line, char* name, char* url, int &ovol) {
char *tmpe;
const char* cursor = line;
char buf[5];
tmpe = strstr(cursor, "\t");
if (tmpe == NULL) return false;
strlcpy(name, cursor, tmpe - cursor + 1);
if (strlen(name) == 0) return false;
cursor = tmpe + 1;
tmpe = strstr(cursor, "\t");
if (tmpe == NULL) return false;
strlcpy(url, cursor, tmpe - cursor + 1);
if (strlen(url) == 0) return false;
cursor = tmpe + 1;
if (strlen(cursor) == 0) return false;
strlcpy(buf, cursor, 4);
ovol = atoi(buf);
return true;
}
bool Config::parseJSON(const char* line, char* name, char* url, int &ovol) {
char* tmps, *tmpe;
const char* cursor = line;
char port[8], host[246], file[254];
tmps = strstr(cursor, "\":\"");
if (tmps == NULL) return false;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) return false;
strlcpy(name, tmps + 3, tmpe - tmps - 3 + 1);
if (strlen(name) == 0) return false;
cursor = tmpe + 3;
tmps = strstr(cursor, "\":\"");
if (tmps == NULL) return false;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) return false;
strlcpy(host, tmps + 3, tmpe - tmps - 3 + 1);
if (strlen(host) == 0) return false;
if (strstr(host, "http://") == NULL && strstr(host, "https://") == NULL) {
sprintf(file, "http://%s", host);
strlcpy(host, file, strlen(file) + 1);
}
cursor = tmpe + 3;
tmps = strstr(cursor, "\":\"");
if (tmps == NULL) return false;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) return false;
strlcpy(file, tmps + 3, tmpe - tmps - 3 + 1);
cursor = tmpe + 3;
tmps = strstr(cursor, "\":\"");
if (tmps == NULL) return false;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) return false;
strlcpy(port, tmps + 3, tmpe - tmps - 3 + 1);
int p = atoi(port);
if (p > 0) {
sprintf(url, "%s:%d%s", host, p, file);
} else {
sprintf(url, "%s%s", host, file);
}
cursor = tmpe + 3;
tmps = strstr(cursor, "\":\"");
if (tmps == NULL) return false;
tmpe = strstr(tmps, "\"}");
if (tmpe == NULL) return false;
strlcpy(port, tmps + 3, tmpe - tmps - 3 + 1);
ovol = atoi(port);
return true;
}
bool Config::parseWsCommand(const char* line, char* cmd, char* val, byte cSize) {
char *tmpe;
tmpe = strstr(line, "=");
if (tmpe == NULL) return false;
memset(cmd, 0, cSize);
strlcpy(cmd, line, tmpe - line + 1);
if (strlen(tmpe + 1) == 0) return false;
memset(val, 0, cSize);
strlcpy(val, tmpe + 1, strlen(line) - strlen(cmd) + 1);
return true;
}
bool Config::parseSsid(const char* line, char* ssid, char* pass) {
char *tmpe;
tmpe = strstr(line, "\t");
if (tmpe == NULL) return false;
uint16_t pos = tmpe - line;
if (pos > 19 || strlen(line) > 61) return false;
memset(ssid, 0, 20);
strlcpy(ssid, line, pos + 1);
memset(pass, 0, 40);
strlcpy(pass, line + pos + 1, strlen(line) - pos);
return true;
}
bool Config::saveWifiFromNextion(const char* post){
File file = SPIFFS.open(SSIDS_PATH, "w");
if (!file) {
return false;
} else {
file.print(post);
file.close();
ESP.restart();
return true;
}
}
bool Config::saveWifi() {
if (!SPIFFS.exists(TMP_PATH)) return false;
SPIFFS.remove(SSIDS_PATH);
SPIFFS.rename(TMP_PATH, SSIDS_PATH);
ESP.restart();
return true;
}
bool Config::initNetwork() {
File file = SPIFFS.open(SSIDS_PATH, "r");
if (!file || file.isDirectory()) {
return false;
}
char ssidval[20], passval[40];
byte c = 0;
while (file.available()) {
if (parseSsid(file.readStringUntil('\n').c_str(), ssidval, passval)) {
strlcpy(ssids[c].ssid, ssidval, 20);
strlcpy(ssids[c].password, passval, 40);
ssidsCount++;
c++;
}
}
file.close();
return true;
}
void Config::setBrightness(bool dosave){
#if BRIGHTNESS_PIN!=255
if(!store.dspon && dosave) {
display.wakeup();
}
//analogWrite(BRIGHTNESS_PIN, config.store.dspon?map(store.brightness, 0, 100, 0, 255):0);
analogWrite(BRIGHTNESS_PIN, map(store.brightness, 0, 100, 0, 255));
if(!store.dspon) store.dspon = true;
if(dosave) save();
#endif
#ifdef USE_NEXTION
// if(!store.dspon && dosave) {
nextion.wake();
// }
char cmd[15];
snprintf(cmd, 15, "dims=%d", store.brightness);
nextion.putcmd(cmd);
if(!store.dspon) store.dspon = true;
if(dosave) save();
#endif
}
void Config::setDspOn(bool dspon){
store.dspon = dspon;
save();
#ifdef USE_NEXTION
if(!dspon) nextion.sleep();
else nextion.wake();
#endif
if(!dspon){
#if BRIGHTNESS_PIN!=255
analogWrite(BRIGHTNESS_PIN, 0);
#endif
display.deepsleep();
}else{
display.wakeup();
#if BRIGHTNESS_PIN!=255
analogWrite(BRIGHTNESS_PIN, map(store.brightness, 0, 100, 0, 255));
#endif
}
}
void Config::doSleep(){
if(BRIGHTNESS_PIN!=255) analogWrite(BRIGHTNESS_PIN, 0);
display.deepsleep();
#ifdef USE_NEXTION
nextion.sleep();
#endif
if(WAKE_PIN!=255) esp_sleep_enable_ext0_wakeup((gpio_num_t)WAKE_PIN, LOW);
esp_sleep_enable_timer_wakeup(config.sleepfor * 60 * 1000000ULL);
esp_deep_sleep_start();
}
void Config::sleepForAfter(uint16_t sf, uint16_t sa){
sleepfor = sf;
if(sa > 0) _sleepTimer.attach(sa * 60, doSleep);
else doSleep();
}

185
yoRadio/src/core/config.h Normal file
View File

@@ -0,0 +1,185 @@
#ifndef config_h
#define config_h
#include "Arduino.h"
#include <Ticker.h>
#include "options.h"
#define EEPROM_SIZE 768
#define EEPROM_START 500
#define EEPROM_START_IR 0
#define EEPROM_START_2 10
#define BUFLEN 140
#define PLAYLIST_PATH "/data/playlist.csv"
#define SSIDS_PATH "/data/wifi.csv"
#define TMP_PATH "/data/tmpfile.txt"
#define INDEX_PATH "/data/index.dat"
#ifdef DEBUG_V
#define DBGH() { Serial.printf("[%s:%s:%d] Heap: %d\n", __PRETTY_FUNCTION__, __FILE__, __LINE__, xPortGetFreeHeapSize()); }
#define DBGVB( ... ) { char buf[200]; sprintf( buf, __VA_ARGS__ ) ; Serial.print("[DEBUG]\t"); Serial.println(buf); }
#else
#define DBGVB( ... )
#define DBGH()
#endif
#define EVERY_MS(x) static uint32_t tmr; bool flag = millis() - tmr >= (x); if (flag) tmr += (x); if (flag)
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;
uint16_t div;
uint16_t weather;
uint16_t vumax;
uint16_t vumin;
uint16_t clock;
uint16_t seconds;
uint16_t dow;
uint16_t date;
uint16_t heap;
uint16_t buffer;
uint16_t ip;
uint16_t vol;
uint16_t rssi;
uint16_t bitrate;
uint16_t volbarout;
uint16_t volbarin;
uint16_t playlist[5];
};
struct config_t
{
unsigned int config_set; //must be 4262
byte volume;
int8_t balance;
int8_t trebble;
int8_t middle;
int8_t bass;
uint16_t lastStation;
uint16_t countStation;
byte lastSSID;
bool audioinfo;
byte smartstart;
int8_t tzHour;
int8_t tzMin;
uint16_t timezoneOffset;
bool vumeter;
uint8_t softapdelay;
bool flipscreen;
bool invertdisplay;
bool numplaylist;
bool fliptouch;
bool dbgtouch;
bool dspon;
uint8_t brightness;
uint8_t contrast;
char sntp1[35];
char sntp2[35];
bool showweather;
char weatherlat[10];
char weatherlon[10];
char weatherkey[64];
uint8_t volsteps;
uint16_t encacc;
uint8_t irto;
uint8_t irtlp;
bool btnpullup;
uint16_t btnlongpress;
uint16_t btnclickticks;
uint16_t btnpressticks;
bool encpullup;
bool enchalf;
bool enc2pullup;
bool enc2half;
bool forcemono;
bool i2sinternal;
bool rotate90;
};
#if IR_PIN!=255
struct ircodes_t
{
unsigned int ir_set; //must be 4224
uint64_t irVals[20][3];
};
#endif
struct station_t
{
char name[BUFLEN];
char url[BUFLEN];
char title[BUFLEN];
uint16_t bitrate;
int ovol;
};
struct neworkItem
{
char ssid[20];
char password[40];
};
class Config {
public:
config_t store;
station_t station;
theme_t theme;
#if IR_PIN!=255
int irindex;
uint8_t irchck;
ircodes_t ircodes;
#endif
neworkItem ssids[5];
byte ssidsCount;
uint16_t sleepfor;
public:
Config() {};
void save();
#if IR_PIN!=255
void saveIR();
#endif
void init();
void loadTheme();
byte setVolume(byte val);
void saveVolume();
void setTone(int8_t bass, int8_t middle, int8_t trebble);
void setBalance(int8_t balance);
byte setLastStation(byte val);
byte setCountStation(byte val);
byte setLastSSID(byte val);
void setTitle(const char* title);
void setStation(const char* station);
bool parseCSV(const char* line, char* name, char* url, int &ovol);
bool parseJSON(const char* line, char* name, char* url, int &ovol);
bool parseWsCommand(const char* line, char* cmd, char* val, byte cSize);
bool parseSsid(const char* line, char* ssid, char* pass);
void loadStation(uint16_t station);
bool initNetwork();
bool saveWifi();
bool saveWifiFromNextion(const char* post);
void setSmartStart(byte ss);
void initPlaylist();
void indexPlaylist();
void fillPlMenu(char plmenu[][40], int from, byte count, bool removeNum = false);
void setTimezone(int8_t tzh, int8_t tzm);
void setTimezoneOffset(uint16_t tzo);
uint16_t getTimezoneOffset();
void setBrightness(bool dosave=false);
void setDspOn(bool dspon);
void sleepForAfter(uint16_t sleepfor, uint16_t sleepafter=0);
private:
template <class T> int eepromWrite(int ee, const T& value);
template <class T> int eepromRead(int ee, T& value);
void setDefaults();
Ticker _sleepTimer;
static void doSleep();
uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
};
extern Config config;
#endif

View File

@@ -0,0 +1,635 @@
#include "Arduino.h"
#include "controls.h"
#include "options.h"
#include "config.h"
#include "player.h"
#include "display.h"
#include "netserver.h"
long encOldPosition = 0;
long enc2OldPosition = 0;
int lpId = -1;
#define ISPUSHBUTTONS BTN_LEFT!=255 || BTN_CENTER!=255 || BTN_RIGHT!=255 || ENC_BTNB!=255 || BTN_UP!=255 || BTN_DOWN!=255 || ENC2_BTNB!=255
#if ISPUSHBUTTONS
#include "OneButton.h"
OneButton button[] {{BTN_LEFT, true, BTN_INTERNALPULLUP}, {BTN_CENTER, true, BTN_INTERNALPULLUP}, {BTN_RIGHT, true, BTN_INTERNALPULLUP}, {ENC_BTNB, true, ENC_INTERNALPULLUP}, {BTN_UP, true, BTN_INTERNALPULLUP}, {BTN_DOWN, true, BTN_INTERNALPULLUP}, {ENC2_BTNB, true, ENC2_INTERNALPULLUP}};
constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]);
#endif
#if ENC_HALFQUARD==false
#define ENCODER_STEPS 4
#elif ENC_HALFQUARD==true
#define ENCODER_STEPS 2
#elif ENC_HALFQUARD==255
#define ENCODER_STEPS 1
#endif
#if ENC2_HALFQUARD==false
#define ENCODER2_STEPS 4
#elif ENC2_HALFQUARD==true
#define ENCODER2_STEPS 2
#elif ENC2_HALFQUARD==255
#define ENCODER2_STEPS 1
#endif
#if (ENC_BTNL!=255 && ENC_BTNR!=255) || (ENC2_BTNL!=255 && ENC2_BTNR!=255)
#include "../yoEncoder/yoEncoder.h"
#if (ENC_BTNL!=255 && ENC_BTNR!=255)
yoEncoder encoder = yoEncoder(ENC_BTNL, ENC_BTNR, ENCODER_STEPS, ENC_INTERNALPULLUP);
#endif
#if (ENC2_BTNL!=255 && ENC2_BTNR!=255)
yoEncoder encoder2 = yoEncoder(ENC2_BTNL, ENC2_BTNR, ENCODER2_STEPS, ENC2_INTERNALPULLUP);
#endif
#endif
#if TS_CS!=255
#include <XPT2046_Touchscreen.h>
XPT2046_Touchscreen ts(TS_CS);
#endif
#if IR_PIN!=255
#include <assert.h>
#include <IRrecv.h>
#include <IRremoteESP8266.h>
#include <IRac.h>
#include <IRtext.h>
#include <IRutils.h>
byte irVolRepeat = 0;
const uint16_t kCaptureBufferSize = 1024;
const uint8_t kTimeout = IR_TIMEOUT;
const uint16_t kMinUnknownSize = 12;
#define LEGACY_TIMING_INFO false
IRrecv irrecv(IR_PIN, kCaptureBufferSize, kTimeout, true);
decode_results irResults;
#endif
#if ENC_BTNL!=255
void IRAM_ATTR readEncoderISR()
{
encoder.readEncoder_ISR();
}
#endif
#if ENC2_BTNL!=255
void IRAM_ATTR readEncoder2ISR()
{
encoder2.readEncoder_ISR();
}
#endif
void initControls() {
#if ENC_BTNL!=255
encoder.begin();
encoder.setup(readEncoderISR);
encoder.setBoundaries(0, 254, true);
encoder.setAcceleration(config.store.encacc);
#endif
#if ENC2_BTNL!=255
encoder2.begin();
encoder2.setup(readEncoder2ISR);
encoder2.setBoundaries(0, 254, true);
encoder2.setAcceleration(config.store.encacc);
#endif
#if ISPUSHBUTTONS
for (int i = 0; i < nrOfButtons; i++)
{
if ((i == 0 && BTN_LEFT == 255) || (i == 1 && BTN_CENTER == 255) || (i == 2 && BTN_RIGHT == 255) || (i == 3 && ENC_BTNB == 255) || (i == 4 && BTN_UP == 255) || (i == 5 && BTN_DOWN == 255) || (i == 6 && ENC2_BTNB == 255)) continue;
button[i].attachClick([](void* p) {
onBtnClick((int)p);
}, (void*)i);
button[i].attachDoubleClick([](void* p) {
onBtnDoubleClick((int)p);
}, (void*)i);
button[i].attachLongPressStart([](void* p) {
onBtnLongPressStart((int)p);
}, (void*)i);
button[i].attachLongPressStop([](void* p) {
onBtnLongPressStop((int)p);
}, (void*)i);
button[i].setClickTicks(BTN_CLICK_TICKS);
button[i].setPressTicks(BTN_PRESS_TICKS);
}
#endif
#if TS_CS!=255
ts.begin();
ts.setRotation(config.store.fliptouch?3:1);
#endif
#if IR_PIN!=255
pinMode(IR_PIN, INPUT);
assert(irutils::lowLevelSanityCheck() == 0);
#if DECODE_HASH
irrecv.setUnknownThreshold(kMinUnknownSize);
#endif // DECODE_HASH
irrecv.setTolerance(config.store.irtlp);
irrecv.enableIRIn();
#endif // IR_PIN!=255
}
void loopControls() {
if(display.mode()==LOST || display.mode()==UPDATING) return;
if (ctrls_on_loop) ctrls_on_loop();
#if ENC_BTNL!=255
encoderLoop();
#endif
#if ENC2_BTNL!=255
encoder2Loop();
#endif
#if ISPUSHBUTTONS
for (unsigned i = 0; i < nrOfButtons; i++)
{
if ((i == 0 && BTN_LEFT == 255) || (i == 1 && BTN_CENTER == 255) || (i == 2 && BTN_RIGHT == 255) || (i == 3 && ENC_BTNB == 255) || (i == 4 && BTN_UP == 255) || (i == 5 && BTN_DOWN == 255) || (i == 6 && ENC2_BTNB == 255)) continue;
button[i].tick();
if (lpId >= 0) {
if (DSP_MODEL == DSP_DUMMY && (lpId == 4 || lpId == 5)) continue;
onBtnDuringLongPress(lpId);
yield();
}
yield();
}
#endif
#if IR_PIN!=255
irLoop();
#endif
#if TS_CS!=255
touchLoop();
#endif
yield();
}
#if ENC_BTNL!=255
void encoderLoop() {
int8_t encoderDelta = encoder.encoderChanged();
if (encoderDelta!=0)
{
controlsEvent(encoderDelta > 0, encoderDelta);
}
}
#endif
#if ENC2_BTNL!=255
void encoder2Loop() {
int8_t encoderDelta = encoder2.encoderChanged();
if (encoderDelta!=0)
{
uint8_t bp = 2;
if (ENC2_BTNB != 255) {
bp = digitalRead(ENC2_BTNB);
}
if (bp == HIGH && display.mode() == PLAYER) {
display.putRequest(NEWMODE, STATIONS);
while(display.mode() != STATIONS) {delay(10);}
}
controlsEvent(encoderDelta > 0, encoderDelta);
}
}
#endif
#if IR_PIN!=255
void irBlink() {
if (player.mode == STOPPED) {
for (byte i = 0; i < 7; i++) {
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
delay(100);
}
}
}
void irNum(byte num) {
uint16_t s;
if (display.numOfNextStation == 0 && num == 0) return;
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);
}
void irLoop() {
if (irrecv.decode(&irResults)) {
if(irResults.value<256) return;
if (netserver.irRecordEnable) {
Serial.print(resultToHumanReadableBasic(&irResults));
Serial.println("--------------------------");
config.ircodes.irVals[config.irindex][config.irchck]=irResults.value;
netserver.irToWs(typeToString(irResults.decode_type, irResults.repeat).c_str(), irResults.value);
return;
}
if (!irResults.repeat/* && irResults.command!=0*/) {
irVolRepeat = 0;
}
switch (irVolRepeat) {
case 1: {
controlsEvent(display.mode() == STATIONS ? false : true);
break;
}
case 2: {
controlsEvent(display.mode() == STATIONS ? true : false);
break;
}
}
for(int target=0; target<17; target++){
for(int j=0; j<3; j++){
if(config.ircodes.irVals[target][j]==irResults.value){
switch (target){
case IR_PLAY: {
irBlink();
if (display.mode() == NUMBERS) {
display.putRequest(NEWMODE, PLAYER);
player.play(display.numOfNextStation);
display.numOfNextStation = 0;
break;
}
onBtnClick(1);
break;
}
case IR_PREV: {
player.prev();
break;
}
case IR_NEXT: {
player.next();
break;
}
case IR_UP: {
controlsEvent(display.mode() == STATIONS ? false : true);
irVolRepeat = 1;
break;
}
case IR_DOWN: {
controlsEvent(display.mode() == STATIONS ? true : false);
irVolRepeat = 2;
break;
}
case IR_HASH: {
if (display.mode() == NUMBERS) {
display.putRequest(NEWMODE, PLAYER);
display.numOfNextStation = 0;
break;
}
display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER);
break;
}
case IR_0: {
irNum(0);
break;
}
case IR_1: {
irNum(1);
break;
}
case IR_2: {
irNum(2);
break;
}
case IR_3: {
irNum(3);
break;
}
case IR_4: {
irNum(4);
break;
}
case IR_5: {
irNum(5);
break;
}
case IR_6: {
irNum(6);
break;
}
case IR_7: {
irNum(7);
break;
}
case IR_8: {
irNum(8);
break;
}
case IR_9: {
irNum(9);
break;
}
case IR_AST: {
break;
}
} /* switch (target) */
target=17;
break;
} /* if(config.ircodes.irVals[target][j]==irResults.value) */
} /* for(int j=0; j<3; j++) */
} /* for(int target=0; target<16; target++) */
} /* if (irrecv.decode(&irResults)) */
}
#endif // if IR_PIN!=255
#if TS_CS!=255
#ifndef TS_X_MIN
#define TS_X_MIN 400
#endif
#ifndef TS_X_MAX
#define TS_X_MAX 3800
#endif
#ifndef TS_Y_MIN
#define TS_Y_MIN 260
#endif
#ifndef TS_Y_MAX
#define TS_Y_MAX 3800
#endif
#ifndef TS_STEPS
#define TS_STEPS 40
#endif
boolean wastouched = true;
unsigned long touchdelay;
uint16_t touchVol, touchStation;
uint16_t oldTouchP[2];
tsDirection_e direct;
unsigned long touchLongPress;
tsDirection_e tsDirection(uint16_t x, uint16_t y) {
int16_t dX = x - oldTouchP[0];
int16_t dY = y - oldTouchP[1];
if (abs(dX) > 20 || abs(dY) > 20) {
if (abs(dX) > abs(dY)) {
if (dX > 0) {
return TSD_RIGHT;
} else {
return TSD_LEFT;
}
} else {
if (dY > 0) {
return TSD_DOWN;
} else {
return TSD_UP;
}
}
} else {
return TDS_REQUEST;
}
}
void touchLoop() {
if (!checklpdelay(100, touchdelay)) return;
boolean istouched = ts.touched();
if (istouched) {
TS_Point p = ts.getPoint();
uint16_t touchX = map(p.x, TS_X_MIN, TS_X_MAX, 0, 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;
touchVol = touchX;
touchStation = touchY;
direct = TDS_REQUEST;
touchLongPress=millis();
} else { /* SWIPE TOUCH */
direct = tsDirection(touchX, touchY);
switch (direct) {
case TSD_LEFT:
case TSD_RIGHT: {
touchLongPress=millis();
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;
}
}
break;
}
case TSD_UP:
case TSD_DOWN: {
touchLongPress=millis();
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;
}
}
break;
}
default:
break;
}
}
if (config.store.dbgtouch) {
Serial.print(", x = ");
Serial.print(p.x);
Serial.print(", y = ");
Serial.println(p.y);
}
} else {
if (wastouched) {/* END TOUCH */
if (direct == TDS_REQUEST) {
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);
}
}
direct = TSD_STAY;
}
}
wastouched = istouched;
}
#endif // if TS_CS!=255
void onBtnLongPressStart(int id) {
switch ((controlEvt_e)id) {
case EVT_BTNLEFT:
case EVT_BTNRIGHT:
case EVT_BTNUP:
case EVT_BTNDOWN: {
lpId = id;
break;
}
case EVT_BTNCENTER:
case EVT_ENCBTNB: {
display.putRequest(NEWMODE, display.mode() == PLAYER ? STATIONS : PLAYER);
break;
}
case EVT_ENC2BTNB: {
display.putRequest(NEWMODE, display.mode() == PLAYER ? VOL : PLAYER);
break;
}
default:
break;
}
}
void onBtnLongPressStop(int id) {
switch ((controlEvt_e)id) {
case EVT_BTNLEFT:
case EVT_BTNRIGHT:
case EVT_BTNUP:
case EVT_BTNDOWN: {
lpId = -1;
break;
}
default:
break;
}
}
unsigned long lpdelay;
boolean checklpdelay(int m, unsigned long &tstamp) {
if (millis() - tstamp > m) {
tstamp = millis();
return true;
} else {
return false;
}
}
void onBtnDuringLongPress(int id) {
if (checklpdelay(BTN_LONGPRESS_LOOP_DELAY, lpdelay)) {
switch ((controlEvt_e)id) {
case EVT_BTNLEFT: {
controlsEvent(false);
break;
}
case EVT_BTNRIGHT: {
controlsEvent(true);
break;
}
case EVT_BTNUP:
case EVT_BTNDOWN: {
if (display.mode() == PLAYER) {
display.putRequest(NEWMODE, STATIONS);
}
if (display.mode() == STATIONS) {
controlsEvent(id == EVT_BTNDOWN);
}
break;
}
default:
break;
}
}
}
void controlsEvent(bool toRight, int8_t volDelta) {
if (display.mode() == NUMBERS) {
display.numOfNextStation = 0;
display.putRequest(NEWMODE, PLAYER);
}
if (display.mode() != STATIONS) {
display.putRequest(NEWMODE, VOL);
if(volDelta!=0){
int nv = config.store.volume+volDelta;
if(nv<0) nv=0;
if(nv>254) nv=254;
player.setVol((byte)nv, false);
}else{
player.stepVol(toRight);
}
}
if (display.mode() == STATIONS) {
display.resetQueue();
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);
}
}
void onBtnClick(int id) {
switch ((controlEvt_e)id) {
case EVT_BTNLEFT: {
controlsEvent(false);
break;
}
case EVT_BTNCENTER:
case EVT_ENCBTNB:
case EVT_ENC2BTNB: {
if (display.mode() == NUMBERS) {
display.numOfNextStation = 0;
display.putRequest(NEWMODE, PLAYER);
}
if (display.mode() == PLAYER) {
player.toggle();
}
if (display.mode() == STATIONS) {
display.putRequest(NEWMODE, PLAYER);
player.play(display.currentPlItem);
}
break;
}
case EVT_BTNRIGHT: {
controlsEvent(true);
break;
}
case EVT_BTNUP:
case EVT_BTNDOWN: {
if (DSP_MODEL == DSP_DUMMY) {
if (id == EVT_BTNUP) {
player.next();
} else {
player.prev();
}
} else {
if (display.mode() == PLAYER) {
display.putRequest(NEWMODE, STATIONS);
}
if (display.mode() == STATIONS) {
controlsEvent(id == EVT_BTNDOWN);
}
}
break;
}
}
}
void onBtnDoubleClick(int id) {
switch ((controlEvt_e)id) {
case EVT_BTNLEFT: {
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);
break;
}
case EVT_BTNRIGHT: {
if (display.mode() != PLAYER) return;
player.next();
break;
}
default:
break;
}
}
void setIRTolerance(uint8_t tl){
config.store.irtlp=tl;
config.save();
#if IR_PIN!=255
irrecv.setTolerance(config.store.irtlp);
#endif
}
void setEncAcceleration(uint16_t acc){
config.store.encacc=acc;
config.save();
#if ENC_BTNL!=255
encoder.setAcceleration(config.store.encacc);
#endif
#if ENC2_BTNL!=255
encoder2.setAcceleration(config.store.encacc);
#endif
}
void flipTS(){
#if TS_CS!=255
ts.setRotation(config.store.fliptouch?3:1);
#endif
}

View File

@@ -0,0 +1,38 @@
#ifndef controls_h
#define controls_h
#include "options.h"
enum controlEvt_e { EVT_BTNLEFT, EVT_BTNCENTER, EVT_BTNRIGHT, EVT_ENCBTNB, EVT_BTNUP, EVT_BTNDOWN, EVT_ENC2BTNB };
enum tsDirection_e { TSD_STAY, TSD_LEFT, TSD_RIGHT, TSD_UP, TSD_DOWN, TDS_REQUEST };
#if IR_PIN!=255
enum : uint8_t { IR_UP=0, IR_PREV=1, IR_PLAY=2, IR_NEXT=3, IR_DOWN=4, IR_1=5, IR_2=6, IR_3=7, IR_4=8, IR_5=9, IR_6=10, IR_7=11, IR_8=12, IR_9=13, IR_AST=14, IR_0=15, IR_HASH=16 };
#endif
boolean checklpdelay(int m, unsigned long &tstamp);
void initControls();
void loopControls();
void encoderLoop();
void encoder2Loop();
void irLoop();
void touchLoop();
void irNum(byte num);
void irBlink();
void controlsEvent(bool toRight, int8_t volDelta = 0);
void onBtnClick(int id);
void onBtnDoubleClick(int id);
void onBtnDuringLongPress(int id);
void onBtnLongPressStart(int id);
void onBtnLongPressStop(int id);
tsDirection_e tsDirection(uint16_t x, uint16_t y);
void setIRTolerance(uint8_t tl);
void setEncAcceleration(uint16_t acc);
void flipTS();
extern __attribute__((weak)) void ctrls_on_loop();
#endif

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

126
yoRadio/src/core/mqtt.cpp Normal file
View File

@@ -0,0 +1,126 @@
#include "mqtt.h"
#ifdef MQTT_HOST
#include "WiFi.h"
#include "telnet.h"
#include "player.h"
#include "config.h"
AsyncMqttClient mqttClient;
TimerHandle_t mqttReconnectTimer;
void connectToMqtt() {
mqttClient.connect();
}
void mqttInit() {
mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
mqttClient.onConnect(onMqttConnect);
mqttClient.onDisconnect(onMqttDisconnect);
mqttClient.onMessage(onMqttMessage);
if(strlen(MQTT_USER)>0) mqttClient.setCredentials(MQTT_USER, MQTT_PASS);
mqttClient.setServer(MQTT_HOST, MQTT_PORT);
connectToMqtt();
}
void onMqttConnect(bool sessionPresent) {
char buf[140];
sprintf(buf, "%s%s", MQTT_ROOT_TOPIC, "command");
mqttClient.subscribe(buf, 2);
mqttPublishStatus();
mqttPublishVolume();
mqttPublishPlaylist();
}
void mqttPublishStatus() {
if(mqttClient.connected()){
char topic[140], status[BUFLEN*3];
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "status");
sprintf(status, "{\"status\": %d, \"station\": %d, \"name\": \"%s\", \"title\": \"%s\"}", player.mode==PLAYING?1:0, config.store.lastStation, config.station.name, config.station.title);
mqttClient.publish(topic, 0, true, status);
}
}
void mqttPublishPlaylist() {
if(mqttClient.connected()){
char topic[140], playlist[140];
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "playlist");
sprintf(playlist, "http://%s%s", WiFi.localIP().toString().c_str(), PLAYLIST_PATH);
mqttClient.publish(topic, 0, true, playlist);
}
}
void mqttPublishVolume(){
if(mqttClient.connected()){
char topic[140], vol[5];
sprintf(topic, "%s%s", MQTT_ROOT_TOPIC, "volume");
sprintf(vol, "%d", config.store.volume);
mqttClient.publish(topic, 0, true, vol);
}
}
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
if (WiFi.isConnected()) {
xTimerStart(mqttReconnectTimer, 0);
}
}
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
if (len == 0) return;
char buf[20];
strlcpy(buf, payload, len+1);
if (strcmp(buf, "prev") == 0) {
player.prev();
return;
}
if (strcmp(buf, "next") == 0) {
player.next();
return;
}
if (strcmp(buf, "toggle") == 0) {
player.toggle();
return;
}
if (strcmp(buf, "stop") == 0) {
player.mode = STOPPED;
telnet.info();
return;
}
if (strcmp(buf, "start") == 0 || strcmp(buf, "play") == 0) {
//player.play(config.store.lastStation);
player.request.station = config.store.lastStation;
return;
}
if (strcmp(buf, "boot") == 0 || strcmp(buf, "reboot") == 0) {
ESP.restart();
return;
}
if (strcmp(buf, "volm") == 0) {
player.stepVol(false);
return;
}
if (strcmp(buf, "volp") == 0) {
player.stepVol(true);
return;
}
int volume;
if ( sscanf(buf, "vol %d", &volume) == 1) {
if (volume < 0) volume = 0;
if (volume > 254) volume = 254;
player.setVol(volume, false);
return;
}
//uint16_t sb;
int sb;
if (sscanf(buf, "play %d", &sb) == 1 ) {
if (sb < 1) sb = 1;
if (sb >= config.store.countStation) sb = config.store.countStation;
//player.play(sb);
player.request.station = (uint16_t)sb;
player.request.doSave = true;
return;
}
}
#endif // ifdef MQTT_HOST

21
yoRadio/src/core/mqtt.h Normal file
View File

@@ -0,0 +1,21 @@
#ifndef mqtt_h
#define mqtt_h
#if __has_include("../../mqttoptions.h")
#include "../../mqttoptions.h"
#include <AsyncMqttClient.h>
void mqttInit();
void connectToMqtt();
void onMqttConnect(bool sessionPresent);
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total);
void mqttPublishStatus();
void mqttPublishPlaylist();
void mqttPublishVolume();
#endif // if __has_include("mqttoptions.h")
#endif

View File

@@ -0,0 +1,738 @@
#include "netserver.h"
#include <SPIFFS.h>
#include "config.h"
#include "player.h"
#include "telnet.h"
#include "display.h"
#include "options.h"
#include "network.h"
#include "mqtt.h"
#include "controls.h"
#include <Update.h>
#ifndef MIN_MALLOC
#define MIN_MALLOC 24112
#endif
//#define CORS_DEBUG
NetServer netserver;
AsyncWebServer webserver(80);
AsyncWebSocket websocket("/ws");
AsyncUDP udp;
String processor(const String& var);
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
void handleUpdate(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final);
void handleHTTPArgs(AsyncWebServerRequest * request);
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len);
bool shouldReboot = false;
#ifdef MQTT_HOST
Ticker mqttplaylistticker;
bool mqttplaylistblock = false;
void mqttplaylistSend() {
mqttplaylistblock = true;
mqttplaylistticker.detach();
mqttPublishPlaylist();
mqttplaylistblock = false;
}
#endif
char* updateError() {
static char ret[140] = {0};
sprintf(ret, "Update failed with error (%d)<br /> %s", (int)Update.getError(), Update.errorString());
return ret;
}
bool NetServer::begin() {
importRequest = IMDONE;
irRecordEnable = false;
webserver.on("/", HTTP_ANY, handleHTTPArgs);
webserver.on(PLAYLIST_PATH, HTTP_GET, handleHTTPArgs);
webserver.on(INDEX_PATH, HTTP_GET, handleHTTPArgs);
webserver.on(SSIDS_PATH, HTTP_GET, handleHTTPArgs);
webserver.on("/upload", HTTP_POST, beginUpload, handleUpload);
webserver.on("/update", HTTP_GET, handleHTTPArgs);
webserver.on("/update", HTTP_POST, beginUpdate, handleUpdate);
webserver.on("/settings", HTTP_GET, handleHTTPArgs);
if (IR_PIN != 255) webserver.on("/ir", HTTP_GET, handleHTTPArgs);
webserver.serveStatic("/", SPIFFS, "/www/").setCacheControl("max-age=31536000");
#ifdef CORS_DEBUG
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Origin"), F("*"));
DefaultHeaders::Instance().addHeader(F("Access-Control-Allow-Headers"), F("content-type"));
#endif
webserver.begin();
websocket.onEvent(onWsEvent);
webserver.addHandler(&websocket);
//echo -n "helle?" | socat - udp-datagram:255.255.255.255:44490,broadcast
if (udp.listen(44490)) {
udp.onPacket([](AsyncUDPPacket packet) {
if (strcmp((char*)packet.data(), "helle?") == 0)
packet.println(WiFi.localIP());
});
}
return true;
}
void NetServer::beginUpdate(AsyncWebServerRequest *request) {
shouldReboot = !Update.hasError();
AsyncWebServerResponse *response = request->beginResponse(200, "text/plain", shouldReboot ? "OK" : updateError());
response->addHeader("Connection", "close");
request->send(response);
}
void handleUpdate(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!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);
if (!Update.begin(UPDATE_SIZE_UNKNOWN, target)) {
Update.printError(Serial);
request->send(200, "text/html", updateError());
}
}
if (!Update.hasError()) {
if (Update.write(data, len) != len) {
Update.printError(Serial);
request->send(200, "text/html", updateError());
}
}
if (final) {
if (Update.end(true)) {
Serial.printf("Update Success: %uB\n", index + len);
} else {
Update.printError(Serial);
request->send(200, "text/html", updateError());
}
}
}
void NetServer::beginUpload(AsyncWebServerRequest *request) {
if (request->hasParam("plfile", true, true)) {
netserver.importRequest = IMPL;
request->send(200);
} else if (request->hasParam("wifile", true, true)) {
netserver.importRequest = IMWIFI;
request->send(200);
} else {
request->send(404);
}
}
size_t NetServer::chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index){
File requiredfile = SPIFFS.open(netserver.chunkedPathBuffer, "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, netserver.chunkedPathBuffer, canread, maxLen);
requiredfile.seek(index, SeekSet);
requiredfile.read(buffer, canread);
index += canread;
if (requiredfile) requiredfile.close();
return canread;
}
void NetServer::chunkedHtmlPage(const String& contentType, AsyncWebServerRequest *request, const char * path, bool gzip) {
memset(chunkedPathBuffer, 0, sizeof(chunkedPathBuffer));
strlcpy(chunkedPathBuffer, path, sizeof(chunkedPathBuffer)-1);
AsyncWebServerResponse *response = request->beginChunkedResponse(contentType, chunkedHtmlPageCallback, processor);
xSemaphoreTake(player.playmutex, portMAX_DELAY);
request->send(response);
xSemaphoreGive(player.playmutex);
}
void NetServer::loop() {
if (shouldReboot) {
Serial.println("Rebooting...");
delay(100);
ESP.restart();
}
websocket.cleanupClients();
switch (importRequest) {
case IMPL: importPlaylist(); importRequest = IMDONE; break;
case IMWIFI: config.saveWifi(); importRequest = IMDONE; break;
default: break;
}
if (rssi < 255) requestOnChange(NRSSI, 0);
}
#if IR_PIN!=255
void NetServer::irToWs(const char* protocol, uint64_t irvalue) {
char buf[BUFLEN] = { 0 };
sprintf (buf, "{\"ircode\": %llu, \"protocol\": \"%s\"}", irvalue, protocol);
websocket.textAll(buf);
}
void NetServer::irValsToWs() {
if (!irRecordEnable) return;
char buf[BUFLEN] = { 0 };
sprintf (buf, "{\"irvals\": [%llu, %llu, %llu]}", config.ircodes.irVals[config.irindex][0], config.ircodes.irVals[config.irindex][1], config.ircodes.irVals[config.irindex][2]);
websocket.textAll(buf);
}
#endif
void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t clientId) {
AwsFrameInfo *info = (AwsFrameInfo*)arg;
if (info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
data[len] = 0;
char cmd[65], val[65];
if (config.parseWsCommand((const char*)data, cmd, val, 65)) {
if (strcmp(cmd, "getmode") == 0 ) { requestOnChange(GETMODE, clientId); return; }
if (strcmp(cmd, "getindex") == 0 ) { requestOnChange(GETINDEX, clientId); return; }
if (strcmp(cmd, "getsystem") == 0 ) { requestOnChange(GETSYSTEM, clientId); return; }
if (strcmp(cmd, "getscreen") == 0 ) { requestOnChange(GETSCREEN, clientId); return; }
if (strcmp(cmd, "gettimezone") == 0 ) { requestOnChange(GETTIMEZONE, clientId); return; }
if (strcmp(cmd, "getcontrols") == 0 ) { requestOnChange(GETCONTROLS, clientId); return; }
if (strcmp(cmd, "getweather") == 0 ) { requestOnChange(GETWEATHER, clientId); return; }
if (strcmp(cmd, "getactive") == 0 ) { requestOnChange(GETACTIVE, clientId); return; }
if (strcmp(cmd, "smartstart") == 0) {
byte valb = atoi(val);
config.store.smartstart = valb == 1 ? 1 : 2;
if (!player.isRunning() && config.store.smartstart == 1) config.store.smartstart = 0;
config.save();
return;
}
if (strcmp(cmd, "audioinfo") == 0) {
byte valb = atoi(val);
config.store.audioinfo = valb;
config.save();
display.putRequest(AUDIOINFO);
return;
}
if (strcmp(cmd, "vumeter") == 0) {
byte valb = atoi(val);
config.store.vumeter = valb;
config.save();
display.putRequest(SHOWVUMETER);
return;
}
if (strcmp(cmd, "softap") == 0) {
byte valb = atoi(val);
config.store.softapdelay = valb;
config.save();
return;
}
if (strcmp(cmd, "invertdisplay") == 0) {
byte valb = atoi(val);
config.store.invertdisplay = valb;
config.save();
display.invert();
return;
}
if (strcmp(cmd, "numplaylist") == 0) {
byte valb = atoi(val);
config.store.numplaylist = valb;
config.save();
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
return;
}
if (strcmp(cmd, "fliptouch") == 0) {
byte valb = atoi(val);
config.store.fliptouch = valb == 1;
config.save();
flipTS();
return;
}
if (strcmp(cmd, "dbgtouch") == 0) {
byte valb = atoi(val);
config.store.dbgtouch = valb == 1;
config.save();
return;
}
if (strcmp(cmd, "flipscreen") == 0) {
byte valb = atoi(val);
config.store.flipscreen = valb;
config.save();
display.flip();
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
return;
}
if (strcmp(cmd, "brightness") == 0) {
byte valb = atoi(val);
if (!config.store.dspon) requestOnChange(DSPON, 0);
config.store.brightness = valb;
config.setBrightness(true);
return;
}
if (strcmp(cmd, "screenon") == 0) {
byte valb = atoi(val);
config.setDspOn(valb == 1);
return;
}
if (strcmp(cmd, "contrast") == 0) {
byte valb = atoi(val);
config.store.contrast = valb;
config.save();
display.setContrast();
return;
}
if (strcmp(cmd, "tzh") == 0) {
int vali = atoi(val);
config.store.tzHour = vali;
return;
}
if (strcmp(cmd, "tzm") == 0) {
int vali = atoi(val);
config.store.tzMin = vali;
return;
}
if (strcmp(cmd, "sntp2") == 0) {
strlcpy(config.store.sntp2, val, 35);
return;
}
if (strcmp(cmd, "sntp1") == 0) {
strlcpy(config.store.sntp1, val, 35);
bool tzdone = 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);
tzdone = true;
} else if (strlen(config.store.sntp1) > 0) {
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
tzdone = true;
}
if (tzdone) {
//network.requestTimeSync(true);
network.forceTimeSync = true;
config.save();
}
return;
}
if (strcmp(cmd, "volsteps") == 0) {
uint8_t valb = atoi(val);
config.store.volsteps = valb;
config.save();
return;
}
if (strcmp(cmd, "encacceleration") == 0) {
uint16_t valb = atoi(val);
setEncAcceleration(valb);
config.store.encacc = valb;
config.save();
return;
}
if (strcmp(cmd, "irtlp") == 0) {
uint8_t valb = atoi(val);
setIRTolerance(valb);
return;
}
if (strcmp(cmd, "showweather") == 0) {
uint8_t valb = atoi(val);
config.store.showweather = valb == 1;
config.save();
network.trueWeather=false;
network.forceWeather = true;
display.putRequest(SHOWWEATHER);
return;
}
if (strcmp(cmd, "lat") == 0) {
strlcpy(config.store.weatherlat, val, 10);
return;
}
if (strcmp(cmd, "lon") == 0) {
strlcpy(config.store.weatherlon, val, 10);
return;
}
if (strcmp(cmd, "key") == 0) {
strlcpy(config.store.weatherkey, val, 64);
config.save();
network.trueWeather=false;
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
return;
}
/* RESETS */
if (strcmp(cmd, "reset") == 0) {
if (strcmp(val, "system") == 0) {
config.store.smartstart = 2;
config.store.audioinfo = false;
config.store.vumeter = false;
config.store.softapdelay = 0;
config.save();
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
requestOnChange(GETSYSTEM, clientId);
return;
}
if (strcmp(val, "screen") == 0) {
config.store.flipscreen = false;
display.flip();
config.store.invertdisplay = false;
display.invert();
config.store.dspon = true;
config.store.brightness = 100;
config.setBrightness(false);
config.store.contrast = 55;
display.setContrast();
config.store.numplaylist = false;
config.save();
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
requestOnChange(GETSCREEN, clientId);
return;
}
if (strcmp(val, "timezone") == 0) {
config.store.tzHour = 3;
config.store.tzMin = 0;
strlcpy(config.store.sntp1, "pool.ntp.org", 35);
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.forceTimeSync = true;
requestOnChange(GETTIMEZONE, clientId);
return;
}
if (strcmp(val, "weather") == 0) {
config.store.showweather = 0;
strlcpy(config.store.weatherlat, "55.7512", 10);
strlcpy(config.store.weatherlon, "37.6184", 10);
strlcpy(config.store.weatherkey, "", 64);
config.save();
network.trueWeather=false;
display.putRequest(NEWMODE, CLEAR); display.putRequest(NEWMODE, PLAYER);
requestOnChange(GETWEATHER, clientId);
return;
}
if (strcmp(val, "controls") == 0) {
config.store.volsteps = 1;
config.store.fliptouch = false;
config.store.dbgtouch = false;
setEncAcceleration(200);
setIRTolerance(40);
requestOnChange(GETCONTROLS, clientId);
return;
}
} /* EOF RESETS */
if (strcmp(cmd, "volume") == 0) {
byte v = atoi(val);
player.setVol(v, false);
}
if (strcmp(cmd, "balance") == 0) {
int8_t valb = atoi(val);
player.setBalance(valb);
config.setBalance(valb);
netserver.requestOnChange(BALANCE, 0);
return;
}
if (strcmp(cmd, "treble") == 0) {
int8_t valb = atoi(val);
player.setTone(config.store.bass, config.store.middle, valb);
config.setTone(config.store.bass, config.store.middle, valb);
netserver.requestOnChange(EQUALIZER, 0);
return;
}
if (strcmp(cmd, "middle") == 0) {
int8_t valb = atoi(val);
player.setTone(config.store.bass, valb, config.store.trebble);
config.setTone(config.store.bass, valb, config.store.trebble);
netserver.requestOnChange(EQUALIZER, 0);
return;
}
if (strcmp(cmd, "bass") == 0) {
int8_t valb = atoi(val);
player.setTone(valb, config.store.middle, config.store.trebble);
config.setTone(valb, config.store.middle, config.store.trebble);
netserver.requestOnChange(EQUALIZER, 0);
return;
}
if (strcmp(cmd, "submitplaylist") == 0) {
// xSemaphoreTake(player.playmutex, portMAX_DELAY);
return;
}
if (strcmp(cmd, "submitplaylistdone") == 0) {
#ifdef MQTT_HOST
//mqttPublishPlaylist();
mqttplaylistticker.attach(5, mqttplaylistSend);
#endif
// xSemaphoreGive(player.playmutex);
if (player.isRunning()) {
player.request.station = config.store.lastStation;
player.request.doSave = false;
}
return;
}
#if IR_PIN!=255
if (strcmp(cmd, "irbtn") == 0) {
config.irindex = atoi(val);
irRecordEnable = (config.irindex >= 0);
config.irchck = 0;
irValsToWs();
if (config.irindex < 0) config.saveIR();
}
if (strcmp(cmd, "chkid") == 0) {
config.irchck = atoi(val);
}
if (strcmp(cmd, "irclr") == 0) {
byte cl = atoi(val);
config.ircodes.irVals[config.irindex][cl] = 0;
}
#endif
}
}
}
void NetServer::getPlaylist(uint8_t clientId) {
char buf[160] = {0};
sprintf(buf, "{\"file\": \"http://%s%s\"}", WiFi.localIP().toString().c_str(), PLAYLIST_PATH);
if (clientId == 0) { websocket.textAll(buf); } else { websocket.text(clientId, buf); }
}
bool NetServer::importPlaylist() {
File tempfile = SPIFFS.open(TMP_PATH, "r");
if (!tempfile) {
return false;
}
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
String line = tempfile.readStringUntil('\n');
if (config.parseCSV(line.c_str(), sName, sUrl, sOvol)) {
File playlistfile = SPIFFS.open(PLAYLIST_PATH, "w");
playlistfile.println(line);
while (tempfile.available()) {
line = tempfile.readStringUntil('\n');
if (config.parseCSV(line.c_str(), sName, sUrl, sOvol)) {
playlistfile.println(line);
}
}
playlistfile.close();
tempfile.close();
SPIFFS.remove(TMP_PATH);
requestOnChange(PLAYLISTSAVED, 0);
return true;
}
if (config.parseJSON(line.c_str(), sName, sUrl, sOvol)) {
File playlistfile = SPIFFS.open(PLAYLIST_PATH, "w");
String wline = String(sName) + "\t" + String(sUrl) + "\t" + String(sOvol);
playlistfile.println(wline);
while (tempfile.available()) {
line = tempfile.readStringUntil('\n');
if (config.parseJSON(line.c_str(), sName, sUrl, sOvol)) {
wline = String(sName) + "\t" + String(sUrl) + "\t" + String(sOvol);
playlistfile.println(wline);
}
}
playlistfile.close();
tempfile.close();
SPIFFS.remove(TMP_PATH);
requestOnChange(PLAYLISTSAVED, 0);
return true;
}
tempfile.close();
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) {
case PLAYLIST: getPlaylist(clientId); break;
case PLAYLISTSAVED: config.indexPlaylist(); config.initPlaylist(); getPlaylist(clientId); break;
case GETACTIVE: {
bool dbgact = false, nxtn=false;
String act = F("\"group_wifi\",");
if (network.status == CONNECTED) {
act += F("\"group_system\",");
if (BRIGHTNESS_PIN != 255 || DSP_CAN_FLIPPED || DSP_MODEL == DSP_NOKIA5110 || dbgact) act += F("\"group_display\",");
#ifdef USE_NEXTION
act += F("\"group_nextion\",");
if (!SHOW_WEATHER || dbgact) act += F("\"group_weather\",");
nxtn=true;
#endif
#if defined(LCD_I2C) || defined(DSP_OLED)
act += F("\"group_oled\",");
#endif
#ifndef HIDE_VU
act += F("\"group_vu\",");
#endif
if (BRIGHTNESS_PIN != 255 || nxtn || dbgact) act += F("\"group_brightness\",");
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 (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\",");
}
act = act.substring(0, act.length() - 1);
sprintf (buf, "{\"act\":[%s]}", act.c_str());
break;
}
case GETMODE: sprintf (buf, "{\"pmode\":\"%s\"}", network.status == CONNECTED ? "player" : "ap"); break;
case GETINDEX: requestOnChange(STATION, clientId); requestOnChange(TITLE, clientId); requestOnChange(VOLUME, clientId); requestOnChange(EQUALIZER, clientId); requestOnChange(BALANCE, clientId); requestOnChange(BITRATE, clientId); requestOnChange(MODE, clientId); return; break;
case GETSYSTEM: sprintf (buf, "{\"sst\":%d,\"aif\":%d,\"vu\":%d,\"softr\":%d}", config.store.smartstart != 2, config.store.audioinfo, config.store.vumeter, config.store.softapdelay); break;
case GETSCREEN: sprintf (buf, "{\"flip\":%d,\"inv\":%d,\"nump\":%d,\"tsf\":%d,\"tsd\":%d,\"dspon\":%d,\"br\":%d,\"con\":%d}", config.store.flipscreen, config.store.invertdisplay, config.store.numplaylist, config.store.fliptouch, config.store.dbgtouch, config.store.dspon, config.store.brightness, config.store.contrast); break;
case GETTIMEZONE: sprintf (buf, "{\"tzh\":%d,\"tzm\":%d,\"sntp1\":\"%s\",\"sntp2\":\"%s\"}", config.store.tzHour, config.store.tzMin, config.store.sntp1, config.store.sntp2); break;
case GETWEATHER: sprintf (buf, "{\"wen\":%d,\"wlat\":\"%s\",\"wlon\":\"%s\",\"wkey\":\"%s\"}", config.store.showweather, config.store.weatherlat, config.store.weatherlon, config.store.weatherkey); break;
case GETCONTROLS: sprintf (buf, "{\"vols\":%d,\"enca\":%d,\"irtl\":%d}", config.store.volsteps, config.store.encacc, config.store.irtlp); break;
case DSPON: sprintf (buf, "{\"dspontrue\":%d}", 1); break;
case STATION: sprintf (buf, "{\"nameset\": \"%s\"}", config.station.name); requestOnChange(ITEM, clientId); break;
case ITEM: sprintf (buf, "{\"current\": %d}", config.store.lastStation); break;
case TITLE: sprintf (buf, "{\"meta\": \"%s\"}", config.station.title); if (player.requestToStart) { telnet.info(); player.requestToStart = false; } else { telnet.printf("##CLI.META#: %s\n> ", config.station.title); } break;
case VOLUME: sprintf (buf, "{\"vol\": %d}", config.store.volume); break;
case NRSSI: sprintf (buf, "{\"rssi\": %d}", rssi); rssi = 255; break;
case BITRATE: sprintf (buf, "{\"bitrate\": %d}", config.station.bitrate); break;
case MODE: sprintf (buf, "{\"mode\": \"%s\"}", player.mode == PLAYING ? "playing" : "stopped"); break;
case EQUALIZER: sprintf (buf, "{\"bass\": %d, \"middle\": %d, \"trebble\": %d}", config.store.bass, config.store.middle, config.store.trebble); break;
case BALANCE: sprintf (buf, "{\"balance\": %d}", config.store.balance); break;
}
if (strlen(buf) > 0) {
if (clientId == 0) { websocket.textAll(buf); }else{ websocket.text(clientId, buf); }
#ifdef MQTT_HOST
if (clientId == 0 && (request == STATION || request == ITEM || request == TITLE || request == MODE)) mqttPublishStatus();
if (clientId == 0 && request == VOLUME) mqttPublishVolume();
#endif
}
}
String processor(const String& var) { // %Templates%
if (var == "VERSION") return VERSION;
return String();
}
void handleUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
if (!index) {
request->_tempFile = SPIFFS.open(TMP_PATH , "w");
}
if (len) {
request->_tempFile.write(data, len);
//TODO check index+len size
}
if (final) {
request->_tempFile.close();
}
}
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len) {
switch (type) {
case WS_EVT_CONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str()); break;
case WS_EVT_DISCONNECT: if (config.store.audioinfo) Serial.printf("[WEBSOCKET] client #%u disconnected\n", client->id()); break;
case WS_EVT_DATA: netserver.onWsMessage(arg, data, len, client->id()); break;
case WS_EVT_PONG:
case WS_EVT_ERROR:
break;
}
}
void handleHTTPArgs(AsyncWebServerRequest * request) {
if (request->method() == HTTP_GET) {
DBGVB("[%s] client ip=%s request of %s", __func__, request->client()->remoteIP().toString().c_str(), request->url().c_str());
if (strcmp(request->url().c_str(), PLAYLIST_PATH) == 0 || strcmp(request->url().c_str(), SSIDS_PATH) == 0 || strcmp(request->url().c_str(), INDEX_PATH) == 0 || strcmp(request->url().c_str(), TMP_PATH) == 0) {
#ifdef MQTT_HOST
if (strcmp(request->url().c_str(), PLAYLIST_PATH) == 0) while (mqttplaylistblock) vTaskDelay(5);
#endif
netserver.chunkedHtmlPage("application/octet-stream", request, request->url().c_str());
return;
}
if (strcmp(request->url().c_str(), "/") == 0 && request->params() == 0) {
netserver.chunkedHtmlPage(String(), request, network.status == CONNECTED ? "/www/index.html" : "/www/settings.html");
return;
}
if (strcmp(request->url().c_str(), "/update") == 0 || strcmp(request->url().c_str(), "/settings") == 0 || strcmp(request->url().c_str(), "/ir") == 0) {
char buf[40] = { 0 };
sprintf(buf, "/www%s.html", request->url().c_str());
netserver.chunkedHtmlPage(String(), request, buf);
return;
}
}
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(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; }
if (request->hasArg("volm")) { player.stepVol(false); commandFound=true; }
if (request->hasArg("volp")) { player.stepVol(true); commandFound=true; }
if (request->hasArg("trebble") && request->hasArg("middle") && request->hasArg("bass")) {
AsyncWebParameter* pt = request->getParam("trebble", request->method() == HTTP_POST);
AsyncWebParameter* pm = request->getParam("middle", request->method() == HTTP_POST);
AsyncWebParameter* pb = request->getParam("bass", request->method() == HTTP_POST);
int t = atoi(pt->value().c_str());
int m = atoi(pm->value().c_str());
int b = atoi(pb->value().c_str());
player.setTone(b, m, t);
config.setTone(b, m, t);
netserver.requestOnChange(EQUALIZER, 0);
commandFound=true;
}
if (request->hasArg("ballance")) {
AsyncWebParameter* p = request->getParam("ballance", request->method() == HTTP_POST);
int b = atoi(p->value().c_str());
player.setBalance(b);
config.setBalance(b);
netserver.requestOnChange(BALANCE, 0);
commandFound=true;
}
if (request->hasArg("playstation") || request->hasArg("play")) {
AsyncWebParameter* p = request->getParam(request->hasArg("playstation") ? "playstation" : "play", request->method() == HTTP_POST);
int id = atoi(p->value().c_str());
if (id < 1) id = 1;
if (id > config.store.countStation) id = config.store.countStation;
player.request.station = id;
player.request.doSave = true;
commandFound=true;
DBGVB("[%s] play=%d", __func__, id);
}
if (request->hasArg("vol")) {
AsyncWebParameter* p = request->getParam("vol", request->method() == HTTP_POST);
int v = atoi(p->value().c_str());
if (v < 0) v = 0;
if (v > 254) v = 254;
config.store.volume = v;
player.setVol(v, false);
commandFound=true;
DBGVB("[%s] vol=%d", __func__, v);
}
if (request->hasArg("dspon")) {
AsyncWebParameter* p = request->getParam("dspon", request->method() == HTTP_POST);
int d = atoi(p->value().c_str());
config.setDspOn(d!=0);
commandFound=true;
}
if (request->hasArg("dim")) {
AsyncWebParameter* p = request->getParam("dim", request->method() == HTTP_POST);
int d = atoi(p->value().c_str());
if (d < 0) d = 0;
if (d > 100) d = 100;
config.store.brightness = (uint8_t)d;
config.setBrightness(true);
commandFound=true;
}
if (request->hasArg("sleep")) {
AsyncWebParameter* sfor = request->getParam("sleep", request->method() == HTTP_POST);
int sford = atoi(sfor->value().c_str());
int safterd = 0;
if(request->hasArg("after")){
AsyncWebParameter* safter = request->getParam("after", request->method() == HTTP_POST);
safterd = atoi(safter->value().c_str());
}
if(sford > 0 && safterd >= 0){
request->send(200);
config.sleepForAfter(sford, safterd);
commandFound=true;
}
}
if (request->params() > 0) {
request->send(commandFound?200:404);
return;
}
} else {
if (request->params() > 0) {
request->send(404);
return;
}
}
}

View File

@@ -0,0 +1,41 @@
#ifndef netserver_h
#define netserver_h
#include "Arduino.h"
#include "ESPAsyncWebServer.h"
#include "AsyncUDP.h"
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 };
class NetServer {
public:
import_e importRequest;
bool resumePlay;
char chunkedPathBuffer[40];
public:
NetServer() {};
bool begin();
void loop();
void requestOnChange(requestType_e request, uint8_t clientId);
void setRSSI(int val) { rssi = val; };
void chunkedHtmlPage(const String& contentType, AsyncWebServerRequest *request, const char * path, bool gzip = false);
void onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t clientId);
#if IR_PIN!=255
bool irRecordEnable;
void irToWs(const char* protocol, uint64_t irvalue);
void irValsToWs();
#endif
private:
requestType_e request;
int rssi;
void getPlaylist(uint8_t clientId);
bool importPlaylist();
static size_t chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index);
static void beginUpload(AsyncWebServerRequest *request);
static void beginUpdate(AsyncWebServerRequest *request);
};
extern NetServer netserver;
#endif

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

@@ -0,0 +1,37 @@
#ifndef network_h
#define network_h
#include <Ticker.h>
#include "time.h"
#define apSsid "yoRadioAP"
#define apPassword "12345987"
//#define TSYNC_DELAY 10800000 // 1000*60*60*3 = 3 hours
#define TSYNC_DELAY 3600000 // 1000*60*60 = 1 hour
#define WEATHER_STRING_L 254
enum n_Status_e { CONNECTED, SOFT_AP, FAILED };
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 rtimer;
void raiseSoftAP();
};
extern Network network;
extern __attribute__((weak)) void network_on_connect();
#endif

335
yoRadio/src/core/options.h Normal file
View File

@@ -0,0 +1,335 @@
#ifndef options_h
#define options_h
#define VERSION "0.8.00b"
/*******************************************************
DO NOT EDIT THIS FILE.
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 */
#endif
#if __has_include("../../mytheme.h")
#include "../../mytheme.h" /* <- Theme file */
#endif
/*******************************************************
The connection tables are located here https://github.com/e2002/yoradio#connection-tables
********************************************************/
#define DSP_DUMMY 0 // without display
#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
#ifndef DSP_MODEL
#define DSP_MODEL DSP_DUMMY
#endif
/* TFT DISPLAY */
#ifndef TFT_CS
#define TFT_CS 5
#endif
#ifndef TFT_RST
#define TFT_RST 15 // Or set to -1 and connect to Esp EN pin
#endif
#ifndef TFT_DC
#define TFT_DC 4
#endif
/* NEXTION */
#ifndef NEXTION_RX
#define NEXTION_RX 255
#endif
#ifndef NEXTION_TX
#define NEXTION_TX 255
#endif
/* OLED I2C DISPLAY */
#ifndef I2C_SDA
#define I2C_SDA 21
#endif
#ifndef I2C_SCL
#define I2C_SCL 22
#endif
#ifndef I2C_RST
#define I2C_RST -1
#endif
/* VS1053 */
#ifndef VS1053_CS
#define VS1053_CS 255 // 27
#endif
#ifndef VS1053_DCS
#define VS1053_DCS 25
#endif
#ifndef VS1053_DREQ
#define VS1053_DREQ 26
#endif
#ifndef VS1053_RST
#define VS1053_RST -1 // set to -1 if connected to Esp EN pin
#endif
/* I2S DAC */
#ifndef I2S_DOUT
#define I2S_DOUT 27 // DIN connection
#endif
#ifndef I2S_BCLK
#define I2S_BCLK 26 // BCLK Bit clock
#endif
#ifndef I2S_LRC
#define I2S_LRC 25 // WSEL Left Right Clock
#endif
/* ENCODER */
#ifndef ENC_BTNL
#define ENC_BTNL 255
#endif
#ifndef ENC_BTNB
#define ENC_BTNB 255
#endif
#ifndef ENC_BTNR
#define ENC_BTNR 255
#endif
#ifndef ENC_INTERNALPULLUP // Thanks for Buska1968. See this topic: https://4pda.to/forum/index.php?s=&showtopic=1010378&view=findpost&p=113385448
#define ENC_INTERNALPULLUP true
#endif
#ifndef ENC_HALFQUARD
#define ENC_HALFQUARD false
#endif
#ifndef ENC2_BTNL
#define ENC2_BTNL 255
#endif
#ifndef ENC2_BTNB
#define ENC2_BTNB 255
#endif
#ifndef ENC2_BTNR
#define ENC2_BTNR 255
#endif
#ifndef ENC2_INTERNALPULLUP
#define ENC2_INTERNALPULLUP true
#endif
#ifndef ENC2_HALFQUARD
#define ENC2_HALFQUARD false
#endif
/* BUTTONS */
#ifndef BTN_LEFT
#define BTN_LEFT 255
#endif
#ifndef BTN_CENTER
#define BTN_CENTER 255
#endif
#ifndef BTN_RIGHT
#define BTN_RIGHT 255
#endif
#ifndef BTN_UP
#define BTN_UP 255
#endif
#ifndef BTN_DOWN
#define BTN_DOWN 255
#endif
#ifndef BTN_INTERNALPULLUP
#define BTN_INTERNALPULLUP true
#endif
#ifndef BTN_LONGPRESS_LOOP_DELAY
#define BTN_LONGPRESS_LOOP_DELAY 200 // delay between calling DuringLongPress event
#endif
#ifndef BTN_CLICK_TICKS
#define BTN_CLICK_TICKS 300
#endif
#ifndef BTN_PRESS_TICKS
#define BTN_PRESS_TICKS 500
#endif
/* TOUCH SCREEN */
#ifndef TS_CS
#define TS_CS 255
#endif
/* LCD DISPLAY */
#ifndef LCD_RS
#define LCD_RS 255
#endif
#ifndef LCD_E
#define LCD_E 255
#endif
#ifndef LCD_D4
#define LCD_D4 255
#endif
#ifndef LCD_D5
#define LCD_D5 255
#endif
#ifndef LCD_D6
#define LCD_D6 255
#endif
#ifndef LCD_D7
#define LCD_D7 255
#endif
/* ESP DEVBOARD */
#ifndef LED_BUILTIN
#define LED_BUILTIN 2
#endif
/* Other settings. You can overwrite them in the myoptions.h file */
#ifndef MUTE_PIN
#define MUTE_PIN 255 // MUTE Pin
#endif
#ifndef MUTE_VAL
#define MUTE_VAL HIGH // Write this to MUTE_PIN when player is stopped
#endif
#ifndef BRIGHTNESS_PIN
#define BRIGHTNESS_PIN 255 // BRIGHTNESS Pin
#endif
#ifndef PLAYER_FORCE_MONO
#define PLAYER_FORCE_MONO false // mono option - false stereo, true mono
#endif
#ifndef I2S_INTERNAL
#define I2S_INTERNAL false // If true - use esp32 internal DAC
#endif
#ifndef ROTATE_90
#define ROTATE_90 false // Optional 90 degree rotation for square displays
#endif
#ifndef WAKE_PIN
#define WAKE_PIN 255 // Wake Pin (for manual wakeup from sleep mode. can match with BTN_XXXX, ENC_BTNB, ENC2_BTNB. must be one of: 0,2,4,12,13,14,15,25,26,27,32,33,34,35,36,39)
#endif
/*
*** ST7735 display submodel ***
INITR_BLACKTAB // 1.8' https://aliexpress.ru/item/1005002822797745.html
See this note If INITR_BLACKTAB have a noisy line on one side of the screen https://github.com/e2002/yoradio#note-if-initr_blacktab-dsp-have-a-noisy-line-on-one-side-of-the-screen-then-in-adafruit_st7735cpp
INITR_144GREENTAB // 1.44' https://aliexpress.ru/item/1005002822797745.html
INITR_MINI160x80 // 0.96' 160x80 ST7735S https://????
INITR_GREENTAB
INITR_REDTAB
*/
#ifndef DTYPE
#define DTYPE INITR_BLACKTAB
#endif
/* IR */
#ifndef IR_PIN
#define IR_PIN 255
#endif
#ifndef IR_TIMEOUT
#define IR_TIMEOUT 80 // kTimeout, see IRremoteESP8266 documentation
#endif
/* THEMES */
/* color name R G B */
#ifndef COLOR_BACKGROUND
#define COLOR_BACKGROUND 0, 0, 0
#endif
#ifndef COLOR_STATION_NAME
#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
#endif
#ifndef COLOR_SNG_TITLE_2
#define COLOR_SNG_TITLE_2 165, 162, 132
#endif
#ifndef COLOR_WEATHER
#define COLOR_WEATHER 255, 150, 0
#endif
#ifndef COLOR_VU_MAX
#define COLOR_VU_MAX 231, 211, 90
#endif
#ifndef COLOR_VU_MIN
#define COLOR_VU_MIN 123, 125, 123
#endif
#ifndef COLOR_CLOCK
#define COLOR_CLOCK 231, 211, 90
#endif
#ifndef COLOR_SECONDS
#define COLOR_SECONDS 231, 211, 90
#endif
#ifndef COLOR_DAY_OF_W
#define COLOR_DAY_OF_W 255, 255, 255
#endif
#ifndef COLOR_DATE
#define COLOR_DATE 255, 255, 255
#endif
#ifndef COLOR_HEAP
#define COLOR_HEAP 41, 40, 41
#endif
#ifndef COLOR_BUFFER
#define COLOR_BUFFER 165, 162, 132
#endif
#ifndef COLOR_IP
#define COLOR_IP 165, 162, 132
#endif
#ifndef COLOR_VOLUME_VALUE
#define COLOR_VOLUME_VALUE 165, 162, 132
#endif
#ifndef COLOR_RSSI
#define COLOR_RSSI 165, 162, 132
#endif
#ifndef COLOR_VOLBAR_OUT
#define COLOR_VOLBAR_OUT 231, 211, 90
#endif
#ifndef COLOR_VOLBAR_IN
#define COLOR_VOLBAR_IN 231, 211, 90
#endif
#ifndef COLOR_DIGITS
#define COLOR_DIGITS 255, 255, 255
#endif
#ifndef COLOR_DIVIDER
#define COLOR_DIVIDER 165, 162, 132
#endif
#ifndef COLOR_PLAYLIST_0
#define COLOR_PLAYLIST_0 115, 115, 115
#endif
#ifndef COLOR_PLAYLIST_1
#define COLOR_PLAYLIST_1 89, 89, 89
#endif
#ifndef COLOR_PLAYLIST_2
#define COLOR_PLAYLIST_2 56, 56, 56
#endif
#ifndef COLOR_PLAYLIST_3
#define COLOR_PLAYLIST_3 35, 35, 35
#endif
#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

200
yoRadio/src/core/player.cpp Normal file
View File

@@ -0,0 +1,200 @@
#include "options.h"
#include "player.h"
#include "config.h"
#include "telnet.h"
#include "display.h"
#include "netserver.h"
Player player;
#if VS1053_CS!=255 && !I2S_INTERNAL
Player::Player(): Audio(VS1053_CS, VS1053_DCS, VS1053_DREQ) {
}
void ResetChip(){
pinMode(VS1053_RST, OUTPUT);
digitalWrite(VS1053_RST, LOW);
delay(30);
digitalWrite(VS1053_RST, HIGH);
delay(100);
}
#else
#if !I2S_INTERNAL
Player::Player() {}
#else
Player::Player(): Audio(true, I2S_DAC_CHANNEL_BOTH_EN) {}
#endif
#endif
void Player::init() {
if(MUTE_PIN!=255) pinMode(MUTE_PIN, OUTPUT);
#if I2S_DOUT!=255
#if !I2S_INTERNAL
setPinout(I2S_BCLK, I2S_LRC, I2S_DOUT);
#endif
#else
SPI.begin();
if(VS1053_RST>0) ResetChip();
begin();
#endif
setBalance(config.store.balance);
setTone(config.store.bass, config.store.middle, config.store.trebble);
setVolume(0);
mode = STOPPED;
setOutputPins(false);
requestToStart = true;
volTimer=false;
zeroRequest();
playmutex = xSemaphoreCreateMutex();
}
void Player::stopInfo() {
config.setSmartStart(0);
telnet.info();
netserver.requestOnChange(MODE, 0);
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);
} else {
if (isRunning()) stop();
}
if (request.station > 0) {
if (request.doSave) {
config.setLastStation(request.station);
}
play(request.station);
if (player_on_station_change) player_on_station_change();
zeroRequest();
}
if (request.volume >= 0) {
config.setVolume(request.volume);
telnet.printf("##CLI.VOL#: %d\n", config.store.volume);
Audio::setVolume(volToI2S(request.volume));
zeroRequest();
display.putRequest(DRAWVOL);
netserver.requestOnChange(VOLUME, 0);
}
if(volTimer){
if((millis()-volTicks)>3000){
config.saveVolume();
volTimer=false;
}
}
}
void Player::zeroRequest() {
request.station = 0;
request.volume = -1;
request.doSave = false;
}
void Player::setOutputPins(bool isPlaying) {
digitalWrite(LED_BUILTIN, isPlaying);
if(MUTE_PIN!=255) digitalWrite(MUTE_PIN, isPlaying?!MUTE_VAL:MUTE_VAL);
}
void Player::play(uint16_t stationId) {
display.putRequest(PSTOP);
setDefaults();
setOutputPins(false);
config.setTitle(const_PlConnect);
config.station.bitrate=0;
netserver.requestOnChange(TITLE, 0);
config.loadStation(stationId);
setVol(config.store.volume, true);
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)) {
mode = PLAYING;
config.setSmartStart(1);
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...");
};
}
void Player::prev() {
if (config.store.lastStation == 1) config.store.lastStation = config.store.countStation; else config.store.lastStation--;
request.station = config.store.lastStation;
request.doSave = true;
}
void Player::next() {
if (config.store.lastStation == config.store.countStation) config.store.lastStation = 1; else config.store.lastStation++;
request.station = config.store.lastStation;
request.doSave = true;
}
void Player::toggle() {
if (mode == PLAYING) {
mode = STOPPED;
} else {
request.station = config.store.lastStation;
}
}
void Player::stepVol(bool up) {
if (up) {
if (config.store.volume <= 254 - config.store.volsteps) {
setVol(config.store.volume + config.store.volsteps, false);
}else{
setVol(254, false);
}
} else {
if (config.store.volume >= config.store.volsteps) {
setVol(config.store.volume - config.store.volsteps, false);
}else{
setVol(0, false);
}
}
}
byte Player::volToI2S(byte volume) {
int vol = map(volume, 0, 254 - config.station.ovol * 3 , 0, 254);
if (vol > 254) vol = 254;
if (vol < 0) vol = 0;
return vol;
}
void Player::setVol(byte volume, bool inside) {
if (inside) {
setVolume(volToI2S(volume));
} else {
volTicks = millis();
volTimer = true;
request.volume = volume;
request.doSave = true;
}
}

51
yoRadio/src/core/player.h Normal file
View File

@@ -0,0 +1,51 @@
#ifndef player_h
#define player_h
#include "options.h"
#if I2S_DOUT!=255 || I2S_INTERNAL
#include "../audioI2S/AudioEx.h"
#else
#include "../audioVS1053/audioVS1053Ex.h"
#endif
enum audioMode_e { PLAYING, STOPPED };
struct audiorequest_t
{
uint16_t station;
int volume;
bool doSave;
};
class Player: public Audio {
private:
uint32_t volTicks; /* delayed volume save */
bool volTimer; /* delayed volume save */
public:
audioMode_e mode;
audiorequest_t request;
bool requestToStart;
void zeroRequest();
SemaphoreHandle_t playmutex=NULL;
public:
Player();
void init();
void loop();
void play(uint16_t stationId);
void stop(const char *nttl = NULL);
void prev();
void next();
void toggle();
void stepVol(bool up);
void setVol(byte volume, bool inside);
byte volToI2S(byte volume);
void stopInfo();
void setOutputPins(bool isPlaying);
};
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

460
yoRadio/src/core/telnet.cpp Normal file
View File

@@ -0,0 +1,460 @@
#include <stdarg.h>
#include "WiFi.h"
#include "config.h"
#include "player.h"
#include "network.h"
#include "telnet.h"
Telnet telnet;
bool Telnet::_isIPSet(IPAddress ip) {
return ip.toString() == "0.0.0.0";
}
bool Telnet::begin() {
if (WiFi.status() == WL_CONNECTED || _isIPSet(WiFi.softAPIP())) {
server.begin();
server.setNoDelay(true);
Serial.printf("Ready! Use 'telnet %s 23' to connect\n", WiFi.localIP().toString().c_str());
return true;
} else {
return false;
}
}
void Telnet::stop() {
server.stop();
}
void Telnet::emptyClientStream(WiFiClient client) {
client.flush();
delay(50);
while (client.available()) {
client.read();
}
}
void Telnet::cleanupClients() {
for (int i = 0; i < MAX_TLN_CLIENTS; i++) {
if (!clients[i].connected()) {
if (clients[i]) {
Serial.printf("Client [%d] is %s\n", i, clients[i].connected() ? "connected" : "disconnected");
clients[i].stop();
}
}
}
}
void Telnet::handleSerial(){
if(Serial.available()){
String request = Serial.readStringUntil('\n'); request.trim();
on_input(request.c_str(), 100);
}
}
void Telnet::loop() {
uint8_t i;
if (WiFi.status() == WL_CONNECTED) {
if (server.hasClient()) {
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
if (!clients[i] || !clients[i].connected()) {
if (clients[i]) {
clients[i].stop();
}
clients[i] = server.available();
if (!clients[i]) Serial.println("available broken");
on_connect(clients[i].remoteIP().toString().c_str(), i);
clients[i].setNoDelay(true);
emptyClientStream(clients[i]);
break;
}
}
if (i >= MAX_TLN_CLIENTS) {
server.available().stop();
}
}
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
if (clients[i] && clients[i].connected() && clients[i].available()) {
String inputstr = clients[i].readStringUntil('\n');
inputstr.trim();
on_input(inputstr.c_str(), i);
}
}
} else {
for (i = 0; i < MAX_TLN_CLIENTS; i++) {
if (clients[i]) {
clients[i].stop();
}
}
delay(1000);
}
handleSerial();
yield();
}
void Telnet::print(const char *buf) {
for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
if (clients[id] && clients[id].connected()) {
print(id, buf);
}
}
Serial.print(buf);
}
void Telnet::print(byte id, const char *buf) {
if (clients[id] && clients[id].connected()) {
clients[id].print(buf);
}
}
void Telnet::printf(const char *format, ...) {
char buf[MAX_PRINTF_LEN];
va_list args;
va_start (args, format );
vsnprintf(buf, MAX_PRINTF_LEN, format, args);
va_end (args);
for (int id = 0; id < MAX_TLN_CLIENTS; id++) {
if (clients[id] && clients[id].connected()) {
clients[id].print(buf);
}
}
Serial.print(buf);
}
/*void Telnet::printf(byte id, const char *format, ...) {
va_list argptr;
va_start(argptr, format);
char *szBuffer = 0;
const size_t nBufferLength = vsnprintf(szBuffer, 0, format, argptr) + 1;
if (nBufferLength == 1) return;
szBuffer = (char *) malloc(nBufferLength);
if (! szBuffer) return;
vsnprintf(szBuffer, nBufferLength, format, argptr);
va_end(argptr);
if(id>MAX_TLN_CLIENTS){
Serial.print(szBuffer);
free(szBuffer);
return;
}
if (clients[id] && clients[id].connected()) {
clients[id].print(szBuffer);
free(szBuffer);
}
}*/
void Telnet::printf(byte id, const char *format, ...) {
char buf[MAX_PRINTF_LEN];
va_list argptr;
va_start(argptr, format);
vsnprintf(buf, MAX_PRINTF_LEN, format, argptr);
va_end(argptr);
if(id>MAX_TLN_CLIENTS){
Serial.print(buf);
return;
}
if (clients[id] && clients[id].connected()) {
clients[id].print(buf);
}
}
void Telnet::on_connect(const char* str, byte clientId) {
Serial.printf("Telnet: [%d] %s connected\n", clientId, str);
print(clientId, "\nWelcome to ёRadio!\n(Use ^] + q to disconnect.)\n> ");
}
void Telnet::info() {
telnet.printf("##CLI.INFO#\n");
char timeStringBuff[50];
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &network.timeinfo);
telnet.printf("##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset
telnet.printf("##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
if (player.mode == PLAYING) {
telnet.printf("##CLI.META#: %s\n", config.station.title);
}
telnet.printf("##CLI.VOL#: %d\n", config.store.volume);
if (player.mode == PLAYING) {
telnet.printf("##CLI.PLAYING#\n");
} else {
telnet.printf("##CLI.STOPPED#\n");
}
telnet.printf("> ");
}
void Telnet::on_input(const char* str, byte clientId) {
if (strlen(str) == 0) return;
if(network.status == CONNECTED){
if (strcmp(str, "cli.prev") == 0 || strcmp(str, "prev") == 0) {
player.prev();
return;
}
if (strcmp(str, "cli.next") == 0 || strcmp(str, "next") == 0) {
player.next();
return;
}
if (strcmp(str, "cli.toggle") == 0 || strcmp(str, "toggle") == 0) {
player.toggle();
return;
}
if (strcmp(str, "cli.stop") == 0 || strcmp(str, "stop") == 0) {
player.mode = STOPPED;
//display.title("[stopped]");
info();
return;
}
if (strcmp(str, "cli.start") == 0 || strcmp(str, "start") == 0 || strcmp(str, "cli.play") == 0 || strcmp(str, "play") == 0) {
player.play(config.store.lastStation);
return;
}
if (strcmp(str, "cli.vol") == 0 || strcmp(str, "vol") == 0) {
printf(clientId, "##CLI.VOL#: %d\n> ", config.store.volume);
return;
}
if (strcmp(str, "cli.vol-") == 0 || strcmp(str, "vol-") == 0) {
player.stepVol(false);
return;
}
if (strcmp(str, "cli.vol+") == 0 || strcmp(str, "vol+") == 0) {
player.stepVol(true);
return;
}
if (strcmp(str, "sys.date") == 0 || strcmp(str, "date") == 0 || strcmp(str, "time") == 0) {
network.requestTimeSync(true, clientId > MAX_TLN_CLIENTS?clientId:0);
return;
}
int volume;
if (sscanf(str, "vol(%d)", &volume) == 1 || sscanf(str, "cli.vol(\"%d\")", &volume) == 1 || sscanf(str, "vol %d", &volume) == 1) {
if (volume < 0) volume = 0;
if (volume > 254) volume = 254;
player.setVol(volume, false);
return;
}
if (strcmp(str, "cli.audioinfo") == 0 || strcmp(str, "audioinfo") == 0) {
printf(clientId, "##CLI.AUDIOINFO#: %d\n> ", config.store.audioinfo > 0);
return;
}
int ainfo;
if (sscanf(str, "audioinfo(%d)", &ainfo) == 1 || sscanf(str, "cli.audioinfo(\"%d\")", &ainfo) == 1 || sscanf(str, "audioinfo %d", &ainfo) == 1) {
config.store.audioinfo = ainfo > 0;
printf(clientId, "new audioinfo value is: %d\n> ", config.store.audioinfo);
config.save();
return;
}
if (strcmp(str, "cli.smartstart") == 0 || strcmp(str, "smartstart") == 0) {
printf(clientId, "##CLI.SMARTSTART#: %d\n> ", config.store.smartstart);
return;
}
int sstart;
if (sscanf(str, "smartstart(%d)", &sstart) == 1 || sscanf(str, "cli.smartstart(\"%d\")", &sstart) == 1 || sscanf(str, "smartstart %d", &sstart) == 1) {
config.store.smartstart = (byte)sstart;
printf(clientId, "new smartstart value is: %d\n> ", config.store.smartstart);
config.save();
return;
}
if (strcmp(str, "cli.list") == 0 || strcmp(str, "list") == 0) {
printf(clientId, "#CLI.LIST#\n");
File file = SPIFFS.open(PLAYLIST_PATH, "r");
if (!file || file.isDirectory()) {
return;
}
char sName[BUFLEN], sUrl[BUFLEN];
int sOvol;
byte c = 1;
while (file.available()) {
if (config.parseCSV(file.readStringUntil('\n').c_str(), sName, sUrl, sOvol)) {
printf(clientId, "#CLI.LISTNUM#: %*d: %s, %s\n", 3, c, sName, sUrl);
c++;
}
}
printf(clientId, "##CLI.LIST#\n");
printf(clientId, "> ");
return;
}
if (strcmp(str, "cli.info") == 0 || strcmp(str, "info") == 0) {
printf(clientId, "##CLI.INFO#\n");
char timeStringBuff[50];
strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S", &network.timeinfo);
if (config.store.tzHour < 0) {
printf(clientId, "##SYS.DATE#: %s%03d:%02d\n", timeStringBuff, config.store.tzHour, config.store.tzMin);
} else {
printf(clientId, "##SYS.DATE#: %s+%02d:%02d\n", timeStringBuff, config.store.tzHour, config.store.tzMin);
}
printf(clientId, "##CLI.NAMESET#: %d %s\n", config.store.lastStation, config.station.name);
if (player.mode == PLAYING) {
printf(clientId, "##CLI.META#: %s\n", config.station.title);
}
printf(clientId, "##CLI.VOL#: %d\n", config.store.volume);
if (player.mode == PLAYING) {
printf(clientId, "##CLI.PLAYING#\n");
} else {
printf(clientId, "##CLI.STOPPED#\n");
}
printf(clientId, "> ");
return;
}
int sb;
if (sscanf(str, "play(%d)", &sb) == 1 || sscanf(str, "cli.play(\"%d\")", &sb) == 1 || sscanf(str, "play %d", &sb) == 1 ) {
if (sb < 1) sb = 1;
if (sb >= config.store.countStation) sb = config.store.countStation;
player.play((uint16_t)sb);
return;
}
if (strcmp(str, "sys.tzo") == 0 || strcmp(str, "tzo") == 0) {
printf(clientId, "##SYS.TZO#: %d:%d\n> ", config.store.tzHour, config.store.tzMin);
return;
}
//int16_t tzh, tzm;
int tzh, tzm;
if (sscanf(str, "tzo(%d:%d)", &tzh, &tzm) == 2 || sscanf(str, "sys.tzo(\"%d:%d\")", &tzh, &tzm) == 2 || sscanf(str, "tzo %d:%d", &tzh, &tzm) == 2) {
if (tzh < -12) tzh = -12;
if (tzh > 14) tzh = 14;
if (tzm < 0) tzm = 0;
if (tzm > 59) tzm = 59;
config.setTimezone((int8_t)tzh, (int8_t)tzm);
if(tzh<0){
printf(clientId, "new timezone offset: %03d:%02d\n", config.store.tzHour, config.store.tzMin);
}else{
printf(clientId, "new timezone offset: %02d:%02d\n", config.store.tzHour, config.store.tzMin);
}
network.requestTimeSync(true);
return;
}
if (sscanf(str, "tzo(%d)", &tzh) == 1 || sscanf(str, "sys.tzo(\"%d\")", &tzh) == 1 || sscanf(str, "tzo %d", &tzh) == 1) {
if (tzh < -12) tzh = -12;
if (tzh > 14) tzh = 14;
config.setTimezone((int8_t)tzh, 0);
if(tzh<0){
printf(clientId, "new timezone offset: %03d:%02d\n", config.store.tzHour, config.store.tzMin);
}else{
printf(clientId, "new timezone offset: %02d:%02d\n", config.store.tzHour, config.store.tzMin);
}
network.requestTimeSync(true);
return;
}
if (sscanf(str, "dspon(%d)", &tzh) == 1 || sscanf(str, "cli.dspon(\"%d\")", &tzh) == 1 || sscanf(str, "dspon %d", &tzh) == 1) {
config.setDspOn(tzh!=0);
return;
}
if (sscanf(str, "dim(%d)", &tzh) == 1 || sscanf(str, "cli.dim(\"%d\")", &tzh) == 1 || sscanf(str, "dim %d", &tzh) == 1) {
if (tzh < 0) tzh = 0;
if (tzh > 100) tzh = 100;
config.store.brightness = (uint8_t)tzh;
config.setBrightness(true);
return;
}
if (sscanf(str, "sleep(%d,%d)", &tzh, &tzm) == 2 || sscanf(str, "cli.sleep(\"%d\",\"%d\")", &tzh, &tzm) == 2 || sscanf(str, "sleep %d %d", &tzh, &tzm) == 2) {
if(tzh>0 && tzm>0) {
printf(clientId, "sleep for %d minutes after %d minutes ...\n> ", tzh, tzm);
config.sleepForAfter(tzh, tzm);
}else{
printf(clientId, "##CMD_ERROR#\tunknown command <%s>\n> ", str);
}
return;
}
if (sscanf(str, "sleep(%d)", &tzh) == 1 || sscanf(str, "cli.sleep(\"%d\")", &tzh) == 1 || sscanf(str, "sleep %d", &tzh) == 1) {
if(tzh>0) {
printf(clientId, "sleep for %d minutes ...\n> ", tzh);
config.sleepForAfter(tzh);
}else{
printf(clientId, "##CMD_ERROR#\tunknown command <%s>\n> ", str);
}
return;
}
}
if (strcmp(str, "sys.version") == 0 || strcmp(str, "version") == 0) {
printf(clientId, "##SYS.VERSION#: %s\n> ", VERSION);
return;
}
if (strcmp(str, "sys.boot") == 0 || strcmp(str, "boot") == 0 || strcmp(str, "reboot") == 0) {
ESP.restart();
return;
}
if (strcmp(str, "wifi.list") == 0 || strcmp(str, "wifi") == 0) {
printf(clientId, "#WIFI.SCAN#\n");
int n = WiFi.scanNetworks();
if (n == 0) {
printf(clientId, "no networks found\n");
} else {
for (int i = 0; i < n; ++i) {
printf(clientId, "%d", i + 1);
printf(clientId, ": ");
printf(clientId, "%s", WiFi.SSID(i));
printf(clientId, " (");
printf(clientId, "%d", WiFi.RSSI(i));
printf(clientId, ")");
printf(clientId, (WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*");
printf(clientId, "\n");
delay(10);
}
}
printf(clientId, "#WIFI.SCAN#\n> ");
return;
}
if (strcmp(str, "wifi.con") == 0 || strcmp(str, "conn") == 0) {
printf(clientId, "#WIFI.CON#\n");
File file = SPIFFS.open(SSIDS_PATH, "r");
if (file && !file.isDirectory()) {
char sSid[BUFLEN], sPas[BUFLEN];
byte c = 1;
while (file.available()) {
if (config.parseSsid(file.readStringUntil('\n').c_str(), sSid, sPas)) {
printf(clientId, "%d: %s, %s\n", c, sSid, sPas);
c++;
}
}
}
printf(clientId, "##WIFI.CON#\n> ");
return;
}
if (strcmp(str, "wifi.station") == 0 || strcmp(str, "station") == 0 || strcmp(str, "ssid") == 0) {
printf(clientId, "#WIFI.STATION#\n");
File file = SPIFFS.open(SSIDS_PATH, "r");
if (file && !file.isDirectory()) {
char sSid[BUFLEN], sPas[BUFLEN];
byte c = 1;
while (file.available()) {
if (config.parseSsid(file.readStringUntil('\n').c_str(), sSid, sPas)) {
if(c==config.store.lastSSID) printf(clientId, "%d: %s, %s\n", c, sSid, sPas);
c++;
}
}
}
printf(clientId, "##WIFI.STATION#\n> ");
return;
}
char newssid[20], newpass[40];
if (sscanf(str, "wifi.con(\"%[^\"]\",\"%[^\"]\")", newssid, newpass) == 2 || sscanf(str, "wifi.con(%[^,],%[^)])", newssid, newpass) == 2 || sscanf(str, "wifi.con(%[^ ] %[^)])", newssid, newpass) == 2 || sscanf(str, "wifi %[^ ] %s", newssid, newpass) == 2) {
char buf[BUFLEN];
snprintf(buf, BUFLEN, "New SSID: \"%s\" with PASS: \"%s\" for next boot\n> ", newssid, newpass);
printf(clientId, buf);
printf(clientId, "...REBOOTING...\n> ");
memset(buf, 0, BUFLEN);
snprintf(buf, BUFLEN, "%s\t%s", newssid, newpass);
config.saveWifiFromNextion(buf);
return;
}
if (strcmp(str, "wifi.status") == 0 || strcmp(str, "status") == 0) {
printf(clientId, "#WIFI.STATUS#\nStatus:\t\t%d\nMode:\t\t%s\nIP:\t\t%s\nMask:\t\t%s\nGateway:\t%s\nRSSI:\t\t%d dBm\n##WIFI.STATUS#\n> ",
WiFi.status(), WiFi.getMode()==WIFI_STA?"WIFI_STA":"WIFI_AP",
WiFi.getMode()==WIFI_STA?WiFi.localIP().toString():WiFi.softAPIP().toString(),
WiFi.getMode()==WIFI_STA?WiFi.subnetMask().toString():"255.255.255.0",
WiFi.getMode()==WIFI_STA?WiFi.gatewayIP().toString():WiFi.softAPIP().toString(),
WiFi.RSSI()
);
return;
}
if (strcmp(str, "wifi.rssi") == 0 || strcmp(str, "rssi") == 0) {
printf(clientId, "#WIFI.RSSI#\t%d dBm\n> ", WiFi.RSSI());
return;
}
if (strcmp(str, "sys.heap") == 0 || strcmp(str, "heap") == 0) {
printf(clientId, "Free heap:\t%d bytes\n> ", xPortGetFreeHeapSize());
return;
}
if (strcmp(str, "wifi.discon") == 0 || strcmp(str, "discon") == 0 || strcmp(str, "disconnect") == 0) {
printf(clientId, "#WIFI.DISCON#\tdisconnected...\n> ");
WiFi.disconnect();
return;
}
telnet.printf(clientId, "##CMD_ERROR#\tunknown command <%s>\n> ", str);
}

34
yoRadio/src/core/telnet.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef telnet_h
#define telnet_h
#include <WiFi.h>
#define MAX_TLN_CLIENTS 5
#define MAX_PRINTF_LEN BUFLEN+50
class Telnet {
public:
Telnet() {};
bool begin();
void loop();
void stop();
void print(byte id, const char *buf);
void print(const char *buf);
void printf(byte id, const char *format, ...);
void printf(const char *format, ...);
void cleanupClients();
void info();
protected:
WiFiServer server = WiFiServer(23);
WiFiClient clients[MAX_TLN_CLIENTS];
void emptyClientStream(WiFiClient client);
void on_connect(const char* str, byte clientId);
void on_input(const char* str, byte clientId);
private:
bool _isIPSet(IPAddress ip);
void handleSerial();
};
extern Telnet telnet;
#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 */
#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(){
void DspCore::_clockSeconds(){
setTextSize(1);
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 - 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(":");
setFont();
yield();
}
#define VTOP TITLE_TOP1+6
void DspCore::drawVolumeBar(bool withNumber) {
int16_t vTop = sheight - TFT_FRAMEWDT - 2;
int16_t vWidth = swidth - TFT_FRAMEWDT * 2;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth);
fillRect(TFT_FRAMEWDT, vTop, vWidth, 2, config.theme.background);
fillRect(TFT_FRAMEWDT, vTop, ww, 2, config.theme.volbarout);
if (withNumber) {
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI28pt7b);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, VTOP + hv);
print(volstr);
setFont();
}
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI28pt7b);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, VTOP + hv);
print(numstr);
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
}
void DspCore::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
}
_clockSeconds();
}
void DspCore::rssi(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
setTextSize(1);
rightText(str, vTop, config.theme.rssi, config.theme.background);
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
setTextSize(1);
setTextColor(config.theme.ip, config.theme.background);
setCursor(4, vTop);
print(str);
void DspCore::clearClock(){
dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -335,45 +128,17 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
/*
void DspCore::sendCommand(uint8_t commandByte, uint8_t *dataBytes, uint8_t numDataBytes) {
TAKE_MUTEX();
Adafruit_GC9106Ex::sendCommand(commandByte, dataBytes, numDataBytes);
GIVE_MUTEX();
void DspCore::loop(bool force) { }
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::sendCommand(uint8_t commandByte, const uint8_t *dataBytes, uint8_t numDataBytes) {
TAKE_MUTEX();
Adafruit_GC9106Ex::sendCommand(commandByte, dataBytes, numDataBytes);
GIVE_MUTEX();
void DspCore::setTextSize(uint8_t s){
Adafruit_GFX::setTextSize(s);
}
void DspCore::sendCommand16(uint16_t commandWord, const uint8_t *dataBytes, uint8_t numDataBytes) {
TAKE_MUTEX();
Adafruit_GC9106Ex::sendCommand16(commandWord, dataBytes, numDataBytes);
GIVE_MUTEX();
}*/
void DspCore::set_TextSize(uint8_t s) {
setTextSize(s);
}
void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
}
void DspCore::printText(const char* txt) {
print(txt);
}
void DspCore::loop(bool force) {
}
void DspCore::flip(){
setRotation(config.store.flipscreen?1:3);
}
@@ -385,4 +150,33 @@ void DspCore::invert(){
void DspCore::sleep(void) { sendCommand(GC9106_SLPIN); delay(150); sendCommand(GC9106_DISPOFF); delay(150); }
void DspCore::wake(void) { sendCommand(GC9106_DISPON); delay(150); sendCommand(GC9106_SLPOUT); delay(150); }
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_GC9106Ex::writePixel(x, y, color);
}
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_GC9106Ex::writeFillRect(x, y, w, h, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setFont(&DS_DIGI28pt7b);
setTextSize(1);
}
#endif

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);
if (withNumber) {
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;
}
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 */
}
void DspCore::drawNextStationNum(uint16_t num) {
void DspCore::_clockTime(){
if(_oldtimeleft>0) fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
_timeleft = width()-clockRightSpace-CHARWIDTH*2*2-24-_timewidth;
setTextSize(1);
setTextColor(config.theme.digit, config.theme.background);
setFont(&DS_DIGI28pt7b);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(numstr);
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
drawFastVLine(width()-clockRightSpace-CHARWIDTH*2*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/
drawFastHLine(width()-clockRightSpace-CHARWIDTH*2*2-18, clockTop-clockTimeHeight+21, 32, config.theme.div); /*divider hor*/
sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900);
strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf));
_datewidth = strlen(_dateBuf) * CHARWIDTH;
_dateleft = width() - 8 - clockRightSpace - _datewidth;
}
void DspCore::frameTitle(const char* str) {
setTextSize(META_SIZE);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
TAKE_MUTEX();
drawLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, TITLE_TOP1-8, config.theme.div);
GIVE_MUTEX();
}
void DspCore::rssi(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
char buf[20];
sprintf(buf, "RSSI:%s", str);
setTextSize(1);
rightText(buf, vTop, config.theme.rssi, config.theme.background);
}
void DspCore::ip(const char* str) {
if(!started){
started = true;
clear(config.theme.background);
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate();
}
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
char buf[30];
sprintf(buf, "IP: %s", str);
setTextSize(1);
setTextColor(config.theme.ip, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
print(buf);
_clockSeconds();
}
void DspCore::set_TextSize(uint8_t s) {
setTextSize(s);
void DspCore::clearClock(){
fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
void DspCore::startWrite(void) {
//TAKE_MUTEX();
TFT_22_ILI9225::startWrite();
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
void DspCore::endWrite(void) {
TFT_22_ILI9225::endWrite();
//GIVE_MUTEX();
}
void DspCore::printText(const char* txt) {
print(txt);
void DspCore::loop(bool force) {
//delay(5);
}
void DspCore::loop(bool force) {
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::drawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_t w, int16_t h) {
@@ -458,14 +241,46 @@ void DspCore::drawRGBBitmap(int16_t x, int16_t y, const uint16_t *bitmap, int16_
drawBitmap(x, y, bitmap, w, h);
GIVE_MUTEX();
}
void DspCore::flip(){
TAKE_MUTEX();
setOrientation(config.store.flipscreen?3:1);
GIVE_MUTEX();
}
void DspCore::invert(){
TAKE_MUTEX();
invertDisplay(config.store.invertdisplay);
GIVE_MUTEX();
}
void DspCore::sleep(void) { setDisplay(false); }
void DspCore::wake(void) { setDisplay(true); }
void DspCore::sleep(void) { TAKE_MUTEX(); setDisplay(false); GIVE_MUTEX(); }
void DspCore::wake(void) { TAKE_MUTEX(); setDisplay(true); GIVE_MUTEX(); }
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) { }
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { }
uint16_t DspCore::drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) {
return cfont.width;
}
}
return TFT_22_ILI9225::drawChar(x, y, ch, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setTextSize(1);
setFont(&DS_DIGI28pt7b);
}
#endif

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);
}
if (withNumber) {
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI42pt7b);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
/*setTextColor(config.theme.background);
sprintf(volstr, "%d", oldVolume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(volstr);*/
sprintf(volstr, "%d", oldVolume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect((swidth - wv) / 2 - 12, (sheight-hv)/2, wv+24, hv+4, config.theme.background);
setTextColor(config.theme.vol);
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
//fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(volstr);
oldVolume=config.store.volume;
setFont();
}
void DspCore::_clockDate(){
if(_olddateleft>0)
dsp.fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background);
setTextColor(config.theme.date, config.theme.background);
setCursor(_dateleft, clockTop+10);
print(_dateBuf); /* print date */
strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf));
_olddatewidth = _datewidth;
_olddateleft = _dateleft;
setTextSize(3);
setTextColor(config.theme.dow, config.theme.background);
setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-CHARHEIGHT*3+4);
print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */
}
void DspCore::drawNextStationNum(uint16_t num) {
void DspCore::_clockTime(){
if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
_timeleft = width()-clockRightSpace-CHARWIDTH*3*2-24-_timewidth;
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI42pt7b);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(numstr);
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
drawFastVLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/
drawFastHLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight+29, 44, config.theme.div); /*divider hor*/
sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900);
strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf));
_datewidth = strlen(_dateBuf) * CHARWIDTH;
_dateleft = width() - 8 - clockRightSpace - _datewidth;
}
void DspCore::frameTitle(const char* str) {
setTextSize(META_SIZE);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, config.theme.div);
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate();
}
_clockSeconds();
}
void DspCore::rssi(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
char buf[20];
sprintf(buf, "RSSI:%s", str);
setTextSize(1);
rightText(buf, vTop, config.theme.rssi, config.theme.background);
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
char buf[30];
sprintf(buf, "IP: %s", str);
setTextSize(1);
setTextColor(config.theme.ip, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
print(buf);
void DspCore::clearClock(){
dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -365,25 +142,17 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
void DspCore::set_TextSize(uint8_t s) {
setTextSize(s);
void DspCore::loop(bool force) { }
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
void DspCore::setTextSize(uint8_t s){
Adafruit_GFX::setTextSize(s);
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
}
void DspCore::printText(const char* txt) {
print(txt);
}
void DspCore::loop(bool force) {
}
void DspCore::flip(){
setRotation(config.store.flipscreen?1:3);
}
@@ -395,4 +164,31 @@ void DspCore::invert(){
void DspCore::sleep(void) { sendCommand(ILI9341_SLPIN); delay(150); sendCommand(ILI9341_DISPOFF); delay(150);}
void DspCore::wake(void) { sendCommand(ILI9341_DISPON); delay(150); sendCommand(ILI9341_SLPOUT); delay(150);}
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_ILI9341::writePixel(x, y, color);
}
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_ILI9341::writeFillRect(x, y, w, h, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setFont(&DS_DIGI42pt7b);
setTextSize(1);
}
#endif

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,227 +1,153 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_1602 || DSP_MODEL==DSP_2004 || DSP_MODEL==DSP_2004I2C
#include "displayLC1602.h"
#include "../../player.h"
#include "../../config.h"
#include "../../network.h"
#include "../core/player.h"
#include "../core/config.h"
#include "../core/network.h"
#ifndef SCREEN_ADDRESS
#define SCREEN_ADDRESS 0x27 ///< See datasheet for Address or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#endif
#ifdef LCD_2004
const byte controlspaces[] = { CLOCK_SPACE, 0, 0, VOL_SPACE };
#else
const byte controlspaces[] = { CLOCK_SPACE, VOL_SPACE };
#define SCREEN_ADDRESS 0x27 ///< See datasheet for Address or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#endif
DspCore::DspCore(): DSP_INIT {}
#include "tools/utf8RusLCD.h"
void DspCore::apScreen() {
clear();
setCursor(0,0);
print("YORADIO AP MODE");
print(utf8Rus(const_lcdApMode, false));
setCursor(0,1);
print(WiFi.softAPIP().toString().c_str());
#ifdef LCD_2004
setCursor(0, 2);
print("AP NAME: ");
print(utf8Rus(const_lcdApName, false));
print(apSsid);
setCursor(0, 3);
print("PASSWORD: ");
print(utf8Rus(const_lcdApPass, false));
print(apPassword);
#endif
}
void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
byte Arrow[] PROGMEM = { B00000, B00100, B00010, B01001, B00010, B00100, B00000, B00000 };
void DspCore::initDisplay() {
#ifdef LCD_I2C
init();
backlight();
#else
#ifdef LCD_2004
begin(20, 4);
#else
begin(16, 2);
#ifdef LCD_2004
begin(20, 4);
#else
begin(16, 2);
#endif
#endif
#endif
#ifdef LCD_2004
screenwidth = 20;
screenheight = 4;
#else
screenwidth = 16;
screenheight = 2;
#endif
swidth = screenwidth;
sheight = screenheight;
fillSpaces = true;
clearClipping();
createChar(0, Arrow);
}
void DspCore::drawLogo() {
void DspCore::drawLogo(uint16_t top) { }
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
centerText("NEXT STATION", 0, 0, 0);
clear();
for (byte i = 0; i < PLMITEMS; i++) {
plMenu[i][0] = '\0';
}
#ifdef LCD_2004
config.fillPlMenu(plMenu, currentItem-1, PLMITEMS);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 1) {
strlcpy(currentItemText, ">", 2);
//strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
strlcat(currentItemText, plMenu[i], PLMITEMLENGHT - 2);
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
char tmp[swidth+1] = {0};
strlcpy(tmp, utf8Rus(plMenu[i], true), swidth);
clearScroll(1 + i, 0, 0);
setCursor(1, 1 + i);
char tmp[width()] = {0};
strlcpy(tmp, utf8Rus(plMenu[i], true), width());
setCursor(1, i);
print(tmp);
}
}
#else
config.fillPlMenu(plMenu, currentItem, PLMITEMS);
for (byte i = 0; i < PLMITEMS; i++) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
}
#endif
setCursor(0,1);
write(byte(0));
}
void DspCore::clearDsp() {
void DspCore::clearDsp(bool black) {
clear();
}
void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
}
void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
tWidth = strlen(text);
tHeight = 1;
sWidth = strlen(separator);
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
for(uint16_t x=0; 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;
void DspCore::_getTimeBounds() { }
void DspCore::_clockSeconds(){
setCursor(_timeleft+_dotsLeft, clockTop);
print((network.timeinfo.tm_sec % 2 == 0)?":":" ");
}
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));
}
nextX=0;
yOffset = y;
setCursor(x, y);
_clockSeconds();
}
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::clearClock() { }
void DspCore::loop(bool force) {
// delay(100);
}
void DspCore::loop(bool force) {
if (checkdelay(SCROLLTIME, loopdelay)) {
//display();
}
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = 1;
height = 1;
}
boolean DspCore::checkdelay(int m, unsigned long & tstamp) {
if (millis() - tstamp > m) {
tstamp = millis();
return true;
} else {
return false;
}
}
void DspCore::flip(){ }
void DspCore::invert(){ }
void DspCore::sleep(void) {
noDisplay();
@@ -236,111 +162,20 @@ void DspCore::wake(void) {
#endif
}
char* DspCore::utf8Rus(const char* str, bool uppercase) {
int index = 0;
static char strn[BUFLEN];
static char newStr[BUFLEN];
bool E = false;
strlcpy(strn, str, BUFLEN);
newStr[0] = '\0';
bool next = false;
for (char *iter = strn; *iter != '\0'; ++iter)
{
if (E) {
E = false;
continue;
}
byte rus = (byte) * iter;
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (rus == 209 && (byte) * (iter + 1) == 145) {
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (next) {
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
next = false;
}
if (rus == 208) next = true;
if (rus == 209) {
*iter = (char)208;
next = true;
}
*iter = toupper(*iter);
}
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) { }
while (strn[index])
{
if (strlen(newStr) > BUFLEN - 2) break;
if (strn[index] >= 0xBF)
{
switch (strn[index]) {
case 0xD0: {
switch (strn[index + 1])
{
case 0x90: strcat(newStr, "A"); break;
case 0x91: strcat(newStr, "B"); break;
case 0x92: strcat(newStr, "V"); break;
case 0x93: strcat(newStr, "G"); break;
case 0x94: strcat(newStr, "D"); break;
case 0x95: strcat(newStr, "E"); break;
case 0x96: strcat(newStr, "ZH"); break;
case 0x97: strcat(newStr, "Z"); break;
case 0x98: strcat(newStr, "I"); break;
case 0x99: strcat(newStr, "Y"); break;
case 0x9A: strcat(newStr, "K"); break;
case 0x9B: strcat(newStr, "L"); break;
case 0x9C: strcat(newStr, "M"); break;
case 0x9D: strcat(newStr, "N"); break;
case 0x9E: strcat(newStr, "O"); break;
case 0x9F: strcat(newStr, "P"); break;
case 0xA0: strcat(newStr, "R"); break;
case 0xA1: strcat(newStr, "S"); break;
case 0xA2: strcat(newStr, "T"); break;
case 0xA3: strcat(newStr, "U"); break;
case 0xA4: strcat(newStr, "F"); break;
case 0xA5: strcat(newStr, "H"); break;
case 0xA6: strcat(newStr, "TS"); break;
case 0xA7: strcat(newStr, "CH"); break;
case 0xA8: strcat(newStr, "SH"); break;
case 0xA9: strcat(newStr, "SHCH"); break;
case 0xAA: strcat(newStr, "'"); break;
case 0xAB: strcat(newStr, "YU"); break;
case 0xAC: strcat(newStr, "'"); break;
case 0xAD: strcat(newStr, "E"); break;
case 0xAE: strcat(newStr, "YU"); break;
case 0xAF: strcat(newStr, "YA"); break;
}
break;
}
case 0xD1: {
if (strn[index + 1] == 0x91) {
strcat(newStr, "YO"); break;
break;
}
break;
}
}
int sind = index + 2;
while (strn[sind]) {
strn[sind - 1] = strn[sind];
sind++;
}
strn[sind - 1] = 0;
} else {
char Temp[2] = {(char) strn[index] , 0 } ;
strcat(newStr, Temp);
}
index++;
}
return newStr;
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) { }
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
setClipping({0, 0, width(), height()});
}
void DspCore::setNumFont(){ }
#endif

View File

@@ -1,121 +1,76 @@
#ifndef displayLC1602_h
#define displayLC1602_h
#include "../core/options.h"
#include "Arduino.h"
#include "tools/l10n.h"
#define DSP_FLIPPED 0
#define DSP_NOT_FLIPPED
#define DSP_LCD
#define CHARWIDTH 1
#define CHARHEIGHT 1
#include "widgets/widgets.h"
#include "widgets/pages.h"
#if DSP_MODEL==DSP_2004 || DSP_MODEL==DSP_2004I2C
#define LCD_2004
#define LCD_2004
#endif
#if DSP_MODEL==DSP_1602I2C || DSP_MODEL==DSP_2004I2C
#define LCD_I2C
#include "../LiquidCrystalI2C/LiquidCrystalI2CEx.h"
#define LCD_I2C
#include "../LiquidCrystalI2C/LiquidCrystalI2CEx.h"
#else
#include <LiquidCrystal.h>
#include <LiquidCrystal.h>
#endif
#ifdef LCD_I2C
#ifdef LCD_2004
#define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 20, 4, I2C_SDA, I2C_SCL)
#else
#define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 16, 2, I2C_SDA, I2C_SCL)
#endif
#else
#define DSP_INIT LiquidCrystal(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7)
#endif
#ifdef LCD_2004
#define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 20, 4, I2C_SDA, I2C_SCL)
#if __has_include("conf/displayLCD2004conf_custom.h")
#include "conf/displayLCD2004conf_custom.h"
#else
#include "conf/displayLCD2004conf.h"
#endif
#else
#define DSP_INIT LiquidCrystal_I2C(SCREEN_ADDRESS, 16, 2, I2C_SDA, I2C_SCL)
#endif
#else
#define DSP_INIT LiquidCrystal(LCD_RS, LCD_E, LCD_D4, LCD_D5, LCD_D6, LCD_D7)
#if __has_include("conf/displayLCD1602conf_custom.h")
#include "conf/displayLCD1602conf_custom.h"
#else
#include "conf/displayLCD1602conf.h"
#endif
#endif
#define BOOT_PRG_COLOR 0x1
#define BOOT_TXT_COLOR 0x1
#define PINK 0x1
#define TFT_LINEHGHT 1
#define TFT_FRAMEWDT 0
#define DSP_CAN_SLEEP true
#define PLMITEMLENGHT 40
#define PLMITEMHEIGHT 9
#define TITLE_TOP1 1
#ifdef LCD_2004
#define PLMITEMS 3
#define TITLE_TOP2 2
#define PL_TOP 2
#define BOOTSTR_TOP2 1
#define BOOTSTR_TOP1 2
#else
#define PLMITEMS 1
#define TITLE_SIZE2 0
#define IP_INST_VOL
#define PL_TOP 1
#define BOOTSTR_TOP2 0
#define BOOTSTR_TOP1 1
#endif
#define PLCURRENT_SIZE 1
#define SCROLLDELTA 1
#define SCROLLTIME 250
#define STARTTIME_PL 2000
/* not used required */
#define bootLogoTop 0
const char rssiFmt[] PROGMEM = "";
const MoveConfig clockMove PROGMEM = { 0, 0, -1 };
const MoveConfig weatherMove PROGMEM = { 0, 0, -1 };
const MoveConfig weatherMoveVU PROGMEM = { 0, 0, -1 };
const char const_lcdApMode[] PROGMEM = "YORADIO AP MODE";
const char const_lcdApName[] PROGMEM = "AP NAME: ";
const char const_lcdApPass[] PROGMEM = "PASSWORD: ";
#ifdef LCD_I2C
class DspCore: public LiquidCrystal_I2C {
class DspCore: public LiquidCrystal_I2C {
#else
class DspCore: public LiquidCrystal {
class DspCore: public LiquidCrystal {
#endif
public:
bool fillSpaces;
DspCore();
char plMenu[PLMITEMS][PLMITEMLENGHT];
uint16_t clockY;
void initD(uint16_t &screenwidth, uint16_t &screenheight);
void apScreen();
void drawLogo();
void clearDsp();
void centerText(const char* text, byte y, uint16_t fg, uint16_t bg);
void rightText(const char* text, byte y, uint16_t fg, uint16_t bg);
void set_TextSize(uint8_t s);
void set_TextColor(uint16_t fg, uint16_t bg);
void set_Cursor(int16_t x, int16_t y);
void printText(const char* txt);
void printClock(const char* timestr);
void printClock(struct tm timeinfo, bool dots, bool redraw = false);
void displayHeapForDebug();
void drawVolumeBar(bool withNumber);
void drawNextStationNum(uint16_t num);
char* utf8Rus(const char* str, bool uppercase=true);
void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg);
void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg);
void frameTitle(const char* str);
void rssi(const char* str);
void ip(const char* str);
void drawPlaylist(uint16_t currentItem, char* currentItemText);
void loop(bool force=false);
void flip(){};
void invert(){};
void sleep();
void wake();
private:
uint16_t swidth, sheight, xOffset, yOffset;
int16_t nextX;
unsigned long loopdelay;
boolean checkdelay(int m, unsigned long &tstamp);
#include "tools/commongfx.h"
};
extern DspCore dsp;
/*
* TFT COLORS
*/
#define CLOCK_SPACE 6
#ifdef LCD_2004
#define VOL_SPACE 0
#else
#define VOL_SPACE 3
#endif
#define SILVER 0
#define TFT_BG 0
#define TFT_FG CLOCK_SPACE
#define TFT_LOGO VOL_SPACE
#endif

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();
}
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,9 +34,11 @@ extern DspCore dsp;
/*
* TFT COLORS
*/
#define SILVER BLACK
#define TFT_BG WHITE
#define TFT_FG BLACK
#define TFT_LOGO BLACK
#define BOOT_PRG_COLOR BLACK
#define BOOT_TXT_COLOR BLACK
#define SILVER BLACK
#define TFT_BG WHITE
#define TFT_FG BLACK
#define TFT_LOGO BLACK
#endif

View File

@@ -1,25 +1,23 @@
#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
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#endif
#define LOGO_WIDTH 21
#define LOGO_HEIGHT 32
#ifndef I2CFREQ_HZ
#define I2CFREQ_HZ 4000000UL
#define I2CFREQ_HZ 4000000UL
#endif
const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
const unsigned char logo [] PROGMEM=
{
0x06, 0x03, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x8f, 0xc0, 0x1f, 0x8f, 0xc0,
@@ -42,128 +40,42 @@ DspCore::DspCore(): Adafruit_SH1107(64, 128, &I2CSH1106, -1) {
}
#endif
char* DspCore::utf8Rus(const char* str, bool uppercase) {
int index = 0;
static char strn[BUFLEN];
bool E = false;
strlcpy(strn, str, BUFLEN);
if (uppercase) {
bool next = false;
for (char *iter = strn; *iter != '\0'; ++iter)
{
if (E) {
E = false;
continue;
}
byte rus = (byte) * iter;
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (rus == 209 && (byte) * (iter + 1) == 145) {
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (next) {
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
next = false;
}
if (rus == 208) next = true;
if (rus == 209) {
*iter = (char)208;
next = true;
}
*iter = toupper(*iter);
}
}
while (strn[index])
{
if (strn[index] >= 0xBF)
{
switch (strn[index]) {
case 0xD0: {
if (strn[index + 1] == 0x81) {
strn[index] = 0xA8;
break;
}
if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30;
break;
}
case 0xD1: {
if (strn[index + 1] == 0x91) {
//strn[index] = 0xB7;
strn[index] = 0xB8;
break;
}
if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70;
break;
}
}
int sind = index + 2;
while (strn[sind]) {
strn[sind - 1] = strn[sind];
sind++;
}
strn[sind - 1] = 0;
}
index++;
}
return strn;
}
void DspCore::apScreen() {
setTextSize(1);
setTextColor(TFT_FG, TFT_BG);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 2 * TFT_LINEHGHT);
print("AP NAME: ");
print(apSsid);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 3 * TFT_LINEHGHT);
print("PASSWORD: ");
print(apPassword);
setTextColor(SILVER, TFT_BG);
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT * 2);
print("SETTINGS PAGE ON: ");
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT);
print("http://");
print(WiFi.softAPIP().toString().c_str());
print("/");
}
#include "tools/utf8RusGFX.h"
void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
void DspCore::initDisplay() {
I2CSH1106.begin(I2C_SDA, I2C_SCL);
if (!begin(SCREEN_ADDRESS, true)) {
Serial.println(F("SH110X allocation failed"));
for (;;); // Don't proceed, loop forever
}
config.theme.background = TFT_BG;
config.theme.meta = TFT_FG;
config.theme.meta = TFT_BG;
config.theme.clock = TFT_FG;
config.theme.weather = TFT_FG;
config.theme.metabg = TFT_FG;
config.theme.metafill = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
config.theme.ip = TFT_FG;
config.theme.vol = TFT_FG;
config.theme.bitrate = TFT_FG;
config.theme.digit = TFT_FG;
config.theme.buffer = TFT_FG;
config.theme.volbarout = TFT_FG;
config.theme.volbarin = TFT_FG;
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
cp437(true);
fillScreen(TFT_BG);
flip();
invert();
setTextWrap(false);
screenwidth = width();
screenheight = height();
swidth = screenwidth;
sheight = screenheight;
}
void DspCore::drawLogo() {
clearDisplay();
drawBitmap(
(width() - LOGO_WIDTH ) / 2,
8,
logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
display();
void DspCore::drawLogo(uint16_t top) {
drawBitmap((width() - LOGO_WIDTH ) / 2, top, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -172,184 +84,100 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 3, PLMITEMS);
setTextSize(1);
int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) + 1, swidth, PLMITEMHEIGHT, TFT_LOGO);
int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
setTextColor(TFT_FG, TFT_BG);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 3) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
display();
}
void DspCore::clearDsp() {
void DspCore::clearDsp(bool black) {
fillScreen(TFT_BG);
}
void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
if (TFT_FRAMEWDT == 0) return;
fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
uint8_t DspCore::_charWidth(unsigned char c){
return CHARWIDTH*clockTimeHeight;
}
void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
int16_t x1, y1;
uint16_t w, h;
setTextSize(textsize);
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
tWidth = w;
tHeight = h;
getTextBounds(separator, 0, 0, &x1, &y1, &w, &h);
sWidth = w;
uint16_t DspCore::textWidth(const char *txt){
uint16_t w = 0, l=strlen(txt);
for(uint16_t c=0;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,52 +27,20 @@ class DspCore: public Adafruit_SH1106G {
#else
class DspCore: public Adafruit_SH1107 {
#endif
public:
DspCore();
char plMenu[PLMITEMS][PLMITEMLENGHT];
uint16_t clockY;
void initD(uint16_t &screenwidth, uint16_t &screenheight);
void apScreen();
void drawLogo();
void clearDsp();
void centerText(const char* text, byte y, uint16_t fg, uint16_t bg);
void rightText(const char* text, byte y, uint16_t fg, uint16_t bg);
void set_TextSize(uint8_t s);
void set_TextColor(uint16_t fg, uint16_t bg);
void set_Cursor(int16_t x, int16_t y);
void printText(const char* txt);
void printClock(const char* timestr);
void printClock(struct tm timeinfo, bool dots, bool redraw = false);
void displayHeapForDebug();
void drawVolumeBar(bool withNumber);
void drawNextStationNum(uint16_t num);
char* utf8Rus(const char* str, bool uppercase);
void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg);
void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg);
void frameTitle(const char* str);
void rssi(const char* str);
void ip(const char* str);
void drawPlaylist(uint16_t currentItem, char* currentItemText);
void loop(bool force=false);
void flip();
void invert();
void sleep();
void wake();
private:
uint16_t swidth, sheight;
unsigned long loopdelay;
boolean checkdelay(int m, unsigned long &tstamp);
#include "tools/commongfx.h"
};
extern DspCore dsp;
/*
* TFT COLORS
* OLED COLORS
*/
#define SILVER SH110X_WHITE
#define TFT_BG SH110X_BLACK
#define TFT_FG SH110X_WHITE
#define TFT_LOGO SH110X_WHITE
#define BOOT_PRG_COLOR SH110X_WHITE
#define BOOT_TXT_COLOR SH110X_WHITE
#define PINK SH110X_WHITE
#define SILVER SH110X_WHITE
#define TFT_BG SH110X_BLACK
#define TFT_FG SH110X_WHITE
#define TFT_LOGO SH110X_WHITE
#endif

View File

@@ -1,24 +1,22 @@
#include "../../options.h"
#include "../core/options.h"
#if DSP_MODEL==DSP_SSD1305 || DSP_MODEL==DSP_SSD1305I2C
#include "displaySSD1305.h"
#include "../../player.h"
#include "../../config.h"
#include "../../network.h"
#include "../core/player.h"
#include "../core/config.h"
#include "../core/network.h"
#ifndef SCREEN_ADDRESS
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#endif
#define LOGO_WIDTH 21
#define LOGO_HEIGHT 32
#ifndef DEF_SPI_FREQ
#define DEF_SPI_FREQ 8000000UL /* set it to 0 for system default */
#define DEF_SPI_FREQ 8000000UL /* set it to 0 for system default */
#endif
const char *dow[7] = {"вс","пн","вт","ср","чт","пт","сб"};
const unsigned char logo [] PROGMEM=
{
0x06, 0x03, 0x00, 0x0f, 0x07, 0x80, 0x1f, 0x8f, 0xc0, 0x1f, 0x8f, 0xc0,
@@ -42,99 +40,10 @@ DspCore::DspCore(): Adafruit_SSD1305(128, 64, &I2CSSD1305, -1){
}
#endif
char* DspCore::utf8Rus(const char* str, bool uppercase) {
int index = 0;
static char strn[BUFLEN];
bool E = false;
strlcpy(strn, str, BUFLEN);
if (uppercase) {
bool next = false;
for (char *iter = strn; *iter != '\0'; ++iter)
{
if (E) {
E = false;
continue;
}
byte rus = (byte) * iter;
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (rus == 209 && (byte) * (iter + 1) == 145) {
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (next) {
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
next = false;
}
if (rus == 208) next = true;
if (rus == 209) {
*iter = (char)208;
next = true;
}
*iter = toupper(*iter);
}
}
while (strn[index])
{
if (strn[index] >= 0xBF)
{
switch (strn[index]) {
case 0xD0: {
if (strn[index + 1] == 0x81) {
strn[index] = 0xA8;
break;
}
if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30;
break;
}
case 0xD1: {
if (strn[index + 1] == 0x91) {
//strn[index] = 0xB7;
strn[index] = 0xB8;
break;
}
if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70;
break;
}
}
int sind = index + 2;
while (strn[sind]) {
strn[sind - 1] = strn[sind];
sind++;
}
strn[sind - 1] = 0;
}
index++;
}
return strn;
}
void DspCore::apScreen() {
setTextSize(1);
setTextColor(TFT_FG, TFT_BG);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 2 * TFT_LINEHGHT);
print("AP NAME: ");
print(apSsid);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + 3 * TFT_LINEHGHT);
print("PASSWORD: ");
print(apPassword);
setTextColor(SILVER, TFT_BG);
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT * 2);
print("SETTINGS PAGE ON: ");
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT);
print("http://");
print(WiFi.softAPIP().toString().c_str());
print("/");
}
#include "tools/utf8RusGFX.h"
void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
void DspCore::initDisplay() {
#if DSP_MODEL==DSP_SSD1305I2C
I2CSSD1305.begin(I2C_SDA, I2C_SCL);
#endif
@@ -143,28 +52,32 @@ void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
for (;;); // Don't proceed, loop forever
}
config.theme.background = TFT_BG;
config.theme.meta = TFT_FG;
config.theme.meta = TFT_BG;
config.theme.clock = TFT_FG;
config.theme.weather = TFT_FG;
config.theme.metabg = TFT_FG;
config.theme.metafill = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
config.theme.ip = TFT_FG;
config.theme.vol = TFT_FG;
config.theme.bitrate = TFT_FG;
config.theme.digit = TFT_FG;
config.theme.buffer = TFT_FG;
config.theme.volbarout = TFT_FG;
config.theme.volbarin = TFT_FG;
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
cp437(true);
fillScreen(TFT_BG);
flip();
invert();
setTextWrap(false);
screenwidth = width();
screenheight = height();
swidth = screenwidth;
sheight = screenheight;
}
void DspCore::drawLogo() {
clearDisplay();
drawBitmap(
(width() - LOGO_WIDTH ) / 2,
8,
logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
void DspCore::drawLogo(uint16_t top) {
drawBitmap( (width() - LOGO_WIDTH ) / 2, 8, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
display();
}
@@ -174,171 +87,103 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 3, PLMITEMS);
setTextSize(1);
int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) + 2, swidth, PLMITEMHEIGHT-1, TFT_LOGO);
int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
setTextColor(TFT_FG, TFT_BG);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 3) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
}
void DspCore::clearDsp() {
void DspCore::clearDsp(bool black) {
fillScreen(TFT_BG);
}
void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
if (TFT_FRAMEWDT == 0) return;
fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
uint8_t DspCore::_charWidth(unsigned char c){
return CHARWIDTH*clockTimeHeight;
}
void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
int16_t x1, y1;
uint16_t w, h;
setTextSize(textsize);
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
tWidth = w;
tHeight = h;
getTextBounds(separator, 0, 0, &x1, &y1, &w, &h);
sWidth = w;
uint16_t DspCore::textWidth(const char *txt){
uint16_t w = 0, l=strlen(txt);
for(uint16_t c=0;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();
}
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,76 +1,42 @@
#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 SILVER WHITE
#define TFT_BG BLACK
#define TFT_FG WHITE
#define TFT_LOGO WHITE
#define BOOT_PRG_COLOR WHITE
#define BOOT_TXT_COLOR WHITE
#define PINK WHITE
#define SILVER WHITE
#define TFT_BG BLACK
#define TFT_FG WHITE
#define TFT_LOGO WHITE
#endif

View File

@@ -1,14 +1,14 @@
#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
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32 or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#endif
#if DSP_MODEL==DSP_SSD1306
@@ -30,144 +30,53 @@ const unsigned char logo [] PROGMEM=
#endif
#ifndef I2CFREQ_HZ
#define I2CFREQ_HZ 4000000UL
#define I2CFREQ_HZ 4000000UL
#endif
TwoWire I2CSSD1306 = TwoWire(0);
DspCore::DspCore(): Adafruit_SSD1306(128, ((DSP_MODEL==DSP_SSD1306)?64:32), &I2CSSD1306, I2C_RST, I2CFREQ_HZ) {
DspCore::DspCore(): Adafruit_SSD1306(128, ((DSP_MODEL==DSP_SSD1306)?64:32), &I2CSSD1306, I2C_RST, I2CFREQ_HZ) { }
}
#include "tools/utf8RusGFX.h"
char* DspCore::utf8Rus(const char* str, bool uppercase) {
int index = 0;
static char strn[BUFLEN];
bool E = false;
strlcpy(strn, str, BUFLEN);
if (uppercase) {
bool next = false;
for (char *iter = strn; *iter != '\0'; ++iter)
{
if (E) {
E = false;
continue;
}
byte rus = (byte) * iter;
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (rus == 209 && (byte) * (iter + 1) == 145) {
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (next) {
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
next = false;
}
if (rus == 208) next = true;
if (rus == 209) {
*iter = (char)208;
next = true;
}
*iter = toupper(*iter);
}
}
while (strn[index])
{
if (strn[index] >= 0xBF)
{
switch (strn[index]) {
case 0xD0: {
if (strn[index + 1] == 0x81) {
strn[index] = 0xA8;
break;
}
if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30;
break;
}
case 0xD1: {
if (strn[index + 1] == 0x91) {
//strn[index] = 0xB7;
strn[index] = 0xB8;
break;
}
if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70;
break;
}
}
int sind = index + 2;
while (strn[sind]) {
strn[sind - 1] = strn[sind];
sind++;
}
strn[sind - 1] = 0;
}
index++;
}
return strn;
}
void DspCore::apScreen() {
setTextSize(1);
setTextColor(TFT_FG, TFT_BG);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + ((DSP_MODEL==DSP_SSD1306)?2:1) * TFT_LINEHGHT);
print("AP NAME: ");
print(apSsid);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + ((DSP_MODEL==DSP_SSD1306)?3:2) * TFT_LINEHGHT);
print("PASSWORD: ");
print(apPassword);
setTextColor(SILVER, TFT_BG);
#if DSP_MODEL==DSP_SSD1306
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT * 2);
print("SETTINGS PAGE ON: ");
#endif
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT);
print("http://");
print(WiFi.softAPIP().toString().c_str());
print("/");
}
void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
void DspCore::initDisplay() {
I2CSSD1306.begin(I2C_SDA, I2C_SCL);
if (!begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for (;;); // Don't proceed, loop forever
}
maxcontrast = DSP_MODEL==DSP_SSD1306?0xCF:0x8F;
config.theme.background = TFT_BG;
config.theme.meta = TFT_FG;
config.theme.meta = TFT_BG;
config.theme.clock = TFT_FG;
config.theme.weather = TFT_FG;
config.theme.metabg = TFT_FG;
config.theme.metafill = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.title2 = TFT_FG;
config.theme.rssi = TFT_FG;
config.theme.ip = TFT_FG;
config.theme.vol = TFT_FG;
config.theme.bitrate = TFT_FG;
config.theme.digit = TFT_FG;
config.theme.buffer = TFT_FG;
config.theme.volbarout = TFT_FG;
config.theme.volbarin = TFT_FG;
for(byte i=0;i<5;i++) config.theme.playlist[i] = TFT_FG;
cp437(true);
fillScreen(TFT_BG);
flip();
invert();
setTextWrap(false);
screenwidth = width();
screenheight = height();
swidth = screenwidth;
sheight = screenheight;
fillSpaces = true;
}
void DspCore::drawLogo() {
clearDisplay();
void DspCore::drawLogo(uint16_t top) {
#if DSP_MODEL==DSP_SSD1306
drawBitmap(
(width() - LOGO_WIDTH ) / 2,
8,
logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
drawBitmap((width() - LOGO_WIDTH ) / 2, 8, logo, LOGO_WIDTH, LOGO_HEIGHT, 1);
#else
setTextSize(2);
centerText(utf8Rus("ёRadio", false), 0, TFT_FG, TFT_BG);
setTextSize(1);
setCursor((width() - 6*CHARWIDTH) / 2, 0);
setTextColor(TFT_FG, TFT_BG);
print(utf8Rus("ёRadio", false));
setTextSize(1);
#endif
display();
@@ -179,187 +88,114 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 2, PLMITEMS);
setTextSize((DSP_MODEL==DSP_SSD1306)?2:1);
int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) + 1, swidth, PLMITEMHEIGHT, TFT_LOGO);
int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
setTextColor(TFT_FG, TFT_BG);
for (byte i = 0; i < PLMITEMS; i++) {
if (i == 2) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
fillRect(0, yStart + i * PLMITEMHEIGHT, swidth, PLMITEMHEIGHT - 1, TFT_BG);
fillRect(0, yStart + i * PLMITEMHEIGHT, width(), PLMITEMHEIGHT - 1, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
}
void DspCore::clearDsp() {
void DspCore::clearDsp(bool black) {
fillScreen(TFT_BG);
}
void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
if (TFT_FRAMEWDT == 0) return;
fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
uint8_t DspCore::_charWidth(unsigned char c){
return CHARWIDTH*clockTimeHeight;
}
void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
int16_t x1, y1;
uint16_t w, h;
setTextSize(textsize);
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
tWidth = w;
tHeight = h;
getTextBounds(separator, 0, 0, &x1, &y1, &w, &h);
sWidth = w;
uint16_t DspCore::textWidth(const char *txt){
uint16_t w = 0, l=strlen(txt);
for(uint16_t c=0;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);
setTextSize(1);
rightText(buf, vTop, SILVER, TFT_BG);
strftime(buf, 4, "%H", &network.timeinfo);
_dotsLeft=textWidth(buf);
}
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::_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);
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::set_TextSize(uint8_t s) {
setTextSize(s);
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_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
}
_clockSeconds();
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
void DspCore::clearClock(){
dsp.fillRect(_timeleft, clockTop, _timewidth, clockTimeHeight*CHARHEIGHT, config.theme.background);
}
void DspCore::printText(const char* txt) {
print(txt);
}
void DspCore::startWrite(void) { }
void DspCore::endWrite(void) { }
void DspCore::loop(bool force) {
if (checkdelay(SCROLLTIME, loopdelay) || force) {
#if DSP_MODEL==DSP_SSD1306x32
if(fillSpaces) printClock(insideClc);
#endif
display();
}
yield();
display();
delay(5);
}
boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
if (millis() - tstamp > m) {
tstamp = millis();
return true;
} else {
return false;
}
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::setTextSize(uint8_t s){
Adafruit_GFX::setTextSize(s);
}
void DspCore::flip(){
setRotation(config.store.flipscreen?2:0);
}
@@ -371,4 +207,31 @@ void DspCore::invert(){
void DspCore::sleep(void) { ssd1306_command(SSD1306_DISPLAYOFF); }
void DspCore::wake(void) { ssd1306_command(SSD1306_DISPLAYON); }
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_SSD1306::writePixel(x, y, color);
}
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_SSD1306::writeFillRect(x, y, w, h, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setTextSize(2);
}
#endif

View File

@@ -1,98 +1,47 @@
#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 SILVER WHITE
#define TFT_BG BLACK
#define TFT_FG WHITE
#define TFT_LOGO WHITE
#define BOOT_PRG_COLOR WHITE
#define BOOT_TXT_COLOR WHITE
#define PINK WHITE
#define SILVER WHITE
#define TFT_BG BLACK
#define TFT_FG WHITE
#define TFT_LOGO WHITE
#endif

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
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; or scan it https://create.arduino.cc/projecthub/abdularbi17/how-to-scan-i2c-address-in-arduino-eaadda
#endif
#ifndef I2CFREQ_HZ
#define I2CFREQ_HZ 6000000UL
#define I2CFREQ_HZ 6000000UL
#endif
TwoWire tw = TwoWire(0);
DspCore::DspCore(): Adafruit_SSD1327(128, 128, &tw, I2C_RST, I2CFREQ_HZ) {
DspCore::DspCore(): Adafruit_SSD1327(128, 128, &tw, I2C_RST/*, I2CFREQ_HZ*/) {}
}
#include "tools/utf8RusGFX.h"
char* DspCore::utf8Rus(const char* str, bool uppercase) {
int index = 0;
static char strn[BUFLEN];
bool E = false;
strlcpy(strn, str, BUFLEN);
if (uppercase) {
bool next = false;
for (char *iter = strn; *iter != '\0'; ++iter)
{
if (E) {
E = false;
continue;
}
byte rus = (byte) * iter;
if (rus == 208 && (byte) * (iter + 1) == 129) { // ёКостыли
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (rus == 209 && (byte) * (iter + 1) == 145) {
*iter = (char)209;
*(iter + 1) = (char)145;
E = true;
continue;
}
if (next) {
if (rus >= 128 && rus <= 143) *iter = (char)(rus + 32);
if (rus >= 176 && rus <= 191) *iter = (char)(rus - 32);
next = false;
}
if (rus == 208) next = true;
if (rus == 209) {
*iter = (char)208;
next = true;
}
*iter = toupper(*iter);
}
}
while (strn[index])
{
if (strn[index] >= 0xBF)
{
switch (strn[index]) {
case 0xD0: {
if (strn[index + 1] == 0x81) {
strn[index] = 0xA8;
break;
}
if (strn[index + 1] >= 0x90 && strn[index + 1] <= 0xBF) strn[index] = strn[index + 1] + 0x30;
break;
}
case 0xD1: {
if (strn[index + 1] == 0x91) {
//strn[index] = 0xB7;
strn[index] = 0xB8;
break;
}
if (strn[index + 1] >= 0x80 && strn[index + 1] <= 0x8F) strn[index] = strn[index + 1] + 0x70;
break;
}
}
int sind = index + 2;
while (strn[sind]) {
strn[sind - 1] = strn[sind];
sind++;
}
strn[sind - 1] = 0;
}
index++;
}
return strn;
}
void DspCore::apScreen() {
setTextSize(1);
setTextColor(TFT_FG, TFT_BG);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + ((DSP_MODEL==DSP_SSD1306)?2:1) * TFT_LINEHGHT);
print("AP NAME: ");
print(apSsid);
setCursor(TFT_FRAMEWDT, TFT_FRAMEWDT + ((DSP_MODEL==DSP_SSD1306)?3:2) * TFT_LINEHGHT);
print("PASSWORD: ");
print(apPassword);
setTextColor(SILVER, TFT_BG);
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT * 2);
print("SETTINGS PAGE ON: ");
setCursor(TFT_FRAMEWDT, sheight - TFT_LINEHGHT);
print("http://");
print(WiFi.softAPIP().toString().c_str());
print("/");
}
#define CLR_ITEM1 0xA
#define CLR_ITEM2 0x8
#define CLR_ITEM3 0x5
void DspCore::initD(uint16_t &screenwidth, uint16_t &screenheight) {
void DspCore::initDisplay() {
tw.begin(I2C_SDA, I2C_SCL);
if (!begin(SCREEN_ADDRESS)) {
Serial.println(F("SSD1327 allocation failed"));
for (;;);
}
config.theme.background = TFT_BG;
config.theme.meta = TFT_FG;
config.theme.title1 = TFT_FG;
config.theme.meta = TFT_BG;
config.theme.metabg = TFT_LOGO;
config.theme.metafill = TFT_LOGO;
config.theme.title1 = TFT_LOGO;
config.theme.title2 = SILVER;
config.theme.clock = TFT_LOGO;
config.theme.rssi = TFT_FG;
config.theme.weather = ORANGE;
config.theme.heap = SILVER;
config.theme.ip = SILVER;
config.theme.vol = SILVER;
config.theme.bitrate = TFT_LOGO;
config.theme.digit = TFT_LOGO;
config.theme.buffer = TFT_FG;
config.theme.volbarout = TFT_FG;
config.theme.volbarin = SILVER;
config.theme.playlist[0] = CLR_ITEM1;
config.theme.playlist[1] = CLR_ITEM2;
config.theme.playlist[2] = CLR_ITEM3;
config.theme.playlist[3] = CLR_ITEM3;
config.theme.playlist[4] = CLR_ITEM3;
cp437(true);
fillScreen(TFT_BG);
flip();
invert();
setTextWrap(false);
screenwidth = width();
screenheight = height();
swidth = screenwidth;
sheight = screenheight;
setClockBounds();
}
void DspCore::drawLogo() {
drawGrayscaleBitmap((swidth - 99) / 2, 18, bootlogobw, 99, 64);
void DspCore::drawLogo(uint16_t top) {
drawRGBBitmap((DSP_WIDTH - 62) / 2, top, bootlogo40, 62, 40);
}
void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
@@ -158,8 +68,7 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
}
config.fillPlMenu(plMenu, currentItem - 3, PLMITEMS);
setTextSize(2);
int yStart = (sheight / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
fillRect(0, (sheight / 2 - PLMITEMHEIGHT / 2) - 1, swidth, PLMITEMHEIGHT + 2, TFT_LOGO);
int yStart = (height() / 2 - PLMITEMHEIGHT / 2) - PLMITEMHEIGHT * (PLMITEMS - 1) / 2 + 3;
for (byte i = 0; i < PLMITEMS; i++) {
if (abs(i - 3) == 3) setTextColor(CLR_ITEM3, TFT_BG);
if (abs(i - 3) == 2) setTextColor(CLR_ITEM2, TFT_BG);
@@ -168,252 +77,97 @@ void DspCore::drawPlaylist(uint16_t currentItem, char* currentItemText) {
strlcpy(currentItemText, plMenu[i], PLMITEMLENGHT - 1);
} else {
setCursor(TFT_FRAMEWDT, yStart + i * PLMITEMHEIGHT);
fillRect(0, yStart + i * PLMITEMHEIGHT - 1, swidth, PLMITEMHEIGHT - 4, TFT_BG);
fillRect(0, yStart + i * PLMITEMHEIGHT - 1, width(), PLMITEMHEIGHT - 4, TFT_BG);
print(utf8Rus(plMenu[i], true));
}
}
//display();
}
void DspCore::clearDsp() {
fillScreen(TFT_BG);
//clearDisplay();
void DspCore::clearDsp(bool black) {
fillScreen(black?0:config.theme.background);
}
void DspCore::drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg) {
if (TFT_FRAMEWDT == 0) return;
fillRect(0, texttop, TFT_FRAMEWDT, textheight, bg);
fillRect(swidth - TFT_FRAMEWDT, texttop, TFT_FRAMEWDT, textheight, bg);
GFXglyph *pgm_read_glyph_ptr(const GFXfont *gfxFont, uint8_t c) {
return gfxFont->glyph + c;
}
void DspCore::getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth) {
int16_t x1, y1;
uint16_t w, h;
setTextSize(textsize);
getTextBounds(text, 0, 0, &x1, &y1, &w, &h);
tWidth = w;
tHeight = h;
getTextBounds(separator, 0, 0, &x1, &y1, &w, &h);
sWidth = w;
uint8_t DspCore::_charWidth(unsigned char c){
GFXglyph *glyph = pgm_read_glyph_ptr(&DS_DIGI28pt7b, c - 0x20);
return pgm_read_byte(&glyph->xAdvance);
}
void DspCore::clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg) {
fillRect(0, texttop, swidth, textheight, bg);
uint16_t DspCore::textWidth(const char *txt){
uint16_t w = 0, l=strlen(txt);
for(uint16_t c=0;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;
void DspCore::_clockDate(){ }
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);
setTextSize(1);
getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot);
setCursor((swidth - wot) / 2 - 4, clockY+28+6);
setTextColor(TFT_BG);
print(oldTimeBuf);
strlcpy(oldTimeBuf, timestr, 20);
setTextColor(TFT_LOGO);
//fillRect(0, clockY, swidth, cheight + 3, TFT_BG);
getTextBounds(timestr, 0, 0, &x, &y, &ncwidth, &ncheight);
setCursor((swidth - ncwidth) / 2 - 4, clockY+28+6);
print(timestr);
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
//display();*/
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
}
byte DspCore::getPw(uint16_t ncwidth){
byte pw = 6;
if(ncwidth<35) pw = 7;
if(ncwidth<20) pw = 8;
return pw;
}
void DspCore::printClock(struct tm timeinfo, bool dots, bool redraw){
char timeBuf[50] = { 0 };
char tmpBuf[4] = { 0 };
uint16_t ncwidth, ncheight;
strftime(timeBuf, sizeof(timeBuf), "%H %M", &timeinfo);
setTextSize(1);
setFont(&DS_DIGI28pt7b);
if(strstr(oldTimeBuf, timeBuf)==NULL || redraw){
getTextBounds(oldTimeBuf, 0, 0, &x, &y, &wot, &hot);
setCursor((swidth - wot) / 2 - 4, clockY+28+6);
setTextColor(TFT_BG);
print(oldTimeBuf);
dot = (swidth - wot) / 2 - 4;
/* dots */
strlcpy(tmpBuf, oldTimeBuf, 3);
getTextBounds(tmpBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
dot = dot + ncwidth + getPw(ncwidth);
setCursor(dot, clockY+28+6);
print(":");
/* dots */
strlcpy(oldTimeBuf, timeBuf, 20);
setTextSize(1);
getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
setTextColor(TFT_LOGO);
setCursor((swidth - ncwidth) / 2 - 4, clockY+28+6);
dot = (swidth - ncwidth) / 2 - 4;
setTextSize(1);
print(timeBuf);
/* dots */
strftime(timeBuf, sizeof(timeBuf), "%H", &timeinfo);
getTextBounds(timeBuf, 0, 0, &x, &y, &ncwidth, &ncheight);
dot = dot + ncwidth + getPw(ncwidth);
/* dots */
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
}
setCursor(dot, clockY+28+6);
setTextColor(dots?TFT_BG:TFT_LOGO);
print(":");
setFont();
display();
//_clockSeconds();
}
void DspCore::drawVolumeBar(bool withNumber) {
//if (withNumber) delay(150); /* buuuut iiiiit's toooooo faaaast 0000__oooo !!111 ommm____nomm____nomm */
int16_t vTop = sheight - TFT_FRAMEWDT * 2;
int16_t vWidth = swidth - TFT_FRAMEWDT - 4;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
fillRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, TFT_BG);
drawRect(TFT_FRAMEWDT, vTop - 2, vWidth, 6, TFT_LOGO);
fillRect(TFT_FRAMEWDT + 1, vTop - 1, ww, 5, TFT_LOGO);
if (withNumber) {
setTextSize(1);
setTextColor(TFT_FG);
setFont(&DS_DIGI28pt7b);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 48, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
//fillRect(24, 48, 80, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 48 + hv);
print(volstr);
setFont();
}
//display();
void DspCore::clearClock(){
dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth, clockTimeHeight, config.theme.background);
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(1);
setTextColor(TFT_FG);
setFont(&DS_DIGI28pt7b);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, 48, swidth - TFT_FRAMEWDT / 2, hv + 3, TFT_BG);
setCursor((swidth - wv) / 2, 48 + hv);
print(numstr);
setFont();
//display();
}
void DspCore::startWrite(void) { }
void DspCore::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, TFT_LOGO, TFT_BG);
//display();
}
void DspCore::rssi(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
setTextSize(1);
rightText(str, vTop, SILVER, TFT_BG);
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
setTextSize(1);
setTextColor(SILVER, TFT_BG);
setCursor(4, vTop);
print(str);
}
void DspCore::set_TextSize(uint8_t s) {
setTextSize(s);
}
void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
}
void DspCore::printText(const char* txt) {
print(txt);
//display();
}
void DspCore::endWrite(void) { }
void DspCore::loop(bool force) {
if (checkdelay(LOOP_DELAY, loopdelay) || force) {
display();
}
yield();
display();
delay(5);
}
boolean DspCore::checkdelay(int m, unsigned long &tstamp) {
if (millis() - tstamp > m) {
tstamp = millis();
return true;
} else {
return false;
}
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::setTextSize(uint8_t s){
Adafruit_GFX::setTextSize(s);
}
void DspCore::flip(){
if(ROTATE_90){
setRotation(config.store.flipscreen?3:1);
@@ -429,4 +183,32 @@ void DspCore::invert(){
void DspCore::sleep(void) { oled_command(SSD1327_DISPLAYOFF); }
void DspCore::wake(void) { oled_command(SSD1327_DISPLAYON); }
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_SSD1327::writePixel(x, y, color);
}
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_SSD1327::writeFillRect(x, y, w, h, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setFont(&DS_DIGI28pt7b);
setTextSize(1);
}
#endif

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(":");
setFont();
yield();
}
#ifdef DSP_MINI
#define VTOP TITLE_TOP1+6
#else
#define VTOP 48
#endif
void DspCore::drawVolumeBar(bool withNumber) {
#ifdef DSP_MINI
int16_t vTop = sheight - TFT_FRAMEWDT - 2;
int16_t vWidth = swidth - TFT_FRAMEWDT * 2;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth);
fillRect(TFT_FRAMEWDT, vTop, vWidth, 2, config.theme.background);
fillRect(TFT_FRAMEWDT, vTop, ww, 2, config.theme.volbarin);
#else
int16_t vTop = sheight - TFT_FRAMEWDT - 6;
int16_t vWidth = swidth - TFT_FRAMEWDT * 2;
uint8_t ww = map(config.store.volume, 0, 254, 0, vWidth - 2);
fillRect(TFT_FRAMEWDT, vTop - 2 + 3, vWidth, 5, config.theme.background);
fillRect(TFT_FRAMEWDT + 1, vTop - 1 + 3, ww, 3, config.theme.volbarin);
drawRect(TFT_FRAMEWDT, vTop - 2 + 3, vWidth, 5, config.theme.volbarout);
#endif
if (withNumber) {
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI28pt7b);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, VTOP + hv);
print(volstr);
setFont();
}
}
void DspCore::drawNextStationNum(uint16_t num) {
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI28pt7b);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, VTOP, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, VTOP + hv);
print(numstr);
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
}
void DspCore::frameTitle(const char* str) {
setTextSize(2);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
}
_clockSeconds();
}
void DspCore::rssi(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
setTextSize(1);
rightText(str, vTop, config.theme.rssi, config.theme.background);
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
setTextSize(1);
setTextColor(config.theme.ip, config.theme.background);
setCursor(4, vTop);
print(str);
void DspCore::clearClock(){
dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth, clockTimeHeight, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -378,24 +136,15 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
void DspCore::set_TextSize(uint8_t s) {
setTextSize(s);
void DspCore::loop(bool force) { }
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
}
void DspCore::printText(const char* txt) {
print(txt);
}
void DspCore::loop(bool force) {
void DspCore::setTextSize(uint8_t s){
Adafruit_GFX::setTextSize(s);
}
void DspCore::flip(){
@@ -417,4 +166,32 @@ void DspCore::wake(void) {
enableDisplay(true); delay(150); enableSleep(false); delay(150);
}
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_ST7735::writePixel(x, y, color);
}
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_ST7735::writeFillRect(x, y, w, h, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setFont(&DS_DIGI28pt7b);
setTextSize(1);
}
#endif

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
#if DTYPE==INITR_MINI160x80
#include "conf/displayST7735_miniconf.h"
#elif DTYPE==INITR_144GREENTAB
#include "conf/displayST7735_144conf.h"
#else
#include "conf/displayST7735_blackconf.h"
#endif
#endif
#define PLMITEMS 7
#define PLMITEMLENGHT 40
#if DTYPE==INITR_MINI160x80
#define PLMITEMHEIGHT 19
#else
#define PLMITEMHEIGHT 21
#endif
#define TITLE_TOP2 TFT_FRAMEWDT + 3 * TFT_LINEHGHT
#if !defined(SCROLLDELTA) || !defined(SCROLLTIME)
#define SCROLLDELTA 2
#define SCROLLTIME 30
#endif
#define TFT_FULLTIME 1
#if DTYPE==INITR_MINI160x80
#define TITLE_SIZE2 0
#define TITLE_TOP1 TFT_FRAMEWDT + 2 * TFT_LINEHGHT-3
#define BOOTSTR_TOP1 50
#define BOOTSTR_TOP2 65
#endif
#define BOOT_PRG_COLOR 0xE68B
#define BOOT_TXT_COLOR 0xFFFF
#define PINK 0xF97F
class DspCore: public Adafruit_ST7735 {
public:
DspCore();
char plMenu[PLMITEMS][PLMITEMLENGHT];
uint16_t clockY;
void initD(uint16_t &screenwidth, uint16_t &screenheight);
void apScreen();
void drawLogo();
void clearDsp();
void centerText(const char* text, byte y, uint16_t fg, uint16_t bg);
void rightText(const char* text, byte y, uint16_t fg, uint16_t bg);
void set_TextSize(uint8_t s);
void set_TextColor(uint16_t fg, uint16_t bg);
void set_Cursor(int16_t x, int16_t y);
void printText(const char* txt);
void printClock(const char* timestr);
void printClock(struct tm timeinfo, bool dots, bool redraw = false);
void displayHeapForDebug();
void drawVolumeBar(bool withNumber);
void drawNextStationNum(uint16_t num);
char* utf8Rus(const char* str, bool uppercase);
void drawScrollFrame(uint16_t texttop, uint16_t textheight, uint16_t bg);
void getScrolBbounds(const char* text, const char* separator, byte textsize, uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth);
void clearScroll(uint16_t texttop, uint16_t textheight, uint16_t bg);
void frameTitle(const char* str);
void rssi(const char* str);
void ip(const char* str);
void drawPlaylist(uint16_t currentItem, char* currentItemText);
void loop(bool force=false);
virtual void startWrite(void);
virtual void endWrite(void);
void flip();
void invert();
void sleep();
void wake();
private:
uint16_t swidth, sheight;
char oldTimeBuf[20];
uint16_t wot, hot, dot;
int16_t x, y;
uint16_t cwidth, cheight;
void setClockBounds();
byte getPw(uint16_t ncwidth);
#include "tools/commongfx.h"
};
extern DspCore dsp;

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 */
#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);
setTextSize(1);
centerText(buf, volTop, config.theme.vol, config.theme.background);
}
if (withNumber) {
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI42pt7b);
char volstr[4];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(volstr, "%d", config.store.volume);
getTextBounds(volstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(volstr);
setFont();
}
yield();
}
void DspCore::drawNextStationNum(uint16_t num) {
setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-clockTimeHeight+1);
sprintf(_bufforseconds, "%02d", network.timeinfo.tm_sec);
print(_bufforseconds); /* print seconds */
setTextSize(1);
setTextColor(config.theme.digit);
setFont(&DS_DIGI42pt7b);
char numstr[7];
uint16_t wv, hv;
int16_t x1, y1;
sprintf(numstr, "%d", num);
getTextBounds(numstr, 0, 0, &x1, &y1, &wv, &hv);
fillRect(TFT_FRAMEWDT, (sheight-hv)/2, swidth - TFT_FRAMEWDT / 2, hv + 3, config.theme.background);
setCursor((swidth - wv) / 2, (sheight-hv)/2 + hv);
print(numstr);
setTextColor((network.timeinfo.tm_sec % 2 == 0) ? config.theme.clock : config.theme.background, config.theme.background);
setCursor(_timeleft+_dotsLeft, clockTop);
print(":"); /* print dots */
setFont();
}
void DspCore::frameTitle(const char* str) {
setTextSize(META_SIZE);
centerText(str, TFT_FRAMEWDT, config.theme.meta, config.theme.background);
drawFastHLine(TFT_FRAMEWDT, TITLE_TOP1-8, swidth-TFT_FRAMEWDT*2, config.theme.div);
void DspCore::_clockDate(){
if(_olddateleft>0)
dsp.fillRect(_olddateleft, clockTop+10, _olddatewidth, CHARHEIGHT, config.theme.background);
setTextColor(config.theme.date, config.theme.background);
setCursor(_dateleft, clockTop+10);
print(_dateBuf); /* print date */
strlcpy(_oldDateBuf, _dateBuf, sizeof(_dateBuf));
_olddatewidth = _datewidth;
_olddateleft = _dateleft;
setTextSize(3);
setTextColor(config.theme.dow, config.theme.background);
setCursor(width() - 8 - clockRightSpace - CHARWIDTH*3*2, clockTop-CHARHEIGHT*3+4);
print(utf8Rus(dow[network.timeinfo.tm_wday], false)); /* print dow */
}
void DspCore::rssi(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
char buf[20];
sprintf(buf, "RSSI:%s", str);
void DspCore::_clockTime(){
if(_oldtimeleft>0) dsp.fillRect(_oldtimeleft, clockTop-clockTimeHeight+1, _oldtimewidth, clockTimeHeight, config.theme.background);
_timeleft = width()-clockRightSpace-CHARWIDTH*3*2-24-_timewidth;
setTextSize(1);
rightText(buf, vTop, config.theme.rssi, config.theme.background);
setFont(&DS_DIGI42pt7b);
setTextColor(config.theme.clock, config.theme.background);
setCursor(_timeleft, clockTop);
print(_timeBuf);
setFont();
strlcpy(_oldTimeBuf, _timeBuf, sizeof(_timeBuf));
_oldtimewidth = _timewidth;
_oldtimeleft = _timeleft;
drawFastVLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight, clockTimeHeight+3, config.theme.div); /*divider vert*/
drawFastHLine(width()-clockRightSpace-CHARWIDTH*3*2-18, clockTop-clockTimeHeight+29, 44, config.theme.div); /*divider hor*/
sprintf(_buffordate, "%2d %s %d", network.timeinfo.tm_mday,mnths[network.timeinfo.tm_mon], network.timeinfo.tm_year+1900);
strlcpy(_dateBuf, utf8Rus(_buffordate, true), sizeof(_dateBuf));
_datewidth = strlen(_dateBuf) * CHARWIDTH;
_dateleft = width() - 8 - clockRightSpace - _datewidth;
}
void DspCore::ip(const char* str) {
int16_t vTop = sheight - TFT_FRAMEWDT * 2 - TFT_LINEHGHT - 2;
char buf[30];
sprintf(buf, "IP: %s", str);
setTextSize(1);
setTextColor(config.theme.ip, config.theme.background);
setCursor(TFT_FRAMEWDT, vTop);
print(buf);
void DspCore::printClock(uint16_t top, uint16_t rightspace, uint16_t timeheight, bool redraw){
clockTop = top;
clockRightSpace = rightspace;
clockTimeHeight = timeheight;
strftime(_timeBuf, sizeof(_timeBuf), "%H:%M", &network.timeinfo);
if(strcmp(_oldTimeBuf, _timeBuf)!=0 || redraw){
_getTimeBounds();
_clockTime();
if(strcmp(_oldDateBuf, _dateBuf)!=0 || redraw) _clockDate();
}
_clockSeconds();
}
void DspCore::clearClock(){
dsp.fillRect(_timeleft, clockTop-clockTimeHeight, _timewidth+CHARWIDTH*3*2+24, clockTimeHeight+10+CHARHEIGHT, config.theme.background);
}
void DspCore::startWrite(void) {
@@ -362,25 +152,19 @@ void DspCore::endWrite(void) {
GIVE_MUTEX();
}
void DspCore::set_TextSize(uint8_t s) {
setTextSize(s);
}
void DspCore::set_TextColor(uint16_t fg, uint16_t bg) {
setTextColor(fg, bg);
}
void DspCore::set_Cursor(int16_t x, int16_t y) {
setCursor(x, y);
}
void DspCore::printText(const char* txt) {
print(txt);
}
void DspCore::loop(bool force) {
}
void DspCore::charSize(uint8_t textsize, uint8_t& width, uint16_t& height){
width = textsize * CHARWIDTH;
height = textsize * CHARHEIGHT;
}
void DspCore::setTextSize(uint8_t s){
Adafruit_GFX::setTextSize(s);
}
void DspCore::flip(){
#if DSP_MODEL==DSP_ST7789
setRotation(config.store.flipscreen?3:1);
@@ -405,4 +189,32 @@ void DspCore::wake(void) {
enableDisplay(true); delay(150); enableSleep(false); delay(150);
}
void DspCore::writePixel(int16_t x, int16_t y, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x > _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_ST7789::writePixel(x, y, color);
}
void DspCore::writeFillRect(int16_t x, int16_t y, int16_t w, int16_t h, uint16_t color) {
if(_clipping){
if ((x < _cliparea.left) || (x >= _cliparea.left+_cliparea.width) || (y < _cliparea.top) || (y > _cliparea.top + _cliparea.height)) return;
}
Adafruit_ST7789::writeFillRect(x, y, w, h, color);
}
void DspCore::setClipping(clipArea ca){
_cliparea = ca;
_clipping = true;
}
void DspCore::clearClipping(){
_clipping = false;
}
void DspCore::setNumFont(){
setFont(&DS_DIGI42pt7b);
setTextSize(1);
}
#endif

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,
@@ -54,7 +54,7 @@ const GFXglyph DS_DIGI15pt7bGlyphs[] PROGMEM = {
};
const GFXfont DS_DIGI15pt7b PROGMEM = {
(uint8_t *)DS_DIGI15pt7bBitmaps,
(GFXglyph *)DS_DIGI15pt7bGlyphs, 0x20, 0x3A, 29 };
(uint8_t *)DS_DIGI15pt7bBitmaps,
(GFXglyph *)DS_DIGI15pt7bGlyphs, 0x20, 0x3A, 29 };
// Approx. 664 bytes

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};
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
#define CORE_STACK_SIZE 1024*3
#endif
HardwareSerial hSerial(1); // use UART1
Ticker weatherticker;
//char weather[254] = { 0 };
//bool weatherRequest = false;
const char *ndow[7] = {"воскресенье","понедельник","вторник","среда","четверг","пятница","суббота"};
const char *nmnths[12] = {"января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"};
#ifdef DUMMYDISPLAY
void ticks() {
network.timeinfo.tm_sec ++;
mktime(&network.timeinfo);
nextion.putRequest({CLOCK,0});
if(nextion.mode==TIMEZONE) nextion.localTime(network.timeinfo);
if(nextion.mode==INFO) nextion.rssi();
if(nextion.dt){
int rssi = WiFi.RSSI();
netserver.setRSSI(rssi);
}
nextion.dt=!nextion.dt;
}
#endif
Nextion::Nextion() {
@@ -44,24 +22,12 @@ Nextion::Nextion() {
void nextionCore0( void * pvParameters ){
delay(500);
while(true){
// if(displayQueue==NULL) break;
nextion.loop();
vTaskDelay(5);
}
vTaskDelete( NULL );
}
void Nextion::createCore0Task(){
xTaskCreatePinnedToCore(
nextionCore0, /* Task function. */
"TaskCore0", /* name of task. */
CORE_STACK_SIZE, /* Stack size of task */
NULL, /* parameter of the task */
4, /* no one flies higher than the Toruk */
&_TaskCore0, /* Task handle to keep track of created task */
!xPortGetCoreID()); /* pin task to core 0 */
}
void Nextion::begin(bool dummy) {
_dummyDisplay=dummy;
hSerial.begin(NEXTION_BAUD, SERIAL_8N1, NEXTION_RX, NEXTION_TX);
@@ -80,9 +46,10 @@ void Nextion::begin(bool dummy) {
putcmd("");
putcmd("bkcmd=0");
// putcmd("page boot");
if(dummy) {
_displayQueue = xQueueCreate( 5, sizeof( requestParams_t ) );
createCore0Task();
if(dummy) {
xTaskCreatePinnedToCore(nextionCore0, "TaskCore0", CORE_STACK_SIZE, NULL, 4, &_TaskCore0, !xPortGetCoreID());
}
}
@@ -92,30 +59,20 @@ void Nextion::start(){
return;
}
#ifdef DUMMYDISPLAY
if(_dummyDisplay) _timer.attach_ms(1000, ticks);
display.mode = PLAYER;
config.setTitle("[READY]");
display.mode(PLAYER);
config.setTitle(const_PlReady);
#endif
mode = PLAYER;
putcmd("page player");
delay(100);
#ifdef DUMMYDISPLAY
newNameset(config.station.name);
newTitle(config.station.title);
#endif
setVol(config.store.volume, mode == VOL);
}
void Nextion::apScreen() {
putcmd("apscreenlock=1");
putcmd("page settings_wifi");
//char cmd[20];
/*for(int i=0;i<5;i++){
snprintf(cmd, sizeof(cmd)-1, "vis b%d,%d", i, 0);
putcmd(cmd);
}*/
//putcmd("vis btnBack,0");
}
void Nextion::putRequest(requestParams_t request){
@@ -128,20 +85,14 @@ void Nextion::processQueue(){
requestParams_t request;
if(xQueueReceive(_displayQueue, &request, 20)){
switch (request.type){
case NEWMODE: {
swichMode((displayMode_e)request.payload);
break;
}
case CLOCK: {
printClock(network.timeinfo);
break;
}
case NEWTITLE: {
newTitle(config.station.title);
break;
}
case RETURNTITLE: {
//returnTile();
case NEWMODE: swichMode((displayMode_e)request.payload); break;
case CLOCK: printClock(network.timeinfo); break;
case DSPRSSI: rssi(); break;
case NEWTITLE: newTitle(config.station.title); break;
case BOOTSTRING: {
char buf[50];
snprintf(buf, 50, bootstrFmt, config.ssids[request.payload].ssid);
bootString(buf);
break;
}
case NEWSTATION: {
@@ -150,18 +101,9 @@ void Nextion::processQueue(){
bitratePic(ICON_NA);
break;
}
case NEXTSTATION: {
drawNextStationNum((displayMode_e)request.payload);
break;
}
case DRAWPLAYLIST: {
int p = request.payload ? display.currentPlItem + 1 : display.currentPlItem - 1;
if (p < 1) p = config.store.countStation;
if (p > config.store.countStation) p = 1;
display.currentPlItem = p;
drawPlaylist(display.currentPlItem);
break;
}
case SHOWWEATHER: weatherVisible(strlen(config.store.weatherkey)>0 && config.store.showweather); break;
case NEXTSTATION: drawNextStationNum(request.payload); break;
case DRAWPLAYLIST: drawPlaylist(request.payload); break;
case DRAWVOL: {
if(!_volInside){
setVol(config.store.volume, mode == VOL);
@@ -169,36 +111,17 @@ void Nextion::processQueue(){
_volInside=false;
break;
}
default: break;
}
}
switch (mode) {
case PLAYER: {
//drawPlayer();
break;
}
case INFO:
case TIMEZONE: {
//sendInfo();
break;
}
case VOL: {
if (millis() - _volDelay > 3000) {
_volDelay = millis();
swichMode(PLAYER);
}
break;
}
case NUMBERS: {
//meta.loop();
break;
}
case STATIONS: {
//plCurrent.loop();
break;
}
default:
break;
#ifdef DUMMYDISPLAY
if(mode==VOL || mode==STATIONS || mode==NUMBERS ){
if (millis() - _volDelay > (mode==VOL?3000:30000)) {
_volDelay = millis();
swichMode(PLAYER);
}
}
#endif
}
void Nextion::loop() {
@@ -230,14 +153,14 @@ void Nextion::loop() {
rxbuf[rx_pos] = '\0';
rx_pos = 0;
if (sscanf(rxbuf, "page=%s", scanBuf) == 1){
if(strcmp(scanBuf, "player") == 0) display.putRequest({NEWMODE, PLAYER});
if(strcmp(scanBuf, "playlist") == 0) display.putRequest({NEWMODE, STATIONS});
if(strcmp(scanBuf, "player") == 0) display.putRequest(NEWMODE, PLAYER);
if(strcmp(scanBuf, "playlist") == 0) display.putRequest(NEWMODE, STATIONS);
if(strcmp(scanBuf, "info") == 0) {
putcmd("yoversion.txt", VERSION);
putcmd("espcore.txt", _espcoreversion);
putcmd("ipaddr.txt", WiFi.localIP().toString().c_str());
putcmd("ssid.txt", WiFi.SSID().c_str());
display.putRequest({NEWMODE, INFO});
display.putRequest(NEWMODE, INFO);
}
if(strcmp(scanBuf, "eq") == 0) {
putcmd("t4.txt", config.store.balance, true);
@@ -248,7 +171,7 @@ void Nextion::loop() {
putcmd("h2.val", config.store.middle+16);
putcmd("t7.txt", config.store.bass, true);
putcmd("h3.val", config.store.bass+16);
display.putRequest({NEWMODE, SETTINGS});
display.putRequest(NEWMODE, SETTINGS);
}
if(strcmp(scanBuf, "wifi") == 0) {
if(mode != WIFI){
@@ -260,7 +183,7 @@ void Nextion::loop() {
snprintf(cell, sizeof(cell) - 1, "t%d.txt", i*2+1);
putcmd(cell, config.ssids[i].password);
}
display.putRequest({NEWMODE, WIFI});
display.putRequest(NEWMODE, WIFI);
}
}
if(strcmp(scanBuf, "time") == 0) {
@@ -268,25 +191,31 @@ void Nextion::loop() {
putcmd("tzHour.val", config.store.tzHour);
putcmdf("tzMinText.txt=\"%02d\"", config.store.tzMin);
putcmd("tzMin.val", config.store.tzMin);
display.putRequest({NEWMODE, TIMEZONE});
display.putRequest(NEWMODE, TIMEZONE);
}
if(strcmp(scanBuf, "sys") == 0) {
putcmd("smartstart.val", config.store.smartstart==2?0:1);
putcmd("audioinfo.val", config.store.audioinfo);
display.putRequest({NEWMODE, SETTINGS});
display.putRequest(NEWMODE, SETTINGS);
}
}
if (sscanf(rxbuf, "ctrls=%s", scanBuf) == 1){
if(strcmp(scanBuf, "up") == 0) {
display.resetQueue();
display.putRequest({DRAWPLAYLIST, false});
int p = display.currentPlItem - 1;
if (p < 1) p = config.store.countStation;
display.currentPlItem = p;
display.putRequest(DRAWPLAYLIST, p);
}
if(strcmp(scanBuf, "dn") == 0) {
display.resetQueue();
display.putRequest({DRAWPLAYLIST, true});
int p = display.currentPlItem + 1;
if (p > config.store.countStation) p = 1;
display.currentPlItem = p;
display.putRequest(DRAWPLAYLIST, p);
}
if(strcmp(scanBuf, "go") == 0) {
display.putRequest({NEWMODE, PLAYER});
display.putRequest(NEWMODE, PLAYER);
player.request.station=display.currentPlItem;
}
if(strcmp(scanBuf, "toggle") == 0) {
@@ -320,23 +249,21 @@ void Nextion::loop() {
}
if (sscanf(rxbuf, "tzhour=%d", &scanDigit) == 1){
config.setTimezone((int8_t)scanDigit, config.store.tzMin);
//configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), SNTP_SERVER);
if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
}else if(strlen(config.store.sntp1)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
}
network.requestTimeSync(true);
network.forceTimeSync = true;
}
if (sscanf(rxbuf, "tzmin=%d", &scanDigit) == 1){
config.setTimezone(config.store.tzHour, (int8_t)scanDigit);
//configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), SNTP_SERVER);
if(strlen(config.store.sntp1)>0 && strlen(config.store.sntp2)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1, config.store.sntp2);
}else if(strlen(config.store.sntp1)>0){
configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), config.store.sntp1);
}
network.requestTimeSync(true);
network.forceTimeSync = true;
}
if (sscanf(rxbuf, "audioinfo=%d", &scanDigit) == 1){
config.store.audioinfo = scanDigit;
@@ -411,7 +338,7 @@ void Nextion::bitrate(int bpm){
if(bpm>0){
putcmd("player.bitrate.txt", bpm, true);
}else{
putcmd("player.bitrate.txt=\"und\"");
putcmd("player.bitrate.txt=\" \"");
}
}
@@ -434,6 +361,13 @@ void Nextion::bitratePic(uint8_t pic){
putcmd("player.bitrate.pic", pic);
}
void Nextion::audioinfo(const char* info){
if (strstr(info, "format is aac") != NULL) bitratePic(ICON_AAC);
if (strstr(info, "format is flac") != NULL) bitratePic(ICON_FLAC);
if (strstr(info, "format is mp3") != NULL) bitratePic(ICON_MP3);
if (strstr(info, "format is wav") != NULL) bitratePic(ICON_WAV);
}
void Nextion::bootString(const char* bs) {
char buf[50] = { 0 };
strlcpy(buf, bs, 50);
@@ -449,10 +383,7 @@ void Nextion::newNameset(const char* meta){
void Nextion::setVol(uint8_t vol, bool dialog){
if(dialog){
putcmd("dialog.text.txt", vol, true);
}/*else{
putcmd("player.volText.txt", vol, true);
putcmd("player.volumeSlider.val", vol);
}*/
}
putcmd("player.volText.txt", vol, true);
putcmd("player.volumeSlider.val", vol);
}
@@ -480,12 +411,14 @@ void Nextion::newTitle(const char* title){
}
void Nextion::printClock(struct tm timeinfo){
char timeStringBuff[70] = { 0 };
char timeStringBuff[100] = { 0 };
strftime(timeStringBuff, sizeof(timeStringBuff), "player.clock.txt=\"%H:%M\"", &timeinfo);
putcmd(timeStringBuff);
putcmdf("player.secText.txt=\"%02d\"", timeinfo.tm_sec);
snprintf(timeStringBuff, sizeof(timeStringBuff), "player.dateText.txt=\"%s, %d %s %d\"", ndow[timeinfo.tm_wday], timeinfo.tm_mday, nmnths[timeinfo.tm_mon], timeinfo.tm_year+1900);
snprintf(timeStringBuff, sizeof(timeStringBuff), "player.dateText.txt=\"%s, %d %s %d\"", dowf[timeinfo.tm_wday], timeinfo.tm_mday, mnths[timeinfo.tm_mon], timeinfo.tm_year+1900);
putcmd(utf8Rus(timeStringBuff, false));
if(mode==TIMEZONE) localTime(network.timeinfo);
if(mode==INFO) rssi();
}
void Nextion::localTime(struct tm timeinfo){
@@ -505,6 +438,7 @@ void Nextion::drawPlaylist(uint16_t currentPlItem){
snprintf(cmd, sizeof(cmd) - 1, "t%d.txt=\"%s\"", i, nextion.utf8Rus(plMenu[i], true));
putcmd(cmd);
}
_volDelay = millis();
}
void Nextion::drawNextStationNum(uint16_t num) {//dialog
@@ -512,10 +446,9 @@ void Nextion::drawNextStationNum(uint16_t num) {//dialog
char currentItemText[40] = {0};
config.fillPlMenu(plMenu, num, 1, true);
strlcpy(currentItemText, plMenu[0], 39);
//meta.setText(dsp.utf8Rus(currentItemText, true));
putcmd("dialog.title.txt", utf8Rus(currentItemText, true));
putcmd("dialog.text.txt", num, true);
//dsp.drawNextStationNum(num);
_volDelay = millis();
}
void Nextion::swichMode(displayMode_e newmode){
@@ -525,12 +458,8 @@ void Nextion::swichMode(displayMode_e newmode){
if (newmode == mode) return;
mode = newmode;
#ifdef DUMMYDISPLAY
display.mode = newmode;
display.mode(newmode);
#endif
/* if (newmode != STATIONS) {
ip();
volume();
}*/
if (newmode == PLAYER) {
putcmd("page player");
putcmd("dialog.title.txt", "");
@@ -560,158 +489,6 @@ void Nextion::swichMode(displayMode_e newmode){
}
}
bool Nextion::getForecast(){
WiFiClient client;
const char* host = "api.openweathermap.org";
if (!client.connect(host, 80)) {
Serial.println("## OPENWEATHERMAP ###: connection failed");
return false;
}
char httpget[250] = {0};
sprintf(httpget, "GET /data/2.5/weather?lat=%s&lon=%s&units=metric&lang=ru&appid=%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", config.store.weatherlat, config.store.weatherlon, config.store.weatherkey, host);
client.print(httpget);
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 2000UL) {
Serial.println("## OPENWEATHERMAP ###: client available timeout !");
client.stop();
return false;
}
}
timeout = millis();
String line = "";
if (client.connected()) {
while (client.available())
{
line = client.readStringUntil('\n');
if (strstr(line.c_str(), "\"temp\"") != NULL) {
client.stop();
break;
}
if ((millis() - timeout) > 500)
{
client.stop();
Serial.println("## OPENWEATHERMAP ###: client read timeout !");
return false;
}
}
}
if (strstr(line.c_str(), "\"temp\"") == NULL) {
Serial.println("## OPENWEATHERMAP ###: weather not found !");
return false;
}
char *tmpe;
char *tmps;
const char* cursor = line.c_str();
char desc[120], temp[20], hum[20], press[20], icon[5];
tmps = strstr(cursor, "\"description\":\"");
if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
tmps += 15;
tmpe = strstr(tmps, "\",\"");
if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: description not found !"); return false;}
strlcpy(desc, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
// "ясно","icon":"01d"}],
tmps = strstr(cursor, "\"icon\":\"");
if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
tmps += 8;
tmpe = strstr(tmps, "\"}");
if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: icon not found !"); return false;}
strlcpy(icon, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
tmps = strstr(cursor, "\"temp\":");
if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
tmps += 7;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: temp not found !"); return false;}
strlcpy(temp, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
float tempf = atof(temp);
tmps = strstr(cursor, "\"pressure\":");
if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
tmps += 11;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: pressure not found !"); return false;}
strlcpy(press, tmps, tmpe - tmps + 1);
cursor = tmpe + 2;
int pressi = (float)atoi(press) / 1.333;
tmps = strstr(cursor, "humidity\":");
if (tmps == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
tmps += 10;
tmpe = strstr(tmps, ",\"");
if (tmpe == NULL) { Serial.println("## OPENWEATHERMAP ###: humidity not found !"); return false;}
strlcpy(hum, tmps, tmpe - tmps + 1);
if(config.store.audioinfo) Serial.printf("## OPENWEATHERMAP ###: description: %s, temp:%.1f C, pressure:%dmmHg, humidity:%s%%\n", desc, tempf, pressi, hum);
putcmdf("press_txt.txt=\"%dmm\"", pressi);
putcmdf("hum_txt.txt=\"%d%%\"", atoi(hum));
char cmd[30];
snprintf(cmd, sizeof(cmd)-1,"temp_txt.txt=\"%.1f\"", tempf);
putcmd(cmd);
int iconofset;
if(strstr(icon,"01")!=NULL){
iconofset = 0;
}else if(strstr(icon,"02")!=NULL){
iconofset = 1;
}else if(strstr(icon,"03")!=NULL){
iconofset = 2;
}else if(strstr(icon,"04")!=NULL){
iconofset = 3;
}else if(strstr(icon,"09")!=NULL){
iconofset = 4;
}else if(strstr(icon,"10")!=NULL){
iconofset = 5;
}else if(strstr(icon,"11")!=NULL){
iconofset = 6;
}else if(strstr(icon,"13")!=NULL){
iconofset = 7;
}else if(strstr(icon,"50")!=NULL){
iconofset = 8;
}else{
iconofset = 9;
}
putcmd("cond_img.pic", 50+iconofset);
weatherVisible(1);
return true;
}
void Nextion::getWeather(void * pvParameters){
delay(200);
if (nextion.getForecast()) {
// nextion.weatherRequest = true;
weatherticker.detach();
weatherticker.attach(WEATHER_REQUEST_INTERVAL, nextion.updateWeather);
} else {
weatherticker.detach();
weatherticker.attach(WEATHER_REQUEST_INTERVAL_FAULTY, nextion.updateWeather);
}
vTaskDelete( NULL );
}
void Nextion::updateWeather() {
if(strlen(config.store.weatherkey)==0 || !config.store.showweather) {
nextion.weatherVisible(0);
return;
}
xTaskCreatePinnedToCore(
nextion.getWeather, /* Task function. */
"nextiongetWeather", /* name of task. */
1024 * 4, /* Stack size of task */
NULL, /* parameter of the task */
0, /* priority of the task */
&nextion.weatherUpdateTaskHandle, /* Task handle to keep track of created task */
0); /* pin task to core 0 */
}
void Nextion::startWeather(){
updateWeather();
}
void Nextion::sleep(void) {
putcmd("sleep=1");
}

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,12 +60,8 @@ class Nextion {
void swichMode(displayMode_e newmode);
void drawNextStationNum(uint16_t num);
void putRequest(requestParams_t request);
void startWeather();
bool getForecast();
static void updateWeather();
static void getWeather(void * pvParameters);
void sleep();
void wake();
void sleep();
void wake();
};
extern Nextion nextion;

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