Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / IoT / Arduino

Arduino Home Distillery

5.00/5 (14 votes)
18 Dec 2021CPOL18 min read 35.6K   351  
Distil hand-sanitizer and other social lubricants using an Arduino
Create the essential tool you will need to survive the social isolation of Covid-19 (and have plenty of hand-sanitizer to keep your home clean).

Update: 2021/12/18 - Heat Control improved Heat-Control feature

Introduction

The process of distillation was known to ancient Mesopotamia over 3000 years ago and is still an important part of our times. Distillers ferment liquids, then separate the alcohol content from water by raising the temperature of their product above the boiling point of alcohol but below the boiling point of water, collect the alcohol steam and condense it back into liquid. Industries have giant vats of fermented grains mixed with sugar and yeast. They carefully control the temperature and collect what they bottle and sell using this same procedure. It's the reason your great-aunt still dances on New Year's Eve and, if you're like me, the reason you asked your boss's pretty daughter to marry you at the office christmas party, all those years ago. There are plenty of websites and YouTube videos you can watch to get you started on building your own still so I won't dwell on that too much but will rather tell you about the Arduino project I built to help me run mine.

How to Build a Still

Get a cooking pot, a hot plate, a bucket of cold water and some copper tubing. Plug the copper to a hole in the lid of your pot and dive it down through the bucket, then collect the runny stuff that comes out the bottom.

That's the gist of it, but here are some resources to help you along.

Image 1

Distillation

According to this Wikipedia article on Moonshine, "methanol is not produced in toxic amounts by fermentation of sugars from grain starches" but is only a problem when unscrupulous distillers adulterate their product with Methanol to 'improve' its potency. Most of the information about home distillation you will find on the internet will inform you that because the few toxins that are produced by fermentation boil at lower temperatures than alcohol, these toxins are first to be boiled away and are therefore discarded at the beginning of a run. However, scientific research states that methanol, acetone and aldehydes (bad stuff) appear in minute amounts throughout the distillation process (home distillation or commercial) and will be present regardless. That old Tennessee HillBilly in the moonshine video linked-to above has been discarding the 'head & foreshots' before drinking the product of his still for over 40 years and he doesn't even wear glasses. Given this example, I feel it's safe to say that moonshine can clean your car's engine block and get you drunk but won't make you blind unless you spike it with your wife's nail-polish remover.... which you probably shouldn't do.

Arduino

For this project, I used the Arduino Mega 2560 microcontroller. It could potentially run on smaller boards but I didn't want to take any chances with memory issues or risk running out of control pins.

Here's a list of the necessary components:

Still

16 gallon stainless steel pot (not aluminum) $30
hot plate $25
tall/narrow bucket $8
10' of 3/8" outer-diameter copper pipe with attachments $30
Total $100.00

Arduino

Arduino 2560 microcontroller $15
   
YZC-133 3Kg Scale Electronic Load Cell Weighing Sensor $5
1 TM7711 electronic load cells $10
LCD1604 $10
2x DS18B20 Temperature Sensors $7
4x4 keypad $3
5KΩ potentiometer $3
buzzer $2
SG90 9G Servo Motor $3
5V 1 Channel Relay Board Module $3
74HC595 shift register $2
LEDs, assortment of resistors, wiring, solder & iron $20
Total $90.00

Here's another short video about the build.

The most difficult and time consuming part of building this still was drilling two holes through the stainless steel lid of my pot. I had to tape down the drill's button to power it on while I held it for a total of 5+ hours per hole! (broke half a dozen bits in the process). I dented the lid with a hammer, to be able to hold the drill in place, then sat at my desk watching Season 12 of Supernatural on DVD until I finally pierced through the smallest hole before finally enlarging it with bigger bits. That done, the condenser (bucket of water) had to be reinforced with sturdy wire to make sure that the copper line (worm) always flows downwards (or you'll get something called 'blue worm', corrosion in the copper line when it doesn't dry properly in storage between runs). I said I wouldn't dwell on the actual still-building part of this project so... we'll just move along to the Arduino stuff.

Menus

