• Please visit and share your knowledge at our sister communities:
  • If you have not, please join our official Homebrewing Facebook Group!

    Homebrewing Facebook Group

Mash Monitor

Homebrew Talk

Help Support Homebrew Talk:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
Screen shots as promised. The Mash Monitor app was running on my desktop. My laptop ran the Arduino Emulator, showing digital pin values on one side and analog pin values on the other. For some reason, my emulator code has about a 5% receive error rate. I'm not really interested in debugging it any further, though, since the actual board will be here soon.

Here's the virtual setup:
Digital Pin 0: Relay controlling the steam vessel heating element (HI = on)
Digital Pin 1: Relay controlling the steam solenoid valve (HI = open)
Analog Pin 0: Steam pressure sensor (multiply by .02 to get actual PSI)
Analog Pin 1: Mash temperature sensor (multiply by .18 and add 32 to get actual degrees F)

The correction factors are a rough guess since I don't have the actual sensors yet.
The screen shots were taken at separate times, so the values don't correlate exactly between the pictures, but you get the idea.

4688-serialmashmonitor.JPG
4688-arduinoemulator.JPG


brewman ! said:
Just a note... if we are going to share code, we should all work with the same Java libraries. I'll be using SWT because I've used it for other projects and it works well. Its part of the eclipse.org project.
I'm pretty happy with NetBeans, the swing library, and RxTx. I'll try to keep my code organized and commented, and most of my serial comm classes don't directly reference the GUI. RxTx mirrors javax.comm, so you can just import javax.comm.* instead of gnu.io.* if you're on a Linux/Solaris platform. So, it should be pretty easy to share code at some point.
Jer said:
I've pretty much abandon the USB idea since the only benefit I see to it is that it would power the controller, but since it can only supply 500mA, that's not enough to drive the relays and solenoids, so I'll probably just stick with rs232...
The Arduino USB board shows up as a serial port - you use RS232 serial comm to interact with it. The solid state relays I have on order with Jameco take about 5VDC@3mA to switch up to 280VAC@40A. I'm confident that USB can handle a few of those.
 
It works, it works!

I got the Arduino board and components today, and I couldn't wait to hook it up! My Mash Monitor software only required minor changes, and it was really easy to program the Arduino. I had something working within a few minutes of connecting the board.

Here's a pic of my first test actually controlling a heating element (SWMBO's hair dryer) hooked up to house current. The software works! It's not very elegant (yet), but it automatically controlled the hair dryer to maintain a user-selectable temperature across the LM34 sensor.

4688-arduinotest.JPG
 
Here's a portion of my code. I didn't include the automatically generated GUI code and Swing objects. I also didn't include the Main class because it only initiates the GUI, and everything is triggered from event listeners tied to it. As such, you can't just compile and run this code - you need a GUI or command line interface initialized by the missing Main class.

It's not the most elegant code, but you can see how to read and write to/from the Arduino. I'm fairly happy with this portion of the code, though I'll probably clean it up a bit. Future changes will include a separate procedure for making the serial connection (rather than including it in the GUI initialization), less hard coding of things like pin numbers and transfer functions, and more user control over the entire board (rather than just two digital pins).

Code:
package mashmonitor;
import java.util.*;
import javax.swing.*;
import gnu.io.*;
import java.io.*;
import java.math.BigDecimal;
/**
 * Application to monitor and/or control mash temperatures during beer brewing
 * Intended for use with an Arduino USB/Serial board
 * Has built in ability to almost completely control an Arduino board
 *
 * @author  Yuri
 *
 * NOTE: When comments reference bits within a byte,
 *       they are numbered 0-7, with 7 being the 1's place
 */

public class MashMonitorUI extends javax.swing.JFrame implements SerialPortEventListener{
    //"constant" declarations
    private static final String PORT = "COM3"; //which comm port to open
    private static final int HIGH = 1;
    private static final int LOW = 0;
    
    //transfer functions for 10 bit input from Arduino board
    //first number is a multiplier, second number is additive
    private static final double[][] aPinTransferFunctions = {{.25,.02,0,0,0,0},{32,0,0,0,0,0}};
    private static final int STEAMPRESSURE = 1;
    private static final int MASHTEMP = 0;
    private static final int STEAMHEAT = 13;
    private static final int MASHHEAT = 12;
    
    private static InputStream in; //input stream of serial port
    private static OutputStream out; //output stream of serial port
    private static int[] aPins = new int[6];
    private static double[] sensorValues = new double[6];
    private static Boolean isAutomated = false; //variable to determine the control state
    private static SerialPort serialPort = null; //serial port on which to connect
    private static TempController controller; //control class that does most of the work here
    private static boolean successfulTransfer = false; //first successful data transfer sets this true
    
