Files
yoradio/yoRadio/src/core/config.cpp
2022-12-13 16:31:32 +03:00

667 lines
18 KiB
C++

#include "config.h"
#include <EEPROM.h>
//#include <SPIFFS.h>
#include "display.h"
#include "player.h"
Config config;
#if DSP_HSPI || TS_HSPI || VS_HSPI
SPIClass SPI2(HSPI);
#endif
SPIClass SDSPI(VSPI);
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(store.play_mode==80) store.play_mode=PM_WEB;
//if (!SPIFFS.begin(false, "/spiffs", 30)) {
if (!SPIFFS.begin(false)) {
return;
}
loadTheme();
ssidsCount = 0;
if(SDC_CS!=255 && store.play_mode==PM_WEB){
pinMode(SDC_CS, OUTPUT); digitalWrite(SDC_CS, HIGH);
SDSPI.begin(SDC_SPI);
SDSPI.setFrequency(1000000);
if(!SD.begin(SDC_CS)){
store.play_mode=PM_WEB;
Serial.println("##[ERROR]#\tCard Mount Failed");
}else{
initSDPlaylist();
}
}
if(store.play_mode==PM_WEB) 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
bootInfo();
}
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.plcurrent = color565(COLOR_PL_CURRENT);
theme.plcurrentbg = color565(COLOR_PL_CURRENT_BG);
theme.plcurrentfill = color565(COLOR_PL_CURRENT_FILL);
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.play_mode = 0;
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(uint16_t val) {
store.lastStation = val;
save();
return store.lastStation;
}
byte Config::setCountStation(uint16_t 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();
}
}
bool endsWith (const char* base, const char* str) {
//fb
int slen = strlen(str) - 1;
const char *p = base + strlen(base) - 1;
while(p > base && isspace(*p)) p--; // rtrim
p -= slen;
if (p < base) return false;
return (strncmp(p, str, slen) == 0);
}
void Config::listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels){
File root = SD.open(dirname);
if(!root){
Serial.println("Failed to open directory");
return;
}
if(!root.isDirectory()){
Serial.println("Not a directory");
return;
}
File file = root.openNextFile();
uint32_t pos = 0;
while(file){
if(file.isDirectory()){
if(levels){
listSD(plSDfile, plSDindex, file.path(), levels -1);
}
} else {
if(endsWith(file.name(), ".mp3") || endsWith(file.name(), ".m4a") || endsWith(file.name(), ".aac") || endsWith(file.name(), ".wav") || endsWith(file.name(), ".flac")){
pos = plSDfile.position();
plSDfile.print(file.name()); plSDfile.print("\t"); plSDfile.print(file.path()); plSDfile.print("\t"); plSDfile.println(0);
plSDindex.write((byte *) &pos, 4);
Serial.print(" plSDfile.position:\t");
Serial.println(pos);
}
}
file = root.openNextFile();
}
}
void Config::indexSDPlaylist() {
File playlist = SPIFFS.open(PLAYLIST_SD_PATH, "w");
if (!playlist) {
return;
}
File index = SPIFFS.open(INDEX_SD_PATH, "w");
listSD(playlist, index, "/", 2);
index.close();
playlist.close();
}
void Config::initSDPlaylist() {
store.countStation = 0;
indexSDPlaylist();
if (SPIFFS.exists(INDEX_SD_PATH)) {
File index = SD.open(INDEX_SD_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 > 29 || strlen(line) > 71) return false;
memset(ssid, 0, 30);
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[30], passval[40];
byte c = 0;
while (file.available()) {
if (parseSsid(file.readStringUntil('\n').c_str(), ssidval, passval)) {
strlcpy(ssids[c].ssid, ssidval, 30);
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::doSleepW(){
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_deep_sleep_start();
}
void Config::sleepForAfter(uint16_t sf, uint16_t sa){
sleepfor = sf;
if(sa > 0) _sleepTimer.attach(sa * 60, doSleep);
else doSleep();
}
void Config::bootInfo() {
BOOTLOG("************************************************");
BOOTLOG("* ёPadio v%s *", YOVERSION);
BOOTLOG("************************************************");
BOOTLOG("------------------------------------------------");
BOOTLOG("arduino:\t%d", ARDUINO);
BOOTLOG("compiler:\t%s", __VERSION__);
BOOTLOG("esp32core:\t%d.%d.%d", ESP_ARDUINO_VERSION_MAJOR, ESP_ARDUINO_VERSION_MINOR, ESP_ARDUINO_VERSION_PATCH);
uint32_t chipId = 0;
for(int i=0; i<17; i=i+8) {
chipId |= ((ESP.getEfuseMac() >> (40 - i)) & 0xff) << i;
}
BOOTLOG("chip:\t\tmodel: %s | rev: %d | id: %d | cores: %d | psram: %d", ESP.getChipModel(), ESP.getChipRevision(), chipId, ESP.getChipCores(), ESP.getPsramSize());
BOOTLOG("display:\t%d", DSP_MODEL);
if(VS1053_CS==255) {
BOOTLOG("audio:\t\t%s (%d, %d, %d)", "I2S", I2S_DOUT, I2S_BCLK, I2S_LRC);
}else{
BOOTLOG("audio:\t\t%s (%d, %d, %d, %d, %s)", "VS1053", VS1053_CS, VS1053_DCS, VS1053_DREQ, VS1053_RST, VS_HSPI?"true":"false");
}
BOOTLOG("audioinfo:\t%s", store.audioinfo?"true":"false");
BOOTLOG("smartstart:\t%d", store.smartstart);
BOOTLOG("vumeter:\t%s", store.vumeter?"true":"false");
BOOTLOG("softapdelay:\t%d", store.softapdelay);
BOOTLOG("flipscreen:\t%s", store.flipscreen?"true":"false");
BOOTLOG("invertdisplay:\t%s", store.invertdisplay?"true":"false");
BOOTLOG("showweather:\t%s", store.showweather?"true":"false");
BOOTLOG("buttons:\tleft=%d, center=%d, right=%d, up=%d, down=%d, pullup=%s", BTN_LEFT, BTN_CENTER, BTN_RIGHT, BTN_UP, BTN_DOWN, BTN_INTERNALPULLUP?"true":"false");
BOOTLOG("encoders:\tl1=%d, b1=%d, r1=%d, pullup=%s, l2=%d, b2=%d, r2=%d, pullup=%s", ENC_BTNL, ENC_BTNB, ENC_BTNR, ENC_INTERNALPULLUP?"true":"false", ENC2_BTNL, ENC2_BTNB, ENC2_BTNR, ENC2_INTERNALPULLUP?"true":"false");
BOOTLOG("ir:\t\t%d", IR_PIN);
BOOTLOG("------------------------------------------------");
}