Strange behavior with JSN-SR04T sensor

I’m not sure if my issue is firmware-related or hardware-related, so I may be posting this in the wrong place.

I have an app (running on a Photon) that monitors the level of water in a storage tank using a JSN-SR04T ultrasonic sensor and Tim Eckel’s New_Ping library. For some reason that I cannot figure out, my code - below - results in water level measurements that alternate back and forth between two values even when the water level hasn’t changed. I’ve attached a screenshot of the event log that shows a sample of the alternating values. Note that the built-in functions for querying the height and gallons between scheduled events often return different values than the scheduled events do. Note, also, that the “-1” value for gallons associated with the higher height value is a result of exceeding the dimension of the associated array; that’s not my current concern.

Does anyone with JSN-SR04T experience have any thoughts about why this is happening and how to fix it, i.e. how to generate consistent values?

// This #include statement was automatically added by the Particle IDE.
#include <New_Ping.h>

// Define variables:
int gallons;
int distance; // Distance from sensor to top of water
int height; // Height of water in the tank
int gap = 7; // Distance between sensor and maximum water level
int Alert = 1;

#define trigPin 2 // 10 on breadboard
#define echoPin 3 // 9 on breadboard
#define MAX_DISTANCE 400
NewPing sonar = NewPing(trigPin, echoPin, MAX_DISTANCE);

void setup() {

    Particle.function("Gallons", getGallons);
    Particle.function("Inches", getInches);
   }

int depth [67] = {919,905,891,877,863,849,836,822,808,794,780,766,752,738,724,710,696,682,668,654,641,627,613,599,585,571,557,543,529,515,501,487,473,460,446,432,418,404,390,376,362,348,334,320,306,292,279,265,251,237,223,209,195,181,167,153,139,125,111,97,84,70,56,42,28,14,0};

// Function to retrieve gallons
int getGallons(String command) { 
    distance = sonar.ping_in(); // Inches to top of water
    distance = distance - gap; // Adjust for air gap between top of water and sensor
    gallons = depth [distance];
    return gallons;
} // End getGallons Function

// Function to retrieve height
int getInches(String command) { 
    distance = sonar.ping_in(); // Inches to top of water
    distance = distance - gap; // Adjust for air gap between top of water and sensor
    height = 67 - distance; // Inches above bottom of tank
    return height;
} // End getInches Function

void loop() {

distance = sonar.ping_in();// Inches to top of water
distance = distance - gap;
height = 67 - distance ; // // Inches above bottom of tank
gallons = depth [distance];

Particle.publish ("Gallons",String(gallons),60, PRIVATE);
Particle.publish ("Height",String(height),60, PRIVATE);

if (gallons < 500){
  Particle.publish ("Alert", String(Alert), PRIVATE); 
}

delay(60000); // Take a measurement every minute

}

Brian, Do you have Version 2 of the JSN by chance?
They are usually labeled on the Screen Print, or you can look for R27 Pad on the board :

If you have version 2, You can operate the JSN in Serial Mode without needing a Library.
I can share some sample Code for V2 that works well.

Also, where in the tank do you have the sensor mounted?
Is it close to the center of the tank’s footprint ?
If not, you might be getting reflections from the sidewalls bouncing around causing bogus readings.

1 Like

Hey Ryan, good to hear from you again! I haven’t checked the version number yet (it’s installed at another site), but luckily Tim Eckel responded to a query I sent to him directly and he had a fix. He says that ultrasonic sensors are notorious for occasional errors so he suggested that I replace the “distance = sonar.ping_in();” command which results in only a single ping - every minute in my case - with “distance = sonar.convert_in(sonar.ping_median(9));”. As a result, rather than send a single ping, the SR04T sends - in this case - 9 pings in rapid sequence and then average them. Brilliant! Problem solved. Working perfectly now. But to your comment about sensor placement: it is placed dead center at the top of a 64" diameter 77" tall 1000 gallon cylindrical tank, so no reflections.

