Ultrasonic water level sensor

Disclaimer: I know nothing about electronics but can be taught!

I have a couple of lengthy threads here on this forum but am starting a new one due to a rather significant change in my application.

In late 2018 I built a system to control a water pump in a well that is 1000 feet from its associated storage tank. It uses Particle Electrons attached to NCD MCP23008 relay boards. The firmware running on the Electrons simply turns the pump on or off as a function of the tank’s water level, signaled by a float switch. All works great.

Now I want to embellish the system with the ability to remotely monitor the water level between its high and low levels. Based on the recommendation of others here, I started looking at an AMS5812 pressure transducer. This device would connect to the MCP23008 via the I2C interface and connect to the water tank via a silicone tube. Unfortunately, several issues have got in my way, not the least of which are 1) the requirement to keep the sensing tube under water at the bottom of the tank, 2) the need to keep the tube from freezing and 2) the risk of contaminating the potable water if there should ever be damage to or a leak in the tube. Just too mechanical for me to feel comfortable.

Consequently, I am thinking that it might be better to switch to an ultrasonic sensor. They are cheap ($5+/-), apparently reliable and accurate, connect to the electronics via wire, do not reside underwater at the bottom of the tank but above the high water mark, etc., etc.

There are lots of tutorials online on how to build an ultrasonic water level gauge using Arduino, but so far I have not yet found any info on how to achieve the same objective with an Electron.

My desire is to interface the sensor to my existing Electron using either a direct connection to my MCP23008 if possible or, I suspect more likely, some kind of PCB/shield that is powered off the MCP’s I2C interface. Appropriate firmware would be added to the existing code running on the Electron.

If there is no way to interface to my existing Electron, I can acquire an Arduino and build an entirely independent system, but that would add additional cellular charges which I’d prefer to avoid.

Is there anyone out there that can offer some guidance or point me to any online resources that I have not yet found?

Many thanks in advance!

Hi,

We actually sell a tank level sensor that has a range of 5 meters here:


As long as your tank is less than 5 meters tall this would be exactly what you are describing. However I fully understand this is a much more expensive solution than you were probably looking for. This device is more for our industrial user base. It sounds like you might fit more in our Doc Brown user base :smirk:
This however is nothing more than an ultra sonic sensor device like you’re describing, just more refined than a standard Arduino based device. My recommendation would be to source an I2C ultrasonic distance sensor(just google that and you’ll find them). This can be connected to the I2C port on the MCP23008 board, however be careful to ensure that the I2C ultrasonic sensor is 5VDC tolerant on the SCL and SDA lines used for the I2C data communication. You should be able to source a 3rd party Arduino library for the sensor online, if you can find a Particle library that would be even better but honestly porting an Arduino library to Particle is cake.

Hopefully this helps.

Yes, I did see that Travis and, indeed, it’s way beyond my price range. And there is no significant value to me in the wireless capability. Furthermore, the while the water tank holds 1600 gallons, it’s wide but not tall; the high water mark is only about 4 feet above the bottom.

I have located dozens of ultrasonic sensors and a decent subset of those are of the waterproof variety, but I haven’t yet found one that has a I2C interface. But then, I’ve never included ‘I2C’ in my search parameters. I’ll do that now…

Follow-up question Travis:

