From 7bcffb0253644e45d143b65ce3f6c4360434db0c Mon Sep 17 00:00:00 2001 From: 0xFEEDC0DE64 Date: Sat, 2 Jan 2021 18:55:44 +0100 Subject: [PATCH] Initial commit with refactored sources from goe charger source code --- cleanuphelper.h | 36 +++++++++++++ delayedconstruction.h | 99 ++++++++++++++++++++++++++++++++++++ espflags.h | 114 ++++++++++++++++++++++++++++++++++++++++++ esputils.h | 111 ++++++++++++++++++++++++++++++++++++++++ event_group.h | 36 +++++++++++++ http_client.h | 77 ++++++++++++++++++++++++++++ websocket_client.h | 36 +++++++++++++ wifiutils.h | 63 +++++++++++++++++++++++ 8 files changed, 572 insertions(+) create mode 100644 cleanuphelper.h create mode 100644 delayedconstruction.h create mode 100644 espflags.h create mode 100644 esputils.h create mode 100644 event_group.h create mode 100644 http_client.h create mode 100644 websocket_client.h create mode 100644 wifiutils.h diff --git a/cleanuphelper.h b/cleanuphelper.h new file mode 100644 index 0000000..5f1237f --- /dev/null +++ b/cleanuphelper.h @@ -0,0 +1,36 @@ +#pragma once + +// system includes +#include +#include + +namespace espcpputils { +template +class CleanupHelper +{ +public: + CleanupHelper(T && cleanup) : + m_cleanup{std::move(cleanup)} + {} + + ~CleanupHelper() + { + if (m_cleanup) + (*m_cleanup)(); + } + + void disarm() + { + m_cleanup = std::nullopt; + } + +private: + std::optional m_cleanup; +}; + +template +CleanupHelper makeCleanupHelper(T && cleanup) +{ + return CleanupHelper(std::move(cleanup)); +} +} // namespace espcpputils diff --git a/delayedconstruction.h b/delayedconstruction.h new file mode 100644 index 0000000..e14dda3 --- /dev/null +++ b/delayedconstruction.h @@ -0,0 +1,99 @@ +#pragma once + +// system includes +#include +#include +#include + +// local includes +#include "esputils.h" + +namespace espcpputils { +template +class DelayedConstruction +{ + ESP_DISABLE_COPY_MOVE(DelayedConstruction) + +public: + DelayedConstruction() = default; + ~DelayedConstruction() + { + if (m_constructed) + destruct(); + } + + template + void construct(Targs &&...args) + { + assert(!m_constructed); + new (&helper.value) T {std::forward(args)...}; + m_constructed = true; + } + + void destruct() + { + assert(m_constructed); + helper.value.~T(); + m_constructed = false; + } + + T &get() + { + assert(m_constructed); + return helper.value; + } + + const T &get() const + { + assert(m_constructed); + return helper.value; + } + + T *operator->() + { + assert(m_constructed); + return &helper.value; + } + + const T *operator->() const + { + assert(m_constructed); + return &helper.value; + } + + T &operator*() + { + assert(m_constructed); + return helper.value; + } + + const T &operator*() const + { + assert(m_constructed); + return helper.value; + } + + //! allows for getting the address before the object has been constructed + T &getUnsafe() + { + return helper.value; + } + + //! allows for getting the address before the object has been constructed + const T &getUnsafe() const + { + return helper.value; + } + + bool constructed() const { return m_constructed; } + +private: + bool m_constructed{}; + union Helper + { + Helper() {} + ~Helper() {} + T value; + } helper; +}; +} // namespace espcpputils diff --git a/espflags.h b/espflags.h new file mode 100644 index 0000000..29012f8 --- /dev/null +++ b/espflags.h @@ -0,0 +1,114 @@ +#pragma once + +// system includes +#include +#include +#include +#include + +namespace espcpputils { +class EspFlag +{ + using uint = unsigned int; + using ushort = unsigned short; + + int i; +public: + constexpr inline EspFlag(int value) noexcept : i(value) {} + constexpr inline operator int() const noexcept { return i; } + + constexpr inline EspFlag(uint value) noexcept : i(int(value)) {} + constexpr inline EspFlag(short value) noexcept : i(int(value)) {} + constexpr inline EspFlag(ushort value) noexcept : i(int(uint(value))) {} + constexpr inline operator uint() const noexcept { return uint(i); } +}; + +template +class EspFlags +{ + static_assert((sizeof(Enum) <= sizeof(int)), + "EspFlags uses an int as storage, so an enum with underlying " + "long long will overflow."); + static_assert((std::is_enum::value), "EspFlags is only usable on enumeration types."); + + using uint = unsigned int; + using ushort = unsigned short; + +public: + typedef typename std::conditional< + std::is_unsigned::type>::value, + unsigned int, + signed int + >::type Int; + + typedef Enum enum_type; + + constexpr inline EspFlags() noexcept : i(0) {} + constexpr inline EspFlags(Enum flags) noexcept : i(Int(flags)) {} + + constexpr inline EspFlags(EspFlag flag) noexcept : i(flag) {} + + constexpr inline EspFlags(std::initializer_list flags) noexcept + : i(initializer_list_helper(flags.begin(), flags.end())) {} + + constexpr inline EspFlags &operator&=(int mask) noexcept { i &= mask; return *this; } + constexpr inline EspFlags &operator&=(uint mask) noexcept { i &= mask; return *this; } + constexpr inline EspFlags &operator&=(Enum mask) noexcept { i &= Int(mask); return *this; } + constexpr inline EspFlags &operator|=(EspFlags other) noexcept { i |= other.i; return *this; } + constexpr inline EspFlags &operator|=(Enum other) noexcept { i |= Int(other); return *this; } + constexpr inline EspFlags &operator^=(EspFlags other) noexcept { i ^= other.i; return *this; } + constexpr inline EspFlags &operator^=(Enum other) noexcept { i ^= Int(other); return *this; } + + constexpr inline operator Int() const noexcept { return i; } + + constexpr inline EspFlags operator|(EspFlags other) const noexcept { return EspFlags(EspFlag(i | other.i)); } + constexpr inline EspFlags operator|(Enum other) const noexcept { return EspFlags(EspFlag(i | Int(other))); } + constexpr inline EspFlags operator^(EspFlags other) const noexcept { return EspFlags(EspFlag(i ^ other.i)); } + constexpr inline EspFlags operator^(Enum other) const noexcept { return EspFlags(EspFlag(i ^ Int(other))); } + constexpr inline EspFlags operator&(int mask) const noexcept { return EspFlags(EspFlag(i & mask)); } + constexpr inline EspFlags operator&(uint mask) const noexcept { return EspFlags(EspFlag(i & mask)); } + constexpr inline EspFlags operator&(Enum other) const noexcept { return EspFlags(EspFlag(i & Int(other))); } + constexpr inline EspFlags operator~() const noexcept { return EspFlags(EspFlag(~i)); } + + constexpr inline bool operator!() const noexcept { return !i; } + + constexpr inline bool testFlag(Enum flag) const noexcept { return (i & Int(flag)) == Int(flag) && (Int(flag) != 0 || i == Int(flag) ); } + constexpr inline EspFlags &setFlag(Enum flag, bool on = true) noexcept + { + return on ? (*this |= flag) : (*this &= ~Int(flag)); + } + +private: + constexpr static inline Int initializer_list_helper(typename std::initializer_list::const_iterator it, + typename std::initializer_list::const_iterator end) + noexcept + { + return (it == end ? Int(0) : (Int(*it) | initializer_list_helper(it + 1, end))); + } + + Int i; +}; + +class EspIncompatibleFlag +{ + int i; +public: + constexpr inline explicit EspIncompatibleFlag(int i) noexcept; + constexpr inline operator int() const noexcept { return i; } +}; + +constexpr inline EspIncompatibleFlag::EspIncompatibleFlag(int value) noexcept : i(value) {} +} // namespace espcpputils + +#define ESP_DECLARE_FLAGS(Flags, Enum)\ +typedef ::espcpputils::EspFlags Flags; + +#define ESP_DECLARE_INCOMPATIBLE_FLAGS(Flags) \ +constexpr inline ::espcpputils::EspIncompatibleFlag operator|(Flags::enum_type f1, int f2) noexcept \ +{ return ::espcpputils::EspIncompatibleFlag(int(f1) | f2); } + +#define ESP_DECLARE_OPERATORS_FOR_FLAGS(Flags) \ +constexpr inline ::espcpputils::EspFlags operator|(Flags::enum_type f1, Flags::enum_type f2) noexcept \ +{ return ::espcpputils::EspFlags(f1) | f2; } \ +constexpr inline ::espcpputils::EspFlags operator|(Flags::enum_type f1, ::espcpputils::EspFlags f2) noexcept \ +{ return f2 | f1; } ESP_DECLARE_INCOMPATIBLE_FLAGS(Flags) diff --git a/esputils.h b/esputils.h new file mode 100644 index 0000000..8e63d5c --- /dev/null +++ b/esputils.h @@ -0,0 +1,111 @@ +#pragma once + +// system includes +#include +#include +#include +#include + +/* + Avoid "unused parameter" warnings +*/ +#define ESP_UNUSED(x) (void)x; + +/* These two macros make it possible to turn the builtin line expander into a + * string literal. */ +#define ESP_STRINGIFY2(x) #x +#define ESP_STRINGIFY(x) ESP_STRINGIFY2(x) + +/* + Some classes do not permit copies to be made of an object. These + classes contains a private copy constructor and assignment + operator to disable copying (the compiler gives an error message). +*/ +#define ESP_DISABLE_COPY(Class) \ + Class(const Class &) = delete;\ + Class &operator=(const Class &) = delete; +#define ESP_DISABLE_MOVE(Class) \ + Class(Class &&) = delete; \ + Class &operator=(Class &&) = delete; +#define ESP_DISABLE_COPY_MOVE(Class) \ + ESP_DISABLE_COPY(Class) \ + ESP_DISABLE_MOVE(Class) + + +/* These two macros make it possible to define a typesafe enum with parse and + * toString methods */ +#define DECLARE_TYPESAFE_ENUM_HELPER1(name) name, +#define DECLARE_TYPESAFE_ENUM_HELPER2(name) case TheEnum::name: return #name; +#define DECLARE_TYPESAFE_ENUM_HELPER3(name) else if (str == ESP_STRINGIFY(name)) return TheEnum::name; + +#define DECLARE_TYPESAFE_ENUM(Name, Derivation, Values) \ + enum class Name Derivation \ + { \ + Values(DECLARE_TYPESAFE_ENUM_HELPER1) \ + }; \ + namespace { \ + template \ + T toString(Name value) \ + { \ + switch (value) \ + { \ + using TheEnum = Name; \ + Values(DECLARE_TYPESAFE_ENUM_HELPER2) \ + } \ + return T{"Unknown " #Name "("} + int(value) + ')'; \ + } \ + template \ + std::optional parse##Name(const T &str) \ + { \ + using TheEnum = Name; \ + if (false) {} \ + Values(DECLARE_TYPESAFE_ENUM_HELPER3) \ + return std::nullopt; \ + } \ + } // namespace + +namespace espcpputils { +template +constexpr const T& clamp( const T& v, const T& lo, const T& hi ) +{ + // assert( !(hi < lo) ); + return (v < lo) ? lo : (hi < v) ? hi : v; +} + +template +T mapValue(T x, T in_min, T in_max, T out_min, T out_max) +{ + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} + +template +class Signal +{ +public: + using Slot = std::function; + + Signal &operator+=(Slot &&slot) + { + m_slots.emplace_back(std::move(slot)); + return *this; + } + + Signal &operator+=(const Slot &slot) + { + m_slots.emplace_back(slot); + return *this; + } + + template + void operator()(Targs && ...args) const + { + for (const auto &slot : m_slots) + slot(std::forward(args)...); + } + +private: + std::vector m_slots; +}; + + +} // namespace espcpputils diff --git a/event_group.h b/event_group.h new file mode 100644 index 0000000..554f28c --- /dev/null +++ b/event_group.h @@ -0,0 +1,36 @@ +#pragma once + +// espressif includes +#include +#include + +// local includes +#include "esputils.h" + +namespace espcpputils { +class event_group +{ + ESP_DISABLE_COPY_MOVE(event_group) + +public: + event_group() : + handle{xEventGroupCreate()} + {} + + ~event_group() { if (handle) vEventGroupDelete(handle); } + + EventBits_t waitBits(const EventBits_t uxBitsToWaitFor, const BaseType_t xClearOnExit, const BaseType_t xWaitForAllBits, TickType_t xTicksToWait) + { return xEventGroupWaitBits(handle, uxBitsToWaitFor, xClearOnExit, xWaitForAllBits, xTicksToWait); } + + EventBits_t clearBits(const EventBits_t uxBitsToClear) { return xEventGroupClearBits(handle, uxBitsToClear); } + + EventBits_t setBits(const EventBits_t uxBitsToSet) { return xEventGroupSetBits(handle, uxBitsToSet); } + + EventBits_t groupSync(const EventBits_t uxBitsToSet, const EventBits_t uxBitsToWaitFor, TickType_t xTicksToWait) + { return xEventGroupSync(handle, uxBitsToSet, uxBitsToWaitFor, xTicksToWait); } + + EventBits_t getBits() const { return xEventGroupClearBits(handle, 0); } + + const EventGroupHandle_t handle; +}; +} // namespace espcpputils diff --git a/http_client.h b/http_client.h new file mode 100644 index 0000000..1b9368d --- /dev/null +++ b/http_client.h @@ -0,0 +1,77 @@ +#pragma once + +// espressif includes +#include + +// local includes +#include "esputils.h" + +namespace espcpputils { +class http_client +{ + ESP_DISABLE_COPY_MOVE(http_client) + +public: + http_client(const esp_http_client_config_t *config) : + handle{esp_http_client_init(config)} + {} + + ~http_client() { if (handle) esp_http_client_cleanup(handle); } + + esp_err_t perform() { return esp_http_client_perform(handle); } + esp_err_t set_url(const char *url) { return esp_http_client_set_url(handle, url); } + esp_err_t set_post_field(const char *data, int len) { return esp_http_client_set_post_field(handle, data, len); } + int get_post_field(char **data) { return esp_http_client_get_post_field(handle, data); } + esp_err_t set_header(const char *key, const char *value) { return esp_http_client_set_header(handle, key, value); } + esp_err_t get_header(const char *key, char **value) { return esp_http_client_get_header(handle, key, value); } + esp_err_t get_username(char **value) { return esp_http_client_get_username(handle, value); } + esp_err_t set_username(const char *username) { return esp_http_client_set_username(handle, username); } + esp_err_t get_password(char **value) { return esp_http_client_get_password(handle, value); } + esp_err_t set_password(char *password) { return esp_http_client_set_password(handle, password); } + esp_err_t set_method(esp_http_client_method_t method) { return esp_http_client_set_method(handle, method); } + esp_err_t delete_header(const char *key) { return esp_http_client_delete_header(handle, key); } + esp_err_t open(int write_len) { return esp_http_client_open(handle, write_len); } + int write(const char *buffer, int len) { return esp_http_client_write(handle, buffer, len); } + int fetch_headers() { return esp_http_client_fetch_headers(handle); } + bool is_chunked_response() const { return esp_http_client_is_chunked_response(handle); } + int read(char *buffer, int len) { return esp_http_client_read(handle, buffer, len); } + int get_status_code() const { return esp_http_client_get_status_code(handle); } + int get_content_length() const { return esp_http_client_get_content_length(handle); } + esp_err_t close() { return esp_http_client_close(handle); } + esp_http_client_transport_t get_transport_type() const { return esp_http_client_get_transport_type(handle); } + esp_err_t set_redirection() { return esp_http_client_set_redirection(handle); } + void add_auth() { return esp_http_client_add_auth(handle); } + bool is_complete_data_received() const { return esp_http_client_is_complete_data_received(handle); } + + static esp_http_client_config_t zeroInitializedConfig() + { + return esp_http_client_config_t { + .url = {}, + .host = {}, + .port = {}, + .username = {}, + .password = {}, + .auth_type = {}, + .path = {}, + .query = {}, + .cert_pem = {}, + .client_cert_pem = {}, + .client_key_pem = {}, + .method = {}, + .timeout_ms = {}, + .disable_auto_redirect = {}, + .max_redirection_count = {}, + .event_handler = {}, + .transport_type = {}, + .buffer_size = {}, + .buffer_size_tx = {}, + .user_data = {}, + .is_async = {}, + .use_global_ca_store = {}, + .skip_cert_common_name_check = {} + }; + } + + const esp_http_client_handle_t handle; +}; +} // namespace espcpputils diff --git a/websocket_client.h b/websocket_client.h new file mode 100644 index 0000000..5c4af51 --- /dev/null +++ b/websocket_client.h @@ -0,0 +1,36 @@ +#pragma once + +// espressif includes +#include + +// local includes +#include "esputils.h" + +namespace espcpputils { +class websocket_client +{ + ESP_DISABLE_COPY_MOVE(websocket_client) + +public: + websocket_client(const esp_websocket_client_config_t &config) : + handle{esp_websocket_client_init(&config)} + { + } + + ~websocket_client() { if (handle) esp_websocket_client_destroy(handle); } + + esp_err_t set_uri (const char *uri) { return esp_websocket_client_set_uri (handle, uri); } + esp_err_t start () { return esp_websocket_client_start (handle); } + esp_err_t stop () { return esp_websocket_client_stop (handle); } + int send (const char *data, int len, TickType_t timeout) { return esp_websocket_client_send (handle, data, len, timeout); } + int send_bin (const char *data, int len, TickType_t timeout) { return esp_websocket_client_send_bin (handle, data, len, timeout); } + int send_text (const char *data, int len, TickType_t timeout) { return esp_websocket_client_send_text (handle, data, len, timeout); } + //esp_err_t close (TickType_t timeout) { return esp_websocket_client_close (handle, timeout); } + //esp_err_t close_with_code(int code, const char *data, int len, TickType_t timeout) { return esp_websocket_client_close_with_code(handle, code, data, len, timeout); } + bool is_connected () const { return esp_websocket_client_is_connected (handle); } + esp_err_t register_events(esp_websocket_event_id_t event, esp_event_handler_t event_handler, void *event_handler_arg) + { return esp_websocket_register_events(handle, event, event_handler, event_handler_arg); } + + const esp_websocket_client_handle_t handle; +}; +} // namespace espcpputils diff --git a/wifiutils.h b/wifiutils.h new file mode 100644 index 0000000..708c5e3 --- /dev/null +++ b/wifiutils.h @@ -0,0 +1,63 @@ +#pragma once + +// Arduino includes +#include +#include + +namespace espcpputils { +namespace { +template +T scanResultToString(int16_t result) +{ + if (result < 0) + { + switch (result) + { + case WIFI_SCAN_RUNNING: return "WIFI_SCAN_RUNNING"; + case WIFI_SCAN_FAILED: return "WIFI_SCAN_FAILED"; + } + + return T{"Failed ("} + result + ')'; + } + + return T{"Finished ("} + result + ')'; +} + +template +T 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_MAX: return "WIFI_AUTH_MAX"; + } + + return T{"Unknown wifi_auth_mode_t("} + encryptionType + ')'; +} + +template +T toString(wl_status_t status) +{ + switch (status) + { + case WL_NO_SHIELD: return "WL_NO_SHIELD"; + case WL_IDLE_STATUS: return "WL_IDLE_STATUS"; + case WL_NO_SSID_AVAIL: return "WL_NO_SSID_AVAIL"; + case WL_SCAN_COMPLETED: return "WL_SCAN_COMPLETED"; + case WL_CONNECTED: return "WL_CONNECTED"; + case WL_CONNECT_FAILED: return "WL_CONNECT_FAILED"; + case WL_CONNECTION_LOST: return "WL_CONNECTION_LOST"; + case WL_DISCONNECTED: return "WL_DISCONNECTED"; + } + + return T{"Unknown wl_status_t("} + int(status) + ')'; +} +} // namespace +} // namespace espcpputils