Hi everyone. I have a Boron transmitting data for a weather station at a remote paragliding site. The Boron operated continuously from May 25th 2024 to August 9th 2024, when it went offline. I went to the site and inspected it - it was still on and running. I swapped the Boron for a new one because I assumed that something was wrong with it. The new Boron transmitted data for 3.5 hours before going offline as well.
I am unsure of how to troubleshoot this issue. I don't think my Boron is banned from the network because it is only transmitting data every 8 minutes. What I want to get feedback on first is whether there's a problem in the code I wrote for handling reconnections. Here it is:
#include "Particle.h"
#include <algorithm>
SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);
const int ULP_SLEEP_TIME_MS = 8 * 60 * 1000;
void ulpSleep(int durationInMs) {
systemSleepConfig.mode(SystemSleepMode::ULTRA_LOW_POWER)
.duration(durationInMs)
.network(NETWORK_INTERFACE_CELLULAR, SystemSleepNetworkFlag::INACTIVE_STANDBY);
System.sleep(systemSleepConfig);
}
void sleepIfBatteryChargeIsLow() {
while (true) {
batteryStateOfCharge = System.batteryCharge();
Log.info("Battery: %2f", batteryStateOfCharge);
if (batteryStateOfCharge > 10 || batteryStateOfCharge < 0) {
// We have enough battery to operate.
// System.batteryCharge() returns -1 when battery state cannot be determined.
return;
} else {
Log.info("Battery low. Going to sleep.");
ulpSleep(ULP_SLEEP_TIME_MS);
}
}
}
void setup()
{
sleepIfBatteryChargeIsLow();
// ... Setup things for sampling the weather sensors.
// Connect to the Particle cloud so the Boron's clock is accurate.
if (Particle.connected() == false) {
Log.info("Connecting to Particle Cloud.");
Particle.connect();
if (waitFor(Particle.connected, 100000)) {
Log.info("Connected to Particle Cloud.");
} else {
Log.info("Failed to connect to the Particle Cloud.");
}
}
}
void loop()
{
sleepIfBatteryChargeIsLow();
// Sample weather sensors for 8 minutes.
// Then we transmit the data:
if (Particle.connected() == false) {
Log.info("Connecting to Particle Cloud.");
Particle.connect();
if (waitFor(Particle.connected, 100000)) {
Log.info("Connected to Particle Cloud.");
} else {
Log.info("Failed to connect to the Particle Cloud.");
// We don't try to send the data since the connect failed.
return;
}
}
// kd = Kaaikop Data
bool success = Particle.publish("kd", jsonArrayOfWeatherReadings, PRIVATE, WITH_ACK);
Log.info("Publish result: %d", success);
}
The code basically does the following:
During setup, connects to the particle network. It's ok if we fail to connect.
During the loop, we sample the weather sensors for 8 minutes, then we try to transmit the data. It's ok if we fail to transmit the data, we just move on to the next 8 minute block of sampling and try again.
Am I making a mistake that could cause my Boron to end up stuck and not try to reconnect?
Since you are using SYSTEM_MODE(AUTOMATIC), the connection will be managed automatically and you don't need to do anything to connect. Even if SEMI_AUTOMATIC, once you start the connection process once, you don't need to do anything else.
The check for Particle.connected() should be before the Particle.publish so it will only attempt to publish when connected, and at the rate you desire.
Your wait for connection is 100 seconds, and that's insufficient. We recommend 11 minutes, because if connection failure occurs for more than 10 minutes, the cellular modem hardware will be reset by completely powering it down, which can sometimes allow a reconnection to occur. You aren't disconnecting from cellular on connection failure, so the modem reset will eventually occur with your code, but it's something to be aware of if you do.
You're using ULP with cellular active, which still uses a significant amount of power. However your sampling interval of 8 minutes is higher than recommended for reconnecting with cellular off. The recommendation in your case is to use SEMI_AUTOMATIC mode and disconnect from cellular when the battery is low. Keep track of the last time you woke and reconnect only when there is sufficient battery power and a minimum of 10 minutes has elapsed. If the battery is high, could continue to use cellular on while sleeping.
Thanks for the reply, rickkas7. Just a minor correction, I am using SYSTEM_MODE(SEMI_AUTOMATIC), not SYSTEM_MODE(AUTOMATIC) (see line 4 of the code snippet I attached). I chose SEMI_AUTOMATIC to implement the idea suggested here:
One common use case for this is checking the battery charge before connecting to cellular on the Boron, B-Series SoM, Electron, and E-Series.
In setup I initiate a sleep if the battery charge is low before attempting to connect.
Thanks for pointing out the missing Particle.connected() check that should be wrapping my publish. I will tweak the code as follows:
What I want to understand better is the behaviour of Particle.publish() when not connected, since that's something my code currently allows. Can calling publish while not connected make the device end up stuck in the publish and fail to reconnect?
Regarding the 100 second wait period - as you mention, the modem reset should eventually be happening. For my use-case, I don't want to wait 11 minutes, I'd rather have the device move on to sample the next window of data and simply discard the window that it could not send.
Regarding ULP sleep with cellular active - I have a large battery and solar panel powering the device, so power consumption is not a concern. Before my device went offline, it was reporting that the battery charge was at 100%.
In SEMI_AUTOMATIC you should do the battery check in setup() then call Particle.connect() once, then it will automatically reconnect if necessary. This is how Tracker Edge and Monitor Edge firmware works.
If you are still in the process of connecting when you call Particle.publish, the publish call will block for up to 10 minutes until the modem is reset, or until cloud connected.
Hey Rickkas7. I updated my code with the improvements you mentioned, and I also added a mechanism so after 3 failed publishes (which takes around 30 minutes with my current sample - publish loop) the Boron reboots.
I installed a Boron with that new firmware on the mountain location 2 days ago. What I've observed is an improvement over the previous firmware - the Boron reconnects to the network sporadically, whereas previously it would stay offline and never reconnect. However, out of 36 hours the Boron has only managed to be online for about 4 hours.
I wanted to share the connection diagnostics for the Boron from the vitals dashboard so that maybe you or someone else can see if they spot anything problematic. I can share the ID of the Boron as well if that would help. Thanks for your time and help.
I eventually found a solution to the problem. I bought an external antenna from Amazon and a u.fl adapter. I installed it at the mountain and the connection problems are now gone - I'm getting my steady stream of data as before. In case it helps others, this is what I bought: https://www.amazon.ca/dp/B07P7MVWL5?ref=ppx_yo2ov_dt_b_fed_asin_title
I suspect that having the antenna that comes with the Boron inside of a weather proof plastic enclosure tightly packed with a battery, a Boron and a charging controller affected the signal quality, but I am no antenna expert so that is just a guess. Since installing the external antenna the signal quality reported by my Boron went from 14% to fluctuating between 20% and 60% (some times dipping below 20%).
Thank you! I have a boron at my camp as a power outage monitor and the signal is weak. I tried adding an antenna a couple years ago, but must not hav3 bought the correct one as the signal strength didn’t improve much.
I installed another boron at my neighbor’s house and it is monitoring his sump pump. It sends hourly updates and I noticed his misses updates much more frequently than mine. I will try your antenna combo and see it that improves his reliability.