Jump to content

Water Tank Meter


Jen-in-Wellies

Featured Posts

Last year I added a water tank level meter to the solar hot water control system I'd built for my boat.

This used a cheap (around £6) flow sensor fitted between the sediment filter and the water pump. The sensor measured the volume passing and the Arduino logic controller calculated how much water was left in the tank. This was displayed as a tank percent full on a set of LED's. This has been working very reliably since. A couple of neighbouring boaters on the mooring have asked for something similar for their boats.

The water tank meters I have built for them use the same model of flow meter, a YFS201B. This time the PLC is a smaller Arduino Nano, rather than an Uno. The level is indicated using an SG90 servo motor to turn a needle, giving an analogue like gauge. The first one is now installed and working. The gauge for the second boat is ready to fit.

It is wet and miserable outside today, so I'm writing up a bit about them.

First off, a short video of the first gauge being tested. The sketch is simulating 1 litre of water being drawn from a 70l tank every few seconds. The first half of the gauges range covers from 100% full to 20%. The second half covers from 20% to empty. A warning LED lights from 10% down. We are more interested in what the gauge is showing when it is nearly empty, hence the expansion of the range below 20%. The electronics for the second gauge can be seen on the right of the video. Version 2 includes a light sensor. This gauge is going to be in a bedroom and the sensor will disable the bright warning LED at night so the owners can sleep. Total material costs for each gauge comes to around £25, depending on where you get them. More to follow...

 

Jen

 

 

  • Greenie 3
  • Love 1
Link to comment
Share on other sites

Neat. I assume the button resets it to 100% when the water tank is filled, to eliminate drift. We just use a domestic water meter, but that requires remembering to read the meter when you fill the tank, and a bit of mental arithmetic to get a reading. Tempted to go for the arduino now.....

 

 

MP.

 

Link to comment
Share on other sites

Just now, MoominPapa said:

Neat. I assume the button resets it to 100% when the water tank is filled, to eliminate drift. We just use a domestic water meter, but that requires remembering to read the meter when you fill the tank, and a bit of mental arithmetic to get a reading. Tempted to go for the arduino now.....

 

 

MP.

 

That's it. You need to remember to press the button when you fill the tank, but it is an easy habit to get in to.

 

The electronics.

The gauges are powered from the same supply as the water pump. There is a risk therefore of voltage spikes. I used an 18V automotive grade metal oxide varistor across the power input lines to dump any spikes. There is a self resetting fuse to protect the wiring in to the gauge. The gauge runs on 5V, so I used a buck voltage converter to drop the 12 to 14V in. Arduino PLC's do have a built in voltage regulator, but this is quite wasteful of power at higher voltages, so I fed the Arduino straight in to the 5V pin from a more efficient converter.

 

SG90 servos go from 0 to 180 degrees. Actually a bit more than 180 degrees, so you need a bit of calibration sums in the code so the position matches where you think it should be. They are cheap as chips on ebay, around £1 each if ordered in bulk.

The ambient light sensor is a BP103 phototransistor. The same type I used on the solar thermal system. I bought a pack of five last year, so had several left over. The base pin is left unconnected. If you don't want to disable the low warning LED at night, then either remove it from the circuit and sketch, or put a high value resistor to pull A0 up to 5V to simulate daytime all day round.

The circuit diagram is below.

Jen

schematic.jpg.30d985aa69858d88fc1b1473ed30f77a.jpg

Link to comment
Share on other sites

1 minute ago, Alan de Enfield said:

I've just got a 'fuel tank' type gauge, when it gets down to 1/4 I re fill and no resetting needed.

 

I'm simple- its simple - I like simple !

Simple is good. There are lots of ways of doing this. Got away for years with knowing that a tank lasted me about a week, so Saturday was fill up day. Right at the start of owning this boat I made a transparent sight gauge. This worked well, but went green from algae, so I removed it. Yuck!

  • Greenie 1
Link to comment
Share on other sites

I have an electric 'fuel gauge' on my main fuel tank (1000 litres) but on the two wing-tanks (900 litres each) I have the typical 'clear plastic tube' type sight gauge. They do tend to get discoloured over the years and no as easy to read.

 

Nothing against someone doing high tech stuff.

Link to comment
Share on other sites

Programming.

There are two Arduino sketches here. First thing we need to find out is how much water the tank contains. The first sketch displays how much water has flown through the sensor. Fill up the tank, connect an Arduino to the flow meter, load this sketch and have a serial monitor open on your laptop. Open up the taps on the boat and wait till the tank empties. Read the tankUsed from the serial monitor. This, minus a few percent safety margin will become the tankFull constant in the water gauge sketch.

 

Tank calibration sketch.

#include <FlowMeter.h>  // https://github.com/sekdiy/FlowMeter

