Merge branch 'main' of gitlab.enterpriselab.ch:adis_team_gueti_roaster/adis_hs2022_team_4 into main

# Conflicts:
#	ADIS_ESP32_Eclipse/main/CMakeLists.txt
#	ADIS_ESP32_Eclipse/main/main.c
main
Jonas Arnold 4 years ago
commit 44027da070
  1. 7
      ADIS_ESP32_Eclipse/.project
  2. 6
      ADIS_ESP32_Eclipse/CMakeLists.txt
  3. 42
      ADIS_ESP32_Eclipse/config/IncludeMcuLibConfig.h
  4. 3
      ADIS_ESP32_Eclipse/main/.gitignore
  5. 24
      ADIS_ESP32_Eclipse/main/CMakeLists.txt
  6. 93
      ADIS_ESP32_Eclipse/main/Identify.h
  7. 319
      ADIS_ESP32_Eclipse/main/Shell.c
  8. 74
      ADIS_ESP32_Eclipse/main/Shell.h
  9. 110
      ADIS_ESP32_Eclipse/main/esp32_mac.c
  10. 64
      ADIS_ESP32_Eclipse/main/esp32_mac.h
  11. 147
      ADIS_ESP32_Eclipse/main/led.c
  12. 42
      ADIS_ESP32_Eclipse/main/led.h
  13. 2
      ADIS_ESP32_Eclipse/main/main.c
  14. 114
      ADIS_ESP32_Eclipse/main/ping.c
  15. 30
      ADIS_ESP32_Eclipse/main/ping.h
  16. 59
      ADIS_ESP32_Eclipse/main/ping_shell.c
  17. 39
      ADIS_ESP32_Eclipse/main/ping_shell.h
  18. 107
      ADIS_ESP32_Eclipse/main/platform.c
  19. 32
      ADIS_ESP32_Eclipse/main/platform.h
  20. 66
      ADIS_ESP32_Eclipse/main/robot.c
  21. 43
      ADIS_ESP32_Eclipse/main/robot.h
  22. 632
      ADIS_ESP32_Eclipse/main/rs485.c
  23. 61
      ADIS_ESP32_Eclipse/main/rs485.h
  24. 89
      ADIS_ESP32_Eclipse/main/sntp_time.c
  25. 27
      ADIS_ESP32_Eclipse/main/sntp_time.h
  26. 31
      ADIS_ESP32_Eclipse/main/timer.c
  27. 21
      ADIS_ESP32_Eclipse/main/timer.h
  28. 303
      ADIS_ESP32_Eclipse/main/udp_client.c
  29. 39
      ADIS_ESP32_Eclipse/main/udp_client.h
  30. 151
      ADIS_ESP32_Eclipse/main/udp_server.c
  31. 29
      ADIS_ESP32_Eclipse/main/udp_server.h
  32. 46
      ADIS_ESP32_Eclipse/main/udp_server_shell.c
  33. 34
      ADIS_ESP32_Eclipse/main/udp_server_shell.h
  34. 383
      ADIS_ESP32_Eclipse/main/wifi.c
  35. 45
      ADIS_ESP32_Eclipse/main/wifi.h
  36. 23
      McuLib/CMakeLists.txt

@ -24,4 +24,11 @@
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature> <nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature> <nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
</natures> </natures>
<linkedResources>
<link>
<name>McuLib</name>
<type>2</type>
<locationURI>PARENT-1-PROJECT_LOC/McuLib</locationURI>
</link>
</linkedResources>
</projectDescription> </projectDescription>

@ -2,5 +2,11 @@
# CMakeLists in this exact order for cmake to work correctly # CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5) 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) 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) project(esp32-adis)

@ -0,0 +1,42 @@
/*
* ESP32 IncludeMcuLibConfig
*
* Author: Erich Styger
* License: PDX-License-Identifier: BSD-3-Clause
*/
#ifndef MCULIB_CONFIG_CONFIG_H_
#define MCULIB_CONFIG_CONFIG_H_
/* -------------- Core and McuLib general settings -------------------------- */
#define McuLib_CONFIG_CPU_IS_ARM_CORTEX_M (0) /* ESP32 is detected automatically */
/* -------------- FreeRTOS and McuRTOS settings -------------------------- */
#define configHEAP_SCHEME_IDENTIFICATION (0) /* ESP-IDF RTOS used */
/* -------------- McuShell settings -------------------------- */
#define McuShell_CONFIG_PROJECT_NAME_STRING "ESP32 Shell"
#define McuShell_CONFIG_ECHO_ENABLED (1)
#define McuShell_CONFIG_PROMPT_STRING "ESP32> "
#define McuShell_CONFIG_MULTI_CMD_ENABLED (1)
#define McuShell_CONFIG_MULTI_CMD_SIZE (96)
/* -------------- McuCriticalSections settings -------------------------- */
#define McuCriticalSection_CONFIG_USE_RTOS_CRITICAL_SECTION (1)
/* -------------- McuTimeDate settings -------------------------- */
#define McuTimeDate_CONFIG_TICK_TIME_MS (10)
/* -------------------------------------------------*/
/* McuLog */
#define McuLog_CONFIG_IS_ENABLED (1)
#define McuLog_CONFIG_DEFAULT_LEVEL (McuLog_DEBUG)
#define McuLog_CONFIG_USE_COLOR (1)
#define McuLog_CONFIG_NOF_CONSOLE_LOGGER (1)
#define McuLog_CONFIG_LOG_TIMESTAMP_DATE (1)
#define McuLog_CONFIG_USE_RTT_DATA_LOGGER (0)
#define McuLog_CONFIG_RTT_DATA_LOGGER_BUFFER_SIZE (128)
/* -------------------------------------------------*/
/* McuUART485 */
#define McuUart485_CONFIG_USE_RS_485 (1)
#endif /* MCULIB_CONFIG_CONFIG_H_ */

@ -0,0 +1,3 @@
/build/
# do NOT put the password on git
/pwd.h

@ -1,2 +1,22 @@
idf_component_register(SRCS "main.c" "myTask.c" "myTask.h" idf_component_register(
INCLUDE_DIRS "") SRCS
"main.c"
"myTask.c"
"platform.c"
"led.c"
"wifi.c"
"udp_client.c"
"udp_server.c"
"udp_server_shell.c"
"Shell.c"
"esp32_mac.c"
"rs485.c"
"ping.c"
"ping_shell.c"
"timer.c"
"sntp_time.c"
"robot.c"
INCLUDE_DIRS
"."
)

@ -0,0 +1,93 @@
/**
* \file
* \brief Module to identify different devices based on their unique ID.
* \author Erich Styger, erich.styger@hslu.ch
* \license SPDX-License-Identifier: BSD-3-Clause
* With this module individual devices are identified based on their unique ID.
*/
#ifndef __IDENTIFY_H_
#define __IDENTIFY_H_
#include "platform.h"
#ifdef __cplusplus
extern "C" {
#endif
#if PL_CONFIG_USE_IDENTIFY
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Parses a command
* \param cmd Command string to be parsed
* \param handled Sets this variable to TRUE if command was handled
* \param io I/O stream to be used for input/output
* \return Error code, ERR_OK if everything was fine
*/
uint8_t ID_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io);
#endif /* PL_CONFIG_USE_SHELL */
typedef enum { /*! \todo ADIS: Verify and update information */
ID_ROBOT_E0,
ID_ROBOT_E1,
ID_ROBOT_E2,
ID_ROBOT_E3,
ID_ROBOT_E4,
ID_ROBOT_E5,
ID_ROBOT_E6,
ID_ROBOT_E7,
ID_ROBOT_E8,
ID_ROBOT_E9,
ID_ROBOT_E10,
ID_ROBOT_E11,
ID_ROBOT_E12,
ID_ROBOT_E13,
ID_ROBOT_E14,
// ID_ROBOT_E15,
// ID_ROBOT_E16,
ID_ROBOT_E17,
ID_ROBOT_E18,
ID_ROBOT_E27,
ID_ROBOT_E34, /* no robot? */
ID_ROBOT_L0, /* USB port ripped off */
ID_ROBOT_L1,
ID_ROBOT_L3,
ID_ROBOT_L17,
ID_ROBOT_L20,
ID_ROBOT_R0, /* USB port ripped off */
ID_ROBOT_R8,
ID_ROBOT_R9,
ID_ROBOT_R23,
ID_ROBOT_R27,
ID_ROBOT_R28,
ID_ROBOT_R29,
ID_ROBOT_R32,
ID_ROBOT_R33,
ID_ROBOT_R34,
ID_ROBOT_R36,
ID_ROBOT_R37,
ID_ROBOT_R44,
ID_ROBOT_R45,
ID_ROBOT_UNKNOWN, /* unknown robot, unknown ID */
ID_ROBOT_NONE /* initialization value, used internally */
} ID_Robot_e;
ID_Robot_e ID_WhichDevice(void);
/*! \brief Module de-initialization */
void ID_Deinit(void);
/*! \brief Module initialization */
void ID_Init(void);
#endif /* PL_CONFIG_USE_IDENTIFY */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* __IDENTIFY_H_ */

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

@ -0,0 +1,74 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef SHELL_H_
#define SHELL_H_
#include "McuShell.h"
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \brief Send a string to all supported I/Os
* \param str String to send
*/
void SHELL_SendString(const unsigned char *str);
/*!
* \brief Send a string to a given IO. It tries to accelerate it by sending a buffer instead char by char.
* \param str String to be sent.
* \param io I/O to be used.
*/
void SHELL_SendStringToIO(const unsigned char *str, McuShell_ConstStdIOType *io);
/*!
* \brief Send a character to all supported I/Os
* \param ch Character to send
*/
void SHELL_SendChar(unsigned char ch);
/*!
* \brief Parses a command with a given standard I/O channel
* \param command Command to be parsed
* \param io I/O to be used. If NULL, the standard default I/O will be used
* \param silent If parsing shall be silent or not
* \return Error code, ERR_OK for no error
*/
uint8_t SHELL_ParseCommandIO(const unsigned char *command, McuShell_ConstStdIOType *io, bool silent);
/*!
* \brief Send a string to the ESP shell get a response back
* \param send Message to send
* \param response Where to store the response
* \param responseSize Size of the response buffer
*/
void SHELL_SendToESPAndGetResponse(const unsigned char *send, unsigned char *response, size_t responseSize);
/*!
* \brief Send a string to the robot get a response back
* \param send Message to send
* \param response Where to store the response
* \param responseSize Size of the response buffer
*/
void SHELL_SendToRobotAndGetResponse(const unsigned char *send, unsigned char *response, size_t responseSize);
/*!
* \brief Module de-initialization
*/
void SHELL_Deinit(void);
/*!
* \brief Module Initialization
*/
void SHELL_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* SHELL_H_ */

