Tinker journal

Over-engineering my homebrewing tank

I have a second hand Klarstein Maischfest 30L brewing tank that I use to brew beer. It is an ok, entry level, electric kettle with 2 x 1500W heating elements and an integrated temperature controller.

A photograph of the klarstein brewing tank

The kettle.

However, there is one issue with it, the temperature controller is bad. The way it is working is by turning on heating if temperature is below target minus 3°C, and turn it off when temperature is above target plus 3°C. This range of +/- 3°C is simply not acceptable for brewing beer. The first step, called the mashing, consist of putting malted grain in water at specific temperatures to make some magic happen with grain sugar. Usually you want +/- 1°C precision for the chemistry to do its work effectively.

Add to this issue, the fact that the thermometer probe gives the temperature the water was 1 minute ago, and you get a messy roller coaster instead of a constant temp.

So, until recently, I was a human temp controller. Basically, I sat in front of the vessel during the mashing process, and measured the temperature manually with an external thermometer every 10 minutes. When the temperature was under the target, I turned the kettle on with a fake higher target so it was triggering the heating elements, then stopped it after 20s or so, and checked again.

It was way too much work, so I decided to spend several days and too much money working on a way to fix that issue.

The plan

I first opened the false bottom of the kettle to access the circuitry.

Picture of the inside of the kettle

A view of the kettle internals.

There is two electric 1500W heating elements there, a thermal fuse, a thermal trip switch, and the ON/OFF switch.

Everything is connected to an exposed PCB containing the relays to control the heating elements, the power supply, and some misc components such as a buzzer.

Picture of the exposed pcb

The PCB

You can see on this PCB that there is a connector with red cables going to an hidden PCB behind. The other PCB is not visible but it is holding the lcd screen, and probably the microcontroller for the logic. That is good news for me! That means that instead of bypassing everything by connecting to the heating elements direclty, I can only bypass the microcontroller. By connecting a new microcontroller to the connector on the exposed PCB, I can control the perfectly fine relays, and use the original power supply and thermometer probe. Moreover, it allows me to keep the safety features such as the thermo sensible switch at the bottom of the kettle.

I wanted to keep the original microcontroller available though, in case my new system would not work properly. My plan was to have a switch on the outside of the kettle to be able to select what controller to use. The switch would allow me to disconnect the original controller from the exposed relays PCB and connect the external new Arduino controller.

Diagram of the plan

The plan.

Retroengineering the PCB.

Looking at the connector between the relays/power PCB and the microcontroller one, we can see the following pins: VCC5, GND, NTC, BUZ, HT1, HT2, ZERO. By unplugging the microcontroller PCB, and plugging prototyping wires to the pins, I managed to confirm my understanding of those pins by testing it with a multimeter and a breadboard.

There is 4 input pins (from the relays/power PCB to the microcontroller PCB):

There is 3 ouptut pins:

Kettle modification

Let’s start by modifying the kettle, so I can plug an Arduino to it. My plan is to have the new temp controller working outside the kettle, by being plugged through a 7 pin connector going through the false bottom wall.

Developping and testing the controller will be easier this way, and this makes my design actually modular. If I want to upgrade or fix the new controller later, I can unplug it and put a new one on the connector.

I bought some thermally protected wire for this as the heating elements are very close to all the wires inside the false bottom.

Because the wall is curved, it is kind of tricky to attach a new PCB. There is no place to do so cleanly. I decided to 3D print a curved support that would fit the wall nicely, and serve as a protection for the connector and the switch on the outside wall (water or sugary liquid can be easily spilled on the outside while cleaning or brewing, and I like the idea of protecting a little bit the electrical part from it). Hopefully, the plastic part is not heated to much, and far enough from the heating elements. Finger crossed.

Here are pictures of the finished modification:

Picture of inside after the modification

The « controller switch » PCB

Picture of outside after the modification

“-Good coloring! -That’s bone.”

NTC voltage to temp and heating behavior of the kettle

NTC theory

