SOFTWARE

A fully functional software version of the PC-program can be downloaded from here (ebrew_Qt_exe.zip file, v3.20). In order to have a complete brewing system, you also need the brewing hardware with the latest firmware.

Github: All source-files are available on Github, see github/Emile666/ebrew_Qt

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 it over the network to the PC in the brewery.

This particular setup has worked for years without any problem. The hardware was connected to the PC by means of a parallel port (or printer port). With the transition to a new hardware platform, this was upgraded to a (nowadays) more common USB connection. And then the set-up with the microcontroller becomes interesting again. Lots of old functionality, like the generation of the PWM signal, is easy to realize for any microcontroller. So the amount of hardware components can be reduced, as a result of this. The latest version of the hardware uses an Ethernet connection for its communication and the USB connection is only used for debugging purposes. This microcontroller also needs software, which we call firmware, to distinguish it from the PC-software.

What shall we use as programming language for both systems? Well, for the firmware this is quite clear: up until 2022 the Arduino Nano was used, for which I use Atmel Studio and the C programming language, see github.com/Emile666/Brew_Arduino for the source-files. The latest version of the brewing hardware doesn't have an Arduino Nano anymore, but is equipped with an STM microcontroller, the STMM8S207, which is a 64 pin LQFP device. The firmware for the STM8S207 is written with IAR Embedded Workbench - STM8 series, also in the C programming language, see github.com/Emile666/Brew_HW_Stm8s207 for the source-files. For embedded systems you almost never have a decent alternative for C, so this is the preferred set-up.

For the PC-program it is less clear because of the large variety of programming languages available. I do not really like the new programming languages like Java and C#/.Net. I typically write my programs in the C programming language, but I definitely needed a nice front-end GUI (windows, menus, all the stuff) for the PC program and C in itself is not really suitable for that. The solution however was to use the C++ programming language, which is object-oriented and you can even use your existing C libraries (if needed) and build a nice looking program.

The PC application was initially built using Borland C++ Builder and this setup was used until 2017. C++ Builder became obsolete and an alternative needed to be found. The current PC application is developed with the Qt development environment, see www.qt.io), again with C++ as the programming language.

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 called e-brew! by the way, which stands for electronic brewing). This page consists of the following paragraphs:

1. GRAPHICAL USER INTERFACE (GUI)

To shield the complexity of the software for the use of the program, a decent Graphical User Interface (GUI) is needed. This GUI defines the look and feel of the program and makes sure that everything is running smoothly during brewing. After inital power-up, the main-screen is shown, of which a screenshot is given at the top of this page.

All necessary information for a brewing day is shown or can be adjusted, which is ideal if you want to check certain values:

If any of the values read is wrong, or when there is no communication, the corresponding colors of the sensors turn Red. The screenshot shows that all temperatures are read correctly here (all green). Also the flows of sensors 2 and 3 are read correctly too.

1.1 FEATURES OF THE PC BREWING PROGRAM

The menu-bar of the PC brewing program contains the following menu-items:

Back to the Top of the Page

2. SOFTWARE ARCHITECTURE

The PC-program contains a number of important components which together make up the entire system. Because the C++ language is object oriënted, it is normal to identify a number of objects or classes. The main components for the PC-program are:

The PC-program's main tasks are to allow the brewer to do all kinds of settings and adjustments needed for the brewing process (times, temperatures, parameters) and to send commands to the brewing-hardware on a periodic basis, using a standard communication protocol. The responses from the brewing-hardware are made visible to the various screens. The following screen-shot gives an overview of all project-files and screens used:

The total PC-application contains approximately 5600 lines of C++ code, of which the main-object (called MainEbrew) already contains 2200 Lines of Code (LOC).

Next to the PC-program there's a second important software system: the firmware for the ATmega328 on the Arduino Nano. This software-system also contains quite a lot of code: for revision R1.45 this was approximately 3550 lines of C-code. All-in-all: both systems together contain more than 9000 lines of C and C++ code!

Back to the Top of the Page

