From c3a744e2084f865f4005ac5a91600eb489cd99b8 Mon Sep 17 00:00:00 2001 From: Reid Pettibone <154724842+ReidP-source@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:50:03 -0700 Subject: [PATCH 1/3] Proxy changes. refs #269 --- examples/Lab Examples/Dendrometer/hub/hub.ino | 53 ++++++++------- .../Lab Examples/Dendrometer/node/node.ino | 67 ++++++------------- src/Radio/Loom_LoRa/Loom_LoRa.cpp | 15 +++-- 3 files changed, 59 insertions(+), 76 deletions(-) diff --git a/examples/Lab Examples/Dendrometer/hub/hub.ino b/examples/Lab Examples/Dendrometer/hub/hub.ino index 41573b16..f80d4f3a 100644 --- a/examples/Lab Examples/Dendrometer/hub/hub.ino +++ b/examples/Lab Examples/Dendrometer/hub/hub.ino @@ -1,6 +1,6 @@ #include "arduino_secrets.h" -#include +#include //4.7 #include #include @@ -11,13 +11,14 @@ const unsigned long REPORT_INTERVAL = 1 * 60 * 60 * 1000; Manager manager("Hub", 0); - Loom_Hypnos hypnos(manager, HYPNOS_VERSION::V3_3, TIME_ZONE::PST); Loom_Analog batteryVoltage(manager); Loom_LoRa lora(manager); Loom_LTE lte(manager, "hologram", "", "", A5); -Loom_MongoDB mqtt(manager, lte.getClient(), SECRET_BROKER, SECRET_PORT, DATABASE, BROKER_USER, BROKER_PASS); +Loom_MongoDB mqtt(manager, lte, SECRET_BROKER, SECRET_PORT, DATABASE, BROKER_USER, BROKER_PASS); +Loom_BatchSD batchSD(hypnos, 16); //set batch size uploading +int packetNumber = 0; void setup() { // Start the serial interface @@ -28,6 +29,10 @@ void setup() setRTC(); + // Sets the LTE board to use batch SD to only start when we actually need to publish data + lte.setBatchSD(batchSD); + + // load MQTT credentials from the SD card, if they exist mqtt.loadConfigFromJSON(hypnos.readFile("mqtt_creds.json")); @@ -38,28 +43,30 @@ void setup() void loop() { // Wait 5 seconds for a message - if (lora.receive(5000)) - { - manager.display_data(); - hypnos.logToSD(); - mqtt.publish(); - } - - static unsigned long timer = millis(); - if (millis() - timer > REPORT_INTERVAL) - { - manager.set_device_name("Hub"); - manager.set_instance_num(0); - - manager.measure(); - manager.package(); - manager.display_data(); - mqtt.publish(); - - timer = millis(); - } + do{ + if (lora.receiveBatch(5000, &packetNumber)) + { + manager.display_data(); + hypnos.logToSD(); + mqtt.publish(batchSD); + } + }while(packetNumber > 0); + static unsigned long timer = millis(); + if (millis() - timer > REPORT_INTERVAL) + { + manager.set_device_name("Hub"); + manager.set_instance_num(0); + + manager.measure(); + manager.package(); + manager.display_data(); + mqtt.publish(); + + timer = millis(); + } } + void setRTC() { if (!Serial) diff --git a/examples/Lab Examples/Dendrometer/node/node.ino b/examples/Lab Examples/Dendrometer/node/node.ino index 76c11b04..fa2d8ed0 100644 --- a/examples/Lab Examples/Dendrometer/node/node.ino +++ b/examples/Lab Examples/Dendrometer/node/node.ino @@ -1,12 +1,11 @@ -#include //4.3 +#include //4.6 #include #include #include #include #include #include -#include -#include + #include "AS5311.h" @@ -14,16 +13,14 @@ /* DEVICE CONFIGURATION */ ////////////////////////// static const uint8_t NODE_NUMBER = 1; -static const char * DEVICE_NAME = "BlueberryDendrometer_"; +static const char * DEVICE_NAME = "HazelnutDendrometer_"; ////Select one wireless communication option #define DENDROMETER_LORA -// #define DENDROMETER_WIFI -////These two time values are added together to determine the measurement interval -static const int8_t MEASUREMENT_INTERVAL_MINUTES = 15; -static const int8_t MEASUREMENT_INTERVAL_SECONDS = 0; -static const uint8_t TRANSMIT_INTERVAL = 16; // to save power, only transmit a packet every X measurements + +TimeSpan sleepInterval; +static const uint8_t TRANSMIT_INTERVAL = 16; // to save power, only transmit every X measurements ////Use teros 10? -#define DENDROMETER_TEROS10 +//#define DENDROMETER_TEROS10 ////////////////////////// ////////////////////////// @@ -42,20 +39,14 @@ Loom_Analog analog(manager); Loom_Teros10 teros(manager, A0); #endif Loom_SHT31 sht(manager); -Loom_Neopixel statusLight(manager, false, false, true, NEO_GRB); // using channel 2 (physical pin A2). use RGB for through-hole devices +Loom_Neopixel statusLight(manager, false, false, true, NEO_GRB); // using channel 2 (physical pin A2). use RGB for through-hole LED devices. GRB otherwise. // magnet sensor AS5311 magnetSensor(AS5311_CS, AS5311_CLK, AS5311_DO); // wireless -#if defined DENDROMETER_LORA && defined DENDROMETER_WIFI -#error Choose ONE wireless communication protocol. -#elif defined DENDROMETER_LORA +#if defined DENDROMETER_LORA Loom_LoRa lora(manager, NODE_NUMBER); -#elif defined DENDROMETER_WIFI -#include "credentials/arduino_secrets.h" -Loom_WIFI wifi(manager, CommunicationMode::CLIENT, SECRET_SSID, SECRET_PASS); -Loom_MongoDB mqtt(manager, wifi.getClient(), SECRET_BROKER, SECRET_PORT, MQTT_DATABASE, BROKER_USER, BROKER_PASS); Loom_BatchSD batchSD(hypnos, TRANSMIT_INTERVAL); #else #warning Wireless communication disabled! @@ -84,23 +75,21 @@ void flashColor(uint8_t r, uint8_t g, uint8_t b); */ void setup() { + pinMode(BUTTON_PIN, INPUT_PULLUP); // Enable pullup on button pin. this is necessary for the interrupt (and the button check on the next line) delay(10); bool userInput = !digitalRead(BUTTON_PIN); // wait for serial connection ONLY button is pressed (low reading) manager.beginSerial(userInput); // wait for serial connection ONLY button is pressed + + hypnos.setLogName("HazelnutDendrometer_1data"); //SD card CSV file name + hypnos.enable(); + sleepInterval = hypnos.getConfigFromSD("HypnosConfig.json"); - hypnos.setLogName("data"); - hypnos.enable(); -#if defined DENDROMETER_WIFI - wifi.setBatchSD(batchSD); - wifi.setMaxRetries(2); - mqtt.setMaxRetries(1); - wifi.loadConfigFromJSON(hypnos.readFile("wifi_creds.json")); - mqtt.loadConfigFromJSON(hypnos.readFile("mqtt_creds.json")); +#if defined DENDROMETER_LORA + lora.setBatchSD(batchSD); #endif manager.initialize(); - setRTC(userInput); checkMagnetSensor(); @@ -165,30 +154,12 @@ void measureVPD() } /** - * transmit the current data packet over LoRa - * loop counter starts high so an initial transmission can be triggered by pressing the button - * (the first transmission will happen the second time this function is called) + * transmit the batch data packet over LoRa */ void transmit() { #if defined DENDROMETER_LORA - static uint8_t loopCounter = TRANSMIT_INTERVAL - 2; - loopCounter++; - if (loopCounter >= TRANSMIT_INTERVAL) - { - lora.send(0); - loopCounter = 0; - } -#elif defined DENDROMETER_WIFI - if (!batchSD.shouldPublish()) - { - char output[100]; - snprintf_P(output, OUTPUT_SIZE, PSTR(" Not ready to publish. Currently at packet %i of %i"), - batchSD.getCurrentBatch(), batchSD.getBatchSize()); - Serial.println(output); - } - if (wifi.isConnected()) - mqtt.publish(batchSD); + lora.sendBatch(0); #endif } @@ -197,7 +168,7 @@ void transmit() */ void sleepCycle() { - hypnos.setInterruptDuration(TimeSpan(0, 0, MEASUREMENT_INTERVAL_MINUTES, MEASUREMENT_INTERVAL_SECONDS)); + hypnos.setInterruptDuration(sleepInterval); // Reattach to the interrupt after we have set the alarm so we can have repeat triggers hypnos.reattachRTCInterrupt(); attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), ISR_BUTTON, FALLING); diff --git a/src/Radio/Loom_LoRa/Loom_LoRa.cpp b/src/Radio/Loom_LoRa/Loom_LoRa.cpp index 1695a8dc..e88a68d9 100644 --- a/src/Radio/Loom_LoRa/Loom_LoRa.cpp +++ b/src/Radio/Loom_LoRa/Loom_LoRa.cpp @@ -195,11 +195,16 @@ FragReceiveStatus Loom_LoRa::receiveFrag(uint timeout, bool shouldProxy, uint8_t if (isReady) { if (shouldProxy) { - const char *name = manager->getDocument()["id"]["name"]; - manager->set_device_name(name); - - int instNum = manager->getDocument()["id"]["instance"]; - manager->set_instance_num(instNum); + // If tempdoc has an ID (not a fragment body) we can set a proxy + JsonObjectConst proxyDoc = tempdoc["id"].as(); + if (proxyDoc.isNull()) { + proxyDoc = manager->getDocument()["id"].as(); + } + if (!proxyDoc.isNull()) { + proxyDoc->set_device_name(proxyDoc["name"].as()); + proxyDoc->set_instance_num(proxyDoc["instance"].as()); + + } } if (expectedOutstandingPackets > 0) { From c4e3a1acb6331735a9a87e98b28821d29326df70 Mon Sep 17 00:00:00 2001 From: Reid Pettibone <154724842+ReidP-source@users.noreply.github.com> Date: Thu, 19 Mar 2026 17:55:35 -0700 Subject: [PATCH 2/3] Spelling fixes... --- src/Radio/Loom_LoRa/Loom_LoRa.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Radio/Loom_LoRa/Loom_LoRa.cpp b/src/Radio/Loom_LoRa/Loom_LoRa.cpp index e88a68d9..e83ea305 100644 --- a/src/Radio/Loom_LoRa/Loom_LoRa.cpp +++ b/src/Radio/Loom_LoRa/Loom_LoRa.cpp @@ -196,14 +196,13 @@ FragReceiveStatus Loom_LoRa::receiveFrag(uint timeout, bool shouldProxy, uint8_t if (isReady) { if (shouldProxy) { // If tempdoc has an ID (not a fragment body) we can set a proxy - JsonObjectConst proxyDoc = tempdoc["id"].as(); + JsonObjectConst proxyDoc = tempDoc["id"].as(); if (proxyDoc.isNull()) { proxyDoc = manager->getDocument()["id"].as(); } if (!proxyDoc.isNull()) { - proxyDoc->set_device_name(proxyDoc["name"].as()); - proxyDoc->set_instance_num(proxyDoc["instance"].as()); - + manager->set_device_name(proxyDoc["name"].as()); + manager->set_instance_num(proxyDoc["instance"].as()); } } From 278e7f7653070a94f05aeadb15f98ebf0b8d7187 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 23 Apr 2026 19:34:17 +0000 Subject: [PATCH 3/3] Auto-format with clang-format --- src/Radio/Loom_LoRa/Loom_LoRa.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Radio/Loom_LoRa/Loom_LoRa.cpp b/src/Radio/Loom_LoRa/Loom_LoRa.cpp index e83ea305..4375a2f1 100644 --- a/src/Radio/Loom_LoRa/Loom_LoRa.cpp +++ b/src/Radio/Loom_LoRa/Loom_LoRa.cpp @@ -195,14 +195,14 @@ FragReceiveStatus Loom_LoRa::receiveFrag(uint timeout, bool shouldProxy, uint8_t if (isReady) { if (shouldProxy) { - // If tempdoc has an ID (not a fragment body) we can set a proxy + // If tempdoc has an ID (not a fragment body) we can set a proxy JsonObjectConst proxyDoc = tempDoc["id"].as(); if (proxyDoc.isNull()) { - proxyDoc = manager->getDocument()["id"].as(); + proxyDoc = manager->getDocument()["id"].as(); } if (!proxyDoc.isNull()) { - manager->set_device_name(proxyDoc["name"].as()); - manager->set_instance_num(proxyDoc["instance"].as()); + manager->set_device_name(proxyDoc["name"].as()); + manager->set_instance_num(proxyDoc["instance"].as()); } }