commit 2383894664cb881aadf4b07ef4166ce7bc04ceef Author: YoungSoo Shin Date: Wed Aug 20 18:56:07 2025 +0900 init commit Signed-off-by: YoungSoo Shin diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c24dd0d --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea +sdkconfig.old +/managed_components \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..f718166 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(odroid-remote-http) diff --git a/README.md b/README.md new file mode 100644 index 0000000..271dc3b --- /dev/null +++ b/README.md @@ -0,0 +1,85 @@ +# ODROID Remote HTTP Power Manager (OWPM) + +A web-based remote power and monitoring tool for ODROID Single Board Computers (SBCs), powered by an ESP32. This project provides a comprehensive web interface to control power, monitor real-time metrics, and access the serial console of your ODROID from any web browser on your local network. + +## Features + +- **Web-Based UI**: Modern, responsive interface built with Bootstrap and vanilla JavaScript. +- **Power Control**: Independently toggle Main (e.g., 12V) and USB (5V) power supplies. +- **Power Actions**: Remotely trigger Reset and Power On/Off actions. +- **Real-time Metrics**: Monitor voltage, current, and power consumption with a live-updating graph. +- **Interactive Serial Terminal**: Access your ODROID's serial console directly from the web UI via WebSockets. +- **Wi-Fi Management**: + - Scan and connect to Wi-Fi networks (STA mode). + - Enable Access Point mode (AP+STA) to connect directly to the device. + - Configure static IP settings. +- **System Info**: View device uptime and connection status. +- **Customizable Theme**: Switch between light and dark modes, with the preference saved locally. + +## Prerequisites + +Before you begin, ensure you have the following installed and configured on your system: + +- **[ESP-IDF (Espressif IoT Development Framework)](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html)**: This project is developed and tested with ESP-IDF v5.x. +- **[Node.js and npm](https://nodejs.org/)**: Required to build the web application. Node.js LTS version (e.g., 18.x or later) is recommended. + +## How to Build and Flash + +1. **Clone the repository:** + ```bash + git clone + cd odroid-remote-http + ``` + +2. **Build the Web Application:** + The web interface needs to be compiled before building the main firmware. + ```bash + cd page + npm install + npm run build + cd .. + ``` + +3. **Set up the ESP-IDF environment:** + Open a terminal and source the ESP-IDF export script. The path may vary depending on your installation location. + ```bash + . $HOME/esp/esp-idf/export.sh + ``` + +4. **Set the target chip:** + Specify your ESP32 variant (e.g., `esp32`, `esp32s3`). + ```bash + idf.py set-target esp32 + ``` + +5. **(Optional) Configure the project:** + You can configure project-specific settings, such as default Wi-Fi credentials, by running `menuconfig`. + ```bash + idf.py menuconfig + ``` + +6. **Build the project:** + This command compiles the application, bootloader, partition table, and embeds the compiled web page data. + ```bash + idf.py build + ``` + +7. **Flash the firmware:** + Connect your ESP32 board to your computer and replace `/dev/ttyUSB0` with your device's serial port. + ```bash + idf.py -p /dev/ttyUSB0 flash + ``` + +8. **Monitor the output:** + To view the serial logs from the device, use the `monitor` command. This is useful for finding the device's IP address after it connects to your Wi-Fi. + ```bash + idf.py -p /dev/ttyUSB0 monitor + ``` + To exit the monitor, press `Ctrl+]`. + +## Usage + +1. After flashing, the ESP32 will either connect to the pre-configured Wi-Fi network or start an Access Point (AP). +2. Check the serial monitor logs to find the IP address assigned to the device in STA mode, or the default AP address (usually `192.168.4.1`). +3. Open a web browser and navigate to the device's IP address. +4. You should now see the ODROID Remote control panel. \ No newline at end of file diff --git a/dependencies.lock b/dependencies.lock new file mode 100644 index 0000000..3533f2e --- /dev/null +++ b/dependencies.lock @@ -0,0 +1,59 @@ +dependencies: + espressif/cmake_utilities: + component_hash: 05165f30922b422b4b90c08845e6d449329b97370fbd06309803d8cb539d79e3 + dependencies: + - name: idf + require: private + version: '>=4.1' + source: + registry_url: https://components.espressif.com + type: service + version: 1.1.1 + espressif/led_indicator: + component_hash: 5b2531835a989825c0dc94465e3481086473e086dca109b99bea5605d8e70396 + dependencies: + - name: espressif/cmake_utilities + registry_url: https://components.espressif.com + require: private + version: '*' + - name: idf + require: private + version: '>=4.0' + - name: espressif/led_strip + registry_url: https://components.espressif.com + require: public + version: 2.5.5 + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.1.1 + espressif/led_strip: + component_hash: 28c6509a727ef74925b372ed404772aeedf11cce10b78c3f69b3c66799095e2d + dependencies: + - name: idf + require: private + version: '>=4.4' + source: + registry_url: https://components.espressif.com + type: service + version: 2.5.5 + idf: + source: + type: idf + version: 5.4.0 + joltwallet/littlefs: + component_hash: 8e12955f47e27e6070b76715a96d6c75fc2b44f069e8c33679332d9bdd3120c4 + dependencies: + - name: idf + require: private + version: '>=5.0' + source: + registry_url: https://components.espressif.com/ + type: service + version: 1.20.1 +direct_dependencies: +- espressif/led_indicator +- joltwallet/littlefs +manifest_hash: 445ef18c991ae952f2f16ffe06a905b6d1414a42286212d7b2459fa32945a09c +target: esp32c3 +version: 2.0.0 diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..22f339a --- /dev/null +++ b/docs/API.md @@ -0,0 +1,209 @@ +# ODROID Remote API Documentation + +This document outlines the HTTP REST and WebSocket APIs for communication between the web interface and the ESP32 device. + +--- + +## WebSocket API + +The WebSocket API provides a full-duplex communication channel for real-time data, such as sensor metrics and the interactive serial console. + +**Endpoint**: `/ws` + +### Server-to-Client Messages + +The server pushes messages to the client, which can be either JSON objects or raw binary data. JSON messages always contain a `type` field to identify the payload. + +#### JSON Messages + +| Type | Description | Payload Example | +|---------------|-----------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------| +| `sensor_data` | Pushed periodically (e.g., every second) with the latest power metrics. | `{"type":"sensor_data", "voltage":12.01, "current":1.52, "power":18.25, "uptime_sec":3600, "timestamp": 1672531200}` | +| `wifi_status` | Pushed periodically or on change to update the current Wi-Fi connection status. | `{"type":"wifi_status", "connected":true, "ssid":"MyHome_WiFi", "rssi":-65}` | + +**Field Descriptions:** +- `sensor_data`: + - `voltage` (float): The measured voltage in Volts (V). + - `current` (float): The measured current in Amperes (A). + - `power` (float): The calculated power in Watts (W). + - `uptime_sec` (integer): The system uptime in seconds. + - `timestamp` (integer): A Unix timestamp (seconds) of when the measurement was taken. +- `wifi_status`: + - `connected` (boolean): `true` if connected to a Wi-Fi network, `false` otherwise. + - `ssid` (string): The SSID of the connected network. Null if not connected. + - `rssi` (integer): The Received Signal Strength Indicator in dBm. Null if not connected. + +#### Raw Binary Data + +- **Description**: Raw binary data from the ODROID's serial (UART) port is forwarded directly to the client. This is used to display the terminal output. +- **Payload**: `(binary data)` + +### Client-to-Server Messages + +The client primarily sends raw binary data, which is interpreted as terminal input. + +- **Description**: Raw binary data representing user input from the web terminal. This data is forwarded directly to the ODROID's serial (UART) port. +- **Payload**: `(binary data)` + +--- + +## HTTP REST API + +The REST API is used for configuration and to trigger specific actions. All request and response bodies are in `application/json` format. + +### Endpoint: `/api/control` + +Manages power relays and system actions. + +#### `GET /api/control` + +Retrieves the current status of the power relays. + +- **Success Response (200 OK)** + ```json + { + "load_12v_on": true, + "load_5v_on": false + } + ``` + - `load_12v_on` (boolean): The state of the main 12V power relay. + - `load_5v_on` (boolean): The state of the 5V USB power relay. + +#### `POST /api/control` + +Sets the state of power relays or triggers a power action. You can send one or more commands in a single request. + +- **Request Body Examples**: + - To turn the main power on: + ```json + { "load_12v_on": true } + ``` + - To trigger a system reset: + ```json + { "reset_trigger": true } + ``` + - To toggle the power button: + ```json + { "power_trigger": true } + ``` + +- **Request Fields**: + - `load_12v_on` (boolean, optional): Set the state of the 12V relay. + - `load_5v_on` (boolean, optional): Set the state of the 5V relay. + - `reset_trigger` (boolean, optional): If `true`, momentarily triggers the reset button. + - `power_trigger` (boolean, optional): If `true`, momentarily triggers the power button. + +- **Success Response**: `204 No Content` +- **Error Response**: `400 Bad Request` if the request body is invalid. + +--- + +### Endpoint: `/api/wifi` + +Manages all Wi-Fi and network-related configurations. + +#### `GET /api/wifi` + +Retrieves the complete current network configuration. + +- **Success Response (200 OK)** + ```json + { + "connected": true, + "ssid": "MyHome_WiFi", + "mode": "apsta", + "net_type": "static", + "ip": { + "ip": "192.168.1.100", + "gateway": "192.168.1.1", + "subnet": "255.255.255.0", + "dns1": "8.8.8.8", + "dns2": "8.8.4.4" + } + } + ``` +- **Response Fields**: + - `connected` (boolean): Current Wi-Fi connection state. + - `ssid` (string): The SSID of the connected network. + - `mode` (string): The current Wi-Fi mode (`"sta"` or `"apsta"`). + - `net_type` (string): The network type (`"dhcp"` or `"static"`). + - `ip` (object): Contains IP details. Present even if using DHCP (may show last-leased IP). + +#### `POST /api/wifi` + +This is a multi-purpose endpoint. The server determines the action based on the fields provided in the request body. + +- **Action: Connect to a Wi-Fi Network** + - **Request Body**: + ```json + { + "ssid": "MyHome_WiFi", + "password": "my_secret_password" + } + ``` + - **Success Response (200 OK)**: + ```json + { "status": "connection_initiated" } + ``` + +- **Action: Configure Network Type (DHCP/Static)** + - **Request Body (for DHCP)**: + ```json + { "net_type": "dhcp" } + ``` + - **Request Body (for Static IP)**: + ```json + { + "net_type": "static", + "ip": "192.168.1.100", + "gateway": "192.168.1.1", + "subnet": "255.255.255.0", + "dns1": "8.8.8.8", + "dns2": "8.8.4.4" + } + ``` + - **Success Response**: `204 No Content` + +- **Action: Configure Wi-Fi Mode (STA/APSTA)** + - **Request Body (for STA mode)**: + ```json + { "mode": "sta" } + ``` + - **Request Body (for AP+STA mode)**: + ```json + { + "mode": "apsta", + "ap_ssid": "ODROID-Remote-AP", + "ap_password": "hardkernel" + } + ``` + *Note: `ap_password` is optional. If omitted, the AP will be open.* + - **Success Response**: `204 No Content` + +--- + +### Endpoint: `/api/wifi/scan` + +Scans for available Wi-Fi networks. + +#### `GET /api/wifi/scan` + +- **Success Response (200 OK)**: Returns a JSON array of found networks. + ```json + [ + { + "ssid": "MyHome_WiFi", + "rssi": -55, + "authmode": "WPA2_PSK" + }, + { + "ssid": "GuestNetwork", + "rssi": -78, + "authmode": "OPEN" + } + ] + ``` +- **Response Fields**: + - `ssid` (string): The network's Service Set Identifier. + - `rssi` (integer): Signal strength in dBm. + - `authmode` (string): The authentication mode (e.g., `"OPEN"`, `"WPA_PSK"`, `"WPA2_PSK"`). diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt new file mode 100644 index 0000000..f4efbd2 --- /dev/null +++ b/main/CMakeLists.txt @@ -0,0 +1,31 @@ +# Define the web application source directory and the final output file +set(WEB_APP_SOURCE_DIR ${CMAKE_SOURCE_DIR}/page) +set(GZ_OUTPUT_FILE ${WEB_APP_SOURCE_DIR}/dist/index.html.gz) + +# Register the component. Now, CMake knows how GZ_OUTPUT_FILE is generated +# and can correctly handle the dependency for embedding. +idf_component_register(SRC_DIRS "app" "nconfig" "wifi" "indicator" "system" "service" "ina226" + INCLUDE_DIRS "include" + EMBED_FILES ${GZ_OUTPUT_FILE} +) + +# Define a custom command to build the web app. +# This command explicitly tells CMake that it produces the GZ_OUTPUT_FILE. +add_custom_command( + OUTPUT ${GZ_OUTPUT_FILE} + COMMAND npm install + COMMAND npm run build + WORKING_DIRECTORY ${WEB_APP_SOURCE_DIR} + # Re-run the build if package.json or vite.config.js changes + DEPENDS ${WEB_APP_SOURCE_DIR}/index.html ${WEB_APP_SOURCE_DIR}/src/main.js ${WEB_APP_SOURCE_DIR}/src/style.css + COMMENT "Building Node.js project to produce ${GZ_OUTPUT_FILE}" + VERBATIM +) + +# Create a target that depends on the output file. When this target is built, +# it ensures the custom command above is executed first. +add_custom_target(build_web_app ALL + DEPENDS ${GZ_OUTPUT_FILE} +) + + diff --git a/main/Kconfig b/main/Kconfig new file mode 100644 index 0000000..cd9acb0 --- /dev/null +++ b/main/Kconfig @@ -0,0 +1,82 @@ +menu "ODROID-MONITOR" + menu "GPIO" + orsource "$IDF_PATH/examples/common_components/env_caps/$IDF_TARGET/Kconfig.env_caps" + + config GPIO_INA226_SCL + int "INA226 SCL GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 0 + help + GPIO number for I2C Master data line. + + config GPIO_INA226_SDA + int "INA226 SDA GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 1 + help + GPIO number for I2C Master data line. + + config GPIO_INA226_INT + int "INA226 ALERT GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 10 + help + GPIO number for I2C Master data line. + + config GPIO_UART_TX + int "UART TX GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 6 + help + GPIO number for UART data line. + + config GPIO_UART_RX + int "UART RX GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 7 + help + GPIO number for UART data line. + + config GPIO_LED_STATUS + int "Status LED GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 8 + help + GPIO number for LED. + + config GPIO_LED_WIFI + int "Wi-Fi LED GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 9 + help + GPIO number for LED. + + config GPIO_SW_12V + int "12v Load Switch GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 4 + help + GPIO number for Load switch. + + config GPIO_SW_5V + int "5v Load Switch GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 5 + help + GPIO number for Load switch. + + config GPIO_TRIGGER_POWER + int "Trigger power GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 2 + help + GPIO number for Trigger. + + config GPIO_TRIGGER_RESET + int "Trigger reset GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 3 + help + GPIO number for Trigger. + endmenu +endmenu \ No newline at end of file diff --git a/main/app/odroid-remote-http.c b/main/app/odroid-remote-http.c new file mode 100644 index 0000000..7a27011 --- /dev/null +++ b/main/app/odroid-remote-http.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "driver/uart.h" +#include "esp_http_server.h" +#include "indicator.h" +#include "nconfig.h" +#include "system.h" +#include "wifi.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" + +static const char *TAG = "odroid-remote"; + +void app_main(void) { + init_led(); + led_set(LED_BLU, BLINK_TRIPLE); + led_off(LED_BLU); + + // NVS 초기화 + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + // 네트워크 초기화 + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + ESP_ERROR_CHECK(init_nconfig()); + + // WiFi 연결 + wifi_connect(); + sync_time(); + + start_webserver(); +} \ No newline at end of file diff --git a/main/idf_component.yml b/main/idf_component.yml new file mode 100644 index 0000000..9146229 --- /dev/null +++ b/main/idf_component.yml @@ -0,0 +1,3 @@ +dependencies: + espressif/led_indicator: ^1.1.1 + joltwallet/littlefs: ==1.20.1 diff --git a/main/ina226/ina226.c b/main/ina226/ina226.c new file mode 100644 index 0000000..0e19a80 --- /dev/null +++ b/main/ina226/ina226.c @@ -0,0 +1,118 @@ +#include "ina226.h" + +#include +#include +#include +#include + +#define INA226_REG_CONFIG (0x00) +#define INA226_REG_SHUNT_VOLTAGE (0x01) +#define INA226_REG_BUS_VOLTAGE (0x02) +#define INA226_REG_POWER (0x03) +#define INA226_REG_CURRENT (0x04) +#define INA226_REG_CALIBRATION (0x05) +#define INA226_REG_ALERT_MASK (0x06) +#define INA226_REG_ALERT_LIMIT (0x07) +#define INA226_REG_MANUFACTURER_ID (0xFE) +#define INA226_REG_DIE_ID (0xFF) + +#define INA226_CFG_AVERAGING_OFFSET 9 +#define INA226_CFG_BUS_VOLTAGE_OFFSET 6 +#define INA226_CFG_SHUNT_VOLTAGE_OFFSET 3 + +static esp_err_t ina226_read_reg(ina226_t *handle, uint8_t reg_addr, uint16_t *data, size_t len) +{ + return i2c_master_transmit_receive(handle->dev_handle, ®_addr, 1, (uint8_t *)data, len, handle->timeout_ms); +} + +static esp_err_t ina226_write_reg(ina226_t *handle, uint8_t reg_addr, uint16_t value) +{ + uint8_t write_buf[3] = {reg_addr, value >> 8, value & 0xFF}; + return i2c_master_transmit(handle->dev_handle, write_buf, sizeof(write_buf), handle->timeout_ms); +} + +esp_err_t ina226_get_manufacturer_id(ina226_t *device, uint16_t *manufacturer_id) +{ + return ina226_read_reg(device, INA226_REG_MANUFACTURER_ID, manufacturer_id, 2); +} + +esp_err_t ina226_get_die_id(ina226_t *device, uint16_t *die_id) +{ + return ina226_read_reg(device, INA226_REG_DIE_ID, die_id, 2); +} + +esp_err_t ina226_get_shunt_voltage(ina226_t *device, float *voltage) +{ + uint8_t data[2]; + esp_err_t err = ina226_read_reg(device, INA226_REG_SHUNT_VOLTAGE, (uint16_t*)data, 2); + *voltage = (float) (data[0] << 8 | data[1]) * 2.5e-6f; /* fixed to 2.5 uV */ + return err; +} + +esp_err_t ina226_get_bus_voltage(ina226_t *device, float *voltage) +{ + uint8_t data[2]; + esp_err_t err = ina226_read_reg(device, INA226_REG_BUS_VOLTAGE, (uint16_t*)data, 2); + *voltage = (float) (data[0] << 8 | data[1]) * 0.00125f; + return err; +} + +esp_err_t ina226_get_current(ina226_t *device, float *current) +{ + uint8_t data[2]; + esp_err_t err = ina226_read_reg(device, INA226_REG_CURRENT, (uint16_t*)data, 2); + *current = ((float) (data[0] << 8 | data[1])) * device->current_lsb; + return err; +} + +esp_err_t ina226_get_power(ina226_t *device, float *power) +{ + uint8_t data[2]; + esp_err_t err = ina226_read_reg(device, INA226_REG_POWER, (uint16_t*)data, 2); + *power = (float) (data[0] << 8 | data[1]) * device->power_lsb; + return err; +} + +esp_err_t ina226_init(ina226_t *device, i2c_master_dev_handle_t dev_handle, const ina226_config_t *config) +{ + esp_err_t err; + device->timeout_ms = config->timeout_ms; + device->dev_handle = dev_handle; + uint16_t bitmask = 0; + bitmask |= (config->averages << INA226_CFG_AVERAGING_OFFSET); + bitmask |= (config->bus_conv_time << INA226_CFG_BUS_VOLTAGE_OFFSET); + bitmask |= (config->shunt_conv_time << INA226_CFG_SHUNT_VOLTAGE_OFFSET); + bitmask |= config->mode; + err = ina226_write_reg(device, INA226_REG_CONFIG, bitmask); + if(err != ESP_OK) return err; + + /* write calibration*/ + float minimum_lsb = config->max_current / 32767; + float current_lsb = (uint16_t)(minimum_lsb * 100000000); + current_lsb /= 100000000; + current_lsb /= 0.0001; + current_lsb = ceil(current_lsb); + current_lsb *= 0.0001; + device->current_lsb = current_lsb; + device->power_lsb = current_lsb * 25; + uint16_t calibration_value = (uint16_t)((0.00512) / (current_lsb * config->r_shunt)); + err = ina226_write_reg(device, INA226_REG_CALIBRATION, calibration_value); + if(err != ESP_OK) return err; + + return ESP_OK; +} + +esp_err_t ina226_get_alert_mask(ina226_t *device, ina226_alert_t *alert_mask) +{ + return ina226_read_reg(device, INA226_REG_ALERT_MASK, (uint16_t *)alert_mask, 2); +} + +esp_err_t ina226_set_alert_mask(ina226_t *device, ina226_alert_t alert_mask) +{ + return ina226_write_reg(device, INA226_REG_ALERT_MASK, (uint16_t)alert_mask); +} + +esp_err_t ina226_set_alert_limit(ina226_t *device, float voltage) +{ + return ina226_write_reg(device, INA226_REG_ALERT_LIMIT, (uint16_t)(voltage)); +} \ No newline at end of file diff --git a/main/include/ina226.h b/main/include/ina226.h new file mode 100644 index 0000000..2980f55 --- /dev/null +++ b/main/include/ina226.h @@ -0,0 +1,105 @@ +#ifndef _INA226_H_ +#define _INA226_H_ + +#include +#include "esp_err.h" +#include "driver/i2c_master.h" + +typedef enum +{ + INA226_AVERAGES_1 = 0b000, + INA226_AVERAGES_4 = 0b001, + INA226_AVERAGES_16 = 0b010, + INA226_AVERAGES_64 = 0b011, + INA226_AVERAGES_128 = 0b100, + INA226_AVERAGES_256 = 0b101, + INA226_AVERAGES_512 = 0b110, + INA226_AVERAGES_1024 = 0b111 +} ina226_averages_t; + +typedef enum +{ + INA226_BUS_CONV_TIME_140_US = 0b000, + INA226_BUS_CONV_TIME_204_US = 0b001, + INA226_BUS_CONV_TIME_332_US = 0b010, + INA226_BUS_CONV_TIME_588_US = 0b011, + INA226_BUS_CONV_TIME_1100_US = 0b100, + INA226_BUS_CONV_TIME_2116_US = 0b101, + INA226_BUS_CONV_TIME_4156_US = 0b110, + INA226_BUS_CONV_TIME_8244_US = 0b111 +} ina226_bus_conv_time_t; + + +typedef enum +{ + INA226_SHUNT_CONV_TIME_140_US = 0b000, + INA226_SHUNT_CONV_TIME_204_US = 0b001, + INA226_SHUNT_CONV_TIME_332_US = 0b010, + INA226_SHUNT_CONV_TIME_588_US = 0b011, + INA226_SHUNT_CONV_TIME_1100_US = 0b100, + INA226_SHUNT_CONV_TIME_2116_US = 0b101, + INA226_SHUNT_CONV_TIME_4156_US = 0b110, + INA226_SHUNT_CONV_TIME_8244_US = 0b111 +} ina226_shunt_conv_time_t; + +typedef enum +{ + + INA226_MODE_POWER_DOWN = 0b000, + INA226_MODE_SHUNT_TRIG = 0b001, + INA226_MODE_BUS_TRIG = 0b010, + INA226_MODE_SHUNT_BUS_TRIG = 0b011, + INA226_MODE_ADC_OFF = 0b100, + INA226_MODE_SHUNT_CONT = 0b101, + INA226_MODE_BUS_CONT = 0b110, + INA226_MODE_SHUNT_BUS_CONT = 0b111, +} ina226_mode_t; + +typedef enum +{ + INA226_ALERT_SHUNT_OVER_VOLTAGE = 0xf, + INA226_ALERT_SHUNT_UNDER_VOLTAGE = 0xe, + INA226_ALERT_BUS_OVER_VOLTAGE = 0xd, + INA226_ALERT_BUS_UNDER_VOLTAGE = 0xc, + INA226_ALERT_POWER_OVER_LIMIT = 0xb, + INA226_ALERT_CONVERSION_READY = 0xa, + INA226_ALERT_FUNCTION_FLAG = 0x4, + INA226_ALERT_CONVERSION_READY_FLAG = 0x3, + INA226_ALERT_MATH_OVERFLOW_FLAG = 0x2, + INA226_ALERT_POLARITY = 0x1, + INA226_ALERT_LATCH_ENABLE = 0x0 +} ina226_alert_t; + +typedef struct +{ + i2c_port_t i2c_port; + int i2c_addr; + int timeout_ms; + ina226_averages_t averages; + ina226_bus_conv_time_t bus_conv_time; + ina226_shunt_conv_time_t shunt_conv_time; + ina226_mode_t mode; + float r_shunt; /* ohm */ + float max_current; /* amps */ +} ina226_config_t; + +typedef struct +{ + i2c_master_dev_handle_t dev_handle; + int timeout_ms; + float current_lsb; + float power_lsb; +} ina226_t; + +esp_err_t ina226_get_manufacturer_id(ina226_t *device, uint16_t *manufacturer_id); +esp_err_t ina226_get_die_id(ina226_t *device, uint16_t *die_id); +esp_err_t ina226_get_shunt_voltage(ina226_t *device, float *voltage); +esp_err_t ina226_get_bus_voltage(ina226_t *device, float *voltage); +esp_err_t ina226_get_current(ina226_t *device, float *current); +esp_err_t ina226_get_power(ina226_t *device, float *power); +esp_err_t ina226_get_alert_mask(ina226_t *device, ina226_alert_t *alert_mask); +esp_err_t ina226_set_alert_mask(ina226_t *device, ina226_alert_t alert_mask); +esp_err_t ina226_set_alert_limit(ina226_t *device, float voltage); +esp_err_t ina226_init(ina226_t *device, i2c_master_dev_handle_t dev_handle, const ina226_config_t *config); + +#endif \ No newline at end of file diff --git a/main/include/indicator.h b/main/include/indicator.h new file mode 100644 index 0000000..edf920e --- /dev/null +++ b/main/include/indicator.h @@ -0,0 +1,29 @@ +// +// Created by shinys on 25. 7. 29. +// + +#ifndef LED_H +#define LED_H + +enum blink_type +{ + BLINK_SLOW = 0, + BLINK_FAST, + BLINK_DOUBLE, + BLINK_TRIPLE, + BLINK_SOLID, + BLINK_MAX, +}; + +enum blink_led +{ + LED_RED = 0, + LED_BLU = 1, + LED_MAX, +}; + +void init_led(void); +void led_set(enum blink_led led, enum blink_type type); +void led_off(enum blink_led led); + +#endif //LED_H diff --git a/main/include/nconfig.h b/main/include/nconfig.h new file mode 100644 index 0000000..69b5e33 --- /dev/null +++ b/main/include/nconfig.h @@ -0,0 +1,46 @@ +// +// Created by shinys on 25. 7. 10. +// + +#ifndef NCONFIG_H +#define NCONFIG_H + +#include "nvs.h" +#include "esp_err.h" + +#define NCONFIG_NVS_NAMESPACE "er" +#define NCONFIG_NOT_FOUND ESP_ERR_NVS_NOT_FOUND + +esp_err_t init_nconfig(); + +enum nconfig_type +{ + WIFI_SSID, + WIFI_PASSWORD, + WIFI_MODE, + AP_SSID, + AP_PASSWORD, + NETIF_HOSTNAME, + NETIF_IP, + NETIF_GATEWAY, + NETIF_SUBNET, + NETIF_DNS1, + NETIF_DNS2, + NETIF_TYPE, + UART_BAUD_RATE, + NCONFIG_TYPE_MAX, +}; + +// Write config +esp_err_t nconfig_write(enum nconfig_type type, const char* data); + +// Check config is set and get config value length +esp_err_t nconfig_get_str_len(enum nconfig_type type, size_t *len); + +// Read config +esp_err_t nconfig_read(enum nconfig_type type, char* data, size_t len); + +// Remove key +esp_err_t nconfig_delete(enum nconfig_type type); + +#endif //NCONFIG_H diff --git a/main/include/system.h b/main/include/system.h new file mode 100644 index 0000000..6f1f78c --- /dev/null +++ b/main/include/system.h @@ -0,0 +1,12 @@ +// +// Created by shinys on 25. 8. 5. +// + +#ifndef SYSTEM_H +#define SYSTEM_H + +void start_reboot_timer(int sec); +void stop_reboot_timer(); +void start_webserver(); + +#endif //SYSTEM_H diff --git a/main/include/wifi.h b/main/include/wifi.h new file mode 100644 index 0000000..8b48a25 --- /dev/null +++ b/main/include/wifi.h @@ -0,0 +1,23 @@ +// +// Created by shinys on 25. 7. 10. +// + +#ifndef WIFI_H +#define WIFI_H +#include "esp_err.h" +#include "esp_netif_types.h" +#include "esp_wifi_types_generic.h" + +const char* auth_mode_str(wifi_auth_mode_t mode); +esp_err_t wifi_connect(void); +esp_err_t wifi_disconnect(void); +void wifi_scan_aps(wifi_ap_record_t **ap_records, uint16_t* count); +esp_err_t wifi_get_current_ap_info(wifi_ap_record_t *ap_info); +esp_err_t wifi_get_current_ip_info(esp_netif_ip_info_t *ip_info); +esp_err_t wifi_get_dns_info(esp_netif_dns_type_t type, esp_netif_dns_info_t *dns_info); +esp_err_t wifi_use_dhcp(void); +esp_err_t wifi_use_static(const char *ip, const char *gw, const char *netmask, const char *dns1, const char *dns2); +esp_err_t wifi_switch_mode(const char* mode); +void sync_time(); + +#endif //WIFI_H diff --git a/main/indicator/indicator.c b/main/indicator/indicator.c new file mode 100644 index 0000000..ce65cb8 --- /dev/null +++ b/main/indicator/indicator.c @@ -0,0 +1,109 @@ +// +// Created by shinys on 25. 7. 29. +// + +#include "indicator.h" + +#include + +#define LED_STATUS_GPIO CONFIG_GPIO_LED_STATUS +#define LED_WIFI_GPIO CONFIG_GPIO_LED_WIFI + +static const blink_step_t slow_blink[] = { + {LED_BLINK_HOLD, LED_STATE_ON, 500}, + {LED_BLINK_HOLD, LED_STATE_OFF, 500}, + {LED_BLINK_HOLD, LED_STATE_ON, 500}, + {LED_BLINK_HOLD, LED_STATE_OFF, 500}, + {LED_BLINK_LOOP, 0, 0}, +}; + +static const blink_step_t fast_blink[] = { + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 100}, + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 100}, + {LED_BLINK_LOOP, 0, 0}, +}; + +static const blink_step_t double_blink[] = { + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 100}, + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 500}, + {LED_BLINK_LOOP, 0, 0}, +}; + +static const blink_step_t triple_blink[] = { + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 100}, + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 100}, + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_HOLD, LED_STATE_OFF, 500}, + {LED_BLINK_LOOP, 0, 0}, +}; + +static const blink_step_t solid_blink[] = { + {LED_BLINK_HOLD, LED_STATE_ON, 100}, + {LED_BLINK_LOOP, 0, 0}, +}; + +blink_step_t const *led_mode[] = { + [BLINK_SLOW] = slow_blink, + [BLINK_FAST] = fast_blink, + [BLINK_DOUBLE] = double_blink, + [BLINK_TRIPLE] = triple_blink, + [BLINK_SOLID] = solid_blink, + [BLINK_MAX] = NULL, +}; + +led_indicator_handle_t led_handle[LED_MAX] = {0}; +int recent_type[LED_MAX] = {-1, -1}; + +void init_led(void) +{ + led_indicator_ledc_config_t ledc_config = {0}; + led_indicator_config_t config = {0}; + + ledc_config.is_active_level_high = true; + ledc_config.timer_inited = false; + ledc_config.timer_num = LEDC_TIMER_0; + ledc_config.gpio_num = LED_STATUS_GPIO; + ledc_config.channel = LEDC_CHANNEL_0; + + config.mode = LED_LEDC_MODE; + config.led_indicator_ledc_config = &ledc_config; + config.blink_lists = led_mode; + config.blink_list_num = BLINK_MAX; + + led_handle[LED_RED] = led_indicator_create(&config); + + ledc_config.is_active_level_high = true; + ledc_config.timer_inited = false; + ledc_config.timer_num = LEDC_TIMER_0; + ledc_config.gpio_num = LED_WIFI_GPIO; + ledc_config.channel = LEDC_CHANNEL_1; + + config.mode = LED_LEDC_MODE; + config.led_indicator_ledc_config = &ledc_config; + config.blink_lists = led_mode; + config.blink_list_num = BLINK_MAX; + + led_handle[LED_BLU] = led_indicator_create(&config); +} + +void led_set(enum blink_led led, enum blink_type type) +{ + if (recent_type[led] != -1) + led_indicator_stop(led_handle[led], recent_type[led]); + + recent_type[led] = type; + led_indicator_start(led_handle[led], type); +} + +void led_off(enum blink_led led) +{ + if (recent_type[led] != -1) + led_indicator_stop(led_handle[led], recent_type[led]); + recent_type[led] = -1; +} diff --git a/main/nconfig/nconfig.c b/main/nconfig/nconfig.c new file mode 100644 index 0000000..ce1c802 --- /dev/null +++ b/main/nconfig/nconfig.c @@ -0,0 +1,85 @@ +// +// Created by shinys on 25. 7. 10. +// + +#include "nconfig.h" + +#include "nvs_flash.h" +#include "esp_err.h" + +static nvs_handle_t handle; + +const static char *keys[NCONFIG_TYPE_MAX] = { + [WIFI_SSID] = "wifi_ssid", + [WIFI_PASSWORD] = "wifi_pw", + [WIFI_MODE] = "wifi_mode", + [AP_SSID] = "ap_ssid", + [AP_PASSWORD] = "ap_pw", + [NETIF_HOSTNAME] = "hostname", + [NETIF_IP] = "ip", + [NETIF_GATEWAY] = "gw", + [NETIF_SUBNET] = "sn", + [NETIF_DNS1] = "dns1", + [NETIF_DNS2] = "dns2", + [NETIF_TYPE] = "dhcp", + [UART_BAUD_RATE] = "baudrate", +}; + +struct default_value { + enum nconfig_type type; + const char *value; +}; + +struct default_value const default_values[] = { + {WIFI_SSID, "HK_BOB_24G"}, + {WIFI_PASSWORD, ""}, + {NETIF_TYPE, "dhcp"}, + {NETIF_HOSTNAME, "odroid"}, + {UART_BAUD_RATE, "1500000"}, + {NETIF_DNS1, "8.8.8.8"}, + {NETIF_DNS2, "8.8.4.4"}, +}; + +esp_err_t init_nconfig() +{ + esp_err_t ret = nvs_open(NCONFIG_NVS_NAMESPACE, NVS_READWRITE, &handle); + if (ret != ESP_OK) return ret; + + for (int i = 0; i < sizeof(default_values) / sizeof(default_values[0]); ++i) { + // check key is not exist or value is null + size_t len = 0; + nconfig_get_str_len(default_values[i].type, &len); + if (len <= 1) // nconfig_get_str_len return err or value is '\0' + { + if (nconfig_write(default_values[i].type, default_values[i].value) != ESP_OK) // if nconfig write fail, system panic + return ESP_FAIL; + } + } + + return ESP_OK; +} + +esp_err_t nconfig_write(enum nconfig_type type, const char* data) +{ + return nvs_set_str(handle, keys[type], data); +} + +esp_err_t nconfig_delete(enum nconfig_type type) +{ + return nvs_erase_key(handle, keys[type]); +} + +esp_err_t nconfig_get_str_len(enum nconfig_type type, size_t *len) +{ + return nvs_get_str(handle, keys[type], NULL, len); +} + +esp_err_t nconfig_read(enum nconfig_type type, char* data, size_t len) +{ + return nvs_get_str(handle, keys[type], data, &len); +} + +esp_err_t nconfig_read_bool(enum nconfig_type type, char* data, size_t len) +{ + return nvs_get_str(handle, keys[type], data, &len); +} \ No newline at end of file diff --git a/main/service/control.c b/main/service/control.c new file mode 100644 index 0000000..dcacaf4 --- /dev/null +++ b/main/service/control.c @@ -0,0 +1,192 @@ +#include "webserver.h" +#include "driver/gpio.h" +#include "esp_http_server.h" +#include "esp_log.h" +#include "cJSON.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "esp_timer.h" + +static const char *TAG = "CONTROL"; + +// --- GPIO 핀 정의 --- +#define GPIO_12V_SWITCH CONFIG_GPIO_SW_12V +#define GPIO_5V_SWITCH CONFIG_GPIO_SW_5V +#define GPIO_POWER_TRIGGER CONFIG_GPIO_TRIGGER_POWER +#define GPIO_RESET_TRIGGER CONFIG_GPIO_TRIGGER_RESET + +// --- 상태 변수, 뮤텍스 및 타이머 핸들 --- +static bool status_12v_on = false; +static bool status_5v_on = false; +static SemaphoreHandle_t state_mutex; +static esp_timer_handle_t power_trigger_timer; +static esp_timer_handle_t reset_trigger_timer; + +/** + * @brief 타이머 만료 시 GPIO를 다시 HIGH로 설정하는 콜백 함수 + */ +static void trigger_off_callback(void* arg) +{ + gpio_num_t gpio_pin = (int) arg; + gpio_set_level(gpio_pin, 1); // 핀을 다시 HIGH로 복구 + ESP_LOGI(TAG, "GPIO %d trigger finished.", gpio_pin); +} + +static void update_gpio_switches() +{ + gpio_set_level(GPIO_12V_SWITCH, status_12v_on); + gpio_set_level(GPIO_5V_SWITCH, status_5v_on); + ESP_LOGI(TAG, "Switches updated: 12V=%s, 5V=%s", status_12v_on ? "ON" : "OFF", status_5v_on ? "ON" : "OFF"); +} + +static esp_err_t control_get_handler(httpd_req_t *req) +{ + cJSON *root = cJSON_CreateObject(); + + xSemaphoreTake(state_mutex, portMAX_DELAY); + cJSON_AddBoolToObject(root, "load_12v_on", status_12v_on); + cJSON_AddBoolToObject(root, "load_5v_on", status_5v_on); + xSemaphoreGive(state_mutex); + + char *json_string = cJSON_Print(root); + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, json_string, strlen(json_string)); + + free(json_string); + cJSON_Delete(root); + + return ESP_OK; +} + +static esp_err_t control_post_handler(httpd_req_t *req) +{ + char buf[128]; + int ret, remaining = req->content_len; + + if (remaining >= sizeof(buf)) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Request content too long"); + return ESP_FAIL; + } + + ret = httpd_req_recv(req, buf, remaining); + if (ret <= 0) { + if (ret == HTTPD_SOCK_ERR_TIMEOUT) { + httpd_resp_send_408(req); + } + return ESP_FAIL; + } + buf[ret] = '\0'; + ESP_LOGI(TAG, "Received JSON: %s", buf); + + cJSON *root = cJSON_Parse(buf); + if (root == NULL) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format"); + return ESP_FAIL; + } + + bool state_changed = false; + xSemaphoreTake(state_mutex, portMAX_DELAY); + + cJSON *item_12v = cJSON_GetObjectItem(root, "load_12v_on"); + if (cJSON_IsBool(item_12v)) { + status_12v_on = cJSON_IsTrue(item_12v); + state_changed = true; + } + + cJSON *item_5v = cJSON_GetObjectItem(root, "load_5v_on"); + if (cJSON_IsBool(item_5v)) { + status_5v_on = cJSON_IsTrue(item_5v); + state_changed = true; + } + + if (state_changed) { + update_gpio_switches(); + } + xSemaphoreGive(state_mutex); + + cJSON *power_trigger = cJSON_GetObjectItem(root, "power_trigger"); + if (cJSON_IsTrue(power_trigger)) { + ESP_LOGI(TAG, "Triggering GPIO %d LOW for 3 seconds...", GPIO_POWER_TRIGGER); + gpio_set_level(GPIO_POWER_TRIGGER, 0); + esp_timer_stop(power_trigger_timer); // Stop timer if it's already running + ESP_ERROR_CHECK(esp_timer_start_once(power_trigger_timer, 3000000)); // 3초 + } + + cJSON *reset_trigger = cJSON_GetObjectItem(root, "reset_trigger"); + if (cJSON_IsTrue(reset_trigger)) { + ESP_LOGI(TAG, "Triggering GPIO %d LOW for 3 seconds...", GPIO_RESET_TRIGGER); + gpio_set_level(GPIO_RESET_TRIGGER, 0); + esp_timer_stop(reset_trigger_timer); // Stop timer if it's already running + ESP_ERROR_CHECK(esp_timer_start_once(reset_trigger_timer, 3000000)); // 3초 + } + + cJSON_Delete(root); + + httpd_resp_sendstr(req, "{\"status\":\"ok\"}"); + return ESP_OK; +} + +static void control_module_init(void) +{ + state_mutex = xSemaphoreCreateMutex(); + + gpio_config_t switch_conf = { + .pin_bit_mask = (1ULL << GPIO_12V_SWITCH) | (1ULL << GPIO_5V_SWITCH), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&switch_conf); + update_gpio_switches(); + + gpio_config_t trigger_conf = { + .pin_bit_mask = (1ULL << GPIO_POWER_TRIGGER) | (1ULL << GPIO_RESET_TRIGGER), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + }; + gpio_config(&trigger_conf); + gpio_set_level(GPIO_POWER_TRIGGER, 1); + gpio_set_level(GPIO_RESET_TRIGGER, 1); + + const esp_timer_create_args_t power_timer_args = { + .callback = &trigger_off_callback, + .arg = (void*) GPIO_POWER_TRIGGER, + .name = "power_trigger_off" + }; + ESP_ERROR_CHECK(esp_timer_create(&power_timer_args, &power_trigger_timer)); + + const esp_timer_create_args_t reset_timer_args = { + .callback = &trigger_off_callback, + .arg = (void*) GPIO_RESET_TRIGGER, + .name = "reset_trigger_off" + }; + ESP_ERROR_CHECK(esp_timer_create(&reset_timer_args, &reset_trigger_timer)); + + ESP_LOGI(TAG, "Control module initialized"); +} + +void register_control_endpoint(httpd_handle_t server) +{ + control_module_init(); + httpd_uri_t get_uri = { + .uri = "/api/control", + .method = HTTP_GET, + .handler = control_get_handler, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &get_uri); + + httpd_uri_t post_uri = { + .uri = "/api/control", + .method = HTTP_POST, + .handler = control_post_handler, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &post_uri); + + ESP_LOGI(TAG, "Registered /api/control endpoints (GET, POST)"); +} \ No newline at end of file diff --git a/main/service/datalog.c b/main/service/datalog.c new file mode 100644 index 0000000..29df189 --- /dev/null +++ b/main/service/datalog.c @@ -0,0 +1,175 @@ +#include "datalog.h" +#include +#include +#include +#include +#include "esp_littlefs.h" +#include "esp_log.h" + +static const char* TAG = "DATALOG"; +static const char* LOG_FILE_PATH = "/littlefs/datalog.csv"; +static const char* TEMP_LOG_FILE_PATH = "/littlefs/datalog.tmp"; +#define MAX_LOG_SIZE (1024 * 1024) + +void datalog_init(void) +{ + ESP_LOGI(TAG, "Initializing DataLog with LittleFS"); + + esp_vfs_littlefs_conf_t conf = { + .base_path = "/littlefs", + .partition_label = "littlefs", + .format_if_mount_failed = true, + .dont_mount = false, + }; + + 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; + } + + size_t total = 0, used = 0; + ret = esp_littlefs_info(NULL, &total, &used); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(ret)); + } + else + { + ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used); + } + + // Check if file exists + FILE* f = fopen(LOG_FILE_PATH, "r"); + if (f == NULL) + { + ESP_LOGI(TAG, "Log file not found, creating new one."); + FILE* f_write = fopen(LOG_FILE_PATH, "w"); + if (f_write == NULL) + { + ESP_LOGE(TAG, "Failed to create log file."); + } + else + { + // Add header + fprintf(f_write, "timestamp,voltage,current,power\n"); + fclose(f_write); + } + } + else + { + ESP_LOGI(TAG, "Log file found."); + fclose(f); + } +} + +void datalog_add(uint32_t timestamp, float voltage, float current, float power) +{ + char new_line[100]; + int new_line_len = snprintf(new_line, sizeof(new_line), "%lu,%.3f,%.3f,%.3f\n", timestamp, voltage, current, power); + + struct stat st; + long size = 0; + if (stat(LOG_FILE_PATH, &st) == 0) + { + size = st.st_size; + } + + if (size + new_line_len <= MAX_LOG_SIZE) + { + FILE* f = fopen(LOG_FILE_PATH, "a"); + if (f == NULL) + { + ESP_LOGE(TAG, "Failed to open log file for appending."); + return; + } + fputs(new_line, f); + fclose(f); + } + else + { + ESP_LOGI(TAG, "Log file is full. Rotating log file."); + FILE* f_read = fopen(LOG_FILE_PATH, "r"); + if (f_read == NULL) + { + ESP_LOGE(TAG, "Could not open log for reading"); + return; + } + + FILE* f_write = fopen(TEMP_LOG_FILE_PATH, "w"); + if (f_write == NULL) + { + ESP_LOGE(TAG, "Could not open temp file for writing"); + fclose(f_read); + return; + } + + long size_to_remove = (size + new_line_len) - MAX_LOG_SIZE; + char line[256]; + + // Keep header + if (fgets(line, sizeof(line), f_read) != NULL) + { + fputs(line, f_write); + } + else + { + ESP_LOGE(TAG, "Could not read header"); + fclose(f_read); + fclose(f_write); + return; + } + + long bytes_skipped = 0; + while (fgets(line, sizeof(line), f_read) != NULL) + { + bytes_skipped += strlen(line); + if (bytes_skipped >= size_to_remove) + { + fputs(line, f_write); + break; + } + } + + while (fgets(line, sizeof(line), f_read) != NULL) + { + fputs(line, f_write); + } + + fputs(new_line, f_write); + + fclose(f_read); + fclose(f_write); + + if (remove(LOG_FILE_PATH) != 0) + { + ESP_LOGE(TAG, "Failed to remove old log file"); + } + else if (rename(TEMP_LOG_FILE_PATH, LOG_FILE_PATH) != 0) + { + ESP_LOGE(TAG, "Failed to rename temp file"); + } + else + { + ESP_LOGI(TAG, "Log file rotated successfully."); + } + } +} + +const char* datalog_get_path(void) +{ + return LOG_FILE_PATH; +} diff --git a/main/service/datalog.h b/main/service/datalog.h new file mode 100644 index 0000000..83fcd7d --- /dev/null +++ b/main/service/datalog.h @@ -0,0 +1,10 @@ +#ifndef MAIN_SERVICE_DATALOG_H_ +#define MAIN_SERVICE_DATALOG_H_ + +#include + +void datalog_init(void); +void datalog_add(uint32_t timestamp, float voltage, float current, float power); +const char* datalog_get_path(void); + +#endif /* MAIN_SERVICE_DATALOG_H_ */ diff --git a/main/service/monitor.c b/main/service/monitor.c new file mode 100644 index 0000000..d0555c1 --- /dev/null +++ b/main/service/monitor.c @@ -0,0 +1,135 @@ +// +// Created by shinys on 25. 8. 18.. +// + + +#include "monitor.h" +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "cJSON.h" +#include "esp_netif.h" +#include "esp_wifi_types_generic.h" +#include "ina226.h" +#include "webserver.h" +#include "wifi.h" +#include "datalog.h" + +#define INA226_SDA CONFIG_GPIO_INA226_SDA +#define INA226_SCL CONFIG_GPIO_INA226_SCL + +ina226_t ina; +i2c_master_bus_handle_t bus_handle; +i2c_master_dev_handle_t dev_handle; + +// Timer callback function to read sensor data +static void sensor_timer_callback(void *arg) +{ + // Generate random sensor data + float voltage = 0; + float current = 0; + float power = 0; + + ina226_get_bus_voltage(&ina, &voltage); + ina226_get_power(&ina, &power); + ina226_get_current(&ina, ¤t); + + // Get system uptime + int64_t uptime_us = esp_timer_get_time(); + uint32_t uptime_sec = (uint32_t)(uptime_us / 1000000); + uint32_t timestamp = (uint32_t)time(NULL); + + datalog_add(timestamp, voltage, current, power); + + // Create JSON object with sensor data + cJSON *root = cJSON_CreateObject(); + cJSON_AddStringToObject(root, "type", "sensor_data"); + cJSON_AddNumberToObject(root, "voltage", voltage); + cJSON_AddNumberToObject(root, "current", current); + cJSON_AddNumberToObject(root, "power", power); + cJSON_AddNumberToObject(root, "timestamp", timestamp); + cJSON_AddNumberToObject(root, "uptime_sec", uptime_sec); + + // Push data to WebSocket clients + push_data_to_ws(root); +} + +static void status_wifi_callback(void *arg) +{ + wifi_ap_record_t ap_info; + cJSON *root = cJSON_CreateObject(); + + if (wifi_get_current_ap_info(&ap_info) == ESP_OK) { + cJSON_AddStringToObject(root, "type", "wifi_status"); + cJSON_AddBoolToObject(root, "connected", true); + cJSON_AddStringToObject(root, "ssid", (const char *)ap_info.ssid); + cJSON_AddNumberToObject(root, "rssi", ap_info.rssi); + } else { + cJSON_AddBoolToObject(root, "connected", false); + } + + push_data_to_ws(root); +} + +ina226_config_t ina_config = { + .i2c_port = I2C_NUM_0, + .i2c_addr = 0x40, + .timeout_ms = 100, + .averages = INA226_AVERAGES_16, + .bus_conv_time = INA226_BUS_CONV_TIME_1100_US, + .shunt_conv_time = INA226_SHUNT_CONV_TIME_1100_US, + .mode = INA226_MODE_SHUNT_BUS_CONT, + .r_shunt = 0.01f, + .max_current = 8 +}; + +static void init_ina226() +{ + i2c_master_bus_config_t bus_config = { + .i2c_port = I2C_NUM_0, + .sda_io_num = (gpio_num_t) INA226_SDA, + .scl_io_num = (gpio_num_t) INA226_SCL, + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .flags.enable_internal_pullup = false, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus_handle)); + + i2c_device_config_t dev_config = { + .dev_addr_length = I2C_ADDR_BIT_LEN_7, + .device_address = 0x40, + .scl_speed_hz = 400000, + }; + ESP_ERROR_CHECK(i2c_master_bus_add_device(bus_handle, &dev_config, &dev_handle)); + + ESP_ERROR_CHECK(ina226_init(&ina, dev_handle, &ina_config)); +} + +static esp_timer_handle_t sensor_timer; +static esp_timer_handle_t wifi_status_timer; + +void init_status_monitor() +{ + init_ina226(); + datalog_init(); + + // Timer configuration + const esp_timer_create_args_t sensor_timer_args = { + .callback = &sensor_timer_callback, + .name = "sensor_reading_timer" // Optional name for debugging + }; + + const esp_timer_create_args_t wifi_timer_args = { + .callback = &status_wifi_callback, + .name = "wifi_status_timer" // Optional name for debugging + }; + + ESP_ERROR_CHECK(esp_timer_create(&sensor_timer_args, &sensor_timer)); + ESP_ERROR_CHECK(esp_timer_create(&wifi_timer_args, &wifi_status_timer)); + + ESP_ERROR_CHECK(esp_timer_start_periodic(sensor_timer, 1000000)); // 1sec + ESP_ERROR_CHECK(esp_timer_start_periodic(wifi_status_timer, 1000000 * 5)); // 5s in microseconds +} diff --git a/main/service/monitor.h b/main/service/monitor.h new file mode 100644 index 0000000..2f52e47 --- /dev/null +++ b/main/service/monitor.h @@ -0,0 +1,25 @@ +// +// Created by shinys on 25. 8. 18.. +// + +#ifndef ODROID_REMOTE_HTTP_MONITOR_H +#define ODROID_REMOTE_HTTP_MONITOR_H + +#include + +#include "esp_http_server.h" + +// 버퍼에 저장할 데이터의 개수 +#define SENSOR_BUFFER_SIZE 100 + +// 단일 센서 데이터를 저장하기 위한 구조체 +typedef struct { + float voltage; + float current; + float power; + uint32_t timestamp; // 데이터를 읽은 시간 (부팅 후 ms) +} sensor_data_t; + +void init_status_monitor(); + +#endif //ODROID_REMOTE_HTTP_MONITOR_H \ No newline at end of file diff --git a/main/service/setting.c b/main/service/setting.c new file mode 100644 index 0000000..c86ec5c --- /dev/null +++ b/main/service/setting.c @@ -0,0 +1,249 @@ +#include "webserver.h" +#include "cJSON.h" +#include "esp_http_server.h" +#include "esp_log.h" +#include "nconfig.h" +#include "wifi.h" +#include "system.h" +#include "esp_netif.h" +#include "freertos/task.h" + +static const char *TAG = "webserver"; + +static esp_err_t setting_get_handler(httpd_req_t *req) +{ + wifi_ap_record_t ap_info; + cJSON *root = cJSON_CreateObject(); + + char mode_buf[16]; + if (nconfig_read(WIFI_MODE, mode_buf, sizeof(mode_buf)) == ESP_OK) { + cJSON_AddStringToObject(root, "mode", mode_buf); + } else { + cJSON_AddStringToObject(root, "mode", "sta"); // Default to sta + } + + char net_type_buf[16]; + if (nconfig_read(NETIF_TYPE, net_type_buf, sizeof(net_type_buf)) == ESP_OK) { + cJSON_AddStringToObject(root, "net_type", net_type_buf); + } else { + cJSON_AddStringToObject(root, "net_type", "dhcp"); // Default to dhcp + } + + // Add baudrate to the response + char baud_buf[16]; + if (nconfig_read(UART_BAUD_RATE, baud_buf, sizeof(baud_buf)) == ESP_OK) { + cJSON_AddStringToObject(root, "baudrate", baud_buf); + } + + if (wifi_get_current_ap_info(&ap_info) == ESP_OK) { + cJSON_AddBoolToObject(root, "connected", true); + cJSON_AddStringToObject(root, "ssid", (const char *)ap_info.ssid); + cJSON_AddNumberToObject(root, "rssi", ap_info.rssi); + + esp_netif_ip_info_t ip_info; + cJSON* ip_obj = cJSON_CreateObject(); + if (wifi_get_current_ip_info(&ip_info) == ESP_OK) { + char ip_str[16]; + esp_ip4addr_ntoa(&ip_info.ip, ip_str, sizeof(ip_str)); + cJSON_AddStringToObject(ip_obj, "ip", ip_str); + esp_ip4addr_ntoa(&ip_info.gw, ip_str, sizeof(ip_str)); + cJSON_AddStringToObject(ip_obj, "gateway", ip_str); + esp_ip4addr_ntoa(&ip_info.netmask, ip_str, sizeof(ip_str)); + cJSON_AddStringToObject(ip_obj, "subnet", ip_str); + } + + esp_netif_dns_info_t dns_info; + char dns_str[16]; + if (wifi_get_dns_info(ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + esp_ip4addr_ntoa(&dns_info.ip.u_addr.ip4, dns_str, sizeof(dns_str)); + cJSON_AddStringToObject(ip_obj, "dns1", dns_str); + } + if (wifi_get_dns_info(ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + esp_ip4addr_ntoa(&dns_info.ip.u_addr.ip4, dns_str, sizeof(dns_str)); + cJSON_AddStringToObject(ip_obj, "dns2", dns_str); + } + cJSON_AddItemToObject(root, "ip", ip_obj); + + } else { + cJSON_AddBoolToObject(root, "connected", false); + } + + const char *json_string = cJSON_Print(root); + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, json_string, HTTPD_RESP_USE_STRLEN); + cJSON_Delete(root); + free((void*)json_string); + + return ESP_OK; +} + +static esp_err_t wifi_scan(httpd_req_t *req) +{ + wifi_ap_record_t *ap_records; + uint16_t count; + + wifi_scan_aps(&ap_records, &count); + + cJSON *root = cJSON_CreateArray(); + for (int i = 0; i < count; i++) + { + cJSON *ap_obj = cJSON_CreateObject(); + cJSON_AddStringToObject(ap_obj, "ssid", (const char *)ap_records[i].ssid); + cJSON_AddNumberToObject(ap_obj, "rssi", ap_records[i].rssi); + cJSON_AddStringToObject(ap_obj, "authmode", auth_mode_str(ap_records[i].authmode)); + cJSON_AddItemToArray(root, ap_obj); + } + + if (count > 0) + free(ap_records); + + + const char *json_string = cJSON_Print(root); + httpd_resp_set_type(req, "application/json"); + httpd_resp_send(req, json_string, HTTPD_RESP_USE_STRLEN); + cJSON_Delete(root); + free((void*)json_string); + + return ESP_OK; +} + +static esp_err_t setting_post_handler(httpd_req_t *req) +{ + char buf[512]; + int received = httpd_req_recv(req, buf, sizeof(buf) - 1); + + if (received <= 0) { + if (received == HTTPD_SOCK_ERR_TIMEOUT) httpd_resp_send_408(req); + return ESP_FAIL; + } + buf[received] = '\0'; + + cJSON *root = cJSON_Parse(buf); + if (root == NULL) { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); + return ESP_FAIL; + } + + cJSON *mode_item = cJSON_GetObjectItem(root, "mode"); + cJSON *net_type_item = cJSON_GetObjectItem(root, "net_type"); + cJSON *ssid_item = cJSON_GetObjectItem(root, "ssid"); + cJSON *baud_item = cJSON_GetObjectItem(root, "baudrate"); + + if (mode_item && cJSON_IsString(mode_item)) { + const char* mode = mode_item->valuestring; + ESP_LOGI(TAG, "Received mode switch request: %s", mode); + + if (strcmp(mode, "sta") == 0 || strcmp(mode, "apsta") == 0) { + if (strcmp(mode, "apsta") == 0) { + cJSON *ap_ssid_item = cJSON_GetObjectItem(root, "ap_ssid"); + cJSON *ap_pass_item = cJSON_GetObjectItem(root, "ap_password"); + + if (ap_ssid_item && cJSON_IsString(ap_ssid_item)) { + nconfig_write(AP_SSID, ap_ssid_item->valuestring); + } else { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "AP SSID required for APSTA mode"); + cJSON_Delete(root); + return ESP_FAIL; + } + + if (ap_pass_item && cJSON_IsString(ap_pass_item)) { + nconfig_write(AP_PASSWORD, ap_pass_item->valuestring); + } else { + nconfig_delete(AP_PASSWORD); // Open network + } + } + + wifi_switch_mode(mode); + httpd_resp_sendstr(req, "{\"status\":\"mode_switch_initiated\"}"); + } else { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid mode"); + } + } else if (net_type_item && cJSON_IsString(net_type_item)) { + const char* type = net_type_item->valuestring; + ESP_LOGI(TAG, "Received network config: %s", type); + + if (strcmp(type, "static") == 0) { + cJSON *ip_item = cJSON_GetObjectItem(root, "ip"); + cJSON *gw_item = cJSON_GetObjectItem(root, "gateway"); + cJSON *sn_item = cJSON_GetObjectItem(root, "subnet"); + cJSON *d1_item = cJSON_GetObjectItem(root, "dns1"); + cJSON *d2_item = cJSON_GetObjectItem(root, "dns2"); + + const char* ip = cJSON_IsString(ip_item) ? ip_item->valuestring : NULL; + const char* gw = cJSON_IsString(gw_item) ? gw_item->valuestring : NULL; + const char* sn = cJSON_IsString(sn_item) ? sn_item->valuestring : NULL; + const char* d1 = cJSON_IsString(d1_item) ? d1_item->valuestring : NULL; + const char* d2 = cJSON_IsString(d2_item) ? d2_item->valuestring : NULL; + + if (ip && gw && sn && d1) { + nconfig_write(NETIF_TYPE, "static"); + nconfig_write(NETIF_IP, ip); + nconfig_write(NETIF_GATEWAY, gw); + nconfig_write(NETIF_SUBNET, sn); + nconfig_write(NETIF_DNS1, d1); + if (d2) nconfig_write(NETIF_DNS2, d2); else nconfig_delete(NETIF_DNS2); + + wifi_use_static(ip, gw, sn, d1, d2); + httpd_resp_sendstr(req, "{\"status\":\"static_config_applied\"}"); + } else { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing static IP fields"); + } + } else if (strcmp(type, "dhcp") == 0) { + nconfig_write(NETIF_TYPE, "dhcp"); + wifi_use_dhcp(); + httpd_resp_sendstr(req, "{\"status\":\"dhcp_config_applied\"}"); + } + } else if (ssid_item && cJSON_IsString(ssid_item)) { + cJSON *pass_item = cJSON_GetObjectItem(root, "password"); + if (cJSON_IsString(pass_item)) { + nconfig_write(WIFI_SSID, ssid_item->valuestring); + nconfig_write(WIFI_PASSWORD, pass_item->valuestring); + nconfig_write(NETIF_TYPE, "dhcp"); // Default to DHCP on new connection + + httpd_resp_sendstr(req, "{\"status\":\"connection_initiated\"}"); + wifi_disconnect(); + vTaskDelay(pdMS_TO_TICKS(500)); + wifi_connect(); + } else { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Password required"); + } + } else if (baud_item && cJSON_IsString(baud_item)) { + const char* baudrate = baud_item->valuestring; + ESP_LOGI(TAG, "Received baudrate set request: %s", baudrate); + nconfig_write(UART_BAUD_RATE, baudrate); + change_baud_rate(strtol(baudrate, NULL, 10)); + httpd_resp_sendstr(req, "{\"status\":\"baudrate_updated\"}"); + } else { + httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid payload"); + } + + cJSON_Delete(root); + return ESP_OK; +} + +void register_wifi_endpoint(httpd_handle_t server) +{ + httpd_uri_t status = { + .uri = "/api/setting", + .method = HTTP_GET, + .handler = setting_get_handler, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &status); + + httpd_uri_t set = { + .uri = "/api/setting", + .method = HTTP_POST, + .handler = setting_post_handler, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &set); + + httpd_uri_t scan = { + .uri = "/api/wifi/scan", + .method = HTTP_GET, + .handler = wifi_scan, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &scan); +} diff --git a/main/service/webserver.c b/main/service/webserver.c new file mode 100644 index 0000000..6e3b109 --- /dev/null +++ b/main/service/webserver.c @@ -0,0 +1,102 @@ +#include "webserver.h" +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "esp_netif.h" +#include "driver/uart.h" +#include "esp_http_server.h" +#include "indicator.h" +#include "nconfig.h" +#include "monitor.h" +#include "wifi.h" +#include "datalog.h" + +#include "lwip/err.h" +#include "lwip/sockets.h" +#include "lwip/sys.h" + +static const char *TAG = "WEBSERVER"; + +static esp_err_t index_handler(httpd_req_t *req) { + extern const unsigned char index_html_start[] asm("_binary_index_html_gz_start"); + extern const unsigned char index_html_end[] asm("_binary_index_html_gz_end"); + const size_t index_html_size = (index_html_end - index_html_start); + + httpd_resp_set_hdr(req, "Content-Encoding", "gzip"); + httpd_resp_set_type(req, "text/html"); + httpd_resp_send(req, (const char *)index_html_start, index_html_size); + + return ESP_OK; +} + +static esp_err_t datalog_download_handler(httpd_req_t *req) +{ + const char *filepath = datalog_get_path(); + FILE *f = fopen(filepath, "r"); + if (f == NULL) { + ESP_LOGE(TAG, "Failed to open datalog file for reading"); + httpd_resp_send_404(req); + return ESP_FAIL; + } + + httpd_resp_set_type(req, "text/csv"); + httpd_resp_set_hdr(req, "Content-Disposition", "attachment; filename=\"datalog.csv\""); + + char buffer[1024]; + size_t bytes_read; + while ((bytes_read = fread(buffer, 1, sizeof(buffer), f)) > 0) { + if (httpd_resp_send_chunk(req, buffer, bytes_read) != ESP_OK) { + ESP_LOGE(TAG, "File sending failed!"); + fclose(f); + httpd_resp_send_chunk(req, NULL, 0); + httpd_resp_send_500(req); + return ESP_FAIL; + } + } + + fclose(f); + httpd_resp_send_chunk(req, NULL, 0); + return ESP_OK; +} + +// HTTP 서버 시작 +void start_webserver(void) { + httpd_handle_t server = NULL; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.stack_size = 1024 * 8; + config.max_uri_handlers = 10; + + if (httpd_start(&server, &config) != ESP_OK) { + return ; + } + + // Index page + httpd_uri_t index = { + .uri = "/", + .method = HTTP_GET, + .handler = index_handler, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &index); + + httpd_uri_t datalog_uri = { + .uri = "/datalog.csv", + .method = HTTP_GET, + .handler = datalog_download_handler, + .user_ctx = NULL + }; + httpd_register_uri_handler(server, &datalog_uri); + + register_wifi_endpoint(server); + register_ws_endpoint(server); + register_control_endpoint(server); + init_status_monitor(); +} diff --git a/main/service/webserver.h b/main/service/webserver.h new file mode 100644 index 0000000..7983be7 --- /dev/null +++ b/main/service/webserver.h @@ -0,0 +1,17 @@ +// +// Created by shinys on 25. 8. 18.. +// + +#ifndef ODROID_REMOTE_HTTP_WEBSERVER_H +#define ODROID_REMOTE_HTTP_WEBSERVER_H +#include "cJSON.h" +#include "esp_http_server.h" +#include "system.h" + +void register_wifi_endpoint(httpd_handle_t server); +void register_ws_endpoint(httpd_handle_t server); +void register_control_endpoint(httpd_handle_t server); +void push_data_to_ws(cJSON *data); +esp_err_t change_baud_rate(int baud_rate); + +#endif //ODROID_REMOTE_HTTP_WEBSERVER_H \ No newline at end of file diff --git a/main/service/ws.c b/main/service/ws.c new file mode 100644 index 0000000..63075c6 --- /dev/null +++ b/main/service/ws.c @@ -0,0 +1,178 @@ +// +// Created by shinys on 25. 8. 18.. +// + +#include "cJSON.h" +#include "webserver.h" +#include "esp_err.h" +#include "esp_http_server.h" +#include "esp_log.h" +#include "nconfig.h" +#include "driver/uart.h" + +#define UART_NUM UART_NUM_1 +#define BUF_SIZE (4096) +#define RD_BUF_SIZE (BUF_SIZE) +#define UART_TX_PIN CONFIG_GPIO_UART_TX +#define UART_RX_PIN CONFIG_GPIO_UART_RX + +static const char *TAG = "ws-uart"; + +static int client_fd = -1; + +struct status_message +{ + cJSON *data; +}; + +QueueHandle_t status_queue; + +// Status task +static void status_task(void *arg) +{ + httpd_handle_t server = (httpd_handle_t)arg; + struct status_message msg; + + while (1) { + if (xQueueReceive(status_queue, &msg, portMAX_DELAY)) { + if (client_fd <= 0) continue; + + char *json_string = cJSON_Print(msg.data); + httpd_ws_frame_t ws_pkt; + memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); + ws_pkt.payload = (uint8_t *)json_string; + ws_pkt.len = strlen(json_string); + ws_pkt.type = HTTPD_WS_TYPE_TEXT; + esp_err_t err = httpd_ws_send_frame_async(server, client_fd, &ws_pkt); + free(json_string); + cJSON_Delete(msg.data); + + if (err != ESP_OK) + { + // try close... + httpd_ws_frame_t close_frame = { + .final = true, + .fragmented = false, + .type = HTTPD_WS_TYPE_CLOSE, + .payload = NULL, + .len = 0 + }; + + httpd_ws_send_frame_async(server, client_fd, &close_frame); + client_fd = -1; + } + } + vTaskDelay(1); + } +} + +// UART task +static void uart_read_task(void *arg) { + httpd_handle_t server = (httpd_handle_t)arg; + + uint8_t data[RD_BUF_SIZE]; + while (1) { + int len = uart_read_bytes(UART_NUM, data, RD_BUF_SIZE, 10 / portTICK_PERIOD_MS); + if (len > 0 && client_fd != -1) { + httpd_ws_frame_t ws_pkt; + memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); + ws_pkt.payload = data; + ws_pkt.len = len; + ws_pkt.type = HTTPD_WS_TYPE_BINARY; + + esp_err_t err = httpd_ws_send_frame_async(server, client_fd, &ws_pkt); + if (err != ESP_OK) + { + // try close... + httpd_ws_frame_t close_frame = { + .final = true, + .fragmented = false, + .type = HTTPD_WS_TYPE_CLOSE, + .payload = NULL, + .len = 0 + }; + + httpd_ws_send_frame_async(server, client_fd, &close_frame); + client_fd = -1; + } + } + vTaskDelay(1); + } +} + +// 웹소켓 처리 핸들러 +static esp_err_t ws_handler(httpd_req_t *req) { + if (req->method == HTTP_GET) { + ESP_LOGI(TAG, "Accept websocket connection"); + client_fd = httpd_req_to_sockfd(req); + xQueueReset(status_queue); + return ESP_OK; + } + + httpd_ws_frame_t ws_pkt; + uint8_t buf[BUF_SIZE]; + memset(&ws_pkt, 0, sizeof(httpd_ws_frame_t)); + ws_pkt.payload = buf; + ws_pkt.type = HTTPD_WS_TYPE_BINARY; + + esp_err_t ret = httpd_ws_recv_frame(req, &ws_pkt, BUF_SIZE); + if (ret != ESP_OK) { + ESP_LOGI(TAG, "웹소켓 프레임 수신 실패"); + return ret; + } + + uart_write_bytes(UART_NUM, (const char *)ws_pkt.payload, ws_pkt.len); + + return ESP_OK; +} + +void register_ws_endpoint(httpd_handle_t server) +{ + size_t baud_rate_len; + + nconfig_get_str_len(UART_BAUD_RATE, &baud_rate_len); + char buf[baud_rate_len]; + nconfig_read(UART_BAUD_RATE, buf, baud_rate_len); + + uart_config_t uart_config = { + .baud_rate = strtol(buf, NULL, 10), + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + // .source_clk = UART_SCLK_APB, + }; + + ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config)); + ESP_ERROR_CHECK(uart_set_pin(UART_NUM, UART_TX_PIN, UART_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, BUF_SIZE * 2, 0, NULL, ESP_INTR_FLAG_IRAM)); + + httpd_uri_t ws = { + .uri = "/ws", + .method = HTTP_GET, + .handler = ws_handler, + .user_ctx = NULL, + .is_websocket = true + }; + httpd_register_uri_handler(server, &ws); + + status_queue = xQueueCreate(10, sizeof(struct status_message)); + + xTaskCreate(uart_read_task, "uart_read_task", 1024*6, server, 8, NULL); + xTaskCreate(status_task, "status_task", 4096, server, 7, NULL); +} + +void push_data_to_ws(cJSON *data) +{ + struct status_message msg; + msg.data = data; + if (xQueueSend(status_queue, &msg, 10) != pdPASS) + { + ESP_LOGW(TAG, "Queue full"); + } +} + +esp_err_t change_baud_rate(int baud_rate) +{ + return uart_set_baudrate(UART_NUM, baud_rate); +} \ No newline at end of file diff --git a/main/system/system.c b/main/system/system.c new file mode 100644 index 0000000..97eb91b --- /dev/null +++ b/main/system/system.c @@ -0,0 +1,46 @@ +// +// Created by shinys on 25. 8. 5. +// + +#include + +#include +#include +#include + +static const char *TAG = "odroid"; +int t = 0; + +TaskHandle_t reboot_handle = NULL; + +static void reboot_task(void *arg) +{ + while (t > 0) + { + ESP_LOGW(TAG, "ESP will reboot in [%d] sec..., If you want stop reboot, use command \"reboot -s\"", t); + vTaskDelay(1000 / portTICK_PERIOD_MS); + --t; + } + esp_restart(); +} + +void start_reboot_timer(int sec) +{ + + if (reboot_handle != NULL) + { + ESP_LOGW(TAG, "The reboot timer is already running."); + return; + } + t = sec; + xTaskCreate(reboot_task, "reboot_task", 2048, NULL, 8, &reboot_handle); +} + +void stop_reboot_timer() +{ + if (reboot_handle == NULL) + { + return; + } + vTaskDelete(reboot_handle); +} \ No newline at end of file diff --git a/main/wifi/wifi.c b/main/wifi/wifi.c new file mode 100644 index 0000000..a054fb3 --- /dev/null +++ b/main/wifi/wifi.c @@ -0,0 +1,550 @@ +// +// Created by shinys on 25. 7. 10. +// + +#include "wifi.h" + +#include "nconfig.h" +#include "indicator.h" + +#include +#include +#include +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "esp_event.h" +#include "esp_wifi.h" +#include "esp_wifi_default.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "esp_netif_sntp.h" +#include "rom/ets_sys.h" + +static const char *TAG = "odroid"; +#define MAX_RETRY 10 +#define MAX_SCAN 20 + + +const char* auth_mode_str(wifi_auth_mode_t mode) +{ + switch (mode) + { + case WIFI_AUTH_OPEN: + return "OPEN"; + case WIFI_AUTH_WEP: + return "WEP"; + case WIFI_AUTH_WPA_PSK: + return "WPA_PSK"; + case WIFI_AUTH_WPA2_PSK: + return "WPA2_PSK"; + case WIFI_AUTH_WPA_WPA2_PSK: + return "WPA_WPA2_PSK"; + case WIFI_AUTH_ENTERPRISE: + return "ENTERPRISE"; + case WIFI_AUTH_WPA3_PSK: + return "WPA3_PSK"; + case WIFI_AUTH_WPA2_WPA3_PSK: + return "WPA2_WPA3_PSK"; + case WIFI_AUTH_WAPI_PSK: + return "WAPI_PSK"; + case WIFI_AUTH_OWE: + return "OWE"; + case WIFI_AUTH_WPA3_ENT_192: + return "WPA3_ENT_192"; + case WIFI_AUTH_WPA3_EXT_PSK: + return "WPA3_EXT_PSK"; + case WIFI_AUTH_WPA3_EXT_PSK_MIXED_MODE: + return "WPA3_EXT_PSK_MIXED_MODE"; + case WIFI_AUTH_DPP: + return "DPP"; + case WIFI_AUTH_WPA3_ENTERPRISE: + return "WPA3_ENTERPRISE"; + case WIFI_AUTH_WPA2_WPA3_ENTERPRISE: + return "WPA2_WPA3_ENTERPRISE"; + default: + return "UNKNOWN"; + } +} + +static const char* wifi_reason_str(wifi_err_reason_t reason) { + switch (reason) { + case WIFI_REASON_UNSPECIFIED: return "UNSPECIFIED"; + case WIFI_REASON_AUTH_EXPIRE: return "AUTH_EXPIRE"; + case WIFI_REASON_AUTH_LEAVE: return "AUTH_LEAVE"; + case WIFI_REASON_ASSOC_EXPIRE: return "ASSOC_EXPIRE"; + case WIFI_REASON_ASSOC_TOOMANY: return "ASSOC_TOOMANY"; + case WIFI_REASON_NOT_AUTHED: return "NOT_AUTHED"; + case WIFI_REASON_NOT_ASSOCED: return "NOT_ASSOCED"; + case WIFI_REASON_ASSOC_LEAVE: return "ASSOC_LEAVE"; + case WIFI_REASON_ASSOC_NOT_AUTHED: return "ASSOC_NOT_AUTHED"; + case WIFI_REASON_DISASSOC_PWRCAP_BAD: return "DISASSOC_PWRCAP_BAD"; + case WIFI_REASON_DISASSOC_SUPCHAN_BAD: return "DISASSOC_SUPCHAN_BAD"; + case WIFI_REASON_IE_INVALID: return "IE_INVALID"; + case WIFI_REASON_MIC_FAILURE: return "MIC_FAILURE"; + case WIFI_REASON_4WAY_HANDSHAKE_TIMEOUT: return "4WAY_HANDSHAKE_TIMEOUT"; + case WIFI_REASON_GROUP_KEY_UPDATE_TIMEOUT: return "GROUP_KEY_UPDATE_TIMEOUT"; + case WIFI_REASON_IE_IN_4WAY_DIFFERS: return "IE_IN_4WAY_DIFFERS"; + case WIFI_REASON_GROUP_CIPHER_INVALID: return "GROUP_CIPHER_INVALID"; + case WIFI_REASON_PAIRWISE_CIPHER_INVALID: return "PAIRWISE_CIPHER_INVALID"; + case WIFI_REASON_AKMP_INVALID: return "AKMP_INVALID"; + case WIFI_REASON_UNSUPP_RSN_IE_VERSION: return "UNSUPP_RSN_IE_VERSION"; + case WIFI_REASON_INVALID_RSN_IE_CAP: return "INVALID_RSN_IE_CAP"; + case WIFI_REASON_802_1X_AUTH_FAILED: return "802_1X_AUTH_FAILED"; + case WIFI_REASON_CIPHER_SUITE_REJECTED: return "CIPHER_SUITE_REJECTED"; + case WIFI_REASON_INVALID_PMKID: return "INVALID_PMKID"; + case WIFI_REASON_BEACON_TIMEOUT: return "BEACON_TIMEOUT"; + case WIFI_REASON_NO_AP_FOUND: return "NO_AP_FOUND"; + case WIFI_REASON_AUTH_FAIL: return "AUTH_FAIL"; + case WIFI_REASON_ASSOC_FAIL: return "ASSOC_FAIL"; + case WIFI_REASON_HANDSHAKE_TIMEOUT: return "HANDSHAKE_TIMEOUT"; + case WIFI_REASON_CONNECTION_FAIL: return "CONNECTION_FAIL"; + case WIFI_REASON_AP_TSF_RESET: return "AP_TSF_RESET"; + case WIFI_REASON_ROAMING: return "ROAMING"; + default: return "UNKNOWN"; + } +} + +static esp_netif_t *wifi_sta_netif = NULL; +static esp_netif_t *wifi_ap_netif = NULL; + +static int s_retry_num = 0; + +static esp_err_t wifi_sta_do_disconnect(void); + +static void sntp_sync_time_cb(struct timeval *tv) +{ + time_t now = 0; + struct tm timeinfo = { 0 }; + time(&now); + localtime_r(&now, &timeinfo); + char strftime_buf[64]; + strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); + ESP_LOGI(TAG, "Time synchronized: %s", strftime_buf); +} + +static void handler_on_wifi_disconnect(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + s_retry_num++; + if (s_retry_num > MAX_RETRY) { + ESP_LOGW(TAG, "WiFi Connect failed %d times, stop reconnect.", s_retry_num); + /* let example_wifi_sta_do_connect() return */ + + wifi_sta_do_disconnect(); + start_reboot_timer(60); + return; + } + wifi_event_sta_disconnected_t *disconn = event_data; + if (disconn->reason == WIFI_REASON_ROAMING) { + ESP_LOGD(TAG, "station roaming, do nothing"); + return; + } + ESP_LOGW(TAG, "Wi-Fi disconnected, reason: (%s)", wifi_reason_str(disconn->reason)); + ESP_LOGI(TAG, "Trying to reconnect..."); + esp_err_t err = esp_wifi_connect(); + if (err == ESP_ERR_WIFI_NOT_STARTED) { + return; + } + ESP_ERROR_CHECK(err); +} + +static void handler_on_wifi_connect(void *esp_netif, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + +} + +static void handler_on_sta_got_ip(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + stop_reboot_timer(); + s_retry_num = 0; + ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; + if (strcmp("sta", esp_netif_get_desc(event->esp_netif)) != 0) { + return; + } + ESP_LOGI(TAG, "Got IPv4 event: Interface \"%s\" address: " IPSTR, esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip)); + ESP_LOGI(TAG, "- IPv4 address: " IPSTR ",", IP2STR(&event->ip_info.ip)); + sync_time(); + led_set(LED_BLU, BLINK_SOLID); +} + +static void wifi_ap_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); + } +} + + +static esp_err_t set_hostname(esp_netif_t* esp_netif, const char *hostname) +{ + if (esp_netif_set_hostname(esp_netif, hostname) != ESP_OK) return ESP_FAIL; + return ESP_OK; +} + +static void wifi_start(wifi_mode_t mode) +{ + size_t hostname_len; + char type_buf[16]; + + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + + if (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) { + esp_netif_inherent_config_t esp_netif_config = ESP_NETIF_INHERENT_DEFAULT_WIFI_STA(); + wifi_sta_netif = esp_netif_create_wifi(WIFI_IF_STA, &esp_netif_config); + + if (nconfig_read(NETIF_TYPE, type_buf, sizeof(type_buf)) == ESP_OK && strcmp(type_buf, "static") == 0) { + ESP_LOGI(TAG, "Using static IP configuration"); + char ip_buf[16], gw_buf[16], mask_buf[16], dns1_buf[16], dns2_buf[16]; + nconfig_read(NETIF_IP, ip_buf, sizeof(ip_buf)); + nconfig_read(NETIF_GATEWAY, gw_buf, sizeof(gw_buf)); + nconfig_read(NETIF_SUBNET, mask_buf, sizeof(mask_buf)); + const char* dns1 = (nconfig_read(NETIF_DNS1, dns1_buf, sizeof(dns1_buf)) == ESP_OK) ? dns1_buf : NULL; + const char* dns2 = (nconfig_read(NETIF_DNS2, dns2_buf, sizeof(dns2_buf)) == ESP_OK) ? dns2_buf : NULL; + if (dns1 == NULL) + wifi_use_static(ip_buf, gw_buf, mask_buf, "8.8.8.8", "8.8.4.4"); + else + wifi_use_static(ip_buf, gw_buf, mask_buf, dns1, dns2); + } else { + ESP_LOGI(TAG, "Using DHCP configuration"); + wifi_use_dhcp(); + } + + nconfig_get_str_len(NETIF_HOSTNAME, &hostname_len); + char buf[hostname_len]; + nconfig_read(NETIF_HOSTNAME, buf, sizeof(buf)); + set_hostname(wifi_sta_netif, buf); + } + + if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA) { + esp_netif_inherent_config_t esp_netif_config_ap = ESP_NETIF_INHERENT_DEFAULT_WIFI_AP(); + wifi_ap_netif = esp_netif_create_wifi(WIFI_IF_AP, &esp_netif_config_ap); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &wifi_ap_event_handler, NULL)); + } + + esp_wifi_set_default_wifi_sta_handlers(); + + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + ESP_ERROR_CHECK(esp_wifi_set_mode(mode)); + ESP_ERROR_CHECK(esp_wifi_start()); +} + + +static void wifi_stop(void) +{ + esp_err_t err = esp_wifi_stop(); + if (err == ESP_ERR_WIFI_NOT_INIT) { + return; + } + ESP_ERROR_CHECK(err); + + if (wifi_ap_netif) { + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, &wifi_ap_event_handler); + esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, &wifi_ap_event_handler); + } + + ESP_ERROR_CHECK(esp_wifi_deinit()); + + if (wifi_sta_netif) { + esp_netif_destroy(wifi_sta_netif); + wifi_sta_netif = NULL; + } + if (wifi_ap_netif) { + esp_netif_destroy(wifi_ap_netif); + wifi_ap_netif = NULL; + } +} + + +static esp_err_t wifi_sta_do_connect(wifi_config_t wifi_config) +{ + stop_reboot_timer(); + s_retry_num = 0; + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, handler_on_wifi_disconnect, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, handler_on_sta_got_ip, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, handler_on_wifi_connect, wifi_sta_netif)); + + + ESP_LOGI(TAG, "Connecting to %s...", (char*)wifi_config.sta.ssid); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + esp_err_t ret = esp_wifi_connect(); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "WiFi connect failed! ret:%x", ret); + return ret; + } + return ESP_OK; +} + +static esp_err_t wifi_sta_do_disconnect(void) +{ + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &handler_on_wifi_disconnect)); + ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &handler_on_sta_got_ip)); + ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, &handler_on_wifi_connect)); + led_set(LED_BLU, BLINK_DOUBLE); + + return esp_wifi_disconnect(); +} + +static void wifi_shutdown(void) +{ + wifi_sta_do_disconnect(); + wifi_stop(); +} + +static esp_err_t do_connect(void) +{ + esp_err_t err; + char mode_buf[16] = {0}; + wifi_mode_t mode = WIFI_MODE_STA; // Default mode + + if (nconfig_read(WIFI_MODE, mode_buf, sizeof(mode_buf)) == ESP_OK) { + if (strcmp(mode_buf, "apsta") == 0) { + mode = WIFI_MODE_APSTA; + ESP_LOGI(TAG, "Starting in APSTA mode"); + } else { // "sta" or anything else defaults to STA + mode = WIFI_MODE_STA; + ESP_LOGI(TAG, "Starting in STA mode"); + } + } else { + ESP_LOGI(TAG, "WIFI_MODE not set, defaulting to STA mode"); + } + + wifi_start(mode); + + // Configure and connect STA interface if needed + if (mode == WIFI_MODE_STA || mode == WIFI_MODE_APSTA) { + wifi_config_t sta_config = {0}; + bool sta_creds_ok = false; + if (nconfig_read(WIFI_SSID, (char*)sta_config.sta.ssid, 32) == ESP_OK && strlen((char*)sta_config.sta.ssid) > 0) { + if (nconfig_read(WIFI_PASSWORD, (char*)sta_config.sta.password, 64) == ESP_OK) { + sta_creds_ok = true; + } + } + + if (sta_creds_ok) { + err = wifi_sta_do_connect(sta_config); + if (err != ESP_OK && mode == WIFI_MODE_STA) { + // In STA-only mode, failure to connect is a fatal error + return err; + } + } else if (mode == WIFI_MODE_STA) { + // In STA-only mode, missing credentials is a fatal error + ESP_LOGE(TAG, "Missing STA credentials in STA mode."); + return ESP_FAIL; + } else { + // In APSTA mode, missing credentials is a warning + ESP_LOGW(TAG, "Missing STA credentials in APSTA mode. STA will not connect."); + } + } + + // Configure AP interface if needed + if (mode == WIFI_MODE_AP || mode == WIFI_MODE_APSTA) { + char ap_ssid[32], ap_pass[64]; + wifi_config_t ap_config = { + .ap = { + .channel = 1, + .max_connection = 4, + .authmode = WIFI_AUTH_WPA2_PSK, + .ssid_hidden = 0, + }, + }; + + if (nconfig_read(AP_SSID, ap_ssid, sizeof(ap_ssid)) == ESP_OK && strlen(ap_ssid) > 0) { + strcpy((char*)ap_config.ap.ssid, ap_ssid); + } else { + strcpy((char*)ap_config.ap.ssid, "ODROID-REMOTE-AP"); + } + + if (nconfig_read(AP_PASSWORD, ap_pass, sizeof(ap_pass)) == ESP_OK && strlen(ap_pass) >= 8) { + strcpy((char*)ap_config.ap.password, ap_pass); + } else { + ap_config.ap.authmode = WIFI_AUTH_OPEN; + memset(ap_config.ap.password, 0, sizeof(ap_config.ap.password)); + } + ap_config.ap.ssid_len = strlen((char*)ap_config.ap.ssid); + + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); + ESP_LOGI(TAG, "AP configured, SSID: %s", ap_config.ap.ssid); + } + + return ESP_OK; +} + +esp_err_t wifi_connect(void) +{ + led_set(LED_BLU, BLINK_DOUBLE); + + static esp_sntp_config_t ntp_cfg = ESP_NETIF_SNTP_DEFAULT_CONFIG_MULTIPLE(3, + ESP_SNTP_SERVER_LIST("time.windows.com", "pool.ntp.org", "216.239.35.0")); // google public ntp + ntp_cfg.start = false; + ntp_cfg.sync_cb = sntp_sync_time_cb; + ntp_cfg.smooth_sync = true; // Sync immediately when started + esp_netif_sntp_init(&ntp_cfg); + + if (do_connect() != ESP_OK) { + return ESP_FAIL; + } + ESP_ERROR_CHECK(esp_register_shutdown_handler(&wifi_shutdown)); + return ESP_OK; +} + + +esp_err_t wifi_disconnect(void) +{ + wifi_shutdown(); + ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&wifi_shutdown)); + return ESP_OK; +} + +void wifi_scan_aps(wifi_ap_record_t **ap_records, uint16_t* count) +{ + ESP_LOGI(TAG, "Starting WiFi scan..."); + + esp_err_t err = esp_wifi_scan_start(NULL, true); + if (err != ESP_OK) { + ESP_LOGE(TAG, "esp_wifi_scan_start failed: %s", esp_err_to_name(err)); + *count = 0; + *ap_records = NULL; + return; + } + + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(count)); + ESP_LOGI(TAG, "Found %u access points", *count); + + if (*count == 0) + *ap_records = NULL; + else + *ap_records = calloc(*count, sizeof(wifi_ap_record_t)); + ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(count, *ap_records)); + ESP_LOGI(TAG, "Scan done"); +} + +esp_err_t wifi_get_current_ap_info(wifi_ap_record_t *ap_info) +{ + esp_err_t ret = esp_wifi_sta_get_ap_info(ap_info); + if (ret != ESP_OK) { + // Clear ssid and set invalid rssi on error + memset(ap_info->ssid, 0, sizeof(ap_info->ssid)); + ap_info->rssi = -127; + } + return ret; +} + +esp_err_t wifi_get_current_ip_info(esp_netif_ip_info_t *ip_info) +{ + return esp_netif_get_ip_info(wifi_sta_netif, ip_info); +} + +esp_err_t wifi_get_dns_info(esp_netif_dns_type_t type, esp_netif_dns_info_t *dns_info) +{ + if (wifi_sta_netif) { + return esp_netif_get_dns_info(wifi_sta_netif, type, dns_info); + } + return ESP_FAIL; +} + +esp_err_t wifi_use_static(const char *ip, const char *gw, const char *netmask, const char *dns1, const char *dns2) +{ + if (wifi_sta_netif == NULL) { + return ESP_FAIL; + } + + esp_err_t err = esp_netif_dhcpc_stop(wifi_sta_netif); + if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_NOT_STOPPED) { + ESP_LOGE(TAG, "Failed to stop DHCP client: %s", esp_err_to_name(err)); + return err; + } + + esp_netif_ip_info_t ip_info; + inet_pton(AF_INET, ip, &ip_info.ip); + inet_pton(AF_INET, gw, &ip_info.gw); + inet_pton(AF_INET, netmask, &ip_info.netmask); + err = esp_netif_set_ip_info(wifi_sta_netif, &ip_info); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set static IP info: %s", esp_err_to_name(err)); + return err; + } + + esp_netif_dns_info_t dns_info; + if (dns1 && strlen(dns1) > 0) { + inet_pton(AF_INET, dns1, &dns_info.ip.u_addr.ip4); + esp_netif_set_dns_info(wifi_sta_netif, ESP_NETIF_DNS_MAIN, &dns_info); + } else { + esp_netif_set_dns_info(wifi_sta_netif, ESP_NETIF_DNS_MAIN, NULL); + } + + if (dns2 && strlen(dns2) > 0) { + inet_pton(AF_INET, dns2, &dns_info.ip.u_addr.ip4); + esp_netif_set_dns_info(wifi_sta_netif, ESP_NETIF_DNS_BACKUP, &dns_info); + } else { + esp_netif_set_dns_info(wifi_sta_netif, ESP_NETIF_DNS_BACKUP, NULL); + } + + return ESP_OK; +} + +esp_err_t wifi_use_dhcp(void) +{ + if (wifi_sta_netif == NULL) { + return ESP_FAIL; + } + esp_err_t err = esp_netif_dhcpc_start(wifi_sta_netif); + if (err != ESP_OK && err != ESP_ERR_ESP_NETIF_DHCP_ALREADY_STARTED) { + ESP_LOGE(TAG, "Failed to start DHCP client: %s", esp_err_to_name(err)); + return err; + } + return ESP_OK; +} + +esp_err_t wifi_switch_mode(const char* mode) +{ + if (strcmp(mode, "sta") != 0 && strcmp(mode, "apsta") != 0) { + ESP_LOGE(TAG, "Invalid mode specified: %s. Use 'sta' or 'apsta'.", mode); + return ESP_ERR_INVALID_ARG; + } + + char current_mode_buf[16] = {0}; + if (nconfig_read(WIFI_MODE, current_mode_buf, sizeof(current_mode_buf)) == ESP_OK) { + if (strcmp(current_mode_buf, mode) == 0) { + ESP_LOGI(TAG, "Already in %s mode.", mode); + return ESP_OK; + } + } + + ESP_LOGI(TAG, "Switching Wi-Fi mode to %s.", mode); + + esp_err_t err = nconfig_write(WIFI_MODE, mode); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to save new Wi-Fi mode to NVS"); + return err; + } + + wifi_disconnect(); + err = wifi_connect(); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to connect in new mode %s", mode); + return err; + } + + ESP_LOGI(TAG, "Successfully switched to %s mode.", mode); + return ESP_OK; +} + +void sync_time() +{ + esp_netif_sntp_start(); + ESP_LOGI(TAG, "SNTP service started, waiting for time synchronization..."); +} \ No newline at end of file diff --git a/page/.gitignore b/page/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/page/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/page/index.html b/page/index.html new file mode 100644 index 0000000..d3d1733 --- /dev/null +++ b/page/index.html @@ -0,0 +1,319 @@ + + + + + ODROID Remote + + + + + +
+
+
+
+ + + Disconnected + + + + Offline + +
+
+ --.-- V | + --.-- A | + --.-- W +
+
+

