From 81a8da24a0a868973218cb749bca4b432260be72 Mon Sep 17 00:00:00 2001 From: Thaddeus Hughes Date: Sat, 27 Dec 2025 00:01:59 -0600 Subject: [PATCH] integrating web stuff into SC-F001 LFG --- main/CMakeLists.txt | 17 +- main/control_fsm.c | 8 +- main/filemgmt.c | 328 --------- main/filemgmt.h | 33 - main/ftp.c | 1413 ------------------------------------ main/ftp.h | 214 ------ main/landingpage.html | 159 ++++ main/main.c | 6 +- main/power_mgmt.c | 22 +- main/rf.h | 30 - main/{rf.c => rf_433.c} | 32 +- main/rf_433.h | 30 + main/rtc.c | 2 - main/solar.c | 6 +- main/storage.c | 4 +- main/storage.h | 4 +- main/uart_comms.c | 14 +- main/webpage.h | 3 + main/webpage_compile.py | 31 + main/webpage_minified.html | 28 + main/webserver.c | 333 +++++++++ main/webserver.h | 3 + sdkconfig | 16 +- sdkconfig.old | 10 +- 24 files changed, 665 insertions(+), 2081 deletions(-) delete mode 100644 main/filemgmt.c delete mode 100644 main/filemgmt.h delete mode 100644 main/ftp.c delete mode 100644 main/ftp.h create mode 100644 main/landingpage.html delete mode 100644 main/rf.h rename main/{rf.c => rf_433.c} (92%) create mode 100644 main/rf_433.h create mode 100644 main/webpage.h create mode 100644 main/webpage_compile.py create mode 100644 main/webpage_minified.html create mode 100644 main/webserver.c create mode 100644 main/webserver.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 181c5cd..2e96531 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,9 +2,24 @@ # for more information about component CMakeLists.txt files. idf_component_register( - SRCS main.c i2c.c rtc.c storage.c uart_comms.c control_fsm.c power_mgmt.c rf.c rtc.c sensors.c solar.c # list the source files of this component + SRCS main.c i2c.c rtc.c storage.c uart_comms.c control_fsm.c power_mgmt.c rf_433.c rtc.c sensors.c solar.c webserver.c # list the source files of this component INCLUDE_DIRS # optional, add here public include directories PRIV_INCLUDE_DIRS # optional, add here private include directories REQUIRES # optional, list the public requirements (component names) PRIV_REQUIRES # optional, list the private requirements ) + + +if(NOT CMAKE_BUILD_EARLY_EXPANSION) + add_custom_command( + OUTPUT ${COMPONENT_DIR}/webpage.h + COMMAND python ${COMPONENT_DIR}/webpage_compile.py + DEPENDS ${COMPONENT_DIR}/landingpage.html ${COMPONENT_DIR}/webpage_compile.py + WORKING_DIRECTORY ${COMPONENT_DIR} + COMMENT "Generating webpage.h from landingpage.html" + VERBATIM + ) + + add_custom_target(generate_webpage_h DEPENDS ${COMPONENT_DIR}/webpage.h) + add_dependencies(${COMPONENT_LIB} generate_webpage_h) +endif() \ No newline at end of file diff --git a/main/control_fsm.c b/main/control_fsm.c index 3904b73..26bddaa 100644 --- a/main/control_fsm.c +++ b/main/control_fsm.c @@ -80,7 +80,7 @@ static inline bool timer_done() { return current_time >= timer_end; } void pulseOverride(relay_t relay) { if (current_state == STATE_IDLE) - override_times[relay] = current_time + get_param(PARAM_RF_PULSE_LENGTH).u64; + override_times[relay] = current_time + get_param_value_t(PARAM_RF_PULSE_LENGTH).u64; } /*void fsm_begin_auto_move() { @@ -120,9 +120,9 @@ int8_t fsm_get_current_progress(int8_t denominator) { } -#define JACK_TIME get_param(PARAM_JACK_MSPI ).u32 * 1000 * get_param(PARAM_JACK_DIST ).u8 -#define DRIVE_TIME get_param(PARAM_DRIVE_MSPF).u32 * 1000 * get_param(PARAM_DRIVE_DIST).u8 -#define DRIVE_DIST get_param(PARAM_DRIVE_TPDF).u32 / 10 * get_param(PARAM_DRIVE_DIST).u8 +#define JACK_TIME get_param_value_t(PARAM_JACK_MSPI ).u32 * 1000 * get_param_value_t(PARAM_JACK_DIST ).u8 +#define DRIVE_TIME get_param_value_t(PARAM_DRIVE_MSPF).u32 * 1000 * get_param_value_t(PARAM_DRIVE_DIST).u8 +#define DRIVE_DIST get_param_value_t(PARAM_DRIVE_TPDF).u32 / 10 * get_param_value_t(PARAM_DRIVE_DIST).u8 void control_task(void *param) { esp_task_wdt_add(NULL); diff --git a/main/filemgmt.c b/main/filemgmt.c deleted file mode 100644 index 1e9ff4c..0000000 --- a/main/filemgmt.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * filemgmt.c - * - * Created on: Nov 20, 2025 - * Author: Thad - */ - -#include "filemgmt.h" -#include "endian.h" -#include "esp_log.h" -#include -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_system.h" -#include "esp_mac.h" // for MACSTR -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_vfs.h" -#include "nvs_flash.h" -#include "esp_vfs_fat.h" -#include "esp_littlefs.h" -#include "ftp.h" -#include "control_fsm.h" -#include "power_mgmt.h" -#include "rtc.h" -#include "sensors.h" - - -const char* partition_label = "storage"; -const char *TAG = "MAIN"; -const char *mount_point = "/root"; - -RTC_DATA_ATTR bool find_new_filename = true; - -void start_new_log_file() { - find_new_filename = true; -} - -EventGroupHandle_t xEventTask; -int FTP_TASK_FINISH_BIT = BIT2; - -esp_err_t start_filesystem() { - ESP_LOGI(TAG, "Initializing LittleFS on Builtin SPI Flash Memory"); - - esp_vfs_littlefs_conf_t conf = { - .base_path = mount_point, - .partition_label = partition_label, - .format_if_mount_failed = true, - .dont_mount = false, - }; - - // Use settings defined above to initialize and mount LittleFS filesystem. - // Note: esp_vfs_littlefs_register is an all-in-one convenience function. - esp_err_t ret = esp_vfs_littlefs_register(&conf); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - ESP_LOGE(TAG, "Failed to mount or format filesystem"); - } else if (ret == ESP_ERR_NOT_FOUND) { - ESP_LOGE(TAG, "Failed to find LittleFS partition"); - } else { - ESP_LOGE(TAG, "Failed to initialize LittleFS (%s)", esp_err_to_name(ret)); - } - return ret; - } - - size_t total = 0, used = 0; - ret = esp_littlefs_info(conf.partition_label, &total, &used); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(ret)); - //esp_littlefs_format(conf.partition_label); - return ret; - } - ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); - ESP_LOGI(TAG, "Mount LittleFS on %s", mount_point); - return ret; -} - - -static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) -{ - if (event_id == WIFI_EVENT_AP_STACONNECTED) { - wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; - ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", MAC2STR(event->mac), event->aid); - } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { - wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; - ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid); - } -} - -extern void ftp_task (void *pvParameters); -// Start WiFi AP and FTP Server -// filemgmt.c -esp_err_t start_ftp_server(void) -{ - ESP_LOGI("FTP", "START"); - - close_current_log(); - - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - - // THIS IS THE CORRECT WAY – recreate the default AP netif every time - esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, - ESP_EVENT_ANY_ID, - &wifi_event_handler, - NULL, - NULL)); - - wifi_config_t wifi_config = { - .ap = { - .ssid = ESP_WIFI_AP_SSID, - .ssid_len = strlen(ESP_WIFI_AP_SSID), - .password = ESP_WIFI_AP_PASSWORD, - .max_connection = ESP_WIFI_AP_N_CONNECTIONS, - .authmode = strlen(ESP_WIFI_AP_PASSWORD) ? WIFI_AUTH_WPA_WPA2_PSK : WIFI_AUTH_OPEN - }, - }; - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - ESP_LOGI(TAG, "WiFi AP started – IP 192.168.4.1"); - - xEventTask = xEventGroupCreate(); - xTaskCreate(ftp_task, "FTP", 1024*6, NULL, 2, NULL); - - return ESP_OK; -} - -void stop_ftp_server(void) -{ - ESP_LOGI("FTP", "OFF"); - - ftp_terminate(); - vTaskDelay(pdMS_TO_TICKS(500)); // give task time to die - - esp_wifi_stop(); - esp_wifi_deinit(); - - // This single call does everything correctly: - // Destroy default Wi-Fi interface (handles AP) - esp_netif_t *ap_netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF"); - if (ap_netif) { - esp_netif_destroy_default_wifi(ap_netif); // Correct API for AP - } - - esp_event_loop_delete_default(); - esp_netif_deinit(); - - ESP_LOGI("FTP", "WiFi + FTP completely stopped – safe to restart"); -} - - - -void stop_filesystem() { - esp_vfs_littlefs_unregister(partition_label); - ESP_LOGI(TAG, "LittleFS unmounted"); -} - -#define LOG_ENTRY_SIZE 32 -#define MAX_LOG_FILES 999 // rotate after N files -#define ENTRIES_PER_FILE 8000 // ~256 KB per file → safe - -static FILE* logfile = NULL; -RTC_DATA_ATTR char current_log_path[32] = "/root/log000.bin"; -static uint32_t current_entry_count = 0; - -// Helper: close current file safely -void close_current_log(void) -{ - if (logfile) { - fflush(logfile); - fsync(fileno(logfile)); // CRITICAL: forces write to flash - fclose(logfile); - logfile = NULL; - current_entry_count = 0; - } -} - -// Helper: open next log file (rotates 000→009) -bool open_next_log_file(void) -{ - close_current_log(); - - if (find_new_filename) { - // Find next filename - for (int i = 0; i < MAX_LOG_FILES; i++) { - snprintf(current_log_path, sizeof(current_log_path), "/root/log%03d.bin", i); - struct stat st; - if (stat(current_log_path, &st) != 0) { - break; // file doesn't exist → use this one - } - if (i == MAX_LOG_FILES-1) { - ESP_LOGE("FILE", "RAN OUT OF FILES, GOING TO MAX"); - // All files exist → overwrite oldest (log000.bin) - strcpy(current_log_path, "/root/log999.bin"); - } - } - - logfile = fopen(current_log_path, "ab"); // binary append - if (!logfile) { - ESP_LOGE("FILE", "Failed to open %s", current_log_path); - return false; - } - - // Optional: truncate if too big (safety) - fseek(logfile, 0, SEEK_END); - if (ftell(logfile) > ENTRIES_PER_FILE * LOG_ENTRY_SIZE) { - fclose(logfile); - logfile = fopen(current_log_path, "wb"); // truncate - } - - find_new_filename = false; - } else { - logfile = fopen(current_log_path, "ab"); - } - - - - ESP_LOGI("FILE", "Logging to %s", current_log_path); - return true; -} - -void file_log() { - if (!logfile && !open_next_log_file()) { - return; - } - - char entry[LOG_ENTRY_SIZE] = {0}; - entry[0] = LOG_ENTRY_SIZE; - - // pack 64-bit timestamp into bytes 1-8 - uint64_t be_timestamp = htobe64(rtc_time_ms()); - memcpy(&entry[1], &be_timestamp, 8); - - // pack 32-bit voltages/currents into bytes 9-24 - int32_t be_voltage = htobe32(get_battery_mV()); - memcpy(&entry[9], &be_voltage, 4); - int32_t be_current1 = htobe32(get_bridge_mA(BRIDGE_DRIVE)); - memcpy(&entry[13], &be_current1, 4); - int32_t be_current2 = htobe32(get_bridge_mA(BRIDGE_JACK)); - memcpy(&entry[17], &be_current2, 4); - int32_t be_current3 = htobe32(get_bridge_mA(BRIDGE_AUX)); - memcpy(&entry[21], &be_current3, 4); - - - int32_t be_counter = htobe32(get_sensor_counter(SENSOR_DRIVE)); - memcpy(&entry[25], &be_counter, 4); - - entry[29] = get_sensor(SENSOR_DRIVE); - entry[30] = get_sensor(SENSOR_JACK); - entry[31] = fsm_get_state(); - - ESP_LOGI("FILE", "LOGGING TO %s: 0x %02x %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x %02x %02x", current_log_path, - entry[0], - entry[1], - entry[2], - entry[3], - entry[4], - entry[5], - entry[6], - entry[7], - entry[8], - entry[9], - entry[10], - entry[11], - entry[12], - entry[13], - entry[14], - entry[15], - entry[16], - entry[17], - entry[18], - entry[19], - entry[20], - entry[21], - entry[22], - entry[23], - entry[24], - entry[25], - entry[26], - entry[27], - entry[28], - entry[29], - entry[30], - entry[31]); - - - /* SEND TO FILE */ - - size_t written = fwrite(entry, 1, LOG_ENTRY_SIZE, logfile); - if (written != LOG_ENTRY_SIZE) { - ESP_LOGE("FILE", "Partial write! Closing file."); - close_current_log(); - return; - } - - current_entry_count++; - - // Periodic flush (every 50–100 entries) + full sync every 500 - if (current_entry_count % 100 == 0) { - fflush(logfile); - } - if (current_entry_count % 500 == 0) { - fsync(fileno(logfile)); - } - - // Rotate if getting large - if (current_entry_count >= ENTRIES_PER_FILE) { - ESP_LOGI("FILE", "Rotating log file"); - open_next_log_file(); - } -} - diff --git a/main/filemgmt.h b/main/filemgmt.h deleted file mode 100644 index 816a71a..0000000 --- a/main/filemgmt.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * filemgmt.h - * - * Created on: Nov 20, 2025 - * Author: Thad - */ - -#ifndef MAIN_FILEMGMT_H_ -#define MAIN_FILEMGMT_H_ - -#include "esp_err.h" - - -#define ESP_WIFI_AP_SSID "stockcropper" -#define ESP_WIFI_AP_PASSWORD "" -#define ESP_WIFI_AP_N_CONNECTIONS 4 - -// Open the filesystem (e.g. for logging) -esp_err_t start_filesystem(); - -// Start WiFi AP and FTP Server -esp_err_t start_ftp_server(); - -void stop_filesystem(); -void stop_ftp_server(); - -void file_log(); - -void close_current_log(); - -void start_new_log_file(); - -#endif /* MAIN_FILEMGMT_H_ */ diff --git a/main/ftp.c b/main/ftp.c deleted file mode 100644 index b2ad4f0..0000000 --- a/main/ftp.c +++ /dev/null @@ -1,1413 +0,0 @@ -/* - * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo - * - * The MIT License (MIT) - * - * Copyright (c) 2018 LoBo (https://github.com/loboris) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* - * This file is based on 'ftp' from Pycom Limited. - * - * Author: LoBo, loboris@gmail.com - * Copyright (c) 2017, LoBo - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "dirent.h" -#include "esp_system.h" -//#include "esp_spi_flash.h" // ESP-IDF V4 -#include "esp_flash.h" // ESP-IDF V5 -#include "nvs_flash.h" -#include "esp_log.h" - -#include "esp_wifi.h" - -#include "lwip/sockets.h" -#include "lwip/dns.h" -#include "lwip/netdb.h" - -//#include "freertos/semphr.h" - -#include "ftp.h" - -extern int FTP_TASK_FINISH_BIT; -extern EventGroupHandle_t xEventTask; - -int ftp_buff_size = CONFIG_MICROPY_FTPSERVER_BUFFER_SIZE; -int ftp_timeout = FTP_CMD_TIMEOUT_MS; -const char *FTP_TAG = "[Ftp]"; -const char *MOUNT_POINT = "/root"; - -static uint8_t ftp_stop = 0; -char ftp_user[FTP_USER_PASS_LEN_MAX + 1] = "anonymous"; -char ftp_pass[FTP_USER_PASS_LEN_MAX + 1] = ""; - -/****************************************************************************** - DECLARE PRIVATE DATA - ******************************************************************************/ -static ftp_data_t ftp_data = {0}; -static char *ftp_path = NULL; -static char *ftp_scratch_buffer = NULL;; -static char *ftp_cmd_buffer = NULL; -static uint8_t ftp_nlist = 0; -static const ftp_cmd_t ftp_cmd_table[] = { { "FEAT" }, { "SYST" }, { "CDUP" }, { "CWD" }, - { "PWD" }, { "XPWD" }, { "SIZE" }, { "MDTM" }, - { "TYPE" }, { "USER" }, { "PASS" }, { "PASV" }, - { "LIST" }, { "RETR" }, { "STOR" }, { "DELE" }, - { "RMD" }, { "MKD" }, { "RNFR" }, { "RNTO" }, - { "NOOP" }, { "QUIT" }, { "APPE" }, { "NLST" }, { "AUTH" } }; - -// ==== PRIVATE FUNCTIONS =================================================== - -uint64_t mp_hal_ticks_ms() { - uint64_t time_ms = xTaskGetTickCount() * portTICK_PERIOD_MS; - return time_ms; -} - -//-------------------------------- -static void stoupper (char *str) { - while (str && *str != '\0') { - *str = (char)toupper((int)(*str)); - str++; - } -} - -// ==== File functions ========================================= - -//-------------------------------------------------------------- -static bool ftp_open_file (const char *path, const char *mode) { - ESP_LOGI(FTP_TAG, "ftp_open_file: path=[%s]", path); - char fullname[128]; - strcpy(fullname, MOUNT_POINT); - strcat(fullname, path); - ESP_LOGI(FTP_TAG, "ftp_open_file: fullname=[%s]", fullname); - //ftp_data.fp = fopen(path, mode); - ftp_data.fp = fopen(fullname, mode); - if (ftp_data.fp == NULL) { - ESP_LOGE(FTP_TAG, "ftp_open_file: open fail [%s]", fullname); - return false; - } - ftp_data.e_open = E_FTP_FILE_OPEN; - return true; -} - -//-------------------------------------- -static void ftp_close_files_dir (void) { - if (ftp_data.e_open == E_FTP_FILE_OPEN) { - fclose(ftp_data.fp); - ftp_data.fp = NULL; - } - else if (ftp_data.e_open == E_FTP_DIR_OPEN) { - closedir(ftp_data.dp); - ftp_data.dp = NULL; - } - ftp_data.e_open = E_FTP_NOTHING_OPEN; -} - -//------------------------------------------------ -static void ftp_close_filesystem_on_error (void) { - ftp_close_files_dir(); - if (ftp_data.fp) { - fclose(ftp_data.fp); - ftp_data.fp = NULL; - } - if (ftp_data.dp) { - closedir(ftp_data.dp); - ftp_data.dp = NULL; - } -} - -//--------------------------------------------------------------------------------------------- -static ftp_result_t ftp_read_file (char *filebuf, uint32_t desiredsize, uint32_t *actualsize) { - ftp_result_t result = E_FTP_RESULT_CONTINUE; - *actualsize = fread(filebuf, 1, desiredsize, ftp_data.fp); - if (*actualsize == 0) { - if (feof(ftp_data.fp)) - result = E_FTP_RESULT_OK; - else - result = E_FTP_RESULT_FAILED; - ftp_close_files_dir(); - } else if (*actualsize < desiredsize) { - ftp_close_files_dir(); - result = E_FTP_RESULT_OK; - } - return result; -} - -//----------------------------------------------------------------- -static ftp_result_t ftp_write_file (char *filebuf, uint32_t size) { - ftp_result_t result = E_FTP_RESULT_FAILED; - uint32_t actualsize = fwrite(filebuf, 1, size, ftp_data.fp); - if (actualsize == size) { - result = E_FTP_RESULT_OK; - } else { - ftp_close_files_dir(); - } - return result; -} - -//--------------------------------------------------------------- -static ftp_result_t ftp_open_dir_for_listing (const char *path) { - if (ftp_data.dp) { - closedir(ftp_data.dp); - ftp_data.dp = NULL; - } - ESP_LOGI(FTP_TAG, "ftp_open_dir_for_listing path=[%s] MOUNT_POINT=[%s]", path, MOUNT_POINT); - char fullname[128]; - strcpy(fullname, MOUNT_POINT); - strcat(fullname, path); - ESP_LOGI(FTP_TAG, "ftp_open_dir_for_listing: %s", fullname); - ftp_data.dp = opendir(fullname); // Open the directory - if (ftp_data.dp == NULL) { - return E_FTP_RESULT_FAILED; - } - ftp_data.e_open = E_FTP_DIR_OPEN; - ftp_data.listroot = false; - return E_FTP_RESULT_CONTINUE; -} - -//--------------------------------------------------------------------------------- -static int ftp_get_eplf_item (char *dest, uint32_t destsize, struct dirent *de) { - - char *type = (de->d_type & DT_DIR) ? "d" : "-"; - - // Get full file path needed for stat function - char fullname[128]; - strcpy(fullname, MOUNT_POINT); - strcat(fullname, ftp_path); - //strcpy(fullname, ftp_path); - if (fullname[strlen(fullname)-1] != '/') strcat(fullname, "/"); - strcat(fullname, de->d_name); - - struct stat buf; - int res = stat(fullname, &buf); - ESP_LOGI(FTP_TAG, "ftp_get_eplf_item res=%d buf.st_size=%ld", res, buf.st_size); - if (res < 0) { - buf.st_size = 0; - buf.st_mtime = 946684800; // Jan 1, 2000 - } - - char str_time[64]; - struct tm *tm_info; - time_t now; - if (time(&now) < 0) now = 946684800; // get the current time from the RTC - tm_info = localtime(&buf.st_mtime); // get broken-down file time - - // if file is older than 180 days show dat,month,year else show month, day and time - if ((buf.st_mtime + FTP_UNIX_SECONDS_180_DAYS) < now) strftime(str_time, 127, "%b %d %Y", tm_info); - else strftime(str_time, 63, "%b %d %H:%M", tm_info); - - int addsize = destsize + 64; - - while (addsize >= destsize) { - if (ftp_nlist) addsize = snprintf(dest, destsize, "%s\r\n", de->d_name); - else addsize = snprintf(dest, destsize, "%srw-rw-rw- 1 root root %9"PRIu32" %s %s\r\n", type, (uint32_t)buf.st_size, str_time, de->d_name); - if (addsize >= destsize) { - ESP_LOGW(FTP_TAG, "Buffer too small, reallocating [%d > %"PRIi32"]", ftp_buff_size, ftp_buff_size + (addsize - destsize) + 64); - char *new_dest = realloc(dest, ftp_buff_size + (addsize - destsize) + 65); - if (new_dest) { - ftp_buff_size += (addsize - destsize) + 64; - destsize += (addsize - destsize) + 64; - dest = new_dest; - addsize = destsize + 64; - } - else { - ESP_LOGE(FTP_TAG, "Buffer reallocation ERROR"); - addsize = 0; - } - } - } - return addsize; -} - -#if 0 -//--------------------------------------------------------------------------- -static int ftp_get_eplf_drive (char *dest, uint32_t destsize, char *name) { - char *type = "d"; - struct tm *tm_info; - time_t seconds; - time(&seconds); // get the time from the RTC - tm_info = gmtime(&seconds); - char str_time[64]; - strftime(str_time, 63, "%b %d %Y", tm_info); - - return snprintf(dest, destsize, "%srw-rw-rw- 1 root root %9u %s %s\r\n", type, 0, str_time, name); -} -#endif - -//-------------------------------------------------------------------------------------- -static ftp_result_t ftp_list_dir(char *list, uint32_t maxlistsize, uint32_t *listsize) { - uint next = 0; - uint listcount = 0; - ftp_result_t result = E_FTP_RESULT_CONTINUE; - struct dirent *de; - - // read up to 8 directory items - while (((maxlistsize - next) > 64) && (listcount < 8)) { - de = readdir(ftp_data.dp); // Read a directory item - ESP_LOGI(FTP_TAG, "readdir de=%p", de); - if (de == NULL) { - result = E_FTP_RESULT_OK; - break; // Break on error or end of dp - } - if (de->d_name[0] == '.' && de->d_name[1] == 0) continue; // Ignore . entry - if (de->d_name[0] == '.' && de->d_name[1] == '.' && de->d_name[2] == 0) continue; // Ignore .. entry - - // add the entry to the list - ESP_LOGI(FTP_TAG, "Add to dir list: %s", de->d_name); - next += ftp_get_eplf_item((list + next), (maxlistsize - next), de); - listcount++; - } - if (result == E_FTP_RESULT_OK) { - ftp_close_files_dir(); - } - *listsize = next; - return result; -} - -// ==== Socket functions ============================================================== - -//------------------------------------ -static void ftp_close_cmd_data(void) { - closesocket(ftp_data.c_sd); - closesocket(ftp_data.d_sd); - ftp_data.c_sd = -1; - ftp_data.d_sd = -1; - ftp_close_filesystem_on_error (); -} - -//---------------------------- -static void _ftp_reset(void) { - // close all connections and start all over again - ESP_LOGW(FTP_TAG, "FTP RESET"); - closesocket(ftp_data.lc_sd); - closesocket(ftp_data.ld_sd); - ftp_data.lc_sd = -1; - ftp_data.ld_sd = -1; - ftp_close_cmd_data(); - - ftp_data.e_open = E_FTP_NOTHING_OPEN; - ftp_data.state = E_FTP_STE_START; - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; -} - -//------------------------------------------------------------------------------------- -static bool ftp_create_listening_socket (int32_t *sd, uint32_t port, uint8_t backlog) { - struct sockaddr_in sServerAddress; - int32_t _sd; - int32_t result; - - // open a socket for ftp data listen - *sd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); - _sd = *sd; - - if (_sd > 0) { - // enable non-blocking mode - uint32_t option = fcntl(_sd, F_GETFL, 0); - option |= O_NONBLOCK; - fcntl(_sd, F_SETFL, option); - - // enable address reusing - option = 1; - result = setsockopt(_sd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option)); - - // bind the socket to a port number - sServerAddress.sin_family = AF_INET; - sServerAddress.sin_addr.s_addr = INADDR_ANY; - sServerAddress.sin_len = sizeof(sServerAddress); - sServerAddress.sin_port = htons(port); - - result |= bind(_sd, (const struct sockaddr *)&sServerAddress, sizeof(sServerAddress)); - - // start listening - result |= listen (_sd, backlog); - - if (!result) { - return true; - } - closesocket(*sd); - } - return false; -} - -//-------------------------------------------------------------------------------------------- -static ftp_result_t ftp_wait_for_connection (int32_t l_sd, int32_t *n_sd, uint32_t *ip_addr) { - struct sockaddr_in sClientAddress; - socklen_t in_addrSize; - - // accepts a connection from a TCP client, if there is any, otherwise returns EAGAIN - *n_sd = accept(l_sd, (struct sockaddr *)&sClientAddress, (socklen_t *)&in_addrSize); - int32_t _sd = *n_sd; - if (_sd < 0) { - if (errno == EAGAIN) { - return E_FTP_RESULT_CONTINUE; - } - // error - _ftp_reset(); - return E_FTP_RESULT_FAILED; - } - - if (ip_addr) { - // check on which network interface the client was connected and save the IP address - struct sockaddr_in clientAddr, serverAddr; - in_addrSize = sizeof(struct sockaddr_in); - getpeername(_sd, (struct sockaddr *)&clientAddr, (socklen_t *)&in_addrSize); - getsockname(_sd, (struct sockaddr *)&serverAddr, (socklen_t *)&in_addrSize); - ESP_LOGI(FTP_TAG, "Client IP: 0x%08"PRIx32, clientAddr.sin_addr.s_addr); - ESP_LOGI(FTP_TAG, "Server IP: 0x%08"PRIx32, serverAddr.sin_addr.s_addr); - *ip_addr = serverAddr.sin_addr.s_addr; - } - - // enable non-blocking mode if not data channel connection - uint32_t option = fcntl(_sd, F_GETFL, 0); - if (l_sd != ftp_data.ld_sd) option |= O_NONBLOCK; - fcntl(_sd, F_SETFL, option); - - // client connected, so go on - return E_FTP_RESULT_OK; -} - -//----------------------------------------------------------- -static void ftp_send_reply (uint32_t status, char *message) { - if (!message) { - message = ""; - } - snprintf((char *)ftp_cmd_buffer, 4, "%"PRIu32, status); - strcat ((char *)ftp_cmd_buffer, " "); - strcat ((char *)ftp_cmd_buffer, message); - strcat ((char *)ftp_cmd_buffer, "\r\n"); - - int32_t timeout = 200; - ftp_result_t result; - //uint32_t size = strlen((char *)ftp_cmd_buffer); - size_t size = strlen((char *)ftp_cmd_buffer); - - ESP_LOGI(FTP_TAG, "Send reply: [%.*s]", size-2, ftp_cmd_buffer); - vTaskDelay(1); - - while (1) { - result = send(ftp_data.c_sd, ftp_cmd_buffer, size, 0); - if (result == size) { - if (status == 221) { - closesocket(ftp_data.d_sd); - ftp_data.d_sd = -1; - closesocket(ftp_data.ld_sd); - ftp_data.ld_sd = -1; - closesocket(ftp_data.c_sd); - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; - ftp_close_filesystem_on_error(); - } - else if (status == 426 || status == 451 || status == 550) { - closesocket(ftp_data.d_sd); - ftp_data.d_sd = -1; - ftp_close_filesystem_on_error(); - } - vTaskDelay(1); - ESP_LOGI(FTP_TAG, "Send reply: OK (%u)", size); - break; - } - else { - vTaskDelay(1); - if ((timeout <= 0) || (errno != EAGAIN)) { - // error - _ftp_reset(); - ESP_LOGW(FTP_TAG, "Error sending command reply."); - break; - } - } - timeout -= portTICK_PERIOD_MS; - } -} - -//------------------------------------------ -static void ftp_send_list(uint32_t datasize) -{ - int32_t timeout = 200; - ftp_result_t result; - - ESP_LOGI(FTP_TAG, "Send list data: (%"PRIu32")", datasize); - vTaskDelay(1); - - while (1) { - result = send(ftp_data.d_sd, ftp_data.dBuffer, datasize, 0); - if (result == datasize) { - vTaskDelay(1); - ESP_LOGI(FTP_TAG, "Send OK"); - break; - } - else { - vTaskDelay(1); - if ((timeout <= 0) || (errno != EAGAIN)) { - // error - _ftp_reset(); - ESP_LOGW(FTP_TAG, "Error sending list data."); - break; - } - } - timeout -= portTICK_PERIOD_MS; - } -} - -//----------------------------------------------- -static void ftp_send_file_data(uint32_t datasize) -{ - ftp_result_t result; - uint32_t timeout = 200; - - ESP_LOGI(FTP_TAG, "Send file data: (%"PRIu32")", datasize); - vTaskDelay(1); - - while (1) { - result = send(ftp_data.d_sd, ftp_data.dBuffer, datasize, 0); - if (result == datasize) { - vTaskDelay(1); - ESP_LOGI(FTP_TAG, "Send OK"); - break; - } - else { - vTaskDelay(1); - if ((timeout <= 0) || (errno != EAGAIN)) { - // error - _ftp_reset(); - ESP_LOGW(FTP_TAG, "Error sending file data."); - break; - } - } - timeout -= portTICK_PERIOD_MS; - } -} - -//------------------------------------------------------------------------------------------------ -static ftp_result_t ftp_recv_non_blocking (int32_t sd, void *buff, int32_t Maxlen, int32_t *rxLen) -{ - if (sd < 0) return E_FTP_RESULT_FAILED; - - *rxLen = recv(sd, buff, Maxlen, 0); - if (*rxLen > 0) return E_FTP_RESULT_OK; - else if (errno != EAGAIN) return E_FTP_RESULT_FAILED; - - return E_FTP_RESULT_CONTINUE; -} - -// ==== Directory functions ======================= - -//----------------------------------- -#if 0 -static void ftp_fix_path(char *pwd) { - ESP_LOGI(FTP_TAG, "ftp_fix_path: pwd=[%s]", pwd); - char ph_path[128]; - uint len = strlen(pwd); - - if (len == 0) { - strcpy (pwd, "/"); - } - else if ((len > 1) && (pwd[len-1] == '/')) pwd[len-1] = '\0'; - - // Convert to physical path - if (strstr(pwd, VFS_NATIVE_INTERNAL_MP) == pwd) { - ESP_LOGI(FTP_TAG, "ftp_fix_path: VFS_NATIVE_INTERNAL_MP=[%s]", VFS_NATIVE_INTERNAL_MP); - sprintf(ph_path, "%s%s", VFS_NATIVE_MOUNT_POINT, pwd+strlen(VFS_NATIVE_INTERNAL_MP)); - if (strcmp(ph_path, VFS_NATIVE_MOUNT_POINT) == 0) strcat(ph_path, "/"); - ESP_LOGI(FTP_TAG, "ftp_fix_path: ph_path=[%s]", ph_path); - strcpy(pwd, ph_path); - } - else if (strstr(pwd, VFS_NATIVE_EXTERNAL_MP) == pwd) { - ESP_LOGI(FTP_TAG, "ftp_fix_path: VFS_NATIVE_EXTERNAL_MP=[%s]", VFS_NATIVE_EXTERNAL_MP); - sprintf(ph_path, "%s%s", VFS_NATIVE_SDCARD_MOUNT_POINT, pwd+strlen(VFS_NATIVE_EXTERNAL_MP)); - if (strcmp(ph_path, VFS_NATIVE_SDCARD_MOUNT_POINT) == 0) strcat(ph_path, "/"); - ESP_LOGI(FTP_TAG, "ftp_fix_path: ph_path=[%s]", ph_path); - strcpy(pwd, ph_path); - } -} -#endif - - -/* - * Add directory or file name to the current path - * Initially, pwd is set to "/" (file system root) - * There are two possible entries in root: - * "flash" which can be fatfs or spiffs - * "sd" sd card - * flash and sd entries have to be translated to their VFS names - * trailing '/' is required for flash&sd root entries (translated) - */ -//------------------------------------------------- -static void ftp_open_child (char *pwd, char *dir) { - ESP_LOGI(FTP_TAG, "open_child: [%s] + [%s]", pwd, dir); - if (strlen(dir) > 0) { - if (dir[0] == '/') { - // ** absolute path - strcpy(pwd, dir); - } - else { - // ** relative path - // add trailing '/' if needed - if ((strlen(pwd) > 1) && (pwd[strlen(pwd)-1] != '/') && (dir[0] != '/')) strcat(pwd, "/"); - // append directory/file name - strcat(pwd, dir); - } -#if 0 - ftp_fix_path(pwd); -#endif - } - ESP_LOGI(FTP_TAG, "open_child, New pwd: %s", pwd); -} - -// Return to parent directory -//--------------------------------------- -static void ftp_close_child (char *pwd) { - ESP_LOGI(FTP_TAG, "close_child: %s", pwd); - uint len = strlen(pwd); - if (pwd[len-1] == '/') { - pwd[len-1] = '\0'; - len--; - if ((len == 0) || (strcmp(pwd, VFS_NATIVE_MOUNT_POINT) == 0) || (strcmp(pwd, VFS_NATIVE_SDCARD_MOUNT_POINT) == 0)) { - strcpy(pwd, "/"); - } - } - else { - while (len) { - if (pwd[len-1] == '/') { - pwd[len-1] = '\0'; - len--; - break; - } - len--; - } - - if (len == 0) { - strcpy (pwd, "/"); - } - else if ((strcmp(pwd, VFS_NATIVE_MOUNT_POINT) == 0) || (strcmp(pwd, VFS_NATIVE_SDCARD_MOUNT_POINT) == 0)) { - strcat(pwd, "/"); - } - } - ESP_LOGI(FTP_TAG, "close_child, New pwd: %s", pwd); -} - -// Remove file name from path -//----------------------------------------------------------- -static void remove_fname_from_path (char *pwd, char *fname) { - ESP_LOGI(FTP_TAG, "remove_fname_from_path: %s - %s", pwd, fname); - if (strlen(fname) == 0) return; - char *xpwd = strstr(pwd, fname); - if (xpwd == NULL) return; - - xpwd[0] = '\0'; - -#if 0 - ftp_fix_path(pwd); -#endif - ESP_LOGI(FTP_TAG, "remove_fname_from_path: New pwd: %s", pwd); -} - -// ==== Param functions ================================================= - -//------------------------------------------------------------------------------------------ -static void ftp_pop_param(char **str, char *param, bool stop_on_space, bool stop_on_newline) -{ - char lastc = '\0'; - while (**str != '\0') { - if (stop_on_space && (**str == ' ')) break; - if ((**str == '\r') || (**str == '\n')) { - if (!stop_on_newline) { - (*str)++; - continue; - } - else break; - } - if ((**str == '/') && (lastc == '/')) { - (*str)++; - continue; - } - lastc = **str; - *param++ = **str; - (*str)++; - } - *param = '\0'; -} - -//-------------------------------------------------- -static ftp_cmd_index_t ftp_pop_command(char **str) { - char _cmd[FTP_CMD_SIZE_MAX]; - ftp_pop_param (str, _cmd, true, true); - stoupper (_cmd); - for (ftp_cmd_index_t i = 0; i < E_FTP_NUM_FTP_CMDS; i++) { - if (!strcmp (_cmd, ftp_cmd_table[i].cmd)) { - // move one step further to skip the space - (*str)++; - return i; - } - } - return E_FTP_CMD_NOT_SUPPORTED; -} - -// Get file name from parameter and append to ftp_path -//------------------------------------------------------- -static void ftp_get_param_and_open_child(char **bufptr) { - ftp_pop_param(bufptr, ftp_scratch_buffer, false, false); - ftp_open_child(ftp_path, ftp_scratch_buffer); - ftp_data.closechild = true; -} - -// ==== Ftp command processing ===== - -//---------------------------------- -static void ftp_process_cmd (void) { - int32_t len; - char *bufptr = (char *)ftp_cmd_buffer; - ftp_result_t result; - struct stat buf; - int res; - - memset(bufptr, 0, FTP_MAX_PARAM_SIZE + FTP_CMD_SIZE_MAX); - ftp_data.closechild = false; - - // use the reply buffer to receive new commands - result = ftp_recv_non_blocking(ftp_data.c_sd, ftp_cmd_buffer, FTP_MAX_PARAM_SIZE + FTP_CMD_SIZE_MAX, &len); - if (result == E_FTP_RESULT_OK) { - ftp_cmd_buffer[len] = '\0'; - // bufptr is moved as commands are being popped - ftp_cmd_index_t cmd = ftp_pop_command(&bufptr); - if (!ftp_data.loggin.passvalid && - ((cmd != E_FTP_CMD_USER) && (cmd != E_FTP_CMD_PASS) && (cmd != E_FTP_CMD_QUIT) && (cmd != E_FTP_CMD_FEAT) && (cmd != E_FTP_CMD_AUTH))) { - ftp_send_reply(332, NULL); - return; - } - if ((cmd >= 0) && (cmd < E_FTP_NUM_FTP_CMDS)) { - ESP_LOGI(FTP_TAG, "CMD: %s", ftp_cmd_table[cmd].cmd); - } - else { - ESP_LOGI(FTP_TAG, "CMD: %d", cmd); - } - char fullname[128]; - char fullname2[128]; - strcpy(fullname, MOUNT_POINT); - strcpy(fullname2, MOUNT_POINT); - - switch (cmd) { - case E_FTP_CMD_FEAT: - ftp_send_reply(502, "no-features"); - break; - case E_FTP_CMD_AUTH: - ftp_send_reply(504, "not-supported"); - break; - case E_FTP_CMD_SYST: - ftp_send_reply(215, "UNIX Type: L8"); - break; - case E_FTP_CMD_CDUP: - ftp_close_child(ftp_path); - ftp_send_reply(250, NULL); - break; - case E_FTP_CMD_CWD: - ftp_pop_param (&bufptr, ftp_scratch_buffer, false, false); - - if (strlen(ftp_scratch_buffer) > 0) { - if ((ftp_scratch_buffer[0] == '.') && (ftp_scratch_buffer[1] == '\0')) { - ftp_data.dp = NULL; - ftp_send_reply(250, NULL); - break; - } - if ((ftp_scratch_buffer[0] == '.') && (ftp_scratch_buffer[1] == '.') && (ftp_scratch_buffer[2] == '\0')) { - ftp_close_child (ftp_path); - ftp_send_reply(250, NULL); - break; - } - else ftp_open_child (ftp_path, ftp_scratch_buffer); - } - - if ((ftp_path[0] == '/') && (ftp_path[1] == '\0')) { - ftp_data.dp = NULL; - ftp_send_reply(250, NULL); - } - else { - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_CWD fullname=[%s]", fullname); - //ftp_data.dp = opendir(ftp_path); - ftp_data.dp = opendir(fullname); - if (ftp_data.dp != NULL) { - closedir(ftp_data.dp); - ftp_data.dp = NULL; - ftp_send_reply(250, NULL); - } - else { - ftp_close_child (ftp_path); - ftp_send_reply(550, NULL); - } - } - break; - case E_FTP_CMD_PWD: - case E_FTP_CMD_XPWD: - { - char lpath[128]; -#if 0 - if (strstr(ftp_path, VFS_NATIVE_MOUNT_POINT) == ftp_path) { - sprintf(lpath, "%s%s", VFS_NATIVE_INTERNAL_MP, ftp_path+strlen(VFS_NATIVE_MOUNT_POINT)); - } - else if (strstr(ftp_path, VFS_NATIVE_SDCARD_MOUNT_POINT) == ftp_path) { - sprintf(lpath, "%s%s", VFS_NATIVE_EXTERNAL_MP, ftp_path+strlen(VFS_NATIVE_SDCARD_MOUNT_POINT)); - } - else strcpy(lpath,ftp_path); -#endif - strcpy(lpath,ftp_path); - - ftp_send_reply(257, lpath); - } - break; - case E_FTP_CMD_SIZE: - { - ftp_get_param_and_open_child (&bufptr); - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_SIZE fullname=[%s]", fullname); - //int res = stat(ftp_path, &buf); - int res = stat(fullname, &buf); - if (res == 0) { - // send the file size - snprintf((char *)ftp_data.dBuffer, ftp_buff_size, "%"PRIu32, (uint32_t)buf.st_size); - ftp_send_reply(213, (char *)ftp_data.dBuffer); - } else { - ftp_send_reply(550, NULL); - } - } - break; - case E_FTP_CMD_MDTM: - ftp_get_param_and_open_child (&bufptr); - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_MDTM fullname=[%s]", fullname); - //res = stat(ftp_path, &buf); - res = stat(fullname, &buf); - if (res == 0) { - // send the file modification time - //snprintf((char *)ftp_data.dBuffer, ftp_buff_size, "%u", (uint32_t)buf.st_mtime); - //snprintf((char *)ftp_data.dBuffer, ftp_buff_size, "20210212010203"); - //time_t time = buf.st_mtime + (CONFIG_LOCAL_TIMEZONE*60*60); - time_t time = buf.st_mtime; - struct tm *ptm = localtime(&time); - //char buf[128]; - //strftime(buf, sizeof(buf), "%Y%m%d%H%M%S", ptm); - strftime((char *)ftp_data.dBuffer, ftp_buff_size, "%Y%m%d%H%M%S", ptm); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_MDTM ftp_data.dBuffer=[%s]", ftp_data.dBuffer); - ftp_send_reply(213, (char *)ftp_data.dBuffer); - } else { - ftp_send_reply(550, NULL); - } - break; - case E_FTP_CMD_TYPE: - ftp_send_reply(200, NULL); - break; - case E_FTP_CMD_USER: - ftp_pop_param (&bufptr, ftp_scratch_buffer, true, true); - if (!memcmp(ftp_scratch_buffer, ftp_user, MAX(strlen(ftp_scratch_buffer), strlen(ftp_user)))) { - ftp_data.loggin.uservalid = true && (strlen(ftp_user) == strlen(ftp_scratch_buffer)); - } - ftp_send_reply(331, NULL); - break; - case E_FTP_CMD_PASS: - ftp_pop_param (&bufptr, ftp_scratch_buffer, true, true); - if (!memcmp(ftp_scratch_buffer, ftp_pass, MAX(strlen(ftp_scratch_buffer), strlen(ftp_pass))) && - ftp_data.loggin.uservalid) { - ftp_data.loggin.passvalid = true && (strlen(ftp_pass) == strlen(ftp_scratch_buffer)); - if (ftp_data.loggin.passvalid) { - ftp_send_reply(230, NULL); - ESP_LOGW(FTP_TAG, "Connected."); - break; - } - } - ftp_send_reply(530, NULL); - break; - case E_FTP_CMD_PASV: - { - // some servers (e.g. google chrome) send PASV several times very quickly - closesocket(ftp_data.d_sd); - ftp_data.d_sd = -1; - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; - bool socketcreated = true; - if (ftp_data.ld_sd < 0) { - socketcreated = ftp_create_listening_socket(&ftp_data.ld_sd, FTP_PASIVE_DATA_PORT, FTP_DATA_CLIENTS_MAX - 1); - } - if (socketcreated) { - uint8_t *pip = (uint8_t *)&ftp_data.ip_addr; - ftp_data.dtimeout = 0; - snprintf((char *)ftp_data.dBuffer, ftp_buff_size, "(%u,%u,%u,%u,%u,%u)", - pip[0], pip[1], pip[2], pip[3], (FTP_PASIVE_DATA_PORT >> 8), (FTP_PASIVE_DATA_PORT & 0xFF)); - ftp_data.substate = E_FTP_STE_SUB_LISTEN_FOR_DATA; - ESP_LOGI(FTP_TAG, "Data socket created"); - ftp_send_reply(227, (char *)ftp_data.dBuffer); - } - else { - ESP_LOGW(FTP_TAG, "Error creating data socket"); - ftp_send_reply(425, NULL); - } - } - break; - case E_FTP_CMD_LIST: - case E_FTP_CMD_NLST: - ftp_get_param_and_open_child(&bufptr); - if (cmd == E_FTP_CMD_LIST) ftp_nlist = 0; - else ftp_nlist = 1; - if (ftp_open_dir_for_listing(ftp_path) == E_FTP_RESULT_CONTINUE) { - ftp_data.state = E_FTP_STE_CONTINUE_LISTING; - ftp_send_reply(150, NULL); - } - else ftp_send_reply(550, NULL); - break; - case E_FTP_CMD_RETR: - ftp_data.total = 0; - ftp_data.time = 0; - ftp_get_param_and_open_child(&bufptr); - if ((strlen(ftp_path) > 0) && (ftp_path[strlen(ftp_path)-1] != '/')) { - if (ftp_open_file(ftp_path, "rb")) { - ftp_data.state = E_FTP_STE_CONTINUE_FILE_TX; - vTaskDelay(20 / portTICK_PERIOD_MS); - ftp_send_reply(150, NULL); - } - else { - ftp_data.state = E_FTP_STE_END_TRANSFER; - ftp_send_reply(550, NULL); - } - } - else { - ftp_data.state = E_FTP_STE_END_TRANSFER; - ftp_send_reply(550, NULL); - } - break; - case E_FTP_CMD_APPE: - ftp_data.total = 0; - ftp_data.time = 0; - ftp_get_param_and_open_child(&bufptr); - if ((strlen(ftp_path) > 0) && (ftp_path[strlen(ftp_path)-1] != '/')) { - if (ftp_open_file(ftp_path, "ab")) { - ftp_data.state = E_FTP_STE_CONTINUE_FILE_RX; - vTaskDelay(20 / portTICK_PERIOD_MS); - ftp_send_reply(150, NULL); - } - else { - ftp_data.state = E_FTP_STE_END_TRANSFER; - ftp_send_reply(550, NULL); - } - } - else { - ftp_data.state = E_FTP_STE_END_TRANSFER; - ftp_send_reply(550, NULL); - } - break; - case E_FTP_CMD_STOR: - ftp_data.total = 0; - ftp_data.time = 0; - ftp_get_param_and_open_child(&bufptr); - if ((strlen(ftp_path) > 0) && (ftp_path[strlen(ftp_path)-1] != '/')) { - ESP_LOGI(FTP_TAG, "E_FTP_CMD_STOR ftp_path=[%s]", ftp_path); - if (ftp_open_file(ftp_path, "wb")) { - ftp_data.state = E_FTP_STE_CONTINUE_FILE_RX; - vTaskDelay(20 / portTICK_PERIOD_MS); - ftp_send_reply(150, NULL); - } - else { - ftp_data.state = E_FTP_STE_END_TRANSFER; - ftp_send_reply(550, NULL); - } - } - else { - ftp_data.state = E_FTP_STE_END_TRANSFER; - ftp_send_reply(550, NULL); - } - break; - case E_FTP_CMD_DELE: - ftp_get_param_and_open_child(&bufptr); - if ((strlen(ftp_path) > 0) && (ftp_path[strlen(ftp_path)-1] != '/')) { - ESP_LOGI(FTP_TAG, "E_FTP_CMD_DELE ftp_path=[%s]", ftp_path); - - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_DELE fullname=[%s]", fullname); - - //if (unlink(ftp_path) == 0) { - if (unlink(fullname) == 0) { - vTaskDelay(20 / portTICK_PERIOD_MS); - ftp_send_reply(250, NULL); - } - else ftp_send_reply(550, NULL); - } - else ftp_send_reply(250, NULL); - break; - case E_FTP_CMD_RMD: - ftp_get_param_and_open_child(&bufptr); - if ((strlen(ftp_path) > 0) && (ftp_path[strlen(ftp_path)-1] != '/')) { - ESP_LOGI(FTP_TAG, "E_FTP_CMD_RMD ftp_path=[%s]", ftp_path); - - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_MKD fullname=[%s]", fullname); - - //if (rmdir(ftp_path) == 0) { - if (rmdir(fullname) == 0) { - vTaskDelay(20 / portTICK_PERIOD_MS); - ftp_send_reply(250, NULL); - } - else ftp_send_reply(550, NULL); - } - else ftp_send_reply(250, NULL); - break; - case E_FTP_CMD_MKD: - ftp_get_param_and_open_child(&bufptr); - if ((strlen(ftp_path) > 0) && (ftp_path[strlen(ftp_path)-1] != '/')) { - ESP_LOGI(FTP_TAG, "E_FTP_CMD_MKD ftp_path=[%s]", ftp_path); - - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_MKD fullname=[%s]", fullname); - - //if (mkdir(ftp_path, 0755) == 0) { - if (mkdir(fullname, 0755) == 0) { - vTaskDelay(20 / portTICK_PERIOD_MS); - ftp_send_reply(250, NULL); - } - else ftp_send_reply(550, NULL); - } - else ftp_send_reply(250, NULL); - break; - case E_FTP_CMD_RNFR: - ftp_get_param_and_open_child(&bufptr); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_RNFR ftp_path=[%s]", ftp_path); - - strcat(fullname, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_MKD fullname=[%s]", fullname); - - //res = stat(ftp_path, &buf); - res = stat(fullname, &buf); - if (res == 0) { - ftp_send_reply(350, NULL); - // save the path of the file to rename - strcpy((char *)ftp_data.dBuffer, ftp_path); - } else { - ftp_send_reply(550, NULL); - } - break; - case E_FTP_CMD_RNTO: - ftp_get_param_and_open_child(&bufptr); - // the path of the file to rename was saved in the data buffer - ESP_LOGI(FTP_TAG, "E_FTP_CMD_RNTO ftp_path=[%s], ftp_data.dBuffer=[%s]", ftp_path, (char *)ftp_data.dBuffer); - strcat(fullname, (char *)ftp_data.dBuffer); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_RNTO fullname=[%s]", fullname); - strcat(fullname2, ftp_path); - ESP_LOGI(FTP_TAG, "E_FTP_CMD_RNTO fullname2=[%s]", fullname2); - - //if (rename((char *)ftp_data.dBuffer, ftp_path) == 0) { - if (rename(fullname, fullname2) == 0) { - ftp_send_reply(250, NULL); - } else { - ftp_send_reply(550, NULL); - } - break; - case E_FTP_CMD_NOOP: - ftp_send_reply(200, NULL); - break; - case E_FTP_CMD_QUIT: - ftp_send_reply(221, NULL); - break; - default: - // command not implemented - ftp_send_reply(502, NULL); - break; - } - - if (ftp_data.closechild) { - remove_fname_from_path(ftp_path, ftp_scratch_buffer); - } - } - else if (result == E_FTP_RESULT_CONTINUE) { - if (ftp_data.ctimeout > ftp_timeout) { - ftp_send_reply(221, NULL); - ESP_LOGW(FTP_TAG, "Connection timeout"); - } - } - else { - ftp_close_cmd_data(); - } -} - -//--------------------------------------- -static void ftp_wait_for_enabled (void) { - // Check if the telnet service has been enabled - if (ftp_data.enabled) { - ftp_data.state = E_FTP_STE_START; - } -} - -// ==== PUBLIC FUNCTIONS =================================================================== - -//--------------------- -void ftp_deinit(void) { - if (ftp_path) free(ftp_path); - if (ftp_cmd_buffer) free(ftp_cmd_buffer); - if (ftp_data.dBuffer) free(ftp_data.dBuffer); - if (ftp_scratch_buffer) free(ftp_scratch_buffer); - ftp_path = NULL; - ftp_cmd_buffer = NULL; - ftp_data.dBuffer = NULL; - ftp_scratch_buffer = NULL; - - memset(&ftp_data, 0, sizeof(ftp_data)); - ftp_data.c_sd = -1; - ftp_data.d_sd = -1; - ftp_data.ld_sd = -1; -} - -//------------------- -bool ftp_init(void) { - ftp_stop = 0; - // Allocate memory for the data buffer, and the file system structures (from the RTOS heap) - ftp_deinit(); - - memset(&ftp_data, 0, sizeof(ftp_data_t)); - ftp_data.dBuffer = malloc(ftp_buff_size+1); - if (ftp_data.dBuffer == NULL) return false; - ftp_path = malloc(FTP_MAX_PARAM_SIZE); - if (ftp_path == NULL) { - free(ftp_data.dBuffer); - return false; - } - ftp_scratch_buffer = malloc(FTP_MAX_PARAM_SIZE); - if (ftp_scratch_buffer == NULL) { - free(ftp_path); - free(ftp_data.dBuffer); - return false; - } - ftp_cmd_buffer = malloc(FTP_MAX_PARAM_SIZE + FTP_CMD_SIZE_MAX); - if (ftp_cmd_buffer == NULL) { - free(ftp_scratch_buffer); - free(ftp_path); - free(ftp_data.dBuffer); - return false; - } - - //SOCKETFIFO_Init((void *)ftp_fifoelements, FTP_SOCKETFIFO_ELEMENTS_MAX); - - ftp_data.c_sd = -1; - ftp_data.d_sd = -1; - ftp_data.lc_sd = -1; - ftp_data.ld_sd = -1; - ftp_data.e_open = E_FTP_NOTHING_OPEN; - ftp_data.state = E_FTP_STE_DISABLED; - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; - - //if (ftp_mutex == NULL) ftp_mutex = xSemaphoreCreateMutex(); - return true; -} - -//============================ -int ftp_run (uint32_t elapsed) -{ - //if (xSemaphoreTake(ftp_mutex, FTP_MUTEX_TIMEOUT_MS / portTICK_PERIOD_MS) !=pdTRUE) return -1; - if (ftp_stop) return -2; - - ftp_data.dtimeout += elapsed; - ftp_data.ctimeout += elapsed; - ftp_data.time += elapsed; - - switch (ftp_data.state) { - case E_FTP_STE_DISABLED: - ftp_wait_for_enabled(); - break; - case E_FTP_STE_START: - if (ftp_create_listening_socket(&ftp_data.lc_sd, FTP_CMD_PORT, FTP_CMD_CLIENTS_MAX - 1)) { - ftp_data.state = E_FTP_STE_READY; - } - break; - case E_FTP_STE_READY: - if (ftp_data.c_sd < 0 && ftp_data.substate == E_FTP_STE_SUB_DISCONNECTED) { - if (E_FTP_RESULT_OK == ftp_wait_for_connection(ftp_data.lc_sd, &ftp_data.c_sd, &ftp_data.ip_addr)) { - ftp_data.txRetries = 0; - ftp_data.logginRetries = 0; - ftp_data.ctimeout = 0; - ftp_data.loggin.uservalid = false; - ftp_data.loggin.passvalid = false; - strcpy (ftp_path, "/"); - ESP_LOGI(FTP_TAG, "Connected."); - //ftp_send_reply (220, "Micropython FTP Server"); - ftp_send_reply (220, "ESP32 FTP Server"); - break; - } - } - if (ftp_data.c_sd > 0 && ftp_data.substate != E_FTP_STE_SUB_LISTEN_FOR_DATA) { - ftp_process_cmd(); - if (ftp_data.state != E_FTP_STE_READY) { - break; - } - } - break; - case E_FTP_STE_END_TRANSFER: - if (ftp_data.d_sd >= 0) { - closesocket(ftp_data.d_sd); - ftp_data.d_sd = -1; - } - break; - case E_FTP_STE_CONTINUE_LISTING: - // go on with listing - { - uint32_t listsize = 0; - ftp_result_t list_res = ftp_list_dir((char *)ftp_data.dBuffer, ftp_buff_size, &listsize); - if (listsize > 0) ftp_send_list(listsize); - if (list_res == E_FTP_RESULT_OK) { - ftp_send_reply(226, NULL); - ftp_data.state = E_FTP_STE_END_TRANSFER; - } - ftp_data.ctimeout = 0; - } - break; - case E_FTP_STE_CONTINUE_FILE_TX: - // read and send the next block from the file - { - uint32_t readsize; - ftp_result_t result; - ftp_data.ctimeout = 0; - result = ftp_read_file ((char *)ftp_data.dBuffer, ftp_buff_size, &readsize); - if (result == E_FTP_RESULT_FAILED) { - ftp_send_reply(451, NULL); - ftp_data.state = E_FTP_STE_END_TRANSFER; - } - else { - if (readsize > 0) { - ftp_send_file_data(readsize); - ftp_data.total += readsize; - ESP_LOGI(FTP_TAG, "Sent %"PRIu32", total: %"PRIu32, readsize, ftp_data.total); - } - if (result == E_FTP_RESULT_OK) { - ftp_send_reply(226, NULL); - ftp_data.state = E_FTP_STE_END_TRANSFER; - ESP_LOGI(FTP_TAG, "File sent (%"PRIu32" bytes in %"PRIu32" msec).", ftp_data.total, ftp_data.time); - } - } - } - break; - case E_FTP_STE_CONTINUE_FILE_RX: - { - int32_t len; - ftp_result_t result = E_FTP_RESULT_OK; - - ESP_LOGI(FTP_TAG, "ftp_buff_size=%d", ftp_buff_size); - result = ftp_recv_non_blocking(ftp_data.d_sd, ftp_data.dBuffer, ftp_buff_size, &len); - if (result == E_FTP_RESULT_OK) { - // block of data received - ftp_data.dtimeout = 0; - ftp_data.ctimeout = 0; - // save received data to file - if (E_FTP_RESULT_OK != ftp_write_file ((char *)ftp_data.dBuffer, len)) { - ftp_send_reply(451, NULL); - ftp_data.state = E_FTP_STE_END_TRANSFER; - ESP_LOGW(FTP_TAG, "Error writing to file"); - } - else { - ftp_data.total += len; - ESP_LOGI(FTP_TAG, "Received %"PRIu32", total: %"PRIu32, len, ftp_data.total); - } - } - else if (result == E_FTP_RESULT_CONTINUE) { - // nothing received - if (ftp_data.dtimeout > FTP_DATA_TIMEOUT_MS) { - ftp_close_files_dir(); - ftp_send_reply(426, NULL); - ftp_data.state = E_FTP_STE_END_TRANSFER; - ESP_LOGW(FTP_TAG, "Receiving to file timeout"); - } - } - else { - // File received (E_FTP_RESULT_FAILED) - ftp_close_files_dir(); - ftp_send_reply(226, NULL); - ftp_data.state = E_FTP_STE_END_TRANSFER; - ESP_LOGI(FTP_TAG, "File received (%"PRIu32" bytes in %"PRIu32" msec).", ftp_data.total, ftp_data.time); - break; - } - } - break; - default: - break; - } - - switch (ftp_data.substate) { - case E_FTP_STE_SUB_DISCONNECTED: - break; - case E_FTP_STE_SUB_LISTEN_FOR_DATA: - if (E_FTP_RESULT_OK == ftp_wait_for_connection(ftp_data.ld_sd, &ftp_data.d_sd, NULL)) { - ftp_data.dtimeout = 0; - ftp_data.substate = E_FTP_STE_SUB_DATA_CONNECTED; - ESP_LOGI(FTP_TAG, "Data socket connected"); - } - else if (ftp_data.dtimeout > FTP_DATA_TIMEOUT_MS) { - ESP_LOGW(FTP_TAG, "Waiting for data connection timeout (%"PRIi32")", ftp_data.dtimeout); - ftp_data.dtimeout = 0; - // close the listening socket - closesocket(ftp_data.ld_sd); - ftp_data.ld_sd = -1; - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; - } - break; - case E_FTP_STE_SUB_DATA_CONNECTED: - if (ftp_data.state == E_FTP_STE_READY && (ftp_data.dtimeout > FTP_DATA_TIMEOUT_MS)) { - // close the listening and the data socket - closesocket(ftp_data.ld_sd); - closesocket(ftp_data.d_sd); - ftp_data.ld_sd = -1; - ftp_data.d_sd = -1; - ftp_close_filesystem_on_error (); - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; - ESP_LOGW(FTP_TAG, "Data connection timeout"); - } - break; - default: - break; - } - - // check the state of the data sockets - if (ftp_data.d_sd < 0 && (ftp_data.state > E_FTP_STE_READY)) { - ftp_data.substate = E_FTP_STE_SUB_DISCONNECTED; - ftp_data.state = E_FTP_STE_READY; - ESP_LOGI(FTP_TAG, "Data socket disconnected"); - } - - //xSemaphoreGive(ftp_mutex); - return 0; -} - -//---------------------- -bool ftp_enable (void) { - bool res = false; - if (ftp_data.state == E_FTP_STE_DISABLED) { - ftp_data.enabled = true; - res = true; - } - return res; -} - -//------------------------- -bool ftp_isenabled (void) { - bool res = (ftp_data.enabled == true); - return res; -} - -//----------------------- -bool ftp_disable (void) { - bool res = false; - if (ftp_data.state == E_FTP_STE_READY) { - _ftp_reset(); - ftp_data.enabled = false; - ftp_data.state = E_FTP_STE_DISABLED; - res = true; - } - return res; -} - -//--------------------- -bool ftp_reset (void) { - _ftp_reset(); - return true; -} - -// Return current ftp server state -//------------------ -int ftp_getstate() { - int fstate = ftp_data.state | (ftp_data.substate << 8); - if ((ftp_data.state == E_FTP_STE_READY) && (ftp_data.c_sd > 0)) fstate = E_FTP_STE_CONNECTED; - return fstate; -} - -//------------------------- -bool ftp_terminate (void) { - bool res = false; - if (ftp_data.state == E_FTP_STE_READY) { - ftp_stop = 1; - _ftp_reset(); - res = true; - } - return res; -} - -//------------------------- -bool ftp_stop_requested() { - bool res = (ftp_stop == 1); - return res; -} - -#if 0 -//------------------------------- -int32_t ftp_get_maxstack (void) { - int32_t maxstack = ftp_stack_size - uxTaskGetStackHighWaterMark(NULL); - return maxstack; -} -#endif - -//------------------------------- -void ftp_task (void *pvParameters) -{ - ESP_LOGI(FTP_TAG, "ftp_task start"); - ftp_deinit(); - esp_log_level_set(FTP_TAG, ESP_LOG_DEBUG); - strcpy(ftp_user, "anonymous"); - strcpy(ftp_pass, ""); - ESP_LOGI(FTP_TAG, "ftp_user:[%s] ftp_pass:[%s]", ftp_user, ftp_pass); - - uint64_t elapsed, time_ms = mp_hal_ticks_ms(); - // Initialize ftp, create rx buffer and mutex - if (!ftp_init()) { - ESP_LOGE("[Ftp]", "Init Error"); - xEventGroupSetBits(xEventTask, FTP_TASK_FINISH_BIT); - vTaskDelete(NULL); - } - - // We have network connection, enable ftp - ftp_enable(); - - time_ms = mp_hal_ticks_ms(); - while (1) { - // Calculate time between two ftp_run() calls - elapsed = mp_hal_ticks_ms() - time_ms; - time_ms = mp_hal_ticks_ms(); - - int res = ftp_run(elapsed); - if (res < 0) { - if (res == -1) { - ESP_LOGE("[Ftp]", "\nRun Error"); - } - // -2 is returned if Ftp stop was requested by user - break; - } - - vTaskDelay(1); - - } // end while - - ESP_LOGW("[Ftp]", "\nTask terminated!"); - ftp_deinit(); - xEventGroupSetBits(xEventTask, FTP_TASK_FINISH_BIT); - vTaskDelete(NULL); -} diff --git a/main/ftp.h b/main/ftp.h deleted file mode 100644 index 5c718b8..0000000 --- a/main/ftp.h +++ /dev/null @@ -1,214 +0,0 @@ -/* - * This file is part of the MicroPython ESP32 project, https://github.com/loboris/MicroPython_ESP32_psRAM_LoBo - * - * The MIT License (MIT) - * - * Copyright (c) 2018 LoBo (https://github.com/loboris) - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/* - * This file is based on 'ftp' from Pycom Limited. - * - * Author: LoBo, loboris@gmail.com - * Copyright (c) 2017, LoBo - */ - -#ifndef FTP_H_ -#define FTP_H_ - -#include "sdkconfig.h" -#include "freertos/FreeRTOS.h" -#include "freertos/queue.h" -#include "sys/dirent.h" - -/****************************************************************************** - DEFINE PRIVATE CONSTANTS - ******************************************************************************/ -#define FTP_CMD_PORT 21 -#define FTP_ACTIVE_DATA_PORT 20 -#define FTP_PASIVE_DATA_PORT 2024 -#define FTP_CMD_SIZE_MAX 6 -#define FTP_CMD_CLIENTS_MAX 1 -#define FTP_DATA_CLIENTS_MAX 1 -#define FTP_MAX_PARAM_SIZE (MICROPY_ALLOC_PATH_MAX + 1) -#define FTP_UNIX_SECONDS_180_DAYS 15552000 -#define FTP_DATA_TIMEOUT_MS 10000 // 10 seconds -#define FTP_SOCKETFIFO_ELEMENTS_MAX 4 - - -typedef enum { - E_FTP_STE_DISABLED = 0, - E_FTP_STE_START, - E_FTP_STE_READY, - E_FTP_STE_END_TRANSFER, - E_FTP_STE_CONTINUE_LISTING, - E_FTP_STE_CONTINUE_FILE_TX, - E_FTP_STE_CONTINUE_FILE_RX, - E_FTP_STE_CONNECTED -} ftp_state_t; - -typedef enum { - E_FTP_STE_SUB_DISCONNECTED = 0, - E_FTP_STE_SUB_LISTEN_FOR_DATA, - E_FTP_STE_SUB_DATA_CONNECTED -} ftp_substate_t; - - -/****************************************************************************** - DEFINE PRIVATE TYPES - ******************************************************************************/ -typedef enum { - E_FTP_RESULT_OK = 0, - E_FTP_RESULT_CONTINUE, - E_FTP_RESULT_FAILED -} ftp_result_t; - -typedef struct { - bool uservalid : 1; - bool passvalid : 1; -} ftp_loggin_t; - -typedef enum { - E_FTP_NOTHING_OPEN = 0, - E_FTP_FILE_OPEN, - E_FTP_DIR_OPEN -} ftp_e_open_t; - -typedef enum { - E_FTP_CLOSE_NONE = 0, - E_FTP_CLOSE_DATA, - E_FTP_CLOSE_CMD_AND_DATA, -} ftp_e_closesocket_t; - -typedef struct { - uint8_t *dBuffer; - uint32_t ctimeout; - union { - DIR *dp; - FILE *fp; - }; - int32_t lc_sd; - int32_t ld_sd; - int32_t c_sd; - int32_t d_sd; - int32_t dtimeout; - uint32_t ip_addr; - uint8_t state; - uint8_t substate; - uint8_t txRetries; - uint8_t logginRetries; - ftp_loggin_t loggin; - uint8_t e_open; - bool closechild; - bool enabled; - bool listroot; - uint32_t total; - uint32_t time; -} ftp_data_t; - -typedef struct { - char * cmd; -} ftp_cmd_t; - -typedef enum { - E_FTP_CMD_NOT_SUPPORTED = -1, - E_FTP_CMD_FEAT = 0, - E_FTP_CMD_SYST, - E_FTP_CMD_CDUP, - E_FTP_CMD_CWD, - E_FTP_CMD_PWD, - E_FTP_CMD_XPWD, - E_FTP_CMD_SIZE, - E_FTP_CMD_MDTM, - E_FTP_CMD_TYPE, - E_FTP_CMD_USER, - E_FTP_CMD_PASS, - E_FTP_CMD_PASV, - E_FTP_CMD_LIST, - E_FTP_CMD_RETR, - E_FTP_CMD_STOR, - E_FTP_CMD_DELE, - E_FTP_CMD_RMD, - E_FTP_CMD_MKD, - E_FTP_CMD_RNFR, - E_FTP_CMD_RNTO, - E_FTP_CMD_NOOP, - E_FTP_CMD_QUIT, - E_FTP_CMD_APPE, - E_FTP_CMD_NLST, - E_FTP_CMD_AUTH, - E_FTP_NUM_FTP_CMDS -} ftp_cmd_index_t; - - - -#define FTP_USER_PASS_LEN_MAX 32 -#define FTP_DEF_USER "micro" -#define FTP_DEF_PASS "python" -#define FTP_MUTEX_TIMEOUT_MS 1000 -#define FTP_CMD_TIMEOUT_MS (CONFIG_MICROPY_FTPSERVER_TIMEOUT*1000) - -#define CONFIG_MICROPY_FTPSERVER_BUFFER_SIZE 1024 -#define CONFIG_MICROPY_FTPSERVER_TIMEOUT 300 -#define ONFIG_MICROPY_FILESYSTEM_TYPE 0 -#define MICROPY_ALLOC_PATH_MAX (512) - -// #define MAX_ACTIVE_INTERFACES 3 -//tcpip_adapter_if_t tcpip_if[MAX_ACTIVE_INTERFACES] = {TCPIP_ADAPTER_IF_MAX}; - -#define VFS_NATIVE_MOUNT_POINT "/_#!#_spiffs" -#define VFS_NATIVE_SDCARD_MOUNT_POINT "/_#!#_sdcard" -#define VFS_NATIVE_INTERNAL_PART_LABEL "internalfs" -#define VFS_NATIVE_INTERNAL_MP "/flash" -#define VFS_NATIVE_EXTERNAL_MP "/sd" -#define VFS_NATIVE_TYPE_SPIFLASH 0 -#define VFS_NATIVE_TYPE_SDCARD 1 - -#ifndef MIN -#define MIN(x, y) ((x) < (y) ? (x) : (y)) -#endif -#ifndef MAX -#define MAX(x, y) ((x) > (y) ? (x) : (y)) -#endif - -#if 0 -extern const char *FTP_TAG; -extern char ftp_user[FTP_USER_PASS_LEN_MAX + 1]; -extern char ftp_pass[FTP_USER_PASS_LEN_MAX + 1]; -extern uint32_t ftp_stack_size; -extern QueueHandle_t ftp_mutex; -extern int ftp_buff_size; -extern int ftp_timeout; -#endif - -bool ftp_init (void); -void ftp_deinit (void); -int ftp_run (uint32_t elapsed); -bool ftp_enable (void); -bool ftp_isenabled (void); -bool ftp_disable (void); -bool ftp_reset (void); -int ftp_getstate(); -bool ftp_terminate (void); -bool ftp_stop_requested(); -int32_t ftp_get_maxstack (void); -uint64_t mp_hal_ticks_ms(void); - -#endif /* FTP_H_ */ diff --git a/main/landingpage.html b/main/landingpage.html new file mode 100644 index 0000000..c93d088 --- /dev/null +++ b/main/landingpage.html @@ -0,0 +1,159 @@ + + + + Control Panel + + + + + + + + + + + + +
-System Time
+ + + + + + + diff --git a/main/main.c b/main/main.c index 2aa4ba5..55fa6cf 100644 --- a/main/main.c +++ b/main/main.c @@ -10,7 +10,8 @@ #include "rtc.h" #include "sensors.h" #include "solar.h" -#include "rf.h" +#include "rf_433.h" +#include "webserver.h" #define TAG "MAIN" @@ -140,9 +141,10 @@ void app_main(void) { /*** FULL BOOT ***/ if (uart_init() != ESP_OK) ESP_LOGE(TAG, "UART FAILED"); if (power_init() != ESP_OK) ESP_LOGE(TAG, "POWER FAILED"); - if (rf_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED"); + if (rf_433_init() != ESP_OK) ESP_LOGE(TAG, "RF FAILED"); if (fsm_init() != ESP_OK) ESP_LOGE(TAG, "FSM FAILED"); if (sensors_init() != ESP_OK) ESP_LOGE(TAG, "SENSORS FAILED"); + if (webserver_init() != ESP_OK) ESP_LOGE(TAG, "WEBSERVER FAILED"); /*** MAIN LOOP ***/ diff --git a/main/power_mgmt.c b/main/power_mgmt.c index 9bc8089..f4d9bb7 100644 --- a/main/power_mgmt.c +++ b/main/power_mgmt.c @@ -123,7 +123,7 @@ esp_err_t process_battery_voltage(void) ema_battery = (float)raw; ema_battery_init = true; } else { - float alpha = get_param(PARAM_ADC_ALPHA_BATTERY).f32; + float alpha = get_param_value_t(PARAM_ADC_ALPHA_BATTERY).f32; if (isnan(raw)) { ESP_LOGI(TAG, "RAW BATTERY IS NAN"); } else { @@ -182,7 +182,7 @@ esp_err_t process_bridge_current(bridge_t bridge) { channel->ema_current = (float)raw_a; channel->ema_init = true; } else { - float alpha = get_param(PARAM_ADC_ALPHA_ISENS).f32; + float alpha = get_param_value_t(PARAM_ADC_ALPHA_ISENS).f32; if (isnan(raw_a)) { ESP_LOGI(TAG, "RAW BATTERY IS NAN"); channel->ema_current = NAN; @@ -197,14 +197,14 @@ esp_err_t process_bridge_current(bridge_t bridge) { // === AUTO-ZERO LEARNING PHASE === if (now > channel->az_enable_time) { //ESP_LOGI(TAG, "AZING %d", bridge); - float db = get_param(PARAM_ADC_DB_IAZ).f32; + float db = get_param_value_t(PARAM_ADC_DB_IAZ).f32; if (isnan(db) || fabsf(channel->ema_current) <= db) { // Valid zero sample if (!channel->az_initialized) { channel->az_offset = channel->ema_current; channel->az_initialized = true; } else { - float alpha = get_param(PARAM_ADC_ALPHA_IAZ).f32; + float alpha = get_param_value_t(PARAM_ADC_ALPHA_IAZ).f32; if (isnan(raw_a)) { ESP_LOGI(TAG, "RAW BATTERY IS NAN"); } else { @@ -231,13 +231,13 @@ esp_err_t process_bridge_current(bridge_t bridge) { float I_nominal = NAN; switch(bridge) { case BRIDGE_DRIVE: - I_nominal = get_param(PARAM_EFUSE_INOM_1).f32; + I_nominal = get_param_value_t(PARAM_EFUSE_INOM_1).f32; break; case BRIDGE_JACK: - I_nominal = get_param(PARAM_EFUSE_INOM_2).f32; + I_nominal = get_param_value_t(PARAM_EFUSE_INOM_2).f32; break; case BRIDGE_AUX: - I_nominal = get_param(PARAM_EFUSE_INOM_3).f32; + I_nominal = get_param_value_t(PARAM_EFUSE_INOM_3).f32; break; } @@ -245,7 +245,7 @@ esp_err_t process_bridge_current(bridge_t bridge) { float I_norm = fabsf(channel->current / I_nominal); // Instant trip on extreme overcurrent - if (I_norm >= get_param(PARAM_EFUSE_KINST).f32) { + if (I_norm >= get_param_value_t(PARAM_EFUSE_KINST).f32) { channel->tripped = true; channel->trip_time = now; ESP_LOGI(TAG, "FUSE TRIP: Inom: %+.5f HEAT:%+2.5f", I_norm, channel->heat); @@ -259,7 +259,7 @@ esp_err_t process_bridge_current(bridge_t bridge) { if (I_norm < 1.0f) { // if we are hot we radiate more heat // (I^2/I^2*t) * (1/t) * t = I^2/I^2*t - channel->heat -= channel->heat * get_param(PARAM_EFUSE_TAUCOOL).f32 * UPDATE_S; + channel->heat -= channel->heat * get_param_value_t(PARAM_EFUSE_TAUCOOL).f32 * UPDATE_S; channel->heat = fmaxf(0.0f, channel->heat); // keep it from going negative // channel.tripped = false; // Auto-clear if cooled (WTF why this is insane) } @@ -267,7 +267,7 @@ esp_err_t process_bridge_current(bridge_t bridge) { // If built-up heat exceeds the time limit, trip // Recall units of heat are (current_actual^2/current_nominal^2)*time // Ergo, heat is measured in seconds - if (channel->heat > get_param(PARAM_EFUSE_HEAT_THRESH).f32) { + if (channel->heat > get_param_value_t(PARAM_EFUSE_HEAT_THRESH).f32) { channel->tripped = true; channel->trip_time = now; @@ -275,7 +275,7 @@ esp_err_t process_bridge_current(bridge_t bridge) { // And enough time has passed // Go ahead and reset the e-fuse } else if (channel->tripped && - (now - channel->trip_time) > get_param(PARAM_EFUSE_TCOOL).i64) { + (now - channel->trip_time) > get_param_value_t(PARAM_EFUSE_TCOOL).i64) { channel->tripped = false; // channel.heat = 0.0f // I think we should wait for the e-fuse to catch up } diff --git a/main/rf.h b/main/rf.h deleted file mode 100644 index 33a4b61..0000000 --- a/main/rf.h +++ /dev/null @@ -1,30 +0,0 @@ - -#ifndef RF_H -#define RF_H - -#include -#include - - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" - -#define NUM_RF_BUTTONS 8 - -int64_t recieveKeycode(); - -esp_err_t rf_init(); -esp_err_t rf_stop(); - -void rf_set_keycode(uint8_t index, int64_t code); - -int8_t rf_get_keycode(); -int64_t rf_get_raw_keycode(); - -void rf_clear_queue(); - -void rf_learn_keycode(uint8_t index); -void rf_cancel_learn_keycode(); - -#endif \ No newline at end of file diff --git a/main/rf.c b/main/rf_433.c similarity index 92% rename from main/rf.c rename to main/rf_433.c index 9032417..0145648 100644 --- a/main/rf.c +++ b/main/rf_433.c @@ -15,7 +15,7 @@ #include "driver/rmt_rx.h" #include "driver/gpio.h" #include "esp_log.h" -#include "rf.h" +#include "rf_433.h" #include "storage.h" @@ -51,7 +51,7 @@ static bool rfrx_done(rmt_channel_handle_t channel, const rmt_rx_done_event_data } // Task that receives and decodes RF signals -static void rf_receiver_task(void* param) { +static void rf_433_receiver_task(void* param) { esp_task_wdt_add(NULL); esp_log_level_set("rmt", ESP_LOG_NONE); // disable rmt messages about hw buffer too small const uint16_t tlow = (P_HIGH - P_LOW - (2 * P_MARGIN)); @@ -137,7 +137,7 @@ static void rf_receiver_task(void* param) { ESP_LOGI(TAG, "GOT KEYCODE 0x%lx [%d]", (long) code, len); if (learn_flag >= 0) { - set_param(PARAM_KEYCODE_0 + learn_flag, + set_param_value_t(PARAM_KEYCODE_0 + learn_flag, (param_value_t){.i64 = encoded}); ESP_LOGI(TAG, "LEARNED KEYCODE"); learn_flag = -1; @@ -156,7 +156,7 @@ static void rf_receiver_task(void* param) { for (uint8_t i = 0; i < NUM_RF_BUTTONS; i++) { - int64_t match = get_param(PARAM_KEYCODE_0+i).i64; + int64_t match = get_param_value_t(PARAM_KEYCODE_0+i).i64; if (encoded == match) { switch (i) { case 0: pulseOverride(RELAY_A1); pulseOverride(RELAY_A3); break; @@ -216,36 +216,36 @@ static void rf_receiver_task(void* param) { vTaskDelete(NULL); } -esp_err_t rf_init() { +esp_err_t rf_433_init() { g_code_queue = xQueueCreate(5, sizeof(rf_code_t)); assert(g_code_queue); - xTaskCreate(rf_receiver_task, TAG, 4096, NULL, 10, NULL); + xTaskCreate(rf_433_receiver_task, TAG, 4096, NULL, 10, NULL); return ESP_OK; } -esp_err_t rf_stop() { return ESP_OK; } +esp_err_t rf_433_stop() { return ESP_OK; } -void rf_set_keycode(uint8_t index, int64_t code) { - set_param(PARAM_KEYCODE_0+index, (param_value_t){.i64=code}); +void rf_433_set_keycode(uint8_t index, int64_t code) { + set_param_value_t(PARAM_KEYCODE_0+index, (param_value_t){.i64=code}); } -void rf_learn_keycode(uint8_t index) { +void rf_433_learn_keycode(uint8_t index) { if (index >= 8) return; learn_flag = index; } -void rf_cancel_learn_keycode() { +void rf_433_cancel_learn_keycode() { learn_flag = -1; } -int8_t rf_get_keycode() { +/*int8_t rf_433_get_keycode() { rf_code_t received_code; if (xQueueReceive(g_code_queue, &received_code, 0) == pdPASS) { int64_t newcode = ((int64_t)received_code.num_symbols << 56) | received_code.code; for (uint8_t i = 0; i < NUM_RF_BUTTONS; i++) { - if (newcode == get_param(PARAM_KEYCODE_0+i).i64) + if (newcode == get_param_value_t(PARAM_KEYCODE_0+i).i64) return i; } ESP_LOGI("RF", "Received unknown code 0x%08lx (%d) [0x%16llx]", (unsigned long)received_code.code, received_code.num_symbols, (unsigned long long) newcode); @@ -253,7 +253,7 @@ int8_t rf_get_keycode() { return -1; } -int64_t rf_get_raw_keycode() { +int64_t rf_433_get_raw_keycode() { rf_code_t received_code; int64_t code = -1; if (xQueueReceive(g_code_queue, &received_code, 0) == pdPASS) { @@ -261,8 +261,8 @@ int64_t rf_get_raw_keycode() { //ESP_LOGI("RF", "Raw Code 0x%08lx (%d) [0x%16llx]", (unsigned long)received_code.code, received_code.num_symbols, (unsigned long long) code); } return code; -} +}*/ -void rf_clear_queue() { +void rf_433_clear_queue() { xQueueReset(g_code_queue); } \ No newline at end of file diff --git a/main/rf_433.h b/main/rf_433.h new file mode 100644 index 0000000..d982d40 --- /dev/null +++ b/main/rf_433.h @@ -0,0 +1,30 @@ + +#ifndef RF_H +#define RF_H + +#include +#include + + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" + +#define NUM_RF_BUTTONS 8 + +int64_t recieveKeycode(); + +esp_err_t rf_433_init(); +esp_err_t rf_433_stop(); + +void rf_433_set_keycode(uint8_t index, int64_t code); + +/* +int8_t rf_433_get_keycode(); +int64_t rf_433_get_raw_keycode(); +*/ + +void rf_433_learn_keycode(uint8_t index); +void rf_433_cancel_learn_keycode(); + +#endif \ No newline at end of file diff --git a/main/rtc.c b/main/rtc.c index 6e5cdee..8f7796f 100644 --- a/main/rtc.c +++ b/main/rtc.c @@ -24,7 +24,6 @@ #include "freertos/task.h" #include "i2c.h" #include "rtc_wdt.h" -#include "filemgmt.h" //#include "esp32/rtc_clk.h" // For RTC_SLOW_FREQ_32K_XTAL enum and rtc_clk_slow_freq_set() #include "driver/rtc_io.h" // For RTC I/O handling (optional but recommended for pin configuration) @@ -85,7 +84,6 @@ void enter_deep_sleep(void) void rtc_set_time(struct tm *tm) { rtc_set = true; - start_new_log_file(); struct timeval tv = { .tv_sec = mktime(tm), .tv_usec = 0 }; settimeofday(&tv, NULL); reset_solar_fsm(); diff --git a/main/solar.c b/main/solar.c index 314a19a..eaa711f 100644 --- a/main/solar.c +++ b/main/solar.c @@ -50,18 +50,18 @@ esp_err_t run_solar_fsm() { //if (rtc_is_set()) { switch(current_charge_state) { case CHG_STATE_BULK: - if (now > timer+get_param(PARAM_CHG_BULK_S).i64) { + if (now > timer+get_param_value_t(PARAM_CHG_BULK_S).i64) { current_charge_state = CHG_STATE_FLOAT; } break; case CHG_STATE_FLOAT: // if we have sufficient voltage, reset the timer - if (vbat > get_param(PARAM_CHG_LOW_V).f32) { + if (vbat > get_param_value_t(PARAM_CHG_LOW_V).f32) { timer = now; } - if (now > timer+get_param(PARAM_CHG_LOW_S).i64) { + if (now > timer+get_param_value_t(PARAM_CHG_LOW_S).i64) { timer = now; current_charge_state = CHG_STATE_BULK; } diff --git a/main/storage.c b/main/storage.c index b369d7d..9f0d811 100644 --- a/main/storage.c +++ b/main/storage.c @@ -59,7 +59,7 @@ static bool log_initialized = false; // PARAMETER FUNCTIONS // ============================================================================ -param_value_t get_param(param_idx_t id) { +param_value_t get_param_value_t(param_idx_t id) { if (id >= NUM_PARAMS) { ESP_LOGE(TAG, "Invalid parameter ID: %d", id); param_value_t err = {0}; @@ -68,7 +68,7 @@ param_value_t get_param(param_idx_t id) { return parameter_table[id]; } -esp_err_t set_param(param_idx_t id, param_value_t val) { +esp_err_t set_param_value_t(param_idx_t id, param_value_t val) { if (id >= NUM_PARAMS) { ESP_LOGE(TAG, "Invalid parameter ID: %d", id); return ESP_ERR_INVALID_ARG; diff --git a/main/storage.h b/main/storage.h index e1a1acd..2eeaf40 100644 --- a/main/storage.h +++ b/main/storage.h @@ -122,8 +122,8 @@ extern const char* parameter_names[NUM_PARAMS]; esp_err_t storage_init(); esp_err_t log_init(); -param_value_t get_param(param_idx_t id); -esp_err_t set_param(param_idx_t id, param_value_t val); +param_value_t get_param_value_t(param_idx_t id); +esp_err_t set_param_value_t(param_idx_t id, param_value_t val); param_type_e get_param_type(param_idx_t id); const char* get_param_name(param_idx_t id); param_value_t get_param_default(param_idx_t id); diff --git a/main/uart_comms.c b/main/uart_comms.c index a150293..f748135 100644 --- a/main/uart_comms.c +++ b/main/uart_comms.c @@ -11,7 +11,7 @@ #include "storage.h" #include #include -#include "rf.h" +#include "rf_433.h" #define TAG "UART" @@ -188,7 +188,7 @@ static void cmd_set_param(char *args) { return; } - esp_err_t err = set_param(id, param_val); + esp_err_t err = set_param_value_t(id, param_val); if (err == ESP_OK) { printf("OK: Parameter %u (%s) set to ", id, get_param_name(id)); @@ -223,7 +223,7 @@ static void cmd_get_param(char *args) { } // Get parameter - param_value_t val = get_param(id); + param_value_t val = get_param_value_t(id); printf("Parameter %u (%s) = ", id, get_param_name(id)); print_param_value(id, val); } @@ -262,7 +262,7 @@ static void cmd_reset_param(char *args) { // Reset to default param_value_t default_val = get_param_default(id); - esp_err_t err = set_param(id, default_val); + esp_err_t err = set_param_value_t(id, default_val); if (err == ESP_OK) { printf("OK: Parameter %u (%s) reset to default: ", @@ -287,7 +287,7 @@ static void cmd_list_params(char *args) { }; for (int i = 0; i < NUM_PARAMS; i++) { - param_value_t val = get_param(i); + param_value_t val = get_param_value_t(i); param_type_e type = get_param_type(i); printf("%-3d | %-17s | %-4s | ", i, get_param_name(i), type_names[type]); @@ -317,7 +317,7 @@ static void cmd_rf_learn(char *args) { char *id_str = strtok(args, " \t"); if (id_str == NULL) { - rf_cancel_learn_keycode(); + rf_433_cancel_learn_keycode(); return; } @@ -331,7 +331,7 @@ static void cmd_rf_learn(char *args) { param_idx_t id = (param_idx_t)id_u64; if (id < 8) { printf("Listening for keycode for slot %d\n", id); - rf_learn_keycode(id); + rf_433_learn_keycode(id); return; } printf("ERROR: Keycode slot index out of bounds.\n"); diff --git a/main/webpage.h b/main/webpage.h new file mode 100644 index 0000000..114ff89 --- /dev/null +++ b/main/webpage.h @@ -0,0 +1,3 @@ +const char html_content[] = {0x1f,0x8b,0x08,0x00,0x22,0x72,0x4f,0x69,0x02,0xff,0xa5,0x57,0x6d,0x73,0x1a,0x39,0x12,0xfe,0xce,0xaf,0x90,0x9d,0xbd,0x14,0x93,0x85,0x01,0x6c,0x57,0xd6,0x0b,0x0c,0x59,0x5f,0xec,0x6c,0xbc,0xe5,0x24,0xae,0xe0,0x6c,0x5d,0x95,0xcb,0xc5,0xc8,0x33,0x3d,0xa0,0x8b,0x46,0x9a,0x93,0x84,0x81,0x23,0xfc,0xf7,0xed,0xd6,0xf0,0x6a,0xe3,0xe4,0xea,0x36,0x29,0x83,0x46,0xd3,0x6a,0x3d,0xfd,0xf4,0x2b,0xdd,0x83,0x54,0x27,0x6e,0x56,0x00,0x1b,0xb9,0x5c,0xf6,0xba,0x4e,0x38,0x09,0xbd,0xb7,0x5a,0x39,0xa3,0x25,0xbb,0xe6,0x0a,0x64,0xb7,0x51,0x6e,0x76,0xad,0x9b,0xe1,0xd7,0xab,0x79,0xa2,0xa5,0x36,0xed,0x17,0x00,0xd0,0xb9,0xe7,0xc9,0xd7,0xa1,0xd1,0x63,0x95,0xd6,0x97,0xbb,0xad,0x56,0xab,0x93,0xe1,0xf1,0x7a,0xc6,0x73,0x21,0x67,0x6d,0xcb,0x95,0xad,0x5b,0x30,0x22,0x5b,0x08,0x55,0x8c,0xdd,0xdc,0xc1,0xd4,0xd5,0xb9,0x14,0x43,0xd5,0x36,0x62,0x38,0x72,0x9d,0x7b,0x3d,0xad,0x5b,0xf1,0x5f,0xa1,0x86,0xed,0x7b,0x6d,0x52,0x30,0x75,0xdc,0xd9,0xa3,0xf9,0xf8,0xf8,0xb8,0x53,0x0a,0xb4,0x5b,0xc5,0x94,0x59,0x2d,0x45,0xca,0x5e,0xbc,0x7e,0xfd,0xba,0x33,0x11,0xa9,0x1b,0xb5,0x5b,0xcd,0xe6,0x3f,0x76,0xae,0xce,0xb5,0xd2,0xb6,0xe0,0x09,0x2c,0xc2,0x64,0xc4,0xd5,0x10,0xd2,0xf9,0x06,0xe4,0x81,0xc8,0x0b,0x6d,0x1c,0x57,0x6e,0xdf,0x55,0xe9,0xf1,0xe6,0xfd,0xe2,0x45,0xa2,0xf3,0x5c,0xb8,0xc1,0xbd,0x53,0x5b,0x0a,0x3a,0xc9,0xd8,0x58,0x5c,0x17,0x5a,0x28,0x07,0x66,0xbf,0x96,0x15,0x60,0xa5,0x15,0x6c,0xc3,0xcc,0xb9,0x19,0x0a,0x55,0x77,0xba,0xc0,0xe7,0x62,0xda,0x29,0x78,0x9a,0x12,0x01,0xfe,0xc1,0xdb,0x30,0x01,0x22,0xa7,0xfd,0x4b,0xb3,0xb9,0x7d,0xff,0x6d,0x2a,0x2c,0xbf,0x97,0x90,0xde,0xad,0x90,0x9c,0x9e,0x9e,0xae,0x90,0x28,0x4d,0xc4,0x4a,0x3d,0x81,0x74,0x0f,0x9a,0x93,0x93,0x93,0x85,0xa3,0xb3,0xf3,0x25,0xcb,0xb8,0x2f,0x79,0x61,0xa1,0xbd,0x5a,0x6c,0x01,0x5c,0xb8,0x74,0xbe,0x76,0x86,0x73,0x3a,0xdf,0xa6,0xfc,0xe8,0xe8,0x68,0x0d,0xf8,0xb4,0x98,0x2e,0x9c,0x69,0x8f,0xf4,0x03,0x98,0xf9,0x9e,0x68,0xe0,0xf4,0x7f,0xd1,0x6d,0x94,0xb1,0xd3,0x6d,0x8c,0x80,0xa7,0xbd,0xee,0xbd,0x4e,0x67,0x18,0x6b,0x84,0x86,0x89,0x34,0xf2,0x0b,0x7c,0x36,0xf8,0x97,0xf6,0xea,0x18,0x70,0xa9,0x5f,0xf5,0x67,0xd6,0x41,0xce,0x6e,0x44,0x0e,0xeb,0xbd,0xae,0x8f,0x22,0x3a,0x25,0xd4,0xc0,0xe1,0x1b,0xa6,0x55,0xe9,0xdd,0x08,0x49,0xfd,0xfa,0xb6,0x74,0x74,0xd5,0x8d,0x84,0x0d,0x18,0x1e,0x2f,0xa2,0x16,0xa3,0xf8,0x8e,0x52,0xee,0x80,0xe4,0xeb,0x52,0x27,0x1c,0x23,0x7d,0xad,0xb0,0x51,0x7e,0x98,0x15,0x00,0x86,0xd8,0x31,0x6c,0x54,0x74,0x82,0x40,0xc7,0x68,0xbc,0x62,0x2b,0xd6,0xe9,0xda,0x8d,0x33,0xe8,0x66,0x29,0x92,0xaf,0xab,0xad,0x82,0x1b,0x9e,0xdb,0x6a,0xd0,0xeb,0xf3,0x07,0x60,0x25,0x12,0xdb,0x6d,0x94,0x3a,0xb6,0xef,0x69,0x2c,0x0d,0x2e,0x4d,0xe1,0x49,0x02,0x85,0x8b,0xc2,0x7b,0xa1,0x48,0x7f,0x26,0x4c,0x3e,0xe1,0x06,0x06,0x99,0x40,0x76,0x3c,0x74,0x5a,0xad,0xb1,0xa0,0xc8,0xb8,0x90,0x9a,0xa7,0x3b,0x10,0xca,0xad,0x77,0xcb,0xb3,0x88,0xe1,0x8b,0xdf,0x60,0xab,0x9d,0x0d,0x0c,0x9b,0x18,0x51,0xb8,0x9e,0x04,0xc7,0x3c,0xe0,0xc1,0x03,0x97,0x63,0xb0,0xd1,0xed,0x5d,0x27,0xd1,0xca,0xae,0x76,0x15,0xcf,0x69,0x33,0x3e,0x37,0x02,0x8d,0x39,0x17,0x16,0x53,0x21,0x81,0xb8,0x16,0xdf,0x5c,0x9f,0xbf,0xc3,0xaf,0x8b,0x6c,0x6c,0x81,0x9d,0xe5,0x0e,0xd7,0xbf,0x73,0xa1,0xf0,0xeb,0x53,0x96,0x59,0x70,0xf1,0x5d,0xad,0xd4,0x30,0x56,0xc2,0x91,0x06,0xff,0x2e,0x23,0x39,0xbf,0xfa,0x13,0xff,0x72,0x1b,0xdf,0x75,0xb2,0xb1,0x4a,0x9c,0x40,0x8b,0x86,0x50,0x9d,0x06,0x73,0x03,0x6e,0x6c,0x90,0x6a,0x9d,0x8c,0x73,0x50,0x2e,0x1c,0x82,0xbb,0x90,0x40,0xcb,0x7f,0xce,0x2e,0x53,0x94,0x58,0x54,0x1a,0x0d,0xf6,0x1e,0xb3,0x42,0x52,0x66,0xb0,0x65,0x4a,0x33,0x4f,0xa2,0x65,0x5c,0xa5,0x0c,0x94,0x8f,0x28,0x37,0x02,0x66,0xc9,0x05,0xa5,0xc9,0x95,0xf5,0x45,0xdb,0x01,0x02,0x32,0x98,0x83,0x0c,0x13,0xc9,0xad,0xbd,0x42,0xeb,0x42,0x0c,0xe8,0x6a,0xbc,0x54,0x1a,0x07,0x35,0x04,0x15,0x6f,0x5c,0x1d,0x07,0xe1,0x2a,0x06,0xa2,0x83,0x96,0x47,0x52,0xaf,0xd7,0x59,0x2b,0x64,0xbf,0x5f,0xdc,0xb0,0xf3,0xb3,0x9b,0x33,0x7a,0xde,0xdc,0x94,0x81,0x4b,0x46,0x7d,0xc7,0xdd,0x18,0xe3,0x61,0x4e,0x5c,0x4f,0x47,0x26,0x52,0x30,0x61,0xff,0xfa,0x70,0xf5,0xde,0xb9,0xe2,0x33,0xfc,0x07,0x49,0x77,0x1d,0xdc,0x0e,0x75,0x01,0xaa,0x1a,0xa3,0x22,0x64,0x66,0x84,0xef,0xda,0x8d,0x46,0xeb,0xd7,0xa3,0xb0,0xf5,0xfa,0x34,0x3c,0x09,0x5b,0x98,0x3a,0xa4,0x26,0xae,0x1d,0x34,0x83,0x9a,0x17,0x57,0xe4,0xd9,0x68,0x75,0x17,0xea,0x17,0x59,0x95,0x5e,0x94,0x82,0x51,0x14,0x1d,0x35,0x9b,0x81,0x33,0xb3,0x39,0xf9,0x53,0x4b,0x08,0xa5,0x1e,0x7a,0x01,0x03,0xb6,0xc0,0x2d,0xb8,0xc1,0x12,0x1c,0x74,0x08,0x15,0x26,0x05,0x8f,0xfe,0xe8,0x7f,0xfa,0x18,0xa2,0xcf,0x2c,0xec,0x91,0x22,0x53,0xbf,0x14,0x94,0x3c,0xcc,0x67,0x5b,0x26,0x40,0x22,0xe9,0x19,0xe3,0x0f,0x5c,0x48,0x62,0xa4,0x82,0xd7,0x93,0x9e,0x90,0xde,0x97,0xc6,0x92,0x78,0x54,0x6d,0xbc,0x62,0xbf,0x0d,0x06,0xd7,0x5f,0x3e,0x5f,0x0c,0x06,0xec,0x55,0x83,0x91,0xf9,0xe7,0xf8,0x66,0x23,0xfd,0xaa,0x05,0xc7,0x41,0x10,0x3a,0x7d,0xd9,0xff,0xd4,0x77,0x06,0x0b,0x4a,0x35,0x08,0x2d,0x06,0x34,0x54,0x9b,0xb5,0xd6,0xaf,0x41,0x87,0xbc,0xb0,0xcc,0x73,0x74,0x81,0x8f,0x54,0x9f,0xc8,0xde,0x03,0x7d,0xa7,0x0d,0xb0,0x32,0x7c,0x59,0x35,0x85,0x8c,0x8f,0xa5,0x63,0x4e,0x33,0xc8,0x0b,0x37,0x63,0xdc,0x18,0x3e,0x23,0xa4,0xb9,0xb0,0x16,0x55,0x07,0x95,0x9d,0x78,0xf7,0x20,0xca,0x94,0xfd,0xf6,0xed,0xf6,0x6e,0x91,0x70,0x74,0x59,0x15,0x0d,0x58,0xb1,0x06,0xc6,0x68,0x53,0x8d,0x2f,0xe8,0x8b,0x92,0x82,0x74,0x30,0xe2,0x2a,0xae,0x41,0x19,0x8c,0x67,0x72,0xc2,0x67,0x96,0x19,0x50,0x58,0x28,0x59,0x59,0xce,0xe0,0x01,0x14,0x5d,0x6a,0x4a,0x07,0xb3,0x0c,0x59,0xb2,0x0c,0x35,0xd0,0x7d,0x4c,0xd8,0x12,0x5c,0xa5,0x3c,0x73,0x43,0x47,0xaa,0xc1,0x62,0xe9,0x57,0x7f,0xe3,0xc6,0xb1,0x4f,0xb1,0x7c,0x04,0x37,0xd1,0xe6,0x2b,0xf3,0x8f,0x04,0xa3,0xb6,0x47,0x8f,0xc5,0x2d,0x5c,0xaf,0x63,0x71,0x47,0xc4,0xbb,0xc7,0x23,0x8d,0x88,0x5b,0xbf,0x8a,0x4b,0x2f,0xbf,0x95,0xc0,0x0d,0x83,0x29,0x26,0x03,0x59,0xea,0xa9,0x01,0xec,0x6c,0xcc,0xe8,0x09,0xf2,0xeb,0x3f,0xef,0x11,0x00,0x90,0x81,0xa8,0x72,0xca,0x9a,0x3e,0xeb,0x28,0xdd,0x30,0x8b,0x1c,0xc9,0x05,0x95,0x0c,0x71,0x76,0xbc,0xda,0x90,0x4e,0x84,0x12,0xd4,0xd0,0x8d,0x7a,0x47,0x9d,0xa0,0xdc,0x4c,0x01,0x11,0xc0,0x67,0x3d,0xa9,0xb6,0xca,0x6b,0xaf,0xb4,0x2e,0x50,0x07,0xb6,0x8d,0xe1,0xc8,0xeb,0xfa,0x78,0xf6,0xe1,0xa2,0xbf,0xf4,0x1e,0x39,0x53,0xd9,0xb1,0xf1,0xbc,0x9a,0x19,0x5b,0x16,0x7f,0xcb,0xec,0x48,0x4f,0x54,0x65,0xab,0x54,0x85,0x78,0xf1,0x05,0x47,0x0f,0x56,0xe9,0xb1,0x26,0x82,0xa8,0xe7,0x6d,0x45,0x10,0x65,0x7f,0x09,0x05,0xc6,0xb4,0x71,0x74,0xf3,0x13,0x78,0x75,0xc4,0x82,0x8f,0x28,0x82,0x3e,0x78,0x7f,0xf3,0xe1,0x2a,0x8a,0x2b,0x6c,0xcf,0x3f,0xea,0x17,0x3f,0xcd,0xc5,0xc2,0xd7,0xf2,0xef,0x48,0x10,0x84,0x1f,0x08,0x2d,0x8b,0xbf,0xaf,0xef,0x87,0x34,0x13,0x1d,0x52,0x65,0x3f,0xc4,0x60,0xa7,0x0b,0x0e,0xcb,0xb0,0x8e,0x0e,0x7f,0x9a,0x6f,0x07,0xed,0xad,0xb8,0x3b,0x88,0xa2,0x07,0x8d,0x5d,0xb8,0xf9,0xf2,0xe5,0xd3,0x37,0x6a,0x2c,0xe5,0x9b,0x47,0xdb,0xed,0x98,0x76,0x63,0x54,0xa9,0x95,0xbf,0x33,0x3a,0x7c,0xd2,0x28,0x0f,0x7b,0x3f,0xb2,0x68,0xab,0xa4,0xa3,0xce,0x6f,0xdf,0xe2,0xf8,0x19,0xfb,0xe2,0x45,0xb0,0x2e,0x8f,0x47,0x21,0xbb,0xfe,0xd4,0xdf,0x57,0x1f,0x1f,0x75,0xcc,0xf9,0x77,0xaa,0x6d,0xb3,0xb6,0x6e,0x09,0x98,0x51,0x66,0xd6,0xc7,0x00,0x4a,0x30,0xf3,0xcf,0xa4,0xa4,0xda,0x80,0x06,0x85,0xeb,0xaa,0xbd,0x0e,0x82,0xd2,0xd0,0xde,0xf7,0x2a,0x2f,0x56,0xad,0xf2,0x34,0xd2,0x1e,0x45,0x9b,0x2a,0x33,0x27,0xf4,0x34,0x72,0x30,0xd4,0x9a,0x4a,0x4c,0x85,0x0a,0x69,0x81,0x42,0x27,0xa3,0xe8,0x03,0x77,0xa3,0x30,0x93,0x1a,0x83,0x7c,0x5d,0xcd,0x4a,0x2d,0x9e,0xef,0x80,0xba,0x16,0x9d,0xad,0x06,0x0d,0x2a,0x6d,0x5b,0xe5,0x9d,0x88,0x78,0xae,0xbe,0x6f,0x6a,0x3b,0xf6,0xcf,0x25,0xbe,0xf7,0x38,0x28,0x01,0xa6,0x3c,0x8d,0xe2,0x68,0x7b,0xfd,0x06,0x03,0x05,0xcf,0xf3,0xa2,0xc0,0xf2,0xc8,0x89,0xc4,0xc6,0xbf,0xad,0x46,0xaa,0x36,0x39,0xef,0xeb,0xb8,0xf5,0x65,0x54,0x64,0xb3,0xea,0x9c,0xec,0x69,0x7b,0xdc,0x8b,0x20,0xa8,0x2d,0xa9,0x5a,0xf7,0x3b,0x03,0x39,0x0e,0x6d,0x5b,0x2d,0x6f,0x01,0xd2,0x82,0xb7,0xfe,0x7a,0x9d,0xfa,0x3b,0x14,0xf8,0x99,0xab,0x64,0x2c,0xb4,0x08,0xc3,0x55,0xe3,0x41,0x1c,0xdc,0xb6,0xee,0x6a,0x68,0x7c,0xb4,0x45,0x03,0x16,0xf4,0x2b,0x9c,0x42,0xcd,0x5b,0x8e,0x2d,0x25,0x20,0x76,0x7d,0x08,0xbe,0xa1,0xcf,0xb6,0x6f,0x34,0xef,0xb0,0x85,0xb9,0x1d,0xe6,0x9e,0x52,0xd5,0xb0,0xc5,0xdf,0x27,0xe6,0x69,0xb3,0x7c,0xdc,0x29,0x5f,0xbe,0xfc,0x31,0x33,0xcf,0x73,0x2c,0xd2,0xd2,0xa0,0x4b,0x85,0xe6,0xa4,0x41,0xcd,0x1b,0xd3,0xc6,0x4f,0xa4,0x7c,0xb1,0xd8,0xaa,0xc2,0x8f,0x07,0x34,0x1f,0x9a,0x34,0xd8,0x5d,0xfa,0x50,0xa5,0x04,0xd8,0x99,0xfc,0xb0,0x28,0x63,0x84,0x1e,0xac,0x25,0x42,0x5a,0xad,0xea,0x55,0x30,0xe7,0x12,0x6b,0x19,0x36,0x04,0xed,0x75,0x30,0xeb,0x93,0x82,0xb0,0x76,0xca,0x39,0x6a,0xb1,0x52,0x1f,0x3d,0xd2,0x70,0xdb,0xbc,0xab,0xfd,0x70,0x16,0x79,0x3e,0x58,0xb5,0xe3,0xff,0xa7,0x53,0xf0,0x67,0x26,0xb8,0x3a,0x52,0x07,0x3c,0xff,0x9f,0x9d,0xf3,0x66,0x69,0xe7,0x72,0x9a,0xb5,0x63,0x9c,0x95,0xad,0xcd,0xc6,0x32,0x64,0xe7,0xf0,0x80,0x73,0x02,0xce,0x74,0x33,0xec,0x70,0xf7,0x5a,0xbb,0x30,0x0e,0xda,0xbb,0xe2,0xd4,0x7b,0x21,0x6d,0xb3,0xf8,0xe7,0x8d,0xde,0xe7,0x9a,0xed,0x86,0xd2,0xed,0x1e,0xcb,0xd2,0x31,0xb9,0x7a,0xe9,0xbd,0x9d,0x48,0x20,0x36,0xcb,0x52,0x77,0x89,0x55,0x51,0x70,0x89,0x9d,0x8c,0xa7,0x95,0x09,0xb6,0x46,0xec,0x24,0x2b,0xd3,0x36,0x73,0x60,0x07,0x7f,0x04,0x95,0xa3,0x37,0xce,0xe2,0xfe,0xf7,0x4f,0xc3,0xff,0xe4,0xfe,0x0b,0x7e,0xb9,0xec,0xb3,0x82,0x0f,0x00,0x00}; + +const unsigned int html_content_len = 1798; diff --git a/main/webpage_compile.py b/main/webpage_compile.py new file mode 100644 index 0000000..fe59137 --- /dev/null +++ b/main/webpage_compile.py @@ -0,0 +1,31 @@ +import minify_html +import gzip + +with open("landingpage.html", "r", encoding="utf-8") as fin: + original_html = fin.read() + +minified_html = minify_html.minify( + original_html, + minify_js=True, + minify_css=True, + keep_comments=False, # Remove comments + keep_html_and_head_opening_tags=False, # Remove and if possible + keep_closing_tags=True, # Keep closing tags (safer; set False for more aggression if you test thoroughly) + remove_processing_instructions=True, # Remove etc. + #keep_comments=False, + remove_bangs=False # Keep bang (removing saves bytes but can break some edge cases) +) + +with open("webpage_minified.html", "w") as fout: + fout.write(minified_html) + + +minified_bytes = minified_html.encode('utf-8') +gzipped_bytes = gzip.compress(minified_bytes, compresslevel=9) + + +with open("webpage.h", "w") as fout: + fout.write("const char html_content[] = {") + fout.write(','.join(f'0x{byte:02x}' for byte in gzipped_bytes)) + fout.write("};\n\n") + fout.write(f"const unsigned int html_content_len = {len(gzipped_bytes)};\n") \ No newline at end of file diff --git a/main/webpage_minified.html b/main/webpage_minified.html new file mode 100644 index 0000000..3be2e77 --- /dev/null +++ b/main/webpage_minified.html @@ -0,0 +1,28 @@ +Control Panel
-System Time
\ No newline at end of file diff --git a/main/webserver.c b/main/webserver.c new file mode 100644 index 0000000..0b81a66 --- /dev/null +++ b/main/webserver.c @@ -0,0 +1,333 @@ +/* WiFi softAP Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include "esp_ota_ops.h" +#include "esp_timer.h" +#include "rtc.h" +#include "string.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_wifi.h" +#include "esp_system.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_http_server.h" +#include "esp_netif.h" +#include +#include +#include +#include +#include "stdio.h" +#include "storage.h" +//#include "mdns.h" +#include "webpage.h" + + +#define HOSTNAME "sc.local" +#define SOFT_AP_SSID "stockcropper" +#define SOFT_AP_PASSWORD "stockcropper" +#define SERVER_PORT 80 + +static const char *TAG = "WEBSERVER"; + + +static httpd_handle_t httpServerInstance = NULL; + +char httpBuffer[1024]; + +/* Handler to serve the HTML page */ +static esp_err_t root_get_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "root_get_handler"); + // Send the HTML response + + httpd_resp_set_type(req, "text/html"); // Original MIME type + httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); // Tell browser it's gzipped + return httpd_resp_send(req, (const char *)html_content, html_content_len); +} + +static esp_err_t log_get_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "log_get_handler"); + // Send the HTML response + httpd_resp_set_type(req, "text/html"); + return httpd_resp_send(req, html_content, HTTPD_RESP_USE_STRLEN); +} + +// set time: timestamp (unix epoch, seconds) +static esp_err_t st_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "st_post_handler"); + // Send the HTML response + + int ret=0; + int remaining = req -> content_len; + while (remaining > 0) { + if ((ret = httpd_req_recv(req, httpBuffer, MIN(remaining, sizeof(httpBuffer))))<= 0) { + if(ret == HTTPD_SOCK_ERR_TIMEOUT){ + continue; + } + return ESP_FAIL; + } + + } + + ESP_LOGI(TAG, "ST POST %.*s", ret, httpBuffer); + return httpd_resp_send(req, "200 OK", HTTPD_RESP_USE_STRLEN); +} + +// set parameters id & value +static esp_err_t sp_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "sp_post_handler"); + // Send the HTML response + httpd_resp_set_type(req, "text/html"); + return httpd_resp_send(req, "/sp NOT IMPLEMENTED", HTTPD_RESP_USE_STRLEN); +} + + +static esp_err_t move_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "move_post_handler"); + // Send the HTML response + httpd_resp_set_type(req, "text/html"); + return httpd_resp_send(req, "/move NOT IMPLEMENTED", HTTPD_RESP_USE_STRLEN); +} + + + +static esp_err_t stop_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "stop_post_handler"); + // Send the HTML response + httpd_resp_set_type(req, "text/html"); + return httpd_resp_send(req, "/stop NOT IMPLEMENTED", HTTPD_RESP_USE_STRLEN); +} + + +/* Handler for Status GET request*/ +static esp_err_t status_get_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "status_get_handler"); + size_t head = 0; + + // Set the response type to JSON + httpd_resp_set_type(req, "application/json"); + + // Start building the JSON string with time + head += sprintf(httpBuffer+head, "{\"time\":%lld,\"params\":[", system_rtc_get_raw_time()); + + for (param_idx_t i = 0; i < NUM_PARAMS; i++) { + if (i > 0) { + head += sprintf(httpBuffer+head, ","); + } + + // Retrieve the parameter; assuming get_param(i) returns a param_t struct with union + param_value_t param = get_param_value_t(i); + + // Append the parameter value based on its type + switch (get_param_type(i)) { + case PARAM_TYPE_u8: head+=sprintf(httpBuffer+head, "%u", param.u8); break; + case PARAM_TYPE_i8: head+=sprintf(httpBuffer+head, "%d", param.i8); break; + case PARAM_TYPE_u16: head+=sprintf(httpBuffer+head, "%u", param.u16); break; + case PARAM_TYPE_i16: head+=sprintf(httpBuffer+head, "%d", param.i16); break; + case PARAM_TYPE_u32: head+=sprintf(httpBuffer+head, "%lu", (unsigned long)param.u32); break; + case PARAM_TYPE_i32: head+=sprintf(httpBuffer+head, "%ld", (long)param.i32); break; + case PARAM_TYPE_u64: head+=sprintf(httpBuffer+head, "%llu", param.u64); break; + case PARAM_TYPE_i64: head+=sprintf(httpBuffer+head, "%lld", param.i64); break; + case PARAM_TYPE_f32: head+=sprintf(httpBuffer+head, "%.8f", param.f32); break; + case PARAM_TYPE_f64: head+=sprintf(httpBuffer+head, "%.8f", param.f64); break; + } + } + + // Close the JSON array and object + head += sprintf(httpBuffer+head, "]}"); + + return httpd_resp_send(req, httpBuffer, head); +} +static esp_err_t ota_post_handler(httpd_req_t *req) { + ESP_LOGI(TAG, "OTA POST request received"); + + esp_ota_handle_t update_handle = 0; + const esp_partition_t *update_partition = esp_ota_get_next_update_partition(NULL); + if (update_partition == NULL) { + ESP_LOGE(TAG, "No OTA partition found"); + return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "No OTA partition"); + } + + esp_err_t err = esp_ota_begin(update_partition, OTA_SIZE_UNKNOWN, &update_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err)); + return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA begin failed"); + } + + char buf[1024]; + int recv_len; + int total_len = req->content_len; + int remaining = total_len; + + while (remaining > 0) { + recv_len = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf))); + if (recv_len <= 0) { + if (recv_len == HTTPD_SOCK_ERR_TIMEOUT) { + continue; + } + esp_ota_abort(update_handle); + return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Receive failed"); + } + + err = esp_ota_write(update_handle, (const void *)buf, recv_len); + if (err != ESP_OK) { + esp_ota_abort(update_handle); + ESP_LOGE(TAG, "esp_ota_write failed (%s)", esp_err_to_name(err)); + return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA write failed"); + } + + remaining -= recv_len; + } + + err = esp_ota_end(update_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_end failed (%s)", esp_err_to_name(err)); + return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "OTA end failed"); + } + + err = esp_ota_set_boot_partition(update_partition); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)", esp_err_to_name(err)); + return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Set boot partition failed"); + } + + ESP_LOGI(TAG, "OTA update successful. Rebooting in 2 seconds..."); + + // Send response FIRST + httpd_resp_send(req, "OTA update successful, rebooting...", HTTPD_RESP_USE_STRLEN); + + // THEN delay and reboot + vTaskDelay(pdMS_TO_TICKS(2000)); // Give time for TCP to close properly + esp_restart(); + + return ESP_OK; +} + +httpd_uri_t uris[] = {{ + .uri = "/status", + .method = HTTP_GET, + .handler = status_get_handler, + .user_ctx = NULL +},{ + .uri = "/", + .method = HTTP_GET, + .handler = root_get_handler, + .user_ctx = NULL +},{ + .uri = "/st", + .method = HTTP_ANY, + .handler = st_post_handler, + .user_ctx = NULL +},{ + .uri = "/log", + .method = HTTP_GET, + .handler = log_get_handler, + .user_ctx = NULL +},{ + .uri = "/sp", + .method = HTTP_POST, + .handler = sp_post_handler, + .user_ctx = NULL +},{ + .uri = "/move", + .method = HTTP_POST, + .handler = move_post_handler, + .user_ctx = NULL +},{ + .uri = "/stop", + .method = HTTP_POST, + .handler = stop_post_handler, + .user_ctx = NULL +},{ + .uri = "/ota", + .method = HTTP_POST, + .handler = ota_post_handler, + .user_ctx = NULL +}}; + + +static void startHttpServer(void) { + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.server_port = SERVER_PORT; + + ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); + if (httpd_start(&httpServerInstance, &config) == ESP_OK) { + for (uint8_t i=0; i<(sizeof(uris)/sizeof(httpd_uri_t)); i++) { + httpd_register_uri_handler(httpServerInstance, &uris[i]); + + } + } +} + +/* Event handler for WiFi events */ +static void wifi_event_handler(void* arg, esp_event_base_t event_base, + int32_t event_id, void* event_data) { + if (event_id == WIFI_EVENT_AP_STACONNECTED) { + ESP_LOGI(TAG, "Station connected."); + //startHttpServer(); + } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) { + ESP_LOGI(TAG, "Station disconnected."); + //stopHttpServer(); + } +} + +void launchSoftAp() { + ESP_ERROR_CHECK(nvs_flash_init()); + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // Create default WiFi AP and get the netif handle + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + assert(ap_netif); // Optional: check for NULL + + // Set your custom hostname here (max 32 chars, no spaces/special chars recommended) + ESP_ERROR_CHECK(esp_netif_set_hostname(ap_netif, HOSTNAME)); + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + // Register the event handler + ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, + ESP_EVENT_ANY_ID, + &wifi_event_handler, + NULL, + NULL)); + + wifi_config_t wifi_config = { + .ap = { + .ssid = SOFT_AP_SSID, + .ssid_len = strlen(SOFT_AP_SSID), + .password = SOFT_AP_PASSWORD, + .max_connection = 4, + .authmode = WIFI_AUTH_WPA2_PSK + }, + }; + + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + //dns_server_config_t dns_config = DNS_SERVER_CONFIG_SINGLE(HOSTNAME, "192.168.4.1"); + //ESP_ERROR_CHECK(dns_server_start(&dns_config)); + + //ESP_ERROR_CHECK(mdns_init()); + //ESP_ERROR_CHECK(mdns_hostname_set(HOSTNAME)); // Matches the netif hostname + //ESP_ERROR_CHECK(mdns_instance_name_set("My ESP32 Device")); // Optional friendly name + // After mdns_init() and hostname set + //ESP_ERROR_CHECK(mdns_service_add(NULL, "_http", "_tcp", 80, NULL, 0)); + + ESP_LOGI(TAG, "SoftAP set up. SSID:%s password:%s", SOFT_AP_SSID, SOFT_AP_PASSWORD); +} + +esp_err_t webserver_init(void) { + launchSoftAp(); + startHttpServer(); + return ESP_OK; +} \ No newline at end of file diff --git a/main/webserver.h b/main/webserver.h new file mode 100644 index 0000000..6a792b2 --- /dev/null +++ b/main/webserver.h @@ -0,0 +1,3 @@ +#include "esp_err.h" + +esp_err_t webserver_init(void); \ No newline at end of file diff --git a/sdkconfig b/sdkconfig index edec036..bdba70f 100644 --- a/sdkconfig +++ b/sdkconfig @@ -353,14 +353,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set CONFIG_ESPTOOLPY_FLASHFREQ="40m" # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="2MB" +CONFIG_ESPTOOLPY_FLASHSIZE="8MB" # CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set CONFIG_ESPTOOLPY_BEFORE_RESET=y # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set @@ -374,12 +374,12 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 # # Partition Table # -CONFIG_PARTITION_TABLE_SINGLE_APP=y +# CONFIG_PARTITION_TABLE_SINGLE_APP is not set # CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set # CONFIG_PARTITION_TABLE_TWO_OTA is not set -# CONFIG_PARTITION_TABLE_CUSTOM is not set +CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" -CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" CONFIG_PARTITION_TABLE_OFFSET=0x8000 CONFIG_PARTITION_TABLE_MD5=y # end of Partition Table @@ -733,7 +733,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32 # ESP HTTPS OTA # # CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set -# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set +CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y # end of ESP HTTPS OTA # @@ -1880,7 +1880,7 @@ CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y CONFIG_GDBSTUB_SUPPORT_TASKS=y CONFIG_GDBSTUB_MAX_TASKS=32 -# CONFIG_OTA_ALLOW_HTTP is not set +CONFIG_OTA_ALLOW_HTTP=y # CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4 diff --git a/sdkconfig.old b/sdkconfig.old index d9afd26..466bad4 100644 --- a/sdkconfig.old +++ b/sdkconfig.old @@ -353,14 +353,14 @@ CONFIG_ESPTOOLPY_FLASHFREQ_40M=y # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set CONFIG_ESPTOOLPY_FLASHFREQ="40m" # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set -# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y # CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y +# CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_32MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_64MB is not set # CONFIG_ESPTOOLPY_FLASHSIZE_128MB is not set -CONFIG_ESPTOOLPY_FLASHSIZE="8MB" +CONFIG_ESPTOOLPY_FLASHSIZE="2MB" # CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set CONFIG_ESPTOOLPY_BEFORE_RESET=y # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set @@ -733,7 +733,7 @@ CONFIG_HTTPD_PURGE_BUF_LEN=32 # ESP HTTPS OTA # # CONFIG_ESP_HTTPS_OTA_DECRYPT_CB is not set -# CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP is not set +CONFIG_ESP_HTTPS_OTA_ALLOW_HTTP=y # end of ESP HTTPS OTA # @@ -1880,7 +1880,7 @@ CONFIG_POST_EVENTS_FROM_ISR=y CONFIG_POST_EVENTS_FROM_IRAM_ISR=y CONFIG_GDBSTUB_SUPPORT_TASKS=y CONFIG_GDBSTUB_MAX_TASKS=32 -# CONFIG_OTA_ALLOW_HTTP is not set +CONFIG_OTA_ALLOW_HTTP=y # CONFIG_TWO_UNIVERSAL_MAC_ADDRESS is not set CONFIG_FOUR_UNIVERSAL_MAC_ADDRESS=y CONFIG_NUMBER_OF_UNIVERSAL_MAC_ADDRESS=4