Imported existing sources

This commit is contained in:
2021-06-28 21:55:13 +02:00
parent f02699287f
commit 1d6c9f338c
9 changed files with 2598 additions and 0 deletions

39
CMakeLists.txt Normal file
View File

@@ -0,0 +1,39 @@
set(headers
src/espwifistack.h
src/espwifistackconfig.h
src/espwifistackenums.h
src/espwifiutils.h
)
set(sources
src/espwifistack.cpp
src/espwifistackenums.cpp
src/espwifiutils.cpp
)
set(dependencies
cpputils
espchrono
espcpputils
expected
)
idf_component_register(
INCLUDE_DIRS
src
SRCS
${headers}
${sources}
REQUIRES
${dependencies}
)
target_compile_options(${COMPONENT_TARGET}
PRIVATE
-fstack-reuse=all
-fstack-protector-all
-Wno-unused-function
-Wno-deprecated-declarations
-Wno-missing-field-initializers
-Wno-parentheses
)

39
Kconfig.projbuild Normal file
View File

@@ -0,0 +1,39 @@
menu "Simple WiFi Stack settings"
choice LOG_LOCAL_LEVEL_WIFI_STACK
bool "WIFI_STACK log verbosity"
default LOG_LOCAL_LEVEL_WIFI_STACK_INFO
help
Specify how much output to compile into the binary.
You can set lower verbosity level at runtime using
esp_log_level_set function.
Note that this setting limits which log statements
are compiled into the program. So setting this to,
say, "Warning" would mean that changing log level
to "Debug" at runtime will not be possible.
config LOG_LOCAL_LEVEL_WIFI_STACK_NONE
bool "No output"
config LOG_LOCAL_LEVEL_WIFI_STACK_ERROR
bool "Error"
config LOG_LOCAL_LEVEL_WIFI_STACK_WARN
bool "Warning"
config LOG_LOCAL_LEVEL_WIFI_STACK_INFO
bool "Info"
config LOG_LOCAL_LEVEL_WIFI_STACK_DEBUG
bool "Debug"
config LOG_LOCAL_LEVEL_WIFI_STACK_VERBOSE
bool "Verbose"
endchoice
config LOG_LOCAL_LEVEL_WIFI_STACK
int
default 0 if LOG_LOCAL_LEVEL_WIFI_STACK_NONE
default 1 if LOG_LOCAL_LEVEL_WIFI_STACK_ERROR
default 2 if LOG_LOCAL_LEVEL_WIFI_STACK_WARN
default 3 if LOG_LOCAL_LEVEL_WIFI_STACK_INFO
default 4 if LOG_LOCAL_LEVEL_WIFI_STACK_DEBUG
default 5 if LOG_LOCAL_LEVEL_WIFI_STACK_VERBOSE
endmenu

2027
src/espwifistack.cpp Normal file

File diff suppressed because it is too large Load Diff

59
src/espwifistack.h Normal file
View File

@@ -0,0 +1,59 @@
#pragma once
// system includes
#include <string>
#include <vector>
// esp-idf includes
#include <esp_wifi.h>
#include <esp_wifi_types.h>
#include <esp_netif_types.h>
// 3rdparty lib includes
#include <tl/expected.hpp>
// local includes
#include "espwifistackconfig.h"
#include "espwifistackenums.h"
#include "espwifiutils.h"
#include "espchrono.h"
#include "cppsignal.h"
namespace wifi_stack {
struct scan_result
{
std::vector<wifi_ap_record_t> entries;
espchrono::millis_clock::time_point finished;
};
extern esp_netif_t* esp_netifs[ESP_IF_MAX];
extern const WiFiState &wifiStateMachineState;
extern cpputils::Signal<> scanResultChanged;
//! Call once at startup
void init(const config &config);
//! Call repeatedly, approx. every 100ms
void update(const config &config);
//! Tells the status of the STA interface (connected, ...)
WiFiStaStatus get_sta_status();
//! Tries to begin a new scan, if succeeds clears the old scan result
esp_err_t begin_scan(const config &config);
//! Tells the status of the currently running scan (finished, ...)
WiFiScanStatus get_scan_status();
//! Retrieves the current scan result (TODO: create a fresh container
//! every time to free memory and avoid cross thread access crashes)
const std::optional<scan_result> &get_scan_result();
//! Clears the scan result
void delete_scan_result();
tl::expected<wifi_ap_record_t, std::string> get_sta_ap_info();
} // namespace wifi_stack

94
src/espwifistackconfig.h Normal file
View File

@@ -0,0 +1,94 @@
#pragma once
// system includes
#include <string>
#include <array>
#include <cstdint>
// esp-idf includes
#include <esp_wifi_types.h>
// local includes
#include "espwifiutils.h"
namespace wifi_stack {
struct wifi_entry
{
std::string ssid;
std::string key;
friend bool operator==(const wifi_entry &left, const wifi_entry &right)
{
return left.ssid == right.ssid &&
left.key == right.key;
}
friend bool operator!=(const wifi_entry &left, const wifi_entry &right)
{
return !(left == right);
}
};
struct ap_config : public wifi_entry
{
int channel;
wifi_auth_mode_t authmode;
bool ssid_hidden;
int max_connection;
uint16_t beacon_interval;
ip_address_t ip;
ip_address_t subnet;
friend bool operator==(const ap_config &left, const ap_config &right)
{
return *static_cast<const wifi_entry *>(&left) == *static_cast<const wifi_entry *>(&right) &&
left.channel == right.channel &&
left.authmode == right.authmode &&
left.ssid_hidden == right.ssid_hidden &&
left.max_connection == right.max_connection &&
left.beacon_interval == right.beacon_interval &&
left.ip == right.ip &&
left.subnet == right.subnet;
}
friend bool operator!=(const ap_config &left, const ap_config &right)
{
return !(left == right);
}
};
struct ip_setting
{
bool dhcpEnabled;
ip_address_t staticIp;
ip_address_t staticGateway;
ip_address_t staticSubnet;
ip_address_t staticDns1;
ip_address_t staticDns2;
friend bool operator==(const ip_setting &left, const ip_setting &right)
{
return left.dhcpEnabled == right.dhcpEnabled &&
left.staticIp == right.staticIp &&
left.staticGateway == right.staticGateway &&
left.staticSubnet == right.staticSubnet &&
left.staticDns1 == right.staticDns1 &&
left.staticDns2 == right.staticDns2;
}
friend bool operator!=(const ip_setting &left, const ip_setting &right)
{
return !(left == right);
}
};
struct config
{
bool wifiEnabled;
std::string hostname;
std::array<wifi_entry, 10> wifis;
ip_setting sta_ip;
ap_config ap;
int8_t min_rssi;
};
} // namespace wifi_stack

View File

@@ -0,0 +1,9 @@
#include "espwifistackenums.h"
namespace wifi_stack {
IMPLEMENT_TYPESAFE_ENUM(WiFiState, : uint8_t, WiFiStateValues)
IMPLEMENT_TYPESAFE_ENUM(WiFiStaStatus, : uint8_t, WiFiStaStatusValues)
IMPLEMENT_TYPESAFE_ENUM(WiFiScanStatus, : uint8_t, WiFiScanStatusValues)
} // namespace wifi_stack

35
src/espwifistackenums.h Normal file
View File

@@ -0,0 +1,35 @@
#pragma once
// local includes
#include "cpptypesafeenum.h"
namespace wifi_stack {
#define WiFiStateValues(x) \
x(None) \
x(Scanning) \
x(Connecting) \
x(Connected)
DECLARE_TYPESAFE_ENUM(WiFiState, : uint8_t, WiFiStateValues)
#define WiFiStaStatusValues(x) \
x(WL_IDLE_STATUS) \
x(WL_NO_SSID_AVAIL) \
x(WL_SCAN_COMPLETED) \
x(WL_CONNECTED) \
x(WL_CONNECT_FAILED) \
x(WL_CONNECTION_LOST) \
x(WL_DISCONNECTED) \
x(WL_CONNECTING) \
x(WL_DISCONNECTING) \
x(WL_NO_SHIELD)
DECLARE_TYPESAFE_ENUM(WiFiStaStatus, : uint8_t, WiFiStaStatusValues)
#define WiFiScanStatusValues(x) \
x(None) \
x(Scanning) \
x(Finished) \
x(Failed)
DECLARE_TYPESAFE_ENUM(WiFiScanStatus, : uint8_t, WiFiScanStatusValues)
} // namespace wifi_stack

