I made one for my holding tank using a capacitive sensor (used the MODA tank sensor from
https://tankedge.com/accessories.html). I used SensESP, a voltage reducer (for reading the sensor), a 12volt to 5 volt buck converter (to power the board off of 12 volt) and a lot of trial and error to get it working. It lives in a water-proof case under my mattress next to the tank and connects to signalk via wifi.
Just upgraded it to the new SensESP, which took a few minutes, as I had to update the programming a bit, but it was even easier and cleaner than the first time. Once I figure out the concepts and stopped trying to "Over Program" the code, it all came together.
There is some calculations needed, but those are outside of SensESP. I add an extra SKPath so I can monitor the ADC read values (for empty & full), and then do the calculation for needed Mulitplier and Offset, which I can enter in the web set up for SensESP. I also use a long period for MovingAverage to help smooth out reading fluctuations. It's all done in a couple lines of code. Here's what I have:
//
// This application demonstrates core SensESP concepts in a very
// concise manner. You can build and upload the application as is
// and observe the value changes on the serial port monitor.
//
// You can use this source file as a basis for your own projects.
// Remove the parts that are not relevant to you, and add your own code
// for external hardware libraries.
#include "sensesp/sensors/analog_input.h"
#include "sensesp/sensors/sensor.h"
#include "sensesp/signalk/signalk_output.h"
#include <sensesp/transforms/linear.h>
#include <sensesp/transforms/moving_average.h>
#include "sensesp_app_builder.h"
using namespace sensesp;
reactesp::ReactESP app;
// The setup function performs one-time application initialization.
void setup() {
#ifndef SERIAL_DEBUG_DISABLED
SetupSerialDebug(115200);
#endif
// Construct the global SensESPApp() object
SensESPAppBuilder builder;
sensesp_app = (&builder)
// Set a custom hostname for the app.
->set_hostname("New-SensESP")
->set_wifi_manager_password("SensESP32")
// Optionally, hard-code the WiFi and Signal K server
// settings. This is normally not needed.
//->set_wifi("My WiFi SSID", "my_wifi_password")
//->set_sk_server("192.168.10.3", 80)
->get_app();
const char* sk_path0 = "tanks.blackWater.0.currentLevel";
const char* sk_read0 = "tanks.blackWater.0.reading";
const char* analog_in_config_path = "/blackWater/analogInput";
const char* linear_config_path = "/blackWater/linear";
const char* level_avg_samples = "/blackWater/average";
uint8_t pin0 = 35; // read pin for sensor "0"
uint read_delay = 1000; // read frequency
const float multiplier = 0.00862517325816782; // 100% divided by total range (high-low read)
const float offset = -0.380027721306852; // Offset to compensate for low-end
float scale = 1.0;
// Create a new Analog Input Sensor that reads an analog input pin0
// and sends it to SignalK.
auto* blackwater_read = new AnalogInput(
pin0, read_delay, analog_in_config_path);
blackwater_read->connect_to(new SKOutputFloat(sk_read0))
->connect_to(new Linear(multiplier, offset, linear_config_path)) // adjust for vacuum reading
->connect_to(new MovingAverage(120,scale, level_avg_samples))
->connect_to(new SKOutputFloat(
sk_path0, // Signal K path
new SKMetadata("ratio", // Define output units
"Blackwater Tank Volume") // Value description
))
->attach([blackwater_read]() {
debugD("Analog input value: %f", blackwater_read->get());
});
// Start networking, SK server connections and other SensESP internals
sensesp_app->start();
}
void loop() { app.tick(); }
_________
Works great.
I'm moving on to a vacuum switch on my fuel filters next with the idea I can monitor fuel suction and get an alarm when it's time to change filters or if there's water in the bowl.