Introduction: Inline Digital Hydrometer

Many industrial processes require chemical analysis of a fluid and sometimes just knowing it's density is all that is necessary to know what it's main constituents are. This is particularly true in whiskey production, for example.

Here, I am using a very accurate pressure sensor which is connected to some elaborate looking copper pipework to measure the weight of a column of fluid of set height. As long as the ambient pressure and fluid temperature does not change, the density of the fluid will be directly proportional to the pressure generated by the column of fluid. Simple?

Step 1: How It Works

The distillate or 'alcohol' from the condenser enters the top chamber of the gadget and pours down a small internal pipe drawn in green. If this pipe were not here, the alcohol would just flow straight out of the overflow and not circulate into the main body of the device and there would be a large error in the readings.

So, once the device is full of liquid, the flow of fresh liquid down the green pipe causes the 'old' liquid to be pushed out so that we are always measuring the fresh stuff.

The small bore sensor tube is attached to a vertical 'anti capillary' tube, which runs parallel to the main pipework and goes to the HSCDRRN005NDAA5 pressure sensor in the control panel which has barbed fittings on it for very small pipes. If the small flexible pipe was connected directly to the bottom of the gadget it would fill up with fluid and not empty properly due to the forces of capillary action keeping it in the pipe. This capillary action is not a problem in a bigger bore pipe and all we see is a meniscus.

Just to make things even more complicated, there's a solenoid valve at the very bottom of the device which can be automatically opened to dump the contents of the device at different stages of the process.

As the process continues, the 'alcohol', which is actually composed of mostly alcohol and water with a few other weird and wonderful liquids in much lower concentrations, becomes weaker and weaker with more and more water and strange oily chemicals called 'fusils'. It's really useful to be able to measure the concentration of the distillate being produced at all stages of the process as we don't want too much water or too many other heavy chemicals as some of these can give us a bad hangover! (But an interesting taste).

Step 2: Parts

  • 1DS18B20 1-Wire Temperature Sensorpart # DS18B201
  • ADS1115 16-bit ADC Breakout1
  • HSCDRRN005NDAA5chip label HSCDRRN005NDAA5; pins 8; true; hole size 1.0mm,0.508mm; package DIP (Dual Inline) [THT]; pin spacing 300mil1
  • Piezo Speaker1
  • LCD screenpins 16; type Character1
  • Arduino Nano (Rev3.0)type Arduino Nano (3.0)1
  • Rotary Potentiometer (Small)maximum resistance 100kΩ; track Linear; type Rotary Shaft Potentiometer; package THT; size Rotary - 9mm1
  • 100Ω Resistor tolerance ±5%; bands 4; package THT; pin spacing 400 mil; resistance
  • 100Ω14.7kΩ Resistor tolerance ±5%; bands 4; package THT; pin spacing 400 mil; resistance
  • 4.7kΩ11kΩ Resistor tolerance ±5%; bands 4; package THT; pin spacing 400 mil; resistance 1kΩ

Step 3: Arduino Setup

As can be seen, the setup features a ADS1115 16-bit ADC, an Arduino Nano and a HSCDRRN005NDAA5 chip. The 16-bit ADC gives better resolution than the normal Arduino 8 bit, particularly as the pressure sensing chip measures both positive and negative pressures.

Step 4: Arduino Code

There's nothing too fancy about the code, except that I've made a custom LCD character for a delta sign and the nano takes 30 readings from the pressure sensor before calculating an average.

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_ADS1015.h>
#include <Wire.h>
#include <LiquidCrystal.h>
#define ONE_WIRE_BUS 12
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress pipeProbe01 = { 0x28, 0x1E, 0x28, 0x08, 0x00, 0x00, 0x80, 0x00  };
float tempC;
float previousTempC =0;
float deltaTempC =0;
Adafruit_ADS1115 ads;  /* Use this for the 16-bit version */
// Adafruit_ADS1015 ads;     /* Use this for the 12-bit version */

LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
byte delta[8] =
{
  B00000,
  B00100,
  B00100,
  B01010,
  B01010,
  B10001,
  B11111,
  B00000
};
const int analogInPin = A0;  // Analog input pin that the potentiometer is attached to
const int analogOutPin = 9; // Analog output pin that the LED is attached to

long sensorValue = 0;        // value read from the pot
int outputValue = 0;        // value output to the PWM (analog out)
int outputValueMin = 1000;
int outputValueMax = -1000;
int i=0;
int c=0;
int x=0;
int z=0;
int y=0;
int tempCount=0;
float av=0;
long totalAdjustedOutputValue=0;
int adjustedOutputValue = 0;
float hrs=1;
void setup() 
{
  lcd.createChar(1, delta);
  lcd.begin(20, 4);
  lcd.setCursor(2,0);  
  lcd.write(1);
    // locate devices on the bus
  Serial.print("Locating devices...");
  sensors.begin();
  Serial.print("Found ");
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(" devices.");

  // report parasite power requirements
  Serial.print("Parasite power is: "); 
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");

  if (!sensors.getAddress(pipeProbe01, 0)) Serial.println("Unable to find address for Device 0"); 
  // set the resolution to 9 bit (Each Dallas/Maxim device is capable of several different resolutions)
  sensors.setResolution(pipeProbe01, 12);
 
  Serial.print("Device 0 Resolution: ");
  Serial.print(sensors.getResolution(pipeProbe01), DEC); 
  Serial.println();
  delay (2000);
  
    pinMode(8, INPUT_PULLUP);   // Momentary switch
    ads.begin();

    
  // initialize serial communications at 9600 bps:
    Serial.begin(9600);
  sensors.requestTemperatures(); // Send the command to get temperatures
  printTemperature(pipeProbe01);
  previousTempC = tempC;
}

