This commit is contained in:
e2002
2023-05-19 14:43:27 +03:00
parent 57a5666868
commit b975057b3f
25 changed files with 323 additions and 98 deletions

View File

@@ -144,7 +144,7 @@ class yoradioDevice(MediaPlayerEntity):
if js['on']==1:
self._state = STATE_PLAYING if js['status']==1 else STATE_IDLE
else:
self._state = STATE_OFF
self._state = STATE_PLAYING if js['status']==1 else STATE_OFF
self._current_source = str(js['station']) + '. ' + js['name']
try:
self.async_schedule_update_ha_state()

View File

@@ -226,6 +226,19 @@ Work is in progress...
---
## Version history
#### v0.9.200
**!!! a [full update](#update-over-web-interface) with Sketch data upload is required. After updating please press CTRL+F5 in browser !!!**
- implementation of WEB/SD mode switching without reboot
- replacement of SD cards without turning off the power
- switching WEB / SD from the web interface. full update required, including SPIFFS Data
- fixing the Home Assistant integration behavior logic
- SD_HSPI parameter now works. Pins HSPI - 13(MOSI) 12(MISO) 14(CLK)
- new parameter SD_SPIPINS. ```#define SD_SPIPINS sck, miso, mosi```
- sck, miso, mosi - any available pins. Used for "TTGO Tm Music Album" boards ```#define SD_SPIPINS 14, 2, 15```
- fixed a bug with garbage appearing on display ILI9225
- the slider for moving along the SD track is temporarily not working.
- bug fixes
#### v0.9.180
- OneButton library moved to the project

View File

@@ -10,7 +10,7 @@
<title>ёRadio - Player</title>
<style> </style>
</head>
<body class="%MODE%">
<body class="modeweb">
<div class="content">
<div class="logo"></div>
<div id="navbar">
@@ -57,12 +57,13 @@
</ul>
</div>
<div id="volnav">
<div id="modeline"><span id="modeweb" class="modeitem" data-name="web">Web</span><span id="modesd" data-name="sd" class="modeitem">SD</span></div>
<input type="range" id="volrange" class="slider" name="volume" data-slaveid="volinfo" min="0" max="254" value="0">
</div>
<div id="sdnav" class="hidden">
<div id="sdposvals">
<div id="sdposvalscurrent">0:00</div>
<div id="snuffle" class="playerbytton active" data-name="snuffle"></div>
<div id="snuffle" class="playerbytton active" data-name="snuffle"><span></span></div>
<div id="sdposvalsend">0:00</div>
</div>
<input type="range" id="sdpos" class="slider" name="sdpos" min="0" max="100" value="0">

Binary file not shown.

Binary file not shown.

View File

@@ -65,6 +65,7 @@ const char const_DlgLost[] PROGMEM = "* LOST *";
const char const_DlgUpdate[] PROGMEM = "* UPDATING *";
const char const_DlgNextion[] PROGMEM = "* NEXTION *";
const char const_getWeather[] PROGMEM = "";
const char const_waitForSD[] PROGMEM = "WAIT FOR SD";
const char apNameTxt[] PROGMEM = "AP NAME";
const char apPassTxt[] PROGMEM = "PASSWORD";

View File

@@ -65,6 +65,7 @@ const char const_DlgLost[] PROGMEM = "ОТКЛЮЧЕНО";
const char const_DlgUpdate[] PROGMEM = "ОБНОВЛЕНИЕ";
const char const_DlgNextion[] PROGMEM = "NEXTION";
const char const_getWeather[] PROGMEM = "";
const char const_waitForSD[] PROGMEM = "ОЖИДАНИЕ SD";
const char apNameTxt[] PROGMEM = "ТОЧКА ДОСТУПА";
const char apPassTxt[] PROGMEM = "ПАРОЛЬ";

View File

@@ -201,6 +201,9 @@ static void _async_service_task(void *pvParameters){
}
#endif
}
#if ATCP_TASK_DELAY>0
vTaskDelay(ATCP_TASK_DELAY);
#endif
}
vTaskDelete(NULL);
_async_service_task_handle = NULL;
@@ -218,7 +221,7 @@ static bool _start_async_task(){
return false;
}
if(!_async_service_task_handle){
xTaskCreateUniversal(_async_service_task, "async_tcp", XTASK_MEM_SIZE, NULL, 3, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
xTaskCreateUniversal(_async_service_task, "async_tcp", XTASK_MEM_SIZE, NULL, XTASK_PRIOTITY, &_async_service_task_handle, CONFIG_ASYNC_TCP_RUNNING_CORE);
if(!_async_service_task_handle){
return false;
}

View File

@@ -32,7 +32,7 @@ extern "C" {
//If core is not defined, then we are running in Arduino or PIO
#ifndef CONFIG_ASYNC_TCP_RUNNING_CORE
#define CONFIG_ASYNC_TCP_RUNNING_CORE 0 //any available core (-1)
#define CONFIG_ASYNC_TCP_RUNNING_CORE 1 //any available core (-1)
#endif
#ifndef CONFIG_ASYNC_TCP_USE_WDT
@@ -40,7 +40,13 @@ extern "C" {
#endif
#ifndef XTASK_MEM_SIZE
#define XTASK_MEM_SIZE 8192 / 2
#define XTASK_MEM_SIZE 6144 // 8192 / 2
#endif
#ifndef XTASK_PRIOTITY
#define XTASK_PRIOTITY 3 //3
#endif
#ifndef ATCP_TASK_DELAY
#define ATCP_TASK_DELAY 2
#endif
#ifndef XQUEUE_SIZE

View File

@@ -711,14 +711,14 @@ bool Audio::connecttoFS(fs::FS &fs, const char* path, uint32_t resumeFilePos) {
m_file_size = audiofile.size();//TEST loop
cardLock(false);
char* afn = NULL; // audioFileName
cardLock(true);
//cardLock(true);
#ifdef SDFATFS_USED
audiofile.getName(chbuf, sizeof(chbuf));
afn = strdup(chbuf);
#else
afn = strdup(audiofile.name());
#endif
cardLock(false);
//cardLock(false);
uint8_t dotPos = lastIndexOf(afn, ".");
for(uint8_t i = dotPos + 1; i < strlen(afn); i++){
afn[i] = toLowerCase(afn[i]);
@@ -1553,7 +1553,8 @@ int Audio::read_ID3_Header(uint8_t *data, size_t len) {
AUDIO_INFO("file has no mp3 tag, skip metadata");
m_audioDataSize = m_contentlength;
AUDIO_INFO("Audio-Length: %u", m_audioDataSize);
if(audio_progress) audio_progress(295903, m_audioDataSize);
//if(audio_progress) audio_progress(295903, m_audioDataSize);
if(audio_progress) audio_progress(0, m_audioDataSize);
return -1; // error, no ID3 signature found
}
ID3version = *(data + 3);
@@ -3034,14 +3035,14 @@ void Audio::processLocalFile() {
} //TEST loop
f_stream = false;
m_streamType = ST_NONE;
cardLock(true);
//cardLock(true);
#ifdef SDFATFS_USED
audiofile.getName(chbuf, sizeof(chbuf));
char *afn =strdup(chbuf);
#else
char *afn =strdup(audiofile.name()); // store temporary the name
#endif
cardLock(false);
//cardLock(false);
stopSong();
if(m_codec == CODEC_MP3) MP3Decoder_FreeBuffers();
if(m_codec == CODEC_AAC) AACDecoder_FreeBuffers();
@@ -4399,9 +4400,9 @@ bool Audio::setPinout(uint8_t BCLK, uint8_t LRC, uint8_t DOUT, int8_t DIN, int8_
//---------------------------------------------------------------------------------------------------------------------
uint32_t Audio::getFileSize() {
if(!audiofile) return 0;
cardLock(true);
//cardLock(true);
uint32_t s = audiofile.size();
cardLock(false);
//cardLock(false);
return s;
}
//---------------------------------------------------------------------------------------------------------------------

View File

@@ -152,6 +152,7 @@ Audio::Audio(uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin, uint8_t spi,
m_endFillByte=0;
curvol=50;
m_LFcount=0;
mutex_pl = xSemaphoreCreateMutex();
}
Audio::~Audio(){
// destructor
@@ -302,7 +303,7 @@ void Audio::begin(){
pinMode(dreq_pin, INPUT); // DREQ is an input
pinMode(cs_pin, OUTPUT); // The SCI and SDI signals
pinMode(dcs_pin, OUTPUT);
mutex_pl = xSemaphoreCreateMutex();
//mutex_pl = xSemaphoreCreateMutex();
DCS_HIGH();
CS_HIGH();
delay(170);
@@ -743,9 +744,9 @@ void Audio::processLocalFile() {
f_stream = false;
m_f_localfile = false;
cardLock(true);
//cardLock(true);
char *afn =strdup(audiofile.name()); // store temporary the name
cardLock(false);
//cardLock(false);
stopSong();
sprintf(chbuf, "End of file \"%s\"", afn);
if(audio_info) audio_info(chbuf);
@@ -2153,7 +2154,8 @@ int Audio::read_MP3_Header(uint8_t *data, size_t len) {
if(audio_info) audio_info("file has no mp3 tag, skip metadata");
m_audioDataSize = m_contentlength;
sprintf(chbuf, "Audio-Length: %u", m_audioDataSize);
if(audio_progress) audio_progress(295903, m_audioDataSize);
//if(audio_progress) audio_progress(295903, m_audioDataSize);
if(audio_progress) audio_progress(0, m_audioDataSize);
if(audio_info) audio_info(chbuf);
return -1; // error, no ID3 signature found
}
@@ -2482,9 +2484,9 @@ void Audio::showID3Tag(const char* tag, const char* value){
//---------------------------------------------------------------------------------------------------------------------
uint32_t Audio::getFileSize(){
if (!audiofile) return 0;
cardLock(true);
//cardLock(true);
uint32_t s = audiofile.size();
cardLock(false);
//cardLock(false);
return s;
}
//---------------------------------------------------------------------------------------------------------------------

View File

@@ -7,9 +7,14 @@
Config config;
#if DSP_HSPI || TS_HSPI || VS_HSPI || SD_HSPI
#if DSP_HSPI || TS_HSPI || VS_HSPI
SPIClass SPI2(HSPI);
#endif
#if defined(SD_SPIPINS) || SD_HSPI
SPIClass SDSPI(HSPI);
#endif
#define SDL() if(player.mutex_pl!=NULL) player.cardLock(true)
#define SDU() vTaskDelay(2); if(player.mutex_pl!=NULL) player.cardLock(false)
void u8fix(char *src){
char last = src[strlen(src)-1];
@@ -32,6 +37,13 @@ void Config::init() {
emptyFS = true;
#if IR_PIN!=255
irindex=-1;
#endif
#if defined(SD_SPIPINS) || SD_HSPI
#if !defined(SD_SPIPINS)
SDSPI.begin();
#else
SDSPI.begin(SD_SPIPINS); // SCK, MISO, MOSI
#endif
#endif
eepromRead(EEPROM_START, store);
if (store.config_set != 4262) setDefaults();
@@ -50,25 +62,112 @@ void Config::init() {
emptyFS = _isFSempty();
if(emptyFS) BOOTLOG("SPIFFS is empty!");
ssidsCount = 0;
sdResumePos = 0;
if(SDC_CS!=255){
if(!SD.begin(SDC_CS)){
store.play_mode=PM_WEB;
Serial.println("##[ERROR]#\tCard Mount Failed");
_cardStatus = CS_NONE;
mountSDbusy = false;
if(SDC_CS!=255) randomSeed(analogRead(SDC_CS));
backupSDStation = 0;
//checkSD();
_bootDone=false;
bootInfo();
}
bool Config::_sdCardIsConnected() {
if(SD.sectorSize()<1) return false;
SDL();
uint8_t buff[SD.sectorSize()] = { 0 };
bool bread = SD.readRAW(buff, 1);
if(SD.sectorSize()>0 && !bread) SD.end();
SDU();
return bread;
}
bool Config::_sdBegin(){
bool out;
SDL();
#if defined(SD_SPIPINS) || SD_HSPI
out = SD.begin(SDC_CS, SDSPI);
#else
out = SD.begin(SDC_CS);
#endif
SDU();
return out;
}
void Config::checkSD(){
if(SDC_CS==255) return;
mountSDbusy = true;
cardStatus_e prevCardStatus = _cardStatus;
if(_sdCardIsConnected()){
if(_cardStatus==CS_NONE || _cardStatus==CS_PRESENT || _cardStatus==CS_EJECTED) {
_cardStatus=CS_PRESENT;
}else{
if(store.play_mode==PM_SDCARD) initSDPlaylist();
_cardStatus=CS_MOUNTED;
}
if(_cardStatus==CS_PRESENT && store.play_mode==PM_WEB && SD_AUTOPLAY && prevCardStatus==CS_EJECTED) config.changeMode(PM_SDCARD);
}else{
if(_cardStatus==CS_MOUNTED || _cardStatus==CS_PRESENT || _cardStatus==CS_EJECTED){
if(_cardStatus!=CS_EJECTED && store.play_mode==PM_SDCARD && SD_AUTOPLAY) config.changeMode(PM_WEB);
_cardStatus=CS_EJECTED;
}
backupSDStation = 0;
}
mountSDbusy = false;
}
void Config::_mountSD(){
if(SDC_CS==255 || mountSDbusy || display.mode()==SDCHANGE) return;
mountSDbusy = true;
if(SD.sectorSize()<1) SDinit = _sdBegin();
if(!_sdCardIsConnected()) {
if(store.play_mode==PM_SDCARD){
SDinit = false;
}
}else{
if(!SDinit){
if(!_sdBegin()){
Serial.println("##[ERROR]#\tCard Mount Failed");
}else{
SDinit = true;
}
}
}
mountSDbusy = false;
}
void Config::initPlaylistMode(){
sdResumePos = 0;
SDinit = false;
if(SDC_CS!=255){
if(!_sdBegin()){
store.play_mode=PM_WEB;
Serial.println("SD Mount Failed");
}else{
Serial.println("SD Mounted");
if(store.play_mode==PM_SDCARD) {
if(_cardStatus!=CS_MOUNTED){
_cardStatus=CS_MOUNTED;
if(_bootDone) Serial.println("Waiting for SD card indexing..."); else BOOTLOG("Waiting for SD card indexing...");
initSDPlaylist();
}else{
if(backupSDStation==0) {
store.lastStation = random(1, store.countStation);
backupSDStation = store.lastStation;
}else store.lastStation = backupSDStation;
}
}
SDinit = true;
}
}else{
store.play_mode=PM_WEB;
}
if(store.play_mode==PM_WEB && !emptyFS) initPlaylist();
if (store.lastStation == 0 && store.countStation > 0) {
store.lastStation = 1;
store.lastStation = store.play_mode==PM_WEB?1:random(1, store.countStation);
save();
}
_bootDone = true;
loadStation(store.lastStation);
bootInfo();
}
void Config::_initHW(){
@@ -218,12 +317,51 @@ void Config::save() {
store.lastStation = ls;
store.play_mode = pm;
}
void Config::setSnuffle(bool sn){
sdSnuffle=sn;
save();
if(sdSnuffle) player.next();
//player blah blah blah
}
void Config::changeMode(int newmode){
if(SDC_CS==255) return;
if(!SDinit) {
_mountSD();
if(!SDinit){
Serial.println("##[ERROR]#\tSD Not Found");
netserver.requestOnChange(GETPLAYERMODE, 0);
return;
}
}
if(store.play_mode==PM_SDCARD) store.lastStation = config.backupLastStation;
if(newmode<0){
store.play_mode++;
if(store.play_mode > MAX_PLAY_MODE){
store.play_mode=0;
}
}else{
store.play_mode=(playMode_e)newmode;
}
save();
if(store.play_mode==PM_SDCARD && _cardStatus!=CS_MOUNTED){
display.putRequest(NEWMODE, SDCHANGE);
while(display.mode()!=SDCHANGE)
delay(10);
delay(50);
}
initPlaylistMode();
if (store.smartstart == 1) player.sendCommand({PR_PLAY, store.lastStation});
else
player.sendCommand({PR_STOP, 0});
netserver.requestOnChange(GETPLAYERMODE, 0);
netserver.requestOnChange(GETMODE, 0);
display.putRequest(NEWMODE, PLAYER);
display.putRequest(NEWSTATION);
}
#if IR_PIN!=255
void Config::saveIR(){
eepromWrite(EEPROM_START_IR, ircodes);
@@ -325,10 +463,9 @@ void Config::initPlaylist() {
}
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
while(p > base && isspace(*p)) p--;
p -= slen;
if (p < base) return false;
return (strncmp(p, str, slen) == 0);
@@ -338,11 +475,12 @@ bool Config::checkNoMedia(const char* path){
char nomedia[BUFLEN]= {0};
strlcat(nomedia, path, BUFLEN);
strlcat(nomedia, "/.nomedia", BUFLEN);
return SD.exists(nomedia);
SDL(); bool nm = SD.exists(nomedia); SDU();
return nm;
}
void Config::listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels){
File root = SD.open(dirname);
SDL(); File root = SD.open(dirname); SDU();
if(!root){
Serial.println("##[ERROR]#\tFailed to open directory");
return;
@@ -352,9 +490,9 @@ void Config::listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8
return;
}
File file = root.openNextFile();
SDL(); File file = root.openNextFile(); SDU();
uint32_t pos = 0;
//vTaskDelay(5);
while(file){
if(file.isDirectory()){
@@ -368,19 +506,22 @@ void Config::listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8
plSDindex.write((byte *) &pos, 4);
}
}
file = root.openNextFile();
SDL(); file = root.openNextFile(); SDU();
}
}
void Config::indexSDPlaylist() {
mountSDbusy = true;
File playlist = SPIFFS.open(PLAYLIST_SD_PATH, "w");
if (!playlist) {
mountSDbusy = false;
return;
}
File index = SPIFFS.open(INDEX_SD_PATH, "w");
listSD(playlist, index, "/", 2);
index.close();
playlist.close();
mountSDbusy = false;
}
void Config::initSDPlaylist() {
@@ -390,6 +531,7 @@ void Config::initSDPlaylist() {
File index = SPIFFS.open(INDEX_SD_PATH, "r");
store.countStation = index.size() / 4;
store.lastStation = random(1, store.countStation);
backupSDStation = store.lastStation;
index.close();
save();
}
@@ -715,9 +857,10 @@ void Config::bootInfo() {
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("buttons:\tleft=%d, center=%d, right=%d, up=%d, down=%d, mode=%d, pullup=%s", BTN_LEFT, BTN_CENTER, BTN_RIGHT, BTN_UP, BTN_DOWN, BTN_MODE, 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);
if(SDC_CS!=255) BOOTLOG("SD:\t\t%d", SDC_CS);
BOOTLOG("------------------------------------------------");
}

View File

@@ -36,6 +36,7 @@
#define MAX_PLAY_MODE 1
enum playMode_e : uint8_t { PM_WEB=0, PM_SDCARD=1 };
enum cardStatus_e : uint8_t { CS_NONE=0, CS_PRESENT=1, CS_MOUNTED=2, CS_EJECTED=3 };
enum BitrateFormat { BF_UNCNOWN, BF_MP3, BF_AAC, BF_FLAC, BF_OGG, BF_WAV };
void u8fix(char *src);
@@ -159,8 +160,11 @@ class Config {
uint16_t sleepfor;
uint32_t sdResumePos;
uint16_t backupLastStation;
uint16_t backupSDStation;
bool sdSnuffle;
bool emptyFS;
bool SDinit;
bool mountSDbusy;
public:
Config() {};
void save();
@@ -190,6 +194,8 @@ class Config {
void setBitrateFormat(BitrateFormat fmt) { configFmt = fmt; }
void initPlaylist();
void indexPlaylist();
void initSDPlaylist();
void indexSDPlaylist();
uint8_t fillPlMenu(int from, uint8_t count, bool fromNextion=false);
char * stationByNum(uint16_t num);
void setTimezone(int8_t tzh, int8_t tzm);
@@ -201,19 +207,26 @@ class Config {
void bootInfo();
void doSleepW();
void setSnuffle(bool sn);
void changeMode(int newmode=-1);
void initPlaylistMode();
void checkSD();
cardStatus_e getSDStatus(){ return _cardStatus; };
private:
template <class T> int eepromWrite(int ee, const T& value);
template <class T> int eepromRead(int ee, T& value);
cardStatus_e _cardStatus;
bool _bootDone;
void setDefaults();
Ticker _sleepTimer;
static void doSleep();
uint16_t color565(uint8_t r, uint8_t g, uint8_t b);
void listSD(File &plSDfile, File &plSDindex, const char * dirname, uint8_t levels);
void initSDPlaylist();
void indexSDPlaylist();
bool checkNoMedia(const char* path);
void _initHW();
bool _isFSempty();
bool _sdCardIsConnected();
void _mountSD();
bool _sdBegin();
char _stationBuf[BUFLEN/2];
};

View File

@@ -41,7 +41,7 @@ constexpr uint8_t nrOfButtons = sizeof(button) / sizeof(button[0]);
#endif
#endif
#if TS_MODEL!=TS_MODEL_UNDEFINED
#if (TS_MODEL!=TS_MODEL_UNDEFINED) && (DSP_MODEL!=DSP_DUMMY)
#include "touchscreen.h"
TouchScreen touchscreen;
#endif
@@ -114,7 +114,7 @@ void initControls() {
button[i].setPressTicks(BTN_PRESS_TICKS);
}
#endif
#if TS_MODEL!=TS_MODEL_UNDEFINED
#if (TS_MODEL!=TS_MODEL_UNDEFINED) && (DSP_MODEL!=DSP_DUMMY)
touchscreen.init();
#endif
#if IR_PIN!=255
@@ -129,7 +129,7 @@ void initControls() {
}
void loopControls() {
if(display.mode()==LOST || display.mode()==UPDATING) return;
if(display.mode()==LOST || display.mode()==UPDATING || display.mode()==SDCHANGE) return;
if (ctrls_on_loop) ctrls_on_loop();
#if ENC_BTNL!=255
encoder1Loop();
@@ -151,7 +151,7 @@ void loopControls() {
#if IR_PIN!=255
irLoop();
#endif
#if TS_MODEL!=TS_MODEL_UNDEFINED
#if (TS_MODEL!=TS_MODEL_UNDEFINED) && (DSP_MODEL!=DSP_DUMMY)
touchscreen.loop();
#endif
}
@@ -504,15 +504,7 @@ void onBtnClick(int id) {
break;
}
case EVT_BTNMODE: {
if(SDC_CS==255) break;
if(config.store.play_mode==PM_SDCARD) config.store.lastStation = config.backupLastStation;
config.store.play_mode++;
if(config.store.play_mode > MAX_PLAY_MODE){
config.store.play_mode=0;
}
config.save();
ESP.restart();
config.changeMode();
break;
}
default: break;
@@ -560,7 +552,7 @@ void setEncAcceleration(uint16_t acc){
#endif
}
void flipTS(){
#if TS_MODEL!=TS_MODEL_UNDEFINED
#if (TS_MODEL!=TS_MODEL_UNDEFINED) && (DSP_MODEL!=DSP_DUMMY)
touchscreen.flip();
#endif
}

View File

@@ -286,6 +286,7 @@ void Display::_swichMode(displayMode_e newmode) {
if (newmode == LOST) _showDialog(const_DlgLost);
if (newmode == UPDATING) _showDialog(const_DlgUpdate);
if (newmode == SLEEPING) _showDialog("SLEEPING");
if (newmode == SDCHANGE) _showDialog(const_waitForSD);
if (newmode == INFO || newmode == SETTINGS || newmode == TIMEZONE || newmode == WIFI) _showDialog(const_DlgNextion);
if (newmode == NUMBERS) _showDialog("");
if (newmode == STATIONS) {
@@ -419,6 +420,10 @@ void Display::loop() {
#endif*/
break;
}
case WAITFORSD: {
if(_bootstring) _bootstring->setText(const_waitForSD);
break;
}
case DSPRSSI: if(_rssi){ _setRSSI(request.payload); } if (_heapbar && config.store.audioinfo) _heapbar->setValue(player.isRunning()?player.inBufferFilled():0); break;
case PSTART: _layoutChange(true); break;
case PSTOP: _layoutChange(false); break;

View File

@@ -8,11 +8,11 @@
#include "../displays/dspcore.h"
enum displayMode_e { PLAYER, VOL, STATIONS, NUMBERS, LOST, UPDATING, INFO, SETTINGS, TIMEZONE, WIFI, CLEAR, SLEEPING };
enum displayMode_e { PLAYER, VOL, STATIONS, NUMBERS, LOST, UPDATING, INFO, SETTINGS, TIMEZONE, WIFI, CLEAR, SLEEPING, SDCHANGE };
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 };
enum displayRequestType_e { BOOTSTRING, NEWMODE, CLOCK, NEWTITLE, NEWSTATION, NEXTSTATION, DRAWPLAYLIST, DRAWVOL, DBITRATE, AUDIOINFO, SHOWVUMETER, DSPRSSI, SHOWWEATHER, NEWWEATHER, PSTOP, PSTART, DSP_START, WAITFORSD };
struct requestParams_t
{
displayRequestType_e type;

View File

@@ -199,7 +199,17 @@ void NetServer::processQueue(){
uint8_t clientId = request.clientId;
switch (request.type) {
case PLAYLIST: getPlaylist(clientId); break;
case PLAYLISTSAVED: config.indexPlaylist(); config.initPlaylist(); getPlaylist(clientId); break;
case PLAYLISTSAVED: {
if(config.store.play_mode==PM_SDCARD) {
// config.indexSDPlaylist();
config.initSDPlaylist();
}
if(config.store.play_mode==PM_WEB){
// config.indexPlaylist();
config.initPlaylist();
}
getPlaylist(clientId); break;
}
case GETACTIVE: {
bool dbgact = false, nxtn=false;
String act = F("\"group_wifi\",");
@@ -231,27 +241,30 @@ void NetServer::processQueue(){
sprintf (wsbuf, "{\"act\":[%s]}", act.c_str());
break;
}
case GETMODE: sprintf (wsbuf, "{\"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); if (config.store.play_mode==PM_SDCARD) { requestOnChange(SDPOS, clientId); requestOnChange(SDLEN, clientId); requestOnChange(SDSNUFFLE, clientId); } return; break;
case GETSYSTEM: sprintf (wsbuf, "{\"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 (wsbuf, "{\"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 (wsbuf, "{\"tzh\":%d,\"tzm\":%d,\"sntp1\":\"%s\",\"sntp2\":\"%s\"}", config.store.tzHour, config.store.tzMin, config.store.sntp1, config.store.sntp2); break;
case GETWEATHER: sprintf (wsbuf, "{\"wen\":%d,\"wlat\":\"%s\",\"wlon\":\"%s\",\"wkey\":\"%s\"}", config.store.showweather, config.store.weatherlat, config.store.weatherlon, config.store.weatherkey); break;
case GETCONTROLS: sprintf (wsbuf, "{\"vols\":%d,\"enca\":%d,\"irtl\":%d}", config.store.volsteps, config.store.encacc, config.store.irtlp); break;
case DSPON: sprintf (wsbuf, "{\"dspontrue\":%d}", 1); break;
case STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); break;
case STATIONNAME: sprintf (wsbuf, "{\"nameset\": \"%s\"}", config.station.name); break;
case ITEM: sprintf (wsbuf, "{\"current\": %d}", config.store.lastStation); break;
case TITLE: sprintf (wsbuf, "{\"meta\": \"%s\"}", config.station.title); telnet.printf("##CLI.META#: %s\n> ", config.station.title); break;
case VOLUME: sprintf (wsbuf, "{\"vol\": %d}", config.store.volume); telnet.printf("##CLI.VOL#: %d\n", config.store.volume); break;
case NRSSI: sprintf (wsbuf, "{\"rssi\": %d}", rssi); /*rssi = 255;*/ break;
case SDPOS: sprintf (wsbuf, "{\"sdpos\": %d,\"sdend\": %d,\"sdtpos\": %d,\"sdtend\": %d}", player.getFilePos(), player.getFileSize(), player.getAudioCurrentTime(), player.getAudioFileDuration()); break;
case SDLEN: sprintf (wsbuf, "{\"sdmin\": %d,\"sdmax\": %d}", player.sd_min, player.sd_max); break;
case SDSNUFFLE: sprintf (wsbuf, "{\"snuffle\": %d}", config.sdSnuffle); break;
case BITRATE: sprintf (wsbuf, "{\"bitrate\": %d}", config.station.bitrate); break;
case MODE: sprintf (wsbuf, "{\"mode\": \"%s\"}", player.status() == PLAYING ? "playing" : "stopped"); telnet.info(); break;
case EQUALIZER: sprintf (wsbuf, "{\"bass\": %d, \"middle\": %d, \"trebble\": %d}", config.store.bass, config.store.middle, config.store.trebble); break;
case BALANCE: sprintf (wsbuf, "{\"balance\": %d}", config.store.balance); break;
case GETMODE: sprintf (wsbuf, "{\"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); requestOnChange(SDINIT, clientId);requestOnChange(GETPLAYERMODE, clientId); if (config.store.play_mode==PM_SDCARD) { requestOnChange(SDPOS, clientId); requestOnChange(SDLEN, clientId); requestOnChange(SDSNUFFLE, clientId); } return; break;
case GETSYSTEM: sprintf (wsbuf, "{\"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 (wsbuf, "{\"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 (wsbuf, "{\"tzh\":%d,\"tzm\":%d,\"sntp1\":\"%s\",\"sntp2\":\"%s\"}", config.store.tzHour, config.store.tzMin, config.store.sntp1, config.store.sntp2); break;
case GETWEATHER: sprintf (wsbuf, "{\"wen\":%d,\"wlat\":\"%s\",\"wlon\":\"%s\",\"wkey\":\"%s\"}", config.store.showweather, config.store.weatherlat, config.store.weatherlon, config.store.weatherkey); break;
case GETCONTROLS: sprintf (wsbuf, "{\"vols\":%d,\"enca\":%d,\"irtl\":%d}", config.store.volsteps, config.store.encacc, config.store.irtlp); break;
case DSPON: sprintf (wsbuf, "{\"dspontrue\":%d}", 1); break;
case STATION: requestOnChange(STATIONNAME, clientId); requestOnChange(ITEM, clientId); break;
case STATIONNAME: sprintf (wsbuf, "{\"nameset\": \"%s\"}", config.station.name); break;
case ITEM: sprintf (wsbuf, "{\"current\": %d}", config.store.lastStation); break;
case TITLE: sprintf (wsbuf, "{\"meta\": \"%s\"}", config.station.title); telnet.printf("##CLI.META#: %s\n> ", config.station.title); break;
case VOLUME: sprintf (wsbuf, "{\"vol\": %d}", config.store.volume); telnet.printf("##CLI.VOL#: %d\n", config.store.volume); break;
case NRSSI: sprintf (wsbuf, "{\"rssi\": %d}", rssi); /*rssi = 255;*/ break;
case SDPOS: sprintf (wsbuf, "{\"sdpos\": %d,\"sdend\": %d,\"sdtpos\": %d,\"sdtend\": %d}", player.getFilePos(), player.getFileSize(), player.getAudioCurrentTime(), player.getAudioFileDuration()); break;
case SDLEN: sprintf (wsbuf, "{\"sdmin\": %d,\"sdmax\": %d}", player.sd_min, player.sd_max); break;
case SDSNUFFLE: sprintf (wsbuf, "{\"snuffle\": %d}", config.sdSnuffle); break;
case BITRATE: sprintf (wsbuf, "{\"bitrate\": %d}", config.station.bitrate); break;
case MODE: sprintf (wsbuf, "{\"mode\": \"%s\"}", player.status() == PLAYING ? "playing" : "stopped"); telnet.info(); break;
case EQUALIZER: sprintf (wsbuf, "{\"bass\": %d, \"middle\": %d, \"trebble\": %d}", config.store.bass, config.store.middle, config.store.trebble); break;
case BALANCE: sprintf (wsbuf, "{\"balance\": %d}", config.store.balance); break;
case SDINIT: sprintf (wsbuf, "{\"sdinit\": %d}", SDC_CS!=255); break;
case GETPLAYERMODE: sprintf (wsbuf, "{\"playermode\": \"%s\"}", config.store.play_mode==PM_SDCARD?"modesd":"modeweb"); break;
case CHANGEMODE: config.changeMode(newConfigMode); return; break;
default: break;
}
if (strlen(wsbuf) > 0) {
@@ -308,6 +321,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
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, "newmode") == 0 ) { newConfigMode = atoi(val); requestOnChange(CHANGEMODE, 0); return; }
if (strcmp(cmd, "smartstart") == 0) {
byte valb = atoi(val);
config.store.smartstart = valb == 1 ? 1 : 2;
@@ -527,6 +541,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
player.setVol(v);
}
if (strcmp(cmd, "sdpos") == 0) {
return;
if (config.store.play_mode==PM_SDCARD){
config.sdResumePos = 0;
if(!player.isRunning()){
@@ -536,6 +551,7 @@ void NetServer::onWsMessage(void *arg, uint8_t *data, size_t len, uint8_t client
player.setFilePos(atoi(val)-player.sd_min);
}
}
return;
}
if (strcmp(cmd, "snuffle") == 0) {
config.setSnuffle(strcmp(val, "true") == 0);
@@ -657,6 +673,7 @@ bool NetServer::importPlaylist() {
}
void NetServer::requestOnChange(requestType_e request, uint8_t clientId) {
if(nsQueue==NULL) return;
nsRequestParams_t nsrequest;
nsrequest.type = request;
nsrequest.clientId = clientId;
@@ -667,13 +684,13 @@ String processor(const String& var) { // %Templates%
if (var == "ACTION") return (network.status == CONNECTED && !config.emptyFS)?"webboard":"";
if (var == "UPLOADWIFI") return (network.status == CONNECTED)?" hidden":"";
if (var == "VERSION") return YOVERSION;
if (var == "MODE") {
/*if (var == "MODE") {
if(config.store.play_mode==PM_SDCARD) {
return "modescard";
}else{
return "modeweb";
}
}
}*/
return String();
}

View File

@@ -5,10 +5,10 @@
#include "../AsyncWebServer/ESPAsyncWebServer.h"
#include "AsyncUDP.h"
enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, STATIONNAME=3, ITEM=4, TITLE=5, VOLUME=6, NRSSI=7, BITRATE=8, MODE=9, EQUALIZER=10, BALANCE=11, PLAYLISTSAVED=12, GETMODE=13, GETINDEX=14, GETACTIVE=15, GETSYSTEM=16, GETSCREEN=17, GETTIMEZONE=18, GETWEATHER=19, GETCONTROLS=20, DSPON=21, SDPOS=22, SDLEN=23, SDSNUFFLE=24 };
enum requestType_e : uint8_t { PLAYLIST=1, STATION=2, STATIONNAME=3, ITEM=4, TITLE=5, VOLUME=6, NRSSI=7, BITRATE=8, MODE=9, EQUALIZER=10, BALANCE=11, PLAYLISTSAVED=12, GETMODE=13, GETINDEX=14, GETACTIVE=15, GETSYSTEM=16, GETSCREEN=17, GETTIMEZONE=18, GETWEATHER=19, GETCONTROLS=20, DSPON=21, SDPOS=22, SDLEN=23, SDSNUFFLE=24, SDINIT=25, GETPLAYERMODE=26, CHANGEMODE=27 };
enum import_e : uint8_t { IMDONE=0, IMPL=1, IMWIFI=2 };
const char emptyfs_html[] PROGMEM = R"(
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, minimum-scale=0.25"><meta charset="UTF-8"><link rel="icon" href="data:;base64,iVBORw0KGgo="><title>ёRadio - WEB Board Uploader</title><style>body{background-color:#000;color:#e3d25f;font-size:20px;}
<!DOCTYPE html><html><head><meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=0.25"><meta charset="UTF-8"><link rel="icon" href="data:;base64,iVBORw0KGgo="><title>ёRadio - WEB Board Uploader</title><style>body{background-color:#000;color:#e3d25f;font-size:20px;}
hr{margin:20px 0;border:0; border-top:#555 1px solid;} p{text-align:center;margin-bottom:10px;} section{max-width:500px; text-align:center;margin:0 auto 30px auto;}
input[type=file]{color:#ccc;} input[type=file]::file-selector-button, input[type=submit]{border:2px solid #e3d25f;color:#000;padding:6px 16px;border-radius:25px;background-color:#e3d25f;margin:0 6px;cursor:pointer;}
input[type=submit]{font-size:18px;text-transform:uppercase;padding:8px 26px;margin-top:10px;font-family:Times;} span{color:#ccc} .flex{display:flex;justify-content: space-around;margin-top:10px;}
@@ -59,6 +59,7 @@ class NetServer {
void loop();
void requestOnChange(requestType_e request, uint8_t clientId);
void setRSSI(int val) { rssi = val; };
int getRSSI() { return rssi; };
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);
bool irRecordEnable;
@@ -69,7 +70,7 @@ class NetServer {
private:
requestType_e request;
QueueHandle_t nsQueue;
int rssi;
int rssi, newConfigMode;
void getPlaylist(uint8_t clientId);
bool importPlaylist();
static size_t chunkedHtmlPageCallback(uint8_t* buffer, size_t maxLen, size_t index);

View File

@@ -14,6 +14,8 @@ bool getWeather(char *wstr);
void doSync(void * pvParameters);
void ticks() {
if(!display.ready()) return; //waiting for SD is ready
static const uint16_t weatherSyncInterval=1800;
//static const uint16_t weatherSyncIntervalFail=10;
static const uint16_t timeSyncInterval=3600;
@@ -42,11 +44,10 @@ void ticks() {
}
if(player.isRunning() && config.store.play_mode==PM_SDCARD) netserver.requestOnChange(SDPOS, 0);
if(divrssi) {
int rs = WiFi.RSSI();
netserver.setRSSI(rs);
netserver.setRSSI(WiFi.RSSI());
netserver.requestOnChange(NRSSI, 0);
display.putRequest(DSPRSSI, rs);
display.putRequest(DSPRSSI, netserver.getRSSI());
if(!config.mountSDbusy) player.sendCommand({PR_CHECKSD, 0});
}
}

View File

@@ -1,7 +1,7 @@
#ifndef options_h
#define options_h
#define YOVERSION "0.9.180"
#define YOVERSION "0.9.200"
/*******************************************************
DO NOT EDIT THIS FILE.
@@ -306,7 +306,10 @@ The connection tables are located here https://github.com/e2002/yoradio#connecti
#define MAX_AUDIO_SOCKET_TIMEOUT false // max audio socket timeout?
#endif
#ifndef BITRATE_FULL
#define BITRATE_FULL true
#define BITRATE_FULL true // display bitreta badget
#endif
#ifndef SD_AUTOPLAY
#define SD_AUTOPLAY true // auto play from SD card when inserted
#endif
/*
*** ST7735 display submodel ***

View File

@@ -60,7 +60,7 @@ void Player::init() {
//setOutputPins(false);
_volTimer=false;
playmutex = xSemaphoreCreateMutex();
randomSeed(analogRead(0));
//randomSeed(analogRead(0));
#if PLAYER_FORCE_MONO
forceMono(true);
#endif
@@ -108,7 +108,7 @@ void Player::_stop(bool alreadyStopped){
}
void Player::initHeaders(const char *file) {
if(strlen(file)==0) return;
if(strlen(file)==0 || true) return; //TODO Read TAGs
connecttoFS(SD,file);
eofHeader = false;
while(!eofHeader) Audio::loop();
@@ -139,6 +139,10 @@ void Player::loop() {
Audio::setVolume(volToI2S(requestP.payload));
break;
}
case PR_CHECKSD: {
config.checkSD();
break;
}
default: break;
}
}
@@ -182,9 +186,18 @@ void Player::_play(uint16_t stationId) {
netserver.loop();
netserver.loop();
config.setSmartStart(0);
if (config.store.play_mode==PM_WEB?connecttohost(config.station.url):connecttoFS(SD,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min)) {
bool isConnected = false;
if(config.store.play_mode==PM_SDCARD && SDC_CS!=255)
isConnected=connecttoFS(SD,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min);
else config.store.play_mode=PM_WEB;
if(config.store.play_mode==PM_WEB) isConnected=connecttohost(config.station.url);
if(isConnected){
//if (config.store.play_mode==PM_WEB?connecttohost(config.station.url):connecttoFS(SD,config.station.url,config.sdResumePos==0?_resumeFilePos:config.sdResumePos-player.sd_min)) {
_status = PLAYING;
if(config.store.play_mode==PM_SDCARD) config.sdResumePos = 0;
if(config.store.play_mode==PM_SDCARD) {
config.sdResumePos = 0;
config.backupSDStation = stationId;
}
//config.setTitle("");
config.setSmartStart(1);
netserver.requestOnChange(MODE, 0);

View File

@@ -15,7 +15,7 @@
#define PLERR_LN 64
#define SET_PLAY_ERROR(...) {char buff[512 + 64]; sprintf(buff,__VA_ARGS__); setError(buff);}
enum playerRequestType_e : uint8_t { PR_PLAY = 1, PR_STOP = 2, PR_PREV = 3, PR_NEXT = 4, PR_VOL = 5 };
enum playerRequestType_e : uint8_t { PR_PLAY = 1, PR_STOP = 2, PR_PREV = 3, PR_NEXT = 4, PR_VOL = 5, PR_CHECKSD = 6 };
struct playerRequestParams_t
{
playerRequestType_e type;

View File

@@ -1,5 +1,5 @@
#include "options.h"
#if TS_MODEL!=TS_MODEL_UNDEFINED
#if (TS_MODEL!=TS_MODEL_UNDEFINED) && (DSP_MODEL!=DSP_DUMMY)
#include "touchscreen.h"
#include "config.h"

View File

@@ -38,14 +38,15 @@ void DspCore::setCursor(int16_t x, int16_t y){
}
uint16_t DspCore::print(const char* s){
TAKE_MUTEX();
if(_gFont){
TAKE_MUTEX();
drawGFXText(_cursorx, _cursory, s, _fgcolor);
GIVE_MUTEX();
return 0;
}else{
_cursorx=drawText(_cursorx, _cursory, s, _fgcolor);
GIVE_MUTEX();
//GIVE_MUTEX();
return _cursorx;
}
}
@@ -279,7 +280,10 @@ uint16_t DspCore::drawChar(uint16_t x, uint16_t y, uint16_t ch, uint16_t color)
return cfont.width;
}
}
return TFT_22_ILI9225::drawChar(x, y, ch, color);
TAKE_MUTEX();
uint16_t ret=TFT_22_ILI9225::drawChar(x, y, ch, color);
GIVE_MUTEX();
return ret;
}
void DspCore::setClipping(clipArea ca){

View File

@@ -43,6 +43,11 @@ void setup() {
while(!display.ready()) delay(10);
return;
}
if(SDC_CS!=255) {
display.putRequest(WAITFORSD, 0);
Serial.print("##[BOOT]#\tSD search\t");
}
config.initPlaylistMode();
netserver.begin();
telnet.begin();
initControls();