@ -0,0 +1,110 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_IDENTIFY
#include "esp32_mac.h"
#include "esp_system.h"
#include "Identify.h"
#include "McuUtility.h"
#include "McuXFormat.h"
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
static const ESP32_Device_t ESP32_devices[] =
{ /* registered at https://eeeportal.hslu.ch */
{.hostName="UnknownRobot", .robotID=ID_ROBOT_UNKNOWN, .macStr="00:00:00:00:00:00", .eee_id="eee-0000", .eee_pwd="deadbeef"}, /* fallback entry */
{.hostName="ADISRobotE0", .robotID=ID_ROBOT_E0, .macStr="d8:a0:1d:42:e2:08", .eee_id="eee-01367", .eee_pwd="xq4vZdu3eWLWcSGDuYwU"},
{.hostName="ADISRobotE1", .robotID=ID_ROBOT_E1, .macStr="d8:a0:1d:42:ec:a4", .eee_id="eee-01023", .eee_pwd="i24Z3W5VtDFSkdNtk93f"},
{.hostName="ADISRobotE2", .robotID=ID_ROBOT_E2, .macStr="d8:a0:1d:42:eb:c4", .eee_id="eee-01381", .eee_pwd="DcUYXx0saffo5ST8TihG"},
{.hostName="ADISRobotE3", .robotID=ID_ROBOT_E3, .macStr="d8:a0:1d:42:e6:98", .eee_id="eee-01407", .eee_pwd="OjhpZJrXUPWAC3mR0odX"},
{.hostName="ADISRobotE4", .robotID=ID_ROBOT_E4, .macStr="d8:a0:1d:42:e4:48", .eee_id="eee-01369", .eee_pwd="vDk13U6MzxkwGO879TUy"},
{.hostName="ADISRobotE5", .robotID=ID_ROBOT_E5, .macStr="d8:a0:1d:42:de:9c", .eee_id="eee-01370", .eee_pwd="jCGeKTeWVrcncJO8u73M"},
{.hostName="ADISRobotE6", .robotID=ID_ROBOT_E6, .macStr="d8:a0:1d:42:ec:40", .eee_id="eee-01382", .eee_pwd="fJjX12cWv4U0XfaJ46N0"},
{.hostName="ADISRobotE7", .robotID=ID_ROBOT_E7, .macStr="d8:a0:1d:42:df:14", .eee_id="eee-01387", .eee_pwd="TEkK2FL5Yc8A4dcsf7sk"},
{.hostName="ADISRobotE8", .robotID=ID_ROBOT_E8, .macStr="d8:a0:1d:42:ec:74", .eee_id="eee-01371", .eee_pwd="aaxQfxvPnCMVLvCZ3ays"},
{.hostName="ADISRobotE9", .robotID=ID_ROBOT_E9, .macStr="d8:a0:1d:42:e4:64", .eee_id="eee-01446", .eee_pwd="4dZNLrMCZqsQbsNKTQSL"},
{.hostName="ADISRobotE10", .robotID=ID_ROBOT_E10, .macStr="d8:a0:1d:42:e6:f8", .eee_id="eee-01383", .eee_pwd="KEsv8fendMWx1nHkdQUj"},
{.hostName="ADISRobotE11", .robotID=ID_ROBOT_E11, .macStr="d8:a0:1d:42:e0:24", .eee_id="eee-01609", .eee_pwd="WeWLiT7eH01Jqrt4DAfi"}, // issue with EEE!
{.hostName="ADISRobotE12", .robotID=ID_ROBOT_E12, .macStr="d8:a0:1d:42:df:d4", .eee_id="eee-01393", .eee_pwd="HtcKHGmaMUwwJgqh7Hao"},
{.hostName="ADISRobotE13", .robotID=ID_ROBOT_E13, .macStr="d8:a0:1d:42:e5:88", .eee_id="eee-01384", .eee_pwd="q320iZDcc6bFPvXYzRxF"},
{.hostName="ADISRobotE14", .robotID=ID_ROBOT_E14, .macStr="50:02:91:9c:0d:ac", .eee_id="eee-01635", .eee_pwd="V4V6LdNW7WJUnv4zbRKm"},
{.hostName="ADISRobotE17", .robotID=ID_ROBOT_E17, .macStr="d8:a0:1d:42:e5:98", .eee_id="eee-00535", .eee_pwd="fXB5kVK6qS1khJ7qybYD"},
{.hostName="ADISRobotE18", .robotID=ID_ROBOT_E18, .macStr="d8:a0:1d:42:e3:b8", .eee_id="eee-01390", .eee_pwd="W4KuRQERUMwHquUHwVot"},
{.hostName="ADISRobotE27", .robotID=ID_ROBOT_E27, .macStr="d8:a0:1d:42:df:e0", .eee_id="eee-01591", .eee_pwd="28o6wxyGLXmQ9DnUHw1P"},
{.hostName="ADISRobotL1", .robotID=ID_ROBOT_L1, .macStr="d8:a0:1d:42:e5:d0", .eee_id="eee-01372", .eee_pwd="zwtshA1tnqXxYqrt1mgo"},
{.hostName="ADISRobotL3", .robotID=ID_ROBOT_L1, .macStr="50:02:91:a0:f8:ac", .eee_id="eee-01388", .eee_pwd="jhw1KSG1piDP7zthNUDt"},
{.hostName="ADISRobotL17", .robotID=ID_ROBOT_L17, .macStr="d8:a0:1d:42:ed:24", .eee_id="eee-01373", .eee_pwd="XPiSwZ6Vr6Y5Y3ukvq5a"},
{.hostName="ADISRobotL20", .robotID=ID_ROBOT_L20, .macStr="d8:a0:1d:42:df:94", .eee_id="eee-01374", .eee_pwd="88kY6j6nurJZpqt2gMMF"},
{.hostName="ADISRobotR8", .robotID=ID_ROBOT_R8, .macStr="d8:a0:1d:42:ed:50", .eee_id="eee-01368", .eee_pwd="xaA8qck6edMS3h2tsHun"},
{.hostName="ADISRobotR9", .robotID=ID_ROBOT_R9, .macStr="d8:a0:1d:42:e3:d8", .eee_id="eee-01392", .eee_pwd="NxJZeTDUpGGXC0L1QxX9"},
{.hostName="ADISRobotR23", .robotID=ID_ROBOT_R23, .macStr="d8:a0:1d:42:e2:b8", .eee_id="eee-01385", .eee_pwd="deOkgVLPBoh6BQXH3Awf"},
{.hostName="ADISRobotR27", .robotID=ID_ROBOT_R27, .macStr="50:02:91:a1:0f:78", .eee_id="eee-01375", .eee_pwd="7O4HCSqbWVW0zCvfhyA2"},
{.hostName="ADISRobotR28", .robotID=ID_ROBOT_R28, .macStr="d8:a0:1d:42:e0:44", .eee_id="eee-01386", .eee_pwd="mzyEvEfx0BvgYTOgF4F2"},
{.hostName="ADISRobotR29", .robotID=ID_ROBOT_R29, .macStr="d8:a0:1d:42:ec:a8", .eee_id="eee-01410", .eee_pwd="c1nbzEkaEmXz9rEeBEfC"},
{.hostName="ADISRobotR32", .robotID=ID_ROBOT_R32, .macStr="d8:a0:1d:42:e0:24", .eee_id="eee-01376", .eee_pwd="LWWEBe2Au4Hy472W0pbA"},
{.hostName="ADISRobotR33", .robotID=ID_ROBOT_R33, .macStr="50:02:91:9f:8e:28", .eee_id="eee-01405", .eee_pwd="iWv6FaKso2ARMsPWpdkv"},
{.hostName="ADISRobotR34", .robotID=ID_ROBOT_R34, .macStr="50:02:91:9f:8d:dc", .eee_id="eee-01592", .eee_pwd="kmCgbmQjuxubnOAKu5Jo"},
{.hostName="ADISRobotR36", .robotID=ID_ROBOT_R36, .macStr="d8:a0:1d:42:e3:ac", .eee_id="eee-01406", .eee_pwd="gq3rkAs5VOuSgnFOJ3f5"},
{.hostName="ADISRobotR37", .robotID=ID_ROBOT_R37, .macStr="d8:a0:1d:42:ec:2c", .eee_id="eee-01377", .eee_pwd="d2GVw71vruR9baZA4EdE"},
{.hostName="ADISRobotR44", .robotID=ID_ROBOT_R44, .macStr="d8:a0:1d:42:e9:98", .eee_id="eee-01394", .eee_pwd="3gG3dQTyjjsteuv18EY9"},
{.hostName="ADISRobotR45", .robotID=ID_ROBOT_R45, .macStr="d8:a0:1d:42:e3:bc", .eee_id="eee-01417", .eee_pwd="zD5zCmXSo18qSMq55Lvs"},
/* ESP32 modules with no robot! */
{.hostName="ADISRobotE34", .robotID=ID_ROBOT_NONE, .macStr="50:02:91:a0:f8:18", .eee_id="eee-01389", .eee_pwd="yehFaZWK9kKSULwLyPyR"},
/* ESP32 breakout modules */
{.hostName="ESP32BRKOUT01", .robotID=ID_ROBOT_NONE, .macStr="d8:a0:1d:62:93:40", .eee_id="eee-01408", .eee_pwd="Gu03RtUCsrE99PafZCTo"},
{.hostName="ESP32DEVKIT02", .robotID=ID_ROBOT_NONE, .macStr="24:0a:c4:13:8e:30", .eee_id="eee-01411", .eee_pwd="6kagmMLOR3tDiRx6GfXm"},
};
void ESP32_MacToString(uint8_t mac[6], uint8_t *buf, size_t bufSize) {
McuXFormat_xsnprintf((char*)buf, bufSize, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
}
void ESP32_MacRead(uint8_t mac[6]) {
ESP_ERROR_CHECK(esp_read_mac(&mac[0], ESP_MAC_WIFI_STA));
}
bool ESP32_MacAreSame(const uint8_t macA[6], const uint8_t macB[6]) {
for(int j=0; j<6; j++) {
if(macA[j] != macB[j]) {
return false; /* no match */
}
}
return true; /* match! */
}
const ESP32_Device_t *ESP32_MacStrGetDevice(const char *macStr) {
int i;
for(i=0; i<sizeof(ESP32_devices)/sizeof(ESP32_devices[0]); i++) {
if (McuUtility_strcmp(macStr, ESP32_devices[i].macStr)==0) {
return &ESP32_devices[i];
}
}
return &ESP32_devices[0]; /* unknown robot entry */
}
const ESP32_Device_t *ESP32_GetDeviceConfig(void) {
uint8_t mac[6];
uint8_t macStr[sizeof("aa:bb:cc:dd:ee:ff")];
ESP32_MacRead(mac);
ESP32_MacToString(mac, macStr, sizeof(macStr));
return ESP32_MacStrGetDevice((char*)macStr);
}
void ESP32_MacInit(void) {
/* nothing needed */
}
#endif /* PL_CONFIG_USE_IDENTIFY */

@ -0,0 +1,64 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef ESP32_MAC_H_
#define ESP32_MAC_H_
#include "platform.h"
#if PL_CONFIG_USE_IDENTIFY
#include "Identify.h"
#include <stdint.h>
#include <stddef.h> /* for size_t */
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
const char *hostName; /* name for module, usually matching robot, e.g. "AdisRobotR8" */
ID_Robot_e robotID; /* ID of the robot */
const char *macStr; /* ESP32 MAC, e.g. "d8:a0:1d:42:ed:50" */
const char *eee_id; /* eee network id/name */
const char *eee_pwd; /* eee network password */
} ESP32_Device_t;
/*!
* \brief transforms a binary MAC into a string, e.g. "d8:a0:1d:42:ed:50"
* \param mac binary MAC
* \param buf buffer where to store the string
* \param bufSize size of buffer
*/
void ESP32_MacToString(uint8_t mac[6], uint8_t *buf, size_t bufSize);
/*!
* \brief return for a given MAC address string (e.g. "d8:a0:1d:42:e2:08") the device or NULL if not found.
*/
const ESP32_Device_t *ESP32_MacStrGetDevice(const char *macStr);
/*!
* \brief Read the MAC address into buffer
* \param mac buffer where to store the MAC address
*/
void ESP32_MacRead(uint8_t mac[6]);
/*!
* \brief return a device configuration based on MAC address
* \return pointer to device configuration. Pointing to a dummy configuration if MAC is not found in list.
*/
const ESP32_Device_t *ESP32_GetDeviceConfig(void);
/*!
* \brief Module initialization
*/
void ESP32_MacInit(void);
#endif /* PL_CONFIG_USE_IDENTIFY */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* ESP32_MAC_H_ */

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#include <stdio.h>
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "led.h"
#include "McuUtility.h"
#include "McuLED.h"
#include "McuLog.h"
#define TAG "Blinky"
static McuLED_Handle_t ledHandle;
static TaskHandle_t taskHandle;
static bool blinkyIsRunning = false;
#define LED_OFF_TIME_MS 1000
static uint32_t onTimeMs = 10; /* default on time of LED */
/* red led on the shield is on IO10, LOW active */
#define BLINK_GPIO (GPIO_NUM_10) /* IO10 */
#define LED_LOW_ACTIVE (1)
void LED_SetOnTime(uint32_t ms) {
onTimeMs = ms;
}
void LED_On(void) {
McuLED_On(ledHandle);
}
void LED_Off(void) {
McuLED_Off(ledHandle);
}
void LED_Suspend(void) {
if (taskHandle!=NULL) {
vTaskSuspend(taskHandle);
blinkyIsRunning = false;
LED_Off();
}
}
void LED_Resume(void) {
if (taskHandle!=NULL) {
vTaskResume(taskHandle);
blinkyIsRunning = true;
}
}
void LED_GetStatus(unsigned char *buf, size_t bufSize) {
buf[0] = '\0';
if (taskHandle!=NULL) {
eTaskState state;
state = eTaskGetState(taskHandle);
switch(state) {
case eSuspended:
McuUtility_strcpy(buf, bufSize, (unsigned char*)"suspended");
break;
case eRunning:
case eBlocked:
McuUtility_strcpy(buf, bufSize, (unsigned char*)"running");
break;
default:
case eDeleted:
McuUtility_strcpy(buf, bufSize, (unsigned char*)"ERROR!");
break;
}
} else {
McuUtility_strcpy(buf, bufSize, (unsigned char*)"ERROR: no task!");
}
}
static void blinkyTask(void *pv) {
(void)pv;
blinkyIsRunning = true;
ESP_LOGI(TAG, "running my blinky task");
McuLog_info("started blinky task");
for(;;) {
LED_On();
vTaskDelay(pdMS_TO_TICKS(onTimeMs));
LED_Off();
vTaskDelay(pdMS_TO_TICKS(LED_OFF_TIME_MS));
}
}
#if PL_CONFIG_USE_SHELL
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
McuShell_SendStatusStr((unsigned char*)"led", (unsigned char*)"ESP32 LED status\r\n", io->stdOut);
McuShell_SendStatusStr((unsigned char*)" status", blinkyIsRunning?(unsigned char*)"resumed\r\n":(unsigned char*)"suspended\r\n", io->stdOut);
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"led", (unsigned char*)"Group of ESP32 LED commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows LED help or status\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" suspend", (unsigned char*)"Suspend the LED task\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" resume", (unsigned char*)"Resume the LED task\r\n", io->stdOut);
return ERR_OK;
}
uint8_t LED_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"led help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"led status")==0) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)"led suspend")==0) {
*handled = TRUE;
LED_Suspend();
} else if (McuUtility_strcmp((char*)cmd, (char*)"led resume")==0) {
*handled = TRUE;
LED_Resume();
}
return ERR_OK;
}
#endif /* PL_CONFIG_USE_SHELL */
void LED_Init(void) {
BaseType_t res;
McuLED_Config_t config;
McuLED_GetDefaultConfig(&config);
config.hw.pin = BLINK_GPIO;
config.isLowActive = LED_LOW_ACTIVE;
config.isOnInit = false;
ledHandle = McuLED_InitLed(&config);
if (ledHandle==NULL) {
ESP_LOGE(TAG, "failed creating led handle!");
return;
}
res = xTaskCreate(blinkyTask, "blinkyTask", 4*1024/sizeof(StackType_t), NULL, tskIDLE_PRIORITY, &taskHandle);
if (res==pdPASS) {
ESP_LOGI(TAG, "created blinky task");
} else {
ESP_LOGE(TAG, "failed creating blinky!");
}
}

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef LED_H_
#define LED_H_
#include "platform.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
void LED_SetOnTime(uint32_t ms);
void LED_Suspend(void);
void LED_Resume(void);
void LED_GetStatus(unsigned char *buf, size_t bufSize);
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Command line and shell handler
* \param cmd The command to be parsed
* \param handled If command has been recognized and handled
* \param io I/O handler to be used
* \return error code, otherwise ERR_OK
*/
uint8_t LED_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
#endif
/*! \brief Module initialization */
void LED_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* LED_H_ */