193
src/espwifiutils.cpp Normal file
View File

@@ -0,0 +1,193 @@
#include "espwifiutils.h"
// esp-idf includes
#include <esp_log.h>
namespace wifi_stack {
namespace {
constexpr const char * const TAG = "WIFI_STACK";
} // namespace
bool goe_wifi_ap_config_equal(const wifi_ap_config_t& lhs, const wifi_ap_config_t& rhs)
{
auto leftSsid = lhs.ssid_len ?
std::string_view{reinterpret_cast<const char*>(lhs.ssid), lhs.ssid_len} :
std::string_view{reinterpret_cast<const char*>(lhs.ssid)};
auto rightSsid = rhs.ssid_len ?
std::string_view{reinterpret_cast<const char*>(rhs.ssid), rhs.ssid_len} :
std::string_view{reinterpret_cast<const char*>(rhs.ssid)};
return leftSsid == rightSsid &&
std::string_view{reinterpret_cast<const char*>(lhs.password)} == std::string_view{reinterpret_cast<const char*>(rhs.password)} &&
lhs.channel == rhs.channel &&
lhs.authmode == rhs.authmode &&
lhs.ssid_hidden == rhs.ssid_hidden &&
lhs.max_connection == rhs.max_connection &&
lhs.beacon_interval == rhs.beacon_interval &&
lhs.pairwise_cipher == rhs.pairwise_cipher;
}
bool goe_wifi_sta_config_equal(const wifi_sta_config_t& lhs, const wifi_sta_config_t& rhs)
{
return std::string_view{reinterpret_cast<const char*>(lhs.ssid)} == std::string_view{reinterpret_cast<const char*>(rhs.ssid)} &&
std::string_view{reinterpret_cast<const char*>(lhs.password)} == std::string_view{reinterpret_cast<const char*>(rhs.password)} &&
lhs.scan_method == rhs.scan_method &&
lhs.bssid_set == rhs.bssid_set &&
(lhs.bssid_set ? (*reinterpret_cast<const mac_t *>(lhs.bssid) == *reinterpret_cast<const mac_t *>(rhs.bssid)) : true) &&
lhs.channel == rhs.channel &&
lhs.listen_interval == rhs.listen_interval &&
lhs.sort_method == rhs.sort_method &&
lhs.threshold.rssi == rhs.threshold.rssi &&
lhs.threshold.authmode == rhs.threshold.authmode &&
lhs.pmf_cfg.capable == rhs.pmf_cfg.capable &&
lhs.pmf_cfg.required == rhs.pmf_cfg.required &&
bool(lhs.rm_enabled) == bool(rhs.rm_enabled) &&
bool(lhs.btm_enabled) == bool(rhs.btm_enabled);
}
std::string toString(wifi_auth_mode_t encryptionType)
{
switch (encryptionType)
{
case WIFI_AUTH_OPEN: return "WIFI_AUTH_OPEN";
case WIFI_AUTH_WEP: return "WIFI_AUTH_WEP";
case WIFI_AUTH_WPA_PSK: return "WIFI_AUTH_WPA_PSK";
case WIFI_AUTH_WPA2_PSK: return "WIFI_AUTH_WPA2_PSK";
case WIFI_AUTH_WPA_WPA2_PSK: return "WIFI_AUTH_WPA_WPA2_PSK";
case WIFI_AUTH_WPA2_ENTERPRISE: return "WIFI_AUTH_WPA2_ENTERPRISE";
case WIFI_AUTH_WPA3_PSK: return "WIFI_AUTH_WPA3_PSK";
case WIFI_AUTH_WPA2_WPA3_PSK: return "WIFI_AUTH_WPA2_WPA3_PSK";
case WIFI_AUTH_WAPI_PSK: return "WIFI_AUTH_WAPI_PSK";
case WIFI_AUTH_MAX: return "WIFI_AUTH_MAX";
}
return std::string{"Unknown wifi_auth_mode_t("} + std::to_string(int(encryptionType)) + ')';
}
std::string toString(const mac_t &mac)
{
char macStr[18]{0};
std::snprintf(macStr, 18, "%02hhX:%02hhX:%02hhX:%02hhX:%02hhX:%02hhX",
mac.at(0), mac.at(1), mac.at(2), mac.at(3), mac.at(4), mac.at(5));
return std::string{macStr};
}
bool ip_address_t::fromString(std::string_view address)
{
// TODO: add support for "a", "a.b", "a.b.c" formats
// TODO: replace with scanf for better performance
uint16_t acc = 0; // Accumulator
uint8_t dots = 0;
for (char c : address)
{
if (c >= '0' && c <= '9')
{
acc = acc * 10 + (c - '0');
if (acc > 255) {
// Value out of [0..255] range
return false;
}
}
else if (c == '.')
{
if (dots == 3) {
// Too much dots (there must be 3 dots)
return false;
}
_bytes[dots++] = acc;
acc = 0;
}
else
{
// Invalid char
return false;
}
}
if (dots != 3) {
// Too few dots (there must be 3 dots)
return false;
}
_bytes[3] = acc;
return true;
}
std::string toString(const ip_address_t &address)
{
char szRet[16];
sprintf(szRet,"%hhu.%hhu.%hhu.%hhu", address[0], address[1], address[2], address[3]);
return std::string{szRet};
}
ip_address_t goe_wifi_calculate_network_id(ip_address_t ip, ip_address_t subnet)
{
ip_address_t networkID;
for (size_t i = 0; i < 4; i++)
networkID[i] = subnet[i] & ip[i];
return networkID;
}
ip_address_t goe_wifi_calculate_broadcast(ip_address_t ip, ip_address_t subnet)
{
ip_address_t broadcastIp;
for (int i = 0; i < 4; i++)
broadcastIp[i] = ~subnet[i] | ip[i];
return broadcastIp;
}
uint8_t goe_wifi_calculate_subnet_cidr(ip_address_t subnetMask)
{
uint8_t CIDR = 0;
for (uint8_t i = 0; i < 4; i++) {
if (subnetMask[i] == 0x80) // 128
CIDR += 1;
else if (subnetMask[i] == 0xC0) // 192
CIDR += 2;
else if (subnetMask[i] == 0xE0) // 224
CIDR += 3;
else if (subnetMask[i] == 0xF0) // 242
CIDR += 4;
else if (subnetMask[i] == 0xF8) // 248
CIDR += 5;
else if (subnetMask[i] == 0xFC) // 252
CIDR += 6;
else if (subnetMask[i] == 0xFE) // 254
CIDR += 7;
else if (subnetMask[i] == 0xFF) // 255
CIDR += 8;
}
return CIDR;
}
std::string toString(ip4_addr_t val)
{
return toString(ip_address_t{val.addr});
}
std::string toString(const ip6_addr_t &val)
{
char str[40];
ip6addr_ntoa_r(&val, str, sizeof(str));
return std::string{str};
}
std::string toString(ip_addr_t val)
{
switch (val.type)
{
case IPADDR_TYPE_V4: return toString(val.u_addr.ip4);
case IPADDR_TYPE_V6: return toString(val.u_addr.ip6);
default:
ESP_LOGW(TAG, "unknown ipv%i", val.type);
return "unknown ipv" + std::to_string(val.type);
}
}
} // namespace wifi_stack

