Skip to content
Snippets Groups Projects
Verified Commit a551c486 authored by Zoe Michaela Dietmar Pfister's avatar Zoe Michaela Dietmar Pfister :gay_pride_flag:
Browse files

Merge remote-tracking branch 'origin/38-ntp-server-redundancy' into develop

# Conflicts:
#	host/host_central_mast/src/main.cpp
parents 1a9fc691 67602608
No related branches found
No related tags found
2 merge requests!39Merge Develop into Main,!23NTP Refactor into dev
Showing
with 541 additions and 0 deletions
//
// Created by zoe on 12/20/22.
//
#include "ConnectionManager.h"
#include "LTEConnectionException.hpp"
#include "ModemFunctionalityLevel.h"
#include <utility>
ConnectionManager::ConnectionManager(TinyGsm &modem, GPRSCredentials credentials, ModemPins modemPins)
: modem(modem), credentials(std::move(credentials)), modemPins(modemPins) {}
void ConnectionManager::restartModem() {
// Restart takes quite some time
// To skip it, call init() instead of restart()
esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Restarting modem...\n");
if (!modem.restart()) {
esp_log_write(ESP_LOG_WARN, TAG_GSM.c_str(),
"Failed to restart modem, attempting to continue without restarting\n");
}
}
void ConnectionManager::initModem() {
esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "Initializing modem...\n");
if (!modem.init()) {
esp_log_write(ESP_LOG_WARN, TAG_GSM.c_str(), "Failed to initialize modem, attempting to continue...\n");
}
setModemFunctionalityLevel(MINIMAL);
}
void ConnectionManager::setModemFunctionalityLevel(
ModemFunctionalityLevel functionalityLevel) { // This sets the level of functionality of the modem. Full
// functionality is where the highest
// level of power is drawn. Minimum functionality (default) is where the lowest level of power is drawn.
// https://m2msupport.net/m2msupport/atcfun-set-phone-functionality/
modem.sendAT(("+CFUN=" + std::to_string(functionalityLevel) + " ").c_str());
if (modem.waitResponse(10000L) != 1) {
DBG(" +CFUN=0 false ");
}
}
void ConnectionManager::disableGPS() {
// Set SIM7000G GPIO4 LOW ,turn off GPS power
// CMD:AT+SGPIO=0,4,1,0
// Only in version 20200415 is there a function to control GPS power
modem.sendAT("+SGPIO=0,4,1,0");
if (modem.waitResponse(10000L) != 1) {
DBG(" SGPIO=0,4,1,0 false ");
}
modem.disableGPS();
}
void ConnectionManager::enableGPS() {
// Set SIM7000G GPIO4 LOW ,turn on GPS power
// CMD:AT+SGPIO=0,4,1,1
// Only in version 20200415 is there a function to control GPS power
modem.sendAT("+SGPIO=0,4,1,1");
if (modem.waitResponse(10000L) != 1) {
DBG(" SGPIO=0,4,1,1 false ");
}
modem.enableGPS();
}
void ConnectionManager::setNetworkMode(NetworkMode networkMode) {
String res;
res = modem.setNetworkMode(networkMode);
if (res != "1") {
DBG("setNetworkMode false ");
throw LTEConnectionException("Failed to set network mode");
}
}
void ConnectionManager::setPreferredMode(Mode mode) {
String res;
res = modem.setPreferredMode(mode);
if (res != "1") {
DBG("setPreferredMode false ");
return;
}
}
void ConnectionManager::setBand(BandMode bandMode, int band) {
/*AT+CBANDCFG=<mode>,<band>[,<band>…]
* <mode> "CAT-M" "NB-IOT"
* <band> The value of <band> must is in the band list of getting from AT+CBANDCFG=?
* For example, my SIM card carrier "NB-iot" supports B8. I will configure +CBANDCFG= "Nb-iot ",8
*/
bandMode == BandMode::BAND_CAT_M ? modem.sendAT(("+CBANDCFG=\"CAT-M\"," + std::to_string(band) + " ").c_str())
: modem.sendAT(("+CBANDCFG=\"NB-IOT\"," + std::to_string(band) + " ").c_str());
if (modem.waitResponse(10000L) != 1) {
DBG(" +CBANDCFG=\"NB-IOT\" ");
}
}
bool ConnectionManager::gprsConnect() {
return modem.gprsConnect(credentials.apn.c_str(), credentials.user.c_str(), credentials.password.c_str());
}
bool ConnectionManager::isNetworkConnected() {
return modem.isNetworkConnected();
}
std::string ConnectionManager::connect(int port, RequestInformation requestInformation) {
TinyGsmClient client{modem, 0};
if (!client.connect(requestInformation.host.c_str(), port)) {
throw LTEConnectionException("Failed to connect to host");
}
String request = requestInformation.buildRequest();
client.print(request);
String line;
// print response
while (client.connected()) {
line += client.readStringUntil('\n');
if (line == "\r") {
esp_log_write(ESP_LOG_DEBUG, TAG_GSM.c_str(), "headers received\n");
break;
}
}
client.stop();
return line.c_str();
}
void ConnectionManager::modemPowerOn() {
pinMode(modemPins.pwr, OUTPUT);
digitalWrite(modemPins.pwr, LOW);
delay(1000);
digitalWrite(modemPins.pwr, HIGH);
}
void ConnectionManager::modemPowerOff() {
modem.sendAT("+CPOWD=1");
if (modem.waitResponse(10000L) != 1) {
esp_log_write(ESP_LOG_WARN, TAG_GSM.c_str(), "Failed to power off modem\n");
}
modem.poweroff();
pinMode(modemPins.pwr, OUTPUT);
digitalWrite(modemPins.pwr, LOW);
delay(1500);
digitalWrite(modemPins.pwr, HIGH);
}
void ConnectionManager::unlockSimCard() {
// Unlock your SIM card with a PIN if needed
if (modem.getSimStatus() != 3) {
modem.simUnlock(credentials.password.c_str());
}
}
bool ConnectionManager::waitForNetwork() {
if (!modem.waitForNetwork(600000L, true)) {
delay(10000);
return false;
}
return true;
}
bool ConnectionManager::gprsDisconnect() {
return modem.gprsDisconnect();
}
//
// Created by zoe on 12/20/22.
//
#ifndef HOST_CENTRAL_MAST_CONNECTIONMANAGER_H
#define HOST_CENTRAL_MAST_CONNECTIONMANAGER_H
#define TINY_GSM_MODEM_SIM7000
#include "GPRSCredentials.h"
#include "Mode.h"
#include "ModemFunctionalityLevel.h"
#include "NetworkMode.h"
#include "RequestInformation.h"
#include "StreamDebugger.h"
#include <TinyGsmClient.h>
static const std::string TAG_GSM = "GSM";
class ConnectionManager {
public:
struct ModemPins {
int dtr;
int tx;
int rx;
int pwr;
};
private:
TinyGsm &modem;
const GPRSCredentials credentials;
ModemPins modemPins;
public:
ConnectionManager(TinyGsm &modem, GPRSCredentials credentials, ModemPins modemPins);
std::string connect(int port, RequestInformation requestInformation);
void enableGPS();
void disableGPS();
void initModem();
void restartModem();
void setNetworkMode(NetworkMode networkMode = NetworkMode::LTE_ONLY);
void setPreferredMode(Mode mode = Mode::CAT_M);
void setModemFunctionalityLevel(ModemFunctionalityLevel functionalityLevel = MINIMAL);
enum BandMode { BAND_CAT_M, BAND_NB_IoT };
void setBand(BandMode bandMode, int band);
bool gprsConnect();
bool gprsDisconnect();
bool isNetworkConnected();
void modemPowerOn();
void modemPowerOff();
void unlockSimCard();
bool waitForNetwork();
};
#endif // HOST_CENTRAL_MAST_CONNECTIONMANAGER_H
//
// 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
//
// 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
//
// 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
//
// 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
//
// 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
//
// Created by zoe on 1/9/23.
//
#ifndef HOST_CENTRAL_MAST_REQUESTINFORMATION_H
#define HOST_CENTRAL_MAST_REQUESTINFORMATION_H
#include <Arduino.h>
struct RequestInformation {
String method;
String host;
String path;
String body;
RequestInformation(String method, String host, String path, String body) {
this->method = method;
this->host = host;
this->path = path;
this->body = body;
}
// TODO: Move to configuration file
const String INFLUXDB_TOKEN =
"dUh2gbVLv7e3egqocxriDsJQNUacA9qZ5YXsYtdnVAglnHgy4nx-jDVO7nGlSF34BosfnuwnUDaviC7dQeC5RQ==";
String buildRequest() {
String request = "";
request += method + " " + path + " HTTP/1.1\r\n";
request += "Host: " + host + "\r\n";
request += "Authorization: Token " + INFLUXDB_TOKEN + "\r\n";
request += "User-Agent: ESP32\r\n";
request += "Content-Type: text/plain\r\n";
request += "Content-Length: " + String(body.length()) + "\r\n";
if (body.length() > 0) {
request += "\r\n";
request += body;
}
return request;
}
};
#endif // HOST_CENTRAL_MAST_REQUESTINFORMATION_H
//
// 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
//
// Created by zoe on 12/19/22.
//
#ifndef HOST_CENTRAL_MAST_STRINGTOTIMECONVERSIONEXCEPTION_HPP
#define HOST_CENTRAL_MAST_STRINGTOTIMECONVERSIONEXCEPTION_HPP
#include <exception>
#include <string>
#include "TimeManager.h"
class StringToTimeConversionException : public std::exception {
public:
explicit StringToTimeConversionException(const std::string &message) : message(message) {}
const char *what() const noexcept override {
esp_log_write(ESP_LOG_ERROR, "TimeManager", "Error converting time to epoch: %s",
message.c_str());
return message.c_str();
}
private:
std::string message;
};
#endif //HOST_CENTRAL_MAST_STRINGTOTIMECONVERSIONEXCEPTION_HPP
//
// Created by zoe on 12/19/22.
//
#include "TimeManager.h"
TimeManager::TimeManager(TinyGsmSim7000 &modem) : modem(modem) {}
void TimeManager::syncNTP(const std::string &ntpServer, const std::string &backupNtpServer) {
// create list of ntp servers
std::vector<std::string> ntpServers;
ntpServers.push_back(ntpServer);
ntpServers.push_back(backupNtpServer);
tryNtpServerSync(backupNtpServer, ntpServers);
}
void TimeManager::tryNtpServerSync(const std::string &backupNtpServer,
std::vector<std::string> &ntpServers) { // try to sync ntp with each server
for (const auto &server : ntpServers) {
try {
synchronizeNTPWithModem(server);
return;
} catch (NTPException &e) {
if (server == backupNtpServer) {
esp_log_write(ESP_LOG_ERROR, TAG, "Failed to sync NTP with %s (%s)\n", server.c_str(), e.what());
}
esp_log_write(ESP_LOG_ERROR, TAG, "Failed to sync NTP with %s (%s). Retrying with backup...\n",
server.c_str(), e.what());
}
}
}
void TimeManager::synchronizeNTPWithModem(const std::string &ntpServer) {
esp_log_write(ESP_LOG_DEBUG, TAG, "NTP Server Syncing...\n");
long error = modem.NTPServerSync(ntpServer.c_str(), timeZone);
/**
According to TinGsmNTP.tpp, the error codes are:
case 1: "Network time synchronization is successful";
case 61: "Network error";
case 62: "DNS resolution error";
case 63: "Connection error";
case 64: "Service response error";
case 65: "Service response timeout";
default: "Unknown error: " + String(error);
*/
// 255 is ok in our case (https://github.com/vshymanskyy/TinyGSM/pull/652)
if (error != 1 && error != 255) {
throw NTPException(modem.ShowNTPError(error).c_str());
} else {
esp_log_write(ESP_LOG_DEBUG, TAG, "NTP: Network time synchronization is successful\n");
}
}
time_t TimeManager::timeToUnixEpochSeconds(const std::string &time, const std::string &format) {
// 22/10/27,10:16:20+00
struct tm tm {};
time_t dateInEpoch = 0;
// For some unknown reason, we have to use the C functions here instead of the std:: ones.
if (strptime(time.c_str(), format.c_str(), &tm)) {
time_t curTime;
struct tm *timeinfo;
timeinfo = localtime(&curTime);
timeinfo->tm_year = tm.tm_year;
timeinfo->tm_mon = tm.tm_mon;
timeinfo->tm_mday = tm.tm_mday;
timeinfo->tm_hour = tm.tm_hour;
timeinfo->tm_min = tm.tm_min;
timeinfo->tm_sec = tm.tm_sec;
timeinfo->tm_isdst = -1;
dateInEpoch = mktime(timeinfo);
} else {
throw StringToTimeConversionException("Failed to parse date");
}
return dateInEpoch;
}
void TimeManager::writeModemTimeToRTC() {
auto gsmDateTimeString = modem.getGSMDateTime(DATE_FULL);
esp_log_write(ESP_LOG_DEBUG, TAG, "GSM DateTime: %s\n", gsmDateTimeString.c_str());
time_t time = timeToUnixEpochSeconds(gsmDateTimeString.c_str());
rtc.setTime(time);
esp_log_write(ESP_LOG_INFO, TAG, "Time set to EPOCH: %s\n", String(rtc.getEpoch()).c_str());
}
//
// Created by zoe on 12/19/22.
//
#ifndef HOST_CENTRAL_MAST_TIMEMANAGER_H
#define HOST_CENTRAL_MAST_TIMEMANAGER_H
#define TINY_GSM_MODEM_SIM7000
#include "ESP32Time.h"
#include "NTPException.hpp"
#include "StringToTimeConversionException.hpp"
#include <TinyGsmClientSIM7000.h>
#include <iomanip>
#include <string>
class TimeManager {
public:
explicit TimeManager(TinyGsmSim7000 &modem);
private:
constexpr static char *TAG = "TimeManager";
ESP32Time rtc;
int timeZone = 0;
// I would like this to be a const reference but the functions used by the modem are not const
TinyGsmSim7000 &modem;
// convert time to unix epoch seconds
static time_t timeToUnixEpochSeconds(const std::string &time, const std::string &format = "%y/%m/%d,%T+00");
void synchronizeNTPWithModem(const std::string &ntpServer);
void tryNtpServerSync(const std::string &backupNtpServer, std::vector<std::string> &ntpServers);
public:
// sync ntp
void syncNTP(const std::string &ntpServer, const std::string &backupNtpServer);
// write modem time to rtc
void writeModemTimeToRTC();
};
#endif // HOST_CENTRAL_MAST_TIMEMANAGER_H
\ No newline at end of file
......@@ -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 =
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment