Last active
May 12, 2025 17:43
-
-
Save rdlauer/763b49a52272f693642de380cb47b433 to your computer and use it in GitHub Desktop.
Testing Notecard Power Consumption with Mojo (Starnote Edition)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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