The waterproof sensors I’ve found so far (e.g. https://smile.amazon.com/dp/B082KYFYZS/?coliid=I1TOALXF4I26UY&colid=14JY8575AU243&psc=1&ref_=lv_ov_lig_dp_it), have a 6.5 foot cable. It is not mentioned whether that is I2C but I doubt it due to the length being considerably longer than the I2C protocol supports.

Since the distance from my water tank to the weatherproof enclosure that contains my NCD relay board and Electron is about 20 feet, the first thing I need to do is splice an extension into that cable. I asked if that could be done (via a question I submitted on Amazon) and was informed that yes, I can.

Once I can reach the enclosure, I need something to convert the signal to I2C so I can interface to the relay board. Does that make sense? Is the 4-pin connection on the sensor’s associated PCB look like I2C? If not, does NCD have anything like that?

That appears to be a bare bones sensor in the sense that it does not have any sort of IC that converts readings for you. The way it works is you send a signal to the trigger line, then begin monitoring the echo line, once you get a signal on the echo line you determine the amount of time it took from when you triggered the trigger line to when the echo line signaled. From that duration you can determine the distance. So you could use something like an Arduino board to do nothing but this task, and have it implement I2C as a slave device and connect it to the Electron. The Arduino can monitor the sensor, convert what it sees to a distance, and then transmit that information over the I2C connection to the Electron. It sound like a lot but it’s really not that bad. I’d recommend looking at the Arduino documentation for Wire which is the I2C protocol.

Sorry for the delayed response by the way. We’re running a skeleton crew here so everyone is working their butt off.

Thanks,
Travis

Thanks again Travis. I’ll check the Arduino option. But you say this sensor appears to be “bare bones” yet it comes with a PCB with a 4-pin interface that I thought might be I2C. I’ve posted a question about that on one of the many Amazon listings for the device.

From the limited information on the product page I can see the 4 pins are +5VDC, GND, Trigger, and Echo. This means the device is not I2C. I2C has SCL SDA GND and +V.

Yup. Kinda figured that. But while I’ve got you, is a Particle Photon interchangeable with an Electron on NCD’s relay shields? I’m looking for a way to create apps at home where I do not have access to the AT&T cellular network required for the Electron, i.e. develop on a Photon and, once ready, port the code to an Electron later.

Hi,

Yes, Photon modules are compatible on all of our IoT interface boards. So you can develop on a Photon and then transition to an Electron when you need to.

Outstanding! Be safe Travis.

Following up Travis. I ordered and have received a waterproof ultrasonic sensor (with associated PCB) and an Arduino Uno. I have also ordered a Photon, due here next Monday. My plan is to connect the sensor to the Arduino and, via I2C, the Arduino to my recently-purchased backup NCD relay shield.

I have found code for the Arduino that reads the sensor’s output and have tested it; it works! I have also found code for both the Arduino and the Photon to manage serial communications between them, but everything I’ve seen so far assumes that the two devices can be directly wired together. However, the Photon will be inserted into my NCD relay shield and I cannot directly connect to specific Photon pins; I have to communicate via I2C.

So my problem is to figure out what code I need to run on the Photon to retrieve that info from the Arduino via I2C. While I continue to look all over the Internet for same, do you have any insight to offer?

OK folks, I’m up against a roadblock yet again. I have all the piece parts in place, including the Photon installed on my NCD MCP23008. As previously mentioned, the Arduino serial monitor shows that the ultrasonic sensor is working fine!

I subsequently added I2C code per a tutorial I found on-line (https://circuitdigest.com/microcontroller-projects/arduino-i2c-tutorial-communication-between-two-arduino), but I can’t pass the sensor’s distance values from the Arduino to the Photon via I2C.

Here’s the current version of the code running on the Arduino:

#include <Wire.h>

// Define Trig and Echo pin:
#define trigPin 2
#define echoPin 3

// Define variables:
long duration;
int distance;
int(requestEvent);

void setup() {
// Define inputs and outputs
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);

// Begin Serial communication at a baudrate of 9600:
Serial.begin(9600);
Wire.begin(8);
Wire.onRequest(requestEvent);

}
void loop() {
// Clear the trigPin by setting it LOW:
digitalWrite(trigPin, LOW);

delayMicroseconds(5);
// Trigger the sensor by setting the trigPin high for 10 microseconds:
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

// Read the echoPin. pulseIn() returns the duration (length of the pulse) in microseconds:
duration = pulseIn(echoPin, HIGH);

// Calculate the distance in cm:
distance = duration*0.034/2;

// Convert to inches:
distance = distance*0.393701;

// Adjust for error:
distance = distance*1.037;

// Print the distance on the Serial Monitor (Ctrl+Shift+M):
Serial.print(“Distance = “);
Serial.print(distance);
Serial.println(” in”);

// The above works perfectly

void requestEvent(); //This Function is called when Master wants value from slave

{
Wire.write(distance);
}
// Introduce a 1-second delay:
delay(1000);
}

And here’s the code currently running on the Photon:

#include <Wire.h>

int (distance);
String data = String(10);

void setup() {
//Set the speed of the I2C port on the Photon and start it.
Serial.begin(9600);
Wire.begin();
}

void loop() {
Wire.requestFrom(8,1);
distance = Wire.read();
Particle.publish ("Distance = ",String(distance),60, PRIVATE);
delay(10000);
}

Note that at first the Arduino compiler generated the error message “‘requestEvent’ was not declared in this scope”. Although the tutorial code did not declare ‘requestEvent’, I added “int(requestEvent);” to my code and that satisfied the compiler. However, the ‘distance’ value does not show up in the Particle Console’s events.

I’m sure that my issue is something simple, but since I can’t figure it out on my own, I’m begging for some guidance.

![IMG_0780|375x500]
(upload://1a1MGpiVhTi6qCA6bUCz2EDdqzM.jpeg)


The first thing that jumps out at me is on the Arduino code in the requestEvent function. You are calling:
Wire.write(distance);
Distance is an integer. That function call handles either a String or a Byte from what I can see in the docs here:
https://www.arduino.cc/en/Reference/WireWrite
I would say your int is being cast to a byte and is probably chopping off the the rest. You’ll need to convert that int to a byte array and then call the write function passing it the byte array and the length of the array.

This may not be the only issue but I’d correct that before moving on any further.

Thanks for the suggestions Travis, but still no joy. I declared a new variable - ‘gallons’ - that I want to send to the Photon. It will not go!

I declared ‘gallons’ as a byte, as a long and as an integer. In all cases it compiled, but only zeros showed up in the Particle console.

I declared ‘gallons’ as a String and as a float, but neither would compile. The Wire.write function does not accept String or float variables (the lack of String support begs the question: how can you ever pass anything but a number via I2C?).

I tried sending an actual string (not to be confused with a String variable); Wire.write accepted it, i.e. it compiled, but again nothing showed up in the Particle console except zeros.

Of course, even if a variable byte type would work, a single byte wouldn’t allow me to pass numbers with two decimal places like I want to do. But for testing purposes, I’d be happy if I could pass just an integer, but I can’t.

I’ve included the latest iteration of the Arduino code in case anything else looks awry to you. The Photon code remains unchanged (of course, that’s probably where the problem lies).

#include <Wire.h>
#include <stdlib.h>

/* Arduino example sketch to control a JSN-SR04T ultrasonic distance sensor with Arduino. No library needed. More info: https://www.makerguides.com */
// Define Trig and Echo pin:
#define trigPin 2
#define echoPin 3

// Define variables:
long duration;
float distance;
byte gallons;
byte requestEvent;
String depth;

void setup() {
// Define inputs and outputs
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);

// Begin Serial communication at a baudrate of 9600:
Serial.begin(9600);
Wire.begin(8);
Wire.onRequest(requestEvent);

}

void loop() {

// Clear the trigPin by setting it LOW:
digitalWrite(trigPin, LOW);

delayMicroseconds(5);
// Trigger the sensor by setting the trigPin high for 10 microseconds:
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);

// Read the echoPin. pulseIn() returns the duration (length of the pulse) in microseconds:
duration = pulseIn(echoPin, HIGH);

// Calculate the distance in cm:
distance = duration * 0.034 / 2;

// Convert to inches:
distance = distance * 0.393701;

// Adjust for error:
distance = distance * 1.037;

// int i;
// char buff[10];
// String depth = “”;
// dtostrf(distance, 5, 2, buff); //5 is mininum width, 2 is precision
// depth += buff;

gallons = distance * 30;

// Print the distance on the Serial Monitor (Ctrl+Shift+M):
Serial.print(“Distance = “);
Serial.print(distance);
Serial.println(” in”);
Serial.print(“Depth = “);
Serial.print(gallons);
Serial.println(” gallons”);

void requestEvent(); //This Function is called when Master wants value from slave

{
Wire.beginTransmission(32);
Wire.write(gallons);
Wire.endTransmission();
}
// Introduce a 5-second delay:
delay(5000);
}

