I should mention the idea of building a computer controlled brewery came from Howard's excellent post here. If it wasn't for his very well documented write up of his set-up I would have thought both the software and hardware were way beyond me, cheers Howard

Here is the code I have cobbled together:
Code: Select all
/*-------------------------------------------------------------
Release 1 03/4/2012
Semi-automated HERMS (Heat Exchange Recirculating Mash System)
brewery and sous vide cooker.
Developed with Arduino IDE release 0023 and associated libraries
All code published as open source- do with it what you may!
by Jamie Abbott (j.abbott(at)dsl.pipex.com) 2011-12
--------------------------------------------------------------*/
#include <LiquidCrystal.h>
#include <PID_v1.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Bounce.h>
#include <avr/pgmspace.h>
#define ONE_WIRE_BUS 8 //One-Wire bus for 3 DS18b20 tempreature sensors on pin 8
OneWire oneWire(ONE_WIRE_BUS); //setup a oneWire instance to communicate with any OneWire devices
DallasTemperature sensors(&oneWire); //pass oneWire reference to Dallas Temperature.
float HLTtemp, Mash_CookTemp, BoilTemp,value; //define names of temp. sensors
LiquidCrystal lcd (2,3,4,5,6,7); //setup LCD display pins
const boolean PotPin=A0; //pump speed potentiometer
const boolean button_pin1=A5; //
const boolean button_pin2=A4; //buttons on analogue pins
const boolean button_pin3=A3; //
Bounce button1 = Bounce(button_pin1,5); //
Bounce button2 = Bounce(button_pin2,5); //setup de-bouncing on buttons
Bounce button3 = Bounce(button_pin3,5); //
const boolean HLTrelay=11; //
const boolean Mash_CookRelay=12; //solid State Relays controlling heating elements on analouge pins
const boolean BoilerRelay=13; //
const byte buzzer=9; //buzzer alarm to alert when a brew process has finished
const byte HERMSpumpFET=10; //MOSFET to control solar pump that re-circs wort around HERMS kettle control
unsigned short HLTtarget=78; //default mash liqour target temp. (hot enough to pre-heat mash tun)
unsigned short MashTarget=66; //default target mash temp.
unsigned short BoilTarget=100; //default boil target temp.
unsigned short SpargeTarget=75; //default sparge liqour target temp.
unsigned short MashTime=90; //default mash duration
unsigned short BoilTime=90; //default boil duration
unsigned short ChillTarget=25; //default target cool temp.
unsigned short PotValue; //to hold value from pump potentiometer
unsigned short PumpDisplay; //value as % of pump speed
unsigned short CookTarget=57; //default target cook temp.
unsigned long CookTime; //to store cooking duration
unsigned long CookHrs=2; //default cook time hours
unsigned long CookMins=30; //default cook time minutes
long time;
unsigned long MashStartTime, BrewEndTime, BoilStartTime, BrewStartTime, BrewDuration, CookStartTime; //brewday finish time
unsigned long lastPIDCalculation = 0;
boolean HLTon = true;
boolean BoilOff = true;
boolean ChillOn = true;
boolean set = true;
boolean CookTargetSet = true;
boolean CookHrsSet = true;
boolean CookMinsSet = true;
boolean state = true;
boolean BoilPause = false;
#define COOK 1 //menu mode for cooking sous vide
#define BREW 2 //menu mode for brewing
byte mode=0; //initalise menu mode
byte CookMenu=1; //initalise brew menu
byte BrewMenu=1; //initalise brew menu
double Setpoint, Input, Output; //
PID myPID(&Input, &Output, &Setpoint,5,2,1, DIRECT); //Specifies the PID links and initial tuning parameters
int WindowSize=2500; //
unsigned long windowStartTime; //
byte CustomChar[8]={ //
B00010, //
B01110, //
B10001, //Creates letter 'é'
B11111, //
B10000, //
B01110}; //
int eAcute; // //
//Text strings below stored in flash memory to save sapce in SRAM
const prog_uchar s01[] PROGMEM =" Welcome!";
const prog_uchar s02[] PROGMEM =" what do you want";
const prog_uchar s03[] PROGMEM =" to do?";
const prog_uchar s04[] PROGMEM ="COOK S-V BREW";
const prog_uchar s05[] PROGMEM =" Welcome to Jamie's";
const prog_uchar s06[] PROGMEM =" Brewery" ;
const prog_uchar s07[] PROGMEM =" Press any button";
const prog_uchar s08[] PROGMEM =" to go forward ";
const prog_uchar s09[] PROGMEM =" Welcome to Jamie's ";
const prog_uchar s10[] PROGMEM =" Sous Vide Cooker! ";
const prog_uchar s11[] PROGMEM =" Press any button ";
const prog_uchar s12[] PROGMEM =" to go forward ";
const prog_uchar s13[] PROGMEM =" HLT: ";
const prog_uchar s14[] PROGMEM =" MT: ";
const prog_uchar s15[] PROGMEM =" Boiler: ";
const prog_uchar s16[] PROGMEM =" PRESS ANY BUTTON";
const prog_uchar s17[] PROGMEM ="Ready to start mash!";
const prog_uchar s18[] PROGMEM ="Prime pump&fill HLT ";
const prog_uchar s19[] PROGMEM =" for sparge liquor ";
const prog_uchar s20[] PROGMEM =" ";
const prog_uchar s21[] PROGMEM =" HLT Setup ";
const prog_uchar s22[] PROGMEM ="Current Temp:";
const prog_uchar s23[] PROGMEM =" UP SET DOWN";
const prog_uchar s24[] PROGMEM =" Mash Setup ";
const prog_uchar s25[] PROGMEM =" Boil Setup ";
const prog_uchar s26[] PROGMEM ="Current Time:";
const prog_uchar s27[] PROGMEM =" Setup compleate";
const prog_uchar s28[] PROGMEM =" Ready to brew!";
const prog_uchar s29[] PROGMEM =" Is HLT Filled? ";
const prog_uchar s30[] PROGMEM ="BACK START";
const prog_uchar s31[] PROGMEM =" Mash liquor heating";
const prog_uchar s32[] PROGMEM ="Mash liquor ready!!!";
const prog_uchar s33[] PROGMEM =" HLT temp:";
const prog_uchar s34[] PROGMEM =" Target:";
const prog_uchar s35[] PROGMEM ="CHANGE OFF MASH";
const prog_uchar s36[] PROGMEM ="CHANGE ON MASH";
const prog_uchar s37[] PROGMEM ="CHANGE ON END";
const prog_uchar s38[] PROGMEM ="CHANGE OFF END";
const prog_uchar s39[] PROGMEM =" Mash temp:";
const prog_uchar s40[] PROGMEM =" Time left:";
const prog_uchar s41[] PROGMEM ="BACK CHANGE STOP";
const prog_uchar s42[] PROGMEM =" Mash Complete! ";
const prog_uchar s43[] PROGMEM =" BACK HLT ON BOIL";
const prog_uchar s44[] PROGMEM =" START ";
const prog_uchar s45[] PROGMEM ="BACK SPARGE";
const prog_uchar s46[] PROGMEM =" Sparge in ";
const prog_uchar s47[] PROGMEM =" Progress ";
const prog_uchar s48[] PROGMEM =" START";
const prog_uchar s49[] PROGMEM =" BACK HLT OFF BOIL";
const prog_uchar s50[] PROGMEM =" Boil temp:";
const prog_uchar s51[] PROGMEM ="BACK CHILL";
const prog_uchar s52[] PROGMEM ="BACK OFF END";
const prog_uchar s53[] PROGMEM ="BACK ON END";
const prog_uchar s54[] PROGMEM =" Boil finished!";
const prog_uchar s55[] PROGMEM ="CHANGE ON CHILL";
const prog_uchar s56[] PROGMEM ="CHANGE OFF CHILL";
const prog_uchar s57[] PROGMEM =" Chilling ";
const prog_uchar s58[] PROGMEM =" Chilling Finished! ";
const prog_uchar s59[] PROGMEM =" Wort temp:";
const prog_uchar s60[] PROGMEM =" Boil Finished! ";
const prog_uchar s61[] PROGMEM =" BREWDAY FINISHED! ";
const prog_uchar s62[] PROGMEM =" Time:";
const prog_uchar s63[] PROGMEM =" Here's to a good ";
const prog_uchar s64[] PROGMEM =" Beer! ";
const prog_uchar s65[] PROGMEM =" Sparge Liquor ";
const prog_uchar s66[] PROGMEM =" Temp:";
const prog_uchar s67[] PROGMEM ="BACK";
const prog_uchar s68[] PROGMEM =" UP HOURS DOWN";
const prog_uchar s69[] PROGMEM =" UP MINS DOWN";
const prog_uchar s70[] PROGMEM =" Cooking Complete ";
const prog_uchar s71[] PROGMEM =" Cook Temp:";
const prog_uchar s72[] PROGMEM =" Cook Time:";
const prog_uchar s73[] PROGMEM =" Change mins";
const prog_uchar s74[] PROGMEM ="BACK CHANGE OFF";
const prog_uchar s75[] PROGMEM ="BACK CHANGE ON";
const prog_uchar s76[] PROGMEM ="Pump";
const prog_uchar s77[] PROGMEM =" Bon app tit! ";
void PrintText (const prog_uchar str[]){ //
char c; //
if(!str) return; //print text stored in ProgMem
while((c = pgm_read_byte(str++))) //
lcd.print(c,BYTE);} //
//-------------------------------------//
void StartScreen(){
/* Welcome!
what do you want
to do?
COOK S-V BREW */
lcd.clear();
PrintText (s01);
lcd.setCursor (0,1);
PrintText (s02);
lcd.setCursor (0,2);
PrintText (s03);
lcd.setCursor (0,3);
PrintText (s04);
while (mode==0){ //
if (button1.update()) { //Initiates Sous Vied cooking mode
if (button1.read()==HIGH) { //
mode=COOK; //
}
}
if (button3.update()) {
if (button3.read()==HIGH) {
mode=BREW;
}
}
}
}
//-------------------------------------//
void CookWelcome(){
/* Welcome to Jamie's
Sous Vide Cooker!
Press any button
to go forward */
lcd.clear();
while (CookMenu==1){
PrintText (s09);
lcd.setCursor(0,1);
PrintText(s10);
lcd.setCursor(0,2);
PrintText (s11);
lcd.setCursor(0,3);
PrintText (s12);
if (button1.update()||button2.update()||button3.update()) {
if ((button1.read()||button2.read()||button3.read())==HIGH){
CookMenu++;
CookStartTime=millis();
}
}
}
}
//-------------------------------------//
void Cooking(){
/* Cook Temp: 56.2°C
Target: 56.0°C
Time Left: 1h 38m
BACK CHANGE OFF */
lcd.clear();
float h,m;
unsigned long over;
CookTime=(CookHrs*3600000)+(CookMins*60000);
CookHrsSet=false;
CookMinsSet=false;
while (CookMenu==2){
if (sensors.getDeviceCount()==0){ //
lcd.setCursor(0,0); //
PrintText (s20); //
lcd.setCursor(0,1); // Display warning
lcd.print(" *temprature probe* "); // if temp probe
lcd.setCursor(0,2); // not attached
lcd.print(" *not attached!* "); //
lcd.setCursor(0,3); //
PrintText (s20); //
}else{
time = CookTime - (millis()-CookStartTime)/60000;
h=int(time/3600000);
over=time%3600000;
m=int(over/60000);
sensors.requestTemperatures();
Mash_CookTemp=sensors.getTempCByIndex(2);
Input = Mash_CookTemp;
Setpoint = CookTarget;
myPID.Compute();
if(millis() - windowStartTime>WindowSize){
windowStartTime += WindowSize;}
if(Output > millis() - windowStartTime) digitalWrite(Mash_CookRelay,HIGH);
else digitalWrite(Mash_CookRelay,LOW);
PrintText(s71);
lcd.print(Mash_CookTemp);
lcd.setCursor(16,0);
degC();
lcd.setCursor(0,1);
PrintText(s34);
lcd.print(CookTarget);
lcd.setCursor(0,2);
PrintText(s40);
lcd.print(h,0);
lcd.print("h ");
lcd.print(m,0);
lcd.print("m ");
lcd.setCursor(0,3);
PrintText(s74);
PumpControl(); //HERMS pump used to recirculate water to maintain uniform water temp.
CookMenuButons(); //back - forward buttons
if (button2.update()){ //
if (button2.read()==HIGH){ //Change target cooking temp.
CookTargetSet=false; //
}
}
/* Cook Temp: 56.2°C
Target: 58.0°C
Time Left: 2h 38m
UP HOURS DOWN */
while (!CookTargetSet){
lcd.setCursor(0,3);
PrintText(s68);
if (button1.update()) {
if (button1.read()) {
CookTarget++;
}
}
if (button3.update()) {
if (button3.read()) {
CookTarget--;
}
}
lcd.setCursor(12,1);
lcd.print(CookTarget);
degCTarget();
if (CookTarget > 100){
CookTarget=0;}
if (CookTarget < 100){
lcd.setCursor(14,1);
degCTarget();}
if (CookTarget < 10){
lcd.setCursor(13,1);
degCTarget();}
if (button2.update()) {
if (button2.read()){
lcd.setCursor(0,3);
PrintText(s68);
CookTargetSet=true;
CookMenu++;
}
}
}
if (time<60000)CookMenu=5;
}
}
}
//-------------------------------------//
void CookTimeSetHours(){
/* Cook Temp: 56.2°C
Target: 56.0°C
Time Left: 3h 8m
UP MINS DOWN */
while (CookMenu==3){
lcd.setCursor(12,2);
lcd.print(CookHrs);
if (CookHrs > 72){
CookHrs=0;}
if (CookHrs < 10){
lcd.setCursor(11,2);}
if (CookHrs < 0){
CookHrs = 72;}
lcd.setCursor(0,3);
PrintText(s69);
if (button1.update()) {
if (button1.read()) {
CookHrs++;
}
}
if (button3.update()) {
if (button3.read()) {
CookHrs--;
}
}
if (button2.update()) {
if (button2.read()){
lcd.setCursor(0,3);
PrintText(s69);
CookMenu++;
}
}
}
}
//-------------------------------------//
void CookTimeSetMins(){
/* Cook Temp: 56.2°C
Target: 56.0°C
Time Left: 3h 15m
UP SET DOWN */
while (CookMenu==4){
lcd.setCursor(15,2);
lcd.print(CookMins);
if (CookMins > 59){
CookMins=0;}
if (CookMins < 1){
CookMins=59;}
if (CookHrs < 10){
lcd.setCursor(14,2);
degCTarget();}
lcd.setCursor(0,3);
PrintText(s23);
if (button1.update()) {
if (button1.read()) {
CookMins++;
}
}
if (button3.update()) {
if (button3.read()) {
CookMins--;
}
}
if (button2.update()) {
if (button2.read()){
CookMenu=2;
}
}
}
}
//-------------------------------------//
void CookFinished(){
/* Cooking Complete
Bon appétit! */
lcd.clear();
digitalWrite(Mash_CookRelay,LOW);
while (CookMenu==5){
PrintText(s70);
lcd.setCursor(0,1);
lcd.print(" ");
lcd.setCursor(0,3);
PrintText(s77);
lcd.setCursor(0,3);
lcd.setCursor(10,3);
lcd.write(eAcute);
}
}
//-------------------------------------//
void BrewWelcome(){
/* Welcome to Jamie's
Brewery
Press any button
to go forward */
lcd.clear();
PrintText(s05);
lcd.setCursor(0,1);
PrintText(s06);
lcd.setCursor(0,2);
PrintText(s07);
lcd.setCursor(0,3);
PrintText(s08);
while (BrewMenu==1){
if (button1.update()||button2.update()||button3.update()) {
if ((button1.read()||button2.read()||button3.read())==HIGH){
BrewMenu++;
}
}
}
}
//-------------------------------------//
void BrewStart(){
/* HLT: 22.5°C
MT: 21.8°C
Boiler: 22.1°C
PRESS ANY BUTTON */
BrewStartTime = millis();
lcd.clear();
lcd.setCursor(0,0);
PrintText(s13);
lcd.setCursor(0,1);
PrintText(s14);
lcd.setCursor(0,2);
PrintText(s15);
lcd.setCursor(0,3);
PrintText(s16);
while (BrewMenu==2){
if (sensors.getDeviceCount()<3){ //
lcd.setCursor(0,0); //
PrintText (s20); //
lcd.setCursor(0,1); // Display warning if
lcd.print("*temprature probes*"); // all 3 temp probes
lcd.setCursor(0,2); // are not attached
lcd.print(" * not attached!* "); //
lcd.setCursor(0,3); //
PrintText (s20); //
}else{
sensors.requestTemperatures();
HLTtemp=sensors.getTempCByIndex(1);
Mash_CookTemp=sensors.getTempCByIndex(2);
BoilTemp=sensors.getTempCByIndex(0);
lcd.setCursor(11,0);
lcd.print (HLTtemp);
if (HLTtemp<10){
lcd.setCursor(14,0);
degC();
}else{
lcd.setCursor(15,0);
degC();}
lcd.setCursor(11,1);
lcd.print(Mash_CookTemp);
if (Mash_CookTemp<10){
lcd.setCursor(14,1);
degC();
}else{
lcd.setCursor(15,1);
degC();}
lcd.setCursor(11,2);
lcd.print(BoilTemp);
if (BoilTemp<10){
lcd.setCursor(14,2);
degC();
}else{
lcd.setCursor(15,2);
degC();}
if (button1.update()||button2.update()||button3.update()) {
if ((button1.read()||button2.read()||button3.read())==HIGH){
BrewMenu++;
}
}
}
}
}
//-------------------------------------//
void MashLiquorHeating(){
/* Mash liquor heating
HLT temp:62.7°C
Target:78.0°C
CHANGE OFF MASH */
digitalWrite(Mash_CookRelay,LOW);
digitalWrite(BoilerRelay,LOW);
analogWrite (HERMSpumpFET,0);
lcd.clear();
while (BrewMenu == 3){
sensors.requestTemperatures();
HLTtemp=sensors.getTempCByIndex(1);
lcd.setCursor(0,1);
PrintText(s33);
lcd.print(HLTtemp);
if (HLTtemp<10){
lcd.setCursor(15,1);
degC();
}else{
lcd.setCursor(16,1);
degC();}
lcd.setCursor(0,2);
PrintText(s34);
lcd.print(HLTtarget);
degCTarget();
if (button3.update()) {
if (button3.read() == HIGH) {
BrewMenu++;
}
}
if (button1.update()) {
if (button1.read() == HIGH) {
set=false;
}
}
while (!set){
lcd.setCursor(0,3);
PrintText(s23);
if (button1.update()) {
if (button1.read()) {
HLTtarget++;
}
}
if (button3.update()) {
if (button3.read()) {
HLTtarget--;
}
}
lcd.setCursor(12,2);
lcd.print(HLTtarget);
if (HLTtarget<10){
lcd.setCursor(14,2);
degCTarget();
}else{
lcd.setCursor(15,2);
degCTarget();}
if (HLTtarget > 100){
HLTtarget=0;}
if (HLTtarget < 100){
lcd.setCursor(14,2);
degCTarget();}
if (HLTtarget < 10){
lcd.setCursor(13,2);
degCTarget();}
if (button2.update()) {
if (button2.read()){
set=true;
}
}
}
if (button2.update()) {
if (button2.read() == HIGH) {
if (!HLTon) HLTon=true;
else HLTon=false;
}
}
HTLcontrol();
}
}
//-------------------------------------//
void HTLcontrol(){
/* Mash liquor ready!!!
HLT temp:78.1°C
Target:78.0°C
CHANGE ON MASH */
lcd.setCursor(0,3);
analogWrite (HERMSpumpFET,0);
if (!HLTon){
PrintText(s36);
digitalWrite(HLTrelay,LOW);
}else{
PrintText(s35);
if (HLTtemp > HLTtarget){
digitalWrite(buzzer,HIGH);
digitalWrite(HLTrelay,LOW);
lcd.setCursor(0,0);
PrintText(s32);
}else{
digitalWrite(HLTrelay,HIGH);
digitalWrite(buzzer,LOW);
lcd.setCursor(0,0);
PrintText(s31);
}
}
}
//-------------------------------------//
void PumpControl(){ // Controls pump speed via MOSFET by reading potentiometer value
PotValue = analogRead(PotPin);
PotValue = map(PotValue, 0, 1023, 0, 255);
analogWrite (HERMSpumpFET,PotValue);
}
//-------------------------------------//
void MashStart(){
/* Ready to start mash!
Prime pump&fill HLT
for sparge liquor
PRESS ANY BUTTON */
lcd.clear();
digitalWrite(buzzer,LOW);
digitalWrite(HLTrelay,LOW);
while (BrewMenu==4){
PrintText(s17);
lcd.setCursor(0,1);
PrintText(s18);
lcd.setCursor(0,2);
PrintText(s19);
lcd.setCursor(0,3);
PrintText(s16);
if (button1.update()||button2.update()||button3.update()) {
if ((button1.read()||button2.read()||button3.read())==HIGH){
BrewMenu++;
}
}
}
}
//-------------------------------------//
void Mashing(){
/* Mash temp:65.8°C
Target:66.0°C
Time left:38mins
BACK CHANGE STOP */
lcd.clear();
MashStartTime=millis();
while (BrewMenu==5){
sensors.requestTemperatures();
Mash_CookTemp=sensors.getTempCByIndex(2);
HLTtemp=sensors.getTempCByIndex(1);
Input = Mash_CookTemp;
Setpoint = MashTarget;
myPID.Compute();
if(millis() - windowStartTime>WindowSize){
windowStartTime += WindowSize;}
if(Output > millis() - windowStartTime) digitalWrite(Mash_CookRelay,HIGH);
else digitalWrite(Mash_CookRelay,LOW);
time = MashTime - (millis()-MashStartTime)/60000;
PrintText(s39);
lcd.setCursor(12,0);
lcd.print(Mash_CookTemp);
lcd.setCursor(16,0);
degC();
lcd.setCursor(0,1);
PrintText(s34);
lcd.print(MashTarget);
degCTarget();
lcd.setCursor(0,2);
PrintText(s40);
lcd.print (time);
lcd.print("mins ");
lcd.setCursor(0,3);
PrintText(s41);
SpargeLiquorHeating(); //heat sarge liquor whilst mashing
PumpControl();
if (time<1)BrewMenu++, analogWrite (HERMSpumpFET,0);
if (button1.update()) {
if (button1.read()) {
digitalWrite (Mash_CookRelay, LOW);
analogWrite (HERMSpumpFET,0);
BrewMenu--;
}
}
if (button3.update()) {
if (button3.read()) {
digitalWrite (Mash_CookRelay, LOW);
analogWrite (HERMSpumpFET,0);
BrewMenu++;
}
}
if (button2.update()) {
if (button2.read() == HIGH) {
set=false;
while (!set){
lcd.setCursor(0,3);
PrintText(s23);
if (button1.update()) {
if (button1.read()) {
MashTarget++;
}
}
if (button3.update()) {
if (button3.read()) {
MashTarget--;
}
}
lcd.setCursor(12,1);
lcd.print(MashTarget);
degCTarget();
if (MashTarget > 100){
MashTarget=0;}
if (MashTarget < 100){
lcd.setCursor(14,1);
degCTarget();
}
if (MashTarget < 10){
lcd.setCursor(13,1);
degCTarget();
}
if (button2.update()) {
if (button2.read()){
lcd.setCursor(0,0);
set=true;
}
}
}
}
}
}
}
//-------------------------------------//
void SpargeLiquorHeating(){
digitalWrite(BoilerRelay,LOW);
sensors.requestTemperatures();
HLTtemp=sensors.getTempCByIndex(1);
if (HLTtemp < SpargeTarget){
digitalWrite (HLTrelay, HIGH);}
if (HLTtemp > SpargeTarget+5){
digitalWrite (HLTrelay, LOW);}
}
//-------------------------------------//
void MashFinished(){
/* Mash Complete!
START
BACK SPARGE */
digitalWrite(buzzer,HIGH);
digitalWrite(Mash_CookRelay,LOW);
digitalWrite(HLTrelay,LOW);
lcd.clear();
while (BrewMenu==6){
PrintText(s42);
lcd.setCursor(0,2);
PrintText(s44);
lcd.setCursor(0,3);
PrintText(s45);
BrewMenuButons();
}
}
//-------------------------------------//
void Sparging(){
/* Sparge in
Progress
START
BACK HLT OFF BOIL */
digitalWrite(buzzer,LOW);
lcd.clear();
while (BrewMenu==7){
SpargeLiquorHeating();
PrintText(s46);
lcd.setCursor(0,1);
PrintText(s47);
lcd.setCursor(0,2);
PrintText(s48);
BrewMenuButons();
SpargeLiquorHeating();
if (button2.update()) {
if (button2.read() == HIGH);{
digitalWrite(HLTrelay, HIGH);
lcd.setCursor(0,3);
PrintText(s49);}
}else{
digitalWrite(HLTrelay, LOW);
lcd.setCursor(0,3);
PrintText(s43);}
}
}
//-------------------------------------//
void Boiling(){
/* Boil temp:88.0°C
Target:100.0°C
Time left:89mins
BACK CHANGE STOP */
digitalWrite(HLTrelay,LOW);
digitalWrite(Mash_CookRelay,LOW);
digitalWrite(buzzer,LOW);
lcd.clear();
while (BrewMenu==8){
digitalWrite(HLTrelay,LOW);
digitalWrite(Mash_CookRelay,LOW);
sensors.requestTemperatures();
BoilTemp=sensors.getTempCByIndex(0);
if (BoilTemp > 95){
BoilStartTime=millis();}
time = BoilTime - (millis()-BoilStartTime)/60000;
PrintText(s50);
lcd.setCursor(12,0);
lcd.print(BoilTemp);
if (BoilTemp >= 100){lcd.setCursor(17,0);
degC();
}else{
lcd.setCursor(16,0);
degC();}
lcd.setCursor(0,1);
PrintText(s34);
lcd.setCursor(12,1);
lcd.print(BoilTarget);
degCTarget();
lcd.setCursor(0,2);
PrintText(s40);
lcd.setCursor(12,2);
lcd.print(time);
lcd.print("mins ");
lcd.setCursor(0,3);
PrintText(s41);
if (time<1)BrewMenu++;
if (BoilTarget>BoilTemp){
digitalWrite(BoilerRelay, HIGH);
}else{
digitalWrite(BoilerRelay,LOW);}
if (button3.update()) {
if (button3.read()) {
BrewMenu++;
}
}
if (button1.update()) {
if (button1.read() == HIGH) {
digitalWrite (BoilerRelay,LOW);
BrewMenu--;
}
}
if (button2.update()) {
if (button2.read() == HIGH) {
set=false;
while (!set){
lcd.setCursor(0,3);
PrintText(s23);
if (button1.update()) {
if (button1.read()) {
BoilTarget++;
}
}
if (button3.update()) {
if (button3.read()) {
BoilTarget--;
}
}
lcd.setCursor(12,1);
lcd.print(BoilTarget);
degCTarget();
if (BoilTarget > 100){
BoilTarget=0;}
if (BoilTarget < 100){
lcd.setCursor(14,1);
degCTarget();}
if (BoilTarget < 10){
lcd.setCursor(13,1);
degCTarget();}
if (button2.update()){
if (button2.read()){
lcd.setCursor(0,0);
set=true;
}
}
}
}
}
}
}
//-------------------------------------//
void BoilFinished(){
/* Boil Finished!
BACK CHILL */
lcd.clear();
while (BrewMenu==9){
digitalWrite(buzzer,HIGH);
digitalWrite(HLTrelay,LOW);
digitalWrite(Mash_CookRelay,LOW);
digitalWrite(BoilerRelay,LOW);
PrintText(s60);
lcd.setCursor(0,3);
PrintText(s51);
BrewMenuButons();
}
}
//-------------------------------------//
void Chilling(){
/* Chilling
Wort temp:28.2°C
Target:25.0°C
BACK CHANGE STOP */
digitalWrite(buzzer,LOW);
lcd.clear();
while (BrewMenu==10){
digitalWrite(HLTrelay,LOW);
digitalWrite(Mash_CookRelay,LOW);
digitalWrite(BoilerRelay,LOW);
sensors.requestTemperatures();
BoilTemp=sensors.getTempCByIndex(0);
PrintText(s57);
lcd.setCursor(0,1);
PrintText(s59);
lcd.print(BoilTemp);
lcd.setCursor(17,1);
degC();
lcd.setCursor(0,2);
PrintText(s34);
lcd.print(ChillTarget);
degCTarget();
lcd.setCursor(0,3);
PrintText(s41);
if (button3.update()) {
if (button3.read() == HIGH) {
BrewMenu++;
}
}
if (button1.update()) {
if (button1.read() == HIGH) {
BrewMenu--;
}
}
if (button2.update()) {
if (button2.read() == HIGH) {
set=false;
while (!set){
lcd.setCursor(0,3);
PrintText(s23);
if (button1.update()) {
if (button1.read()) {
ChillTarget++;
}
}
if (button3.update()) {
if (button3.read()) {
ChillTarget++;
}
}
lcd.setCursor(12,2);
lcd.print(ChillTarget);
degCTarget();
if (ChillTarget > 100){
ChillTarget=0;}
if (ChillTarget < 100){
lcd.setCursor(14,2);
degCTarget();
}
if (ChillTarget < 10){
lcd.setCursor(13,2);
degCTarget();
}
if (button2.update()) {
if (button2.read()){
set=true;
}
}
}
if (button2.update()) {
if (button2.read() == HIGH) {
if (!ChillOn) ChillOn=true;
else ChillOn=false;
}
}
}
}
}
}
//-------------------------------------//
void ChillingControl(){
lcd.setCursor(0,3);
if (!ChillOn){
PrintText(s36);
digitalWrite(HLTrelay,LOW);
}else{
PrintText(s35);
if (BoilTemp > ChillTarget){
digitalWrite(buzzer,HIGH);
lcd.setCursor(0,0);
PrintText(s32);
}else{
digitalWrite(buzzer,LOW);
lcd.setCursor(0,0);
PrintText(s31);
}
}
}
//-------------------------------------//
void BrewdayFinished(){
/* BREWDAY FINISHED!
Time:6hrs 35mins
Here's to a good
Beer! */
float h,m;
unsigned long over;
BrewEndTime=millis();
BrewDuration=BrewEndTime-BrewStartTime;
h=int(BrewDuration/3600000);
over=BrewDuration%3600000;
m=int(over/60000);
lcd.clear();
while (BrewMenu==11){
PrintText(s61);
lcd.setCursor(0,1);
PrintText(s62);
lcd.print(h,0);
lcd.print("hrs ");
lcd.print(m,0);
lcd.print("mins");
lcd.setCursor(0,2);
PrintText(s63);
lcd.setCursor(0,3);
PrintText(s64);
}
}
void CookMenuButons(){ //Back and forward cook menu buttons
if (button1.update()){
if (button1.read() == HIGH){
if (CookMenu>1) CookMenu--;
}
}
if (button3.update()){
if (button3.read()==HIGH){
CookMenu++;
}
}
}
void BrewMenuButons(){ //Back and forward brew menu buttons
if (button1.update()){
if (button1.read()==HIGH){
if (BrewMenu>1) BrewMenu--;
}
}
if (button3.update()){
if (button3.read()==HIGH){
BrewMenu++;
}
}
}
void degC(){ //prints '°C' and masks the two
lcd.print((char)223); //least signigicant figures from
lcd.print("C "); //temp sensor reading
}
void degCTarget(){ //
lcd.print(".0"); //prints '0°C'
lcd.print((char)223); //
lcd.print("C "); //
}
void setup(){
lcd.begin(20,4);
pinMode (Mash_CookRelay,OUTPUT);
pinMode(HLTrelay,OUTPUT);
pinMode (BoilerRelay,OUTPUT);
windowStartTime = millis(); //
myPID.SetOutputLimits(0, WindowSize); //PID set-up
myPID.SetMode(AUTOMATIC); //
lcd.createChar(eAcute,CustomChar);//initialse custom letter 'é'
}
void loop(){
switch (mode) {
case 0:
StartScreen();
break;
case 1:
switch (CookMenu){
case 1:
CookWelcome ();
break;
case 2:
Cooking ();
break;
case 3:
CookTimeSetHours();
break;
case 4:
CookTimeSetMins();
break;
case 5:
CookFinished();
break;
}
break;
case 2:
switch (BrewMenu){
case 1:
BrewWelcome ();
break;
case 2:
BrewStart();
case 3:
MashLiquorHeating();
break;
case 4:
MashStart();
break;
case 5:
Mashing();
break;
case 6:
MashFinished();
break;
case 7:
Sparging();
break;
case 8:
Boiling();
break;
case 9:
BoilFinished();
break;
case 10:
Chilling();
break;
case 11:
BrewdayFinished();
break;
}
break;
}
}
The major bits of hardware are the 20x4 line HD44780 display and Maxim DS18B20 temp sensors. Both have code libraries freely available to download and use from the Arduino website. I also used a potentiometer and a MOSFET to control the speed of the solar pump and SSRs to switch the heating elements in the HLT, HERMS unit and boiler both are easy to interface with with the software.
Cheers,
Jamie