Arduino strength of distillate based on vapor temperature

Hello, my name is Lloyd and I am an American living in China for many years. I've designed, produced and shipped many, many tons of distilling equipment and sold it worldwide, mostly to the US. My market ranges from the hobby distiller hiding his still in his garage to huge stills that power major distilleries (the home market is most of my business).

While operating a still, the distillate collected drops slowly in strength throughout the run until the alcohol inside the boiler is depleted. For centuries we distillers have relied on glass hydrometers (more precisely, alcoholmeters) that float in the distillate to measure the strength of the alcohol being collected as that helps to determine the "cuts" - the good stuff from the bad stuff. We call the cuts the "Heads, hearts and tails". We are after the Hearts, the rest taste bad.

There is a correlation between vapor temperature (as measured at the top of the still head) and the strength of the alcohol (Alcohol By Volume or ABV) that is collected. Basically, the higher the vapor temperature the less alcohol is in the vapor as alcohol has a lower boiling point than water.
78.1C vapor temp is 95.5% alcohol while 98C vapor is 19% alcohol. Unfortunately, the values are not linear and the equation to get the values is quite complex. On a graph, its roughly the shape of one side of a bell.

The first great step in automation is the need to digitally display the ABV from its vapor temperature and I believe this is do-able. To my knowledge, and I'm considered by many as a distilling expert, this has not been done.

An R3 clone is quite cheap and readily available here, as is a 4 line LCD display with backpack.
Since I have VERY limited experience in coding anything I am willing to pay someone to help me get this project fulfilled. Perhaps even a partnership if you prefer that to money up front.

The vapor needs to be read from a common sensor, such as a Dallas one-wire, and the LCD needs to display that temperature and its ABV from a look up table. The table is from 100 degrees C to 78.1 C, in 0.1 degree increments for a total of about 220 entries (each value is 4 digits, for example, 56.3 - the value depending on the vapor temperature). I can supply the table data values.

I've tried to describe the project in the greatest possible detail but if you don't understand something please ask.

I should be able to prototype and test this on a working still and I've made a few simple Arduino projects. My ability to write code is at the level of a beginner and, honestly, much of it is over my head. To my shame, if I could not modify someone else's code then I could do nothing Arduino at all.

It is common for me to spend a lot of time and money to develop a product because I tend to vend hundreds or thousands of copies of it over time. My track record is roughly 70%. Not all of my projects are commercial successes but the good ones far outweigh the failures.

I believe this is a good one and I'm usually right but I'm willing to take all of the financial risk in case I'm wrong. My products are almost exclusively hardware like stainless steel and copper. Anything electronic is quite new to my product offering.

Thanks for reading to the end,

Lloyd

Edit: corrected a silly typo.

78.1C vapor temp is 95.5% alcohol while 98C vapor is 19% alcohol.

So, a range of about 20 degrees C...

Unfortunately, the values are not linear and the equation to get the values is quite complex. On a graph, its roughly the shape of one side of a bell.

But, at 0.1 degree intervals, that is only 200 points in a look-up table.

The vapor needs to be read from a common sensor, such as a Dallas one-wire,

Vapor temperature, you mean, right?

The table is from 0 degrees C to 78.1 C, in 0.1 degree increments for a total of about 220 entries

Your calculator needs new batteries. The range from 0 to 78.1 seems to miss the real interesting part.

each value is 4 digits, for example, 56.3

Those 4 digits being '5', '6', '3', and ?

Saving the value as 563 would need only two bytes.

If you wanted to supply an Arduino, a temperature sensor, an LCD, and to clarify the requirements and supply the table of values, I could write the code needed to make it work and return the hardware completely assembled. PM me, if interested.

Hi Paul,

Thanks for the response and to clarify... ethanol alcohol boils within a range but we only measure between 78.1C (produces 95.5% alcohol) and 100C (produces 0% alcohol).

Yes, we want to read the vapor temperature.

My inability to provide proper jargon should probably have stated 4 "places" instead of "4 digits"? if you count the decimal point.