ODROID Power Mate

+
+
+ + +
+ +
+
+ + + +
+
+
+
+
+
+
+
+
+
+ + +
+ + +
+
    +
  • + Main Power (12V) +
    +
    + +
    +
    +
  • +
  • + USB Power (5V) +
    +
    + +
    +
    +
  • +
  • + Power Actions +
    + + +
    +
  • +
  • + System Uptime +
    + --:--:-- +
    +
  • +
+
+
+
+
+ +
+
+
+ +
Power Input
+
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ + + + + + + + + + + \ No newline at end of file diff --git a/page/package-lock.json b/page/package-lock.json new file mode 100644 index 0000000..294f8a3 --- /dev/null +++ b/page/package-lock.json @@ -0,0 +1,1283 @@ +{ + "name": "odroid-remote-page", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "odroid-remote-page", + "version": "0.0.0", + "dependencies": { + "@xterm/addon-fit": "^0.9.0", + "@xterm/xterm": "^5.5.0", + "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", + "chart.js": "^4.4.3" + }, + "devDependencies": { + "vite": "^7.0.4", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-singlefile": "^2.0.1" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@kurkle/color": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", + "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==" + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "peer": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true + }, + "node_modules/@xterm/addon-fit": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.9.0.tgz", + "integrity": "sha512-hDlPPbTVPYyvwXu/asW8HbJkI/2RMi0cMaJnBZYVeJB0SWP2NeESMCNr+I7CvBlyI0sAxpxOg8Wk4OMkxBz9WA==", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, + "node_modules/@xterm/xterm": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", + "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==" + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/bootstrap": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.7.tgz", + "integrity": "sha512-7KgiD8UHjfcPBHEpDNg+zGz8L3LqR3GVwqZiBRFX04a1BCArZOz1r2kjly2HQ0WokqTO0v1nF+QAt8dsW4lKlw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.8" + } + }, + "node_modules/bootstrap-icons": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.13.1.tgz", + "integrity": "sha512-ijombt4v6bv5CLeXvRWKy7CuM3TRTuPEuGaGKvTV5cz65rQSY8RQ2JcHt6b90cBBAC7s8fsf2EkQDldzCoXUjw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ] + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chart.js": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", + "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=8" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/esbuild": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" + } + }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/vite": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz", + "integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==", + "dev": true, + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.6", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.40.0", + "tinyglobby": "^0.2.14" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite-plugin-compression": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/vite-plugin-compression/-/vite-plugin-compression-0.5.1.tgz", + "integrity": "sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "debug": "^4.3.3", + "fs-extra": "^10.0.0" + }, + "peerDependencies": { + "vite": ">=2.0.0" + } + }, + "node_modules/vite-plugin-singlefile": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/vite-plugin-singlefile/-/vite-plugin-singlefile-2.3.0.tgz", + "integrity": "sha512-DAcHzYypM0CasNLSz/WG0VdKOCxGHErfrjOoyIPiNxTPTGmO6rRD/te93n1YL/s+miXq66ipF1brMBikf99c6A==", + "dev": true, + "dependencies": { + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">18.0.0" + }, + "peerDependencies": { + "rollup": "^4.44.1", + "vite": "^5.4.11 || ^6.0.0 || ^7.0.0" + } + } + } +} diff --git a/page/package.json b/page/package.json new file mode 100644 index 0000000..75f8356 --- /dev/null +++ b/page/package.json @@ -0,0 +1,23 @@ +{ + "name": "odroid-remote-page", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "devDependencies": { + "vite": "^7.0.4", + "vite-plugin-singlefile": "^2.0.1", + "vite-plugin-compression": "^0.5.1" + }, + "dependencies": { + "@xterm/xterm": "^5.5.0", + "bootstrap": "^5.3.3", + "bootstrap-icons": "^1.11.3", + "@xterm/addon-fit": "^0.9.0", + "chart.js": "^4.4.3" + } +} diff --git a/page/src/api.js b/page/src/api.js new file mode 100644 index 0000000..17df83c --- /dev/null +++ b/page/src/api.js @@ -0,0 +1,112 @@ +/** + * @file api.js + * @description This module centralizes all API calls to the server's RESTful endpoints. + * It abstracts the fetch logic, error handling, and JSON parsing for network and control operations. + */ + +/** + * Fetches the list of available Wi-Fi networks from the server. + * @returns {Promise>} A promise that resolves to an array of Wi-Fi access point objects. + * @throws {Error} Throws an error if the network request fails. + */ +export async function fetchWifiScan() { + const response = await fetch('/api/wifi/scan'); + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + return await response.json(); +} + +/** + * Sends a request to connect to a specific Wi-Fi network. + * @param {string} ssid The SSID of the network to connect to. + * @param {string} password The password for the network. + * @returns {Promise} A promise that resolves to the server's JSON response. + * @throws {Error} Throws an error if the connection request fails. + */ +export async function postWifiConnect(ssid, password) { + const response = await fetch('/api/setting', { // Updated URL + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ssid, password }), + }); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(errorText || `Connection failed with status: ${response.status}`); + } + return await response.json(); +} + +/** + * Posts updated network settings (e.g., static IP, DHCP, AP mode) to the server. + * @param {Object} payload The settings object to be sent. + * @returns {Promise} A promise that resolves to the raw fetch response. + * @throws {Error} Throws an error if the request fails. + */ +export async function postNetworkSettings(payload) { + const response = await fetch('/api/setting', { // Updated URL + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + }); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(errorText || `Failed to apply settings with status: ${response.status}`); + } + return response; +} + +/** + * Posts the selected UART baud rate to the server. + * @param {string} baudrate The selected baud rate. + * @returns {Promise} A promise that resolves to the raw fetch response. + * @throws {Error} Throws an error if the request fails. + */ +export async function postBaudRateSetting(baudrate) { + const response = await fetch('/api/setting', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ baudrate }), + }); + if (!response.ok) { + const errorText = await response.text(); + throw new Error(errorText || `Failed to apply baudrate with status: ${response.status}`); + } + return response; +} + +/** + * Fetches the current network settings and Wi-Fi status from the server. + * @returns {Promise} A promise that resolves to an object containing the current settings. + * @throws {Error} Throws an error if the network request fails. + */ +export async function fetchSettings() { + const response = await fetch('/api/setting'); // Updated URL + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + return await response.json(); +} + +/** + * Fetches the current status of the power control relays (12V and 5V). + * @returns {Promise} A promise that resolves to an object with the power status. + * @throws {Error} Throws an error if the network request fails. + */ +export async function fetchControlStatus() { + const response = await fetch('/api/control'); + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + return await response.json(); +} + +/** + * Sends a command to the server to control power functions (e.g., toggle relays, trigger reset). + * @param {Object} command The control command object. + * @returns {Promise} A promise that resolves to the raw fetch response. + * @throws {Error} Throws an error if the request fails. + */ +export async function postControlCommand(command) { + const response = await fetch('/api/control', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify(command) + }); + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + return response; +} diff --git a/page/src/chart.js b/page/src/chart.js new file mode 100644 index 0000000..82b5f7e --- /dev/null +++ b/page/src/chart.js @@ -0,0 +1,211 @@ +/** + * @file chart.js + * @description This module manages the Chart.js instances for visualizing sensor data. + * It handles initialization, theme updates, data updates, and resizing for the three separate charts. + */ + +import { Chart, registerables } from 'chart.js'; +import { powerChartCtx, voltageChartCtx, currentChartCtx, htmlEl, graphTabPane } from './dom.js'; + +// Register all necessary Chart.js components +Chart.register(...registerables); + +// Store chart instances in an object +export const charts = { + power: null, + voltage: null, + current: null +}; + +const CHART_DATA_POINTS = 30; // Number of data points to display on the chart + +/** + * Creates an array of empty labels for initial chart rendering. + * @returns {Array} An array of empty strings. + */ +function initialLabels() { + return Array(CHART_DATA_POINTS).fill(''); +} + +/** + * Creates an array of null data points for initial chart rendering. + * @returns {Array} An array of nulls. + */ +function initialData() { + return Array(CHART_DATA_POINTS).fill(null); +} + +/** + * Creates a common configuration object for a single line chart. + * @param {string} title - The title of the chart (e.g., 'Power (W)'). + * @returns {Object} A Chart.js options object. + */ +function createChartOptions(title) { + return { + responsive: true, + maintainAspectRatio: false, + interaction: { mode: 'index', intersect: false }, + plugins: { + legend: { position: 'top' }, + title: { display: true, text: title } + }, + scales: { + x: { ticks: { autoSkipPadding: 10, maxRotation: 0, minRotation: 0 } }, + y: { } + } + }; +} + +/** + * Initializes all three charts (Power, Voltage, Current). + * If chart instances already exist, they are destroyed and new ones are created. + */ +export function initCharts() { + // Destroy existing charts if they exist + for (const key in charts) { + if (charts[key]) { + charts[key].destroy(); + } + } + + // Create Power Chart + if (powerChartCtx) { + charts.power = new Chart(powerChartCtx, { + type: 'line', + data: { + labels: initialLabels(), + datasets: [ + { label: 'Power (W)', data: initialData(), borderWidth: 2, fill: false, tension: 0.2, pointRadius: 2 }, + { label: 'Avg Power', data: initialData(), borderWidth: 1.5, borderDash: [10, 5], fill: false, tension: 0, pointRadius: 0 } + ] + }, + options: createChartOptions('Power') + }); + } + + // Create Voltage Chart + if (voltageChartCtx) { + charts.voltage = new Chart(voltageChartCtx, { + type: 'line', + data: { + labels: initialLabels(), + datasets: [ + { label: 'Voltage (V)', data: initialData(), borderWidth: 2, fill: false, tension: 0.2, pointRadius: 2 }, + { label: 'Avg Voltage', data: initialData(), borderWidth: 1.5, borderDash: [10, 5], fill: false, tension: 0, pointRadius: 0 } + ] + }, + options: createChartOptions('Voltage') + }); + } + + // Create Current Chart + if (currentChartCtx) { + charts.current = new Chart(currentChartCtx, { + type: 'line', + data: { + labels: initialLabels(), + datasets: [ + { label: 'Current (A)', data: initialData(), borderWidth: 2, fill: false, tension: 0.2, pointRadius: 2 }, + { label: 'Avg Current', data: initialData(), borderWidth: 1.5, borderDash: [10, 5], fill: false, tension: 0, pointRadius: 0 } + ] + }, + options: createChartOptions('Current') + }); + } +} + +/** + * Applies a new theme (light or dark) to all charts. + * @param {string} themeName - The name of the theme to apply ('light' or 'dark'). + */ +export function applyChartsTheme(themeName) { + const isDark = themeName === 'dark'; + const gridColor = isDark ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.1)'; + const labelColor = isDark ? '#dee2e6' : '#212529'; + + const powerColor = getComputedStyle(htmlEl).getPropertyValue('--chart-power-color'); + const voltageColor = getComputedStyle(htmlEl).getPropertyValue('--chart-voltage-color'); + const currentColor = getComputedStyle(htmlEl).getPropertyValue('--chart-current-color'); + + const updateThemeForChart = (chart, color) => { + if (!chart) return; + chart.options.scales.x.grid.color = gridColor; + chart.options.scales.y.grid.color = gridColor; + chart.options.scales.x.ticks.color = labelColor; + chart.options.scales.y.ticks.color = labelColor; + chart.options.plugins.legend.labels.color = labelColor; + chart.options.plugins.title.color = labelColor; + chart.data.datasets[0].borderColor = color; + chart.data.datasets[1].borderColor = color; + chart.data.datasets[1].borderDash = [10, 5]; + chart.update('none'); + }; + + updateThemeForChart(charts.power, powerColor); + updateThemeForChart(charts.voltage, voltageColor); + updateThemeForChart(charts.current, currentColor); +} + +/** + * Updates all charts with new sensor data. + * @param {Object} data - The new sensor data object from the WebSocket. + */ +export function updateCharts(data) { + const timeLabel = new Date(data.timestamp * 1000).toLocaleTimeString(); + + const updateSingleChart = (chart, value) => { + if (!chart) return; + + // Shift old data + chart.data.labels.shift(); + chart.data.datasets.forEach(dataset => dataset.data.shift()); + + // Push new data + chart.data.labels.push(timeLabel); + chart.data.datasets[0].data.push(value.toFixed(2)); + + // Calculate average and adjust Y-axis scale + const dataArray = chart.data.datasets[0].data.filter(v => v !== null).map(v => parseFloat(v)); + if (dataArray.length > 0) { + const sum = dataArray.reduce((acc, val) => acc + val, 0); + const avg = (sum / dataArray.length).toFixed(2); + chart.data.datasets[1].data.push(avg); + + // Adjust Y-axis scale for centering + const minVal = Math.min(...dataArray); + const maxVal = Math.max(...dataArray); + let padding = (maxVal - minVal) * 0.1; // 10% padding of the range + + if (padding === 0) { + // If all values are the same, add 10% padding of the value itself, or a small default + padding = maxVal > 0 ? maxVal * 0.1 : 0.1; + } + + chart.options.scales.y.min = Math.max(0, minVal - padding); + chart.options.scales.y.max = maxVal + padding; + + } else { + chart.data.datasets[1].data.push(null); + } + + // Only update the chart if the tab is visible + if (graphTabPane.classList.contains('show')) { + chart.update('none'); + } + }; + + updateSingleChart(charts.power, data.power); + updateSingleChart(charts.voltage, data.voltage); + updateSingleChart(charts.current, data.current); +} + +/** + * Resizes all chart canvases. This is typically called on window resize events. + */ +export function resizeCharts() { + for (const key in charts) { + if (charts[key]) { + charts[key].resize(); + } + } +} diff --git a/page/src/dom.js b/page/src/dom.js new file mode 100644 index 0000000..4335f03 --- /dev/null +++ b/page/src/dom.js @@ -0,0 +1,78 @@ +/** + * @file dom.js + * @description This module selects and exports all necessary DOM elements for the application. + * This centralizes DOM access, making it easier to manage and modify element selectors. + */ + +// --- Theme Elements --- +export const themeToggle = document.getElementById('theme-toggle'); +export const themeIcon = document.getElementById('theme-icon'); +export const htmlEl = document.documentElement; + +// --- Control & Status Elements --- +export const mainPowerToggle = document.getElementById('main-power-toggle'); +export const usbPowerToggle = document.getElementById('usb-power-toggle'); +export const resetButton = document.getElementById('reset-button'); +export const powerActionButton = document.getElementById('power-action-button'); +export const voltageDisplay = document.getElementById('voltage-display'); +export const currentDisplay = document.getElementById('current-display'); +export const powerDisplay = document.getElementById('power-display'); +export const uptimeDisplay = document.getElementById('uptime-display'); + +// --- Terminal Elements --- +export const terminalContainer = document.getElementById('terminal'); +export const clearButton = document.getElementById('clear-button'); +export const downloadButton = document.getElementById('download-button'); + +// --- Chart & Graph Elements --- +export const graphTabPane = document.getElementById('graph-tab-pane'); +export const powerChartCtx = document.getElementById('powerChart')?.getContext('2d'); +export const voltageChartCtx = document.getElementById('voltageChart')?.getContext('2d'); +export const currentChartCtx = document.getElementById('currentChart')?.getContext('2d'); + +// --- WebSocket Status Elements --- +export const websocketStatus = document.getElementById('websocket-status'); +export const websocketIcon = document.getElementById('websocket-icon'); +export const websocketStatusText = document.getElementById('websocket-status-text'); + +// --- Header Wi-Fi Status --- +export const wifiStatus = document.getElementById('wifi-status'); +export const wifiIcon = document.getElementById('wifi-icon'); +export const wifiSsidStatus = document.getElementById('wifi-ssid-status'); + +// --- Settings Modal & General --- +export const settingsButton = document.getElementById('settings-button'); +export const settingsModal = document.getElementById('settingsModal'); + +// --- Wi-Fi Settings Elements --- +export const wifiApList = document.getElementById('wifi-ap-list'); +export const scanWifiButton = document.getElementById('scan-wifi-button'); +export const currentWifiSsid = document.getElementById('current-wifi-ssid'); +export const currentWifiIp = document.getElementById('current-wifi-ip'); + +// --- Wi-Fi Connection Modal Elements --- +export const wifiModalEl = document.getElementById('wifiModal'); +export const wifiSsidConnectInput = document.getElementById('wifi-ssid-connect'); +export const wifiPasswordConnectInput = document.getElementById('wifi-password-connect'); +export const wifiConnectButton = document.getElementById('wifi-connect-button'); + +// --- Static IP Config Elements --- +export const staticIpToggle = document.getElementById('static-ip-toggle'); +export const staticIpConfig = document.getElementById('static-ip-config'); +export const networkApplyButton = document.getElementById('network-apply-button'); +export const staticIpInput = document.getElementById('ip-address'); +export const staticGatewayInput = document.getElementById('gateway'); +export const staticNetmaskInput = document.getElementById('netmask'); +export const dns1Input = document.getElementById('dns1'); +export const dns2Input = document.getElementById('dns2'); + +// --- AP Mode Config Elements --- +export const apModeToggle = document.getElementById('ap-mode-toggle'); +export const apModeConfig = document.getElementById('ap-mode-config'); +export const apModeApplyButton = document.getElementById('ap-mode-apply-button'); +export const apSsidInput = document.getElementById('ap-ssid'); +export const apPasswordInput = document.getElementById('ap-password'); + +// --- Device Settings Elements --- +export const baudRateSelect = document.getElementById('baud-rate-select'); +export const baudRateApplyButton = document.getElementById('baud-rate-apply-button'); diff --git a/page/src/events.js b/page/src/events.js new file mode 100644 index 0000000..2f23be2 --- /dev/null +++ b/page/src/events.js @@ -0,0 +1,93 @@ +/** + * @file events.js + * @description This module sets up all the event listeners for the application. + * It connects user interactions (like clicks and toggles) to the corresponding + * functions in other modules (UI, API, etc.). + */ + +import * as dom from './dom.js'; +import * as api from './api.js'; +import * as ui from './ui.js'; +import { clearTerminal, fitTerminal, downloadTerminalOutput } from './terminal.js'; +import { debounce, isMobile } from './utils.js'; + +// A flag to track if charts have been initialized +let chartsInitialized = false; + +/** + * Sets up all event listeners for the application's interactive elements. + */ +export function setupEventListeners() { + // --- Theme Toggle --- + dom.themeToggle.addEventListener('change', () => { + const newTheme = dom.themeToggle.checked ? 'dark' : 'light'; + localStorage.setItem('theme', newTheme); + ui.applyTheme(newTheme); + }); + + // --- Terminal Controls --- + dom.clearButton.addEventListener('click', clearTerminal); + dom.downloadButton.addEventListener('click', downloadTerminalOutput); + + // --- Power Controls --- + dom.mainPowerToggle.addEventListener('change', () => api.postControlCommand({'load_12v_on': dom.mainPowerToggle.checked}).then(ui.updateControlStatus)); + dom.usbPowerToggle.addEventListener('change', () => api.postControlCommand({'load_5v_on': dom.usbPowerToggle.checked}).then(ui.updateControlStatus)); + dom.resetButton.addEventListener('click', () => api.postControlCommand({'reset_trigger': true})); + dom.powerActionButton.addEventListener('click', () => api.postControlCommand({'power_trigger': true})); + + // --- Settings Modal Controls --- + dom.scanWifiButton.addEventListener('click', ui.scanForWifi); + dom.wifiConnectButton.addEventListener('click', ui.connectToWifi); + dom.networkApplyButton.addEventListener('click', ui.applyNetworkSettings); + dom.apModeApplyButton.addEventListener('click', ui.applyApModeSettings); + dom.baudRateApplyButton.addEventListener('click', ui.applyBaudRateSettings); + + // --- Settings Modal Toggles (for showing/hiding sections) --- + dom.apModeToggle.addEventListener('change', () => { + dom.apModeConfig.style.display = dom.apModeToggle.checked ? 'block' : 'none'; + }); + + dom.staticIpToggle.addEventListener('change', () => { + dom.staticIpConfig.style.display = dom.staticIpToggle.checked ? 'block' : 'none'; + }); + + // --- General App Listeners --- + dom.settingsButton.addEventListener('click', ui.initializeSettings); + + // --- Accessibility: Remove focus from modal elements before hiding --- + const blurActiveElement = () => { + if (document.activeElement && typeof document.activeElement.blur === 'function') { + document.activeElement.blur(); + } + }; + dom.settingsModal.addEventListener('hide.bs.modal', blurActiveElement); + dom.wifiModalEl.addEventListener('hide.bs.modal', blurActiveElement); + + // --- Bootstrap Tab Events --- + document.querySelectorAll('button[data-bs-toggle="tab"]').forEach(tabEl => { + tabEl.addEventListener('shown.bs.tab', async (event) => { + const tabId = event.target.getAttribute('data-bs-target'); + + if (tabId === '#graph-tab-pane') { + // Dynamically import the chart module only when the tab is shown + const chartModule = await import('./chart.js'); + + if (!chartsInitialized) { + chartModule.initCharts(); + chartsInitialized = true; + } else { + chartModule.resizeCharts(); + } + } else if (tabId === '#terminal-tab-pane') { + // Fit the terminal when its tab is shown, especially for mobile. + if (isMobile()) { + fitTerminal(); + } + } + }); + }); + + // --- Window Resize Event --- + // Debounced to avoid excessive calls during resizing. + window.addEventListener('resize', debounce(ui.handleResize, 150)); +} diff --git a/page/src/main.js b/page/src/main.js new file mode 100644 index 0000000..492f448 --- /dev/null +++ b/page/src/main.js @@ -0,0 +1,109 @@ +/** + * @file main.js + * @description The main entry point for the web application. + * This file imports all necessary modules, sets up the application structure, + * initializes components, and establishes the WebSocket connection. + */ + +// --- Stylesheets --- +import 'bootstrap/dist/css/bootstrap.min.css'; +import 'bootstrap-icons/font/bootstrap-icons.css'; +import './style.css'; + +// --- Module Imports --- +import { initWebSocket } from './websocket.js'; +import { setupTerminal, term } from './terminal.js'; +import { + applyTheme, + initUI, + updateControlStatus, + updateSensorUI, + updateWifiStatusUI, + updateWebsocketStatus +} from './ui.js'; +import { setupEventListeners } from './events.js'; + +// --- WebSocket Event Handlers --- + +/** + * Callback function for when the WebSocket connection is successfully opened. + * Updates the UI to show an 'Online' status and fetches the initial control status. + */ +function onWsOpen() { + updateWebsocketStatus(true); + if (term) { + term.write('\x1b[32mConnected to WebSocket Server\x1b[0m\r\n'); + } + updateControlStatus(); +} + +/** + * Callback function for when the WebSocket connection is closed. + * Updates the UI to show an 'Offline' status and attempts to reconnect after a delay. + */ +function onWsClose() { + updateWebsocketStatus(false); + if (term) { + term.write('\r\n\x1b[31mConnection closed. Reconnecting...\x1b[0m\r\n'); + } + // Attempt to re-establish the connection after 2 seconds + setTimeout(initialize, 2000); +} + +/** + * Callback function for when a message is received from the WebSocket server. + * It handles both JSON messages (for sensor and status updates) and binary data (for the terminal). + * @param {MessageEvent} event - The WebSocket message event. + */ +function onWsMessage(event) { + if (typeof event.data === 'string') { + try { + const message = JSON.parse(event.data); + if (message.type === 'sensor_data') { + updateSensorUI(message); + } else if (message.type === 'wifi_status') { + updateWifiStatusUI(message); + } + } catch (e) { + // Ignore non-JSON string messages + } + } else if (term && event.data instanceof ArrayBuffer) { + // Write raw UART data to the terminal + const data = new Uint8Array(event.data); + term.write(data); + } +} + +// --- Application Initialization --- + +/** + * Initializes the entire application. + * This function sets up the UI, theme, terminal, chart, WebSocket connection, and event listeners. + */ +function initialize() { + // Initialize basic UI components + initUI(); + + // Set up the interactive components first + setupTerminal(); + + // Apply the saved theme or detect the user's preferred theme. + // This must be done AFTER the chart and terminal are initialized. + const savedTheme = localStorage.getItem('theme') || (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'); + applyTheme(savedTheme); + + // Establish the WebSocket connection with the defined handlers + initWebSocket({ + onOpen: onWsOpen, + onClose: onWsClose, + onMessage: onWsMessage + }); + + // Attach all event listeners to the DOM elements + setupEventListeners(); +} + +// --- Start Application --- + +// Wait for the DOM to be fully loaded before initializing the application. +document.addEventListener('DOMContentLoaded', initialize); diff --git a/page/src/style.css b/page/src/style.css new file mode 100644 index 0000000..f9ad2f0 --- /dev/null +++ b/page/src/style.css @@ -0,0 +1,153 @@ + +:root { + --bs-body-font-family: 'Courier New', Courier, monospace; + --chart-power-color: #007bff; + --chart-voltage-color: #28a745; + --chart-current-color: #ffc107; +} + +[data-bs-theme="dark"] { + --chart-power-color: #569cd6; + --chart-voltage-color: #4ec9b0; + --chart-current-color: #dcdcaa; +} + +body, .card, .modal-content, .list-group-item, .nav-tabs .nav-link { + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out, border-color 0.2s ease-in-out; +} + +html, body { + height: 100%; +} + +body { + display: flex; + flex-direction: column; + padding-top: 1rem; +} + +main { + flex-grow: 1; + padding-bottom: 2rem; +} + +#terminal-tab-pane .card-body { + text-align: center; +} + +#terminal-container { + padding: 0.5rem; + background-color: var(--bs-body-bg); + overflow: auto; +} + +#terminal-wrapper { + display: inline-block; + text-align: left; + /* Removed height: 100% to allow shrink-wrapping */ +} + +#graph-tab-pane .card-body { + display: flex; + flex-direction: column; + justify-content: center; /* Vertically center the chart content */ +} + +.xterm .xterm-viewport { + background-color: transparent !important; +} + +.xterm-viewport::-webkit-scrollbar { + width: 12px; +} + +.xterm-viewport::-webkit-scrollbar-track { + background: var(--bs-tertiary-bg); +} + +.xterm-viewport::-webkit-scrollbar-thumb { + background-color: var(--bs-secondary-bg); + border-radius: 10px; + border: 3px solid var(--bs-tertiary-bg); +} + +.xterm-viewport::-webkit-scrollbar-thumb:hover { + background-color: var(--bs-secondary-color); +} + +.xterm-viewport { + scrollbar-width: thin; + scrollbar-color: var(--bs-secondary-bg) var(--bs-tertiary-bg); +} + +.chart-canvas { + height: 30rem !important; +} + +.control-wrapper { + min-height: 38px; /* Match button height for alignment */ + display: flex; + align-items: center; + justify-content: flex-end; +} + +.control-wrapper .form-switch { + display: flex; + align-items: center; + height: 100%; +} + +footer { + flex-shrink: 0; + font-size: 0.9em; +} + +footer a { + text-decoration: none; +} + +.wifi-ap-row { + cursor: pointer; +} + +/* Mobile Optimizations */ +@media (max-width: 767.98px) { + .main-header { + flex-direction: column; + } + + .header-controls { + width: 100%; + justify-content: space-between !important; + margin-top: 0.5rem; + } + + .main-header h1 { + font-size: 1.75rem; + } + + .control-list-item { + flex-direction: column; + align-items: flex-start !important; + } + + .control-list-item > .control-wrapper { + margin-top: 0.5rem; + width: 100%; + display: flex; + justify-content: space-between; + } + + .control-list-item > .control-wrapper > .btn { + flex-grow: 1; + } + + #terminal-wrapper, #terminal-container { + width: 100%; + /* Removed height: 100% */ + } + + #terminal-tab-pane .card-body { + padding: 0; + } +} diff --git a/page/src/terminal.js b/page/src/terminal.js new file mode 100644 index 0000000..614a7aa --- /dev/null +++ b/page/src/terminal.js @@ -0,0 +1,135 @@ +/** + * @file terminal.js + * @description This module manages the Xterm.js terminal instance, including setup, + * theme handling, and data communication with the WebSocket. + */ + +import { Terminal } from '@xterm/xterm'; +import '@xterm/xterm/css/xterm.css'; +import { FitAddon } from '@xterm/addon-fit'; +import { terminalContainer } from './dom.js'; +import { isMobile } from './utils.js'; +import { websocket, sendWebsocketMessage } from './websocket.js'; + +// Exported terminal instance and addon for global access +export let term; +export let fitAddon; + +// Theme definitions for the terminal +const lightTheme = { + background: 'transparent', + foreground: '#212529', + cursor: '#212529', + selectionBackground: 'rgba(0,0,0,0.1)' +}; + +const darkTheme = { + background: 'transparent', + foreground: '#dee2e6', + cursor: '#dee2e6', + selectionBackground: 'rgba(255,255,255,0.1)' +}; + +/** + * Initializes the Xterm.js terminal, loads addons, and attaches it to the DOM. + * Sets up the initial size and the data handler for sending input to the WebSocket. + */ +export function setupTerminal() { + // Ensure the container is empty before creating a new terminal + while (terminalContainer.firstChild) { + terminalContainer.removeChild(terminalContainer.firstChild); + } + + fitAddon = new FitAddon(); + term = new Terminal({ convertEol: true, cursorBlink: true }); + term.loadAddon(fitAddon); + term.open(terminalContainer); + + // Adjust terminal size based on device type + if (isMobile()) { + fitAddon.fit(); + } else { + term.resize(80, 24); // Default size for desktop + } + + // Handle user input and send it over the WebSocket + term.onData(data => { + sendWebsocketMessage(data); + }); +} + +/** + * Applies a new theme (light or dark) to the terminal. + * @param {string} themeName - The name of the theme to apply ('light' or 'dark'). + */ +export function applyTerminalTheme(themeName) { + if (!term) return; + const isDark = themeName === 'dark'; + term.options.theme = isDark ? darkTheme : lightTheme; +} + +/** + * Clears the terminal screen. + */ +export function clearTerminal() { + if (term) { + term.clear(); + } +} + +/** + * Fits the terminal to the size of its container element. + * Useful for responsive adjustments on window resize. + */ +export function fitTerminal() { + if (fitAddon) { + fitAddon.fit(); + } +} + +/** + * Gathers all text from the terminal buffer and downloads it as a .log file. + */ +export function downloadTerminalOutput() { + if (!term) { + console.error("Terminal is not initialized."); + return; + } + + const buffer = term.buffer.active; + let fullText = ''; + + // Iterate through the buffer to get all lines + for (let i = 0; i < buffer.length; i++) { + const line = buffer.getLine(i); + if (line) { + fullText += line.translateToString() + '\n'; + } + } + + if (fullText.trim() === '') { + console.warn("Terminal is empty, nothing to download."); + // Optionally, provide user feedback here (e.g., a toast notification) + return; + } + + // Create a blob from the text content + const blob = new Blob([fullText], { type: 'text/plain;charset=utf-8' }); + + // Create a link element to trigger the download + const link = document.createElement('a'); + const url = URL.createObjectURL(blob); + + // Generate a filename with a timestamp + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + link.download = `odroid-log-${timestamp}.log`; + link.href = url; + + // Append to the document, click, and then remove + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + + // Clean up the object URL + URL.revokeObjectURL(url); +} diff --git a/page/src/ui.js b/page/src/ui.js new file mode 100644 index 0000000..2c031cb --- /dev/null +++ b/page/src/ui.js @@ -0,0 +1,346 @@ +/** + * @file ui.js + * @description This module manages all UI interactions and updates. + * It bridges the gap between backend data (from API and WebSockets) and the user-facing components, + * handling everything from theme changes to dynamic content updates. + */ + +import * as bootstrap from 'bootstrap'; +import * as dom from './dom.js'; +import * as api from './api.js'; +import { formatUptime, isMobile } from './utils.js'; +import { applyTerminalTheme, fitTerminal } from './terminal.js'; +import { applyChartsTheme, resizeCharts, updateCharts } from './chart.js'; + +// Instance of the Bootstrap Modal for Wi-Fi connection +let wifiModal; + +/** + * Initializes the UI components, such as the Bootstrap modal. + */ +export function initUI() { + wifiModal = new bootstrap.Modal(dom.wifiModalEl); +} + +/** + * Applies the selected theme (light or dark) to the entire application. + * @param {string} themeName - The name of the theme ('light' or 'dark'). + */ +export function applyTheme(themeName) { + const isDark = themeName === 'dark'; + dom.htmlEl.setAttribute('data-bs-theme', themeName); + dom.themeIcon.className = isDark ? 'bi bi-moon-stars-fill' : 'bi bi-sun-fill'; + dom.themeToggle.checked = isDark; + applyTerminalTheme(themeName); + applyChartsTheme(themeName); +} + +/** + * Updates the UI with the latest sensor data. + * @param {Object} data - The sensor data object from the WebSocket. + */ +export function updateSensorUI(data) { + dom.voltageDisplay.textContent = `${data.voltage.toFixed(2)} V`; + dom.currentDisplay.textContent = `${data.current.toFixed(2)} A`; + dom.powerDisplay.textContent = `${data.power.toFixed(2)} W`; + if (data.uptime_sec !== undefined) { + dom.uptimeDisplay.textContent = formatUptime(data.uptime_sec); + } + updateCharts(data); +} + +/** + * Updates the Wi-Fi status indicator in the header. + * @param {Object} data - The Wi-Fi status object from the WebSocket. + */ +export function updateWifiStatusUI(data) { + if (data.connected) { + dom.wifiSsidStatus.textContent = data.ssid; + dom.wifiStatus.title = `Signal Strength: ${data.rssi} dBm`; + let iconClass = 'bi me-2 '; + if (data.rssi >= -60) iconClass += 'bi-wifi'; + else if (data.rssi >= -75) iconClass += 'bi-wifi-2'; + else iconClass += 'bi-wifi-1'; + dom.wifiIcon.className = iconClass; + dom.wifiStatus.classList.replace('text-muted', 'text-success'); + dom.wifiStatus.classList.remove('text-danger'); + } else { + dom.wifiSsidStatus.textContent = 'Disconnected'; + dom.wifiStatus.title = ''; + dom.wifiIcon.className = 'bi bi-wifi-off me-2'; + dom.wifiStatus.classList.replace('text-success', 'text-muted'); + dom.wifiStatus.classList.remove('text-danger'); + } +} + +/** + * Initiates a Wi-Fi scan and updates the settings modal with the results. + */ +export async function scanForWifi() { + dom.scanWifiButton.disabled = true; + dom.scanWifiButton.innerHTML = ` Scanning...`; + dom.wifiApList.innerHTML = 'Scanning for networks...'; + + try { + const apRecords = await api.fetchWifiScan(); + dom.wifiApList.innerHTML = ''; // Clear loading message + + if (apRecords.length === 0) { + dom.wifiApList.innerHTML = 'No networks found.'; + } else { + apRecords.forEach(ap => { + const row = document.createElement('tr'); + row.className = 'wifi-ap-row'; + + let rssiIcon; + if (ap.rssi >= -60) rssiIcon = 'bi-wifi'; + else if (ap.rssi >= -75) rssiIcon = 'bi-wifi-2'; + else rssiIcon = 'bi-wifi-1'; + + row.innerHTML = ` + ${ap.ssid} + + ${ap.authmode} + `; + + row.addEventListener('click', () => { + dom.wifiSsidConnectInput.value = ap.ssid; + dom.wifiPasswordConnectInput.value = ''; + wifiModal.show(); + dom.wifiModalEl.addEventListener('shown.bs.modal', () => { + dom.wifiPasswordConnectInput.focus(); + }, { once: true }); + }); + + dom.wifiApList.appendChild(row); + }); + } + } catch (error) { + console.error('Error scanning for Wi-Fi:', error); + dom.wifiApList.innerHTML = `Scan failed: ${error.message}`; + } finally { + dom.scanWifiButton.disabled = false; + dom.scanWifiButton.innerHTML = 'Scan'; + } +} + +/** + * Handles the Wi-Fi connection process, sending credentials to the server. + */ +export async function connectToWifi() { + const ssid = dom.wifiSsidConnectInput.value; + const password = dom.wifiPasswordConnectInput.value; + if (!ssid) return; + + dom.wifiConnectButton.disabled = true; + dom.wifiConnectButton.innerHTML = ` Connecting...`; + + try { + const result = await api.postWifiConnect(ssid, password); + if (result.status === 'connection_initiated') { + wifiModal.hide(); + setTimeout(() => { + alert(`Connection to "${ssid}" initiated. The device will try to reconnect. Please check the Wi-Fi status icon.`); + }, 500); + } else { + throw new Error(result.message || 'Unknown server response.'); + } + } catch (error) { + console.error('Error connecting to Wi-Fi:', error); + alert(`Failed to connect: ${error.message}`); + } finally { + dom.wifiConnectButton.disabled = false; + dom.wifiConnectButton.innerHTML = 'Connect'; + } +} + +/** + * Applies network settings (Static IP or DHCP) by sending the configuration to the server. + */ +export async function applyNetworkSettings() { + const useStatic = dom.staticIpToggle.checked; + let payload; + + dom.networkApplyButton.disabled = true; + dom.networkApplyButton.innerHTML = ` Applying...`; + + if (useStatic) { + const ip = dom.staticIpInput.value; + const gateway = dom.staticGatewayInput.value; + const subnet = dom.staticNetmaskInput.value; + const dns1 = dom.dns1Input.value; + const dns2 = dom.dns2Input.value; + + if (!ip || !gateway || !subnet || !dns1) { + alert('For static IP, you must provide IP Address, Gateway, Netmask, and DNS Server.'); + dom.networkApplyButton.disabled = false; + dom.networkApplyButton.innerHTML = 'Apply'; + return; + } + + payload = { net_type: 'static', ip, gateway, subnet, dns1 }; + if (dns2) payload.dns2 = dns2; + } else { + payload = { net_type: 'dhcp' }; + } + + try { + await api.postNetworkSettings(payload); + alert('Network settings applied. Reconnect to the network for changes to take effect.'); + initializeSettings(); + } catch (error) { + console.error('Error applying network settings:', error); + alert(`Failed to apply settings: ${error.message}`); + } finally { + dom.networkApplyButton.disabled = false; + dom.networkApplyButton.innerHTML = 'Apply'; + } +} + +/** + * Applies AP Mode settings (AP+STA or STA) by sending the configuration to the server. + */ +export async function applyApModeSettings() { + const mode = dom.apModeToggle.checked ? 'apsta' : 'sta'; + let payload = { mode }; + + dom.apModeApplyButton.disabled = true; + dom.apModeApplyButton.innerHTML = ` Applying...`; + + if (mode === 'apsta') { + const ap_ssid = dom.apSsidInput.value; + const ap_password = dom.apPasswordInput.value; + + if (!ap_ssid) { + alert('AP SSID cannot be empty when enabling APSTA mode.'); + dom.apModeApplyButton.disabled = false; + dom.apModeApplyButton.innerHTML = 'Apply'; + return; + } + payload.ap_ssid = ap_ssid; + if (ap_password) { + payload.ap_password = ap_password; + } + } + + try { + await api.postNetworkSettings(payload); // Reuses the same API endpoint + alert(`Successfully switched mode to ${mode}. The device will now reconfigure.`); + initializeSettings(); + } catch (error) { + console.error('Error switching Wi-Fi mode:', error); + alert(`Failed to switch mode: ${error.message}`); + } finally { + dom.apModeApplyButton.disabled = false; + dom.apModeApplyButton.innerHTML = 'Apply'; + } +} + +/** + * Applies the selected UART baud rate by sending it to the server. + */ +export async function applyBaudRateSettings() { + const baudrate = dom.baudRateSelect.value; + dom.baudRateApplyButton.disabled = true; + dom.baudRateApplyButton.innerHTML = ` Applying...`; + + try { + await api.postBaudRateSetting(baudrate); + } catch (error) { + console.error('Error applying baud rate:', error); + } finally { + dom.baudRateApplyButton.disabled = false; + dom.baudRateApplyButton.innerHTML = 'Apply'; + } +} + +/** + * Fetches and displays the current network and device settings in the settings modal. + */ +export async function initializeSettings() { + try { + const data = await api.fetchSettings(); + + // Wi-Fi Connection Status + if (data.connected) { + dom.currentWifiSsid.textContent = data.ssid; + dom.currentWifiIp.textContent = `IP Address: ${data.ip ? data.ip.ip : 'N/A'}`; + } else { + dom.currentWifiSsid.textContent = 'Not Connected'; + dom.currentWifiIp.textContent = 'IP Address: -'; + } + + // Network (Static/DHCP) Settings + if (data.ip) { + dom.staticIpInput.value = data.ip.ip || ''; + dom.staticGatewayInput.value = data.ip.gateway || ''; + dom.staticNetmaskInput.value = data.ip.subnet || ''; + dom.dns1Input.value = data.ip.dns1 || ''; + dom.dns2Input.value = data.ip.dns2 || ''; + } + dom.staticIpToggle.checked = data.net_type === 'static'; + dom.staticIpConfig.style.display = dom.staticIpToggle.checked ? 'block' : 'none'; + + // AP Mode Settings + dom.apModeToggle.checked = data.mode === 'apsta'; + dom.apModeConfig.style.display = dom.apModeToggle.checked ? 'block' : 'none'; + dom.apSsidInput.value = ''; // For security, don't pre-fill + dom.apPasswordInput.value = ''; + + // Device Settings + if (data.baudrate) { + dom.baudRateSelect.value = data.baudrate; + } + + } catch (error) { + console.error('Error initializing settings:', error); + // Reset fields on error + dom.currentWifiSsid.textContent = 'Status Unknown'; + dom.currentWifiIp.textContent = 'IP Address: -'; + dom.staticIpToggle.checked = false; + dom.staticIpConfig.style.display = 'none'; + dom.apModeToggle.checked = false; + dom.apModeConfig.style.display = 'none'; + } +} + +/** + * Fetches and updates the status of the power control toggles. + */ +export async function updateControlStatus() { + try { + const status = await api.fetchControlStatus(); + dom.mainPowerToggle.checked = status.load_12v_on; + dom.usbPowerToggle.checked = status.load_5v_on; + } catch (error) { + console.error('Error fetching control status:', error); + } +} + +/** + * Handles window resize events to make components responsive. + */ +export function handleResize() { + if (isMobile()) { + fitTerminal(); + } + resizeCharts(); +} + +/** + * Updates the WebSocket connection status indicator in the header. + * @param {boolean} isConnected - True if the WebSocket is connected, false otherwise. + */ +export function updateWebsocketStatus(isConnected) { + if (isConnected) { + dom.websocketStatusText.textContent = 'Online'; + dom.websocketIcon.className = 'bi bi-check-circle-fill me-2'; + dom.websocketStatus.classList.remove('text-danger'); + dom.websocketStatus.classList.add('text-success'); + } else { + dom.websocketStatusText.textContent = 'Offline'; + dom.websocketIcon.className = 'bi bi-x-circle-fill me-2'; + dom.websocketStatus.classList.remove('text-success'); + dom.websocketStatus.classList.add('text-danger'); + } +} diff --git a/page/src/utils.js b/page/src/utils.js new file mode 100644 index 0000000..1229df6 --- /dev/null +++ b/page/src/utils.js @@ -0,0 +1,44 @@ +/** + * @file utils.js + * @description Provides utility functions used throughout the application. + */ + +/** + * Creates a debounced function that delays invoking `func` until after `delay` milliseconds + * have elapsed since the last time the debounced function was invoked. + * @param {Function} func The function to debounce. + * @param {number} delay The number of milliseconds to delay. + * @returns {Function} Returns the new debounced function. + */ +export function debounce(func, delay) { + let timeout; + return function (...args) { + const context = this; + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(context, args), delay); + }; +} + +/** + * Formats a duration in total seconds into a human-readable string (e.g., "1d 02:30:15"). + * @param {number} totalSeconds The total seconds to format. + * @returns {string} The formatted uptime string. + */ +export function formatUptime(totalSeconds) { + const days = Math.floor(totalSeconds / 86400); + const hours = Math.floor((totalSeconds % 86400) / 3600); + const minutes = Math.floor((totalSeconds % 3600) / 60); + const seconds = totalSeconds % 60; + const pad = (num) => String(num).padStart(2, '0'); + const timeString = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`; + return days > 0 ? `${days}d ${timeString}` : timeString; +} + +/** + * Checks if the current device is likely a mobile device based on screen width. + * @returns {boolean} True if the window width is less than or equal to 767.98px, false otherwise. + */ +export function isMobile() { + // The 767.98px breakpoint is typically used by Bootstrap for medium (md) devices. + return window.innerWidth <= 767.98; +} diff --git a/page/src/websocket.js b/page/src/websocket.js new file mode 100644 index 0000000..b551ff0 --- /dev/null +++ b/page/src/websocket.js @@ -0,0 +1,41 @@ +/** + * @file websocket.js + * @description This module handles the WebSocket connection for real-time, two-way + * communication with the server. It provides functions to initialize the connection + * and send messages. + */ + +// The WebSocket instance, exported for potential direct access if needed. +export let websocket; + +// The WebSocket server address, derived from the current page's hostname. +const gateway = `ws://${window.location.hostname}/ws`; + +/** + * Initializes the WebSocket connection and sets up event handlers. + * @param {Object} callbacks - An object containing callback functions for WebSocket events. + * @param {function} callbacks.onOpen - Called when the connection is successfully opened. + * @param {function} callbacks.onClose - Called when the connection is closed. + * @param {function} callbacks.onMessage - Called when a message is received from the server. + */ +export function initWebSocket({ onOpen, onClose, onMessage }) { + console.log(`Trying to open a WebSocket connection to ${gateway}...`); + websocket = new WebSocket(gateway); + // Set binary type to arraybuffer to handle raw binary data from the UART. + websocket.binaryType = "arraybuffer"; + + // Assign event handlers from the provided callbacks + if (onOpen) websocket.onopen = onOpen; + if (onClose) websocket.onclose = onClose; + if (onMessage) websocket.onmessage = onMessage; +} + +/** + * Sends data over the WebSocket connection if it is open. + * @param {string | ArrayBuffer} data - The data to send to the server. + */ +export function sendWebsocketMessage(data) { + if (websocket && websocket.readyState === WebSocket.OPEN) { + websocket.send(data); + } +} diff --git a/page/vite.config.js b/page/vite.config.js new file mode 100644 index 0000000..31b2241 --- /dev/null +++ b/page/vite.config.js @@ -0,0 +1,10 @@ +import { defineConfig } from 'vite'; +import { viteSingleFile } from 'vite-plugin-singlefile'; +import viteCompression from 'vite-plugin-compression'; + +export default defineConfig({ + plugins: [ + viteSingleFile(), + viteCompression(), + ], +}); diff --git a/partitions.csv b/partitions.csv new file mode 100644 index 0000000..d95c518 --- /dev/null +++ b/partitions.csv @@ -0,0 +1,6 @@ +# ESP-IDF Partition Table +# Name, Type, SubType, Offset, Size, Flags +nvs,data,nvs,0x9000,24K, +phy_init,data,phy,0xf000,4K, +factory,app,factory,0x10000,2M, +littlefs, data, littlefs, ,1536K, \ No newline at end of file diff --git a/sdkconfig b/sdkconfig new file mode 100644 index 0000000..ffb3601 --- /dev/null +++ b/sdkconfig @@ -0,0 +1,2105 @@ +# +# Automatically generated file. DO NOT EDIT. +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Configuration +# +CONFIG_SOC_ADC_SUPPORTED=y +CONFIG_SOC_DEDICATED_GPIO_SUPPORTED=y +CONFIG_SOC_UART_SUPPORTED=y +CONFIG_SOC_GDMA_SUPPORTED=y +CONFIG_SOC_AHB_GDMA_SUPPORTED=y +CONFIG_SOC_GPTIMER_SUPPORTED=y +CONFIG_SOC_TWAI_SUPPORTED=y +CONFIG_SOC_BT_SUPPORTED=y +CONFIG_SOC_ASYNC_MEMCPY_SUPPORTED=y +CONFIG_SOC_USB_SERIAL_JTAG_SUPPORTED=y +CONFIG_SOC_TEMP_SENSOR_SUPPORTED=y +CONFIG_SOC_XT_WDT_SUPPORTED=y +CONFIG_SOC_PHY_SUPPORTED=y +CONFIG_SOC_WIFI_SUPPORTED=y +CONFIG_SOC_SUPPORTS_SECURE_DL_MODE=y +CONFIG_SOC_EFUSE_KEY_PURPOSE_FIELD=y +CONFIG_SOC_EFUSE_HAS_EFUSE_RST_BUG=y +CONFIG_SOC_EFUSE_SUPPORTED=y +CONFIG_SOC_RTC_FAST_MEM_SUPPORTED=y +CONFIG_SOC_RTC_MEM_SUPPORTED=y +CONFIG_SOC_I2S_SUPPORTED=y +CONFIG_SOC_RMT_SUPPORTED=y +CONFIG_SOC_SDM_SUPPORTED=y +CONFIG_SOC_GPSPI_SUPPORTED=y +CONFIG_SOC_LEDC_SUPPORTED=y +CONFIG_SOC_I2C_SUPPORTED=y +CONFIG_SOC_SYSTIMER_SUPPORTED=y +CONFIG_SOC_SUPPORT_COEXISTENCE=y +CONFIG_SOC_AES_SUPPORTED=y +CONFIG_SOC_MPI_SUPPORTED=y +CONFIG_SOC_SHA_SUPPORTED=y +CONFIG_SOC_HMAC_SUPPORTED=y +CONFIG_SOC_DIG_SIGN_SUPPORTED=y +CONFIG_SOC_FLASH_ENC_SUPPORTED=y +CONFIG_SOC_SECURE_BOOT_SUPPORTED=y +CONFIG_SOC_MEMPROT_SUPPORTED=y +CONFIG_SOC_BOD_SUPPORTED=y +CONFIG_SOC_CLK_TREE_SUPPORTED=y +CONFIG_SOC_ASSIST_DEBUG_SUPPORTED=y +CONFIG_SOC_WDT_SUPPORTED=y +CONFIG_SOC_SPI_FLASH_SUPPORTED=y +CONFIG_SOC_RNG_SUPPORTED=y +CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y +CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y +CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y +CONFIG_SOC_PM_SUPPORTED=y +CONFIG_SOC_XTAL_SUPPORT_40M=y +CONFIG_SOC_AES_SUPPORT_DMA=y +CONFIG_SOC_AES_GDMA=y +CONFIG_SOC_AES_SUPPORT_AES_128=y +CONFIG_SOC_AES_SUPPORT_AES_256=y +CONFIG_SOC_ADC_DIG_CTRL_SUPPORTED=y +CONFIG_SOC_ADC_ARBITER_SUPPORTED=y +CONFIG_SOC_ADC_DIG_IIR_FILTER_SUPPORTED=y +CONFIG_SOC_ADC_MONITOR_SUPPORTED=y +CONFIG_SOC_ADC_DMA_SUPPORTED=y +CONFIG_SOC_ADC_PERIPH_NUM=2 +CONFIG_SOC_ADC_MAX_CHANNEL_NUM=5 +CONFIG_SOC_ADC_ATTEN_NUM=4 +CONFIG_SOC_ADC_DIGI_CONTROLLER_NUM=1 +CONFIG_SOC_ADC_PATT_LEN_MAX=8 +CONFIG_SOC_ADC_DIGI_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_DIGI_RESULT_BYTES=4 +CONFIG_SOC_ADC_DIGI_DATA_BYTES_PER_CONV=4 +CONFIG_SOC_ADC_DIGI_IIR_FILTER_NUM=2 +CONFIG_SOC_ADC_DIGI_MONITOR_NUM=2 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_HIGH=83333 +CONFIG_SOC_ADC_SAMPLE_FREQ_THRES_LOW=611 +CONFIG_SOC_ADC_RTC_MIN_BITWIDTH=12 +CONFIG_SOC_ADC_RTC_MAX_BITWIDTH=12 +CONFIG_SOC_ADC_CALIBRATION_V1_SUPPORTED=y +CONFIG_SOC_ADC_SELF_HW_CALI_SUPPORTED=y +CONFIG_SOC_ADC_SHARED_POWER=y +CONFIG_SOC_APB_BACKUP_DMA=y +CONFIG_SOC_BROWNOUT_RESET_SUPPORTED=y +CONFIG_SOC_SHARED_IDCACHE_SUPPORTED=y +CONFIG_SOC_CACHE_MEMORY_IBANK_SIZE=0x4000 +CONFIG_SOC_CPU_CORES_NUM=1 +CONFIG_SOC_CPU_INTR_NUM=32 +CONFIG_SOC_CPU_HAS_FLEXIBLE_INTC=y +CONFIG_SOC_CPU_HAS_CSR_PC=y +CONFIG_SOC_CPU_BREAKPOINTS_NUM=8 +CONFIG_SOC_CPU_WATCHPOINTS_NUM=8 +CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=0x80000000 +CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=3072 +CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16 +CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100 +CONFIG_SOC_AHB_GDMA_VERSION=1 +CONFIG_SOC_GDMA_NUM_GROUPS_MAX=1 +CONFIG_SOC_GDMA_PAIRS_PER_GROUP_MAX=3 +CONFIG_SOC_GPIO_PORT=1 +CONFIG_SOC_GPIO_PIN_COUNT=22 +CONFIG_SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER=y +CONFIG_SOC_GPIO_FILTER_CLK_SUPPORT_APB=y +CONFIG_SOC_GPIO_SUPPORT_FORCE_HOLD=y +CONFIG_SOC_GPIO_SUPPORT_DEEPSLEEP_WAKEUP=y +CONFIG_SOC_GPIO_IN_RANGE_MAX=21 +CONFIG_SOC_GPIO_OUT_RANGE_MAX=21 +CONFIG_SOC_GPIO_DEEP_SLEEP_WAKE_VALID_GPIO_MASK=0 +CONFIG_SOC_GPIO_DEEP_SLEEP_WAKE_SUPPORTED_PIN_CNT=6 +CONFIG_SOC_GPIO_VALID_DIGITAL_IO_PAD_MASK=0x00000000003FFFC0 +CONFIG_SOC_GPIO_CLOCKOUT_BY_GPIO_MATRIX=y +CONFIG_SOC_GPIO_CLOCKOUT_CHANNEL_NUM=3 +CONFIG_SOC_GPIO_SUPPORT_HOLD_IO_IN_DSLP=y +CONFIG_SOC_DEDIC_GPIO_OUT_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_GPIO_IN_CHANNELS_NUM=8 +CONFIG_SOC_DEDIC_PERIPH_ALWAYS_ENABLE=y +CONFIG_SOC_I2C_NUM=1 +CONFIG_SOC_HP_I2C_NUM=1 +CONFIG_SOC_I2C_FIFO_LEN=32 +CONFIG_SOC_I2C_CMD_REG_NUM=8 +CONFIG_SOC_I2C_SUPPORT_SLAVE=y +CONFIG_SOC_I2C_SUPPORT_HW_CLR_BUS=y +CONFIG_SOC_I2C_SUPPORT_XTAL=y +CONFIG_SOC_I2C_SUPPORT_RTC=y +CONFIG_SOC_I2C_SUPPORT_10BIT_ADDR=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_BROADCAST=y +CONFIG_SOC_I2C_SLAVE_CAN_GET_STRETCH_CAUSE=y +CONFIG_SOC_I2C_SLAVE_SUPPORT_I2CRAM_ACCESS=y +CONFIG_SOC_I2S_NUM=1 +CONFIG_SOC_I2S_HW_VERSION_2=y +CONFIG_SOC_I2S_SUPPORTS_XTAL=y +CONFIG_SOC_I2S_SUPPORTS_PLL_F160M=y +CONFIG_SOC_I2S_SUPPORTS_PCM=y +CONFIG_SOC_I2S_SUPPORTS_PDM=y +CONFIG_SOC_I2S_SUPPORTS_PDM_TX=y +CONFIG_SOC_I2S_PDM_MAX_TX_LINES=2 +CONFIG_SOC_I2S_SUPPORTS_TDM=y +CONFIG_SOC_LEDC_SUPPORT_APB_CLOCK=y +CONFIG_SOC_LEDC_SUPPORT_XTAL_CLOCK=y +CONFIG_SOC_LEDC_TIMER_NUM=4 +CONFIG_SOC_LEDC_CHANNEL_NUM=6 +CONFIG_SOC_LEDC_TIMER_BIT_WIDTH=14 +CONFIG_SOC_LEDC_SUPPORT_FADE_STOP=y +CONFIG_SOC_MMU_LINEAR_ADDRESS_REGION_NUM=1 +CONFIG_SOC_MMU_PERIPH_NUM=1 +CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000 +CONFIG_SOC_MPU_REGIONS_MAX_NUM=8 +CONFIG_SOC_RMT_GROUPS=1 +CONFIG_SOC_RMT_TX_CANDIDATES_PER_GROUP=2 +CONFIG_SOC_RMT_RX_CANDIDATES_PER_GROUP=2 +CONFIG_SOC_RMT_CHANNELS_PER_GROUP=4 +CONFIG_SOC_RMT_MEM_WORDS_PER_CHANNEL=48 +CONFIG_SOC_RMT_SUPPORT_RX_PINGPONG=y +CONFIG_SOC_RMT_SUPPORT_RX_DEMODULATION=y +CONFIG_SOC_RMT_SUPPORT_TX_ASYNC_STOP=y +CONFIG_SOC_RMT_SUPPORT_TX_LOOP_COUNT=y +CONFIG_SOC_RMT_SUPPORT_TX_SYNCHRO=y +CONFIG_SOC_RMT_SUPPORT_TX_CARRIER_DATA_ONLY=y +CONFIG_SOC_RMT_SUPPORT_XTAL=y +CONFIG_SOC_RMT_SUPPORT_APB=y +CONFIG_SOC_RMT_SUPPORT_RC_FAST=y +CONFIG_SOC_RTC_CNTL_CPU_PD_DMA_BUS_WIDTH=128 +CONFIG_SOC_RTC_CNTL_CPU_PD_REG_FILE_NUM=108 +CONFIG_SOC_SLEEP_SYSTIMER_STALL_WORKAROUND=y +CONFIG_SOC_SLEEP_TGWDT_STOP_WORKAROUND=y +CONFIG_SOC_RTCIO_PIN_COUNT=0 +CONFIG_SOC_MPI_MEM_BLOCKS_NUM=4 +CONFIG_SOC_MPI_OPERATIONS_NUM=3 +CONFIG_SOC_RSA_MAX_BIT_LEN=3072 +CONFIG_SOC_SHA_DMA_MAX_BUFFER_SIZE=3968 +CONFIG_SOC_SHA_SUPPORT_DMA=y +CONFIG_SOC_SHA_SUPPORT_RESUME=y +CONFIG_SOC_SHA_GDMA=y +CONFIG_SOC_SHA_SUPPORT_SHA1=y +CONFIG_SOC_SHA_SUPPORT_SHA224=y +CONFIG_SOC_SHA_SUPPORT_SHA256=y +CONFIG_SOC_SDM_GROUPS=1 +CONFIG_SOC_SDM_CHANNELS_PER_GROUP=4 +CONFIG_SOC_SDM_CLK_SUPPORT_APB=y +CONFIG_SOC_SPI_PERIPH_NUM=2 +CONFIG_SOC_SPI_MAX_CS_NUM=6 +CONFIG_SOC_SPI_MAXIMUM_BUFFER_SIZE=64 +CONFIG_SOC_SPI_SUPPORT_DDRCLK=y +CONFIG_SOC_SPI_SLAVE_SUPPORT_SEG_TRANS=y +CONFIG_SOC_SPI_SUPPORT_CD_SIG=y +CONFIG_SOC_SPI_SUPPORT_CONTINUOUS_TRANS=y +CONFIG_SOC_SPI_SUPPORT_SLAVE_HD_VER2=y +CONFIG_SOC_SPI_SUPPORT_CLK_APB=y +CONFIG_SOC_SPI_SUPPORT_CLK_XTAL=y +CONFIG_SOC_SPI_PERIPH_SUPPORT_CONTROL_DUMMY_OUT=y +CONFIG_SOC_SPI_SCT_SUPPORTED=y +CONFIG_SOC_SPI_SCT_REG_NUM=14 +CONFIG_SOC_SPI_SCT_BUFFER_NUM_MAX=y +CONFIG_SOC_SPI_SCT_CONF_BITLEN_MAX=0x3FFFA +CONFIG_SOC_MEMSPI_IS_INDEPENDENT=y +CONFIG_SOC_SPI_MAX_PRE_DIVIDER=16 +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_WAIT_IDLE=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_AUTO_RESUME=y +CONFIG_SOC_SPI_MEM_SUPPORT_IDLE_INTR=y +CONFIG_SOC_SPI_MEM_SUPPORT_SW_SUSPEND=y +CONFIG_SOC_SPI_MEM_SUPPORT_CHECK_SUS=y +CONFIG_SOC_SPI_MEM_SUPPORT_CONFIG_GPIO_BY_EFUSE=y +CONFIG_SOC_SPI_MEM_SUPPORT_WRAP=y +CONFIG_SOC_MEMSPI_SRC_FREQ_80M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_40M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_26M_SUPPORTED=y +CONFIG_SOC_MEMSPI_SRC_FREQ_20M_SUPPORTED=y +CONFIG_SOC_SYSTIMER_COUNTER_NUM=2 +CONFIG_SOC_SYSTIMER_ALARM_NUM=3 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_LO=32 +CONFIG_SOC_SYSTIMER_BIT_WIDTH_HI=20 +CONFIG_SOC_SYSTIMER_FIXED_DIVIDER=y +CONFIG_SOC_SYSTIMER_INT_LEVEL=y +CONFIG_SOC_SYSTIMER_ALARM_MISS_COMPENSATE=y +CONFIG_SOC_TIMER_GROUPS=2 +CONFIG_SOC_TIMER_GROUP_TIMERS_PER_GROUP=1 +CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54 +CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y +CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y +CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=2 +CONFIG_SOC_MWDT_SUPPORT_XTAL=y +CONFIG_SOC_TWAI_CONTROLLER_NUM=1 +CONFIG_SOC_TWAI_CLK_SUPPORT_APB=y +CONFIG_SOC_TWAI_BRP_MIN=2 +CONFIG_SOC_TWAI_BRP_MAX=16384 +CONFIG_SOC_TWAI_SUPPORTS_RX_STATUS=y +CONFIG_SOC_EFUSE_DIS_DOWNLOAD_ICACHE=y +CONFIG_SOC_EFUSE_DIS_PAD_JTAG=y +CONFIG_SOC_EFUSE_DIS_USB_JTAG=y +CONFIG_SOC_EFUSE_DIS_DIRECT_BOOT=y +CONFIG_SOC_EFUSE_SOFT_DIS_JTAG=y +CONFIG_SOC_EFUSE_DIS_ICACHE=y +CONFIG_SOC_EFUSE_BLOCK9_KEY_PURPOSE_QUIRK=y +CONFIG_SOC_SECURE_BOOT_V2_RSA=y +CONFIG_SOC_EFUSE_SECURE_BOOT_KEY_DIGESTS=3 +CONFIG_SOC_EFUSE_REVOKE_BOOT_KEY_DIGESTS=y +CONFIG_SOC_SUPPORT_SECURE_BOOT_REVOKE_KEY=y +CONFIG_SOC_FLASH_ENCRYPTED_XTS_AES_BLOCK_MAX=32 +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES=y +CONFIG_SOC_FLASH_ENCRYPTION_XTS_AES_128=y +CONFIG_SOC_MEMPROT_CPU_PREFETCH_PAD_SIZE=16 +CONFIG_SOC_MEMPROT_MEM_ALIGN_SIZE=512 +CONFIG_SOC_UART_NUM=2 +CONFIG_SOC_UART_HP_NUM=2 +CONFIG_SOC_UART_FIFO_LEN=128 +CONFIG_SOC_UART_BITRATE_MAX=5000000 +CONFIG_SOC_UART_SUPPORT_APB_CLK=y +CONFIG_SOC_UART_SUPPORT_RTC_CLK=y +CONFIG_SOC_UART_SUPPORT_XTAL_CLK=y +CONFIG_SOC_UART_SUPPORT_WAKEUP_INT=y +CONFIG_SOC_UART_SUPPORT_FSM_TX_WAIT_SEND=y +CONFIG_SOC_COEX_HW_PTI=y +CONFIG_SOC_PHY_DIG_REGS_MEM_SIZE=21 +CONFIG_SOC_MAC_BB_PD_MEM_SIZE=192 +CONFIG_SOC_WIFI_LIGHT_SLEEP_CLK_WIDTH=12 +CONFIG_SOC_PM_SUPPORT_WIFI_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_BT_WAKEUP=y +CONFIG_SOC_PM_SUPPORT_CPU_PD=y +CONFIG_SOC_PM_SUPPORT_WIFI_PD=y +CONFIG_SOC_PM_SUPPORT_BT_PD=y +CONFIG_SOC_PM_SUPPORT_RC_FAST_PD=y +CONFIG_SOC_PM_SUPPORT_VDDSDIO_PD=y +CONFIG_SOC_PM_SUPPORT_MAC_BB_PD=y +CONFIG_SOC_PM_CPU_RETENTION_BY_RTCCNTL=y +CONFIG_SOC_PM_MODEM_RETENTION_BY_BACKUPDMA=y +CONFIG_SOC_PM_MODEM_PD_BY_SW=y +CONFIG_SOC_CLK_RC_FAST_D256_SUPPORTED=y +CONFIG_SOC_RTC_SLOW_CLK_SUPPORT_RC_FAST_D256=y +CONFIG_SOC_CLK_RC_FAST_SUPPORT_CALIBRATION=y +CONFIG_SOC_CLK_XTAL32K_SUPPORTED=y +CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_FAST_RC=y +CONFIG_SOC_TEMPERATURE_SENSOR_SUPPORT_XTAL=y +CONFIG_SOC_WIFI_HW_TSF=y +CONFIG_SOC_WIFI_FTM_SUPPORT=y +CONFIG_SOC_WIFI_GCMP_SUPPORT=y +CONFIG_SOC_WIFI_WAPI_SUPPORT=y +CONFIG_SOC_WIFI_CSI_SUPPORT=y +CONFIG_SOC_WIFI_MESH_SUPPORT=y +CONFIG_SOC_WIFI_SUPPORT_VARIABLE_BEACON_WINDOW=y +CONFIG_SOC_WIFI_PHY_NEEDS_USB_WORKAROUND=y +CONFIG_SOC_BLE_SUPPORTED=y +CONFIG_SOC_BLE_MESH_SUPPORTED=y +CONFIG_SOC_BLE_50_SUPPORTED=y +CONFIG_SOC_BLE_DEVICE_PRIVACY_SUPPORTED=y +CONFIG_SOC_BLUFI_SUPPORTED=y +CONFIG_SOC_PHY_COMBO_MODULE=y +CONFIG_IDF_CMAKE=y +CONFIG_IDF_TOOLCHAIN="gcc" +CONFIG_IDF_TOOLCHAIN_GCC=y +CONFIG_IDF_TARGET_ARCH_RISCV=y +CONFIG_IDF_TARGET_ARCH="riscv" +CONFIG_IDF_TARGET="esp32c3" +CONFIG_IDF_INIT_VERSION="5.4.0" +CONFIG_IDF_TARGET_ESP32C3=y +CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005 + +# +# Build type +# +CONFIG_APP_BUILD_TYPE_APP_2NDBOOT=y +# CONFIG_APP_BUILD_TYPE_RAM is not set +CONFIG_APP_BUILD_GENERATE_BINARIES=y +CONFIG_APP_BUILD_BOOTLOADER=y +CONFIG_APP_BUILD_USE_FLASH_SECTIONS=y +# CONFIG_APP_REPRODUCIBLE_BUILD is not set +# CONFIG_APP_NO_BLOBS is not set +# end of Build type + +# +# Bootloader config +# + +# +# Bootloader manager +# +CONFIG_BOOTLOADER_COMPILE_TIME_DATE=y +CONFIG_BOOTLOADER_PROJECT_VER=1 +# end of Bootloader manager + +CONFIG_BOOTLOADER_OFFSET_IN_FLASH=0x0 +CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_DEBUG is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_NONE is not set + +# +# Log +# +# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +# CONFIG_BOOTLOADER_LOG_LEVEL_DEBUG is not set +# CONFIG_BOOTLOADER_LOG_LEVEL_VERBOSE is not set +CONFIG_BOOTLOADER_LOG_LEVEL=3 + +# +# Format +# +# CONFIG_BOOTLOADER_LOG_COLORS is not set +CONFIG_BOOTLOADER_LOG_TIMESTAMP_SOURCE_CPU_TICKS=y +# end of Format +# end of Log + +# +# Serial Flash Configurations +# +# CONFIG_BOOTLOADER_FLASH_DC_AWARE is not set +CONFIG_BOOTLOADER_FLASH_XMC_SUPPORT=y +# end of Serial Flash Configurations + +# CONFIG_BOOTLOADER_FACTORY_RESET is not set +# CONFIG_BOOTLOADER_APP_TEST is not set +CONFIG_BOOTLOADER_REGION_PROTECTION_ENABLE=y +CONFIG_BOOTLOADER_WDT_ENABLE=y +# CONFIG_BOOTLOADER_WDT_DISABLE_IN_USER_CODE is not set +CONFIG_BOOTLOADER_WDT_TIME_MS=9000 +# CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ON_POWER_ON is not set +# CONFIG_BOOTLOADER_SKIP_VALIDATE_ALWAYS is not set +CONFIG_BOOTLOADER_RESERVE_RTC_SIZE=0 +# CONFIG_BOOTLOADER_CUSTOM_RESERVE_RTC is not set +# end of Bootloader config + +# +# Security features +# +CONFIG_SECURE_BOOT_V2_RSA_SUPPORTED=y +CONFIG_SECURE_BOOT_V2_PREFERRED=y +# CONFIG_SECURE_SIGNED_APPS_NO_SECURE_BOOT is not set +# CONFIG_SECURE_BOOT is not set +# CONFIG_SECURE_FLASH_ENC_ENABLED is not set +CONFIG_SECURE_ROM_DL_MODE_ENABLED=y +# end of Security features + +# +# Application manager +# +CONFIG_APP_COMPILE_TIME_DATE=y +# CONFIG_APP_EXCLUDE_PROJECT_VER_VAR is not set +# CONFIG_APP_EXCLUDE_PROJECT_NAME_VAR is not set +# CONFIG_APP_PROJECT_VER_FROM_CONFIG is not set +CONFIG_APP_RETRIEVE_LEN_ELF_SHA=9 +# end of Application manager + +CONFIG_ESP_ROM_HAS_CRC_LE=y +CONFIG_ESP_ROM_HAS_CRC_BE=y +CONFIG_ESP_ROM_HAS_MZ_CRC32=y +CONFIG_ESP_ROM_HAS_JPEG_DECODE=y +CONFIG_ESP_ROM_UART_CLK_IS_XTAL=y +CONFIG_ESP_ROM_USB_SERIAL_DEVICE_NUM=3 +CONFIG_ESP_ROM_HAS_RETARGETABLE_LOCKING=y +CONFIG_ESP_ROM_HAS_ERASE_0_REGION_BUG=y +CONFIG_ESP_ROM_HAS_ENCRYPTED_WRITES_USING_LEGACY_DRV=y +CONFIG_ESP_ROM_GET_CLK_FREQ=y +CONFIG_ESP_ROM_NEEDS_SWSETUP_WORKAROUND=y +CONFIG_ESP_ROM_HAS_LAYOUT_TABLE=y +CONFIG_ESP_ROM_HAS_SPI_FLASH=y +CONFIG_ESP_ROM_HAS_ETS_PRINTF_BUG=y +CONFIG_ESP_ROM_HAS_NEWLIB=y +CONFIG_ESP_ROM_HAS_NEWLIB_NANO_FORMAT=y +CONFIG_ESP_ROM_HAS_NEWLIB_32BIT_TIME=y +CONFIG_ESP_ROM_NEEDS_SET_CACHE_MMU_SIZE=y +CONFIG_ESP_ROM_RAM_APP_NEEDS_MMU_INIT=y +CONFIG_ESP_ROM_HAS_SW_FLOAT=y +CONFIG_ESP_ROM_USB_OTG_NUM=-1 +CONFIG_ESP_ROM_HAS_VERSION=y +CONFIG_ESP_ROM_SUPPORT_DEEP_SLEEP_WAKEUP_STUB=y + +# +# Boot ROM Behavior +# +CONFIG_BOOT_ROM_LOG_ALWAYS_ON=y +# CONFIG_BOOT_ROM_LOG_ALWAYS_OFF is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_HIGH is not set +# CONFIG_BOOT_ROM_LOG_ON_GPIO_LOW is not set +# end of Boot ROM Behavior + +# +# Serial flasher config +# +# CONFIG_ESPTOOLPY_NO_STUB is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set +# CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set +CONFIG_ESPTOOLPY_FLASHMODE_DIO=y +# CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set +CONFIG_ESPTOOLPY_FLASH_SAMPLE_MODE_STR=y +CONFIG_ESPTOOLPY_FLASHMODE="dio" +CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +# CONFIG_ESPTOOLPY_FLASHFREQ_40M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set +# CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set +CONFIG_ESPTOOLPY_FLASHFREQ="80m" +# CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set +# CONFIG_ESPTOOLPY_FLASHSIZE_2MB is not set +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=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="4MB" +# CONFIG_ESPTOOLPY_HEADER_FLASHSIZE_UPDATE is not set +CONFIG_ESPTOOLPY_BEFORE_RESET=y +# CONFIG_ESPTOOLPY_BEFORE_NORESET is not set +CONFIG_ESPTOOLPY_BEFORE="default_reset" +CONFIG_ESPTOOLPY_AFTER_RESET=y +# CONFIG_ESPTOOLPY_AFTER_NORESET is not set +CONFIG_ESPTOOLPY_AFTER="hard_reset" +CONFIG_ESPTOOLPY_MONITOR_BAUD=115200 +# end of Serial flasher config + +# +# Partition Table +# +# 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_TWO_OTA_LARGE is not set +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# Compiler options +# +CONFIG_COMPILER_OPTIMIZATION_DEBUG=y +# CONFIG_COMPILER_OPTIMIZATION_SIZE is not set +# CONFIG_COMPILER_OPTIMIZATION_PERF is not set +# CONFIG_COMPILER_OPTIMIZATION_NONE is not set +CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_ENABLE=y +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE is not set +CONFIG_COMPILER_ASSERT_NDEBUG_EVALUATE=y +CONFIG_COMPILER_FLOAT_LIB_FROM_GCCLIB=y +CONFIG_COMPILER_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT is not set +CONFIG_COMPILER_HIDE_PATHS_MACROS=y +# CONFIG_COMPILER_CXX_EXCEPTIONS is not set +# CONFIG_COMPILER_CXX_RTTI is not set +CONFIG_COMPILER_STACK_CHECK_MODE_NONE=y +# CONFIG_COMPILER_STACK_CHECK_MODE_NORM is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_STRONG is not set +# CONFIG_COMPILER_STACK_CHECK_MODE_ALL is not set +# CONFIG_COMPILER_NO_MERGE_CONSTANTS is not set +# CONFIG_COMPILER_WARN_WRITE_STRINGS is not set +# CONFIG_COMPILER_SAVE_RESTORE_LIBCALLS is not set +CONFIG_COMPILER_DISABLE_DEFAULT_ERRORS=y +# CONFIG_COMPILER_DISABLE_GCC12_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC13_WARNINGS is not set +# CONFIG_COMPILER_DISABLE_GCC14_WARNINGS is not set +# CONFIG_COMPILER_DUMP_RTL_FILES is not set +CONFIG_COMPILER_RT_LIB_GCCLIB=y +CONFIG_COMPILER_RT_LIB_NAME="gcc" +CONFIG_COMPILER_ORPHAN_SECTIONS_WARNING=y +# CONFIG_COMPILER_ORPHAN_SECTIONS_PLACE is not set +# CONFIG_COMPILER_STATIC_ANALYZER is not set +# end of Compiler options + +# +# Component config +# + +# +# Application Level Tracing +# +# CONFIG_APPTRACE_DEST_JTAG is not set +CONFIG_APPTRACE_DEST_NONE=y +# CONFIG_APPTRACE_DEST_UART1 is not set +# CONFIG_APPTRACE_DEST_USB_CDC is not set +CONFIG_APPTRACE_DEST_UART_NONE=y +CONFIG_APPTRACE_UART_TASK_PRIO=1 +CONFIG_APPTRACE_LOCK_ENABLE=y +# end of Application Level Tracing + +# +# Bluetooth +# +# CONFIG_BT_ENABLED is not set +CONFIG_BT_ALARM_MAX_NUM=50 +# end of Bluetooth + +# +# Console Library +# +# CONFIG_CONSOLE_SORTED_HELP is not set +# end of Console Library + +# +# Driver Configurations +# + +# +# TWAI Configuration +# +# CONFIG_TWAI_ISR_IN_IRAM is not set +CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y +# end of TWAI Configuration + +# +# Legacy ADC Driver Configuration +# +# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set + +# +# Legacy ADC Calibration Configuration +# +# CONFIG_ADC_CALI_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy ADC Calibration Configuration +# end of Legacy ADC Driver Configuration + +# +# Legacy Timer Group Driver Configurations +# +# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy Timer Group Driver Configurations + +# +# Legacy RMT Driver Configurations +# +# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy RMT Driver Configurations + +# +# Legacy I2S Driver Configurations +# +# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy I2S Driver Configurations + +# +# Legacy SDM Driver Configurations +# +# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy SDM Driver Configurations + +# +# Legacy Temperature Sensor Driver Configurations +# +# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set +# end of Legacy Temperature Sensor Driver Configurations +# end of Driver Configurations + +# +# eFuse Bit Manager +# +# CONFIG_EFUSE_CUSTOM_TABLE is not set +# CONFIG_EFUSE_VIRTUAL is not set +CONFIG_EFUSE_MAX_BLK_LEN=256 +# end of eFuse Bit Manager + +# +# ESP-TLS +# +CONFIG_ESP_TLS_USING_MBEDTLS=y +CONFIG_ESP_TLS_USE_DS_PERIPHERAL=y +# CONFIG_ESP_TLS_CLIENT_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_SESSION_TICKETS is not set +# CONFIG_ESP_TLS_SERVER_CERT_SELECT_HOOK is not set +# CONFIG_ESP_TLS_SERVER_MIN_AUTH_MODE_OPTIONAL is not set +# CONFIG_ESP_TLS_PSK_VERIFICATION is not set +# CONFIG_ESP_TLS_INSECURE is not set +# end of ESP-TLS + +# +# ADC and ADC Calibration +# +# CONFIG_ADC_ONESHOT_CTRL_FUNC_IN_IRAM is not set +# CONFIG_ADC_CONTINUOUS_ISR_IRAM_SAFE is not set +# CONFIG_ADC_CONTINUOUS_FORCE_USE_ADC2_ON_C3_S3 is not set +# CONFIG_ADC_ONESHOT_FORCE_USE_ADC2_ON_C3 is not set +# CONFIG_ADC_ENABLE_DEBUG_LOG is not set +# end of ADC and ADC Calibration + +# +# Wireless Coexistence +# +CONFIG_ESP_COEX_ENABLED=y +# CONFIG_ESP_COEX_EXTERNAL_COEXIST_ENABLE is not set +# CONFIG_ESP_COEX_GPIO_DEBUG is not set +# end of Wireless Coexistence + +# +# Common ESP-related +# +CONFIG_ESP_ERR_TO_NAME_LOOKUP=y +# end of Common ESP-related + +# +# ESP-Driver:GPIO Configurations +# +# CONFIG_GPIO_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:GPIO Configurations + +# +# ESP-Driver:GPTimer Configurations +# +CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y +# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set +# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set +# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:GPTimer Configurations + +# +# ESP-Driver:I2C Configurations +# +# CONFIG_I2C_ISR_IRAM_SAFE is not set +# CONFIG_I2C_ENABLE_DEBUG_LOG is not set +# CONFIG_I2C_ENABLE_SLAVE_DRIVER_VERSION_2 is not set +# end of ESP-Driver:I2C Configurations + +# +# ESP-Driver:I2S Configurations +# +# CONFIG_I2S_ISR_IRAM_SAFE is not set +# CONFIG_I2S_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:I2S Configurations + +# +# ESP-Driver:LEDC Configurations +# +# CONFIG_LEDC_CTRL_FUNC_IN_IRAM is not set +# end of ESP-Driver:LEDC Configurations + +# +# ESP-Driver:RMT Configurations +# +# CONFIG_RMT_ISR_IRAM_SAFE is not set +# CONFIG_RMT_RECV_FUNC_IN_IRAM is not set +# CONFIG_RMT_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:RMT Configurations + +# +# ESP-Driver:Sigma Delta Modulator Configurations +# +# CONFIG_SDM_CTRL_FUNC_IN_IRAM is not set +# CONFIG_SDM_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Sigma Delta Modulator Configurations + +# +# ESP-Driver:SPI Configurations +# +# CONFIG_SPI_MASTER_IN_IRAM is not set +CONFIG_SPI_MASTER_ISR_IN_IRAM=y +# CONFIG_SPI_SLAVE_IN_IRAM is not set +CONFIG_SPI_SLAVE_ISR_IN_IRAM=y +# end of ESP-Driver:SPI Configurations + +# +# ESP-Driver:Temperature Sensor Configurations +# +# CONFIG_TEMP_SENSOR_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:Temperature Sensor Configurations + +# +# ESP-Driver:UART Configurations +# +CONFIG_UART_ISR_IN_IRAM=y +# end of ESP-Driver:UART Configurations + +# +# ESP-Driver:USB Serial/JTAG Configuration +# +CONFIG_USJ_ENABLE_USB_SERIAL_JTAG=y +# end of ESP-Driver:USB Serial/JTAG Configuration + +# +# Ethernet +# +CONFIG_ETH_ENABLED=y +CONFIG_ETH_USE_SPI_ETHERNET=y +# CONFIG_ETH_SPI_ETHERNET_DM9051 is not set +# CONFIG_ETH_SPI_ETHERNET_W5500 is not set +# CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL is not set +# CONFIG_ETH_USE_OPENETH is not set +# CONFIG_ETH_TRANSMIT_MUTEX is not set +# end of Ethernet + +# +# Event Loop Library +# +# CONFIG_ESP_EVENT_LOOP_PROFILING is not set +CONFIG_ESP_EVENT_POST_FROM_ISR=y +CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y +# end of Event Loop Library + +# +# GDB Stub +# +CONFIG_ESP_GDBSTUB_ENABLED=y +# CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME is not set +CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y +CONFIG_ESP_GDBSTUB_MAX_TASKS=32 +# end of GDB Stub + +# +# ESP HTTP client +# +CONFIG_ESP_HTTP_CLIENT_ENABLE_HTTPS=y +# CONFIG_ESP_HTTP_CLIENT_ENABLE_BASIC_AUTH is not set +# CONFIG_ESP_HTTP_CLIENT_ENABLE_DIGEST_AUTH is not set +# CONFIG_ESP_HTTP_CLIENT_ENABLE_CUSTOM_TRANSPORT is not set +CONFIG_ESP_HTTP_CLIENT_EVENT_POST_TIMEOUT=2000 +# end of ESP HTTP client + +# +# HTTP Server +# +CONFIG_HTTPD_MAX_REQ_HDR_LEN=512 +CONFIG_HTTPD_MAX_URI_LEN=512 +CONFIG_HTTPD_ERR_RESP_NO_DELAY=y +CONFIG_HTTPD_PURGE_BUF_LEN=32 +# CONFIG_HTTPD_LOG_PURGE_DATA is not set +CONFIG_HTTPD_WS_SUPPORT=y +# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set +CONFIG_HTTPD_SERVER_EVENT_POST_TIMEOUT=2000 +# end of HTTP Server + +# +# 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_EVENT_POST_TIMEOUT=2000 +# end of ESP HTTPS OTA + +# +# ESP HTTPS server +# +# CONFIG_ESP_HTTPS_SERVER_ENABLE is not set +CONFIG_ESP_HTTPS_SERVER_EVENT_POST_TIMEOUT=2000 +# end of ESP HTTPS server + +# +# Hardware Settings +# + +# +# Chip revision +# +# CONFIG_ESP32C3_REV_MIN_0 is not set +# CONFIG_ESP32C3_REV_MIN_1 is not set +# CONFIG_ESP32C3_REV_MIN_2 is not set +CONFIG_ESP32C3_REV_MIN_3=y +# CONFIG_ESP32C3_REV_MIN_4 is not set +# CONFIG_ESP32C3_REV_MIN_101 is not set +CONFIG_ESP32C3_REV_MIN_FULL=3 +CONFIG_ESP_REV_MIN_FULL=3 + +# +# Maximum Supported ESP32-C3 Revision (Rev v1.99) +# +CONFIG_ESP32C3_REV_MAX_FULL=199 +CONFIG_ESP_REV_MAX_FULL=199 +CONFIG_ESP_EFUSE_BLOCK_REV_MIN_FULL=0 +CONFIG_ESP_EFUSE_BLOCK_REV_MAX_FULL=199 + +# +# Maximum Supported ESP32-C3 eFuse Block Revision (eFuse Block Rev v1.99) +# +# end of Chip revision + +# +# MAC Config +# +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_STA=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_WIFI_AP=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_BT=y +CONFIG_ESP_MAC_ADDR_UNIVERSE_ETH=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP_MAC_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP32C3_UNIVERSAL_MAC_ADDRESSES_TWO is not set +CONFIG_ESP32C3_UNIVERSAL_MAC_ADDRESSES_FOUR=y +CONFIG_ESP32C3_UNIVERSAL_MAC_ADDRESSES=4 +# CONFIG_ESP_MAC_USE_CUSTOM_MAC_AS_BASE_MAC is not set +# end of MAC Config + +# +# Sleep Config +# +# CONFIG_ESP_SLEEP_POWER_DOWN_FLASH is not set +CONFIG_ESP_SLEEP_FLASH_LEAKAGE_WORKAROUND=y +# CONFIG_ESP_SLEEP_MSPI_NEED_ALL_IO_PU is not set +CONFIG_ESP_SLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_ESP_SLEEP_WAIT_FLASH_READY_EXTRA_DELAY=0 +# CONFIG_ESP_SLEEP_CACHE_SAFE_ASSERTION is not set +# CONFIG_ESP_SLEEP_DEBUG is not set +CONFIG_ESP_SLEEP_GPIO_ENABLE_INTERNAL_RESISTORS=y +# end of Sleep Config + +# +# RTC Clock Config +# +CONFIG_RTC_CLK_SRC_INT_RC=y +# CONFIG_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_RTC_CLK_CAL_CYCLES=1024 +# end of RTC Clock Config + +# +# Peripheral Control +# +CONFIG_PERIPH_CTRL_FUNC_IN_IRAM=y +# end of Peripheral Control + +# +# GDMA Configurations +# +CONFIG_GDMA_CTRL_FUNC_IN_IRAM=y +# CONFIG_GDMA_ISR_IRAM_SAFE is not set +# CONFIG_GDMA_ENABLE_DEBUG_LOG is not set +# end of GDMA Configurations + +# +# Main XTAL Config +# +CONFIG_XTAL_FREQ_40=y +CONFIG_XTAL_FREQ=40 +# end of Main XTAL Config + +CONFIG_ESP_SPI_BUS_LOCK_ISR_FUNCS_IN_IRAM=y +# end of Hardware Settings + +# +# ESP-Driver:LCD Controller Configurations +# +# CONFIG_LCD_ENABLE_DEBUG_LOG is not set +# end of ESP-Driver:LCD Controller Configurations + +# +# ESP-MM: Memory Management Configurations +# +# end of ESP-MM: Memory Management Configurations + +# +# ESP NETIF Adapter +# +CONFIG_ESP_NETIF_IP_LOST_TIMER_INTERVAL=120 +# CONFIG_ESP_NETIF_PROVIDE_CUSTOM_IMPLEMENTATION is not set +CONFIG_ESP_NETIF_TCPIP_LWIP=y +# CONFIG_ESP_NETIF_LOOPBACK is not set +CONFIG_ESP_NETIF_USES_TCPIP_WITH_BSD_API=y +CONFIG_ESP_NETIF_REPORT_DATA_TRAFFIC=y +# CONFIG_ESP_NETIF_RECEIVE_REPORT_ERRORS is not set +# CONFIG_ESP_NETIF_L2_TAP is not set +# CONFIG_ESP_NETIF_BRIDGE_EN is not set +# CONFIG_ESP_NETIF_SET_DNS_PER_DEFAULT_NETIF is not set +# end of ESP NETIF Adapter + +# +# Partition API Configuration +# +# end of Partition API Configuration + +# +# PHY +# +CONFIG_ESP_PHY_ENABLED=y +CONFIG_ESP_PHY_CALIBRATION_AND_DATA_STORAGE=y +# CONFIG_ESP_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP_PHY_MAX_TX_POWER=20 +# CONFIG_ESP_PHY_REDUCE_TX_POWER is not set +CONFIG_ESP_PHY_ENABLE_USB=y +# CONFIG_ESP_PHY_ENABLE_CERT_TEST is not set +CONFIG_ESP_PHY_RF_CAL_PARTIAL=y +# CONFIG_ESP_PHY_RF_CAL_NONE is not set +# CONFIG_ESP_PHY_RF_CAL_FULL is not set +CONFIG_ESP_PHY_CALIBRATION_MODE=0 +# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set +# CONFIG_ESP_PHY_RECORD_USED_TIME is not set +# end of PHY + +# +# Power Management +# +# CONFIG_PM_ENABLE is not set +# CONFIG_PM_SLP_IRAM_OPT is not set +CONFIG_PM_POWER_DOWN_CPU_IN_LIGHT_SLEEP=y +# end of Power Management + +# +# ESP PSRAM +# + +# +# ESP Ringbuf +# +# CONFIG_RINGBUF_PLACE_FUNCTIONS_INTO_FLASH is not set +# end of ESP Ringbuf + +# +# ESP Security Specific +# +# end of ESP Security Specific + +# +# ESP System Settings +# +# CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_80 is not set +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_160=y +CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ=160 +# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set +CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y +# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set +# CONFIG_ESP_SYSTEM_PANIC_GDBSTUB is not set +CONFIG_ESP_SYSTEM_PANIC_REBOOT_DELAY_SECONDS=0 +CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE=y +CONFIG_ESP_SYSTEM_RTC_FAST_MEM_AS_HEAP_DEPCHECK=y +CONFIG_ESP_SYSTEM_ALLOW_RTC_FAST_MEM_AS_HEAP=y +# CONFIG_ESP_SYSTEM_USE_EH_FRAME is not set + +# +# Memory protection +# +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=y +CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y +# end of Memory protection + +CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584 +CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y +# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set +CONFIG_ESP_MAIN_TASK_AFFINITY=0x0 +CONFIG_ESP_MINIMAL_SHARED_STACK_SIZE=2048 +CONFIG_ESP_CONSOLE_UART_DEFAULT=y +# CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG is not set +# CONFIG_ESP_CONSOLE_UART_CUSTOM is not set +# CONFIG_ESP_CONSOLE_NONE is not set +# CONFIG_ESP_CONSOLE_SECONDARY_NONE is not set +CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG=y +CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y +CONFIG_ESP_CONSOLE_UART=y +CONFIG_ESP_CONSOLE_UART_NUM=0 +CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=0 +CONFIG_ESP_CONSOLE_UART_BAUDRATE=115200 +CONFIG_ESP_INT_WDT=y +CONFIG_ESP_INT_WDT_TIMEOUT_MS=300 +CONFIG_ESP_TASK_WDT_EN=y +CONFIG_ESP_TASK_WDT_INIT=y +# CONFIG_ESP_TASK_WDT_PANIC is not set +CONFIG_ESP_TASK_WDT_TIMEOUT_S=5 +CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +# CONFIG_ESP_PANIC_HANDLER_IRAM is not set +# CONFIG_ESP_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP_DEBUG_OCDAWARE=y +CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4=y + +# +# Brownout Detector +# +CONFIG_ESP_BROWNOUT_DET=y +CONFIG_ESP_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP_BROWNOUT_DET_LVL_SEL_2 is not set +CONFIG_ESP_BROWNOUT_DET_LVL=7 +# end of Brownout Detector + +CONFIG_ESP_SYSTEM_BROWNOUT_INTR=y +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=y +CONFIG_ESP_SYSTEM_HW_PC_RECORD=y +# end of ESP System Settings + +# +# IPC (Inter-Processor Call) +# +CONFIG_ESP_IPC_TASK_STACK_SIZE=1024 +# end of IPC (Inter-Processor Call) + +# +# ESP Timer (High Resolution Timer) +# +# CONFIG_ESP_TIMER_PROFILING is not set +CONFIG_ESP_TIME_FUNCS_USE_RTC_TIMER=y +CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER=y +CONFIG_ESP_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP_TIMER_INTERRUPT_LEVEL=1 +# CONFIG_ESP_TIMER_SHOW_EXPERIMENTAL is not set +CONFIG_ESP_TIMER_TASK_AFFINITY=0x0 +CONFIG_ESP_TIMER_TASK_AFFINITY_CPU0=y +CONFIG_ESP_TIMER_ISR_AFFINITY_CPU0=y +# CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD is not set +CONFIG_ESP_TIMER_IMPL_SYSTIMER=y +# end of ESP Timer (High Resolution Timer) + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_ENABLED=y +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +# CONFIG_ESP_WIFI_STATIC_TX_BUFFER is not set +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y +# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set +CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0 +CONFIG_ESP_WIFI_RX_MGMT_BUF_NUM_DEF=5 +# CONFIG_ESP_WIFI_CSI_ENABLED is not set +CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP_WIFI_TX_BA_WIN=6 +CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP_WIFI_RX_BA_WIN=6 +CONFIG_ESP_WIFI_NVS_ENABLED=y +CONFIG_ESP_WIFI_SOFTAP_BEACON_MAX_LEN=752 +CONFIG_ESP_WIFI_MGMT_SBUF_NUM=32 +CONFIG_ESP_WIFI_IRAM_OPT=y +# CONFIG_ESP_WIFI_EXTRA_IRAM_OPT is not set +CONFIG_ESP_WIFI_RX_IRAM_OPT=y +CONFIG_ESP_WIFI_ENABLE_WPA3_SAE=y +CONFIG_ESP_WIFI_ENABLE_SAE_PK=y +CONFIG_ESP_WIFI_SOFTAP_SAE_SUPPORT=y +CONFIG_ESP_WIFI_ENABLE_WPA3_OWE_STA=y +# CONFIG_ESP_WIFI_SLP_IRAM_OPT is not set +CONFIG_ESP_WIFI_SLP_DEFAULT_MIN_ACTIVE_TIME=50 +CONFIG_ESP_WIFI_SLP_DEFAULT_MAX_ACTIVE_TIME=10 +CONFIG_ESP_WIFI_SLP_DEFAULT_WAIT_BROADCAST_DATA_TIME=15 +# CONFIG_ESP_WIFI_FTM_ENABLE is not set +CONFIG_ESP_WIFI_STA_DISCONNECTED_PM_ENABLE=y +# CONFIG_ESP_WIFI_GCMP_SUPPORT is not set +CONFIG_ESP_WIFI_GMAC_SUPPORT=y +CONFIG_ESP_WIFI_SOFTAP_SUPPORT=y +# CONFIG_ESP_WIFI_SLP_BEACON_LOST_OPT is not set +CONFIG_ESP_WIFI_ESPNOW_MAX_ENCRYPT_NUM=7 +CONFIG_ESP_WIFI_MBEDTLS_CRYPTO=y +CONFIG_ESP_WIFI_MBEDTLS_TLS_CLIENT=y +# CONFIG_ESP_WIFI_WAPI_PSK is not set +# CONFIG_ESP_WIFI_SUITE_B_192 is not set +# CONFIG_ESP_WIFI_11KV_SUPPORT is not set +# CONFIG_ESP_WIFI_MBO_SUPPORT is not set +# CONFIG_ESP_WIFI_DPP_SUPPORT is not set +# CONFIG_ESP_WIFI_11R_SUPPORT is not set +# CONFIG_ESP_WIFI_WPS_SOFTAP_REGISTRAR is not set + +# +# WPS Configuration Options +# +# CONFIG_ESP_WIFI_WPS_STRICT is not set +# CONFIG_ESP_WIFI_WPS_PASSPHRASE is not set +# end of WPS Configuration Options + +# CONFIG_ESP_WIFI_DEBUG_PRINT is not set +# CONFIG_ESP_WIFI_TESTING_OPTIONS is not set +CONFIG_ESP_WIFI_ENTERPRISE_SUPPORT=y +# CONFIG_ESP_WIFI_ENT_FREE_DYNAMIC_BUFFER is not set +# end of Wi-Fi + +# +# Core dump +# +# CONFIG_ESP_COREDUMP_ENABLE_TO_FLASH is not set +# CONFIG_ESP_COREDUMP_ENABLE_TO_UART is not set +CONFIG_ESP_COREDUMP_ENABLE_TO_NONE=y +# end of Core dump + +# +# FAT Filesystem support +# +CONFIG_FATFS_VOLUME_COUNT=2 +CONFIG_FATFS_LFN_NONE=y +# CONFIG_FATFS_LFN_HEAP is not set +# CONFIG_FATFS_LFN_STACK is not set +# CONFIG_FATFS_SECTOR_512 is not set +CONFIG_FATFS_SECTOR_4096=y +# CONFIG_FATFS_CODEPAGE_DYNAMIC is not set +CONFIG_FATFS_CODEPAGE_437=y +# CONFIG_FATFS_CODEPAGE_720 is not set +# CONFIG_FATFS_CODEPAGE_737 is not set +# CONFIG_FATFS_CODEPAGE_771 is not set +# CONFIG_FATFS_CODEPAGE_775 is not set +# CONFIG_FATFS_CODEPAGE_850 is not set +# CONFIG_FATFS_CODEPAGE_852 is not set +# CONFIG_FATFS_CODEPAGE_855 is not set +# CONFIG_FATFS_CODEPAGE_857 is not set +# CONFIG_FATFS_CODEPAGE_860 is not set +# CONFIG_FATFS_CODEPAGE_861 is not set +# CONFIG_FATFS_CODEPAGE_862 is not set +# CONFIG_FATFS_CODEPAGE_863 is not set +# CONFIG_FATFS_CODEPAGE_864 is not set +# CONFIG_FATFS_CODEPAGE_865 is not set +# CONFIG_FATFS_CODEPAGE_866 is not set +# CONFIG_FATFS_CODEPAGE_869 is not set +# CONFIG_FATFS_CODEPAGE_932 is not set +# CONFIG_FATFS_CODEPAGE_936 is not set +# CONFIG_FATFS_CODEPAGE_949 is not set +# CONFIG_FATFS_CODEPAGE_950 is not set +CONFIG_FATFS_CODEPAGE=437 +CONFIG_FATFS_FS_LOCK=0 +CONFIG_FATFS_TIMEOUT_MS=10000 +CONFIG_FATFS_PER_FILE_CACHE=y +# CONFIG_FATFS_USE_FASTSEEK is not set +CONFIG_FATFS_USE_STRFUNC_NONE=y +# CONFIG_FATFS_USE_STRFUNC_WITHOUT_CRLF_CONV is not set +# CONFIG_FATFS_USE_STRFUNC_WITH_CRLF_CONV is not set +CONFIG_FATFS_VFS_FSTAT_BLKSIZE=0 +# CONFIG_FATFS_IMMEDIATE_FSYNC is not set +# CONFIG_FATFS_USE_LABEL is not set +CONFIG_FATFS_LINK_LOCK=y +# end of FAT Filesystem support + +# +# FreeRTOS +# + +# +# Kernel +# +# CONFIG_FREERTOS_SMP is not set +CONFIG_FREERTOS_UNICORE=y +CONFIG_FREERTOS_HZ=100 +CONFIG_FREERTOS_OPTIMIZED_SCHEDULER=y +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set +# CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL is not set +CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY=y +CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=1 +CONFIG_FREERTOS_IDLE_TASK_STACKSIZE=1536 +# CONFIG_FREERTOS_USE_IDLE_HOOK is not set +# CONFIG_FREERTOS_USE_TICK_HOOK is not set +CONFIG_FREERTOS_MAX_TASK_NAME_LEN=16 +# CONFIG_FREERTOS_ENABLE_BACKWARD_COMPATIBILITY is not set +CONFIG_FREERTOS_USE_TIMERS=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_NAME="Tmr Svc" +# CONFIG_FREERTOS_TIMER_TASK_AFFINITY_CPU0 is not set +CONFIG_FREERTOS_TIMER_TASK_NO_AFFINITY=y +CONFIG_FREERTOS_TIMER_SERVICE_TASK_CORE_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_TIMER_TASK_PRIORITY=1 +CONFIG_FREERTOS_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_FREERTOS_TIMER_QUEUE_LENGTH=10 +CONFIG_FREERTOS_QUEUE_REGISTRY_SIZE=0 +CONFIG_FREERTOS_TASK_NOTIFICATION_ARRAY_ENTRIES=1 +# CONFIG_FREERTOS_USE_TRACE_FACILITY is not set +# CONFIG_FREERTOS_USE_LIST_DATA_INTEGRITY_CHECK_BYTES is not set +# CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS is not set +# CONFIG_FREERTOS_USE_APPLICATION_TASK_TAG is not set +# end of Kernel + +# +# Port +# +CONFIG_FREERTOS_TASK_FUNCTION_WRAPPER=y +# CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK is not set +CONFIG_FREERTOS_TLSP_DELETION_CALLBACKS=y +# CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK is not set +# CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP is not set +CONFIG_FREERTOS_CHECK_MUTEX_GIVEN_BY_OWNER=y +CONFIG_FREERTOS_ISR_STACKSIZE=1536 +CONFIG_FREERTOS_INTERRUPT_BACKTRACE=y +CONFIG_FREERTOS_TICK_SUPPORT_SYSTIMER=y +CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL1=y +# CONFIG_FREERTOS_CORETIMER_SYSTIMER_LVL3 is not set +CONFIG_FREERTOS_SYSTICK_USES_SYSTIMER=y +# CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH is not set +# CONFIG_FREERTOS_CHECK_PORT_CRITICAL_COMPLIANCE is not set +# end of Port + +# +# Extra +# +# end of Extra + +CONFIG_FREERTOS_PORT=y +CONFIG_FREERTOS_NO_AFFINITY=0x7FFFFFFF +CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y +CONFIG_FREERTOS_DEBUG_OCDAWARE=y +CONFIG_FREERTOS_ENABLE_TASK_SNAPSHOT=y +CONFIG_FREERTOS_PLACE_SNAPSHOT_FUNS_INTO_FLASH=y +CONFIG_FREERTOS_NUMBER_OF_CORES=1 +# end of FreeRTOS + +# +# Hardware Abstraction Layer (HAL) and Low Level (LL) +# +CONFIG_HAL_ASSERTION_EQUALS_SYSTEM=y +# CONFIG_HAL_ASSERTION_DISABLE is not set +# CONFIG_HAL_ASSERTION_SILENT is not set +# CONFIG_HAL_ASSERTION_ENABLE is not set +CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2 +CONFIG_HAL_SPI_MASTER_FUNC_IN_IRAM=y +CONFIG_HAL_SPI_SLAVE_FUNC_IN_IRAM=y +# CONFIG_HAL_ECDSA_GEN_SIG_CM is not set +# end of Hardware Abstraction Layer (HAL) and Low Level (LL) + +# +# Heap memory debugging +# +CONFIG_HEAP_POISONING_DISABLED=y +# CONFIG_HEAP_POISONING_LIGHT is not set +# CONFIG_HEAP_POISONING_COMPREHENSIVE is not set +CONFIG_HEAP_TRACING_OFF=y +# CONFIG_HEAP_TRACING_STANDALONE is not set +# CONFIG_HEAP_TRACING_TOHOST is not set +# CONFIG_HEAP_USE_HOOKS is not set +# CONFIG_HEAP_TASK_TRACKING is not set +# CONFIG_HEAP_ABORT_WHEN_ALLOCATION_FAILS is not set +# CONFIG_HEAP_PLACE_FUNCTION_INTO_FLASH is not set +# end of Heap memory debugging + +# +# Log +# + +# +# Log Level +# +# CONFIG_LOG_DEFAULT_LEVEL_NONE is not set +# CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set +# CONFIG_LOG_DEFAULT_LEVEL_WARN is not set +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +# CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set +# CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set +CONFIG_LOG_DEFAULT_LEVEL=3 +CONFIG_LOG_MAXIMUM_EQUALS_DEFAULT=y +# CONFIG_LOG_MAXIMUM_LEVEL_DEBUG is not set +# CONFIG_LOG_MAXIMUM_LEVEL_VERBOSE is not set +CONFIG_LOG_MAXIMUM_LEVEL=3 + +# +# Level Settings +# +# CONFIG_LOG_MASTER_LEVEL is not set +CONFIG_LOG_DYNAMIC_LEVEL_CONTROL=y +# CONFIG_LOG_TAG_LEVEL_IMPL_NONE is not set +# CONFIG_LOG_TAG_LEVEL_IMPL_LINKED_LIST is not set +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST=y +# CONFIG_LOG_TAG_LEVEL_CACHE_ARRAY is not set +CONFIG_LOG_TAG_LEVEL_CACHE_BINARY_MIN_HEAP=y +CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_SIZE=31 +# end of Level Settings +# end of Log Level + +# +# Format +# +# CONFIG_LOG_COLORS is not set +CONFIG_LOG_TIMESTAMP_SOURCE_RTOS=y +# CONFIG_LOG_TIMESTAMP_SOURCE_SYSTEM is not set +# end of Format +# end of Log + +# +# LWIP +# +CONFIG_LWIP_ENABLE=y +CONFIG_LWIP_LOCAL_HOSTNAME="espressif" +# CONFIG_LWIP_NETIF_API is not set +CONFIG_LWIP_TCPIP_TASK_PRIO=18 +# CONFIG_LWIP_TCPIP_CORE_LOCKING is not set +# CONFIG_LWIP_CHECK_THREAD_SAFETY is not set +CONFIG_LWIP_DNS_SUPPORT_MDNS_QUERIES=y +# CONFIG_LWIP_L2_TO_L3_COPY is not set +# CONFIG_LWIP_IRAM_OPTIMIZATION is not set +# CONFIG_LWIP_EXTRA_IRAM_OPTIMIZATION is not set +CONFIG_LWIP_TIMERS_ONDEMAND=y +CONFIG_LWIP_ND6=y +# CONFIG_LWIP_FORCE_ROUTER_FORWARDING is not set +CONFIG_LWIP_MAX_SOCKETS=10 +# CONFIG_LWIP_USE_ONLY_LWIP_SELECT is not set +# CONFIG_LWIP_SO_LINGER is not set +CONFIG_LWIP_SO_REUSE=y +CONFIG_LWIP_SO_REUSE_RXTOALL=y +# CONFIG_LWIP_SO_RCVBUF is not set +# CONFIG_LWIP_NETBUF_RECVINFO is not set +CONFIG_LWIP_IP_DEFAULT_TTL=64 +CONFIG_LWIP_IP4_FRAG=y +CONFIG_LWIP_IP6_FRAG=y +# CONFIG_LWIP_IP4_REASSEMBLY is not set +# CONFIG_LWIP_IP6_REASSEMBLY is not set +CONFIG_LWIP_IP_REASS_MAX_PBUFS=10 +# CONFIG_LWIP_IP_FORWARD is not set +# CONFIG_LWIP_STATS is not set +CONFIG_LWIP_ESP_GRATUITOUS_ARP=y +CONFIG_LWIP_GARP_TMR_INTERVAL=60 +CONFIG_LWIP_ESP_MLDV6_REPORT=y +CONFIG_LWIP_MLDV6_TMR_INTERVAL=40 +CONFIG_LWIP_TCPIP_RECVMBOX_SIZE=32 +CONFIG_LWIP_DHCP_DOES_ARP_CHECK=y +# CONFIG_LWIP_DHCP_DOES_ACD_CHECK is not set +# CONFIG_LWIP_DHCP_DOES_NOT_CHECK_OFFERED_IP is not set +# CONFIG_LWIP_DHCP_DISABLE_CLIENT_ID is not set +CONFIG_LWIP_DHCP_DISABLE_VENDOR_CLASS_ID=y +# CONFIG_LWIP_DHCP_RESTORE_LAST_IP is not set +CONFIG_LWIP_DHCP_OPTIONS_LEN=68 +CONFIG_LWIP_NUM_NETIF_CLIENT_DATA=0 +CONFIG_LWIP_DHCP_COARSE_TIMER_SECS=1 + +# +# DHCP server +# +CONFIG_LWIP_DHCPS=y +CONFIG_LWIP_DHCPS_LEASE_UNIT=60 +CONFIG_LWIP_DHCPS_MAX_STATION_NUM=8 +CONFIG_LWIP_DHCPS_STATIC_ENTRIES=y +CONFIG_LWIP_DHCPS_ADD_DNS=y +# end of DHCP server + +# CONFIG_LWIP_AUTOIP is not set +CONFIG_LWIP_IPV4=y +CONFIG_LWIP_IPV6=y +# CONFIG_LWIP_IPV6_AUTOCONFIG is not set +CONFIG_LWIP_IPV6_NUM_ADDRESSES=3 +# CONFIG_LWIP_IPV6_FORWARD is not set +# CONFIG_LWIP_NETIF_STATUS_CALLBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=y +CONFIG_LWIP_LOOPBACK_MAX_PBUFS=8 + +# +# TCP +# +CONFIG_LWIP_MAX_ACTIVE_TCP=16 +CONFIG_LWIP_MAX_LISTENING_TCP=16 +CONFIG_LWIP_TCP_HIGH_SPEED_RETRANSMISSION=y +CONFIG_LWIP_TCP_MAXRTX=12 +CONFIG_LWIP_TCP_SYNMAXRTX=12 +CONFIG_LWIP_TCP_MSS=1440 +CONFIG_LWIP_TCP_TMR_INTERVAL=250 +CONFIG_LWIP_TCP_MSL=60000 +CONFIG_LWIP_TCP_FIN_WAIT_TIMEOUT=20000 +CONFIG_LWIP_TCP_SND_BUF_DEFAULT=5760 +CONFIG_LWIP_TCP_WND_DEFAULT=5760 +CONFIG_LWIP_TCP_RECVMBOX_SIZE=6 +CONFIG_LWIP_TCP_ACCEPTMBOX_SIZE=6 +CONFIG_LWIP_TCP_QUEUE_OOSEQ=y +CONFIG_LWIP_TCP_OOSEQ_TIMEOUT=6 +CONFIG_LWIP_TCP_OOSEQ_MAX_PBUFS=4 +# CONFIG_LWIP_TCP_SACK_OUT is not set +CONFIG_LWIP_TCP_OVERSIZE_MSS=y +# CONFIG_LWIP_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_LWIP_TCP_OVERSIZE_DISABLE is not set +CONFIG_LWIP_TCP_RTO_TIME=1500 +# end of TCP + +# +# UDP +# +CONFIG_LWIP_MAX_UDP_PCBS=16 +CONFIG_LWIP_UDP_RECVMBOX_SIZE=6 +# end of UDP + +# +# Checksums +# +# CONFIG_LWIP_CHECKSUM_CHECK_IP is not set +# CONFIG_LWIP_CHECKSUM_CHECK_UDP is not set +CONFIG_LWIP_CHECKSUM_CHECK_ICMP=y +# end of Checksums + +CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_LWIP_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_LWIP_TCPIP_TASK_AFFINITY_CPU0 is not set +CONFIG_LWIP_TCPIP_TASK_AFFINITY=0x7FFFFFFF +CONFIG_LWIP_IPV6_MEMP_NUM_ND6_QUEUE=3 +CONFIG_LWIP_IPV6_ND6_NUM_NEIGHBORS=5 +CONFIG_LWIP_IPV6_ND6_NUM_PREFIXES=5 +CONFIG_LWIP_IPV6_ND6_NUM_ROUTERS=3 +CONFIG_LWIP_IPV6_ND6_NUM_DESTINATIONS=10 +# CONFIG_LWIP_PPP_SUPPORT is not set +# CONFIG_LWIP_SLIP_SUPPORT is not set + +# +# ICMP +# +CONFIG_LWIP_ICMP=y +# CONFIG_LWIP_MULTICAST_PING is not set +# CONFIG_LWIP_BROADCAST_PING is not set +# end of ICMP + +# +# LWIP RAW API +# +CONFIG_LWIP_MAX_RAW_PCBS=16 +# end of LWIP RAW API + +# +# SNTP +# +CONFIG_LWIP_SNTP_MAX_SERVERS=3 +# CONFIG_LWIP_DHCP_GET_NTP_SRV is not set +CONFIG_LWIP_SNTP_UPDATE_DELAY=3600000 +CONFIG_LWIP_SNTP_STARTUP_DELAY=y +CONFIG_LWIP_SNTP_MAXIMUM_STARTUP_DELAY=5000 +# end of SNTP + +# +# DNS +# +CONFIG_LWIP_DNS_MAX_HOST_IP=1 +CONFIG_LWIP_DNS_MAX_SERVERS=3 +# CONFIG_LWIP_FALLBACK_DNS_SERVER_SUPPORT is not set +# CONFIG_LWIP_DNS_SETSERVER_WITH_NETIF is not set +# end of DNS + +CONFIG_LWIP_BRIDGEIF_MAX_PORTS=7 +CONFIG_LWIP_ESP_LWIP_ASSERT=y + +# +# Hooks +# +# CONFIG_LWIP_HOOK_TCP_ISN_NONE is not set +CONFIG_LWIP_HOOK_TCP_ISN_DEFAULT=y +# CONFIG_LWIP_HOOK_TCP_ISN_CUSTOM is not set +CONFIG_LWIP_HOOK_IP6_ROUTE_NONE=y +# CONFIG_LWIP_HOOK_IP6_ROUTE_DEFAULT is not set +# CONFIG_LWIP_HOOK_IP6_ROUTE_CUSTOM is not set +CONFIG_LWIP_HOOK_ND6_GET_GW_NONE=y +# CONFIG_LWIP_HOOK_ND6_GET_GW_DEFAULT is not set +# CONFIG_LWIP_HOOK_ND6_GET_GW_CUSTOM is not set +CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_NONE=y +# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_DEFAULT is not set +# CONFIG_LWIP_HOOK_IP6_SELECT_SRC_ADDR_CUSTOM is not set +CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_DEFAULT is not set +# CONFIG_LWIP_HOOK_NETCONN_EXT_RESOLVE_CUSTOM is not set +CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_NONE=y +# CONFIG_LWIP_HOOK_DNS_EXT_RESOLVE_CUSTOM is not set +# CONFIG_LWIP_HOOK_IP6_INPUT_NONE is not set +CONFIG_LWIP_HOOK_IP6_INPUT_DEFAULT=y +# CONFIG_LWIP_HOOK_IP6_INPUT_CUSTOM is not set +# end of Hooks + +# CONFIG_LWIP_DEBUG is not set +# end of LWIP + +# +# mbedTLS +# +CONFIG_MBEDTLS_INTERNAL_MEM_ALLOC=y +# CONFIG_MBEDTLS_DEFAULT_MEM_ALLOC is not set +# CONFIG_MBEDTLS_CUSTOM_MEM_ALLOC is not set +CONFIG_MBEDTLS_ASYMMETRIC_CONTENT_LEN=y +CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384 +CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=4096 +# CONFIG_MBEDTLS_DYNAMIC_BUFFER is not set +# CONFIG_MBEDTLS_DEBUG is not set + +# +# mbedTLS v3.x related +# +# CONFIG_MBEDTLS_SSL_PROTO_TLS1_3 is not set +# CONFIG_MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH is not set +# CONFIG_MBEDTLS_X509_TRUSTED_CERT_CALLBACK is not set +# CONFIG_MBEDTLS_SSL_CONTEXT_SERIALIZATION is not set +CONFIG_MBEDTLS_SSL_KEEP_PEER_CERTIFICATE=y +CONFIG_MBEDTLS_PKCS7_C=y +# end of mbedTLS v3.x related + +# +# Certificate Bundle +# +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=y +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=y +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_CMN is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_NONE is not set +# CONFIG_MBEDTLS_CUSTOM_CERTIFICATE_BUNDLE is not set +# CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEPRECATED_LIST is not set +CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_MAX_CERTS=200 +# end of Certificate Bundle + +# CONFIG_MBEDTLS_ECP_RESTARTABLE is not set +CONFIG_MBEDTLS_CMAC_C=y +CONFIG_MBEDTLS_HARDWARE_AES=y +CONFIG_MBEDTLS_AES_USE_INTERRUPT=y +CONFIG_MBEDTLS_AES_INTERRUPT_LEVEL=0 +CONFIG_MBEDTLS_GCM_SUPPORT_NON_AES_CIPHER=y +CONFIG_MBEDTLS_HARDWARE_MPI=y +CONFIG_MBEDTLS_LARGE_KEY_SOFTWARE_MPI=y +CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y +CONFIG_MBEDTLS_MPI_INTERRUPT_LEVEL=0 +CONFIG_MBEDTLS_HARDWARE_SHA=y +CONFIG_MBEDTLS_ROM_MD5=y +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_SIGN is not set +# CONFIG_MBEDTLS_ATCA_HW_ECDSA_VERIFY is not set +CONFIG_MBEDTLS_HAVE_TIME=y +# CONFIG_MBEDTLS_PLATFORM_TIME_ALT is not set +# CONFIG_MBEDTLS_HAVE_TIME_DATE is not set +CONFIG_MBEDTLS_ECDSA_DETERMINISTIC=y +CONFIG_MBEDTLS_SHA512_C=y +# CONFIG_MBEDTLS_SHA3_C is not set +CONFIG_MBEDTLS_TLS_SERVER_AND_CLIENT=y +# CONFIG_MBEDTLS_TLS_SERVER_ONLY is not set +# CONFIG_MBEDTLS_TLS_CLIENT_ONLY is not set +# CONFIG_MBEDTLS_TLS_DISABLED is not set +CONFIG_MBEDTLS_TLS_SERVER=y +CONFIG_MBEDTLS_TLS_CLIENT=y +CONFIG_MBEDTLS_TLS_ENABLED=y + +# +# TLS Key Exchange Methods +# +# CONFIG_MBEDTLS_PSK_MODES is not set +CONFIG_MBEDTLS_KEY_EXCHANGE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ELLIPTIC_CURVE=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_RSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_ECDSA=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECDH_RSA=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_SSL_RENEGOTIATION=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +# CONFIG_MBEDTLS_SSL_PROTO_GMTSSL1_1 is not set +# CONFIG_MBEDTLS_SSL_PROTO_DTLS is not set +CONFIG_MBEDTLS_SSL_ALPN=y +CONFIG_MBEDTLS_CLIENT_SSL_SESSION_TICKETS=y +CONFIG_MBEDTLS_SERVER_SSL_SESSION_TICKETS=y + +# +# Symmetric Ciphers +# +CONFIG_MBEDTLS_AES_C=y +# CONFIG_MBEDTLS_CAMELLIA_C is not set +# CONFIG_MBEDTLS_DES_C is not set +# CONFIG_MBEDTLS_BLOWFISH_C is not set +# CONFIG_MBEDTLS_XTEA_C is not set +CONFIG_MBEDTLS_CCM_C=y +CONFIG_MBEDTLS_GCM_C=y +# CONFIG_MBEDTLS_NIST_KW_C is not set +# end of Symmetric Ciphers + +# CONFIG_MBEDTLS_RIPEMD160_C is not set + +# +# Certificates +# +CONFIG_MBEDTLS_PEM_PARSE_C=y +CONFIG_MBEDTLS_PEM_WRITE_C=y +CONFIG_MBEDTLS_X509_CRL_PARSE_C=y +CONFIG_MBEDTLS_X509_CSR_PARSE_C=y +# end of Certificates + +CONFIG_MBEDTLS_ECP_C=y +CONFIG_MBEDTLS_PK_PARSE_EC_EXTENDED=y +CONFIG_MBEDTLS_PK_PARSE_EC_COMPRESSED=y +# CONFIG_MBEDTLS_DHM_C is not set +CONFIG_MBEDTLS_ECDH_C=y +CONFIG_MBEDTLS_ECDSA_C=y +# CONFIG_MBEDTLS_ECJPAKE_C is not set +CONFIG_MBEDTLS_ECP_DP_SECP192R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP521R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP192K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP224K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_SECP256K1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP256R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP384R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_BP512R1_ENABLED=y +CONFIG_MBEDTLS_ECP_DP_CURVE25519_ENABLED=y +CONFIG_MBEDTLS_ECP_NIST_OPTIM=y +# CONFIG_MBEDTLS_ECP_FIXED_POINT_OPTIM is not set +# CONFIG_MBEDTLS_POLY1305_C is not set +# CONFIG_MBEDTLS_CHACHA20_C is not set +# CONFIG_MBEDTLS_HKDF_C is not set +# CONFIG_MBEDTLS_THREADING_C is not set +CONFIG_MBEDTLS_ERROR_STRINGS=y +CONFIG_MBEDTLS_FS_IO=y +# end of mbedTLS + +# +# ESP-MQTT Configurations +# +CONFIG_MQTT_PROTOCOL_311=y +# CONFIG_MQTT_PROTOCOL_5 is not set +CONFIG_MQTT_TRANSPORT_SSL=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET=y +CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y +# CONFIG_MQTT_MSG_ID_INCREMENTAL is not set +# CONFIG_MQTT_SKIP_PUBLISH_IF_DISCONNECTED is not set +# CONFIG_MQTT_REPORT_DELETED_MESSAGES is not set +# CONFIG_MQTT_USE_CUSTOM_CONFIG is not set +# CONFIG_MQTT_TASK_CORE_SELECTION_ENABLED is not set +# CONFIG_MQTT_CUSTOM_OUTBOX is not set +# end of ESP-MQTT Configurations + +# +# Newlib +# +CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF=y +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_LF is not set +# CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF is not set +# CONFIG_NEWLIB_STDIN_LINE_ENDING_LF is not set +CONFIG_NEWLIB_STDIN_LINE_ENDING_CR=y +# CONFIG_NEWLIB_NANO_FORMAT is not set +CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC_HRT=y +# CONFIG_NEWLIB_TIME_SYSCALL_USE_RTC is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_HRT is not set +# CONFIG_NEWLIB_TIME_SYSCALL_USE_NONE is not set +# end of Newlib + +# +# NVS +# +# CONFIG_NVS_ENCRYPTION is not set +# CONFIG_NVS_ASSERT_ERROR_CHECK is not set +# CONFIG_NVS_LEGACY_DUP_KEYS_COMPATIBILITY is not set +# end of NVS + +# +# OpenThread +# +# CONFIG_OPENTHREAD_ENABLED is not set + +# +# OpenThread Spinel +# +# CONFIG_OPENTHREAD_SPINEL_ONLY is not set +# end of OpenThread Spinel +# end of OpenThread + +# +# Protocomm +# +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y +CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y +# end of Protocomm + +# +# PThreads +# +CONFIG_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_PTHREAD_STACK_MIN=768 +CONFIG_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_PTHREAD_TASK_NAME_DEFAULT="pthread" +# end of PThreads + +# +# MMU Config +# +CONFIG_MMU_PAGE_SIZE_64KB=y +CONFIG_MMU_PAGE_MODE="64KB" +CONFIG_MMU_PAGE_SIZE=0x10000 +# end of MMU Config + +# +# Main Flash configuration +# + +# +# SPI Flash behavior when brownout +# +CONFIG_SPI_FLASH_BROWNOUT_RESET_XMC=y +CONFIG_SPI_FLASH_BROWNOUT_RESET=y +# end of SPI Flash behavior when brownout + +# +# Optional and Experimental Features (READ DOCS FIRST) +# + +# +# Features here require specific hardware (READ DOCS FIRST!) +# +# CONFIG_SPI_FLASH_AUTO_SUSPEND is not set +CONFIG_SPI_FLASH_SUSPEND_TSUS_VAL_US=50 +# CONFIG_SPI_FLASH_FORCE_ENABLE_XMC_C_SUSPEND is not set +# end of Optional and Experimental Features (READ DOCS FIRST) +# end of Main Flash configuration + +# +# SPI Flash driver +# +# CONFIG_SPI_FLASH_VERIFY_WRITE is not set +# CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set +CONFIG_SPI_FLASH_ROM_DRIVER_PATCH=y +# CONFIG_SPI_FLASH_ROM_IMPL is not set +CONFIG_SPI_FLASH_DANGEROUS_WRITE_ABORTS=y +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_FAILS is not set +# CONFIG_SPI_FLASH_DANGEROUS_WRITE_ALLOWED is not set +# CONFIG_SPI_FLASH_BYPASS_BLOCK_ERASE is not set +CONFIG_SPI_FLASH_YIELD_DURING_ERASE=y +CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS=20 +CONFIG_SPI_FLASH_ERASE_YIELD_TICKS=1 +CONFIG_SPI_FLASH_WRITE_CHUNK_SIZE=8192 +# CONFIG_SPI_FLASH_SIZE_OVERRIDE is not set +# CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED is not set +# CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST is not set + +# +# Auto-detect flash chips +# +CONFIG_SPI_FLASH_VENDOR_XMC_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_GD_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_ISSI_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_MXIC_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_WINBOND_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_BOYA_SUPPORTED=y +CONFIG_SPI_FLASH_VENDOR_TH_SUPPORTED=y +CONFIG_SPI_FLASH_SUPPORT_ISSI_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_MXIC_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_GD_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_WINBOND_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_BOYA_CHIP=y +CONFIG_SPI_FLASH_SUPPORT_TH_CHIP=y +# end of Auto-detect flash chips + +CONFIG_SPI_FLASH_ENABLE_ENCRYPTED_READ_WRITE=y +# end of SPI Flash driver + +# +# SPIFFS Configuration +# +CONFIG_SPIFFS_MAX_PARTITIONS=3 + +# +# SPIFFS Cache Configuration +# +CONFIG_SPIFFS_CACHE=y +CONFIG_SPIFFS_CACHE_WR=y +# CONFIG_SPIFFS_CACHE_STATS is not set +# end of SPIFFS Cache Configuration + +CONFIG_SPIFFS_PAGE_CHECK=y +CONFIG_SPIFFS_GC_MAX_RUNS=10 +# CONFIG_SPIFFS_GC_STATS is not set +CONFIG_SPIFFS_PAGE_SIZE=256 +CONFIG_SPIFFS_OBJ_NAME_LEN=32 +# CONFIG_SPIFFS_FOLLOW_SYMLINKS is not set +CONFIG_SPIFFS_USE_MAGIC=y +CONFIG_SPIFFS_USE_MAGIC_LENGTH=y +CONFIG_SPIFFS_META_LENGTH=4 +CONFIG_SPIFFS_USE_MTIME=y + +# +# Debug Configuration +# +# CONFIG_SPIFFS_DBG is not set +# CONFIG_SPIFFS_API_DBG is not set +# CONFIG_SPIFFS_GC_DBG is not set +# CONFIG_SPIFFS_CACHE_DBG is not set +# CONFIG_SPIFFS_CHECK_DBG is not set +# CONFIG_SPIFFS_TEST_VISUALISATION is not set +# end of Debug Configuration +# end of SPIFFS Configuration + +# +# TCP Transport +# + +# +# Websocket +# +CONFIG_WS_TRANSPORT=y +CONFIG_WS_BUFFER_SIZE=1024 +# CONFIG_WS_DYNAMIC_BUFFER is not set +# end of Websocket +# end of TCP Transport + +# +# Unity unit testing library +# +CONFIG_UNITY_ENABLE_FLOAT=y +CONFIG_UNITY_ENABLE_DOUBLE=y +# CONFIG_UNITY_ENABLE_64BIT is not set +# CONFIG_UNITY_ENABLE_COLOR is not set +CONFIG_UNITY_ENABLE_IDF_TEST_RUNNER=y +# CONFIG_UNITY_ENABLE_FIXTURE is not set +# CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL is not set +# end of Unity unit testing library + +# +# Virtual file system +# +CONFIG_VFS_SUPPORT_IO=y +CONFIG_VFS_SUPPORT_DIR=y +CONFIG_VFS_SUPPORT_SELECT=y +CONFIG_VFS_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_VFS_SELECT_IN_RAM=y +CONFIG_VFS_SUPPORT_TERMIOS=y +CONFIG_VFS_MAX_COUNT=8 + +# +# Host File System I/O (Semihosting) +# +CONFIG_VFS_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# end of Host File System I/O (Semihosting) + +CONFIG_VFS_INITIALIZE_DEV_NULL=y +# end of Virtual file system + +# +# Wear Levelling +# +# CONFIG_WL_SECTOR_SIZE_512 is not set +CONFIG_WL_SECTOR_SIZE_4096=y +CONFIG_WL_SECTOR_SIZE=4096 +# end of Wear Levelling + +# +# Wi-Fi Provisioning Manager +# +CONFIG_WIFI_PROV_SCAN_MAX_ENTRIES=16 +CONFIG_WIFI_PROV_AUTOSTOP_TIMEOUT=30 +CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y +# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set +# end of Wi-Fi Provisioning Manager + +# +# ODROID-MONITOR +# + +# +# GPIO +# +CONFIG_ENV_GPIO_RANGE_MIN=0 +CONFIG_ENV_GPIO_RANGE_MAX=19 +CONFIG_ENV_GPIO_IN_RANGE_MAX=19 +CONFIG_ENV_GPIO_OUT_RANGE_MAX=19 +CONFIG_GPIO_INA226_SCL=0 +CONFIG_GPIO_INA226_SDA=1 +CONFIG_GPIO_INA226_INT=10 +CONFIG_GPIO_UART_TX=6 +CONFIG_GPIO_UART_RX=7 +CONFIG_GPIO_LED_STATUS=8 +CONFIG_GPIO_LED_WIFI=9 +CONFIG_GPIO_SW_12V=4 +CONFIG_GPIO_SW_5V=5 +CONFIG_GPIO_TRIGGER_POWER=2 +CONFIG_GPIO_TRIGGER_RESET=3 +# end of GPIO +# end of ODROID-MONITOR + +# +# CMake Utilities +# +# CONFIG_CU_RELINKER_ENABLE is not set +# CONFIG_CU_DIAGNOSTICS_COLOR_NEVER is not set +CONFIG_CU_DIAGNOSTICS_COLOR_ALWAYS=y +# CONFIG_CU_DIAGNOSTICS_COLOR_AUTO is not set +# CONFIG_CU_GCC_LTO_ENABLE is not set +# CONFIG_CU_GCC_STRING_1BYTE_ALIGN is not set +# end of CMake Utilities + +# +# LED Indicator +# +CONFIG_BRIGHTNESS_TICKS=10 +CONFIG_USE_GAMMA_CORRECTION=y + +# +# LEDC Config +# +CONFIG_LEDC_LOW_SPEED_MODE=y +CONFIG_LEDC_SPEED_MODE_VALUE=0 +CONFIG_LEDC_TIMER_BIT_NUM=13 +CONFIG_LEDC_TIMER_FREQ_HZ=5000 +# end of LEDC Config +# end of LED Indicator + +# +# LittleFS +# +# CONFIG_LITTLEFS_SDMMC_SUPPORT is not set +CONFIG_LITTLEFS_MAX_PARTITIONS=3 +CONFIG_LITTLEFS_PAGE_SIZE=256 +CONFIG_LITTLEFS_OBJ_NAME_LEN=64 +CONFIG_LITTLEFS_READ_SIZE=128 +CONFIG_LITTLEFS_WRITE_SIZE=128 +CONFIG_LITTLEFS_LOOKAHEAD_SIZE=128 +CONFIG_LITTLEFS_CACHE_SIZE=512 +CONFIG_LITTLEFS_BLOCK_CYCLES=512 +CONFIG_LITTLEFS_USE_MTIME=y +# CONFIG_LITTLEFS_USE_ONLY_HASH is not set +# CONFIG_LITTLEFS_HUMAN_READABLE is not set +CONFIG_LITTLEFS_MTIME_USE_SECONDS=y +# CONFIG_LITTLEFS_MTIME_USE_NONCE is not set +# CONFIG_LITTLEFS_SPIFFS_COMPAT is not set +# CONFIG_LITTLEFS_FLUSH_FILE_EVERY_WRITE is not set +# CONFIG_LITTLEFS_FCNTL_GET_PATH is not set +# CONFIG_LITTLEFS_MULTIVERSION is not set +# CONFIG_LITTLEFS_MALLOC_STRATEGY_DISABLE is not set +CONFIG_LITTLEFS_MALLOC_STRATEGY_DEFAULT=y +# CONFIG_LITTLEFS_MALLOC_STRATEGY_INTERNAL is not set +CONFIG_LITTLEFS_ASSERTS=y +# CONFIG_LITTLEFS_MMAP_PARTITION is not set +# end of LittleFS +# end of Component config + +# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set + +# Deprecated options for backward compatibility +# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set +# CONFIG_NO_BLOBS is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set +CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y +# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set +# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set +CONFIG_LOG_BOOTLOADER_LEVEL=3 +# CONFIG_APP_ROLLBACK_ENABLE is not set +# CONFIG_FLASH_ENCRYPTION_ENABLED is not set +# CONFIG_FLASHMODE_QIO is not set +# CONFIG_FLASHMODE_QOUT is not set +CONFIG_FLASHMODE_DIO=y +# CONFIG_FLASHMODE_DOUT is not set +CONFIG_MONITOR_BAUD=115200 +CONFIG_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG=y +CONFIG_COMPILER_OPTIMIZATION_DEFAULT=y +# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set +# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set +CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y +# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set +# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set +CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2 +# CONFIG_CXX_EXCEPTIONS is not set +CONFIG_STACK_CHECK_NONE=y +# CONFIG_STACK_CHECK_NORM is not set +# CONFIG_STACK_CHECK_STRONG is not set +# CONFIG_STACK_CHECK_ALL is not set +# CONFIG_WARN_WRITE_STRINGS is not set +# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set +CONFIG_ESP32_APPTRACE_DEST_NONE=y +CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y +# CONFIG_EXTERNAL_COEX_ENABLE is not set +# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set +# CONFIG_EVENT_LOOP_PROFILING is not set +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_ESP_SYSTEM_PD_FLASH is not set +CONFIG_ESP32C3_LIGHTSLEEP_GPIO_RESET_WORKAROUND=y +CONFIG_ESP32C3_RTC_CLK_SRC_INT_RC=y +# CONFIG_ESP32C3_RTC_CLK_SRC_EXT_CRYS is not set +# CONFIG_ESP32C3_RTC_CLK_SRC_EXT_OSC is not set +# CONFIG_ESP32C3_RTC_CLK_SRC_INT_8MD256 is not set +CONFIG_ESP32C3_RTC_CLK_CAL_CYCLES=1024 +CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y +# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set +CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20 +CONFIG_ESP32_PHY_MAX_TX_POWER=20 +# CONFIG_REDUCE_PHY_TX_POWER is not set +# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set +CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y +# CONFIG_ESP32C3_DEFAULT_CPU_FREQ_80 is not set +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_160=y +CONFIG_ESP32C3_DEFAULT_CPU_FREQ_MHZ=160 +CONFIG_ESP32C3_MEMPROT_FEATURE=y +CONFIG_ESP32C3_MEMPROT_FEATURE_LOCK=y +CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 +CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304 +CONFIG_MAIN_TASK_STACK_SIZE=3584 +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +# CONFIG_ESP_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART=y +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 +CONFIG_INT_WDT=y +CONFIG_INT_WDT_TIMEOUT_MS=300 +CONFIG_TASK_WDT=y +CONFIG_ESP_TASK_WDT=y +# CONFIG_TASK_WDT_PANIC is not set +CONFIG_TASK_WDT_TIMEOUT_S=5 +CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y +# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set +CONFIG_ESP32C3_DEBUG_OCDAWARE=y +CONFIG_BROWNOUT_DET=y +CONFIG_ESP32C3_BROWNOUT_DET=y +CONFIG_ESP32C3_BROWNOUT_DET=y +CONFIG_BROWNOUT_DET_LVL_SEL_7=y +CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_7=y +# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_6 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_5 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_4 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_3 is not set +# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set +# CONFIG_ESP32C3_BROWNOUT_DET_LVL_SEL_2 is not set +CONFIG_BROWNOUT_DET_LVL=7 +CONFIG_ESP32C3_BROWNOUT_DET_LVL=7 +CONFIG_IPC_TASK_STACK_SIZE=1024 +CONFIG_TIMER_TASK_STACK_SIZE=3584 +CONFIG_ESP32_WIFI_ENABLED=y +CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10 +CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32 +# CONFIG_ESP32_WIFI_STATIC_TX_BUFFER is not set +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER=y +CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=1 +CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32 +# CONFIG_ESP32_WIFI_CSI_ENABLED is not set +CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y +CONFIG_ESP32_WIFI_TX_BA_WIN=6 +CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y +CONFIG_ESP32_WIFI_RX_BA_WIN=6 +CONFIG_ESP32_WIFI_RX_BA_WIN=6 +CONFIG_ESP32_WIFI_NVS_ENABLED=y +CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752 +CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32 +CONFIG_ESP32_WIFI_IRAM_OPT=y +CONFIG_ESP32_WIFI_RX_IRAM_OPT=y +CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y +CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y +CONFIG_WPA_MBEDTLS_CRYPTO=y +CONFIG_WPA_MBEDTLS_TLS_CLIENT=y +# CONFIG_WPA_WAPI_PSK is not set +# CONFIG_WPA_SUITE_B_192 is not set +# CONFIG_WPA_11KV_SUPPORT is not set +# CONFIG_WPA_MBO_SUPPORT is not set +# CONFIG_WPA_DPP_SUPPORT is not set +# CONFIG_WPA_11R_SUPPORT is not set +# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set +# CONFIG_WPA_WPS_STRICT is not set +# CONFIG_WPA_DEBUG_PRINT is not set +# CONFIG_WPA_TESTING_OPTIONS is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set +# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set +CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y +CONFIG_TIMER_TASK_PRIORITY=1 +CONFIG_TIMER_TASK_STACK_DEPTH=2048 +CONFIG_TIMER_QUEUE_LENGTH=10 +# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set +# CONFIG_HAL_ASSERTION_SILIENT is not set +# CONFIG_L2_TO_L3_COPY is not set +CONFIG_ESP_GRATUITOUS_ARP=y +CONFIG_GARP_TMR_INTERVAL=60 +CONFIG_TCPIP_RECVMBOX_SIZE=32 +CONFIG_TCP_MAXRTX=12 +CONFIG_TCP_SYNMAXRTX=12 +CONFIG_TCP_MSS=1440 +CONFIG_TCP_MSL=60000 +CONFIG_TCP_SND_BUF_DEFAULT=5760 +CONFIG_TCP_WND_DEFAULT=5760 +CONFIG_TCP_RECVMBOX_SIZE=6 +CONFIG_TCP_QUEUE_OOSEQ=y +CONFIG_TCP_OVERSIZE_MSS=y +# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set +# CONFIG_TCP_OVERSIZE_DISABLE is not set +CONFIG_UDP_RECVMBOX_SIZE=6 +CONFIG_TCPIP_TASK_STACK_SIZE=3072 +CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y +# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set +CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF +# CONFIG_PPP_SUPPORT is not set +CONFIG_ESP32C3_TIME_SYSCALL_USE_RTC_SYSTIMER=y +# CONFIG_ESP32C3_TIME_SYSCALL_USE_RTC is not set +# CONFIG_ESP32C3_TIME_SYSCALL_USE_SYSTIMER is not set +# CONFIG_ESP32C3_TIME_SYSCALL_USE_NONE is not set +CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5 +CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072 +CONFIG_ESP32_PTHREAD_STACK_MIN=768 +CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1 +CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread" +CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set +# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set +CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y +CONFIG_SUPPORT_TERMIOS=y +CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1 +# End of deprecated options diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..9ee3a3f --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1,8 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) 5.4.0 Project Minimal Configuration +# +CONFIG_IDF_TARGET="esp32c3" +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_UART_ISR_IN_IRAM=y +CONFIG_HTTPD_WS_SUPPORT=y