Add WebSocket support for switch status updates

- Define `LoadSwStatus` protobuf message for switch state data (`main` and `usb`).
- Introduce `updateSwitchStatusUI` to synchronize UI with WebSocket switch status payloads.
- Update backend logic to manage and broadcast switch status changes dynamically.

Signed-off-by: YoungSoo Shin <shinys000114@gmail.com>
This commit is contained in:
2025-09-08 16:30:40 +09:00
parent c2a5116cd2
commit 3902740a25
6 changed files with 167 additions and 60 deletions

View File

@@ -18,7 +18,7 @@ PB_BIND(WifiStatus, WifiStatus, AUTO)
PB_BIND(UartData, UartData, AUTO) PB_BIND(UartData, UartData, AUTO)
PB_BIND(LoadSwStatus, LoadSwStatus, AUTO)
PB_BIND(StatusMessage, StatusMessage, AUTO) PB_BIND(StatusMessage, StatusMessage, AUTO)

View File

@@ -11,14 +11,16 @@
/* Struct definitions */ /* Struct definitions */
/* Represents data for a single sensor channel */ /* Represents data for a single sensor channel */
typedef struct _SensorChannelData { typedef struct _SensorChannelData
{
float voltage; float voltage;
float current; float current;
float power; float power;
} SensorChannelData; } SensorChannelData;
/* Contains data for all sensor channels and system info */ /* Contains data for all sensor channels and system info */
typedef struct _SensorData { typedef struct _SensorData
{
bool has_usb; bool has_usb;
SensorChannelData usb; SensorChannelData usb;
bool has_main; bool has_main;
@@ -30,23 +32,35 @@ typedef struct _SensorData {
} SensorData; } SensorData;
/* Contains WiFi connection status */ /* Contains WiFi connection status */
typedef struct _WifiStatus { typedef struct _WifiStatus
{
bool connected; bool connected;
pb_callback_t ssid; pb_callback_t ssid;
int32_t rssi; int32_t rssi;
} WifiStatus; } WifiStatus;
/* Contains raw UART data */ /* Contains raw UART data */
typedef struct _UartData { typedef struct _UartData
{
pb_callback_t data; pb_callback_t data;
} UartData; } UartData;
/* Contains load sw status */
typedef struct _LoadSwStatus
{
bool main;
bool usb;
} LoadSwStatus;
/* Top-level message for all websocket communication */ /* Top-level message for all websocket communication */
typedef struct _StatusMessage { typedef struct _StatusMessage
{
pb_size_t which_payload; pb_size_t which_payload;
union { union
{
SensorData sensor_data; SensorData sensor_data;
WifiStatus wifi_status; WifiStatus wifi_status;
LoadSwStatus sw_status;
UartData uart_data; UartData uart_data;
} payload; } payload;
} StatusMessage; } StatusMessage;
@@ -57,80 +71,112 @@ extern "C" {
#endif #endif
/* Initializer values for message structs */ /* Initializer values for message structs */
#define SensorChannelData_init_default {0, 0, 0} #define SensorChannelData_init_default {0, 0, 0}
#define SensorData_init_default {false, SensorChannelData_init_default, false, SensorChannelData_init_default, false, SensorChannelData_init_default, 0, 0} #define SensorData_init_default \
#define WifiStatus_init_default {0, {{NULL}, NULL}, 0} {false, SensorChannelData_init_default, false, SensorChannelData_init_default, \
#define UartData_init_default {{{NULL}, NULL}} false, SensorChannelData_init_default, 0, 0}
#define StatusMessage_init_default {0, {SensorData_init_default}} #define WifiStatus_init_default {0, {{NULL}, NULL}, 0}
#define SensorChannelData_init_zero {0, 0, 0} #define UartData_init_default \
#define SensorData_init_zero {false, SensorChannelData_init_zero, false, SensorChannelData_init_zero, false, SensorChannelData_init_zero, 0, 0} { \
#define WifiStatus_init_zero {0, {{NULL}, NULL}, 0} { \
#define UartData_init_zero {{{NULL}, NULL}} {NULL}, NULL \
#define StatusMessage_init_zero {0, {SensorData_init_zero}} } \
}
#define LoadSwStatus_init_default {0, 0}
#define StatusMessage_init_default \
{ \
0, { SensorData_init_default } \
}
#define SensorChannelData_init_zero {0, 0, 0}
#define SensorData_init_zero \
{false, SensorChannelData_init_zero, false, SensorChannelData_init_zero, false, SensorChannelData_init_zero, 0, 0}
#define WifiStatus_init_zero {0, {{NULL}, NULL}, 0}
#define UartData_init_zero \
{ \
{ \
{NULL}, NULL \
} \
}
#define LoadSwStatus_init_zero {0, 0}
#define StatusMessage_init_zero \
{ \
0, { SensorData_init_zero } \
}
/* Field tags (for use in manual encoding/decoding) */ /* Field tags (for use in manual encoding/decoding) */
#define SensorChannelData_voltage_tag 1 #define SensorChannelData_voltage_tag 1
#define SensorChannelData_current_tag 2 #define SensorChannelData_current_tag 2
#define SensorChannelData_power_tag 3 #define SensorChannelData_power_tag 3
#define SensorData_usb_tag 1 #define SensorData_usb_tag 1
#define SensorData_main_tag 2 #define SensorData_main_tag 2
#define SensorData_vin_tag 3 #define SensorData_vin_tag 3
#define SensorData_timestamp_tag 4 #define SensorData_timestamp_tag 4
#define SensorData_uptime_sec_tag 5 #define SensorData_uptime_sec_tag 5
#define WifiStatus_connected_tag 1 #define WifiStatus_connected_tag 1
#define WifiStatus_ssid_tag 2 #define WifiStatus_ssid_tag 2
#define WifiStatus_rssi_tag 3 #define WifiStatus_rssi_tag 3
#define UartData_data_tag 1 #define UartData_data_tag 1
#define StatusMessage_sensor_data_tag 1 #define LoadSwStatus_main_tag 1
#define StatusMessage_wifi_status_tag 2 #define LoadSwStatus_usb_tag 2
#define StatusMessage_uart_data_tag 3 #define StatusMessage_sensor_data_tag 1
#define StatusMessage_wifi_status_tag 2
#define StatusMessage_sw_status_tag 3
#define StatusMessage_uart_data_tag 4
/* Struct field encoding specification for nanopb */ /* Struct field encoding specification for nanopb */
#define SensorChannelData_FIELDLIST(X, a) \ #define SensorChannelData_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, FLOAT, voltage, 1) \ X(a, STATIC, SINGULAR, FLOAT, voltage, 1) \
X(a, STATIC, SINGULAR, FLOAT, current, 2) \ X(a, STATIC, SINGULAR, FLOAT, current, 2) \
X(a, STATIC, SINGULAR, FLOAT, power, 3) X(a, STATIC, SINGULAR, FLOAT, power, 3)
#define SensorChannelData_CALLBACK NULL #define SensorChannelData_CALLBACK NULL
#define SensorChannelData_DEFAULT NULL #define SensorChannelData_DEFAULT NULL
#define SensorData_FIELDLIST(X, a) \ #define SensorData_FIELDLIST(X, a) \
X(a, STATIC, OPTIONAL, MESSAGE, usb, 1) \ X(a, STATIC, OPTIONAL, MESSAGE, usb, 1) \
X(a, STATIC, OPTIONAL, MESSAGE, main, 2) \ X(a, STATIC, OPTIONAL, MESSAGE, main, 2) \
X(a, STATIC, OPTIONAL, MESSAGE, vin, 3) \ X(a, STATIC, OPTIONAL, MESSAGE, vin, 3) \
X(a, STATIC, SINGULAR, UINT32, timestamp, 4) \ X(a, STATIC, SINGULAR, UINT32, timestamp, 4) \
X(a, STATIC, SINGULAR, UINT32, uptime_sec, 5) X(a, STATIC, SINGULAR, UINT32, uptime_sec, 5)
#define SensorData_CALLBACK NULL #define SensorData_CALLBACK NULL
#define SensorData_DEFAULT NULL #define SensorData_DEFAULT NULL
#define SensorData_usb_MSGTYPE SensorChannelData #define SensorData_usb_MSGTYPE SensorChannelData
#define SensorData_main_MSGTYPE SensorChannelData #define SensorData_main_MSGTYPE SensorChannelData
#define SensorData_vin_MSGTYPE SensorChannelData #define SensorData_vin_MSGTYPE SensorChannelData
#define WifiStatus_FIELDLIST(X, a) \ #define WifiStatus_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, connected, 1) \ X(a, STATIC, SINGULAR, BOOL, connected, 1) \
X(a, CALLBACK, SINGULAR, STRING, ssid, 2) \ X(a, CALLBACK, SINGULAR, STRING, ssid, 2) \
X(a, STATIC, SINGULAR, INT32, rssi, 3) X(a, STATIC, SINGULAR, INT32, rssi, 3)
#define WifiStatus_CALLBACK pb_default_field_callback #define WifiStatus_CALLBACK pb_default_field_callback
#define WifiStatus_DEFAULT NULL #define WifiStatus_DEFAULT NULL
#define UartData_FIELDLIST(X, a) \ #define UartData_FIELDLIST(X, a) X(a, CALLBACK, SINGULAR, BYTES, data, 1)
X(a, CALLBACK, SINGULAR, BYTES, data, 1)
#define UartData_CALLBACK pb_default_field_callback #define UartData_CALLBACK pb_default_field_callback
#define UartData_DEFAULT NULL #define UartData_DEFAULT NULL
#define StatusMessage_FIELDLIST(X, a) \ #define LoadSwStatus_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload,sensor_data,payload.sensor_data), 1) \ X(a, STATIC, SINGULAR, BOOL, main, 1) \
X(a, STATIC, ONEOF, MESSAGE, (payload,wifi_status,payload.wifi_status), 2) \ X(a, STATIC, SINGULAR, BOOL, usb, 2)
X(a, STATIC, ONEOF, MESSAGE, (payload,uart_data,payload.uart_data), 3) #define LoadSwStatus_CALLBACK NULL
#define LoadSwStatus_DEFAULT NULL
#define StatusMessage_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload, sensor_data, payload.sensor_data), 1) \
X(a, STATIC, ONEOF, MESSAGE, (payload, wifi_status, payload.wifi_status), 2) \
X(a, STATIC, ONEOF, MESSAGE, (payload, sw_status, payload.sw_status), 3) \
X(a, STATIC, ONEOF, MESSAGE, (payload, uart_data, payload.uart_data), 4)
#define StatusMessage_CALLBACK NULL #define StatusMessage_CALLBACK NULL
#define StatusMessage_DEFAULT NULL #define StatusMessage_DEFAULT NULL
#define StatusMessage_payload_sensor_data_MSGTYPE SensorData #define StatusMessage_payload_sensor_data_MSGTYPE SensorData
#define StatusMessage_payload_wifi_status_MSGTYPE WifiStatus #define StatusMessage_payload_wifi_status_MSGTYPE WifiStatus
#define StatusMessage_payload_sw_status_MSGTYPE LoadSwStatus
#define StatusMessage_payload_uart_data_MSGTYPE UartData #define StatusMessage_payload_uart_data_MSGTYPE UartData
extern const pb_msgdesc_t SensorChannelData_msg; extern const pb_msgdesc_t SensorChannelData_msg;
extern const pb_msgdesc_t SensorData_msg; extern const pb_msgdesc_t SensorData_msg;
extern const pb_msgdesc_t WifiStatus_msg; extern const pb_msgdesc_t WifiStatus_msg;
extern const pb_msgdesc_t UartData_msg; extern const pb_msgdesc_t UartData_msg;
extern const pb_msgdesc_t LoadSwStatus_msg;
extern const pb_msgdesc_t StatusMessage_msg; extern const pb_msgdesc_t StatusMessage_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ /* Defines for backwards compatibility with code written before nanopb-0.4.0 */
@@ -138,15 +184,17 @@ extern const pb_msgdesc_t StatusMessage_msg;
#define SensorData_fields &SensorData_msg #define SensorData_fields &SensorData_msg
#define WifiStatus_fields &WifiStatus_msg #define WifiStatus_fields &WifiStatus_msg
#define UartData_fields &UartData_msg #define UartData_fields &UartData_msg
#define LoadSwStatus_fields &LoadSwStatus_msg
#define StatusMessage_fields &StatusMessage_msg #define StatusMessage_fields &StatusMessage_msg
/* Maximum encoded size of messages (where known) */ /* Maximum encoded size of messages (where known) */
/* WifiStatus_size depends on runtime parameters */ /* WifiStatus_size depends on runtime parameters */
/* UartData_size depends on runtime parameters */ /* UartData_size depends on runtime parameters */
/* StatusMessage_size depends on runtime parameters */ /* StatusMessage_size depends on runtime parameters */
#define STATUS_PB_H_MAX_SIZE SensorData_size #define LoadSwStatus_size 4
#define SensorChannelData_size 15 #define STATUS_PB_H_MAX_SIZE SensorData_size
#define SensorData_size 63 #define SensorChannelData_size 15
#define SensorData_size 63
#ifdef __cplusplus #ifdef __cplusplus
} /* extern "C" */ } /* extern "C" */

