-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathwater-level-monitor-using-sr04-and-labview.html
148 lines (147 loc) · 16.9 KB
/
water-level-monitor-using-sr04-and-labview.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
<html>
<head>
<title>Napster's Experiments with Freedom</title>
<link rel="stylesheet" type="text/css" href="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/styles/xt256.min.css">
<script src="http://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.7.0/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script type="text/javascript" src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<link rel="stylesheet" type="text/css" href="css/post.css">
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-20813670-9', 'auto');
ga('send', 'pageview');
</script>
</head>
<body><h1 id="hc-sr04-based-water-level-monitor-with-labview-interface">HC-SR04 based Water Level Monitor with LabView Interface</h1>
<p>I have been experimenting with an ultrasonic range sensor (HC-SR04) and thought about this wild idea of using it as a water level monitor. It turned out that the idea was not that wild, I could find some related articles on the internet. Despite having some concerns about field usability, I have decided to put together a prototype to see what happens.</p>
<h2 id="the-hc-sr04-ultrasonic-range-sensor">The HC-SR04 Ultrasonic Range Sensor</h2>
<figure>
<img src="images/sr04.jpg" alt="HC-SR04 Sensor"><figcaption><em>HC-SR04 Sensor</em></figcaption>
</figure>
<p>The breakout board has 2 data/control pins apart from the power supply. One is called <code>TRIGGER</code> and the other is <code>ECHO</code>. The sensor has two cylindrical metal drums on one side. Apparently one of these is a ultrasound pulse generator, and the other is a receptacle. They are placed close to each other. I’m not sure how exactly does this device range objects, but the concept is vaguely discussed in some internet articles. The idea is that, the pulse generated by one drum reflects off of any objects in direct vicinity, and the reflected pulses are picked up by the receptacle. I believe that the system assumes that the object does not interact with the ultrasonic wavelengths. If it does, the reflected wavelengths may be varied. Technically speaking, this is a mini SONAR.</p>
<p><strong>Note:-</strong> <em>Strictly speaking, this should have difference in interactions with the surface of water, and objects under water. SONAR is generally designed to detect objects under water, but here I go anyway</em></p>
<figure>
<img src="images/sr04-working.png" alt="HC-SR04 Working"><figcaption><em>HC-SR04 Working</em></figcaption>
</figure>
<p>Lets try to understand the timing features of this configuration. When we give it a <code>HIGH</code> on the <code>TRIGGER</code> pin for at least 10uS, the device gets triggered. It subsequently sends a 40KHz pulse from the generator, and receives reflections from nearby objects. There should a transfer function to convert the time difference in send/receive to distance. Then depending on the measured distance, the board sends us back a <code>HIGH</code> on the <code>ECHO</code> pin. The length of this pulse will be directly proportional to the distance measured by the device.</p>
<h2 id="interfacing-hc-sr04-with-arduino">Interfacing HC-SR04 with Arduino</h2>
<p>With this understanding, I have wired up the sensor with Arduino. The convenience here is that, since Arduino itself acts as a serial port device, using the USB-to-TTL emulation, it becomes useful in the next stage when we want to see data directly on a computer.</p>
<figure>
<img src="images/sr04-arduino.jpg" alt="HC-SR04 Wired up to the Arduino"><figcaption><em>HC-SR04 Wired up to the Arduino</em></figcaption>
</figure>
<p>Corresponding program is a reference program that I happen to find on the internet. The code below is in public domain, and you are free to use it the way you want. I have modified it to simplify the serial communication.</p>
<div class="sourceCode" id="cb1"><pre class="sourceCode c"><code class="sourceCode c"><a class="sourceLine" id="cb1-1" data-line-number="1"></a>
<a class="sourceLine" id="cb1-2" data-line-number="2"><span class="co">/**</span></a>
<a class="sourceLine" id="cb1-3" data-line-number="3"><span class="co"> * HC-SR04 Demo</span></a>
<a class="sourceLine" id="cb1-4" data-line-number="4"><span class="co"> * Demonstration of the HC-SR04 Ultrasonic Sensor</span></a>
<a class="sourceLine" id="cb1-5" data-line-number="5"><span class="co"> * Date: August 3, 2016</span></a>
<a class="sourceLine" id="cb1-6" data-line-number="6"><span class="co"> * </span></a>
<a class="sourceLine" id="cb1-7" data-line-number="7"><span class="co"> * Description:</span></a>
<a class="sourceLine" id="cb1-8" data-line-number="8"><span class="co"> * Connect the ultrasonic sensor to the Arduino as per the</span></a>
<a class="sourceLine" id="cb1-9" data-line-number="9"><span class="co"> * hardware connections below. Run the sketch and open a serial</span></a>
<a class="sourceLine" id="cb1-10" data-line-number="10"><span class="co"> * monitor. The distance read from the sensor will be displayed</span></a>
<a class="sourceLine" id="cb1-11" data-line-number="11"><span class="co"> * in centimeters and inches.</span></a>
<a class="sourceLine" id="cb1-12" data-line-number="12"><span class="co"> * </span></a>
<a class="sourceLine" id="cb1-13" data-line-number="13"><span class="co"> * Hardware Connections:</span></a>
<a class="sourceLine" id="cb1-14" data-line-number="14"><span class="co"> * Arduino | HC-SR04 </span></a>
<a class="sourceLine" id="cb1-15" data-line-number="15"><span class="co"> * -------------------</span></a>
<a class="sourceLine" id="cb1-16" data-line-number="16"><span class="co"> * 5V | VCC </span></a>
<a class="sourceLine" id="cb1-17" data-line-number="17"><span class="co"> * 7 | Trig </span></a>
<a class="sourceLine" id="cb1-18" data-line-number="18"><span class="co"> * 8 | Echo </span></a>
<a class="sourceLine" id="cb1-19" data-line-number="19"><span class="co"> * GND | GND</span></a>
<a class="sourceLine" id="cb1-20" data-line-number="20"><span class="co"> * </span></a>
<a class="sourceLine" id="cb1-21" data-line-number="21"><span class="co"> * License:</span></a>
<a class="sourceLine" id="cb1-22" data-line-number="22"><span class="co"> * Public Domain</span></a>
<a class="sourceLine" id="cb1-23" data-line-number="23"><span class="co"> */</span></a>
<a class="sourceLine" id="cb1-24" data-line-number="24"></a>
<a class="sourceLine" id="cb1-25" data-line-number="25"><span class="co">// Pins</span></a>
<a class="sourceLine" id="cb1-26" data-line-number="26"><span class="dt">const</span> <span class="dt">int</span> TRIG_PIN = <span class="dv">7</span>;</a>
<a class="sourceLine" id="cb1-27" data-line-number="27"><span class="dt">const</span> <span class="dt">int</span> ECHO_PIN = <span class="dv">8</span>;</a>
<a class="sourceLine" id="cb1-28" data-line-number="28"></a>
<a class="sourceLine" id="cb1-29" data-line-number="29"><span class="co">// Anything over 400 cm (23200 us pulse) is "out of range"</span></a>
<a class="sourceLine" id="cb1-30" data-line-number="30"><span class="dt">const</span> <span class="dt">unsigned</span> <span class="dt">int</span> MAX_DIST = <span class="dv">23200</span>;</a>
<a class="sourceLine" id="cb1-31" data-line-number="31"></a>
<a class="sourceLine" id="cb1-32" data-line-number="32"><span class="dt">void</span> setup() {</a>
<a class="sourceLine" id="cb1-33" data-line-number="33"></a>
<a class="sourceLine" id="cb1-34" data-line-number="34"> <span class="co">// The Trigger pin will tell the sensor to range find</span></a>
<a class="sourceLine" id="cb1-35" data-line-number="35"> pinMode(TRIG_PIN, OUTPUT);</a>
<a class="sourceLine" id="cb1-36" data-line-number="36"> digitalWrite(TRIG_PIN, LOW);</a>
<a class="sourceLine" id="cb1-37" data-line-number="37"></a>
<a class="sourceLine" id="cb1-38" data-line-number="38"> <span class="co">// We'll use the serial monitor to view the sensor output</span></a>
<a class="sourceLine" id="cb1-39" data-line-number="39"> Serial.begin(<span class="dv">9600</span>);</a>
<a class="sourceLine" id="cb1-40" data-line-number="40">}</a>
<a class="sourceLine" id="cb1-41" data-line-number="41"></a>
<a class="sourceLine" id="cb1-42" data-line-number="42"><span class="dt">void</span> loop() {</a>
<a class="sourceLine" id="cb1-43" data-line-number="43"></a>
<a class="sourceLine" id="cb1-44" data-line-number="44"> <span class="dt">unsigned</span> <span class="dt">long</span> t1;</a>
<a class="sourceLine" id="cb1-45" data-line-number="45"> <span class="dt">unsigned</span> <span class="dt">long</span> t2;</a>
<a class="sourceLine" id="cb1-46" data-line-number="46"> <span class="dt">unsigned</span> <span class="dt">long</span> pulse_width;</a>
<a class="sourceLine" id="cb1-47" data-line-number="47"> <span class="dt">float</span> cm;</a>
<a class="sourceLine" id="cb1-48" data-line-number="48"></a>
<a class="sourceLine" id="cb1-49" data-line-number="49"> <span class="co">// Hold the trigger pin high for at least 10 us</span></a>
<a class="sourceLine" id="cb1-50" data-line-number="50"> digitalWrite(TRIG_PIN, HIGH);</a>
<a class="sourceLine" id="cb1-51" data-line-number="51"> delayMicroseconds(<span class="dv">10</span>);</a>
<a class="sourceLine" id="cb1-52" data-line-number="52"> digitalWrite(TRIG_PIN, LOW);</a>
<a class="sourceLine" id="cb1-53" data-line-number="53"></a>
<a class="sourceLine" id="cb1-54" data-line-number="54"> <span class="co">// Wait for pulse on echo pin</span></a>
<a class="sourceLine" id="cb1-55" data-line-number="55"> <span class="cf">while</span> ( digitalRead(ECHO_PIN) == <span class="dv">0</span> );</a>
<a class="sourceLine" id="cb1-56" data-line-number="56"></a>
<a class="sourceLine" id="cb1-57" data-line-number="57"> <span class="co">// Measure how long the echo pin was held high (pulse width)</span></a>
<a class="sourceLine" id="cb1-58" data-line-number="58"> <span class="co">// Note: the micros() counter will overflow after ~70 min</span></a>
<a class="sourceLine" id="cb1-59" data-line-number="59"> t1 = micros();</a>
<a class="sourceLine" id="cb1-60" data-line-number="60"> <span class="cf">while</span> ( digitalRead(ECHO_PIN) == <span class="dv">1</span>);</a>
<a class="sourceLine" id="cb1-61" data-line-number="61"> t2 = micros();</a>
<a class="sourceLine" id="cb1-62" data-line-number="62"> pulse_width = t2 - t1;</a>
<a class="sourceLine" id="cb1-63" data-line-number="63"></a>
<a class="sourceLine" id="cb1-64" data-line-number="64"> <span class="co">// Calculate distance in centimeters</span></a>
<a class="sourceLine" id="cb1-65" data-line-number="65"> <span class="co">// are found in the datasheet, and calculated from the assumed speed </span></a>
<a class="sourceLine" id="cb1-66" data-line-number="66"> <span class="co">//of sound in air at sea level (~340 m/s).</span></a>
<a class="sourceLine" id="cb1-67" data-line-number="67"> cm = pulse_width / <span class="fl">58.0</span>;</a>
<a class="sourceLine" id="cb1-68" data-line-number="68"> </a>
<a class="sourceLine" id="cb1-69" data-line-number="69"> <span class="co">// Assuming when there is no water in the tank,</span></a>
<a class="sourceLine" id="cb1-70" data-line-number="70"> <span class="co">// the distance measured will be at a maxima.</span></a>
<a class="sourceLine" id="cb1-71" data-line-number="71"> <span class="co">// In this case, the depth of the tank : 300cm.</span></a>
<a class="sourceLine" id="cb1-72" data-line-number="72"> cm = <span class="dv">300</span> - cm;</a>
<a class="sourceLine" id="cb1-73" data-line-number="73"> <span class="co">// Print out results</span></a>
<a class="sourceLine" id="cb1-74" data-line-number="74"> <span class="cf">if</span> ( pulse_width > MAX_DIST ) {</a>
<a class="sourceLine" id="cb1-75" data-line-number="75"> Serial.println(<span class="st">"Out of range"</span>);</a>
<a class="sourceLine" id="cb1-76" data-line-number="76"> } <span class="cf">else</span> {</a>
<a class="sourceLine" id="cb1-77" data-line-number="77"> Serial.print(cm);</a>
<a class="sourceLine" id="cb1-78" data-line-number="78"> Serial.print(<span class="st">"</span><span class="sc">\n</span><span class="st">"</span>);</a>
<a class="sourceLine" id="cb1-79" data-line-number="79"> }</a>
<a class="sourceLine" id="cb1-80" data-line-number="80"> </a>
<a class="sourceLine" id="cb1-81" data-line-number="81"> <span class="co">// Wait at least 60ms before next measurement</span></a>
<a class="sourceLine" id="cb1-82" data-line-number="82"> delay(<span class="dv">300</span>);</a>
<a class="sourceLine" id="cb1-83" data-line-number="83">}</a></code></pre></div>
<h2 id="stage-1-testing">Stage-1 Testing</h2>
<p>The first stage testing is to make sure that the data send out by the Arduino reaches the target machine correctly. If you are running Arduino IDE on your target machine itself, it is relatively easy since your drivers are in place, as well as your IDE has a serial monitor. In my case, I’m programming the Arduino from my Mac, and my target host is a Windows-7, is running as a guest on VirtualBox. In that case, I should have the FTDI VCP (Virtual COM Port) drivers for Windows in place. Arduino Duemilanove uses FT232RL chip. Your control board may be using a different driver. A serial monitor program such as CoolTerm can be used to make sure the data from the sensor is coming correctly on the target host. You may use any program of your choice to do the same.</p>
<h2 id="labview-program">LabView Program</h2>
<figure>
<img src="images/labview-water-level-monitor.png" alt="Labview Block Diagram for the Water Level Monitor Project"><figcaption><em>Labview Block Diagram for the Water Level Monitor Project</em></figcaption>
</figure>
<p>The LabView program is relatively simple. You will need VISA drivers for LabView to be able to read from serial devices. You may download it from the NI website. What the LabView program does is explained below. The serial port is opened first, and the program reads 4 bytes from it at a time. This can be changed by the constant widget in the diagram, or at the runtime by using a control widget. My assumptions here is that, all the numbers coming from the sensor is less than 300 (Max depth of the tank), which fits well within 4 bytes. The read-buffer from the VISA Reader is at the same time attached to an Indicator (so that we can see what data is being received on the front panel), as well as a String —> Decimal number converter. Note that, I have not added a divider to set the tank input between the tank’s scale which is 0 - 100. It should be pretty straight forward to do. Finally, when the read operation is done, close the serial port. Note that, I have also connected the number output to a waveform chart. This is to monitor historical data.</p>
<h2 id="front-panel">Front-Panel</h2>
<figure>
<img src="images/labview-water-level-monitor-front-panel.png" alt="Water Level Monitor Running"><figcaption><em>Water Level Monitor Running</em></figcaption>
</figure>
<p>The front panel is also very simple. It contains a tank and a waveform chart. (I’ve a taste for classic controls, you may choose better widgets). The first 4 bytes of every new line printed by the Arduino program is displayed in the read buffer indicator. Once converted to a number, the tank widget is redrawn with appropriate progress. Paralelly, the same data is used to draw the waveform.</p>
<h2 id="caveats">Caveats</h2>
<ol type="1">
<li>As this is a prototype project, I have omitted many details. Say for example, the block diagram does a very minimal job in reading data from the serial device. If there is no data coming in from the device for 20 seconds, the program simply crashes. You may add conditional blocks to change the behavior.</li>
<li>Only the first 4 bytes of incoming data is read. Ideally, the reading should be full, and appropriate error handling should be done when the received data cannot be converted to a number. Again, adds more controls to the diagram.</li>
<li>In the field, the communication between the monitor and the sensor shall be done wirelessly, since the water tank is located elsewhere compared to your monitoring computer. This can be achieved by using any wireless com setup.</li>
</ol>
<div id="disqus_thread"></div>
<script type="text/javascript">
var disqus_shortname = 'subinsebastien';
(function() {
var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
dsq.src = '//' + disqus_shortname + '.disqus.com/embed.js';
(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
})();
</script>
</body>
</html>