@ -13,10 +13,12 @@
#include "esp_system.h" #include "esp_system.h"
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#include "myTask.h" #include "myTask.h"
#include "platform.h"
void app_main(void) void app_main(void)
{ {
printf("Hello world!\n"); printf("Hello world!\n");
PL_Init();
/* Print chip information */ /* Print chip information */
esp_chip_info_t chip_info; esp_chip_info_t chip_info;

@ -0,0 +1,114 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_PING
#include "ping.h"
#include "sdkconfig.h"
#include "lwip/inet.h"
#include "lwip/netdb.h"
#include "lwip/sockets.h"
#include "esp_console.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "argtable3/argtable3.h"
#include "ping/ping_sock.h"
static void cmd_ping_on_ping_success(esp_ping_handle_t hdl, void *args) {
// optionally, get callback arguments
// const char* str = (const char*) args;
// printf("%s\r\n", str); // "foo"
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
printf("%d bytes from %s icmp_seq=%d ttl=%d time=%d ms\n",
recv_len, inet_ntoa(target_addr.u_addr.ip4), seqno, ttl, elapsed_time);
}
static void cmd_ping_on_ping_timeout(esp_ping_handle_t hdl, void *args) {
uint16_t seqno;
ip_addr_t target_addr;
esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
printf("From %s icmp_seq=%d timeout\n", inet_ntoa(target_addr.u_addr.ip4), seqno);
}
void cmd_ping_on_ping_end(esp_ping_handle_t hdl, void *args) {
ip_addr_t target_addr;
uint32_t transmitted;
uint32_t received;
uint32_t total_time_ms;
esp_ping_get_profile(hdl, ESP_PING_PROF_REQUEST, &transmitted, sizeof(transmitted));
esp_ping_get_profile(hdl, ESP_PING_PROF_REPLY, &received, sizeof(received));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_DURATION, &total_time_ms, sizeof(total_time_ms));
uint32_t loss = (uint32_t)((1 - ((float)received) / transmitted) * 100);
if (IP_IS_V4(&target_addr)) {
printf("\n--- %s ping statistics ---\n", inet_ntoa(*ip_2_ip4(&target_addr)));
} else {
printf("\n--- %s ping statistics ---\n", inet6_ntoa(*ip_2_ip6(&target_addr)));
}
printf("%d packets transmitted, %d received, %d%% packet loss, time %dms\n",
transmitted, received, loss, total_time_ms);
// delete the ping sessions, so that we clean up all resources and can create a new ping session
// we don't have to call delete function in the callback, instead we can call delete function from other tasks
esp_ping_delete_session(hdl);
}
int PING_cmd_do_ping(const char *host) {
esp_ping_config_t config = ESP_PING_DEFAULT_CONFIG();
/* parse IP address */
struct sockaddr_in6 sock_addr6;
ip_addr_t target_addr;
const char *hostp = host; /* extra pointer for getaddrinfo() as it might change it */
memset(&target_addr, 0, sizeof(target_addr));
if (inet_pton(AF_INET6, hostp, &sock_addr6.sin6_addr) == 1) {
/* convert ip6 string to ip6 address */
ipaddr_aton(host, &target_addr);
} else {
struct addrinfo hint;
struct addrinfo *res = NULL;
memset(&hint, 0, sizeof(hint));
/* convert ip4 string or hostname to ip4 or ip6 address */
if (getaddrinfo(hostp, NULL, &hint, &res) != 0) {
printf("ping: unknown host %s\n", host);
return 1;
}
if (res->ai_family == AF_INET) {
struct in_addr addr4 = ((struct sockaddr_in *) (res->ai_addr))->sin_addr;
inet_addr_to_ip4addr(ip_2_ip4(&target_addr), &addr4);
} else {
struct in6_addr addr6 = ((struct sockaddr_in6 *) (res->ai_addr))->sin6_addr;
inet6_addr_to_ip6addr(ip_2_ip6(&target_addr), &addr6);
}
freeaddrinfo(res);
}
config.target_addr = target_addr;
/* set callback functions */
esp_ping_callbacks_t cbs = {
.on_ping_success = cmd_ping_on_ping_success,
.on_ping_timeout = cmd_ping_on_ping_timeout,
.on_ping_end = cmd_ping_on_ping_end,
.cb_args = NULL
};
esp_ping_handle_t ping;
esp_ping_new_session(&config, &cbs, &ping);
esp_ping_start(ping);
return 0; /* ok */
}
void PING_Cmd_Init(void) {
}
#endif /* PL_CONFIG_USE_PING */

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef MAIN_PING_H_
#define MAIN_PING_H_
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \brief performs a network ping
* \param host host name or address
* \return 0 for ok, negative values for error
*/
int PING_cmd_do_ping(const char *host);
/*!
* \brief Module initialization
*/
void PING_cmd_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* MAIN_PING_H_ */

@ -0,0 +1,59 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_PING
#include "ping_shell.h"
#include "ping.h"
#include "wifi.h"
#include "McuShell.h"
#include "McuUtility.h"
#if PL_CONFIG_USE_SHELL
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
McuShell_SendStatusStr((unsigned char*)"ping", (unsigned char*)"ESP32 ping status\r\n", io->stdOut);
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"ping", (unsigned char*)"Group of ESP32 ping commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows ESP32 ping help or status\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" <host>", (unsigned char*)"Ping host\r\n", io->stdOut);
return ERR_OK;
}
uint8_t PING_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
const unsigned char *p;
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"ping help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"ping status")==0) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strncmp((char*)cmd, (char*)"ping ", sizeof("ping ")-1)==0) {
*handled = TRUE;
if (!WiFi_isConnected()) {
McuShell_SendStr((unsigned char*)"Network not connected\r\n", io->stdErr);
return ERR_FAILED;
}
p = cmd + sizeof("ping ")-1;
if (PING_cmd_do_ping((const char*)p)==0) {
return ERR_OK;
} else {
return ERR_FAILED;
}
}
return ERR_OK;
}
#endif /* PL_CONFIG_USE_SHELL */
void PING_Init(void) {
}
#endif /* PL_CONFIG_USE_PING */

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef PING_SHELL_H_
#define PING_SHELL_H_
#include "platform.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Command line and shell handler
* \param cmd The command to be parsed
* \param handled If command has been recognized and handled
* \param io I/O handler to be used
* \return error code, otherwise ERR_OK
*/
uint8_t PING_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
#endif /* PL_CONFIG_USE_SHELL */
/*!
* \brief Module initialization
*/
void PING_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* PING_SHELL_H_ */

@ -0,0 +1,107 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#include "esp_err.h"
#if PL_CONFIG_USE_BLINKY
#include "led.h"
#endif
#if PL_CONFIG_USE_WIFI
#include "wifi.h"
#include "nvs_flash.h"
#endif
#if PL_CONFIG_USE_UDP_CLIENT
#include "udp_client.h"
#endif
#if PL_CONFIG_USE_UDP_SERVER
#include "udp_server.h"
#endif
#if PL_CONFIG_USE_SHELL
#include "Shell.h"
#endif
#if PL_CONFIG_USE_I2C
#include "i2c.h"
#endif
#if PL_CONFIG_USE_NVMC
#include "nvmc.h"
#endif
#if PL_CONFIG_USE_PING
#include "ping_shell.h"
#endif
#if PL_CONFIG_USE_SNTP_TIME
#include "sntp_time.h"
#endif
#if PL_CONFIG_USE_TIME_DATE
#include "McuTimeDate.h"
#include "timer.h"
#endif
#if PL_CONFIG_USE_ROBO_REMOTE
#include "robot.h"
#endif
#include "McuLib.h"
#include "McuGPIO.h"
#include "McuLED.h"
#include "McuUtility.h"
#include "McuWait.h"
#include "McuShell.h"
#include "McuRTOS.h"
#include "McuXFormat.h"
#include "McuLog.h"
#include "McuCriticalSection.h"
#include "esp32_mac.h"
#include "rs485.h"
void PL_Init(void) {
McuLib_Init();
McuLog_Init();
McuWait_Init();
McuRTOS_Init();
McuUtility_Init();
McuGPIO_Init();
McuLED_Init();
#if PL_CONFIG_USE_TIME_DATE
McuTimeDate_Init();
TMR_Init();
#endif
#if PL_CONFIG_USE_BLINKY
LED_Init();
#endif
#if PL_CONFIG_USE_WIFI
ESP_ERROR_CHECK(nvs_flash_init()); /* need to call this before using any WiFi functions */
ESP32_MacInit();
WiFi_Init();
#endif
#if PL_CONFIG_USE_UDP_SERVER
UDP_Server_Init();
#endif
#if PL_CONFIG_USE_UDP_CLIENT
UDP_Client_Init();
#endif
#if PL_CONFIG_USE_SHELL
McuXFormat_Init();
McuShell_Init();
SHELL_Init();
#endif
#if PL_CONFIG_USE_I2C
I2C_Init();
#endif
#if PL_CONFIG_USE_NVMC
NVMC_Init();
#endif
#if PL_CONFIG_USE_RS485
RS485_Init();
#endif
#if PL_CONFIG_USE_PING
PING_Init();
#endif
#if PL_CONFIG_USE_SNTP_TIME
SNTP_Init();
#endif
#if PL_CONFIG_USE_ROBO_REMOTE
ROBOT_Init();
#endif
}

@ -0,0 +1,32 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef PLATFORM_H_
#define PLATFORM_H_
/* platform configuration macros: turn on to enable functionality */
#define PL_CONFIG_USE_BLINKY (0) /*!< if using blinky LED */
#define PL_CONFIG_USE_WIFI (0) /*!< if using WiFi/WLAN */
#define PL_CONFIG_USE_IDENTIFY (0 && PL_CONFIG_USE_WIFI) /*!< used to identify ESP32 and robot, needed for EEE network */
#define PL_CONFIG_USE_UDP_SERVER (0 && PL_CONFIG_USE_WIFI) /*!< UDP server, used for communication to robot */
#define PL_CONFIG_USE_UDP_CLIENT (0 && PL_CONFIG_USE_WIFI) /*!< UDP client, optionally available for tests */
#define PL_CONFIG_USE_PING (0 && PL_CONFIG_USE_WIFI) /*!< shell command with ping, to test network connection */
#define PL_CONFIG_USE_SHELL (0) /*!< implements shell between robot and ESP32 */
#define PL_CONFIG_USE_RS485 (0) /*!< ESP32 using RS-485 to split-flaps */
#define PL_CONFIG_USE_SNTP_TIME (0 && PL_CONFIG_USE_WIFI)
#define PL_CONFIG_USE_TIME_DATE (0) /*!< if using Time and Date information */
#define PL_CONFIG_USE_ROBO_REMOTE (0 && PL_CONFIG_USE_UDP_SERVER) /* UDP Remote controller for robot */
/*! \brief Module and platform initialization */
void PL_Init(void);
/* the following ones are not implemented yet: */
#define PL_CONFIG_USE_I2C (0)
#define PL_CONFIG_USE_NVMC (0)
#define PL_CONFIG_USE_WDT (0) /* NYI */
#endif /* PLATFORM_H_ */

@ -0,0 +1,66 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_ROBO_REMOTE
#include "robot.h"
#include "udp_server.h"
#include "wifi.h"
#include "McuShell.h"
#include "McuUtility.h"
#include "Shell.h"
#if PL_CONFIG_USE_SHELL
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
McuShell_SendStatusStr((unsigned char*)"robo", (unsigned char*)"ESP32 robo status\r\n", io->stdOut);
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"robo", (unsigned char*)"Group of robot commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows robot help or status\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" send <text>", (unsigned char*)"Send a text to the robot\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" sendcmd <cmd>", (unsigned char*)"Send a command to the robot, e.g. '#buzzer buz 100 200'\r\n", io->stdOut);
return ERR_OK;
}
uint8_t ROBOT_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"robo help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"robo status")==0) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strncmp((char*)cmd, (char*)"robo send ", sizeof("robo send ")-1)==0) {
const unsigned char *p;
*handled = TRUE;
p = cmd+sizeof("robo send ")-1;
McuShell_SendStr(p, io->stdOut); /* send to standard I/O which is the UART to the robot */
return ERR_OK;
} else if (McuUtility_strncmp((char*)cmd, (char*)"robo sendcmd ", sizeof("robo sendcmd ")-1)==0) {
static uint8_t response[10*1024];
const unsigned char *p;
*handled = TRUE;
p = cmd+sizeof("robo sendcmd ")-1;
SHELL_SendToRobotAndGetResponse(p, response, sizeof(response));
McuShell_SendStr(response, io->stdOut); /* show result on console */
return ERR_OK;
}
return ERR_OK;
}
#endif /* PL_CONFIG_USE_SHELL */
void ROBOT_Deinit(void) {
/* nothing needed */
}
void ROBOT_Init(void) {
/* nothing needed */
}
#endif /* PL_CONFIG_USE_ROBO_REMOTE */

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef MAIN_ROBOT_H_
#define MAIN_ROBOT_H_
#include "platform.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Command line and shell handler
* \param cmd The command to be parsed
* \param handled If command has been recognized and handled
* \param io I/O handler to be used
* \return error code, otherwise ERR_OK
*/
uint8_t ROBOT_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
#endif /* PL_CONFIG_USE_SHELL */
/*! \brief Module de-initialization */
void ROBOT_Deinit(void);
/*! \brief Module initialization */
void ROBOT_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* MAIN_ROBOT_H_ */

