summaryrefslogtreecommitdiff
path: root/minion/src/wifi_module.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'minion/src/wifi_module.cpp')
-rw-r--r--minion/src/wifi_module.cpp1302
1 files changed, 651 insertions, 651 deletions
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());
+}