//Sensor properties for YF-S201B flow meter
FlowSensorProperties YF_S201B = {30.0f, 7.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

// connect a YF-S201B flow meter to interrupt pin 2.
FlowMeter Meter1 = FlowMeter(2, YF_S201B);

// set the measurement update period to 1s (1000 ms)
const unsigned long period = 2000;

float tankUsed = 0;

// define an 'interrupt service handler' (ISR)
void Meter1ISR() {
    // let our flow meter count the pulses
    Meter1.count();
}

void setup() {
    // prepare serial communication
    Serial.begin(9600);

    // enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin
    attachInterrupt(INT0, Meter1ISR, RISING);

    // sometimes initializing the gear generates some pulses that we should ignore
    Meter1.reset();
}

void loop() {
    // wait between output updates
    delay(period);

    // process the (possibly) counted ticks
    Meter1.tick(period);

    tankUsed = Meter1.getTotalVolume();

    // output some measurement result
    Serial.println("Currently " + String(Meter1.getCurrentFlowrate()) + " l/min, " + String(Meter1.getTotalVolume())+ " l total.");
    Serial.println("Currently " + String(Meter1.getTotalVolume())+ " l total.");
    Serial.print("tankUsed ");
    Serial.println(tankUsed);

}

The water tank gauge sketch follows. It will need customising with your own tank capacity (tankFull) and servoCal and servoOffset figures. SG90 servos vary from one to the next, so you'll need to experiment with these so zero and full match the needle positions at 0 and 180 degrees.

/*A water meter for boats.
   Written by Jen-in-Wellies. May 2020.
   Uses a YF-S201B flow meter at the water pump inlet to track water tank contents. Indicates contents using
   a servo as an "analogue" dial. Low level warning LED. If this is used in a bedroom, a BP103 phototransistor
   can measure the ambient light level and disable the low level warning LED at night.
*/

#include <FlowMeter.h> // https://github.com/sekdiy/FlowMeter
FlowSensorProperties YF_S201B = {30.0f, 7.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
FlowMeter Meter1 = FlowMeter(2, YF_S201B);
void Meter1ISR() {
  // let the flow meter count the pulses
  Meter1.count();
}
#include <Servo.h> // Standard servo library.
Servo myservo;

#include <EEPROM.h> //Standard EEPROM Library to store water level over power downs and resets.

//Pins.
const byte lowPin = 5; //Low water warning LED pin
const byte resetPin = 7; //Reset water level to 100%
const byte servoPin = 9; //output pin for servo.
const int photoPin = A0; //input pin from BP103 phototransistor.

//Customise these values to the boat on which it is installed.
const int tankFull = 64; // Tank capacity in litres.
const float servoCal = 0.96; // Compensation to get 180 degrees programmed servo rotation to equal 180 degrees in real life.
const byte servoOffset = 0; //The c in y=mx+c. Zero the servo angle at 100%

//constants and variables.
const byte lowLevel = 10; //Low water level % for warning LED to light.
float tankStored; //Stored tank level in EEPROM
const byte tankAddress = 0; //Address used on EEPROM
float tankUsed; //Current cumulative water used in litres.
float tankPercent; //Percentage tank water level.
float servoAngle; //Calculated angle to swing the servo.
float currentVolume = 0; //Current volume passed by sender.
const unsigned long time1 = 2000L; //loop time (ms)
unsigned long timer1 = 0L; //count for the needle movement loop
const unsigned long time2 = 300000L; //time for the loop copying current tank level to EEPROM (ms).
unsigned long timer2 = 0L; //count for the tank level EEPROM loop.
unsigned long now = 0L; //time now.
const int photoThreshold = 50; //Ambient light level at which the low water LED is switched on, or off.
int photoLevel; // ambient light level.
const byte deBug = 1; //Set to 1 to send serial debug data over USB. 0 otherwise.
const byte zeroEEPROM = 1; //Reset the eeprom stored tank used value to zero by setting this to zero. Once only on new Arduino, then set to 1.

void setup() {
  // put your setup code here, to run once:

  //Define digital pins.
  pinMode(lowPin, OUTPUT);
  pinMode(resetPin, INPUT_PULLUP);

  //Water Meter. Enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin
  attachInterrupt(INT0, Meter1ISR, RISING);
  // sometimes initializing the gear generates some pulses that we should ignore
  Meter1.reset();

  //Flash low level LED a couple of times.
  digitalWrite(lowPin, LOW); //LED on
  delay(500);
  digitalWrite(lowPin, HIGH); //LED off
  delay(500);
  digitalWrite(lowPin, LOW); //LED on
  delay(500);
  digitalWrite(lowPin, HIGH); //LED off

  //Attach servo to pin and move to 0 degrees (full)
  myservo.attach(servoPin);

  timer1 = millis();
  timer2 = millis();

  //Set EEPROM stored tank used Normally not used. Useful to set up a new Arduino. Zero (0.00), or carry over value from old Uno.
  if (zeroEEPROM == 0) {
    EEPROM.put(tankAddress, 0.0);
  }
  //ReadEEPROM for stored tank used and set initial tankUsed to this values.
  EEPROM.get(tankAddress, tankStored);
  tankUsed = tankStored;

  if (deBug == 1) {
    Serial.begin(9600); //debug
  }

}

void loop() {
  // put your main code here, to run repeatedly:

  //Water Meter. Check if water tank level reset button is pressed. If so, then set tankUsed and EEPROM stored value to zero.
  //Do once only per press by checking if tankUsed has been reset, to prevent multiple EEPROM writes and wearing out.
  if ((digitalRead(resetPin) == LOW) && (tankUsed != 0.0)) {
    tankUsed = 0.0;
    EEPROM.put(tankAddress, tankUsed);
    Meter1.reset(); //Reset water meter.
  }

  now = millis(); //Current time for the conditional loops timer1 and timer 2

  //Check every time2 interval and write tankUsed to tankStored if they are different.
  //Saves water level over resets. A longer time than time1 to save wear and tear on EEPROM.
  if ((now - timer2) > time2) {
    EEPROM.get(tankAddress, tankStored); //get current stored tank level.
    if (tankUsed != tankStored) {
      EEPROM.put(tankAddress, tankUsed);
    }
    timer2 = now;
  }

  //Check every time1 interval to see how much water has been used. Set servo to new tank level.
  if ((now - timer1) > time1) {
    //Check water usage over time1 cycle and update tankUsed and tankPercent.
    Meter1.tick(now - timer1);
    currentVolume = Meter1.getCurrentVolume();
    if (currentVolume > 0) {
      tankUsed = tankUsed + currentVolume; //Add amount measured since last loop
    }
    
    tankPercent = 100 * (tankFull - tankUsed) / tankFull;
    if (tankPercent < 0) tankPercent = 0.00; //No negative tankPercent.

    //Calculate servo angles. O to 20% takes up half the deflection. 20 to 100% the other half.
    if (tankPercent >= 20) {
      servoAngle = 180 - ((tankPercent * 1.125) + 67.5);
    }
    else {
      servoAngle = 180 - (tankPercent * 4.5);
    }

    myservo.write((servoAngle * servoCal) + servoOffset); //Use servoCal and servoOffset to calibrate servo angle swing for individual servo.
    delay(15);

    photoLevel = analogRead(photoPin); //Read the light level. if a phototransistor is fitted.
    delay(15);

    //Light the low level warning LED if the tank level is low and it isn't dark.
    if ((tankPercent <= lowLevel) && (photoLevel > photoThreshold)) {
      digitalWrite(lowPin, LOW);
    }
    else {
      digitalWrite(lowPin, HIGH);
    }

    timer1 = now;

    //Debug
    if (deBug == 1) {
      Serial.println("DEBUG");
      //Serial.print("time stamp ");
      //Serial.println(millis());
      Serial.print("now ");
      Serial.println(now);
      //Serial.print("timer1 ");
      //Serial.println(timer1);
      //Serial.print("timer2 ");
      //Serial.println(timer2);
      //Serial.print("photoLevel ");
      //Serial.println(photoLevel);
      Serial.print("Now - timer1 ");
      Serial.println(now - timer1);
      Serial.print("now - timer2 ");
      Serial.println(now - timer2);
      Serial.print("reset button ");
      if (digitalRead(resetPin) == LOW) {
        Serial.println("Pressed");
      }
      else {
        Serial.println("Open");
      }
      Serial.print("tankStored ");
      Serial.println(EEPROM.get(tankAddress, tankStored));
      Serial.print("tankUsed ");
      Serial.println(tankUsed);
      Serial.print("tank percent ");
      Serial.println(tankPercent);
      Serial.print("servo angle ");
      Serial.println(servoAngle);
      Serial.print("servoAngle * servoCal + servoOffset");
      Serial.println((servoAngle * servoCal) + servoOffset);
    }
  }
}

 

Link to comment
Share on other sites

Just now, Jen-in-Wellies said:

Programming.

There are two Arduino sketches here. First thing we need to find out is how much water the tank contains. The first sketch displays how much water has flown through the sensor. Fill up the tank, connect an Arduino to the flow meter, load this sketch and have a serial monitor open on your laptop. Open up the taps on the boat and wait till the tank empties. Read the tankUsed from the serial monitor. This, minus a few percent safety margin will become the tankFull constant in the water gauge sketch.

 

Tank calibration sketch.


#include <FlowMeter.h>  // https://github.com/sekdiy/FlowMeter

//Sensor properties for YF-S201B flow meter
FlowSensorProperties YF_S201B = {30.0f, 7.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

// connect a YF-S201B flow meter to interrupt pin 2.
FlowMeter Meter1 = FlowMeter(2, YF_S201B);

// set the measurement update period to 1s (1000 ms)
const unsigned long period = 2000;

float tankUsed = 0;

// define an 'interrupt service handler' (ISR)
void Meter1ISR() {
    // let our flow meter count the pulses
    Meter1.count();
}

void setup() {
    // prepare serial communication
    Serial.begin(9600);

    // enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin
    attachInterrupt(INT0, Meter1ISR, RISING);

    // sometimes initializing the gear generates some pulses that we should ignore
    Meter1.reset();
}

void loop() {
    // wait between output updates
    delay(period);

    // process the (possibly) counted ticks
    Meter1.tick(period);

    tankUsed = Meter1.getTotalVolume();

    // output some measurement result
    Serial.println("Currently " + String(Meter1.getCurrentFlowrate()) + " l/min, " + String(Meter1.getTotalVolume())+ " l total.");
    Serial.println("Currently " + String(Meter1.getTotalVolume())+ " l total.");
    Serial.print("tankUsed ");
    Serial.println(tankUsed);

}

The water tank gauge sketch follows. It will need customising with your own tank capacity (tankFull) and servoCal and servoOffset figures. SG90 servos vary from one to the next, so you'll need to experiment with these so zero and full match the needle positions at 0 and 180 degrees.


/*A water meter for boats.
   Written by Jen-in-Wellies. May 2020.
   Uses a YF-S201B flow meter at the water pump inlet to track water tank contents. Indicates contents using
   a servo as an "analogue" dial. Low level warning LED. If this is used in a bedroom, a BP103 phototransistor
   can measure the ambient light level and disable the low level warning LED at night.
*/

#include <FlowMeter.h> // https://github.com/sekdiy/FlowMeter
FlowSensorProperties YF_S201B = {30.0f, 7.5f, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};
FlowMeter Meter1 = FlowMeter(2, YF_S201B);
void Meter1ISR() {
  // let the flow meter count the pulses
  Meter1.count();
}
#include <Servo.h> // Standard servo library.
Servo myservo;

#include <EEPROM.h> //Standard EEPROM Library to store water level over power downs and resets.

//Pins.
const byte lowPin = 5; //Low water warning LED pin
const byte resetPin = 7; //Reset water level to 100%
const byte servoPin = 9; //output pin for servo.
const int photoPin = A0; //input pin from BP103 phototransistor.

//Customise these values to the boat on which it is installed.
const int tankFull = 64; // Tank capacity in litres.
const float servoCal = 0.96; // Compensation to get 180 degrees programmed servo rotation to equal 180 degrees in real life.
const byte servoOffset = 0; //The c in y=mx+c. Zero the servo angle at 100%

//constants and variables.
const byte lowLevel = 10; //Low water level % for warning LED to light.
float tankStored; //Stored tank level in EEPROM
const byte tankAddress = 0; //Address used on EEPROM
float tankUsed; //Current cumulative water used in litres.
float tankPercent; //Percentage tank water level.
float servoAngle; //Calculated angle to swing the servo.
float currentVolume = 0; //Current volume passed by sender.
const unsigned long time1 = 2000L; //loop time (ms)
unsigned long timer1 = 0L; //count for the needle movement loop
const unsigned long time2 = 300000L; //time for the loop copying current tank level to EEPROM (ms).
unsigned long timer2 = 0L; //count for the tank level EEPROM loop.
unsigned long now = 0L; //time now.
const int photoThreshold = 50; //Ambient light level at which the low water LED is switched on, or off.
int photoLevel; // ambient light level.
const byte deBug = 1; //Set to 1 to send serial debug data over USB. 0 otherwise.
const byte zeroEEPROM = 1; //Reset the eeprom stored tank used value to zero by setting this to zero. Once only on new Arduino, then set to 1.

void setup() {
  // put your setup code here, to run once:

  //Define digital pins.
  pinMode(lowPin, OUTPUT);
  pinMode(resetPin, INPUT_PULLUP);

  //Water Meter. Enable a call to the 'interrupt service handler' (ISR) on every rising edge at the interrupt pin
  attachInterrupt(INT0, Meter1ISR, RISING);
  // sometimes initializing the gear generates some pulses that we should ignore
  Meter1.reset();

  //Flash low level LED a couple of times.
  digitalWrite(lowPin, LOW); //LED on
  delay(500);
  digitalWrite(lowPin, HIGH); //LED off
  delay(500);
  digitalWrite(lowPin, LOW); //LED on
  delay(500);
  digitalWrite(lowPin, HIGH); //LED off

  //Attach servo to pin and move to 0 degrees (full)
  myservo.attach(servoPin);

  timer1 = millis();
  timer2 = millis();

  //Set EEPROM stored tank used Normally not used. Useful to set up a new Arduino. Zero (0.00), or carry over value from old Uno.
  if (zeroEEPROM == 0) {
    EEPROM.put(tankAddress, 0.0);
  }
  //ReadEEPROM for stored tank used and set initial tankUsed to this values.
  EEPROM.get(tankAddress, tankStored);
  tankUsed = tankStored;

  if (deBug == 1) {
    Serial.begin(9600); //debug
  }

}

void loop() {
  // put your main code here, to run repeatedly:

  //Water Meter. Check if water tank level reset button is pressed. If so, then set tankUsed and EEPROM stored value to zero.
  //Do once only per press by checking if tankUsed has been reset, to prevent multiple EEPROM writes and wearing out.
  if ((digitalRead(resetPin) == LOW) && (tankUsed != 0.0)) {
    tankUsed = 0.0;
    EEPROM.put(tankAddress, tankUsed);
    Meter1.reset(); //Reset water meter.
  }

  now = millis(); //Current time for the conditional loops timer1 and timer 2

  //Check every time2 interval and write tankUsed to tankStored if they are different.
  //Saves water level over resets. A longer time than time1 to save wear and tear on EEPROM.
  if ((now - timer2) > time2) {
    EEPROM.get(tankAddress, tankStored); //get current stored tank level.
    if (tankUsed != tankStored) {
      EEPROM.put(tankAddress, tankUsed);
    }
    timer2 = now;
  }

  //Check every time1 interval to see how much water has been used. Set servo to new tank level.
  if ((now - timer1) > time1) {
    //Check water usage over time1 cycle and update tankUsed and tankPercent.
    Meter1.tick(now - timer1);
    currentVolume = Meter1.getCurrentVolume();
    if (currentVolume > 0) {
      tankUsed = tankUsed + currentVolume; //Add amount measured since last loop
    }
    
    tankPercent = 100 * (tankFull - tankUsed) / tankFull;
    if (tankPercent < 0) tankPercent = 0.00; //No negative tankPercent.

    //Calculate servo angles. O to 20% takes up half the deflection. 20 to 100% the other half.
    if (tankPercent >= 20) {
      servoAngle = 180 - ((tankPercent * 1.125) + 67.5);
    }
    else {
      servoAngle = 180 - (tankPercent * 4.5);
    }

    myservo.write((servoAngle * servoCal) + servoOffset); //Use servoCal and servoOffset to calibrate servo angle swing for individual servo.
    delay(15);

    photoLevel = analogRead(photoPin); //Read the light level. if a phototransistor is fitted.
    delay(15);

    //Light the low level warning LED if the tank level is low and it isn't dark.
    if ((tankPercent <= lowLevel) && (photoLevel > photoThreshold)) {
      digitalWrite(lowPin, LOW);
    }
    else {
      digitalWrite(lowPin, HIGH);
    }

    timer1 = now;

    //Debug
    if (deBug == 1) {
      Serial.println("DEBUG");
      //Serial.print("time stamp ");
      //Serial.println(millis());
      Serial.print("now ");
      Serial.println(now);
      //Serial.print("timer1 ");
      //Serial.println(timer1);
      //Serial.print("timer2 ");
      //Serial.println(timer2);
      //Serial.print("photoLevel ");
      //Serial.println(photoLevel);
      Serial.print("Now - timer1 ");
      Serial.println(now - timer1);
      Serial.print("now - timer2 ");
      Serial.println(now - timer2);
      Serial.print("reset button ");
      if (digitalRead(resetPin) == LOW) {
        Serial.println("Pressed");
      }
      else {
        Serial.println("Open");
      }
      Serial.print("tankStored ");
      Serial.println(EEPROM.get(tankAddress, tankStored));
      Serial.print("tankUsed ");
      Serial.println(tankUsed);
      Serial.print("tank percent ");
      Serial.println(tankPercent);
      Serial.print("servo angle ");
      Serial.println(servoAngle);
      Serial.print("servoAngle * servoCal + servoOffset");
      Serial.println((servoAngle * servoCal) + servoOffset);
    }
  }
}

 

 

 

I'm not very good at following that type of sketch - I got lost after "#include"

Link to comment
Share on other sites

Hardware.

The needle that is used to indicate tank level is made from one of the actuator horns that are typically included when you buy an SG90 servo. This has a series of holes in it for fitting actuating wires that, for example, in a model aeroplane, would go to a control surface. These are filled in with araldite. Once the araldite is dry, the needle can be filed to a point. I painted them red with nail polish once they were the final shape. The centres are splined to fit on to the splined output shaft of the servo. If the splines mean the needle isn't exactly set at zero when at 100%, then the servoOffset constant in the sketch can be used to trim it to zero.

The dial face on these was made from a piece of 3mm thick plywood, 65 x 65mm thick. The gauge face was designed in OpenSCAD to get the dial marks and holes in the correct positions. If you don't use OpenSCAD, then it can be designed in any Cad program you are used to.

I have included the openSCad  at the bottom.

The exported design was imported in to a graphics program and turned in to a black and white print that was printed out on to thick paper. If you save the picture below it should print out at the correct size.

 

Dial-print.jpg.8a79737e13d33c50848817d2f849a389.jpg

 

The printed dial was then glued with wood glue to the plywood. Holes and openings were drilled for the servo, light sensor, LED, reset button and fixing holes. The servo is held in with some M2 press fit threaded inserts pushed in to the ply and M2 10mm long bots. The paper dial is protected by coating it either with clear nail varnish (the first one I made), or with a clear spray varnish (the second).

 

The electronics are built up on to a bit of vero strip board. It is all 0.1" pitch, including the Arduino Nano, so not difficult to solder together. For the lead between the flow rate sender and the Arduino is done with some four core twisted pair wire, with the signal and ground using one twisted pair and the +5V the other pair. This seems to work OK over a couple of metres, but at some point will no doubt start to pick up interference, or not work reliably, so there is a maximum distance that it will cover. I just don't know what that is yet!

 

The cheaper Arduino Nano clones can be had for a few quid. Sometimes these need USB drivers installed on the laptop, or may not have a boot loader set up on the Arduino, so it pays to shop around and maybe not go for the cheapest Ebay ones. The ones I got were in the UK and have worked well, but needed the aforementioned USB drivers installed to be able to talk with them.

 

Jen

 

OpenSCad file below if anyone wants to customise it. Give it an .scad extension to use.

//Dial design for water gauge
$fn=50;
overallSize = 65;
dialRadius = 26;
fixingDia = 2; //full size 3
textRadius = 18;
fontSize = 3;
markLength = 9;
markWidth = 0.5;
markDisplace = 12;
bigmarkLength = 12;
bigmarkWidth = 0.5;
bigmarkDisplace = 14;
pointerLength = 16;
pointerWidth = 5;
pointerAngle = 30;
font = "Liberation Sans";

//square([50,11.8], center=true); //servo body

//Pointer
/*rotate([0,0,pointerAngle]){
    translate([pointerLength/2,0,0]){
        square([pointerLength,pointerWidth], center=true);
    }
}*/

//dial outline
difference(){
    circle(dialRadius+1);
    circle(dialRadius);
}

//overall outline
difference(){
    square([overallSize,overallSize], center=true);
    square([overallSize-0.5,overallSize-0.5], center = true);
}

//Fixing Holes
translate([dialRadius,dialRadius,0]){
    circle(d=fixingDia);
}
translate([dialRadius,-dialRadius,0]){
    circle(d=fixingDia);
}
translate([-dialRadius,dialRadius,0]){
    circle(d=fixingDia);
}
translate([-dialRadius,-dialRadius,0]){
    circle(d=fixingDia);
}

//Legend
translate([0,-markDisplace*2/3,0]){
    text("Water", font = font, size = fontSize, valign = "center", halign = "center");
}
translate([0,-markDisplace,0]){
    text("%", font = font, size = fontSize, valign = "center", halign = "center");
}

//Drill marks for servo, LED, phototransistor and push button.
translate([0,0,0]){
    circle(d=2.8); //Spindle. Full size 11
}
translate([6.3,0,0]){
    circle(d=2); //gear. Full size 5
}
/*translate([6.3-2.5,0,0]){
    square([5,5],center=true); //gear
}*/

translate([-markDisplace, -markDisplace,0]){
    circle(d=2); // Low water LED. Full size 11
}
translate([markDisplace, -markDisplace,0]){
    circle(d=2); //Full reset button. Full size 11
}
translate([0, -markDisplace*3/2,0]){
    circle(d=2); //phototransistor. Full size 4.3
}

translate([-8.3,0,0]){
    circle(d=2);
}
translate([19,0,0]){
    circle(d=2);
}


//Text for LED and push button
translate([-markDisplace, -markDisplace/3,0]){
    text("Low", font = font, size = fontSize, valign = "center", halign = "center");
}

translate([markDisplace, -markDisplace/3,0]){
    text("Full", font = font, size = fontSize, valign = "center", halign = "center");
}

//Text and marks for needle pointer.
rotate([0,0,0]){
   translate([bigmarkDisplace,0,0]){
        square([bigmarkLength,bigmarkWidth], center = true);
    }
    /*translate([textRadius,0,0]){
       text("F", font = font, size = fontSize, valign = "center");
    }*/
}
rotate([0,0,0]){
    translate([0,markDisplace,0]){
        square([markWidth,markLength], center = true);
    }
    translate([0,textRadius,0]){
       text("20", font = font, size = fontSize, valign = "bottom", halign = "center");
    }
}
rotate([0,0,135]){
    translate([markDisplace,0,0]){
        square([markLength,markWidth], center = true);
    }
    translate([textRadius,0,0]){
        rotate([0,0,180]){
            text("10", font = font, size = fontSize, valign = "center", halign = "right");
        }
    }
}
rotate([0,0,45]){
    translate([markDisplace,0,0]){
        square([markLength,markWidth], center = true);
    }
    translate([textRadius,0,0]){
       text("60", font = font, size = fontSize, valign = "center");
    }
}
rotate([0,0,180]){
    translate([bigmarkDisplace,0,0]){
        square([bigmarkLength,bigmarkWidth], center = true);
    }
}
    /*translate([-textRadius,0,0]){
       text("E", font = font, size = fontSize, valign = "center", halign = "right");
    }*/


//Bigmarks insterstitial to numbers.
rotate([0,0,22.5]){
    translate([bigmarkDisplace,0,0]){
        square([bigmarkLength,bigmarkWidth], center = true);
    }
}
rotate([0,0,22.5+45]){
    translate([bigmarkDisplace,0,0]){
        square([bigmarkLength,bigmarkWidth], center = true);
    }
}
rotate([0,0,22.5+90]){
    translate([bigmarkDisplace,0,0]){
        square([bigmarkLength,bigmarkWidth], center = true);
    }
}
rotate([0,0,22.5+135]){
    translate([bigmarkDisplace,0,0]){
        square([bigmarkLength,bigmarkWidth], center = true);
    }
}
  

 

 

Edited by Jen-in-Wellies
  • Greenie 1
Link to comment
Share on other sites

Using a servo motor to implement a simple analog meter is a bit of overkill ?, but then daft projects are fun, I have converted a couple of old brass pressure gauges to electric operation.

 

Integrating flow to deduce volume is dangerous, a sort of liquefied amp hour counter. Do you remember the Car Transporter that fell over in the Solent a couple of years ago? They were using a similar system to monitor the contents of the ballast tanks and that's partly why they fell over.

 

Do you have access to the top of the tank?  There is an ultrasonic transducer pair that are used by the Arduino robot fraternity, maybe you could use this to measure the distance between the top of the tank and the water surface?. You could implement both systems and compare the results, but then would need another system to do the best of three thing?.

 

Have not gone through your software, C makes me feel sick, but have you done the going to sleep thing to minimise current draw?

 

..............Dave

Link to comment
Share on other sites

6 minutes ago, dmr said:

Integrating flow to deduce volume is dangerous, a sort of liquefied amp hour counter. Do you remember the Car Transporter that fell over in the Solent a couple of years ago? They were using a similar system to monitor the contents of the ballast tanks and that's partly why they fell over.

Agreed. This is why there is a reset button to take it all back to a known state (Full 100%)  every time the tank is refilled. Saves it getting too out of step with reality.

6 minutes ago, dmr said:

Do you have access to the top of the tank?

No.

6 minutes ago, dmr said:

Have not gone through your software, C makes me feel sick, but have you done the going to sleep thing to minimise current draw?

Yep. C takes no prisoners with mistakes. Looking in to and minimising current draw is the next step in the project.

Jen

Edited by Jen-in-Wellies
Link to comment
Share on other sites

13 minutes ago, system 4-50 said:

C? I love it. Especially when the compiler gets to the end of the source code and says "There is a problem somewhere".

The Arduino IDE's compiler isn't quite that bad. It will point you to roughly what line it sees a problem. OK the actual problem might be in a previous line that then makes the line shown invalid, but usually enough of a clue to fix after some head scratching.

Jen

Link to comment
Share on other sites

9 minutes ago, Jen-in-Wellies said:

The Arduino IDE's compiler isn't quite that bad. It will point you to roughly what line it sees a problem. OK the actual problem might be in a previous line that then makes the line shown invalid, but usually enough of a clue to fix after some head scratching.

Jen

......and I thought Lithium Batteries were complicated! ?

Well done Jen. Looks a great (cheap) way of doing it. Got any spares?

Link to comment
Share on other sites

9 minutes ago, Dr Bob said:

......and I thought Lithium Batteries were complicated! ?

Well done Jen. Looks a great (cheap) way of doing it. Got any spares?

I'm not planning to make any more, but I've hopefully put enough detail in this thread that someone else could. As Arduino projects go it is pretty simple. Only three inputs and two outputs.

I've tried reading the Lithium battery threads, but it makes my head hurt.

Jen

Edited by Jen-in-Wellies
  • Happy 1
Link to comment
Share on other sites

38 minutes ago, Jen-in-Wellies said:

Agreed. This is why there is a reset button to take it all back to a known state (Full 100%)  every time the tank is refilled. Saves it getting too out of step with reality.

No.

Yep. C takes no prisoners with mistakes. Looking in to and minimising current draw is the next step in the project.

Jen

There's a good in depth article somewhere on the www about using the sleep modes to significantly reduce consumption, wish i'd kept the link. Sleeping would fit in well with your servo display, I would think the Arduino would only need to wake up once every few minutes, you could even detect when the tank is filling and maybe wake a bit more often.

 

There are some nice things about C, and for hardware stuff its much less traumatic than assembler, but I'm a VB person and I like the If---End If thing and a bit of verbosity,  I don't get on with the Curly Brace syntax languages.  

 

and as for Python and white space ?

 

...............Dave

Link to comment
Share on other sites

1 minute ago, dmr said:

There's a good in depth article somewhere on the www about using the sleep modes to significantly reduce consumption, wish i'd kept the link. Sleeping would fit in well with your servo display, I would think the Arduino would only need to wake up once every few minutes, you could even detect when the tank is filling and maybe wake a bit more often.

 

There are some nice things about C, and for hardware stuff its much less traumatic than assembler, but I'm a VB person and I like the If---End If thing and a bit of verbosity,  I don't get on with the Curly Brace syntax languages.  

 

and as for Python and white space ?

 

...............Dave

I've done a bit of reading on power minimisation, but need to do a lot more. The flow sensor is on an interrupt pin, so this will hopefully be possible to use to wake it up when water flow is happening. The rest of the time it can be asleep as the servo won't be moving and nothing will be happening.

The flow meter is in the outlet from the tank. Either before, or after the pump. It doesn't detect tank filling at all. You tell it the tank is full by hitting the reset button.

Jen

Link to comment
Share on other sites

24 minutes ago, Jen-in-Wellies said:

The Arduino IDE's compiler isn't quite that bad. It will point you to roughly what line it sees a problem. OK the actual problem might be in a previous line that then makes the line shown invalid, but usually enough of a clue to fix after some head scratching.

Jen

Mostly its very good, but just occasionally a misplaced {,},;,  or " can really throw it. I use the BBedit editor and that helps to show the structure considerably.

I use a TFT display driven by a Teensy to show all three of my tank gauges in a bar style.

  • Greenie 1
Link to comment
Share on other sites

13 minutes ago, Jen-in-Wellies said:

I've done a bit of reading on power minimisation, but need to do a lot more. The flow sensor is on an interrupt pin, so this will hopefully be possible to use to wake it up when water flow is happening. The rest of the time it can be asleep as the servo won't be moving and nothing will be happening.

The flow meter is in the outlet from the tank. Either before, or after the pump. It doesn't detect tank filling at all. You tell it the tank is full by hitting the reset button.

Jen

Yes, I was not thinking clearly  (doing too many things at once). I find our meter really useful when filling the tank, especially here in Hebden Bridge where the flow varies from day to day and a fill can take 4 hours or more, so its good to estimate a "time to full" to get on with other jobs. Is your tank integral or stainless steel? As you are competent at programming and electronics there must be a clever way of doing an absolute measurement, though the system we have measures the water pressure (weight) and that mostly works well.

 

and yes, a wake on interrupt should be a good way of doing it.

 

...................Dave

Link to comment
Share on other sites

19 minutes ago, dmr said:

though the system we have measures the water pressure (weight) and that mostly works well.

That sounds a good way of getting an absolute reading. There are various in tank senders around which would work well if the tank is accessible for fitting. If not, then it may be possible to fit a sender in a remote vertical pipe, T'd in to the pipe between tank and pump. The level in this, if it is open vented, will match that in the tank. Similar to the sight gauges that people sometimes make, but with remote reading to a gauge. It wouldn't be accurate when the pump runs due to the low pressure it puts on that pipe, but otherwise would be pretty good.

Jen

Link to comment
Share on other sites

39 minutes ago, Jen-in-Wellies said:

That sounds a good way of getting an absolute reading. There are various in tank senders around which would work well if the tank is accessible for fitting. If not, then it may be possible to fit a sender in a remote vertical pipe, T'd in to the pipe between tank and pump. The level in this, if it is open vented, will match that in the tank. Similar to the sight gauges that people sometimes make, but with remote reading to a gauge. It wouldn't be accurate when the pump runs due to the low pressure it puts on that pipe, but otherwise would be pretty good.

Jen

We have the system from MCS

 

http://mcsboatproducts.co.uk/portfolio/fresh-water-gauge/

 

It looks like a little a low pressure sensor and an op-amp, with gain and offset preset resistors. It sits in the pipe just after the tank outlet. The only constraint is that it needs to be near the level of the bottom of the tank. It works very well. We go on holiday every January and it usually has to be recalibrated afterwards, this must be due to a partial freezing of the water despite my best efforts. As you predict it goes wrong (reading drops to zero) whenever the pump runs. I have read this can be (possibly) avoided by putting the sender in a little spur rather than in-line, that's on the todo list. If you can find a suitable sensor then you could do the same via the Arduino so still use your nice servo display. I think it should still work fine if the sender was some way from the tank as long as it was at the correct level, if there is no flow the pressure must be constant along the pipe run.

 

..................Dave

  • Greenie 1
Link to comment
Share on other sites

  • 2 weeks later...

I love this project! A possible addition could be a sensor measuring how much was going in as well, because I'd like to know sometimes how far off from full I am while filling.

Has anyone experimented with other approaches to measuring the water that is actually in the tank, like a float or a pressure based system? I suppose this is going to be inaccurate based on the trim, but perhaps it could be combined with the Hall effect sensor to get an accurate reading?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.