diff --git a/ChangeLog.md b/ChangeLog.md index 7da7ceced..7acb088e7 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Changed +- **BREAKING** Refactored how the publisher transmit buffer works. This will require adjustment to custom data publishers. ### Added ### Removed diff --git a/src/LoggerBase.cpp b/src/LoggerBase.cpp index 2e1a54c5a..08be51e40 100644 --- a/src/LoggerBase.cpp +++ b/src/LoggerBase.cpp @@ -305,25 +305,11 @@ bool Logger::syncRTC() { PRINTOUT(F("Could not wake modem for clock sync.")); } watchDogTimer.resetWatchDog(); - // Power down the modem - but only if there will be more than 15 seconds - // before the NEXT logging interval - it can take the modem that long to - // shut down - - uint32_t setupFinishTime = getNowLocalEpoch(); - if (setupFinishTime % (_loggingIntervalMinutes * 60) > 15) { - MS_DBG(F("At"), formatDateTime_ISO8601(setupFinishTime), F("with"), - setupFinishTime % (_loggingIntervalMinutes * 60), - F("seconds until next logging interval, putting modem to " - "sleep")); - _logModem->disconnectInternet(); - _logModem->modemSleepPowerDown(); - } else { - MS_DBG(F("At"), formatDateTime_ISO8601(setupFinishTime), - F("there are only"), - setupFinishTime % (_loggingIntervalMinutes * 60), - F("seconds until next logging interval; leaving modem on " - "and connected to the internet.")); - } + + // Power down the modem now that we are done with it + MS_DBG(F("Powering down modem after clock sync.")); + _logModem->disconnectInternet(); + _logModem->modemSleepPowerDown(); } watchDogTimer.resetWatchDog(); return success; @@ -1053,12 +1039,10 @@ bool Logger::initializeSDCard(void) { // Protected helper function - This sets a timestamp on a file void Logger::setFileTimestamp(File fileToStamp, uint8_t stampFlag) { - fileToStamp.timestamp(stampFlag, dtFromEpoch(getNowLocalEpoch()).year(), - dtFromEpoch(getNowLocalEpoch()).month(), - dtFromEpoch(getNowLocalEpoch()).date(), - dtFromEpoch(getNowLocalEpoch()).hour(), - dtFromEpoch(getNowLocalEpoch()).minute(), - dtFromEpoch(getNowLocalEpoch()).second()); + DateTime dt = dtFromEpoch(getNowLocalEpoch()); + + fileToStamp.timestamp(stampFlag, dt.year(), dt.month(), dt.date(), + dt.hour(), dt.minute(), dt.second()); } diff --git a/src/LoggerModem.cpp b/src/LoggerModem.cpp index ff6737708..7b6d2b4d1 100644 --- a/src/LoggerModem.cpp +++ b/src/LoggerModem.cpp @@ -434,31 +434,16 @@ float loggerModem::getModemTemperature() { // Helper to get approximate RSSI from CSQ (assuming no noise) int16_t loggerModem::getRSSIFromCSQ(int16_t csq) { - int16_t CSQs[33] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 99}; - int16_t RSSIs[33] = {-113, -111, -109, -107, -105, -103, -101, -99, -97, - -95, -93, -91, -89, -87, -85, -83, -81, -79, - -77, -75, -73, -71, -69, -67, -65, -63, -61, - -59, -57, -55, -53, -51, 0}; - for (uint8_t i = 0; i < 33; i++) { - if (CSQs[i] == csq) return RSSIs[i]; - } - return 0; + if ((csq < 0) || (csq > 31)) return 0; + // equation matches previous table. not sure the original motivation. + return ((csq * 2) - 113); } // Helper to get signal percent from CSQ int16_t loggerModem::getPctFromCSQ(int16_t csq) { - int16_t CSQs[33] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, - 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 99}; - int16_t PCTs[33] = {0, 3, 6, 10, 13, 16, 19, 23, 26, 29, 32, - 36, 39, 42, 45, 48, 52, 55, 58, 61, 65, 68, - 71, 74, 78, 81, 84, 87, 90, 94, 97, 100, 0}; - for (uint8_t i = 0; i < 33; i++) { - if (CSQs[i] == csq) return PCTs[i]; - } - return 0; + if ((csq < 0) || (csq > 31)) return 0; + // equation matches previous table. not sure the original motivation. + return (csq * 827 + 127) >> 8; } // Helper to get signal percent from RSSI diff --git a/src/VariableBase.cpp b/src/VariableBase.cpp index f330bf234..a557c267f 100644 --- a/src/VariableBase.cpp +++ b/src/VariableBase.cpp @@ -228,7 +228,8 @@ bool Variable::checkUUIDFormat(void) { int first_invalid = strspn(_uuid, acceptableChars); if (first_invalid != 36) { MS_DBG(F("UUID for"), getVarCode(), '(', _uuid, ')', - F("has a bad character"), _uuid[first_invalid], F("at"), first_invalid); + F("has a bad character"), _uuid[first_invalid], F("at"), + first_invalid); return false; } return true; diff --git a/src/dataPublisherBase.cpp b/src/dataPublisherBase.cpp index 76e298b6c..1f4ab0a51 100644 --- a/src/dataPublisherBase.cpp +++ b/src/dataPublisherBase.cpp @@ -10,7 +10,9 @@ */ #include "dataPublisherBase.h" -char dataPublisher::txBuffer[MS_SEND_BUFFER_SIZE] = {'\0'}; +char dataPublisher::txBuffer[MS_SEND_BUFFER_SIZE]; +Client* dataPublisher::txBufferOutClient = nullptr; +size_t dataPublisher::txBufferLen; // Basic chunks of HTTP const char* dataPublisher::getHeader = "GET "; @@ -21,19 +23,16 @@ const char* dataPublisher::hostHeader = "\r\nHost: "; // Constructors dataPublisher::dataPublisher() {} -dataPublisher::dataPublisher(Logger& baseLogger, uint8_t sendEveryX, - uint8_t sendOffset) +dataPublisher::dataPublisher(Logger& baseLogger, int sendEveryX) : _baseLogger(&baseLogger), - _sendEveryX(sendEveryX), - _sendOffset(sendOffset) { + _sendEveryX(sendEveryX) { _baseLogger->registerDataPublisher(this); // register self with logger } dataPublisher::dataPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX, uint8_t sendOffset) + int sendEveryX) : _baseLogger(&baseLogger), _inClient(inClient), - _sendEveryX(sendEveryX), - _sendOffset(sendOffset) { + _sendEveryX(sendEveryX) { _baseLogger->registerDataPublisher(this); // register self with logger } // Destructor @@ -53,11 +52,10 @@ void dataPublisher::attachToLogger(Logger& baseLogger) { } -// Sets the parameters for frequency of sending and any offset, if needed -// NOTE: These parameters are not currently used!! -void dataPublisher::setSendFrequency(uint8_t sendEveryX, uint8_t sendOffset) { +// Sets the interval (in units of the logging interval) between attempted +// data transmissions +void dataPublisher::setSendInterval(int sendEveryX) { _sendEveryX = sendEveryX; - _sendOffset = sendOffset; } @@ -71,34 +69,46 @@ void dataPublisher::begin(Logger& baseLogger) { } -// Empties the outgoing buffer -void dataPublisher::emptyTxBuffer(void) { - MS_DBG(F("Dumping the TX Buffer")); - for (int i = 0; i < MS_SEND_BUFFER_SIZE; i++) { txBuffer[i] = '\0'; } +void dataPublisher::txBufferInit(Client* outClient) { + // remember client we are sending to + txBufferOutClient = outClient; + + // reset buffer length to be empty + txBufferLen = 0; } +void dataPublisher::txBufferAppend(const char* data, size_t length) { + while (length > 0) { + size_t remaining = MS_SEND_BUFFER_SIZE - txBufferLen; + size_t amount = remaining < length ? remaining : length; + + memcpy(&txBuffer[txBufferLen], data, amount); + length -= amount; + data += amount; + txBufferLen += amount; + + if (txBufferLen == MS_SEND_BUFFER_SIZE) { txBufferFlush(); } + } +} -// Returns how much space is left in the buffer -int dataPublisher::bufferFree(void) { - MS_DBG(F("Current TX Buffer Size:"), strlen(txBuffer)); - return MS_SEND_BUFFER_SIZE - strlen(txBuffer); +void dataPublisher::txBufferAppend(const char* s) { + txBufferAppend(s, strlen(s)); } +void dataPublisher::txBufferAppend(char c) { + txBufferAppend(&c, 1); +} -// Sends the tx buffer to a stream and then clears it -void dataPublisher::printTxBuffer(Stream* stream, bool addNewLine) { -// Send the out buffer so far to the serial for debugging +void dataPublisher::txBufferFlush() { #if defined(STANDARD_SERIAL_OUTPUT) - STANDARD_SERIAL_OUTPUT.write(txBuffer, strlen(txBuffer)); - if (addNewLine) { PRINTOUT('\n'); } + // send to debugging stream + STANDARD_SERIAL_OUTPUT.write((const uint8_t*)txBuffer, txBufferLen); STANDARD_SERIAL_OUTPUT.flush(); #endif - stream->write(txBuffer, strlen(txBuffer)); - if (addNewLine) { stream->print("\r\n"); } - stream->flush(); + txBufferOutClient->write((const uint8_t*)txBuffer, txBufferLen); + txBufferOutClient->flush(); - // empty the buffer after printing it - emptyTxBuffer(); + txBufferLen = 0; } diff --git a/src/dataPublisherBase.h b/src/dataPublisherBase.h index d43ba5dde..bedaa1cf7 100644 --- a/src/dataPublisherBase.h +++ b/src/dataPublisherBase.h @@ -32,11 +32,10 @@ * @def MS_SEND_BUFFER_SIZE * @brief Send Buffer * - * This determines how many characters to set out at once over the TCP/UDP - * connection. Increasing this may decrease data use by a loger, while - * decreasing it will save memory. Do not make it smaller than 47 (to keep all - * variable values with their UUID's) or bigger than 1500 (a typical TCP/UDP - * Maximum Transmission Unit). + * This determines how many characters to set out at once over the TCP + * connection. Increasing this may decrease data use by a logger, while + * decreasing it will save memory. Do not make it smaller than 32 or bigger + * than 1500 (a typical TCP Maximum Transmission Unit). * * This can be changed by setting the build flag MS_SEND_BUFFER_SIZE when * compiling. @@ -86,20 +85,10 @@ class dataPublisher { * logger. * * @param baseLogger The logger supplying the data to be published - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. Not respected by all publishers. */ - explicit dataPublisher(Logger& baseLogger, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + explicit dataPublisher(Logger& baseLogger, int sendEveryX = 1); /** * @brief Construct a new data Publisher object. * @@ -107,20 +96,10 @@ class dataPublisher { * @param inClient An Arduino client instance to use to print data to. * Allows the use of any type of client and multiple clients tied to a * single TinyGSM modem instance - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. Not respected by all publishers. */ - dataPublisher(Logger& baseLogger, Client* inClient, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + dataPublisher(Logger& baseLogger, Client* inClient, int sendEveryX = 1); /** * @brief Destroy the data Publisher object - no action is taken. */ @@ -145,30 +124,19 @@ class dataPublisher { */ void attachToLogger(Logger& baseLogger); /** - * @brief Set the parameters for frequency of sending and any offset, if - * needed. + * @brief Sets the interval (in units of the logging interval) between + * attempted data transmissions * - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note These parameters are not currently used! + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. Not respected by all publishers. */ - void setSendFrequency(uint8_t sendEveryX, uint8_t sendOffset); + void setSendInterval(int sendEveryX); /** * @brief Begin the publisher - linking it to the client and logger. * * This can be used as an alternative to adding the logger and client in the - * constructor. This is slightly "safer" because we expect the publishers - * to be created in the "global scope" and we cannot control the order in - * which objects in that global scope will be created. That is, we cannot - * guarantee that the logger will actually be created before the publisher - * that wants to attach to it unless we wait to attach the publisher until - * in the setup or loop function of the main program. In reality, it is - * very unlikely that this is necessary. + * constructor. * * @param baseLogger The logger supplying the data to be published * @param inClient An Arduino client instance to use to print data to. @@ -186,13 +154,7 @@ class dataPublisher { * logger. * * This can be used as an alternative to adding the logger and client in the - * constructor. This is slightly "safer" because we expect the publishers - * to be created in the "global scope" and we cannot control the order in - * which objects in that global scope will be created. That is, we cannot - * guarantee that the logger will actually be created before the publisher - * that wants to attach to it unless we wait to attach the publisher until - * in the setup or loop function of the main program. In reality, it is - * very unlikely that this is necessary. + * constructor. * * @param baseLogger The logger supplying the data to be published */ @@ -280,40 +242,54 @@ class dataPublisher { Client* _inClient = nullptr; /** - * @brief A buffer for outgoing data. + * @brief The buffer for outgoing data. */ static char txBuffer[MS_SEND_BUFFER_SIZE]; /** - * @brief Get the number of empty spots in the buffer. + * @brief The pointer to the client instance the TX buffer is writing to. + */ + static Client* txBufferOutClient; + /** + * @brief The number of used characters in the TX buffer. + */ + static size_t txBufferLen; + /** + * @brief Initialize the TX buffer to be empty and start writing to the + * given client. * - * @return **int** The number of available characters in the buffer + * @param client The client to transmit to. */ - static int bufferFree(void); + static void txBufferInit(Client* outClient); /** - * @brief Fill the TX buffer with nulls ('\0'). + * @brief Append the given data to the TX buffer, flushing if necessary. + * + * @param data The data start pointer. + * @param length The number of bytes to add. */ - static void emptyTxBuffer(void); + static void txBufferAppend(const char* data, size_t length); /** - * @brief Write the TX buffer to a stream and also to the debugging - * port. + * @brief Append the given string to the TX buffer, flushing if necessary. * - * @param stream A pointer to an Arduino Stream instance to use to print - * data - * @param addNewLine True to add a new line character ("\n") at the end of - * the print + * @param s The null-terminated string to append. */ - static void printTxBuffer(Stream* stream, bool addNewLine = false); - + static void txBufferAppend(const char* s); + /** + * @brief Append the given char to the TX buffer, flushing if necessary. + * + * @param c The char to append. + */ + static void txBufferAppend(char c); /** - * @brief Unimplemented; intended for future use to enable caching and bulk - * publishing. + * @brief Write the TX buffer contents to the initialized stream and also to + * the debugging port. */ - uint8_t _sendEveryX = 1; + static void txBufferFlush(); + /** - * @brief Unimplemented; intended for future use to enable publishing data - * at a time slightly delayed from when it is collected. + * @brief Interval (in units of the logging interval) between + * attempted data transmissions. Not respected by all publishers. */ - uint8_t _sendOffset = 0; + int _sendEveryX = 1; // Basic chunks of HTTP /** diff --git a/src/publishers/DreamHostPublisher.cpp b/src/publishers/DreamHostPublisher.cpp index fbfa07e7d..74412523e 100644 --- a/src/publishers/DreamHostPublisher.cpp +++ b/src/publishers/DreamHostPublisher.cpp @@ -23,23 +23,21 @@ const char* DreamHostPublisher::timestampTagDH = "&Loggertime="; // Constructors DreamHostPublisher::DreamHostPublisher() : dataPublisher() {} -DreamHostPublisher::DreamHostPublisher(Logger& baseLogger, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) {} +DreamHostPublisher::DreamHostPublisher(Logger& baseLogger, int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) {} DreamHostPublisher::DreamHostPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) {} + int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) {} DreamHostPublisher::DreamHostPublisher(Logger& baseLogger, const char* dhUrl, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) { + int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) { setDreamHostPortalRX(dhUrl); } DreamHostPublisher::DreamHostPublisher(Logger& baseLogger, Client* inClient, - const char* dhUrl, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) { + const char* dhUrl, int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) { setDreamHostPortalRX(dhUrl); } // Destructor @@ -52,41 +50,6 @@ void DreamHostPublisher::setDreamHostPortalRX(const char* dhUrl) { } -// This prints the URL out to an Arduino stream -void DreamHostPublisher::printSensorDataDreamHost(Stream* stream) { - stream->print(_DreamHostPortalRX); - stream->print(loggerTag); - stream->print(_baseLogger->getLoggerID()); - stream->print(timestampTagDH); - stream->print(String(Logger::markedLocalEpochTime - - 946684800)); // Correct time from epoch to y2k - - for (uint8_t i = 0; i < _baseLogger->getArrayVarCount(); i++) { - stream->print('&'); - stream->print(_baseLogger->getVarCodeAtI(i)); - stream->print('='); - stream->print(_baseLogger->getValueStringAtI(i)); - } -} - - -// This prints a fully structured GET request for DreamHost to the -// specified stream -void DreamHostPublisher::printDreamHostRequest(Stream* stream) { - // Start the request - stream->print(getHeader); - - // Stream the full URL with parameters - printSensorDataDreamHost(stream); - - // Send the rest of the HTTP header - stream->print(HTTPtag); - stream->print(hostHeader); - stream->print(dreamhostHost); - stream->print(F("\r\n\r\n")); -} - - // A way to begin with everything already set void DreamHostPublisher::begin(Logger& baseLogger, Client* inClient, const char* dhUrl) { @@ -111,59 +74,41 @@ int16_t DreamHostPublisher::publishData(Client* outClient) { MS_START_DEBUG_TIMER; if (outClient->connect(dreamhostHost, dreamhostPort)) { MS_DBG(F("Client connected after"), MS_PRINT_DEBUG_TIMER, F("ms\n")); + txBufferInit(outClient); // copy the initial post header into the tx buffer - snprintf(txBuffer, sizeof(txBuffer), "%s", getHeader); + txBufferAppend(getHeader); // add in the dreamhost receiver URL - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", _DreamHostPortalRX); + txBufferAppend(_DreamHostPortalRX); // start the URL parameters - if (bufferFree() < 16) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", loggerTag); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", - _baseLogger->getLoggerID()); - - if (bufferFree() < 22) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", timestampTagDH); + txBufferAppend(loggerTag); + txBufferAppend(_baseLogger->getLoggerID()); + + txBufferAppend(timestampTagDH); ltoa((Logger::markedLocalEpochTime - 946684800), tempBuffer, 10); // BASE 10 - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); + txBufferAppend(tempBuffer); for (uint8_t i = 0; i < _baseLogger->getArrayVarCount(); i++) { - // Once the buffer fills, send it out - if (bufferFree() < 47) printTxBuffer(outClient); - - txBuffer[strlen(txBuffer)] = '&'; - _baseLogger->getVarCodeAtI(i).toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = '='; - _baseLogger->getValueStringAtI(i).toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); + txBufferAppend('&'); + txBufferAppend(_baseLogger->getVarCodeAtI(i).c_str()); + txBufferAppend('='); + txBufferAppend(_baseLogger->getValueStringAtI(i).c_str()); } // add the rest of the HTTP GET headers to the outgoing buffer - if (bufferFree() < 52) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", HTTPtag); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", hostHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", dreamhostHost); - txBuffer[strlen(txBuffer)] = '\r'; - txBuffer[strlen(txBuffer)] = '\n'; - txBuffer[strlen(txBuffer)] = '\r'; - txBuffer[strlen(txBuffer)] = '\n'; - - // Send out the finished request (or the last unsent section of it) - printTxBuffer(outClient); + txBufferAppend(HTTPtag); + txBufferAppend(hostHeader); + txBufferAppend(dreamhostHost); + txBufferAppend('\r'); + txBufferAppend('\n'); + txBufferAppend('\r'); + txBufferAppend('\n'); + + // Flush the complete request + txBufferFlush(); // Wait 10 seconds for a response from the server uint32_t start = millis(); @@ -189,6 +134,7 @@ int16_t DreamHostPublisher::publishData(Client* outClient) { int16_t responseCode = 0; if (did_respond > 0) { char responseCode_char[4]; + responseCode_char[3] = 0; for (uint8_t i = 0; i < 3; i++) { responseCode_char[i] = tempBuffer[i + 9]; } @@ -197,7 +143,7 @@ int16_t DreamHostPublisher::publishData(Client* outClient) { responseCode = 504; } - PRINTOUT(F("-- Response Code --")); + PRINTOUT(F("\n-- Response Code --")); PRINTOUT(responseCode); return responseCode; diff --git a/src/publishers/DreamHostPublisher.h b/src/publishers/DreamHostPublisher.h index 6a7fe1a46..64a465be6 100644 --- a/src/publishers/DreamHostPublisher.h +++ b/src/publishers/DreamHostPublisher.h @@ -51,20 +51,10 @@ class DreamHostPublisher : public dataPublisher { * logger. * * @param baseLogger The logger supplying the data to be published - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ - explicit DreamHostPublisher(Logger& baseLogger, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + explicit DreamHostPublisher(Logger& baseLogger, int sendEveryX = 1); /** * @brief Construct a new DreamHost Publisher object * @@ -72,33 +62,21 @@ class DreamHostPublisher : public dataPublisher { * @param inClient An Arduino client instance to use to print data to. * Allows the use of any type of client and multiple clients tied to a * single TinyGSM modem instance - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ DreamHostPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + int sendEveryX = 1); /** * @brief Construct a new DreamHost Publisher object * * @param baseLogger The logger supplying the data to be published * @param dhUrl The URL for sending data to DreamHost - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ DreamHostPublisher(Logger& baseLogger, const char* dhUrl, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + int sendEveryX = 1); /** * @brief Construct a new DreamHost Publisher object * @@ -107,14 +85,11 @@ class DreamHostPublisher : public dataPublisher { * Allows the use of any type of client and multiple clients tied to a * single TinyGSM modem instance * @param dhUrl The URL for sending data to DreamHost - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ DreamHostPublisher(Logger& baseLogger, Client* inClient, const char* dhUrl, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + int sendEveryX = 1); /** * @brief Destroy the DreamHost Publisher object */ @@ -133,26 +108,6 @@ class DreamHostPublisher : public dataPublisher { */ void setDreamHostPortalRX(const char* dhUrl); - /** - * @brief This creates all of the URL parameter tags and values and writes - * the result to an Arduino stream. - * - * HTML headers are not included. - * - * @param stream The Arduino stream to write out the URL and parameters to. - */ - void printSensorDataDreamHost(Stream* stream); - - /** - * @brief This prints a fully structured GET request for DreamHost to the - * specified stream. - * - * This includes the HTML headers. - * - * @param stream The Arduino stream to write out the URL and parameters to. - */ - void printDreamHostRequest(Stream* stream); - // A way to begin with everything already set /** * @copydoc dataPublisher::begin(Logger& baseLogger, Client* inClient) @@ -165,7 +120,6 @@ class DreamHostPublisher : public dataPublisher { */ void begin(Logger& baseLogger, const char* dhUrl); - // int16_t postDataDreamHost(void); /** * @brief Utilizes an attached modem to make a TCP connection to the * DreamHost URL and then stream out a get request over that connection. diff --git a/src/publishers/EnviroDIYPublisher.cpp b/src/publishers/EnviroDIYPublisher.cpp index 5d017ea99..ec65e0f48 100644 --- a/src/publishers/EnviroDIYPublisher.cpp +++ b/src/publishers/EnviroDIYPublisher.cpp @@ -31,25 +31,24 @@ const char* EnviroDIYPublisher::timestampTag = "\",\"timestamp\":\""; // Constructors EnviroDIYPublisher::EnviroDIYPublisher() : dataPublisher() {} -EnviroDIYPublisher::EnviroDIYPublisher(Logger& baseLogger, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) {} +EnviroDIYPublisher::EnviroDIYPublisher(Logger& baseLogger, int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) {} EnviroDIYPublisher::EnviroDIYPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) {} + int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) {} EnviroDIYPublisher::EnviroDIYPublisher(Logger& baseLogger, const char* registrationToken, const char* samplingFeatureUUID, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) { + int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) { setToken(registrationToken); _baseLogger->setSamplingFeatureUUID(samplingFeatureUUID); } EnviroDIYPublisher::EnviroDIYPublisher(Logger& baseLogger, Client* inClient, const char* registrationToken, const char* samplingFeatureUUID, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) { + int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) { setToken(registrationToken); _baseLogger->setSamplingFeatureUUID(samplingFeatureUUID); } @@ -83,45 +82,6 @@ uint16_t EnviroDIYPublisher::calculateJsonSize() { return jsonLength; } -// This prints a properly formatted JSON for EnviroDIY to an Arduino stream -void EnviroDIYPublisher::printSensorDataJSON(Stream* stream) { - stream->print(samplingFeatureTag); - stream->print(_baseLogger->getSamplingFeatureUUID()); - stream->print(timestampTag); - stream->print(Logger::formatDateTime_ISO8601(Logger::markedLocalEpochTime)); - stream->print(F("\",")); - - for (uint8_t i = 0; i < _baseLogger->getArrayVarCount(); i++) { - stream->print('"'); - stream->print(_baseLogger->getVarUUIDAtI(i)); - stream->print(F("\":")); - stream->print(_baseLogger->getValueStringAtI(i)); - if (i + 1 != _baseLogger->getArrayVarCount()) { stream->print(','); } - } - - stream->print('}'); -} - - -// This prints a fully structured post request for EnviroDIY to the -// specified stream. -void EnviroDIYPublisher::printEnviroDIYRequest(Stream* stream) { - // Stream the HTTP headers for the post request - stream->print(postHeader); - stream->print(postEndpoint); - stream->print(HTTPtag); - stream->print(hostHeader); - stream->print(enviroDIYHost); - stream->print(tokenHeader); - stream->print(_registrationToken); - stream->print(contentLengthHeader); - stream->print(calculateJsonSize()); - stream->print(contentTypeHeader); - - // Stream the JSON itself - printSensorDataJSON(stream); -} - // A way to begin with everything already set void EnviroDIYPublisher::begin(Logger& baseLogger, Client* inClient, @@ -144,7 +104,6 @@ void EnviroDIYPublisher::begin(Logger& baseLogger, // EnviroDIY/ODM2DataSharingPortal and then streams out a post request // over that connection. // The return is the http status code of the response. -// int16_t EnviroDIYPublisher::postDataEnviroDIY(void) int16_t EnviroDIYPublisher::publishData(Client* outClient) { // Create a buffer for the portions of the request and response char tempBuffer[37] = ""; @@ -157,83 +116,51 @@ int16_t EnviroDIYPublisher::publishData(Client* outClient) { MS_START_DEBUG_TIMER; if (outClient->connect(enviroDIYHost, enviroDIYPort)) { MS_DBG(F("Client connected after"), MS_PRINT_DEBUG_TIMER, F("ms\n")); + txBufferInit(outClient); // copy the initial post header into the tx buffer - snprintf(txBuffer, sizeof(txBuffer), "%s", postHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", postEndpoint); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", HTTPtag); + txBufferAppend(postHeader); + txBufferAppend(postEndpoint); + txBufferAppend(HTTPtag); // add the rest of the HTTP POST headers to the outgoing buffer - // before adding each line/chunk to the outgoing buffer, we make sure - // there is space for that line, sending out buffer if not - if (bufferFree() < 28) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", hostHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", enviroDIYHost); - - if (bufferFree() < 47) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tokenHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", _registrationToken); - - if (bufferFree() < 26) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", - contentLengthHeader); + txBufferAppend(hostHeader); + txBufferAppend(enviroDIYHost); + txBufferAppend(tokenHeader); + txBufferAppend(_registrationToken); + + txBufferAppend(contentLengthHeader); itoa(calculateJsonSize(), tempBuffer, 10); // BASE 10 - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); + txBufferAppend(tempBuffer); - if (bufferFree() < 42) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", contentTypeHeader); + txBufferAppend(contentTypeHeader); // put the start of the JSON into the outgoing response_buffer - if (bufferFree() < 21) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", samplingFeatureTag); - - if (bufferFree() < 36) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", - _baseLogger->getSamplingFeatureUUID()); - - if (bufferFree() < 42) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", timestampTag); - Logger::formatDateTime_ISO8601(Logger::markedLocalEpochTime) - .toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = '"'; - txBuffer[strlen(txBuffer)] = ','; + txBufferAppend(samplingFeatureTag); + txBufferAppend(_baseLogger->getSamplingFeatureUUID()); + + txBufferAppend(timestampTag); + txBufferAppend( + Logger::formatDateTime_ISO8601(Logger::markedLocalEpochTime) + .c_str()); + txBufferAppend('"'); + txBufferAppend(','); for (uint8_t i = 0; i < _baseLogger->getArrayVarCount(); i++) { - // Once the buffer fills, send it out - if (bufferFree() < 47) printTxBuffer(outClient); - - txBuffer[strlen(txBuffer)] = '"'; - _baseLogger->getVarUUIDAtI(i).toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = '"'; - txBuffer[strlen(txBuffer)] = ':'; - _baseLogger->getValueStringAtI(i).toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); + txBufferAppend('"'); + txBufferAppend(_baseLogger->getVarUUIDAtI(i).c_str()); + txBufferAppend('"'); + txBufferAppend(':'); + txBufferAppend(_baseLogger->getValueStringAtI(i).c_str()); if (i + 1 != _baseLogger->getArrayVarCount()) { - txBuffer[strlen(txBuffer)] = ','; + txBufferAppend(','); } else { - txBuffer[strlen(txBuffer)] = '}'; + txBufferAppend('}'); } } - // Send out the finished request (or the last unsent section of it) - printTxBuffer(outClient, true); + // Flush the complete request + txBufferFlush(); // Wait 10 seconds for a response from the server uint32_t start = millis(); @@ -260,6 +187,7 @@ int16_t EnviroDIYPublisher::publishData(Client* outClient) { int16_t responseCode = 0; if (did_respond > 0) { char responseCode_char[4]; + responseCode_char[3] = 0; for (uint8_t i = 0; i < 3; i++) { responseCode_char[i] = tempBuffer[i + 9]; } @@ -268,7 +196,7 @@ int16_t EnviroDIYPublisher::publishData(Client* outClient) { responseCode = 504; } - PRINTOUT(F("-- Response Code --")); + PRINTOUT(F("\n-- Response Code --")); PRINTOUT(responseCode); return responseCode; diff --git a/src/publishers/EnviroDIYPublisher.h b/src/publishers/EnviroDIYPublisher.h index 8a728dd1d..742681dc6 100644 --- a/src/publishers/EnviroDIYPublisher.h +++ b/src/publishers/EnviroDIYPublisher.h @@ -51,20 +51,10 @@ class EnviroDIYPublisher : public dataPublisher { * logger. * * @param baseLogger The logger supplying the data to be published - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ - explicit EnviroDIYPublisher(Logger& baseLogger, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + explicit EnviroDIYPublisher(Logger& baseLogger, int sendEveryX = 1); /** * @brief Construct a new EnviroDIY Publisher object * @@ -72,20 +62,11 @@ class EnviroDIYPublisher : public dataPublisher { * @param inClient An Arduino client instance to use to print data to. * Allows the use of any type of client and multiple clients tied to a * single TinyGSM modem instance - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ EnviroDIYPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + int sendEveryX = 1); /** * @brief Construct a new EnviroDIY Publisher object * @@ -94,15 +75,11 @@ class EnviroDIYPublisher : public dataPublisher { * Monitor My Watershed data portal. * @param samplingFeatureUUID The sampling feature UUID for the site on the * Monitor My Watershed data portal. - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ EnviroDIYPublisher(Logger& baseLogger, const char* registrationToken, - const char* samplingFeatureUUID, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + const char* samplingFeatureUUID, int sendEveryX = 1); /** * @brief Construct a new EnviroDIY Publisher object * @@ -114,16 +91,12 @@ class EnviroDIYPublisher : public dataPublisher { * Monitor My Watershed data portal. * @param samplingFeatureUUID The sampling feature UUID for the site on the * Monitor My Watershed data portal. - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ EnviroDIYPublisher(Logger& baseLogger, Client* inClient, const char* registrationToken, - const char* samplingFeatureUUID, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + const char* samplingFeatureUUID, int sendEveryX = 1); /** * @brief Destroy the EnviroDIY Publisher object */ @@ -149,30 +122,7 @@ class EnviroDIYPublisher : public dataPublisher { * @return uint16_t The number of characters in the JSON object. */ uint16_t calculateJsonSize(); - // /** - // * @brief Calculates how long the full post request will be, including - // * headers - // * - // * @return uint16_t The length of the full request including HTTP - // headers. - // */ - // uint16_t calculatePostSize(); - /** - * @brief This generates a properly formatted JSON for EnviroDIY and prints - * it to the input Arduino stream object. - * - * @param stream The Arduino stream to write out the JSON to. - */ - void printSensorDataJSON(Stream* stream); - - /** - * @brief This prints a fully structured post request for Monitor My - * Watershed/EnviroDIY to the specified stream. - * - * @param stream The Arduino stream to write out the request to. - */ - void printEnviroDIYRequest(Stream* stream); // A way to begin with everything already set /** @@ -194,7 +144,6 @@ class EnviroDIYPublisher : public dataPublisher { void begin(Logger& baseLogger, const char* registrationToken, const char* samplingFeatureUUID); - // int16_t postDataEnviroDIY(void); /** * @brief Utilize an attached modem to open a a TCP connection to the * EnviroDIY/ODM2DataSharingPortal and then stream out a post request over @@ -217,12 +166,10 @@ class EnviroDIYPublisher : public dataPublisher { * * @{ */ - static const char* postEndpoint; ///< The endpoint - static const char* enviroDIYHost; ///< The host name - static const int enviroDIYPort; ///< The host port - static const char* tokenHeader; ///< The token header text - // static const char *cacheHeader; ///< The cache header text - // static const char *connectionHeader; ///< The keep alive header text + static const char* postEndpoint; ///< The endpoint + static const char* enviroDIYHost; ///< The host name + static const int enviroDIYPort; ///< The host port + static const char* tokenHeader; ///< The token header text static const char* contentLengthHeader; ///< The content length header text static const char* contentTypeHeader; ///< The content type header text /**@}*/ diff --git a/src/publishers/ThingSpeakPublisher.cpp b/src/publishers/ThingSpeakPublisher.cpp index e1b0e3a7f..b3c9fb706 100644 --- a/src/publishers/ThingSpeakPublisher.cpp +++ b/src/publishers/ThingSpeakPublisher.cpp @@ -25,18 +25,17 @@ const char* ThingSpeakPublisher::mqttUser = THING_SPEAK_USER_NAME; // Constructors ThingSpeakPublisher::ThingSpeakPublisher() : dataPublisher() {} -ThingSpeakPublisher::ThingSpeakPublisher(Logger& baseLogger, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) {} +ThingSpeakPublisher::ThingSpeakPublisher(Logger& baseLogger, int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) {} ThingSpeakPublisher::ThingSpeakPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) {} + int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) {} ThingSpeakPublisher::ThingSpeakPublisher(Logger& baseLogger, const char* thingSpeakMQTTKey, const char* thingSpeakChannelID, const char* thingSpeakChannelKey, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) { + int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) { setMQTTKey(thingSpeakMQTTKey); setChannelID(thingSpeakChannelID); setChannelKey(thingSpeakChannelKey); @@ -45,8 +44,8 @@ ThingSpeakPublisher::ThingSpeakPublisher(Logger& baseLogger, Client* inClient, const char* thingSpeakMQTTKey, const char* thingSpeakChannelID, const char* thingSpeakChannelKey, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) { + int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) { setMQTTKey(thingSpeakMQTTKey); setChannelID(thingSpeakChannelID); setChannelKey(thingSpeakChannelKey); @@ -129,27 +128,19 @@ int16_t ThingSpeakPublisher::publishData(Client* outClient) { _thingSpeakChannelKey); MS_DBG(F("Topic ["), strlen(topicBuffer), F("]:"), String(topicBuffer)); - emptyTxBuffer(); + // buffer is used only locally, it does not transmit + txBufferInit(nullptr); - Logger::formatDateTime_ISO8601(Logger::markedLocalEpochTime) - .toCharArray(tempBuffer, 26); - snprintf(txBuffer + strlen(txBuffer), sizeof(txBuffer) - strlen(txBuffer), - "%s", "created_at="); - snprintf(txBuffer + strlen(txBuffer), sizeof(txBuffer) - strlen(txBuffer), - "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = '&'; + txBufferAppend("created_at="); + txBufferAppend( + Logger::formatDateTime_ISO8601(Logger::markedLocalEpochTime).c_str()); for (uint8_t i = 0; i < numChannels; i++) { - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", "field"); + txBufferAppend("&field"); itoa(i + 1, tempBuffer, 10); // BASE 10 - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = '='; - _baseLogger->getValueStringAtI(i).toCharArray(tempBuffer, 26); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - if (i + 1 != numChannels) { txBuffer[strlen(txBuffer)] = '&'; } + txBufferAppend(tempBuffer); + txBufferAppend('='); + txBufferAppend(_baseLogger->getValueStringAtI(i).c_str()); } MS_DBG(F("Message ["), strlen(txBuffer), F("]:"), String(txBuffer)); diff --git a/src/publishers/ThingSpeakPublisher.h b/src/publishers/ThingSpeakPublisher.h index 2b0bbe89d..b0dec9cf1 100644 --- a/src/publishers/ThingSpeakPublisher.h +++ b/src/publishers/ThingSpeakPublisher.h @@ -76,20 +76,10 @@ class ThingSpeakPublisher : public dataPublisher { * logger. * * @param baseLogger The logger supplying the data to be published - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ - explicit ThingSpeakPublisher(Logger& baseLogger, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + explicit ThingSpeakPublisher(Logger& baseLogger, int sendEveryX = 1); /** * @brief Construct a new ThingSpeak Publisher object * @@ -97,20 +87,11 @@ class ThingSpeakPublisher : public dataPublisher { * @param inClient An Arduino client instance to use to print data to. * Allows the use of any type of client and multiple clients tied to a * single TinyGSM modem instance - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ ThingSpeakPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + int sendEveryX = 1); /** * @brief Construct a new ThingSpeak Publisher object * @@ -118,15 +99,12 @@ class ThingSpeakPublisher : public dataPublisher { * @param thingSpeakMQTTKey Your MQTT API Key from Account > MyProfile. * @param thingSpeakChannelID The numeric channel id for your channel * @param thingSpeakChannelKey The write API key for your channel - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ ThingSpeakPublisher(Logger& baseLogger, const char* thingSpeakMQTTKey, const char* thingSpeakChannelID, - const char* thingSpeakChannelKey, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + const char* thingSpeakChannelKey, int sendEveryX = 1); /** * @brief Construct a new ThingSpeak Publisher object * @@ -137,17 +115,13 @@ class ThingSpeakPublisher : public dataPublisher { * @param thingSpeakMQTTKey Your MQTT API Key from Account > MyProfile. * @param thingSpeakChannelID The numeric channel id for your channel * @param thingSpeakChannelKey The write API key for your channel - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ ThingSpeakPublisher(Logger& baseLogger, Client* inClient, const char* thingSpeakMQTTKey, const char* thingSpeakChannelID, - const char* thingSpeakChannelKey, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + const char* thingSpeakChannelKey, int sendEveryX = 1); /** * @brief Destroy the ThingSpeak Publisher object */ diff --git a/src/publishers/UbidotsPublisher.cpp b/src/publishers/UbidotsPublisher.cpp index c840cdfcf..5477345a4 100644 --- a/src/publishers/UbidotsPublisher.cpp +++ b/src/publishers/UbidotsPublisher.cpp @@ -34,26 +34,23 @@ const char* UbidotsPublisher::payload = "{"; // Constructors UbidotsPublisher::UbidotsPublisher() : dataPublisher() {} -UbidotsPublisher::UbidotsPublisher(Logger& baseLogger, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) {} +UbidotsPublisher::UbidotsPublisher(Logger& baseLogger, int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) {} UbidotsPublisher::UbidotsPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX, uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) {} + int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) {} UbidotsPublisher::UbidotsPublisher(Logger& baseLogger, const char* authentificationToken, - const char* deviceID, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, sendEveryX, sendOffset) { + const char* deviceID, int sendEveryX) + : dataPublisher(baseLogger, sendEveryX) { setToken(authentificationToken); _baseLogger->setSamplingFeatureUUID(deviceID); MS_DBG(F("dataPublisher object created")); } UbidotsPublisher::UbidotsPublisher(Logger& baseLogger, Client* inClient, const char* authentificationToken, - const char* deviceID, uint8_t sendEveryX, - uint8_t sendOffset) - : dataPublisher(baseLogger, inClient, sendEveryX, sendOffset) { + const char* deviceID, int sendEveryX) + : dataPublisher(baseLogger, inClient, sendEveryX) { setToken(authentificationToken); _baseLogger->setSamplingFeatureUUID(deviceID); MS_DBG(F("dataPublisher object created")); @@ -93,46 +90,6 @@ uint16_t UbidotsPublisher::calculateJsonSize() { } -// This prints a properly formatted JSON for EnviroDIY to an Arduino stream -void UbidotsPublisher::printSensorDataJSON(Stream* stream) { - stream->print(payload); - - for (uint8_t i = 0; i < _baseLogger->getArrayVarCount(); i++) { - stream->print('"'); - stream->print(_baseLogger->getVarUUIDAtI(i)); - stream->print(F("\":{'value':")); - stream->print(_baseLogger->getValueStringAtI(i)); - stream->print(",'timestamp':"); - stream->print(Logger::markedUTCEpochTime); - stream->print( - F("000}")); // Convert microseconds to milliseconds for ubidots - if (i + 1 != _baseLogger->getArrayVarCount()) { stream->print(','); } - } - - stream->print(F("}}")); -} - - -// This prints a fully structured post request for Ubidots to the -// specified stream. -void UbidotsPublisher::printUbidotsRequest(Stream* stream) { - // Stream the HTTP headers for the post request - stream->print(postHeader); - stream->print(postEndpoint); - stream->print(HTTPtag); - stream->print(hostHeader); - stream->print(ubidotsHost); - stream->print(tokenHeader); - stream->print(_authentificationToken); - stream->print(contentLengthHeader); - stream->print(calculateJsonSize()); - stream->print(contentTypeHeader); - - // Stream the JSON itself - printSensorDataJSON(stream); -} - - // A way to begin with everything already set void UbidotsPublisher::begin(Logger& baseLogger, Client* inClient, const char* authentificationToken, @@ -167,93 +124,48 @@ int16_t UbidotsPublisher::publishData(Client* outClient) { MS_START_DEBUG_TIMER; if (outClient->connect(ubidotsHost, ubidotsPort)) { MS_DBG(F("Client connected after"), MS_PRINT_DEBUG_TIMER, F("ms\n")); + txBufferInit(outClient); // copy the initial post header into the tx buffer - snprintf(txBuffer, sizeof(txBuffer), "%s", postHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", postEndpoint); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", - _baseLogger->getSamplingFeatureUUID()); - txBuffer[strlen(txBuffer)] = '/'; - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", HTTPtag); + txBufferAppend(postHeader); + txBufferAppend(postEndpoint); + txBufferAppend(_baseLogger->getSamplingFeatureUUID()); + txBufferAppend('/'); + txBufferAppend(HTTPtag); // add the rest of the HTTP POST headers to the outgoing buffer - // before adding each line/chunk to the outgoing buffer, we make sure - // there is space for that line, sending out buffer if not - if (bufferFree() < 28) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", hostHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", ubidotsHost); + txBufferAppend(hostHeader); + txBufferAppend(ubidotsHost); + txBufferAppend(tokenHeader); + txBufferAppend(_authentificationToken); - if (bufferFree() < 47) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tokenHeader); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", - _authentificationToken); - - if (bufferFree() < 26) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", - contentLengthHeader); + txBufferAppend(contentLengthHeader); itoa(calculateJsonSize(), tempBuffer, 10); // BASE 10 - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); + txBufferAppend(tempBuffer); - if (bufferFree() < 42) printTxBuffer(outClient); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", contentTypeHeader); + txBufferAppend(contentTypeHeader); // put the start of the JSON into the outgoing response_buffer - if (bufferFree() < 21) printTxBuffer(outClient); - - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", payload); + txBufferAppend(payload); for (uint8_t i = 0; i < _baseLogger->getArrayVarCount(); i++) { - // Once the buffer fills, send it out - if (bufferFree() < 47) printTxBuffer(outClient); - - txBuffer[strlen(txBuffer)] = '"'; - _baseLogger->getVarUUIDAtI(i).toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = '"'; - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", ":{"); - txBuffer[strlen(txBuffer)] = '"'; - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", "value"); - txBuffer[strlen(txBuffer)] = '"'; - txBuffer[strlen(txBuffer)] = ':'; - _baseLogger->getValueStringAtI(i).toCharArray(tempBuffer, 37); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - txBuffer[strlen(txBuffer)] = ','; - txBuffer[strlen(txBuffer)] = '"'; - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", "timestamp"); - txBuffer[strlen(txBuffer)] = '"'; - txBuffer[strlen(txBuffer)] = ':'; + txBufferAppend('"'); + txBufferAppend(_baseLogger->getVarUUIDAtI(i).c_str()); + txBufferAppend("\":{\"value\":"); + txBufferAppend(_baseLogger->getValueStringAtI(i).c_str()); + txBufferAppend(",\"timestamp\":"); ltoa(Logger::markedUTCEpochTime, tempBuffer, 10); // BASE 10 - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", tempBuffer); - snprintf(txBuffer + strlen(txBuffer), - sizeof(txBuffer) - strlen(txBuffer), "%s", "000"); + txBufferAppend(tempBuffer); + txBufferAppend("000}"); if (i + 1 != _baseLogger->getArrayVarCount()) { - txBuffer[strlen(txBuffer)] = '}'; - txBuffer[strlen(txBuffer)] = ','; + txBufferAppend(','); } else { - txBuffer[strlen(txBuffer)] = '}'; - txBuffer[strlen(txBuffer)] = '}'; + txBufferAppend('}'); } } - // Send out the finished request (or the last unsent section of it) - printTxBuffer(outClient, true); + // Flush the complete request + txBufferFlush(); // Wait 10 seconds for a response from the server uint32_t start = millis(); @@ -279,6 +191,7 @@ int16_t UbidotsPublisher::publishData(Client* outClient) { int16_t responseCode = 0; if (did_respond > 0) { char responseCode_char[4]; + responseCode_char[3] = 0; for (uint8_t i = 0; i < 3; i++) { responseCode_char[i] = tempBuffer[i + 9]; } @@ -287,7 +200,7 @@ int16_t UbidotsPublisher::publishData(Client* outClient) { responseCode = 504; } - PRINTOUT(F("-- Response Code --")); + PRINTOUT(F("\n-- Response Code --")); PRINTOUT(responseCode); return responseCode; diff --git a/src/publishers/UbidotsPublisher.h b/src/publishers/UbidotsPublisher.h index adb47f9e2..6f4a7f328 100644 --- a/src/publishers/UbidotsPublisher.h +++ b/src/publishers/UbidotsPublisher.h @@ -50,20 +50,10 @@ class UbidotsPublisher : public dataPublisher { * logger. * * @param baseLogger The logger supplying the data to be published - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ - explicit UbidotsPublisher(Logger& baseLogger, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + explicit UbidotsPublisher(Logger& baseLogger, int sendEveryX = 1); /** * @brief Construct a new Ubidots Publisher object * @@ -71,20 +61,10 @@ class UbidotsPublisher : public dataPublisher { * @param inClient An Arduino client instance to use to print data to. * Allows the use of any type of client and multiple clients tied to a * single TinyGSM modem instance - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected - * - * @note It is possible (though very unlikey) that using this constructor - * could cause errors if the compiler attempts to initialize the publisher - * instance before the logger instance. If you suspect you are seeing that - * issue, use the null constructor and a populated begin(...) within your - * set-up function. + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ - UbidotsPublisher(Logger& baseLogger, Client* inClient, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + UbidotsPublisher(Logger& baseLogger, Client* inClient, int sendEveryX = 1); /** * @brief Construct a new Ubidots Publisher object * @@ -95,15 +75,11 @@ class UbidotsPublisher : public dataPublisher { * specific device's setup panel). * @param deviceID The device API Label from Ubidots, derived from the * user-specified device name. - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ UbidotsPublisher(Logger& baseLogger, const char* authentificationToken, - const char* deviceID, uint8_t sendEveryX = 1, - uint8_t sendOffset = 0); + const char* deviceID, int sendEveryX = 1); /** * @brief Construct a new Ubidots Publisher object * @@ -117,15 +93,12 @@ class UbidotsPublisher : public dataPublisher { * specific device's setup panel). * @param deviceID The device API Label from Ubidots, derived from the * user-specified device name. - * @param sendEveryX Currently unimplemented, intended for future use to - * enable caching and bulk publishing - * @param sendOffset Currently unimplemented, intended for future use to - * enable publishing data at a time slightly delayed from when it is - * collected + * @param sendEveryX Interval (in units of the logging interval) between + * attempted data transmissions. NOTE: not implemented by this publisher! */ UbidotsPublisher(Logger& baseLogger, Client* inClient, const char* authentificationToken, const char* deviceID, - uint8_t sendEveryX = 1, uint8_t sendOffset = 0); + int sendEveryX = 1); /** * @brief Destroy the EnviroDIY Publisher object */ @@ -153,30 +126,6 @@ class UbidotsPublisher : public dataPublisher { * @return uint16_t The number of characters in the JSON object. */ uint16_t calculateJsonSize(); - // /** - // * @brief Calculates how long the full post request will be, including - // * headers - // * - // * @return uint16_t The length of the full request including HTTP - // headers. - // */ - // uint16_t calculatePostSize(); - - /** - * @brief This generates a properly formatted JSON for Ubidots and prints - * it to the input Arduino stream object. - * - * @param stream The Arduino stream to write out the JSON to. - */ - void printSensorDataJSON(Stream* stream); - - /** - * @brief This prints a fully structured post request for the Ubidots API - * to the specified stream. - * - * @param stream The Arduino stream to write out the request to. - */ - void printUbidotsRequest(Stream* stream); // A way to begin with everything already set /** @@ -225,12 +174,10 @@ class UbidotsPublisher : public dataPublisher { * * @{ */ - static const char* postEndpoint; ///< The endpoint - static const char* ubidotsHost; ///< The host name - static const int ubidotsPort; ///< The host port - static const char* tokenHeader; ///< The token header text - // static const char *cacheHeader; ///< The cache header text - // static const char *connectionHeader; ///< The keep alive header text + static const char* postEndpoint; ///< The endpoint + static const char* ubidotsHost; ///< The host name + static const int ubidotsPort; ///< The host port + static const char* tokenHeader; ///< The token header text static const char* contentLengthHeader; ///< The content length header text static const char* contentTypeHeader; ///< The content type header text /**@}*/ diff --git a/src/sensors/YosemitechY700.h b/src/sensors/YosemitechY700.h index 809a0808f..83cde2428 100644 --- a/src/sensors/YosemitechY700.h +++ b/src/sensors/YosemitechY700.h @@ -70,11 +70,11 @@ /// 1000 ms. #define Y700_WARM_UP_TIME_MS 1000 /// @brief Sensor::_stabilizationTime_ms; time between "StartMeasurement" -/// command and stable reading - Y700 takes 4 s to get stability <1 mm, +/// command and stable reading - Y700 takes 4 s to get stability <1 mm, /// but 12 s for <0.1 mm. If highest precision is required, increase to 12000. #define Y700_STABILIZATION_TIME_MS 4000 /// @brief Sensor::_measurementTime_ms; the Y700 takes <1 s for new values. -/// but >1 s for values that don't seem autocorrelated. +/// but >1 s for values that don't seem autocorrelated. #define Y700_MEASUREMENT_TIME_MS 1000 /**@}*/ @@ -211,9 +211,9 @@ class YosemitechY700_Pressure : public Variable { * @param varCode A short code to help identify the variable in files; * optional with a default value of "Y700Pres". */ - explicit YosemitechY700_Pressure(YosemitechY700* parentSense, - const char* uuid = "", - const char* varCode = Y700_PRES_DEFAULT_CODE) + explicit YosemitechY700_Pressure( + YosemitechY700* parentSense, const char* uuid = "", + const char* varCode = Y700_PRES_DEFAULT_CODE) : Variable(parentSense, (const uint8_t)Y700_PRES_VAR_NUM, (uint8_t)Y700_PRES_RESOLUTION, Y700_PRES_VAR_NAME, Y700_PRES_UNIT_NAME, varCode, uuid) {}