static const char *TAG = "blinky"; #include #include #include #include #include #include "wifi.hpp" #include "http_serv.hpp" #include "ota.hpp" #include "device.hpp" #include "utils.hpp" #include "leds.hpp" #include "gamma.hpp" #include "presets.hpp" #include "utils.hpp" #include "spi_leds.hpp" //#include "screen_leds.hpp" #include "rmt_leds.hpp" // Dummy FS static void start_filesystem() { ESP_LOGI(TAG, "Initializing filesystem"); esp_vfs_spiffs_conf_t conf = { .base_path = "/spiffs", .partition_label = NULL, .max_files = 5, .format_if_mount_failed = true }; // Use settings defined above to initialize and mount SPIFFS filesystem. // Note: esp_vfs_spiffs_register is an all-in-one convenience function. ESP_ERROR_CHECK(esp_vfs_spiffs_register(&conf)); } #include #include #include #include static void log_dir(const std::string &path) { struct stat st; int err = stat(path.c_str(), &st); if (err < 0) { ESP_LOGE(TAG, "%s %d", path.c_str(), errno); } else { ESP_LOGI(TAG, "%s %ld", path.c_str(), st.st_size); } DIR *dir; if ((dir = opendir(path.c_str())) == NULL) { //ESP_LOGE(TAG, "Failed to open %s", path.c_str()); return; } struct dirent *de; while ((de = readdir(dir)) != NULL) { log_dir(path + "/" + de->d_name); } closedir(dir); } constexpr char config_path[] = "/spiffs/blinky.json"; //#define PROFILE_PERF //#define PROFILE_MEM // Entry Point extern "C" void app_main(void) { // Initialize NVS for WiFi Data esp_err_t ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK(ret); // WiFi config_wifi(); // OTA Server start_ota_serv(); // Flash Filesystem start_filesystem(); // TODO: Scrape this out log_dir("/spiffs"); Device device("blinky3"); cJSON *json = cJSON_Parse(read_file(config_path).c_str()); if (json) { ESP_LOGI(TAG, "Setting config"); device.set_json_config(json); cJSON_Delete(json); } // TerminalLEDs() // ScreenLEDs() // SPI_LEDs(39); // RMT_LEDs(GPIO_NUM_17); LEDStrip *LEDs = new RMT_LEDs(17); int frequency = 30; while (true) { Gamma::reload(); // Trash the old preset in case we can't find the set effect LEDs->setPattern(NULL); Presets::reload(); ESP_LOGI(TAG, "Configuring LEDs"); device.lock(); const std::string effect = device.get_effect(); bool is_on = device.is_on(); int length = device.get_strip_length(); int brightness = device.get_brightness(); cJSON *json = device.make_json_config_locked(); device.unlock(); LEDs->setBrightness(brightness); if (length != LEDs->getLength()) { LEDs->setLength(length); } const Pattern *pattern = Presets::find(effect); if (pattern) { ESP_LOGI(TAG, "Setting pattern '%s'", effect.c_str()); LEDs->setPattern(pattern); char *config = cJSON_PrintUnformatted(json); write_file(config_path, config); ESP_LOGI(TAG, "Saved config"); cJSON_free(config); } else { ESP_LOGW(TAG, "Could not find pattern '%s'", effect.c_str()); pattern = LEDs->getPattern(); } cJSON_Delete(json); if (length <= 0 || !pattern || !is_on) { if (!is_on) ESP_LOGI(TAG, "Device off"); if (length <= 0) ESP_LOGW(TAG, "No LEDs configured"); if (!pattern) ESP_LOGW(TAG, "No LED pattern set"); ESP_LOGI(TAG, "Waiting for new config"); LEDs->clear(); device.wait(); } else { ESP_LOGI(TAG, "Starting animation"); int period_us = 1000000 / frequency; int64_t target_us = time_us(); int64_t profile_start = target_us; #ifdef PROFILE_PERF int64_t total_delay_us = 0; int n_delays = 0; int64_t after_sleep_us = 0; int n_processes = 0; int64_t total_process_us = 0; #endif // PROFILE_PERF while (true) { int64_t now = time_us(); #ifdef PROFILE_PERF if (after_sleep_us > 0) { total_process_us += now - after_sleep_us; ++n_processes; } #endif // PROFILE_PERF if (now - profile_start >= 5000000) { #ifdef PROFILE_MEM heap_caps_print_heap_info(MALLOC_CAP_8BIT); #endif // PROFILE_MEM #ifdef PROFILE_PERF ESP_LOGI(TAG, "Average delay: %f ms", (double)total_delay_us / (n_delays * 1000)); total_delay_us = 0; n_delays = 0; ESP_LOGI(TAG, "Average processing: %f us", (double)total_process_us / n_processes); total_process_us = 0; n_processes = 0; #endif // PROFILE_PERF profile_start = now; } int64_t delay_us = target_us - now; if (delay_us < 0) delay_us = 0; if (device.wait(delay_us / 1000)) { // If the semaphore is set, go back to top of outer loop break; } target_us = now + delay_us + period_us; #ifdef PROFILE_PERF after_sleep_us = time_us(); total_delay_us += (after_sleep_us - now); ++n_delays; #endif // PROFILE_PERF LEDs->step(); LEDs->show(); } } } // Spin }