Hum, hadn't thought to supply the parts for the programmer to write the code but that's do-able for me.

Kinda makes good sense, actually.

....as long as I don't need to send the still, too :slight_smile:

Oh, you really can't return the hardware to me in China as the postage and custom's fees would far outweigh the value of the hardware. Cheap to send out from here, expensive to receive kind of thing.

....as long as I don't need to send the still, too

I'm sure that I can find another source of temperatures in the range 75 to 100C.

Oh, you really can't return the hardware to me in China as the postage and custom's fees would far outweigh the value of the hardware. Cheap to send out from here, expensive to receive kind of thing.

So, get two of each. When I have the same LCD you have wired in such a way that I can write to it, and have the same temperature sensor that you have, I can develop the code and supply wiring diagram and photos. And code, of course.

Lloyd778:
My inability to provide proper jargon should probably have stated 4 "places" instead of "4 digits"? if you count the decimal point.

He's just being grouchy.

Surely you have digital themometer - ah, you want the arduino to do conversion to partial pressure.

This is totally do-able, no trouble at all.

I'm sure that I can find another source of temperatures in the range 75 to 100C.

I have a stove and a saucepan :slight_smile: .

If you have done a few arduino projects, which bit do you feel not capable of doing? If it's just the interpolation on your curve, I could bash out something for you. There's libraries to drive LCDs, and reading a temperature sensor - well, hopefully that's just an analog input with a correction to get it to scale.

Ok man, here you go.

Just a demo that spits output to the serial port so you can view it with the monitor.

I'm assuming that if you are using an LCD, then it understands regular ASCII characters and will accept a nul-terminated C string.

// need 5 characters - 4 characters and the null terminator
char lcd_output[5];

// the conversion table is a series of pairs of float.
// first value is the temp, second value is the ABV
// the first values (temperatures) **MUST** be in ascending order
// however, they don't have to be consecutive

float conversionTable[][2] = 
{
  { 5, 90},
  { 15, 80},
  { 40, 40},
  { 60, 10},
  { 75, 0}
};

const int LEN = sizeof(conversionTable) / sizeof(*conversionTable);


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

  Serial.begin(9600);
  while(!Serial);
  
  Serial.print("Starting demo in:");
  for(int i = 3;i>0;i--) {
    Serial.print(' ');
    Serial.print(i);
    delay(1000);
  }
  Serial.println("\nOK! Beginning demo!");
  
  float temp;
  
  // we will simulate a range of temperatures being read from the sensor
  
  for(temp = -5; temp <= 85; temp += 5) {
    Serial.print("Calculating ABV at ");
    Serial.print(temp);
    Serial.print(" deg C.");
    
    // this function computes the ABV. A result below 0 or 100 or higher
    // indicates that the value is out of range
    
    float abv = calculateABV(temp);
    Serial.print(" Computed ABV is ");
    Serial.print(abv);
    Serial.print(".");
    
    // this function works out what to display on the LCD.
    // if the value is out of range, then we will see 
    // -LO- or -HI- rather than a number
    
    pct2string(abv, lcd_output);
    
    Serial.print(" LCD output is [");
    Serial.print(lcd_output);
    Serial.println("].");
    
    
  }
  
}

void loop() {
}

float calculateABV(float temp) {
  // is the temp less than the first element of our table?

  if(temp < conversionTable[0][0]) return 1000; // boy, that's strong stuff!
  
  if(temp > conversionTable[LEN-1][0]) return -1; // no alcohol at all

  // ok. lets find the pair of entries n the table between which our
  // temperature lies
  
  int lo = 0;
  int hi = LEN-1;
  
  while(hi-lo > 1) {
    int mid = (hi+lo)/2;
    
    if(temp >= conversionTable[mid][0]) {
      lo = mid;
    }
    else {
      hi = mid;
    }
  }
  
  if(conversionTable[hi][1] == conversionTable[lo][1]) {
    return conversionTable[hi][1];
  }

  // aaaand interpolate

  return conversionTable[lo][1] 
    + (conversionTable[hi][1]-conversionTable[lo][1])
    * (temp - conversionTable[lo][0]) / 
    (conversionTable[hi][0]-conversionTable[lo][0]);
}

