Software
Although a significant amount of hardware / electronics has been created, it is useless without any software controlling it.When I started with the design of my system I wanted to have full flexibility. I could go for a microcontroller based design, with embedded software in it, so you would not need a PC. But I have various PCs hanging around in my house, so why not use one of them (in a network configuration) for controlling your home brewery?
In this way, I have lots of flexibility. If I want some new behaviour built into my brewing program, I make an update, test it and distribute to the PC in the brewery.
Then there is always the question of the programming language to use. I do not really like the new programming languages like Java and C#/.Net (I never ever used a MS programming language and I never will!).
I normally write my programs in the C programming language, but I definitely needed a nice front-end GUI (windows, menus, all the stuff). C in itself is not really suitable for that. The solution however was to use the C++ programming language, you can use your existing C libraries and also build a nice looking program.
And if you use Borland's very very excellent Borland C++ Builder, programming becomes a lot of fun!
So the configuration is chosen, the programming language is set. What to do next?
Well, I can tell a bit about the internal structure of my brewing program (which I have called e-brew! by the way, which stands for electronic brewing).
This page consists of the following paragraphs:
- 1. Overview / Architecture
- 2. Main Interrupt Loop
- 3. State Transition Diagram
- 4. Graphical User Interface (GUI)
1. Overview / Architecture

This picture shows the software architecture. I have tried to design it as modular as possible, so that if I ever change hardware or I2C devices, it is relatively easy to update the software.
- At the lowest level you have the I2C physical layer routines which deal with reading a byte from / writing a byte to the parallel port (to which the electronics box is connected). A direct access of the parallel port under Windows 2000 / XP does not work anymore, so you need an device-driver for that. Fortunately, several of these parallel port drivers are available. I used the PortTalk driver, which you can download for free from the BeyondLogic site.
- The I2C hardware layer deals with transferring bytes to/from the I2C bus, using the routines from the I2C physical layer. Two methods are available here, one that uses the PCF8584 I2C Bus controller, the other method uses a principle called bit-banging (the I2C signals are generated directly from the parallel port, the PCF8584 is not used in that case). I only use the PCF8584 routines, but bit-banging is supported as well (if I ever need that).
- The I2C bus layer contains the necessary routines for the I2C bus itself, such as i2c_start(), i2c_init(), i2c_read() and i2c_write(). These routines use the routines from the I2C hardware layer.
- The I2C device layer contains all routines that access the I2C devices. Currently there are routines for the SAA1064 (LED-display), the LM92 digital temperature sensor, the MAX1238 AD-converter, the PCF8574 IO-chip and the PCF8591 AD-DA converter. These routines use the routines from the I2C hardware layer.
- Another library is written specifically for the ebrew application to support various functions. This library is written in C and contains routines that deal with the log-file, the state transition diagram, the PID controller etcetera. These routines are called from the application/object layer.
- The application/object layer is the top-level software layer. It contains the C++ objects that are needed to build the entire application.
2. Main Interrupt Loop
The ebrew application needs to have tight timing requirements, since several AD conversions are done. In order to minimize noise, the AD conversions should take place at fixed time-periods. It would be nice to have a real-time embedded operating system, but Windows is far from being real-time or embedded. Fortunately, there is a nice little C++ object, called Animation Timer (TAnimTimer) that assures accurate timing down to the millisecond (0.001 second). And this is sufficient for our purposes.So I placed this timer object on the Main Form of the ebrew application, and used the OnTimer() event to generate my Main interrupt loop. No matter what happens within the ebrew application or within Windows (besides a complete system crash), the main interrupt loop is always called every 50 milliseconds (which is 20 times per second). This is therefore the main time base within the ebrew application. Everything that is done, is related to this 50 msec. interrupt!

