diff options
Diffstat (limited to 'minion/src/wifi_module.cpp')
-rw-r--r-- | minion/src/wifi_module.cpp | 1302 |
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());
+}
|