diff --git a/README.md b/README.md index 2a2c965..9776a4e 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,16 @@ Three tact buttons or Encoder or all together | RSTL | 15* | TFT_RST | | DCL | 4* | TFT_DC | +| NOKIA5110 | ESP-32 | options.h | +| ------ | ------ | ------ | +| RST | 15* | TFT_RST | +| CE | 5* | TFT_CS | +| DC | 4* | TFT_DC | +| DIN | 23 | - | +| CLK | 18 | - | +| VCC | +3v3 | - | +| GND | GND | - | + | I2C Display | ESP-32 | options.h | | ------ | ------ | ------ | | GND | GND | - | @@ -76,7 +86,7 @@ Adafruit_GFX, Adafruit_ST7735\*, Adafruit_SSD1306\*, Adafruit_PCD8544\*, (\* dep ## Hardware setup Hardware is connected in the **[options.h](yoRadio/options.h)** file. \ _so that the settings are not overwritten when updating git, you need to put the file **myoptions.h** ([exsample](exsamples/myoptions.h)) in the root of the project and make settings in it_ -```` +````c++ /* DISPLAY MODEL * 0 - DUMMY * 1 - ST7735 @@ -86,16 +96,16 @@ _so that the settings are not overwritten when updating git, you need to put the #define DSP_MODEL 1 ```` The ST7735 display model is configured in the file [src/displays/displayST7735.cpp](yoRadio/src/displays/displayST7735.cpp) -```` +````c++ #define DTYPE INITR_BLACKTAB // 1.8' https://aliexpress.ru/item/1005002822797745.html //#define DTYPE INITR_144GREENTAB // 1.44' https://aliexpress.ru/item/1005002822797745.html ```` Rotation of the ST7735 display is configured in the file [src/displays/displayST7735.h](yoRadio/src/displays/displayST7735.h) -```` +````c++ #define TFT_ROTATE 3 // 180 degress ```` If there is a noisy line on one side of the screen, then in Adafruit_ST7735.cpp: -```` +````c++ // Black tab, change MADCTL color filter if ((options == INITR_BLACKTAB) || (options == INITR_MINI160x80)) { uint8_t data = 0xC0; @@ -107,14 +117,14 @@ If there is a noisy line on one side of the screen, then in Adafruit_ST7735.cpp: --- ## Quick start -1. In ArduinoIDE - upload sketch data (Tools→ESP32 Sketch Data Upload) -2. Upload the sketch to the board ([example of the board connection](images/board.jpg)) +1. In ArduinoIDE - upload sketch data via Tools→ESP32 Sketch Data Upload ([it's here](images/board2.jpg)) +2. Upload the sketch to the board ([example of the board settings](images/board.jpg)) 3. Connect to yoRadioAP acces point with password 12345987, go to http://192.168.4.1/ configure and wifi connections. \ _\*this step can be skipped if you add WiFiSSID WiFiPassword pairs to the [yoRadio/data/data/wifi.csv](yoRadio/data/data/wifi.csv) file (tab-separated values, one line per access point) before uploading the sketch data in step 1_ -4. After successful connection go to http://\/ , add stations to playlist (or import WebStations.txt from KaRadio) +4. After successful connection go to http://\/ , add stations to playlist (or import WebStations.txt from KaRadio) 5. Well done! -**localization:** +**Localization:** Если Adafruit_GFX ещё не русифицирована, русифицировать её, заменив файл Arduino/libraries/Adafruit_GFX_Library/glcdfont.c файлом [yoRadio/fonts/glcdfont.c](yoRadio/fonts/glcdfont.c) --- @@ -122,27 +132,35 @@ _\*this step can be skipped if you add WiFiSSID WiFiPassword pairs to the [yoRad - Сan add up to 65535 stations to a playlist. Supports and imports [KaRadio](https://github.com/karawin/Ka-Radio32) playlists (WebStations.txt) - Telnet with KaRadio format output \ **Commands**: \ -cli.prev (or simply prev) - previous station \ -cli.next, next - next station \ -cli.toggle, toggle - start/stop \ -cli.stop, stop - stop \ -cli.start, start, cli.play, play - start playing \ -cli.play("x"), play(x), play x - play station x \ -cli.vol, vol - the current value of volume (0-254) \ -cli.vol("x"), vol(x), vol x - set volume (0-254) \ -cli.audioinfo, audioinfo - the current value of debug (0-1) \ -cli.audioinfo("x"), audioinfo(x), audioinfo x - debug on/off (0-1) \ -cli.smartstart, smartstart - the current value of smart start \ -cli.smartstart("x"), smartstart(x), smartstart x - smart start: 2-off, 0-1 - start playing on boot, if the radio was playing before the reboot \ -cli.list, list - get playlist \ -cli.info, info - get current state \ -sys.boot, boot, reboot - reboot \ -sys.date - date/time + **cli.prev** (_or simply_ **prev**) - previous station \ + **cli.next** _or_ **next** - next station \ + **cli.toggle** _or_ **toggle** - start/stop \ + **cli.stop** _or_ **stop** - stop \ + **cli.start** _or_ **start** _or_ **cli.play** _or_ **play** - start playing \ + **cli.play("x")** _or_ **play(x)** _or_ **play x** - play station x \ + **cli.vol** _or_ **vol** - display the current value of volume (0-254) \ + **cli.vol("x")** _or_ **vol(x)** _or_ **vol x** - set volume (0-254) \ + **cli.audioinfo** _or_ **audioinfo** - display the current value of debug (0-1) \ + **cli.audioinfo("x")** _or_ **audioinfo(x)** _or_ **audioinfo x** - debug on/off (0-1) \ + **cli.smartstart** _or_ **smartstart** - display the current value of smart start \ + **cli.smartstart("x")** _or_ **smartstart(x)** _or_ **smartstart x** - smart start: 2-off, 0-1 - start playing on boot, if the radio was playing before the reboot \ + **cli.list** _or_ **list** - display playlist \ + **cli.info** _or_ **info** - display current state \ + **sys.boot** _or_ **boot** _or_ **reboot** - reboot \ + **sys.date** - sync date/time and display it \ + **sys.tzo** _or_ **tzo** - display the timezone offset \ + **sys.tzo("h:m")** _or_ **tzo(h:m)** _or_ **tzo h:m** - set timezone offset \ + **sys.tzo("h")** _or_ **tzo(h)** _or_ **tzo h** - set timezone offset in hours only --- ## Version history +#### v0.4.210 +- added timezone config by telnet +- fix telnet output +- some separation apples and oranges + #### v0.4.199 -- excluded required installation of all libraries for displays. +- excluded required installation of all libraries for displays #### v0.4.197 - added support for Nokia 5110 SPI displays diff --git a/images/board.jpg b/images/board.jpg index 9589a54..d2b91fb 100644 Binary files a/images/board.jpg and b/images/board.jpg differ diff --git a/images/board2.jpg b/images/board2.jpg new file mode 100644 index 0000000..1767176 Binary files /dev/null and b/images/board2.jpg differ diff --git a/yoRadio/audiohandlers.ino b/yoRadio/audiohandlers.ino index e0248c2..bf7cffa 100644 --- a/yoRadio/audiohandlers.ino +++ b/yoRadio/audiohandlers.ino @@ -33,7 +33,7 @@ void audio_showstreamtitle(const char *info) { telnet.info(); player.requesToStart = false; } else { - telnet.printf("##CLI.META#: %s\n", info); + telnet.printf("##CLI.META#: %s\n> ", info); } } } diff --git a/yoRadio/config.cpp b/yoRadio/config.cpp index f7c8cd6..8fed721 100644 --- a/yoRadio/config.cpp +++ b/yoRadio/config.cpp @@ -6,6 +6,12 @@ Config config; void Config::init() { eepromRead(EEPROM_START, store); + if (store.tz_set!=57){ // update to v0.4.200 + store.tz_set = 57; + store.tzHour = 3; + store.tzMin = 0; + store.timezoneOffset = 0; + } if (store.config_set != 4256) setDefaults(); //if (!SPIFFS.begin(false, "/spiffs", 30)) { if (!SPIFFS.begin(false)) { @@ -54,6 +60,25 @@ void Config::setDefaults() { store.lastSSID = 0; store.audioinfo = false; store.smartstart = 2; + store.tz_set = 57; + store.tzHour = 3; + store.tzMin = 0; + store.timezoneOffset = 0; +} + +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() { diff --git a/yoRadio/config.h b/yoRadio/config.h index 0dd5331..0fe1119 100644 --- a/yoRadio/config.h +++ b/yoRadio/config.h @@ -23,6 +23,10 @@ struct config_t byte lastSSID; bool audioinfo; byte smartstart; + byte tz_set; // must be 57 + int8_t tzHour; + int8_t tzMin; + uint16_t timezoneOffset; }; struct station_t @@ -67,6 +71,9 @@ class Config { void initPlaylist(); void indexPlaylist(); void fillPlMenu(char plmenu[][40], int from, byte count); + void setTimezone(int8_t tzh, int8_t tzm); + void setTimezoneOffset(uint16_t tzo); + uint16_t getTimezoneOffset(); private: template int eepromWrite(int ee, const T& value); template int eepromRead(int ee, T& value); diff --git a/yoRadio/display.cpp b/yoRadio/display.cpp index c483e83..a5817b0 100644 --- a/yoRadio/display.cpp +++ b/yoRadio/display.cpp @@ -1,10 +1,11 @@ +#include "options.h" + #include "WiFi.h" #include "time.h" #include "display.h" #include "player.h" #include "netserver.h" -#include "options.h" #include "network.h" #if DSP_MODEL==0 @@ -85,7 +86,7 @@ void Scroll::clearscrolls() { void Scroll::loop() { if (checkdelay(x == TFT_FRAMEWDT ? delayStartScroll : SCROLLTIME, scrolldelay)) { scroll(); - ticks(); + sticks(); } yield(); } @@ -103,7 +104,7 @@ void Scroll::drawFrame() { dsp.drawScrollFrame(texttop, textheight, bg); } -void Scroll::ticks() { +void Scroll::sticks() { if (!doscroll || locked) return; setTextParams(); dsp.set_Cursor(x, texttop); @@ -170,7 +171,6 @@ void Display::start() { station(); rssi(); time(); - configTime(TIMEZONE, OFFSET, "pool.ntp.org", "ru.pool.ntp.org"); timer.attach_ms(1000, ticks); // Экстреминатус секвестирован } @@ -213,14 +213,8 @@ void Display::swichMode(displayMode_e newmode) { void Display::drawPlayer() { if (clockRequest) { - if (syncTicks % 21600 == 0) { //6hours - configTime(TIMEZONE, OFFSET, "pool.ntp.org", "ru.pool.ntp.org"); - yield(); - syncTicks = 0; - } - getLocalTime(&timeinfo); + getLocalTime(&network.timeinfo); time(); - syncTicks++; clockRequest = false; } meta.loop(); @@ -322,9 +316,9 @@ void Display::time() { if (!dt) { heap(); rssi(); - strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M", &timeinfo); + strftime(timeStringBuff, sizeof(timeStringBuff), "%H:%M", &network.timeinfo); } else { - strftime(timeStringBuff, sizeof(timeStringBuff), "%H %M", &timeinfo); + strftime(timeStringBuff, sizeof(timeStringBuff), "%H %M", &network.timeinfo); } dsp.printClock(timeStringBuff); dt = !dt; diff --git a/yoRadio/display.h b/yoRadio/display.h index b2c59b2..7838f6c 100644 --- a/yoRadio/display.h +++ b/yoRadio/display.h @@ -7,9 +7,6 @@ enum displayMode_e { PLAYER, VOL, STATIONS }; -#define TIMEZONE 10800 // 3600*3=10800 (UTC+3) -#define OFFSET 0 // Daylight Offset (sec.) - class Scroll { public: Scroll() { }; @@ -33,7 +30,7 @@ class Scroll { void getbounds(uint16_t &tWidth, uint16_t &tHeight, uint16_t &sWidth); boolean checkdelay(int m, unsigned long &tstamp); void scroll(); - void ticks(); + void sticks(); void clear(); void setTextParams(); void drawFrame(); @@ -41,8 +38,6 @@ class Scroll { class Display { public: - struct tm timeinfo; - uint16_t syncTicks; bool clockRequest; uint16_t screenwidth, screenheight; displayMode_e mode; diff --git a/yoRadio/network.cpp b/yoRadio/network.cpp index dcfab54..1f23b7a 100644 --- a/yoRadio/network.cpp +++ b/yoRadio/network.cpp @@ -2,9 +2,15 @@ #include "WiFi.h" #include "display.h" #include "options.h" +#include "config.h" +#include "telnet.h" Network network; +void syncTime() { + network.requestTimeSync(true); +} + void Network::begin() { config.initNetwork(); if (config.ssidsCount == 0) { @@ -35,7 +41,7 @@ void Network::begin() { break; } } - if(WiFi.status() != WL_CONNECTED && ls==startedls){ + if (WiFi.status() != WL_CONNECTED && ls == startedls) { raiseSoftAP(); return; } @@ -46,6 +52,22 @@ void Network::begin() { } digitalWrite(LED_BUILTIN, LOW); status = CONNECTED; + requestTimeSync(); + ntimer.attach_ms(TSYNC_DELAY, syncTime); +} + +void Network::requestTimeSync(bool withTelnetOutput) { + configTime(config.store.tzHour * 3600 + config.store.tzMin * 60, config.getTimezoneOffset(), "pool.ntp.org", "ru.pool.ntp.org"); + if (withTelnetOutput) { + getLocalTime(&timeinfo); + char timeStringBuff[50]; + strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S", &timeinfo); + if (config.store.tzHour < 0) { + telnet.printf(0, "##SYS.DATE#: %s%03d:%02d\n> ", timeStringBuff, config.store.tzHour, config.store.tzMin); + } else { + telnet.printf(0, "##SYS.DATE#: %s+%02d:%02d\n> ", timeStringBuff, config.store.tzHour, config.store.tzMin); + } + } } void Network::raiseSoftAP() { diff --git a/yoRadio/network.h b/yoRadio/network.h index 6ec70d1..b4a890d 100644 --- a/yoRadio/network.h +++ b/yoRadio/network.h @@ -1,18 +1,24 @@ #ifndef network_h #define network_h +#include +#include "time.h" #define apSsid "yoRadioAP" #define apPassword "12345987" +#define TSYNC_DELAY 10800000 // 1000*60*60*3 = 3 hours enum n_Status_e { CONNECTED, SOFT_AP, FAILED }; class Network { public: n_Status_e status; + struct tm timeinfo; public: Network() {}; void begin(); + void requestTimeSync(bool withTelnetOutput=false); private: + Ticker ntimer; void raiseSoftAP(); }; diff --git a/yoRadio/options.h b/yoRadio/options.h index f5be549..4a3be75 100644 --- a/yoRadio/options.h +++ b/yoRadio/options.h @@ -1,7 +1,7 @@ #ifndef options_h #define options_h -#define VERSION "0.4.199" +#define VERSION "0.4.210" /* DISPLAY MODEL * 0 - DUMMY diff --git a/yoRadio/player.cpp b/yoRadio/player.cpp index cb3c0ee..be6fabc 100644 --- a/yoRadio/player.cpp +++ b/yoRadio/player.cpp @@ -30,6 +30,7 @@ void Player::loop() { } else { if (isRunning()) { digitalWrite(LED_BUILTIN, LOW); + display.title("[stopped]"); stopSong(); stopInfo(); } diff --git a/yoRadio/telnet.cpp b/yoRadio/telnet.cpp index 9c05de5..31340d6 100644 --- a/yoRadio/telnet.cpp +++ b/yoRadio/telnet.cpp @@ -4,7 +4,7 @@ #include "config.h" #include "telnet.h" #include "player.h" -#include "display.h" +#include "network.h" Telnet telnet; @@ -139,7 +139,7 @@ void Telnet::info() { byte volume; telnet.printf("##CLI.INFO#\n"); char timeStringBuff[50]; - strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &display.timeinfo); + 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) { @@ -170,7 +170,7 @@ void Telnet::on_input(const char* str, byte clientId) { } if (strcmp(str, "cli.stop") == 0 || strcmp(str, "stop") == 0) { player.mode = STOPPED; - display.title("[stopped]"); + //display.title("[stopped]"); info(); return; } @@ -183,13 +183,11 @@ void Telnet::on_input(const char* str, byte clientId) { return; } if (strcmp(str, "cli.vol") == 0 || strcmp(str, "vol") == 0) { - printf(clientId, "##CLI.VOL#: %d\n", config.store.volume); + printf(clientId, "##CLI.VOL#: %d\n> ", config.store.volume); return; } if (strcmp(str, "sys.date") == 0) { - char timeStringBuff[50]; - strftime(timeStringBuff, sizeof(timeStringBuff), "%Y-%m-%dT%H:%M:%S+03:00", &display.timeinfo); - telnet.printf("##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset + network.requestTimeSync(true); return; } int volume; @@ -199,24 +197,25 @@ void Telnet::on_input(const char* str, byte clientId) { 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); + printf(clientId, "##CLI.AUDIOINFO#: %d\n> ", config.store.audioinfo > 0); return; } byte 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; + 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); + printf(clientId, "##CLI.SMARTSTART#: %d\n> ", config.store.smartstart); return; } byte sstart; if (sscanf(str, "smartstart(%d)", &sstart) == 1 || sscanf(str, "cli.smartstart(\"%d\")", &sstart) == 1 || sscanf(str, "smartstart %d", &sstart) == 1) { config.store.smartstart = sstart; + printf(clientId, "new smartstart value is: %d\n> ", config.store.audioinfo); config.save(); return; } @@ -242,8 +241,12 @@ void Telnet::on_input(const char* str, byte clientId) { 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+03:00", &display.timeinfo); - printf(clientId, "##SYS.DATE#: %s\n", timeStringBuff); //TODO timezone offset + 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); @@ -257,7 +260,6 @@ void Telnet::on_input(const char* str, byte clientId) { printf(clientId, "> "); return; } - uint8_t 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; @@ -265,5 +267,37 @@ void Telnet::on_input(const char* str, byte clientId) { player.play(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; + 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(tzh, 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(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; + } + telnet.printf(clientId, "unknown command: %s\n> ", str); }