    /** Creates new form MashMonitorUI */
    public MashMonitorUI() {
        initComponents();
        //open a serial port with an event listener
        //perhaps this code should be a separate procedure
        //so that the user can try to re-establish the serial connection
        //on the port of his/her choice if serial communication fails
        try {
            //find the port
            CommPortIdentifier portId = CommPortIdentifier.getPortIdentifier(PORT);
            //open the port
            serialPort = (SerialPort)portId.open("MashMonitor", 2000);
            //set the port up for communication
            serialPort.setSerialPortParams(9600,serialPort.DATABITS_8,serialPort.STOPBITS_1,serialPort.PARITY_NONE);
            //set the input and output stream variables
            in = serialPort.getInputStream();
            out = serialPort.getOutputStream();
            //add an event listener
            serialPort.addEventListener(this);
            serialPort.notifyOnDataAvailable(true);
            jLabelStatus.setText("Listening on port: "+portId.getName());
        } catch (Exception e) { //catch everything
            jLabelStatus.setText("ERROR: "+e.toString()+" - "+e.getMessage());
        }
    }
    //hande serial events - read only
    //wait for 14 bytes of data before reading (corresponding to each analog pin)
    //two "wrapper" bytes of all 1's surround
    //2 bytes per value, upper 8 bits first, followed by lower 2 bits (to get 10 bit precision)
    public void serialEvent(SerialPortEvent event) {
        if (event.getEventType()== SerialPortEvent.DATA_AVAILABLE) {
            int[] tempArray = new int[aPins.length * 2 + 2]; //an array to hold all input
            try {
                //wait for 14 bytes
                if (in.available() >= tempArray.length) {
                    for (int i = 0; i < tempArray.length; i++) {
                        tempArray[i] = in.read();
                    }
                    //if the first and last bytes aren't all 1's (255), discard everything
                    if ((tempArray[0] & tempArray[tempArray.length - 1]) != 255) {
                        while (in.available() > 0) {
                            in.read();
                        }
                        //else, read the bytes into the values for the sensors
                    } else {
                        for (int i = 0; i < aPins.length; i++) {
                            aPins[i] = (tempArray[i * 2 + 1] << 2) + tempArray[i * 2 + 2];
                        }
                        if (!successfulTransfer) {
                            successfulTransfer = true;
                            jLabelStatus.setText("Data transfer initiated.");
                        }
                    }
                    updateSensorValues();
                }
            } catch (Exception e) {
                successfulTransfer = false;
                jLabelStatus.setText(e.toString()+": "+e.getMessage());
            }
        }
    }
    
    //write data to the Arduino
    //dPin holds the pin number to change
    //state is the digital state to set the pin (HIGH or LOW)
    private static void serialUpdate(int dPin, int state) {
        try {
            //write a single byte command
            //bits 5 and 6 hold the pin number
            //bit 7 holds the pin state
            out.write((dPin << 1) + state);
        } catch (Exception e) {
            successfulTransfer = false;
            jLabelStatus.setText("Serial write failed.");
        }
    }