@ -0,0 +1,632 @@
/*
* Copyright (c) 2019-2022, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_RS485
#include "rs485.h"
#include "McuGPIO.h"
#include "McuUart485.h"
#include "McuShell.h"
#include "McuRTOS.h"
#include "McuUtility.h"
#include "McuLog.h"
#include "Shell.h"
#if McuLib_CONFIG_CPU_IS_KINETIS || McuLib_CONFIG_CPU_IS_LPC
#include "nvmc.h"
#endif
#if PL_CONFIG_USE_WDT
#include "watchdog.h"
#endif
#if 0 /* \TODO */
#include "stepper.h"
#endif
typedef enum RS485_Response_e {
RS485_RESPONSE_CONTINUE, /* continue scanning and parsing */
RS485_RESPONSE_OK, /* ok response */
RS485_RESPONSE_NOK, /* not ok response */
RS485_RESPONSE_TIMEOUT, /* timeout */
} RS485_Response_e;
static bool RS485_DoLogging = false; /* if traffic on the bus shall be reported on the shell */
static SemaphoreHandle_t RS485_stdioMutex; /* mutex to protect access to standard I/O */
uint8_t RS485_GetAddress(void) {
#if McuLib_CONFIG_CPU_IS_KINETIS || McuLib_CONFIG_CPU_IS_LPC
uint8_t addr = 0;
if (NVMC_GetRS485Addr(&addr)==ERR_OK) {
return addr;
}
return 0; /* failed */
#elif McuLib_CONFIG_CPU_IS_ESP32
return 1; /* hard coded */
#endif
}
static void RS485_SendChar(unsigned char ch) {
McuUart485_stdio.stdOut(ch);
}
static void RS485_NullSend(unsigned char ch) {
/* do nothing */
}
static void RS485_ReadChar(uint8_t *c) {
*c = '\0'; /* only sending on this channel */
}
static bool RS485_CharPresent(void) {
return false; /* only sending on this channel */
}
McuShell_ConstStdIOType RS485_stdio = {
.stdIn = (McuShell_StdIO_In_FctType)RS485_ReadChar,
.stdOut = (McuShell_StdIO_OutErr_FctType)RS485_SendChar,
.stdErr = (McuShell_StdIO_OutErr_FctType)RS485_SendChar,
.keyPressed = RS485_CharPresent, /* if input is not empty */
#if McuShell_CONFIG_ECHO_ENABLED
.echoEnabled = false,
#endif
};
McuShell_ConstStdIOType RS485_stdioBroadcast = {
.stdIn = (McuShell_StdIO_In_FctType)RS485_ReadChar,
.stdOut = (McuShell_StdIO_OutErr_FctType)RS485_NullSend,
.stdErr = (McuShell_StdIO_OutErr_FctType)RS485_NullSend,
.keyPressed = RS485_CharPresent, /* if input is not empty */
#if McuShell_CONFIG_ECHO_ENABLED
.echoEnabled = false,
#endif
};
/*-----------------------------------------------------------------------*/
/* parser I/O handler, used to parse the returned data after sending a command to the RS-485 bus */
static void RS485Parse_SendChar(unsigned char ch) {
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
McuUart485_stdio.stdOut(ch);
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */
}
}
static void RS485Parse_ReadChar(uint8_t *c) {
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
McuUart485_stdio.stdIn(c);
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */
}
}
static bool RS485Parse_CharPresent(void) {
bool inputPresent = false;
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
inputPresent = McuUart485_stdio.keyPressed();
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */
}
return inputPresent;
}
static McuShell_ConstStdIOType RS485Parse_stdio = {
.stdIn = (McuShell_StdIO_In_FctType)RS485Parse_ReadChar,
.stdOut = (McuShell_StdIO_OutErr_FctType)RS485Parse_SendChar,
.stdErr = (McuShell_StdIO_OutErr_FctType)RS485Parse_SendChar,
.keyPressed = RS485Parse_CharPresent, /* if input is not empty */
#if McuShell_CONFIG_ECHO_ENABLED
.echoEnabled = false,
#endif
};
/*-----------------------------------------------------------------------*/
static void RS485_SendStr(unsigned char *str) {
while(*str!='\0') {
RS485_stdio.stdOut(*str++);
}
}
static uint8_t CalcCRC(const uint8_t *data, uint8_t dataSize, uint8_t start) {
uint8_t crc, i, x, y;
crc = start;
for(x=0;x<dataSize;x++){
y = data[x];
for(i=0;i<8;i++) { /* go through all bits of the data byte */
if((crc&0x01)^(y&0x01)) {
crc >>= 1;
crc ^= 0x8c;
} else {
crc >>= 1;
}
y >>= 1;
}
}
return crc;
}
static uint8_t CalcMsgCrc(const unsigned char *msg) {
/* header is "@dd ss cc ...", both dd, ss and cc are 8bit HEX values. The CRC (cc) is not included in the CRC */
uint8_t crc;
crc = CalcCRC(msg, sizeof("@dd ss ")-1, 0);
crc = CalcCRC(msg+sizeof("@dd ss cc")-1, strlen((char*)msg+sizeof("@dd ss cc")-1), crc);
return crc;
}
typedef enum CMD_ParserState_e {
CMD_PARSER_INIT,
CMD_PARSER_START_DETECTED, /* start '@' detected */
CMD_PARSER_SCAN_DST_ADDR, /* scanning destination address */
CMD_PARSER_SCAN_SRC_ADDR, /* scan source address */
CMD_PARSER_SCAN_CRC, /* scan CRC */
CMD_PARSER_SCAN_OK_NOK, /* reading NOK or OK */
} CMD_ParserState_e;
static RS485_Response_e Scan(CMD_ParserState_e *state, unsigned char ch, unsigned char *buf, size_t bufSize, uint8_t fromAddr) {
/* scan for "@<dstaddr> <srcAddr> <CRC> OK"
* or "@<dstaddr> <srcAddr> <CRC> NOK"
*/
const unsigned char *p;
uint8_t addr;
uint8_t exp_crc;
static uint8_t crc = 0;
if (ch=='@') { /* a marker? start scanning again */
*state = CMD_PARSER_INIT;
}
for(;;) { /* breaks or returns */
switch(*state) {
case CMD_PARSER_INIT:
buf[0] = '\0'; /* reset buffer */
if (ch=='@') { /* a marker? start scanning again */
McuUtility_chcat(buf, bufSize, ch);
*state = CMD_PARSER_START_DETECTED;
break; /* continue state machine */
}
return RS485_RESPONSE_CONTINUE;
case CMD_PARSER_START_DETECTED:
*state = CMD_PARSER_SCAN_DST_ADDR;
return RS485_RESPONSE_CONTINUE;
case CMD_PARSER_SCAN_DST_ADDR:
McuUtility_chcat(buf, bufSize, ch);
if (ch==' ') {
p = &buf[sizeof("@")-1];
if (McuUtility_ScanHex8uNumberNoPrefix(&p, &addr)==ERR_OK && addr==RS485_GetAddress()) {
*state = CMD_PARSER_SCAN_SRC_ADDR;
return RS485_RESPONSE_CONTINUE;
} else {
*state = CMD_PARSER_INIT;
}
} else if (ch=='\n') { /* failed */
*state = CMD_PARSER_INIT;
}
return RS485_RESPONSE_CONTINUE;
case CMD_PARSER_SCAN_SRC_ADDR:
McuUtility_chcat(buf, bufSize, ch);
if (ch==' ') {
p = &buf[sizeof("@ss ")-1];
if (McuUtility_ScanHex8uNumberNoPrefix(&p, &addr)==ERR_OK && addr==fromAddr) {
*state = CMD_PARSER_SCAN_CRC;
return RS485_RESPONSE_CONTINUE;
} else {
*state = CMD_PARSER_INIT;
}
} else if (ch=='\n') { /* failed */
*state = CMD_PARSER_INIT;
}
return RS485_RESPONSE_CONTINUE;
case CMD_PARSER_SCAN_CRC:
McuUtility_chcat(buf, bufSize, ch);
if (ch==' ') {
p = &buf[sizeof("@ss dd ")-1];
if (McuUtility_ScanHex8uNumberNoPrefix(&p, &crc)==ERR_OK) {
*state = CMD_PARSER_SCAN_OK_NOK;
return RS485_RESPONSE_CONTINUE;
} else {
*state = CMD_PARSER_INIT;
}
} else if (ch=='\n') { /* failed */
*state = CMD_PARSER_INIT;
}
return RS485_RESPONSE_CONTINUE;
case CMD_PARSER_SCAN_OK_NOK:
McuUtility_chcat(buf, bufSize, ch);
if (ch=='\n') {
p = &buf[sizeof("@ss dd cc ")-1];
if (McuUtility_strcmp((char*)p, (char*)"OK\n")==0) { /* a match! */
buf[sizeof("@ss dd cc OK")-1] = '\0'; /* overwrite '\n' as not included in CRC */
exp_crc = CalcMsgCrc(buf);
*state = CMD_PARSER_INIT;
if (crc==exp_crc) {
return RS485_RESPONSE_OK;
} else {
return RS485_RESPONSE_NOK;
}
} else if (McuUtility_strcmp((char*)buf, (char*)"NOK\n")==0) { /* a match! */
buf[sizeof("@ss dd cc NOK")-1] = '\0'; /* overwrite '\n' as not included in CRC */
exp_crc = CalcMsgCrc(buf);
*state = CMD_PARSER_INIT;
if (crc==exp_crc) {
return RS485_RESPONSE_NOK;
} else {
return RS485_RESPONSE_NOK; /* CRC is not ok, and message is NOK too */
}
} else if (ch=='\n') { /* failed */
*state = CMD_PARSER_INIT;
break; /* continue in state machine */
}
}
return RS485_RESPONSE_CONTINUE;
default:
break;
} /* switch */
/* get here with a break */
} /* for */
return RS485_RESPONSE_CONTINUE;
}
static RS485_Response_e WaitForResponse(int32_t timeoutMs, uint8_t fromAddr, McuShell_ConstStdIOType *shellIO, McuShell_ConstStdIOType *rsIO) {
unsigned char buf[24] = ""; /* enough for "@<addr> <fromAddr> OK" or "@<addr> <fromAddr> NOK" */
unsigned char ch;
RS485_Response_e resp;
CMD_ParserState_e state = CMD_PARSER_INIT;
for(;;) { /* returns */
/* read response text and write into buffer or to console */
static unsigned char lineBuffer[512]; /* enough for a line of text coming back from the bus */
lineBuffer[0] = '\0'; /* initialize buffer */
do {
rsIO->stdIn(&ch);
if (ch!='\0') {
McuUtility_chcat(lineBuffer, sizeof(lineBuffer), ch);
if (ch=='\n') {
if (lineBuffer[0]!='@') { /* do not send things like OK or NOK messages from bus */
SHELL_SendStringToIO(lineBuffer, shellIO);
}
lineBuffer[0] = '\0'; /* reset buffer */
}
}
} while(ch!='\0');
ch = McuUart485_GetResponseQueueChar();
if (ch!='\0') {
resp = Scan(&state, ch, buf, sizeof(buf), fromAddr);
if (resp==RS485_RESPONSE_OK || resp==RS485_RESPONSE_NOK) {
return resp;
}
} else { /* empty response buffer: check normal incoming characters */
vTaskDelay(pdMS_TO_TICKS(50));
#if PL_CONFIG_USE_WDT
WDT_Report(WDT_REPORT_ID_CURR_TASK, 50);
#endif
timeoutMs -= 50;
if (timeoutMs<=0) {
return RS485_RESPONSE_TIMEOUT;
}
}
} /* for */
return RS485_RESPONSE_CONTINUE;
}
uint8_t RS485_SendCommand(uint8_t dstAddr, const unsigned char *cmd, int32_t timeoutMs, uint32_t nofRetry, McuShell_ConstStdIOType *shellIO, McuShell_ConstStdIOType *rsIO) {
/* example: send "@16 1 cmd stepper status" */
unsigned char buf[McuShell_DEFAULT_SHELL_BUFFER_SIZE];
uint8_t res = ERR_OK;
RS485_Response_e resp;
uint8_t crc, hex;
if (rsIO==NULL) { /* assign default */
rsIO = &RS485Parse_stdio;
}
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)("@"));
McuUtility_strcatNum8Hex(buf, sizeof(buf), dstAddr); /* add destination address */
McuUtility_chcat(buf, sizeof(buf), ' ');
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress()); /* add src address */
McuUtility_chcat(buf, sizeof(buf), ' ');
McuUtility_strcatNum8Hex(buf, sizeof(buf), 0); /* dummy crc, will be replaced with real one */
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)(" cmd "));
McuUtility_strcat(buf, sizeof(buf), cmd);
/* update crc */
crc = CalcMsgCrc(buf);
hex = (char)((crc>>4) & 0x0F);
buf[sizeof("@dd ss ")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10)));
hex = (char)(crc & 0x0F);
buf[sizeof("@dd ss c")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10)));
if (xSemaphoreTakeRecursive(RS485_stdioMutex, portMAX_DELAY)==pdPASS) { /* take mutex */
for(;;) { /* breaks */
McuUart485_ClearResponseQueue(); /* clear up if there is something pending */
if (RS485_DoLogging) {
McuLog_trace("Tx: %s", buf);
}
RS485_SendStr(buf);
RS485_SendStr((unsigned char*)"\n");
if (dstAddr==RS485_BROADCAST_ADDRESS) {
/* do not wait for a OK/NOK response for broadcast messages. The caller has to check with 'lastError' */
res = ERR_OK;
break; /* leave loop */
} else {
vTaskDelay(pdMS_TO_TICKS(100)); /* give back some time for receiving a response */
resp = WaitForResponse(timeoutMs, dstAddr, shellIO, rsIO);
if (resp==RS485_RESPONSE_OK) {
res = ERR_OK;
break; /* fine, leave loop */
} else if (resp==RS485_RESPONSE_TIMEOUT) { /* board did not respond? */
res = ERR_BUSOFF; /* retry */
} else if (resp==RS485_RESPONSE_NOK) { /* not ok, crc error? */
res = ERR_CRC; /* retry */
}
}
/* NOK or timeout */
if (nofRetry==0) { /* tried enough */
res = ERR_FAILED;
break; /* leave loop */
}
nofRetry--; /* try again */
} /* for */
(void)xSemaphoreGiveRecursive(RS485_stdioMutex); /* give back mutex */
}
return res;
}
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
uint8_t buf[16];
McuShell_SendStatusStr((unsigned char*)"rs", (unsigned char*)"RS-485 settings\r\n", io->stdOut);
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"0x");
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress());
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" addr", buf, io->stdOut);
McuShell_SendStatusStr((unsigned char*)" log", RS485_DoLogging?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut);
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"rs", (unsigned char*)"Group of RS-485 commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Print help or status information\r\n", io->stdOut);
#if PL_CONFIG_USE_NVMC
McuShell_SendHelpStr((unsigned char*)" addr <addr>", (unsigned char*)"Set RS-485 address\r\n", io->stdOut);
#endif
McuShell_SendHelpStr((unsigned char*)" send <text>", (unsigned char*)"Send a text to the RS-485 bus\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" sendcmd <addr> <cmd>", (unsigned char*)"Send a shell command to the RS-485 address and check response\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" log on|off", (unsigned char*)"Log RS-485 bus activity to McuLog\r\n", io->stdOut);
return ERR_OK;
}
uint8_t RS485_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io) {
const unsigned char *p;
int32_t val;
if (McuUtility_strcmp((char*)cmd, McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, "rs help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if ((McuUtility_strcmp((char*)cmd, McuShell_CMD_STATUS)==0) || (McuUtility_strcmp((char*)cmd, "rs status")==0)) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strncmp((char*)cmd, "rs log ", sizeof("rs log ")-1)==0) {
*handled = TRUE;
p = cmd + sizeof("rs log ")-1;
if (McuUtility_strcmp((char*)p, "on")==0) {
RS485_DoLogging = true;
return ERR_OK;
} else if (McuUtility_strcmp((char*)p, "off")==0) {
RS485_DoLogging = false;
return ERR_OK;
}
return ERR_FAILED;
#if PL_CONFIG_USE_NVMC
} else if (McuUtility_strncmp((char*)cmd, "rs addr ", sizeof("rs addr ")-1)==0) {
*handled = true;
p = cmd + sizeof("rs addr ")-1;
if (McuUtility_xatoi(&p, &val)==ERR_OK && val>=0 && val<=0xff) {
return NVMC_SetRS485Addr(val);
}
return ERR_FAILED;
#endif
} else if (McuUtility_strncmp((char*)cmd, "rs send ", sizeof("rs send ")-1)==0) {
*handled = true;
RS485_SendStr((unsigned char*)cmd+sizeof("rs send ")-1);
RS485_SendStr((unsigned char*)("\n"));
} else if (McuUtility_strncmp((char*)cmd, "rs sendcmd ", sizeof("rs sendcmd ")-1)==0) {
*handled = true;
p = cmd + sizeof("rs sendcmd ")-1;
if (McuUtility_xatoi(&p, &val)==ERR_OK) { /* parse destination address */
unsigned char buffer[McuShell_CONFIG_DEFAULT_SHELL_BUFFER_SIZE];
while (*p==' ') { /* skip leading spaces */
p++;
}
if (*p=='"') { /* double-quoted command: it can contain multiple commands */
if (McuUtility_ScanDoubleQuotedString(&p, buffer, sizeof(buffer))!=ERR_OK) {
return ERR_FAILED;
}
p = buffer;
}
return RS485_SendCommand(val, (unsigned char*)p, 10000, 0, io, &RS485Parse_stdio); /* 10 seconds should be enough */
}
return ERR_FAILED;
}
return ERR_OK;
}
static uint8_t CheckHeader(unsigned char *msg, const unsigned char **startCmd, uint8_t *sourceAddr, uint8_t *destinationAddr) {
/* format is in the form "@<DST_ADDR> <SRC_ADDR> <CRC> cmd help" */
const unsigned char *p;
uint8_t dstAddr, srcAddr;
uint8_t expected_crc, crc, res;
unsigned char buf[42];
/* init with defaults in case of error */
*sourceAddr = RS485_BROADCAST_ADDRESS;
*destinationAddr = RS485_BROADCAST_ADDRESS;
*startCmd = msg;
if (*msg=='@') {
p = msg+1; /* skip '@' */
/* check for "@<DST_ADDR> <SRC_ADDR>" */
if ( McuUtility_ScanHex8uNumberNoPrefix(&p, &dstAddr)==ERR_OK
&& (dstAddr==RS485_GetAddress() || dstAddr==RS485_BROADCAST_ADDRESS) /* broadcast or matching destination address */
&& McuUtility_ScanHex8uNumberNoPrefix(&p, &srcAddr)==ERR_OK /* get source address */
)
{
*sourceAddr = srcAddr;
*destinationAddr = dstAddr;
/* check CRC */
res = McuUtility_ScanHex8uNumberNoPrefix(&p, &crc);
if (res!=ERR_OK) {
return ERR_CRC;
}
expected_crc = CalcMsgCrc(msg);
if (crc!=expected_crc) {
if (dstAddr!=RS485_BROADCAST_ADDRESS) { /* only send back error if it was not a broadcast */
McuUtility_strcpy(buf, sizeof(buf), (uint8_t*)"CRC_ERR 0x");
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress());
McuUtility_strcat(buf, sizeof(buf), (uint8_t*)": 0x");
McuUtility_strcatNum8Hex(buf, sizeof(buf), crc);
McuUtility_strcat(buf, sizeof(buf), (uint8_t*)" expected 0x");
McuUtility_strcatNum8Hex(buf, sizeof(buf), expected_crc);
McuUtility_chcat(buf, sizeof(buf), '\n');
McuShell_SendStr(buf, RS485_stdio.stdErr);
}
return ERR_CRC;
}
*startCmd = p;
return ERR_OK;
}
}
return ERR_FAILED;
}
static void RS485Task(void *pv) {
static uint8_t cmdBuf[McuShell_DEFAULT_SHELL_BUFFER_SIZE]; /* command line and text from the RS-485 bus */
const unsigned char *startCmd;
uint8_t srcAddr, dstAddr;
uint8_t res, crc;
uint8_t buf[32];
unsigned char hex;
bool reply;
static uint8_t lastError = ERR_OK;
(void)pv; /* not used */
McuLog_trace("Starting RS485 Task");
#if PL_CONFIG_USE_WDT
WDT_SetTaskHandle(WDT_REPORT_ID_TASK_RS485, xTaskGetCurrentTaskHandle());
#endif
cmdBuf[0] = '\0';
for(;;) {
while (!McuUart485_stdio.keyPressed()) { /* if nothing in input queue, give back some CPU time */
vTaskDelay(pdMS_TO_TICKS(10));
#if PL_CONFIG_USE_WDT
WDT_Report(WDT_REPORT_ID_TASK_RS485, 10);
#endif
}
if (McuShell_ReadCommandLine(cmdBuf, sizeof(cmdBuf), &RS485Parse_stdio)==ERR_OK) {
reply = false;
srcAddr = RS485_ILLEGAL_ADDRESS;
dstAddr = RS485_ILLEGAL_ADDRESS;
if (cmdBuf[0]=='@' && strlen((char*)cmdBuf)>sizeof("@dd ss cc ")-1) { /* have a valid message? */
if (RS485_DoLogging) {
McuLog_trace("Rx: %s", cmdBuf);
}
reply = false; /* default */
res = CheckHeader(cmdBuf, &startCmd, &srcAddr, &dstAddr);
if (res == ERR_CRC) { /* wrong crc */
lastError = ERR_CRC;
reply = true;
} else if (res==ERR_OK) { /* header was ok */
res = ERR_FAILED; /* set default return value */
if (McuUtility_strcmp((char*)startCmd, (char*)" cmd lastError")==0) {
reply = true;
res = lastError; /* report back last error */
lastError = ERR_OK; /* clear error */
} else if (McuUtility_strcmp((char*)startCmd, (char*)" cmd idle")==0) {
reply = true;
#if 0 /* \TODO */
if (STEPPER_IsIdle()) {
res = ERR_OK; /* ERR_OK if board is idle */
} else {
res = ERR_FAILED; /* not idle */
}
#else
res = ERR_FAILED; /* not idle */
#endif
} else if (McuUtility_strncmp((char*)startCmd, " cmd ", sizeof(" cmd ")-1)==0) { /* shell command? */
McuUart485_ClearResponseQueue(); /* clear any pending response: we are going to parse a new command */
startCmd += sizeof(" cmd ")-1;
if (dstAddr==RS485_BROADCAST_ADDRESS) {
res = SHELL_ParseCommandIO(startCmd, &RS485_stdioBroadcast, true); /* do not write anything back if broadcast */
} else {
res = SHELL_ParseCommandIO(startCmd, &RS485_stdio, true);
}
lastError = res; /* remember error status if we get asked later on */
reply = true;
} else if (McuUtility_strcmp((char*)startCmd, (char*)" OK")==0) {
reply = false;
} else if (McuUtility_strcmp((char*)startCmd, (char*)" NOK")==0) {
reply = false;
}
}
} else {
/* not starting with '@', print it ... */
McuUtility_strcat(cmdBuf, sizeof(cmdBuf), (unsigned char*)"\r\n"); /* for the shell parser, the new-line has been removed. Add it again for output */
SHELL_SendString((unsigned char *)cmdBuf); /* \TODO do not send directly to UART: instead, use a stdio which buffers the output */
}
cmdBuf[0] = '\0'; /* reset buffer for next iteration */
/* send response back to sender */
if (reply && dstAddr!=RS485_BROADCAST_ADDRESS) { /* normal message, send response. For broadcasts it is up to the caller to check the last error */
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"@");
McuUtility_strcatNum8Hex(buf, sizeof(buf), srcAddr);
McuUtility_chcat(buf, sizeof(buf), ' ');
McuUtility_strcatNum8Hex(buf, sizeof(buf), RS485_GetAddress());
McuUtility_chcat(buf, sizeof(buf), ' ');
McuUtility_strcatNum8Hex(buf, sizeof(buf), 0); /* dummy crc, will be replaced later */
if (res==ERR_OK) {
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" OK");
} else {
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)" NOK");
}
crc = CalcMsgCrc(buf);
hex = (char)((crc>>4) & 0x0F);
buf[sizeof("@dd ss ")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10)));
hex = (char)(crc & 0x0F);
buf[sizeof("@dd ss c")-1] = (char)(hex + ((hex <= 9) ? '0' : ('A'-10)));
McuUtility_chcat(buf, sizeof(buf), '\n');
RS485_SendStr(buf);
}
}
} /* for */
}
void RS485_Deinit(void) {
McuUart485_Deinit();
}
void RS485_Init(void) {
McuUart485_Init();
if (xTaskCreate(
RS485Task, /* pointer to the task */
"RS-485", /* task name for kernel awareness debugging */
1300/sizeof(StackType_t), /* task stack size */
(void*)NULL, /* optional task startup argument */
tskIDLE_PRIORITY+4, /* initial priority */
(TaskHandle_t*)NULL /* optional task handle to create */
) != pdPASS)
{
McuLog_fatal("Failed creating RS-485 task");
for(;;){} /* error! probably out of memory */
}
RS485_stdioMutex = xSemaphoreCreateRecursiveMutex();
if (RS485_stdioMutex==NULL) { /* creation failed? */
McuLog_fatal("Failed creating RS-485 Standard I/O mutex");
for(;;);
}
vQueueAddToRegistry(RS485_stdioMutex, "RS485StdIoMutex");
}
#endif /* PL_CONFIG_USE_RS485 */

