summaryrefslogtreecommitdiff
path: root/minion
diff options
context:
space:
mode:
authorHampusM <hampus@hampusmat.com>2022-05-31 11:39:04 +0200
committerHampusM <hampus@hampusmat.com>2022-05-31 11:39:04 +0200
commite4b7b5ba2e1371b142167e75a3ddcd63a0e00a48 (patch)
treedf6866e73fdf19962fdd29fd6de5c54ee971db8b /minion
parente75bc5d3fe24979ca59c995a7decbcbf720a20a8 (diff)
style(minion): change eol to LF
Diffstat (limited to 'minion')
-rw-r--r--minion/.gitignore5
-rw-r--r--minion/platformio.ini36
-rw-r--r--minion/scripts/override_compiledb_path.py14
-rw-r--r--minion/src/util.cpp92
-rw-r--r--minion/src/util.hpp98
-rw-r--r--minion/src/wifi_module.cpp1302
-rw-r--r--minion/src/wifi_module.hpp322
7 files changed, 937 insertions, 932 deletions
diff --git a/minion/.gitignore b/minion/.gitignore
new file mode 100644
index 0000000..89cc49c
--- /dev/null
+++ b/minion/.gitignore
@@ -0,0 +1,5 @@
+.pio
+.vscode/.browse.c_cpp.db*
+.vscode/c_cpp_properties.json
+.vscode/launch.json
+.vscode/ipch
diff --git a/minion/platformio.ini b/minion/platformio.ini
index 2abc5cf..62d5c1d 100644
--- a/minion/platformio.ini
+++ b/minion/platformio.ini
@@ -1,18 +1,18 @@
-[platformio]
-build_dir = build
-
-[env:uno]
-platform = atmelavr
-board = uno
-framework = arduino
-build_src_flags =
- -Wextra
- -Wpedantic
- -Wshadow
- -Wcast-align
- -Wunused
- -Wold-style-cast
- -Wconversion
- -pedantic
- -felide-constructors
-extra_scripts = post:scripts/override_compiledb_path.py
+[platformio]
+build_dir = build
+
+[env:uno]
+platform = atmelavr
+board = uno
+framework = arduino
+build_src_flags =
+ -Wextra
+ -Wpedantic
+ -Wshadow
+ -Wcast-align
+ -Wunused
+ -Wold-style-cast
+ -Wconversion
+ -pedantic
+ -felide-constructors
+extra_scripts = post:scripts/override_compiledb_path.py
diff --git a/minion/scripts/override_compiledb_path.py b/minion/scripts/override_compiledb_path.py
index 38f52e9..3048b3e 100644
--- a/minion/scripts/override_compiledb_path.py
+++ b/minion/scripts/override_compiledb_path.py
@@ -1,7 +1,7 @@
-import os
-
-Import("env")
-
-path = os.path.join("$BUILD_DIR", "compile_commands.json")
-
-env.Replace(COMPILATIONDB_PATH=path)
+import os
+
+Import("env")
+
+path = os.path.join("$BUILD_DIR", "compile_commands.json")
+
+env.Replace(COMPILATIONDB_PATH=path)
diff --git a/minion/src/util.cpp b/minion/src/util.cpp
index 708e47d..b55a6e8 100644
--- a/minion/src/util.cpp
+++ b/minion/src/util.cpp
@@ -1,46 +1,46 @@
-#include "util.hpp"
-
-namespace util
-{
-
-auto str_ends_with(const char *target, size_t target_end, const char *other) noexcept
- -> bool
-{
- if (target == nullptr || other == nullptr)
- {
- return false;
- }
-
- const auto other_str_length = strlen(other);
-
- if (other_str_length > target_end)
- {
- return false;
- }
-
- return strncmp(target + target_end - other_str_length, other, other_str_length) == 0;
-}
-
-void substr(const char *str, const char *end, char *dest) noexcept
-{
- auto *dest_head = dest;
-
- for (const char *char_ptr = str; char_ptr != end; ++char_ptr)
- {
- *dest_head = *char_ptr;
- dest_head++;
- }
-}
-
-auto streq(const char *str_one, const char *str_two) noexcept -> bool
-{
- return strcmp(str_one, str_two) == 0;
-}
-
-void quit() noexcept
-{
- while (true)
- ;
-}
-
-} // namespace util
+#include "util.hpp"
+
+namespace util
+{
+
+auto str_ends_with(const char *target, size_t target_end, const char *other) noexcept
+ -> bool
+{
+ if (target == nullptr || other == nullptr)
+ {
+ return false;
+ }
+
+ const auto other_str_length = strlen(other);
+
+ if (other_str_length > target_end)
+ {
+ return false;
+ }
+
+ return strncmp(target + target_end - other_str_length, other, other_str_length) == 0;
+}
+
+void substr(const char *str, const char *end, char *dest) noexcept
+{
+ auto *dest_head = dest;
+
+ for (const char *char_ptr = str; char_ptr != end; ++char_ptr)
+ {
+ *dest_head = *char_ptr;
+ dest_head++;
+ }
+}
+
+auto streq(const char *str_one, const char *str_two) noexcept -> bool
+{
+ return strcmp(str_one, str_two) == 0;
+}
+
+void quit() noexcept
+{
+ while (true)
+ ;
+}
+
+} // namespace util
diff --git a/minion/src/util.hpp b/minion/src/util.hpp
index 2955cf1..a36dc5b 100644
--- a/minion/src/util.hpp
+++ b/minion/src/util.hpp
@@ -1,49 +1,49 @@
-#pragma once
-
-#include <stddef.h>
-#include <stdlib.h>
-#include <string.h>
-
-namespace util
-{
-
-template <typename Type>
-auto malloc(size_t size) noexcept -> Type *
-{
- return static_cast<Type *>(::malloc(size));
-}
-
-/**
- * Returns whether or not a string ends with the content of another string.
- *
- * @param target The string to compare the end of.
- * @param target_end The end position of the target string.
- * @param other The string to compare with.
- */
-auto str_ends_with(const char *target, size_t target_end, const char *other) noexcept
- -> bool;
-
-/**
- * Extracts a portion of a string.
- *
- * @param str The target string.
- * @param end A pointer to a place inside the target string.
- * @param dest Output buffer.
- */
-void substr(const char *str, const char *end, char *dest) noexcept;
-
-/**
- * Compares two strings.
- *
- * Wrapper function for strcmp.
- *
- * @param str_one The first string.
- * @param str_two The second string.
- *
- * @returns Whether or not the two strings contain the same content.
- */
-auto streq(const char *str_one, const char *str_two) noexcept -> bool;
-
-void quit() noexcept;
-
-} // namespace util
+#pragma once
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+
+namespace util
+{
+
+template <typename Type>
+auto malloc(size_t size) noexcept -> Type *
+{
+ return static_cast<Type *>(::malloc(size));
+}
+
+/**
+ * Returns whether or not a string ends with the content of another string.
+ *
+ * @param target The string to compare the end of.
+ * @param target_end The end position of the target string.
+ * @param other The string to compare with.
+ */
+auto str_ends_with(const char *target, size_t target_end, const char *other) noexcept
+ -> bool;
+
+/**
+ * Extracts a portion of a string.
+ *
+ * @param str The target string.
+ * @param end A pointer to a place inside the target string.
+ * @param dest Output buffer.
+ */
+void substr(const char *str, const char *end, char *dest) noexcept;
+
+/**
+ * Compares two strings.
+ *
+ * Wrapper function for strcmp.
+ *
+ * @param str_one The first string.
+ * @param str_two The second string.
+ *
+ * @returns Whether or not the two strings contain the same content.
+ */
+auto streq(const char *str_one, const char *str_two) noexcept -> bool;
+
+void quit() noexcept;
+
+} // namespace util
diff --git a/minion/src/wifi_module.cpp b/minion/src/wifi_module.cpp
index 8a2dd6a..51b22dc 100644
--- a/minion/src/wifi_module.cpp
+++ b/minion/src/wifi_module.cpp
@@ -1,651 +1,651 @@
-#include "wifi_module.hpp"
-
-#include "network_connection.hpp"
-#include "util.hpp"
-
-#include <Arduino.h>
-#include <string.h>
-
-WiFiModule::WiFiModule(const WiFiModuleOptions &options) noexcept
- : _serial(options.receive_pin, options.transmit_pin)
-{
-}
-
-void WiFiModule::begin(size_t baudrate) noexcept
-{
- _serial.begin(baudrate);
-}
-
-auto WiFiModule::get_available() noexcept -> int
-{
- return _serial.available();
-}
-
-void WiFiModule::reset() noexcept
-{
- const auto send_success = _send_serial("AT+RST");
-
- if (!send_success)
- {
- Serial.println(F("Failed to send command to reset wifi module"));
- return;
- }
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- const auto response_status = _read(2000U, response);
-
- Serial.println(
- response_status == WiFiModuleResponseStatus::OK ? F("Reset wifi module")
- : F("Failed to reset wifi module")
- );
-}
-
-auto WiFiModule::connect_to_wifi(const char *ssid, const char *password) noexcept -> bool
-{
- const auto cmd = "AT+CWJAP_CUR";
-
- auto command_length =
- WIFI_CONNECT_COMMAND_BASE_LENGTH + strlen(cmd) + strlen(ssid) + strlen(password);
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- Serial.println(
- F("Heap memory allocation failed for creating a wifi connection command")
- );
- return false;
- }
-
- snprintf(command, command_length, "%s=\"%s\",\"%s\"", cmd, ssid, password);
-
- const auto send_success = _send_serial(command);
-
- if (!send_success)
- {
- Serial.println(F("Failed to send wifi connect command"));
- free(command);
- return false;
- }
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- const auto response_status = _read(40000U, response);
-
- return response_status == WiFiModuleResponseStatus::OK;
-}
-
-void WiFiModule::set_wifi_mode(WifiMode wifi_mode) noexcept
-{
- const char cmd[] = "AT+CWMODE_CUR";
-
- const auto command_length = strlen(cmd) + 2U + 1U;
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- return;
- }
-
- snprintf(command, command_length, "%s=%u", cmd, static_cast<int>(wifi_mode));
-
- _send_serial(command);
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- _read(TIMEOUT_SHORT, response);
-}
-
-void WiFiModule::set_multiple_connections_enabled(bool is_enabled) noexcept
-{
- const char cmd[] = "AT+CIPMUX";
-
- auto command_length = strlen(cmd) + 2U + 1U;
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- return;
- }
-
- snprintf(command, command_length, "%s=%u", cmd, is_enabled ? 1U : 0U);
-
- _send_serial(command);
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- _read(TIMEOUT_SHORT, response);
-}
-
-void WiFiModule::set_echo_enabled(bool is_enabled) noexcept
-{
- const char cmd[] = "ATE";
-
- auto command_length = strlen(cmd) + 1U + 1U;
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- return;
- }
-
- snprintf(command, command_length, "%s%u", cmd, is_enabled ? 1U : 0U);
-
- _send_serial(command);
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- const auto response_status = _read(1500U, response);
-
- Serial.print(
- response_status == WiFiModuleResponseStatus::OK ? F("Turned ")
- : F("Failed to turn ")
- );
- Serial.print(is_enabled ? F("on") : F("off"));
- Serial.println(F(" AT commands echo"));
-}
-
-void WiFiModule::create_tcp_server(size_t port) noexcept
-{
- const auto *cmd = "AT+CIPSERVER";
-
- auto command_length = CREATE_TCP_SERVER_COMMAND_BASE_LENGTH + strlen(cmd);
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- return;
- }
-
- snprintf(command, command_length, "%s=1,%u", cmd, port);
-
- _send_serial(command);
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- _read(TIMEOUT_MEDIUM, response);
-}
-
-auto WiFiModule::test() noexcept -> bool
-{
- const auto send_success = _send_serial("AT");
-
- if (!send_success)
- {
- return false;
- }
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- const auto response_status = _read(8000U, response);
-
- if (response_status == WiFiModuleResponseStatus::TIMEOUT)
- {
- return false;
- }
-
- return strcmp(response, "") != 0;
-}
-
-auto WiFiModule::get_local_ip(char *local_ip_out) noexcept -> const char *
-{
- const auto send_success = _send_serial("AT+CIFSR");
-
- if (!send_success)
- {
- return local_ip_out;
- }
-
- auto *buf = util::malloc<char>(strlen(local_ip_out) + 1U);
-
- if (buf == nullptr)
- {
- strcpy(local_ip_out, "Memory allocation failure");
- return local_ip_out;
- }
-
- strcpy(buf, "");
-
- const auto response_status = _read(10000U, buf);
-
- if (response_status != WiFiModuleResponseStatus::OK)
- {
- free(buf);
-
- sprintf(
- local_ip_out,
- "Response status was not OK. Was %d",
- static_cast<int>(response_status)
- );
-
- return local_ip_out;
- }
-
- auto local_ip_end = strstr(buf, "\"\r\n+CIFSR:STAMAC");
-
- if (local_ip_end == nullptr)
- {
- free(buf);
- strcpy(local_ip_out, "Response parsing error");
- return local_ip_out;
- }
-
- const auto staip_title_length = 16U; // The length of '+CIFSR:STAIP,"'
-
- util::substr(buf + staip_title_length, local_ip_end, local_ip_out);
-
- free(buf);
-
- return local_ip_out;
-}
-
-auto WiFiModule::get_mac_address(char *mac_address_out) noexcept -> const char *
-{
- const auto send_success = _send_serial("AT+CIFSR");
-
- if (!send_success)
- {
- return mac_address_out;
- }
-
- auto *buf = util::malloc<char>(strlen(mac_address_out) + 1U);
-
- if (buf == nullptr)
- {
- strcpy(mac_address_out, "Memory allocation failure");
- return mac_address_out;
- }
-
- strcpy(buf, "");
-
- const auto response_status = _read(10000U, buf);
-
- if (response_status != WiFiModuleResponseStatus::OK)
- {
- free(buf);
-
- sprintf(
- mac_address_out,
- "Response status was not OK. Was %d",
- static_cast<int>(response_status)
- );
-
- return mac_address_out;
- }
-
- const auto stamac_title = "CIFSR:STAMAC,\"";
-
- auto mac_address_start = strstr(buf, stamac_title);
-
- if (mac_address_start == nullptr)
- {
- free(buf);
- strcpy(mac_address_out, "Response parsing error");
- return mac_address_out;
- }
-
- mac_address_start += strlen(stamac_title);
-
- util::substr(mac_address_start, mac_address_start + 17U, mac_address_out);
-
- free(buf);
-
- return mac_address_out;
-}
-
-auto WiFiModule::read_incoming_request() noexcept -> HTTPRequest
-{
- char request_prefix[] = "+IPD,";
-
- if (get_available() == 0 || !_serial.find(request_prefix))
- {
- return HTTPRequest::create_invalid();
- }
-
- const auto min_available_bytes = 5;
-
- // Wait for the data buffer a bit
- while (get_available() < min_available_bytes)
- {
- }
-
- const auto connection = NetworkConnection(_read_connection_id());
-
- auto request_data_length = _read_request_data_length();
-
- const auto request_method = _read_request_method(request_data_length);
-
- // Read request path
- char request_path[REQUEST_PATH_MAX_LENGTH] = "";
- _read_to(&request_path[0], sizeof(request_path) - 1U, ' ', TIMEOUT_MEDIUM);
-
- request_data_length -= strlen(request_path) + 1U;
-
- // Read request HTTP version
- char http_version[REQUEST_HTTP_VERSION_MAX_LENGTH] = "";
- _read_to(&http_version[0], sizeof(http_version) - 1U, '\r', TIMEOUT_MEDIUM);
-
- request_data_length -= strlen(http_version) + 1U;
-
- // Skip the newline
- while (get_available() == 0)
- {
- }
- _serial.read();
- request_data_length -= 1U;
-
- auto request_data = util::malloc<char>(request_data_length + 1U);
-
- if (request_data == nullptr)
- {
- return HTTPRequest::create_invalid();
- }
-
- strcpy(request_data, "");
-
- _read_bytes(request_data, request_data_length, TIMEOUT_LONG);
-
- free(request_data);
-
- return HTTPRequest(
- connection,
- request_method,
- http_version,
- request_path,
- 0, // request_data_length,
- nullptr // request_data
- );
-}
-
-auto WiFiModule::close_connection(NetworkConnection &connection) noexcept -> bool
-{
- const auto *cmd = "AT+CIPCLOSE";
-
- auto command_length = CLOSE_CONNECTION_COMMAND_BASE_LENGTH + strlen(cmd);
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- Serial.println(
- F("Heap memory allocation failed for creating close connection command")
- );
- return false;
- }
-
- snprintf(command, command_length, "%s=%u", cmd, connection.id());
-
- _send_serial(command);
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- const auto response_status = _read(4000U, response);
-
- if (response_status != WiFiModuleResponseStatus::OK)
- {
- Serial.print(F("Failed to close connection to "));
- Serial.println(connection.id());
- Serial.println(response);
- return false;
- }
-
- connection.set_is_closed(true);
-
- Serial.print(F("Closed connection to "));
- Serial.println(connection.id());
-
- return true;
-}
-
-auto WiFiModule::send_response(
- const NetworkConnection &connection,
- size_t status_code,
- const char **headers,
- size_t headers_cnt,
- const char *body
-) noexcept -> bool
-{
- const auto *cmd = "AT+CIPSEND";
-
- auto tot_headers_lengths = 0U;
-
- for (size_t index = 0U; index < headers_cnt; index++)
- {
- tot_headers_lengths += strlen(headers[index]) + 2U;
- }
-
- const auto data_length = strlen_P(RESPONSE_HTTP_VERSION) + 1 +
- RESPONSE_STATUS_CODE_LENGTH + 4 + strlen(body) +
- tot_headers_lengths;
-
- auto command_length = SEND_RESPONSE_COMMAND_BASE_LENGTH + strlen(cmd) + data_length;
-
- auto *command = util::malloc<char>(command_length);
-
- if (command == nullptr)
- {
- Serial.println(
- F("Heap memory allocation failed for creating send response command")
- );
- return false;
- }
-
- snprintf(command, command_length, "%s=%u,%u", cmd, connection.id(), data_length);
-
- _send_serial(command);
-
- free(command);
-
- char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
-
- _read(TIMEOUT_MEDIUM, response);
-
- _serial.print(reinterpret_cast<const __FlashStringHelper *>(RESPONSE_HTTP_VERSION));
- _serial.print(F(" "));
- _serial.print(status_code);
- _serial.print(F("\r\n"));
-
- // Print headers
- for (size_t index = 0U; index < headers_cnt; index++)
- {
- _serial.print(headers[index]);
- _serial.print(F("\r\n"));
- }
-
- _serial.print(F("\r\n"));
- _serial.print(body);
-
- strcpy(response, "");
-
- _read(TIMEOUT_MEDIUM, response);
-
- return true;
-}
-
-auto WiFiModule::_send_serial(const char *command) noexcept -> bool
-{
- auto full_command_length = strlen(command) + 2U + 1U;
-
- auto *full_command = util::malloc<char>(full_command_length);
-
- if (full_command == nullptr)
- {
- // NOLINTNEXTLINE(readability-simplify-boolean-expr)
- return false;
- }
-
- snprintf(full_command, full_command_length, "%s\r\n", command);
-
- _serial.print(full_command);
-
- free(full_command);
-
- return true;
-}
-
-auto WiFiModule::_read_connection_id() noexcept -> int8_t
-{
- const auto connection_id = _serial.read() - ASCII_TO_CHAR;
-
- // Skip the comma
- _serial.read();
-
- return static_cast<int8_t>(connection_id);
-}
-
-auto WiFiModule::_read_request_data_length() noexcept -> size_t
-{
- char data_length_buf[REQUEST_DATA_LENGTH_BUF_SIZE] = "";
-
- _read_to(&data_length_buf[0], sizeof(data_length_buf) - 1U, ':', TIMEOUT_SHORT);
-
- return static_cast<size_t>(strtoul(data_length_buf, nullptr, NUMBER_BASE));
-}
-
-auto WiFiModule::_read_request_method(size_t &request_data_length) noexcept
- -> HTTPRequestMethod
-{
- char request_method_buf[REQUEST_METHOD_STR_MAX_LENGTH] = "";
-
- _read_to(
- &request_method_buf[0],
- sizeof(request_method_buf) - 1U,
- ' ',
- TIMEOUT_MEDIUM
- );
-
- const auto request_method = str_to_http_request_method(request_method_buf);
-
- request_data_length -= strlen(request_method_buf) + 1U;
-
- return request_method;
-}
-
-auto WiFiModule::_read(uint64_t timeout, char *response_out) noexcept
- -> WiFiModuleResponseStatus
-{
- const auto start_time = millis();
- size_t index = 0U;
- bool has_end = false;
- auto status = WiFiModuleResponseStatus::TIMEOUT;
-
- while (!has_end && (start_time + timeout) > millis())
- {
- while (_serial.available() != 0)
- {
- char character = _read_byte();
-
- if (has_end)
- {
- continue;
- }
-
- const auto end_pos = index + 1U;
-
- response_out[index] = character;
- response_out[end_pos] = '\0';
-
- if (util::str_ends_with(response_out, end_pos, "OK"))
- {
- status = WiFiModuleResponseStatus::OK;
- has_end = true;
- }
-
- if (util::str_ends_with(response_out, end_pos, "ERROR"))
- {
- status = WiFiModuleResponseStatus::ERROR;
- has_end = true;
- }
-
- if (util::str_ends_with(response_out, end_pos, "FAIL"))
- {
- status = WiFiModuleResponseStatus::FAIL;
- has_end = true;
- }
-
- index++;
- }
- }
-
- return status;
-}
-
-void WiFiModule::_read_to(
- char *buffer_out,
- size_t length, // NOLINT(bugprone-easily-swappable-parameters)
- char stop_char,
- uint64_t timeout
-) noexcept
-{
- auto position = 0U;
- const auto start_time = millis();
-
- while (position < length && (start_time + timeout) > millis())
- {
- if (get_available() == 0)
- {
- continue;
- }
-
- auto character = _read_byte();
-
- if (character == stop_char)
- {
- break;
- }
-
- buffer_out[position++] = character;
- }
-
- buffer_out[position] = '\0';
-}
-
-// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
-void WiFiModule::_read_bytes(char *buffer_out, size_t length, uint64_t timeout) noexcept
-{
- const auto original_length = length;
-
- auto position = 0U;
- const auto start_time = millis();
-
- while (position != length && (start_time + timeout) > millis())
- {
- if (get_available() == 0)
- {
- continue;
- }
-
- char character = _read_byte();
-
- buffer_out[position++] = character;
-
- if (character == '\n' && position != original_length)
- {
- length -= 2U;
- }
- }
-
- buffer_out[position] = '\0';
-}
-
-auto WiFiModule::_read_byte() noexcept -> char
-{
- return static_cast<char>(_serial.read());
-}
+#include "wifi_module.hpp"
+
+#include "network_connection.hpp"
+#include "util.hpp"
+
+#include <Arduino.h>
+#include <string.h>
+
+WiFiModule::WiFiModule(const WiFiModuleOptions &options) noexcept
+ : _serial(options.receive_pin, options.transmit_pin)
+{
+}
+
+void WiFiModule::begin(size_t baudrate) noexcept
+{
+ _serial.begin(baudrate);
+}
+
+auto WiFiModule::get_available() noexcept -> int
+{
+ return _serial.available();
+}
+
+void WiFiModule::reset() noexcept
+{
+ const auto send_success = _send_serial("AT+RST");
+
+ if (!send_success)
+ {
+ Serial.println(F("Failed to send command to reset wifi module"));
+ return;
+ }
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ const auto response_status = _read(2000U, response);
+
+ Serial.println(
+ response_status == WiFiModuleResponseStatus::OK ? F("Reset wifi module")
+ : F("Failed to reset wifi module")
+ );
+}
+
+auto WiFiModule::connect_to_wifi(const char *ssid, const char *password) noexcept -> bool
+{
+ const auto cmd = "AT+CWJAP_CUR";
+
+ auto command_length =
+ WIFI_CONNECT_COMMAND_BASE_LENGTH + strlen(cmd) + strlen(ssid) + strlen(password);
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ Serial.println(
+ F("Heap memory allocation failed for creating a wifi connection command")
+ );
+ return false;
+ }
+
+ snprintf(command, command_length, "%s=\"%s\",\"%s\"", cmd, ssid, password);
+
+ const auto send_success = _send_serial(command);
+
+ if (!send_success)
+ {
+ Serial.println(F("Failed to send wifi connect command"));
+ free(command);
+ return false;
+ }
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ const auto response_status = _read(40000U, response);
+
+ return response_status == WiFiModuleResponseStatus::OK;
+}
+
+void WiFiModule::set_wifi_mode(WifiMode wifi_mode) noexcept
+{
+ const char cmd[] = "AT+CWMODE_CUR";
+
+ const auto command_length = strlen(cmd) + 2U + 1U;
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ return;
+ }
+
+ snprintf(command, command_length, "%s=%u", cmd, static_cast<int>(wifi_mode));
+
+ _send_serial(command);
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ _read(TIMEOUT_SHORT, response);
+}
+
+void WiFiModule::set_multiple_connections_enabled(bool is_enabled) noexcept
+{
+ const char cmd[] = "AT+CIPMUX";
+
+ auto command_length = strlen(cmd) + 2U + 1U;
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ return;
+ }
+
+ snprintf(command, command_length, "%s=%u", cmd, is_enabled ? 1U : 0U);
+
+ _send_serial(command);
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ _read(TIMEOUT_SHORT, response);
+}
+
+void WiFiModule::set_echo_enabled(bool is_enabled) noexcept
+{
+ const char cmd[] = "ATE";
+
+ auto command_length = strlen(cmd) + 1U + 1U;
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ return;
+ }
+
+ snprintf(command, command_length, "%s%u", cmd, is_enabled ? 1U : 0U);
+
+ _send_serial(command);
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ const auto response_status = _read(1500U, response);
+
+ Serial.print(
+ response_status == WiFiModuleResponseStatus::OK ? F("Turned ")
+ : F("Failed to turn ")
+ );
+ Serial.print(is_enabled ? F("on") : F("off"));
+ Serial.println(F(" AT commands echo"));
+}
+
+void WiFiModule::create_tcp_server(size_t port) noexcept
+{
+ const auto *cmd = "AT+CIPSERVER";
+
+ auto command_length = CREATE_TCP_SERVER_COMMAND_BASE_LENGTH + strlen(cmd);
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ return;
+ }
+
+ snprintf(command, command_length, "%s=1,%u", cmd, port);
+
+ _send_serial(command);
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ _read(TIMEOUT_MEDIUM, response);
+}
+
+auto WiFiModule::test() noexcept -> bool
+{
+ const auto send_success = _send_serial("AT");
+
+ if (!send_success)
+ {
+ return false;
+ }
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ const auto response_status = _read(8000U, response);
+
+ if (response_status == WiFiModuleResponseStatus::TIMEOUT)
+ {
+ return false;
+ }
+
+ return strcmp(response, "") != 0;
+}
+
+auto WiFiModule::get_local_ip(char *local_ip_out) noexcept -> const char *
+{
+ const auto send_success = _send_serial("AT+CIFSR");
+
+ if (!send_success)
+ {
+ return local_ip_out;
+ }
+
+ auto *buf = util::malloc<char>(strlen(local_ip_out) + 1U);
+
+ if (buf == nullptr)
+ {
+ strcpy(local_ip_out, "Memory allocation failure");
+ return local_ip_out;
+ }
+
+ strcpy(buf, "");
+
+ const auto response_status = _read(10000U, buf);
+
+ if (response_status != WiFiModuleResponseStatus::OK)
+ {
+ free(buf);
+
+ sprintf(
+ local_ip_out,
+ "Response status was not OK. Was %d",
+ static_cast<int>(response_status)
+ );
+
+ return local_ip_out;
+ }
+
+ auto local_ip_end = strstr(buf, "\"\r\n+CIFSR:STAMAC");
+
+ if (local_ip_end == nullptr)
+ {
+ free(buf);
+ strcpy(local_ip_out, "Response parsing error");
+ return local_ip_out;
+ }
+
+ const auto staip_title_length = 16U; // The length of '+CIFSR:STAIP,"'
+
+ util::substr(buf + staip_title_length, local_ip_end, local_ip_out);
+
+ free(buf);
+
+ return local_ip_out;
+}
+
+auto WiFiModule::get_mac_address(char *mac_address_out) noexcept -> const char *
+{
+ const auto send_success = _send_serial("AT+CIFSR");
+
+ if (!send_success)
+ {
+ return mac_address_out;
+ }
+
+ auto *buf = util::malloc<char>(strlen(mac_address_out) + 1U);
+
+ if (buf == nullptr)
+ {
+ strcpy(mac_address_out, "Memory allocation failure");
+ return mac_address_out;
+ }
+
+ strcpy(buf, "");
+
+ const auto response_status = _read(10000U, buf);
+
+ if (response_status != WiFiModuleResponseStatus::OK)
+ {
+ free(buf);
+
+ sprintf(
+ mac_address_out,
+ "Response status was not OK. Was %d",
+ static_cast<int>(response_status)
+ );
+
+ return mac_address_out;
+ }
+
+ const auto stamac_title = "CIFSR:STAMAC,\"";
+
+ auto mac_address_start = strstr(buf, stamac_title);
+
+ if (mac_address_start == nullptr)
+ {
+ free(buf);
+ strcpy(mac_address_out, "Response parsing error");
+ return mac_address_out;
+ }
+
+ mac_address_start += strlen(stamac_title);
+
+ util::substr(mac_address_start, mac_address_start + 17U, mac_address_out);
+
+ free(buf);
+
+ return mac_address_out;
+}
+
+auto WiFiModule::read_incoming_request() noexcept -> HTTPRequest
+{
+ char request_prefix[] = "+IPD,";
+
+ if (get_available() == 0 || !_serial.find(request_prefix))
+ {
+ return HTTPRequest::create_invalid();
+ }
+
+ const auto min_available_bytes = 5;
+
+ // Wait for the data buffer a bit
+ while (get_available() < min_available_bytes)
+ {
+ }
+
+ const auto connection = NetworkConnection(_read_connection_id());
+
+ auto request_data_length = _read_request_data_length();
+
+ const auto request_method = _read_request_method(request_data_length);
+
+ // Read request path
+ char request_path[REQUEST_PATH_MAX_LENGTH] = "";
+ _read_to(&request_path[0], sizeof(request_path) - 1U, ' ', TIMEOUT_MEDIUM);
+
+ request_data_length -= strlen(request_path) + 1U;
+
+ // Read request HTTP version
+ char http_version[REQUEST_HTTP_VERSION_MAX_LENGTH] = "";
+ _read_to(&http_version[0], sizeof(http_version) - 1U, '\r', TIMEOUT_MEDIUM);
+
+ request_data_length -= strlen(http_version) + 1U;
+
+ // Skip the newline
+ while (get_available() == 0)
+ {
+ }
+ _serial.read();
+ request_data_length -= 1U;
+
+ auto request_data = util::malloc<char>(request_data_length + 1U);
+
+ if (request_data == nullptr)
+ {
+ return HTTPRequest::create_invalid();
+ }
+
+ strcpy(request_data, "");
+
+ _read_bytes(request_data, request_data_length, TIMEOUT_LONG);
+
+ free(request_data);
+
+ return HTTPRequest(
+ connection,
+ request_method,
+ http_version,
+ request_path,
+ 0, // request_data_length,
+ nullptr // request_data
+ );
+}
+
+auto WiFiModule::close_connection(NetworkConnection &connection) noexcept -> bool
+{
+ const auto *cmd = "AT+CIPCLOSE";
+
+ auto command_length = CLOSE_CONNECTION_COMMAND_BASE_LENGTH + strlen(cmd);
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ Serial.println(
+ F("Heap memory allocation failed for creating close connection command")
+ );
+ return false;
+ }
+
+ snprintf(command, command_length, "%s=%u", cmd, connection.id());
+
+ _send_serial(command);
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ const auto response_status = _read(4000U, response);
+
+ if (response_status != WiFiModuleResponseStatus::OK)
+ {
+ Serial.print(F("Failed to close connection to "));
+ Serial.println(connection.id());
+ Serial.println(response);
+ return false;
+ }
+
+ connection.set_is_closed(true);
+
+ Serial.print(F("Closed connection to "));
+ Serial.println(connection.id());
+
+ return true;
+}
+
+auto WiFiModule::send_response(
+ const NetworkConnection &connection,
+ size_t status_code,
+ const char **headers,
+ size_t headers_cnt,
+ const char *body
+) noexcept -> bool
+{
+ const auto *cmd = "AT+CIPSEND";
+
+ auto tot_headers_lengths = 0U;
+
+ for (size_t index = 0U; index < headers_cnt; index++)
+ {
+ tot_headers_lengths += strlen(headers[index]) + 2U;
+ }
+
+ const auto data_length = strlen_P(RESPONSE_HTTP_VERSION) + 1 +
+ RESPONSE_STATUS_CODE_LENGTH + 4 + strlen(body) +
+ tot_headers_lengths;
+
+ auto command_length = SEND_RESPONSE_COMMAND_BASE_LENGTH + strlen(cmd) + data_length;
+
+ auto *command = util::malloc<char>(command_length);
+
+ if (command == nullptr)
+ {
+ Serial.println(
+ F("Heap memory allocation failed for creating send response command")
+ );
+ return false;
+ }
+
+ snprintf(command, command_length, "%s=%u,%u", cmd, connection.id(), data_length);
+
+ _send_serial(command);
+
+ free(command);
+
+ char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = "";
+
+ _read(TIMEOUT_MEDIUM, response);
+
+ _serial.print(reinterpret_cast<const __FlashStringHelper *>(RESPONSE_HTTP_VERSION));
+ _serial.print(F(" "));
+ _serial.print(status_code);
+ _serial.print(F("\r\n"));
+
+ // Print headers
+ for (size_t index = 0U; index < headers_cnt; index++)
+ {
+ _serial.print(headers[index]);
+ _serial.print(F("\r\n"));
+ }
+
+ _serial.print(F("\r\n"));
+ _serial.print(body);
+
+ strcpy(response, "");
+
+ _read(TIMEOUT_MEDIUM, response);
+
+ return true;
+}
+
+auto WiFiModule::_send_serial(const char *command) noexcept -> bool
+{
+ auto full_command_length = strlen(command) + 2U + 1U;
+
+ auto *full_command = util::malloc<char>(full_command_length);
+
+ if (full_command == nullptr)
+ {
+ // NOLINTNEXTLINE(readability-simplify-boolean-expr)
+ return false;
+ }
+
+ snprintf(full_command, full_command_length, "%s\r\n", command);
+
+ _serial.print(full_command);
+
+ free(full_command);
+
+ return true;
+}
+
+auto WiFiModule::_read_connection_id() noexcept -> int8_t
+{
+ const auto connection_id = _serial.read() - ASCII_TO_CHAR;
+
+ // Skip the comma
+ _serial.read();
+
+ return static_cast<int8_t>(connection_id);
+}
+
+auto WiFiModule::_read_request_data_length() noexcept -> size_t
+{
+ char data_length_buf[REQUEST_DATA_LENGTH_BUF_SIZE] = "";
+
+ _read_to(&data_length_buf[0], sizeof(data_length_buf) - 1U, ':', TIMEOUT_SHORT);
+
+ return static_cast<size_t>(strtoul(data_length_buf, nullptr, NUMBER_BASE));
+}
+
+auto WiFiModule::_read_request_method(size_t &request_data_length) noexcept
+ -> HTTPRequestMethod
+{
+ char request_method_buf[REQUEST_METHOD_STR_MAX_LENGTH] = "";
+
+ _read_to(
+ &request_method_buf[0],
+ sizeof(request_method_buf) - 1U,
+ ' ',
+ TIMEOUT_MEDIUM
+ );
+
+ const auto request_method = str_to_http_request_method(request_method_buf);
+
+ request_data_length -= strlen(request_method_buf) + 1U;
+
+ return request_method;
+}
+
+auto WiFiModule::_read(uint64_t timeout, char *response_out) noexcept
+ -> WiFiModuleResponseStatus
+{
+ const auto start_time = millis();
+ size_t index = 0U;
+ bool has_end = false;
+ auto status = WiFiModuleResponseStatus::TIMEOUT;
+
+ while (!has_end && (start_time + timeout) > millis())
+ {
+ while (_serial.available() != 0)
+ {
+ char character = _read_byte();
+
+ if (has_end)
+ {
+ continue;
+ }
+
+ const auto end_pos = index + 1U;
+
+ response_out[index] = character;
+ response_out[end_pos] = '\0';
+
+ if (util::str_ends_with(response_out, end_pos, "OK"))
+ {
+ status = WiFiModuleResponseStatus::OK;
+ has_end = true;
+ }
+
+ if (util::str_ends_with(response_out, end_pos, "ERROR"))
+ {
+ status = WiFiModuleResponseStatus::ERROR;
+ has_end = true;
+ }
+
+ if (util::str_ends_with(response_out, end_pos, "FAIL"))
+ {
+ status = WiFiModuleResponseStatus::FAIL;
+ has_end = true;
+ }
+
+ index++;
+ }
+ }
+
+ return status;
+}
+
+void WiFiModule::_read_to(
+ char *buffer_out,
+ size_t length, // NOLINT(bugprone-easily-swappable-parameters)
+ char stop_char,
+ uint64_t timeout
+) noexcept
+{
+ auto position = 0U;
+ const auto start_time = millis();
+
+ while (position < length && (start_time + timeout) > millis())
+ {
+ if (get_available() == 0)
+ {
+ continue;
+ }
+
+ auto character = _read_byte();
+
+ if (character == stop_char)
+ {
+ break;
+ }
+
+ buffer_out[position++] = character;
+ }
+
+ buffer_out[position] = '\0';
+}
+
+// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
+void WiFiModule::_read_bytes(char *buffer_out, size_t length, uint64_t timeout) noexcept
+{
+ const auto original_length = length;
+
+ auto position = 0U;
+ const auto start_time = millis();
+
+ while (position != length && (start_time + timeout) > millis())
+ {
+ if (get_available() == 0)
+ {
+ continue;
+ }
+
+ char character = _read_byte();
+
+ buffer_out[position++] = character;
+
+ if (character == '\n' && position != original_length)
+ {
+ length -= 2U;
+ }
+ }
+
+ buffer_out[position] = '\0';
+}
+
+auto WiFiModule::_read_byte() noexcept -> char
+{
+ return static_cast<char>(_serial.read());
+}
diff --git a/minion/src/wifi_module.hpp b/minion/src/wifi_module.hpp
index da4a487..1049c54 100644
--- a/minion/src/wifi_module.hpp
+++ b/minion/src/wifi_module.hpp
@@ -1,161 +1,161 @@
-#pragma once
-
-#include "http/request.hpp"
-#include "network_connection.hpp"
-
-#include <SoftwareSerial.h>
-#include <avr/pgmspace.h>
-#include <stddef.h>
-#include <stdint.h>
-
-constexpr auto MAX_NETWORK_MODULE_RESPONSE_LENGTH = 128U;
-
-constexpr auto REQUEST_DATA_LENGTH_BUF_SIZE = 16U;
-constexpr auto REQUEST_METHOD_STR_MAX_LENGTH = 10U;
-constexpr auto REQUEST_PATH_MAX_LENGTH = 64U;
-constexpr auto REQUEST_HTTP_VERSION_MAX_LENGTH = 10U;
-
-constexpr auto WIFI_CONNECT_COMMAND_BASE_LENGTH = 7U;
-constexpr auto CREATE_TCP_SERVER_COMMAND_BASE_LENGTH = 8U;
-constexpr auto CLOSE_CONNECTION_COMMAND_BASE_LENGTH = 51U;
-constexpr auto SEND_RESPONSE_COMMAND_BASE_LENGTH = 51U;
-
-constexpr auto ASCII_TO_CHAR = 48U;
-
-constexpr auto TIMEOUT_SHORT = 1500U;
-constexpr auto TIMEOUT_MEDIUM = 4000U;
-constexpr auto TIMEOUT_LONG = 10000U;
-
-const char RESPONSE_HTTP_VERSION[] PROGMEM = { "HTTP/1.1" };
-constexpr auto RESPONSE_STATUS_CODE_LENGTH = 3U;
-
-constexpr auto NUMBER_BASE = 10U;
-
-enum WifiMode
-{
- Station = 1,
- SoftAP = 2,
- SoftAPAndStation = 3
-};
-
-enum WiFiModuleResponseStatus
-{
- OK,
- FAIL,
- ERROR,
- TIMEOUT
-};
-
-struct WiFiModuleOptions
-{
- uint8_t receive_pin;
- uint8_t transmit_pin;
-};
-
-class WiFiModule
-{
-public:
- explicit WiFiModule(const WiFiModuleOptions &options) noexcept;
-
- void begin(size_t baudrate) noexcept;
-
- auto get_available() noexcept -> int;
-
- void reset() noexcept;
-
- /**
- * Connects to a wifi network.
- *
- * @param ssid The service set identifier of a wifi network.
- * @param password The wifi network password.
- *
- * @returns Whether or not it succeeded.
- */
- auto connect_to_wifi(const char *ssid, const char *password) noexcept -> bool;
-
- void set_wifi_mode(WifiMode wifi_mode) noexcept;
-
- void set_multiple_connections_enabled(bool is_enabled) noexcept;
-
- void set_echo_enabled(bool is_enabled) noexcept;
-
- void create_tcp_server(size_t port) noexcept;
-
- /**
- * Tests the connection to the wifi module.
- *
- * @returns Whether or not the test succeeded.
- */
- auto test() noexcept -> bool;
-
- /**
- * Gets local IP address of the wifi module.
- *
- * @param local_ip_out Local IP output buffer.
- *
- * @returns A pointer to the local IP output buffer.
- */
- auto get_local_ip(char *local_ip_out) noexcept -> const char *;
-
- /**
- * Gets the MAC address of the wifi module.
- *
- * @param mac_address_out MAC address output buffer.
- *
- * @returns A pointer to the MAC address output buffer.
- */
- auto get_mac_address(char *mac_address_out) noexcept -> const char *;
-
- /**
- * Reads a incoming HTTP request.
- *
- * @returns A request. Has a connection with a ID of -1 if reading the request failed.
- */
- auto read_incoming_request() noexcept -> HTTPRequest;
-
- auto close_connection(NetworkConnection &connection) noexcept -> bool;
-
- auto send_response(
- const NetworkConnection &connection,
- size_t status_code,
- const char **headers,
- size_t headers_cnt,
- const char *body
- ) noexcept -> bool;
-
-private:
- SoftwareSerial _serial;
-
- /**
- * Sends a command to the wifi module.
- *
- * @param command A command without the "AT+" in the beginning.
- *
- * @returns Whether or not it succeeded.
- */
- auto _send_serial(const char *command) noexcept -> bool;
-
- auto _read_connection_id() noexcept -> int8_t;
-
- auto _read_request_data_length() noexcept -> size_t;
-
- auto _read_request_method(size_t &request_data_length) noexcept -> HTTPRequestMethod;
-
- /**
- * Reads from the wifi module until it responds with a status.
- *
- * @param timeout Timeout in milliseconds.
- * @param response_out Response output buffer.
- *
- * @returns The response status.
- *
- */
- auto _read(uint64_t timeout, char *response_out) noexcept -> WiFiModuleResponseStatus;
-
- void
- _read_to(char *buffer_out, size_t length, char stop_char, uint64_t timeout) noexcept;
-
- void _read_bytes(char *buffer_out, size_t length, uint64_t timeout) noexcept;
-
- auto _read_byte() noexcept -> char;
-};
+#pragma once
+
+#include "http/request.hpp"
+#include "network_connection.hpp"
+
+#include <SoftwareSerial.h>
+#include <avr/pgmspace.h>
+#include <stddef.h>
+#include <stdint.h>
+
+constexpr auto MAX_NETWORK_MODULE_RESPONSE_LENGTH = 128U;
+
+constexpr auto REQUEST_DATA_LENGTH_BUF_SIZE = 16U;
+constexpr auto REQUEST_METHOD_STR_MAX_LENGTH = 10U;
+constexpr auto REQUEST_PATH_MAX_LENGTH = 64U;
+constexpr auto REQUEST_HTTP_VERSION_MAX_LENGTH = 10U;
+
+constexpr auto WIFI_CONNECT_COMMAND_BASE_LENGTH = 7U;
+constexpr auto CREATE_TCP_SERVER_COMMAND_BASE_LENGTH = 8U;
+constexpr auto CLOSE_CONNECTION_COMMAND_BASE_LENGTH = 51U;
+constexpr auto SEND_RESPONSE_COMMAND_BASE_LENGTH = 51U;
+
+constexpr auto ASCII_TO_CHAR = 48U;
+
+constexpr auto TIMEOUT_SHORT = 1500U;
+constexpr auto TIMEOUT_MEDIUM = 4000U;
+constexpr auto TIMEOUT_LONG = 10000U;
+
+const char RESPONSE_HTTP_VERSION[] PROGMEM = { "HTTP/1.1" };
+constexpr auto RESPONSE_STATUS_CODE_LENGTH = 3U;
+
+constexpr auto NUMBER_BASE = 10U;
+
+enum WifiMode
+{
+ Station = 1,
+ SoftAP = 2,
+ SoftAPAndStation = 3
+};
+
+enum WiFiModuleResponseStatus
+{
+ OK,
+ FAIL,
+ ERROR,
+ TIMEOUT
+};
+
+struct WiFiModuleOptions
+{
+ uint8_t receive_pin;
+ uint8_t transmit_pin;
+};
+
+class WiFiModule
+{
+public:
+ explicit WiFiModule(const WiFiModuleOptions &options) noexcept;
+
+ void begin(size_t baudrate) noexcept;
+
+ auto get_available() noexcept -> int;
+
+ void reset() noexcept;
+
+ /**
+ * Connects to a wifi network.
+ *
+ * @param ssid The service set identifier of a wifi network.
+ * @param password The wifi network password.
+ *
+ * @returns Whether or not it succeeded.
+ */
+ auto connect_to_wifi(const char *ssid, const char *password) noexcept -> bool;
+
+ void set_wifi_mode(WifiMode wifi_mode) noexcept;
+
+ void set_multiple_connections_enabled(bool is_enabled) noexcept;
+
+ void set_echo_enabled(bool is_enabled) noexcept;
+
+ void create_tcp_server(size_t port) noexcept;
+
+ /**
+ * Tests the connection to the wifi module.
+ *
+ * @returns Whether or not the test succeeded.
+ */
+ auto test() noexcept -> bool;
+
+ /**
+ * Gets local IP address of the wifi module.
+ *
+ * @param local_ip_out Local IP output buffer.
+ *
+ * @returns A pointer to the local IP output buffer.
+ */
+ auto get_local_ip(char *local_ip_out) noexcept -> const char *;
+
+ /**
+ * Gets the MAC address of the wifi module.
+ *
+ * @param mac_address_out MAC address output buffer.
+ *
+ * @returns A pointer to the MAC address output buffer.
+ */
+ auto get_mac_address(char *mac_address_out) noexcept -> const char *;
+
+ /**
+ * Reads a incoming HTTP request.
+ *
+ * @returns A request. Has a connection with a ID of -1 if reading the request failed.
+ */
+ auto read_incoming_request() noexcept -> HTTPRequest;
+
+ auto close_connection(NetworkConnection &connection) noexcept -> bool;
+
+ auto send_response(
+ const NetworkConnection &connection,
+ size_t status_code,
+ const char **headers,
+ size_t headers_cnt,
+ const char *body
+ ) noexcept -> bool;
+
+private:
+ SoftwareSerial _serial;
+
+ /**
+ * Sends a command to the wifi module.
+ *
+ * @param command A command without the "AT+" in the beginning.
+ *
+ * @returns Whether or not it succeeded.
+ */
+ auto _send_serial(const char *command) noexcept -> bool;
+
+ auto _read_connection_id() noexcept -> int8_t;
+
+ auto _read_request_data_length() noexcept -> size_t;
+
+ auto _read_request_method(size_t &request_data_length) noexcept -> HTTPRequestMethod;
+
+ /**
+ * Reads from the wifi module until it responds with a status.
+ *
+ * @param timeout Timeout in milliseconds.
+ * @param response_out Response output buffer.
+ *
+ * @returns The response status.
+ *
+ */
+ auto _read(uint64_t timeout, char *response_out) noexcept -> WiFiModuleResponseStatus;
+
+ void
+ _read_to(char *buffer_out, size_t length, char stop_char, uint64_t timeout) noexcept;
+
+ void _read_bytes(char *buffer_out, size_t length, uint64_t timeout) noexcept;
+
+ auto _read_byte() noexcept -> char;
+};