    //very brute force code to set sensor values
    //would be nice if controls were somewhat user-assignable against values
    //perhaps an init text file could be incorporated to save settings
    //instead of hard coding controls against values
    private static void updateSensorValues() {
        for (int i = 0; i < aPins.length; i++) {
            sensorValues[i] = aPins[i] * aPinTransferFunctions[0][i] + aPinTransferFunctions[1][i];
        }
        jProgressBarMash.setValue((int)sensorValues[MASHTEMP]);
        jLabelMashActualTemp.setText(Integer.toString((int)sensorValues[MASHTEMP]));
        jProgressBarSteam.setValue((int)sensorValues[STEAMPRESSURE]);
        
        //how to round double values in Java
        BigDecimal bd = new BigDecimal(sensorValues[STEAMPRESSURE]);
        bd = bd.setScale(2,BigDecimal.ROUND_UP);
        jLabelSteamActualPressure.setText(bd.toString());
    }
 
Good work, Yuri.

I think that in order to keep the host code reasonable one is going to have to add more functionality to the microcontroller. For instance, most of your temp control loop could be programmed to run on the micro. Then one could set up a few messages such as one to turn the temp control on, one to turn it off and one to get the current status, which would include the current temp.

If one just uses the micro as a fancy I/O board with the IO routines, its going to get complicated quick and if the USB connection is ever lost the whole process will crash. When I do something like this, the micro does most of the heavy lifting and the PC is "just" the GUI.

Nevertheless, good work on getting started.
 
brewman ! said:
If one just uses the micro as a fancy I/O board with the IO routines, its going to get complicated quick and if the USB connection is ever lost the whole process will crash. When I do something like this, the micro does most of the heavy lifting and the PC is "just" the GUI.
I've already begun to figure that out. I was sending some pretty complex messages to the micro, and it was getting all kinds of receive errors. Now I send a single byte whenever I need a state change on a digital pin.

Also, I was looping both the host and micro code - that was a mess. Now the micro loops (sending a sensor update every 100ms - way faster than required), and the host just listens, with an occasional command sent.

I tend to agree that the automated control should probably exist on the micro itself. Although, you could allow the user to "map" sensors to digital control more easily with logic in the host software, allowing more flexibility.

At any rate, I really dig the world of "physical computing." This is fun!
 
I've done a lot of this and the easiest way is to embed the brains in the micro.

Lets use your temp control loop as an example.

The host sends the micro a message. Lets say its MASH_TEMP_CONTROL_ON, with the setpoint for the control, lets say 150F.

The micro does the control loop. So it decides when to turn the relay on and off all by itself.

Every 250ms or so or whenever it needs to know, the host send the micro a MASH_TEMP_STATUS message. When the micro receives this, it replies with a MASH_TEMP_STATUS_REPLY message that gives the status (OK, ERROR, etc.) as well as the current temp, say 152F.

When the host doesn't want the micro to control temp anymore, it sends MASH_TEMP_CONTROL_OFF.

Compartmentalizing things like this really simplifies them. Now there are only 2 loops in the system. One is on the host and its just to update things. Or when the user pushes a button. The other loop is on the micro and it just does its control thing and replies to the host.

As soon as you get the host in the control loop it gets real messy. PC operating systems weren't designed to be real time control systems, except maybe real time Linux. But that is beyond the scope of this project.

I'm sorry I don't have time to work on this more. I feel badly that you are working on this alone. If you have questions ask.
 
brewman ! said:
I'm sorry I don't have time to work on this more. I feel badly that you are working on this alone. If you have questions ask.
Don't sweat it at all. I'm really enjoying the project. The learning curve is pretty steep, but that's a good thing. I look forward to sharing more of my results, and I can't wait to see what you guys come up with.
 
Are you guys going to have your program do a step mash and control all the valves and burners? I found this forum because I have been think of doing the same thing, you guys are much farther ahead of me though.

Dave
 
Yuri_Rage said:
Don't sweat it at all. I'm really enjoying the project. The learning curve is pretty steep, but that's a good thing. I look forward to sharing more of my results, and I can't wait to see what you guys come up with.


:confused:

Is anyone else feeling like they've drank too much??

Honestly seeing this stuff I wish I would have taken some programming classes or something. I'm just completely clueless about this.

Yuri - will you be selling this once it functions correctly?
 
Todd said:
Yuri - will you be selling this once it functions correctly?
Not sure if I'll sell it or not. I might consider it, but it'll be pricy, and I'll really have to put a lot of thought into making it somewhat universal.
 
Are you guys going to have your program do a step mash and control all the valves and burners?

I am going to. Its probably not going to happen until fall now though. I'll see how things go this summer.
 
Same here. I'm going to try and get it ready for its first run this weekend. I have a bit of work to do before then. Right now I have a pile of wires and some unfinished code in front of me.
 
My project for next winter is to learn C++ and the visual C environment. I really need another language in my repertoire.

Awesome stuff guys. After I learn C, I need to learn hardware.
 
Visual C++ sucks for occasional development. There is nothing visual about it and you are always dealing with handles and really weird, complicated structures.

If you want something to allow you to build Windows GUIs, I recommend C++ Builder, which is visual, ironically, Visual Basic, which is again visual or Java via Eclipse with the Visual Editor package.

Another option is to use QT, but then you have to develop in Linux (using Kdevelop or similar) but what you develop will run on Windows.
 
Brew day is tomorrow! It's almost midnight, and I just finished wiring what I need to test this concept under actual conditions. The software is still REALLY rough, but it will automatically maintain a mash temperature as long as there is steam available. The software will control the boiler heating element, but I have to manually click to turn it on and off while monitoring the pressure. That will be automated soon enough, I just ran out of time tonight.

Here are the latest pictures. I wanted to put this into an old desktop computer tower, but I can't find the one I thought I had stashed. So, I hacked together a wooden crate for now. With the present wiring, I can control one 220V circuit and two 110V circuits while monitoring two sensors. At some point, I'm going to add the capability for up to six sensors and four(ish) 110V circuits. This should be enough to get me through tomorrow, though.

4688-mashmonitorwiring.JPG


4688-mashmonitorwiring1.JPG
 
Unofficial results are in!

The steam rig works REALLY well. However, stirring is a MUST! I thought I'd ruined my beer when I heated the mash from 95 degrees F (the rest was unnecessary, except to test the steam concept) to 145 degrees F. It only took 5 minutes for the temperature to register 145, but then it kept climbing to 180(!!!) with the steam turned off. The top of the mash was still cool enough to stick my finger into it, so I decided to stir...aggressively. The temperature reading quickly dropped to 150, then 145, so all was well. I just had a REALLY hot spot. The system requires quite a bit of attention (stirring), though it was intended to be automatic. Guess I need to incorporate a mash rake...

Anyway, I think it turned out fine. Everything worked as well as could be expected, and I think I overshot 75% efficiency (haven't gotten a corrected OG reading yet, but it's looking a bit high).

The obligatory pictures:

4688-automaticsteammashing.JPG


4688-automaticsteammashing1.JPG
 
kladue said:
congratulations on the succesful test run, were you able to track boiler pressure during steam injection.
Absolutely! I was maintaining 11-12 psi with the valves closed. During injection, the pressure would drop to 8 psi fairly quickly, then drop slowly after that. I had at least 10 minutes of steam at any given time, which was more than required for each mash step.

I was not able to successfully track the volume in the mash tun. The steam would create bubbles in the mash, making the mash tun look more full than it actually was. As soon as I started recirculating, the level dropped quite a bit. I don't think I added much water (via steam) to the mash during the entire process.
 
With most of the liquid tied up in the mash was the steam injection very noisy aside from the warmup snaps and pops. I suppose if one wanted to do the math you could figure out the water/steam necessary to move the heat into the mash. Do you have any plans to reduce the steam flow to maintain boiler pressure.
 
kladue said:
With most of the liquid tied up in the mash was the steam injection very noisy aside from the warmup snaps and pops. I suppose if one wanted to do the math you could figure out the water/steam necessary to move the heat into the mash. Do you have any plans to reduce the steam flow to maintain boiler pressure.
Actually, it was very smooth, even with the valve wide open. Recovery time was very minimal (~2 minutes), so I don't think I'm going to worry about reducing the flow.

BTW...80% Efficiency!!!
 
I wonder if the higher efficiency is related to the localized overheated mash liberating more of the starches from the grains while the bulk of the enzymes are preserved away from the hot spots, kind of like a localized decoction mash.
 
Most of the code I used today is actually already posted above. The sensors were an LM34 temp sensor in the mash and a pressure sensor from eBay. I'm going to modify the code to rely more on the microcontroller for automation (i.e., if the computer connection was lost, everything would be maintained). As it is now, the computer does most of the automation.
 
Ahh I started code a while back for an antonymous A but got sidelined due to vacations and such.. :) I stared running into trouble with byte size then started working on just client side software to control the A then just forgot about it. :drunk:

