From e4b7b5ba2e1371b142167e75a3ddcd63a0e00a48 Mon Sep 17 00:00:00 2001 From: HampusM Date: Tue, 31 May 2022 11:39:04 +0200 Subject: style(minion): change eol to LF --- minion/.gitignore | 5 + minion/platformio.ini | 36 +- minion/scripts/override_compiledb_path.py | 14 +- minion/src/util.cpp | 92 +- minion/src/util.hpp | 98 +-- minion/src/wifi_module.cpp | 1302 ++++++++++++++--------------- minion/src/wifi_module.hpp | 322 +++---- 7 files changed, 937 insertions(+), 932 deletions(-) create mode 100644 minion/.gitignore diff --git a/minion/.gitignore b/minion/.gitignore new file mode 100644 index 0000000..89cc49c --- /dev/null +++ b/minion/.gitignore @@ -0,0 +1,5 @@ +.pio +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch diff --git a/minion/platformio.ini b/minion/platformio.ini index 2abc5cf..62d5c1d 100644 --- a/minion/platformio.ini +++ b/minion/platformio.ini @@ -1,18 +1,18 @@ -[platformio] -build_dir = build - -[env:uno] -platform = atmelavr -board = uno -framework = arduino -build_src_flags = - -Wextra - -Wpedantic - -Wshadow - -Wcast-align - -Wunused - -Wold-style-cast - -Wconversion - -pedantic - -felide-constructors -extra_scripts = post:scripts/override_compiledb_path.py +[platformio] +build_dir = build + +[env:uno] +platform = atmelavr +board = uno +framework = arduino +build_src_flags = + -Wextra + -Wpedantic + -Wshadow + -Wcast-align + -Wunused + -Wold-style-cast + -Wconversion + -pedantic + -felide-constructors +extra_scripts = post:scripts/override_compiledb_path.py diff --git a/minion/scripts/override_compiledb_path.py b/minion/scripts/override_compiledb_path.py index 38f52e9..3048b3e 100644 --- a/minion/scripts/override_compiledb_path.py +++ b/minion/scripts/override_compiledb_path.py @@ -1,7 +1,7 @@ -import os - -Import("env") - -path = os.path.join("$BUILD_DIR", "compile_commands.json") - -env.Replace(COMPILATIONDB_PATH=path) +import os + +Import("env") + +path = os.path.join("$BUILD_DIR", "compile_commands.json") + +env.Replace(COMPILATIONDB_PATH=path) diff --git a/minion/src/util.cpp b/minion/src/util.cpp index 708e47d..b55a6e8 100644 --- a/minion/src/util.cpp +++ b/minion/src/util.cpp @@ -1,46 +1,46 @@ -#include "util.hpp" - -namespace util -{ - -auto str_ends_with(const char *target, size_t target_end, const char *other) noexcept - -> bool -{ - if (target == nullptr || other == nullptr) - { - return false; - } - - const auto other_str_length = strlen(other); - - if (other_str_length > target_end) - { - return false; - } - - return strncmp(target + target_end - other_str_length, other, other_str_length) == 0; -} - -void substr(const char *str, const char *end, char *dest) noexcept -{ - auto *dest_head = dest; - - for (const char *char_ptr = str; char_ptr != end; ++char_ptr) - { - *dest_head = *char_ptr; - dest_head++; - } -} - -auto streq(const char *str_one, const char *str_two) noexcept -> bool -{ - return strcmp(str_one, str_two) == 0; -} - -void quit() noexcept -{ - while (true) - ; -} - -} // namespace util +#include "util.hpp" + +namespace util +{ + +auto str_ends_with(const char *target, size_t target_end, const char *other) noexcept + -> bool +{ + if (target == nullptr || other == nullptr) + { + return false; + } + + const auto other_str_length = strlen(other); + + if (other_str_length > target_end) + { + return false; + } + + return strncmp(target + target_end - other_str_length, other, other_str_length) == 0; +} + +void substr(const char *str, const char *end, char *dest) noexcept +{ + auto *dest_head = dest; + + for (const char *char_ptr = str; char_ptr != end; ++char_ptr) + { + *dest_head = *char_ptr; + dest_head++; + } +} + +auto streq(const char *str_one, const char *str_two) noexcept -> bool +{ + return strcmp(str_one, str_two) == 0; +} + +void quit() noexcept +{ + while (true) + ; +} + +} // namespace util diff --git a/minion/src/util.hpp b/minion/src/util.hpp index 2955cf1..a36dc5b 100644 --- a/minion/src/util.hpp +++ b/minion/src/util.hpp @@ -1,49 +1,49 @@ -#pragma once - -#include -#include -#include - -namespace util -{ - -template -auto malloc(size_t size) noexcept -> Type * -{ - return static_cast(::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 +#include +#include + +namespace util +{ + +template +auto malloc(size_t size) noexcept -> Type * +{ + return static_cast(::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 -#include - -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(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(command_length); - - if (command == nullptr) - { - return; - } - - snprintf(command, command_length, "%s=%u", cmd, static_cast(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(_serial.read()); -} +#include "wifi_module.hpp" + +#include "network_connection.hpp" +#include "util.hpp" + +#include +#include + +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(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(command_length); + + if (command == nullptr) + { + return; + } + + snprintf(command, command_length, "%s=%u", cmd, static_cast(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(_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 -#include -#include -#include - -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 +#include +#include +#include + +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; +}; -- cgit v1.2.3-18-g5258