diff --git a/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.cpp b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..29feb8c907b49bf79d48081e135be854dd8a2488 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.cpp @@ -0,0 +1,159 @@ +// +// Created by zoe on 12/20/22. +// + +#include "ConnectionManager.h" +#include "LTEConnectionException.hpp" +#include "ModemFunctionalityLevel.h" + +#include <utility> + +ConnectionManager::ConnectionManager(TinyGsm &modem, GPRSCredentials credentials, ModemPins modemPins) + : modem(modem), credentials(std::move(credentials)), modemPins(modemPins) {} + +void ConnectionManager::restartModem() { + // Restart takes quite some time + // To skip it, call init() instead of restart() + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Restarting modem...\n"); + if (!modem.restart()) { + esp_log_write(ESP_LOG_WARN, TAG_GSM.c_str(), + "Failed to restart modem, attempting to continue without restarting\n"); + } +} + +void ConnectionManager::initModem() { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Initializing modem...\n"); + if (!modem.init()) { + esp_log_write(ESP_LOG_WARN, TAG_GSM.c_str(), "Failed to initialize modem, attempting to continue...\n"); + } + setModemFunctionalityLevel(MINIMAL); +} + +void ConnectionManager::setModemFunctionalityLevel( + ModemFunctionalityLevel functionalityLevel) { // This sets the level of functionality of the modem. Full + // functionality is where the highest + // level of power is drawn. Minimum functionality (default) is where the lowest level of power is drawn. + // https://m2msupport.net/m2msupport/atcfun-set-phone-functionality/ + modem.sendAT(("+CFUN=" + std::to_string(functionalityLevel) + " ").c_str()); + if (modem.waitResponse(10000L) != 1) { + DBG(" +CFUN=0 false "); + } +} + +void ConnectionManager::disableGPS() { + // Set SIM7000G GPIO4 LOW ,turn off GPS power + // CMD:AT+SGPIO=0,4,1,0 + // Only in version 20200415 is there a function to control GPS power + modem.sendAT("+SGPIO=0,4,1,0"); + if (modem.waitResponse(10000L) != 1) { + DBG(" SGPIO=0,4,1,0 false "); + } + modem.disableGPS(); +} + +void ConnectionManager::enableGPS() { + // Set SIM7000G GPIO4 LOW ,turn on GPS power + // CMD:AT+SGPIO=0,4,1,1 + // Only in version 20200415 is there a function to control GPS power + modem.sendAT("+SGPIO=0,4,1,1"); + if (modem.waitResponse(10000L) != 1) { + DBG(" SGPIO=0,4,1,1 false "); + } + modem.enableGPS(); +} + +void ConnectionManager::setNetworkMode(NetworkMode networkMode) { + + String res; + res = modem.setNetworkMode(networkMode); + if (res != "1") { + DBG("setNetworkMode false "); + throw LTEConnectionException("Failed to set network mode"); + } +} + +void ConnectionManager::setPreferredMode(Mode mode) { + String res; + res = modem.setPreferredMode(mode); + if (res != "1") { + DBG("setPreferredMode false "); + return; + } +} +void ConnectionManager::setBand(BandMode bandMode, int band) { + /*AT+CBANDCFG=<mode>,<band>[,<band>…] + * <mode> "CAT-M" "NB-IOT" + * <band> The value of <band> must is in the band list of getting from AT+CBANDCFG=? + * For example, my SIM card carrier "NB-iot" supports B8. I will configure +CBANDCFG= "Nb-iot ",8 + */ + + bandMode == BandMode::BAND_CAT_M ? modem.sendAT(("+CBANDCFG=\"CAT-M\"," + std::to_string(band) + " ").c_str()) + : modem.sendAT(("+CBANDCFG=\"NB-IOT\"," + std::to_string(band) + " ").c_str()); + + if (modem.waitResponse(10000L) != 1) { + DBG(" +CBANDCFG=\"NB-IOT\" "); + } +} +bool ConnectionManager::gprsConnect() { + return modem.gprsConnect(credentials.apn.c_str(), credentials.user.c_str(), credentials.password.c_str()); +} +bool ConnectionManager::isNetworkConnected() { + return modem.isNetworkConnected(); +} +std::string ConnectionManager::connect(int port, RequestInformation requestInformation) { + TinyGsmClient client{modem, 0}; + if (!client.connect(requestInformation.host.c_str(), port)) { + throw LTEConnectionException("Failed to connect to host"); + } + + String request = requestInformation.buildRequest(); + + client.print(request); + + String line; + // print response + while (client.connected()) { + line += client.readStringUntil('\n'); + if (line == "\r") { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "headers received\n"); + break; + } + } + client.stop(); + return line.c_str(); +} + +void ConnectionManager::modemPowerOn() { + pinMode(modemPins.pwr, OUTPUT); + digitalWrite(modemPins.pwr, LOW); + delay(1000); + digitalWrite(modemPins.pwr, HIGH); +} + +void ConnectionManager::modemPowerOff() { + modem.sendAT("+CPOWD=1"); + if (modem.waitResponse(10000L) != 1) { + esp_log_write(ESP_LOG_WARN, TAG_GSM.c_str(), "Failed to power off modem\n"); + } + modem.poweroff(); + pinMode(modemPins.pwr, OUTPUT); + digitalWrite(modemPins.pwr, LOW); + delay(1500); + digitalWrite(modemPins.pwr, HIGH); +} +void ConnectionManager::unlockSimCard() { + // Unlock your SIM card with a PIN if needed + if (modem.getSimStatus() != 3) { + modem.simUnlock(credentials.password.c_str()); + } +} +bool ConnectionManager::waitForNetwork() { + if (!modem.waitForNetwork(600000L, true)) { + delay(10000); + return false; + } + return true; +} +bool ConnectionManager::gprsDisconnect() { + return modem.gprsDisconnect(); +} diff --git a/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.h b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.h new file mode 100644 index 0000000000000000000000000000000000000000..4f774d0dc2738115cf535dd8edb219ed18d86da0 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.h @@ -0,0 +1,71 @@ +// +// Created by zoe on 12/20/22. +// + +#ifndef HOST_CENTRAL_MAST_CONNECTIONMANAGER_H +#define HOST_CENTRAL_MAST_CONNECTIONMANAGER_H + +#define TINY_GSM_MODEM_SIM7000 + +#include "GPRSCredentials.h" +#include "Mode.h" +#include "ModemFunctionalityLevel.h" +#include "NetworkMode.h" +#include "RequestInformation.h" +#include "StreamDebugger.h" +#include <TinyGsmClient.h> + +static const std::string TAG_GSM = "GSM"; + +class ConnectionManager { + + public: + struct ModemPins { + int dtr; + int tx; + int rx; + int pwr; + }; + + private: + TinyGsm &modem; + const GPRSCredentials credentials; + ModemPins modemPins; + + public: + ConnectionManager(TinyGsm &modem, GPRSCredentials credentials, ModemPins modemPins); + + std::string connect(int port, RequestInformation requestInformation); + + void enableGPS(); + + void disableGPS(); + + void initModem(); + + void restartModem(); + + void setNetworkMode(NetworkMode networkMode = NetworkMode::LTE_ONLY); + + void setPreferredMode(Mode mode = Mode::CAT_M); + + void setModemFunctionalityLevel(ModemFunctionalityLevel functionalityLevel = MINIMAL); + + enum BandMode { BAND_CAT_M, BAND_NB_IoT }; + + void setBand(BandMode bandMode, int band); + + bool gprsConnect(); + bool gprsDisconnect(); + + bool isNetworkConnected(); + + void modemPowerOn(); + + void modemPowerOff(); + + void unlockSimCard(); + bool waitForNetwork(); +}; + +#endif // HOST_CENTRAL_MAST_CONNECTIONMANAGER_H diff --git a/host/host_central_mast/lib/NetworkConnectionManager/GPRSCredentials.h b/host/host_central_mast/lib/NetworkConnectionManager/GPRSCredentials.h new file mode 100644 index 0000000000000000000000000000000000000000..1029c27f72637315ac1bda03204e224e4ab69b6b --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/GPRSCredentials.h @@ -0,0 +1,22 @@ +// +// Created by zoe on 12/20/22. +// + +#ifndef HOST_CENTRAL_MAST_GPRSCREDENTIALS_H +#define HOST_CENTRAL_MAST_GPRSCREDENTIALS_H + +#include <string> +#include <utility> + +struct GPRSCredentials { + std::string apn; + std::string user; + std::string password; + + GPRSCredentials(std::string apn, std::string user, std::string password) { + this->apn = std::move(apn); + this->user = std::move(user); + this->password = std::move(password); + } +}; +#endif //HOST_CENTRAL_MAST_GPRSCREDENTIALS_H diff --git a/host/host_central_mast/lib/NetworkConnectionManager/LTEConnectionException.hpp b/host/host_central_mast/lib/NetworkConnectionManager/LTEConnectionException.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1c9067bdbab1a9721b1d41df2b0bbb3aef47bd66 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/LTEConnectionException.hpp @@ -0,0 +1,23 @@ +// +// Created by zoe on 12/20/22. +// + +#ifndef HOST_CENTRAL_MAST_LTECONNECTIONEXCEPTION_HPP +#define HOST_CENTRAL_MAST_LTECONNECTIONEXCEPTION_HPP + +#include <exception> +#include <string> + +class LTEConnectionException : public std::exception { +public: + explicit LTEConnectionException(const std::string &message) : message(message) {} + + const char *what() const noexcept override { + return message.c_str(); + } + +private: + std::string message; +}; + +#endif //HOST_CENTRAL_MAST_LTECONNECTIONEXCEPTION_HPP diff --git a/host/host_central_mast/lib/NetworkConnectionManager/Mode.h b/host/host_central_mast/lib/NetworkConnectionManager/Mode.h new file mode 100644 index 0000000000000000000000000000000000000000..bb248b7920ad62aca446830742948f6fe8de2a95 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/Mode.h @@ -0,0 +1,14 @@ +// +// Created by zoe on 12/20/22. +// + +#ifndef HOST_CENTRAL_MAST_MODE_H +#define HOST_CENTRAL_MAST_MODE_H + +enum Mode { + CAT_M = 1, + NB_IOT = 2, + CAT_M_AND_NB_IOT = 3 +}; + +#endif //HOST_CENTRAL_MAST_MODE_H diff --git a/host/host_central_mast/lib/NetworkConnectionManager/ModemFunctionalityLevel.h b/host/host_central_mast/lib/NetworkConnectionManager/ModemFunctionalityLevel.h new file mode 100644 index 0000000000000000000000000000000000000000..35a458033a388af7a8d7fe3a37649b5ffbc458e7 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/ModemFunctionalityLevel.h @@ -0,0 +1,14 @@ +// +// Created by zoe on 12/20/22. +// + +#ifndef HOST_CENTRAL_MAST_MODEMFUNCTIONALITYLEVEL_H +#define HOST_CENTRAL_MAST_MODEMFUNCTIONALITYLEVEL_H + +enum ModemFunctionalityLevel { + MINIMAL = 0, + FULL = 1, + DISABLE = 4, +}; + +#endif //HOST_CENTRAL_MAST_MODEMFUNCTIONALITYLEVEL_H diff --git a/host/host_central_mast/lib/NetworkConnectionManager/NetworkMode.h b/host/host_central_mast/lib/NetworkConnectionManager/NetworkMode.h new file mode 100644 index 0000000000000000000000000000000000000000..daafc19e6b94bcb6d6a818cb905606f559a23e04 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/NetworkMode.h @@ -0,0 +1,15 @@ +// +// Created by zoe on 12/20/22. +// + +#ifndef HOST_CENTRAL_MAST_NETWORKMODE_H +#define HOST_CENTRAL_MAST_NETWORKMODE_H + +enum NetworkMode { + AUTOMATIC = 2, + GSM_ONLY = 13, + LTE_ONLY = 38, + GSM_AND_LTE_ONLY = 51 +}; + +#endif //HOST_CENTRAL_MAST_NETWORKMODE_H diff --git a/host/host_central_mast/lib/NetworkConnectionManager/RequestInformation.h b/host/host_central_mast/lib/NetworkConnectionManager/RequestInformation.h new file mode 100644 index 0000000000000000000000000000000000000000..84843cfb43fc8feffa7381d381f0cde7976d40b4 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/RequestInformation.h @@ -0,0 +1,43 @@ +// +// Created by zoe on 1/9/23. +// + +#ifndef HOST_CENTRAL_MAST_REQUESTINFORMATION_H +#define HOST_CENTRAL_MAST_REQUESTINFORMATION_H + +#include <Arduino.h> + +struct RequestInformation { + String method; + String host; + String path; + String body; + + RequestInformation(String method, String host, String path, String body) { + this->method = method; + this->host = host; + this->path = path; + this->body = body; + } + + // TODO: Move to configuration file + const String INFLUXDB_TOKEN = + "dUh2gbVLv7e3egqocxriDsJQNUacA9qZ5YXsYtdnVAglnHgy4nx-jDVO7nGlSF34BosfnuwnUDaviC7dQeC5RQ=="; + + String buildRequest() { + String request = ""; + request += method + " " + path + " HTTP/1.1\r\n"; + request += "Host: " + host + "\r\n"; + request += "Authorization: Token " + INFLUXDB_TOKEN + "\r\n"; + request += "User-Agent: ESP32\r\n"; + request += "Content-Type: text/plain\r\n"; + request += "Content-Length: " + String(body.length()) + "\r\n"; + if (body.length() > 0) { + request += "\r\n"; + request += body; + } + return request; + } +}; + +#endif // HOST_CENTRAL_MAST_REQUESTINFORMATION_H diff --git a/host/host_central_mast/lib/TimeManager/NTPException.hpp b/host/host_central_mast/lib/TimeManager/NTPException.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c64c55c49ffa888f61a0364aeb5d1868eb6fcceb --- /dev/null +++ b/host/host_central_mast/lib/TimeManager/NTPException.hpp @@ -0,0 +1,23 @@ +// +// Created by zoe on 12/19/22. +// + +#ifndef HOST_CENTRAL_MAST_NTPEXCEPTION_HPP +#define HOST_CENTRAL_MAST_NTPEXCEPTION_HPP + +#include <exception> +#include <string> + +class NTPException : public std::exception { +public: + explicit NTPException(const std::string &message) : message(message) {} + + const char *what() const noexcept override { + return message.c_str(); + } + +private: + std::string message; +}; + +#endif //HOST_CENTRAL_MAST_NTPEXCEPTION_HPP diff --git a/host/host_central_mast/lib/TimeManager/StringToTimeConversionException.hpp b/host/host_central_mast/lib/TimeManager/StringToTimeConversionException.hpp new file mode 100644 index 0000000000000000000000000000000000000000..32e63f4de431138be07c1746bccce54c3f0a8757 --- /dev/null +++ b/host/host_central_mast/lib/TimeManager/StringToTimeConversionException.hpp @@ -0,0 +1,26 @@ +// +// Created by zoe on 12/19/22. +// + +#ifndef HOST_CENTRAL_MAST_STRINGTOTIMECONVERSIONEXCEPTION_HPP +#define HOST_CENTRAL_MAST_STRINGTOTIMECONVERSIONEXCEPTION_HPP + +#include <exception> +#include <string> +#include "TimeManager.h" + +class StringToTimeConversionException : public std::exception { +public: + explicit StringToTimeConversionException(const std::string &message) : message(message) {} + + const char *what() const noexcept override { + esp_log_write(ESP_LOG_ERROR, "TimeManager", "Error converting time to epoch: %s", + message.c_str()); + return message.c_str(); + } + +private: + std::string message; +}; + +#endif //HOST_CENTRAL_MAST_STRINGTOTIMECONVERSIONEXCEPTION_HPP diff --git a/host/host_central_mast/lib/TimeManager/TimeManager.cpp b/host/host_central_mast/lib/TimeManager/TimeManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4d5c5b5fdf75b8616b4dcb56a65c63f4689ffc59 --- /dev/null +++ b/host/host_central_mast/lib/TimeManager/TimeManager.cpp @@ -0,0 +1,87 @@ +// +// Created by zoe on 12/19/22. +// + +#include "TimeManager.h" + +TimeManager::TimeManager(TinyGsmSim7000 &modem) : modem(modem) {} + +void TimeManager::syncNTP(const std::string &ntpServer, const std::string &backupNtpServer) { + // create list of ntp servers + std::vector<std::string> ntpServers; + ntpServers.push_back(ntpServer); + ntpServers.push_back(backupNtpServer); + tryNtpServerSync(backupNtpServer, ntpServers); +} +void TimeManager::tryNtpServerSync(const std::string &backupNtpServer, + std::vector<std::string> &ntpServers) { // try to sync ntp with each server + for (const auto &server : ntpServers) { + try { + synchronizeNTPWithModem(server); + return; + } catch (NTPException &e) { + if (server == backupNtpServer) { + esp_log_write(ESP_LOG_ERROR, TAG, "Failed to sync NTP with %s (%s)\n", server.c_str(), e.what()); + } + esp_log_write(ESP_LOG_ERROR, TAG, "Failed to sync NTP with %s (%s). Retrying with backup...\n", + server.c_str(), e.what()); + } + } +} +void TimeManager::synchronizeNTPWithModem(const std::string &ntpServer) { + esp_log_write(ESP_LOG_DEBUG, TAG, "NTP Server Syncing...\n"); + + long error = modem.NTPServerSync(ntpServer.c_str(), timeZone); + /** + According to TinGsmNTP.tpp, the error codes are: + case 1: "Network time synchronization is successful"; + case 61: "Network error"; + case 62: "DNS resolution error"; + case 63: "Connection error"; + case 64: "Service response error"; + case 65: "Service response timeout"; + default: "Unknown error: " + String(error); + */ + // 255 is ok in our case (https://github.com/vshymanskyy/TinyGSM/pull/652) + if (error != 1 && error != 255) { + throw NTPException(modem.ShowNTPError(error).c_str()); + } else { + esp_log_write(ESP_LOG_DEBUG, TAG, "NTP: Network time synchronization is successful\n"); + } +} + +time_t TimeManager::timeToUnixEpochSeconds(const std::string &time, const std::string &format) { + // 22/10/27,10:16:20+00 + struct tm tm {}; + time_t dateInEpoch = 0; + + // For some unknown reason, we have to use the C functions here instead of the std:: ones. + if (strptime(time.c_str(), format.c_str(), &tm)) { + time_t curTime; + struct tm *timeinfo; + + timeinfo = localtime(&curTime); + + timeinfo->tm_year = tm.tm_year; + timeinfo->tm_mon = tm.tm_mon; + timeinfo->tm_mday = tm.tm_mday; + timeinfo->tm_hour = tm.tm_hour; + timeinfo->tm_min = tm.tm_min; + timeinfo->tm_sec = tm.tm_sec; + timeinfo->tm_isdst = -1; + + dateInEpoch = mktime(timeinfo); + } else { + throw StringToTimeConversionException("Failed to parse date"); + } + return dateInEpoch; +} + +void TimeManager::writeModemTimeToRTC() { + auto gsmDateTimeString = modem.getGSMDateTime(DATE_FULL); + esp_log_write(ESP_LOG_DEBUG, TAG, "GSM DateTime: %s\n", gsmDateTimeString.c_str()); + time_t time = timeToUnixEpochSeconds(gsmDateTimeString.c_str()); + + rtc.setTime(time); + esp_log_write(ESP_LOG_INFO, TAG, "Time set to EPOCH: %s\n", String(rtc.getEpoch()).c_str()); +} diff --git a/host/host_central_mast/lib/TimeManager/TimeManager.h b/host/host_central_mast/lib/TimeManager/TimeManager.h new file mode 100644 index 0000000000000000000000000000000000000000..d970a292d388ae4b6cc0bce139d0c9196b3bd147 --- /dev/null +++ b/host/host_central_mast/lib/TimeManager/TimeManager.h @@ -0,0 +1,43 @@ +// +// Created by zoe on 12/19/22. +// + +#ifndef HOST_CENTRAL_MAST_TIMEMANAGER_H +#define HOST_CENTRAL_MAST_TIMEMANAGER_H + +#define TINY_GSM_MODEM_SIM7000 + +#include "ESP32Time.h" +#include "NTPException.hpp" +#include "StringToTimeConversionException.hpp" +#include <TinyGsmClientSIM7000.h> +#include <iomanip> +#include <string> + +class TimeManager { + public: + explicit TimeManager(TinyGsmSim7000 &modem); + + private: + constexpr static char *TAG = "TimeManager"; + ESP32Time rtc; + int timeZone = 0; + + // I would like this to be a const reference but the functions used by the modem are not const + TinyGsmSim7000 &modem; + + // convert time to unix epoch seconds + static time_t timeToUnixEpochSeconds(const std::string &time, const std::string &format = "%y/%m/%d,%T+00"); + + void synchronizeNTPWithModem(const std::string &ntpServer); + void tryNtpServerSync(const std::string &backupNtpServer, std::vector<std::string> &ntpServers); + + public: + // sync ntp + void syncNTP(const std::string &ntpServer, const std::string &backupNtpServer); + + // write modem time to rtc + void writeModemTimeToRTC(); +}; + +#endif // HOST_CENTRAL_MAST_TIMEMANAGER_H \ No newline at end of file diff --git a/host/host_central_mast/platformio.ini b/host/host_central_mast/platformio.ini index 1e0fb845c1ef545a0bf8d0d351697508405dd20b..f935fa80544f7810f4d3d5e7d9712f08d8551bde 100644 --- a/host/host_central_mast/platformio.ini +++ b/host/host_central_mast/platformio.ini @@ -13,6 +13,7 @@ platform = espressif32 board = esp-wrover-kit framework = arduino monitor_speed = 115200 +lib_ldf_mode = deep monitor_port = /dev/ttyACM0 upload_port = /dev/ttyACM0 build_flags =