2. SCHEDULER

The PC application demands strict timing-requirements. A lot of things need to be done at fixed time-intervals. Commands have to be send to the brewing-hardware, temperatures and volumes need to be read in, the screens need to be updated, etcetera. It would be nice to have a real-time embedded operating system (called an RTOS), but Windows is far from being real-time or embedded. To execute something every 100 milliseconds is already quite demanding. Fortunately, temperatures are not rapidly changing, so controlling this does not have to be very fast. But something is needed to make sure that all tasks are executed in time. For this I have written a separate scheduler component. A scheduler is a piece of software that makes sure that all tasks in a program are executed at the right time. The scheduler used here is non pre-emptive, which means that after a task has been started, it is not interrupted any more by another task. The next task can only start when this task has finished executing. This is sufficient for our brewing system. You just need to have a bit of attention for the duration of every tasks.

Tasks are easy to add with this scheduler component. To illustrate this, an excerpt of a bit of C code from the PC-application is given here:


	//-----------------------------------------
	// Now add all the tasks for the scheduler
	//-----------------------------------------
    scheduler->add_task("aliveLed"  ,   0,TS_LED_MSEC  ,Ebrew,SLOT(task_alive_led()));      // TASK 0
    scheduler->add_task("readTemps" , 100,TS_TEMPS_MSEC,Ebrew,SLOT(task_read_temps()));     // TASK 1
    scheduler->add_task("pidControl", 300,TS_PID_MSEC  ,Ebrew,SLOT(task_pid_control()));    // TASK 2
    scheduler->add_task("updateStd" , 400,TS_STD_MSEC  ,Ebrew,SLOT(task_update_std()));     // TASK 3
    scheduler->add_task("readFlows" , 600,TS_FLOWS_MSEC,Ebrew,SLOT(task_read_flows()));     // TASK 4
    scheduler->add_task("wrLogFile" ,1600,TS_WR_LOGFILE,Ebrew,SLOT(task_write_logfile()));  // TASK 5
	

Adding tasks is simple and convenient. The task task_update_std() for example executes the state transition diagram function every second (1000 msec.). You can also add an offset: task_update_std() is executed at time stamp 0.40 (400 msec.) and then at timestamps 1.40, 2.40, 3.40 etcetera. The PID controller, task_pid_ctrl(), is controlled in another way: the time-interval between two calls to this function is dependent of the parameter Ts, which is given in seconds. If this parameter is set in the PC-program to 20 seconds, this call here makes sure that the PID controller is executed every 20 seconds. As mentioned before, it is important to make sure that all of these tasks finish in time and do not take too much executing time, since this will delay other tasks. The scheduler is able to measure the time-duration and the maximum time-duration of every task. This information can be used for debugging purposes and looks like (result is from a log-file):


	Task-Name      T(ms) Stat T(ms) M(ms)
-------------------------------------
aliveLed 500 0x02 407 1339 readTemps 2000 0x02 15236 15699 pidControl 1000 0x02 1421 1767 updateStd 1000 0x02 681 1040 readFlows 2000 0x02 55684 55684 wrLogFile 5000 0x02 167 167

From this, it appears that updateStd() had a maximum time-duration of 681 microseconds. Together with the code excerpt before, it gives a good insight in the behaviour of the various tasks. Debugging timing-issues is relatively easy to do. The scheduler itself is called from within an Timer object. This is done every 100 milliseconds. The main function of this function is to update all task-timers and to administrate if a task needs to be enabled of disabled. The execution of a task is not done here, only the administration!

Back to the Top of the Page

3. STATE TRANSITION DIAGRAM (STD)

According to the free on-line 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 control function, that 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 TRUE (e.g. the 'temperature is OK'). Conditions in the STD below are given in RED. When a transition is made to another state, a particular action can be programmed (e.g. set pump on). Actions are given in GREEN. Summarized: there are states, transitions between states, conditions and actions. These need to be defined for the brewing program and is give here:

The total STD of the PC-program has become quite complex. A short explanation for every state is given here:

