Skip to content

Instantly share code, notes, and snippets.

@rdlauer
Last active May 12, 2025 17:43
Show Gist options
  • Save rdlauer/763b49a52272f693642de380cb47b433 to your computer and use it in GitHub Desktop.
Save rdlauer/763b49a52272f693642de380cb47b433 to your computer and use it in GitHub Desktop.
Testing Notecard Power Consumption with Mojo (Starnote Edition)
#include <Arduino.h>
#include <Notecard.h>
#include <stdio.h>
#include <string.h>
#define usbSerial Serial
#define PRODUCT_UID "your-product-uid"
#define DEBUG 0
#define WIFI_SSID "your-ssid"
#define WIFI_PASSWORD "your-password"
#define LAT your-lat
#define LON your-lon
Notecard notecard;
//-----------------------------------------------------------------------------
// Non-blocking State Machine Variables
//-----------------------------------------------------------------------------
enum TestState
{
TEST1_ONE_NOTE, // Test 1: sat-one-note.qo (sync a sample note, 5 iterations)
TEST2_IDLE, // Test 2: sat-idle.qo (measure idle mode for 1 hour, 5 iterations)
TEST3_MINIMUM, // Test 3: sat-low-power.qo (12-hour test with 60-min intervals)
DONE // All tests complete
};
TestState currentTest = TEST1_ONE_NOTE;
int phase = 0; // Phase within the current test
int iteration = 0; // Iteration counter for tests that run multiple times
unsigned long stateStartTime = 0; // Used for delay timing in a phase
// Variables used for the 12-hour test (Test 3)
unsigned long testStartTime = 0;
unsigned long nextAction = 0;
unsigned long duration = 12UL * 60UL * 60UL * 1000UL; // 12 hours
unsigned long interval = 60UL * 60UL * 1000UL; // 60 minutes
//-----------------------------------------------------------------------------
// Non-blocking Hub Sync Variables
//-----------------------------------------------------------------------------
bool syncInProgress = false;
unsigned long syncStartTime = 0;
unsigned long lastSyncPollTime = 0;
//-----------------------------------------------------------------------------
// Function Declarations (Non-blocking versions)
//-----------------------------------------------------------------------------
void ResetMojo()
{
J *req = notecard.newRequest("card.power");
if (req)
{
JAddBoolToObject(req, "reset", true);
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("ResetMojo command sent");
}
}
else
{
if (DEBUG)
{
usbSerial.println("Failed to create card.power request");
}
}
}
void TransportHelper(const char *method)
{
J *req = notecard.newRequest("card.transport");
if (req != NULL)
{
JAddStringToObject(req, "method", method);
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("card.transport method set to: " + String(method));
}
}
}
void HubSetHelper(const char *mode)
{
J *req = notecard.newRequest("hub.set");
if (req != NULL)
{
JAddStringToObject(req, "product", PRODUCT_UID);
JAddStringToObject(req, "mode", mode);
JAddStringToObject(req, "sn", "STARNOTE");
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("hub.set to mode: " + String(mode));
}
}
}
// Start a hub.sync request.
void sendHubSync(bool outOnly)
{
J *req = notecard.newRequest("hub.sync");
if (req != NULL)
{
if (outOnly)
{
JAddBoolToObject(req, "out", true);
}
notecard.sendRequest(req);
syncInProgress = true;
syncStartTime = millis();
lastSyncPollTime = millis();
if (DEBUG)
{
usbSerial.println("hub.sync command sent");
}
}
else
{
if (DEBUG)
{
usbSerial.println("Failed to create hub.sync request");
}
}
}
// Poll hub.sync.status - returns true once sync is complete or times out.
bool pollHubSync()
{
if (!syncInProgress)
return true;
if (millis() - lastSyncPollTime >= 1000UL)
{
lastSyncPollTime = millis();
J *req = notecard.newRequest("hub.sync.status");
if (req != NULL)
{
J *rsp = notecard.requestAndResponse(req);
if (rsp != NULL)
{
int completed = JGetNumber(rsp, "completed");
if (completed > 0)
{
syncInProgress = false;
if (DEBUG)
{
usbSerial.println("hub.sync complete with completed: " + String(completed));
}
}
notecard.deleteResponse(rsp);
}
else
{
if (DEBUG)
{
usbSerial.println("hub.sync.status response failed");
}
}
}
}
if (syncInProgress && (millis() - syncStartTime >= 360000UL)) // 6-min timeout
{
syncInProgress = false;
if (DEBUG)
{
usbSerial.println("reached hub.sync timeout");
}
}
return !syncInProgress;
}
void SendMojoReading(const char *notefileName)
{
float mah = 0.0;
J *req = notecard.newRequest("card.power");
if (req != NULL)
{
J *rsp = notecard.requestAndResponse(req);
if (rsp != NULL)
{
mah = JGetNumber(rsp, "milliamp_hours");
if (DEBUG)
{
usbSerial.println("Taking mojo mah reading: " + String(mah));
}
notecard.deleteResponse(rsp);
}
}
if (mah > 0.0)
{
J *reqNote = notecard.newRequest("note.add");
if (reqNote != NULL)
{
JAddStringToObject(reqNote, "file", notefileName);
J *body = JAddObjectToObject(reqNote, "body");
if (body)
{
JAddNumberToObject(body, "mah", mah);
JAddStringToObject(body, "sku", "STARNOTE");
}
notecard.sendRequest(reqNote);
if (DEBUG)
{
usbSerial.println("note.add request created for file: " + String(notefileName));
}
// Initiate a hub.sync after adding the note.
sendHubSync(true);
}
}
}
void CreateGenericNote()
{
J *req = notecard.newRequest("note.add");
if (req != NULL)
{
JAddStringToObject(req, "file", "sat-data.qo");
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "temp", 12.3);
JAddBoolToObject(body, "alert", true);
}
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("sat-data.qo note created");
}
}
}
//-----------------------------------------------------------------------------
// setup() – Initialization
//-----------------------------------------------------------------------------
void setup()
{
if (DEBUG)
{
delay(2000);
usbSerial.begin(115200);
while (!usbSerial)
;
}
notecard.begin();
if (DEBUG)
{
notecard.setDebugOutputStream(usbSerial);
}
// Reset Notecard (factory reset)
{
J *req = notecard.newRequest("card.restore");
if (req != NULL)
{
JAddBoolToObject(req, "delete", true);
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("Factory resetting notecard with card.restore request");
}
delay(60000); // Allow time for restart
}
}
// Set transport to Wi-Fi and configure Wi-Fi credentials
TransportHelper("wifi");
{
J *req = notecard.newRequest("card.wifi");
if (req != NULL)
{
JAddStringToObject(req, "ssid", WIFI_SSID);
JAddStringToObject(req, "password", WIFI_PASSWORD);
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("Wi-Fi credentials set");
}
}
}
// Create note templates
{
J *req = notecard.newRequest("note.template");
if (req != NULL)
{
JAddStringToObject(req, "file", "sat-data.qo");
JAddStringToObject(req, "format", "compact");
JAddNumberToObject(req, "port", 1);
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "temp", 14.1);
JAddBoolToObject(body, "alert", true);
}
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("note.template sat-data.qo created");
}
}
}
{
J *req = notecard.newRequest("note.template");
if (req != NULL)
{
JAddStringToObject(req, "file", "sat-idle.qo");
JAddStringToObject(req, "format", "compact");
JAddNumberToObject(req, "port", 2);
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "mah", 14.1);
JAddStringToObject(body, "sku", "x");
}
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("note.template sat-idle.qo created");
}
}
}
{
J *req = notecard.newRequest("note.template");
if (req != NULL)
{
JAddStringToObject(req, "file", "sat-one-note.qo");
JAddStringToObject(req, "format", "compact");
JAddNumberToObject(req, "port", 3);
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "mah", 14.1);
JAddStringToObject(body, "sku", "x");
}
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("note.template sat-one-note.qo created");
}
}
}
{
J *req = notecard.newRequest("note.template");
if (req != NULL)
{
JAddStringToObject(req, "file", "sat-low-power.qo");
JAddStringToObject(req, "format", "compact");
JAddNumberToObject(req, "port", 4);
J *body = JAddObjectToObject(req, "body");
if (body)
{
JAddNumberToObject(body, "mah", 14.1);
JAddStringToObject(body, "sku", "x");
}
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("note.template sat-low-power.qo created");
}
}
}
// Set hub mode to minimum
HubSetHelper("minimum");
// Disable accelerometer
{
J *req = notecard.newRequest("card.motion.mode");
if (req != NULL)
{
JAddBoolToObject(req, "stop", true);
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("Accelerometer disabled");
}
}
}
// Set fixed GPS location
{
J *req = notecard.newRequest("card.location.mode");
if (req != NULL)
{
JAddStringToObject(req, "mode", "fixed");
JAddNumberToObject(req, "lat", LAT);
JAddNumberToObject(req, "lon", LON);
notecard.sendRequest(req);
}
}
// Tell Starnote/Notecard to use its own (fixed) location
{
J *req = notecard.newRequest("ntn.gps");
if (req != NULL)
{
JAddBoolToObject(req, "on", true);
notecard.sendRequest(req);
}
}
// Disable attn pin processing
{
J *req = notecard.newRequest("card.attn");
if (req != NULL)
{
JAddBoolToObject(req, "off", true);
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("Attn pin processing disabled");
}
}
}
// Disable aux mode
{
J *req = notecard.newRequest("card.aux");
if (req != NULL)
{
JAddStringToObject(req, "mode", "off");
notecard.sendRequest(req);
if (DEBUG)
{
usbSerial.println("Aux mode disabled");
}
}
}
// Sync all settings using Wi-Fi
sendHubSync(false);
while (!pollHubSync())
{
// MUST wait for Wi-Fi sync to complete before proceeding!
}
// Switch to satellite (ntn) mode
TransportHelper("ntn");
delay(2000);
}
//-----------------------------------------------------------------------------
// loop() – Non-blocking state machine for running tests
//-----------------------------------------------------------------------------
void loop()
{
unsigned long currentMillis = millis();
switch (currentTest)
{
// measure sending a single note 5 times
case TEST1_ONE_NOTE:
switch (phase)
{
case 0:
if (DEBUG)
{
usbSerial.println("Starting sat-one-note iteration #" + String(iteration));
}
ResetMojo();
CreateGenericNote();
sendHubSync(true);
phase = 1;
break;
case 1:
if (pollHubSync())
{
phase = 2;
}
break;
case 2:
SendMojoReading("sat-one-note.qo");
if (DEBUG)
{
usbSerial.println("Ending sat-one-note iteration #" + String(iteration));
}
iteration++;
if (iteration < 5)
{
phase = 0;
}
else
{
stateStartTime = currentMillis;
phase = 3;
}
break;
case 3:
if (currentMillis - stateStartTime >= 30000UL) // 30-sec delay before next test
{
currentTest = TEST2_IDLE;
phase = 0;
iteration = 0;
}
break;
}
break;
// measure idle mode for 1 hour, 5 iterations
case TEST2_IDLE:
switch (phase)
{
case 0:
if (DEBUG)
{
usbSerial.println("Starting sat-idle iteration #" + String(iteration));
}
ResetMojo();
stateStartTime = currentMillis;
phase = 1;
break;
case 1:
if (currentMillis - stateStartTime >= 3600000UL)
{ // 1-hour wait
phase = 2;
}
break;
case 2:
SendMojoReading("sat-idle.qo");
if (DEBUG)
{
usbSerial.println("Ending sat-idle iteration #" + String(iteration));
}
iteration++;
if (iteration < 5)
{
phase = 0; // Next iteration
}
else
{
stateStartTime = currentMillis;
phase = 3;
}
break;
case 3:
if (currentMillis - stateStartTime >= 30000UL)
{ // 30-sec delay between tests
currentTest = TEST3_MINIMUM;
phase = 0;
testStartTime = currentMillis;
nextAction = currentMillis;
}
break;
}
break;
// Test 3: sat-low-power.qo (12-hr minimum mode with 60-min syncs)
case TEST3_MINIMUM:
switch (phase)
{
case 0:
if (DEBUG)
{
usbSerial.println("Starting sat-low-power test (12 hours)");
}
ResetMojo();
phase = 1;
break;
case 1:
if (currentMillis - testStartTime < duration)
{
if (currentMillis >= nextAction)
{
CreateGenericNote();
sendHubSync(true);
phase = 2;
}
}
else
{
phase = 3;
}
break;
case 2:
if (pollHubSync())
{
nextAction += interval; // Schedule next 60-min interval
phase = 1;
}
break;
case 3:
SendMojoReading("sat-low-power.qo");
if (DEBUG)
{
usbSerial.println("sat-low-power test complete");
}
currentTest = DONE;
phase = 0;
break;
}
break;
// All tests complete.
case DONE:
switch (phase)
{
case 0: // The tests are complete!
if (DEBUG)
{
usbSerial.println("ALL TESTS COMPLETE!!!");
}
phase = 1;
break;
case 1:
// Remain here indefinitely.
break;
}
break;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment