diff --git a/ADIS_ESP32_Eclipse/.project b/ADIS_ESP32_Eclipse/.project
index ddcc38f..38c557d 100644
--- a/ADIS_ESP32_Eclipse/.project
+++ b/ADIS_ESP32_Eclipse/.project
@@ -24,4 +24,11 @@
org.eclipse.cdt.managedbuilder.core.managedBuildNature
org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
+
+
+ McuLib
+ 2
+ PARENT-1-PROJECT_LOC/McuLib
+
+
diff --git a/ADIS_ESP32_Eclipse/CMakeLists.txt b/ADIS_ESP32_Eclipse/CMakeLists.txt
index bcc12fe..d00cd3d 100644
--- a/ADIS_ESP32_Eclipse/CMakeLists.txt
+++ b/ADIS_ESP32_Eclipse/CMakeLists.txt
@@ -2,5 +2,11 @@
# CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
+# add my own component direcotry to the list
+list(APPEND EXTRA_COMPONENT_DIRS "./McuLib")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+
+# additional include for IncludeMcuLibConfig
+add_compile_options(-I ./config)
+add_compile_options(-include "./config/IncludeMcuLibConfig.h")
project(esp32-adis)
diff --git a/ADIS_ESP32_Eclipse/config/IncludeMcuLibConfig.h b/ADIS_ESP32_Eclipse/config/IncludeMcuLibConfig.h
new file mode 100644
index 0000000..d70f29a
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/config/IncludeMcuLibConfig.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/.gitignore b/ADIS_ESP32_Eclipse/main/.gitignore
new file mode 100644
index 0000000..7427950
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/.gitignore
@@ -0,0 +1,3 @@
+/build/
+# do NOT put the password on git
+/pwd.h
diff --git a/ADIS_ESP32_Eclipse/main/CMakeLists.txt b/ADIS_ESP32_Eclipse/main/CMakeLists.txt
index 0d9feff..b4fcedb 100644
--- a/ADIS_ESP32_Eclipse/main/CMakeLists.txt
+++ b/ADIS_ESP32_Eclipse/main/CMakeLists.txt
@@ -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
+ "."
+)
\ No newline at end of file
diff --git a/ADIS_ESP32_Eclipse/main/Identify.h b/ADIS_ESP32_Eclipse/main/Identify.h
new file mode 100644
index 0000000..e9932f0
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/Identify.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/Shell.c b/ADIS_ESP32_Eclipse/main/Shell.c
new file mode 100644
index 0000000..18642e9
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/Shell.c
@@ -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
+#include
+#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;istdOut);
+ }
+}
+
+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;istdOut);
+ }
+#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
+#include
+#include
+
+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
+#include /* 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_ */
diff --git a/ADIS_ESP32_Eclipse/main/led.c b/ADIS_ESP32_Eclipse/main/led.c
new file mode 100644
index 0000000..099d8ad
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/led.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2020, Erich Styger
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include "platform.h"
+
+#include
+#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!");
+ }
+}
diff --git a/ADIS_ESP32_Eclipse/main/led.h b/ADIS_ESP32_Eclipse/main/led.h
new file mode 100644
index 0000000..3a28c63
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/led.h
@@ -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
+
+#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_ */
diff --git a/ADIS_ESP32_Eclipse/main/main.c b/ADIS_ESP32_Eclipse/main/main.c
index 669ef2d..0690882 100644
--- a/ADIS_ESP32_Eclipse/main/main.c
+++ b/ADIS_ESP32_Eclipse/main/main.c
@@ -13,10 +13,12 @@
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "myTask.h"
+#include "platform.h"
void app_main(void)
{
printf("Hello world!\n");
+ PL_Init();
/* Print chip information */
esp_chip_info_t chip_info;
diff --git a/ADIS_ESP32_Eclipse/main/ping.c b/ADIS_ESP32_Eclipse/main/ping.c
new file mode 100644
index 0000000..219b366
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/ping.c
@@ -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 */
diff --git a/ADIS_ESP32_Eclipse/main/ping.h b/ADIS_ESP32_Eclipse/main/ping.h
new file mode 100644
index 0000000..17e3bd1
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/ping.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/ping_shell.c b/ADIS_ESP32_Eclipse/main/ping_shell.c
new file mode 100644
index 0000000..b57496a
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/ping_shell.c
@@ -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*)" ", (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 */
diff --git a/ADIS_ESP32_Eclipse/main/ping_shell.h b/ADIS_ESP32_Eclipse/main/ping_shell.h
new file mode 100644
index 0000000..5242849
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/ping_shell.h
@@ -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
+
+#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_ */
diff --git a/ADIS_ESP32_Eclipse/main/platform.c b/ADIS_ESP32_Eclipse/main/platform.c
new file mode 100644
index 0000000..660df67
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/platform.c
@@ -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
+}
diff --git a/ADIS_ESP32_Eclipse/main/platform.h b/ADIS_ESP32_Eclipse/main/platform.h
new file mode 100644
index 0000000..8e0f459
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/platform.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/robot.c b/ADIS_ESP32_Eclipse/main/robot.c
new file mode 100644
index 0000000..64e70a9
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/robot.c
@@ -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 ", (unsigned char*)"Send a text to the robot\r\n", io->stdOut);
+ McuShell_SendHelpStr((unsigned char*)" sendcmd ", (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 */
diff --git a/ADIS_ESP32_Eclipse/main/robot.h b/ADIS_ESP32_Eclipse/main/robot.h
new file mode 100644
index 0000000..98db047
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/robot.h
@@ -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
+
+#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_ */
diff --git a/ADIS_ESP32_Eclipse/main/rs485.c b/ADIS_ESP32_Eclipse/main/rs485.c
new file mode 100644
index 0000000..5420025
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/rs485.c
@@ -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>= 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 "@ OK"
+ * or "@ 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 "@ OK" or "@ 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 ", (unsigned char*)"Set RS-485 address\r\n", io->stdOut);
+#endif
+ McuShell_SendHelpStr((unsigned char*)" send ", (unsigned char*)"Send a text to the RS-485 bus\r\n", io->stdOut);
+ McuShell_SendHelpStr((unsigned char*)" sendcmd ", (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 "@ 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 "@ " */
+ 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 */
diff --git a/ADIS_ESP32_Eclipse/main/rs485.h b/ADIS_ESP32_Eclipse/main/rs485.h
new file mode 100644
index 0000000..765d3d8
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/rs485.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2019-2022, Erich Styger
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef RS485_H_
+#define RS485_H_
+
+#include
+#include
+#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_ */
diff --git a/ADIS_ESP32_Eclipse/main/sntp_time.c b/ADIS_ESP32_Eclipse/main/sntp_time.c
new file mode 100644
index 0000000..c3d49a4
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/sntp_time.c
@@ -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 */
diff --git a/ADIS_ESP32_Eclipse/main/sntp_time.h b/ADIS_ESP32_Eclipse/main/sntp_time.h
new file mode 100644
index 0000000..4471c8b
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/sntp_time.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/timer.c b/ADIS_ESP32_Eclipse/main/timer.c
new file mode 100644
index 0000000..6e5479c
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/timer.c
@@ -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 */
diff --git a/ADIS_ESP32_Eclipse/main/timer.h b/ADIS_ESP32_Eclipse/main/timer.h
new file mode 100644
index 0000000..082f63c
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/timer.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/udp_client.c b/ADIS_ESP32_Eclipse/main/udp_client.c
new file mode 100644
index 0000000..b2945d2
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/udp_client.c
@@ -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
+#include
+#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
+
+#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 ", (unsigned char*)"Set default host destination IP address (double quoted)\r\n", io->stdOut);
+ McuShell_SendHelpStr((unsigned char*)" port ", (unsigned char*)"Set default host destination port number\r\n", io->stdOut);
+ McuShell_SendHelpStr((unsigned char*)" send default ", (unsigned char*)"Send message (double quoted) to default host and port\r\n", io->stdOut);
+ McuShell_SendHelpStr((unsigned char*)" send ", (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 */
diff --git a/ADIS_ESP32_Eclipse/main/udp_client.h b/ADIS_ESP32_Eclipse/main/udp_client.h
new file mode 100644
index 0000000..32baca6
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/udp_client.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/udp_server.c b/ADIS_ESP32_Eclipse/main/udp_server.c
new file mode 100644
index 0000000..8a6ddcf
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/udp_server.c
@@ -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
+#include
+#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
+
+#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 */
diff --git a/ADIS_ESP32_Eclipse/main/udp_server.h b/ADIS_ESP32_Eclipse/main/udp_server.h
new file mode 100644
index 0000000..1fc07e9
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/udp_server.h
@@ -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_ */
diff --git a/ADIS_ESP32_Eclipse/main/udp_server_shell.c b/ADIS_ESP32_Eclipse/main/udp_server_shell.c
new file mode 100644
index 0000000..e99c08a
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/udp_server_shell.c
@@ -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 */
diff --git a/ADIS_ESP32_Eclipse/main/udp_server_shell.h b/ADIS_ESP32_Eclipse/main/udp_server_shell.h
new file mode 100644
index 0000000..748ace5
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/udp_server_shell.h
@@ -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
+
+#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_ */
diff --git a/ADIS_ESP32_Eclipse/main/wifi.c b/ADIS_ESP32_Eclipse/main/wifi.c
new file mode 100644
index 0000000..8eb4f68
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/wifi.c
@@ -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
+#include
+#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 */
diff --git a/ADIS_ESP32_Eclipse/main/wifi.h b/ADIS_ESP32_Eclipse/main/wifi.h
new file mode 100644
index 0000000..79ae4ce
--- /dev/null
+++ b/ADIS_ESP32_Eclipse/main/wifi.h
@@ -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
+
+#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_ */
diff --git a/McuLib/CMakeLists.txt b/McuLib/CMakeLists.txt
new file mode 100644
index 0000000..6e751c2
--- /dev/null
+++ b/McuLib/CMakeLists.txt
@@ -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"
+ )
\ No newline at end of file