The table (upper right corner) shows, for every state given, which valves are open (1) or closed (0) and whether or not the pump is on (1) or off (1). By designing such a table, the behaviour of the various valves becomes very defined. You always know exactly when a valve (or pump) 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 scheduler.

Back to the Top of the Page

5. FIRMWARE FOR ARDUINO NANO / ATmega328

the firmware contains the C source-code for the ATmega328 microcontroller and has not much to do with the design of the PC-program described above. Both software systems do communicate with each other, using the Ethernet connection or the virtual COM port function of the USB connection. A big advantage of the use of a microcontroller is that all hardware related and often time-critical tasks no longer have to be handled by the PC-program. A microcontroller is much better suited for this. At the PC side, we use the Arduino hardware boards. Prior to use, the Arduino Software Environment needs to be installed. This already installs all the drivers needed for the virtual COM port and the USB connection. At the microcontroller side, the story is a bit different. Fortunately we do not have to dig into USB communication (complex!), but we can use existing routines for serial communication. The microcontroller used (ATmega328P) contains a component called USART, which stands for Universal Synchronous/Asynchronous Receiver/Transmitter). As long as you have the proper USART library, it is relatively easy to start with serial communication. Settings used here are 38400,N,8,1 which means: 38400 Baud, no parity bit, 8 data-bits and 1 stop-bit. This is of course identical to the settings in the PC-program.

Back to the overall design of the microcontroller software. I do not use Arduino sketches and the Arduino Programming Environment, since it is too limited for what I need. From some sketches I made proper C libraries, others I wrote myself. As usual for a C project, for every component a C source-file with a header-file (.h) is made. Together this contains all the necessary routines for such a component. For example: the I2C module (with files i2c.c and i2c.h) contain all functions for I2C communication, including specific routines for the ICs used here, such as the DS2482. The overall architectural design looks like the following:

Low-level routines: As you can see in the picture, there are libraries for I2C, SPI, ADC, PWM, One-Wire and USART. Basically for every low-level hardware component of the microcontroller. In order to really understand these libraries, you need to read the ATmega328P microcontroller data sheet. Especially if you would like to make adjustments. But, since the ATmega328P is a very popular microcontroller, there are many good working examples available.

Interrupt routines: The interrupt routines are time-critical pieces of code that cannot wait until the scheduler has time for them. It needs to be executed as soon as something happens. For the embedded design engineer it is important to keep these routines as small as possible: you just cannot start to wait until something happens in such a routine. A nice example of the use of such an interrupt routine are the flow-sensors. These are connected to two port pins of the microcontroller. As soon as a pulse comes in from a flow-sensor, an interrupt is generated by the microcontroller. The interrupt source routine does nothing more than to increase a software counter. Every flow-sensor has it's own unique interrupt routine. Besides these routines there is also a timer interrupt defined for the scheduler. This one is executed every millisecond. Finally there are two more interrupts defined for the serial communication. If the USART hardware from the microcontroller has received a byte, an interrupt is generated. The interrupt routine merely stores the byte read into a ring-buffer. Something similar happens when there is data in the ring-buffer that needs to be send to the PC-program.

Next to these low-level libraries, there is also a miscellaneous library. This library contain functions that deal with signal processing, such as filtering. It contains a moving-average filter (which is a low pass filter) and a slope-limiter function. Furthermore a ring-buffer for the serial communication and a RS232 command handler. Every command from the serial interface is processed by this function.

Ethernet/Wiz550io: the brewing-hardware is equipped with a WIZ550io Ethernet module, which is controlled through the SPI interface. The C-libraries are derived from several Arduino sketches (which were a mess to read). With this addition it becomes possible for the brewing hardware to send/receive commands using the UDP protocol, instead of using the USB connection. During powerup, the brewing hardware sends status info to the USB port, such as the local IP address of the brewing hardware. It looks like this:

The scheduler from the PC-program is reused here as well. So the software is used on various platforms.

Back to the Top of the Page