103
src/espwifiutils.h Normal file
View File

@@ -0,0 +1,103 @@
#pragma once
// system includes
#include <stdint.h>
#include <string>
#include <string_view>
#include <array>
// esp-idf includes
#include <esp_wifi_types.h>
#include <esp_netif_ip_addr.h>
#include <lwip/ip_addr.h>
namespace wifi_stack {
bool goe_wifi_ap_config_equal(const wifi_ap_config_t& lhs, const wifi_ap_config_t& rhs);
bool goe_wifi_sta_config_equal(const wifi_sta_config_t& lhs, const wifi_sta_config_t& rhs);
std::string toString(wifi_auth_mode_t encryptionType);
// A class to make it easier to handle and pass around mac addresses / bssids
class mac_t : public std::array<uint8_t, 6>
{
public:
using std::array<uint8_t, 6>::array;
constexpr explicit mac_t(const uint8_t (&arr)[6]) noexcept
{
std::copy(std::begin(arr), std::end(arr), std::begin(*this));
}
void copyTo(uint8_t (&arr)[6]) const noexcept
{
std::copy(std::begin(*this), std::end(*this), std::begin(arr));
}
};
std::string toString(const mac_t &mac);
// A class to make it easier to handle and pass around IP addresses
// Implementation taken from arduino-esp32
class ip_address_t
{
public:
using value_t = uint32_t;
private:
union {
std::array<uint8_t, 4> _bytes; // IPv4 address
value_t _value;
static_assert(sizeof(_bytes) == sizeof(_value));
};
public:
// Constructors
constexpr ip_address_t() noexcept : _value{0} {}
constexpr ip_address_t(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) noexcept : _bytes{b0, b1, b2, b3} {}
constexpr explicit ip_address_t(value_t address) noexcept : _value{address} {}
constexpr explicit ip_address_t(ip4_addr_t address) noexcept : _value{address.addr} {}
constexpr explicit ip_address_t(esp_ip4_addr_t address) noexcept : _value{address.addr} {}
constexpr ip_address_t(std::array<uint8_t, 4> bytes) noexcept : _bytes{bytes} {}
bool fromString(std::string_view address);
// Access the raw byte array containing the address. Because this returns a pointer
// to the internal structure rather than a copy of the address this function should only
// be used when you know that the usage of the returned uint8_t* will be transient and not
// stored.
constexpr std::array<uint8_t, 4> &bytes() noexcept { return _bytes; }
constexpr std::array<uint8_t, 4> bytes() const noexcept { return _bytes; }
constexpr value_t value() const noexcept { return _value; }
friend constexpr bool operator==(const ip_address_t &left, const ip_address_t &right) noexcept { return left._value == right._value; }
friend constexpr bool operator!=(const ip_address_t &left, const ip_address_t &right) noexcept { return !(left == right); }
// Overloaded index operator to allow getting and setting individual octets of the address
constexpr uint8_t& operator[](int index) noexcept { return _bytes[index]; }
constexpr uint8_t operator[](int index) const noexcept { return _bytes[index]; }
// Overloaded copy operators to allow initialisation of ip_address_t objects from other types
constexpr ip_address_t& operator=(const ip_address_t &other) noexcept { _value = other._value; return *this; }
constexpr ip_address_t& operator=(std::array<uint8_t, 4> bytes) noexcept { _bytes = bytes; return *this; }
constexpr ip_address_t& operator=(value_t value) noexcept { _value = value; return *this; }
};
std::string toString(const ip_address_t &val);
ip_address_t goe_wifi_calculate_network_id(ip_address_t ip, ip_address_t subnet);
ip_address_t goe_wifi_calculate_broadcast(ip_address_t ip, ip_address_t subnet);
uint8_t goe_wifi_calculate_subnet_cidr(ip_address_t subnetMask);
std::string toString(ip4_addr_t val);
std::string toString(const ip6_addr_t &val);
std::string toString(ip_addr_t val);
inline std::string toString(const esp_ip4_addr_t &val)
{ return toString(*reinterpret_cast<const ip4_addr_t *>(&val)); }
inline std::string toString(const esp_ip6_addr_t &val)
{ return toString(*reinterpret_cast<const ip6_addr_t *>(&val)); }
} // namespace wifi_stack