summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/gymnasiearbete.cpp69
-rw-r--r--src/util.cpp35
-rw-r--r--src/util.hpp27
-rw-r--r--src/wifi_module.cpp312
-rw-r--r--src/wifi_module.hpp93
5 files changed, 536 insertions, 0 deletions
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;
+};