Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • informatik/qe/forte/sensor-system
1 result
Show changes
Commits on Source (26)
Showing
with 846 additions and 2 deletions
......@@ -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
...
//
// 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
//
// 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.
//
#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());
}
//
// 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
//
// 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
//
// 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();
}
//
// 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
//
// 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>
#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
//
// 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;
}
}
//
// 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
//
// 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
//
// 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
//
// 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
//
// 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