/
singlwolf
/
Radiola-2S3
Обзор
Документация
Войти
/
singlwolf
/
Radiola-2S3
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/main.cpp
425 строк
12 KB
SinglWolf
v1.0.6
15 апр 2026, 05:04
15 апр 2026, 05:04
55b96c5
Код
Авторство
О чём код?
#include "main.h" #include <fs_system.h> #include "filesystem.h" #include "eeStorage.h" #include "hardware.h" #include "telnet.h" #include "controls.h" #include "net_work.h" #include "web_server.h" #include "services.h" #include "audioVS.h" #include "ui.h" #include "lv_i18n.h" // #include <TFT_eSPI.h> #define fonts lovyan_fonts #include <LovyanGFX.hpp> #include <lgfx/v1/panel/Panel_ILI9341.hpp> #undef fonts // --- КОНФИГ ПИНОВ S3 --- #define LCD_HOST SPI3_HOST // VS1053 на FSPI (SPI2), дисплей на SPI3 #define _SCLK 40 #define _MOSI 41 #define _MISO -1 #define _DC 42 #define _CS 1 #define _RST -1 // Ресет аппаратно на плате #define LCD_W 320 #define LCD_H 240 extern "C" { void *psram_malloc(size_t size) { return heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } void *psram_realloc(void *ptr, size_t size) { return heap_caps_realloc(ptr, size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); } void psram_free(void *ptr) { heap_caps_free(ptr); } void lv_mem_init(void) { // Ничего не делаем, так как используем системную кучу PSRAM } } // --- КЛАСС ДРАЙВЕРА LGFX --- class LGFX_ : public lgfx::LGFX_Device { public: lgfx::Bus_SPI _bus; lgfx::Panel_ILI9341 _panel; // Конструктор пустой, ничего не настраивает LGFX_() {} }; LGFX_ tft; // --- FLUSH CALLBACK --- void my_disp_flush(lv_display_t *d, const lv_area_t *area, uint8_t *px) { int32_t w = lv_area_get_width(area); int32_t h = lv_area_get_height(area); tft.pushImage(area->x1, area->y1, w, h, (lgfx::rgb565_t *)px); lv_display_flush_ready(d); } // void dma_disp_flush(lv_display_t *d, const lv_area_t *area, uint8_t *px) { if (tft.getStartCount() > 0) { tft.waitDMA(); } tft.setAddrWindow(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area)); tft.pushImageDMA(area->x1, area->y1, lv_area_get_width(area), lv_area_get_height(area), (lgfx::rgb565_t *)px); lv_display_flush_ready(d); } #define txtPrintInfo(fmt, ...) \ do \ { \ printf(LOG_COLOR_I); \ printf(fmt, ##__VA_ARGS__); \ printf(LOG_RESET_COLOR "\n"); \ } while (0) // #define txtPrintWarn(fmt, ...) \ do \ { \ printf(LOG_COLOR_W); \ printf(fmt, ##__VA_ARGS__); \ printf(LOG_RESET_COLOR "\n"); \ } while (0) // #define txtPrintError(fmt, ...) \ do \ { \ printf(LOG_COLOR_E); \ printf(fmt, ##__VA_ARGS__); \ printf(LOG_RESET_COLOR "\n"); \ } while (0) // TFT_eSPI tft = TFT_eSPI(TFT_WIDTH, TFT_HEIGHT); /* TFT instance */ // TFT_eSPI tft = TFT_eSPI(); /* TFT instance */ #if LV_USE_LOG != 0 /* Serial debugging */ void my_print(lv_log_level_t level, const char *buf) { LV_UNUSED(level); Serial.printf(buf); Serial.flush(); } #endif void SetRotat() { tft.setRotation((Config->lcd.rotat) ? 1 : 3); /* Landscape orientation, flipped */ lv_obj_invalidate(objects.main); } /* Display flushing void my_disp_flush(lv_display_t *disp, const lv_area_t *area, uint8_t *pixelmap) { uint32_t w = (area->x2 - area->x1 + 1); uint32_t h = (area->y2 - area->y1 + 1); #if (LV_COLOR_16_SWAP) { size_t len = lv_area_get_size(area); lv_draw_sw_rgb565_swap(pixelmap, len); } #endif tft.startWrite(); tft.setAddrWindow(area->x1, area->y1, w, h); tft.pushColors((uint16_t *)pixelmap, w * h, true); tft.endWrite(); lv_display_flush_ready(disp); } */ SET_LOOP_TASK_STACK_SIZE(16 * 1024); // 16KB /*Set tick routine needed for LVGL internal timings*/ static uint32_t my_tick_get_cb() { return millis(); } void listDir(fs::FS &fs, const char *dirname, uint8_t levels) { File appendfile; Serial.printf("Listing directory: %s\n", dirname); if (!fs.exists("/audio/info.txt")) { appendfile = fs.open("/audio/info.txt", FILE_APPEND); } File root = fs.open(dirname); if (!root) { Serial.println("Failed to open directory"); return; } if (!root.isDirectory()) { Serial.println("Not a directory"); return; } File file = root.openNextFile(); while (file) { String filename = file.name(); if (file.isDirectory()) { Serial.print(" DIR : "); Serial.print(filename); time_t t = file.getLastWrite(); struct tm *tmstruct = localtime(&t); Serial.printf( " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); if (levels) { listDir(fs, file.path(), levels - 1); } } else { if (appendfile) { if (filename != "info.txt") { Serial.print(" FILE: "); Serial.print(filename); Serial.print(" SIZE: "); Serial.print(file.size()); time_t t = file.getLastWrite(); struct tm *tmstruct = localtime(&t); Serial.printf( " LAST WRITE: %d-%02d-%02d %02d:%02d:%02d\n", (tmstruct->tm_year) + 1900, (tmstruct->tm_mon) + 1, tmstruct->tm_mday, tmstruct->tm_hour, tmstruct->tm_min, tmstruct->tm_sec); appendfile.println(filename); printf("%s appended successfully\n", filename.c_str()); } } } file = root.openNextFile(); } if (appendfile) appendfile.close(); } // extern "C" void app_main() { initArduino(); Serial.begin(115200); /* prepare for possible serial debug */ // Version infos txtPrintInfo(PROGNAME " Release %d.%d.%d", VER_MAJOR, VER_MINOR, VER_PATH); txtPrintInfo("SDK : %d.%d.%d", ESP_IDF_VERSION_MAJOR, ESP_IDF_VERSION_MINOR, ESP_IDF_VERSION_PATCH); txtPrintInfo("Core: %s", ESP.getCoreVersion()); // Init LVGL lv_init(); #if LV_USE_LOG != 0 // #include "../lib/lvgl/src/misc/lv_log.h" lv_log_register_print_cb(my_print); /* register print function for debugging */ #endif txtPrintInfo("LVGL: %d.%d.%d", lv_version_major(), lv_version_minor(), lv_version_patch()); // Init filesystem sdog.begin(); eeprom.begin(sdog.init_fs_system()); init_filesystem(); /*Set a tick source so that LVGL will know how much time elapsed. */ lv_tick_set_cb(my_tick_get_cb); lv_i18n_init(lv_i18n_language_pack); if (Config->lang == 0) { lv_i18n_set_locale("ru"); } else if (Config->lang == 1) { lv_i18n_set_locale("en"); } dev.init(); // listDir(*FileFS, "/audio", 0); #define BUF_SIZE (LCD_W * LCD_H * sizeof(lv_color16_t)) // lv_color16_t *buf1 = (lv_color16_t *)heap_caps_malloc(BUF_SIZE, MALLOC_CAP_DMA); // auto buf1 = (lv_color16_t *)heap_caps_malloc(BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); // auto buf2 = (lv_color16_t *)heap_caps_malloc(BUF_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA); auto *buf1 = (lv_color16_t *)heap_caps_aligned_alloc(32, BUF_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_DMA); // auto *buf2 = (lv_color16_t *)heap_caps_aligned_alloc(32, BUF_SIZE, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); assert(buf1 != nullptr); // assert(buf2 != nullptr); static lv_disp_t *disp; disp = lv_display_create(320, 240); lv_timer_set_period(disp->refr_timer, 30); // Set the DPI (dot per inch) of the display. dpi = sqrt(hor_res^2 + ver_res^2) / diagonal" // int32_t dpi = sqrt(pow(LCD_W, 2) + pow(LCD_H, 2)) / Config->lcd.inch; auto dpi = (int32_t)(hypotf((float)LCD_W, (float)LCD_H) / Config->lcd.inch); // txtPrintInfo("DPI size: %li", dpi); lv_display_set_dpi(disp, dpi); lv_display_set_flush_cb(disp, dma_disp_flush); // lv_display_set_buffers(disp, buf1, NULL, BUF_SIZE, LV_DISPLAY_RENDER_MODE_FULL); // lv_display_set_antialiasing(disp, true); // lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565); // lv_display_set_buffers(disp, buf1, buf2, BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL); lv_display_set_buffers(disp, buf1, nullptr, BUF_SIZE, LV_DISPLAY_RENDER_MODE_PARTIAL); /*Create a main screen*/ ui_font_main14 = lv_binfont_create("L:/fonts/main14.bin"); ui_font_main14->fallback = &lv_font_montserrat_14; lv_obj_t *ui_Main = lv_obj_create(lv_scr_act()); lv_obj_set_style_text_font(ui_Main, ui_font_main14, 0); lv_obj_remove_flag(ui_Main, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_scrollbar_mode(ui_Main, LV_SCROLLBAR_MODE_OFF); lv_obj_set_size(ui_Main, LV_HOR_RES, LV_VER_RES); lv_obj_align(ui_Main, LV_ALIGN_CENTER, 0, 0); lv_obj_t *LOGO = lv_img_create(ui_Main); lv_image_set_src(LOGO, "L:/img/boot.png"); // img_title lv_obj_t *obj = lv_image_create(LOGO); if (Config->lang == 0) { lv_obj_set_pos(obj, 0, 16); lv_image_set_src(obj, "L:/img/ru.png"); } else { lv_obj_set_pos(obj, 0, 19); lv_image_set_src(obj, "L:/img/en.png"); } lv_obj_set_size(obj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_set_style_align(obj, LV_ALIGN_CENTER, 0); lv_obj_align(LOGO, LV_ALIGN_CENTER, 0, 0); /* TFT init */ // Настройка шины auto bus_cfg = tft._bus.config(); bus_cfg.spi_host = LCD_HOST; bus_cfg.freq_write = APB_CLK_FREQ / Config->lcd.div; bus_cfg.dma_channel = SPI_DMA_CH_AUTO; // bus_cfg.dma_channel = SPI_DMA_DISABLED; bus_cfg.pin_sclk = _SCLK; bus_cfg.pin_mosi = _MOSI; bus_cfg.pin_dc = _DC; tft._bus.config(bus_cfg); tft._panel.setBus(&tft._bus); // Настройка панели auto panel_cfg = tft._panel.config(); panel_cfg.pin_cs = _CS; panel_cfg.pin_rst = _RST; panel_cfg.rgb_order = false; panel_cfg.panel_width = 240; panel_cfg.panel_height = 320; tft._panel.config(panel_cfg); // Инициализация tft.setPanel(&tft._panel); tft.init(); // gpio_set_drive_capability((gpio_num_t)40, GPIO_DRIVE_CAP_0); // SCLK // gpio_set_drive_capability((gpio_num_t)41, GPIO_DRIVE_CAP_0); // MOSI // gpio_set_drive_capability((gpio_num_t)42, GPIO_DRIVE_CAP_1); // DC (можно чуть мощнее) tft.setRotation((Config->lcd.rotat) ? 1 : 3); // Landscape orientation, flipped dev.SetBRG(); lv_refr_now(nullptr); network.begin(); while (!network.getReady()) { delay(1); } if (network.state != WIFI_AP) { lv_obj_delete(ui_Main); lv_binfont_destroy(ui_font_main14); ui_font_main14 = nullptr; // lv_obj_set_style_bg_color(lv_scr_act(), lv_color_black(), LV_PART_MAIN); lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_COVER, LV_PART_MAIN); lv_obj_invalidate(lv_scr_act()); lv_refr_now(nullptr); } initControls(); ui_init(); serv.set_custom_theme(); lv_bar_set_value(objects.volume_bar, eeprom.GetVolume(), LV_ANIM_OFF); lv_obj_add_flag(objects.warn_label, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(objects.mute_label, LV_OBJ_FLAG_HIDDEN); if (!Config->alarm.on) lv_obj_add_flag(objects.alarm_on_label, LV_OBJ_FLAG_HIDDEN); if (!Config->sleep.on) lv_obj_add_flag(objects.sleep_on_label, LV_OBJ_FLAG_HIDDEN); // lv_obj_add_flag(objects.turn_on_label, LV_OBJ_FLAG_HIDDEN); if (!Config->sd) lv_obj_add_flag(objects.sd_card_label, LV_OBJ_FLAG_HIDDEN); if (!Config->led.on) lv_obj_add_flag(objects.brg_label, LV_OBJ_FLAG_HIDDEN); serv.SetupVUmeter(); // telnet.mem_info("перед serv.StartTask"); serv.StartTask(); delay(1); // telnet.mem_info("перед telnet.StartTask"); telnet.StartTask(); delay(1); // telnet.mem_info("перед web.StartTask"); web.StartTask(); delay(1); // telnet.mem_info("после web.StartTask"); // delay(1); if (network.state != WIFI_AP) { lv_label_set_text(objects.ip_address_label, network.getIp()); serv.evtUpateRssi(); lv_obj_remove_flag(objects.play_info, LV_OBJ_FLAG_HIDDEN); // lv_obj_add_flag(ui_LOGO, LV_OBJ_FLAG_HIDDEN); player.begin(); // lv_obj_add_event_cb(objects.roller_playlist, eventPlayList, LV_EVENT_ALL, NULL); if (eeprom.GetAutoPlay()) { player.play(eeprom.GetID()); } else { player.setStation(); serv.evtSetWait(WAIT_COMM); } } // telnet.mem_info(); dev.StartTask(); dev.beep(20); // бип... Serial.println("Setup done"); printf(LOG_COLOR_W "%s. %s" LOG_RESET_COLOR "\n", _("ГОТОВ"), _("Введите " LOG_COLOR_E "help" LOG_COLOR_W " для получения списка команд")); } // END