diff --git a/library.properties b/library.properties index c863abb..10c695a 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=Blues Wireless Notecard -version=1.2.7 +version=1.2.8 author=Blues Wireless maintainer=Blues Wireless sentence=An easy to use Notecard Library for Arduino. diff --git a/src/note-c/n_const.c b/src/note-c/n_const.c index 9f07e30..8741f1b 100644 --- a/src/note-c/n_const.c +++ b/src/note-c/n_const.c @@ -22,4 +22,5 @@ const char *c_mem = "mem"; const char *c_timeout = "timeout"; const char *c_err = "err"; const char *c_req = "req"; +const char *c_cmd = "cmd"; const char *c_bad = "bad"; diff --git a/src/note-c/n_helpers.c b/src/note-c/n_helpers.c index 30775d8..f1339d3 100644 --- a/src/note-c/n_helpers.c +++ b/src/note-c/n_helpers.c @@ -789,9 +789,10 @@ bool NoteGetStatusST(char *statusBuf, int statusBufLen, JTIME *bootTime, bool *r bool NoteSleep(char *stateb64, uint32_t seconds, const char *modes) { bool success = false; - // Put ourselves to sleep + // Use a Command rather than a Request so that the Notecard doesn't try to send + // a response back to us, which would cause a communications error on that end. _Debug("requesting sleep\n"); - J *req = NoteNewRequest("card.attn"); + J *req = NoteNewCommand("card.attn"); if (req != NULL) { // Add the base64 item in a wonderful way that doesn't strdup the huge string J *stringReferenceItem = JCreateStringReference(stateb64); diff --git a/src/note-c/n_hooks.c b/src/note-c/n_hooks.c index abdb4d4..eb89266 100644 --- a/src/note-c/n_hooks.c +++ b/src/note-c/n_hooks.c @@ -503,9 +503,9 @@ char NoteSerialReceive() { @returns A boolean indicating whether the I2C bus was reset. */ /**************************************************************************/ -bool NoteI2CReset() { +bool NoteI2CReset(uint16_t DevAddress) { if (hookActiveInterface == interfaceI2C && hookI2CReset != NULL) { - return hookI2CReset(); + return hookI2CReset(DevAddress); } return false; } @@ -552,7 +552,7 @@ const char *NoteI2CReceive(uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size, */ /**************************************************************************/ uint32_t NoteI2CAddress() { - if (i2cAddress == NOTE_I2C_MAX_DEFAULT) + if (i2cAddress == NOTE_I2C_ADDR_DEFAULT) return 0x17; return i2cAddress; } diff --git a/src/note-c/n_i2c.c b/src/note-c/n_i2c.c index 3c0fd3a..8fdb43f 100644 --- a/src/note-c/n_i2c.c +++ b/src/note-c/n_i2c.c @@ -57,7 +57,7 @@ const char *i2cNoteTransaction(char *json, char **jsonResponse) { estr = _I2CTransmit(_I2CAddress(), chunk, chunklen); if (estr != NULL) { _Free(transmitBuf); - _I2CReset(); + _I2CReset(_I2CAddress()); _UnlockI2C(); #ifdef ERRDBG _Debug("i2c transmit: "); @@ -80,6 +80,10 @@ const char *i2cNoteTransaction(char *json, char **jsonResponse) { // Free the transmit buffer _Free(transmitBuf); + // If no reply expected, we're done + if (jsonResponse == NULL) + return NULL; + // Dynamically grow the buffer as we read. Note that we always put the +1 in the alloc // so we can be assured that it can be null-terminated, which must be the case because // our json parser requires a null-terminated string. @@ -187,7 +191,7 @@ bool i2cNoteReset() { // Reset the I2C subsystem and exit if failure _LockI2C(); - bool success = _I2CReset(); + bool success = _I2CReset(_I2CAddress()); _UnlockI2C(); if (!success) return false; @@ -235,7 +239,7 @@ bool i2cNoteReset() { // Reinitialize i2c if there's no response _LockI2C(); - _I2CReset(); + _I2CReset(_I2CAddress()); _UnlockI2C(); _Debug(ERRSTR("notecard not responding\n", "no notecard\n")); _DelayMs(2000); diff --git a/src/note-c/n_lib.h b/src/note-c/n_lib.h index fd4ff41..44f4561 100644 --- a/src/note-c/n_lib.h +++ b/src/note-c/n_lib.h @@ -87,7 +87,7 @@ bool NoteSerialReset(void); void NoteSerialTransmit(uint8_t *, size_t, bool); bool NoteSerialAvailable(void); char NoteSerialReceive(void); -bool NoteI2CReset(void); +bool NoteI2CReset(uint16_t DevAddress); const char *NoteI2CTransmit(uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size); const char *NoteI2CReceive(uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size, uint32_t *avail); bool NoteHardReset(void); @@ -96,36 +96,39 @@ bool NoteIsDebugOutputActive(void); // Constants, a global optimization to save static string memory extern const char *c_null; - #define c_null_len 4 -extern const char *c_false; +extern const char *c_false; #define c_false_len 5 -extern const char *c_true; +extern const char *c_true; #define c_true_len 4 -extern const char *c_nullstring; +extern const char *c_nullstring; #define c_nullstring_len 0 -extern const char *c_newline; +extern const char *c_newline; #define c_newline_len 2 -extern const char *c_mem; +extern const char *c_mem; #define c_mem_len 3 -extern const char *c_timeout; +extern const char *c_timeout; #define c_timeout_len 7 -extern const char *c_err; +extern const char *c_err; #define c_err_len 3 -extern const char *c_req; +extern const char *c_req; #define c_req_len 3 -extern const char *c_bad; +extern const char *c_cmd; +#define c_cmd_len 3 + +extern const char *c_bad; #define c_bad_len 3 + // Readability wrappers. Anything starting with _ is simply calling the wrapper // function. #define _LockNote NoteLockNote diff --git a/src/note-c/n_request.c b/src/note-c/n_request.c index e24728f..0153fc9 100644 --- a/src/note-c/n_request.c +++ b/src/note-c/n_request.c @@ -75,6 +75,23 @@ J *NoteNewRequest(const char *request) { return reqdoc; } +/**************************************************************************/ +/*! + @brief Create a new command object to populate before sending to the Notecard. + Lock for mutual exclusion, not only because access to the card must be serialized, but also because + both C++ and ArduinoJSON call malloc() which is not a thread-safe operation. + @param request + The name of the command, for example `hub.set`. + @returns a `J` cJSON object with the request name pre-populated. +*/ +/**************************************************************************/ +J *NoteNewCommand(const char *request) { + J *reqdoc = JCreateObject(); + if (reqdoc != NULL) + JAddStringToObject(reqdoc, c_cmd, request); + return reqdoc; +} + /**************************************************************************/ /*! @brief Send a request to the Notecard. @@ -174,6 +191,13 @@ char *NoteRequestResponseJSON(char *reqJSON) { /**************************************************************************/ J *NoteTransaction(J *req) { + // Validate in case of memory failure of the requestor + if (req == NULL) + return NULL; + + // Determine whether or not a response will be expected, by virtue of "cmd" being present + bool noResponseExpected = (JGetString(req, "req")[0] == '\0' && JGetString(req, "cmd")[0] != '\0'); + // If a reset of the module is required for any reason, do it now. // We must do this before acquiring lock. if (resetRequired) { @@ -198,7 +222,11 @@ J *NoteTransaction(J *req) { // Pertform the transaction char *responseJSON; - const char *errStr = _Transaction(json, &responseJSON); + const char *errStr; + if (noResponseExpected) + errStr = _Transaction(json, NULL); + else + errStr = _Transaction(json, &responseJSON); // Free the json JFree(json); @@ -211,6 +239,12 @@ J *NoteTransaction(J *req) { return rsp; } + // Exit with a blank object (with no err field) if no response expected + if (noResponseExpected) { + _UnlockNote(); + return JCreateObject(); + } + // Parse the reply from the card on the input stream J *rspdoc = JParse(responseJSON); if (rspdoc == NULL) { diff --git a/src/note-c/n_serial.c b/src/note-c/n_serial.c index 32c2ebb..63c140f 100644 --- a/src/note-c/n_serial.c +++ b/src/note-c/n_serial.c @@ -43,6 +43,10 @@ const char *serialNoteTransaction(char *json, char **jsonResponse) { _DelayMs(CARD_REQUEST_SERIAL_SEGMENT_DELAY_MS); } + // If no reply expected, we're done + if (jsonResponse == NULL) + return NULL; + // Wait for something to become available, processing timeout errors up-front // because the json parse operation immediately following is subject to the // serial port timeout. We'd like more flexibility in max timeout and ultimately diff --git a/src/note-c/note.h b/src/note-c/note.h index 151ee17..36d4770 100644 --- a/src/note-c/note.h +++ b/src/note-c/note.h @@ -38,25 +38,34 @@ #pragma once // In case they're not yet defined +#include #include #include -// Define our basic floating data type. In most cases "double" is the right answer, however for -// very small microcontrollers these libraries are simply too large. This ensures that we -// use FLOATs when both are the same, and we don't define constants that are too large. +// Determine our basic floating data type. In most cases "double" is the right answer, however for +// very small microcontrollers we must use single-precision. +#if defined(FLT_MAX_EXP) && defined(DBL_MAX_EXP) +#if (FLT_MAX_EXP == DBL_MAX_EXP) +#define NOTE_FLOAT +#endif +#elif defined(__FLT_MAX_EXP__) && defined(__DBL_MAX_EXP__) #if (__FLT_MAX_EXP__ == __DBL_MAX_EXP__) #define NOTE_FLOAT -#define ERRSTR(x,y) (y) -#define NOTE_LOWMEM +#endif #else -#define ERRSTR(x,y) (x) -#define ERRDBG +#error What are floating point exponent length symbols for this compiler? #endif +// If using a short float, we must be on a VERY small MCU. In this case, define additional +// symbols that will save quite a bit of memory in the runtime image. #ifdef NOTE_FLOAT #define JNUMBER float +#define ERRSTR(x,y) (y) +#define NOTE_LOWMEM #else #define JNUMBER double +#define ERRSTR(x,y) (x) +#define ERRDBG #endif // UNIX Epoch time (also known as POSIX time) is the number of seconds that have elapsed since @@ -83,7 +92,7 @@ typedef bool (*serialResetFn) (void); typedef void (*serialTransmitFn) (uint8_t *data, size_t len, bool flush); typedef bool (*serialAvailableFn) (void); typedef char (*serialReceiveFn) (void); -typedef bool (*i2cResetFn) (void); +typedef bool (*i2cResetFn) (uint16_t DevAddress); typedef const char * (*i2cTransmitFn) (uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size); typedef const char * (*i2cReceiveFn) (uint16_t DevAddress, uint8_t* pBuffer, uint16_t Size, uint32_t *avail); @@ -92,6 +101,7 @@ bool NoteReset(void); void NoteResetRequired(void); #define NoteNewBody JCreateObject J *NoteNewRequest(const char *request); +J *NoteNewCommand(const char *request); J *NoteRequestResponse(J *req); char *NoteRequestResponseJSON(char *reqJSON); void NoteSuspendTransactionDebug(void);