The material interface for this Still-Project consists mostly of an LCD1604 & a 4x4 button matrix(some LEDs and a buzzer) and is run by a set of menus. I've spoken about these types of menus before in a previous project called Arduino - Exercise Reps Counter with Analog Voltage Divider Button Controller. I'll summarize here to give you an idea but you should read that article if the menu-system is of any interest to you. Since Arduino microcontrollers have a limited memory capacity and creating too many string variables may overwrite the software part of your project with its own variable (string) components, it's best to avoid using strings altogether and resort to character arrays. Alternatively, you can use the F() macron which "tells the compiler that the contents of the parameter should be stored in the flash memory (program memory or PROGMEM), instead of the SRAM, hence saving you this valuable resource for better use."(techexploration.com). With these menus, however, I go one step further by forcing the microcontroller to ask for the text required to put on the LCD1604 screen. That information (each menu's heading when needed) is sent to the calling method when the LCD is drawn using F() macron, the LCD is told what to put on the screen and the string is then destroyed and resides solely in the LCD screen's memory while the microcontroller itself erases it from its own memory(except for the 16x4 character array I kept in case I decided to flash feedback onto the LCD and wanted to reset the screen to its former glory after that momentary flashing feedback inspiration but I never got around to it so... it's just there for no good reason).

To build this menu, I first 'drew' it up in a textfile using MS NotePad and produced the text shown below where you can see the relation between each menu item through their respective indentations.

Image 2

The menu items themselves are instances of the:

C
class classMenuItem
{
  public:
    classMenuItem *cParent = NULL;
    classMenuItem *cFirstChild = NULL;
    classMenuItem *cPrevSibling = NULL;
    classMenuItem *cNextSibling = NULL;
    enuMenuAction eMenuAction = mnuAction_NoAction;
    byte Index;
};

that then each point to each other in a 'tree-like' assembly which is composed during the setup() call to:

