288 lines
9.0 KiB
C++
288 lines
9.0 KiB
C++
// C STD lib
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
|
|
// SDK
|
|
#include "esp_err.h"
|
|
#include "esp_eth.h"
|
|
#include "esp_eth_driver.h"
|
|
#include "esp_event.h"
|
|
#include "esp_log.h"
|
|
#include "esp_netif.h"
|
|
#include "esp_system.h"
|
|
#include "esp_wifi.h"
|
|
#include "ethernet_init.h"
|
|
|
|
// Project includes
|
|
#include "types.hpp"
|
|
|
|
// Forward declarations
|
|
internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info);
|
|
internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b);
|
|
internal void blink_last_ip_octet();
|
|
|
|
// === Ethernet ===
|
|
|
|
internal constexpr char kLogEthernet[] = "ETH";
|
|
internal constexpr char kNetifDescEth[] = "eth0";
|
|
|
|
internal esp_eth_handle_t *s_eth_handles = nullptr;
|
|
internal uint8_t s_eth_count = 0;
|
|
internal esp_eth_netif_glue_handle_t s_eth_glue = nullptr;
|
|
internal esp_netif_t *s_eth_netif = nullptr;
|
|
internal SemaphoreHandle_t s_semph_get_ip_addrs = nullptr;
|
|
#ifndef NDEBUG
|
|
internal bool s_ethernet_connected = false;
|
|
#endif
|
|
|
|
void eth_on_got_ip(void *arg, esp_event_base_t event_base, int32_t event_id,
|
|
void *event_data) {
|
|
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
|
|
bool isEthernetNetif =
|
|
strncmp(kNetifDescEth, esp_netif_get_desc(event->esp_netif),
|
|
strlen(kNetifDescEth) - 1) == 0;
|
|
if (!isEthernetNetif) {
|
|
return;
|
|
}
|
|
ESP_LOGI(kLogEthernet, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
|
|
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
|
|
|
|
xSemaphoreGive(s_semph_get_ip_addrs);
|
|
}
|
|
|
|
void teardown_ethernet() {
|
|
if (s_semph_get_ip_addrs != nullptr) {
|
|
vSemaphoreDelete(s_semph_get_ip_addrs);
|
|
s_semph_get_ip_addrs = nullptr;
|
|
}
|
|
|
|
if (s_eth_netif) {
|
|
|
|
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_ETH_GOT_IP,
|
|
ð_on_got_ip));
|
|
ESP_ERROR_CHECK(esp_eth_stop(s_eth_handles[0]));
|
|
ESP_ERROR_CHECK(esp_eth_del_netif_glue(s_eth_glue));
|
|
esp_netif_destroy(s_eth_netif);
|
|
ethernet_deinit_all(s_eth_handles);
|
|
}
|
|
|
|
s_eth_glue = nullptr;
|
|
s_eth_netif = nullptr;
|
|
s_eth_handles = nullptr;
|
|
s_eth_count = 0;
|
|
|
|
esp_netif_deinit();
|
|
}
|
|
|
|
internal esp_err_t connect_ethernet(bool blockUntilIPAcquired) {
|
|
#ifndef NDEBUG
|
|
assert(!s_ethernet_connected &&
|
|
"Ethernet connect called but already connected!");
|
|
s_ethernet_connected = true;
|
|
#endif
|
|
|
|
s_semph_get_ip_addrs = xSemaphoreCreateBinary();
|
|
if (s_semph_get_ip_addrs == NULL) {
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
// Connection is split in two steps. First we open the connection and ask for
|
|
// an ip. Second a semaphor will block until the ip is acquired. If we dont
|
|
// block then the user have to verify the semaphore before continuing.
|
|
ESP_ERROR_CHECK(esp_netif_init());
|
|
ESP_ERROR_CHECK(ethernet_init_all(&s_eth_handles, &s_eth_count));
|
|
|
|
esp_netif_inherent_config_t esp_netif_config =
|
|
ESP_NETIF_INHERENT_DEFAULT_ETH();
|
|
// Warning: the interface desc is used in tests to capture actual connection
|
|
// details (IP, gw, mask)
|
|
esp_netif_config.if_desc = kNetifDescEth;
|
|
esp_netif_config.route_prio = 64;
|
|
esp_netif_config_t netif_config = {.base = &esp_netif_config,
|
|
.driver = nullptr,
|
|
.stack = ESP_NETIF_NETSTACK_DEFAULT_ETH};
|
|
s_eth_netif = esp_netif_new(&netif_config);
|
|
|
|
s_eth_glue = esp_eth_new_netif_glue(s_eth_handles[0]);
|
|
esp_netif_attach(s_eth_netif, s_eth_glue);
|
|
|
|
// Register user defined event handlers
|
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP,
|
|
ð_on_got_ip, NULL));
|
|
|
|
ESP_ERROR_CHECK(esp_eth_start(s_eth_handles[0]));
|
|
|
|
// Will teardown connection properly on shutdown
|
|
ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_ethernet));
|
|
|
|
if (blockUntilIPAcquired) {
|
|
ESP_LOGI(kLogEthernet, "Waiting for IP address.");
|
|
xSemaphoreTake(s_semph_get_ip_addrs, portMAX_DELAY);
|
|
blink_last_ip_octet();
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void disconnect_ethernet() {
|
|
#ifndef NDEBUG
|
|
assert(s_ethernet_connected &&
|
|
"Ethernet disconnect called but not connected!");
|
|
s_ethernet_connected = false;
|
|
#endif
|
|
|
|
teardown_ethernet();
|
|
ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&teardown_ethernet));
|
|
}
|
|
|
|
internal esp_err_t check_ethernet_connection(uint32_t timeoutSeconds) {
|
|
ESP_LOGI(kLogEthernet, "Waiting for IP address for %d seconds.",
|
|
timeoutSeconds);
|
|
if (xSemaphoreTake(s_semph_get_ip_addrs,
|
|
pdMS_TO_TICKS(timeoutSeconds * 1000))) {
|
|
blink_last_ip_octet();
|
|
return ESP_OK;
|
|
} else {
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
// === WiFi ===
|
|
|
|
internal constexpr char kLogWifi[] = "WIFI";
|
|
internal esp_netif_t *s_wifi_netif = nullptr;
|
|
internal SemaphoreHandle_t s_semph_get_wifi_ip_addrs = nullptr;
|
|
#ifndef NDEBUG
|
|
internal bool s_wifi_connected = false;
|
|
#endif
|
|
|
|
void wifi_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_DISCONNECTED) {
|
|
ESP_LOGI(kLogWifi, "WiFi disconnected, retrying...");
|
|
esp_wifi_connect();
|
|
} 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;
|
|
ESP_LOGI(kLogWifi, "Got IPv4 event: Interface \"%s\" address: " IPSTR,
|
|
esp_netif_get_desc(event->esp_netif), IP2STR(&event->ip_info.ip));
|
|
|
|
xSemaphoreGive(s_semph_get_wifi_ip_addrs);
|
|
}
|
|
}
|
|
|
|
void teardown_wifi() {
|
|
if (s_semph_get_wifi_ip_addrs != nullptr) {
|
|
vSemaphoreDelete(s_semph_get_wifi_ip_addrs);
|
|
s_semph_get_wifi_ip_addrs = nullptr;
|
|
}
|
|
|
|
esp_event_handler_unregister(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED,
|
|
&wifi_event_handler);
|
|
esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
|
&wifi_event_handler);
|
|
|
|
if (s_wifi_netif) {
|
|
esp_wifi_disconnect();
|
|
esp_wifi_stop();
|
|
esp_wifi_deinit();
|
|
esp_netif_destroy(s_wifi_netif);
|
|
s_wifi_netif = nullptr;
|
|
}
|
|
}
|
|
|
|
internal esp_err_t connect_wifi(const char *ssid, const char *password,
|
|
bool blockUntilIPAcquired) {
|
|
#ifndef NDEBUG
|
|
assert(!s_wifi_connected && "WiFi connect called but already connected!");
|
|
s_wifi_connected = true;
|
|
#endif
|
|
|
|
s_semph_get_wifi_ip_addrs = xSemaphoreCreateBinary();
|
|
if (s_semph_get_wifi_ip_addrs == NULL) {
|
|
return ESP_ERR_NO_MEM;
|
|
}
|
|
|
|
// esp_netif_init() is already called by Ethernet, but safe to call multiple
|
|
// times or we can assume it's initialized.
|
|
s_wifi_netif = esp_netif_create_default_wifi_sta();
|
|
|
|
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, WIFI_EVENT_STA_DISCONNECTED, &wifi_event_handler, NULL));
|
|
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP,
|
|
&wifi_event_handler, NULL));
|
|
|
|
wifi_config_t wifi_config = {};
|
|
strncpy((char *)wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid) - 1);
|
|
strncpy((char *)wifi_config.sta.password, password,
|
|
sizeof(wifi_config.sta.password) - 1);
|
|
// Setting a default auth mode if password is provided
|
|
wifi_config.sta.threshold.authmode =
|
|
password[0] != '\0' ? WIFI_AUTH_WPA2_PSK : WIFI_AUTH_OPEN;
|
|
|
|
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
|
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
|
ESP_ERROR_CHECK(esp_wifi_start());
|
|
ESP_ERROR_CHECK(esp_wifi_connect());
|
|
|
|
ESP_ERROR_CHECK(esp_register_shutdown_handler(&teardown_wifi));
|
|
|
|
if (blockUntilIPAcquired) {
|
|
ESP_LOGI(kLogWifi, "Waiting for IP address.");
|
|
xSemaphoreTake(s_semph_get_wifi_ip_addrs, portMAX_DELAY);
|
|
blink_last_ip_octet();
|
|
}
|
|
|
|
return ESP_OK;
|
|
}
|
|
|
|
void disconnect_wifi() {
|
|
#ifndef NDEBUG
|
|
assert(s_wifi_connected && "WiFi disconnect called but not connected!");
|
|
s_wifi_connected = false;
|
|
#endif
|
|
|
|
teardown_wifi();
|
|
ESP_ERROR_CHECK(esp_unregister_shutdown_handler(&teardown_wifi));
|
|
}
|
|
|
|
internal esp_err_t check_wifi_connection(uint32_t timeoutSeconds) {
|
|
ESP_LOGI(kLogWifi, "Waiting for IP address for %d seconds.", timeoutSeconds);
|
|
if (xSemaphoreTake(s_semph_get_wifi_ip_addrs,
|
|
pdMS_TO_TICKS(timeoutSeconds * 1000))) {
|
|
blink_last_ip_octet();
|
|
return ESP_OK;
|
|
} else {
|
|
return ESP_ERR_TIMEOUT;
|
|
}
|
|
}
|
|
|
|
internal esp_err_t get_ip_info(esp_netif_ip_info_t *ip_info) {
|
|
if (s_eth_netif) {
|
|
return esp_netif_get_ip_info(s_eth_netif, ip_info);
|
|
} else if (s_wifi_netif) {
|
|
return esp_netif_get_ip_info(s_wifi_netif, ip_info);
|
|
}
|
|
return ESP_FAIL;
|
|
}
|
|
|
|
internal void led_blink_number(int n, uint8_t r, uint8_t g, uint8_t b);
|
|
|
|
internal void blink_last_ip_octet() {
|
|
esp_netif_ip_info_t ip_info;
|
|
if (get_ip_info(&ip_info) == ESP_OK) {
|
|
uint8_t last_octet = (ip_info.ip.addr >> 24) & 0xFF;
|
|
printf("IP Address: " IPSTR "\n", IP2STR(&ip_info.ip));
|
|
|
|
// Blink digits of last_octet
|
|
int h = last_octet / 100;
|
|
int t = (last_octet / 10) % 10;
|
|
int u = last_octet % 10;
|
|
|
|
led_blink_number(h, 255, 255, 255);
|
|
led_blink_number(t, 255, 255, 255);
|
|
led_blink_number(u, 255, 255, 255);
|
|
}
|
|
}
|