diff --git a/.clang-format b/.clang-format index 673f71de96f7438eaa3691f49126bd05c6a52af9..7abb256cdd1d5fc9f2a79f19dfa70d6f55c6fe63 100644 --- a/.clang-format +++ b/.clang-format @@ -3,12 +3,13 @@ BasedOnStyle: LLVM ColumnLimit: 120 IndentWidth: 4 TabWidth: 4 -UseTab: ForIndentation +#UseTab: ForIndentation AlignEscapedNewlines: DontAlign AllowShortFunctionsOnASingleLine: Inline AlwaysBreakTemplateDeclarations: Yes BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom +NamespaceIndentation: All BraceWrapping: - AfterFunction: true + AfterFunction: false ... diff --git a/host/host_central_mast/include/MessageType.h b/host/host_central_mast/include/MessageType.h new file mode 100644 index 0000000000000000000000000000000000000000..787afad32ef6fba58a5e7519ca06dff97b827988 --- /dev/null +++ b/host/host_central_mast/include/MessageType.h @@ -0,0 +1,16 @@ +// +// Created by zoe on 1/24/23. +// + +#ifndef HOST_CENTRAL_MAST_MESSAGETYPE_H +#define HOST_CENTRAL_MAST_MESSAGETYPE_H + +enum MessageType { dataAck, hostChange }; + +typedef struct response { + MessageType type; + uint8_t mac[6]; + long time; // Clang-Tidy: Narrowing conversion from 'unsigned long' to signed type 'long' is implementation-defined + // (see rtc in main.cpp) +} response; +#endif // HOST_CENTRAL_MAST_MESSAGETYPE_H diff --git a/host/host_central_mast/lib/NTPManager/NTPException.hpp b/host/host_central_mast/lib/NTPManager/NTPException.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c64c55c49ffa888f61a0364aeb5d1868eb6fcceb --- /dev/null +++ b/host/host_central_mast/lib/NTPManager/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/NTPManager/NTPManager.cpp b/host/host_central_mast/lib/NTPManager/NTPManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f99ff9e7c17706d16cb9ab1a33debc07f283df1d --- /dev/null +++ b/host/host_central_mast/lib/NTPManager/NTPManager.cpp @@ -0,0 +1,88 @@ +// +// Created by zoe on 12/19/22. +// + +#include "NTPManager.h" + +NTPManager::NTPManager(TinyGsmSim7000 &modem) : modem(modem) {} + +void NTPManager::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 NTPManager::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_TIMEMANAGER, "Failed to sync NTP with %s (%s)\n", server.c_str(), + e.what()); + } + esp_log_write(ESP_LOG_ERROR, TAG_TIMEMANAGER, "Failed to sync NTP with %s (%s). Retrying with backup...\n", + server.c_str(), e.what()); + } + } +} +void NTPManager::synchronizeNTPWithModem(const std::string &ntpServer) { + esp_log_write(ESP_LOG_DEBUG, TAG_TIMEMANAGER, "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_TIMEMANAGER, "NTP: Network time synchronization is successful\n"); + } +} + +time_t NTPManager::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 NTPManager::writeModemTimeToRTC() { + auto gsmDateTimeString = modem.getGSMDateTime(DATE_FULL); + esp_log_write(ESP_LOG_DEBUG, TAG_TIMEMANAGER, "GSM DateTime: %s\n", gsmDateTimeString.c_str()); + time_t time = timeToUnixEpochSeconds(gsmDateTimeString.c_str()); + + rtc.setTime(time); + esp_log_write(ESP_LOG_INFO, TAG_TIMEMANAGER, "Time set to EPOCH: %s\n", String(rtc.getEpoch()).c_str()); +} diff --git a/host/host_central_mast/lib/NTPManager/NTPManager.h b/host/host_central_mast/lib/NTPManager/NTPManager.h new file mode 100644 index 0000000000000000000000000000000000000000..37a2a192bd13c63fec4e1627e962dca6d07067ce --- /dev/null +++ b/host/host_central_mast/lib/NTPManager/NTPManager.h @@ -0,0 +1,42 @@ +// +// Created by zoe on 12/19/22. +// + +#ifndef HOST_CENTRAL_MAST_NTPMANAGER_H +#define HOST_CENTRAL_MAST_NTPMANAGER_H + +#define TINY_GSM_MODEM_SIM7000 + +#include "Definitions.h" +#include "ESP32Time.h" +#include "NTPException.hpp" +#include "StringToTimeConversionException.hpp" +#include <TinyGsmClientSIM7000.h> +#include <iomanip> +#include <string> + +class NTPManager { + public: + explicit NTPManager(TinyGsmSim7000 &modem); + + private: + 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_NTPMANAGER_H \ No newline at end of file diff --git a/host/host_central_mast/lib/NTPManager/StringToTimeConversionException.hpp b/host/host_central_mast/lib/NTPManager/StringToTimeConversionException.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f93acc337a2333fae8e3d8c493f7cc2b069d0a5c --- /dev/null +++ b/host/host_central_mast/lib/NTPManager/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 "NTPManager.h" +#include <exception> +#include <string> + +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, "NTPManager", "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/NetworkConnectionManager/ConnectionManager.cpp b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bdadaf9ee807376bef8b8450d30e54380bb3a49d --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.cpp @@ -0,0 +1,181 @@ +// +// Created by zoe on 12/20/22. +// + +#include "ConnectionManager.h" + +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, "Restarting modem...\n"); + if (!modem.restart()) { + esp_log_write(ESP_LOG_WARN, TAG_GSM, "Failed to restart modem, attempting to continue without restarting\n"); + } +} + +void ConnectionManager::initModem() { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Initializing modem...\n"); + if (!modem.init()) { + esp_log_write(ESP_LOG_WARN, TAG_GSM, "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(RequestInformation requestInformation) { + TinyGsmClient client{modem, 0}; + if (!client.connect(requestInformation.host.c_str(), requestInformation.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, "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() { + // Try to power-off (modem may decide to restart automatically) + // To turn off modem completely, please use Reset/Enable pins + modem.sendAT("+CPOWD=1"); + if (modem.waitResponse(10000L) != 1) { + esp_log_write(ESP_LOG_WARN, TAG_GSM, "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(); +} +void ConnectionManager::logModemInformation() { + bool res = modem.isGprsConnected(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "GPRS connected: %s\n", res ? "true" : "false"); + + String ccid = modem.getSimCCID(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "CCID: %s\n", ccid.c_str()); + + String imei = modem.getIMEI(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "IMEI: %s\n", imei.c_str()); + + String imsi = modem.getIMSI(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "IMSI: %s\n", imsi.c_str()); + + String cop = modem.getOperator(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Operator: %s\n", cop.c_str()); + + IPAddress local = modem.localIP(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Local IP: %s\n", local.toString().c_str()); + + int csq = modem.getSignalQuality(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Signal quality: %d\n", csq); +} +bool ConnectionManager::isGprsConnected() { + return modem.isGprsConnected(); +} 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..70b4668a810fb0ceb0a858f4f4d52218201603c4 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/ConnectionManager.h @@ -0,0 +1,75 @@ +// +// 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 "Definitions.h" +#include "GPRSCredentials.h" +#include "LTEConnectionException.hpp" +#include "Mode.h" +#include "ModemFunctionalityLevel.h" +#include "NetworkMode.h" +#include "RequestInformation.h" +#include "StreamDebugger.h" +#include <TinyGsmClient.h> +#include <utility> + +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(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(); + + void logModemInformation(); + bool isGprsConnected(); +}; + +#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..5d416119edc8ec9ad36e153efadc564a8f347c60 --- /dev/null +++ b/host/host_central_mast/lib/NetworkConnectionManager/RequestInformation.h @@ -0,0 +1,54 @@ +// +// Created by zoe on 1/9/23. +// + +#ifndef HOST_CENTRAL_MAST_REQUESTINFORMATION_H +#define HOST_CENTRAL_MAST_REQUESTINFORMATION_H + +#include <Arduino.h> +#include <Definitions.h> +#include <map> + +enum RequestMethod { + GET, + POST, +}; + +struct RequestInformation { + RequestMethod method; + std::map<String, String> headers; + int port; + String host; + String path; + String body; + + RequestInformation(const RequestMethod &method, const String &host, const int &port, const String &path, + const String &body, const std::map<String, String> &headers) { + this->method = method; + this->port = port; + this->headers = headers; + this->host = host; + this->path = path; + this->body = body; + } + + String buildRequest() { + String methodString = method == RequestMethod::GET ? "GET" : "POST"; + String request = ""; + request += methodString + " " + path + " HTTP/1.1\r\n"; + request += "Host: " + host + "\r\n"; + request += "User-Agent: ESP32-Host\r\n"; + for (auto &header : headers) { + request += header.first + ": " + header.second + "\r\n"; + } + // request += "Authorization: Token " + INFLUXDB_TOKEN + "\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/ResendManager/ResendManager.cpp b/host/host_central_mast/lib/ResendManager/ResendManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64d6557ab417c4535f6ab7801f0ce12b566d0021 --- /dev/null +++ b/host/host_central_mast/lib/ResendManager/ResendManager.cpp @@ -0,0 +1,72 @@ +// +// Created by cynthya on 1/5/23. +// + +#include "ResendManager.h" +#include "Utilities.h" + +void ResendManager::init() { + createResendDirectory(); + // log + uint lastResendFileId = getLastResendFileId(); + nextResendFileId = lastResendFileId + 1; + esp_log_write(ESP_LOG_INFO, "ResendManager", "nextResendFileId: %d\n", nextResendFileId); +} + +uint ResendManager::getLastResendFileId() const { // get the next file id to be resend + auto filesInDirectory = SDUtilities::getFilesInDirectory(resendDirectoryPath); + // convert the file names to uint + std::list<uint> fileUintIDs; + + try { + for (const auto &fileName : filesInDirectory) { + fileUintIDs.emplace_back(std::stoul(fileName.c_str())); + } + } catch (std::invalid_argument &e) { + esp_log_write(ESP_LOG_ERROR, "getLastResendFileId", "Failed to convert file name to uint\n"); + throw; + } + // get the max + uint max = 0; + for (const auto &fileID : fileUintIDs) { + if (fileID > max) { + max = fileID; + } + } + + return max; +} + +void ResendManager::createResendDirectory() const { // create directory if it doesn't exist + SDUtilities::createDirectory("/resend"); +} + +void ResendManager::storeForResend(const String &messageToBeSend) { + // create file + String filename = String(nextResendFileId); + SDUtilities::writeFile(messageToBeSend, resendDirectoryPath + "/" + filename); + ResendManager::incrementCount(); +} + +std::optional<ResendPointType> ResendManager::loadNextToBeResendMessage() { + // get first file in resend directory + auto filename = SDUtilities::getFirstFileNameInDirectory(resendDirectoryPath.c_str()); + + if (filename.has_value()) { + + // read file + auto message = SDUtilities::readFile(resendDirectoryPath + "/" + filename.value().c_str()); + return ResendPointType{message, filename.value()}; + } + return std::nullopt; +} + +void ResendManager::deleteResendMessage(const String &filename) { + auto filePath = resendDirectoryPath + "/" + filename; + if (SD.remove(filePath.c_str())) { + esp_log_write(ESP_LOG_INFO, "ResendManager", "Deleted file: %s\n", filePath.c_str()); + } else { + esp_log_write(ESP_LOG_ERROR, "ResendManager", "Failed to delete file: %s\n", filePath.c_str()); + throw; + } +} diff --git a/host/host_central_mast/lib/ResendManager/ResendManager.h b/host/host_central_mast/lib/ResendManager/ResendManager.h new file mode 100644 index 0000000000000000000000000000000000000000..5d7495a657f004248d8c667b326233f090a8bb59 --- /dev/null +++ b/host/host_central_mast/lib/ResendManager/ResendManager.h @@ -0,0 +1,35 @@ +// +// Created by cynthya on 1/5/23. +// + +#ifndef HOST_CENTRAL_MAST_RESENDMANAGER_H +#define HOST_CENTRAL_MAST_RESENDMANAGER_H + +#include "ResendPointType.h" +#include "SD.h" +#include "Utilities.h" +#include <Arduino.h> +#include <cstdio> + +class ResendManager { + public: + // File name (as uint) of the next file to be re-send (i.e. a counter of files to be resend). Max value is + // 4'294'967'295 + + void storeForResend(const String &messageToBeSend); + // I am making this an optional since i want to avoid creating a 'hasNextToBeResend' method since that would require + // another fs operation. If this method returns std::nullopt, it means that there is no file to be resend. + std::optional<ResendPointType> loadNextToBeResendMessage(); + void deleteResendMessage(const String &filename); + + void init(); + + private: + const String resendDirectoryPath = "/resend"; + uint nextResendFileId = 0; + void incrementCount() { nextResendFileId++; } + void createResendDirectory() const; + uint getLastResendFileId() const; +}; + +#endif // HOST_CENTRAL_MAST_RESENDMANAGER_H diff --git a/host/host_central_mast/lib/ResendManager/ResendPointType.h b/host/host_central_mast/lib/ResendManager/ResendPointType.h new file mode 100644 index 0000000000000000000000000000000000000000..11a4bfce68c99422094f4b76e1b5b82e47b0eec0 --- /dev/null +++ b/host/host_central_mast/lib/ResendManager/ResendPointType.h @@ -0,0 +1,15 @@ +// +// Created by cynthya on 1/5/23. +// + +#ifndef HOST_CENTRAL_MAST_RESENDPOINTTYPE_H +#define HOST_CENTRAL_MAST_RESENDPOINTTYPE_H + +#include <Arduino.h> + +struct ResendPointType { + String message; + String filename; +}; + +#endif // HOST_CENTRAL_MAST_RESENDPOINTTYPE_H diff --git a/host/host_central_mast/lib/Utilities/Definitions.h b/host/host_central_mast/lib/Utilities/Definitions.h new file mode 100644 index 0000000000000000000000000000000000000000..5f5cc45c375b37d12d8845de1fdf8f605cc936d2 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/Definitions.h @@ -0,0 +1,46 @@ +// +// Created by zoe on 1/12/23. +// + +#ifndef HOST_CENTRAL_MAST_DEFINITIONS_H +#define HOST_CENTRAL_MAST_DEFINITIONS_H + +#include "Arduino.h" +#include "ESP32Time.h" + +#define uS_TO_S_FACTOR 1000000ULL // Conversion factor for micro seconds to seconds +#define TIME_TO_SLEEP 5 // Time ESP32 will go to sleep (in seconds) + +#define UART_BAUD 115200 +#define PIN_DTR 25 +#define PIN_TX 27 +#define PIN_RX 26 +#define PWR_PIN 4 + +#define SD_MISO 2 +#define SD_MOSI 15 +#define SD_SCLK 14 +#define SD_CS 13 +#define LED_PIN 12 + +// See all AT commands, if wanted +// #define DUMP_AT_COMMANDS + +#define TAG_ESPNOW "ESPNOW" +#define TAG_MAIN "MAIN" +#define TAG_SD "SD" +#define TAG_GSM "GSM" +#define TAG_TIMEMANAGER "TIMEMANAGER" +#define TAG_SDUTILITIES "SDUTILITIES" + +#define SD_CARD_FAIL_COUNTER 100 + +constexpr bool PRINT_TO_SERIAL = true; + +// global definition of RTC. Initialised in main +extern ESP32Time rtc; + +const String INFLUXDB_TOKEN = + "dUh2gbVLv7e3egqocxriDsJQNUacA9qZ5YXsYtdnVAglnHgy4nx-jDVO7nGlSF34BosfnuwnUDaviC7dQeC5RQ=="; + +#endif // HOST_CENTRAL_MAST_DEFINITIONS_H diff --git a/host/host_central_mast/lib/Utilities/SDCardException.h b/host/host_central_mast/lib/Utilities/SDCardException.h new file mode 100644 index 0000000000000000000000000000000000000000..039d70328ab0463196bf2b78aa7559b88ab54089 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/SDCardException.h @@ -0,0 +1,22 @@ +// +// Created by zoe on 1/12/23. +// + +#ifndef HOST_CENTRAL_MAST_SDCARDEXCEPTION_H +#define HOST_CENTRAL_MAST_SDCARDEXCEPTION_H + +#include <Arduino.h> +#include <exception> + +// create a custom exception type called SD Card Exception + +class SDCardException : public std::exception { + public: + SDCardException(const String &message) : message(message) {} + const char *what() const noexcept override { return message.c_str(); } + + private: + String message; +}; + +#endif // HOST_CENTRAL_MAST_SDCARDEXCEPTION_H diff --git a/host/host_central_mast/lib/Utilities/SDCardLogger.cpp b/host/host_central_mast/lib/Utilities/SDCardLogger.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b7fe7c7dcd2ef8b075cd67faae9ae187f01922b0 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/SDCardLogger.cpp @@ -0,0 +1,60 @@ +// +// Created by zoe on 1/24/23. +// + +#include "SDCardLogger.h" + +namespace SDCardLogger { + + char log_print_buffer[512]; + bool printToSerial = false; + + void printDebugToSerial(bool printToSerialAsWell) { + printToSerial = printToSerialAsWell; + } + int vprintf_into_sd(const char *szFormat, va_list args) { + String logstring = "[" + rtc.getDateTime() + "] "; + logstring += szFormat; + + if (!SDUtilities::isSDAvailable()) { + if (printToSerial) { + logstring += " (SD card not available)\n"; + vprintf(logstring.c_str(), args); + } + return 1; + } + + // write evaluated format string into buffer + int ret = vsnprintf(log_print_buffer, sizeof(log_print_buffer), logstring.c_str(), args); + + String date = rtc.getDate(); + String filename = "/log_" + date + ".txt"; + + // output is now in buffer. write to file. + if (ret >= 0) { + if (!SD.exists(filename)) { + File writeLog = SD.open(filename, FILE_WRITE); + if (!writeLog) { + Serial.println("Couldn't open " + filename + " for writing"); + SDUtilities::setSDAvailable(false); + return 1; + } + delay(50); + writeLog.close(); + } + + File logFile = SD.open(filename, FILE_APPEND); + + // debug output + if (printToSerial) { + vprintf(logstring.c_str(), args); + } + logFile.write((uint8_t *)log_print_buffer, (size_t)ret); + // to be safe in case of crashes: flush the output + logFile.flush(); + logFile.close(); + } + return ret; + } + +} // namespace SDCardLogger diff --git a/host/host_central_mast/lib/Utilities/SDCardLogger.h b/host/host_central_mast/lib/Utilities/SDCardLogger.h new file mode 100644 index 0000000000000000000000000000000000000000..c14a07dc0ae0e4579daeb1b41c775253ebd9ca25 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/SDCardLogger.h @@ -0,0 +1,17 @@ +// +// Created by zoe on 1/24/23. +// + +#ifndef HOST_CENTRAL_MAST_SDCARDLOGGER_H +#define HOST_CENTRAL_MAST_SDCARDLOGGER_H + +#include "Definitions.h" +#include "SD.h" +#include "Utilities.h" + +namespace SDCardLogger { + void printDebugToSerial(bool printToSerialAsWell); + int vprintf_into_sd(const char *szFormat, va_list args); +}; // namespace SDCardLogger + +#endif // HOST_CENTRAL_MAST_SDCARDLOGGER_H diff --git a/host/host_central_mast/lib/Utilities/SDSetupException.h b/host/host_central_mast/lib/Utilities/SDSetupException.h new file mode 100644 index 0000000000000000000000000000000000000000..09c1ed348b6bd38798a04117fb42c4446e52381b --- /dev/null +++ b/host/host_central_mast/lib/Utilities/SDSetupException.h @@ -0,0 +1,21 @@ +// +// Created by zoe on 1/12/23. +// + +#ifndef HOST_CENTRAL_MAST_SDSETUPEXCEPTION_H +#define HOST_CENTRAL_MAST_SDSETUPEXCEPTION_H + +#include <Arduino.h> +#include <exception> +// create a custom exception type called SD Setup Exception + +class SDSetupException : public std::exception { + public: + SDSetupException(const String &message) : message(message) {} + const char *what() const noexcept override { return message.c_str(); } + + private: + String message; +}; + +#endif // HOST_CENTRAL_MAST_SDSETUPEXCEPTION_H diff --git a/host/host_central_mast/lib/Utilities/Utilities.cpp b/host/host_central_mast/lib/Utilities/Utilities.cpp new file mode 100644 index 0000000000000000000000000000000000000000..316d33c95d27260f7b3c0ca299ed049db6305bce --- /dev/null +++ b/host/host_central_mast/lib/Utilities/Utilities.cpp @@ -0,0 +1,305 @@ +// +// Created by zoe on 1/9/23. +// + +#include "Utilities.h" + +namespace SDUtilities { + + bool SDAvailable = true; + + void setSDAvailable(bool sdAvailable) { + SDAvailable = sdAvailable; + } + + // Used to try to remount SD card if it fails more than x times (100) + int sdFailCounter = 0; + + bool isSDAvailable() { + return SDAvailable; + } + + void tryRemountingSD() { + try { + setupSDCard(SD_MISO, SD_MOSI, SD_SCLK, SD_CS); + setSDAvailable(true); + } catch (SDSetupException &e) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Couldn't remount SD card: %s\n", e.what()); + } + sdFailCounter = 0; + } + + bool checkSDAvailability(const String &errorMessage) { + if (!isSDAvailable()) { + sdFailCounter++; + if (sdFailCounter % SD_CARD_FAIL_COUNTER == 0) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "SD card not available. Trying to remount...\n"); + tryRemountingSD(); + } + String message = errorMessage + " (SD card not available, failed " + sdFailCounter + " times)\n"; + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "%s", message.c_str()); + // if re-mount was successful, this will be true + return isSDAvailable(); + } + return true; + }; + + std::list<String> getFilesInDirectory(const String &dirname) { + if (!checkSDAvailability("Couldn't get files in directory " + dirname)) { + return {}; + } + std::list<String> files; + File dir = openDirectory(dirname); + while (true) { + File nextFile = dir.openNextFile(); + if (!nextFile) { + break; + } + if (!nextFile.isDirectory()) { + files.emplace_back(nextFile.name()); + } + nextFile.close(); + } + return files; + } + + std::optional<String> getLastFileInDirectory(const String &dirname) { + + if (!checkSDAvailability("Couldn't get last file in directory " + dirname)) { + return std::nullopt; + } + + File root = openDirectory(dirname); + root.rewindDirectory(); + + File file = root.openNextFile(); + while (file) { + File nextFile = root.openNextFile(); + if (!nextFile) { + break; + } + file = nextFile; + } + // log + if (file) { + esp_log_write(ESP_LOG_INFO, TAG_SDUTILITIES, "Last file name: %s\n", file.name()); + return file.name(); + } else { + esp_log_write(ESP_LOG_INFO, TAG_SDUTILITIES, "No file found\n"); + return std::nullopt; + } + } + File openDirectory(const String &dirname) { + + if (!checkSDAvailability("Couldn't open directory " + dirname)) { + throw; + } + + File root = SD.open(dirname); + if (!root) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Failed to open directory\n"); + throw; + } + if (!root.isDirectory()) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Not a directory\n"); + throw; + } + return root; + } + + std::optional<String> getFirstFileNameInDirectory(const String &dirname) { + + if (!checkSDAvailability("Couldn't get first file in directory " + dirname)) { + return std::nullopt; + } + + File root = openDirectory(dirname); + root.rewindDirectory(); + + File file = root.openNextFile(); + if (file) { + esp_log_write(ESP_LOG_INFO, TAG_SDUTILITIES, "file found: %s\n", file.name()); + return file.name(); + } else { + esp_log_write(ESP_LOG_INFO, TAG_SDUTILITIES, "no file found\n"); + return std::nullopt; + } + } + + File openForWrite(const String &filePath) { + + if (!checkSDAvailability("Couldn't open file " + filePath + " for writing")) { + throw; + } + + File file = SD.open(filePath, FILE_WRITE); + if (!file) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Failed to open file for writing\n"); + throw; + } + return file; + } + + File openForRead(const String &filePath) { + + if (!checkSDAvailability("Couldn't open file " + filePath + " for reading")) { + throw; + } + + File file = SD.open(filePath, FILE_READ); + if (!file) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Failed to open file for reading\n"); + throw; + } + return file; + } + + void writeFile(const String &messageToBeSend, const String &filePath) { + + if (!checkSDAvailability("Couldn't write to file " + filePath)) { + throw; + } + + File file = openForWrite(filePath); + if (file.print(messageToBeSend)) { + esp_log_write(ESP_LOG_INFO, TAG_SDUTILITIES, "File written %s\n", filePath.c_str()); + } else { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Write failed %s\n", filePath.c_str()); + throw; + } + file.close(); + } + + String readFile(const String &filePath) { + + if (!checkSDAvailability("Couldn't read file " + filePath)) { + throw; + } + + File file = openForRead(filePath); + + String ret; + + while (file.available()) { + ret += (char)file.read(); + } + + file.close(); + return ret; + } + + void createDirectory(const String &dirname) { + + if (!checkSDAvailability("Couldn't create directory " + dirname)) { + throw; + } + + if (!SD.exists(dirname)) { + SD.mkdir(dirname); + esp_log_write(ESP_LOG_INFO, TAG_SDUTILITIES, "Created directory: %s\n", dirname.c_str()); + } else { + esp_log_write(ESP_LOG_WARN, TAG_SDUTILITIES, "Directory already exists\n"); + } + } + + void setupSDCard(int MISO, int MOSI, int SCLK, int CS) { + SPI.begin(SCLK, MISO, MOSI, CS); + if (!SD.begin(CS)) { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "Card MOUNT FAIL\n"); + throw SDSetupException("Card MOUNT FAIL"); + } else { + uint32_t cardSize = SD.cardSize() / (1024 * 1024); + String sdcardSizeString = "SDCard Size: " + String(cardSize) + "MB"; + esp_log_write(ESP_LOG_DEBUG, TAG_SDUTILITIES, "%s\n", sdcardSizeString.c_str()); + } + } + + void saveStringToSDCard(const std::string &dataString) { + + if (!checkSDAvailability("Couldn't save string to SD card")) { + throw SDCardException("Couldn't save string to SD card"); + } + + File dataFile = SD.open("/datalog.txt", FILE_APPEND); + + // if the file is available, write to it: + if (dataFile) { + if (dataString.length() > 0) { + dataFile.println(dataString.c_str()); + } + dataFile.close(); + } + // if the file isn't open, pop up an error: + else { + esp_log_write(ESP_LOG_ERROR, TAG_SDUTILITIES, "error opening datalog.txt\n"); + throw SDCardException("error opening datalog.txt"); + } + } +} // namespace SDUtilities + +// I don't think this does anything. Copied from the example +void turnOffLEDs() { // Set LED OFF + pinMode(LED_PIN, OUTPUT); + digitalWrite(LED_PIN, HIGH); + + pinMode(PWR_PIN, OUTPUT); + digitalWrite(PWR_PIN, HIGH); + delay(300); + digitalWrite(PWR_PIN, LOW); +} + +String getMacAddressAsString(const uint8_t *mac) { + String macAddress; + for (int i = 0; i < 6; i++) { + macAddress += String(mac[i], HEX); + } + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "MAC: %s\n", macAddress.c_str()); + return macAddress; +} + +String documentToLineProtocolString(const DynamicJsonDocument &doc) { + String measurementType = doc["measurementType"].as<String>(); + String sensorName = doc["sensorName"].as<String>(); + String timestamp = doc["timestamp"].as<String>(); + String protocol = doc["protocol"].as<String>(); + String value = doc["value"].as<String>(); + String channel = doc["channel"].as<String>(); + String clientMac = doc["clientMac"].as<String>(); + + String lineData = sensorName + ",clientMac=" + clientMac + ",protocol=" + protocol + ",channel=" + channel + " " + + measurementType + "=" + value + " " + timestamp; + return lineData; +} +DynamicJsonDocument parseReceivedJsonData(char *data) { + DynamicJsonDocument doc(250); + auto error = deserializeJson(doc, data); + if (error) { + esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW, "Error while parsing json: %s\n", error.c_str()); + // TODO error handling + } + return doc; +} + +String documentToServerReadableString(const DynamicJsonDocument &doc) { + StaticJsonDocument<1024> serverDoc; + String hostMacAddressString = WiFi.macAddress(); + hostMacAddressString.replace(":", ""); + hostMacAddressString.toLowerCase(); + + serverDoc["host"] = hostMacAddressString; + serverDoc["client"] = doc["clientMac"].as<String>(); + serverDoc["sensorProtocol"] = doc["protocol"].as<String>(); + serverDoc["protocolAddress"] = doc["channel"].as<String>(); + serverDoc["hardwareName"] = doc["sensorName"].as<String>(); + // each value is a element in the readigs array + JsonArray readings = serverDoc.createNestedArray("readings"); + JsonObject reading = readings.createNestedObject(); + reading["name"] = doc["measurementType"].as<String>(); + reading["value"] = doc["value"].as<float>(); + serverDoc["time"] = doc["timestamp"].as<uint32_t>(); + + String serverString; + serializeJson(serverDoc, serverString); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Server readable data: %s\n", serverString.c_str()); + return serverString; +} \ No newline at end of file diff --git a/host/host_central_mast/lib/Utilities/Utilities.h b/host/host_central_mast/lib/Utilities/Utilities.h new file mode 100644 index 0000000000000000000000000000000000000000..dc18e3459e0fabe83c64f2e09a4a9ae3bf8c45f6 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/Utilities.h @@ -0,0 +1,38 @@ +// +// Created by zoe on 1/9/23. +// + +#ifndef HOST_CENTRAL_MAST_UTILITIES_H +#define HOST_CENTRAL_MAST_UTILITIES_H + +#include "ArduinoJson.h" +#include "SD.h" +#include "SDCardException.h" +#include "SDSetupException.h" +#include "WiFi.h" +#include <Arduino.h> +#include <Definitions.h> +#include <WString.h> +#include <list> + +namespace SDUtilities { + File openDirectory(const String &dirname); + std::list<String> getFilesInDirectory(const String &dirname); + std::optional<String> getFirstFileNameInDirectory(const String &dirname); + std::optional<String> getLastFileInDirectory(const String &dirname); + void writeFile(const String &messageToBeSend, const String &filePath); + String readFile(const String &filePath); + void createDirectory(const String &dirname); + void setupSDCard(int MISO, int MOSI, int SCLK, int CS); + void saveStringToSDCard(const std::string &dataString); + void setSDAvailable(bool sdAvailable); + bool isSDAvailable(); +} // namespace SDUtilities + +void turnOffLEDs(); +String getMacAddressAsString(const uint8_t *mac); +String documentToLineProtocolString(const DynamicJsonDocument &doc); +DynamicJsonDocument parseReceivedJsonData(char *data); +String documentToServerReadableString(const DynamicJsonDocument &doc); + +#endif // HOST_CENTRAL_MAST_UTILITIES_H 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 = diff --git a/host/host_central_mast/src/main.cpp b/host/host_central_mast/src/main.cpp index 83adf6abac662548043aabe3ad47fb452e621d0d..db4e69de644ebaedab6ae1ecc4bcc215bac99b35 100644 --- a/host/host_central_mast/src/main.cpp +++ b/host/host_central_mast/src/main.cpp @@ -1,44 +1,34 @@ -#include "FS.h" -#include "SD.h" +/*** + * code is modified from the original code found at + * https://github.com/Xinyuan-LilyGO/LilyGO-T-SIM7000G/blob/master/examples/Arduino_TinyGSM/AllFunctions/AllFunctions.ino + */ + +#define TINY_GSM_MODEM_SIM7000 + +#include "ConnectionManager.h" +#include "MessageType.h" +#include "SDCardLogger.h" #include "SPI.h" -#include "time.h" -#include <Arduino.h> +#include "Utilities.h" #include <ArduinoJson.h> +#include <Definitions.h> #include <ESP32Time.h> +#include <NTPManager.h> +#include <ResendManager.h> +#include <TinyGsmClient.h> #include <WiFi.h> #include <esp_log.h> #include <esp_now.h> +#include <map> #include <queue> #include <sys/unistd.h> -static const std::string TAG = "MAIN"; -static const std::string TAG_ESPNOW = "ESPNOW"; -static const std::string TAG_GSM = "GSM"; - -/* - FILE: AllFunctions.ino - AUTHOR: Koby Hale - PURPOSE: Test functionality -*/ +ESP32Time rtc; -#define TINY_GSM_MODEM_SIM7000 #define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb #define SerialAT Serial1 -// See all AT commands, if wanted -#define DUMP_AT_COMMANDS - -// set GSM PIN, if any -#define GSM_PIN "" - -// Your GPRS credentials, if any -const char apn[] = "m2m.public.at"; // SET TO YOUR APN -const char gprsUser[] = ""; -const char gprsPass[] = ""; - -#include <TinyGsmClient.h> - -#ifdef DUMP_AT_COMMANDS // if enabled it requires the streamDebugger lib +#ifdef DUMP_AT_COMMANDS // if enabled it requires the streamDebugger library #include <StreamDebugger.h> StreamDebugger debugger(SerialAT, Serial); TinyGsm modem(debugger); @@ -46,518 +36,256 @@ TinyGsm modem(debugger); TinyGsm modem(SerialAT); #endif -#define uS_TO_S_FACTOR 1000000ULL // Conversion factor for micro seconds to seconds -#define TIME_TO_SLEEP 5 // Time ESP32 will go to sleep (in seconds) - -#define UART_BAUD 115200 -#define PIN_DTR 25 -#define PIN_TX 27 -#define PIN_RX 26 -#define PWR_PIN 4 - -#define SD_MISO 2 -#define SD_MOSI 15 -#define SD_SCLK 14 -#define SD_CS 13 -#define LED_PIN 12 - -enum MessageType{ - dataAck, - hostChange -}; -typedef struct response{ - MessageType type; - uint8_t mac[6]; - long time; -}response; - -uint8_t BROADCAST_MAC[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; +const GPRSCredentials gprsCredentials = GPRSCredentials("m2m.public.at", "", ""); + +ConnectionManager connectionManager{modem, gprsCredentials, + ConnectionManager::ModemPins{PIN_DTR, PIN_TX, PIN_RX, PWR_PIN}}; + +NTPManager ntpManager{modem}; + +uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; esp_now_peer_info_t broadcast = {}; response announce = {}; -ESP32Time rtc; - SemaphoreHandle_t xMutex; TaskHandle_t ESPNOWTask; static std::queue<String> queue; -void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) -{ - // go to sleep -} +ResendManager resendManager; -static char log_print_buffer[512]; - -int vprintf_into_sd(const char *szFormat, va_list args) -{ - String logstring = "[" + rtc.getDateTime() + "] "; - logstring += szFormat; - // write evaluated format string into buffer - int ret = vsnprintf(log_print_buffer, sizeof(log_print_buffer), logstring.c_str(), args); - - String date = rtc.getDate(); - String filename = "/log_" + date + ".txt"; - - // output is now in buffer. write to file. - if (ret >= 0) { - if (!SD.exists(filename)) { - File writeLog = SD.open(filename, FILE_WRITE); - if (!writeLog) - Serial.println("Couldn't open " + filename + " for writing"); - delay(50); - writeLog.close(); - } - - File logFile = SD.open(filename, FILE_APPEND); - - // debug output - vprintf(logstring.c_str(), args); - logFile.write((uint8_t *)log_print_buffer, (size_t)ret); - // to be safe in case of crashes: flush the output - logFile.flush(); - logFile.close(); - } - return ret; +void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { + // go to sleep } -String getMacAddressAsString(const uint8_t *mac); -DynamicJsonDocument parseReceivedJsonData(char *data); -void saveStringToSDCard(const std::string &dataString); -String documentToLineProtocolString(const DynamicJsonDocument &doc); -void turnOffLEDs(); -void setupSDCard(); -void syncUTCTimeToRTC(); -void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) -{ - response response = {}; - // send a host change msg if client is new, simplifies client - response.type = esp_now_is_peer_exist(mac) ? dataAck : hostChange; - esp_read_mac(response.mac, ESP_MAC_WIFI_STA); - response.time = rtc.getEpoch(); - - // this block needs to happen before we send the message, as it's not possible to - // send a message to an unregistered peer (i.e. new client) - if(!esp_now_is_peer_exist(mac)){ - esp_now_peer_info_t client = {}; - memcpy(client.peer_addr, mac, sizeof(uint8_t) * 6); - client.encrypt = false; - client.channel = 0; - - esp_err_t status = esp_now_add_peer(&client); - if(status != ESP_OK){ - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Failed to add new Peer: %d", status); - } - } - - esp_err_t success = esp_now_send(mac, (uint8_t*) &response, sizeof(response)); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), - (success == ESP_OK) ? "Response sent\n" : "Failed to respond\n"); - - - esp_log_write(ESP_LOG_INFO, TAG_ESPNOW.c_str(), "Message recieved\n"); - // copy received data to a char array - char data[len]; - memcpy(data, incomingData, len); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Raw received Data: %s\n", data); - - DynamicJsonDocument doc = parseReceivedJsonData(data); - - String macAddress = getMacAddressAsString(mac); - - // add timestamp and mac address - // doc["timestamp"] = rtc.getEpoch(); - doc["clientMac"] = macAddress; - - // serialize json document again - std::string dataString{}; - serializeJson(doc, dataString); - - saveStringToSDCard(dataString); - - String lineData = documentToLineProtocolString(doc); - - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Line protocol data: %s\n", lineData.c_str()); - - xSemaphoreTake(xMutex, portMAX_DELAY); - queue.push(lineData); - xSemaphoreGive(xMutex); +void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) { -} + esp_log_write(ESP_LOG_INFO, TAG_ESPNOW, "Message received\n"); -String documentToLineProtocolString(const DynamicJsonDocument &doc) -{ - String measurementType = doc["measurementType"].as<String>(); - String sensorName = doc["sensorName"].as<String>(); - String timestamp = doc["timestamp"].as<String>(); - String protocol = doc["protocol"].as<String>(); - String value = doc["value"].as<String>(); - String channel = doc["channel"].as<String>(); - String clientMac = doc["clientMac"].as<String>(); - - String lineData = sensorName + ",clientMac=" + clientMac + ",protocol=" + protocol + ",channel=" + channel + " " - + measurementType + "=" + value + " " + timestamp; - return lineData; -} -void saveStringToSDCard(const std::string &dataString) -{ - File dataFile = SD.open("/datalog.txt", FILE_APPEND); - - // if the file is available, write to it: - if (dataFile) { - if (dataString.length() > 0) { - dataFile.println(dataString.c_str()); - } - dataFile.close(); - } - // if the file isn't open, pop up an error: - else { - esp_log_write(ESP_LOG_ERROR, TAG.c_str(), "error opening datalog.txt\n"); - // TODO: Error handling - } -} -DynamicJsonDocument parseReceivedJsonData(char *data) -{ - DynamicJsonDocument doc(250); - auto error = deserializeJson(doc, data); - if (error) { - esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW.c_str(), "Error while parsing json: %s\n", error.f_str()); - // TODO error handling - } - return doc; -} -String getMacAddressAsString(const uint8_t *mac) -{ - String macAddress; - for (int i = 0; i < 6; i++) { - macAddress += String(mac[i], HEX); - } - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "MAC: %s\n", macAddress.c_str()); - return macAddress; -} + if (!esp_now_is_peer_exist(mac)) { + esp_now_peer_info_t client = {}; + memcpy(client.peer_addr, mac, sizeof(uint8_t) * 6); + client.encrypt = false; + client.channel = 0; -[[noreturn]] void ESPNOWReceiveTask(void *parameter) -{ - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "ESPNOWReceiveTask started on core %d\n", xPortGetCoreID()); + esp_err_t status = esp_now_add_peer(&client); + if (status != ESP_OK) { + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Failed to add new Peer: %d", status); + } + } - WiFi.mode(WIFI_STA); + // TODO: Think if response to client may contain error messages + response response = {}; + response.type = dataAck; + esp_read_mac(response.mac, ESP_MAC_WIFI_STA); + response.time = rtc.getEpoch(); + esp_err_t success = esp_now_send(mac, (uint8_t *)&response, sizeof(response)); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, (success == ESP_OK) ? "Response sent\n" : "Failed to respond\n"); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Initialising ESPNow...\n"); + char data[len]; + memcpy(data, incomingData, len); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Raw received Data: %s\n", data); - if (esp_now_init() != ESP_OK) { - // initialization failed - esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW.c_str(), "Initialising ESPNow FAILED\n"); - exit(ESP_FAIL); - } + DynamicJsonDocument doc = parseReceivedJsonData(data); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Initialising ESPNow SUCCESS\n"); + String macAddress = getMacAddressAsString(mac); - esp_now_register_recv_cb(on_data_recv); + // add timestamp and mac address + // doc["timestamp"] = rtc.getEpoch(); + doc["clientMac"] = macAddress; - while (true) { - } -} + // serialize json document again + std::string dataString{}; + serializeJson(doc, dataString); -time_t timeToUnixEpochSeconds(const std::string &time) -{ - // 22/10/27,10:16:20+00 - struct tm tm {}; - time_t dateInEpoch = 0; - - if (strptime(time.c_str(), "%y/%m/%d,%T+00", &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); - } - return dateInEpoch; -} + try { + SDUtilities::saveStringToSDCard(dataString); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW, "Failed to save data to SD card: %s", e.what()); + } -void setup() -{ - // Set console baud rate - Serial.begin(115200); - delay(10); - setupSDCard(); - - // https://stackoverflow.com/questions/60442350/arduinos-esp-log-set-vprintf-does-not-work-on-esp32 - esp_log_set_vprintf(&vprintf_into_sd); - esp_log_level_set("*", ESP_LOG_VERBOSE); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "%s", WiFi.macAddress().c_str()); - - turnOffLEDs(); - - xMutex = xSemaphoreCreateMutex(); - - delay(1000); - - // create ESPNOWReceiveTask. TODO: Until the UTC time is not synced, this will not add the correct time. If we - // TODO: create the task after the time is synced, no messages will be received until synchronization is done - xTaskCreatePinnedToCore(ESPNOWReceiveTask, /* Function to implement the task */ - "ESPNOWReceiveTask", /* Name of the task */ - 10000, /* Stack size in words */ - nullptr, /* Task input parameter */ - 0, /* Priority of the task */ - &ESPNOWTask, /* Task handle. */ - 0); /* Core where the task should run */ - - SerialAT.begin(UART_BAUD, SERIAL_8N1, PIN_RX, PIN_TX); - - // Restart takes quite some time - // To skip it, call init() instead of restart() - esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Initializing 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"); - } - - syncUTCTimeToRTC(); - - broadcast.channel = 0; - broadcast.encrypt = false; - memcpy(&broadcast.peer_addr, &BROADCAST_MAC, sizeof(BROADCAST_MAC)); - if(esp_now_add_peer(&broadcast) != ESP_OK){ - esp_log_write(ESP_LOG_WARN, TAG_ESPNOW.c_str(), "Failed to add Broadcast Host"); - } - - announce.type = hostChange; - esp_read_mac(announce.mac, ESP_MAC_WIFI_STA); - announce.time = rtc.getEpoch(); - if(esp_now_send(BROADCAST_MAC, (uint8_t *) &announce, sizeof(announce)) != ESP_OK){ - esp_log_write(ESP_LOG_WARN, TAG_ESPNOW.c_str(), "Failed to announce mac"); - } - else{ - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Mac announced!"); - } -} -void syncUTCTimeToRTC() -{ - esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "NTP Server Syncing...\n"); - modem.NTPServerSync("pool.ntp.org", 0); - auto gsmDateTimeString = modem.getGSMDateTime(DATE_FULL); - esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "GSM DateTime: %s\n", gsmDateTimeString.c_str()); - time_t time = timeToUnixEpochSeconds(gsmDateTimeString.c_str()); - rtc.setTime(time); - esp_log_write(ESP_LOG_INFO, TAG_GSM.c_str(), "Time set to EPOCH: %s\n", String(rtc.getEpoch()).c_str()); + String serverData = documentToServerReadableString(doc); + + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Data to be sent: %s\n", serverData.c_str()); + + xSemaphoreTake(xMutex, portMAX_DELAY); + queue.push(serverData); + xSemaphoreGive(xMutex); } -void setupSDCard() -{ - SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); - if (!SD.begin(SD_CS)) { - esp_log_write(ESP_LOG_ERROR, TAG.c_str(), "Card MOUNT FAIL\n"); - // TODO: Error handling - } else { - uint32_t cardSize = SD.cardSize() / (1024 * 1024); - String sdcardSizeString = "SDCard Size: " + String(cardSize) + "MB"; - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "%s\n", sdcardSizeString.c_str()); - } + +[[noreturn]] void esp_loop() { + while (true) { + } } -// I don't think this does anything. Copied from the example -void turnOffLEDs() -{ // Set LED OFF - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, HIGH); +[[noreturn]] void ESPNOWReceiveTask(void *parameter) { + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "ESPNOWReceiveTask started on core %d\n", xPortGetCoreID()); + + WiFi.mode(WIFI_STA); - pinMode(PWR_PIN, OUTPUT); - digitalWrite(PWR_PIN, HIGH); - delay(300); - digitalWrite(PWR_PIN, LOW); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Initialising ESPNow...\n"); + + if (esp_now_init() != ESP_OK) { + // initialization failed + esp_log_write(ESP_LOG_ERROR, TAG_ESPNOW, "Initialising ESPNow FAILED\n"); + exit(ESP_FAIL); + } + + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Initialising ESPNow SUCCESS\n"); + + esp_now_register_recv_cb(on_data_recv); + + broadcast.channel = 0; + broadcast.encrypt = false; + memcpy(&broadcast.peer_addr, &BROADCAST_MAC, sizeof(BROADCAST_MAC)); + if (esp_now_add_peer(&broadcast) != ESP_OK) { + esp_log_write(ESP_LOG_WARN, TAG_ESPNOW, "Failed to add Broadcast Host"); + } + + announce.type = hostChange; + esp_read_mac(announce.mac, ESP_MAC_WIFI_STA); + announce.time = rtc.getEpoch(); + if (esp_now_send(BROADCAST_MAC, (uint8_t *)&announce, sizeof(announce)) != ESP_OK) { + esp_log_write(ESP_LOG_WARN, TAG_ESPNOW, "Failed to announce mac"); + } else { + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Mac announced!"); + } + + esp_loop(); } -const String INFLUXDB_TOKEN = - "dUh2gbVLv7e3egqocxriDsJQNUacA9qZ5YXsYtdnVAglnHgy4nx-jDVO7nGlSF34BosfnuwnUDaviC7dQeC5RQ=="; - -struct RequestInformation { - String method; - String host; - String path; - String body; -}; - -String buildRequest(const RequestInformation &requestInformation) -{ - String request = ""; - request += requestInformation.method + " " + requestInformation.path + " HTTP/1.1\r\n"; - request += "Host: " + requestInformation.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(requestInformation.body.length()) + "\r\n"; - request += "\r\n"; - request += requestInformation.body; - return request; +void setup() { + // Set console baud rate + Serial.begin(115200); + delay(10); + try { + SDUtilities::setupSDCard(SD_MISO, SD_MOSI, SD_SCLK, SD_CS); + } catch (const SDSetupException &e) { + SDUtilities::setSDAvailable(false); + esp_log_write(ESP_LOG_ERROR, TAG_MAIN, "SD Card setup failed: %s\n", e.what()); + } + + SDCardLogger::printDebugToSerial(PRINT_TO_SERIAL); + + // https://stackoverflow.com/questions/60442350/arduinos-esp-log-set-vprintf-does-not-work-on-esp32 + esp_log_set_vprintf(&SDCardLogger::vprintf_into_sd); + esp_log_level_set("*", ESP_LOG_VERBOSE); + esp_log_write(ESP_LOG_DEBUG, TAG_MAIN, "%s", WiFi.macAddress().c_str()); + + resendManager.init(); + turnOffLEDs(); + + xMutex = xSemaphoreCreateMutex(); + + delay(1000); + + // create ESPNOWReceiveTask. TODO: Until the UTC time is not synced, this will not add the correct time. If we + // TODO: create the task after the time is synced, no messages will be received until synchronization is done + xTaskCreatePinnedToCore(ESPNOWReceiveTask, /* Function to implement the task */ + "ESPNOWReceiveTask", /* Name of the task */ + 10000, /* Stack size in words */ + nullptr, /* Task input parameter */ + 0, /* Priority of the task */ + &ESPNOWTask, /* Task handle. */ + 0); /* Core where the task should run */ + + SerialAT.begin(UART_BAUD, SERIAL_8N1, PIN_RX, PIN_TX); } -void loop() -{ - - // Restart takes quite some time - // To skip it, call init() instead of restart() - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Initializing modem...\n"); - if (!modem.init()) { - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), - "Failed to restart modem, attempting to continue without restarting\n"); - } - - announce.time = rtc.getEpoch(); - if(esp_now_send(BROADCAST_MAC, (uint8_t *) &announce, sizeof(announce)) != ESP_OK){ - esp_log_write(ESP_LOG_WARN, TAG_ESPNOW.c_str(), "Failed to announce mac\n"); - } - else{ - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Mac announced!\n"); - } - - String name = modem.getModemName(); - delay(500); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Modem Name %s\n", name.c_str()); - - String modemInfo = modem.getModemInfo(); - delay(500); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Modem Info: %s\n", modemInfo.c_str()); - - // 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.sendAT("+CFUN=0 "); - if (modem.waitResponse(10000L) != 1) { - DBG(" +CFUN=0 false "); - } - delay(200); - - /* - 2 Automatic - 13 GSM only - 38 LTE only - 51 GSM and LTE only - * * * */ - String res; - res = modem.setNetworkMode(38); - if (res != "1") { - DBG("setNetworkMode false "); - return; - } - delay(200); - - /* - 1 CAT-M - 2 NB-Iot - 3 CAT-M and NB-IoT - * * */ - // res = modem.setPreferredMode(1); - // if (res != "1") { - // - // DBG("setPreferredMode false "); - // return; - // } - delay(200); - - /*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 - */ - modem.sendAT("+CBANDCFG=\"CAT-M\",8 "); - if (modem.waitResponse(10000L) != 1) { - DBG(" +CBANDCFG=\"NB-IOT\" "); - } - delay(200); - - modem.sendAT("+CFUN=1 "); - if (modem.waitResponse(10000L) != 1) { - DBG(" +CFUN=1 false "); - } - delay(200); - - // modem.disableGPS(); - delay(200); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "%s\n", String(modem.getSignalQuality()).c_str()); - delay(200); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Trying to connect to network\n"); - modem.gprsConnect(apn, gprsUser, gprsPass); - delay(200); - syncUTCTimeToRTC(); - - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Waiting for network...\n"); - if (!modem.isNetworkConnected()) { - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Network not connected\n"); - return; - } else { - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Network connected\n"); - delay(200); - - // quality - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "%s\n", String(modem.getSignalQuality()).c_str()); - // make a http post request - String url = "influxdb.qe-forte.uibk.ac.at"; - String path = "/api/v2/write?org=QE&bucket=esp32test&precision=s"; - Serial.print("Connecting to "); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "%s\n", url.c_str()); - // Use WiFiClient class to create TCP connections - - while (!queue.empty()) { - - xSemaphoreTake(xMutex, portMAX_DELAY); - String lineData = queue.front(); - queue.pop(); - xSemaphoreGive(xMutex); - - RequestInformation requestInformation{.method = "POST", .host = url, .path = path, .body = lineData}; - - //"sensorName":"DRS26","timestamp":1666872216,"protocol":"I2C","value":0,"channel":0,"measurementType":"CIRCUMFERENCE_INCREMENT" - - String request = buildRequest(requestInformation); - esp_log_write(ESP_LOG_VERBOSE, TAG.c_str(), "request: %s\n", request.c_str()); - - TinyGsmClient client{modem}; - const int httpPort = 80; - if (!client.connect(url.c_str(), httpPort)) { - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "connection failed\n"); - return; - } - - client.print(request); - - // print response - while (client.connected()) { - String line = client.readStringUntil('\n'); - if (line == "\r") { - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "headers received\n"); - break; - } - } - client.stop(); - delay(1000); - } - DBG("Network connected"); - } - -#if TINY_GSM_POWERDOWN - // Try to power-off (modem may decide to restart automatically) - // To turn off modem completely, please use Reset/Enable pins - // modem.sendAT("+CPOWD=1"); - // if (modem.waitResponse(10000L) != 1) { - // DBG("+CPOWD=1"); - // } - // modem.poweroff(); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Poweroff."); -#endif +void loop() { + + connectionManager.modemPowerOn(); + delay(5000L); + + // Restart takes quite some time + // To skip it, call init() instead of restart() + connectionManager.restartModem(); + + announce.time = rtc.getEpoch(); + if (esp_now_send(BROADCAST_MAC, (uint8_t *)&announce, sizeof(announce)) != ESP_OK) { + esp_log_write(ESP_LOG_WARN, TAG_ESPNOW, "Failed to announce mac\n"); + } else { + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW, "Mac announced!\n"); + } + + connectionManager.disableGPS(); + + try { + // may fail if already set + connectionManager.setNetworkMode(LTE_ONLY); + connectionManager.setPreferredMode(CAT_M); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM, "Error setting network mode: %s\n", e.what()); + } + + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Modem Name: %s\n", modem.getModemName().c_str()); + + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Modem Info: %s\n", modem.getModemInfo().c_str()); + + connectionManager.unlockSimCard(); + + if (!connectionManager.waitForNetwork()) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM, "Network not available\n"); + return; + } + + if (connectionManager.isNetworkConnected()) { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Network connected\n"); + } + + delay(200); + + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Connecting to %s\n", gprsCredentials.apn.c_str()); + if (!connectionManager.gprsConnect()) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM, "GPRS not connected\n"); + return; + } + + connectionManager.logModemInformation(); + + ntpManager.syncNTP("pool.ntp.org", "at.pool.ntp.org"); + + try { + ntpManager.writeModemTimeToRTC(); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM, "Error writing time to rtc: %s\n", e.what()); + } + + if (connectionManager.isGprsConnected()) { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "GPRS connected\n"); + // make list of map of headers + std::map<String, String> headers; + headers["Content-Type"] = "application/json"; + headers["Accept"] = "application/json"; + + xSemaphoreTake(xMutex, portMAX_DELAY); + String data = queue.front(); + queue.pop(); + xSemaphoreGive(xMutex); + + RequestInformation requestInformation{POST, "influxdb.qe-forte.uibk.ac.at", + 80, "/api/v2/write?org=QE&bucket=esp32test&precision=s", + data, headers}; + + try { + connectionManager.connect(requestInformation); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM, "Error sending data: %s\n", e.what()); + } + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "Data sent: %s\n", data.c_str()); + } + + connectionManager.gprsDisconnect(); + delay(5000L); + + if (!modem.isGprsConnected()) { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM, "GPRS disconnected\n"); + } else { + esp_log_write(ESP_LOG_ERROR, TAG_GSM, "GPRS not disconnected\n"); + } + + connectionManager.modemPowerOff(); - delay(1000); + delay(10000); } diff --git a/host/host_central_mast/src/main2.cpp b/host/host_central_mast/src/main2.cpp deleted file mode 100644 index aaedda0570dfadd1926531602f0c527d39e6f870..0000000000000000000000000000000000000000 --- a/host/host_central_mast/src/main2.cpp +++ /dev/null @@ -1,304 +0,0 @@ -///************************************************************** -// * -// * This sketch connects to a website and downloads a page. -// * It can be used to perform HTTP/RESTful API calls. -// * -// * TinyGSM Getting Started guide: -// * https://tiny.cc/tinygsm-readme -// * -// **************************************************************/ -// -//// Select your modem: -//// #define TINY_GSM_MODEM_SIM800 -//// #define TINY_GSM_MODEM_SIM808 -//// #define TINY_GSM_MODEM_SIM868 -//// #define TINY_GSM_MODEM_SIM900 -//#define TINY_GSM_MODEM_SIM7000 -//// #define TINY_GSM_MODEM_SIM7000SSL -//// #define TINY_GSM_MODEM_SIM7080 -//// #define TINY_GSM_MODEM_SIM5360 -//// #define TINY_GSM_MODEM_SIM7600 -//// #define TINY_GSM_MODEM_UBLOX -//// #define TINY_GSM_MODEM_SARAR4 -//// #define TINY_GSM_MODEM_M95 -//// #define TINY_GSM_MODEM_BG96 -//// #define TINY_GSM_MODEM_A6 -//// #define TINY_GSM_MODEM_A7 -//// #define TINY_GSM_MODEM_M590 -//// #define TINY_GSM_MODEM_MC60 -//// #define TINY_GSM_MODEM_MC60E -//// #define TINY_GSM_MODEM_ESP8266 -//// #define TINY_GSM_MODEM_XBEE -//// #define TINY_GSM_MODEM_SEQUANS_MONARCH -// -//// Set serial for debug console (to the Serial Monitor, default speed 115200) -//#define SerialMon Serial -// -//// Set serial for AT commands (to the module) -//// Use Hardware Serial on Mega, Leonardo, Micro -//#ifndef __AVR_ATmega328P__ -//#define SerialAT Serial1 -// -//// or Software Serial on Uno, Nano -//#else -//#include <SoftwareSerial.h> -//SoftwareSerial SerialAT(2, 3); // RX, TX -//#endif -// -//// Increase RX buffer to capture the entire response -//// Chips without internal buffering (A6/A7, ESP8266, M590) -//// need enough space in the buffer for the entire response -//// else data will be lost (and the http library will fail). -//#if !defined(TINY_GSM_RX_BUFFER) -//#define TINY_GSM_RX_BUFFER 650 -//#endif -// -//// See all AT commands, if wanted -//#define DUMP_AT_COMMANDS -// -//// Define the serial console for debug prints, if needed -//#define TINY_GSM_DEBUG SerialMon -// -//// Range to attempt to autobaud -//// NOTE: DO NOT AUTOBAUD in production code. Once you've established -//// communication, set a fixed baud rate using modem.setBaud(#). -//#define GSM_AUTOBAUD_MIN 9600 -//#define GSM_AUTOBAUD_MAX 115200 -// -//// Add a reception delay, if needed. -//// This may be needed for a fast processor at a slow baud rate. -//// #define TINY_GSM_YIELD() { delay(2); } -// -//// Uncomment this if you want to use SSL -//// #define USE_SSL -// -//// Define how you're planning to connect to the internet. -//// This is only needed for this example, not in other code. -//#define TINY_GSM_USE_GPRS true -//#define TINY_GSM_USE_WIFI false -// -//// set GSM PIN, if any -//#define GSM_PIN "" -// -//// Your GPRS credentials, if any -//const char apn[] = "m2m.public.at"; -//const char gprsUser[] = ""; -//const char gprsPass[] = ""; -// -//// Your WiFi connection credentials, if applicable -//const char wifiSSID[] = "YourSSID"; -//const char wifiPass[] = "YourWiFiPass"; -// -//// Server details -//const char server[] = "vsh.pp.ua"; -//const char resource[] = "/TinyGSM/logo.txt"; -// -//#include <TinyGsmClient.h> -//#include <WiFi.h> -//#include <esp_now.h> -// -//// Just in case someone defined the wrong thing.. -//#if TINY_GSM_USE_GPRS && not defined TINY_GSM_MODEM_HAS_GPRS -//#undef TINY_GSM_USE_GPRS -//#undef TINY_GSM_USE_WIFI -//#define TINY_GSM_USE_GPRS false -//#define TINY_GSM_USE_WIFI true -//#endif -//#if TINY_GSM_USE_WIFI && not defined TINY_GSM_MODEM_HAS_WIFI -//#undef TINY_GSM_USE_GPRS -//#undef TINY_GSM_USE_WIFI -//#define TINY_GSM_USE_GPRS true -//#define TINY_GSM_USE_WIFI false -//#endif -// -//#ifdef DUMP_AT_COMMANDS -//#include <StreamDebugger.h> -//StreamDebugger debugger(SerialAT, SerialMon); -//TinyGsm modem(debugger); -//#else -//TinyGsm modem(SerialAT); -//#endif -// -//#ifdef USE_SSL -//TinyGsmClientSecure client_satellite(modem); -//const int port = 443; -//#else -//TinyGsmClient client_satellite(modem); -//const int port = 80; -//#endif -// -//TaskHandle_t Task1; -// -//void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) -//{ -// // go to sleep -//} -// -//void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) -//{ -// // print mac -// Serial.print("Message recieved: Core "); -// Serial.println(xPortGetCoreID()); -// for (int i = 0; i < 6; i++) { -// Serial.print(mac[i], HEX); -// Serial.print(":"); -// } -// Serial.println(); -// char data[len]; -// memcpy(data, incomingData, len); -// Serial.println(data); -//} -// -//[[noreturn]] void Task1code(void *parameter) -//{ -// Serial.println(xPortGetCoreID()); -// WiFi.mode(WIFI_STA); -// Serial.println("ESPNow init"); -// if (esp_now_init() != ESP_OK) { -// // initialization failed -// Serial.println("ESPNow init failed"); -// // not sure about this -// } -// Serial.println("ESPNow init success"); -// esp_now_register_recv_cb(on_data_recv); -// -// while (true) { -// } -//} -// -//void setup2() -//{ -// // Set console baud rate -// SerialMon.begin(115200); -// delay(10); -// -// // !!!!!!!!!!! -// // Set your reset, enable, power pins here -// // !!!!!!!!!!! -// -// SerialMon.println("Wait..."); -// -// xTaskCreatePinnedToCore(Task1code, /* Function to implement the task */ -// "Task1", /* Name of the task */ -// 10000, /* Stack size in words */ -// nullptr, /* Task input parameter */ -// 0, /* Priority of the task */ -// &Task1, /* Task handle. */ -// 0); /* Core where the task should run */ -// -// // Set GSM module baud rate -// // TinyGsmAutoBaud(SerialAT, GSM_AUTOBAUD_MIN, GSM_AUTOBAUD_MAX); -// modem.setBaud(9600); -// // SerialAT.begin(9600); -// delay(6000); -// -// // Restart takes quite some time -// // To skip it, call init() instead of restart() -// SerialMon.println("Initializing modem..."); -// modem.restart(); -// // modem.init(); -// -// String modemInfo = modem.getModemInfo(); -// SerialMon.print("Modem Info: "); -// SerialMon.println(modemInfo); -// -//#if TINY_GSM_USE_GPRS -// // Unlock your SIM card with a PIN if needed -// if (GSM_PIN && modem.getSimStatus() != 3) { -// modem.simUnlock(GSM_PIN); -// } -//#endif -//} -// -//void loop2() -//{ -//#if TINY_GSM_USE_WIFI -// // Wifi connection parameters must be set before waiting for the network -// SerialMon.print(F("Setting SSID/password...")); -// if (!modem.networkConnect(wifiSSID, wifiPass)) { -// SerialMon.println(" fail"); -// delay(10000); -// return; -// } -// SerialMon.println(" success"); -//#endif -// -//#if TINY_GSM_USE_GPRS && defined TINY_GSM_MODEM_XBEE -// // The XBee must run the gprsConnect function BEFORE waiting for network! -// modem.gprsConnect(apn, gprsUser, gprsPass); -//#endif -// -// SerialMon.print("Waiting for network..."); -// if (!modem.waitForNetwork()) { -// SerialMon.println(" fail"); -// delay(10000); -// return; -// } -// SerialMon.println(" success"); -// -// if (modem.isNetworkConnected()) { -// SerialMon.println("Network connected"); -// } -// -//#if TINY_GSM_USE_GPRS -// // GPRS connection parameters are usually set after network registration -// SerialMon.print(F("Connecting to ")); -// SerialMon.print(apn); -// if (!modem.gprsConnect(apn, gprsUser, gprsPass)) { -// SerialMon.println(" fail"); -// delay(10000); -// return; -// } -// SerialMon.println(" success"); -// -// if (modem.isGprsConnected()) { -// SerialMon.println("GPRS connected"); -// } -//#endif -// -// SerialMon.print("Connecting to "); -// SerialMon.println(server); -// if (!client_satellite.connect(server, port)) { -// SerialMon.println(" fail"); -// delay(10000); -// return; -// } -// SerialMon.println(" success"); -// -// // Make a HTTP GET request: -// SerialMon.println("Performing HTTP GET request..."); -// client_satellite.print(String("GET ") + resource + " HTTP/1.1\r\n"); -// client_satellite.print(String("Host: ") + server + "\r\n"); -// client_satellite.print("Connection: close\r\n\r\n"); -// client_satellite.println(); -// -// uint32_t timeout = millis(); -// while (client_satellite.connected() && millis() - timeout < 10000L) { -// // Print available data -// while (client_satellite.available()) { -// char c = client_satellite.read(); -// SerialMon.print(c); -// timeout = millis(); -// } -// } -// SerialMon.println(); -// -// // Shutdown -// -// client_satellite.stop(); -// SerialMon.println(F("Server disconnected")); -// -//#if TINY_GSM_USE_WIFI -// modem.networkDisconnect(); -// SerialMon.println(F("WiFi disconnected")); -//#endif -//#if TINY_GSM_USE_GPRS -// modem.gprsDisconnect(); -// SerialMon.println(F("GPRS disconnected")); -//#endif -// -// // Do nothing forevermore -// while (true) { -// delay(1000); -// } -//}