Minimum Viable Code to Test New Sensor

I’m hoping to get some guidance from the community to develop the minimum viable code base to achieve my goal. I’m very inexperienced and untrained with writing code, but I do dabble on the hardware side. I have developed a new plant-based sensor to monitor water stress and I’m eager to test it in the field to see if it produces meaningful data. I’d like to let it run for about a week. That should be enough time to determine if it works or not. I’ve designed a carrier board for the Boron LTE that works off standard alkaline batteries. For this test, I’m not worried about power optimization. Two AA batteries will power the carrier without issue for a week.
The carrier has hardware designed to power on the Boron every 20 minutes, so we don’t need code to wake up the carrier.
Two sensors will be connected to the carrier. An ADS1115 is used to measure the voltage. The sensors need to be sampled as follows:

  • Read ADS1115 every second with time stamp for 5 seconds (5 total readings per sensor: voltage and timestamp). The readings will establish the pre-excitation baseline output.
  • Turn on sensor excitation for 6 seconds. Do not sample during this period.
  • Then for the next 120 seconds, read ADS1115 every second and add timestamp. So, 120 readings (voltage and timestamp) for each sensor.
    I’ve built a very basic test program to sample the sensors as described and upload the data to Ubidots via a Particle webhook every second. System mode is Automatic and Thread is not enabled. That mostly works, but what I’m seeing is that about 10-20% of the data is missing on Ubidots. It is critical that data is not lost, so how would the community suggest I go about modifying my code to prevent this? If someone could point me to some examples to look at, I’m happy to do the work myself.

Here is my simple, untrained code. It does include Battery voltage readings. I may or may not include them.

#include "Adafruit_ADS1X15.h"
#include "Ubidots.h"


Adafruit_ADS1115 ads;
const char* WEBHOOK_NAME = "Ubidots_Test";
Ubidots ubidots("webhook", UBI_PARTICLE);

void setup() {
  Serial.begin(9600);
  delay(5000);
  //Time.zone(-7);
  ads.setGain(GAIN_TWO);
  ads.begin();

}

void loop() {
  double multiplier = 0.0625F;
  unsigned long timestamp_seconds = Time.local(); 
  short adc1, adc2, adc3;
  double av1, av2, av3; 
  adc1 = ads.readADC_SingleEnded(0);
  av1 = adc1 * multiplier * 2;
  adc2 = ads.readADC_SingleEnded(1);
  av2 = adc2 * multiplier;
  adc3 = ads.readADC_SingleEnded(2);
  av3 = adc3 * multiplier;
  ubidots.add("battery", av1, NULL, timestamp_seconds);
  ubidots.add("sensor1", av2, NULL, timestamp_seconds);
  ubidots.add("sensor2", av3, NULL, timestamp_seconds);
  bool bufferSent = false;
  bufferSent = ubidots.send(WEBHOOK_NAME, PRIVATE);
  if(bufferSent){
    Serial.println("Values sent by the device");
  }
  delay(1000);
1 Like

You have many options, and a huge range of complexity.

Since you mentioned it’s critical to prevent data loss for a test run, I’d want to decouple the data upload from the measurement cycle, especially since you’re dealing with such a small amount of data points per run.

That could be as simple as storing the data/timestamps during the 2 minutes, then transition into sending the data to ubidots after the Boron confirms a successful test where all data was captured. That removes the possibility of “rate limiting”, etc, from the initial bench testing.

For the field, you might want to use an Asynchronous Library w/ System Threading, etc, to minimize runtime & thus power usage.

@wineisgud4u ,

Like @Rftop said, there are many ways to do this.

One suggestion I wanted to make on preventing data loss. Publishing data does not guarantee it will be received and stored by Ubidots. I also use Ubidots as my back-end here is the outline of how I prevent lost data when sending to Ubidots:

  1. I use a webhook integration to send data to Ubidots - not the library. That said, this same approach may work with the library as well.

  2. I subscribe to the webhook and assign a response handler function.

  3. In the webhook, I use a response template to strip out all the response from Ubidots except the HTTP code. If a data point has been received and stored at Ubidots, you will get a “201” response code.

  4. In the response handler, I check the response code. If it is “201”, the “in flight data” is cleared and the device goes back to sleep. If there is a different response, the “in flight data” will be resent on the next connect window.

By making this process “closed loop”, I can be fairly confident that data is not lost in transmission. If this process sounds like something you would like to try, I can give you some code samples to get you started.

Thanks,

Chip

2 Likes

Hi Mr Wine :slight_smile:
I would give this library a shot to see if the data loss you are observing is due to the publishes getting lost since the device was not connected:

It could be as simple as adding something like this before setup():

retained uint8_t publishQueueRetainedBuffer[2048];
PublishQueueAsync publishQueue(publishQueueRetainedBuffer, sizeof(publishQueueRetainedBuffer));

then in setup():

publishQueue.setup();

then replace your Particle.publish:

publishQueue.publish(event, message, 60, PRIVATE, WITH_ACK);

NOTE (from the library):
You must use system threading mode with PublishQueueAsyncRK. The library will no longer initialize if you do not have threading enabled.

SYSTEM_THREAD(ENABLED);

This can improve the situation, but I believe for best results you would want what @chipmc described too.

Cheers,
Gustavo.
PS: this is already hinted at by @Rftop above.

@gusgonnet , I am using the process you outlined now with good effect as my current use case allows for occasional data loss.

However, I wanted to point out that the WITH_ACK flag is not a guarantee that the data is delivered to Ubidots. It is only a way to delay the return of execution from the Particle.publish (I would assume same for publisQueuePosixRK) until an acknowledgment is received from the Particle cloud.

The only way to ensure the data is received and stored by Ubidots is to validate a “201” code response from Ubidots for the variable in question. This brings more complexity so, the approach you outlined should probably be the 1st approach. The “closed loop” approach could be used if data loss is observed.

Chip

1 Like

yup, that's true, Chip. It improves the reliability on the leg Boron->Particle Cloud, but does nothing for Particle Cloud->Ubidots. It still shoots a message in "open loop".
It might be an improvement worth trying, but for ultimately getting no data loss, one will need what you described: the closed loop.
Thanks!

Thank you for the help! I tweaked the code more today and its running pretty well without having to use a closed loop. I’m still seeing data loss, but < 2% is negligible. I’ll install it tomorrow in a neighboring Chardonnay vineyard to see how it works.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.