In order to control the water temperature accurately, I needed to have a relation between the voltage read on the NTC pins and the actual probe temperature. NTC probes work by varying the resistance of the probe according to the temperature. The resistance decrease as the temperature rises. The detailed math is described in the thermistor Wikipedia article, but basically, the resistance / temperature coefficient, is described best by the Steinhart–Hart equation: \[\frac{1}{T} = A + B \ln{R_{ntc}} + C(\ln{R_{ntc}})^3\]

But we have the NTC pin voltage and not the NTC resistance. Looking at the PCB in detail, here is the portion of the board bringing the NTC resistance information to the NTC pin:

Schematics of the NTC pin

Schematics of the NTC pin

This gives the following relation using the voltage divider formulae: \[V_{cc5} \frac{R_{ntc}}{R_{ntc} + R_2} = V_{ntc} \frac{R_{ntc}}{R_{ntc} + R_1}\]

On the arduino though, when connecting the \(V_{ntc}\) voltage to an analog reading pin, I will get the reading \(1024 \frac{V_{ntc}}{V_{in}}\). As I plan to connect the \(V_{cc5}\) voltage to the \(V_{in}\) pin, I have the relation betwen the voltages and the reading of the analog pin P: \[ P = 1024 \frac{V_{ntc}}{V_{cc5}} \]

This gives me the final relation between the NTC resistance and the analog pin reading: \[ R_{ntc} = \frac{\frac{P}{1024}R_1 - R_2}{1 - \frac{P}{1024}}\]

Replacing the NTC resistance in the Steinhart-Hart equation we have: \[\frac{1}{T} = A + B \ln{(\frac{\frac{P}{1024}R_1 - R_2}{1 - \frac{P}{1024}})} + C(\ln{(\frac{\frac{P}{1024}R_1 - R_2}{1 - \frac{P}{1024}})})^3\]

We need to find A, B and C. To retrieve those unknown parameters, we have to make some actual temperature and voltage measurement accross the range of usual temperatures and just perform a curve fitting.

Temperature of the water inside the kettle

I was also planning of using those temperature measurement to learn more about my kettle behavior in term of heat loss and heating power. I was hoping to use those values in the controller to better control the temperature, and I was curious about the actual numbers.

The main goal was to get 3 values:

With thoses values, I would be able to make the temperature controller.

Using some basic thermodynamic we have the following formulae for the variation of heat in the kettle: \[\frac{dQ}{dt} = Q_{in} - Q_{loss}\]

The total heat energy of the water is given by: \[Q = mcT\] with \(m\) the mass of water, \(c\) the specific heat capacity of the liquid (\(4186 J.kg^{-1}.K^{-1}\) for water2) and \(T\) the temperature.

I first planned to perform curve fitting using a simple model taking into account only convection loss in the \(Q_{loss}\) parameter.

\[Q_{loss} = \frac{UA}{mc} (T - T_{ambient})\]

With A the area of the volume of water and U a heat loss coefficent.

The issue is that my tank is open on the top and the evaporation of water is really not a negligible heat loss. Math at this point became to much for me as evaporation depends on a lot of factors, as well as the actual water temperature in a non-linear way. I am not even sure the differential equation is solvable? This website is only giving experimental(?) charts.

I figured I only needed a good approximation around a limited temperature range as the brewing mash process is always taking place between 50 and 80°C. So, I went ahead with the actual measurements to see the curve shape and know if I could make good linear or polynomial reductions, or simply use the experimental plots to get the values without having any model behind.

Temperatures measurements

I performed the following tests:

Here are the plots:

Plot of the heating phase

Temperature of water during heating

Plot of the cooling phase

Temperature of water during cooling

All those data points gave me a nice scatter graph of the NTC voltage compared to the temperature of the water. You can see the really nice curve fitting of the Steinhart–Hart equation in red.

Plot of the ntc voltage

NTC voltage over water temperature scatter plot, with the curve fitting done.

I also looked at the graphs around the typical mashing temperature. During heating phase, we can simply do a linear regression:

Plot of the heating fitting

linear regression of the heating test temperature around the typical mash temperatures.