C
void Menus_init()
{
  // menus
  cMenus[0].cFirstChild = &cMenus[1];
  cMenus[0].cNextSibling = NULL;
  cMenus[0].eMenuAction = mnuAction_NoAction;

  //    case 1 : "Modes"
  cMenus[1].cFirstChild = &cMenus[2];
  cMenus[1].cNextSibling = &cMenus[30];
  cMenus[1].eMenuAction = mnuAction_NoAction;

  //    case 2: return "warm up";
  cMenus[2].cFirstChild = &cMenus[3];
  cMenus[2].cNextSibling = &cMenus[5];
  cMenus[2].eMenuAction =   mnuAction_NoAction;

  //    case 3: "Warm-up select"
  cMenus[3].cFirstChild = NULL;
  cMenus[3].cNextSibling = &cMenus[4];
  cMenus[3].eMenuAction =  mnuAction_Select_WarmUp;

  //    case 4: "Warm-up - MaxTemp"
  cMenus[4].cFirstChild = NULL;
  cMenus[4].cNextSibling = NULL;
  cMenus[4].eMenuAction =   mnuAction_WarmUp_MaxTemp_Set;

  //    case 5: "head"
  cMenus[5].cFirstChild = &cMenus[6];
  cMenus[5].cNextSibling = &cMenus[10];
  cMenus[5].eMenuAction =   mnuAction_NoAction;

  //    case 6: "head - select"
  cMenus[6].cFirstChild = NULL;
  cMenus[6].cNextSibling = &cMenus[7];
  cMenus[6].eMenuAction =   mnuAction_Select_Head;

  //    case 7: "head - auto-hearts"
  cMenus[7].cFirstChild = &cMenus[8];
  cMenus[7].cNextSibling = NULL;
  cMenus[7].eMenuAction =  mnuAction_NoAction;

  //    case 8: "head - auto-hearts - Toggle"
  cMenus[8].cFirstChild = NULL;
  cMenus[8].cNextSibling = &cMenus[9];
  cMenus[8].eMenuAction =   mnuAction_Head_AutoHearts_Toggle;

  //    case 9: "head - auto-hearts - Max Volume"
  cMenus[9].cFirstChild = NULL;
  cMenus[9].cNextSibling = NULL;
  cMenus[9].eMenuAction =   mnuAction_Head_AutoHearts_MaxVolume;

  //    case 10: "hearts"
  cMenus[10].cFirstChild = &cMenus[11];
  cMenus[10].cNextSibling = &cMenus[24];
  cMenus[10].eMenuAction =   mnuAction_NoAction;

  //    case 11: "hearts - select"
  cMenus[11].cFirstChild = NULL;
  cMenus[11].cNextSibling = &cMenus[12];
  cMenus[11].eMenuAction =   mnuAction_Select_Hearts;

  
///////////////////  content excised for brevity //////////////////////////


  //  case 89 : Light Toggle
  cMenus[89].cFirstChild = NULL;
  cMenus[89].cNextSibling = &cMenus[90];
  cMenus[89].eMenuAction = mnuAction_Light_Toggle;

  //  case 90 : Shine Density Set
  cMenus[90].cFirstChild = NULL;
  cMenus[90].cNextSibling = &cMenus[91];
  cMenus[90].eMenuAction = mnuAction_ShineDensity_Set;

  //    case 91 : "ExitMenu"
  cMenus[91].cFirstChild = NULL;
  cMenus[91].cNextSibling = NULL;
  cMenus[91].eMenuAction = mnuAction_ExitMenu;

  // set cParent & cPrevSibling
  for (int intMnuCounter = 0; intMnuCounter < bytMenus_Num; intMnuCounter ++)
  {
    cMenus[intMnuCounter].Index = (byte)intMnuCounter;
    classMenuItem *cMnu = &cMenus[intMnuCounter];
    if (cMnu->cFirstChild != NULL)
    {
      classMenuItem *cChild = cMnu->cFirstChild;

      do
      {
        cChild->cParent = cMnu;
        cChild = cChild->cNextSibling;
      } while (cChild != NULL);
    }

    if (cMnu->cNextSibling != NULL)
      cMnu->cNextSibling->cPrevSibling = cMnu;
  }

  /*
    Serial.println(F("Menus_init() debug for-loop start"));
    for (int intCounter = 0; intCounter < bytMenus_Num; intCounter ++)
    debug_Print_Menu(intCounter);

    Serial.println(F("Menus_init() debug for-loop end"));
    //*/
}

Once they are assembled together (at the end of the code shown above), for-next and do-while loops are used to create the reverse links that point back upwards from child-to-parent (rather than risk programmer error incorrectly entering the same data in reverse a second time). A pointer is used to keep track of where in the menu the user is so that the LCD1604 display may reflect what the user-expects to see when scrolling up/down backwards or forwards along the menu pointers.

It's not the easiest way to assemble a menu but after building a few of these, you get more comfortable with them and can add/change things without too much difficulty.  However, I do plan on writing a C# app that will better interface with the Arduino developer to graphically build the Arduino menu and then spit-up the Arduino code needed to implement it as designed.... when I get around to it.

When the user makes an 'end-selection', the following method uses a switch-case to redirect program flow to the appropriate method call.

C
void mnuSelect()
{
  switch (cMnu_Selection->eMenuAction)
  {
    case mnuAction_WarmUp_MaxTemp_Set:
      {
        WarmUp_MaxTemp_Set();
      }
      break;

    case   mnuAction_Head_AutoHearts_Toggle:
      {
        bolHead_AutoHearts = !bolHead_AutoHearts;
        EEPROM_Head_AutoHearts_Toggle_Write();
        mnuDraw();
      }
      break;

    case mnuAction_Head_AutoHearts_MaxVolume:
      {
        Head_AutoHearts_MaxVolume_Set();
      }
      break;

    case mnuAction_Hearts_HeatControls_Toggle:
      {
        bolHearts_HeatControl_Toggle = !bolHearts_HeatControl_Toggle ;
        EEPROM_Hearts_HeatControl_Toggle_Write();
        mnuDraw();
      }
      break;
//                                                              excised for brevity

    case mnuAction_NoAction:
      {
        cMnu_Current = cMnu_Selection;
        cMnu_Selection = cMnu_Current->cFirstChild;
        mnuDraw();
      }
      break;

  }
}

