646 lines
24 KiB
C++
646 lines
24 KiB
C++
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: IO.c
|
|
// Created: Sep 2024
|
|
// Version: v1.0
|
|
// Author(s): Philip Smart
|
|
// Description: This source file contains wrappers and extensions to standard ESP32 services and
|
|
// hardware, specifically stdin, stdout etc. It is a CPP file albeit its contents are C,
|
|
// this is due to an issue with IDF whene it throws assertions when including FreeRTOS.h
|
|
// when compiling with gcc rather than g++.
|
|
// IDF has quite a few issues when using UART0, hence wrappers and work arounds.
|
|
// Credits:
|
|
// Copyright: (c) 2026 Philip Smart <philip.smart@net2net.org>
|
|
//
|
|
// History: Sep 2024 - Initial write based on logic from the tzpuPico project.
|
|
//
|
|
// Notes: See Makefile to enable/disable conditional components
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This source file is free software: you can redistribute it and#or modify
|
|
// it under the terms of the GNU General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This source file is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include "esp_attr.h" // RTC_NOINIT_ATTR
|
|
#include "freertos/FreeRTOS.h"
|
|
#include "freertos/queue.h"
|
|
#include "freertos/task.h"
|
|
#include "esp_log.h"
|
|
#include "esp_app_format.h"
|
|
#include "esp_ota_ops.h"
|
|
#include "esp_system.h"
|
|
#include "esp_efuse.h"
|
|
#include "hal/gpio_hal.h"
|
|
#include "esp_efuse_table.h"
|
|
#include "esp_efuse_custom_table.h"
|
|
#include "nvs_flash.h"
|
|
#include "nvs.h"
|
|
#include "driver/gpio.h"
|
|
#include "driver/uart.h"
|
|
#include "soc/soc.h"
|
|
#include "soc/rtc_cntl_reg.h"
|
|
#include "sdkconfig.h"
|
|
#include "esp_vfs_fat.h"
|
|
#include "esp_vfs.h"
|
|
#include "sdmmc_cmd.h"
|
|
#include "driver/sdmmc_host.h"
|
|
#include "IO.h"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C"
|
|
{
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// Important:
|
|
//
|
|
// All configuration is performed via the 'idf.py menuconfig' command.
|
|
// The file 'sdkconfig' contains the configured parameter defines.
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
// RTC NOINIT memory — survives software resets.
|
|
// Set to OOB_RESTART_MAGIC before esp_restart() in the OOB handler so
|
|
// CommandProcessor can detect an OOB-triggered restart and use the fast
|
|
// 100 ms startup delay instead of the 3500 ms SPI-crash-recovery delay.
|
|
// On a power-on reset this will contain garbage — the magic value guards
|
|
// against the (vanishingly unlikely) case that garbage matches the constant.
|
|
#define OOB_RESTART_MAGIC 0xAA55CC33u
|
|
RTC_NOINIT_ATTR uint32_t g_oob_restart_magic;
|
|
|
|
// Flag set by OOB handler to tell CommandProcessor to reinitialize the SPI slave.
|
|
// The RP2350 sends OOB RESET_INPUT via UART after every reset, then sends a SPI
|
|
// NOP to unblock the ESP32 from spi_slave_transmit. The CommandProcessor checks
|
|
// this flag after each transaction and reinitializes if set.
|
|
volatile bool g_spi_clear_requested = false;
|
|
|
|
// Module globals.
|
|
static t_cmdFrame cmdFrame;
|
|
static t_Logger logger;
|
|
static char ioBuf[MAX_OOB_MSG_SIZE]; // The ioBuf is used to construct responses or commands to be sent to the rp2350.
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
static SemaphoreHandle_t logMutex;
|
|
|
|
// VFS operations structure
|
|
static esp_vfs_t customVFS = {
|
|
.flags = ESP_VFS_FLAG_DEFAULT,
|
|
.write = IO_rp2350CustomWrite,
|
|
.read = NULL,
|
|
.open = NULL,
|
|
.close = NULL,
|
|
.fstat = NULL,
|
|
// Add more NULLs to silence -Wmissing-field-initializers if needed
|
|
};
|
|
#endif
|
|
|
|
// UART 0 Configuration if running in NORMAL mode, ie. debug goes to USB.
|
|
#if defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
uart_config_t uartConfig = {
|
|
.baud_rate = 115200 * 4,
|
|
.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,
|
|
};
|
|
#endif
|
|
|
|
// Getter to return address of temporary storage for forming Response or Command messages.
|
|
char *IO_getIoBuf(void)
|
|
{
|
|
return (ioBuf);
|
|
}
|
|
|
|
// Custom VFS write handler (no void* ctx)
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
ssize_t IO_rp2350CustomWrite(int fd, const void *data, size_t size)
|
|
{
|
|
if (fd == STDOUT_FILENO)
|
|
{
|
|
// Process the data
|
|
for (size_t i = 0; i < size; i++)
|
|
{
|
|
// Use VFS to write to the UART.
|
|
// esp_vfs_write(_REENT, STDOUT_FILENO, &((const char *)data)[i], 1);
|
|
putchar((int) ((const char *) data)[i]);
|
|
}
|
|
return size;
|
|
}
|
|
return -1; // Unsupported fd
|
|
}
|
|
#endif
|
|
|
|
// Direct output handler, put string to stdout (uart0) no formatting, framing etc.
|
|
ssize_t IO_rp2350WriteString(const void *data, size_t size)
|
|
{
|
|
// Process the data
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
for (size_t i = 0; i < size; i++)
|
|
{
|
|
putchar((int) ((const char *) data)[i]);
|
|
}
|
|
#endif
|
|
#if defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
uart_write_bytes(UART_NUM_0, (const char *) data, size);
|
|
//ESP_LOGI(IOTAG, "Written %dbytes to UART0", size);
|
|
#endif
|
|
return size;
|
|
}
|
|
|
|
// Method to handle log output data enroute to the UART. As the ESP32 is behind an RP2350 which itself uses USB
|
|
// to create virtual COM ports, we frame the log data which in turn is detected, processed by the rp2350 and then routed
|
|
// to a virtual COM port.
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
int IO_rp2350WriteLog(const char *fmt, va_list args)
|
|
{
|
|
// Locals.
|
|
static bool inFrame = false;
|
|
int retCount = 0;
|
|
|
|
// If not configured, exit.
|
|
if (logger.logBuffer == NULL)
|
|
return 0;
|
|
|
|
// Guard with a mutex as multiple threads can write to log at same time.
|
|
if (xSemaphoreTake(logMutex, portMAX_DELAY) == pdTRUE)
|
|
{
|
|
// In frame mode, put an STX and ETX around message, allows rp2350 to detect and process as needed.
|
|
if (logger.logMode == LOGGING_FRAMED || logger.logMode == LOGGING_FRAMED_VIEW || logger.logMode == LOGGING_FILE)
|
|
{
|
|
// If not inside a buffer frame, then add STX to start of frame and subsequent calls, until EOL are tagged onto end of buffer.
|
|
if (inFrame == false)
|
|
{
|
|
logger.logBuffer[logger.logPos++] = (logger.logMode == LOGGING_FRAMED || logger.logMode == LOGGING_FILE ? LOG_STX : 0x5b);
|
|
inFrame = true;
|
|
}
|
|
|
|
// Add new string to end of buffer then scan for EOL. If EOL found, add ETX and send.
|
|
retCount = vsnprintf(logger.logLine, MAX_LOG_LINE_SIZE, fmt, args);
|
|
for (int idx = 0; idx < retCount; idx++)
|
|
{
|
|
// Filter out CR's.
|
|
if (logger.logLine[idx] == 0x0d)
|
|
{
|
|
// End of buffer and a new frame open, close.
|
|
if (logger.logPos == 1 && idx == retCount - 1)
|
|
{
|
|
logger.logPos = 0;
|
|
inFrame = false;
|
|
}
|
|
}
|
|
else
|
|
|
|
// If the EOL terminator is found, then complete the frame and start a new one.
|
|
if (logger.logLine[idx] == 0x0a || logger.logPos == (MAX_LOG_BUFFER_SIZE - 3))
|
|
{
|
|
if (logger.logMode == LOGGING_FILE && logger.logFile != NULL)
|
|
{
|
|
// Write without frame characters.
|
|
fwrite(&logger.logBuffer[1], 1, (logger.logPos - 1), logger.logFile);
|
|
}
|
|
|
|
// Add ETX.
|
|
logger.logBuffer[logger.logPos++] = (logger.logMode == LOGGING_FRAMED || logger.logMode == LOGGING_FILE ? LOG_ETX : 0x5d);
|
|
|
|
// In framed view mode, terminate EOL.
|
|
if (logger.logMode == LOGGING_FRAMED_VIEW)
|
|
{
|
|
logger.logBuffer[logger.logPos] = 0x0a;
|
|
logger.logBuffer[logger.logPos + 1] = 0x0d;
|
|
logger.logPos = idx + 2;
|
|
}
|
|
|
|
// Send to stdout (hits my_vfs_write via /dev/uart/0)
|
|
fwrite(logger.logBuffer, 1, logger.logPos, stdout);
|
|
|
|
logger.logPos = 0;
|
|
// End of buffer, then close frame else start new frame.
|
|
if (idx == retCount - 1)
|
|
{
|
|
inFrame = false;
|
|
}
|
|
else
|
|
{
|
|
logger.logBuffer[logger.logPos++] = (logger.logMode == LOGGING_FRAMED || logger.logMode == LOGGING_FILE ? LOG_STX : 0x5b);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
logger.logBuffer[logger.logPos++] = logger.logLine[idx];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
// Normal mode, just apply original logic.
|
|
if (logger.logMode == LOGGING_NORMAL)
|
|
{
|
|
// Write directly to stdout.
|
|
retCount = vprintf(fmt, args);
|
|
|
|
// If logmode has changed then ensure buffer is reset.
|
|
if (logger.logPos > 0)
|
|
{
|
|
logger.logPos = 0;
|
|
inFrame = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If logmode has changed then ensure buffer is reset.
|
|
if (logger.logPos > 0)
|
|
{
|
|
logger.logPos = 0;
|
|
inFrame = false;
|
|
}
|
|
}
|
|
|
|
// Release semaphore.
|
|
xSemaphoreGive(logMutex);
|
|
}
|
|
|
|
return retCount;
|
|
}
|
|
#endif
|
|
|
|
// Task to read stdin and push to queue
|
|
void IO_stdinReaderTask(void *pvParameters)
|
|
{
|
|
// Locals.
|
|
bool qFull = false;
|
|
uint32_t oobCommand = 0x00000000;
|
|
|
|
ESP_LOGI(IOTAG, "Starting stdin task");
|
|
while (1)
|
|
{
|
|
// If an input queue has been opened, we read the chars from stdin (RP2350) and buffer them for further processing.
|
|
if (cmdFrame.inputQueue != NULL)
|
|
{
|
|
char buf;
|
|
int rxChar = EOF;
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
rxChar = getchar();
|
|
buf = (char) rxChar;
|
|
|
|
if (rxChar != EOF)
|
|
#elif defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
size_t bytesWaiting;
|
|
uart_get_buffered_data_len(UART_NUM_0, &bytesWaiting);
|
|
if (bytesWaiting > 0)
|
|
{
|
|
rxChar = uart_read_bytes(UART_NUM_0, &buf, 1, 1);
|
|
}
|
|
|
|
if (rxChar == 1)
|
|
#endif
|
|
{
|
|
// Assemble out of band control commands.
|
|
oobCommand <<= 8;
|
|
oobCommand = (oobCommand & 0xFFFFFF00) | (uint8_t) buf;
|
|
|
|
// Process any recognised Out Of Band commands, if non recognised, default to queuing the char.
|
|
switch (oobCommand)
|
|
{
|
|
// The RP2350 sends IO_OOB_CMD_RESET_INPUT (0xAA5555AA) on startup, providing
|
|
// ~3 seconds of advance warning before it issues its first SPI command.
|
|
// During RP2350 reset the SPI SCK/CS pins float, causing the ESP32 SPI slave
|
|
// ISR to fire at MHz rates. This starves the FreeRTOS timer ISR and corrupts
|
|
// internal FreeRTOS state (e.g. s_timer_task → 0x39300000, invalid), leading to
|
|
// a "Cache disabled but cached memory region accessed" crash in
|
|
// vTaskGenericNotifyGiveFromISR when the timer ISR fires with the corrupted
|
|
// task handle.
|
|
//
|
|
// Fix: call spi_slave_free() HERE, while spihost is still valid, to properly
|
|
// deregister the SPI slave ISR *before* the SPI pins start glitching.
|
|
// Then restart ESP32 cleanly. The 3-second window between this OOB command
|
|
// and the RP2350's first SPI attempt gives us enough time to restart safely.
|
|
case IO_OOB_CMD_RESET_INPUT:
|
|
oobCommand = 0x00000000;
|
|
xQueueReset(cmdFrame.inputQueue);
|
|
ESP_LOGW(IOTAG,
|
|
"Clearing STDIN Q:%d,%d,%d,%s — restarting ESP32 cleanly.",
|
|
cmdFrame.inFrame,
|
|
cmdFrame.frameLength,
|
|
cmdFrame.frameReceived,
|
|
cmdFrame.frameBuffer);
|
|
cmdFrame.inFrame = false;
|
|
cmdFrame.frameLength = 0;
|
|
cmdFrame.frameReceived = false;
|
|
// Flag this restart as OOB-triggered so CommandProcessor uses
|
|
// the 100 ms startup delay (not 3500 ms SPI-crash-recovery delay).
|
|
// There is no mid-transaction state to recover from — the SPI slave
|
|
// is about to be freed cleanly below.
|
|
// Do NOT restart. Instead, signal CommandProcessor to
|
|
// reinitialize the SPI slave. The RP2350 will follow up
|
|
// with a NOP flush to unblock spi_slave_transmit.
|
|
ESP_LOGW(IOTAG, "OOB RESET_INPUT — requesting SPI slave reinit.");
|
|
g_spi_clear_requested = true;
|
|
break;
|
|
|
|
// Hard reboot of the ESP32 required?
|
|
case IO_OOB_CMD_RESET_MCU:
|
|
cmdFrame.reboot = true;
|
|
ESP_LOGW(IOTAG, "RESET MCU.");
|
|
break;
|
|
|
|
default:
|
|
// Queue the input char, it will either be recognised as part of a frame or taken by
|
|
// a stdin sink.
|
|
if (xQueueSend(cmdFrame.inputQueue, &buf, 10 / portTICK_PERIOD_MS) != pdTRUE)
|
|
{
|
|
if (!qFull)
|
|
{
|
|
ESP_LOGW(IOTAG, "Q full, drop:%c", buf);
|
|
qFull = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (qFull)
|
|
{
|
|
ESP_LOGW(IOTAG, "Q emptying...");
|
|
qFull = false;
|
|
}
|
|
else
|
|
{
|
|
//ESP_LOGW(IOTAG, "(%c)", buf);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
vTaskDelay(1); // Yield
|
|
//vTaskDelay(10 / portTICK_PERIOD_MS); // Yield
|
|
}
|
|
}
|
|
|
|
// Custom getchar wrapper (framed or singular byte mode)
|
|
int IO_rp2350GetChar(bool framed)
|
|
{
|
|
// Locals.
|
|
int rxChar = EOF;
|
|
char buf;
|
|
|
|
// If no data in queue and not blocking, return EOF
|
|
if (cmdFrame.inputQueue != NULL && xQueueReceive(cmdFrame.inputQueue, &buf, 0) != pdTRUE)
|
|
{
|
|
return EOF;
|
|
}
|
|
else
|
|
// If no queue, read directly from stdin.
|
|
if (cmdFrame.inputQueue == NULL)
|
|
{
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
rxChar = getchar();
|
|
buf = (uint8_t) rxChar;
|
|
|
|
// No character, exit.
|
|
if (rxChar == EOF)
|
|
return (EOF);
|
|
|
|
#elif defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
size_t bytesWaiting;
|
|
uart_get_buffered_data_len(UART_NUM_0, &bytesWaiting);
|
|
if (bytesWaiting > 0)
|
|
{
|
|
rxChar = uart_read_bytes(UART_NUM_0, &buf, 1, 1);
|
|
}
|
|
|
|
// No character, exit.
|
|
if (rxChar == 0 || rxChar == EOF)
|
|
return (EOF);
|
|
|
|
ESP_LOGI(IOTAG, "Rx: %c (0x%x),%d", buf, buf, framed);
|
|
#endif
|
|
}
|
|
|
|
if (framed)
|
|
{
|
|
// Frame detection mode
|
|
if (buf == CMD_STX)
|
|
{
|
|
cmdFrame.inFrame = true;
|
|
cmdFrame.frameLength = 0;
|
|
cmdFrame.frameReceived = false;
|
|
//ESP_LOGI(IOTAG, "STX detected from RP2350");
|
|
return IO_rp2350GetChar(true); // Next char
|
|
}
|
|
|
|
if (cmdFrame.inFrame)
|
|
{
|
|
if (buf == CMD_ETX)
|
|
{
|
|
cmdFrame.inFrame = false;
|
|
cmdFrame.frameReceived = true;
|
|
//ESP_LOGI(IOTAG, "ETX detected, command received:%s, %d bytes", cmdFrame.frameBuffer, cmdFrame.frameLength);
|
|
return EOF;
|
|
}
|
|
if (cmdFrame.frameLength < FRAME_BUFFER_SIZE - 1)
|
|
{
|
|
cmdFrame.frameBuffer[cmdFrame.frameLength++] = buf;
|
|
cmdFrame.frameBuffer[cmdFrame.frameLength] = '\0';
|
|
}
|
|
else
|
|
{
|
|
ESP_LOGW(IOTAG, "Frame buffer overflow");
|
|
cmdFrame.inFrame = false;
|
|
cmdFrame.frameLength = 0;
|
|
}
|
|
return IO_rp2350GetChar(true); // Next char
|
|
}
|
|
|
|
// If the char hasnt been consumed, push back as it will be needed by stdin.
|
|
if (cmdFrame.inputQueue != NULL && xQueueSendToFront(cmdFrame.inputQueue, &buf, 10 / portTICK_PERIOD_MS) != pdTRUE)
|
|
{
|
|
ESP_LOGW(IOTAG, "Q full on push back, drop:%c", buf);
|
|
}
|
|
}
|
|
//ESP_LOGW(IOTAG, "Processed uart byte:%c, framed:%d", buf, framed);
|
|
|
|
// Non-framed mode: return the character directly
|
|
return ((int) buf);
|
|
}
|
|
|
|
// Retrieve framed command from RP2350
|
|
bool IO_rp2350GetCmdFrame(char *buffer, int max_len, int *out_len)
|
|
{
|
|
// If a frame hasnt been assembled, then try to build one if data waiting.
|
|
IO_rp2350GetChar(true);
|
|
|
|
// No frame, exit.
|
|
if (!cmdFrame.frameReceived)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int len = cmdFrame.frameLength;
|
|
if (len > max_len - 1)
|
|
{
|
|
len = max_len - 1;
|
|
}
|
|
memcpy(buffer, cmdFrame.frameBuffer, len);
|
|
memset(&buffer[len], 0x00, max_len - len);
|
|
*out_len = len;
|
|
|
|
cmdFrame.frameReceived = false;
|
|
//ESP_LOGI(IOTAG, "Command retrieved: %s", buffer);
|
|
return (true);
|
|
}
|
|
|
|
// Send framed response to RP2350
|
|
void IO_rp2350SendResponseFrame(const char *response)
|
|
{
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
printf("%c%s%c", RSP_STX, response, RSP_ETX);
|
|
#elif defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
char tmpbuf[256];
|
|
sprintf(tmpbuf, "%c%s%c", RSP_STX, response, RSP_ETX);
|
|
uart_write_bytes(UART_NUM_0, tmpbuf, strlen(tmpbuf));
|
|
//ESP_LOGI(IOTAG, "SendResponse: (%s)(%d) ", tmpbuf, strlen(tmpbuf));
|
|
#endif
|
|
}
|
|
|
|
// Send command to RP2350.
|
|
void IO_rp2350SendCommand(const char *cmd)
|
|
{
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
printf("%c%s%c", CMD_STX, cmd, CMD_ETX);
|
|
#elif defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
char tmpbuf[256];
|
|
sprintf(tmpbuf, "%c%s%c", CMD_STX, cmd, CMD_ETX);
|
|
uart_write_bytes(UART_NUM_0, tmpbuf, strlen(tmpbuf));
|
|
ESP_LOGI(IOTAG, "SendCommand: (%s)(%d) ", tmpbuf, strlen(tmpbuf));
|
|
#endif
|
|
}
|
|
|
|
// Change the logging mode for this ESP32.
|
|
bool IO_setLogMode(t_LogMode newLogMode)
|
|
{
|
|
// Locals
|
|
bool result = false;
|
|
t_LogMode updatedLogMode = newLogMode;
|
|
|
|
if (newLogMode < LOGGING_OFF || newLogMode > LOGGING_NORMAL)
|
|
return (result);
|
|
|
|
if (logger.logMode != LOGGING_FILE && logger.logFile != NULL)
|
|
{
|
|
fclose(logger.logFile);
|
|
logger.logFile = NULL;
|
|
}
|
|
if (logger.logMode == LOGGING_FILE && logger.logFile == NULL)
|
|
{
|
|
logger.logFile = fopen(IO_DEFAULT_LOG_FILE, "a");
|
|
if (logger.logFile == NULL)
|
|
{
|
|
ESP_LOGE(IOTAG, "Failed to open logging file:%s", IO_DEFAULT_LOG_FILE);
|
|
updatedLogMode = LOGGING_FRAMED;
|
|
}
|
|
else
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
logger.logMode = updatedLogMode;
|
|
return (result);
|
|
}
|
|
|
|
// If a reboot has been signalled, then report back so the primary thread can coordinate reboots.
|
|
bool IO_doReboot(void)
|
|
{
|
|
return (cmdFrame.reboot);
|
|
}
|
|
|
|
// Initialisation.
|
|
bool IO_init(t_LogMode initialLogMode)
|
|
{
|
|
// Locals.
|
|
bool result = false;
|
|
|
|
// Initialise control for receiving frames.
|
|
cmdFrame.inFrame = false;
|
|
cmdFrame.frameLength = 0;
|
|
cmdFrame.frameReceived = false;
|
|
cmdFrame.reboot = false;
|
|
|
|
#if defined(CONFIG_USE_ESP32_USB_OUTPUT)
|
|
ESP_LOGI(IOTAG, "Initialising IO as independant USB for logging and UART for RP2350 comms.");
|
|
if (initialLogMode == LOGGING_NORMAL)
|
|
{
|
|
//uart_driver_install(UART_NUM_0, UART0_MAX_BUFFER_SIZE * 2, 0, 0, NULL, 0);
|
|
uart_driver_install(UART_NUM_0, UART0_MAX_BUFFER_SIZE * 2, UART0_MAX_BUFFER_SIZE, 0, NULL, 0);
|
|
uart_param_config(UART_NUM_0, &uartConfig);
|
|
uart_set_pin(UART_NUM_0, CONFIG_UART0_TX_PIN, CONFIG_UART0_RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
|
|
}
|
|
#endif
|
|
|
|
// Create input queue, to store stdin chars.
|
|
cmdFrame.inputQueue = xQueueCreate(INPUT_QUEUE_SIZE, sizeof(char));
|
|
if (cmdFrame.inputQueue != NULL)
|
|
{
|
|
// Start task to read stdin.
|
|
xTaskCreate(IO_stdinReaderTask, "IO_stdinReaderTask", 8192, NULL, 5, NULL);
|
|
}
|
|
|
|
#if defined(CONFIG_USE_RP2350_OUTPUT)
|
|
// Initialise logger mutex.
|
|
logMutex = xSemaphoreCreateMutex();
|
|
if (logMutex == NULL)
|
|
{
|
|
ESP_LOGE(IOTAG, "Failed to create logger Mutex.");
|
|
}
|
|
|
|
// Set a custom logging and input function, if memory permits, to allow us to control the log output and stdin input.
|
|
ESP_LOGI(IOTAG, "Initialising IO as logging and RP2350 comms routed through UART0 to RP2350.");
|
|
if ((logger.logBuffer = (char *) malloc(MAX_LOG_BUFFER_SIZE)) != NULL && (logger.logLine = (char *) malloc(MAX_LOG_LINE_SIZE)) != NULL)
|
|
{
|
|
logger.logMode = initialLogMode;
|
|
logger.logPos = 0;
|
|
|
|
// Register VFS for stdout only
|
|
esp_vfs_unregister("/dev/uart/0");
|
|
esp_err_t err = esp_vfs_register("/dev/uart/0", &customVFS, NULL);
|
|
if (err != ESP_OK)
|
|
{
|
|
ESP_LOGE(IOTAG, "VFS register failed: %d", err);
|
|
|
|
// Release memory as we cant use the extended stdin/stdout processing.
|
|
if (logger.logBuffer)
|
|
free(logger.logBuffer);
|
|
if (logger.logLine)
|
|
free(logger.logLine);
|
|
logger.logBuffer = logger.logLine = NULL;
|
|
}
|
|
else
|
|
{
|
|
ESP_LOGI(IOTAG, "VFS registered for stdout");
|
|
|
|
// Override vprintf used by the logger.
|
|
esp_log_set_vprintf(IO_rp2350WriteLog);
|
|
|
|
// Extended logger online.
|
|
result = true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return (result);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|