void pct2string(float pct, char *str) {
  if(pct < 0) {
    strcpy(str, "-LO-");
    return;
  }
  if(pct >= 100) {
    strcpy(str, "-HI-");
    return;
  }
  
  int rounded = (int)(pct*10+.5);
  
  str[0] = (rounded/100) % 10 + '0';
  str[1] = (rounded/10) % 10 + '0';
  str[2] = '.';
  str[3] = (rounded) % 10 + '0';
  str[4] = '\0';
}

Thank you, PaulMurrayCbr. PM sent.

I'll be away for the next few days but hope to get started on this project as soon as I return.

Best,

Lloyd

Lloyd778:
78.1C vapor temp is 95.5% alcohol while 98C vapor is 19% alcohol.

That numbers are no constants for every location on the world. The boiling point of liquids or liquid mixtures is heavily dependent on the air pressure. And the air pressure is heavily dependent on the height above sea level. So if you live at the sea shore the boiling temperatures are different than when living on a mountain.

I.e. pure water at sea level boils at 100°C, but at 1400 m height above sea level the boiling point of pure water is 95°C.

Calculations with Arduino 'float' numbers are not a big problem, even in complex calculations, as long as you don't need to calculate more than 6 significant digits.

You are correct, jurs, and the only information I can locate to compensate for altitude is:

A * F where A= altitude in meters and F= 0.00031 (the correction for ethanol).

This formula, I'm led to believe, is more accurate at the higher proof end of the scale while 0.00034 gives a more accurate correction at the low end when the boiler is almost depleted of alcohol.

We don't much care for high accuracy when the distillate collected is below ~70% ABV as most of the "good" alcohol has already been collected.

This is what I have so far. I do not understand much of it and I have no idea yet how to use the array.

Still left to do is to replace the "?" with the value on the look-up table (array) and find a way to compensate for the user's altitude. I suppose another pot could be used and tie that to another array for altitude.
Zero meters to 2,000 meters in 20 meter "steps" is about 100 entries which is do-able. 2,000 meters should cover almost everyone?

While the alarm is not really needed, that feature could be re-purposed for the altitude input and display.
Currently the "alarm" (AKA RelayPin) just lights an LED but it could buzz a buzzer or signal a relay to activate. I had thought it useful to set for 70C on startup and that would let me know to reduce the boiler power before collection begun. Then set to, say, 79C and my 6 plate still would be sucking tails. But 0.0 to 100.0 in 0.1 resolution would be way too many steps for a pot.

#include <OneWire.h>
#include <Wire.h>  // Comes with Arduino IDE
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

int rly=12;
#define RelayPin 12

int DS18S20_Pin = 10; //DS18S20 Signal pin on digital 10
double Setpoint;  // double
double Input, Output;

int sensorPin = A0; // select the input pin for the potentiometer
int sensorValue = 0; // variable to store the value coming from the sensor
int mys=0;

const int numReadings = 30;

double readings[numReadings]; // the readings from the analog input
int index = 0; // the index of the current reading
double total = 0; // the running total
double input = 0; // the average

int inputPin = A0;

//Temperature chip i/o
OneWire ds(DS18S20_Pin); // on digital pin 10

