
Brewery Programming
- barneey
- Telling imaginary friend stories
- Posts: 5423
- Joined: Mon Jul 25, 2011 10:42 pm
- Location: East Kent
Re: Brewery Programming
I will probadly have a little trunking left Fil if you need a few offcuts 

Hair of the dog, bacon, butty.
Hops, cider pips & hello.
Name the Movie + song :)
Hops, cider pips & hello.
Name the Movie + song :)
-
- Telling imaginary friend stories
- Posts: 5229
- Joined: Sun Oct 16, 2011 1:49 pm
- Location: Cowley, Oxford
Re: Brewery Programming
ha ha I think your monster build trunking would dwarf the box leaving little space for anything barneey.. the spaghetti is just temp as i finalise the h/w config .. the board will probably piggyback the lcd and just have power, ssr control and probe wires bridging between lid and base. i will just need to fit another button for the reset.
ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate

-
- Telling imaginary friend stories
- Posts: 5229
- Joined: Sun Oct 16, 2011 1:49 pm
- Location: Cowley, Oxford
Re: Brewery Programming
found this for reading pt100/pt1000 rtd sensors with an arduino.. ends up using 2 analog outputs and one input and involves a bit of a circuit.. i priced up the components as £12 on ebay and have taken a punt, i may be able to put it together.. http://openenergymonitor.org/emon/build ... re-sensing,,
ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate

Re: Brewery Programming
wrote up a bit of code to turn the brauduino shield into a fermentation controller


when I get a bit of time I might merge the 2 codes to make a 1v brewery controller and a ferment controller in one


Code: Select all
/*
brauduino semi automated single vessel RIMS
created by s.mathison
using part code orginal developed by Michael Pilcher 2/9/2010
Copyright (C) 2012 Stephen Mathison
compiled on Arduino V1.0
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.*/
//libraries
#include <EEPROM.h>
#include <LiquidCrystal.h>
#include <OneWire.h>
#include <avr/pgmspace.h>
OneWire ds(11);
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
// push buttons
const char Button_up = A3;
const char Button_dn = A2;
const char Button_prev = A1;
const char Button_nxt = A0;
// outputs
const int Heat = 9;
const int Cool = 8;
const int Buzz = 10;
//Global Variables
unsigned long convStartTime,displayTime,start,buttonPress;
float Temp_c,setTemp,hyst;
byte data[2];
byte firstNumber;
byte secondNumber;
byte remainder;
byte second;
byte minute;
byte hour;
byte day;
byte compDelay;
byte compTime;
const byte gBigFontShapeTable[] PROGMEM = {
//* LT[8] =
B00111,
B01111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
//* UB[8] =
B11111,
B11111,
B11111,
B00000,
B00000,
B00000,
B00000,
B00000,
//* RT[8] =
B11100,
B11110,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
//* LL[8] =
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B01111,
B00111,
//* LB[8] =
B00000,
B00000,
B00000,
B00000,
B00000,
B11111,
B11111,
B11111,
//* LR[8] =
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11110,
B11100,
//* UMB[8] =
B11111,
B11111,
B11111,
B00000,
B00000,
B00000,
B11111,
B11111,
//* LMB[8] =
B11111,
B00000,
B00000,
B00000,
B00000,
B11111,
B11111,
B11111
};
const byte gBigFontAsciiTable[] PROGMEM = {
0, 1, 2, 3, 4, 5, // 0x30 0
1, 2, 32, 32, 255, 32, // 0x31 1
6, 6, 2, 3, 7, 7, // 0x32 2
6, 6, 2, 7, 7, 5, // 0x33 3
3, 4, 2, 32, 32, 5, // 0x34 4
255, 6, 6, 7, 7, 5, // 0x35 5
// 0, 6, 6, 7, 7, 5, // 0x35 5
0, 6, 6, 3, 7, 5, // 0x36 6
1, 1, 2, 32, 0, 32, // 0x37 7
0, 6, 2, 3, 7, 5, // 0x38 8
0, 6, 2, 32, 32, 255, // 0x39 9
};
boolean Conv_start = false;
boolean run = false;
boolean cool_on = false;
boolean showlp = false;
//***********Start of the Functions***************
static void BigNumber_SendCustomChars(void)
{
uint8_t customCharDef[10];
uint8_t ii;
int jj;
for (ii=0; ii<8; ii++)
{
for (jj=0; jj<8; jj++)
{
customCharDef[jj] = pgm_read_byte_near(gBigFontShapeTable + (ii * 8) + jj);
}
lcd.createChar(ii, customCharDef);
}
}
// 1 second button press
int Button_1sec_press (int Button_press){
if (digitalRead(Button_press)==0){
delay (1000);
if (digitalRead(Button_press)==0){
lcd.clear();
while(digitalRead(Button_press)==0){
}
return 1;
}
}
return 0;
}
// repeat button press
int Button_repeat (int Button_press){
if (digitalRead(Button_press)==0){
delay(200);
return 1;
}
return 0;
}
// holds whilst button pressed
int Button_hold_press (int Button_press){
if (digitalRead (Button_press)==0){
delay(50);
while (digitalRead (Button_press)==0){
}
return 1;
}
return 0;
}
void display_lcd (int pos , int line ,const char* lable){
lcd.setCursor(pos,line);
lcd.print(lable);
}
// reads the DS18B20 temerature probe
void Temperature(void){
// start conversion and return
if (!(Conv_start)){
ds.reset();
ds.skip();
ds.write(0x44,0);
Conv_start = true;
convStartTime = millis();
return;
}
// check for conversion if it isn't complete return if it is then convert to decimal
if (Conv_start){
if ((millis()-convStartTime)<1000){
return;
}
ds.reset();
ds.skip();
ds.write(0xBE);
for ( int i = 0; i < 2; i++) { // we need 2 bytes
data[i] = ds.read();
}
unsigned int raw = (data[1] << 8) + data[0];
unsigned int whole = (raw >>4);
unsigned int remain = (raw & 0X000f);
Temp_c = (raw) * 0.0625;
firstNumber = whole/10;
secondNumber = whole%10;
remainder = (remain * 10)/16;
Conv_start = false;
return;
}
}
//************************************************************************
//* returns the width of the character
static int DrawBigChar(byte theChar , int xLocation)
{
int offset;
int ii;
byte theByte;
offset = theChar *6;
lcd.setCursor(xLocation, 0);
for (ii=0; ii<3; ii++)
{
theByte = pgm_read_byte_near(gBigFontAsciiTable + offset + ii);
lcd.write(theByte);
}
lcd.setCursor(xLocation, 1);
offset += 3;
for (ii=0; ii<3; ii++)
{
theByte = pgm_read_byte_near(gBigFontAsciiTable + offset + ii);
lcd.write(theByte);
}
}
// displays large font numbers
void PrintDegC(void){
lcd.setCursor(13,0);
lcd.print("o");
lcd.setCursor(8,1);
lcd.write(4);
lcd.setCursor(14,1);
lcd.write(8);
lcd.write(6);
}
//Prints current Temperature in large font
void displayBigTemp(){
DrawBigChar(firstNumber,0);
DrawBigChar(secondNumber,4);
lcd.setCursor(8,1);
lcd.write(4);
DrawBigChar(remainder,10);
PrintDegC();
}
//Timing of the fermentation
void ferment_timing()
{
if ((millis()-start)>1000){ // timing routine
start = millis();
second++;
if (second>59){
second = 0;
minute++;
if(minute>59){
minute = 0;
hour++;
if(hour>23){
hour=0;
day++;
}
}
}
}
}
float change_temp(float& temp_change,int upper_limit,int lower_limit)
{
// Increase set temp
if (Button_repeat(Button_up)){
temp_change+=0.1;
if (temp_change > upper_limit)temp_change = upper_limit;
}
// decrease temp
if (Button_repeat(Button_dn)){
temp_change-=0.1;
if ( temp_change < lower_limit) temp_change = lower_limit;
}
}
void heatOff(){
digitalWrite(Heat,LOW);
}
void heatOn(){
digitalWrite(Heat,HIGH);
}
void coolOff(){
digitalWrite(Cool,LOW);
if (cool_on){
compTime = minute;
cool_on =false;
}
}
void coolOn(){
if ((minute - compTime)>= compDelay){
digitalWrite(Cool,HIGH);
cool_on= true;
}
}
void save_settings (int addr,int data)
{
EEPROM.write(addr,highByte(data));
EEPROM.write((addr+1),lowByte(data));
}
void save_settings (int addr,byte data){
EEPROM.write(addr,data);
}
int change_set(byte& set_change,int upper_limit,int lower_limit,int step_size)
{
// Increase set temp
if (Button_repeat(Button_up)){
set_change+=step_size;
display_lcd(0,1," ");
}
if (set_change > upper_limit)set_change = upper_limit;
// decrease temp
if (Button_repeat(Button_dn))
{
set_change-=step_size;
display_lcd(0,1," ");
}
if ( set_change < lower_limit) set_change = lower_limit;
}
// settings for the unit
void set(){
for(int i=0;i<3;i++){
boolean setLoop = true;
byte setting;
float settingFloat;
int settingWord;
if (i==0){
setTemp= word(EEPROM.read(50),EEPROM.read(51));
setTemp=setTemp/10.0;
while (setLoop){
display_lcd (0,0,"Set Temp = ");
lcd.print(setTemp);
change_temp(setTemp,35,2);
if(Button_hold_press(Button_nxt)){
setTemp=setTemp * 10;
settingWord = word(setTemp);
save_settings(50,settingWord);
setLoop = false;
}
}
}
if (i==1){
setting = (EEPROM.read(52));
display_lcd(0,0," ");
while (setLoop){
settingFloat =setting/10.0;
display_lcd (0,0,"Hyst = ");
lcd.print(settingFloat);
change_set(setting,20,1,1);
if(Button_hold_press(Button_nxt)){
save_settings(52,setting);
setLoop = false;
}
}
}
if (i==2){
setting = EEPROM.read(53);
display_lcd(0,0," ");
while (setLoop){
display_lcd (0,0,"Comp Delay = ");
lcd.print(setting);
change_set(setting,9,2,1);
if(Button_hold_press(Button_nxt)){
save_settings(53,setting);
setLoop = false;
setTemp = word(EEPROM.read(50),EEPROM.read(51));
setTemp = setTemp/10.0;
lcd.clear();
}
}
}
}
}
void loadSettings(){
setTemp = word(EEPROM.read(50),EEPROM.read(51))/10.0;
hyst = EEPROM.read(52)/10.0;
compDelay = EEPROM.read(53);
}
//control process
void control(){
loadSettings();
while (run){
Temperature();
if (showlp){
display_lcd(0,0,"Time=");
lcd.print(day);
lcd.print(":");
lcd.print(hour);
lcd.print(":");
lcd.print(minute);
lcd.print(":");
if(second<10)lcd.print("0");
lcd.print(second);
display_lcd(0,1,"S/A=");
lcd.print(setTemp);
lcd.print("/");
lcd.print(Temp_c);
change_temp(setTemp,35,2);
if (Button_hold_press(Button_nxt)){
lcd.clear();
showlp =false;
}
}
else displayBigTemp();
ferment_timing();
if (Temp_c>=setTemp)heatOff();
if (Temp_c<=setTemp)coolOff();
if (Temp_c<=(setTemp-hyst))heatOn();
if (Temp_c>=(setTemp-hyst))coolOn();
if (Button_hold_press(Button_nxt)){
showlp = true;
lcd.clear();
}
if (Button_1sec_press(Button_prev))run= false;
}
}
void setup(){
lcd.begin(16,2);
pinMode (Button_up,INPUT);
pinMode (Button_dn,INPUT);
pinMode (Button_prev,INPUT);
pinMode (Button_nxt,INPUT);
pinMode (Heat,OUTPUT);
pinMode (Cool,OUTPUT);
pinMode (Buzz,OUTPUT);
// assignes each segment a write number
BigNumber_SendCustomChars();
setTemp = word(EEPROM.read(50),EEPROM.read(51));
setTemp = setTemp/10.0;
}
void loop(){
if (Button_1sec_press(Button_prev)){
run = true;
control();
}
if (Button_1sec_press(Button_nxt)){
set();
}
digitalWrite(Heat,LOW);
digitalWrite(Cool,LOW);
second = 0;
minute = 0;
hour = 0 ;
day = 0;
display_lcd(1,0,"Ferment Control");
display_lcd(1,1,"Set Temp = ");
lcd.print(setTemp);
}
Re: Brewery Programming
oh also I had the LCD scramble on me when I tried to switch my fridge off with the unit, the board already has the snubber circuit connected across the relay contacts, so I decided to put a 25v 2200uF electrolytic cap across the 12v supply input and it has seemed the fix the problem.
cheers steve
cheers steve
Re: Brewery Programming
Math, does this/could this control fermentation with steps in? (ie, 2 days at 12c - 20 days at 9c - 2 days at 18c - few months at 2c)
Its probably beyond me how I'd make it OR how I'd afford it with all the other toys I'm saving up for
but Its a straight toss up between this and 2 STC-1000s. which are cheaper but require you to remember to do things and be close enough to do them.
Its probably beyond me how I'd make it OR how I'd afford it with all the other toys I'm saving up for

Re: Brewery Programming
yeah I have been planning on doing that, in the code already is a timer and it wouldn't be hard to do, just making up a menu that is easy to set would take a bit of work. I have been thinking of maybe 3 profiles you can pick from and you can change those profiles. I will have to think about it a bit longer
cheers steve
cheers steve
-
- Telling imaginary friend stories
- Posts: 5229
- Joined: Sun Oct 16, 2011 1:49 pm
- Location: Cowley, Oxford
Re: Brewery Programming
why not just lift/use the staged mash profile setup and execution in the excellent code u already wrote?Matho wrote:yeah I have been planning on doing that, in the code already is a timer and it wouldn't be hard to do, just making up a menu that is easy to set would take a bit of work. I have been thinking of maybe 3 profiles you can pick from and you can change those profiles. I will have to think about it a bit longer
cheers steve
a rtc might be useful for the longer timescales..
ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate

Re: Brewery Programming
Thanks Fil, I was thinking of implementing something like this
start: 'X' degs
hold 'X' days
ramp to 'x' degs over 'x' amount of days
hold 'x' days
and so on.
The timer should be good for about 7 weeks, if I want more I'll have to write a bit of code to handle the overflow, a RTC would be good too and I have one in my parts bin but I'm too lazy to figure it out.
cheers steve
start: 'X' degs
hold 'X' days
ramp to 'x' degs over 'x' amount of days
hold 'x' days
and so on.
The timer should be good for about 7 weeks, if I want more I'll have to write a bit of code to handle the overflow, a RTC would be good too and I have one in my parts bin but I'm too lazy to figure it out.
cheers steve
-
- Telling imaginary friend stories
- Posts: 5229
- Joined: Sun Oct 16, 2011 1:49 pm
- Location: Cowley, Oxford
Re: Brewery Programming
sampled one to many to make sense this evening
but was thinking a list of stages would define a fermentation each stage with its own ramping data, index, duration and target perhaps expected starting temp too for error checkin? could be coded into a byte or 2 per stage without too much hassle if space is a prob, might even get away with a recersive control function with a depth check of course...
but as i dont intend to do owt about it
so u do it your way 

but as i dont intend to do owt about it


ist update for months n months..
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate
Fermnting: not a lot..
Conditioning: nowt
Maturing: Challenger smash, and a kit lager
Drinking: dry one minikeg left in the store
Coming Soon Lots planned for the near future nowt for the immediate