All of the menu actions are listed in a single enumerated list.

4x4 Button Matrix

The button controller is a 4x4 button matrix. Traditionally, back in the olden times, people used to connect their 4x4 button controllers to their microcontroller using 8 friggin' pins. They then polled this way and that by setting half the pins High and the other Low and then doing it again in reverse. This abominable practise is anathema to public health (there is a by-law in Hypochondria City of Faint County that legislates against this horrid practise) and must be discouraged as I fortuitously explain in my article Matrix Voltage Divider Button Calculator for Microcontrollers.

"One pin to rule them all!" that guy in the video says.

The way the button controller works with only one Arduino pin is by soldering the appropriate resistors to the 8 button matrix pins and providing VCC to half of them, a single analog pin can be used to detect the voltage level at the output. When this is configured properly, each button redirects current through different resistor combinations that create a voltage-divider which is unique to that button, thereby creating a unique voltage signature which the microcontroller can detect. Since each button creates a unique voltage, the microcontroller can then identify which button is pressed. There is no need to connect 8 pins but rather only 1. There is no need to set certain pins as output and others input to test and repeat the process in reverse. There is no need to use the keypad.h library (which takes its own good ol' time to do the same thing for you). You only need query your one single solitary analog pin and if-else your way button-matrix dominance. There is, of course, a practical limit to the Arduino's ability to detect voltage differences since Arduino reports a voltage ranging from 0-5Volts with an integer value that ranges from 0 to 1024 thereby providing a 5mV precision in its voltage measurements. Give yourself a few millivolts to spare and you're good to go.

If you ever plan on using a 4x4 button matrix, I strongly recommend you have a look at my article because the old-way is soooo passée.

Thermometers

There are currently two thermometers connected to the Arduino in this project. The still only needs two: one for the temperature in the wash (the cooking pot) and another to monitor the temperature in the condenser (bucket of cold water). The DS18B20 Temperature Temp Sensors I used each have their own unique indices that are factory 'burned' in them so that they can all connect to a single pin on the microcontroller and you can poll either one (or as many as you want) by referring to them by their numeric rank. By this I mean, the one with the smallest 'factory burned index' connected to your microcontroller is referenced as the 0th thermometer and the next one in rank is 1 and so on. So, because you don't know what their actual 'factory burned index' number is, once they're connected to your microcontroller, you have to figure out which one is which yourself. In the case of this project, it was easy because the temperature of the bucket of cold water is always going to be colder than the temperature of the wash in the cooking pot. These 'reference' values (I am refraining from using the word 'index' here but really we are talking about an indexed reference value) will change if you add or replace one of your thermometers which means you may need to modify the code when (if) you do change, remove or add a thermometer to what you hath wrought before.

Here are the lines of code I used in this project to refer to these thermometers in the loop() method.

C
Thermometers.requestTemperatures();
fltTemperature_Wash = Thermometers.getTempCByIndex(0);
fltTemperature_Cond =  Thermometers.getTempCByIndex(1);

The returned values are centigrade temperatures and it's all pretty basic once you've got them sorted out.

Scales

The YZC-133 Load Cell Weighing scales are a major hassle to put together. Having ordered everything for this project on-line at the same time, I was pleased to discover that the thermometers were so easy to add to the project and then I expected the scales to be similarly easy to plug'n-play but only discovered too late that that was not the case. Research in this department would have helped me prepare ahead of time. The scales are mechanical 'potentiometer-like' blocks of aluminum that change their resistive values when they are contorted by some outside force. Since the Arduino (and all other microcontrollers that I am aware of) do not measure changes in resistance but rather only changes in voltage through their analog pins, you need to run current through these scales to produce an output voltage that will vary along with the changing resistance of the scale. Some articles I've read suggested building a Wheatstone-Bridge which could then be used to create a voltage difference that the Arduino can measure but in the case of these scales, the voltage difference being generated is in the order of nanovolts, values far below the Arduino's voltage resolution of 5mV. So, ultimately, I had to buy TM7711 electronic load cells which are essentially 'voltage signal amplifiers' built specifically for these scales.

That wasn't the only surprise. There's an actual 'build' that you have to put together yourself in order to 'contort' these blocks of aluminum and make them into usable weighing scales. Instructables had a good article which cleared it all up for me. Here is a picture they post showing that 'build' I mentioned in the previous paragraph.

Image 3

When you buy these scales, all you get is the center block with the hole and the wires sticking out of it. So I had to go to a specialty Maritime Fastening store to buy the screws needed to put this together because the wall-mounted demo screw-sizes I found at the local Home-Depot where I bought my bucket and copper line could not easily fit the test-screws into the wedge of aluminum. When I finally got the screws, I tried using some tired old rotten fence lattice wood that I found because it seemed to be the right size, but it fell apart on me. So then I tried taping it together before drilling the holes and screwing it all into place but the plastic electrical tape created a cushion between the wood rendering the scale inconsistent except in its failure to produce accurate results. New, thin, hard wood was necessary. I didn't buy mahogany or teak or anything but just some decent non-descript no-name-brand wood. It was essential.

When I finally put it together properly, it still needed to be configured.

The configuration wasn't too bad. I downloaded a sample configuration sketch from somewhere (not sure where anymore) and it was a little clunky so I added better controls to tweak the values as I zeroed in on the appropriate calibration factors for each of the scales.

C
float fltscale_3kg_CalibrationFactor = 301925;

You can look at the sketch Download Scale_Calibration.zip.

The scales themselves report their results in Imperial values (pounds) so the calibration process was only complicated by the fact that the 'known mass' I started with was in metric and did not result in proper calibration. Once I had figured out that the 'known mass' had to be in pounds, the process was much simplified.

The results of the scale are reported in pounds.  They are then converted to Liters of moonshine by the following method:

float Convert_Lbs_to_Liters(float fltPounds, bool bolReport)
{
  // my moonshine weights 865g/L
  // there are  2.20462lbs/1000g
  // converts lbs to LMS(litres of moonshine)
  //
  // there are 2.20462 pounds in a kilo
  float fltGrams = fltPounds / 2.20462 * 1000;

  // 1L of shine weights 865g  (you should weigh your own product)
  float fltLiters = fltGrams / (float).865;

  if (bolReport)
  {
    Serial.print(F("Convert:"));
    Serial.print(String(fltPounds));
    Serial.print(F("lbs = "));
    Serial.print(String(fltGrams));
    Serial.print(F("grams = "));
    Serial.print(String(fltLiters));
    Serial.println(F("Liters of shine"));
  }
  return fltLiters;
}

which divides the measured weight and divides by 2.20462 (pounds per kilo) to convert the pounds to kilograms.   I used a kitchen scale to weigh one Liter of my product and it told me it weighs 865grams per liter.  So the method shown above then divides the number of kilograms weighing down the scale by 0.865 and translates the weighed mass of hand-sanitizer into its calculated volume.  You can reset the scales so regardless of what kind of collecting vessel is on either weighing scale, you will get a fairly accurate reading of the volume of liquid (hand sanitizer, of course) you've produced.

I had originally bought 2x 3kg scales plus the one 1kg scale. But when I mounted the first 3kg scale into the still's box I wasn't being careful("testing the product" more like) and drove the screws in tightly before noticing the scale(block of aluminum) was not clear of the edge of the box's back frame. Twisting the screws in tightly produced a force far greater than the 3Kg the scale was rated for and destroyed it in the process. So now, my project has only two and not the three scales I had intended to use.

LEDs & 74HC595

There are two sets of three LEDs on either side of the condenser. One set reflects the temperature of the liquid in the cooking pot (the wash) and the other set reflects the temperature of the liquid in the condenser. All six of these LEDs are connected to a 74HC595 shift register. If you've never used shift-registers, you'll be surprised to learn that you can control up to 8 digital peripherals using only 3 of your microcontroller's digital pins. How this works is by serially sending out information from the microcontroller to the Shift-Register (using 3 pins: a clock, a latch and a data line) the Shift-Register then, after receiving the information along a single data-line, sets the values of its 8 output pins High or Low depending on the sequence of bits it received from the microcontroller. You can daisy-chain these Shift-Registers and control any number of digital output lines with them.

It would have been possible to add the Channel-Relay (electronic control switch which toggles on/off the flow of current through a 120V power supply using only 5V from the Arduino) to the Shift-Register but I chose to keep the six LEDs separate from the one channel-relay for simplicity's sake since the Arduino Mega 2560 microcontroller that I used to build this project has plenty of pins and running out of them was never a real threat.

During the soldering phase, I built the two separate light panels and didn't worry too much about which light was connected to which of the 74HC595 Shift-Register's output pins counting on the fact that I could alter their address values in the IDE sketch once they were all wired together. Each LED requires its own resistor and they all tie to ground, so aside from being certain their anode/cathode legs were in the correct direction there were no real issues connecting them all together.

Here are the pin 'address' declarations in the sketch:

C
const byte LED_Condenser_Blue =   B00100000;
const byte LED_Condenser_Green =  B00010000;
const byte LED_Condenser_Red =    B00001000;
const byte LED_Wash_Red =         B00000100;
const byte LED_Wash_Green =       B00000010;
const byte LED_Wash_Orange =      B00000001;

I have them lined up in the here but despite having managed to keep the Condenser lights separate from the Wash lights their addressing sequence does not match their physical left-to-right appearance of the project's end result which is of no consequence since the variable names are used when they are referenced anyway.

Because the LEDs are intended to reflect the temperatures in the Wash & Condenser, I made a method which takes in all the pertinent parameters and then sets each bit value for the LED specified accordingly.

The Build

This project was a lot more work than I expected. Things broke, didn't go as planned or needed much tweaking. Running the still itself takes up a lot of my time and I wind up napping and getting up every other hour or so to change the water in the condenser. I had plans to have the cold water run through a Water-Cooler (bought one on eBay for $120.00) but then had problems getting a water pump that was strong enough to pull the water up out of the water cooler and push it down to the bottom the the condenser. Once I did get a suitable pump, it was so strong (400 Liters per hour) that the water cooler (cools 1Liter per hour) was negligible and the water pump was pulling water out of the top of the cooler before it ever got cold. The idea was to inject cold water at the bottom of the condenser then have the condenser overflow at the top (where the hot copper pipe heats it) into the water cooler but the tube I used to direct that overflow(from the condenser to the water cooler) was 1 1/2 inch in diameter! which turns out is too small to keep up with the pump. So the pump pulled as much as it could until it was sucking air as the over flow trickled in. The way the water cooler itself is designed both the 'cooling chamber's' input (water from the bottle that normally sits on top of it) and output (spout for you to drink from) are at the top so the warm water that gets injected into it is the first water to be pulled out of it. That whole plan had to be kibosched, scrapped and canned. No good. At least now, I can get up and have oatmeal without boiling water, other than that... a bit of a waste.

I did convert the water pump into a water-siphon (sort of thing) that I use to transfer the cheap wine I make by fermenting bottles of fruit juice without gobbling up all the wasted yeast at the bottom. Works great. But I'm still siphoning water out of my condenser every other hour whenever I run my still. Overall ... a fun project.

Steam pressure and your Condenser(non-Arduino related Distillation issue)

If you actually build yourself a still,  you may discover that your cooking pot is leaking steam (alcohol) and you're not producing as much product (hand sanitizer) as you expected.  If that's the case, have a look at this diagram below that takes in consideration the position of your condenser relative to your boiling pot of wash.Image 4

By positioning your condenser above the cooking pot, the steam rising out of the pot will be strong enough to push steam down into the condenser and reduce the pressure in the cooking pot.

History

  • 19th April, 2021: Initial post
  • 1st August, 2021 : nozzle returns to Mode-Position after adjustment
  • 6th October 2021 : fixed scale functions to more accurately measure the liquid content
  • 18th December 2021 : improved Heat-control feature

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)