Hi,

I’d try getting this simple example to work:
https://www.arduino.cc/en/Tutorial/MasterReader

If you can get that to work then you should be able to adapt it to your application.

Let me know if that does not work.

I was just about to post the fact that I figured it out! I was looking for something very obscure and complicated. Nope. I just just placed the ‘request.Event()’ function inside the ‘loop()’ function on the arduino. I learned that you can’t nest functions in C++; duh! I moved it to after the loop’s closing } and BINGO! Everything works. When I’m all done tweaking things, I’ll document the application somewhere and let you and others know.

I really appreciate your ongoing support.

I feel pretty stupid.

Hopefully this will be my last question regarding this application, Travis: after I got everything working perfectly, I stumbled across a post on the Particle forum that shows how I could accomplish the same thing without an Arduino between the ultrasonic sensor and an Electron (https://community.particle.io/t/ultrasonic-sensor-jsn-sr04t-to-electron/33065/5). In one place it says that the sensor’s PCB should be directly connected as follows: TRIG to D6, ECHO to D7, 5v to VIN, Gnd to Gnd. In another place it suggests pins D2 and D3, which suggests that it doesn’t make any difference as long as the selected pins aren’t already in use for some other purpose. I did find an encouraging comment that you posted on this forum back in April '18: “make sure you do not use the D0 or D1 lines on the module for anything else as they are used for I2C communication to the MCP23008 IC on this board, you can use any other pins you like.” Is that still correct? So the question for a knowledgeable NCD dude is: can you again confirm that there are no pins used by your MCP23008 relay board other than D0 and D1 that I cannot use?

Disregard Travis. I got anxious to get this working without the Arduino in the loop so I held my breath and connected the sensor to Photon pins 2 and 3. Works perfectly!!