Files
SC-F001/main/uart_comms.c
2026-04-22 18:31:31 -05:00

315 lines
9.2 KiB
C

#include "uart_comms.h"
#include "comms.h"
#include "bringup.h"
#include "cJSON.h"
#include "driver/uart.h"
#include "esp_log.h"
#include "esp_system.h"
#include "esp_task_wdt.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "rtc.h"
#include <stdio.h>
#include <string.h>
#define TAG "UART"
#define UART_NUM UART_NUM_0
#define BUF_SIZE (1024)
#define CMD_MAX_LEN (2048)
static char cmd_buffer[CMD_MAX_LEN];
static int cmd_pos = 0;
static TaskHandle_t uart_task_handle = NULL;
// Process GET command
static void cmd_get(void) {
printf("\n");
cJSON *response = comms_handle_get();
if (response == NULL) {
printf("ERROR: Failed to generate GET response\n");
return;
}
// Pretty print the JSON
char *json_str = cJSON_Print(response);
cJSON_Delete(response);
if (json_str == NULL) {
printf("ERROR: Failed to serialize JSON\n");
return;
}
printf("%s\n", json_str);
free(json_str);
}
// Process POST command with JSON data
static void cmd_post(char *json_data) {
printf("\n");
// Parse the JSON input
cJSON *request = cJSON_Parse(json_data);
if (request == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
printf("ERROR: JSON parse error before: %s\n", error_ptr);
} else {
printf("ERROR: Failed to parse JSON\n");
}
return;
}
// Call the unified POST handler
cJSON *response = NULL;
(void)comms_handle_post(request, &response);
cJSON_Delete(request);
if (response == NULL) {
printf("ERROR: Failed to generate POST response\n");
return;
}
// Pretty print the response
char *json_str = cJSON_Print(response);
// Check for special response flags before deleting
cJSON *reboot_flag = cJSON_GetObjectItem(response, "reboot");
cJSON *sleep_flag = cJSON_GetObjectItem(response, "sleep");
bool should_reboot = cJSON_IsTrue(reboot_flag);
bool should_sleep = cJSON_IsTrue(sleep_flag);
cJSON_Delete(response);
if (json_str == NULL) {
printf("ERROR: Failed to serialize JSON\n");
return;
}
printf("%s\n", json_str);
free(json_str);
// Handle special actions
if (should_reboot) {
printf("\nRebooting in 2 seconds...\n");
fflush(stdout);
vTaskDelay(pdMS_TO_TICKS(2000));
esp_restart();
}
if (should_sleep) {
printf("\nEntering soft idle in 2 seconds...\n");
fflush(stdout);
vTaskDelay(pdMS_TO_TICKS(2000));
soft_idle_enter();
}
}
// Process help command
static void cmd_help(void) {
printf("\n=== UART JSON Interface ===\n");
printf("\nCommands:\n");
printf(" GET - Get complete system status (JSON)\n");
printf(" POST: {json} - Send command or update parameters (JSON)\n");
printf(" RTCDEBUG - Dump RTC timekeeping state (time, backup, sleep entry, clock source)\n");
printf(" HELP - Show this help\n");
printf("\nPOST JSON Format:\n");
printf("{\n");
printf(" \"time\": 1234567, // Set RTC time (optional)\n");
printf(" \"remaining_dist\": 100, // Set remaining distance (optional)\n");
printf(" \"cmd\": \"start\", // Execute command (optional)\n");
printf(" \"parameters\": { // Update parameters (optional)\n");
printf(" \"PARAM_NAME\": value,\n");
printf(" \"PARAM_NAME2\": value\n");
printf(" }\n");
printf("}\n");
printf("\nAvailable Commands:\n");
printf(" start, stop, undo, fwd, rev, up, down, aux\n");
printf(" reboot, sleep\n");
printf(" rf_learn, rf_clear_temp, rf_disable, rf_enable, rf_status\n");
printf(" cal_jack_start, cal_jack_finish, cal_drive_start, cal_drive_finish, cal_get\n");
printf("\nExamples:\n");
printf(" GET\n");
printf(" POST: {\"cmd\":\"start\"}\n");
printf(" POST: {\"time\":1704067200}\n");
printf(" POST: {\"parameters\":{\"WIFI_SSID\":\"MyNetwork\",\"WIFI_CHANNEL\":6}}\n");
printf(" POST: {\"cmd\":\"rf_learn\",\"channel\":0}\n");
printf("\n");
}
// Parse and execute command
static void process_command(char *cmd) {
// Trim leading whitespace
while (*cmd == ' ' || *cmd == '\t') {
cmd++;
}
// Ignore empty commands
if (*cmd == '\0') {
return;
}
// Route bring-up traffic to the BU parser. BU.BEGIN flips the mode flag
// so the subsequent lines in the same session come here too.
if (bringup_mode_is_active() ||
strncasecmp(cmd, "BU.", 3) == 0) {
bringup_handle_line(cmd);
return;
}
// Convert to uppercase for command matching
char command[16] = {0};
int i = 0;
while (cmd[i] && cmd[i] != ' ' && cmd[i] != '\t' && cmd[i] != ':' && i < 15) {
command[i] = (cmd[i] >= 'a' && cmd[i] <= 'z') ? (cmd[i] - 32) : cmd[i];
i++;
}
command[i] = '\0';
// Execute command
if (strcmp(command, "GET") == 0) {
cmd_get();
}
else if (strcmp(command, "POST") == 0) {
// Find the start of JSON data (after ':')
char *json_start = strchr(cmd, ':');
if (json_start == NULL) {
printf("ERROR: POST command requires JSON data after ':'\n");
printf("Usage: POST: {json data}\n");
return;
}
json_start++; // Skip the ':'
// Trim whitespace
while (*json_start == ' ' || *json_start == '\t') {
json_start++;
}
if (*json_start == '\0') {
printf("ERROR: No JSON data provided\n");
return;
}
cmd_post(json_start);
}
else if (strcmp(command, "HELP") == 0) {
cmd_help();
}
else if (strcmp(command, "RTCDEBUG") == 0) {
rtc_print_debug();
}
else {
printf("ERROR: Unknown command '%s'\n", command);
printf("Type 'HELP' for available commands\n");
}
}
// UART event task
void uart_event_task(void *pvParameters) {
esp_task_wdt_add(NULL);
uint8_t data[BUF_SIZE];
while (1) {
esp_task_wdt_reset();
int len = uart_read_bytes(UART_NUM, data, BUF_SIZE - 1, 20 / portTICK_PERIOD_MS);
if (len > 0) {
for (int i = 0; i < len; i++) {
char c = (char)data[i];
bool quiet = bringup_mode_is_active();
// Echo character (suppressed in bring-up mode — machine-parseable output)
if (!quiet) { printf("%c", c); fflush(stdout); }
// Handle backspace
if (c == '\b' || c == 127) {
if (cmd_pos > 0) {
cmd_pos--;
if (!quiet) { printf(" \b"); fflush(stdout); }
}
continue;
}
// Handle newline/carriage return
if (c == '\n' || c == '\r') {
cmd_buffer[cmd_pos] = '\0';
if (!quiet) printf("\n");
if (cmd_pos > 0) {
process_command(cmd_buffer);
}
cmd_pos = 0;
if (!bringup_mode_is_active()) {
printf("\n> ");
fflush(stdout);
}
continue;
}
// Add to buffer if not full
if (cmd_pos < CMD_MAX_LEN - 1) {
cmd_buffer[cmd_pos++] = c;
}
}
}
}
}
esp_err_t uart_init(void) {
// Configure UART
uart_config_t uart_config = {
.baud_rate = 115200,
.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_DEFAULT,
};
ESP_ERROR_CHECK(uart_driver_install(UART_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(UART_NUM, &uart_config));
// Print startup message
printf("\n\n");
printf("========================================\n");
printf(" UART JSON Interface Ready\n");
printf("========================================\n");
printf("Type 'HELP' for available commands\n");
printf("Type 'GET' to see system status\n");
printf("\n> ");
fflush(stdout);
// Create UART task
xTaskCreate(uart_event_task, "uart_event_task", 4096, NULL, 12, &uart_task_handle);
ESP_LOGI(TAG, "UART interface started");
return ESP_OK;
}
esp_err_t uart_stop(void) {
if (uart_task_handle == NULL) {
ESP_LOGW(TAG, "UART task not running");
return ESP_OK;
}
ESP_LOGI(TAG, "Shutting down UART...");
// Wait for UART TX to finish
uart_wait_tx_done(UART_NUM, pdMS_TO_TICKS(100));
// Delete the UART task
vTaskDelete(uart_task_handle);
uart_task_handle = NULL;
// Small delay to ensure task deletion completes
vTaskDelay(pdMS_TO_TICKS(10));
// Disable UART driver
uart_driver_delete(UART_NUM);
ESP_LOGI(TAG, "UART shutdown complete");
return ESP_OK;
}