diff options
Diffstat (limited to 'minion/src')
-rw-r--r-- | minion/src/util.cpp | 92 | ||||
-rw-r--r-- | minion/src/util.hpp | 98 | ||||
-rw-r--r-- | minion/src/wifi_module.cpp | 1302 | ||||
-rw-r--r-- | minion/src/wifi_module.hpp | 322 |
4 files changed, 907 insertions, 907 deletions
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;
+};
|