/
singlwolf
/
Radiola-2S3
Обзор
Документация
Войти
/
singlwolf
/
Radiola-2S3
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/telnet.cpp
885 строк
21 KB
SinglWolf
v1.0.6
15 апр 2026, 05:04
15 апр 2026, 05:04
55b96c5
Код
Авторство
О чём код?
#include <stdarg.h> #include <fs_system.h> #include "eeStorage.h" #include "web_server.h" #include "services.h" #include "hardware.h" #include "audioVS.h" #include "net_work.h" #include "telnet.h" #include "lv_i18n.h" #include "esp_wifi.h" #include "driver/uart.h" #include "prio_.h" uint8_t Id = 100; #define printTxtInfo(fmt, ...) \ do \ { \ telnet.printf(Id, LOG_COLOR_I); \ telnet.printf(Id, fmt, ##__VA_ARGS__); \ telnet.printf(Id, LOG_RESET_COLOR "\n"); \ } while (0) // #define printTxtWarn(fmt, ...) \ do \ { \ telnet.printf(Id, LOG_COLOR_W); \ telnet.printf(Id, fmt, ##__VA_ARGS__); \ telnet.printf(Id, LOG_RESET_COLOR "\n"); \ } while (0) // #define printTxtError(fmt, ...) \ do \ { \ telnet.printf(Id, LOG_COLOR_E); \ telnet.printf(Id, fmt, ##__VA_ARGS__); \ telnet.printf(Id, LOG_RESET_COLOR "\n"); \ } while (0) // #define printTxtAbout(fmt, ...) \ do \ { \ telnet.printf(Id, LOG_BOLD(LOG_COLOR_CYAN)); \ telnet.printf(Id, fmt, ##__VA_ARGS__); \ telnet.printf(Id, LOG_RESET_COLOR "\n"); \ } while (0) // #define HELP_WIFI "/help/wifi.txt" #define HELP_SYS "/help/sys.txt" #define HELP_CLI "/help/cli.txt" #define HELP_WIFIRU "/help/wifiru.txt" #define HELP_SYSRU "/help/sysru.txt" #define HELP_CLIRU "/help/cliru.txt" #define HELP_TZ "/help/tz.txt" #define HELP_TZRU "/help/tzru.txt" // Telnet telnet; void sysReboot() { if (network.state != WIFI_STA) { ESP.restart(); } else { web.needReboot = true; } } void GetHelp(const char *path) { File file = FileFS->open(path, "r"); if (file) { while (file.available()) { printTxtWarn("%s", file.readStringUntil('\n').c_str()); } } else { printTxtError(_("Файл помощи %s отсутствует или повреждён!!!"), path); } } // void Telnet::begin() { server.begin(); server.setNoDelay(true); } void Telnet::end() { server.stop(); } void Telnet::emptyClientStream(WiFiClient client) { client.clear(); 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::loop() { if (dev.Stop) return; uint8_t i; 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.accept(); 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.accept().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); } } } 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(uint8_t id, const char *buf) { if (id > MAX_TLN_CLIENTS) { Serial.print(buf); return; } if (clients[id] && clients[id].connected()) { clients[id].print(buf); } } void Telnet::printf(const char *format, ...) { va_list args; va_start(args, format); vsnprintf(cmBuf, sizeof(cmBuf), format, args); va_end(args); for (auto &client : clients) { if (client && client.connected()) { client.print(cmBuf); } } if (strcmp(cmBuf, "> ") == 0) return; // if(strstr(buf,"\n> ")==NULL) Serial.print(buf); char *nl = strstr(cmBuf, "\n> "); if (nl != nullptr) { cmBuf[nl - cmBuf + 1] = '\0'; } Serial.print(cmBuf); } void Telnet::printf(uint8_t id, const char *format, ...) { va_list argptr; va_start(argptr, format); vsnprintf(cmBuf, sizeof(cmBuf), format, argptr); va_end(argptr); if (id > MAX_TLN_CLIENTS) { Serial.print(cmBuf); return; } if (clients[id] && clients[id].connected()) { clients[id].print(cmBuf); } } void Telnet::on_connect(const char *str, uint8_t clientId) { Serial.printf("Telnet: [%d] %s connected\n", clientId, str); printf(clientId, "%s\n%s\n", _("Добро пожаловать в Радиолу-2S3!"), _("(Используйте ^] + q или \"bye\" для отключения.)")); printf(clientId, LOG_COLOR_W "%s." LOG_RESET_COLOR "\n", _("Введите " LOG_COLOR_E "help" LOG_COLOR_W " для получения списка команд")); print(clientId, "> "); } std::string Telnet::currentTZ(uint8_t val) { const char *tzval[] = { "EET-2EEST,M3.5.0/3,M10.5.0/4", "EET-2", "MSK-3", "SAMT-4", "YEKT-5", "OMST-6", "KRAT-7", "IRKT-8", "YAKT-9", "VLAT-10", "SAKT-11", "PETT-12", }; const char *tztxt[] = { _("Киев, София"), _("Калининград"), _("Москва"), _("Самара"), _("Екатеринбург"), _("Омск"), _("Красноярск"), _("Иркутск"), _("Чита"), _("Владивосток"), _("Южно-Сахалинск"), _("Петропавловск-Камчатский"), }; if (val > 0) { return tzval[val]; } size_t i = 0; for (i = 0; i < 12; i++) { if (ee->tz == tzval[i]) { break; } } return tztxt[i]; } void Telnet::mem_info(const char *desc) { size_t freeHeap = heap_caps_get_free_size(MALLOC_CAP_DEFAULT); size_t largestBlock = heap_caps_get_largest_free_block(MALLOC_CAP_DEFAULT); auto fragmentation = (float)(100.0 * (1.0 - ((float)largestBlock / (float)freeHeap))); printf(Id, LOG_BOLD(LOG_COLOR_BLUE) "## ABOUT OF MEMORY ##\n"); if (desc != nullptr) { printf(Id, "INFO: %s\n", desc); } printf(Id, "TOTAL SPIRAM:\t%d\n", heap_caps_get_total_size(MALLOC_CAP_SPIRAM)); printf(Id, "TOTAL INTERNAL:\t%d\n", heap_caps_get_total_size(MALLOC_CAP_INTERNAL)); printf(Id, "TOTAL DMA:\t%d\n", heap_caps_get_total_size(MALLOC_CAP_DMA)); printf(Id, "=======================\n"); printf(Id, "USED SPIRAM:\t%d\n", heap_caps_get_total_size(MALLOC_CAP_SPIRAM) - heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); printf(Id, "USED INTERNAL:\t%d\n", heap_caps_get_total_size(MALLOC_CAP_INTERNAL) - heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); printf(Id, "USED DMA:\t%d\n", heap_caps_get_total_size(MALLOC_CAP_DMA) - heap_caps_get_free_size(MALLOC_CAP_DMA)); printf(Id, "=======================\n"); printf(Id, "FREE SPIRAM:\t%d\n", heap_caps_get_free_size(MALLOC_CAP_SPIRAM)); printf(Id, "FREE INTERNAL:\t%d\n", heap_caps_get_free_size(MALLOC_CAP_INTERNAL)); printf(Id, "FREE DMA:\t%d\n", heap_caps_get_free_size(MALLOC_CAP_DMA)); printf(Id, "=======================\n"); printf(Id, "FREE HEAP:\t%u\n", freeHeap); printf(Id, "Largest free block:\n%u\n", largestBlock); printf(Id, "Fragmentation:\n%.2f%%\n", fragmentation); printf(Id, "#######################" LOG_RESET_COLOR "\n"); } // bool cli_parse(const char *str) { // cli.help if (strcmp(str, "cli.help") == 0) { if (Config->lang == 1) GetHelp(HELP_CLI); else GetHelp(HELP_CLIRU); } // cli.prev else if (strcmp(str, "cli.prev") == 0) { player.prev(); } // cli.next else if (strcmp(str, "cli.next") == 0) { player.next(); } // cli.toggle else if (strcmp(str, "cli.toggle") == 0) { player.toggle(); } // cli.stop else if (strcmp(str, "cli.stop") == 0) { player.stop(); } // cli.check else if (strcmp(str, "cli.check") == 0) { printTxtInfo("Format: %u", player.checkFormat()); } // cli.info else if (!strncmp(str, "cli.info", 8)) { uint8_t info_arg = 0; if (strcmp(str, "cli.info") == 0) { printTxtInfo("play info: %s", eeprom.GetInfo() ? "true" : "false"); } else if (sscanf(str, "cli.info(%hhu)", &info_arg) == 1) // %hhu — unsigned char / uint8_t (1 байт) { if ((info_arg == 0) || (info_arg == 1)) { if (eeprom.GetInfo() != (bool)info_arg) { eeprom.SetInfo((bool)info_arg); eeprom.SaveCurrConfig(); } printTxtWarn("play info: %s", eeprom.GetInfo() ? "true" : "false"); } else { return true; } } else { return true; } } // cli.vol else if (!strncmp(str, "cli.vol", 7)) { int volume_arg = 0; if (!strcmp(str, "cli.vol")) { printTxtInfo("#VOLUME#: %u", eeprom.GetVolume()); } // else if (sscanf(str, "cli.vol(%d)", &volume_arg) == 1) { if (volume_arg < 0) volume_arg = 0; if (volume_arg > 255) volume_arg = 255; player.setVol(volume_arg); } else { return true; } } // cli.maxvol else if (!strncmp(str, "cli.maxvol", 10)) { uint vol_arg = 0; if (!strcmp(str, "cli.maxvol")) { printTxtInfo("Current max vol: %u", player.maxVolume()); printTxtInfo("Valid: 63..254."); } else if (sscanf(str, "cli.maxvol(%u)", &vol_arg) == 1) { if (vol_arg <= 254 && vol_arg >= 63) { printTxtInfo("Set max vol: %d current: %d", vol_arg, player.maxVolume()); player.setVolumeSteps(vol_arg); if (eeprom.GetMaxVolume() != vol_arg) { eeprom.SetMaxVolume(vol_arg); } } else { return true; } } else { return true; } } // cli.list else if (!strncmp(str, "cli.list", 8)) { int list_arg = -1; if (!strcmp(str, "cli.list") || sscanf(str, "cli.list(%d)", &list_arg) == 1) { uint8_t i = 1, j = eeprom.GetTotalSta(); j++; bool onlyOne = false; String info = "INFO"; if (list_arg != -1) // a number specified { if ((list_arg < 1) || (list_arg > j)) { return true; } i = (uint8_t)list_arg; if (i > eeprom.GetTotalSta()) i = 1; j = i + 1; onlyOne = true; String info = "NUM"; } if (!onlyOne) printTxtWarn("##LIST STATIONS##"); for (; i < j; i++) { vTaskDelay(1); printTxtAbout("#CLI.LIST%s#: %3d: \"%s\", logo: \"%s\", url: \"%s\"", info.c_str(), (i), eeprom.GetStaName(i).c_str(), eeprom.GetStaLogo(i).c_str(), eeprom.GetStaUrl(i).c_str()); } if (!onlyOne) printTxtWarn("##LIST END##"); } else { return true; } } // cli.play else if (!strncmp(str, "cli.play", 8)) { int sta_arg = 0; if (!strcmp(str, "cli.play")) { player.play(eeprom.GetID()); } else if (sscanf(str, "cli.play(%d)", &sta_arg) == 1) { if ((sta_arg < 1) || (sta_arg >= eeprom.GetTotalSta())) { sta_arg = 1; } player.play((int16_t)sta_arg); } else { return true; } } else { return true; } return false; } // bool sys_parse(const char *str) { // sys.help if (strcmp(str, "sys.help") == 0) { if (Config->lang == 1) GetHelp(HELP_SYS); else GetHelp(HELP_SYSRU); } // sys.en else if (strcmp(str, "sys.en") == 0) { if (Config->lang == 0) { Config->lang = 1; eeprom.SaveConfig(); sysReboot(); } } // sys.ru else if (strcmp(str, "sys.ru") == 0) { if (Config->lang == 1) { Config->lang = 0; eeprom.SaveConfig(); sysReboot(); } } // sys.theme else if (!strncmp(str, "sys.theme", 9)) { uint8_t theme_arg = 0; // sys.theme if (!strcmp(str, "sys.theme")) { printTxtWarn("Config[\"lcd\"][\"dark\"]: %s", Config->lcd.dark ? "true" : "false"); } else if (sscanf(str, "sys.theme(%hhu)", &theme_arg) == 1) // %hhu — unsigned char / uint8_t (1 байт) { if ((theme_arg == 0) || (theme_arg == 1)) { if ((bool)theme_arg != Config->lcd.dark) { Config->lcd.dark = (bool)theme_arg; eeprom.SaveConfig(); dark_theme = theme_arg; serv.evtSetTheme(); } printTxtWarn("Config[\"lcd\"][\"dark\"]: %s", Config->lcd.dark ? "true" : "false"); } else { return true; } } else { return true; } } // sys.reboot else if (strcmp(str, "sys.reboot") == 0) { sysReboot(); } // sys.mem else if (strcmp(str, "sys.mem") == 0) { telnet.mem_info(); } // sys.config else if (strcmp(str, "sys.config") == 0) { const char i[] = "==============================\n"; telnet.printf(Id, LOG_BOLD(LOG_COLOR_BLUE) "### %s ###\n", _("О РАЗМЕРЕ КОНФИГУРАЦИЙ")); telnet.printf(Id, "Config\t:\t%d\n", sizeof(*Config)); telnet.printf(Id, i); telnet.printf(Id, "Plist\t:\t%d\n", eeprom.GetListBytes()); telnet.printf(Id, i); telnet.printf(Id, "EEPROM\t:\t%d\n", sizeof(*ee)); telnet.printf(Id, i); telnet.printf(Id, "KBD\t:\t%d\n", KBD_BTN.size()); telnet.printf(Id, i); telnet.printf(Id, "IR\t:\t%d\n", IR_KEY.size()); telnet.printf(Id, "##############################" LOG_RESET_COLOR "\n"); } // sys.tz else if (!strncmp(str, "sys.tz", 6)) { int tz_arg = 0; // sys.tz // if (!strcmp(str, "sys.tz")) { telnet.printf(Id, "%s: %s\n", _("Текущий часовой пояс"), telnet.currentTZ().c_str()); if (Config->lang == 1) GetHelp(HELP_TZ); else GetHelp(HELP_TZRU); } // sys.tz(5) else if (sscanf(str, "sys.tz(%d)", &tz_arg) == 1) { if ((tz_arg < 1) || (tz_arg > 12)) { printTxtError("%s", _("Допустимое число от 1 до 12!")); return false; } if (ee->tz != telnet.currentTZ(tz_arg - 1).c_str()) { ee->tz = telnet.currentTZ(tz_arg - 1); printTxtInfo(_("Часовой пояс \"%s\" установлен."), telnet.currentTZ().c_str()); setenv("TZ", ee->tz.c_str(), 1); tzset(); eeprom.SaveEEdata(); if (ee->rts.on) { ee->rts.last = 0LL; eeprom.SaveEEdata(); delay(1000); sysReboot(); } } } else { return true; } } // sys.reset else if (strcmp(str, "sys.reset") == 0) { eeprom.EraseAll(); sysReboot(); } else { return true; } return false; } bool wifi_parse(const char *str) { char ssdi[32] = {0}, pass[64] = {0}; // wifi.help if (strcmp(str, "wifi.help") == 0) { if (Config->lang == 1) GetHelp(HELP_WIFI); else GetHelp(HELP_WIFIRU); } else if (sscanf(str, "wifi.con(%[^,],%[^)]", ssdi, pass) == 2) { // wifi.con(OpenWrt,1234567890) String trimString = String(ssdi); trimString.trim(); ee->ssid = trimString.c_str(); trimString = String(pass); trimString.trim(); ee->pass = trimString.c_str(); // printTxtAbout("#WIFI.CON#"); printTxtAbout("## SSID: %s PASSWORD: %s", ee->ssid.c_str(), ee->pass.c_str()); printTxtAbout("## %s", _("проверьте, всё ли правильно")); printTxtAbout("## %s", _("затем наберите команду sys.reboot")); printTxtAbout("##########"); eeprom.SaveEEdata(); } // wifi.list else if (strcmp(str, "wifi.list") == 0) { printTxtAbout("#WIFI.SCAN#"); int n = WiFi.scanNetworks(); if (n == 0) { printTxtError("no networks found"); return false; } else { for (int i = 0; i < n; ++i) { telnet.printf(Id, "%d", i + 1); telnet.printf(Id, ": "); telnet.printf(Id, "%s", WiFi.SSID(i).c_str()); telnet.printf(Id, " ("); telnet.printf(Id, "%li", WiFi.RSSI(i)); telnet.printf(Id, ")"); telnet.printf(Id, (WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*"); telnet.printf(Id, "\n"); delay(10); } } printTxtAbout("#WIFI.SCAN#"); } // wifi.con else if (strcmp(str, "wifi.con") == 0) { if (!ee->ssid.length()) { printTxtError("WIFI SSID is empty!"); return false; } printTxtAbout("##WIFI.CON##"); printTxtAbout("# SSID: %s #", ee->ssid.c_str()); printTxtAbout("##WIFI.CON##"); } // wifi.station else if (strcmp(str, "wifi.station") == 0) { wifi_config_t conf; esp_wifi_get_config((wifi_interface_t)ESP_IF_WIFI_STA, &conf); printTxtAbout("#WIFI.STATION#\nSSID: %s\nPASSWORD: %s\n##WIFI.STATION#", conf.sta.ssid, conf.sta.password); } // wifi.status else if (strcmp(str, "wifi.status") == 0) { printTxtAbout(_("#СТАТУС WIFI#\nСтатус:\t\t%d\nРежим:\t\t%s\nIP:\t\t%s\nМаска:\t\t%s\nШлюз:\t\t%s\nRSSI:\t\t%d %s\n#СТАТУС WIFI#"), WiFi.status(), WiFi.getMode() == WIFI_STA ? "WIFI_STA" : "WIFI_AP", WiFi.getMode() == WIFI_STA ? WiFi.localIP().toString().c_str() : WiFi.softAPIP().toString().c_str(), WiFi.getMode() == WIFI_STA ? WiFi.subnetMask().toString().c_str() : "255.255.255.0", WiFi.getMode() == WIFI_STA ? WiFi.gatewayIP().toString().c_str() : WiFi.softAPIP().toString().c_str(), WiFi.RSSI(), _("дБм")); } // wifi.rssi else if (strcmp(str, "wifi.rssi") == 0) { printTxtAbout("#WIFI.RSSI#\t%d %s #", WiFi.RSSI(), _("дБм")); } // wifi.discon else if (strcmp(str, "wifi.discon") == 0) { printTxtAbout("#WIFI.DISCON#\tdisconnected...> "); WiFi.disconnect(); } else { return true; } return false; } void Telnet::on_input(const char *str, uint8_t clientId) { bool err = false; Id = clientId; // printTxtError("'%s' : strlen: %d", str, strlen(str)); ////////////////////////////////////////////////////////////////// // bye if (!strcmp(str, "bye") && (clientId != 100)) { if (clients[clientId]) { clients[clientId].stop(); } } // ping else if (!strcmp(str, "ping") && (clientId != 100)) { print(Id, ">pong\n"); } // help else if (strcmp(str, "help") == 0) { const char HELP[] = {"\ ///////////\n\ %s\n\ ///////////\n\ sys.help - %s\n\ cli.help - %s\n\ wifi.help - %s\n\ \n\ Выбор языка (Language selection)\n\ sys.en - English\n\ sys.ru - Русский\n\ "}; printTxtWarn(HELP, _("ПОМОЩЬ"), _("список системных команд"), _("список команд аудиоплеера"), _("список команд Wi-Fi")); if (Id < MAX_TLN_CLIENTS) { printTxtWarn("bye - %s", _("Отключение Telnet-клиента от Радиолы-2S3")); printTxtWarn("ping - %s\n", _("Пинг Telnet-сервера Радиолы-2S3")); } } ///////////////////////// * SYS * ////////////////////////////////////////// else if (strncmp(str, "sys.", 4) == 0) { err = sys_parse(str); } ///////////////////////// * WIFI * ////////////////////////////////////////// else if (strncmp(str, "wifi.", 5) == 0) { err = wifi_parse(str); } ///////////////////////// * CLI * /////////////////////////////////////////// else if (strncmp(str, "cli.", 4) == 0) { err = cli_parse(str); } else if (!strlen(str)) { err = false; } else { err = true; } // if (err) { printTxtError("'%s' : %s!", str, _("Неизвестная команда или ошибка в параметре")); } print(Id, "> "); } // void Telnet::StartTask() { if (xTaskCreatePinnedToCore( &Telnet::taskWrapper, NAME_UART, STACK_UART, this, PRIO_UART, nullptr /* &pxCreatedTask */, CPU_UART) != pdPASS) { ESP_LOGE("UART", "ERROR creating task! Out of memory?"); } } void Telnet::taskWrapper(void *param) { Telnet *runner = static_cast<Telnet *>(param); runner->uart_task(); } void Telnet::uart_task() { char buf[255] = {""}; int len = 0; while (true) { for (len = 0; len < (int)sizeof(buf); len++) buf[len] = 0; len = 0; char c = 0; while (true) { if (uart_read_bytes(UART_NUM_0, &c, 1, 1)) { // uart_write_bytes(UART_NUM_0, (char *)&c, 1); Serial.print(c); if ((c == '\r') || (c == '\n')) { break; } buf[len] = c; len++; if (len == sizeof(buf) - 1) len = 0; } } if (!dev.Stop) telnet.on_input(buf, 100); delay(1); } vTaskDelete(nullptr); }