This picture shows a so-called structure chart of the main interrupt loop (which is called t50msec2timer()). It shows when and which functions are called from within this interrupt loop.
The main interrupt loop is called every 50 msec. But it is not economic nor desirable to do everything at the same time. For example: the PID controller function should only be called once every 5 seconds. And it is not useful to update the LED displays every 50 msec., once every second is more than fast enough.
This means that we can divide the main interrupt loop into time-slices. In every time-slice, a particular function is called. In this way, we can divide the processing of functions over time.
Since we have 20 interrupt calls per second, it makes sense to create 20 time-slices, in the structure chart they are numbered from TS1 until TS12 (TS13 till TS20 are not used yet). In time-slice TS1, a call is made to the function get_analog_input(), which retrieves the HLT volume from the AD-converter. In time-slice TS2, the same function is called, but then to retrieve the MLT volume. And so on.
Only the function Generate_IO_Signals() is called every time. This function sends some binary signals to the electronics (e.g. the Alive LED which is on for 500 msec. and off for 500 msec.).
Back to the Top of the Page
3. State Transition Diagram
According to the free online dictionary of Computing, a state transition diagram (STD) is a "A diagram consisting of circles to represent states and directed line segments to represent transitions between the states. One or more actions (outputs) may be associated with each transition.".In terms of my brewing program, a STD is a central function, which controls the various phases of the brewing process. A state can be a 'mash rest' phase or a 'mash preheat' phase. A transition from one state to another state can be made if a certain condition is met (e.g. the 'temperature is ok'). When a transition is made to another state, a particular action can be programmed (e.g. set pump on).
Summarized: there are states, transitions between states, conditions and actions. These need to be defined for the brewing program.
The first thing to do is analyse the process and derive the unique states. For the brewing program, these states are:
- 00. Initialization: this state is entered after the brewing program is started. It remains in this state until the brewer switches the PID controller on
- 01. Wait for HLT temp.: the water is the HLT is heated, but the strike temperature has not been reached yet.
- 02. Fill MLT: HLT temperature is OK, pump is switched on, MLT is filled with the required amount of water from the HLT. In this state, the brewer has to put the malt in the MLT.
- 03. Mash in progress: the MLT temperature has not reached the required mash temperature yet.
- 04. Mash timer running: the MLT temperature has reached the required mash temperature and the mash rest has not been finished yet.
- 13. Mash preheat HLT: the mash rest is not finished yet, but the HLT is already heated to the next mash temperature (the pump is switched off in this state). In case of a time-out (mash rest is finished), a transition is made to state 03. Mash in progress, and the mash-index is increased by one.
- 05. Sparging rest: this state is entered when all mash temperatures and rests have been handled. Mashing is finished, and sparging is started. When this state is entered for the first time (coming from 13. Mash preheat MLT), a transition is made directly to state 06. Pump from MLT to Boil. Otherwise, the STD remains in this state for a predefined time (20-25 minutes). A transition to 09. Empty MLT is made when the sparging index is set to the required value, meaning that all sparging cycles have been done.
- 06. Pump from MLT to Boil. Part of the wort is transferred from the mash/lauter tun (MLT) to the boil kettle. If the required amount is transferred (volume is less than a certain value), a transition is made to 07. Delay_xSec.
- 07. Delay_xSec., the STD remains in this state for one second, allowing the brewing system to settle a bit.
- 08. Pump from HLT to MLT. New fresh water of 80 °C is transferred from the Hot Liquid Tun (HLT) to the MLT. If the required amount is transferred (volume is more than a certain value), a transition is made to 05. Sparging Rest and the sparging index is increased by one.
- 09. Empty MLT. All the fluids in the MLT are pumped to the boil kettle.
- 10. Boil. Start of the boiling phase.
- 11. Empty Heat Exchanger. This state is not used anymore and will probably be removed in a new version of the software.
- 12. Chilling. After boiling, the wort is transferred, via the counter flow chiller, to the fermentation bin.