float conversionTable[][2] =
{
 
  {78.1, 95.5},
  {78.2, 92},
  {78.3, 90.2},
  {78.4, 89},
  {78.5, 88.5},
  {78.6, 88},
  {78.7, 87.5},
  {78.8, 87.1},
  {78.9, 86.7},
  {79, 86.4},
  {79.1, 85.9},
  {79.2, 85.4},
  {79.3, 85.1},
  {79.4, 84.8},
  {79.5, 84.6},
  {79.6, 84.3},
  {79.7, 84.1},
  {79.8, 83.8},
  {79.9, 83.6},
  {80, 83.4},
  {80.1, 83.2},
  {80.2, 83},
  {80.3, 82.7},
  {80.4, 82.4},
  {80.5, 82.2},
  {80.6, 81.9},
  {80.7, 81.6},
  {80.8, 81.4},
  {80.9, 81.2},
  {81, 80.8},
  {81.1, 80.4},
  {81.2, 80.3},
  {81.3, 80.1},
  {81.4, 79.9},
  {81.5, 79.8},
  {81.6, 79.6},
  {81.7, 79.5},
  {81.8, 79.3},
  {81.9, 79.1},
  {82, 79},
  {82.1, 78.8},
  {82.2, 78.7},
  {82.3, 78.5},
  {82.4, 78.3},
  {82.5, 78.2},
  {82.6, 78},
  {82.7, 77.9},
  {82.8, 77.7},
  {82.9, 77.5},
  {83, 77.4},
  {83.1, 77.2},
  {83.2, 77},
  {83.3, 76.9},
  {83.4, 76.7},
  {83.5, 76.5},
  {83.6, 76.4},
  {83.7, 76.2},
  {83.8, 76},
  {83.9, 75.9},
  {84, 75.7},
  {84.1, 75.5},
  {84.2, 75.4},
  {84.3, 75.2},
  {84.4, 75},
  {84.5, 74.8},
  {84.6, 74.7},
  {84.7, 74.5},
  {84.8, 74.3},
  {84.9, 74.1},
  {85, 73.9},
  {85.1, 73.7},
  {85.2, 73.6},
  {85.3, 73.4},
  {85.4, 73.2},
  {85.5, 73},
  {85.6, 72.8},
  {85.7, 72.7},
  {85.8, 72.5},
  {85.9, 72.3},
  {86, 72.1},
  {86.1, 71.9},
  {86.2, 71.7},
  {86.3, 71.5},
  {86.4, 71.3},
  {86.5, 71.1},
  {86.6, 70.9},
  {86.7, 70.7},
  {86.8, 70.5},
  {86.9, 70.3},
  {87, 70.1},
  {87.1, 69.8},
  {87.2, 69.6},
  {87.3, 69.4},
  {87.4, 69.2},
  {87.5, 69},
  {87.6, 68.8},
  {87.7, 68.5},
  {87.8, 68.3},
  {87.9, 68.1},
  {88, 67.9},
  {88.1, 67.6},
  {88.2, 67.4},
  {88.3, 67.1},
  {88.4, 66.9},
  {88.5, 66.7},
  {88.6, 66.4},
  {88.7, 66.1},
  {88.8, 65.7},
  {88.9, 65.4},
  {89, 65},
  {89.1, 64.5},
  {89.2, 64},
  {89.3, 63.7},
  {89.4, 63.4},
  {89.5, 63},
  {89.6, 62.7},
  {89.7, 62.4},
  {89.8, 62.1},
  {89.9, 61.7},
  {90, 61.4},
  {90.1, 60.9},
  {90.2, 60.4},
  {90.3, 59.7},
  {90.4, 59.1},
  {90.5, 58.8},
  {90.6, 58.4},
  {90.7, 58.1},
  {90.8, 57.7},
  {90.9, 57.4},
  {91, 57},
  {91.1, 56.7},
  {91.2, 56.3},
  {91.3, 56},
  {91.4, 55.6},
  {91.5, 55.2},
  {91.6, 54.8},
  {91.7, 54.5},
  {91.8, 54.1},
  {91.9, 53.7},
  {92, 53.3},
  {92.1, 52.9},
  {92.2, 52.5},
  {92.3, 52.1},
  {92.4, 51.7},
  {92.5, 51.3},
  {92.6, 50.9},
  {92.7, 50.5},
  {92.8, 50},
  {92.9, 49.6},
  {93, 49.2},
  {93.1, 48.7},
  {93.2, 48.3},
  {93.3, 47.7},
  {93.4, 47.2},
  {93.5, 46.8},
  {93.6, 46.3},
  {93.7, 45.8},
  {93.8, 45.4},
  {93.9, 44.9},
  {94, 44.4},
  {94.1, 43.9},
  {94.2, 43.4},
  {94.3, 42.9},
  {94.4, 42.4},
  {94.5, 41.8},
  {94.6, 41.3},
  {94.7, 40.8},
  {94.8, 40.2},
  {94.9, 39.7},
  {95, 39.1},
  {95.1, 38.5},
  {95.2, 38},
  {95.3, 37.4},
  {95.4, 36.8},
  {95.5, 36.2},
  {95.6, 35.6},
  {95.7, 35},
  {95.8, 34.3},
  {95.9, 33.7},
  {96, 33},
  {96.1, 32.4},
  {96.2, 31.7},
  {96.3, 31},
  {96.4, 30.3},
  {96.5, 30},
  {96.6, 29.8},
  {96.7, 29.5},
  {96.8, 28.8},
  {96.9, 28},
  {97, 27.3},
  {97.1, 26.5},
  {97.2, 25.7},
  {97.3, 24.9},
  {97.4, 24},
  {97.5, 23.2},
  {97.6, 22.3},
  {97.7, 21.5},
  {97.8, 20.6},
  {97.9, 19.8},
  {98, 19},
  {98.1, 18.2},
  {98.2, 17.4},
  {98.3, 16.6},
  {98.4, 15.8},
  {98.5, 15},
  {98.6, 14},
  {98.7, 13},
  {98.8, 12},
  {98.9, 11},
  {99, 10},
  {99.1, 9},
  {99.2, 8},
  {99.3, 7},
  {99.4, 6},
  {99.5, 5},
  {99.6, 4},
  {99.7, 3},
  {99.8, 2},
  {99.9, 1},
  {100, 0}
};

