From 38965ef1e1f31a6e53c0f64738ab22b455e67815 Mon Sep 17 00:00:00 2001 From: Zoe Pfister <zoe.pfister@student.uibk.ac.at> Date: Thu, 19 Jan 2023 13:54:10 +0100 Subject: [PATCH] Resend Data capabilities --- .../lib/ResendManager/ResendManager.cpp | 72 ++ .../lib/ResendManager/ResendManager.h | 35 + .../lib/ResendManager/ResendPointType.h | 15 + .../lib/Utilities/Definitions.h | 28 + .../lib/Utilities/SDCardException.h | 22 + .../lib/Utilities/SDSetupException.h | 21 + .../lib/Utilities/Utilities.cpp | 151 ++++ .../lib/Utilities/Utilities.h | 25 + host/host_central_mast/src/main.cpp | 838 ++++++++---------- 9 files changed, 739 insertions(+), 468 deletions(-) create mode 100644 host/host_central_mast/lib/ResendManager/ResendManager.cpp create mode 100644 host/host_central_mast/lib/ResendManager/ResendManager.h create mode 100644 host/host_central_mast/lib/ResendManager/ResendPointType.h create mode 100644 host/host_central_mast/lib/Utilities/Definitions.h create mode 100644 host/host_central_mast/lib/Utilities/SDCardException.h create mode 100644 host/host_central_mast/lib/Utilities/SDSetupException.h create mode 100644 host/host_central_mast/lib/Utilities/Utilities.cpp create mode 100644 host/host_central_mast/lib/Utilities/Utilities.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 0000000..1c8eb12 --- /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 = 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 + createDirectory("/resend"); +} + +void ResendManager::storeForResend(const String &messageToBeSend) { + // create file + String filename = String(nextResendFileId); + writeFile(messageToBeSend, resendDirectoryPath + "/" + filename); + ResendManager::incrementCount(); +} + +std::optional<ResendPointType> ResendManager::loadNextToBeResendMessage() { + // get first file in resend directory + auto filename = getFirstFileNameInDirectory(resendDirectoryPath.c_str()); + + if (filename.has_value()) { + + // read file + auto message = 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 0000000..5d7495a --- /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 0000000..11a4bfc --- /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 0000000..ed28c03 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/Definitions.h @@ -0,0 +1,28 @@ +// +// Created by zoe on 1/12/23. +// + +#ifndef HOST_CENTRAL_MAST_DEFINITIONS_H +#define HOST_CENTRAL_MAST_DEFINITIONS_H + +#include "Arduino.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 + +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 0000000..039d703 --- /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/SDSetupException.h b/host/host_central_mast/lib/Utilities/SDSetupException.h new file mode 100644 index 0000000..09c1ed3 --- /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 0000000..21f3aca --- /dev/null +++ b/host/host_central_mast/lib/Utilities/Utilities.cpp @@ -0,0 +1,151 @@ +// +// Created by zoe on 1/9/23. +// + +#include "Utilities.h" + +void setupSDCard(); +void saveStringToSDCard(const std::string &dataString); +std::list<String> getFilesInDirectory(const String &dirname) { + 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) { + + 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, "getLastFileInDirectory", "Last file name: %s\n", file.name()); + return file.name(); + } else { + esp_log_write(ESP_LOG_INFO, "getLastFileInDirectory", "No file found\n"); + return std::nullopt; + } +} +File openDirectory(const String &dirname) { + File root = SD.open(dirname); + if (!root) { + esp_log_write(ESP_LOG_ERROR, "openDirectory", "Failed to open directory\n"); + throw; + } + if (!root.isDirectory()) { + esp_log_write(ESP_LOG_ERROR, "openDirectory", "Not a directory\n"); + throw; + } + return root; +} +std::optional<String> getFirstFileNameInDirectory(const String &dirname) { + + File root = openDirectory(dirname); + root.rewindDirectory(); + + File file = root.openNextFile(); + if (file) { + esp_log_write(ESP_LOG_INFO, "getFirstFileNameInDirectory", "file found: %s\n", file.name()); + return file.name(); + } else { + esp_log_write(ESP_LOG_INFO, "getFirstFileNameInDirectory", "no file found\n"); + return std::nullopt; + } +} + +File openForWrite(const String &filePath) { + File file = SD.open(filePath, FILE_WRITE); + if (!file) { + esp_log_write(ESP_LOG_ERROR, "SD", "Failed to open file for writing\n"); + throw; + } + return file; +} + +File openForRead(const String &filePath) { + File file = SD.open(filePath, FILE_READ); + if (!file) { + esp_log_write(ESP_LOG_ERROR, "SD", "Failed to open file for reading\n"); + throw; + } + return file; +} + +void writeFile(const String &messageToBeSend, const String &filePath) { + File file = openForWrite(filePath); + if (file.print(messageToBeSend)) { + esp_log_write(ESP_LOG_INFO, "SD", "File written %s\n", filePath.c_str()); + } else { + esp_log_write(ESP_LOG_ERROR, "SD", "Write failed %s\n", filePath.c_str()); + throw; + } + file.close(); +} +String readFile(const String &filePath) { + File file = openForRead(filePath); + + String ret; + + while (file.available()) { + ret += (char)file.read(); + } + + file.close(); + return ret; +} + +void createDirectory(const String &dirname) { + if (!SD.exists(dirname)) { + SD.mkdir(dirname); + esp_log_write(ESP_LOG_INFO, "createDirectory", "Created directory: %s\n", dirname.c_str()); + } else { + esp_log_write(ESP_LOG_WARN, "createDirectory", "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, "Utilities", "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, "Utilities", "%s\n", sdcardSizeString.c_str()); + } +} + +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, "Utilities", "error opening datalog.txt\n"); + throw SDCardException("error opening datalog.txt"); + } +} \ 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 0000000..1156c02 --- /dev/null +++ b/host/host_central_mast/lib/Utilities/Utilities.h @@ -0,0 +1,25 @@ +// +// Created by zoe on 1/9/23. +// + +#ifndef HOST_CENTRAL_MAST_UTILITIES_H +#define HOST_CENTRAL_MAST_UTILITIES_H + +#include "SD.h" +#include "SDCardException.h" +#include "SDSetupException.h" +#include <Arduino.h> +#include <Definitions.h> +#include <WString.h> +#include <list> + +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); +#endif // HOST_CENTRAL_MAST_UTILITIES_H diff --git a/host/host_central_mast/src/main.cpp b/host/host_central_mast/src/main.cpp index 9a56051..850beac 100644 --- a/host/host_central_mast/src/main.cpp +++ b/host/host_central_mast/src/main.cpp @@ -1,6 +1,7 @@ #include "FS.h" #include "SD.h" #include "SPI.h" +#include "Utilities.h" #include "time.h" #include <Arduino.h> #include <ArduinoJson.h> @@ -46,32 +47,16 @@ 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) +#include "Definitions.h" -#define UART_BAUD 115200 -#define PIN_DTR 25 -#define PIN_TX 27 -#define PIN_RX 26 -#define PWR_PIN 4 +enum MessageType { dataAck, hostChange }; +typedef struct response { + MessageType type; + uint8_t mac[6]; + long time; +} response; -#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}; +uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; esp_now_peer_info_t broadcast = {}; response announce = {}; @@ -83,483 +68,400 @@ 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 +void on_data_sent(const uint8_t *mac_addr, esp_now_send_status_t status) { + // go to sleep } 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; +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; } 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 = {}; - 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.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); - - 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); - } - } - - 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); - - 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.c_str(), - (success == ESP_OK) ? "Response sent\n" : "Failed to respond\n"); -} +void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) { + 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); + + 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); + } + } + + 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); -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; + 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); + + 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.c_str(), (success == ESP_OK) ? "Response sent\n" : "Failed to respond\n"); } -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 - } + +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.c_str(), "Error while parsing json: %s\n", error.f_str()); - // TODO error handling - } - return doc; +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; +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; } -[[noreturn]] void ESPNOWReceiveTask(void *parameter) -{ - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "ESPNOWReceiveTask started on core %d\n", xPortGetCoreID()); +[[noreturn]] void ESPNOWReceiveTask(void *parameter) { + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "ESPNOWReceiveTask started on core %d\n", xPortGetCoreID()); - WiFi.mode(WIFI_STA); + WiFi.mode(WIFI_STA); - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Initialising ESPNow...\n"); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Initialising ESPNow...\n"); - 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); - } + 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); + } - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Initialising ESPNow SUCCESS\n"); + esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Initialising ESPNow SUCCESS\n"); - esp_now_register_recv_cb(on_data_recv); + esp_now_register_recv_cb(on_data_recv); - while (true) { - } + while (true) { + } } -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; -} +time_t timeToUnixEpochSeconds(const std::string &time) { + // 22/10/27,10:16:20+00 + struct tm tm {}; + time_t dateInEpoch = 0; -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()); -} -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()); - } -} + if (strptime(time.c_str(), "%y/%m/%d,%T+00", &tm)) { + time_t curTime; + struct tm *timeinfo; + + timeinfo = localtime(&curTime); -// I don't think this does anything. Copied from the example -void turnOffLEDs() -{ // Set LED OFF - pinMode(LED_PIN, OUTPUT); - digitalWrite(LED_PIN, HIGH); + 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; - pinMode(PWR_PIN, OUTPUT); - digitalWrite(PWR_PIN, HIGH); - delay(300); - digitalWrite(PWR_PIN, LOW); + dateInEpoch = mktime(timeinfo); + } + return dateInEpoch; } -const String INFLUXDB_TOKEN = - "dUh2gbVLv7e3egqocxriDsJQNUacA9qZ5YXsYtdnVAglnHgy4nx-jDVO7nGlSF34BosfnuwnUDaviC7dQeC5RQ=="; +void setup() { + // Set console baud rate + Serial.begin(115200); + delay(10); + setupSDCard(SD_MISO, SD_MOSI, SD_SCLK, SD_CS); + + // 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()); + + 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()); +} struct RequestInformation { - String method; - String host; - String path; - String body; + 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; +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 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 - - delay(1000); +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"); + } + delay(1000); } -- GitLab