# Conflicts: # ADIS_ESP32_Eclipse/main/CMakeLists.txt # ADIS_ESP32_Eclipse/main/main.cmain
commit
44027da070
@ -0,0 +1,42 @@ |
||||
/*
|
||||
* ESP32 IncludeMcuLibConfig |
||||
* |
||||
* Author: Erich Styger |
||||
* License: PDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef MCULIB_CONFIG_CONFIG_H_ |
||||
#define MCULIB_CONFIG_CONFIG_H_ |
||||
|
||||
/* -------------- Core and McuLib general settings -------------------------- */ |
||||
#define McuLib_CONFIG_CPU_IS_ARM_CORTEX_M (0) /* ESP32 is detected automatically */ |
||||
|
||||
/* -------------- FreeRTOS and McuRTOS settings -------------------------- */ |
||||
#define configHEAP_SCHEME_IDENTIFICATION (0) /* ESP-IDF RTOS used */ |
||||
|
||||
/* -------------- McuShell settings -------------------------- */ |
||||
#define McuShell_CONFIG_PROJECT_NAME_STRING "ESP32 Shell" |
||||
#define McuShell_CONFIG_ECHO_ENABLED (1) |
||||
#define McuShell_CONFIG_PROMPT_STRING "ESP32> " |
||||
#define McuShell_CONFIG_MULTI_CMD_ENABLED (1) |
||||
#define McuShell_CONFIG_MULTI_CMD_SIZE (96) |
||||
|
||||
/* -------------- McuCriticalSections settings -------------------------- */ |
||||
#define McuCriticalSection_CONFIG_USE_RTOS_CRITICAL_SECTION (1) |
||||
|
||||
/* -------------- McuTimeDate settings -------------------------- */ |
||||
#define McuTimeDate_CONFIG_TICK_TIME_MS (10) |
||||
/* -------------------------------------------------*/ |
||||
/* McuLog */ |
||||
#define McuLog_CONFIG_IS_ENABLED (1) |
||||
#define McuLog_CONFIG_DEFAULT_LEVEL (McuLog_DEBUG) |
||||
#define McuLog_CONFIG_USE_COLOR (1) |
||||
#define McuLog_CONFIG_NOF_CONSOLE_LOGGER (1) |
||||
#define McuLog_CONFIG_LOG_TIMESTAMP_DATE (1) |
||||
#define McuLog_CONFIG_USE_RTT_DATA_LOGGER (0) |
||||
#define McuLog_CONFIG_RTT_DATA_LOGGER_BUFFER_SIZE (128) |
||||
/* -------------------------------------------------*/ |
||||
/* McuUART485 */ |
||||
#define McuUart485_CONFIG_USE_RS_485 (1) |
||||
|
||||
#endif /* MCULIB_CONFIG_CONFIG_H_ */ |
||||
@ -0,0 +1,3 @@ |
||||
/build/ |
||||
# do NOT put the password on git |
||||
/pwd.h |
||||
@ -1,2 +1,22 @@ |
||||
idf_component_register(SRCS "main.c" "myTask.c" "myTask.h" |
||||
INCLUDE_DIRS "") |
||||
idf_component_register( |
||||
SRCS |
||||
"main.c" |
||||
"myTask.c" |
||||
"platform.c" |
||||
"led.c" |
||||
"wifi.c" |
||||
"udp_client.c" |
||||
"udp_server.c" |
||||
"udp_server_shell.c" |
||||
"Shell.c" |
||||
"esp32_mac.c" |
||||
"rs485.c" |
||||
"ping.c" |
||||
"ping_shell.c" |
||||
"timer.c" |
||||
"sntp_time.c" |
||||
"robot.c" |
||||
|
||||
INCLUDE_DIRS |
||||
"." |
||||
) |
||||
@ -0,0 +1,93 @@ |
||||
/**
|
||||
* \file |
||||
* \brief Module to identify different devices based on their unique ID. |
||||
* \author Erich Styger, erich.styger@hslu.ch |
||||
* \license SPDX-License-Identifier: BSD-3-Clause |
||||
* With this module individual devices are identified based on their unique ID. |
||||
*/ |
||||
|
||||
#ifndef __IDENTIFY_H_ |
||||
#define __IDENTIFY_H_ |
||||
|
||||
#include "platform.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_IDENTIFY |
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Parses a command |
||||
* \param cmd Command string to be parsed |
||||
* \param handled Sets this variable to TRUE if command was handled |
||||
* \param io I/O stream to be used for input/output |
||||
* \return Error code, ERR_OK if everything was fine |
||||
*/ |
||||
uint8_t ID_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
typedef enum { /*! \todo ADIS: Verify and update information */ |
||||
ID_ROBOT_E0, |
||||
ID_ROBOT_E1, |
||||
ID_ROBOT_E2, |
||||
ID_ROBOT_E3, |
||||
ID_ROBOT_E4, |
||||
ID_ROBOT_E5, |
||||
ID_ROBOT_E6, |
||||
ID_ROBOT_E7, |
||||
ID_ROBOT_E8, |
||||
ID_ROBOT_E9, |
||||
ID_ROBOT_E10, |
||||
ID_ROBOT_E11, |
||||
ID_ROBOT_E12, |
||||
ID_ROBOT_E13, |
||||
ID_ROBOT_E14, |
||||
// ID_ROBOT_E15,
|
||||
// ID_ROBOT_E16,
|
||||
ID_ROBOT_E17, |
||||
ID_ROBOT_E18, |
||||
ID_ROBOT_E27, |
||||
ID_ROBOT_E34, /* no robot? */ |
||||
|
||||
ID_ROBOT_L0, /* USB port ripped off */ |
||||
ID_ROBOT_L1, |
||||
ID_ROBOT_L3, |
||||
ID_ROBOT_L17, |
||||
ID_ROBOT_L20, |
||||
|
||||
ID_ROBOT_R0, /* USB port ripped off */ |
||||
ID_ROBOT_R8, |
||||
ID_ROBOT_R9, |
||||
ID_ROBOT_R23, |
||||
ID_ROBOT_R27, |
||||
ID_ROBOT_R28, |
||||
ID_ROBOT_R29, |
||||
ID_ROBOT_R32, |
||||
ID_ROBOT_R33, |
||||
ID_ROBOT_R34, |
||||
ID_ROBOT_R36, |
||||
ID_ROBOT_R37, |
||||
ID_ROBOT_R44, |
||||
ID_ROBOT_R45, |
||||
|
||||
ID_ROBOT_UNKNOWN, /* unknown robot, unknown ID */ |
||||
ID_ROBOT_NONE /* initialization value, used internally */ |
||||
} ID_Robot_e; |
||||
|
||||
ID_Robot_e ID_WhichDevice(void); |
||||
|
||||
/*! \brief Module de-initialization */ |
||||
void ID_Deinit(void); |
||||
|
||||
/*! \brief Module initialization */ |
||||
void ID_Init(void); |
||||
#endif /* PL_CONFIG_USE_IDENTIFY */ |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* __IDENTIFY_H_ */ |
||||
@ -0,0 +1,319 @@ |
||||
/*
|
||||
* 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" |
||||
|
||||
#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 |
||||
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 */ |
||||
@ -0,0 +1,74 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef SHELL_H_ |
||||
#define SHELL_H_ |
||||
|
||||
#include "McuShell.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*!
|
||||
* \brief Send a string to all supported I/Os |
||||
* \param str String to send |
||||
*/ |
||||
void SHELL_SendString(const unsigned char *str); |
||||
|
||||
/*!
|
||||
* \brief Send a string to a given IO. It tries to accelerate it by sending a buffer instead char by char. |
||||
* \param str String to be sent. |
||||
* \param io I/O to be used. |
||||
*/ |
||||
void SHELL_SendStringToIO(const unsigned char *str, McuShell_ConstStdIOType *io); |
||||
|
||||
/*!
|
||||
* \brief Send a character to all supported I/Os |
||||
* \param ch Character to send |
||||
*/ |
||||
void SHELL_SendChar(unsigned char ch); |
||||
|
||||
/*!
|
||||
* \brief Parses a command with a given standard I/O channel |
||||
* \param command Command to be parsed |
||||
* \param io I/O to be used. If NULL, the standard default I/O will be used |
||||
* \param silent If parsing shall be silent or not |
||||
* \return Error code, ERR_OK for no error |
||||
*/ |
||||
uint8_t SHELL_ParseCommandIO(const unsigned char *command, McuShell_ConstStdIOType *io, bool silent); |
||||
|
||||
/*!
|
||||
* \brief Send a string to the ESP shell get a response back |
||||
* \param send Message to send |
||||
* \param response Where to store the response |
||||
* \param responseSize Size of the response buffer |
||||
*/ |
||||
void SHELL_SendToESPAndGetResponse(const unsigned char *send, unsigned char *response, size_t responseSize); |
||||
|
||||
/*!
|
||||
* \brief Send a string to the robot get a response back |
||||
* \param send Message to send |
||||
* \param response Where to store the response |
||||
* \param responseSize Size of the response buffer |
||||
*/ |
||||
void SHELL_SendToRobotAndGetResponse(const unsigned char *send, unsigned char *response, size_t responseSize); |
||||
|
||||
/*!
|
||||
* \brief Module de-initialization |
||||
*/ |
||||
void SHELL_Deinit(void); |
||||
|
||||
/*!
|
||||
* \brief Module Initialization |
||||
*/ |
||||
void SHELL_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* SHELL_H_ */ |
||||
@ -0,0 +1,110 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_IDENTIFY |
||||
#include "esp32_mac.h" |
||||
#include "esp_system.h" |
||||
#include "Identify.h" |
||||
#include "McuUtility.h" |
||||
#include "McuXFormat.h" |
||||
#include <stdint.h> |
||||
#include <stddef.h> |
||||
#include <stdbool.h> |
||||
|
||||
static const ESP32_Device_t ESP32_devices[] = |
||||
{ /* registered at https://eeeportal.hslu.ch */ |
||||
{.hostName="UnknownRobot", .robotID=ID_ROBOT_UNKNOWN, .macStr="00:00:00:00:00:00", .eee_id="eee-0000", .eee_pwd="deadbeef"}, /* fallback entry */ |
||||
{.hostName="ADISRobotE0", .robotID=ID_ROBOT_E0, .macStr="d8:a0:1d:42:e2:08", .eee_id="eee-01367", .eee_pwd="xq4vZdu3eWLWcSGDuYwU"}, |
||||
{.hostName="ADISRobotE1", .robotID=ID_ROBOT_E1, .macStr="d8:a0:1d:42:ec:a4", .eee_id="eee-01023", .eee_pwd="i24Z3W5VtDFSkdNtk93f"}, |
||||
{.hostName="ADISRobotE2", .robotID=ID_ROBOT_E2, .macStr="d8:a0:1d:42:eb:c4", .eee_id="eee-01381", .eee_pwd="DcUYXx0saffo5ST8TihG"}, |
||||
{.hostName="ADISRobotE3", .robotID=ID_ROBOT_E3, .macStr="d8:a0:1d:42:e6:98", .eee_id="eee-01407", .eee_pwd="OjhpZJrXUPWAC3mR0odX"}, |
||||
{.hostName="ADISRobotE4", .robotID=ID_ROBOT_E4, .macStr="d8:a0:1d:42:e4:48", .eee_id="eee-01369", .eee_pwd="vDk13U6MzxkwGO879TUy"}, |
||||
{.hostName="ADISRobotE5", .robotID=ID_ROBOT_E5, .macStr="d8:a0:1d:42:de:9c", .eee_id="eee-01370", .eee_pwd="jCGeKTeWVrcncJO8u73M"}, |
||||
{.hostName="ADISRobotE6", .robotID=ID_ROBOT_E6, .macStr="d8:a0:1d:42:ec:40", .eee_id="eee-01382", .eee_pwd="fJjX12cWv4U0XfaJ46N0"}, |
||||
{.hostName="ADISRobotE7", .robotID=ID_ROBOT_E7, .macStr="d8:a0:1d:42:df:14", .eee_id="eee-01387", .eee_pwd="TEkK2FL5Yc8A4dcsf7sk"}, |
||||
{.hostName="ADISRobotE8", .robotID=ID_ROBOT_E8, .macStr="d8:a0:1d:42:ec:74", .eee_id="eee-01371", .eee_pwd="aaxQfxvPnCMVLvCZ3ays"}, |
||||
{.hostName="ADISRobotE9", .robotID=ID_ROBOT_E9, .macStr="d8:a0:1d:42:e4:64", .eee_id="eee-01446", .eee_pwd="4dZNLrMCZqsQbsNKTQSL"}, |
||||
{.hostName="ADISRobotE10", .robotID=ID_ROBOT_E10, .macStr="d8:a0:1d:42:e6:f8", .eee_id="eee-01383", .eee_pwd="KEsv8fendMWx1nHkdQUj"}, |
||||
{.hostName="ADISRobotE11", .robotID=ID_ROBOT_E11, .macStr="d8:a0:1d:42:e0:24", .eee_id="eee-01609", .eee_pwd="WeWLiT7eH01Jqrt4DAfi"}, // issue with EEE!
|
||||
{.hostName="ADISRobotE12", .robotID=ID_ROBOT_E12, .macStr="d8:a0:1d:42:df:d4", .eee_id="eee-01393", .eee_pwd="HtcKHGmaMUwwJgqh7Hao"}, |
||||
{.hostName="ADISRobotE13", .robotID=ID_ROBOT_E13, .macStr="d8:a0:1d:42:e5:88", .eee_id="eee-01384", .eee_pwd="q320iZDcc6bFPvXYzRxF"}, |
||||
{.hostName="ADISRobotE14", .robotID=ID_ROBOT_E14, .macStr="50:02:91:9c:0d:ac", .eee_id="eee-01635", .eee_pwd="V4V6LdNW7WJUnv4zbRKm"}, |
||||
{.hostName="ADISRobotE17", .robotID=ID_ROBOT_E17, .macStr="d8:a0:1d:42:e5:98", .eee_id="eee-00535", .eee_pwd="fXB5kVK6qS1khJ7qybYD"}, |
||||
{.hostName="ADISRobotE18", .robotID=ID_ROBOT_E18, .macStr="d8:a0:1d:42:e3:b8", .eee_id="eee-01390", .eee_pwd="W4KuRQERUMwHquUHwVot"}, |
||||
|
||||
{.hostName="ADISRobotE27", .robotID=ID_ROBOT_E27, .macStr="d8:a0:1d:42:df:e0", .eee_id="eee-01591", .eee_pwd="28o6wxyGLXmQ9DnUHw1P"}, |
||||
|
||||
{.hostName="ADISRobotL1", .robotID=ID_ROBOT_L1, .macStr="d8:a0:1d:42:e5:d0", .eee_id="eee-01372", .eee_pwd="zwtshA1tnqXxYqrt1mgo"}, |
||||
{.hostName="ADISRobotL3", .robotID=ID_ROBOT_L1, .macStr="50:02:91:a0:f8:ac", .eee_id="eee-01388", .eee_pwd="jhw1KSG1piDP7zthNUDt"}, |
||||
{.hostName="ADISRobotL17", .robotID=ID_ROBOT_L17, .macStr="d8:a0:1d:42:ed:24", .eee_id="eee-01373", .eee_pwd="XPiSwZ6Vr6Y5Y3ukvq5a"}, |
||||
{.hostName="ADISRobotL20", .robotID=ID_ROBOT_L20, .macStr="d8:a0:1d:42:df:94", .eee_id="eee-01374", .eee_pwd="88kY6j6nurJZpqt2gMMF"}, |
||||
|
||||
{.hostName="ADISRobotR8", .robotID=ID_ROBOT_R8, .macStr="d8:a0:1d:42:ed:50", .eee_id="eee-01368", .eee_pwd="xaA8qck6edMS3h2tsHun"}, |
||||
{.hostName="ADISRobotR9", .robotID=ID_ROBOT_R9, .macStr="d8:a0:1d:42:e3:d8", .eee_id="eee-01392", .eee_pwd="NxJZeTDUpGGXC0L1QxX9"}, |
||||
{.hostName="ADISRobotR23", .robotID=ID_ROBOT_R23, .macStr="d8:a0:1d:42:e2:b8", .eee_id="eee-01385", .eee_pwd="deOkgVLPBoh6BQXH3Awf"}, |
||||
{.hostName="ADISRobotR27", .robotID=ID_ROBOT_R27, .macStr="50:02:91:a1:0f:78", .eee_id="eee-01375", .eee_pwd="7O4HCSqbWVW0zCvfhyA2"}, |
||||
{.hostName="ADISRobotR28", .robotID=ID_ROBOT_R28, .macStr="d8:a0:1d:42:e0:44", .eee_id="eee-01386", .eee_pwd="mzyEvEfx0BvgYTOgF4F2"}, |
||||
{.hostName="ADISRobotR29", .robotID=ID_ROBOT_R29, .macStr="d8:a0:1d:42:ec:a8", .eee_id="eee-01410", .eee_pwd="c1nbzEkaEmXz9rEeBEfC"}, |
||||
{.hostName="ADISRobotR32", .robotID=ID_ROBOT_R32, .macStr="d8:a0:1d:42:e0:24", .eee_id="eee-01376", .eee_pwd="LWWEBe2Au4Hy472W0pbA"}, |
||||
{.hostName="ADISRobotR33", .robotID=ID_ROBOT_R33, .macStr="50:02:91:9f:8e:28", .eee_id="eee-01405", .eee_pwd="iWv6FaKso2ARMsPWpdkv"}, |
||||
{.hostName="ADISRobotR34", .robotID=ID_ROBOT_R34, .macStr="50:02:91:9f:8d:dc", .eee_id="eee-01592", .eee_pwd="kmCgbmQjuxubnOAKu5Jo"}, |
||||
{.hostName="ADISRobotR36", .robotID=ID_ROBOT_R36, .macStr="d8:a0:1d:42:e3:ac", .eee_id="eee-01406", .eee_pwd="gq3rkAs5VOuSgnFOJ3f5"}, |
||||
{.hostName="ADISRobotR37", .robotID=ID_ROBOT_R37, .macStr="d8:a0:1d:42:ec:2c", .eee_id="eee-01377", .eee_pwd="d2GVw71vruR9baZA4EdE"}, |
||||
{.hostName="ADISRobotR44", .robotID=ID_ROBOT_R44, .macStr="d8:a0:1d:42:e9:98", .eee_id="eee-01394", .eee_pwd="3gG3dQTyjjsteuv18EY9"}, |
||||
|
||||
{.hostName="ADISRobotR45", .robotID=ID_ROBOT_R45, .macStr="d8:a0:1d:42:e3:bc", .eee_id="eee-01417", .eee_pwd="zD5zCmXSo18qSMq55Lvs"}, |
||||
|
||||
/* ESP32 modules with no robot! */ |
||||
{.hostName="ADISRobotE34", .robotID=ID_ROBOT_NONE, .macStr="50:02:91:a0:f8:18", .eee_id="eee-01389", .eee_pwd="yehFaZWK9kKSULwLyPyR"}, |
||||
|
||||
/* ESP32 breakout modules */ |
||||
{.hostName="ESP32BRKOUT01", .robotID=ID_ROBOT_NONE, .macStr="d8:a0:1d:62:93:40", .eee_id="eee-01408", .eee_pwd="Gu03RtUCsrE99PafZCTo"}, |
||||
{.hostName="ESP32DEVKIT02", .robotID=ID_ROBOT_NONE, .macStr="24:0a:c4:13:8e:30", .eee_id="eee-01411", .eee_pwd="6kagmMLOR3tDiRx6GfXm"}, |
||||
}; |
||||
|
||||
void ESP32_MacToString(uint8_t mac[6], uint8_t *buf, size_t bufSize) { |
||||
McuXFormat_xsnprintf((char*)buf, bufSize, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); |
||||
} |
||||
|
||||
void ESP32_MacRead(uint8_t mac[6]) { |
||||
ESP_ERROR_CHECK(esp_read_mac(&mac[0], ESP_MAC_WIFI_STA)); |
||||
} |
||||
|
||||
bool ESP32_MacAreSame(const uint8_t macA[6], const uint8_t macB[6]) { |
||||
for(int j=0; j<6; j++) { |
||||
if(macA[j] != macB[j]) { |
||||
return false; /* no match */ |
||||
} |
||||
} |
||||
return true; /* match! */ |
||||
} |
||||
|
||||
const ESP32_Device_t *ESP32_MacStrGetDevice(const char *macStr) { |
||||
int i; |
||||
|
||||
for(i=0; i<sizeof(ESP32_devices)/sizeof(ESP32_devices[0]); i++) { |
||||
if (McuUtility_strcmp(macStr, ESP32_devices[i].macStr)==0) { |
||||
return &ESP32_devices[i]; |
||||
} |
||||
} |
||||
return &ESP32_devices[0]; /* unknown robot entry */ |
||||
} |
||||
|
||||
const ESP32_Device_t *ESP32_GetDeviceConfig(void) { |
||||
uint8_t mac[6]; |
||||
uint8_t macStr[sizeof("aa:bb:cc:dd:ee:ff")]; |
||||
|
||||
ESP32_MacRead(mac); |
||||
ESP32_MacToString(mac, macStr, sizeof(macStr)); |
||||
return ESP32_MacStrGetDevice((char*)macStr); |
||||
} |
||||
|
||||
void ESP32_MacInit(void) { |
||||
/* nothing needed */ |
||||
} |
||||
|
||||
#endif /* PL_CONFIG_USE_IDENTIFY */ |
||||
@ -0,0 +1,64 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef ESP32_MAC_H_ |
||||
#define ESP32_MAC_H_ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_IDENTIFY |
||||
#include "Identify.h" |
||||
#include <stdint.h> |
||||
#include <stddef.h> /* for size_t */ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
typedef struct { |
||||
const char *hostName; /* name for module, usually matching robot, e.g. "AdisRobotR8" */ |
||||
ID_Robot_e robotID; /* ID of the robot */ |
||||
const char *macStr; /* ESP32 MAC, e.g. "d8:a0:1d:42:ed:50" */ |
||||
const char *eee_id; /* eee network id/name */ |
||||
const char *eee_pwd; /* eee network password */ |
||||
} ESP32_Device_t; |
||||
|
||||
/*!
|
||||
* \brief transforms a binary MAC into a string, e.g. "d8:a0:1d:42:ed:50" |
||||
* \param mac binary MAC |
||||
* \param buf buffer where to store the string |
||||
* \param bufSize size of buffer |
||||
*/ |
||||
void ESP32_MacToString(uint8_t mac[6], uint8_t *buf, size_t bufSize); |
||||
|
||||
/*!
|
||||
* \brief return for a given MAC address string (e.g. "d8:a0:1d:42:e2:08") the device or NULL if not found. |
||||
*/ |
||||
const ESP32_Device_t *ESP32_MacStrGetDevice(const char *macStr); |
||||
|
||||
/*!
|
||||
* \brief Read the MAC address into buffer |
||||
* \param mac buffer where to store the MAC address |
||||
*/ |
||||
void ESP32_MacRead(uint8_t mac[6]); |
||||
|
||||
/*!
|
||||
* \brief return a device configuration based on MAC address |
||||
* \return pointer to device configuration. Pointing to a dummy configuration if MAC is not found in list. |
||||
*/ |
||||
const ESP32_Device_t *ESP32_GetDeviceConfig(void); |
||||
|
||||
/*!
|
||||
* \brief Module initialization |
||||
*/ |
||||
void ESP32_MacInit(void); |
||||
|
||||
#endif /* PL_CONFIG_USE_IDENTIFY */ |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* ESP32_MAC_H_ */ |
||||
@ -0,0 +1,147 @@ |
||||
/*
|
||||
* Copyright (c) 2020, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
|
||||
#include <stdio.h> |
||||
#include "driver/gpio.h" |
||||
#include "freertos/FreeRTOS.h" |
||||
#include "freertos/task.h" |
||||
#include "esp_log.h" |
||||
#include "led.h" |
||||
#include "McuUtility.h" |
||||
#include "McuLED.h" |
||||
#include "McuLog.h" |
||||
|
||||
#define TAG "Blinky" |
||||
|
||||
static McuLED_Handle_t ledHandle; |
||||
|
||||
static TaskHandle_t taskHandle; |
||||
static bool blinkyIsRunning = false; |
||||
#define LED_OFF_TIME_MS 1000 |
||||
static uint32_t onTimeMs = 10; /* default on time of LED */ |
||||
|
||||
/* red led on the shield is on IO10, LOW active */ |
||||
#define BLINK_GPIO (GPIO_NUM_10) /* IO10 */ |
||||
#define LED_LOW_ACTIVE (1) |
||||
|
||||
void LED_SetOnTime(uint32_t ms) { |
||||
onTimeMs = ms; |
||||
} |
||||
|
||||
void LED_On(void) { |
||||
McuLED_On(ledHandle); |
||||
} |
||||
void LED_Off(void) { |
||||
McuLED_Off(ledHandle); |
||||
} |
||||
|
||||
void LED_Suspend(void) { |
||||
if (taskHandle!=NULL) { |
||||
vTaskSuspend(taskHandle); |
||||
blinkyIsRunning = false; |
||||
LED_Off(); |
||||
} |
||||
} |
||||
|
||||
void LED_Resume(void) { |
||||
if (taskHandle!=NULL) { |
||||
vTaskResume(taskHandle); |
||||
blinkyIsRunning = true; |
||||
} |
||||
} |
||||
|
||||
void LED_GetStatus(unsigned char *buf, size_t bufSize) { |
||||
buf[0] = '\0'; |
||||
if (taskHandle!=NULL) { |
||||
eTaskState state; |
||||
|
||||
state = eTaskGetState(taskHandle); |
||||
switch(state) { |
||||
case eSuspended: |
||||
McuUtility_strcpy(buf, bufSize, (unsigned char*)"suspended"); |
||||
break; |
||||
case eRunning: |
||||
case eBlocked: |
||||
McuUtility_strcpy(buf, bufSize, (unsigned char*)"running"); |
||||
break; |
||||
default: |
||||
case eDeleted: |
||||
McuUtility_strcpy(buf, bufSize, (unsigned char*)"ERROR!"); |
||||
break; |
||||
} |
||||
} else { |
||||
McuUtility_strcpy(buf, bufSize, (unsigned char*)"ERROR: no task!"); |
||||
} |
||||
} |
||||
|
||||
static void blinkyTask(void *pv) { |
||||
(void)pv; |
||||
blinkyIsRunning = true; |
||||
ESP_LOGI(TAG, "running my blinky task"); |
||||
McuLog_info("started blinky task"); |
||||
for(;;) { |
||||
LED_On(); |
||||
vTaskDelay(pdMS_TO_TICKS(onTimeMs)); |
||||
LED_Off(); |
||||
vTaskDelay(pdMS_TO_TICKS(LED_OFF_TIME_MS)); |
||||
} |
||||
} |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
McuShell_SendStatusStr((unsigned char*)"led", (unsigned char*)"ESP32 LED status\r\n", io->stdOut); |
||||
McuShell_SendStatusStr((unsigned char*)" status", blinkyIsRunning?(unsigned char*)"resumed\r\n":(unsigned char*)"suspended\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"led", (unsigned char*)"Group of ESP32 LED commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows LED help or status\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" suspend", (unsigned char*)"Suspend the LED task\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" resume", (unsigned char*)"Resume the LED task\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t LED_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"led help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"led status")==0) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)"led suspend")==0) { |
||||
*handled = TRUE; |
||||
LED_Suspend(); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)"led resume")==0) { |
||||
*handled = TRUE; |
||||
LED_Resume(); |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
void LED_Init(void) { |
||||
BaseType_t res; |
||||
McuLED_Config_t config; |
||||
|
||||
McuLED_GetDefaultConfig(&config); |
||||
config.hw.pin = BLINK_GPIO; |
||||
config.isLowActive = LED_LOW_ACTIVE; |
||||
config.isOnInit = false; |
||||
ledHandle = McuLED_InitLed(&config); |
||||
if (ledHandle==NULL) { |
||||
ESP_LOGE(TAG, "failed creating led handle!"); |
||||
return; |
||||
} |
||||
res = xTaskCreate(blinkyTask, "blinkyTask", 4*1024/sizeof(StackType_t), NULL, tskIDLE_PRIORITY, &taskHandle); |
||||
if (res==pdPASS) { |
||||
ESP_LOGI(TAG, "created blinky task"); |
||||
} else { |
||||
ESP_LOGE(TAG, "failed creating blinky!"); |
||||
} |
||||
} |
||||
@ -0,0 +1,42 @@ |
||||
/*
|
||||
* Copyright (c) 2020, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef LED_H_ |
||||
#define LED_H_ |
||||
|
||||
#include "platform.h" |
||||
#include <stddef.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
void LED_SetOnTime(uint32_t ms); |
||||
void LED_Suspend(void); |
||||
void LED_Resume(void); |
||||
void LED_GetStatus(unsigned char *buf, size_t bufSize); |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Command line and shell handler |
||||
* \param cmd The command to be parsed |
||||
* \param handled If command has been recognized and handled |
||||
* \param io I/O handler to be used |
||||
* \return error code, otherwise ERR_OK |
||||
*/ |
||||
uint8_t LED_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif |
||||
|
||||
/*! \brief Module initialization */ |
||||
void LED_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* LED_H_ */ |
||||
@ -0,0 +1,114 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_PING |
||||
#include "ping.h" |
||||
#include "sdkconfig.h" |
||||
#include "lwip/inet.h" |
||||
#include "lwip/netdb.h" |
||||
#include "lwip/sockets.h" |
||||
#include "esp_console.h" |
||||
#include "esp_event.h" |
||||
#include "nvs_flash.h" |
||||
#include "argtable3/argtable3.h" |
||||
#include "ping/ping_sock.h" |
||||
|
||||
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) { |
||||
// optionally, get callback arguments
|
||||
// const char* str = (const char*) args;
|
||||
// printf("%s\r\n", str); // "foo"
|
||||
uint8_t ttl; |
||||
uint16_t seqno; |
||||
uint32_t elapsed_time, recv_len; |
||||
ip_addr_t target_addr; |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time)); |
||||
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n", |
||||
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time); |
||||
} |
||||
|
||||
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) { |
||||
uint16_t seqno; |
||||
ip_addr_t target_addr; |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); |
||||
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno); |
||||
} |
||||
|
||||
void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) { |
||||
ip_addr_t target_addr; |
||||
uint32_t transmitted; |
||||
uint32_t received; |
||||
uint32_t total_time_ms; |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr)); |
||||
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms)); |
||||
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100); |
||||
if (IP_IS_V4(&target_addr)) { |
||||
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr))); |
||||
} else { |
||||
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr))); |
||||
} |
||||
printf("%d packets transmitted, %d received, %d%% packet loss, time %dms\n", |
||||
transmitted, received, loss, total_time_ms); |
||||
// delete the ping sessions, so that we clean up all resources and can create a new ping session
|
||||
// we don't have to call delete function in the callback, instead we can call delete function from other tasks
|
||||
esp_ping_delete_session(hdl); |
||||
} |
||||
|
||||
int PING_cmd_do_ping(const char *host) { |
||||
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG(); |
||||
|
||||
/* parse IP address */ |
||||
struct sockaddr_in6 sock_addr6; |
||||
ip_addr_t target_addr; |
||||
const char *hostp = host; /* extra pointer for getaddrinfo() as it might change it */ |
||||
memset(&target_addr, 0, sizeof(target_addr)); |
||||
|
||||
if (inet_pton(AF_INET6, hostp, &sock_addr6.sin6_addr) == 1) { |
||||
/* convert ip6 string to ip6 address */ |
||||
ipaddr_aton(host, &target_addr); |
||||
} else { |
||||
struct addrinfo hint; |
||||
struct addrinfo *res = NULL; |
||||
memset(&hint, 0, sizeof(hint)); |
||||
/* convert ip4 string or hostname to ip4 or ip6 address */ |
||||
if (getaddrinfo(hostp, NULL, &hint, &res) != 0) { |
||||
printf("ping: unknown host %s\n", host); |
||||
return 1; |
||||
} |
||||
if (res->ai_family == AF_INET) { |
||||
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr; |
||||
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4); |
||||
} else { |
||||
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr; |
||||
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6); |
||||
} |
||||
freeaddrinfo(res); |
||||
} |
||||
config.target_addr = target_addr; |
||||
|
||||
/* set callback functions */ |
||||
esp_ping_callbacks_t cbs = { |
||||
.on_ping_success = cmd_ping_on_ping_success, |
||||
.on_ping_timeout = cmd_ping_on_ping_timeout, |
||||
.on_ping_end = cmd_ping_on_ping_end, |
||||
.cb_args = NULL |
||||
}; |
||||
esp_ping_handle_t ping; |
||||
esp_ping_new_session(&config, &cbs, &ping); |
||||
esp_ping_start(ping); |
||||
return 0; /* ok */ |
||||
} |
||||
|
||||
void PING_Cmd_Init(void) { |
||||
} |
||||
#endif /* PL_CONFIG_USE_PING */ |
||||
@ -0,0 +1,30 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef MAIN_PING_H_ |
||||
#define MAIN_PING_H_ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*!
|
||||
* \brief performs a network ping |
||||
* \param host host name or address |
||||
* \return 0 for ok, negative values for error |
||||
*/ |
||||
int PING_cmd_do_ping(const char *host); |
||||
|
||||
/*!
|
||||
* \brief Module initialization |
||||
*/ |
||||
void PING_cmd_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* MAIN_PING_H_ */ |
||||
@ -0,0 +1,59 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_PING |
||||
#include "ping_shell.h" |
||||
#include "ping.h" |
||||
#include "wifi.h" |
||||
#include "McuShell.h" |
||||
#include "McuUtility.h" |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
|
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
McuShell_SendStatusStr((unsigned char*)"ping", (unsigned char*)"ESP32 ping status\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"ping", (unsigned char*)"Group of ESP32 ping commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows ESP32 ping help or status\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" <host>", (unsigned char*)"Ping host\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t PING_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
const unsigned char *p; |
||||
|
||||
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"ping help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"ping status")==0) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"ping ", sizeof("ping ")-1)==0) { |
||||
*handled = TRUE; |
||||
if (!WiFi_isConnected()) { |
||||
McuShell_SendStr((unsigned char*)"Network not connected\r\n", io->stdErr); |
||||
return ERR_FAILED; |
||||
} |
||||
p = cmd + sizeof("ping ")-1; |
||||
if (PING_cmd_do_ping((const char*)p)==0) { |
||||
return ERR_OK; |
||||
} else { |
||||
return ERR_FAILED; |
||||
} |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
|
||||
void PING_Init(void) { |
||||
} |
||||
|
||||
#endif /* PL_CONFIG_USE_PING */ |
||||
@ -0,0 +1,39 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef PING_SHELL_H_ |
||||
#define PING_SHELL_H_ |
||||
|
||||
#include "platform.h" |
||||
#include <stdint.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Command line and shell handler |
||||
* \param cmd The command to be parsed |
||||
* \param handled If command has been recognized and handled |
||||
* \param io I/O handler to be used |
||||
* \return error code, otherwise ERR_OK |
||||
*/ |
||||
uint8_t PING_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
/*!
|
||||
* \brief Module initialization |
||||
*/ |
||||
void PING_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* PING_SHELL_H_ */ |
||||
@ -0,0 +1,107 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#include "esp_err.h" |
||||
#if PL_CONFIG_USE_BLINKY |
||||
#include "led.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_WIFI |
||||
#include "wifi.h" |
||||
#include "nvs_flash.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_CLIENT |
||||
#include "udp_client.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_SERVER |
||||
#include "udp_server.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "Shell.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_I2C |
||||
#include "i2c.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_NVMC |
||||
#include "nvmc.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_PING |
||||
#include "ping_shell.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_SNTP_TIME |
||||
#include "sntp_time.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_TIME_DATE |
||||
#include "McuTimeDate.h" |
||||
#include "timer.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_ROBO_REMOTE |
||||
#include "robot.h" |
||||
#endif |
||||
#include "McuLib.h" |
||||
#include "McuGPIO.h" |
||||
#include "McuLED.h" |
||||
#include "McuUtility.h" |
||||
#include "McuWait.h" |
||||
#include "McuShell.h" |
||||
#include "McuRTOS.h" |
||||
#include "McuXFormat.h" |
||||
#include "McuLog.h" |
||||
#include "McuCriticalSection.h" |
||||
#include "esp32_mac.h" |
||||
#include "rs485.h" |
||||
|
||||
void PL_Init(void) { |
||||
McuLib_Init(); |
||||
McuLog_Init(); |
||||
McuWait_Init(); |
||||
McuRTOS_Init(); |
||||
McuUtility_Init(); |
||||
McuGPIO_Init(); |
||||
McuLED_Init(); |
||||
#if PL_CONFIG_USE_TIME_DATE |
||||
McuTimeDate_Init(); |
||||
TMR_Init(); |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_BLINKY |
||||
LED_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_WIFI |
||||
ESP_ERROR_CHECK(nvs_flash_init()); /* need to call this before using any WiFi functions */ |
||||
ESP32_MacInit(); |
||||
WiFi_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_SERVER |
||||
UDP_Server_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_CLIENT |
||||
UDP_Client_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_SHELL |
||||
McuXFormat_Init(); |
||||
McuShell_Init(); |
||||
SHELL_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_I2C |
||||
I2C_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_NVMC |
||||
NVMC_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_RS485 |
||||
RS485_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_PING |
||||
PING_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_SNTP_TIME |
||||
SNTP_Init(); |
||||
#endif |
||||
#if PL_CONFIG_USE_ROBO_REMOTE |
||||
ROBOT_Init(); |
||||
#endif |
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef PLATFORM_H_ |
||||
#define PLATFORM_H_ |
||||
|
||||
/* platform configuration macros: turn on to enable functionality */ |
||||
#define PL_CONFIG_USE_BLINKY (0) /*!< if using blinky LED */ |
||||
#define PL_CONFIG_USE_WIFI (0) /*!< if using WiFi/WLAN */ |
||||
#define PL_CONFIG_USE_IDENTIFY (0 && PL_CONFIG_USE_WIFI) /*!< used to identify ESP32 and robot, needed for EEE network */ |
||||
#define PL_CONFIG_USE_UDP_SERVER (0 && PL_CONFIG_USE_WIFI) /*!< UDP server, used for communication to robot */ |
||||
#define PL_CONFIG_USE_UDP_CLIENT (0 && PL_CONFIG_USE_WIFI) /*!< UDP client, optionally available for tests */ |
||||
#define PL_CONFIG_USE_PING (0 && PL_CONFIG_USE_WIFI) /*!< shell command with ping, to test network connection */ |
||||
|
||||
#define PL_CONFIG_USE_SHELL (0) /*!< implements shell between robot and ESP32 */ |
||||
#define PL_CONFIG_USE_RS485 (0) /*!< ESP32 using RS-485 to split-flaps */ |
||||
#define PL_CONFIG_USE_SNTP_TIME (0 && PL_CONFIG_USE_WIFI) |
||||
#define PL_CONFIG_USE_TIME_DATE (0) /*!< if using Time and Date information */ |
||||
#define PL_CONFIG_USE_ROBO_REMOTE (0 && PL_CONFIG_USE_UDP_SERVER) /* UDP Remote controller for robot */ |
||||
|
||||
/*! \brief Module and platform initialization */ |
||||
void PL_Init(void); |
||||
|
||||
/* the following ones are not implemented yet: */ |
||||
#define PL_CONFIG_USE_I2C (0) |
||||
#define PL_CONFIG_USE_NVMC (0) |
||||
#define PL_CONFIG_USE_WDT (0) /* NYI */ |
||||
|
||||
#endif /* PLATFORM_H_ */ |
||||
@ -0,0 +1,66 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_ROBO_REMOTE |
||||
#include "robot.h" |
||||
#include "udp_server.h" |
||||
#include "wifi.h" |
||||
#include "McuShell.h" |
||||
#include "McuUtility.h" |
||||
#include "Shell.h" |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
|
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
McuShell_SendStatusStr((unsigned char*)"robo", (unsigned char*)"ESP32 robo status\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"robo", (unsigned char*)"Group of robot commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows robot help or status\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" send <text>", (unsigned char*)"Send a text to the robot\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" sendcmd <cmd>", (unsigned char*)"Send a command to the robot, e.g. '#buzzer buz 100 200'\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t ROBOT_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"robo help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"robo status")==0) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"robo send ", sizeof("robo send ")-1)==0) { |
||||
const unsigned char *p; |
||||
|
||||
*handled = TRUE; |
||||
p = cmd+sizeof("robo send ")-1; |
||||
McuShell_SendStr(p, io->stdOut); /* send to standard I/O which is the UART to the robot */ |
||||
return ERR_OK; |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"robo sendcmd ", sizeof("robo sendcmd ")-1)==0) { |
||||
static uint8_t response[10*1024]; |
||||
const unsigned char *p; |
||||
|
||||
*handled = TRUE; |
||||
p = cmd+sizeof("robo sendcmd ")-1; |
||||
SHELL_SendToRobotAndGetResponse(p, response, sizeof(response)); |
||||
McuShell_SendStr(response, io->stdOut); /* show result on console */ |
||||
return ERR_OK; |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
void ROBOT_Deinit(void) { |
||||
/* nothing needed */ |
||||
} |
||||
|
||||
void ROBOT_Init(void) { |
||||
/* nothing needed */ |
||||
} |
||||
#endif /* PL_CONFIG_USE_ROBO_REMOTE */ |
||||
@ -0,0 +1,43 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef MAIN_ROBOT_H_ |
||||
#define MAIN_ROBOT_H_ |
||||
|
||||
|
||||
#include "platform.h" |
||||
#include <stdint.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Command line and shell handler |
||||
* \param cmd The command to be parsed |
||||
* \param handled If command has been recognized and handled |
||||
* \param io I/O handler to be used |
||||
* \return error code, otherwise ERR_OK |
||||
*/ |
||||
uint8_t ROBOT_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
/*! \brief Module de-initialization */ |
||||
void ROBOT_Deinit(void); |
||||
|
||||
/*! \brief Module initialization */ |
||||
void ROBOT_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
|
||||
|
||||
#endif /* MAIN_ROBOT_H_ */ |
||||
@ -0,0 +1,632 @@ |
||||
/*
|
||||
* Copyright (c) 2019-2022, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_RS485 |
||||
#include "rs485.h" |
||||
#include "McuGPIO.h" |
||||
#include "McuUart485.h" |
||||
#include "McuShell.h" |
||||
#include "McuRTOS.h" |
||||
#include "McuUtility.h" |
||||
#include "McuLog.h" |
||||
#include "Shell.h" |
||||
#if McuLib_CONFIG_CPU_IS_KINETIS || McuLib_CONFIG_CPU_IS_LPC |
||||
#include "nvmc.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_WDT |
||||
#include "watchdog.h" |
||||
#endif |
||||
#if 0 /* \TODO */
|
||||
#include "stepper.h" |
||||
#endif |
||||
|
||||
typedef enum RS485_Response_e { |
||||
RS485_RESPONSE_CONTINUE, /* continue scanning and parsing */ |
||||
RS485_RESPONSE_OK, /* ok response */ |
||||
RS485_RESPONSE_NOK, /* not ok response */ |
||||
RS485_RESPONSE_TIMEOUT, /* timeout */ |
||||
} RS485_Response_e; |
||||
|
||||
static bool RS485_DoLogging = false; /* if traffic on the bus shall be reported on the shell */ |
||||
static SemaphoreHandle_t RS485_stdioMutex; /* mutex to protect access to standard I/O */ |
||||
|
||||
uint8_t RS485_GetAddress(void) { |
||||
#if McuLib_CONFIG_CPU_IS_KINETIS || McuLib_CONFIG_CPU_IS_LPC |
||||
uint8_t addr = 0; |
||||
|
||||
if (NVMC_GetRS485Addr(&addr)==ERR_OK) { |
||||
return addr; |
||||
} |
||||
return 0; /* failed */ |
||||
#elif McuLib_CONFIG_CPU_IS_ESP32 |
||||
return 1; /* hard coded */ |
||||
#endif |
||||
} |
||||
|
||||
static void RS485_SendChar(unsigned char ch) { |
||||
McuUart485_stdio.stdOut(ch); |
||||
} |
||||
|
||||
static void RS485_NullSend(unsigned char ch) { |
||||
/* do nothing */ |
||||
} |
||||
|
||||
static void RS485_ReadChar(uint8_t *c) { |
||||
*c = '\0'; /* only sending on this channel */ |
||||
} |
||||
|
||||
static bool RS485_CharPresent(void) { |
||||
return false; /* only sending on this channel */ |
||||
} |
||||
|
||||
McuShell_ConstStdIOType RS485_stdio = { |
||||
.stdIn = (McuShell_StdIO_In_FctType)RS485_ReadChar, |
||||
.stdOut = (McuShell_StdIO_OutErr_FctType)RS485_SendChar, |
||||
.stdErr = (McuShell_StdIO_OutErr_FctType)RS485_SendChar, |
||||
.keyPressed = RS485_CharPresent, /* if input is not empty */ |
||||
#if McuShell_CONFIG_ECHO_ENABLED |
||||
.echoEnabled = false, |
||||
#endif |
||||
}; |
||||
|
||||
McuShell_ConstStdIOType RS485_stdioBroadcast = { |
||||
.stdIn = (McuShell_StdIO_In_FctType)RS485_ReadChar, |
||||
.stdOut = (McuShell_StdIO_OutErr_FctType)RS485_NullSend, |
||||
.stdErr = (McuShell_StdIO_OutErr_FctType)RS485_NullSend, |
||||
.keyPressed = RS485_CharPresent, /* if input is not empty */ |
||||
#if McuShell_CONFIG_ECHO_ENABLED |
||||
.echoEnabled = false, |
||||
#endif |
||||
}; |
||||
|
||||
/*-----------------------------------------------------------------------*/ |
||||
/* parser I/O handler, used to parse the returned data after sending a command to the RS-485 bus */ |
||||
static void RS485Parse_SendChar(unsigned char ch) { |
||||
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */ |
||||
McuUart485_stdio.stdOut(ch); |
||||
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */ |
||||
} |
||||
} |
||||
|
||||
static void RS485Parse_ReadChar(uint8_t *c) { |
||||
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */ |
||||
McuUart485_stdio.stdIn(c); |
||||
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */ |
||||
} |
||||
} |
||||
|
||||
static bool RS485Parse_CharPresent(void) { |
||||
bool inputPresent = false; |
||||
|
||||
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */ |
||||
inputPresent = McuUart485_stdio.keyPressed(); |
||||
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */ |
||||
} |
||||
return inputPresent; |
||||
} |
||||
|
||||
static McuShell_ConstStdIOType RS485Parse_stdio = { |
||||
.stdIn = (McuShell_StdIO_In_FctType)RS485Parse_ReadChar, |
||||
.stdOut = (McuShell_StdIO_OutErr_FctType)RS485Parse_SendChar, |
||||
.stdErr = (McuShell_StdIO_OutErr_FctType)RS485Parse_SendChar, |
||||
.keyPressed = RS485Parse_CharPresent, /* if input is not empty */ |
||||
#if McuShell_CONFIG_ECHO_ENABLED |
||||
.echoEnabled = false, |
||||
#endif |
||||
}; |
||||
/*-----------------------------------------------------------------------*/ |
||||
static void RS485_SendStr(unsigned char *str) { |
||||
while(*str!='\0') { |
||||
RS485_stdio.stdOut(*str++); |
||||
} |
||||
} |
||||
|
||||
static uint8_t CalcCRC(const uint8_t *data, uint8_t dataSize, uint8_t start) { |
||||
uint8_t crc, i, x, y; |
||||
|
||||
crc = start; |
||||
for(x=0;x<dataSize;x++){ |
||||
y = data[x]; |
||||
for(i=0;i<8;i++) { /* go through all bits of the data byte */ |
||||
if((crc&0x01)^(y&0x01)) { |
||||
crc >>= 1; |
||||
crc ^= 0x8c; |
||||
} else { |
||||
crc >>= 1; |
||||
} |
||||
y >>= 1; |
||||
} |
||||
} |
||||
return crc; |
||||
} |
||||
|
||||
static uint8_t CalcMsgCrc(const unsigned char *msg) { |
||||
/* header is "@dd ss cc ...", both dd, ss and cc are 8bit HEX values. The CRC (cc) is not included in the CRC */ |
||||
uint8_t crc; |
||||
|
||||
crc = CalcCRC(msg, sizeof("@dd ss ")-1, 0); |
||||
crc = CalcCRC(msg+sizeof("@dd ss cc")-1, strlen((char*)msg+sizeof("@dd ss cc")-1), crc); |
||||
return crc; |
||||
} |
||||
|
||||
typedef enum CMD_ParserState_e { |
||||
CMD_PARSER_INIT, |
||||
CMD_PARSER_START_DETECTED, /* start '@' detected */ |
||||
CMD_PARSER_SCAN_DST_ADDR, /* scanning destination address */ |
||||
CMD_PARSER_SCAN_SRC_ADDR, /* scan source address */ |
||||
CMD_PARSER_SCAN_CRC, /* scan CRC */ |
||||
CMD_PARSER_SCAN_OK_NOK, /* reading NOK or OK */ |
||||
} CMD_ParserState_e; |
||||
|
||||
static RS485_Response_e Scan(CMD_ParserState_e *state, unsigned char ch, unsigned char *buf, size_t bufSize, uint8_t fromAddr) { |
||||
/* scan for "@<dstaddr> <srcAddr> <CRC> OK"
|
||||
* or "@<dstaddr> <srcAddr> <CRC> NOK" |
||||
*/ |
||||
const unsigned char *p; |
||||
uint8_t addr; |
||||
uint8_t exp_crc; |
||||
static uint8_t crc = 0; |
||||
|
||||
if (ch=='@') { /* a marker? start scanning again */ |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
for(;;) { /* breaks or returns */ |
||||
switch(*state) { |
||||
case CMD_PARSER_INIT: |
||||
buf[0] = '\0'; /* reset buffer */ |
||||
if (ch=='@') { /* a marker? start scanning again */ |
||||
McuUtility_chcat(buf, bufSize, ch); |
||||
*state = CMD_PARSER_START_DETECTED; |
||||
break; /* continue state machine */ |
||||
} |
||||
return RS485_RESPONSE_CONTINUE; |
||||
|
||||
case CMD_PARSER_START_DETECTED: |
||||
*state = CMD_PARSER_SCAN_DST_ADDR; |
||||
return RS485_RESPONSE_CONTINUE; |
||||
|
||||
case CMD_PARSER_SCAN_DST_ADDR: |
||||
McuUtility_chcat(buf, bufSize, ch); |
||||
if (ch==' ') { |
||||
p = &buf[sizeof("@")-1]; |
||||
if (McuUtility_ScanHex8uNumberNoPrefix(&p, &addr)==ERR_OK && addr==RS485_GetAddress()) { |
||||
*state = CMD_PARSER_SCAN_SRC_ADDR; |
||||
return RS485_RESPONSE_CONTINUE; |
||||
} else { |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
} else if (ch=='\n') { /* failed */ |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
return RS485_RESPONSE_CONTINUE; |
||||
|
||||
case CMD_PARSER_SCAN_SRC_ADDR: |
||||
McuUtility_chcat(buf, bufSize, ch); |
||||
if (ch==' ') { |
||||
p = &buf[sizeof("@ss ")-1]; |
||||
if (McuUtility_ScanHex8uNumberNoPrefix(&p, &addr)==ERR_OK && addr==fromAddr) { |
||||
*state = CMD_PARSER_SCAN_CRC; |
||||
return RS485_RESPONSE_CONTINUE; |
||||
} else { |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
} else if (ch=='\n') { /* failed */ |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
return RS485_RESPONSE_CONTINUE; |
||||
|
||||
case CMD_PARSER_SCAN_CRC: |
||||
McuUtility_chcat(buf, bufSize, ch); |
||||
if (ch==' ') { |
||||
p = &buf[sizeof("@ss dd ")-1]; |
||||
if (McuUtility_ScanHex8uNumberNoPrefix(&p, &crc)==ERR_OK) { |
||||
*state = CMD_PARSER_SCAN_OK_NOK; |
||||
return RS485_RESPONSE_CONTINUE; |
||||
} else { |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
} else if (ch=='\n') { /* failed */ |
||||
*state = CMD_PARSER_INIT; |
||||
} |
||||
return RS485_RESPONSE_CONTINUE; |
||||
|
||||
case CMD_PARSER_SCAN_OK_NOK: |
||||
McuUtility_chcat(buf, bufSize, ch); |
||||
if (ch=='\n') { |
||||
p = &buf[sizeof("@ss dd cc ")-1]; |
||||
if (McuUtility_strcmp((char*)p, (char*)"OK\n")==0) { /* a match! */ |
||||
buf[sizeof("@ss dd cc OK")-1] = '\0'; /* overwrite '\n' as not included in CRC */ |
||||
exp_crc = CalcMsgCrc(buf); |
||||
*state = CMD_PARSER_INIT; |
||||
if (crc==exp_crc) { |
||||
return RS485_RESPONSE_OK; |
||||
} else { |
||||
return RS485_RESPONSE_NOK; |
||||
} |
||||
} else if (McuUtility_strcmp((char*)buf, (char*)"NOK\n")==0) { /* a match! */ |
||||
buf[sizeof("@ss dd cc NOK")-1] = '\0'; /* overwrite '\n' as not included in CRC */ |
||||
exp_crc = CalcMsgCrc(buf); |
||||
*state = CMD_PARSER_INIT; |
||||
if (crc==exp_crc) { |
||||
return RS485_RESPONSE_NOK; |
||||
} else { |
||||
return RS485_RESPONSE_NOK; /* CRC is not ok, and message is NOK too */ |
||||
} |
||||
} else if (ch=='\n') { /* failed */ |
||||
*state = CMD_PARSER_INIT; |
||||
break; /* continue in state machine */ |
||||
} |
||||
} |
||||
return RS485_RESPONSE_CONTINUE; |
||||
|
||||
default: |
||||
break; |
||||
} /* switch */ |
||||
/* get here with a break */ |
||||
} /* for */ |
||||
return RS485_RESPONSE_CONTINUE; |
||||
} |
||||
|
||||
static RS485_Response_e WaitForResponse(int32_t timeoutMs, uint8_t fromAddr, McuShell_ConstStdIOType *shellIO, McuShell_ConstStdIOType *rsIO) { |
||||
unsigned char buf[24] = ""; /* enough for "@<addr> <fromAddr> OK" or "@<addr> <fromAddr> NOK" */ |
||||
unsigned char ch; |
||||
RS485_Response_e resp; |
||||
CMD_ParserState_e state = CMD_PARSER_INIT; |
||||
|
||||
for(;;) { /* returns */ |
||||
/* read response text and write into buffer or to console */ |
||||
static unsigned char lineBuffer[512]; /* enough for a line of text coming back from the bus */ |
||||
|
||||
lineBuffer[0] = '\0'; /* initialize buffer */ |
||||
do { |
||||
rsIO->stdIn(&ch); |
||||
if (ch!='\0') { |
||||
McuUtility_chcat(lineBuffer, sizeof(lineBuffer), ch); |
||||
if (ch=='\n') { |
||||
if (lineBuffer[0]!='@') { /* do not send things like OK or NOK messages from bus */ |
||||
SHELL_SendStringToIO(lineBuffer, shellIO); |
||||
} |
||||
lineBuffer[0] = '\0'; /* reset buffer */ |
||||
} |
||||
} |
||||
} while(ch!='\0'); |
||||
|
||||
ch = McuUart485_GetResponseQueueChar(); |
||||
if (ch!='\0') { |
||||
resp = Scan(&state, ch, buf, sizeof(buf), fromAddr); |
||||
if (resp==RS485_RESPONSE_OK || resp==RS485_RESPONSE_NOK) { |
||||
return resp; |
||||
} |
||||
} else { /* empty response buffer: check normal incoming characters */ |
||||
vTaskDelay(pdMS_TO_TICKS(50)); |
||||
#if PL_CONFIG_USE_WDT |
||||
WDT_Report(WDT_REPORT_ID_CURR_TASK, 50); |
||||
#endif |
||||
timeoutMs -= 50; |
||||
if (timeoutMs<=0) { |
||||
return RS485_RESPONSE_TIMEOUT; |
||||
} |
||||
} |
||||
} /* for */ |
||||
return RS485_RESPONSE_CONTINUE; |
||||
} |
||||
|
||||
uint8_t RS485_SendCommand(uint8_t dstAddr, const unsigned char *cmd, int32_t timeoutMs, uint32_t nofRetry, McuShell_ConstStdIOType *shellIO, McuShell_ConstStdIOType *rsIO) { |
||||
/* example: send "@16 1 cmd stepper status" */ |
||||
unsigned char buf[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; |
||||
uint8_t res = ERR_OK; |
||||
RS485_Response_e resp; |
||||
uint8_t crc, hex; |
||||
|
||||
if (rsIO==NULL) { /* assign default */ |
||||
rsIO = &RS485Parse_stdio; |
||||
} |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)("@")); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), dstAddr); /* add destination address */ |
||||
McuUtility_chcat(buf, sizeof(buf), ' '); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress()); /* add src address */ |
||||
McuUtility_chcat(buf, sizeof(buf), ' '); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), 0); /* dummy crc, will be replaced with real one */ |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)(" cmd ")); |
||||
McuUtility_strcat(buf, sizeof(buf), cmd); |
||||
/* update crc */ |
||||
crc = CalcMsgCrc(buf); |
||||
hex = (char)((crc>>4) & 0x0F); |
||||
buf[sizeof("@dd ss ")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10))); |
||||
hex = (char)(crc & 0x0F); |
||||
buf[sizeof("@dd ss c")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10))); |
||||
|
||||
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */ |
||||
for(;;) { /* breaks */ |
||||
McuUart485_ClearResponseQueue(); /* clear up if there is something pending */ |
||||
if (RS485_DoLogging) { |
||||
McuLog_trace("Tx: %s", buf); |
||||
} |
||||
RS485_SendStr(buf); |
||||
RS485_SendStr((unsigned char*)"\n"); |
||||
if (dstAddr==RS485_BROADCAST_ADDRESS) { |
||||
/* do not wait for a OK/NOK response for broadcast messages. The caller has to check with 'lastError' */ |
||||
res = ERR_OK; |
||||
break; /* leave loop */ |
||||
} else { |
||||
vTaskDelay(pdMS_TO_TICKS(100)); /* give back some time for receiving a response */ |
||||
resp = WaitForResponse(timeoutMs, dstAddr, shellIO, rsIO); |
||||
if (resp==RS485_RESPONSE_OK) { |
||||
res = ERR_OK; |
||||
break; /* fine, leave loop */ |
||||
} else if (resp==RS485_RESPONSE_TIMEOUT) { /* board did not respond? */ |
||||
res = ERR_BUSOFF; /* retry */ |
||||
} else if (resp==RS485_RESPONSE_NOK) { /* not ok, crc error? */ |
||||
res = ERR_CRC; /* retry */ |
||||
} |
||||
} |
||||
/* NOK or timeout */ |
||||
if (nofRetry==0) { /* tried enough */ |
||||
res = ERR_FAILED; |
||||
break; /* leave loop */ |
||||
} |
||||
nofRetry--; /* try again */ |
||||
} /* for */ |
||||
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */ |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
uint8_t buf[16]; |
||||
|
||||
McuShell_SendStatusStr((unsigned char*)"rs", (unsigned char*)"RS-485 settings\r\n", io->stdOut); |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x"); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress()); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
McuShell_SendStatusStr((unsigned char*)" addr", buf, io->stdOut); |
||||
McuShell_SendStatusStr((unsigned char*)" log", RS485_DoLogging?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"rs", (unsigned char*)"Group of RS-485 commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut); |
||||
#if PL_CONFIG_USE_NVMC |
||||
McuShell_SendHelpStr((unsigned char*)" addr <addr>", (unsigned char*)"Set RS-485 address\r\n", io->stdOut); |
||||
#endif |
||||
McuShell_SendHelpStr((unsigned char*)" send <text>", (unsigned char*)"Send a text to the RS-485 bus\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" sendcmd <addr> <cmd>", (unsigned char*)"Send a shell command to the RS-485 address and check response\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" log on|off", (unsigned char*)"Log RS-485 bus activity to McuLog\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t RS485_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
const unsigned char *p; |
||||
int32_t val; |
||||
|
||||
if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "rs help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "rs status")==0)) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} else if (McuUtility_strncmp((char*)cmd, "rs log ", sizeof("rs log ")-1)==0) { |
||||
*handled = TRUE; |
||||
p = cmd + sizeof("rs log ")-1; |
||||
if (McuUtility_strcmp((char*)p, "on")==0) { |
||||
RS485_DoLogging = true; |
||||
return ERR_OK; |
||||
} else if (McuUtility_strcmp((char*)p, "off")==0) { |
||||
RS485_DoLogging = false; |
||||
return ERR_OK; |
||||
} |
||||
return ERR_FAILED; |
||||
#if PL_CONFIG_USE_NVMC |
||||
} else if (McuUtility_strncmp((char*)cmd, "rs addr ", sizeof("rs addr ")-1)==0) { |
||||
*handled = true; |
||||
p = cmd + sizeof("rs addr ")-1; |
||||
if (McuUtility_xatoi(&p, &val)==ERR_OK && val>=0 && val<=0xff) { |
||||
return NVMC_SetRS485Addr(val); |
||||
} |
||||
return ERR_FAILED; |
||||
#endif |
||||
} else if (McuUtility_strncmp((char*)cmd, "rs send ", sizeof("rs send ")-1)==0) { |
||||
*handled = true; |
||||
RS485_SendStr((unsigned char*)cmd+sizeof("rs send ")-1); |
||||
RS485_SendStr((unsigned char*)("\n")); |
||||
} else if (McuUtility_strncmp((char*)cmd, "rs sendcmd ", sizeof("rs sendcmd ")-1)==0) { |
||||
*handled = true; |
||||
p = cmd + sizeof("rs sendcmd ")-1; |
||||
if (McuUtility_xatoi(&p, &val)==ERR_OK) { /* parse destination address */ |
||||
unsigned char buffer[McuShell_CONFIG_DEFAULT_SHELL_BUFFER_SIZE]; |
||||
|
||||
while (*p==' ') { /* skip leading spaces */ |
||||
p++; |
||||
} |
||||
if (*p=='"') { /* double-quoted command: it can contain multiple commands */ |
||||
if (McuUtility_ScanDoubleQuotedString(&p, buffer, sizeof(buffer))!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
p = buffer; |
||||
} |
||||
return RS485_SendCommand(val, (unsigned char*)p, 10000, 0, io, &RS485Parse_stdio); /* 10 seconds should be enough */ |
||||
} |
||||
return ERR_FAILED; |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t CheckHeader(unsigned char *msg, const unsigned char **startCmd, uint8_t *sourceAddr, uint8_t *destinationAddr) { |
||||
/* format is in the form "@<DST_ADDR> <SRC_ADDR> <CRC> cmd help" */ |
||||
const unsigned char *p; |
||||
uint8_t dstAddr, srcAddr; |
||||
uint8_t expected_crc, crc, res; |
||||
unsigned char buf[42]; |
||||
|
||||
/* init with defaults in case of error */ |
||||
*sourceAddr = RS485_BROADCAST_ADDRESS; |
||||
*destinationAddr = RS485_BROADCAST_ADDRESS; |
||||
*startCmd = msg; |
||||
if (*msg=='@') { |
||||
p = msg+1; /* skip '@' */ |
||||
/* check for "@<DST_ADDR> <SRC_ADDR>" */ |
||||
if ( McuUtility_ScanHex8uNumberNoPrefix(&p, &dstAddr)==ERR_OK |
||||
&& (dstAddr==RS485_GetAddress() || dstAddr==RS485_BROADCAST_ADDRESS) /* broadcast or matching destination address */ |
||||
&& McuUtility_ScanHex8uNumberNoPrefix(&p, &srcAddr)==ERR_OK /* get source address */ |
||||
) |
||||
{ |
||||
*sourceAddr = srcAddr; |
||||
*destinationAddr = dstAddr; |
||||
/* check CRC */ |
||||
res = McuUtility_ScanHex8uNumberNoPrefix(&p, &crc); |
||||
if (res!=ERR_OK) { |
||||
return ERR_CRC; |
||||
} |
||||
expected_crc = CalcMsgCrc(msg); |
||||
if (crc!=expected_crc) { |
||||
if (dstAddr!=RS485_BROADCAST_ADDRESS) { /* only send back error if it was not a broadcast */ |
||||
McuUtility_strcpy(buf, sizeof(buf), (uint8_t*)"CRC_ERR 0x"); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress()); |
||||
McuUtility_strcat(buf, sizeof(buf), (uint8_t*)": 0x"); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), crc); |
||||
McuUtility_strcat(buf, sizeof(buf), (uint8_t*)" expected 0x"); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), expected_crc); |
||||
McuUtility_chcat(buf, sizeof(buf), '\n'); |
||||
McuShell_SendStr(buf, RS485_stdio.stdErr); |
||||
} |
||||
return ERR_CRC; |
||||
} |
||||
*startCmd = p; |
||||
return ERR_OK; |
||||
} |
||||
} |
||||
return ERR_FAILED; |
||||
} |
||||
|
||||
static void RS485Task(void *pv) { |
||||
static uint8_t cmdBuf[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; /* command line and text from the RS-485 bus */ |
||||
const unsigned char *startCmd; |
||||
uint8_t srcAddr, dstAddr; |
||||
uint8_t res, crc; |
||||
uint8_t buf[32]; |
||||
unsigned char hex; |
||||
bool reply; |
||||
static uint8_t lastError = ERR_OK; |
||||
|
||||
(void)pv; /* not used */ |
||||
McuLog_trace("Starting RS485 Task"); |
||||
#if PL_CONFIG_USE_WDT |
||||
WDT_SetTaskHandle(WDT_REPORT_ID_TASK_RS485, xTaskGetCurrentTaskHandle()); |
||||
#endif |
||||
cmdBuf[0] = '\0'; |
||||
for(;;) { |
||||
while (!McuUart485_stdio.keyPressed()) { /* if nothing in input queue, give back some CPU time */ |
||||
vTaskDelay(pdMS_TO_TICKS(10)); |
||||
#if PL_CONFIG_USE_WDT |
||||
WDT_Report(WDT_REPORT_ID_TASK_RS485, 10); |
||||
#endif |
||||
} |
||||
if (McuShell_ReadCommandLine(cmdBuf, sizeof(cmdBuf), &RS485Parse_stdio)==ERR_OK) { |
||||
reply = false; |
||||
srcAddr = RS485_ILLEGAL_ADDRESS; |
||||
dstAddr = RS485_ILLEGAL_ADDRESS; |
||||
if (cmdBuf[0]=='@' && strlen((char*)cmdBuf)>sizeof("@dd ss cc ")-1) { /* have a valid message? */ |
||||
if (RS485_DoLogging) { |
||||
McuLog_trace("Rx: %s", cmdBuf); |
||||
} |
||||
reply = false; /* default */ |
||||
res = CheckHeader(cmdBuf, &startCmd, &srcAddr, &dstAddr); |
||||
if (res == ERR_CRC) { /* wrong crc */ |
||||
lastError = ERR_CRC; |
||||
reply = true; |
||||
} else if (res==ERR_OK) { /* header was ok */ |
||||
res = ERR_FAILED; /* set default return value */ |
||||
if (McuUtility_strcmp((char*)startCmd, (char*)" cmd lastError")==0) { |
||||
reply = true; |
||||
res = lastError; /* report back last error */ |
||||
lastError = ERR_OK; /* clear error */ |
||||
} else if (McuUtility_strcmp((char*)startCmd, (char*)" cmd idle")==0) { |
||||
reply = true; |
||||
#if 0 /* \TODO */
|
||||
if (STEPPER_IsIdle()) { |
||||
res = ERR_OK; /* ERR_OK if board is idle */ |
||||
} else { |
||||
res = ERR_FAILED; /* not idle */ |
||||
} |
||||
#else |
||||
res = ERR_FAILED; /* not idle */ |
||||
#endif |
||||
} else if (McuUtility_strncmp((char*)startCmd, " cmd ", sizeof(" cmd ")-1)==0) { /* shell command? */ |
||||
McuUart485_ClearResponseQueue(); /* clear any pending response: we are going to parse a new command */ |
||||
startCmd += sizeof(" cmd ")-1; |
||||
if (dstAddr==RS485_BROADCAST_ADDRESS) { |
||||
res = SHELL_ParseCommandIO(startCmd, &RS485_stdioBroadcast, true); /* do not write anything back if broadcast */ |
||||
} else { |
||||
res = SHELL_ParseCommandIO(startCmd, &RS485_stdio, true); |
||||
} |
||||
lastError = res; /* remember error status if we get asked later on */ |
||||
reply = true; |
||||
} else if (McuUtility_strcmp((char*)startCmd, (char*)" OK")==0) { |
||||
reply = false; |
||||
} else if (McuUtility_strcmp((char*)startCmd, (char*)" NOK")==0) { |
||||
reply = false; |
||||
} |
||||
} |
||||
} else { |
||||
/* not starting with '@', print it ... */ |
||||
McuUtility_strcat(cmdBuf, sizeof(cmdBuf), (unsigned char*)"\r\n"); /* for the shell parser, the new-line has been removed. Add it again for output */ |
||||
SHELL_SendString((unsigned char *)cmdBuf); /* \TODO do not send directly to UART: instead, use a stdio which buffers the output */ |
||||
} |
||||
cmdBuf[0] = '\0'; /* reset buffer for next iteration */ |
||||
/* send response back to sender */ |
||||
if (reply && dstAddr!=RS485_BROADCAST_ADDRESS) { /* normal message, send response. For broadcasts it is up to the caller to check the last error */ |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"@"); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), srcAddr); |
||||
McuUtility_chcat(buf, sizeof(buf), ' '); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress()); |
||||
McuUtility_chcat(buf, sizeof(buf), ' '); |
||||
McuUtility_strcatNum8Hex(buf, sizeof(buf), 0); /* dummy crc, will be replaced later */ |
||||
if (res==ERR_OK) { |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" OK"); |
||||
} else { |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" NOK"); |
||||
} |
||||
crc = CalcMsgCrc(buf); |
||||
hex = (char)((crc>>4) & 0x0F); |
||||
buf[sizeof("@dd ss ")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10))); |
||||
hex = (char)(crc & 0x0F); |
||||
buf[sizeof("@dd ss c")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10))); |
||||
McuUtility_chcat(buf, sizeof(buf), '\n'); |
||||
RS485_SendStr(buf); |
||||
} |
||||
} |
||||
} /* for */ |
||||
} |
||||
|
||||
void RS485_Deinit(void) { |
||||
McuUart485_Deinit(); |
||||
} |
||||
|
||||
void RS485_Init(void) { |
||||
McuUart485_Init(); |
||||
if (xTaskCreate( |
||||
RS485Task, /* pointer to the task */ |
||||
"RS-485", /* task name for kernel awareness debugging */ |
||||
1300/sizeof(StackType_t), /* task stack size */ |
||||
(void*)NULL, /* optional task startup argument */ |
||||
tskIDLE_PRIORITY+4, /* initial priority */ |
||||
(TaskHandle_t*)NULL /* optional task handle to create */ |
||||
) != pdPASS) |
||||
{ |
||||
McuLog_fatal("Failed creating RS-485 task"); |
||||
for(;;){} /* error! probably out of memory */ |
||||
} |
||||
RS485_stdioMutex = xSemaphoreCreateRecursiveMutex(); |
||||
if (RS485_stdioMutex==NULL) { /* creation failed? */ |
||||
McuLog_fatal("Failed creating RS-485 Standard I/O mutex"); |
||||
for(;;); |
||||
} |
||||
vQueueAddToRegistry(RS485_stdioMutex, "RS485StdIoMutex"); |
||||
} |
||||
|
||||
#endif /* PL_CONFIG_USE_RS485 */ |
||||
@ -0,0 +1,61 @@ |
||||
/*
|
||||
* Copyright (c) 2019-2022, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef RS485_H_ |
||||
#define RS485_H_ |
||||
|
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
#include "McuShell.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*! special pre-defined node addresses */ |
||||
#define RS485_BROADCAST_ADDRESS (0x00) |
||||
/*!< special broadcast address */ |
||||
#define RS485_ILLEGAL_ADDRESS (0xff) |
||||
/*!< illegal/initialization value */ |
||||
|
||||
/*!
|
||||
* \brief Send a shell command to the RS-485 bus |
||||
* \param dstAddr Destination node address |
||||
* \param cmd The shell command string |
||||
* \param timeoutMs timeout in milliseconds to wait for a response |
||||
* \param nofRetry Number of retries |
||||
* \param shellIO I/O handler of the calling shell, used for writing output of the command |
||||
* \param rsIO I/O handler for the RS-485 bus, used to read incoming characters |
||||
* \return Error code, or ERR_OK |
||||
*/ |
||||
uint8_t RS485_SendCommand(uint8_t dstAddr, const unsigned char *cmd, int32_t timeoutMs, uint32_t nofRetry, McuShell_ConstStdIOType *shellIO, McuShell_ConstStdIOType *rsIO); |
||||
|
||||
/*!
|
||||
* \brief Getter for RS-485 node address |
||||
* \return address of node |
||||
*/ |
||||
uint8_t RS485_GetAddress(void); |
||||
|
||||
/*!
|
||||
* \brief Shell command line parser |
||||
* \param cmd command to be parsed |
||||
* \param handled if command was recognized |
||||
* \param io I/O handler |
||||
* \return Error code, or ERR_OK |
||||
*/ |
||||
uint8_t RS485_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io); |
||||
|
||||
/*! \brief Module de-initialization */ |
||||
void RS485_Deinit(void); |
||||
|
||||
/*! \brief Module initialization */ |
||||
void RS485_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* RS485_H_ */ |
||||
@ -0,0 +1,89 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_SNTP_TIME |
||||
#include "sntp_time.h" |
||||
#include "esp_sntp.h" |
||||
#include "esp_log.h" |
||||
#include "wifi.h" |
||||
#include "McuTimeDate.h" |
||||
#include "McuLog.h" |
||||
|
||||
static void SNTP_SetTime(void) { |
||||
time_t now = 0; |
||||
struct tm timeinfo = { 0 }; |
||||
|
||||
time(&now); |
||||
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); /* see https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv */ |
||||
tzset(); |
||||
localtime_r(&now, &timeinfo); |
||||
|
||||
char strftime_buf[64]; |
||||
|
||||
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo); |
||||
McuLog_info("The current date/time is: %s", strftime_buf); |
||||
|
||||
#if PL_CONFIG_USE_TIME_DATE |
||||
uint32_t seconds; |
||||
TIMEREC timeRec; |
||||
DATEREC dateRec; |
||||
|
||||
seconds = (uint32_t)time(NULL); /* get number of seconds since 1970 */ |
||||
McuTimeDate_UnixSecondsToTimeDateCustom(seconds, -1, &timeRec, &dateRec, 1970); |
||||
McuTimeDate_SetTimeDate(&timeRec, &dateRec); |
||||
#endif |
||||
} |
||||
|
||||
void time_sync_notification_cb(struct timeval *tv) { |
||||
McuLog_info("Notification of a time synchronization event"); |
||||
SNTP_SetTime(); |
||||
} |
||||
|
||||
static void initialize_sntp(void) { |
||||
McuLog_info("Initializing SNTP"); |
||||
sntp_setoperatingmode(SNTP_OPMODE_POLL); |
||||
sntp_setservername(0, "pool.ntp.org"); |
||||
sntp_set_time_sync_notification_cb(time_sync_notification_cb); |
||||
#ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH |
||||
sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH); |
||||
#endif |
||||
sntp_init(); |
||||
} |
||||
|
||||
int SNTP_ObtainTime(void) { |
||||
const int retry_count = 10; |
||||
int retry = 0; |
||||
|
||||
if (!WiFi_isConnected()) { |
||||
McuLog_error("Cannot get sntp time, WiFi not connected"); |
||||
return -1; /* failure */ |
||||
} |
||||
/**
|
||||
* NTP server address could be acquired via DHCP, |
||||
* see LWIP_DHCP_GET_NTP_SRV menuconfig option |
||||
*/ |
||||
#ifdef LWIP_DHCP_GET_NTP_SRV |
||||
sntp_servermode_dhcp(1); |
||||
#endif |
||||
|
||||
initialize_sntp(); |
||||
|
||||
/* wait for time to be set */ |
||||
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) { |
||||
McuLog_info("Waiting for system time to be set... (%d/%d)", retry, retry_count); |
||||
vTaskDelay(pdMS_TO_TICKS(2000)); |
||||
} |
||||
/* here we have received a valid time/date from the SNTP server */ |
||||
return 0; /* ok */ |
||||
} |
||||
|
||||
void SNTP_Init(void) { |
||||
/* nothing needed */ |
||||
} |
||||
|
||||
#endif /* PL_CONFIG_USE_SNTP_TIME */ |
||||
@ -0,0 +1,27 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef MAIN_SNTP_TIME_H_ |
||||
#define MAIN_SNTP_TIME_H_ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*!
|
||||
* \brief Get the time from the sntp server |
||||
* \return 0 if time has been obtained, negative value in case of error. |
||||
*/ |
||||
int SNTP_ObtainTime(void); |
||||
|
||||
/*! \brief module initialization */ |
||||
void SNTP_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* MAIN_SNTP_TIME_H_ */ |
||||
@ -0,0 +1,31 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_TIME_DATE |
||||
#include "timer.h" |
||||
#include "McuRTOS.h" |
||||
#include "esp_log.h" |
||||
#include "McuTimeDate.h" |
||||
|
||||
static const char *TAG = "Timer"; |
||||
|
||||
static TimerHandle_t timer; |
||||
|
||||
static void timerCallback(TimerHandle_t xTimer) { |
||||
McuTimeDate_AddTick(); |
||||
} |
||||
|
||||
void TMR_Init(void) { |
||||
/* create auto-reload timer to update software RTC */ |
||||
timer = xTimerCreate("timer", pdMS_TO_TICKS(McuTimeDate_CONFIG_TICK_TIME_MS), pdTRUE, NULL, timerCallback); |
||||
if (timer==NULL) { |
||||
ESP_LOGE(TAG, "Failed creating timer"); |
||||
return; |
||||
} |
||||
xTimerStart(timer, 0); |
||||
} |
||||
#endif /* PL_CONFIG_USE_TIME_DATE */ |
||||
@ -0,0 +1,21 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef MAIN_TIMER_H_ |
||||
#define MAIN_TIMER_H_ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
/*! \brief Module initialization */ |
||||
void TMR_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* MAIN_TIMER_H_ */ |
||||
@ -0,0 +1,303 @@ |
||||
/* BSD Socket API Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||
|
||||
Unless required by applicable law or agreed to in writing, this |
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||
CONDITIONS OF ANY KIND, either express or implied. |
||||
*/ |
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_UDP_CLIENT |
||||
#include <string.h> |
||||
#include <sys/param.h> |
||||
#include "freertos/FreeRTOS.h" |
||||
#include "freertos/task.h" |
||||
#include "freertos/event_groups.h" |
||||
#include "esp_system.h" |
||||
#include "esp_wifi.h" |
||||
#include "esp_event.h" |
||||
#include "esp_log.h" |
||||
#include "nvs_flash.h" |
||||
#include "esp_netif.h" |
||||
#include "lwip/err.h" |
||||
#include "lwip/sockets.h" |
||||
#include "lwip/sys.h" |
||||
#include <lwip/netdb.h> |
||||
|
||||
#include "udp_client.h" |
||||
#include "McuShell.h" |
||||
#include "McuUtility.h" |
||||
|
||||
#define CONFIG_EXAMPLE_IPV4 |
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4 |
||||
#define HOST_IP_ADDR "192.168.0.146" |
||||
#else |
||||
#define HOST_IP_ADDR "FE80::30AD:E57B:C212:68AD" /*CONFIG_EXAMPLE_IPV6_ADDR*/ |
||||
#endif |
||||
#define PORT 3333 |
||||
|
||||
static uint16_t udp_client_destination_port = PORT; |
||||
static unsigned char udp_client_destination_host[24] = HOST_IP_ADDR; |
||||
|
||||
static const char *TAG = "udp_client"; |
||||
static TaskHandle_t taskHandle = NULL; /* udp client task handle */ |
||||
|
||||
#if 0 |
||||
static const char *payload = "Message from ESP32 "; |
||||
|
||||
static void udp_client_task(void *pvParameters) { |
||||
char rx_buffer[128]; |
||||
char addr_str[128]; |
||||
int addr_family; |
||||
int ip_protocol; |
||||
|
||||
vTaskSuspend(NULL); /* UDP_Client_Start() will wake me up */ |
||||
for(;;) { |
||||
#ifdef CONFIG_EXAMPLE_IPV4 |
||||
struct sockaddr_in dest_addr; |
||||
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR); |
||||
dest_addr.sin_family = AF_INET; |
||||
dest_addr.sin_port = htons(PORT); |
||||
addr_family = AF_INET; |
||||
ip_protocol = IPPROTO_IP; |
||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); |
||||
#else /* IPV6 */ |
||||
struct sockaddr_in6 dest_addr; |
||||
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr); |
||||
dest_addr.sin6_family = AF_INET6; |
||||
dest_addr.sin6_port = htons(PORT); |
||||
addr_family = AF_INET6; |
||||
ip_protocol = IPPROTO_IPV6; |
||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); |
||||
#endif |
||||
|
||||
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); |
||||
if (sock < 0) { |
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); |
||||
break; |
||||
} |
||||
ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, PORT); |
||||
while (1) { |
||||
int err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); |
||||
if (err < 0) { |
||||
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); |
||||
break; |
||||
} |
||||
ESP_LOGI(TAG, "Message sent"); |
||||
|
||||
struct timeval to; |
||||
|
||||
to.tv_sec = 1; |
||||
to.tv_usec = 0; |
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to)) < 0) { |
||||
ESP_LOGE(TAG, "setting socket timeout failed"); |
||||
} |
||||
|
||||
ESP_LOGI(TAG, "Waiting for response"); |
||||
struct sockaddr_in source_addr; /* Large enough for both IPv4 or IPv6 */ |
||||
socklen_t socklen = sizeof(source_addr); |
||||
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen); |
||||
|
||||
/* Error occurred during receiving */ |
||||
if (len < 0) { |
||||
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); |
||||
break; |
||||
} else { /* Data received */ |
||||
rx_buffer[len] = 0; /* Null-terminate whatever we received and treat like a string */ |
||||
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str); |
||||
ESP_LOGI(TAG, "%s", rx_buffer); |
||||
} |
||||
vTaskDelay(pdMS_TO_TICKS(2000)); |
||||
} |
||||
if (sock != -1) { |
||||
ESP_LOGE(TAG, "Shutting down socket and restarting..."); |
||||
shutdown(sock, 0); |
||||
close(sock); |
||||
} |
||||
} /* for */ |
||||
vTaskDelete(NULL); |
||||
} |
||||
#endif |
||||
|
||||
static uint8_t udp_client_send(const unsigned char *host, uint16_t port, const unsigned char *msg, unsigned char *rxBuffer, size_t rxBufferSize) { |
||||
char addr_str[128]; |
||||
int addr_family; |
||||
int ip_protocol; |
||||
uint8_t res = ERR_OK; |
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4 |
||||
struct sockaddr_in dest_addr; |
||||
dest_addr.sin_addr.s_addr = inet_addr((const char*)host); |
||||
dest_addr.sin_family = AF_INET; |
||||
dest_addr.sin_port = htons(port); |
||||
addr_family = AF_INET; |
||||
ip_protocol = IPPROTO_IP; |
||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); |
||||
#else /* IPV6 */ |
||||
struct sockaddr_in6 dest_addr; |
||||
inet6_aton(host, &dest_addr.sin6_addr); |
||||
dest_addr.sin6_family = AF_INET6; |
||||
dest_addr.sin6_port = htons(port); |
||||
addr_family = AF_INET6; |
||||
ip_protocol = IPPROTO_IPV6; |
||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); |
||||
#endif |
||||
|
||||
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); |
||||
for(;;) { /* breaks in in case of error */ |
||||
if (sock < 0) { |
||||
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno); |
||||
res = ERR_FAILED; |
||||
break; |
||||
} |
||||
ESP_LOGI(TAG, "Socket created, sending to %s:%d", host, port); |
||||
|
||||
int err = sendto(sock, msg, strlen((char*)msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); |
||||
if (err < 0) { |
||||
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno); |
||||
res = ERR_FAILED; |
||||
break; |
||||
} |
||||
ESP_LOGI(TAG, "Message sent"); |
||||
|
||||
struct timeval to; |
||||
|
||||
to.tv_sec = 1; |
||||
to.tv_usec = 0; |
||||
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to)) < 0) { |
||||
ESP_LOGE(TAG, "setting socket timeout failed"); |
||||
res = ERR_FAILED; |
||||
break; |
||||
} |
||||
|
||||
ESP_LOGI(TAG, "Waiting for response"); |
||||
struct sockaddr_in source_addr; /* Large enough for both IPv4 or IPv6 */ |
||||
socklen_t socklen = sizeof(source_addr); |
||||
int len = recvfrom(sock, rxBuffer, rxBufferSize-1, 0, (struct sockaddr *)&source_addr, &socklen); |
||||
|
||||
/* Error occurred during receiving */ |
||||
if (len < 0) { |
||||
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno); |
||||
res = ERR_FAILED; |
||||
break; |
||||
} else { /* Data received */ |
||||
rxBuffer[len] = '\0'; /* Null-terminate whatever we received and treat like a string */ |
||||
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str); |
||||
ESP_LOGI(TAG, "%s", rxBuffer); |
||||
} |
||||
break; |
||||
} /* for */ |
||||
if (sock != -1) { |
||||
ESP_LOGE(TAG, "Shutting down socket"); |
||||
shutdown(sock, 0); |
||||
close(sock); |
||||
} |
||||
return res; |
||||
} |
||||
|
||||
void UDP_Client_Start(void) { |
||||
if (taskHandle!=NULL) { |
||||
vTaskResume(taskHandle); |
||||
} |
||||
} |
||||
|
||||
void UDP_Client_Stop(void) { |
||||
if (taskHandle!=NULL) { |
||||
vTaskSuspend(taskHandle); |
||||
} |
||||
} |
||||
|
||||
void UDP_Client_Init(void) { |
||||
#if 0 |
||||
BaseType_t res; |
||||
|
||||
res = xTaskCreate(udp_client_task, "udp_client", 16*1024/sizeof(StackType_t), NULL, 5, &taskHandle); |
||||
if (res==pdPASS) { |
||||
ESP_LOGI(TAG, "created UDP client task"); |
||||
} else { |
||||
ESP_LOGE(TAG, "failed creating UDP client task!"); |
||||
} |
||||
#endif |
||||
} |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
unsigned char buf[64]; |
||||
|
||||
McuShell_SendStatusStr((unsigned char*)"udpc", (unsigned char*)"UDP client status\r\n", io->stdOut); |
||||
|
||||
McuUtility_strcpy(buf, sizeof(buf), udp_client_destination_host); |
||||
McuUtility_chcat(buf, sizeof(buf), ':'); |
||||
McuUtility_strcatNum16u(buf, sizeof(buf), udp_client_destination_port); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
McuShell_SendStatusStr((unsigned char*)" host", buf, io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"udpc", (unsigned char*)"Group of UDP client commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows motor help or status\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" host <string>", (unsigned char*)"Set default host destination IP address (double quoted)\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" port <number>", (unsigned char*)"Set default host destination port number\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" send default <msg>", (unsigned char*)"Send message (double quoted) to default host and port\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" send <ip> <port> <msg>", (unsigned char*)"Send message (double quoted) to ip (double quoted) and port\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t UDP_Client_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
const unsigned char *p; |
||||
unsigned char msgBuf[64]; |
||||
unsigned char rxBuf[64]; |
||||
unsigned char ip[24]; |
||||
uint16_t port; |
||||
|
||||
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"udpc help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"udpc status")==0) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc host ", sizeof("udpc host ")-1)==0) { |
||||
*handled = TRUE; |
||||
p = cmd + sizeof("udpc host ")-1; |
||||
return McuUtility_ScanDoubleQuotedString(&p, udp_client_destination_host, sizeof(udp_client_destination_host)); |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc port ", sizeof("udpc port ")-1)==0) { |
||||
*handled = TRUE; |
||||
p = cmd + sizeof("udpc port ")-1; |
||||
return McuUtility_ScanDecimal16uNumber(&p, &udp_client_destination_port); |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc send default ", sizeof("udpc send default ")-1)==0) { |
||||
*handled = TRUE; |
||||
p = cmd + sizeof("udpc send default ")-1; |
||||
if (McuUtility_ScanDoubleQuotedString(&p, msgBuf, sizeof(msgBuf))!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
if (udp_client_send(udp_client_destination_host, udp_client_destination_port, msgBuf, rxBuf, sizeof(rxBuf))!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
McuShell_SendStr(rxBuf, io->stdOut); |
||||
return ERR_OK; |
||||
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc send ", sizeof("udpc send ")-1)==0) { |
||||
*handled = TRUE; |
||||
p = cmd + sizeof("udpc send ")-1; |
||||
if (McuUtility_ScanDoubleQuotedString(&p, ip, sizeof(ip))!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
if (McuUtility_ScanDecimal16uNumber(&p, &port)!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
if (McuUtility_ScanDoubleQuotedString(&p, msgBuf, sizeof(msgBuf))!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
if (udp_client_send(ip, port, msgBuf, rxBuf, sizeof(rxBuf))!=ERR_OK) { |
||||
return ERR_FAILED; |
||||
} |
||||
McuShell_SendStr(rxBuf, io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
#endif /* PL_CONFIG_USE_UDP_CLIENT */ |
||||
@ -0,0 +1,39 @@ |
||||
/*
|
||||
* Copyright (c) 2019, 2020, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef UDP_CLIENT_H_ |
||||
#define UDP_CLIENT_H_ |
||||
|
||||
#include "platform.h" |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Command line and shell handler |
||||
* \param cmd The command to be parsed |
||||
* \param handled If command has been recognized and handled |
||||
* \param io I/O handler to be used |
||||
* \return error code, otherwise ERR_OK |
||||
*/ |
||||
uint8_t UDP_Client_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif |
||||
|
||||
void UDP_Client_Start(void); |
||||
void UDP_Client_Stop(void); |
||||
|
||||
/*! \brief Module initialization */ |
||||
void UDP_Client_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UDP_CLIENT_H_ */ |
||||
@ -0,0 +1,151 @@ |
||||
/* BSD Socket API Example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.) |
||||
|
||||
Unless required by applicable law or agreed to in writing, this |
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
||||
CONDITIONS OF ANY KIND, either express or implied. |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_UDP_SERVER |
||||
#include "udp_server.h" |
||||
#include <string.h> |
||||
#include <sys/param.h> |
||||
#include "freertos/FreeRTOS.h" |
||||
#include "freertos/task.h" |
||||
#include "esp_system.h" |
||||
#include "esp_wifi.h" |
||||
#include "esp_event.h" |
||||
#include "esp_log.h" |
||||
#include "nvs_flash.h" |
||||
#include "esp_netif.h" |
||||
|
||||
#include "lwip/err.h" |
||||
#include "lwip/sockets.h" |
||||
#include "lwip/sys.h" |
||||
#include <lwip/netdb.h> |
||||
|
||||
#include "Shell.h" |
||||
#include "McuUtility.h" |
||||
#include "McuLog.h" |
||||
|
||||
#define CONFIG_EXAMPLE_IPV4 |
||||
|
||||
static TaskHandle_t taskHandle = NULL; /* udp server task handle */ |
||||
|
||||
static int SendToSocket(int sock, const char *msg, const struct sockaddr *to, socklen_t tolen) { |
||||
return sendto(sock, msg, McuUtility_strlen((char*)msg), 0, to, tolen); |
||||
} |
||||
|
||||
static void udp_server_task(void *pvParameters) { |
||||
char rx_buffer[128]; |
||||
char addr_str[128]; |
||||
int addr_family; |
||||
int ip_protocol; |
||||
|
||||
vTaskSuspend(NULL); /* UDP_Server_Start() will wake me up */ |
||||
for(;;) { |
||||
#ifdef CONFIG_EXAMPLE_IPV4 |
||||
struct sockaddr_in dest_addr; |
||||
|
||||
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); /** 0.0.0.0 */ |
||||
dest_addr.sin_family = AF_INET; |
||||
dest_addr.sin_port = htons(UDP_SERVER_PORT); |
||||
addr_family = AF_INET; |
||||
ip_protocol = IPPROTO_IP; |
||||
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1); |
||||
#else // IPV6
|
||||
struct sockaddr_in6 dest_addr; |
||||
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un)); |
||||
dest_addr.sin6_family = AF_INET6; |
||||
dest_addr.sin6_port = htons(UDP_SERVER_PORT);PO |
||||
addr_family = AF_INET6; |
||||
ip_protocol = IPPROTO_IPV6; |
||||
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); |
||||
#endif |
||||
|
||||
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol); |
||||
if (sock < 0) { |
||||
McuLog_error("Unable to create socket: errno %d", errno); |
||||
break; |
||||
} |
||||
McuLog_info("Socket created"); |
||||
|
||||
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); |
||||
if (err < 0) { |
||||
McuLog_error("Socket unable to bind: errno %d", errno); |
||||
} |
||||
McuLog_info("Socket bound, port %d", UDP_SERVER_PORT); |
||||
while (1) { |
||||
McuLog_info("Waiting for data on port %d", UDP_SERVER_PORT); |
||||
struct sockaddr_in6 source_addr; /* Large enough for both IPv4 or IPv6 */ |
||||
socklen_t socklen = sizeof(source_addr); |
||||
|
||||
/* receive data (blocking): */ |
||||
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer)-1, 0, (struct sockaddr *)&source_addr, &socklen); |
||||
|
||||
/* Error occurred during receiving */ |
||||
if (len < 0) { |
||||
McuLog_error("recvfrom failed: errno %d", errno); |
||||
break; |
||||
} else { /* Data received */ |
||||
/* Get the sender's ip address as string */ |
||||
if (source_addr.sin6_family == PF_INET) { |
||||
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1); |
||||
} else if (source_addr.sin6_family == PF_INET6) { |
||||
inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1); |
||||
} |
||||
rx_buffer[len] = '\0'; /* Null-terminate whatever we received and treat like a string... */ |
||||
McuLog_info("Received %d bytes from %s:\n%s", len, addr_str, rx_buffer); |
||||
|
||||
/* \TODO Need to handle messages and send them to the robot */ |
||||
|
||||
/* send back response */ |
||||
unsigned char test_response[128]; |
||||
int err; |
||||
|
||||
McuLog_info("Sending back response"); |
||||
McuUtility_strcpy(test_response, sizeof(test_response), (unsigned char*)"OK"); /* default response */ |
||||
if (McuUtility_strncmp(rx_buffer, "test", sizeof("test")-1)==0) { /* hard-coded command */ |
||||
McuUtility_strcpy(test_response, sizeof(test_response), (unsigned char*)"test_response"); |
||||
} |
||||
err = SendToSocket(sock, (const char*)test_response, (struct sockaddr *)&source_addr, sizeof(source_addr)); |
||||
if (err < 0) { |
||||
McuLog_error("Error occurred during sending: errno %d", errno); |
||||
} |
||||
} /* if */ |
||||
} /* while */ |
||||
if (sock != -1) { |
||||
McuLog_error("Shutting down socket and restarting..."); |
||||
shutdown(sock, 0); |
||||
close(sock); |
||||
} |
||||
} /* for */ |
||||
vTaskDelete(NULL); |
||||
} |
||||
|
||||
void UDP_Server_Start(void) { |
||||
if (taskHandle!=NULL) { |
||||
vTaskResume(taskHandle); |
||||
} |
||||
} |
||||
|
||||
void UDP_Server_Stop(void) { |
||||
if (taskHandle!=NULL) { |
||||
vTaskSuspend(taskHandle); |
||||
} |
||||
} |
||||
|
||||
void UDP_Server_Init(void) { |
||||
BaseType_t res; |
||||
|
||||
res = xTaskCreate(udp_server_task, "udp_server", (16*1024)/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+5, &taskHandle); |
||||
if (res==pdPASS) { |
||||
McuLog_info("created UDP server task"); |
||||
} else { |
||||
McuLog_error("failed creating UDP server task!"); |
||||
} |
||||
} |
||||
|
||||
#endif /* PL_CONFIG_USE_UDP_SERVER */ |
||||
@ -0,0 +1,29 @@ |
||||
/*
|
||||
* Copyright (c) 2019, 2020, 202§, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef UDP_SERVER_H_ |
||||
#define UDP_SERVER_H_ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#define UDP_SERVER_PORT (1234) /*!< default UDP server port */ |
||||
|
||||
/*! \brief start the UDP server */ |
||||
void UDP_Server_Start(void); |
||||
|
||||
/*! \brief stop the UDP server */ |
||||
void UDP_Server_Stop(void); |
||||
|
||||
/*! \brief Module initialization */ |
||||
void UDP_Server_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UDP_SERVER_H_ */ |
||||
@ -0,0 +1,46 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_UDP_SERVER |
||||
#include "udp_server_shell.h" |
||||
#include "udp_server.h" |
||||
#include "wifi.h" |
||||
#include "McuShell.h" |
||||
#include "McuUtility.h" |
||||
#include "Shell.h" |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
|
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
unsigned char buf[32]; |
||||
|
||||
McuShell_SendStatusStr((unsigned char*)"udps", (unsigned char*)"ESP32 udp server status\r\n", io->stdOut); |
||||
McuUtility_Num32sToStr(buf, sizeof(buf), UDP_SERVER_PORT); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
McuShell_SendStatusStr((unsigned char*)" port", buf, io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"udps", (unsigned char*)"Group of udp server commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows udp server help or status\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t UDP_Server_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"udps help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"udps status")==0) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
#endif /* PL_CONFIG_USE_UDP_SERVER */ |
||||
@ -0,0 +1,34 @@ |
||||
/*
|
||||
* Copyright (c) 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef UDP_SERVER_SHELL_H_ |
||||
#define UDP_SERVER_SHELL_H_ |
||||
|
||||
#include "platform.h" |
||||
#include <stdint.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Command line and shell handler |
||||
* \param cmd The command to be parsed |
||||
* \param handled If command has been recognized and handled |
||||
* \param io I/O handler to be used |
||||
* \return error code, otherwise ERR_OK |
||||
*/ |
||||
uint8_t UDP_Server_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* UDP_SERVER_SHELL_H_ */ |
||||
@ -0,0 +1,383 @@ |
||||
/*
|
||||
* Copyright (c) 2019, 2020, 2021, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
* ****************************************************************** |
||||
* Dear programmer: |
||||
* When I wrote this code, only god and I knew how it worked. |
||||
* Now, only god knows it! |
||||
* |
||||
* Therefore, if you are trying to optimize or change this code |
||||
* and it fails (most surely), please increase the counter below |
||||
* as a warning for the next person: |
||||
* |
||||
* total_hours_wasted_here = 257 |
||||
* ******************************************************************* |
||||
*/ |
||||
|
||||
#include "platform.h" |
||||
#if PL_CONFIG_USE_WIFI |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
#include "freertos/FreeRTOS.h" |
||||
#include "freertos/task.h" |
||||
#include "freertos/event_groups.h" |
||||
#include "esp_wifi.h" |
||||
#include "esp_wpa2.h" |
||||
#include "esp_event.h" |
||||
#include "esp_log.h" |
||||
#include "esp_system.h" |
||||
#include "nvs_flash.h" |
||||
#include "esp_netif.h" |
||||
#if PL_CONFIG_USE_SNTP_TIME |
||||
#include "sntp_time.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_CLIENT |
||||
#include "udp_client.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_SERVER |
||||
#include "udp_server.h" |
||||
#endif |
||||
#if PL_CONFIG_USE_BLINKY |
||||
#include "led.h" |
||||
#define LED_ON_TIME_MS_CONNECTED 1000 |
||||
#define LED_ON_TIME_MS_DISCONNECTED 5 |
||||
#endif |
||||
#include "McuUtility.h" |
||||
#include "McuXFormat.h" |
||||
#include "esp32_mac.h" |
||||
#include "McuLog.h" |
||||
|
||||
#define EAP_PEAP 1 /* WPA2 Enterprise with password and no certificate */ |
||||
#define EAP_TTLS 2 /* TLS method */ |
||||
|
||||
#include "pwd.h" /* local file with login information */ |
||||
|
||||
#ifndef CONFIG_ESP_MAXIMUM_RETRY |
||||
#define CONFIG_ESP_MAXIMUM_RETRY (2) /* number of retries to connect to the network */ |
||||
#endif |
||||
|
||||
static int s_retry_num = 0; |
||||
|
||||
/* FreeRTOS event group to signal when we are connected & ready to make a request */ |
||||
static EventGroupHandle_t s_wifi_event_group; |
||||
/* The event group allows multiple bits for each event, but we only care about two events:
|
||||
* - we are connected to the AP with an IP |
||||
* - we failed to connect after the maximum amount of retries */ |
||||
#define WIFI_EVENT_HANDLER_CONNECTED_BIT (1<<0) |
||||
#define WIFI_EVENT_HANDLER_FAIL_BIT (1<<1) |
||||
#define WIFI_CONNECTED_BIT (1<<2) |
||||
|
||||
static esp_netif_t *APP_WiFi_NetIf; |
||||
static bool APP_WiFi_isOn = true; |
||||
|
||||
void APP_WiFi_PrintIP(void) { |
||||
tcpip_adapter_ip_info_t ip; |
||||
|
||||
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); |
||||
if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == ESP_OK) { |
||||
McuLog_info("IP:"IPSTR " MASK:"IPSTR " GW:"IPSTR, IP2STR(&ip.ip), IP2STR(&ip.netmask), IP2STR(&ip.gw)); |
||||
} else { |
||||
McuLog_error("failed tcpip_adapter_get_ip_info()"); |
||||
} |
||||
} |
||||
|
||||
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) { |
||||
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) { |
||||
McuLog_info("WIFI_EVENT_STA_START: start event"); |
||||
esp_wifi_connect(); |
||||
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { |
||||
McuLog_info("WIFI_EVENT_STA_DISCONNECTED: disconnected, retry %d", s_retry_num); |
||||
if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) { |
||||
esp_wifi_connect(); |
||||
s_retry_num++; |
||||
McuLog_info("retry to connect to the AP"); |
||||
} else { |
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_EVENT_HANDLER_FAIL_BIT); |
||||
} |
||||
McuLog_info("connect to the AP fail"); |
||||
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { |
||||
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; |
||||
McuLog_info("IP_EVENT_STA_GOT_IP: got ip:" IPSTR, IP2STR(&event->ip_info.ip)); |
||||
s_retry_num = 0; |
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_EVENT_HANDLER_CONNECTED_BIT); |
||||
} |
||||
} |
||||
|
||||
typedef enum { |
||||
WIFI_PASSWORD_METHOD_PSK, |
||||
WIFI_PASSWORD_METHOD_WPA2, |
||||
} WiFi_PasswordMethod_e; |
||||
|
||||
static void GetNetworkSSID(unsigned char *ssidBuf, size_t ssidBufSize) { |
||||
wifi_ap_record_t ap; |
||||
esp_err_t err; |
||||
|
||||
err = esp_wifi_sta_get_ap_info(&ap); |
||||
if (err==ESP_OK) { |
||||
McuUtility_strcpy(ssidBuf, ssidBufSize, ap.ssid); |
||||
} else if (err==ESP_ERR_WIFI_CONN) { |
||||
McuUtility_strcpy(ssidBuf, ssidBufSize, (unsigned char*)"not init"); |
||||
} else if (err==ESP_ERR_WIFI_NOT_CONNECT) { |
||||
McuUtility_strcpy(ssidBuf, ssidBufSize, (unsigned char*)"not connected"); |
||||
} else { |
||||
McuUtility_strcpy(ssidBuf, ssidBufSize, (unsigned char*)"error?"); |
||||
} |
||||
} |
||||
|
||||
static void SetPasswordMode(WiFi_PasswordMethod_e mode) { |
||||
wifi_config_t wifi_config; |
||||
|
||||
McuLog_info("SetPasswordMode(): %d", mode); |
||||
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) ); |
||||
memset(&wifi_config, 0, sizeof(wifi_config_t)); /* initialize all fields */ |
||||
if (mode==WIFI_PASSWORD_METHOD_WPA2) { |
||||
strncpy((char*)wifi_config.sta.ssid, CONFIG_WIFI_EAP_SSID, sizeof(wifi_config.sta.ssid)); |
||||
} else if (mode==WIFI_PASSWORD_METHOD_PSK) { |
||||
strncpy((char*)wifi_config.sta.ssid, CONFIG_WIFI_PSK_SSID, sizeof(wifi_config.sta.ssid)); |
||||
strncpy((char*)wifi_config.sta.password, CONFIG_WIFI_PSK_PASSWORD, sizeof(wifi_config.sta.password)); |
||||
} else { |
||||
McuLog_error("Wrong connection mode: %d", mode); |
||||
} |
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); |
||||
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) ); |
||||
if (mode==WIFI_PASSWORD_METHOD_WPA2) { |
||||
const ESP32_Device_t *device; |
||||
|
||||
device = ESP32_GetDeviceConfig(); |
||||
McuLog_info("EAP_ID: %s", device->eee_id); |
||||
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)device->eee_id, strlen(device->eee_id)) ); |
||||
if (CONFIG_WIFI_EAP_METHOD == EAP_PEAP || CONFIG_WIFI_EAP_METHOD == EAP_TTLS) { |
||||
McuLog_info("EAP_USERNAME: %s", device->eee_id); |
||||
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_username((uint8_t *)device->eee_id, strlen(device->eee_id)) ); |
||||
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)device->eee_pwd, strlen(device->eee_pwd)) ); |
||||
} |
||||
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() ); |
||||
} |
||||
} |
||||
|
||||
static void initialise_wifi(void) { |
||||
WiFi_PasswordMethod_e mode = CONFIG_WIFI_START_WITH; /* starting mode */ |
||||
const ESP32_Device_t *config; |
||||
|
||||
s_wifi_event_group = xEventGroupCreate(); |
||||
ESP_ERROR_CHECK(esp_netif_init()); |
||||
ESP_ERROR_CHECK(esp_event_loop_create_default()); |
||||
APP_WiFi_NetIf = esp_netif_create_default_wifi_sta(); |
||||
|
||||
config = ESP32_GetDeviceConfig(); |
||||
McuLog_info("Setting hostname: %s", config->hostName); |
||||
ESP_ERROR_CHECK(esp_netif_set_hostname(APP_WiFi_NetIf, config->hostName)); |
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); |
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg)); |
||||
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); |
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL)); |
||||
|
||||
SetPasswordMode(mode); |
||||
|
||||
McuLog_info("Starting WiFi"); |
||||
ESP_ERROR_CHECK(esp_wifi_start()); |
||||
|
||||
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
|
||||
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */ |
||||
EventBits_t bits; |
||||
|
||||
do { |
||||
if (APP_WiFi_isOn) { |
||||
bits = xEventGroupWaitBits(s_wifi_event_group, |
||||
WIFI_EVENT_HANDLER_CONNECTED_BIT | WIFI_EVENT_HANDLER_FAIL_BIT, /* bits to wait for */ |
||||
pdTRUE, /* bits to clear on exit: we do not clear the WIFI_CONNECTED_BIT because used by WiFi task */ |
||||
pdFALSE, /* wait for all bits */ |
||||
pdMS_TO_TICKS(20000)); /* wait time */ |
||||
if (bits&WIFI_EVENT_HANDLER_CONNECTED_BIT) { |
||||
break; /* leave loop */ |
||||
} |
||||
if (bits&WIFI_EVENT_HANDLER_FAIL_BIT) { |
||||
McuLog_info("FAILED connecting, restarting WiFi with different mode"); |
||||
ESP_ERROR_CHECK(esp_wifi_stop()); |
||||
/* toggle mode */ |
||||
if (mode==WIFI_PASSWORD_METHOD_PSK) { |
||||
mode = WIFI_PASSWORD_METHOD_WPA2; |
||||
} else if (mode==WIFI_PASSWORD_METHOD_WPA2) { |
||||
ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_disable()); |
||||
mode = WIFI_PASSWORD_METHOD_PSK; |
||||
} |
||||
SetPasswordMode(mode); |
||||
ESP_ERROR_CHECK(esp_wifi_start()); |
||||
} |
||||
} else { |
||||
vTaskDelay(pdMS_TO_TICKS(500)); |
||||
} |
||||
} while(true); /* breaks */ |
||||
|
||||
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened. */ |
||||
if (bits & WIFI_EVENT_HANDLER_CONNECTED_BIT) { |
||||
if (mode == WIFI_PASSWORD_METHOD_WPA2) { |
||||
McuLog_info("connected to AP SSID: %s ", CONFIG_WIFI_EAP_SSID); |
||||
} else if (mode == WIFI_PASSWORD_METHOD_PSK) { |
||||
McuLog_info("connected to AP SSID: %s", CONFIG_WIFI_PSK_SSID); |
||||
} |
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT); |
||||
} else if (bits & WIFI_EVENT_HANDLER_FAIL_BIT) { |
||||
if (mode == WIFI_PASSWORD_METHOD_WPA2) { |
||||
McuLog_info("Failed to connect to SSID: %s", CONFIG_WIFI_EAP_SSID); |
||||
} else if (mode == WIFI_PASSWORD_METHOD_PSK) { |
||||
McuLog_info("Failed to connect to SSID: %s", CONFIG_WIFI_PSK_SSID); |
||||
} |
||||
} else { |
||||
McuLog_error("UNEXPECTED EVENT"); |
||||
} |
||||
} |
||||
|
||||
typedef enum { |
||||
WIFI_STATE_INIT, |
||||
WIFI_STATE_CONNECTED, |
||||
WIFI_STATE_DISCONNECTED, |
||||
} WiFi_State_e; |
||||
|
||||
static void WiFiTask(void *pv) { |
||||
bool isConnected = false; |
||||
WiFi_State_e state = WIFI_STATE_INIT; |
||||
|
||||
McuLog_info("Initialize WiFi"); |
||||
initialise_wifi(); |
||||
#if PL_CONFIG_USE_BLINKY |
||||
LED_SetOnTime(LED_ON_TIME_MS_DISCONNECTED); |
||||
#endif |
||||
for(;;) { |
||||
vTaskDelay(pdMS_TO_TICKS(5000)); |
||||
isConnected = xEventGroupGetBits(s_wifi_event_group)&WIFI_CONNECTED_BIT; |
||||
#if PL_CONFIG_USE_BLINKY |
||||
LED_SetOnTime(isConnected?LED_ON_TIME_MS_CONNECTED:LED_ON_TIME_MS_DISCONNECTED); |
||||
#endif |
||||
if (state == WIFI_STATE_INIT && isConnected) { /* first connection */ |
||||
state = WIFI_STATE_CONNECTED; |
||||
//ESP_LOGI(TAG, "WiFi is connected.");
|
||||
if (isConnected) { |
||||
APP_WiFi_PrintIP(); |
||||
} |
||||
#if PL_CONFIG_USE_UDP_SERVER |
||||
McuLog_info("starting UDP server."); |
||||
UDP_Server_Start(); |
||||
#endif |
||||
#if PL_CONFIG_USE_UDP_CLIENT |
||||
McuLog_info("starting UDP client."); |
||||
UDP_Client_Start(); |
||||
#endif |
||||
#if PL_CONFIG_USE_SNTP_TIME |
||||
if (SNTP_ObtainTime()!=0) { |
||||
McuLog_error("failed getting SNTP time."); |
||||
} |
||||
#endif |
||||
#if 0 |
||||
} else if (isConnected) { |
||||
ESP_LOGI(TAG, "still connected."); |
||||
APP_WiFi_PrintIP(); |
||||
|
||||
const char *hostname; |
||||
ESP_ERROR_CHECK(esp_netif_get_hostname(APP_WiFi_NetIf, &hostname)); |
||||
ESP_LOGI(TAG,"hostname: %s", hostname); |
||||
} else { |
||||
ESP_LOGI(TAG, "not connected yet."); |
||||
#endif |
||||
} |
||||
} /* for */ |
||||
} |
||||
|
||||
bool WiFi_isConnected(void) { |
||||
bool isConnected; |
||||
|
||||
isConnected = xEventGroupGetBits(s_wifi_event_group)&WIFI_CONNECTED_BIT; |
||||
return isConnected; |
||||
} |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
static uint8_t PrintStatus(const McuShell_StdIOType *io) { |
||||
uint8_t buf[32]; |
||||
uint8_t mac[6]; |
||||
|
||||
McuShell_SendStatusStr((unsigned char*)"wifi", (unsigned char*)"ESP32 WiFi status\r\n", io->stdOut); |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)IDF_VER); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
McuShell_SendStatusStr((unsigned char*)" IDF", buf, io->stdOut); |
||||
McuShell_SendStatusStr((unsigned char*)" connected", WiFi_isConnected()?(unsigned char*)"yes\r\n":(unsigned char*)"no\r\n", io->stdOut); |
||||
|
||||
ESP32_MacRead(mac); |
||||
ESP32_MacToString(mac, buf, sizeof(buf)); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
McuShell_SendStatusStr((unsigned char*)" MAC", buf, io->stdOut); |
||||
McuShell_SendStatusStr((unsigned char*)" WiFi", APP_WiFi_isOn?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut); |
||||
|
||||
if (WiFi_isConnected()) { |
||||
unsigned char buf[64]; |
||||
tcpip_adapter_ip_info_t ip; |
||||
const char *hostname; |
||||
|
||||
if (esp_netif_get_hostname(APP_WiFi_NetIf, &hostname)==ERR_OK) { |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)hostname); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
} else { |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"failed\r\n"); |
||||
} |
||||
McuShell_SendStatusStr((unsigned char*)" hostname", buf, io->stdOut); |
||||
|
||||
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t)); |
||||
if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == ESP_OK) { |
||||
McuXFormat_xsnprintf((char*)buf, sizeof(buf), "IP:"IPSTR " MASK:"IPSTR " GW:"IPSTR "\r\n", IP2STR(&ip.ip), IP2STR(&ip.netmask), IP2STR(&ip.gw)); |
||||
} else { |
||||
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"failed\r\n"); |
||||
} |
||||
McuShell_SendStatusStr((unsigned char*)" IP", buf, io->stdOut); |
||||
|
||||
GetNetworkSSID(buf, sizeof(buf)); |
||||
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n"); |
||||
McuShell_SendStatusStr((unsigned char*)" SSID", buf, io->stdOut); |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
|
||||
static uint8_t PrintHelp(const McuShell_StdIOType *io) { |
||||
McuShell_SendHelpStr((unsigned char*)"wifi", (unsigned char*)"Group of ESP32 WiFi commands\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows WiFi help or status\r\n", io->stdOut); |
||||
McuShell_SendHelpStr((unsigned char*)" on|off", (unsigned char*)"Turn WiFi on or off\r\n", io->stdOut); |
||||
return ERR_OK; |
||||
} |
||||
|
||||
uint8_t WiFi_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) { |
||||
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"wifi help")==0) { |
||||
*handled = TRUE; |
||||
return PrintHelp(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"wifi status")==0) { |
||||
*handled = TRUE; |
||||
return PrintStatus(io); |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)"wifi on")==0) { |
||||
*handled = TRUE; |
||||
APP_WiFi_isOn = true; |
||||
return ERR_OK; |
||||
} else if (McuUtility_strcmp((char*)cmd, (char*)"wifi off")==0) { |
||||
*handled = TRUE; |
||||
APP_WiFi_isOn = false; |
||||
return ERR_OK; |
||||
} |
||||
return ERR_OK; |
||||
} |
||||
#endif /* PL_CONFIG_USE_SHELL */ |
||||
|
||||
void WiFi_Init(void) { |
||||
BaseType_t res; |
||||
|
||||
res = xTaskCreate(WiFiTask, "WiFi", 16*1024/sizeof(StackType_t), NULL, tskIDLE_PRIORITY, NULL); |
||||
if (res==pdPASS) { |
||||
McuLog_info("created WiFi task"); |
||||
} else { |
||||
McuLog_error("failed creating WiFi task!"); |
||||
} |
||||
/* set mac address, otherwise will get "system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE" whenever asking for it */ |
||||
uint8_t mac[6]; |
||||
res = esp_read_mac(&mac[0], ESP_MAC_WIFI_STA); |
||||
if (res==ESP_OK) { |
||||
ESP_ERROR_CHECK(esp_base_mac_addr_set(&mac[0])); |
||||
} |
||||
} |
||||
#endif /* PL_CONFIG_USE_WIFI */ |
||||
@ -0,0 +1,45 @@ |
||||
/*
|
||||
* Copyright (c) 2020, Erich Styger |
||||
* |
||||
* SPDX-License-Identifier: BSD-3-Clause |
||||
*/ |
||||
|
||||
#ifndef APPWIFI_H_ |
||||
#define APPWIFI_H_ |
||||
|
||||
#include "platform.h" |
||||
#include <stdbool.h> |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#if PL_CONFIG_USE_SHELL |
||||
#include "McuShell.h" |
||||
|
||||
/*!
|
||||
* \brief Command line and shell handler |
||||
* \param cmd The command to be parsed |
||||
* \param handled If command has been recognized and handled |
||||
* \param io I/O handler to be used |
||||
* \return error code, otherwise ERR_OK |
||||
*/ |
||||
uint8_t WiFi_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io); |
||||
#endif |
||||
|
||||
/*!
|
||||
* \brief Decides if connected to a network or not |
||||
* \return true if connected, false if not |
||||
*/ |
||||
bool WiFi_isConnected(void); |
||||
|
||||
/*!
|
||||
* \brief Module Initialization |
||||
*/ |
||||
void WiFi_Init(void); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* APPWIFI_H_ */ |
||||
@ -0,0 +1,23 @@ |
||||
idf_component_register( |
||||
SRCS |
||||
"src/McuLib.c" |
||||
"src/McuWait.c" |
||||
"src/McuArmTools.c" |
||||
"src/McuRTOS.c" |
||||
"src/McuUtility.c" |
||||
"src/McuShell.c" |
||||
"src/McuXFormat.c" |
||||
"src/McuCriticalSection.c" |
||||
"src/McuLog.c" |
||||
"src/McuGPIO.c" |
||||
"src/McuLED.c" |
||||
"src/McuTimeDate.c" |
||||
"src/McuButton.c" |
||||
"src/McuTrigger.c" |
||||
"src/McuTimeout.c" |
||||
"src/McuRB.c" |
||||
"src/McuUart485.c" |
||||
INCLUDE_DIRS |
||||
"./config" |
||||
"./src" |
||||
) |
||||
Loading…
Reference in new issue