Ihmo i do think the A should be antonymous though, and send sensor data to the client side every other loop or so.

When i started planning mine i planned on 2 temp sensors in the mash tun( upper and lower, or one lower and one that i could turn on and off for spot temp checking.) 1 in hwt, 1 in boiling tank, then a outside abient temp and humidity sensor, to guesstimate water loss from boil etc.. I also thought about hacking a ph probe for the system :)

I may need to start taking a look at it agian...
 
Another successful steam masher. That's 3 of us now, right ?

What did you mean by mash volume ? Provided your boiler isn't pushing water out, the volume of water in the steam you injector is just about nothing compared to the mash volume.
 
Wow -- nice job Yuri. Very humbling to see *REAL* DIY projects in action!

I may have missed it, but was that a 5 gallon batch you brewed with the steam system? If so, that's incredible performance with the steam system (more than 2 - 3 times what I got out of my simple stove-top steam setup). Very nice setup.

Got a total parts list? Ha ha, just kidding. :D

Thanks for posting that -- great thread!!
 
Yuri_Rage said:
FlyGuy said:
I may have missed it, but was that a 5 gallon batch you brewed with the steam system?
It was a 15 gallon batch with a 40 lb grain bill. It impressed even me!

Wow -- that totally rocks man! 15 gallon temp step in < 10 mins? Holy shiite.

:fro:
 
Wow -- that totally rocks man! 15 gallon temp step in < 10 mins? Holy shiite.

*laughs I don't mean to be a know it all, but I told you guys it would work like that !
 
Back
Top