Need to measure the depth of a submarine. Let’s read some datasheets.
The first pressure sensor
The first pressure sensor I bought was Honeywell ABP2LANG004BG2A3XX. It cost 26 euros in a Finnish retail shop, link: https://www.radioduo.fi/p/p/ABP2LANG004BG2A3XX/
It is very small (8x6x10mm), which is great as there is always too little space inside the hull. It has a barbed port that is easy to connect to a hose, perfect!
The communication output is I2C, which is great since I’ve used that before. Besides I2C there were also SPI and analog sensors available, but SPI requires 4 wires instead of 2, and I can’t read analog with Raspberry Pi Zero 2, so that was an easy decision. The spec says this sensor is suited for liquid media, very convenient. Pressure range is 0…4 bar, which is 0…40 meters in water depth. I would have preferred less than that, since I plan to dive max 4 meters and larger range means less accuracy, but fine.
Data rate is 204 samples/s, which is more than enough. From previous subs I know they move very slowly. Water resistance dampens all movements and the subs are heavy compared to the control force, which makes them behave like heavy trucks or something. It could take 10 seconds to submerge from 1.5 m depth. So timing shouldn’t be an issue in the PID control. But high data rate is good as it allows me to use more filtering and further improve accuracy.
But accuracy numbers look disappointing. Datasheet says ±0.25 %FSS BFSL. FSS is Full Scale Span which in our case is 4 bar. BFSL is Best Fit Straight Line which is a kind of an average of the measurements. So 0.25% of 4 bar is 10 mbar which equals about 10 cm of depth. That is bad. I would want the sensor accuracy to be about 1 mm, or at worst case 1 cm. But that is the best I found, so I have to deal with it.
After I received the sensor, I had to solder wires to the pads. Why did I buy a Leadless SMT version, as there were other packages available? Those pads are almost too small to be soldered by hand. I could break the sensor by heating it too much or make a bad solder connection that breaks during dive. A first of many bad decisions.
I connected the wires to Raspberry to test it. Four wires: GND, Vdd (3.3V), SDA, SCL. Then I wrote a simple Python code to read data from the sensor.
The first test I did was against a Lego barometer. I used three hoses and a T-splitter to connect together the sensor, the barometer and a Lego pneumatic pump. I used the pump to increase the pressure to 2 bar. This was just to verify that I wrote the Python code correctly. Sensor readings matched that of the barometer. Great!
Then I measured the amount of noise the data has. I kept the sensor on a table, read data 100 samples/s, and calculated standard deviation. The result was 0.00017 bar which is 1.7 mm of depth. So basically 99.7% of the samples were in a 1 cm range. That was surprisingly good. Based on the datasheet I expected worse. Then again, those datasheet numbers are from the entire pressure range and probably very conservative anyways.
One odd thing I noticed that there were spikes in the data. Randomly one sample would be 10-50 cm too large. It happened once in a while, sometimes every second or so. I never knew why. I just put a few lines of code to filter out outliers and moved on.
Then I made the first test with water. I connected a hose to the sensor and lowered the end of the hose into a container filled with water. This isn’t the best way to test a sensor since the air inside the hose will compress and make the depth readings too small, but it is accurate enough for this purpose. I was interested to see the amount of noise in water pressure readings, and how it fluctuates when you move the hose. I was happy with the results. Noise was in a 1 cm range, response to hose movements was very quick, and the readings were accurate when compared to a measurement tape at the side of the container.
Problem with relative pressure values
At this point I will move much further into the building process, to a time where I first tested the submarine in a big water container. That is when I noticed an unsolvable problem with this sensor. What I had in my hands was a relative pressure sensor. That means it measures pressure relative to the surrounding ambient pressure. Ambient pressure in our case is the pressure inside the hull. I first noticed this when I closed the hull lids airtight and pressed buttons to move the syringe ballast. Sensor readings started immediately to change, even when the submarine wasn’t moving. Damn it!
I did some calculations to verify the problem. The free air space inside the hull is about 2000 ml and the syringe active capacity is 40 ml. Therefore, when the syringe goes from fully retracted to fully extended, the air space inside the hull will diminish from 2000 ml to 1960 ml. I put the numbers into an online Boyle’s law calculator and got a result of pressure increase from 1 bar to 1.0204 bar. That means about 20 cm increase in depth, which was roughly the effect I saw in my tests. So I knew the cause, but this wasn’t good. That error is too large and it will mess up the PID control.
But maybe I can compensate for it in code? I know the syringe position from tachometer data. I will just use the Boyle’s law equations to fix the pressure reading after I’ve read it. So I did put a few lines of code, and the measurement data started to look good again.
Still not good. I did more tests in the water container and noticed moments when the sensor data was a few centimeters off. Not as big error as before, but still large enough to cause problems. This time the problem was with moving end caps. I had had problem with pushing the end caps to their innermost position, because the air inside the hull pushes back. So the caps had some room for movement. They moved because of the outside water pressure and the inside hull pressure, depending on the submarine depth and the syringe position. You couldn’t estimate these movements because of the static friction on the o-rings and such. Later I did find a solution the push the lids fully inside, but at this moment I saw this as another source of unreliability to the sensor data.
For a brief moment I thought about adding another sensor to measure the pressure inside the hull to provide full and accurate compensation. But that would mean more wires and more complexity that shouldn’t be necessary in the first place. So I decided buy a completely new sensor to replace the first one.
The second pressure sensor
The second pressure sensor I bought was Honeywell SSCMANV030PA2A3. It cost 35 euros in Elfa Distrelect, link https://www.elfadistrelec.fi/fi/p/30159454.
This is an absolute pressure sensor, meaning it measures pressure relative to vacuum. The readings are not affected by surrounding ambient pressure.
In some ways this is identical to the first sensor, as it has a barbed port and it is suitable to liquid media. But there are also some differences. This is slightly larger (7×13.3×13.7mm). The pressure range is 0 to 30 psi (2.06 bar), but as this is an absolute sensor, the range is max 1 bar relative to atmosphere. So it can measure up to 10 meters of depth, which is enough for my submarine. Sample rate has gone up to 1000 samples/s (1 ms response time). Accuracy has been doubled from 10 cm to 5 cm, as the range is 2 bar instead of 4 bar. That is good news, but at the same time, output data resolution is only 12 bits (0.03%) whereas the first sensor had 14 bit output. That is slightly concerning, as 12 bits is only 0.5 cm of depth. Have to see how it fares.
Also, I was careful to pick an SMT (Surface Mount Technology) package to make the soldering easier.
When I tested this sensor, I found no random spikes in the data, as with the first sensor. I was happy to remove those ugly outlier filters and simplify the code. The syringe position compensation code was also removed.
Here is the Python code I used to read the sensor using I2C communication. Note that the sensor returns both pressure and temperature. I don’t need temperature, but I read it just to put it into log file, in case I run into overheating problems that I need to investigate.
import smbus bus = smbus.SMBus(1) def readPressureSensor(): #read data PRESSURE_SENSOR_ADDR = 0x28 data = bus.read_i2c_block_data(PRESSURE_SENSOR_ADDR, 0x00, 4) status = (data & 0xC0) >> 6 pressCounts = data | ((data & 0x3F) << 8) tempCounts = ((data & 0xE0) >> 5) | (data << 3) #pressure conversion P_MAX = 2 #[bar] P_MIN = 0 #[bar] O_MAX = 0.9 * pow(2,14) O_MIN = 0.1 * pow(2,14) pressure = (pressCounts - O_MIN) * (P_MAX - P_MIN) / (O_MAX - O_MIN) + P_MIN #[bar] #temperature conversion T_MAX = 150 #[Celsius] T_MIN = -50 #[Celsius] T_COUNTS = pow(2,11) - 1 temperature = tempCounts * (T_MAX - T_MIN) / T_COUNTS + T_MIN #[Celsius] return pressure, temperature
Here is the conversion from pressure to depth. 1 bar equals about 10 meters of depth in water.
#calculate depth GRAVITY = 9.80665 #[m/s2] WATER_DENSITY = 998 #fresh water at 20 Celsius [kg/m3] pressurePa = pressure * 100000 #[Pa] depth = pressurePa / (GRAVITY * WATER_DENSITY) #[m]
I also need a reference pressure. One Finnish weather station website says the atmospheric pressure fluctuated from 0.974 to 1.033 bar in year 2021. That means 59 cm of change in depth. Wow, that is a lot! You could probably get several centimeters of error during a single day. So the reference cannot be hardcoded into the code, it needs to update every time I startup the software. I added some code to take the first reading from the pressure sensor and use that as a reference.
Also note that the pressure sensor is not located exactly at the surface. It is about 7 cm below the surface. The reference point has to account for that also, but to do it, I’d have to start the software while the submarine is floating in the water. So, to make the usage a little bit easier, I added some code to adjust the reference point slowly when it is floating at the surface. I used syringe position = 0 as a proof of surface position, as I really don’t have any better way to know that. In the end, I started the software through WLAN while the submarine was floating in water, so the adjustment code was almost never needed.
#adjust for depth zero point if math.isnan(depthZeroPoint): depthZeroPoint = depth if syringePos <= 0: #when syringe is empty, assume we are in surface and start adjusting depthZeroPoint = 0.01 * depth + 0.99 * depthZeroPoint depth -= depthZeroPoint
I added also some filtering to the depth data. I chose exponential moving average, because it is very easy to implement in a single line of code. The main reason was to help the Derivative part of PID control, which is really sensitive to noise.
#filter depth (exponential moving average) depthFiltered = 0.1 * depth + 0.9 * depthFiltered
Here is an example of the sensor data from a dive in a small river. Both unfiltered and filtered depth is shown. As you see, the 12-bit resolution makes the raw data a bit jerky, but filtering averages that out nicely without introducing much delay.
This sensor proved to be a good one. Data has been accurate, no spikes and no errors from the syringe position or the end cap movements. I’ve used it 10+ hours underwater without any hiccups.
love the blog and your project, just a little mistake in my opinion. When you write about the pressure sensor, you say it goes up to 4 bar and so down to 40m. But actually that would be too far. In 40m depth you have around 5 bar. Every 10m of water level you add 1 bar. But on sea level you already have 1 bar, not 0. so as you enter water, you are already on 1 bar, and at 10m 2 bar.
Hi. Nice to see people double-checking my writing. 🙂
It depends whether the max pressure rating is in absolute value or relative to atmosphere. If it is absolute, then you’re right. After reading the specs again, I think it is in relative values. So the 0…4 bar pressure range is 1…5 bar in absolute values and therefore it works down to 40 m depth. Correct if I’m wrong.
004BG Gage sensor in https://www.tme.eu/Document/6429863a00649a3c0ae15d32e0500d50/ABP2%20Series%20Datasheet%20-%20Issue%20C.pdf
Hm, not sure. I’m reading there absolute 0 to 4. But I’m not very good reading such documents. Plus as you wrote, you will never get that far down to find that out ^^.
And on the end you use a different sensor anyways. My bad, I should read till the end, before commenting =D. Sorry.
we not buy Honeywell ,but find a looks like same one, please help me check it whether can replaced. below link is the product diagram
Physically that looks ok. The 3 mm diameter port should fit to the Lego hose. Otherwise I don’t know, since I can’t read Chinese.