diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | src/gymnasiearbete.cpp | 69 | ||||
| -rw-r--r-- | src/util.cpp | 35 | ||||
| -rw-r--r-- | src/util.hpp | 27 | ||||
| -rw-r--r-- | src/wifi_module.cpp | 312 | ||||
| -rw-r--r-- | src/wifi_module.hpp | 93 | 
6 files changed, 537 insertions, 0 deletions
@@ -1,3 +1,4 @@  build  .vscode  .pio +src/secrets.hpp diff --git a/src/gymnasiearbete.cpp b/src/gymnasiearbete.cpp new file mode 100644 index 0000000..0e1adb5 --- /dev/null +++ b/src/gymnasiearbete.cpp @@ -0,0 +1,69 @@ +#include "secrets.hpp" +#include "wifi_module.hpp" + +#include <Arduino.h> +#include <SoftwareSerial.h> +#include <string.h> + +constexpr auto BAUDRATE = 9600U; + +constexpr auto NETWORK_MODULE_RX_PIN = 3; +constexpr auto NETWORK_MODULE_TX_PIN = 2; + +auto wifi_module = WiFiModule(NETWORK_MODULE_RX_PIN, NETWORK_MODULE_TX_PIN); + +void setup() +{ +	Serial.begin(BAUDRATE); + +	pinMode(NETWORK_MODULE_RX_PIN, INPUT); +	pinMode(NETWORK_MODULE_TX_PIN, OUTPUT); + +	wifi_module.begin(BAUDRATE); + +	wifi_module.reset(); + +	delay(1000); + +	auto wifi_module_connected = wifi_module.test(); + +	Serial.print("Wifi module connected: "); +	Serial.println(wifi_module_connected ? "Yes" : "No"); + +	if (!wifi_module_connected) +	{ +		while (true) +		{ +		} +	} + +	wifi_module.set_echo_enabled(false); + +	delay(1000); + +	const auto wifi_connect_success = wifi_module.connect(WIFI_SSID, WIFI_PASSWORD); + +	if (wifi_connect_success) +	{ +		Serial.print("Connected to wifi network '"); +		Serial.print(WIFI_SSID); +		Serial.println("' successfully"); +	} +	else +	{ +		Serial.print("Failed to connect to wifi network '"); +		Serial.print(WIFI_SSID); +		Serial.println("'"); +	} + +	wifi_module.set_wifi_mode(WifiMode::Station); + +	delay(3000); + +	char local_ip[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; + +	Serial.print("IP address: "); +	Serial.println(wifi_module.get_local_ip(local_ip)); +} + +void loop() {} diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..f587781 --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,35 @@ +#include "util.hpp" + +namespace util +{ + +bool str_ends_with(const char *str, const char *other_str) noexcept +{ +	if (str == nullptr || other_str == nullptr) +	{ +		return false; +	} + +	auto str_length = strlen(str); +	auto other_str_length = strlen(other_str); + +	if (other_str_length > str_length) +	{ +		return false; +	} + +	return strncmp(str + str_length - other_str_length, other_str, 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 + 1 != end; ++char_ptr) +	{ +		*dest_head = *char_ptr; +		dest_head++; +	} +} + +} // namespace util diff --git a/src/util.hpp b/src/util.hpp new file mode 100644 index 0000000..d6f7f1c --- /dev/null +++ b/src/util.hpp @@ -0,0 +1,27 @@ +#pragma once + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +namespace util +{ + +template <typename Type> +Type *malloc(size_t size) noexcept +{ +	return static_cast<Type *>(::malloc(size)); +} + +bool str_ends_with(const char *str, const char *other_str) noexcept; + +/** + * 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; + +} // namespace util diff --git a/src/wifi_module.cpp b/src/wifi_module.cpp new file mode 100644 index 0000000..2e25dc9 --- /dev/null +++ b/src/wifi_module.cpp @@ -0,0 +1,312 @@ +#include "wifi_module.hpp" + +#include "util.hpp" + +#include <Arduino.h> +#include <string.h> + +WiFiModule::WiFiModule(uint8_t receive_pin, uint8_t transmit_pin) noexcept +	: _serial(receive_pin, transmit_pin) +{ +} + +void WiFiModule::begin(size_t baudrate) noexcept +{ +	_serial.begin(baudrate); +} + +int WiFiModule::get_available() noexcept +{ +	return _serial.available(); +} + +void WiFiModule::reset() noexcept +{ +	_send_serial("AT+RST"); + +	char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; + +	const auto response_status = _read(2000U, response); + +	Serial.println( +		response_status == ResponseStatus::OK ? "Reset wifi module" +											  : "Failed to reset wifi module" +	); +} + +bool WiFiModule::connect(const char *ssid, const char *password) noexcept +{ +	const char cmd[] = "AT+CWJAP"; + +	auto command_length = strlen(cmd) + strlen(ssid) + strlen(password) + 6U + 1U; + +	auto *command = util::malloc<char>(command_length); + +	if (command == nullptr) +	{ +		Serial.println("Memory allocation failed for creating full 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("Failed to send connection command"); +		return false; +	} + +	free(command); + +	char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; + +	const auto response_status = _read(20000U, response); + +	if (response_status != ResponseStatus::OK) +	{ +		Serial.print("Connection command responded with status "); +		Serial.println(response_status); +		return false; +	} + +	return true; +} + +void WiFiModule::set_wifi_mode(WifiMode wifi_mode) noexcept +{ +	const char cmd[] = "AT+CWMODE_CUR"; + +	auto command_length = strlen(cmd) + 2U + 1U; + +	auto *command = util::malloc<char>(command_length); + +	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(1500U, 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); + +	snprintf(command, command_length, "%s=%u", cmd, is_enabled ? 1U : 0U); + +	_send_serial(command); + +	free(command); + +	char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; + +	_read(1500U, response); + +	// Serial.println(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); + +	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); + +	if (response_status == ResponseStatus::OK) +	{ +		Serial.print("Turned "); +		Serial.print(is_enabled ? "on" : "off"); +		Serial.println(" AT commands echo"); +	} +	else +	{ +		Serial.print("Failed to turn "); +		Serial.print(is_enabled ? "on" : "off"); +		Serial.println(" AT commands echo"); +	} +} + +void WiFiModule::create_tcp_server(size_t port) noexcept +{ +	const auto cmd = "AT+CIPSERVER"; + +	auto command_length = strlen(cmd) + 7U + 1U; + +	auto *command = util::malloc<char>(command_length); + +	snprintf(command, command_length, "%s=1,%u", cmd, port); + +	_send_serial(command); + +	free(command); + +	char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; + +	_read(1500U, response); + +	// Serial.println(response); +} + +bool WiFiModule::test() noexcept +{ +	const auto send_success = _send_serial("AT"); + +	if (!send_success) +	{ +		return false; +	} + +	char response[MAX_NETWORK_MODULE_RESPONSE_LENGTH] = ""; + +	const auto response_status = _read(5000U, response); + +	if (response_status == ResponseStatus::TIMEOUT) +	{ +		return false; +	} + +	return strcmp(response, "") != 0; +} + +const char *WiFiModule::get_local_ip(char *local_ip_out) noexcept +{ +	const auto command = "AT+CIFSR"; + +	const auto send_success = _send_serial(command); + +	if (!send_success) +	{ +		return local_ip_out; +	} + +	auto *buf = util::malloc<char>(strlen(local_ip_out)); + +	if (buf == nullptr) +	{ +		strcpy(local_ip_out, "Memory allocation failure"); +		return local_ip_out; +	} + +	const auto response_status = _read(10000U, buf); + +	if (response_status != ResponseStatus::OK) +	{ +		free(buf); + +		sprintf( +			local_ip_out, +			"Response status was not OK. Was %d", +			static_cast<int>(response_status) +		); + +		return local_ip_out; +	} + +	const 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; +} + +bool WiFiModule::_send_serial(const char *command) noexcept +{ +	auto full_command_length = strlen(command) + 2U + 1U; + +	auto *full_command = util::malloc<char>(full_command_length); + +	if (full_command == nullptr) +	{ +		return false; +	} + +	snprintf(full_command, full_command_length, "%s\r\n", command); + +	_serial.print(full_command); + +	// Serial.print("Sent command: "); +	// Serial.println(full_command); + +	free(full_command); + +	return true; +} + +ResponseStatus WiFiModule::_read(uint64_t timeout, char *response_out) noexcept +{ +	const auto start_time = millis(); + +	bool has_end = false; +	auto status = ResponseStatus::TIMEOUT; + +	// auto *response_out_head = response_out; + +	while (!has_end && (start_time + timeout) > millis()) +	{ +		while (_serial.available() != 0) +		{ +			char character = _read_byte(); + +			strncat(response_out, &character, 1U); + +			// *response_out_head = character; +			// response_out_head++; + +			if (util::str_ends_with(response_out, "OK")) +			{ +				status = ResponseStatus::OK; +				has_end = true; +			} + +			if (util::str_ends_with(response_out, "ERROR")) +			{ +				status = ResponseStatus::ERROR; +				has_end = true; +			} + +			if (util::str_ends_with(response_out, "FAIL")) +			{ +				status = ResponseStatus::FAIL; +				has_end = true; +			} +		} +	} + +	return status; +} + +char WiFiModule::_read_byte() noexcept +{ +	return static_cast<char>(_serial.read()); +} diff --git a/src/wifi_module.hpp b/src/wifi_module.hpp new file mode 100644 index 0000000..5a4148c --- /dev/null +++ b/src/wifi_module.hpp @@ -0,0 +1,93 @@ +#pragma once + +#include <SoftwareSerial.h> +#include <stddef.h> +#include <stdint.h> + +constexpr auto MAX_NETWORK_MODULE_RESPONSE_LENGTH = 128U; + +enum WifiMode +{ +	Station = 1, +	SoftAP = 2, +	SoftAPAndStation = 3 +}; + +enum ResponseStatus +{ +	OK, +	FAIL, +	ERROR, +	TIMEOUT +}; + +class WiFiModule +{ +public: +	WiFiModule(uint8_t receive_pin, uint8_t transmit_pin) noexcept; + +	void begin(size_t baudrate) noexcept; + +	int get_available() noexcept; + +	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. +	 */ +	bool connect(const char *ssid, const char *password) noexcept; + +	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. +	 */ +	bool test() noexcept; + +	/** +	 * 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. +	 */ +	const char *get_local_ip(char *local_ip_out) noexcept; + +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. +	 */ +	bool _send_serial(const char *command) noexcept; + +	/** +	 * 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. +	 * +	 */ +	ResponseStatus _read(uint64_t timeout, char *response_out) noexcept; + +	char _read_byte() noexcept; +};  | 
