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

383 lines
14 KiB

/*
* 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 */