Add UART multiplexer and INA226 monitoring
This commit is contained in:
@@ -2,4 +2,6 @@ idf_component_register(SRCS "main.c"
|
||||
"dcdc_controller.c"
|
||||
"usb_cdc_cli.c"
|
||||
"ws2812_status.c"
|
||||
"ina226_monitor.c"
|
||||
"uart_mux.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
172
main/ina226_monitor.c
Normal file
172
main/ina226_monitor.c
Normal file
@@ -0,0 +1,172 @@
|
||||
#include "ina226_monitor.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "driver/i2c.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_WATCH_INA226_ENABLED
|
||||
#define CONFIG_WATCH_INA226_ENABLED 0
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_PORT
|
||||
#define CONFIG_WATCH_INA226_I2C_PORT 0
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_SDA
|
||||
#define CONFIG_WATCH_INA226_I2C_SDA 6
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_SCL
|
||||
#define CONFIG_WATCH_INA226_I2C_SCL 7
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_I2C_FREQ_HZ
|
||||
#define CONFIG_WATCH_INA226_I2C_FREQ_HZ 400000
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_CURRENT_LSB_uA
|
||||
#define CONFIG_WATCH_INA226_CURRENT_LSB_uA 100
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_SHUNT_MILLIOHM
|
||||
#define CONFIG_WATCH_INA226_SHUNT_MILLIOHM 10
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_INA226_ADDR
|
||||
#define CONFIG_WATCH_INA226_ADDR 0x40
|
||||
#endif
|
||||
|
||||
#define INA226_REG_CONFIG 0x00
|
||||
#define INA226_REG_BUS 0x02
|
||||
#define INA226_REG_CURRENT 0x04
|
||||
#define INA226_REG_CALIBRATION 0x05
|
||||
|
||||
#define INA226_CONFIG_AVG_16 (0x04 << 9)
|
||||
#define INA226_CONFIG_VBUS_1100US (0x04 << 6)
|
||||
#define INA226_CONFIG_VSH_1100US (0x04 << 3)
|
||||
#define INA226_CONFIG_MODE_SHUNT_BUS_CONT 0x07
|
||||
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
static const char *TAG = "ina226";
|
||||
static bool s_initialized;
|
||||
static float s_current_lsb_ma;
|
||||
static uint8_t s_address = CONFIG_WATCH_INA226_ADDR;
|
||||
static ina226_reading_t s_last_reading;
|
||||
#endif
|
||||
|
||||
esp_err_t ina226_monitor_init(void)
|
||||
{
|
||||
#if !CONFIG_WATCH_INA226_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
i2c_config_t config = {
|
||||
.mode = I2C_MODE_MASTER,
|
||||
.sda_io_num = CONFIG_WATCH_INA226_I2C_SDA,
|
||||
.scl_io_num = CONFIG_WATCH_INA226_I2C_SCL,
|
||||
.sda_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.scl_pullup_en = GPIO_PULLUP_ENABLE,
|
||||
.master.clk_speed = CONFIG_WATCH_INA226_I2C_FREQ_HZ,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(i2c_param_config(CONFIG_WATCH_INA226_I2C_PORT, &config), TAG, "i2c config failed");
|
||||
ESP_RETURN_ON_ERROR(i2c_driver_install(CONFIG_WATCH_INA226_I2C_PORT, config.mode, 0, 0, 0),
|
||||
TAG, "i2c driver install failed");
|
||||
|
||||
double current_lsb_a = ((double)CONFIG_WATCH_INA226_CURRENT_LSB_uA) / 1000000.0;
|
||||
double shunt_ohms = ((double)CONFIG_WATCH_INA226_SHUNT_MILLIOHM) / 1000.0;
|
||||
double calibration = 0.00512 / (current_lsb_a * shunt_ohms);
|
||||
if (calibration > 0xFFFF) calibration = 0xFFFF;
|
||||
uint16_t calibration_value = (uint16_t)calibration;
|
||||
s_current_lsb_ma = (float)current_lsb_a * 1000.0f;
|
||||
|
||||
const uint16_t config_value = INA226_CONFIG_AVG_16 |
|
||||
INA226_CONFIG_VBUS_1100US |
|
||||
INA226_CONFIG_VSH_1100US |
|
||||
INA226_CONFIG_MODE_SHUNT_BUS_CONT;
|
||||
|
||||
uint8_t payload_cfg[3] = { INA226_REG_CONFIG, (uint8_t)(config_value >> 8), (uint8_t)(config_value & 0xFF) };
|
||||
ESP_RETURN_ON_ERROR(i2c_master_write_to_device(CONFIG_WATCH_INA226_I2C_PORT,
|
||||
s_address, payload_cfg, sizeof(payload_cfg),
|
||||
pdMS_TO_TICKS(100)),
|
||||
TAG, "config write failed");
|
||||
|
||||
uint8_t payload_cal[3] = { INA226_REG_CALIBRATION, (uint8_t)(calibration_value >> 8),
|
||||
(uint8_t)(calibration_value & 0xFF) };
|
||||
ESP_RETURN_ON_ERROR(i2c_master_write_to_device(CONFIG_WATCH_INA226_I2C_PORT,
|
||||
s_address, payload_cal, sizeof(payload_cal),
|
||||
pdMS_TO_TICKS(100)),
|
||||
TAG, "calibration write failed");
|
||||
|
||||
s_last_reading = (ina226_reading_t){0};
|
||||
s_initialized = true;
|
||||
ESP_LOGI(TAG, "INA226 ініціалізовано (адреса 0x%02X)", s_address);
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool ina226_monitor_ready(void)
|
||||
{
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
return s_initialized;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t ina226_monitor_channel_count(void)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
static esp_err_t ina226_read_register(uint8_t reg, uint16_t *out_value)
|
||||
{
|
||||
uint8_t value[2];
|
||||
esp_err_t err = i2c_master_write_read_device(CONFIG_WATCH_INA226_I2C_PORT,
|
||||
s_address, ®, 1, value, sizeof(value),
|
||||
pdMS_TO_TICKS(100));
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
*out_value = ((uint16_t)value[0] << 8) | value[1];
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading)
|
||||
{
|
||||
#if !CONFIG_WATCH_INA226_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!s_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
uint16_t bus_raw = 0;
|
||||
uint16_t current_raw = 0;
|
||||
|
||||
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_BUS, &bus_raw), TAG, "bus read failed");
|
||||
ESP_RETURN_ON_ERROR(ina226_read_register(INA226_REG_CURRENT, ¤t_raw), TAG, "current read failed");
|
||||
|
||||
float voltage_v = (float)bus_raw * 1.25f / 1000.0f;
|
||||
float current_ma = (int16_t)current_raw * s_current_lsb_ma;
|
||||
float power_mw = voltage_v * current_ma;
|
||||
|
||||
s_last_reading = (ina226_reading_t){
|
||||
.voltage_v = voltage_v,
|
||||
.current_ma = current_ma,
|
||||
.power_mw = power_mw,
|
||||
};
|
||||
if (out_reading) {
|
||||
*out_reading = s_last_reading;
|
||||
}
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
const ina226_reading_t *ina226_monitor_get_last(void)
|
||||
{
|
||||
#if CONFIG_WATCH_INA226_ENABLED
|
||||
return &s_last_reading;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
18
main/ina226_monitor.h
Normal file
18
main/ina226_monitor.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
typedef struct {
|
||||
float voltage_v;
|
||||
float current_ma;
|
||||
float power_mw;
|
||||
} ina226_reading_t;
|
||||
|
||||
esp_err_t ina226_monitor_init(void);
|
||||
bool ina226_monitor_ready(void);
|
||||
size_t ina226_monitor_channel_count(void);
|
||||
esp_err_t ina226_monitor_sample(ina226_reading_t *out_reading);
|
||||
const ina226_reading_t *ina226_monitor_get_last(void);
|
||||
28
main/main.c
28
main/main.c
@@ -5,6 +5,8 @@
|
||||
#include "esp_log.h"
|
||||
|
||||
#include "dcdc_controller.h"
|
||||
#include "ina226_monitor.h"
|
||||
#include "uart_mux.h"
|
||||
#include "usb_cdc_cli.h"
|
||||
#include "ws2812_status.h"
|
||||
|
||||
@@ -25,6 +27,19 @@ void app_main(void)
|
||||
ESP_LOGW(TAG, "WS2812 статусний індикатор недоступний");
|
||||
}
|
||||
|
||||
if (ina226_monitor_init() == ESP_OK) {
|
||||
ESP_LOGI(TAG, "INA226 моніторинг активовано");
|
||||
ina226_monitor_sample(NULL);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Моніторинг навантаження недоступний");
|
||||
}
|
||||
|
||||
if (uart_mux_init() == ESP_OK) {
|
||||
ESP_LOGI(TAG, "UART мультиплексор активовано");
|
||||
} else {
|
||||
ESP_LOGW(TAG, "UART мультиплексор недоступний");
|
||||
}
|
||||
|
||||
if (usb_cdc_cli_init() != ESP_OK) {
|
||||
ESP_LOGW(TAG, "USB CDC CLI недоступний");
|
||||
} else {
|
||||
@@ -48,6 +63,19 @@ void app_main(void)
|
||||
ws2812_status_mark_active(ch);
|
||||
vTaskDelay(on_time);
|
||||
|
||||
ina226_reading_t reading = {0};
|
||||
if (ina226_monitor_sample(&reading) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Живлення: %.2f В, %.1f мА, %.1f мВт",
|
||||
reading.voltage_v, reading.current_ma, reading.power_mw);
|
||||
}
|
||||
|
||||
if (uart_mux_ready()) {
|
||||
char msg[64];
|
||||
int len = snprintf(msg, sizeof(msg), "PWR %.2fV %.0fmA\r\n",
|
||||
reading.voltage_v, reading.current_ma);
|
||||
uart_mux_write(ch, (const uint8_t *)msg, len, pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
prev_channel = ch;
|
||||
}
|
||||
}
|
||||
|
||||
233
main/uart_mux.c
Normal file
233
main/uart_mux.c
Normal file
@@ -0,0 +1,233 @@
|
||||
#include "uart_mux.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "dcdc_controller.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/uart.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#ifndef CONFIG_WATCH_UART_MUX_CHANNELS
|
||||
#define CONFIG_WATCH_UART_MUX_CHANNELS 5
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC
|
||||
#define CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC 60
|
||||
#endif
|
||||
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
|
||||
#define UART_MUX_MAX_CHANNELS CONFIG_WATCH_UART_MUX_CHANNELS
|
||||
|
||||
static const char *TAG = "uart_mux";
|
||||
|
||||
static const gpio_num_t s_select_pins[3] = {
|
||||
CONFIG_WATCH_UART_MUX_SEL_A0,
|
||||
CONFIG_WATCH_UART_MUX_SEL_A1,
|
||||
CONFIG_WATCH_UART_MUX_SEL_A2,
|
||||
};
|
||||
|
||||
static SemaphoreHandle_t s_mutex;
|
||||
static size_t s_active_channel = SIZE_MAX;
|
||||
static bool s_initialized;
|
||||
static int64_t s_last_heartbeat_us[UART_MUX_MAX_CHANNELS];
|
||||
static TaskHandle_t s_watchdog_task;
|
||||
|
||||
static esp_err_t uart_mux_select_locked(size_t channel)
|
||||
{
|
||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (channel == s_active_channel) {
|
||||
return ESP_OK;
|
||||
}
|
||||
for (int bit = 0; bit < 3; ++bit) {
|
||||
int level = (channel >> bit) & 0x1;
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(s_select_pins[bit], level), TAG,
|
||||
"GPIO set failed");
|
||||
}
|
||||
s_active_channel = channel;
|
||||
s_last_heartbeat_us[channel] = esp_timer_get_time();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void uart_mux_watchdog_task(void *arg)
|
||||
{
|
||||
const TickType_t poll_interval = pdMS_TO_TICKS(1000);
|
||||
const TickType_t read_timeout = pdMS_TO_TICKS(10);
|
||||
const int64_t timeout_us = (int64_t)CONFIG_WATCH_UART_HEARTBEAT_TIMEOUT_SEC * 1000000LL;
|
||||
uint8_t buffer[CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN];
|
||||
|
||||
while (true) {
|
||||
vTaskDelay(poll_interval);
|
||||
int64_t now = esp_timer_get_time();
|
||||
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
||||
if (xSemaphoreTake(s_mutex, pdMS_TO_TICKS(20)) == pdTRUE) {
|
||||
if (uart_mux_select_locked(ch) == ESP_OK) {
|
||||
int read = uart_read_bytes(CONFIG_WATCH_UART_PORT,
|
||||
buffer,
|
||||
sizeof(buffer),
|
||||
read_timeout);
|
||||
if (read > 0) {
|
||||
s_last_heartbeat_us[ch] = now;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
}
|
||||
|
||||
if (dcdc_get_state(ch) && s_last_heartbeat_us[ch] > 0 &&
|
||||
(now - s_last_heartbeat_us[ch]) > timeout_us) {
|
||||
ESP_LOGW(TAG, "Heartbeat каналу %u втрачено, перезавантаження...", (unsigned)ch);
|
||||
dcdc_disable(ch);
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
dcdc_enable(ch);
|
||||
s_last_heartbeat_us[ch] = esp_timer_get_time();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CONFIG_WATCH_UART_MUX_ENABLED
|
||||
|
||||
esp_err_t uart_mux_init(void)
|
||||
{
|
||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pin_bit_mask = 0,
|
||||
};
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
io_conf.pin_bit_mask = 1ULL << s_select_pins[i];
|
||||
ESP_RETURN_ON_ERROR(gpio_config(&io_conf), TAG, "GPIO config failed");
|
||||
ESP_RETURN_ON_ERROR(gpio_set_level(s_select_pins[i], 0), TAG, "GPIO init level failed");
|
||||
}
|
||||
s_active_channel = 0;
|
||||
|
||||
uart_config_t uart_cfg = {
|
||||
.baud_rate = CONFIG_WATCH_UART_BAUD,
|
||||
.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_RETURN_ON_ERROR(uart_param_config(CONFIG_WATCH_UART_PORT, &uart_cfg), TAG, "UART config failed");
|
||||
ESP_RETURN_ON_ERROR(uart_set_pin(CONFIG_WATCH_UART_PORT,
|
||||
CONFIG_WATCH_UART_TX_GPIO,
|
||||
CONFIG_WATCH_UART_RX_GPIO,
|
||||
UART_PIN_NO_CHANGE,
|
||||
UART_PIN_NO_CHANGE),
|
||||
TAG, "UART pin assign failed");
|
||||
ESP_RETURN_ON_ERROR(uart_driver_install(CONFIG_WATCH_UART_PORT,
|
||||
CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN * 2,
|
||||
CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN * 2,
|
||||
0, NULL, 0),
|
||||
TAG, "UART driver install failed");
|
||||
|
||||
s_mutex = xSemaphoreCreateMutex();
|
||||
ESP_RETURN_ON_FALSE(s_mutex, ESP_ERR_NO_MEM, TAG, "mutex alloc failed");
|
||||
|
||||
int64_t now = esp_timer_get_time();
|
||||
for (size_t ch = 0; ch < UART_MUX_MAX_CHANNELS; ++ch) {
|
||||
s_last_heartbeat_us[ch] = now;
|
||||
}
|
||||
|
||||
if (xTaskCreate(uart_mux_watchdog_task, "uart_mux_wd", 4096, NULL, 5, &s_watchdog_task) != pdPASS) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
s_initialized = true;
|
||||
ESP_LOGI(TAG, "UART мультиплексор активовано, каналів: %d", UART_MUX_MAX_CHANNELS);
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool uart_mux_ready(void)
|
||||
{
|
||||
#if CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return s_initialized;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t uart_mux_channel_count(void)
|
||||
{
|
||||
return CONFIG_WATCH_UART_MUX_CHANNELS;
|
||||
}
|
||||
|
||||
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout)
|
||||
{
|
||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!s_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (channel >= UART_MUX_MAX_CHANNELS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!data || length == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (xSemaphoreTake(s_mutex, timeout) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
esp_err_t err = uart_mux_select_locked(channel);
|
||||
if (err == ESP_OK) {
|
||||
int written = uart_write_bytes(CONFIG_WATCH_UART_PORT, (const char *)data, length);
|
||||
if (written < 0 || (size_t)written != length) {
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout)
|
||||
{
|
||||
#if !CONFIG_WATCH_UART_MUX_ENABLED
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (!s_initialized) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (channel >= UART_MUX_MAX_CHANNELS || !buffer || buffer_size == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (xSemaphoreTake(s_mutex, timeout) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
esp_err_t err = uart_mux_select_locked(channel);
|
||||
if (err == ESP_OK) {
|
||||
int read = uart_read_bytes(CONFIG_WATCH_UART_PORT, buffer, buffer_size, timeout);
|
||||
if (read < 0) {
|
||||
err = ESP_FAIL;
|
||||
} else {
|
||||
if (out_length) {
|
||||
*out_length = (size_t)read;
|
||||
}
|
||||
if (read > 0) {
|
||||
s_last_heartbeat_us[channel] = esp_timer_get_time();
|
||||
}
|
||||
}
|
||||
}
|
||||
xSemaphoreGive(s_mutex);
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
12
main/uart_mux.h
Normal file
12
main/uart_mux.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
esp_err_t uart_mux_init(void);
|
||||
bool uart_mux_ready(void);
|
||||
size_t uart_mux_channel_count(void);
|
||||
esp_err_t uart_mux_write(size_t channel, const uint8_t *data, size_t length, TickType_t timeout);
|
||||
esp_err_t uart_mux_read(size_t channel, uint8_t *buffer, size_t buffer_size, size_t *out_length, TickType_t timeout);
|
||||
@@ -18,6 +18,8 @@
|
||||
#include "tinyusb.h"
|
||||
#include "tusb_cdc_acm.h"
|
||||
#include "ws2812_status.h"
|
||||
#include "ina226_monitor.h"
|
||||
#include "uart_mux.h"
|
||||
|
||||
#define CLI_LINE_MAX_LEN 128
|
||||
#define CLI_QUEUE_LEN 8
|
||||
@@ -25,6 +27,9 @@
|
||||
#ifndef CONFIG_TINYUSB_CDC_RX_BUFSIZE
|
||||
#define CONFIG_TINYUSB_CDC_RX_BUFSIZE 64
|
||||
#endif
|
||||
#ifndef CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN
|
||||
#define CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN 128
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
size_t length;
|
||||
@@ -147,6 +152,84 @@ static void usb_cli_handle_switch(const char *cmd, char *args)
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_print_measurement(size_t channel, const ina226_reading_t *reading)
|
||||
{
|
||||
usb_cli_printf("\r\nCH%u: %.2f В, %.1f мА, %.1f мВт",
|
||||
(unsigned)channel, reading->voltage_v, reading->current_ma, reading->power_mw);
|
||||
}
|
||||
|
||||
static void usb_cli_handle_sense(char *args)
|
||||
{
|
||||
if (!ina226_monitor_ready()) {
|
||||
usb_cli_printf("\r\nМоніторинг INA226 недоступний\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ina226_reading_t reading = {0};
|
||||
if (ina226_monitor_sample(&reading) == ESP_OK) {
|
||||
usb_cli_print_measurement(0, &reading);
|
||||
} else {
|
||||
usb_cli_printf("\r\nНе вдалося зчитати дані з INA226\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_handle_uart(char *args)
|
||||
{
|
||||
if (!uart_mux_ready()) {
|
||||
usb_cli_printf("\r\nUART мультиплексор недоступний\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *ctx = NULL;
|
||||
char *action = strtok_r(args, " ", &ctx);
|
||||
if (!action) {
|
||||
usb_cli_printf("\r\nUART usage: uart send <channel> <text> | uart read <channel> [len]\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char *channel_str = strtok_r(NULL, " ", &ctx);
|
||||
dcdc_channel_t channel;
|
||||
if (!usb_cli_parse_channel(channel_str, &channel)) {
|
||||
usb_cli_printf("\r\nНевірний номер каналу\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcasecmp(action, "send") == 0) {
|
||||
char *payload = ctx;
|
||||
if (!payload || *payload == '\0') {
|
||||
usb_cli_printf("\r\nНемає даних для відправлення\r\n");
|
||||
return;
|
||||
}
|
||||
size_t len = strlen(payload);
|
||||
if (uart_mux_write(channel, (const uint8_t *)payload, len, pdMS_TO_TICKS(200)) == ESP_OK) {
|
||||
usb_cli_printf("\r\nВідправлено %u байт\r\n", (unsigned)len);
|
||||
} else {
|
||||
usb_cli_printf("\r\nПомилка відправлення UART\r\n");
|
||||
}
|
||||
} else if (strcasecmp(action, "read") == 0) {
|
||||
char *len_str = ctx ? strtok_r(NULL, " ", &ctx) : NULL;
|
||||
size_t req_len = len_str ? strtoul(len_str, NULL, 10) : CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN;
|
||||
if (req_len == 0) {
|
||||
req_len = CONFIG_WATCH_UART_MUX_DEFAULT_READ_LEN;
|
||||
}
|
||||
if (req_len > 256) {
|
||||
req_len = 256;
|
||||
}
|
||||
uint8_t buffer[256];
|
||||
size_t received = 0;
|
||||
if (uart_mux_read(channel, buffer, req_len, &received, pdMS_TO_TICKS(200)) == ESP_OK && received > 0) {
|
||||
usb_cli_printf("\r\nCH%u UART (%u байт): ", (unsigned)channel, (unsigned)received);
|
||||
for (size_t i = 0; i < received; ++i) {
|
||||
usb_cli_write_raw((char *)&buffer[i], 1);
|
||||
}
|
||||
} else {
|
||||
usb_cli_printf("\r\nДані відсутні\r\n");
|
||||
}
|
||||
} else {
|
||||
usb_cli_printf("\r\nНевірна дія '%s'\r\n", action);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_cli_process_line(char *line)
|
||||
{
|
||||
char *trimmed = usb_cli_trim(line);
|
||||
@@ -167,13 +250,20 @@ static void usb_cli_process_line(char *line)
|
||||
" status - стан всіх каналів\r\n"
|
||||
" enable <n> - увімкнути канал n\r\n"
|
||||
" disable <n> - вимкнути канал n\r\n"
|
||||
" toggle <n> - перемкнути канал n\r\n");
|
||||
" toggle <n> - перемкнути канал n\r\n"
|
||||
" sense [n] - показати напругу/струм/потужність (опц. канал)\r\n"
|
||||
" uart send <n> <msg> - відправити повідомлення Pi n\r\n"
|
||||
" uart read <n> [len] - прочитати дані з Pi n\r\n");
|
||||
} else if (strcasecmp(cmd, "status") == 0) {
|
||||
usb_cli_print_status();
|
||||
} else if (strcasecmp(cmd, "enable") == 0 ||
|
||||
strcasecmp(cmd, "disable") == 0 ||
|
||||
strcasecmp(cmd, "toggle") == 0) {
|
||||
usb_cli_handle_switch(cmd, save_ptr);
|
||||
} else if (strcasecmp(cmd, "sense") == 0) {
|
||||
usb_cli_handle_sense(save_ptr);
|
||||
} else if (strcasecmp(cmd, "uart") == 0) {
|
||||
usb_cli_handle_uart(save_ptr);
|
||||
} else {
|
||||
usb_cli_printf("\r\nНевідома команда '%s'\r\n", cmd);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user