void setup(void) {
  pinMode(rly, OUTPUT);  
  digitalWrite(rly,LOW);
  lcd.begin(20,4);//(LCD_WIDTH, LCD_HEIGHT,1);
  lcd.setCursor(0,1);
  lcd.print("The eParrot v1.0 by");
  lcd.setCursor(0,3);
  lcd.print("    VISIONSTILLS");
  delay(4000);
  lcd.clear();
  pinMode(12, OUTPUT);

}

  void loop(void) {

mys = analogRead(A0);///7.42+75;
  mys=mys/8;  // 5.12 kinda works ...   50K pot.  at mys/10  + 35 works well
   mys=mys+18;  //displays up to 100.00 (.00 do not work)but a functional 99.9 or 100.0 without the .00 would be better

Setpoint = mys;
float temperature = getTemp();//*9/5+32;
Input =temperature;// total / numReadings; 
lcd.setCursor(0,0); 
lcd.print(" ? %   "); lcd.print(temperature);  lcd.print("  ");
lcd.setCursor(15,0); lcd.print(Setpoint);
lcd.setCursor(18,0); lcd.print(" C ");
lcd.setCursor(0,1);
lcd.print(" ABV");
lcd.print("   VAPOR");
lcd.print("   ALARM");
lcd.setCursor(0,3);
lcd.print("   DIGITAL PARROT");

if(temperature < mys) digitalWrite(RelayPin,LOW);
else digitalWrite(RelayPin,HIGH);

{

  }
}
float getTemp(){
//returns the temperature from one DS18S20 in DEG Celsius

byte data[12];
byte addr[8];

if ( !ds.search(addr)) {
//no more sensors on chain, reset search
ds.reset_search();
return -1000;
}

if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.println("CRC is not valid!");
return -1000;
}

if ( addr[0] != 0x10 && addr[0] != 0x28) {
Serial.print("Device is not recognized");
return -1000;
}

ds.reset();
ds.select(addr);
ds.write(0x44,1); // start conversion, with parasite power on at the end

delay(200); //1000

