diff --git a/host/host_central_mast/src/main.cpp b/host/host_central_mast/src/main.cpp index 850beacb573f5cbca01def4d59d8400f2089349f..376579e3ccb6ed5320878b5d51aa518fd54c0279 100644 --- a/host/host_central_mast/src/main.cpp +++ b/host/host_central_mast/src/main.cpp @@ -1,7 +1,9 @@ +#define TINY_GSM_MODEM_SIM7000 + +#include "ConnectionManager.h" #include "FS.h" #include "SD.h" #include "SPI.h" -#include "Utilities.h" #include "time.h" #include <Arduino.h> #include <ArduinoJson.h> @@ -14,7 +16,7 @@ static const std::string TAG = "MAIN"; static const std::string TAG_ESPNOW = "ESPNOW"; -static const std::string TAG_GSM = "GSM"; +// static const std::string TAG_GSM = "GSM"; /* FILE: AllFunctions.ino @@ -22,7 +24,6 @@ static const std::string TAG_GSM = "GSM"; PURPOSE: Test functionality */ -#define TINY_GSM_MODEM_SIM7000 #define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb #define SerialAT Serial1 @@ -37,18 +38,38 @@ const char apn[] = "m2m.public.at"; // SET TO YOUR APN const char gprsUser[] = ""; const char gprsPass[] = ""; +#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 GPRSCredentials gprsCredentials = GPRSCredentials("m2m.public.at", "", ""); + #include <TinyGsmClient.h> #ifdef DUMP_AT_COMMANDS // if enabled it requires the streamDebugger lib + #include <StreamDebugger.h> + StreamDebugger debugger(SerialAT, Serial); TinyGsm modem(debugger); +ConnectionManager connectionManager{modem, gprsCredentials, + ConnectionManager::ModemPins{PIN_DTR, PIN_TX, PIN_RX, PWR_PIN}}; + #else TinyGsm modem(SerialAT); #endif -#include "Definitions.h" - enum MessageType { dataAck, hostChange }; typedef struct response { MessageType type; @@ -56,6 +77,10 @@ typedef struct response { long time; } response; +#include <TimeManager.h> + +TimeManager timeManager{modem}; + uint8_t BROADCAST_MAC[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; esp_now_peer_info_t broadcast = {}; response announce = {}; @@ -106,10 +131,43 @@ int vprintf_into_sd(const char *szFormat, va_list args) { } 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(); + +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["sensor-protocol"] = doc["protocol"].as<String>(); + serverDoc["protocol-address"] = doc["channel"].as<String>(); + serverDoc["hardware-name"] = 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.c_str(), "Server readable data: %s\n", serverString.c_str()); + return serverString; +} + 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 @@ -137,6 +195,8 @@ void on_data_recv(const uint8_t *mac, const uint8_t *incomingData, int len) { // doc["timestamp"] = rtc.getEpoch(); doc["clientMac"] = macAddress; + documentToServerReadableString(doc); + // serialize json document again std::string dataString{}; serializeJson(doc, dataString); @@ -172,6 +232,24 @@ String documentToLineProtocolString(const DynamicJsonDocument &doc) { + 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); @@ -181,6 +259,7 @@ DynamicJsonDocument parseReceivedJsonData(char *data) { } return doc; } + String getMacAddressAsString(const uint8_t *mac) { String macAddress; for (int i = 0; i < 6; i++) { @@ -207,6 +286,22 @@ String getMacAddressAsString(const uint8_t *mac) { 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.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!"); + } + while (true) { } } @@ -239,13 +334,15 @@ void setup() { // Set console baud rate Serial.begin(115200); delay(10); - setupSDCard(SD_MISO, SD_MOSI, SD_SCLK, SD_CS); + 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_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); @@ -264,46 +361,35 @@ void setup() { // 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"); - } +// waitResponse default is 1000ms - 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"); +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 { - esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Mac announced!"); + 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()); } } -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()); + +// 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); } -struct RequestInformation { - String method; - String host; - String path; - String body; -}; +const String INFLUXDB_TOKEN = + "dUh2gbVLv7e3egqocxriDsJQNUacA9qZ5YXsYtdnVAglnHgy4nx-jDVO7nGlSF34BosfnuwnUDaviC7dQeC5RQ=="; String buildRequest(const RequestInformation &requestInformation) { String request = ""; @@ -320,13 +406,12 @@ String buildRequest(const RequestInformation &requestInformation) { void loop() { + connectionManager.modemPowerOn(); + delay(5000L); + // 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"); - } + connectionManager.restartModem(); announce.time = rtc.getEpoch(); if (esp_now_send(BROADCAST_MAC, (uint8_t *)&announce, sizeof(announce)) != ESP_OK) { @@ -335,133 +420,178 @@ void loop() { esp_log_write(ESP_LOG_DEBUG, TAG_ESPNOW.c_str(), "Mac announced!\n"); } + connectionManager.disableGPS(); + + try { + connectionManager.setNetworkMode(LTE_ONLY); + connectionManager.setPreferredMode(CAT_M); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM.c_str(), "Error setting network mode: %s\n", e.what()); + } + String name = modem.getModemName(); - delay(500); - esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Modem Name %s\n", name.c_str()); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.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 "); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Modem Info: %s\n", modemInfo.c_str()); + + connectionManager.unlockSimCard(); + + if (!connectionManager.waitForNetwork()) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM.c_str(), "Network not available\n"); + return; } - modem.sendAT("+CFUN=0 "); - if (modem.waitResponse(10000L) != 1) { - DBG(" +CFUN=0 false "); + if (connectionManager.isNetworkConnected()) { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Network connected\n"); } + 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 "); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Connecting to %s\n", gprsCredentials.apn.c_str()); + if (!connectionManager.gprsConnect()) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM.c_str(), "GPRS not connected\n"); 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); + bool res = modem.isGprsConnected(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "GPRS connected: %s\n", res ? "true" : "false"); - /*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); + String ccid = modem.getSimCCID(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "CCID: %s\n", ccid.c_str()); - modem.sendAT("+CFUN=1 "); - if (modem.waitResponse(10000L) != 1) { - DBG(" +CFUN=1 false "); + String imei = modem.getIMEI(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "IMEI: %s\n", imei.c_str()); + + String imsi = modem.getIMSI(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "IMSI: %s\n", imsi.c_str()); + + String cop = modem.getOperator(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Operator: %s\n", cop.c_str()); + + IPAddress local = modem.localIP(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Local IP: %s\n", local.toString().c_str()); + + int csq = modem.getSignalQuality(); + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Signal quality: %d\n", csq); + + timeManager.syncNTP("pool.ntp.org", "at.pool.ntp.org"); + + try { + timeManager.writeModemTimeToRTC(); + } catch (const std::exception &e) { + esp_log_write(ESP_LOG_ERROR, TAG_GSM.c_str(), "Error writing time to rtc: %s\n", e.what()); } - 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(); + // RequestInformation requestInformation("GET", "vsh.pp.ua", "/TinyGSM/logo.txt", ""); + // auto s = connectionManager.connect(80, requestInformation); + // esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Response: %s\n", s.c_str()); - 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; + connectionManager.gprsDisconnect(); + delay(5000L); + + if (!modem.isGprsConnected()) { + esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "GPRS disconnected\n"); } 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"); + esp_log_write(ESP_LOG_ERROR, TAG_GSM.c_str(), "GPRS not disconnected\n"); } - delay(1000); + + connectionManager.modemPowerOff(); + + // put readings into array + // protocol address + // hardware name != sensor name + + // + // connectionManager.setBand(ConnectionManager::BAND_CAT_M, 20); + // + // delay(200); + // + // connectionManager.setModemFunctionalityLevel(ModemFunctionalityLevel::FULL); + // delay(200); + + // modem.disableGPS(); + // delay(200); + // esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "%s\n", String(modem.getSignalQuality()).c_str()); + // + // esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Trying to connect to network\n"); + // + // Serial.println("\n\n\nWaiting for network..."); + // if (!modem.waitForNetwork()) { + // delay(10000); + // return; + // } + + // esp_log_write(ESP_LOG_DEBUG, TAG.c_str(), "Waiting for network...\n"); + // if (!connectionManager.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); + // connectionManager.gprsConnect(); + // delay(200); + // delay(200); + // syncUTCTimeToRTC(); + // + // // 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); + // String method = "POST"; + // + // RequestInformation requestInformation = RequestInformation(method, url, path, 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()); + // + // connectionManager.connect(80, requestInformation); + // // 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(5000); }