void loop() 
{ 
  y=5;
  z=10;
  if (adjustedOutputValue > 1000)
  {
    while (y<z)
    {
    tone (11,200*y,50);
    y++;
    delay(50);
    }
  }

  sensors.requestTemperatures(); // Send the command to get temperatures
  printTemperature(pipeProbe01);
  
  int pushButton = digitalRead(8);
    if (pushButton == HIGH) 
    {
      Serial.print("Switch Off");
    } else
    {
      Serial.print("Switch On");
      x = outputValue;
      outputValueMin = 10000;
      outputValueMax = -10000;
      c = 0;
      totalAdjustedOutputValue =0;
      av=0;
      hrs=0;
    }
      Serial.println(" ");
  int16_t adc0, adc1, adc2, adc3;

 // adc0 = ads.readADC_SingleEnded(0);
 // Serial.print("AIN0: "); Serial.println(adc0);
 // Serial.println(" ");
     
 i=0;
 c++;
 sensorValue =0;
 while (i<30)
 {
  adc0 = ads.readADC_SingleEnded(0);
  i++;
  sensorValue = sensorValue + adc0;
  delay (50);
 }
  sensorValue = sensorValue/30;
 // sensorValue = adc0;
  // map it to the range of the analog out:
  outputValue = map(sensorValue, 0, 21879, -2008,  2000);
  adjustedOutputValue = outputValue - x;
  // change the analog out value:
  analogWrite(analogOutPin, outputValue);

  // print the results to the serial monitor:
  Serial.print("sensor = ");
  Serial.print(sensorValue);
  Serial.print("\t output = ");
  Serial.println(adjustedOutputValue);

if (adjustedOutputValue > outputValueMax)
{
  outputValueMax = adjustedOutputValue;
}
if (adjustedOutputValue < outputValueMin)
{
  outputValueMin = adjustedOutputValue;
}
  
  totalAdjustedOutputValue = adjustedOutputValue + totalAdjustedOutputValue;
  av = 1.000*totalAdjustedOutputValue/c;
  hrs = c*2.00/60.00/60.00;

if (tempCount ==5)
{
  deltaTempC = tempC - previousTempC;
  previousTempC = tempC;
  tempCount=0;
}
  lcd.setCursor(0,0);  
  lcd.print("                    ");   
  lcd.setCursor(0,1);  
  lcd.print("                    "); 
  lcd.setCursor(0,2);  
  lcd.print("                    ");   
  lcd.setCursor(0,3);  
  lcd.print("                    ");  
   
  lcd.setCursor(0,0);    
  lcd.print("P:");     
  lcd.setCursor(2,0);         
  lcd.print(adjustedOutputValue);  
    lcd.setCursor(11,0); 
    lcd.write(1);
    lcd.print("T:");    
    lcd.setCursor(14,0);    
    lcd.print(deltaTempC);         
  lcd.setCursor(0,1); 
  lcd.print("Max:");  
  lcd.setCursor(4,1);       
  lcd.print(outputValueMax);     
  lcd.setCursor(0,2); 
  lcd.print("Min:");  
  lcd.setCursor(4,2);        
  lcd.print(outputValueMin);    
  lcd.setCursor(0,3);    
  lcd.print("Secs:"); 
  lcd.setCursor(5,3);            
  lcd.print(c*2); 
  lcd.setCursor(11,3); 
  lcd.print("hrs:");  
  lcd.setCursor(15,3);            
  lcd.print(hrs,2);  
  lcd.setCursor(11,1);    
  lcd.print("A:"); 

  lcd.setCursor(13,1);  
  lcd.print(av,3);

  lcd.setCursor(11,2);    
  lcd.print("T:");     
  lcd.setCursor(13,2);    
  lcd.print(tempC);  
  lcd.setCursor(18,2);    
  lcd.print("\337C"); 
     
  delay(2000);
  tempCount++;
}
void printTemperature(DeviceAddress deviceAddress)
{
  tempC = sensors.getTempC(deviceAddress);
  Serial.print("Temp C: ");
  Serial.print(tempC);
  Serial.print(" Temp F: ");
  Serial.println(DallasTemperature::toFahrenheit(tempC)); // Converts tempC to Fahrenheit
}  

Step 5: Real Life Testing

The hydrometer was tested by hooking it up to my Air Still. It worked fairly well and was very reliable, producing results to +- 5% accuracy. Based on these results, eventually it was eventually incorporated into a fully automatic NanoStillery™. Want to know what a NanoStillery is? Next instructable coming soon!

Please in the competitions - top right - Thanks!
Circuits Contest 2016

Participated in the
Circuits Contest 2016

Epilog Contest 8

Participated in the
Epilog Contest 8