And yes, please share your code for V2. If I do have a V2 SR04T device, then that might address an issue that I have posted elsewhere here on the forum: the much bigger water tank that you’ve helped me with before needs to have a water level gauge added. But since 3G cellular is going bye bye in February, I need to install my depth gauge app on a Boron rather than an Electron as originally planned. But Tim’s library won’t install on any Gen 3 device like a Boron. Might your code work?

Thanks for jumping in on a holiday!

I don’t use this exact Sonar Sensor however, something I personally do is reject any signal that is outside of expected range. For example, in your scenario, anything > 77" or maybe less than 10" can be considered garbage readings so don’t include them in your average. In other words take consecutive readings but only add an individual reading to the running sum of all readings IF the reading is within the expected range given the tank size. If not, reject that reading. Then once done sampling, take the rolling sum divided by the total number of good readings to come up with your average. Sounds very similar but a good way to not be influenced by garbage one off readings.

Furthermore, I then also have a feature to take a filter the data when viewing it. For example, if a sensor takes readings every 5 minutes but the tank can only fill/empty a few percentage between readings, then you can also do some filtering/noise rejection when plotting the data over a day or more to provide a very good representation of how full the tank is.

Hope those ideas help as well.

All very good suggestions Jeff! I especially like the filtering obvious garbage readings. The “sonar.convert_in(sonar.ping_median(9))” function seems to be working very well right now, so I’m wondering if maybe it is performing a similar task.

Follow-up to Ryan: my SR04T is version 3.

@blshaw45 , I didn’t know there was a Version 3. Thanks!

Here’s the stripped down sample code for normal operation without needing a Library:

Here’s some sample code for MODE 2, which uses Serial Communication and lets the JSN perform the timing calculations itself.

Both are using the Boron’s Manual Mode, which you may want to change for your application.
The second link also has ThingSpeak integrated and PMIC calls for a Solar Powered Setup.
You’re probably mainly interested in void readDistance() in the second link when using the Serial Output from the JSN. I highly recommend using the JSN in either Mode 2 or 3 (Serial).

Thanks again Ryan! I’m about to start some serious drinking to celebrate the continuation of the pandemic into 2022, but once I recover - if I do - I’ll look at all this in detail. I’m sure I’ll have some questions.

OK, haven’t dived into the beer yet. While there is a lot about your code that I need to better understand, I must confess that the idea of using solar power and a LiPO battery never occurred to me, but it’s brilliant! My plan had been to use a USB wall charger and a long USB cable to power the Boron (there is an AC convenience outlet under a fake rock about 10 feet away from the tank). The electronics will be in a small plastic project box attached to the top of the water tank’s lid, but since the lid has to be unscrewed whenever any kind of maintenance might be required, service personnel would have to disconnect the cable. Having everything, including a tiny 6 volt solar panel, mounted to the lid makes far more sense. But I have no idea - yet - how to wire that all together. I’m guessing the LiPo battery connects directly to the Boron and the solar panel connects - somehow/somewhere - to the feather board, right (otherwise why would I need the latter)? Any info you can share in that regard?

Yes, the Boron has a battery connector for the included Li-Po. The Boron can use a 6V Solar Panel (actually higher) as the recharge source when connected to the micro USB or Vin Pin.
The feather board isn’t required, it’s just an easy way to switch power On/Off to the JSN (in a low-powered/battery application)

Thanks!! There are a bunch of 6 volt solar panels on Amazon, some as cheap as $8. Have you had any experience with any that you’d recommend Ryan?

I’ve tested dozens of panels for the Boron. For 6V, I like Voltaic Systems.

But if you have room, I’d recommend a 12V system if you’re wanting to go Solar.

Something like these-

$30 12V Panel and Controller:

$20 12V SLA battery:

But you’re lucky enough to have mains power 10’ away… so Solar might not be worth the worry/trouble.
You can comment out the PMIC calls in Setup() to give the second link/code a spin, and of course provide the solder bridge on your JSN board for Mode 2, per the datasheet.