Update: websocket optimization

- Use protobuf
 - Eliminate unnecessary optimization logic
 - UART, sensor, status data transmitted as pb data

Signed-off-by: YoungSoo Shin <shinys000114@gmail.com>
This commit is contained in:
2025-09-02 11:19:50 +09:00
parent a32f43ec35
commit ab04ff8413
12 changed files with 1197 additions and 256 deletions

View File

@@ -2,10 +2,8 @@
// Created by shinys on 25. 8. 18..
//
#include "monitor.h"
#include <time.h>
#include "cJSON.h"
#include "datalog.h"
#include "esp_log.h"
#include "esp_netif.h"
@@ -13,16 +11,20 @@
#include "esp_wifi_types_generic.h"
#include "freertos/FreeRTOS.h"
#include "ina3221.h"
#include "pb.h"
#include "pb_encode.h"
#include "status.pb.h"
#include "webserver.h"
#include "wifi.h"
#define PM_SDA CONFIG_I2C_GPIO_SDA
#define PM_SCL CONFIG_I2C_GPIO_SCL
const char* channel_names[] = {"USB", "MAIN", "VIN"};
#define PB_BUFFER_SIZE 256
static const char* TAG = "monitor";
ina3221_t ina3221 = {
/* shunt values are 100 mOhm for each channel */
.shunt = {10, 10, 10},
.mask.mask_register = INA3221_DEFAULT_MASK,
.i2c_dev = {0},
@@ -40,69 +42,100 @@ ina3221_t ina3221 = {
},
};
// Timer callback function to read sensor data
static bool encode_string(pb_ostream_t* stream, const pb_field_t* field, void* const* arg)
{
const char* str = (const char*)(*arg);
if (!str)
{
return true; // Nothing to encode
}
if (!pb_encode_tag_for_field(stream, field))
{
return false;
}
return pb_encode_string(stream, (uint8_t*)str, strlen(str));
}
static void send_pb_message(const pb_msgdesc_t* fields, const void* src_struct)
{
uint8_t buffer[PB_BUFFER_SIZE];
pb_ostream_t stream = pb_ostream_from_buffer(buffer, sizeof(buffer));
if (!pb_encode(&stream, fields, src_struct))
{
ESP_LOGE(TAG, "Failed to encode protobuf message: %s", PB_GET_ERROR(&stream));
return;
}
push_data_to_ws(buffer, stream.bytes_written);
}
static void sensor_timer_callback(void* arg)
{
// 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);
channel_data_t channel_data[NUM_CHANNELS];
channel_data_t channel_data_log[NUM_CHANNELS];
StatusMessage message = StatusMessage_init_zero;
message.which_payload = StatusMessage_sensor_data_tag;
SensorData* sensor_data = &message.payload.sensor_data;
sensor_data->has_usb = true;
sensor_data->has_main = true;
sensor_data->has_vin = true;
SensorChannelData* channels[] = {&sensor_data->usb, &sensor_data->main, &sensor_data->vin};
// Create JSON object with sensor data
cJSON* root = cJSON_CreateObject();
for (uint8_t i = 0; i < INA3221_BUS_NUMBER; i++)
{
float voltage, current, power;
ina3221_get_bus_voltage(&ina3221, i, &voltage);
ina3221_get_shunt_value(&ina3221, i, NULL, &current);
current /= 1000.0f; // mA to A
power = voltage * current;
// Populate data for datalog
channel_data[i].voltage = voltage;
channel_data[i].current = current;
channel_data[i].power = power;
// For datalog
channel_data_log[i] = (channel_data_t){.voltage = voltage, .current = current, .power = power};
// Populate data for websocket
cJSON* v = cJSON_AddObjectToObject(root, channel_names[i]);
cJSON_AddNumberToObject(v, "voltage", voltage);
cJSON_AddNumberToObject(v, "current", current);
cJSON_AddNumberToObject(v, "power", power);
// For protobuf
channels[i]->voltage = voltage;
channels[i]->current = current;
channels[i]->power = power;
}
// Add data to log file
datalog_add(timestamp, channel_data);
// datalog_add(timestamp, channel_data_log);
cJSON_AddStringToObject(root, "type", "sensor_data");
cJSON_AddNumberToObject(root, "timestamp", timestamp);
cJSON_AddNumberToObject(root, "uptime_sec", uptime_sec);
sensor_data->timestamp = timestamp;
sensor_data->uptime_sec = uptime_sec;
// Push data to WebSocket clients
push_data_to_ws(root);
send_pb_message(StatusMessage_fields, &message);
}
static void status_wifi_callback(void* arg)
{
wifi_ap_record_t ap_info;
cJSON* root = cJSON_CreateObject();
StatusMessage message = StatusMessage_init_zero;
message.which_payload = StatusMessage_wifi_status_tag;
WifiStatus* wifi_status = &message.payload.wifi_status;
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);
wifi_status->connected = true;
wifi_status->ssid.funcs.encode = &encode_string;
wifi_status->ssid.arg = (void*)ap_info.ssid;
wifi_status->rssi = ap_info.rssi;
}
else
{
cJSON_AddBoolToObject(root, "connected", false);
wifi_status->connected = false;
wifi_status->ssid.arg = ""; // Empty string
wifi_status->rssi = 0;
}
push_data_to_ws(root);
send_pb_message(StatusMessage_fields, &message);
}
static esp_timer_handle_t sensor_timer;
@@ -111,24 +144,15 @@ static esp_timer_handle_t wifi_status_timer;
void init_status_monitor()
{
ESP_ERROR_CHECK(ina3221_init_desc(&ina3221, 0x40, 0, PM_SDA, PM_SCL));
// logger
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
};
const esp_timer_create_args_t sensor_timer_args = {.callback = &sensor_timer_callback,
.name = "sensor_reading_timer"};
const esp_timer_create_args_t wifi_timer_args = {.callback = &status_wifi_callback, .name = "wifi_status_timer"};
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
ESP_ERROR_CHECK(esp_timer_start_periodic(sensor_timer, 1000000));
ESP_ERROR_CHECK(esp_timer_start_periodic(wifi_status_timer, 1000000 * 5));
}