View File

@@ -14,7 +14,11 @@
#include "driver/gpio.h" #include "driver/gpio.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "pb.h"
#include "pb_encode.h"
#include "pca9557.h" #include "pca9557.h"
#include "status.pb.h"
#include "webserver.h"
#define I2C_PORT 0 #define I2C_PORT 0
@@ -28,6 +32,8 @@
#define POWER_DELAY (CONFIG_TRIGGER_POWER_DELAY_MS * 1000) #define POWER_DELAY (CONFIG_TRIGGER_POWER_DELAY_MS * 1000)
#define RESET_DELAY (CONFIG_TRIGGER_RESET_DELAY_MS * 1000) #define RESET_DELAY (CONFIG_TRIGGER_RESET_DELAY_MS * 1000)
#define PB_BUFFER_SIZE 256
static const char* TAG = "control"; static const char* TAG = "control";
static bool load_switch_12v_status = false; static bool load_switch_12v_status = false;
@@ -41,6 +47,28 @@ static i2c_dev_t pca = {0};
static esp_timer_handle_t power_trigger_timer; static esp_timer_handle_t power_trigger_timer;
static esp_timer_handle_t reset_trigger_timer; static esp_timer_handle_t reset_trigger_timer;
static void send_sw_status_message()
{
StatusMessage message = StatusMessage_init_zero;
message.which_payload = StatusMessage_sw_status_tag;
LoadSwStatus* sw_status = &message.payload.sw_status;
sw_status->main = load_switch_12v_status;
sw_status->usb = load_switch_5v_status;
uint8_t buffer[PB_BUFFER_SIZE];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&stream, StatusMessage_fields, &message))
{
ESP_LOGE(TAG, "Failed to encode protobuf message: %s", PB_GET_ERROR(&stream));
return;
}
push_data_to_ws(buffer, stream.bytes_written);
}
static void trigger_off_callback(void* arg) static void trigger_off_callback(void* arg)
{ {
if (xSemaphoreTake(expander_mutex, MUTEX_TIMEOUT) == pdFALSE) if (xSemaphoreTake(expander_mutex, MUTEX_TIMEOUT) == pdFALSE)
@@ -69,6 +97,8 @@ void config_sw()
load_switch_12v_status = val != 0 ? true : false; load_switch_12v_status = val != 0 ? true : false;
ESP_ERROR_CHECK(pca9557_get_level(&pca, CONFIG_EXPANDER_GPIO_SW_5V, &val)); ESP_ERROR_CHECK(pca9557_get_level(&pca, CONFIG_EXPANDER_GPIO_SW_5V, &val));
load_switch_5v_status = val != 0 ? true : false; load_switch_5v_status = val != 0 ? true : false;
send_sw_status_message();
} }
void init_sw() void init_sw()
@@ -127,9 +157,9 @@ void set_main_load_switch(bool on)
return; return;
} }
pca9557_set_level(&pca, GPIO_MAIN, on); pca9557_set_level(&pca, GPIO_MAIN, on);
xSemaphoreGive(expander_mutex);
load_switch_12v_status = on; load_switch_12v_status = on;
xSemaphoreGive(expander_mutex); xSemaphoreGive(expander_mutex);
send_sw_status_message();
} }
void set_usb_load_switch(bool on) void set_usb_load_switch(bool on)
@@ -143,9 +173,9 @@ void set_usb_load_switch(bool on)
return; return;
} }
pca9557_set_level(&pca, GPIO_USB, on); pca9557_set_level(&pca, GPIO_USB, on);
xSemaphoreGive(expander_mutex);
load_switch_5v_status = on; load_switch_5v_status = on;
xSemaphoreGive(expander_mutex); xSemaphoreGive(expander_mutex);
send_sw_status_message();
} }
bool get_main_load_switch() { return load_switch_12v_status; } bool get_main_load_switch() { return load_switch_12v_status; }