Slope is 0.03089 degres/second. This gives me the time needed to heat this specific volume of water (20L in my test) an additional 1°C: around 30 seconds of constant heating with full power.

For the heat loss, my goal was to know how much the water cools down during the period we need to wait for the probe reading to be accurate. Making a small heating test with a small volume of water to be able to put the external fast thermometer next to the probe, I measured the average lag of the probe to be around 1 minute! Of course.

Plot of the lag of the probe

Same measurement but one thermometer is lagging hard!

During this time, and around the typical mashing temperatures, we see that the water cools down around 0.4°C

Here is the complete slope of the cooling plot in function of the temperature:

Plot of the slope during cooling

Slope measured depending on temperature during cooling.

I may have overdone this, especially since I plan to use only constants values in the controller. But I am happy to have at least order of magnitude for heating duration needed, probe lagging, and cooling.

Later I will probably try to improve the temperature controller by allowing it to adapt to a wider range of volumes, but for now, I just plan to brew some batches, testing it in the field to get a good grasp of what to actually improve, if needed.

Controller design

Hardware

My plan use a 3d printed box containing an arduino, an lcd screen and several controls:

The process took some time, I had to battle with the rotary encoder a little bit, and also, I had to redo 15 bazillions times the crimping of the switch wires. It was my first time using JST connectors, and the female metal connector kept cutting my wires after crimping, or making it so fragile it broke after few hours. I guess my crimping plier was not the right one, but honestly I am not sure what happened.

Picture of wired new controller board

Coming together

I 3d printed a custom box (twice, as half the holes where misplaced in the first iteration).

Picture of wired board in the box

“What’s in the box!?”

And after closing it, seeing it’s not working properly, reopening it, fixing, reclosing it, approximately 8 times, I have finally something that should work. On the picture below you can see that the temperature displayed is 102, but this was before calibrating everything, and making the temperatures measurements I described in the previous section. It’s an hard coded value for now.

Picture of finished hardware

Hopefully, no more opening of the box

Software

Here I want to talk about my strategy for the mash mode. The rest of the code is pretty straightforward: input pin readings, mode selection, display. I used a specific library for the rotary encoder though, as the display library took a significant amount of processing and I was missing some pulses when just looking at clocks and data pins every loops. I first tried to do the interrupts mysefl but figured I could use a nice library instead of reinventing the wheel.

For the mash mode, the goal was to maintain the temperature as constant as possible around the target. There is 2 challenges though:

My idea was to use the order of magnitude for heating duration, lag and cooling I got during testing in addition to the temperature readings, to make sensible adjustements. If I know that, in order to heat the water 1°C more, I needed to turn the heating elements for ~1 min for example, then I know that when the temperature read is 1°C less than target, I should not heat the water continuously for 5 min, even if probe is saying otherwise.

The plan was:

This is what I implemented, using the values calculated during my test phase. For now, the mash mode works like this when probe reading is less than 10°C under the target:

Pretty basic, but for know I have no idea if it is sufficient or not. Let’s brew so beer with those values, I will come back later if needed.

What could be done though?


  1. The zero pin voltage felt weird to me as I was expecting more of a 0V or 5V value. I worried that the Arduino would not reliably read the pin as LOW at 3V. I looked at it in more details: the pin is connected to the VCC5 through a 10K Omhs resistor, and to the GND pin through the output of an optocoupler. The optocoupler is controlled directly by the AC power voltage (disconnected when the safety switch trips, and connected to the 240V AC in normal operating condition.) The weird low voltage may be because of the alternative current driving the optocoupler, and thus making it pulsing. That could explain the 3V measurement on a simple multimeter. I do not have an oscilloscope to check though. In the end, I connected the zero voltage to an analog pin, and I saw there that the pin as indeed oscillating between LOW and HIGH. I figured that if the pin stays HIGH long enough, I can trigger the error. ↩︎

  2. Is the mash actually the same heat capacity as water? The grain present in the water represents a big portion of the mass, I’m not sure about the implications. ↩︎

Tags: