Brew computer Mk 2
Brew computer Mk 2
I posted some details of my orginal brew computer a while back, all very dodgy. Well the Mk2 is far nicer.
This is a simple box, it controls the HLT temperature and is a timer, it also controls my fermenter chamber (free fridge with heater). Why did I not use a normal temperature controller like everyone else, ummmm not sure
There are 3 bits - a temperature sensor that sits in the HLT
The solid state relay box that turns the mains on and off
And the controller box that has a microcontroller, display, and a few buttons.
Whole think probably cost about £70 for all the bits, I am happy to post all the build details and code if anyone is interested?
This is a simple box, it controls the HLT temperature and is a timer, it also controls my fermenter chamber (free fridge with heater). Why did I not use a normal temperature controller like everyone else, ummmm not sure
There are 3 bits - a temperature sensor that sits in the HLT
The solid state relay box that turns the mains on and off
And the controller box that has a microcontroller, display, and a few buttons.
Whole think probably cost about £70 for all the bits, I am happy to post all the build details and code if anyone is interested?
Re: Brew computer Mk 2
OK - but where to start - let's start with the solid state relays.
I use 2 relays so I can control the fermenter fridge, one is used for the fridge and one for the heater (I just have 60W light bulb). I also connect both the HLT and the boiler when brewing, but you do have to be careful here as the kettle elements take 10 Amp and if both are on at the same time you may blow the 13A fuse. I have a bit of clever code that makes sure that both are never on at the same time.
The solid state relays (SSR) were sourced from China on ebay, I get the cheap ones (£6) and they take a few weeks to arrive, but work well. The box came from Maplin, it was around £10. To mount the mains sockets I started by drilling the mounting holes in the base, then drew a line around the bits that stick out (hard to explain but easy when you have the bits). Drilled big holes (10mm) in each corner then used the jig saw to cut the holes. Drill a few more mounting holes for the SSR's, and the 3.5mm stereo jack. Mount all the bits and wire together, the earth on the 3.5mm jack socket goes to the SSR control earth, left and right to each of the relays. The mains cable came from a B&Q extension cable (cheaper that cable and a plug!). A couple of LEDs let you know what is going on.
I use 2 relays so I can control the fermenter fridge, one is used for the fridge and one for the heater (I just have 60W light bulb). I also connect both the HLT and the boiler when brewing, but you do have to be careful here as the kettle elements take 10 Amp and if both are on at the same time you may blow the 13A fuse. I have a bit of clever code that makes sure that both are never on at the same time.
The solid state relays (SSR) were sourced from China on ebay, I get the cheap ones (£6) and they take a few weeks to arrive, but work well. The box came from Maplin, it was around £10. To mount the mains sockets I started by drilling the mounting holes in the base, then drew a line around the bits that stick out (hard to explain but easy when you have the bits). Drilled big holes (10mm) in each corner then used the jig saw to cut the holes. Drill a few more mounting holes for the SSR's, and the 3.5mm stereo jack. Mount all the bits and wire together, the earth on the 3.5mm jack socket goes to the SSR control earth, left and right to each of the relays. The mains cable came from a B&Q extension cable (cheaper that cable and a plug!). A couple of LEDs let you know what is going on.
Re: Brew computer Mk 2
You have inspired me to dig out the pic controller stuff I have and get a move on with my project.
Nice looking setup.
What silcon you got in that baby then?
Nice looking setup.
What silcon you got in that baby then?
Fermenter(s): Lambic, Wheat beer, Amrillo/Cascade Beer
Cornys: Hobgoblin clone, Four Shades Stout, Wheat Beer, Amarillo/Cascade Ale, Apple Wine, Cider, Damson Wine, Ginger Beer
Cornys: Hobgoblin clone, Four Shades Stout, Wheat Beer, Amarillo/Cascade Ale, Apple Wine, Cider, Damson Wine, Ginger Beer
Re: Brew computer Mk 2
We will get to the microcontroller soon (Arduino based ATmega328). Today I'll like to talk about temperature sensors. There are loads of ways to measure temperature, thermocouples and solid state sensors are the most popular. I was after something that was easy to read by the microcontroller, accurate to around 0.5 deg C, cheap, and available. I tried analogue sensors first (LM 34), but had indifferent results, I soon gave up and went digital. After a little research I decided to use the Maxim DS18B20. First if you go on their web site they will send you a couple of free samples (does not get cheaper than that), and they are accurate (I get differential errors of around 0.1 deg C).
You can power them using the data pin, but if you read the spec carefully you find that at HLT temperatures you need the 5V, so 3 wires needed. I decided to connect them via a stereo jack plug and socket.
The real problem was how to get it into the HLT. There are some fantastic stainless steel thermowell (metal tube), but with postage from the US they got expensive. So into the garage and my plumbing box.
I wanted to get to 15 mm pipe so I could use a compression tank coupler (screwfix etc). I start with a bit of 10 mm pipe, cut a bit and unfold to make a flat plate. Clean, flux and solder to the end, file overhang. To get to 15 mm I use a 10mm coupler cut in half, then a small bit of 15mm pipe sloted over each other (it was all I had in my spares box), solder these.
Wire the 3 pins on the sensor to the 3.5 mm socket (farnell). Clean the pipe - you have to clean the flux off or the super glue does not stick. Superglue the sensor and push down the tube to glue to the end. The 3.5 mm socket fits perfectly into the 15 mm pipe, glue with some silicon sealent.
Finally put in the tank coupler, and tighten the compression fitting.
You can power them using the data pin, but if you read the spec carefully you find that at HLT temperatures you need the 5V, so 3 wires needed. I decided to connect them via a stereo jack plug and socket.
The real problem was how to get it into the HLT. There are some fantastic stainless steel thermowell (metal tube), but with postage from the US they got expensive. So into the garage and my plumbing box.
I wanted to get to 15 mm pipe so I could use a compression tank coupler (screwfix etc). I start with a bit of 10 mm pipe, cut a bit and unfold to make a flat plate. Clean, flux and solder to the end, file overhang. To get to 15 mm I use a 10mm coupler cut in half, then a small bit of 15mm pipe sloted over each other (it was all I had in my spares box), solder these.
Wire the 3 pins on the sensor to the 3.5 mm socket (farnell). Clean the pipe - you have to clean the flux off or the super glue does not stick. Superglue the sensor and push down the tube to glue to the end. The 3.5 mm socket fits perfectly into the 15 mm pipe, glue with some silicon sealent.
Finally put in the tank coupler, and tighten the compression fitting.
Re: Brew computer Mk 2
Hi Howard , i am just in the process of building my new brewery using HERMS system and like the look of that digital controller for HLT , Fermenter etc . Could you give me details and price of sensors and controller and did you program through your laptop ?? Cheers
Re: Brew computer Mk 2
So to the brains, endless options here but my all time fav. micro is the Arduino http://arduino.cc/. They cost £20 (I get mine from CoolComponents but there are other supplies around the globe), the software is free, loads of code available for driving the hardware, and the forum is lively with quick answers.
In a later post I'll show how I actually make my own using a bit of Veroboard, this gets the cost down even further.
You can develop the code on any PC, MAC or Linux machine, then program the controller over the USB. I use a USB power supply to power when not connected to my laptop, this cost £1.00 new shipped from China from eBAY, how do they do that?
The other big bit is the display, again I tried a few. I started with a 2 line LCD, but it was not quite big enough, so I use a 4 line x 20 character. Again our friends in China ship these for just over £7.
The only other bit you need are a few buttons and some 3.5mm sockets.
In a later post I'll show how I actually make my own using a bit of Veroboard, this gets the cost down even further.
You can develop the code on any PC, MAC or Linux machine, then program the controller over the USB. I use a USB power supply to power when not connected to my laptop, this cost £1.00 new shipped from China from eBAY, how do they do that?
The other big bit is the display, again I tried a few. I started with a 2 line LCD, but it was not quite big enough, so I use a 4 line x 20 character. Again our friends in China ship these for just over £7.
The only other bit you need are a few buttons and some 3.5mm sockets.
Re: Brew computer Mk 2
Hi Howard,
Nice looking set-up. I also use arduino with Dallas 1wire temperature sensors. I started off using PT100 but the 1wire sensors are just so much easier and require no drive circuit or calibration. I use the 1wire library from the arduino website.
I must say that your thermowell looks a dam sight tidier than mine - I just epoxied the sensors to the inside of some 15mm tube with a big old compression end-cap sealing the end. I really like the idea of the stereo jack - I might even steal that for future upgrades.
The LCD screen is also really neat, is the programming for this difficult? I communicate with my laptop over the serial and have a small python/Qt user interface which displays temperatures and allows me to set controller set points etc. The disadvantage is that I need to take my laptop out to the shed when I want to brew.
James
Nice looking set-up. I also use arduino with Dallas 1wire temperature sensors. I started off using PT100 but the 1wire sensors are just so much easier and require no drive circuit or calibration. I use the 1wire library from the arduino website.
I must say that your thermowell looks a dam sight tidier than mine - I just epoxied the sensors to the inside of some 15mm tube with a big old compression end-cap sealing the end. I really like the idea of the stereo jack - I might even steal that for future upgrades.
The LCD screen is also really neat, is the programming for this difficult? I communicate with my laptop over the serial and have a small python/Qt user interface which displays temperatures and allows me to set controller set points etc. The disadvantage is that I need to take my laptop out to the shed when I want to brew.
James
Re: Brew computer Mk 2
So we have all the hardware, now we need the dreaded C code. Many people get frighted when they see a few hundred lines of code, but like most things take it a step at a time any you will soon have it sorted. Feel free to change, edit, publish, etc. I still have a long list of features I want to add, for example when the HLT starts I would to see the estimated time it will hit the target strike temperature.
I'd be interested in other features you think would be useful - I'll take some more screen photos when I do the next brew.
You will also need the temp sensor code
I'd be interested in other features you think would be useful - I'll take some more screen photos when I do the next brew.
Code: Select all
//BeerComputer v2.0
//Howard Benn Apr 2010
//A super low cost controller for making all grain (AG) beer
//It uses 3 x Maxim single wire temperature probes - HLT, Mash tun, Kettle
//and the super low cost solid state relays (SSR) now available from China on eBay
//also uses a 4 line x 20 char display again low cost from China from eBay
//fits in a ATMEGA168 to save a few quid hence all the PROGMEM stuff
//include librarys
#include <MsTimer2.h> //used for the 1 second update loop
#include <LiquidCrystal.h> //used to control the LCD display - has to be Arduino 0017 or above to work properly
#include <Bounce.h> //debouncer for the switches
#include <avr/pgmspace.h> //need to store screen in PROGMEM
//define IO pins
#define RS_pin 14 //display - setup to make connection the easy
#define E_pin 15
#define D4_pin 16
#define D5_pin 17
#define D6_pin 18
#define D7_pin 19
#define button_1_pin 11 //buttons with pull-down resistors i.e off=0
#define button_2_pin 10
#define button_3_pin 12
#define heat_pin 8 // IO pins for heater and fridge
#define cool_pin 9
#define HLT_pin 8 // IO pin for HLT - same SSR as heat_pin
#define boil_pin 9 // IO pin for Boiler - same SSR as cool_pin
#define one_wire1_pin 2 //one wire interface to temperature sensors
#define one_wire2_pin 3 //one wire interface to temperature sensors
#define one_wire3_pin 4 //one wire interface to temperature sensors
// Sensor validation
#define INVALIDCRC 1
#define INVALIDMODEL 2
#define NODEVICE 3
#define SLAVEGOOD 0
//Global variables
// setup the LCD with the numbers of the interface pins
LiquidCrystal lcd(RS_pin, E_pin, D4_pin, D5_pin, D6_pin, D7_pin);
//setup the buttons
Bounce button_1 = Bounce(button_1_pin, 5);
Bounce button_2 = Bounce(button_2_pin, 5);
Bounce button_3 = Bounce(button_3_pin, 5);
//set up varable to store temp reading
// 64bit address of device
uint8_t TempAddr[8]; //address of sensor
float HLT_fridge_temp = 0; //temperature sensor 0
float mash_temp = 0; //mash tum
float boiler_temp = 0; //boiler temp
//static variables for FERM
int TargetTemp = 21; //target brewing temp for 4 - 6 day brew
#define HistTime 60000 //time in milli-seconds not to change state of the cool, warm status
long timeChanged = 0; //used to record the last time the temp went outside the +- 0.5 oC margin
long StartTime = 0; // time in millis that we start
float BrewMax = 0; // max temp during fermentation
float BrewMin = 100; // min temp during fermentation
byte cool; //true if fridge is on
byte heat; //true if heater on
//static variables for BREW
float HLTtarget = 80; //target temperature for HLT
byte HLTready = 0; //flag to change from heating to maintaining HLT temp
boolean HLToff = true; //flag to manually turn HLT off
long StartHLT = 0; //HLT on start time
long StartMash = 0; //Mash start time
long StartBoil = 0; //Boil start time
float BoilTarget = 98; //boil target to start boil time
boolean BoilOff = true; //flag to manually turn Boiler off
int BoilTime = 90; //Boil time in minutes
long StartCool = 0;
//Menu control
#define FERM 1
#define BREW 2
byte mode = 0;
byte menu = 1; //position on the menu list
/******************************
Due to the limited RAM we have to store the screen data in PROGMEM
******************************/
// 12345678901234567890
const prog_uchar string_0[] PROGMEM = "Welcome to Howard's"; // "String 0" etc are strings to store in Progmem to get over limited RAM
const prog_uchar string_1[] PROGMEM = " Big Beer Making";
const prog_uchar string_2[] PROGMEM = " Machine";
const prog_uchar string_3[] PROGMEM = "FERM TEST BREW";
// 12345678901234567890
const prog_uchar string_10[] PROGMEM = "Change Target Temp";
const prog_uchar string_11[] PROGMEM = "Current target ";
const prog_uchar string_12[] PROGMEM = "New target ";
const prog_uchar string_13[] PROGMEM = "UP SET DOWN";
// 12345678901234567890
const prog_uchar string_20[] PROGMEM = "Not connected";
const prog_uchar string_21[] PROGMEM = " ";
const prog_uchar string_23[] PROGMEM = "BACK HLT BOILER";
// 12345678901234567890
const prog_uchar string_30[] PROGMEM = "Duration ";
const prog_uchar string_31[] PROGMEM = " Fridge on";
const prog_uchar string_32[] PROGMEM = " Heat on ";
const prog_uchar string_33[] PROGMEM = " ";
const prog_uchar string_34[] PROGMEM = "Min ";
const prog_uchar string_35[] PROGMEM = " Max ";
const prog_uchar string_36[] PROGMEM = "TARGET RESET";
// 12345678901234567890
const prog_uchar string_40[] PROGMEM = "Ready to heat HLT";
const prog_uchar string_41[] PROGMEM = "Water in ?";
const prog_uchar string_42[] PROGMEM = "HLT target ";
const prog_uchar string_43[] PROGMEM = " CHANGE HEAT";
// 12345678901234567890
const prog_uchar string_50[] PROGMEM = "Heating HLT";
const prog_uchar string_51[] PROGMEM = "HLT ready ";
const prog_uchar string_52[] PROGMEM = "Temp ";
const prog_uchar string_53[] PROGMEM = " set ";
const prog_uchar string_54[] PROGMEM = "Time ";
const prog_uchar string_55[] PROGMEM = "BACK OFF MASH";
const prog_uchar string_56[] PROGMEM = "BACK ON MASH";
// 12345678901234567890
const prog_uchar string_60[] PROGMEM = "Mash ";
const prog_uchar string_61[] PROGMEM = "HLT OFF ";
const prog_uchar string_62[] PROGMEM = "HLT ON ";
const prog_uchar string_63[] PROGMEM = "Target ";
const prog_uchar string_64[] PROGMEM = "BACK HLT-OFF BOIL";
const prog_uchar string_65[] PROGMEM = "BACK HLT-ON BOIL";
// 12345678901234567890
const prog_uchar string_70[] PROGMEM = "Heating Boiler";
const prog_uchar string_75[] PROGMEM = "Boil started ";
const prog_uchar string_76[] PROGMEM = "Finish ";
const prog_uchar string_71[] PROGMEM = "Boil Target ";
const prog_uchar string_72[] PROGMEM = "Boil Temp ";
const prog_uchar string_73[] PROGMEM = "BACK OFF BOIL";
const prog_uchar string_74[] PROGMEM = "BACK ON BOIL";
// 12345678901234567890
const prog_uchar string_80[] PROGMEM = "Cool started ";
const prog_uchar string_81[] PROGMEM = "BACK FINISH";
// 12345678901234567890
const prog_uchar string_90[] PROGMEM = "All done";
const prog_uchar string_91[] PROGMEM = "Brew time ";
const prog_uchar string_92[] PROGMEM = "Press any button ";
const prog_uchar string_93[] PROGMEM = "to restart";
/******************************
PRINT FUNCTIONS
*******************************/
void LCDPrintFloat( float f){
if (f>99.9) {
lcd.print ("++.+");
return;
}
if (f<0) {
lcd.print ("--.-");
return;
}
if (f<10) lcd.print (" ");
lcd.print((int)f);
lcd.print(".");
int temp = (f - (int)f) * 10;
lcd.print( abs(temp) );
}
void LCDPrintProgMem (const prog_uchar str[]){
char c;
if(!str) return;
while((c = pgm_read_byte(str++)))
lcd.print(c,BYTE);
}
void LCDPrintTemp (float f){
LCDPrintFloat (f);
lcd.write (0xDF);
lcd.print ("C");
}
void LCDPrintInt (int i){
if (i<10) lcd.print ("0");
lcd.print (i);
}
void LCDClearLine (int line){
lcd.setCursor (0,line);
lcd.print(" ");
}
int ChangeTemp (int current) {
// change the target temperature
//Change Target Temp
//Current target 80oC
//New Target 77oC
//UP SET DOWN
int new_temp = current;
boolean set = false;
lcd.clear ();
while (!set) {
lcd.setCursor(0,0);
LCDPrintProgMem (string_10);
lcd.setCursor(0,1);
LCDPrintProgMem (string_11);
LCDPrintTemp(current);
lcd.setCursor(0,2);
LCDPrintProgMem (string_12);
LCDPrintTemp (new_temp);
lcd.setCursor(0,3);
LCDPrintProgMem (string_13);
//read buttons
if ( button_1.update() ) { //UP button
// Auto increment if the button is held
if ( button_1.read() == HIGH) {
// Make the button retrigger in 500 milliseconds
button_1.rebounce(500);
if (new_temp < 99) new_temp++;
}
}
if ( button_3.update() ) { //DOWN button
// Auto increment if the button is held
if ( button_3.read() == HIGH) {
// Make the button retrigger in 500 milliseconds
button_3.rebounce(500);
if (new_temp > 0) new_temp--;
}
}
if ( button_2.update() ) { //SET button
if ( button_2.read() == HIGH) {
set=true;
}
}
}
lcd.clear ();
return (new_temp);
}
void LCDPrintElapsedTimeDHM (long start) {
int days;
int hours;
int mins;
long TimeNow;
long ElapsedTime;
TimeNow = millis ();
ElapsedTime = TimeNow-start;
days = ElapsedTime/86400000;
hours = (ElapsedTime/3600000) % 24;
mins = (ElapsedTime/60000) % 60;
LCDPrintInt (days);
lcd.print(":");
LCDPrintInt (hours);
lcd.print(":");
LCDPrintInt (mins);
}
void LCDPrintElapsedTimeHMS (long start) {
int hours;
int mins;
int seconds;
long TimeNow;
long ElapsedTime;
TimeNow = millis ();
ElapsedTime = TimeNow-start;
hours = ElapsedTime/3600000;
mins = (ElapsedTime/60000) % 60;
seconds = (ElapsedTime/1000) % 60;
LCDPrintInt(hours);
lcd.print(":");
LCDPrintInt(mins);
lcd.print(":");
LCDPrintInt(seconds);
}
void LCDPrintElapsedTimeMS (long start) {
int mins;
int seconds;
long TimeNow;
long ElapsedTime;
TimeNow = millis ();
ElapsedTime = TimeNow-start;
mins = ElapsedTime/60000;
seconds = (ElapsedTime/1000) % 60;
LCDPrintInt(mins);
lcd.print(":");
LCDPrintInt(seconds);
}
void LCDPrintRemaining (long start, int end) {
int mins;
int seconds;
long TimeNow;
long endMS;
long TimeToGo;
TimeNow = millis ();
endMS = end * 60000; //convert from minutes to mS
TimeToGo = start + endMS - TimeNow;
mins = TimeToGo/60000;
seconds = (TimeToGo/1000) % 60;
LCDPrintInt(mins);
lcd.print(":");
LCDPrintInt(seconds);
}
/******************************
Startup screen and control
*******************************/
void LCDPrintWelcome () {
lcd.clear();
lcd.setCursor(0,0);
LCDPrintProgMem (string_0);
lcd.setCursor(0,1);
LCDPrintProgMem (string_1);
lcd.setCursor(0,2);
LCDPrintProgMem (string_2);
lcd.setCursor(0,3);
LCDPrintProgMem (string_3);
}
void WelcomeButtons (){
while (mode == 0) { //repeat until a button is pressed
if ( button_1.update() ) { //set into FERM mode
if ( button_1.read() == HIGH) {
lcd.clear();
mode = FERM;
StartTime = millis ();
cool = false; //set up fridge startup conditions
heat = false;
}
}
if ( button_2.update() ) { //Test
if ( button_2.read() == HIGH) {
lcd.clear();
Test ();
}
}
if ( button_3.update() ) { //set into BREW mode
if ( button_3.read() == HIGH) {
lcd.clear();
mode = BREW;
}
}
}
}
/******************************
Test Mode
*******************************/
void PrintTest (float temperature){
if (temperature != 0) {
LCDPrintTemp (temperature);
LCDPrintProgMem (string_21);
}
else {
LCDPrintProgMem (string_20);
}
}
void Test () {
boolean button_pushed = false;
lcd.clear();
while (!button_pushed) {
lcd.setCursor(0,0);
PrintTest (HLT_fridge_temp);
lcd.setCursor(0,1);
PrintTest (mash_temp);
lcd.setCursor(0,2);
PrintTest (boiler_temp);
lcd.setCursor(0,3);
LCDPrintProgMem (string_23);
if ( button_1.update() ) { //back to start
if ( button_1.read() == HIGH) {
LCDPrintWelcome();
button_pushed = true;
}
}
if ( button_2.update() ) { //HLT on/off
if ( button_2.read() == HIGH) {
if (!HLToff) {
HLToff = true;
digitalWrite (HLT_pin, LOW);
digitalWrite (boil_pin, LOW);
}
else {
HLToff = false;
digitalWrite (HLT_pin, HIGH);
digitalWrite (boil_pin, LOW);
}
}
}
if ( button_3.update() ) { //boiler on/off
if ( button_3.read() == HIGH) {
if (!BoilOff) {
BoilOff = true;
digitalWrite (HLT_pin, LOW);
digitalWrite (boil_pin, LOW);
}
else {
BoilOff = false;
digitalWrite (HLT_pin, LOW);
digitalWrite (boil_pin, HIGH);
}
}
}
}
}
/******************************
Fermentation control
*******************************/
void LCDPrintFerm () {
/* Calculate elapsed time and temperature, print results
Duration 12:10:30
20.1 oC Heat
Min 19.5 Max 22.2
CHANGE FERM TEMP
*/
if (HLT_fridge_temp > BrewMax) BrewMax = HLT_fridge_temp; //set max
if (HLT_fridge_temp < BrewMin) BrewMin = HLT_fridge_temp; //set min
lcd.setCursor(0,0);
LCDPrintProgMem (string_30);
LCDPrintElapsedTimeDHM (StartTime);
lcd.setCursor(0,1);
LCDPrintTemp (HLT_fridge_temp);
if (cool) LCDPrintProgMem (string_31);
if (heat) LCDPrintProgMem (string_32);
if (!cool & !heat) LCDPrintProgMem (string_33);
lcd.setCursor(0,2);
LCDPrintProgMem (string_34);
LCDPrintFloat (BrewMin);
LCDPrintProgMem (string_35);
LCDPrintTemp (BrewMax);
lcd.setCursor(0,3);
LCDPrintProgMem (string_36);
if ( button_1.update() ) { //change button
if ( button_1.read() == HIGH) {
TargetTemp = ChangeTemp (TargetTemp);
}
}
if ( button_3.update() ) { //reset max and min
if ( button_3.read() == HIGH) {
BrewMax = HLT_fridge_temp;
BrewMin = HLT_fridge_temp;
}
}
}
void ControlTemp (){
//Controls the Fridge internal temperature - uses a 0.5 degree historysis and a programmable delay
//to prevent the fridge going on and off too often
//Thought about using the PID lib, but a bit over the top for this
long timeNow;
timeNow = millis();
if ((timeNow - timeChanged) < HistTime) return; // don't do anything if there has been a recent change
//check if we need to turn the fridge on
if (HLT_fridge_temp > (TargetTemp + 0.5)) {
if (cool == false) {
timeChanged = millis(); //if we change state note time
cool = true;
digitalWrite(cool_pin, HIGH); //fridge relay on
}
}
else {
if (cool == true) {
timeChanged = millis();
cool = false;
digitalWrite(cool_pin, LOW); //fridge relay off
}
}
// check if we need to turn the heater on
if (HLT_fridge_temp < (TargetTemp - 0.5)) {
if (heat == false){
timeChanged = millis();
heat = true;
digitalWrite(heat_pin, HIGH); //heat relay on
}
}
else {
if (heat == true) {
timeChanged = millis();
heat = false;
digitalWrite(heat_pin, LOW); //heat relay off
}
}
}
/******************************
Brew mode
*******************************/
void MenuUpDown () {
if ( button_1.update() ) { //Back
if ( button_1.read() == HIGH) {
if (menu > 1) menu--;
lcd.clear();
}
}
if ( button_3.update() ) { //Forward
if ( button_3.read() == HIGH) {
menu++;
lcd.clear();
}
}
}
void HLTcontrol () {
if (HLToff) { //HLT forced off by MMI
digitalWrite (HLT_pin, LOW);
return;
}
if (HLT_fridge_temp < HLTtarget) {
digitalWrite (boil_pin, LOW); //only ever allow 1 element to be on at a time
digitalWrite (HLT_pin, HIGH);
}
else {
digitalWrite (boil_pin, LOW);
digitalWrite (HLT_pin, LOW);
HLTready = true;
}
}
void LCDStartHLT () {
/* get ready to turn HLT on
Ready to heat HLT
Water in?
Current target 80oC
BACK CHANGE HEAT
*/
lcd.setCursor(0,0);
LCDPrintProgMem (string_40);
lcd.setCursor(0,1);
LCDPrintProgMem (string_41);
lcd.setCursor(0,2);
LCDPrintProgMem (string_42);
LCDPrintTemp(HLTtarget);
lcd.setCursor(0,3);
LCDPrintProgMem (string_43);
MenuUpDown (); //check for menu changes
if ( button_2.update() ) { //change button
if ( button_2.read() == HIGH) {
HLTtarget = ChangeTemp (HLTtarget);
}
}
}
void LCDHeatHLT () {
/* HLT on
Heating HLT
Current 66 target 80
Time 0:20:30
BACK OFF MASH */
if (StartHLT == 0) {
StartHLT = millis(); //set starttime first time around
HLToff = false;
}
lcd.setCursor(0,0);
if (!HLTready){
LCDPrintProgMem (string_50);
}
else {
LCDPrintProgMem (string_51);
}
lcd.setCursor(0,1);
LCDPrintProgMem (string_52);
LCDPrintFloat (HLT_fridge_temp);
LCDPrintProgMem (string_53);
LCDPrintTemp(HLTtarget);
lcd.setCursor(0,2);
LCDPrintProgMem (string_54);
LCDPrintElapsedTimeHMS (StartHLT);
lcd.setCursor(0,3);
if (!HLToff) {
LCDPrintProgMem (string_55);
}
else {
LCDPrintProgMem (string_56);
}
MenuUpDown (); //check for menu changes
if ( button_2.update() ) { //change button
if ( button_2.read() == HIGH) {
if (!HLToff) HLToff = true;
else HLToff = false;
}
}
HLTcontrol ();
}
void LCDMash () { //NEED TO SORT setting target temp ?
/*
Mash 66.3oC 40:53
HLT off 50.4oC
target 80.0oC
BACK HLT_ON BOIL */
if (StartMash == 0) StartMash = millis(); //set starttime first time around
lcd.setCursor(0,0);
LCDPrintProgMem (string_60);
LCDPrintTemp (mash_temp);
lcd.print (" ");
LCDPrintElapsedTimeMS (StartMash);
lcd.setCursor(0,1);
if (HLToff) {
LCDPrintProgMem (string_61);
}
else {
LCDPrintProgMem (string_62);
}
LCDPrintTemp (HLT_fridge_temp);
lcd.setCursor(0,2);
LCDPrintProgMem (string_63);
LCDPrintTemp (HLTtarget);
lcd.setCursor(0,3);
if (!HLToff) {
LCDPrintProgMem (string_64);
}
else {
LCDPrintProgMem (string_65);
}
MenuUpDown (); //check for menu changes
if ( button_2.update() ) { //change button
if ( button_2.read() == HIGH) {
if (!HLToff) {
HLToff = true;
}
else {
HLToff = false;
}
}
}
HLTcontrol ();
}
void LCDHeatBoiler () { //Heat Boiler until we hit target temp then start timer
/*
Heating Boiler
Boil target 98 oC
Boiler 80.2 oC
BACK OFF BOIL
*/
lcd.setCursor(0,0);
LCDPrintProgMem (string_70);
lcd.setCursor(0,1);
LCDPrintProgMem (string_71);
LCDPrintTemp (BoilTarget);
lcd.setCursor(0,2);
LCDPrintProgMem (string_72);
LCDPrintTemp (boiler_temp);
lcd.setCursor(0,3);
if (!BoilOff) {
LCDPrintProgMem (string_73);
digitalWrite (HLT_pin, LOW); //always make sure HLT is iff before we turn the boiler on
digitalWrite (boil_pin, HIGH);
}
else {
LCDPrintProgMem (string_74);
digitalWrite (boil_pin, LOW);
}
MenuUpDown (); //check for menu changes
if ( button_2.update() ) { //change button
if ( button_2.read() == HIGH) {
if (!BoilOff) {
BoilOff = true;
}
else {
BoilOff = false;
}
}
}
}
void LCDBoil () { //Boil and display timer
/*
Boil started 24:30
Finish 63:20
Temp 99 oC
BACK OFF COOL
*/
if (StartBoil == 0) {
StartBoil = millis(); //set starttime first time around
}
lcd.setCursor(0,0);
LCDPrintProgMem (string_75);
LCDPrintElapsedTimeMS (StartBoil);
lcd.setCursor(0,1);
LCDPrintProgMem (string_76);
LCDPrintRemaining (StartBoil, BoilTime);
lcd.setCursor(0,2);
LCDPrintProgMem (string_72);
LCDPrintTemp (boiler_temp);
lcd.setCursor(0,3);
if (!BoilOff) {
LCDPrintProgMem (string_73);
digitalWrite (HLT_pin, LOW); //always make sure HLT is iff before we turn the boiler on
digitalWrite (boil_pin, HIGH);
}
else {
LCDPrintProgMem (string_74);
digitalWrite (boil_pin, LOW);
}
MenuUpDown (); //check for menu changes
if ( button_2.update() ) { //change button
if ( button_2.read() == HIGH) {
if (!BoilOff) {
BoilOff = true;
}
else {
BoilOff = false;
}
}
}
}
void LCDCool () { //Boil and display timer
/*
Cool started 24:30
Temp 99 oC
BACK FINISH
*/
if (StartCool == 0) {
StartCool = millis(); //set starttime first time around
digitalWrite (boil_pin, LOW); // turn element off
}
lcd.setCursor(0,0);
LCDPrintProgMem (string_80);
LCDPrintElapsedTimeMS (StartCool);
lcd.setCursor(0,2);
LCDPrintProgMem (string_52);
LCDPrintTemp (boiler_temp);
lcd.setCursor(0,3);
LCDPrintProgMem (string_81);
MenuUpDown (); //check for menu changes
}
void LCDFinish () {
lcd.setCursor(0,0);
LCDPrintProgMem (string_90);
lcd.setCursor(0,1);
LCDPrintProgMem (string_91);
LCDPrintElapsedTimeHMS (StartHLT);
lcd.setCursor(0,2);
LCDPrintProgMem (string_92);
lcd.setCursor(0,3);
LCDPrintProgMem (string_93);
if ( button_1.update() || button_2.update() || button_3.update() ) { //any button
if ( (button_1.read() || button_2.read() || button_3.read()) == HIGH) {
mode = 0;
}
}
}
/******************************
Setup
*******************************/
void OneSecondLoop() {
//read the existing stored temperature values from RAM
//start the next conversion - takes around 750mS so should be ready next time we get here
//Need to remember and reset the pin to make the test loops work
uint8_t pin;
pin = get_pin();
change_pin (one_wire1_pin); //set to first sensor
HLT_fridge_temp = getTempC(); //temperature sensor 0
globalTempRequest();
change_pin (one_wire2_pin);
mash_temp = getTempC(); //mash tum
globalTempRequest();
change_pin (one_wire3_pin);
boiler_temp = getTempC(); //boiler temp
globalTempRequest();
change_pin (pin);
}
void setup() {
// set up the LCD's number of columns and rows:
lcd.begin(20,4);
//configure pins - inputs are defaulted
pinMode(heat_pin, OUTPUT);
pinMode(cool_pin, OUTPUT);
// turn SSRs off
digitalWrite (heat_pin, LOW);
digitalWrite (cool_pin, LOW);
SetAddress (&TempAddr[0]);
MsTimer2::set(1000, OneSecondLoop); // 1 second loop
MsTimer2::start();
}
void loop() {
switch (mode) {
case 0:
LCDPrintWelcome();
WelcomeButtons ();
break;
case 1:
ControlTemp ();
LCDPrintFerm ();
break;
case 2:
switch (menu){
case 1:
LCDStartHLT ();
break;
case 2:
LCDHeatHLT ();
break;
case 3:
LCDMash ();
break;
case 4:
LCDHeatBoiler ();
break;
case 5:
LCDBoil ();
break;
case 6:
LCDCool ();
break;
case 7:
LCDFinish ();
break;
}
break;
}
}
Code: Select all
//Tiny temp - small version of OneWire and DallasTemperature
//with only one sensor per wire but with hot swap
// This is ripped off from the DallasTemperature library,
// and you can ONLY have a single sensor per wire
// The only functions you need are reset to see if there is a device on the bus
// Skip to read without address
// Read scratchpad to read temp
#include <inttypes.h>
#include "OneWire2.h"
#define STARTCONVO 0x44 // Tells device to take a temperature reading and put it on the scratchpad
#define COPYSCRATCH 0x48 // Copy EEPROM
#define READSCRATCH 0xBE // Read EEPROM
#define WRITESCRATCH 0x4E // Write to EEPROM
#define RECALLSCRATCH 0xB8 // Reload from last known
#define READPOWERSUPPLY 0xB4 // determine if device needs parasite power
#define DS18S20MODEL 0x10 // Model ID
#define DS18B20MODEL 0x28 // Model ID
#define DS1822MODEL 0x22 // Model ID
// device resolution
#define TEMP_9_BIT 0x1F // 9 bit
#define TEMP_10_BIT 0x3F // 10 bit
#define TEMP_11_BIT 0x5F // 11 bit
#define TEMP_12_BIT 0x7F // 12 bit
// One Wire address
//OneWire2 *pDataWire;
OneWire2 pDataWire(one_wire1_pin);
// 64bit address of device
uint8_t *arSlaveAddr;
// temperature LSB
uint8_t tempLSB;
// temperature MSB
uint8_t tempMSB;
// T(high)
uint8_t highAlarmTemp;
// T(low)
uint8_t lowAlarmTemp;
// device configuration
uint8_t resolution;
void change_pin (uint8_t pin)
{
pDataWire.change_pin (pin);
}
uint8_t get_pin () {
return (pDataWire.get_pin());
}
// read length bytes of a device's scratch pad
void readScratchPad(uint8_t length)
{
// sanity check
if (length > 8) length = 8;
// send the command
pDataWire.reset();
pDataWire.skip();
pDataWire.write(READSCRATCH);
// read the response
for (uint8_t i = 0; i <= length; i++)
switch (i)
{
case 0:
// byte 0: temperature LSB
tempLSB = pDataWire.read();
break;
case 1:
// byte 1: temperature MSB
tempMSB = pDataWire.read();
break;
case 2:
// byte 2: high alarm temp
highAlarmTemp = pDataWire.read();
break;
case 3:
// byte 3: low alarm temp
lowAlarmTemp = pDataWire.read();
break;
case 4:
// byte 4:
// this is really the configuration register, but for
// our purposes, this is how we'll think of it
resolution = pDataWire.read();
break;
case 5:
case 6:
case 7:
case 8:
break;
}
pDataWire.reset();
}
void SetAddress (uint8_t *newAddr) {
arSlaveAddr = newAddr;
}
// Validate whether the current address is correct
int CheckSensor()
{
if (pDataWire.reset() == false) {
return NODEVICE;
}
pDataWire.read_rom (arSlaveAddr);
// Calculate Cycle-Redudancy-Check (ie: check the data is not invalid
if (OneWire2::crc8(arSlaveAddr, 7) != arSlaveAddr[7]) {
return INVALIDCRC;
}
if (!(arSlaveAddr[0] == DS18B20MODEL
|| arSlaveAddr[0] == DS18S20MODEL
|| arSlaveAddr[0] == DS1822MODEL
)){
return INVALIDMODEL;
}
// Return all good
readScratchPad(8); // read the scratchpad to set model, resolution etc
return SLAVEGOOD;
}
// returns a float with the temperature in degrees C.
float getTemperature()
{
// If we're ready to rock, begin communication channel
if (CheckSensor() != SLAVEGOOD) return 0; // return a value outside our range
// The temp is the first two bytes so just request those 2
readScratchPad(2);
int16_t rawTemperature = (((int16_t) tempMSB) << 8) | tempLSB;
switch (arSlaveAddr[0]) {
case DS18B20MODEL:
case DS1822MODEL:
switch (resolution)
{
case TEMP_12_BIT:
return (float)rawTemperature * 0.0625;
break;
case TEMP_11_BIT:
return (float)(rawTemperature >> 1) * 0.125;
break;
case TEMP_10_BIT:
return (float)(rawTemperature >> 2) * 0.25;
break;
case TEMP_9_BIT:
return (float)(rawTemperature >> 3) * 0.5;
break;
}
break;
case DS18S20MODEL:
return (float)rawTemperature * 0.5;
break;
}
}
// sends convert temperature to all devices on the NewOneWire connection.
// only one device on each bus needs to do this. ideally, it should be
// on your highest resolution device so if a delay is necessary, it will
// be long enough
void globalTempRequest()
{
pDataWire.reset();
pDataWire.skip();
pDataWire.write(STARTCONVO);
}
// returns temperature in degrees C
float getTempC()
{
return getTemperature();
}
// returns temperature in degrees F
float getTempF()
{
return (getTemperature() * 1.8) + 32;
}
// Convert float celcius to fahrenheit
float toFahrenheit(float celcius)
{
return (celcius*1.8)+32;
}
Re: Brew computer Mk 2
Excellent thanks for sharing.
Have received my freebies temp sensing just need to get stuckin and do it.
Have received my freebies temp sensing just need to get stuckin and do it.
Fermenter(s): Lambic, Wheat beer, Amrillo/Cascade Beer
Cornys: Hobgoblin clone, Four Shades Stout, Wheat Beer, Amarillo/Cascade Ale, Apple Wine, Cider, Damson Wine, Ginger Beer
Cornys: Hobgoblin clone, Four Shades Stout, Wheat Beer, Amarillo/Cascade Ale, Apple Wine, Cider, Damson Wine, Ginger Beer
Re: Brew computer Mk 2
Awesome post! Many thanks....howard wrote:Today I'll like to talk about temperature sensors.
I might borrow quite a few of these ideas for my new project if that's OK
Re: Brew computer Mk 2
I mentioned you can just use the Arduino as the brains in the box, but if you want to save a couple of quid (or in my case wanted to use the Ardunio for another project - the bathroom scale HLT volume measurer - more on that an other day) you can build the whole thing on a bit of VeroBoard. I use the ATMega 168 which I have the bootloader code programmed on (see the Arduino web site for details).
The Schematic looks like
Which when veroboarded becomes
Then linked up with the buttons and display looks like
The USB connector is not USB! But it can use a the power from any USB connector, I bought my USB power supply from ebay for £1 inc shipping from China (how do they make money?) I also have a special cable that when I take my ATMega chip out the Arduino and connect the TX and RX allows me to program the chip from my PC.
I have just added the code to predict when the HLT will hit strike temperature. After I have tested it I'll post the updated source code.
The Schematic looks like
Which when veroboarded becomes
Then linked up with the buttons and display looks like
The USB connector is not USB! But it can use a the power from any USB connector, I bought my USB power supply from ebay for £1 inc shipping from China (how do they make money?) I also have a special cable that when I take my ATMega chip out the Arduino and connect the TX and RX allows me to program the chip from my PC.
I have just added the code to predict when the HLT will hit strike temperature. After I have tested it I'll post the updated source code.
Re: Brew computer Mk 2
Oooh. You bugger.
You've just made me buy an Arduino dev board and LCD module.
I think I'm going to need the breadboard module too.
And some of these 1-wire sensor.
And some more SSRs....and stuff.....
You've just made me buy an Arduino dev board and LCD module.
I think I'm going to need the breadboard module too.
And some of these 1-wire sensor.
And some more SSRs....and stuff.....