@ -0,0 +1,61 @@
/*
* Copyright (c) 2019-2022, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef RS485_H_
#define RS485_H_
#include <stdbool.h>
#include <stdint.h>
#include "McuShell.h"
#ifdef __cplusplus
extern "C" {
#endif
/*! special pre-defined node addresses */
#define RS485_BROADCAST_ADDRESS (0x00)
/*!< special broadcast address */
#define RS485_ILLEGAL_ADDRESS (0xff)
/*!< illegal/initialization value */
/*!
* \brief Send a shell command to the RS-485 bus
* \param dstAddr Destination node address
* \param cmd The shell command string
* \param timeoutMs timeout in milliseconds to wait for a response
* \param nofRetry Number of retries
* \param shellIO I/O handler of the calling shell, used for writing output of the command
* \param rsIO I/O handler for the RS-485 bus, used to read incoming characters
* \return Error code, or ERR_OK
*/
uint8_t RS485_SendCommand(uint8_t dstAddr, const unsigned char *cmd, int32_t timeoutMs, uint32_t nofRetry, McuShell_ConstStdIOType *shellIO, McuShell_ConstStdIOType *rsIO);
/*!
* \brief Getter for RS-485 node address
* \return address of node
*/
uint8_t RS485_GetAddress(void);
/*!
* \brief Shell command line parser
* \param cmd command to be parsed
* \param handled if command was recognized
* \param io I/O handler
* \return Error code, or ERR_OK
*/
uint8_t RS485_ParseCommand(const unsigned char *cmd, bool *handled, const McuShell_StdIOType *io);
/*! \brief Module de-initialization */
void RS485_Deinit(void);
/*! \brief Module initialization */
void RS485_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* RS485_H_ */

@ -0,0 +1,89 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_SNTP_TIME
#include "sntp_time.h"
#include "esp_sntp.h"
#include "esp_log.h"
#include "wifi.h"
#include "McuTimeDate.h"
#include "McuLog.h"
static void SNTP_SetTime(void) {
time_t now = 0;
struct tm timeinfo = { 0 };
time(&now);
setenv("TZ", "CET-1CEST,M3.5.0,M10.5.0/3", 1); /* see https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv */
tzset();
localtime_r(&now, &timeinfo);
char strftime_buf[64];
strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
McuLog_info("The current date/time is: %s", strftime_buf);
#if PL_CONFIG_USE_TIME_DATE
uint32_t seconds;
TIMEREC timeRec;
DATEREC dateRec;
seconds = (uint32_t)time(NULL); /* get number of seconds since 1970 */
McuTimeDate_UnixSecondsToTimeDateCustom(seconds, -1, &timeRec, &dateRec, 1970);
McuTimeDate_SetTimeDate(&timeRec, &dateRec);
#endif
}
void time_sync_notification_cb(struct timeval *tv) {
McuLog_info("Notification of a time synchronization event");
SNTP_SetTime();
}
static void initialize_sntp(void) {
McuLog_info("Initializing SNTP");
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, "pool.ntp.org");
sntp_set_time_sync_notification_cb(time_sync_notification_cb);
#ifdef CONFIG_SNTP_TIME_SYNC_METHOD_SMOOTH
sntp_set_sync_mode(SNTP_SYNC_MODE_SMOOTH);
#endif
sntp_init();
}
int SNTP_ObtainTime(void) {
const int retry_count = 10;
int retry = 0;
if (!WiFi_isConnected()) {
McuLog_error("Cannot get sntp time, WiFi not connected");
return -1; /* failure */
}
/**
* NTP server address could be acquired via DHCP,
* see LWIP_DHCP_GET_NTP_SRV menuconfig option
*/
#ifdef LWIP_DHCP_GET_NTP_SRV
sntp_servermode_dhcp(1);
#endif
initialize_sntp();
/* wait for time to be set */
while (sntp_get_sync_status() == SNTP_SYNC_STATUS_RESET && ++retry < retry_count) {
McuLog_info("Waiting for system time to be set... (%d/%d)", retry, retry_count);
vTaskDelay(pdMS_TO_TICKS(2000));
}
/* here we have received a valid time/date from the SNTP server */
return 0; /* ok */
}
void SNTP_Init(void) {
/* nothing needed */
}
#endif /* PL_CONFIG_USE_SNTP_TIME */