View File

@@ -19,6 +19,7 @@ import {
initUI, initUI,
updateControlStatus, updateControlStatus,
updateSensorUI, updateSensorUI,
updateSwitchStatusUI,
updateUptimeUI, updateUptimeUI,
updateWebsocketStatus, updateWebsocketStatus,
updateWifiStatusUI updateWifiStatusUI
@@ -85,6 +86,12 @@ function onWsMessage(event) {
updateWifiStatusUI(decodedMessage.wifiStatus); updateWifiStatusUI(decodedMessage.wifiStatus);
break; break;
case 'swStatus':
if (decodedMessage.swStatus) {
updateSwitchStatusUI(decodedMessage.swStatus);
}
break;
case 'uartData': case 'uartData':
if (term && decodedMessage.uartData && decodedMessage.uartData.data) { if (term && decodedMessage.uartData && decodedMessage.uartData.data) {
term.write(decodedMessage.uartData.data); term.write(decodedMessage.uartData.data);

View File

@@ -61,6 +61,21 @@ export function updateUptimeUI(uptimeInSeconds) {
} }
} }
/**
* Updates the power switch toggle states based on WebSocket data.
* @param {Object} swStatus - The switch status object from the WebSocket message.
*/
export function updateSwitchStatusUI(swStatus) {
if (swStatus) {
if (swStatus.main !== undefined) {
dom.mainPowerToggle.checked = swStatus.main;
}
if (swStatus.usb !== undefined) {
dom.usbPowerToggle.checked = swStatus.usb;
}
}
}
/** /**
* Updates the Wi-Fi status indicator in the header. * Updates the Wi-Fi status indicator in the header.
* @param {Object} data - The Wi-Fi status object from the WebSocket. * @param {Object} data - The Wi-Fi status object from the WebSocket.

View File

@@ -28,11 +28,18 @@ message UartData {
bytes data = 1; bytes data = 1;
} }
// Contains load sw status
message LoadSwStatus {
bool main = 1;
bool usb = 2;
}
// Top-level message for all websocket communication // Top-level message for all websocket communication
message StatusMessage { message StatusMessage {
oneof payload { oneof payload {
SensorData sensor_data = 1; SensorData sensor_data = 1;
WifiStatus wifi_status = 2; WifiStatus wifi_status = 2;
UartData uart_data = 3; LoadSwStatus sw_status = 3;
UartData uart_data = 4;
} }
} }