Home Brew Forums > Home Brewing Beer > Automated Brewing Forum > JBrew's Computer + Arduino Controlled Mashing System
Reply
 
Thread Tools
Old 01-10-2013, 01:59 PM   #1
jbrewkeggin
Feedback Score: 0 reviews
Recipes 
 
Join Date: Mar 2012
Location: Baltimore, MD
Posts: 29
Liked 1 Times on 1 Posts

Default JBrew's Computer + Arduino Controlled Mashing System

Greetings,

I wanted to share my Arduino based Mashing controller that I've been working on. The Arduino is controlled by an old laptop running a Python GUI that I made for pump controls and for setting a target temperature. I decided not to use the autotune library after reading some of the comments in this forum. I'm going to set my constants through trial and error like I would with an off the shelf PID controller. I tested the control loop a few times by duct taping my temperature probes to a fermentation heat belt. I used DS18b20 temperature sensors and they attach to my controller box via DB9 connectors.

Here's the Arduino code. Ignore the PID settings, since I plan to tweak those as I learn more from my data:
Code:
/* jbrewduino */
/* Developed by JBrew */ 

// This Arduino sketch reads serves 3 purposes:
// 1. Toggle On/Off a HLT recirculation pump 
// 2. Toggle On/Off a Mash recirculation + sparge pump
// 3. Read input from DS18B20 "1-Wire" digital temperature sensors 
// and control attached heating element based on temperature feed back.
// This will be accomplished via a PID control loop.

// Valid messages are as follows from the Python program:
// "1" means toggle pump 1 off/on
// "2" means toggle pump 2 off/on
// "3" means toggle heating element off/on
// Codes 80 - 212 are reserved for fahrenheit temperatures
//#define DEBUGG
// All inputs are provided by a Python brewing GUI that transmits serial messages.
#include <Time.h>
#include <Wire.h>
#include <PID_v1.h>
#include <OneWire.h> /* for reading 1-wire bytes */
#include <DallasTemperature.h> /* For parsing and printing temp from DS18B20 bytes */
#ifdef HAVE_SD
#include <SD.h>
#endif
// Data wire is plugged into pin 3 on the Arduino
#define ONE_WIRE_BUS 3

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);
#ifdef HAVE_SD
File dataFile;
char filename[40];
#endif
// Assign the addresses of your 1-Wire temp sensors.

DeviceAddress mashTemp = { 0x28, 0x9E, 0xAC, 0x08, 0x04, 0x00, 0x00, 0x2C };
DeviceAddress hltTemp = { 0x28, 0xFB, 0x1E, 0x15, 0x04, 0x00, 0x00, 0x6D };
String inputString = ""; /* input with temperature from Python GUI */
int newTarget = 0;
int first_write = 0;
int temp = 0;
int bytesSent = 0;
const int heatingPin = 6; /* pin in which the heating element is attached - PID output pin */
int tempLoop = 0; /* This is a global run flag for the temperature feedback loop */
/* We need a valid temperature string because we can also receive messages for toggling 
 * the heating elements and pumps */
int validTemp = 0; 
// On the Ethernet Shield, CS is pin 4. Note that even if it's not
// used as the CS pin, the hardware CS pin (10 on most Arduino boards,
// 53 on the Mega) must be left as an output or the SD library
// functions will not work.
#ifdef HAVE_SD
const int chipSelect = 4;
#endif
//PID Constants
int k_prop = 500;
int k_int = 0;
int k_dif = 0;

double Setpoint, Input, Output; /* PID variables */

//Specify the links and initial tuning parameters
PID myPID(&Input, &Output, &Setpoint,k_prop,k_int,k_dif, DIRECT);
int WindowSize = 1000; /* PID adjusts output between 0 and this window size*/

unsigned long windowStartTime;
unsigned long file_start;

//pumps
const int pumpOne = 5;
const int pumpTwo = 7;
int pumpOneOn = 0;
int pumpTwoOn = 0;

