diff --git a/docs/API.md b/docs/API.md index 099e5db..ecbde93 100644 --- a/docs/API.md +++ b/docs/API.md @@ -114,6 +114,9 @@ Retrieves the complete current network and system configuration. "mode": "apsta", "net_type": "static", "baudrate": "115200", + "vin_current_limit": 8.0, + "main_current_limit": 7.0, + "usb_current_limit": 5.0, "ip": { "ip": "192.168.1.100", "gateway": "192.168.1.1", @@ -130,6 +133,9 @@ Retrieves the complete current network and system configuration. - `mode` (string): The current Wi-Fi mode (`"sta"` or `"apsta"`). - `net_type` (string): The network type (`"dhcp"` or `"static"`). - `baudrate` (string): The current UART baud rate. + - `vin_current_limit` (number): The current limit for VIN in Amps. `0` means disabled. + - `main_current_limit` (number): The current limit for the Main channel in Amps. `0` means disabled. + - `usb_current_limit` (number): The current limit for the USB channel in Amps. `0` means disabled. - `ip` (object): Contains IP configuration details. Present even if using DHCP (may show the last-leased IP). - `ip` (string): The device's IP address. - `gateway` (string): The network gateway address. @@ -160,17 +166,14 @@ This is a multi-purpose endpoint. The server determines the action based on the { "net_type": "dhcp" } ``` - **Request Body (for Static IP)**: - *Note: The `ip` object structure is consistent with the `GET /api/setting` response.* ```json { "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" - } + "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 (200 OK)**: @@ -203,6 +206,18 @@ This is a multi-purpose endpoint. The server determines the action based on the { "status": "baudrate_updated" } ``` +- **Action: Configure Current Limits** + - **Request Body**: + *Note: You can set one or more limits in a single request. A value of `-1.0` disables the limit.* + ```json + { + "vin_current_limit": 7.5, + "main_current_limit": 6.0, + "usb_current_limit": -1.0 + } + ``` + - **Success Response (200 OK)**: `{"status":"current_limit_updated"}` + --- ### Endpoint: `/api/wifi/scan` diff --git a/main/Kconfig b/main/Kconfig index 554e0ec..77b6272 100644 --- a/main/Kconfig +++ b/main/Kconfig @@ -58,6 +58,13 @@ menu "ODROID-MONITOR" help GPIO number for LED. + config GPIO_EXPANDER_RESET + int "Trigger reset GPIO Num" + range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX + default 8 + help + GPIO number for Reset expander. + config EXPANDER_GPIO_SW_12V int "12v Load Switch GPIO Num" range ENV_GPIO_RANGE_MIN ENV_GPIO_OUT_RANGE_MAX diff --git a/main/include/nconfig.h b/main/include/nconfig.h index d1d2b31..8bc26dc 100644 --- a/main/include/nconfig.h +++ b/main/include/nconfig.h @@ -39,6 +39,9 @@ enum nconfig_type NETIF_DNS2, ///< The secondary DNS server address. NETIF_TYPE, ///< The network interface type (e.g., "dhcp" or "static"). UART_BAUD_RATE, ///< The baud rate for the UART communication. + VIN_CURRENT_LIMIT, ///< The maximum current limit for the VIN. + MAIN_CURRENT_LIMIT, ///< The maximum current limit for the MAIN out. + USB_CURRENT_LIMIT, ///< The maximum current limit for the USB out. NCONFIG_TYPE_MAX, ///< Sentinel for the maximum number of configuration types. }; diff --git a/main/nconfig/nconfig.c b/main/nconfig/nconfig.c index 597ccea..55028de 100644 --- a/main/nconfig/nconfig.c +++ b/main/nconfig/nconfig.c @@ -10,10 +10,22 @@ 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", + [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", + [VIN_CURRENT_LIMIT] = "vin_climit", + [MAIN_CURRENT_LIMIT] = "main_climit", + [USB_CURRENT_LIMIT] = "usb_climit", }; struct default_value @@ -33,6 +45,9 @@ struct default_value const default_values[] = { {WIFI_MODE, "apsta"}, {AP_SSID, "odroid-pm"}, {AP_PASSWORD, "powermate"}, + {VIN_CURRENT_LIMIT, "8.0"}, + {MAIN_CURRENT_LIMIT, "7.0"}, + {USB_CURRENT_LIMIT, "5.0"}, }; esp_err_t init_nconfig() diff --git a/main/service/climit.h b/main/service/climit.h new file mode 100644 index 0000000..af1e9cb --- /dev/null +++ b/main/service/climit.h @@ -0,0 +1,21 @@ +// +// Created by shinys on 25. 9. 4.. +// + +#ifndef ODROID_POWER_MATE_CLIMIT_H +#define ODROID_POWER_MATE_CLIMIT_H + +#include "esp_err.h" + +#include + +#define VIN_CURRENT_LIMIT_MAX 8.0f +#define MAIN_CURRENT_LIMIT_MAX 7.0f +#define USB_CURRENT_LIMIT_MAX 5.0f + +esp_err_t climit_set_vin(double value); +esp_err_t climit_set_main(double value); +esp_err_t climit_set_usb(double value); +bool is_overcurrent(); + +#endif // ODROID_POWER_MATE_CLIMIT_H diff --git a/main/service/monitor.c b/main/service/monitor.c index 693534e..cce1575 100644 --- a/main/service/monitor.c +++ b/main/service/monitor.c @@ -3,27 +3,44 @@ // #include "monitor.h" +#include #include +#include "climit.h" #include "datalog.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_timer.h" #include "esp_wifi_types_generic.h" #include "freertos/FreeRTOS.h" +#include "freertos/task.h" // Added for FreeRTOS tasks #include "ina3221.h" #include "pb.h" #include "pb_encode.h" #include "status.pb.h" +#include "sw.h" #include "webserver.h" #include "wifi.h" +#define CHANNEL_VIN INA3221_CHANNEL_3 +#define CHANNEL_MAIN INA3221_CHANNEL_2 +#define CHANNEL_USB INA3221_CHANNEL_1 + #define PM_SDA CONFIG_I2C_GPIO_SDA #define PM_SCL CONFIG_I2C_GPIO_SCL +#define PM_INT_CRITICAL CONFIG_GPIO_INA3221_INT_CRITICAL +#define PM_EXPANDER_RST CONFIG_GPIO_EXPANDER_RESET + #define PB_BUFFER_SIZE 256 static const char* TAG = "monitor"; +static esp_timer_handle_t sensor_timer; +static esp_timer_handle_t wifi_status_timer; +// static esp_timer_handle_t shutdown_load_sw; // No longer needed + +static TaskHandle_t shutdown_task_handle = NULL; // Global task handle + ina3221_t ina3221 = { .shunt = {10, 10, 10}, .mask.mask_register = INA3221_DEFAULT_MASK, @@ -138,12 +155,92 @@ static void status_wifi_callback(void* arg) send_pb_message(StatusMessage_fields, &message); } -static esp_timer_handle_t sensor_timer; -static esp_timer_handle_t wifi_status_timer; +// New FreeRTOS task for shutdown logic +static void shutdown_load_sw_task(void* pvParameters) +{ + while (1) + { + // Wait indefinitely for a notification from the ISR + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + + ESP_LOGW(TAG, "critical interrupt triggered (via task)"); + gpio_set_level(PM_EXPANDER_RST, 0); + vTaskDelay(100 / portTICK_PERIOD_MS); + gpio_set_level(PM_EXPANDER_RST, 1); + config_sw(); + } +} + +static void IRAM_ATTR critical_isr_handler(void* arg) +{ + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + if (shutdown_task_handle != NULL) + { + vTaskNotifyGiveFromISR(shutdown_task_handle, &xHigherPriorityTaskWoken); + } + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); +} + +static void gpio_init() +{ + // critical int + gpio_set_intr_type(PM_INT_CRITICAL, GPIO_INTR_NEGEDGE); + gpio_set_direction(PM_INT_CRITICAL, GPIO_MODE_INPUT); + gpio_install_isr_service(0); + gpio_isr_handler_add(PM_INT_CRITICAL, critical_isr_handler, (void*)PM_INT_CRITICAL); + + // rst expander + gpio_set_level(PM_EXPANDER_RST, 1); + gpio_set_direction(PM_EXPANDER_RST, GPIO_MODE_OUTPUT); +} + +esp_err_t climit_set_vin(double value) +{ + float lim = (float)(value * 1000); + ESP_LOGI(TAG, "Setting VIN current limit to: %fmA", lim); + if (value > 0.0f) + return ina3221_set_critical_alert(&ina3221, CHANNEL_VIN, lim); + return ina3221_set_critical_alert(&ina3221, CHANNEL_VIN, (15.0f * 1000.0f)); +} + +esp_err_t climit_set_main(double value) +{ + float lim = (float)(value * 1000); + ESP_LOGI(TAG, "Setting MAIN current limit to: %fmA", lim); + if (value > 0.0f) + return ina3221_set_critical_alert(&ina3221, CHANNEL_MAIN, lim); + return ina3221_set_critical_alert(&ina3221, CHANNEL_VIN, (15.0f * 1000.0f)); +} + +esp_err_t climit_set_usb(double value) +{ + float lim = (float)(value * 1000); + ESP_LOGI(TAG, "Setting USB current limit to: %fmA", lim); + if (value > 0.0f) + return ina3221_set_critical_alert(&ina3221, CHANNEL_USB, lim); + return ina3221_set_critical_alert(&ina3221, CHANNEL_VIN, (15.0f * 1000.0f)); +} void init_status_monitor() { + gpio_init(); ESP_ERROR_CHECK(ina3221_init_desc(&ina3221, 0x40, 0, PM_SDA, PM_SCL)); + + double lim; + char buf[10]; + + nconfig_read(VIN_CURRENT_LIMIT, buf, sizeof(buf)); + lim = atof(buf); + climit_set_vin(lim); + + nconfig_read(MAIN_CURRENT_LIMIT, buf, sizeof(buf)); + lim = atof(buf); + climit_set_main(lim); + + nconfig_read(USB_CURRENT_LIMIT, buf, sizeof(buf)); + lim = atof(buf); + climit_set_usb(lim); + datalog_init(); const esp_timer_create_args_t sensor_timer_args = {.callback = &sensor_timer_callback, @@ -153,6 +250,8 @@ void init_status_monitor() ESP_ERROR_CHECK(esp_timer_create(&sensor_timer_args, &sensor_timer)); ESP_ERROR_CHECK(esp_timer_create(&wifi_timer_args, &wifi_status_timer)); + xTaskCreate(shutdown_load_sw_task, "shutdown_sw_task", configMINIMAL_STACK_SIZE * 3, NULL, 15, &shutdown_task_handle); + ESP_ERROR_CHECK(esp_timer_start_periodic(sensor_timer, 1000000)); ESP_ERROR_CHECK(esp_timer_start_periodic(wifi_status_timer, 1000000 * 5)); } diff --git a/main/service/setting.c b/main/service/setting.c index 0b233b7..c75158c 100644 --- a/main/service/setting.c +++ b/main/service/setting.c @@ -1,10 +1,11 @@ +#include #include "cJSON.h" +#include "climit.h" #include "esp_http_server.h" #include "esp_log.h" #include "esp_netif.h" #include "esp_timer.h" #include "nconfig.h" -#include "system.h" #include "webserver.h" #include "wifi.h" @@ -15,31 +16,42 @@ 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) + char buf[16]; + if (nconfig_read(WIFI_MODE, buf, sizeof(buf)) == ESP_OK) { - cJSON_AddStringToObject(root, "mode", mode_buf); + cJSON_AddStringToObject(root, "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) + if (nconfig_read(NETIF_TYPE, buf, sizeof(buf)) == ESP_OK) { - cJSON_AddStringToObject(root, "net_type", net_type_buf); + cJSON_AddStringToObject(root, "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) + if (nconfig_read(UART_BAUD_RATE, buf, sizeof(buf)) == ESP_OK) { - cJSON_AddStringToObject(root, "baudrate", baud_buf); + cJSON_AddStringToObject(root, "baudrate", buf); + } + + // Add current limits to the response + if (nconfig_read(VIN_CURRENT_LIMIT, buf, sizeof(buf)) == ESP_OK) + { + cJSON_AddNumberToObject(root, "vin_current_limit", atof(buf)); + } + if (nconfig_read(MAIN_CURRENT_LIMIT, buf, sizeof(buf)) == ESP_OK) + { + cJSON_AddNumberToObject(root, "main_current_limit", atof(buf)); + } + if (nconfig_read(USB_CURRENT_LIMIT, buf, sizeof(buf)) == ESP_OK) + { + cJSON_AddNumberToObject(root, "usb_current_limit", atof(buf)); } if (wifi_get_current_ap_info(&ap_info) == ESP_OK) @@ -143,6 +155,9 @@ static esp_err_t setting_post_handler(httpd_req_t* req) cJSON* net_type_item = cJSON_GetObjectItem(root, "net_type"); cJSON* ssid_item = cJSON_GetObjectItem(root, "ssid"); cJSON* baud_item = cJSON_GetObjectItem(root, "baudrate"); + cJSON* vin_climit_item = cJSON_GetObjectItem(root, "vin_current_limit"); + cJSON* main_climit_item = cJSON_GetObjectItem(root, "main_current_limit"); + cJSON* usb_climit_item = cJSON_GetObjectItem(root, "usb_current_limit"); if (mode_item && cJSON_IsString(mode_item)) { @@ -253,6 +268,41 @@ static esp_err_t setting_post_handler(httpd_req_t* req) change_baud_rate(strtol(baudrate, NULL, 10)); httpd_resp_sendstr(req, "{\"status\":\"baudrate_updated\"}"); } + else if (vin_climit_item || main_climit_item || usb_climit_item) + { + char num_buf[10]; + if (vin_climit_item && cJSON_IsNumber(vin_climit_item)) + { + double val = vin_climit_item->valuedouble; + if (val >= 0.0 && val <= VIN_CURRENT_LIMIT_MAX) + { + snprintf(num_buf, sizeof(num_buf), "%.2f", val); + nconfig_write(VIN_CURRENT_LIMIT, num_buf); + climit_set_vin(val); + } + } + if (main_climit_item && cJSON_IsNumber(main_climit_item)) + { + double val = main_climit_item->valuedouble; + if (val >= 0.0 && val <= MAIN_CURRENT_LIMIT_MAX) + { + snprintf(num_buf, sizeof(num_buf), "%.2f", val); + nconfig_write(MAIN_CURRENT_LIMIT, num_buf); + climit_set_main(val); + } + } + if (usb_climit_item && cJSON_IsNumber(usb_climit_item)) + { + double val = usb_climit_item->valuedouble; + if (val >= 0.0 && val <= USB_CURRENT_LIMIT_MAX) + { + snprintf(num_buf, sizeof(num_buf), "%.2f", val); + nconfig_write(USB_CURRENT_LIMIT, num_buf); + climit_set_usb(val); + } + } + httpd_resp_sendstr(req, "{\"status\":\"current_limit_updated\"}"); + } else { httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid payload"); diff --git a/main/service/sw.c b/main/service/sw.c index 944a8f7..4e20a2b 100644 --- a/main/service/sw.c +++ b/main/service/sw.c @@ -54,9 +54,8 @@ static void trigger_off_callback(void* arg) xSemaphoreGive(expander_mutex); } -void init_sw() +void config_sw() { - ESP_ERROR_CHECK(pca9557_init_desc(&pca, 0x18, I2C_PORT, GPIO_SDA, GPIO_SCL)); ESP_ERROR_CHECK(pca9557_set_mode(&pca, GPIO_MAIN, PCA9557_MODE_OUTPUT)); ESP_ERROR_CHECK(pca9557_set_mode(&pca, GPIO_USB, PCA9557_MODE_OUTPUT)); ESP_ERROR_CHECK(pca9557_set_mode(&pca, GPIO_PWR, PCA9557_MODE_OUTPUT)); @@ -70,6 +69,13 @@ void init_sw() load_switch_12v_status = val != 0 ? true : false; ESP_ERROR_CHECK(pca9557_get_level(&pca, CONFIG_EXPANDER_GPIO_SW_5V, &val)); load_switch_5v_status = val != 0 ? true : false; +} + +void init_sw() +{ + ESP_ERROR_CHECK(pca9557_init_desc(&pca, 0x18, I2C_PORT, GPIO_SDA, GPIO_SCL)); + + config_sw(); const esp_timer_create_args_t power_timer_args = { .callback = &trigger_off_callback, .arg = (void*)GPIO_PWR, .name = "power_trigger_off"}; diff --git a/main/service/sw.h b/main/service/sw.h index 37e4695..d985fe5 100644 --- a/main/service/sw.h +++ b/main/service/sw.h @@ -7,6 +7,7 @@ #include void init_sw(); +void config_sw(); void trig_power(); void trig_reset(); void set_main_load_switch(bool on); diff --git a/main/service/webserver.c b/main/service/webserver.c index 1c3d954..12b9131 100644 --- a/main/service/webserver.c +++ b/main/service/webserver.c @@ -86,5 +86,6 @@ void start_webserver(void) register_wifi_endpoint(server); register_ws_endpoint(server); register_control_endpoint(server); + register_reboot_endpoint(server); init_status_monitor(); } diff --git a/page/index.html b/page/index.html index 9ea737c..f964f17 100644 --- a/page/index.html +++ b/page/index.html @@ -159,6 +159,11 @@ data-bs-target="#ap-mode-settings-pane" type="button" role="tab">AP Mode +