byte present = ds.reset();
ds.select(addr);
ds.write(0xBE); // Read Scratchpad
for (int i = 0; i < 9; i++) { // we need 9 bytes
data[i] = ds.read();
}

ds.reset_search();

byte MSB = data[1];
byte LSB = data[0];

float tempRead = ((MSB << 8) | LSB); //using two's compliment
float TemperatureSum = tempRead / 16;

return TemperatureSum;

}

digital parrot.png

float conversionTable[][2] =

You could make that table half as large by storing the values as ints, scaled by a factor of 10.

Getting the float back is trivial.

  {

  }

Useful?

  float temperature = getTemp();//*9/5+32;

You get the temperature here. You use this to determine where in the lookup table the temperature is, and then use the other value for the alcohol level.

PaulMurrayCbr provided a function, calculateABV(), in reply #6 that does just that.

I sent several PM's to folks here that I hoped could help with my project, and be paid for their time and expertise, but not one responded so I had to go to Freelancer where I got many good offers to look over.

I thank you all for your attention but as a coding newbie (and an old fart that has been around for a long while) I offer you all a suggestion....

Stop talking down to people that admit they are new and don't grasp the intricacies. This is the board to go to when you want to hire professional help? Is it or is it not? If it is not then please provide the link.

Many established forums have the same problem; "I was beat on the head by senior members when I was new so now it's my turn to beat down anyone that is below me".

Not saying that is the case here, it just feels like it.
I find myself afraid to describe my project as best as I'm able.

I never asked for anything free. I never attacked anyone that was "grumpy".

I am new here. Is this the normal way that new people are treated?

@PaulS, with 60,000+ posts that's the best you could do? You see the tree and completely miss the forest.

@jurs was the only one that actually "got it".

Lloyd778:
Stop talking down to people that admit they are new and don't grasp the intricacies. This is the board to go to when you want to hire professional help? Is it or is it not? If it is not then please provide the link.

Lloyd:
I'm on the other end of the equation (offering help & services) and I agree with you completely. The animosity on this forum has turned me off enough times that I've seriously considered just ignored it. However, I've been able to meet some very interesting people and do fun work, so I stick around.

There seems to be a personality type that enjoys "being right" whether or not they really are and simply like jumping all over people. It's gotten annoying enough that instead of posting simple suggestions that can help someone avoid paying for services, I just immediately send Private Messages.

I hope the board can continue to attract new people and be useful to them or it will surely die.

BTW: I thought your project was really interesting especially as I make wine as a hobby. Unfortunately, I was simply too busy to engage in it.

Well,,, I'm probably just being grouchy because someone here didn't take my hand and my money and lead me through a problem that I just cannot get my head around.

My area of expertise has brought incredible success over the years and cracking the digital alcoholmeter puzzle is outside of my comfort zone. I'm far more adept at constructing thousands of small, homeowner size and hundreds of giant, industrial sized stills than programming an Arduino.

But it feels so good when I burn a sketch that works.

OK, I just finished up a project I was working on, so if you still need help, PM me and I'll send you my email.

Hi Lloyd, I worked for 20+ years in a small molecule pilot plant for a large pharma located in Indianapolis. I worked to place sensors in situ to monitor processes and distillations were always a favorite operation that I enjoyed to optimize. I would enjoy the challenge of a collaboration. I have worked on Arduino projects since the beginnings. I am familiar with both sides of what you need both the operation and the automation.

Send me an email at tyhuffman@mac.com

I and I believe many others are interrested in this automatic distilating process with arduino,
could it be possible to explain here the progress of the scketch. which are made for Lioyd

I found one similair on

http://forum.arduino.cc/index.php?topic=168571.0

and

http://thuisdestilleren.messageboard.nl/forum/viewtopic.php?f=13&t=445

it's in dutch but the sketch is there for arduino

I've been working on a similar project and am curious if anyone has made headway with the atmospheric compensation for the dewpoint measurement in order to get a more accurate value for the distillate mixture ratio.