@ -0,0 +1,27 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef MAIN_SNTP_TIME_H_
#define MAIN_SNTP_TIME_H_
#ifdef __cplusplus
extern "C" {
#endif
/*!
* \brief Get the time from the sntp server
* \return 0 if time has been obtained, negative value in case of error.
*/
int SNTP_ObtainTime(void);
/*! \brief module initialization */
void SNTP_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* MAIN_SNTP_TIME_H_ */

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_TIME_DATE
#include "timer.h"
#include "McuRTOS.h"
#include "esp_log.h"
#include "McuTimeDate.h"
static const char *TAG = "Timer";
static TimerHandle_t timer;
static void timerCallback(TimerHandle_t xTimer) {
McuTimeDate_AddTick();
}
void TMR_Init(void) {
/* create auto-reload timer to update software RTC */
timer = xTimerCreate("timer", pdMS_TO_TICKS(McuTimeDate_CONFIG_TICK_TIME_MS), pdTRUE, NULL, timerCallback);
if (timer==NULL) {
ESP_LOGE(TAG, "Failed creating timer");
return;
}
xTimerStart(timer, 0);
}
#endif /* PL_CONFIG_USE_TIME_DATE */

@ -0,0 +1,21 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef MAIN_TIMER_H_
#define MAIN_TIMER_H_
#ifdef __cplusplus
extern "C" {
#endif
/*! \brief Module initialization */
void TMR_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* MAIN_TIMER_H_ */

@ -0,0 +1,303 @@
/* BSD Socket API Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "platform.h"
#if PL_CONFIG_USE_UDP_CLIENT
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "udp_client.h"
#include "McuShell.h"
#include "McuUtility.h"
#define CONFIG_EXAMPLE_IPV4
#ifdef CONFIG_EXAMPLE_IPV4
#define HOST_IP_ADDR "192.168.0.146"
#else
#define HOST_IP_ADDR "FE80::30AD:E57B:C212:68AD" /*CONFIG_EXAMPLE_IPV6_ADDR*/
#endif
#define PORT 3333
static uint16_t udp_client_destination_port = PORT;
static unsigned char udp_client_destination_host[24] = HOST_IP_ADDR;
static const char *TAG = "udp_client";
static TaskHandle_t taskHandle = NULL; /* udp client task handle */
#if 0
static const char *payload = "Message from ESP32 ";
static void udp_client_task(void *pvParameters) {
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
vTaskSuspend(NULL); /* UDP_Client_Start() will wake me up */
for(;;) {
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr(HOST_IP_ADDR);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else /* IPV6 */
struct sockaddr_in6 dest_addr;
inet6_aton(HOST_IP_ADDR, &dest_addr.sin6_addr);
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(PORT);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Socket created, sending to %s:%d", HOST_IP_ADDR, PORT);
while (1) {
int err = sendto(sock, payload, strlen(payload), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
break;
}
ESP_LOGI(TAG, "Message sent");
struct timeval to;
to.tv_sec = 1;
to.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to)) < 0) {
ESP_LOGE(TAG, "setting socket timeout failed");
}
ESP_LOGI(TAG, "Waiting for response");
struct sockaddr_in source_addr; /* Large enough for both IPv4 or IPv6 */
socklen_t socklen = sizeof(source_addr);
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer) - 1, 0, (struct sockaddr *)&source_addr, &socklen);
/* Error occurred during receiving */
if (len < 0) {
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
break;
} else { /* Data received */
rx_buffer[len] = 0; /* Null-terminate whatever we received and treat like a string */
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rx_buffer);
}
vTaskDelay(pdMS_TO_TICKS(2000));
}
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
} /* for */
vTaskDelete(NULL);
}
#endif
static uint8_t udp_client_send(const unsigned char *host, uint16_t port, const unsigned char *msg, unsigned char *rxBuffer, size_t rxBufferSize) {
char addr_str[128];
int addr_family;
int ip_protocol;
uint8_t res = ERR_OK;
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = inet_addr((const char*)host);
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(port);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else /* IPV6 */
struct sockaddr_in6 dest_addr;
inet6_aton(host, &dest_addr.sin6_addr);
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(port);
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
for(;;) { /* breaks in in case of error */
if (sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
res = ERR_FAILED;
break;
}
ESP_LOGI(TAG, "Socket created, sending to %s:%d", host, port);
int err = sendto(sock, msg, strlen((char*)msg), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
res = ERR_FAILED;
break;
}
ESP_LOGI(TAG, "Message sent");
struct timeval to;
to.tv_sec = 1;
to.tv_usec = 0;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &to, sizeof(to)) < 0) {
ESP_LOGE(TAG, "setting socket timeout failed");
res = ERR_FAILED;
break;
}
ESP_LOGI(TAG, "Waiting for response");
struct sockaddr_in source_addr; /* Large enough for both IPv4 or IPv6 */
socklen_t socklen = sizeof(source_addr);
int len = recvfrom(sock, rxBuffer, rxBufferSize-1, 0, (struct sockaddr *)&source_addr, &socklen);
/* Error occurred during receiving */
if (len < 0) {
ESP_LOGE(TAG, "recvfrom failed: errno %d", errno);
res = ERR_FAILED;
break;
} else { /* Data received */
rxBuffer[len] = '\0'; /* Null-terminate whatever we received and treat like a string */
ESP_LOGI(TAG, "Received %d bytes from %s:", len, addr_str);
ESP_LOGI(TAG, "%s", rxBuffer);
}
break;
} /* for */
if (sock != -1) {
ESP_LOGE(TAG, "Shutting down socket");
shutdown(sock, 0);
close(sock);
}
return res;
}
void UDP_Client_Start(void) {
if (taskHandle!=NULL) {
vTaskResume(taskHandle);
}
}
void UDP_Client_Stop(void) {
if (taskHandle!=NULL) {
vTaskSuspend(taskHandle);
}
}
void UDP_Client_Init(void) {
#if 0
BaseType_t res;
res = xTaskCreate(udp_client_task, "udp_client", 16*1024/sizeof(StackType_t), NULL, 5, &taskHandle);
if (res==pdPASS) {
ESP_LOGI(TAG, "created UDP client task");
} else {
ESP_LOGE(TAG, "failed creating UDP client task!");
}
#endif
}
#if PL_CONFIG_USE_SHELL
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
unsigned char buf[64];
McuShell_SendStatusStr((unsigned char*)"udpc", (unsigned char*)"UDP client status\r\n", io->stdOut);
McuUtility_strcpy(buf, sizeof(buf), udp_client_destination_host);
McuUtility_chcat(buf, sizeof(buf), ':');
McuUtility_strcatNum16u(buf, sizeof(buf), udp_client_destination_port);
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" host", buf, io->stdOut);
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"udpc", (unsigned char*)"Group of UDP client commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows motor help or status\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" host <string>", (unsigned char*)"Set default host destination IP address (double quoted)\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" port <number>", (unsigned char*)"Set default host destination port number\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" send default <msg>", (unsigned char*)"Send message (double quoted) to default host and port\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" send <ip> <port> <msg>", (unsigned char*)"Send message (double quoted) to ip (double quoted) and port\r\n", io->stdOut);
return ERR_OK;
}
uint8_t UDP_Client_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
const unsigned char *p;
unsigned char msgBuf[64];
unsigned char rxBuf[64];
unsigned char ip[24];
uint16_t port;
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"udpc help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"udpc status")==0) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc host ", sizeof("udpc host ")-1)==0) {
*handled = TRUE;
p = cmd + sizeof("udpc host ")-1;
return McuUtility_ScanDoubleQuotedString(&p, udp_client_destination_host, sizeof(udp_client_destination_host));
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc port ", sizeof("udpc port ")-1)==0) {
*handled = TRUE;
p = cmd + sizeof("udpc port ")-1;
return McuUtility_ScanDecimal16uNumber(&p, &udp_client_destination_port);
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc send default ", sizeof("udpc send default ")-1)==0) {
*handled = TRUE;
p = cmd + sizeof("udpc send default ")-1;
if (McuUtility_ScanDoubleQuotedString(&p, msgBuf, sizeof(msgBuf))!=ERR_OK) {
return ERR_FAILED;
}
if (udp_client_send(udp_client_destination_host, udp_client_destination_port, msgBuf, rxBuf, sizeof(rxBuf))!=ERR_OK) {
return ERR_FAILED;
}
McuShell_SendStr(rxBuf, io->stdOut);
return ERR_OK;
} else if (McuUtility_strncmp((char*)cmd, (char*)"udpc send ", sizeof("udpc send ")-1)==0) {
*handled = TRUE;
p = cmd + sizeof("udpc send ")-1;
if (McuUtility_ScanDoubleQuotedString(&p, ip, sizeof(ip))!=ERR_OK) {
return ERR_FAILED;
}
if (McuUtility_ScanDecimal16uNumber(&p, &port)!=ERR_OK) {
return ERR_FAILED;
}
if (McuUtility_ScanDoubleQuotedString(&p, msgBuf, sizeof(msgBuf))!=ERR_OK) {
return ERR_FAILED;
}
if (udp_client_send(ip, port, msgBuf, rxBuf, sizeof(rxBuf))!=ERR_OK) {
return ERR_FAILED;
}
McuShell_SendStr(rxBuf, io->stdOut);
return ERR_OK;
}
return ERR_OK;
}
#endif /* PL_CONFIG_USE_SHELL */
#endif /* PL_CONFIG_USE_UDP_CLIENT */

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019, 2020, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef UDP_CLIENT_H_
#define UDP_CLIENT_H_
#include "platform.h"
#ifdef __cplusplus
extern "C" {
#endif
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Command line and shell handler
* \param cmd The command to be parsed
* \param handled If command has been recognized and handled
* \param io I/O handler to be used
* \return error code, otherwise ERR_OK
*/
uint8_t UDP_Client_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
#endif
void UDP_Client_Start(void);
void UDP_Client_Stop(void);
/*! \brief Module initialization */
void UDP_Client_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* UDP_CLIENT_H_ */

@ -0,0 +1,151 @@
/* BSD Socket API Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "platform.h"
#if PL_CONFIG_USE_UDP_SERVER
#include "udp_server.h"
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include <lwip/netdb.h>
#include "Shell.h"
#include "McuUtility.h"
#include "McuLog.h"
#define CONFIG_EXAMPLE_IPV4
static TaskHandle_t taskHandle = NULL; /* udp server task handle */
static int SendToSocket(int sock, const char *msg, const struct sockaddr *to, socklen_t tolen) {
return sendto(sock, msg, McuUtility_strlen((char*)msg), 0, to, tolen);
}
static void udp_server_task(void *pvParameters) {
char rx_buffer[128];
char addr_str[128];
int addr_family;
int ip_protocol;
vTaskSuspend(NULL); /* UDP_Server_Start() will wake me up */
for(;;) {
#ifdef CONFIG_EXAMPLE_IPV4
struct sockaddr_in dest_addr;
dest_addr.sin_addr.s_addr = htonl(INADDR_ANY); /** 0.0.0.0 */
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(UDP_SERVER_PORT);
addr_family = AF_INET;
ip_protocol = IPPROTO_IP;
inet_ntoa_r(dest_addr.sin_addr, addr_str, sizeof(addr_str) - 1);
#else // IPV6
struct sockaddr_in6 dest_addr;
bzero(&dest_addr.sin6_addr.un, sizeof(dest_addr.sin6_addr.un));
dest_addr.sin6_family = AF_INET6;
dest_addr.sin6_port = htons(UDP_SERVER_PORT);PO
addr_family = AF_INET6;
ip_protocol = IPPROTO_IPV6;
inet6_ntoa_r(dest_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
#endif
int sock = socket(addr_family, SOCK_DGRAM, ip_protocol);
if (sock < 0) {
McuLog_error("Unable to create socket: errno %d", errno);
break;
}
McuLog_info("Socket created");
int err = bind(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr));
if (err < 0) {
McuLog_error("Socket unable to bind: errno %d", errno);
}
McuLog_info("Socket bound, port %d", UDP_SERVER_PORT);
while (1) {
McuLog_info("Waiting for data on port %d", UDP_SERVER_PORT);
struct sockaddr_in6 source_addr; /* Large enough for both IPv4 or IPv6 */
socklen_t socklen = sizeof(source_addr);
/* receive data (blocking): */
int len = recvfrom(sock, rx_buffer, sizeof(rx_buffer)-1, 0, (struct sockaddr *)&source_addr, &socklen);
/* Error occurred during receiving */
if (len < 0) {
McuLog_error("recvfrom failed: errno %d", errno);
break;
} else { /* Data received */
/* Get the sender's ip address as string */
if (source_addr.sin6_family == PF_INET) {
inet_ntoa_r(((struct sockaddr_in *)&source_addr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
} else if (source_addr.sin6_family == PF_INET6) {
inet6_ntoa_r(source_addr.sin6_addr, addr_str, sizeof(addr_str) - 1);
}
rx_buffer[len] = '\0'; /* Null-terminate whatever we received and treat like a string... */
McuLog_info("Received %d bytes from %s:\n%s", len, addr_str, rx_buffer);
/* \TODO Need to handle messages and send them to the robot */
/* send back response */
unsigned char test_response[128];
int err;
McuLog_info("Sending back response");
McuUtility_strcpy(test_response, sizeof(test_response), (unsigned char*)"OK"); /* default response */
if (McuUtility_strncmp(rx_buffer, "test", sizeof("test")-1)==0) { /* hard-coded command */
McuUtility_strcpy(test_response, sizeof(test_response), (unsigned char*)"test_response");
}
err = SendToSocket(sock, (const char*)test_response, (struct sockaddr *)&source_addr, sizeof(source_addr));
if (err < 0) {
McuLog_error("Error occurred during sending: errno %d", errno);
}
} /* if */
} /* while */
if (sock != -1) {
McuLog_error("Shutting down socket and restarting...");
shutdown(sock, 0);
close(sock);
}
} /* for */
vTaskDelete(NULL);
}
void UDP_Server_Start(void) {
if (taskHandle!=NULL) {
vTaskResume(taskHandle);
}
}
void UDP_Server_Stop(void) {
if (taskHandle!=NULL) {
vTaskSuspend(taskHandle);
}
}
void UDP_Server_Init(void) {
BaseType_t res;
res = xTaskCreate(udp_server_task, "udp_server", (16*1024)/sizeof(StackType_t), NULL, tskIDLE_PRIORITY+5, &taskHandle);
if (res==pdPASS) {
McuLog_info("created UDP server task");
} else {
McuLog_error("failed creating UDP server task!");
}
}
#endif /* PL_CONFIG_USE_UDP_SERVER */

@ -0,0 +1,29 @@
/*
* Copyright (c) 2019, 2020, 202§, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef UDP_SERVER_H_
#define UDP_SERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
#define UDP_SERVER_PORT (1234) /*!< default UDP server port */
/*! \brief start the UDP server */
void UDP_Server_Start(void);
/*! \brief stop the UDP server */
void UDP_Server_Stop(void);
/*! \brief Module initialization */
void UDP_Server_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* UDP_SERVER_H_ */

@ -0,0 +1,46 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include "platform.h"
#if PL_CONFIG_USE_UDP_SERVER
#include "udp_server_shell.h"
#include "udp_server.h"
#include "wifi.h"
#include "McuShell.h"
#include "McuUtility.h"
#include "Shell.h"
#if PL_CONFIG_USE_SHELL
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
unsigned char buf[32];
McuShell_SendStatusStr((unsigned char*)"udps", (unsigned char*)"ESP32 udp server status\r\n", io->stdOut);
McuUtility_Num32sToStr(buf, sizeof(buf), UDP_SERVER_PORT);
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" port", buf, io->stdOut);
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"udps", (unsigned char*)"Group of udp server commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows udp server help or status\r\n", io->stdOut);
return ERR_OK;
}
uint8_t UDP_Server_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"udps help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"udps status")==0) {
*handled = TRUE;
return PrintStatus(io);
}
return ERR_OK;
}
#endif /* PL_CONFIG_USE_SHELL */
#endif /* PL_CONFIG_USE_UDP_SERVER */

@ -0,0 +1,34 @@
/*
* Copyright (c) 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef UDP_SERVER_SHELL_H_
#define UDP_SERVER_SHELL_H_
#include "platform.h"
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Command line and shell handler
* \param cmd The command to be parsed
* \param handled If command has been recognized and handled
* \param io I/O handler to be used
* \return error code, otherwise ERR_OK
*/
uint8_t UDP_Server_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
#endif /* PL_CONFIG_USE_SHELL */
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* UDP_SERVER_SHELL_H_ */

