initial version made by removing all csp stuff from cspemu

This commit is contained in:
2021-12-10 22:23:34 +01:00
parent 4829c5414b
commit c8ec93722e
120 changed files with 20830 additions and 4 deletions
+9
View File
@@ -0,0 +1,9 @@
set(COMPONENT_ADD_INCLUDEDIRS
"include")
set(COMPONENT_SRCDIRS
"src")
set(COMPONENT_REQUIRES ping tcpip_adapter)
register_component()
+27
View File
@@ -0,0 +1,27 @@
menu "DHCP watchdog"
config DHCPWD_PERIOD_GW_PING_S
int "Connectivity test interval (s)"
default 60
help
Time between two connectivity tests (gateway ping)
config DHCPWD_GETIP_TIMEOUT_S
int "Timeout to get IP (s)"
default 10
help
Timeout after establishing connection to get an IP address from the DHCP server.
config DHCPWD_TASK_STACK_SIZE
int "Task stack size (bytes)"
default 4096
help
DHCP watchdog task stack size
config DHCPWD_TASK_PRIORITY
int "Task priority"
default 3
help
DHCP watchdog task priority
endmenu
+5
View File
@@ -0,0 +1,5 @@
DHCP ping watchdog.
ESP32 sometimes loses wireless connectivity (expiring lease that fails to renew,
AP rebooting and forgetting us, etc). This module periodically pings the gateway
and triggers reconnect if the ping fails.
+3
View File
@@ -0,0 +1,3 @@
COMPONENT_SRCDIRS := src
COMPONENT_ADD_INCLUDEDIRS := include
+82
View File
@@ -0,0 +1,82 @@
/**
* DHCP watchdog
*
* This is a workaround for a rare case where we don't get
* any IP after connecting with STA. If it takes too long,
* try power-cycling the DHCP client. If that fails too,
* try cycling the WiFi stack too.
*
* This does not try to reboot, as there are valid cases when this
* can happen - e.g. no DHCP on the network + no static IP configured yet.
*
* The ping component is used as a dependency.
*/
#ifndef _DHCP_WD_H_
#define _DHCP_WD_H_
#include "esp_netif.h"
#include "esp_event.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
typedef struct dhcp_wd_instance * dhcp_wd_handle_t;
/**
* Start the watchdog. Handle must remain valid until the task is deleted.
*
* @param[in] iface
* @param[out] handle - pointer to a handle variable (will be written to it)
* @return success
*/
esp_err_t dhcp_watchdog_start(esp_netif_t * netif, bool is_wifi, dhcp_wd_handle_t *pHandle);
/**
* Check if a watchdog is running
*
* @param[in] handle
* @return is running
*/
bool dhcp_watchdog_is_running(dhcp_wd_handle_t handle);
/**
* Stop the watchdog and free resources.
* The handle becomes invalid and is set to NULL.
*
* @param[in] handle
* @return success
*/
esp_err_t dhcp_watchdog_stop(dhcp_wd_handle_t *pHandle);
enum dhcp_wd_event {
DHCP_WD_NOTIFY_CONNECTED,
DHCP_WD_NOTIFY_DISCONNECTED,
DHCP_WD_NOTIFY_GOT_IP,
};
/**
* @brief Notify the watchdog task about a wifi state change
*
* Call this from the WiFi event handler.
*
* @param[in] handle
* @param[in] event - detected event
*/
esp_err_t dhcp_watchdog_notify(dhcp_wd_handle_t handle, enum dhcp_wd_event);
enum dhcp_wd_test_result {
DHCP_WD_RESULT_OK = 0,
DHCP_WD_RESULT_PING_LOST,
DHCP_WD_RESULT_NO_GATEWAY,
};
/**
* Manually trigger a connection test by pinging the gateway.
* This is independent on any watchdog tasks and can be run without starting the watchdog.
*
* @param[in] iface - network interface, typically TCPIP_ADAPTER_IF_STA
* @return test result
*/
enum dhcp_wd_test_result dhcp_wd_test_connection(esp_netif_t *iface);
#endif //_DHCP_WD_H_
+277
View File
@@ -0,0 +1,277 @@
#include <string.h>
#include "esp_log.h"
#include "dhcp_wd.h"
#include "esp_wifi.h"
//#include "esp_eth.h"
#include "ping.h"
#define xstr(s) str(s)
#define str(s) #s
#define PERIOD_GW_PING_S CONFIG_DHCPWD_PERIOD_GW_PING_S
#define GETIP_TIMEOUT_S CONFIG_DHCPWD_GETIP_TIMEOUT_S
#define TASK_STACK_SIZE CONFIG_DHCPWD_TASK_STACK_SIZE
#define TASK_PRIO CONFIG_DHCPWD_TASK_PRIORITY
static const char *TAG = "dhcp_wd";
static void dhcp_watchdog_task(void *parm);
struct dhcp_wd_instance {
TaskHandle_t task;
esp_netif_t * iface;
bool is_wifi;
bool running;
};
#define STATES_ENUM \
X(DISCONECTED) \
X(CONECTED_WAIT_IP) \
X(CONECTED_WAIT_IP2) \
X(CONECTED)
enum dhcp_wd_state {
#undef X
#define X(s) STATE_##s,
STATES_ENUM
};
const char *state_names[] = {
#undef X
#define X(s) xstr(s),
STATES_ENUM
};
enum dhcp_wd_notify {
NOTIFY_CONNECTED = BIT0,
NOTIFY_DISCONNECTED = BIT1,
NOTIFY_GOT_IP = BIT2,
NOTIFY_SHUTDOWN = BIT3, // kills the task
};
/** Send a notification to the watchdog task */
esp_err_t dhcp_watchdog_notify(dhcp_wd_handle_t handle, const enum dhcp_wd_event event)
{
assert(handle != NULL);
assert(handle->task != NULL);
uint32_t flag = 0;
switch (event) {
case DHCP_WD_NOTIFY_CONNECTED:
flag = NOTIFY_CONNECTED;
break;
case DHCP_WD_NOTIFY_DISCONNECTED:
flag = NOTIFY_DISCONNECTED;
break;
case DHCP_WD_NOTIFY_GOT_IP:
flag = NOTIFY_GOT_IP;
break;
default:
break;
}
BaseType_t ret = pdPASS;
if (flag != 0) {
ret = xTaskNotify(handle->task, flag, eSetBits);
}
return (pdPASS == ret) ? ESP_OK : ESP_FAIL;
}
/**
* Start the watchdog
*/
esp_err_t dhcp_watchdog_start(esp_netif_t * netif, bool is_wifi, dhcp_wd_handle_t *pHandle)
{
assert(pHandle != NULL);
dhcp_wd_handle_t handle = calloc(1, sizeof(struct dhcp_wd_instance));
if (!handle) return ESP_ERR_NO_MEM;
*pHandle = handle;
handle->iface = netif;
handle->is_wifi = is_wifi;
BaseType_t ret = xTaskCreate(dhcp_watchdog_task, "dhcp-wd", TASK_STACK_SIZE, (void *)handle, TASK_PRIO, &handle->task);
handle->running = true;
return (pdPASS == ret) ? ESP_OK : ESP_FAIL;
}
/**
* Check if a watchdog is still running
*
* @param handle
* @return is running
*/
bool dhcp_watchdog_is_running(dhcp_wd_handle_t handle)
{
return handle->running;
}
/**
* Stop the watchdog and free resources
*/
esp_err_t dhcp_watchdog_stop(dhcp_wd_handle_t *pHandle)
{
assert(pHandle != NULL);
assert(*pHandle != NULL);
xTaskNotify((*pHandle)->task, NOTIFY_SHUTDOWN, eSetBits);
*pHandle = NULL;
return ESP_OK;
}
/**
* @param parm - tcpip_adapter_if_t iface (cast to void *) - typically TCPIP_ADAPTER_IF_STA
*/
static void dhcp_watchdog_task(void *parm)
{
enum dhcp_wd_state state = STATE_DISCONECTED;
dhcp_wd_handle_t handle = parm;
assert(handle != NULL);
assert(handle->iface != NULL);
ESP_LOGI(TAG, "Watchdog started");
while (1) {
uint32_t flags = 0;
uint32_t wait_s;
TickType_t waittime;
switch (state) {
case STATE_DISCONECTED:
wait_s = waittime = portMAX_DELAY;
break;
case STATE_CONECTED_WAIT_IP:
case STATE_CONECTED_WAIT_IP2:
wait_s = GETIP_TIMEOUT_S;
waittime = (GETIP_TIMEOUT_S * 1000) / portTICK_PERIOD_MS;
break;
case STATE_CONECTED:
wait_s = PERIOD_GW_PING_S;
waittime = (PERIOD_GW_PING_S * 1000) / portTICK_PERIOD_MS;
break;
default:
assert(0);
}
ESP_LOGD(TAG, "State %s, wait %d s", state_names[state], wait_s);
BaseType_t rv = xTaskNotifyWait(
/* no clear on entry */ pdFALSE,
/* clear all on exit */ ULONG_MAX,
&flags, waittime);
if (rv == pdPASS) {
// the order here is important in case we get multiple events at once
if (flags & NOTIFY_DISCONNECTED) {
state = STATE_DISCONECTED;
}
if (flags & NOTIFY_CONNECTED) {
state = STATE_CONECTED_WAIT_IP;
}
if (flags & NOTIFY_GOT_IP) {
state = STATE_CONECTED;
}
if (flags & NOTIFY_SHUTDOWN) {
// kill self
handle->running = false;
free(handle);
vTaskDelete(NULL);
return;
}
} else {
// a timeout occurred
switch (state) {
case STATE_DISCONECTED:
// this shouldn't happen, we have infinite delay waiting for disconnected
ESP_LOGW(TAG, "dhcp_wd double discon evt");
break;
case STATE_CONECTED_WAIT_IP:
ESP_LOGW(TAG, "Get IP timeout, restarting DHCP client");
// this is a bit suspicious
// try to restart the DHCPC client
ESP_ERROR_CHECK(esp_netif_dhcpc_stop(handle->iface));
ESP_ERROR_CHECK(esp_netif_dhcpc_start(handle->iface));
state = STATE_CONECTED_WAIT_IP2;
break;
case STATE_CONECTED_WAIT_IP2:
ESP_LOGW(TAG, "Get IP timeout 2, restarting network stack");
// well now this is weird. try flipping the whole WiFi/Eth stack
if (handle->is_wifi) {
ESP_ERROR_CHECK(esp_wifi_disconnect());
}
// this will trigger the disconnected event and loop back into Disconnected
// the disconnect event handler calls connect again
state = STATE_DISCONECTED;
break;
case STATE_CONECTED: {
// Ping gateway to check if we're still connected
enum dhcp_wd_test_result result = dhcp_wd_test_connection(handle->iface);
if (result == DHCP_WD_RESULT_PING_LOST) {
// looks like the gateway silently dropped us
// try kicking the DHCP client, if it helps
ESP_ERROR_CHECK(esp_netif_dhcpc_stop(handle->iface));
ESP_ERROR_CHECK(esp_netif_dhcpc_start(handle->iface));
state = STATE_CONECTED_WAIT_IP2;
// if not, it'll flip the whole wifi stack
} else {
ESP_LOGD(TAG, "Gateway ping OK");
}
break;
}
}
}
}
}
enum dhcp_wd_test_result dhcp_wd_test_connection(esp_netif_t *iface)
{
ESP_LOGD(TAG, "Ping Gateway to check if IP is valid");
ping_opts_t opts = PING_CONFIG_DEFAULT();
opts.count = 3;
opts.interval_ms = 0;
opts.timeout_ms = 1000;
esp_netif_ip_info_t ip_info = {};
ESP_ERROR_CHECK(esp_netif_get_ip_info(iface, &ip_info));
opts.ip_addr.addr = ip_info.gw.addr;
ping_result_t result = {};
if (ip_info.gw.addr != 0) {
esp_err_t ret = ping(&opts, &result);
if (ret != ESP_OK) {
ESP_LOGW(TAG, "Ping error");
return DHCP_WD_RESULT_PING_LOST;
}
ESP_LOGD(TAG, "Ping result: %d tx, %d rx", result.sent, result.received);
if (result.received == 0) {
ESP_LOGW(TAG, "Failed to ping GW");
return DHCP_WD_RESULT_PING_LOST;
} else {
return DHCP_WD_RESULT_OK;
}
} else {
ESP_LOGW(TAG, "No GW IP to ping");
return DHCP_WD_RESULT_NO_GATEWAY;
}
}