void setup(void)
{
  
  
  pinMode(heatingPin, OUTPUT);
  pinMode(pumpOne, OUTPUT);
  pinMode(pumpTwo, OUTPUT); 
  // start serial port
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  
  // Start up the library
  sensors.begin();
  // set the resolution to 10 bit (good enough?)
  sensors.setResolution(mashTemp, 10);
  sensors.setResolution(hltTemp, 10);
  inputString.reserve(30);
  
  //Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  pinMode(10, OUTPUT);
  
  // see if the card is present and can be initialized:
#ifdef HAVE_SD  
  if (!SD.begin(chipSelect)) {
    //Serial.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
#endif
  //Serial.println("card initialized.");

  windowStartTime = millis();
 
}

/* function that prints temperatures in human readable form */
void printTemperature(DeviceAddress deviceAddress)
{
  if(first_write == 0) {
    file_start = millis();
    first_write = 1;
  }
  float tempC = sensors.getTempC(deviceAddress);
  
  if (tempC == -127.00) {
    Serial.print("Error getting temperature");
  } else {
    //Serial.print("C: ");
    //Serial.print(tempC);
      Input = DallasTemperature::toFahrenheit(tempC);

    if (deviceAddress == mashTemp){
#ifdef HAVE_SD
      dataFile.print("MASH");
#endif
#ifdef DEBUGG
      Serial.print("m");
      Serial.print(Input);
      Serial.print("\n");
#endif
      bytesSent = Serial.write("m");
      bytesSent += Serial.write(Serial.print(Input, 2));
      bytesSent += Serial.write("\n");
    }
    if (deviceAddress == hltTemp) {
#ifdef HAVE_SD
      dataFile.print("HLT");
#endif
#ifdef DEBUGG
      Serial.print("h");
      Serial.print(Input);
      Serial.print("\n");
#endif
      bytesSent += Serial.write("h");
      bytesSent += Serial.write(Serial.print(Input,2));
      bytesSent += Serial.write("\n");
      
    }
#ifdef HAVE_SD
    dataFile.print(",");
    dataFile.print(Input);
    dataFile.print(",");
    dataFile.print(millis() - file_start);
    dataFile.print(",");
    dataFile.println(Output);
#endif
  }
}

void temperatureControl(){
  sensors.requestTemperatures();
  //Serial.print("HLT temperature is: ");
  printTemperature(hltTemp);  
 
      
  //Serial.print("Mash temperature is: ");
  printTemperature(mashTemp); 
  
  myPID.Compute();/* This must be called once per loop to compute the new output*/
  /************************************************
   * turn the output pin on/off based on pid output
   ************************************************/
  if(millis() - windowStartTime>WindowSize)
  { //time to shift the Relay Window
    windowStartTime += WindowSize;
  }
  if(Output > millis() - windowStartTime) {
#ifdef DEBUGG    
    Serial.println("turning heating element ON");
#endif
    Serial.write("HE1");
    Serial.write("\n");
    digitalWrite(heatingPin,HIGH); 
  }
  else {
#ifdef DEBUGG
    Serial.println("turning heating element OFF");
#endif
    Serial.write("HE0");
    Serial.write("\n");
    digitalWrite(heatingPin,LOW); 
    
    //Serial.println("turning heating element off");
  }
  
}

/*Loop that receives input temperature from Python program */
void receiveInput() {
  
  while (Serial.available() > 0) {
    /* once we have input we can start temp control loop */
    tempLoop = 1;
    // get the new byte
    char inChar = (char)Serial.read(); 
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag
    // so the main loop can do something about it:
    if (inChar == '\n') {
      //Serial.println(inputString); 
      newTarget = inputString.toInt();
      if (newTarget > 60 && newTarget < 215) {
#ifdef HAVE_SD
        if(dataFile) {
          dataFile.close();
        }
        sprintf(filename, "LOG-%02d%02d.csv", hour(), minute(), second());
        //Serial.println(filename);
        dataFile = SD.open(filename, FILE_WRITE);
        if(dataFile) {
          //Serial.println("New file opened successfully!");
          dataFile.print("kp = ");
          dataFile.print(k_prop);
          dataFile.print(", ki = ");
          dataFile.print(k_int);
          dataFile.print(", kd = ");
          dataFile.print(k_dif);
          
        } else {
          //Serial.println("Issue opening file..."); 
        }
#endif
        validTemp = 1;
        Setpoint = newTarget;
#ifdef HAVE_SD
        dataFile.print(", SP = ");
        dataFile.println(Setpoint);
#endif
        //tell the PID to range between 0 and the full window size
        myPID.SetOutputLimits(0, WindowSize); /* vary its output within a given range(1 secs here) */  
        //turn the PID on
        myPID.SetMode(AUTOMATIC); /*PID is on, MANUAL for off*/ 
        
      }
      else if (newTarget == 3) {
#ifdef DEBUGG
        Serial.println("Received Start/Stop Message!");
#endif        
        if (validTemp == 1) {
#ifdef HAVE_SD
          dataFile.close();
#endif
          Serial.write("HE0");
          Serial.write("\n");
          digitalWrite(heatingPin, LOW);
          validTemp = 0;
        }
        else {
#ifdef HAVE_SD      
          dataFile = SD.open(filename, FILE_WRITE);
#endif    
          Serial.write("HE1");
          Serial.write("\n");
          digitalWrite(heatingPin, HIGH);
          validTemp = 1;
        }
      } 
      else if (newTarget == 1) {
        if (pumpOneOn == 0) {
#ifdef DEBUGG 
          Serial.println("Turning pump 1 on");
#endif
          digitalWrite(pumpOne, HIGH);
          pumpOneOn = 1;
        }
        else {
#ifdef DEBUGG 
          Serial.println("Turning pump 1 off");
#endif
          digitalWrite(pumpOne, LOW);
          pumpOneOn = 0;
        }
      } 
      else if (newTarget == 2) {

        if (pumpTwoOn == 0) {
#ifdef DEBUGG 
          Serial.println("Turning pump 2 on");
#endif
          digitalWrite(pumpTwo, HIGH);
          pumpTwoOn = 1;
        }
        else {
#ifdef DEBUGG 
          Serial.println("Turning pump 2 off");
#endif
          digitalWrite(pumpTwo, LOW);
          pumpTwoOn = 0;
        }  
        
      } 
      inputString = "";
    } /* if (inChar == '\n') */  
  
  } /* while (Serial.available() > 0) */    
}

void loop(void)
{ 
  receiveInput();  
  if(tempLoop == 1 && validTemp == 1)
    temperatureControl();
  
}/* void loop */
Feel free to critique this or let me know that I need to change something.

Thanks!


Click image for larger version

Name:	2012-12-15_18-18-52_448.jpg
Views:	2441
Size:	28.5 KB
ID:	93376   Click image for larger version

Name:	2012-12-15_18-15-35_41.jpg
Views:	2676
Size:	39.5 KB
ID:	93377   Click image for larger version

Name:	2012-07-12_22-30-25_86.jpg
Views:	2989
Size:	38.3 KB
ID:	93378   Click image for larger version

Name:	ResizedImage_1357422768396.jpg
Views:	2501
Size:	45.5 KB
ID:	93379  
jbrewkeggin is offline
matho Likes This 
Reply With Quote
Old 04-12-2013, 02:26 PM   #2
prffalcao
Feedback Score: 0 reviews
Recipes 
 
Join Date: Apr 2013
Posts: 2
Default

Dear friend, I am starting with arduino. How did you made the software interative graphics runing on PC? Would you send me the code? my e-mail is prffalcao@gmail.com


prffalcao is offline
 
Reply With Quote
Old 04-12-2013, 06:31 PM   #3
alien
Feedback Score: 1 reviews
Recipes 
 
Join Date: Apr 2012
Location: Philadelphia, PA
Posts: 1,235
Liked 66 Times on 58 Posts
Likes Given: 61

Default

The python GUI looks very good. Can you show some code for that too, please?

I wrote a simple program in Processing to display the serial output from the Arduino PID library. It was very easy to get started with Processing, it is a lot like the Arduino programming language.
alien is offline
 
Reply With Quote
Old 04-12-2013, 07:45 PM   #4
jimmayhugh
Turgid Member
HBT_LIFETIMESUPPORTER.png
Feedback Score: 0 reviews
 
jimmayhugh's Avatar
Recipes 
 
Join Date: Feb 2011
Location: Las Vegas, NV
Posts: 696
Liked 69 Times on 59 Posts
Likes Given: 3

Default

Well done!!
__________________
TeensyNet Networked Controller
ButtCrack Brewery
And This Is Why We Drink...
Cogito, ergo bibere cervisia sum
jimmayhugh is offline
 
Reply With Quote
Old 04-13-2013, 06:24 AM   #5
birch
Feedback Score: 0 reviews
Recipes 
 
Join Date: Dec 2008
Location: Lincoln, NE
Posts: 21
Default

GUI looks very cool, what control library is that?
birch is offline
 
Reply With Quote
Old 04-14-2013, 07:35 PM   #6
jbrewkeggin
Feedback Score: 0 reviews
Recipes 
 
Join Date: Mar 2012
Location: Baltimore, MD
Posts: 29
Liked 1 Times on 1 Posts

Default

Quote:
Originally Posted by prffalcao View Post
Dear friend, I am starting with arduino. How did you made the software interative graphics runing on PC? Would you send me the code? my e-mail is prffalcao@gmail.com
I didn't see this post before. Are you still interested in getting the code?
jbrewkeggin is offline
 
Reply With Quote
Old 04-15-2013, 01:45 PM   #7
prffalcao
Feedback Score: 0 reviews
Recipes 
 
Join Date: Apr 2013
Posts: 2
Default

Sure I am. Thanks for your attention.

Paulo Falc„o
prffalcao is offline
 
Reply With Quote
Old 04-16-2013, 02:00 AM   #8
rockytoptim
HBT_SUPPORTER.png
Feedback Score: 0 reviews
 
rockytoptim's Avatar
Recipes 
 
Join Date: May 2009
Location: Livonia, MI
Posts: 821
Liked 95 Times on 76 Posts
Likes Given: 3

Default

I would be interested in the python gui as well.
__________________
On Tap: Bee Cave Robust Porter
On Tap: Bee Cave OktoberFest
On Tap: Bee Cave Kolsch
Primary: None
Up next: ????
rockytoptim is online now
 
Reply With Quote
Old 04-19-2013, 09:56 PM   #9
jbrewkeggin
Feedback Score: 0 reviews
Recipes 
 
Join Date: Mar 2012
Location: Baltimore, MD
Posts: 29
Liked 1 Times on 1 Posts

Default

I've been getting several requests for the GUI code, so I made it available here on git hub:

https://github.com/jbrewkeggin/jbrew_controls.git

Let me know if there are problems getting the code. I ran this GUI on an Ubuntu laptop, and I didn't use very good technique as far as the layout goes. Therefore, depending on your resolution, you may have some problems. Also, if you PM me your email address, I'd be happy to email you a .zip containing the code. Let me know how it goes!

Thanks again,

- jbrew
jbrewkeggin is offline
 
Reply With Quote
Old 05-13-2013, 04:01 AM   #10
cool_ece_friend
Feedback Score: 0 reviews
Recipes 
 
Join Date: Jul 2011
Location: El Paso, TX
Posts: 13
Liked 2 Times on 1 Posts
Likes Given: 2

Default

very cool jbrew. What Kilowatt heating element are you controlling with the arduino? Also, I see you have 3 SSRs connected to the arduino.. Would you be able to post a schematic of your control system for me to better understand your circuit? Thank you.


cool_ece_friend is offline
 
Reply With Quote
Reply


Thread Tools


Similar Threads
Thread Thread Starter Forum Replies Last Post
Arduino controlled fermentation chamber xrattiracer Fermenters 32 06-12-2015 04:13 PM
Stirplate with 5V computer fan and arduino jmarshall Chillers and Stir Plates 2 01-19-2014 01:11 PM
Arduino Controlled Homebrew Stir Plate ericxjbrewer Chillers and Stir Plates 3 11-14-2013 02:42 AM
Computer controlled yeast. Bennypapa Fermentation & Yeast 0 11-07-2011 12:33 PM
IDEA: Arduino controlled Wort Boiler dwmcqueen Brew Stands 1 10-25-2007 06:52 PM


Forum Jump

Newest Threads

LATEST SPONSOR DEALS