I have been experimenting with multiple F style notecarriers with cellular card and a Swan mcu’s. My application is for backup remote access to a gate. I have set the notecard to continuous mode as periodic does not make sense in this application. In less than 24 hours from uploading firmware or rebooting the MCU, the device stops responding to .qi files. Notehub shows the notecard as online, but the mcu does not process the .qi file as it should. The only way I can get it respond is to press the reset button on the swan. After a reset, it will work, but then stop after a number of hours, which obviously can’t work for a remote install.
I tried to include code like
JAddNumberToObject(req, “inbound”, 15);
JAddNumberToObject(req, “outbound”, 60);
that I thought I understood it to force a connection, thinking that may be the issue, but I see nothing ever in notehub that tells me this has done anything and maybe I should expect to?
Perhaps I am doing something wrong with the code that is causing memory issues over time leading to a lock up? I don’t have the knowledge to know if I have.
Below is the mcu code I have. I would appreciate any advice on what I am doing wrong. Thanks.
Blockquote
type or paste code here
#include <Arduino.h>
#include <Notecard.h>
#define productUID “app:a1xxxxxxxxxxxxxxxxxxxxx”
#define RELAY_PIN D10
#define COMMAND_CHECK_INTERVAL 10000 // Check for commands every 10 seconds
#define SYNC_INTERVAL 600000 // Sync every 10 minutes (600,000 ms)
// Timeout after HUB_SET_TIMEOUT seconds of retrying hub.set.
#ifndef HUB_SET_TIMEOUT
#define HUB_SET_TIMEOUT 5 //is this minutes?
#endif
Notecard notecard;
unsigned long relayActivationTime = 5000; // Default to 5 seconds
unsigned long relayStartTime = 0;
unsigned long relayDeactivationTime = 0;
bool relayActive = false;
unsigned long lastSyncTime = 0;
unsigned long lastCommandCheckTime = 0;
bool holdActive = false;
void logMessage(const char* message) {
SerialUSB.print(millis());
SerialUSB.print(" | ");
SerialUSB.println(message);
}
//using the environment variable for relay activation time
void updateRelayActivationTime() {
J *req = notecard.newRequest(“env.get”);
JAddStringToObject(req, “name”, “relay_activation_time”);
J *rsp = notecard.requestAndResponse(req);
logMessage(“Updating relay activation time…”);
if (rsp != NULL) {
logMessage(“Response received from Notecard”);
if (!notecard.responseError(rsp)) {
const char *text = JGetString(rsp, “text”);
if (text != NULL) {
char buffer[100];
snprintf(buffer, sizeof(buffer), “Environment variable value (raw): %s”, text);
logMessage(buffer);
double time = atof(text); // Convert string to float
snprintf(buffer, sizeof(buffer), "Converted time value: %.2f", time);
logMessage(buffer);
if (time > 0) {
unsigned long oldTime = relayActivationTime;
relayActivationTime = (unsigned long)(time * 1000); // Convert seconds to milliseconds
snprintf(buffer, sizeof(buffer), "Updated relay activation time from %lu ms to %lu ms", oldTime, relayActivationTime);
logMessage(buffer);
} else {
logMessage("Invalid time value in environment variable. Using previous value.");
}
} else {
logMessage("No 'text' field in response. Using previous time.");
}
} else {
logMessage("Error in response. Using previous time.");
}
notecard.deleteResponse(rsp);
} else {
logMessage(“No response from Notecard. Using previous time.”);
}
char buffer[100];
snprintf(buffer, sizeof(buffer), “Current relay activation time: %lu ms”, relayActivationTime);
logMessage(buffer);
}
// Function to get the Notecard status variables
J *getNotecardStatus() {
J *status = JCreateObject();
char logBuffer[150];
// Get temperature
J *tempReq = notecard.newRequest(“card.temp”);
J *tempRsp = notecard.requestAndResponse(tempReq);
if (tempRsp != NULL) {
double temp = JGetNumber(tempRsp, “value”);
JAddNumberToObject(status, “temperature”, temp);
snprintf(logBuffer, sizeof(logBuffer), “SMN Temperature: %.2f°C”, temp);
logMessage(logBuffer);
notecard.deleteResponse(tempRsp);
} else {
logMessage(“SMN Failed to get temperature”);
}
// Get voltage
J *voltReq = notecard.newRequest(“card.voltage”);
J *voltRsp = notecard.requestAndResponse(voltReq);
if (voltRsp != NULL) {
double voltage = JGetNumber(voltRsp, “value”);
JAddNumberToObject(status, “voltage”, voltage);
snprintf(logBuffer, sizeof(logBuffer), “SMN Voltage: %.2fV”, voltage);
logMessage(logBuffer);
notecard.deleteResponse(voltRsp);
} else {
logMessage(“SMN Failed to get voltage”);
}
// Get wireless info
logMessage(“SMN Requesting wireless information…”);
J *wirelessReq = notecard.newRequest(“card.wireless”);
J *wirelessRsp = notecard.requestAndResponse(wirelessReq);
if (wirelessRsp != NULL) {
char *json = JPrint(wirelessRsp);
if (json) {
logMessage(“SMN Raw wireless response:”);
logMessage(json);
free(json);
}
const char* wirelessStatus = JGetString(wirelessRsp, "status");
if (wirelessStatus) {
JAddStringToObject(status, "wireless_status", wirelessStatus);
snprintf(logBuffer, sizeof(logBuffer), "SMN Wireless Status: %s", wirelessStatus);
logMessage(logBuffer);
}
J *net = JGetObject(wirelessRsp, "net");
if (net != NULL) {
int bars = JGetInt(net, "bars");
const char* rat = JGetString(net, "rat");
int rssi = JGetInt(net, "rssi");
const char* band = JGetString(net, "band");
JAddNumberToObject(status, "bars", bars);
if (rat) JAddStringToObject(status, "rat", rat);
JAddNumberToObject(status, "rssi", rssi);
if (band) JAddStringToObject(status, "band", band);
snprintf(logBuffer, sizeof(logBuffer), "SMN Wireless: Bars: %d, RAT: %s, RSSI: %d, Band: %s",
bars, rat ? rat : "N/A", rssi, band ? band : "N/A");
logMessage(logBuffer);
} else {
logMessage("SMN Wireless: No net object in response");
}
notecard.deleteResponse(wirelessRsp);
} else {
logMessage(“SMN Failed to get wireless info”);
}
// Get motion info
logMessage(“SMN Requesting motion information…”);
J *motionReq = notecard.newRequest(“card.motion”);
J *motionRsp = notecard.requestAndResponse(motionReq);
if (motionRsp != NULL) {
char json = JPrint(motionRsp);
if (json) {
logMessage(“SMN Raw motion response:”);
logMessage(json);
free(json);
}
const char orientation = JGetString(motionRsp, “status”);
if (orientation != NULL) {
JAddStringToObject(status, “orientation”, orientation);
snprintf(logBuffer, sizeof(logBuffer), “SMN Orientation: %s”, orientation);
logMessage(logBuffer);
} else {
logMessage(“SMN Orientation: Invalid reading”);
}
notecard.deleteResponse(motionRsp);
} else {
logMessage(“SMN Failed to get motion info”);
}
return status;
}
// Function to send Notecard status to Notehub
void sendNotecardStatusToNotehub() {
logMessage(“Preparing to send Notecard status to Notehub…”);
J *req = notecard.newRequest(“note.add”);
if (req != NULL) {
JAddStringToObject(req, “file”, “notecard_status.qo”);
JAddBoolToObject(req, “sync”, true);
J *body = JCreateObject();
if (body != NULL) {
// Add additional status information
J *additionalStatus = getNotecardStatus();
if (additionalStatus != NULL) {
JAddItemToObject(body, "notecard_status", additionalStatus);
logMessage("Added Notecard status to the request body.");
} else {
logMessage("Failed to get additional status information.");
}
JAddItemToObject(req, "body", body);
logMessage("Request body created successfully.");
} else {
logMessage("Failed to create request body.");
}
notecard.sendRequest(req);
logMessage("Sent Notecard status update to Notehub.");
} else {
logMessage("Failed to create Notecard request.");
}
}
// Function to send relay status to Notehub
void sendRelayStatusToNotehub(const char* relayState) {
J *req = notecard.newRequest(“note.add”);
if (req != NULL) {
JAddStringToObject(req, “file”, “relay_status.qo”);
JAddBoolToObject(req, “sync”, true);
J *body = JCreateObject();
if (body != NULL) {
JAddStringToObject(body, "relay_output_state", relayState);
JAddItemToObject(req, "body", body);
}
notecard.sendRequest(req);
char buffer[100];
snprintf(buffer, sizeof(buffer), "Sent relay status update to Notehub: %s", relayState);
logMessage(buffer);
}
}
// Function to get the last command
typedef struct {
char command[30];
char source[30];
} Command;
Command getLastCommand(const char* file) {
Command cmd;
cmd.command[0] = ‘\0’;
cmd.source[0] = ‘\0’;
J *req = notecard.newRequest("note.get");
JAddStringToObject(req, "file", file);
JAddBoolToObject(req, "delete", true);
J *rsp = notecard.requestAndResponse(req);
if (!notecard.responseError(rsp)) {
logMessage("Command note retrieved");
J *body = JGetObject(rsp, "body");
if (body != NULL) {
const char *cmdValue = NULL;
if (strcmp(file, "relay_change_request.qi") == 0) {
cmdValue = JGetString(body, "relay"); // For relay commands
} else if (strcmp(file, "notecard_status_request.qi") == 0) {
cmdValue = JGetString(body, "notecard"); // For Notecard status commands
}
if (cmdValue != NULL) {
strncpy(cmd.command, cmdValue, sizeof(cmd.command) - 1);
cmd.command[sizeof(cmd.command) - 1] = '\0'; // Ensure null-termination
strncpy(cmd.source, file, sizeof(cmd.source) - 1);
cmd.source[sizeof(cmd.source) - 1] = '\0';
notecard.deleteResponse(rsp);
return cmd;
}
}
notecard.deleteResponse(rsp);
}
logMessage("No commands available in file");
return cmd;
}
// Function to process Notecard status commands
void processNotecardStatusCommands() {
logMessage(“Checking for Notecard status commands…”);
Command cmd = getLastCommand(“notecard_status_request.qi”);
if (cmd.command[0] != '\0') { // Only process if a command was received
char buffer[100];
snprintf(buffer, sizeof(buffer), "Notecard status command received: '%s' from '%s'", cmd.command, cmd.source);
logMessage(buffer);
if (strcmp(cmd.command, "update_card_status") == 0) {
logMessage("COMMAND: Processing Update Card Status command");
sendNotecardStatusToNotehub();
} else {
snprintf(buffer, sizeof(buffer), "Unknown Notecard status command received: '%s'", cmd.command);
logMessage(buffer);
}
} else {
logMessage("No Notecard status command received");
}
}
// send status to notehub - this is the old generic - not notecard or relay specific
void sendStatusToNotehub(const char* status) {
J *req = notecard.newRequest(“note.add”);
if (req != NULL) {
JAddStringToObject(req, “file”, “status.qo”);
JAddBoolToObject(req, “sync”, true);
J *body = JCreateObject();
if (body != NULL) {
JAddStringToObject(body, "status", status);
// Add additional status information
J *additionalStatus = getNotecardStatus();
if (additionalStatus != NULL) {
JAddItemToObject(body, "notecard_status", additionalStatus);
}
JAddItemToObject(req, "body", body);
}
notecard.sendRequest(req);
logMessage("Sent comprehensive status update to Notehub");
}
}
// end send status to notehub
// Function to process relay commands
void processRelayCommands() {
logMessage(“Checking for relay commands…”);
Command cmd = getLastCommand(“relay_change_request.qi”);
if (cmd.command[0] != '\0') { // Only process if a command was received
char buffer[100];
snprintf(buffer, sizeof(buffer), "Relay command received: '%s' from '%s'", cmd.command, cmd.source);
logMessage(buffer);
if (strcmp(cmd.command, "open") == 0) {
if (!relayActive && !holdActive) {
// Initialize and fetch the relay activation time
updateRelayActivationTime(); // added this to check if environment variable for the relay on time has changed
logMessage("COMMAND: Processing Open command");
snprintf(buffer, sizeof(buffer), "ACTION: Activating relay for %lu ms", relayActivationTime);
logMessage(buffer);
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(RELAY_PIN, HIGH);
sendRelayStatusToNotehub("open");
relayStartTime = millis();
relayDeactivationTime = relayStartTime + relayActivationTime;
snprintf(buffer, sizeof(buffer), "Relay deactivation time set to: %lu", relayDeactivationTime);
logMessage(buffer);
relayActive = true;
} else {
logMessage("Relay already active or on hold, ignoring open command");
}
} else if (strcmp(cmd.command, "hold_open") == 0) {
if (!holdActive) {
logMessage("COMMAND: Processing Hold Open command");
digitalWrite(LED_BUILTIN, HIGH);
digitalWrite(RELAY_PIN, HIGH);
holdActive = true;
relayActive = false; // We're not using the timed activation in this case
sendRelayStatusToNotehub("held_open");
} else {
logMessage("Hold already active, ignoring hold_open command");
}
} else if (strcmp(cmd.command, "close_held_open_gate") == 0) {
if (holdActive) {
logMessage("COMMAND: Processing Release Hold command");
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(RELAY_PIN, LOW);
holdActive = false;
sendRelayStatusToNotehub("closed");
} else {
logMessage("Hold not active, ignoring close_held_open_gate");
}
} else {
snprintf(buffer, sizeof(buffer), "Unknown relay command received: '%s'", cmd.command);
logMessage(buffer);
}
} else {
logMessage("No relay command received");
}
}
// Function to process all commands
void processCommands() {
// Process relay commands
processRelayCommands();
// Process Notecard status commands
processNotecardStatusCommands();
}
/////////////////////////////////////////////////////////////////////////////////////////////////
// Just setup and loop functions below this line
///////////////////////////////////////////////////////////////////////////////////////////////
void setup() {
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
SerialUSB.begin(115200);
delay(15000); // Give it some time to initialize - 15 seconds
logMessage(“Starting the setup loop”);
// Begin communication with the Notecard
notecard.begin();
notecard.setDebugOutputStream(SerialUSB);
// Configure the Notecard for continuous mode
J *req = notecard.newRequest(“hub.set”);
JAddStringToObject(req, “product”, productUID);
JAddStringToObject(req, “mode”, “continuous”);
JAddBoolToObject(req, “sync”, true);
JAddNumberToObject(req, “inbound”, 15); //amount of time in Mimutes between a definite (forced) check for inboud notes via a hub.sync
JAddNumberToObject(req, “outbound”, 60); //amount of time in Minutes between outboud sync (forced) via a hub.sync - others are supposed to happen as the MCU code dictates
// // The hub.set request may fail if it’s sent shortly after power up. We use
// // sendRequestWithRetry to give it a chance to succeed.
// if (!notecard.sendRequestWithRetry(req, HUB_SET_TIMEOUT)) {
// logMessage(“notecard not responding\n”);
// return false;
// }
// notecard.sendRequest(req);
notecard.sendRequestWithRetry(req, 8); //a .sendrequest with an automatic retry after 8 (seconds?) if it fails the first time?
// Initialize and fetch the relay activation time
updateRelayActivationTime();
logMessage(“Setup complete”);
}
void loop() {
unsigned long currentTime = millis();
// Check for commands frequently
if (currentTime - lastCommandCheckTime >= COMMAND_CHECK_INTERVAL) {
char buffer[100];
snprintf(buffer, sizeof(buffer), “Checking commands after %lu ms”, currentTime - lastCommandCheckTime);
logMessage(buffer);
lastCommandCheckTime = currentTime;
processCommands();
processNotecardStatusCommands();
}
// Sync with Notehub less frequently
if (currentTime - lastSyncTime >= SYNC_INTERVAL) {
logMessage(“START SYNC”);
J *rsp = notecard.newRequest("hub.sync");
notecard.sendRequest(rsp);
updateRelayActivationTime();
lastSyncTime = currentTime;
logMessage("END SYNC");
}
// Check relay status and deactivate if necessary (only if not in hold mode)
if (relayActive && !holdActive && currentTime >= relayDeactivationTime) {
logMessage(“ACTION: Deactivating relay”);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(LED_BUILTIN, LOW);
relayActive = false;
sendRelayStatusToNotehub(“inactive output”);
}
// Short delay to prevent tight looping
delay(100);
}