The red lines in the picture are the conditions. If such a condition is TRUE, a transition to the other state is made. During this transition, the action (the green lines) is executed.
The table (upper right corner of the picture) shows which valves need to be open (1) or closed (0) in a particular state. By designing such a table, the behaviour of the various valves becomes very defined. You always know exactly when a valve is open or closed, given the state the STD is in.
There are many more details to tell about this state transition diagram, but this will be sufficient (I hope) to give you an idea about the functionality. The STD is called once every second from the main timer interrupt loop.
Back to the Top of the Page
4. Graphical User Interface (GUI)
The main interrupt loop and the state transition diagram are the heart and soul of the brewing program. But to make this flexible and easy to use when brewing, a nice front-end GUI is needed. Well, here is a screen-shot of the ebrew program:
There is a lot of information displayed on this screen, which is ideal when you are brewing and you need some specific information:
- HLT and MLT thermometers: These thermometers display the actual measured temperature. A digital equivalent is also given (the screen shows an actual temperature of 79.00 °C for the HLT and 76.81 °C for the MLT. The required (reference) temperature for the HLT is 79 °C and for the MLT it is 78 °C (these are values taken during the last sparging phase of a real brewing session, where Thlt is kept to 79 °C continuously).
- PID controller switch-box: The PID-controller can be switched on and off with this switch-box. If it is switched off, the PID controller output is always set to 0 %.
- Current state: this label shows the actual state of the state transition diagram. You can see that the current state is now "05. Sparging Rest".
- Temperature of electronics: this is the actual temperature of the cooling plate of the triac. The thermometer turns red when the temperature becomes too high (the PID controller output is then set to 0 %). The current temperature in the picture is 24 °C.
- Power bar: this is the red bar with the text "heating element". It shows the output of the PID controller. Currently set to 32 %. The label "3000 W" is outdated and needs to be replaced by 24 kW (because of the new installed gas-burner).
- The actual volumes of the kettles: in the picture they are 43.3 litres for the HLT, 58.5 litres for the MLT and 55.6 litres for the boil kettle. Note that the labels with max. volumes are outdated again (I know: I have to make an update of the software!).
- The status of the pump: three options are possible: "Auto", "Pump Off (M)" and "Pump On (M)". These three options can be chosen by clicking on the pump with the right-mouse button. When the option "Auto" is selected, the brewing program (and the STD in particular) takes care of switching the pump on and off. The brewer can override the brewing program by selecting one of the two manual options (indicated with (M)).
- The statuses of each solenoid valve: again, three options are possible here for every valve: "Auto", "Off (M)" and "On (M)". Although I do not have solenoid valves (yet) in my brewing system, the software is completely ready for this.
- The red label in the lower-left corner: this shows debugging information for the PID controller. With these numbers, you can debug what the PID controller is doing.
- The status bar: the status bar shows some general information (like the version number of the program).
4.1 The menu-bar of the ebrew program
The menu-bar of the ebrew program contains the following menu-items:- File -> Read Log File...: in case there was a crash in either the hardware or the software, this function reads the log-file and restores the system to the last know status in the log-file.
- File -> Exit: exit the brewing program, close the I2C-bus, switch off all electronics
- Edit -> Mash Scheme...: you can enter up to 10 pairs of temperature versus time pairs.

- Edit -> Fix Parameters...: every relevant parameter can be given a value. Example: normally, the HLT temperature is read from the digital temperature sensor,
but suppose that you want to set it (e.g. for testing purposes) to some other value, thereby overruling the value from the sensor. You can use this
menu option to fix a parameter to a certain value (and release it again, so that the ebrew program will take over again).

- View -> Check I2C Hardware Devices: run this option if you want to check which I2C devices are present on the I2C bus. The result is a screen showing
which I2C devices were found.

- View -> Data Graphs: this option shows a running graph of some parameters (like temperature and volume).
- View -> Mash/Sparge Progress: this option shows a screen with the status of all mash and sparging timers, so you can see directly how long a particular rest
will last. This picture is taken during the last sparging session (all mash timers have timed-out and the sparging index sp_idx equals 5). The
sparging rest timer is now 855 and will time-out if it reaches 1200.

- Help -> Contents...
- Help -> How to Use Help
- Help -> About...: shows version number of the ebrew program.
- Options -> I2C Hardware Settings...: various parameters that deal with the hardware can be adjusted here.

- Options -> PID Controller Settings...: various parameters that deal with the PID controller can be adjusted here.

- Options -> Sparge & STD Settings...: various parameters that deal with mashing / sparging / STD can be adjusted here.

- Options -> Measurements...: which signal is present on which AD-converter?

Back to the Top of the Page