/* * face_uart.c — UART Command Interface for Face Animations * * Receives emotion commands from Jetson Orin and triggers face animations. * Text-based protocol over USART3 @ 115200 baud. */ #include "face_uart.h" #include "face_animation.h" #include #include #include /* === Ring Buffer State === */ static struct { uint8_t buf[FACE_UART_RX_BUF_SZ]; uint16_t head; /* Write index (ISR) */ uint16_t tail; /* Read index (process) */ uint16_t count; /* Bytes in buffer */ } rx_buf = {0}; /* === Forward Declarations === */ static void uart_send_response(const char *cmd, const char *status); /* === Private Functions === */ /** * Extract a line from RX buffer (newline-terminated). * Returns length if found, 0 otherwise. */ static uint16_t extract_line(char *line, uint16_t max_len) { uint16_t len = 0; uint16_t idx = rx_buf.tail; /* Scan for newline */ while (idx != rx_buf.head && len < max_len - 1) { uint8_t byte = rx_buf.buf[idx]; if (byte == '\n') { /* Found end of line */ for (uint16_t i = 0; i < len; i++) { line[i] = rx_buf.buf[(rx_buf.tail + i) % FACE_UART_RX_BUF_SZ]; } line[len] = '\0'; /* Trim trailing whitespace */ while (len > 0 && (line[len - 1] == '\r' || isspace(line[len - 1]))) { line[--len] = '\0'; } /* Update tail and count */ rx_buf.tail = (idx + 1) % FACE_UART_RX_BUF_SZ; rx_buf.count -= (len + 1 + 1); /* +1 for newline, +1 for any preceding data */ return len; } len++; idx = (idx + 1) % FACE_UART_RX_BUF_SZ; } return 0; /* No complete line */ } /** * Convert string to uppercase for case-insensitive command matching. */ static void str_toupper(char *str) { for (int i = 0; str[i]; i++) str[i] = toupper((unsigned char)str[i]); } /** * Parse and execute a command. */ static void parse_command(const char *cmd) { if (!cmd || !cmd[0]) return; char cmd_upper[32]; strncpy(cmd_upper, cmd, sizeof(cmd_upper) - 1); cmd_upper[sizeof(cmd_upper) - 1] = '\0'; str_toupper(cmd_upper); /* Command dispatch */ if (strcmp(cmd_upper, "HAPPY") == 0) { face_animation_set_emotion(FACE_HAPPY); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "SAD") == 0) { face_animation_set_emotion(FACE_SAD); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "CURIOUS") == 0) { face_animation_set_emotion(FACE_CURIOUS); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "ANGRY") == 0) { face_animation_set_emotion(FACE_ANGRY); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "SLEEP") == 0 || strcmp(cmd_upper, "SLEEPING") == 0) { face_animation_set_emotion(FACE_SLEEPING); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "NEUTRAL") == 0) { face_animation_set_emotion(FACE_NEUTRAL); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "BLINK") == 0) { face_animation_blink_now(); uart_send_response(cmd_upper, "OK"); } else if (strcmp(cmd_upper, "STATUS") == 0) { const char *emotion_names[] = {"HAPPY", "SAD", "CURIOUS", "ANGRY", "SLEEPING", "NEUTRAL"}; face_emotion_t current = face_animation_get_emotion(); char status[64]; snprintf(status, sizeof(status), "EMOTION=%s, IDLE=%s", emotion_names[current], face_animation_is_idle() ? "true" : "false"); uart_send_response(cmd_upper, status); } else { uart_send_response(cmd_upper, "ERR: unknown command"); } } /** * Send a response string to UART TX. * Format: "CMD: status\n" */ static void uart_send_response(const char *cmd, const char *status) { /* TODO: Implement UART TX * Use HAL_UART_Transmit_IT or similar to send: * "CMD: status\n" */ (void)cmd; /* Suppress unused warnings */ (void)status; } /* === Public API Implementation === */ void face_uart_init(void) { /* TODO: Configure USART3 @ 115200 baud * - Enable USART3 clock (RCC_APB1ENR) * - Configure pins (PB10=TX, PB11=RX) * - Set baud rate to 115200 * - Enable RX interrupt (NVIC + USART3_IRQn) * - Enable USART */ rx_buf.head = 0; rx_buf.tail = 0; rx_buf.count = 0; } void face_uart_process(void) { char line[128]; uint16_t len; /* Extract and process complete commands */ while ((len = extract_line(line, sizeof(line))) > 0) { parse_command(line); } } void face_uart_rx_isr(uint8_t byte) { /* Push byte into ring buffer */ if (rx_buf.count < FACE_UART_RX_BUF_SZ) { rx_buf.buf[rx_buf.head] = byte; rx_buf.head = (rx_buf.head + 1) % FACE_UART_RX_BUF_SZ; rx_buf.count++; } /* Buffer overflow: silently discard oldest byte */ } void face_uart_send(const char *str) { /* TODO: Implement non-blocking UART TX * Use HAL_UART_Transmit_IT() or DMA-based TX queue. */ (void)str; /* Suppress unused warnings */ }