@ -0,0 +1,383 @@
/*
* Copyright (c) 2019, 2020, 2021, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
* ******************************************************************
* Dear programmer:
* When I wrote this code, only god and I knew how it worked.
* Now, only god knows it!
*
* Therefore, if you are trying to optimize or change this code
* and it fails (most surely), please increase the counter below
* as a warning for the next person:
*
* total_hours_wasted_here = 257
* *******************************************************************
*/
#include "platform.h"
#if PL_CONFIG_USE_WIFI
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#if PL_CONFIG_USE_SNTP_TIME
#include "sntp_time.h"
#endif
#if PL_CONFIG_USE_UDP_CLIENT
#include "udp_client.h"
#endif
#if PL_CONFIG_USE_UDP_SERVER
#include "udp_server.h"
#endif
#if PL_CONFIG_USE_BLINKY
#include "led.h"
#define LED_ON_TIME_MS_CONNECTED 1000
#define LED_ON_TIME_MS_DISCONNECTED 5
#endif
#include "McuUtility.h"
#include "McuXFormat.h"
#include "esp32_mac.h"
#include "McuLog.h"
#define EAP_PEAP 1 /* WPA2 Enterprise with password and no certificate */
#define EAP_TTLS 2 /* TLS method */
#include "pwd.h" /* local file with login information */
#ifndef CONFIG_ESP_MAXIMUM_RETRY
#define CONFIG_ESP_MAXIMUM_RETRY (2) /* number of retries to connect to the network */
#endif
static int s_retry_num = 0;
/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;
/* The event group allows multiple bits for each event, but we only care about two events:
* - we are connected to the AP with an IP
* - we failed to connect after the maximum amount of retries */
#define WIFI_EVENT_HANDLER_CONNECTED_BIT (1<<0)
#define WIFI_EVENT_HANDLER_FAIL_BIT (1<<1)
#define WIFI_CONNECTED_BIT (1<<2)
static esp_netif_t *APP_WiFi_NetIf;
static bool APP_WiFi_isOn = true;
void APP_WiFi_PrintIP(void) {
tcpip_adapter_ip_info_t ip;
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t));
if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == ESP_OK) {
McuLog_info("IP:"IPSTR " MASK:"IPSTR " GW:"IPSTR, IP2STR(&ip.ip), IP2STR(&ip.netmask), IP2STR(&ip.gw));
} else {
McuLog_error("failed tcpip_adapter_get_ip_info()");
}
}
static void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data) {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
McuLog_info("WIFI_EVENT_STA_START: start event");
esp_wifi_connect();
} else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
McuLog_info("WIFI_EVENT_STA_DISCONNECTED: disconnected, retry %d", s_retry_num);
if (s_retry_num < CONFIG_ESP_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
McuLog_info("retry to connect to the AP");
} else {
xEventGroupSetBits(s_wifi_event_group, WIFI_EVENT_HANDLER_FAIL_BIT);
}
McuLog_info("connect to the AP fail");
} else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
McuLog_info("IP_EVENT_STA_GOT_IP: got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
xEventGroupSetBits(s_wifi_event_group, WIFI_EVENT_HANDLER_CONNECTED_BIT);
}
}
typedef enum {
WIFI_PASSWORD_METHOD_PSK,
WIFI_PASSWORD_METHOD_WPA2,
} WiFi_PasswordMethod_e;
static void GetNetworkSSID(unsigned char *ssidBuf, size_t ssidBufSize) {
wifi_ap_record_t ap;
esp_err_t err;
err = esp_wifi_sta_get_ap_info(&ap);
if (err==ESP_OK) {
McuUtility_strcpy(ssidBuf, ssidBufSize, ap.ssid);
} else if (err==ESP_ERR_WIFI_CONN) {
McuUtility_strcpy(ssidBuf, ssidBufSize, (unsigned char*)"not init");
} else if (err==ESP_ERR_WIFI_NOT_CONNECT) {
McuUtility_strcpy(ssidBuf, ssidBufSize, (unsigned char*)"not connected");
} else {
McuUtility_strcpy(ssidBuf, ssidBufSize, (unsigned char*)"error?");
}
}
static void SetPasswordMode(WiFi_PasswordMethod_e mode) {
wifi_config_t wifi_config;
McuLog_info("SetPasswordMode(): %d", mode);
ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
memset(&wifi_config, 0, sizeof(wifi_config_t)); /* initialize all fields */
if (mode==WIFI_PASSWORD_METHOD_WPA2) {
strncpy((char*)wifi_config.sta.ssid, CONFIG_WIFI_EAP_SSID, sizeof(wifi_config.sta.ssid));
} else if (mode==WIFI_PASSWORD_METHOD_PSK) {
strncpy((char*)wifi_config.sta.ssid, CONFIG_WIFI_PSK_SSID, sizeof(wifi_config.sta.ssid));
strncpy((char*)wifi_config.sta.password, CONFIG_WIFI_PSK_PASSWORD, sizeof(wifi_config.sta.password));
} else {
McuLog_error("Wrong connection mode: %d", mode);
}
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
if (mode==WIFI_PASSWORD_METHOD_WPA2) {
const ESP32_Device_t *device;
device = ESP32_GetDeviceConfig();
McuLog_info("EAP_ID: %s", device->eee_id);
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)device->eee_id, strlen(device->eee_id)) );
if (CONFIG_WIFI_EAP_METHOD == EAP_PEAP || CONFIG_WIFI_EAP_METHOD == EAP_TTLS) {
McuLog_info("EAP_USERNAME: %s", device->eee_id);
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_username((uint8_t *)device->eee_id, strlen(device->eee_id)) );
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_set_password((uint8_t *)device->eee_pwd, strlen(device->eee_pwd)) );
}
ESP_ERROR_CHECK( esp_wifi_sta_wpa2_ent_enable() );
}
}
static void initialise_wifi(void) {
WiFi_PasswordMethod_e mode = CONFIG_WIFI_START_WITH; /* starting mode */
const ESP32_Device_t *config;
s_wifi_event_group = xEventGroupCreate();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
APP_WiFi_NetIf = esp_netif_create_default_wifi_sta();
config = ESP32_GetDeviceConfig();
McuLog_info("Setting hostname: %s", config->hostName);
ESP_ERROR_CHECK(esp_netif_set_hostname(APP_WiFi_NetIf, config->hostName));
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
SetPasswordMode(mode);
McuLog_info("Starting WiFi");
ESP_ERROR_CHECK(esp_wifi_start());
/* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
* number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
EventBits_t bits;
do {
if (APP_WiFi_isOn) {
bits = xEventGroupWaitBits(s_wifi_event_group,
WIFI_EVENT_HANDLER_CONNECTED_BIT | WIFI_EVENT_HANDLER_FAIL_BIT, /* bits to wait for */
pdTRUE, /* bits to clear on exit: we do not clear the WIFI_CONNECTED_BIT because used by WiFi task */
pdFALSE, /* wait for all bits */
pdMS_TO_TICKS(20000)); /* wait time */
if (bits&WIFI_EVENT_HANDLER_CONNECTED_BIT) {
break; /* leave loop */
}
if (bits&WIFI_EVENT_HANDLER_FAIL_BIT) {
McuLog_info("FAILED connecting, restarting WiFi with different mode");
ESP_ERROR_CHECK(esp_wifi_stop());
/* toggle mode */
if (mode==WIFI_PASSWORD_METHOD_PSK) {
mode = WIFI_PASSWORD_METHOD_WPA2;
} else if (mode==WIFI_PASSWORD_METHOD_WPA2) {
ESP_ERROR_CHECK(esp_wifi_sta_wpa2_ent_disable());
mode = WIFI_PASSWORD_METHOD_PSK;
}
SetPasswordMode(mode);
ESP_ERROR_CHECK(esp_wifi_start());
}
} else {
vTaskDelay(pdMS_TO_TICKS(500));
}
} while(true); /* breaks */
/* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually happened. */
if (bits & WIFI_EVENT_HANDLER_CONNECTED_BIT) {
if (mode == WIFI_PASSWORD_METHOD_WPA2) {
McuLog_info("connected to AP SSID: %s ", CONFIG_WIFI_EAP_SSID);
} else if (mode == WIFI_PASSWORD_METHOD_PSK) {
McuLog_info("connected to AP SSID: %s", CONFIG_WIFI_PSK_SSID);
}
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
} else if (bits & WIFI_EVENT_HANDLER_FAIL_BIT) {
if (mode == WIFI_PASSWORD_METHOD_WPA2) {
McuLog_info("Failed to connect to SSID: %s", CONFIG_WIFI_EAP_SSID);
} else if (mode == WIFI_PASSWORD_METHOD_PSK) {
McuLog_info("Failed to connect to SSID: %s", CONFIG_WIFI_PSK_SSID);
}
} else {
McuLog_error("UNEXPECTED EVENT");
}
}
typedef enum {
WIFI_STATE_INIT,
WIFI_STATE_CONNECTED,
WIFI_STATE_DISCONNECTED,
} WiFi_State_e;
static void WiFiTask(void *pv) {
bool isConnected = false;
WiFi_State_e state = WIFI_STATE_INIT;
McuLog_info("Initialize WiFi");
initialise_wifi();
#if PL_CONFIG_USE_BLINKY
LED_SetOnTime(LED_ON_TIME_MS_DISCONNECTED);
#endif
for(;;) {
vTaskDelay(pdMS_TO_TICKS(5000));
isConnected = xEventGroupGetBits(s_wifi_event_group)&WIFI_CONNECTED_BIT;
#if PL_CONFIG_USE_BLINKY
LED_SetOnTime(isConnected?LED_ON_TIME_MS_CONNECTED:LED_ON_TIME_MS_DISCONNECTED);
#endif
if (state == WIFI_STATE_INIT && isConnected) { /* first connection */
state = WIFI_STATE_CONNECTED;
//ESP_LOGI(TAG, "WiFi is connected.");
if (isConnected) {
APP_WiFi_PrintIP();
}
#if PL_CONFIG_USE_UDP_SERVER
McuLog_info("starting UDP server.");
UDP_Server_Start();
#endif
#if PL_CONFIG_USE_UDP_CLIENT
McuLog_info("starting UDP client.");
UDP_Client_Start();
#endif
#if PL_CONFIG_USE_SNTP_TIME
if (SNTP_ObtainTime()!=0) {
McuLog_error("failed getting SNTP time.");
}
#endif
#if 0
} else if (isConnected) {
ESP_LOGI(TAG, "still connected.");
APP_WiFi_PrintIP();
const char *hostname;
ESP_ERROR_CHECK(esp_netif_get_hostname(APP_WiFi_NetIf, &hostname));
ESP_LOGI(TAG,"hostname: %s", hostname);
} else {
ESP_LOGI(TAG, "not connected yet.");
#endif
}
} /* for */
}
bool WiFi_isConnected(void) {
bool isConnected;
isConnected = xEventGroupGetBits(s_wifi_event_group)&WIFI_CONNECTED_BIT;
return isConnected;
}
#if PL_CONFIG_USE_SHELL
static uint8_t PrintStatus(const McuShell_StdIOType *io) {
uint8_t buf[32];
uint8_t mac[6];
McuShell_SendStatusStr((unsigned char*)"wifi", (unsigned char*)"ESP32 WiFi status\r\n", io->stdOut);
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)IDF_VER);
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" IDF", buf, io->stdOut);
McuShell_SendStatusStr((unsigned char*)" connected", WiFi_isConnected()?(unsigned char*)"yes\r\n":(unsigned char*)"no\r\n", io->stdOut);
ESP32_MacRead(mac);
ESP32_MacToString(mac, buf, sizeof(buf));
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" MAC", buf, io->stdOut);
McuShell_SendStatusStr((unsigned char*)" WiFi", APP_WiFi_isOn?(unsigned char*)"on\r\n":(unsigned char*)"off\r\n", io->stdOut);
if (WiFi_isConnected()) {
unsigned char buf[64];
tcpip_adapter_ip_info_t ip;
const char *hostname;
if (esp_netif_get_hostname(APP_WiFi_NetIf, &hostname)==ERR_OK) {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)hostname);
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"failed\r\n");
}
McuShell_SendStatusStr((unsigned char*)" hostname", buf, io->stdOut);
memset(&ip, 0, sizeof(tcpip_adapter_ip_info_t));
if (tcpip_adapter_get_ip_info(ESP_IF_WIFI_STA, &ip) == ESP_OK) {
McuXFormat_xsnprintf((char*)buf, sizeof(buf), "IP:"IPSTR " MASK:"IPSTR " GW:"IPSTR "\r\n", IP2STR(&ip.ip), IP2STR(&ip.netmask), IP2STR(&ip.gw));
} else {
McuUtility_strcpy(buf, sizeof(buf), (unsigned char*)"failed\r\n");
}
McuShell_SendStatusStr((unsigned char*)" IP", buf, io->stdOut);
GetNetworkSSID(buf, sizeof(buf));
McuUtility_strcat(buf, sizeof(buf), (unsigned char*)"\r\n");
McuShell_SendStatusStr((unsigned char*)" SSID", buf, io->stdOut);
}
return ERR_OK;
}
static uint8_t PrintHelp(const McuShell_StdIOType *io) {
McuShell_SendHelpStr((unsigned char*)"wifi", (unsigned char*)"Group of ESP32 WiFi commands\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" help|status", (unsigned char*)"Shows WiFi help or status\r\n", io->stdOut);
McuShell_SendHelpStr((unsigned char*)" on|off", (unsigned char*)"Turn WiFi on or off\r\n", io->stdOut);
return ERR_OK;
}
uint8_t WiFi_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io) {
if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_HELP)==0 || McuUtility_strcmp((char*)cmd, (char*)"wifi help")==0) {
*handled = TRUE;
return PrintHelp(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)McuShell_CMD_STATUS)==0 || McuUtility_strcmp((char*)cmd, (char*)"wifi status")==0) {
*handled = TRUE;
return PrintStatus(io);
} else if (McuUtility_strcmp((char*)cmd, (char*)"wifi on")==0) {
*handled = TRUE;
APP_WiFi_isOn = true;
return ERR_OK;
} else if (McuUtility_strcmp((char*)cmd, (char*)"wifi off")==0) {
*handled = TRUE;
APP_WiFi_isOn = false;
return ERR_OK;
}
return ERR_OK;
}
#endif /* PL_CONFIG_USE_SHELL */
void WiFi_Init(void) {
BaseType_t res;
res = xTaskCreate(WiFiTask, "WiFi", 16*1024/sizeof(StackType_t), NULL, tskIDLE_PRIORITY, NULL);
if (res==pdPASS) {
McuLog_info("created WiFi task");
} else {
McuLog_error("failed creating WiFi task!");
}
/* set mac address, otherwise will get "system_api: Base MAC address is not set, read default base MAC address from BLK0 of EFUSE" whenever asking for it */
uint8_t mac[6];
res = esp_read_mac(&mac[0], ESP_MAC_WIFI_STA);
if (res==ESP_OK) {
ESP_ERROR_CHECK(esp_base_mac_addr_set(&mac[0]));
}
}
#endif /* PL_CONFIG_USE_WIFI */

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020, Erich Styger
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef APPWIFI_H_
#define APPWIFI_H_
#include "platform.h"
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
#if PL_CONFIG_USE_SHELL
#include "McuShell.h"
/*!
* \brief Command line and shell handler
* \param cmd The command to be parsed
* \param handled If command has been recognized and handled
* \param io I/O handler to be used
* \return error code, otherwise ERR_OK
*/
uint8_t WiFi_ParseCommand(const unsigned char* cmd, bool *handled, const McuShell_StdIOType *io);
#endif
/*!
* \brief Decides if connected to a network or not
* \return true if connected, false if not
*/
bool WiFi_isConnected(void);
/*!
* \brief Module Initialization
*/
void WiFi_Init(void);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* APPWIFI_H_ */

@ -0,0 +1,23 @@
idf_component_register(
SRCS
"src/McuLib.c"
"src/McuWait.c"
"src/McuArmTools.c"
"src/McuRTOS.c"
"src/McuUtility.c"
"src/McuShell.c"
"src/McuXFormat.c"
"src/McuCriticalSection.c"
"src/McuLog.c"
"src/McuGPIO.c"
"src/McuLED.c"
"src/McuTimeDate.c"
"src/McuButton.c"
"src/McuTrigger.c"
"src/McuTimeout.c"
"src/McuRB.c"
"src/McuUart485.c"
INCLUDE_DIRS
"./config"
"./src"
)
Loading…
Cancel
Save