Advanced Distributed Systems module at HSLU
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

325 lines
9.7 KiB

/*
* Copyright (c) 2019-2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_SHELL
#include "Shell.h"
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#if PL_CONFIG_USE_BLINKY
#include "led.h"
#endif
#if PL_CONFIG_USE_WIFI
#include "WiFi.h"
#endif
#if PL_CONFIG_USE_PING
#include "ping_shell.h"
#endif
#if PL_CONFIG_USE_UDP_SERVER
#include "udp_server_shell.h"
#endif
#if PL_CONFIG_USE_UDP_CLIENT
#include "udp_client.h"
#endif
#if PL_CONFIG_USE_RS485
#include "rs485.h"
#include "McuUart485.h"
#endif
#if PL_CONFIG_USE_ROBO_REMOTE
#include "robot.h"
#endif
#include "McuShell.h"
#include "McuUtility.h"
#include "McuRTOS.h"
#include "McuTimeDate.h"
#include "McuLog.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#if PL_CONFIG_CHALLENGE_APP_ACTIVATED
#include "challenge_app.h"
#endif
#define SHELL_ESP32_UART_DEVICE (UART_NUM_0) /* Uart for bootloader and connection to robot */
static TaskHandle_t SHELL_taskHandle;
static SemaphoreHandle_t SHELL_stdioMutex; /* mutex to protect access to ESP32 standard I/O */
static const McuShell_ParseCommandCallback CmdParserTable[] =
{
McuShell_ParseCommand,
McuRTOS_ParseCommand,
#if PL_CONFIG_USE_WIFI
WiFi_ParseCommand,
#endif
#if PL_CONFIG_USE_UDP_CLIENT
UDP_Client_ParseCommand,
#endif
#if PL_CONFIG_USE_PING
PING_ParseCommand,
#endif
#if PL_CONFIG_USE_UDP_SERVER
UDP_Server_ParseCommand,
#endif
#if PL_CONFIG_USE_RS485
RS485_ParseCommand,
McuUart485_ParseCommand,
#endif
#if PL_CONFIG_USE_ROBO_REMOTE
ROBOT_ParseCommand,
#endif
McuLog_ParseCommand,
McuTimeDate_ParseCommand,
#if PL_CONFIG_USE_BLINKY
LED_ParseCommand,
#endif
#if PL_CONFIG_CHALLENGE_APP_ACTIVATED
Challenge_ParseCommand,
#endif
NULL /* Sentinel */
};
typedef struct {
McuShell_ConstStdIOType *stdio;
unsigned char *buf;
size_t bufSize;
} SHELL_IODesc;
static void Uart_SendString(const unsigned char *str) {
size_t len;
int written;
len = strlen((const char*)str);
written = uart_write_bytes(SHELL_ESP32_UART_DEVICE, (const char*)str, len);
if (written!=len) {
McuLog_error("failed sending uart bytes");
}
}
static void Uart_SendChar(unsigned char ch) {
uart_write_bytes(SHELL_ESP32_UART_DEVICE, &ch, 1);
}
static void Uart_ReadChar(uint8_t *c) {
unsigned char ch = '\0';
int len = 0;
if (xSemaphoreTakeRecursive(SHELL_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
len = uart_read_bytes(SHELL_ESP32_UART_DEVICE, &ch, 1, 0);
(void)xSemaphoreGiveRecursive(SHELL_stdioMutex); /* give back mutex */
}
if (len==0) {
*c = '\0';
} else {
*c = ch;
}
}
static bool Uart_CharPresent(void) {
size_t size=0;
if (xSemaphoreTakeRecursive(SHELL_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
uart_get_buffered_data_len(SHELL_ESP32_UART_DEVICE, &size);
(void)xSemaphoreGiveRecursive(SHELL_stdioMutex); /* give back mutex */
}
return size!=0;
}
static McuShell_ConstStdIOType Uart_stdio = {
.stdIn = (McuShell_StdIO_In_FctType)Uart_ReadChar,
.stdOut = (McuShell_StdIO_OutErr_FctType)Uart_SendChar,
.stdErr = (McuShell_StdIO_OutErr_FctType)Uart_SendChar,
.keyPressed = Uart_CharPresent, /* if input is not empty */
#if McuShell_CONFIG_ECHO_ENABLED
.echoEnabled = true,
#endif
};
static uint8_t Uart_DefaultShellBuffer[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; /* default buffer which can be used by the application */
static const SHELL_IODesc ios[] =
{
{&Uart_stdio, Uart_DefaultShellBuffer, sizeof(Uart_DefaultShellBuffer)},
};
void SHELL_SendChar(unsigned char ch) {
for(int i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
McuShell_SendCh(ch, ios[i].stdio->stdOut);
}
}
uint8_t SHELL_ParseCommand(unsigned char *cmd) {
return McuShell_ParseWithCommandTable(cmd, McuShell_GetStdio(), CmdParserTable);
}
uint8_t SHELL_ParseCommandIO(const unsigned char *command, McuShell_ConstStdIOType *io, bool silent) {
if (io==NULL) { /* use a default */
io = McuShell_GetStdio();
}
return McuShell_ParseWithCommandTableExt(command, io, CmdParserTable, silent);
}
void SHELL_SendString(const unsigned char *str) {
#if 0
for(int i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
McuShell_SendStr(str, ios[i].stdio->stdOut);
}
#else /* need to improve write speed, as writing character by character is too slow */
Uart_SendString(str);
#endif
}
void SHELL_SendStringToIO(const unsigned char *str, McuShell_ConstStdIOType *io) {
if (io->stdOut == Uart_SendChar) { /* ESP32 UART? */
/* if out channel is ESP32 UART: speed it up by sending whole buffer */
Uart_SendString(str);
} else { /* send it char by char */
McuShell_SendStr(str, io->stdOut);
}
}
/* ----------------- buffer handling for shell messages sent to ESP32 */
static unsigned char *esp_io_buf; /* pointer to buffer */
static size_t esp_io_buf_size; /* size of buffer */
static void esp_io_buf_SendChar(unsigned char ch) {
McuUtility_chcat(esp_io_buf, esp_io_buf_size, ch);
}
static void esp_io_buf_ReadChar(uint8_t *c) {
*c = '\0';
}
static bool esp_io_buf_CharPresent(void) {
return false;
}
static McuShell_ConstStdIOType esp_stdio = {
.stdIn = (McuShell_StdIO_In_FctType)esp_io_buf_ReadChar,
.stdOut = (McuShell_StdIO_OutErr_FctType)esp_io_buf_SendChar,
.stdErr = (McuShell_StdIO_OutErr_FctType)esp_io_buf_SendChar,
.keyPressed = esp_io_buf_CharPresent, /* if input is not empty */
#if McuShell_CONFIG_ECHO_ENABLED
.echoEnabled = false, /* echo enabled for idf.py monitor */
#endif
};
void SHELL_SendToESPAndGetResponse(const unsigned char *msg, unsigned char *response, size_t responseSize) {
esp_io_buf = response;
esp_io_buf_size = responseSize;
esp_io_buf[0] = '\0'; /* initialize buffer */
McuLog_info("Sending to ESP Shell: %s", msg);
McuShell_ParseWithCommandTableExt(msg, &esp_stdio, CmdParserTable, true); /* send to ESP32 shell */
}
/* ----------------------------------------------------------------------*/
void SHELL_SendToRobotAndGetResponse(const unsigned char *send, unsigned char *response, size_t responseSize) {
unsigned char buffer[128]; /* buffer for sending command to robot */
/* build a frame around the message: that way the robot is able to recognize it */
McuUtility_strcpy(buffer, sizeof(buffer), (unsigned char*)"@robot:cmd ");
McuUtility_strcat(buffer, sizeof(buffer), send);
McuUtility_strcat(buffer, sizeof(buffer), (unsigned char*)"!\r\n");
SHELL_SendString(buffer); /* send to UART, which is read by the robot */
/* get response */
#if 1
/* Important: this consumes directly all characters coming from the robot. That way the ESP32 shell does not get it.
* A mutex is used to block the shell from getting the UART stream.
*/
#define TIMEOUT_MS (500) /* stop if we don't get new input after this timeout */
int timeoutMs = TIMEOUT_MS;
*response = '\0';
if (xSemaphoreTakeRecursive(SHELL_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
while (true) { /* breaks after timeout */
if (!Uart_stdio.keyPressed()) { /* no input: wait for timeout */
timeoutMs -= 50;
if (timeoutMs<=0) {
break; /* timeout */
}
vTaskDelay(pdMS_TO_TICKS(50));
} else { /* character available */
unsigned char ch;
Uart_stdio.stdIn(&ch);
if (ch!='\r') { /* filter out '\r' in "\r\n" */
McuUtility_chcat(response, responseSize, ch);
}
timeoutMs = TIMEOUT_MS; /* reset timeout */
} /* if */
} /* while */
(void)xSemaphoreGiveRecursive(SHELL_stdioMutex); /* give back mutex */
}
if (*response=='\0') { /* if response is empty, send back at least an acknowledgment */
McuUtility_strcpy(response, responseSize, (unsigned char*)"OK"); /* default response */
}
#else
McuUtility_strcpy(response, responseSize, (unsigned char*)"OK"); /* default response */
#endif
}
/* ----------------------------------------------------------------------*/
static void ShellTask(void *pv) {
int i;
McuLog_info("Shell task started");
for(i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
ios[i].buf[0] = '\0'; /* initialize I/O buffers */
}
for(;;) {
/* process all I/Os */
for(i=0;i<sizeof(ios)/sizeof(ios[0]);i++) {
(void)McuShell_ReadAndParseWithCommandTable(ios[i].buf, ios[i].bufSize, ios[i].stdio, CmdParserTable);
}
vTaskDelay(pdMS_TO_TICKS(20));
}
}
static void InitUart(void) {
#define ESP32_UART_BUF_SIZE 512
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,
.rx_flow_ctrl_thresh = 0,
};
/* Configure UART parameters */
uart_param_config(SHELL_ESP32_UART_DEVICE, &uart_config);
uart_set_pin(SHELL_ESP32_UART_DEVICE, GPIO_NUM_1, GPIO_NUM_3, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
/* Install UART driver (we don't need an event queue here) */
uart_driver_install(SHELL_ESP32_UART_DEVICE, ESP32_UART_BUF_SIZE*2, ESP32_UART_BUF_SIZE*2, 0, NULL, 0);
uart_set_mode(SHELL_ESP32_UART_DEVICE, UART_MODE_UART);
}
void SHELL_Init(void) {
BaseType_t res;
InitUart();
McuLog_set_console(&Uart_stdio, 0);
McuShell_SetStdio(&Uart_stdio);
SHELL_stdioMutex = xSemaphoreCreateRecursiveMutex();
if (SHELL_stdioMutex==NULL) { /* creation failed? */
McuLog_fatal("Failed creating mutex");
for(;;);
}
vQueueAddToRegistry(SHELL_stdioMutex, "ShellStdIoMutex");
McuLog_info("Creating Shell task");
res = xTaskCreate(ShellTask, "ShellTask", 4*1024/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+4, &SHELL_taskHandle);
if (res!=pdPASS) {
McuLog_fatal("creating ShellTask task failed!");
}
}
void SHELL_Deinit(void) {
/* nothing needed */
}
#endif /* PL_CONFIG_USE_SHELL */