Help with Arduino HERMS

Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum

Help Support Homebrew Talk - Beer, Wine, Mead, & Cider Brewing Discussion Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.

Starrider

Well-Known Member
Joined
Oct 3, 2012
Messages
79
Reaction score
9
Location
Grass Lake
I hope there are some Arduino coders out here.

I have been (slowly) building an E-Herms setup around an Arduino Uno. A fairly simple setup, or so I thought, and after multiple versions each adding a little more to the features, it was just about done and crashed.

For the controls; I have two DS18B20 probes, one for the HLT and one for the Mash Tun. I have a 12v pump to recirculate from the TUN through a copper coil in the HLT. I had also added PID for a heating element in the HLT although have never tested or used it. I haven’t installed the heating element yet. It has a 20x4 L2C LCD for displaying data and an IR Remote for button input. I have used it on a few batches just monitoring temp and turning the pump on and off to maintain mash temp.

Here is where the trouble starts. It was working but after some minor changes, it stopped registering input from the remote. I added some Serial Prints to see if I could find where it was stopping but seems to just jump over the section where it checks for button presses.

I put together a simple test sketch to verify remote operation and it works perfectly. I then took my working sketch and stripped it all the way back to just registering button presses and updating the LCD display. Except for the detail in the LCD display, the codes looks identical to me. Something must be wrong though because past the “Splash” screen, no button presses will register.

I’m about ready to give up and just buy some Inkbird controllers but it won’t be the same. Plus, I already have the Arduino mounted in an enclosure with quick connects for probes and pump. Enclosure turned out nice even if I do say so myself.

I will try to attach the two sketches in hopes someone can see what is causing it to fail. The short one is the one that works. The longer one no longer works and I see no reason why it shouldn’t.

Ok, so I can't attach the file, I'll try to paste it.

Thanks in advance for any help.

#include <IRremote.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //Set LCD I2C address

int recv_pin=11; //IR receive input pin
IRrecv irrecv(recv_pin);
decode_results results;
unsigned long lastcode;
int volup =0;
int voldn=0;
int chaup=0;
int chadn=0;
int right=0;
int left=0;

//**********************Void Setup*********************************
void setup() {
irrecv.enableIRIn(); //Start the IR receiver
lcd.begin(20, 4);
Serial.begin(9600);

splash();

}
//****************************************************************************
// Void Loop
//****************************************************************************
void loop() {

//*********** Read button presses from remote ****************
if(irrecv.decode(&results)){
Serial.println(results.value,HEX);
if(results.value != 0xFFFFFFFF)
{lastcode=results.value;
}

switch (lastcode) { //Adjust HLT or MLT setpoint based on menu value (Case select)
case 0x2A71BFFD: // Menu button, back to splash screen
splash();
break;
case 0x636d99da: //Right arrow
right++;
break;
case 0xD20E2899: //Left arrow
left++;
break;
case 0x22d912bb: // Volume up button
volup++;
break;
case 0x776c6e7a: // Volume down button
voldn--;
break;
}
irrecv.resume();
}

/*
if(lastcode==0x22d912bb){ //5846514513 0x22d912bb
volup++;
}
if(lastcode==0x776c6e7a){
voldn++;
}
if(lastcode==0x636d99da){
right++;
}
if(lastcode==0xD20E2899){
left++;
}
if(lastcode==0x2A71BFFD){
splash();
}
irrecv.resume();
} */

PrintData();
}

//******************Below are Functions called from above******************************************

void PrintData(){
// First Row
lcd.setCursor(6, 0);
lcd.print(volup);
lcd.setCursor(17, 0);
lcd.print(voldn);

// Second Row
lcd.setCursor(6, 1);
lcd.print(chaup);
lcd.setCursor(17, 1);
lcd.print(chadn);

// Third Row
lcd.setCursor(6, 2);
lcd.print(right);
lcd.setCursor(17, 2);
lcd.print(left);

// Fourth Row
lcd.setCursor(0,3);
lcd.print(lastcode);
}

void Static(){
lcd.clear();
lcd.home();
// First Row
lcd.setCursor(0, 0);
lcd.print("Vol + |");
lcd.setCursor(10, 0);
lcd.print("Vol -");

// Second Row
lcd.setCursor(0, 1);
lcd.print("Cha + |");
lcd.setCursor(10, 1);
lcd.print("Cha -");

// Third Row
lcd.setCursor(0, 2);
lcd.print("Right |");
lcd.setCursor(10, 2);
lcd.print("Left");
}

void splash(){
lcd.clear();
lcd.home();
lcd.setCursor(4, 0);
lcd.print("WELCOME TO");
lcd.setCursor(18, 0);
lcd.write(3); //Upper left logo
lcd.write(4); //Upper right logo
lcd.setCursor(3, 1);
lcd.print("Hickey Brewing");
lcd.setCursor(18, 1);
lcd.write(5); //Lower left logo
lcd.write(6); //Lower right logo
lcd.setCursor(3, 2);
lcd.print("press any key");
lcd.setCursor(0, 3);
lcd.print("v2.2 to start");
//This will pause program until a button is pressed, then continue with void loop.
while (irrecv.decode(&results)==LOW) {
delay(10);
}
irrecv.resume();
Static();
}
 
Here is the longer version that doesn't work:

/* Version 2.5 will add a main menu with navigation to separate screens for
* each vessel and the screen with both.
* Additional ideas:
Auto step mash to change to new temp at desired time

/* RCA Remote code map:
Up Arrow 43E8DC7E
Down Arrow E57092BD
Right Arrow 636D99DA
Left Arrow D20E2899
OK 1DBA1CD5
Menu 2A71BFFD
Volume + 22D912BB
Volume - 776C6E7A
Channel up C3E3EA16
Channel down 656BA055
1 E85BFBA2
2 DABC35D
3 355A375E
4 3B9ABC7D
5 9A13063E
6 36426241
7 5DF0D642
8 3E4AC59D
9 8CF939A6
0 99AD8799
*/
#include <IRremote.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); //Set LCD I2C address

int recv_pin = 11; //IR receive input pin
IRrecv irrecv(recv_pin);
decode_results results;
unsigned long lastcode;

double mlttmp;
//const int mltpmp = 6; //MLT HERMS pump output pin
int Output; //Output (0~255) to send to mltpmp pin
int MLTSetpoint = 154;
char pumppct[4] = "STA";

//#define hltheat 3 //HLT Heater output pin
double hlttmp;
double hltout;
double HLTSetpoint = 85;
int heatpct; //HLT heater output percent

int selection = 1;
int screen = 1;
int vessel = 1;
int item = 1;
int menu = 1;

//Below are custom characters for the LCD display
// degree fahrenheit
byte degree[8] = {
B01000,
B10100,
B01000,
B00000,
B00111,
B00100,
B00111,
B00100,
};

// Logo upper left
byte logo_ul[8] = {
B00000,
B00000,
B00011,
B00100,
B01000,
B10000,
B10101,
B10101,
};

//Logo upper right
byte logo_ur[8] = {
B00000,
B00000,
B11000,
B00100,
B00010,
B00001,
B11001,
B10101,
};

//Logo lower left
byte logo_ll[8] = {
B10111,
B10101,
B10101,
B01000,
B00100,
B00011,
B00000,
B00000,
};

//Logo lower right
byte logo_lr[8] {
B11001,
B10101,
B11001,
B00010,
B00100,
B11000,
B00000,
B00000,
};


//**********************Void Setup*********************************
void setup() {
Serial.begin(9600);
//sensors.begin();

irrecv.enableIRIn(); //Start the IR receiver

lcd.begin(20, 4);
lcd.createChar(0, degree);
lcd.createChar(3, logo_ul);
lcd.createChar(4, logo_ur);
lcd.createChar(5, logo_ll);
lcd.createChar(6, logo_lr);

// Print a splash screen before starting program*****************************
Splash();

}

//**************************Void Loop******************************************
void loop() { //This function loops while the arduino is powered
//*****************************************************************************
//***********Take action from button presses *****************************************

irrecv.resume();
Serial.println("Check for button Press");

if(irrecv.decode(&results)) {
Serial.println(results.value,HEX);
if(results.value != 0xFFFFFFFF)
{
lastcode = results.value;
}

/*
if (results.value == 0x636D99DA && vessel == 1) {
vessel = 2;
}
else if (results.value == 0xD20E2899 && vessel == 2) {
vessel = 1;
//irrecv.resume();
}
*/
switch (lastcode) { //Adjust HLT or MLT setpoint based on screen and vessel value (Case select)

case 0x2A71BFFD: //Menu button
screen = 1; // go back to main menu
Main();
break;
case 0x22d912bb: // Volume up button
if (screen == 2) {
HLTSetpoint++;
}
if (screen == 3) {
MLTSetpoint++;
}
if (screen == 4 && vessel == 1) {
HLTSetpoint++;
}
if (screen == 4 && vessel == 2) {
MLTSetpoint++;
}
break;

case 0x776c6e7a: // Volume down button
if (screen == 2) {
HLTSetpoint--;
}
if (screen == 3) {
MLTSetpoint--;
}
if (screen == 4 && vessel == 1) {
HLTSetpoint--;
}
if (screen == 4 && vessel == 2) {
MLTSetpoint--;
}
break;

case 0x1DBA1CD5: //OK Button
if (screen == 1 && item == 1) {
screen = 2;
HLT();
}
else if (screen == 1 && item == 2) {
screen = 3;
MLT();
}
else if (screen == 1 && item == 3) {
screen = 4;
Both();
}
else if (screen == 1 && item == 4) {
screen = 5;
}
break;
case 0x43E8DC7E: //Up arrow
if (screen == 1 && item == 4) {
item = 3;
lcd.setCursor(9, 1);
lcd.print(" ");
}
else if (screen == 1 && item == 3) {
item = 2;
lcd.setCursor(0, 3);
lcd.print(" ");
}
else if (screen == 1 && item == 2) {
item = 1;
lcd.setCursor(0, 2);
lcd.print(" ");
}
break;
case 0xE57092BD: //down arrow
if (screen == 1 && item == 1) {
item = 2;
lcd.setCursor(0, 1);
lcd.print(" ");
}
else if (screen == 1 && item == 2) {
item = 3;
lcd.setCursor(0, 2);
lcd.print(" ");
}
else if (screen == 1 && item == 3) {
item = 4;
lcd.setCursor(0, 3);
lcd.print(" ");
}
break;
}
}
Serial.println("End of check for button press");
irrecv.resume();

//************ Print setting and actual to LCD **********************************
//Lines below only print data that changes. Static labels are printed in function at bottom

//Serial.println("Which screen are we on?");

switch (screen) { //Prints data to screen based on current screen view
// Case 1 may not be needed. Up/Down Arrow case above clears arrow, can print it as well
case 1: //Main Menu. Only print/move arrow based on item selected
if (item == 1) {
lcd.setCursor(0, 1);
//lcd.write(byte(7)); //Right arrow symbol - custom character
lcd.print((char)126); // Right arrow from character map
}
if (item == 2) {
lcd.setCursor(0, 2);
//lcd.write(byte(7)); //Right arrow symbol
lcd.print((char)126);
}
if (item == 3) {
lcd.setCursor(0, 3);
lcd.print((char)127); //Right arrow symbol
}
if (item == 4) {
lcd.setCursor(9, 1);
lcd.print((char)127); //Right arrow symbol
}
break;

case 2: //HLT Screen
lcd.setCursor(11, 1);
lcd.print(HLTSetpoint, 0);
lcd.setCursor(11, 2);
if (hlttmp < 0) {
lcd.print("ERR");
}
else {
lcd.print(hlttmp, 0);
}
lcd.setCursor(11, 3);
lcd.print(heatpct);
lcd.setCursor(14, 3);
lcd.print("%");
break;

case 3: //MLT Screen
lcd.setCursor (11, 1);
lcd.print(MLTSetpoint, 0);
lcd.setCursor(11, 2);
if (mlttmp < 0) {
lcd.print("ERR");
}
else {
lcd.print(mlttmp, 0);
}
lcd.setCursor(11, 3);
lcd.print(pumppct);
break;

}
//Serial.println("End of loop, start over");
}


//******************Below are Functions called from above******************************************


void HLT () {
//Print static text to LCD for "HLT" screen. This is to help screen flicker by only updating
//the text that changes. Main loop won't clear screen, just overwrite changes.
lcd.clear();
lcd.home();
// First Row
lcd.setCursor(2, 0);
lcd.print("Hot Liquor Tank");

// Second Row
lcd.setCursor(0, 1);
lcd.print("Set Point");
lcd.setCursor(10, 1);

// Third Row
lcd.setCursor(3, 2);
lcd.print("Actual");

// Forth Row
lcd.setCursor(3, 3);
lcd.print("Heater");
}

void MLT() {
//Print static text to LCD for "HLT" screen. This is to help screen flicker by only updating
//the text that changes. Main loop won't clear screen, just overwrite changes.
lcd.clear();
lcd.home();
// First Row
lcd.setCursor(6, 0);
lcd.print("Mash Tun");

// Second Row
lcd.setCursor(0, 1);
lcd.print("Set Point");
lcd.setCursor(15, 1);
lcd.print("Timer");

// Third Row
lcd.setCursor(3, 2);
lcd.print("Actual");
lcd.setCursor(16, 2);
lcd.print("M:");

// Forth Row
lcd.setCursor(5, 3);
lcd.print("Pump");
lcd.setCursor(16, 3);
lcd.print("S:");
}

void Main() {
//Print static text to LCD for "Main Menu" screen. This is to help screen flicker by only updating
//the text that changes. Main loop won't clear screen, just overwrite changes.
lcd.clear();
lcd.home();
// First Row
lcd.setCursor(5, 0);
lcd.print("Main Menu");

// Second Row
lcd.setCursor(2, 1);
lcd.print("HLT");
lcd.setCursor(11, 1);
lcd.print("Boil");
lcd.setCursor(2, 2);
lcd.print("MLT");
lcd.setCursor(11, 2);
lcd.print("Step Mash");

// Third Row
lcd.setCursor(2, 2);
lcd.print("MLT");
lcd.setCursor(11, 2);
lcd.print("Step Mash");

// Forth Row
lcd.setCursor(2, 3);
lcd.print("Both");
}

void Both () {
//Print static text to LCD for "Both" screen. This is to help screen flicker by only updating
//the text that changes. Main loop won't clear screen, just overwrite changes.
lcd.clear();
lcd.home();
// First Row
lcd.setCursor(3, 0);
lcd.print("HLT");
lcd.setCursor(9, 0);
lcd.print((char)255); // Border symbol from character map
//lcd.write(2); //border symbol - custom character
lcd.setCursor(13, 0);
lcd.print("MLT");

// Second Row
lcd.setCursor(0, 1);
lcd.print("Set");
lcd.setCursor(4, 1);
lcd.setCursor(9, 1);
lcd.print((char)255); //border symbol
lcd.print("Set");

// Third Row
lcd.setCursor(0, 2);
lcd.print("Act");
lcd.setCursor(9, 2);
lcd.print((char)255); //border
lcd.print("Act");

// Forth Row
lcd.setCursor(0, 3);
lcd.print("HEAT");
lcd.setCursor(9, 3);
lcd.print((char)255); //Border symbol
lcd.print("PUMP");
}

void Splash(){
lcd.clear();
lcd.home();
lcd.setCursor(4, 0);
lcd.print("WELCOME TO");
lcd.setCursor(18, 0);
lcd.write(3); //Upper left logo
lcd.write(4); //Upper right logo
lcd.setCursor(3, 1);
lcd.print("Hickey Brewing");
lcd.setCursor(18, 1);
lcd.write(5); //Lower left logo
lcd.write(6); //Lower right logo
lcd.setCursor(3, 2);
lcd.print("press any key");
lcd.setCursor(7, 3);
lcd.print("to start");

//This will pause program until menu button is press, then continue with void loop.
while (irrecv.decode(&results) == LOW) {
//irrecv.resume();
delay(20);
}
irrecv.resume();
Main();

}
 
I'll be honest, I don't have the patience to debug that :(. And no experience using an IR remote. But don't give up! I like to see these homebrew controllers.

FYI there is an arduino PID library that could replace your PID box (or multiple boxes) http://playground.arduino.cc/Code/PIDLibrary

It's plenty simple enough to tweak to suit your needs too.
 
My guess is you have a memory problem. How much free RAM is the compiler reporting? Anything less than 80% and you are asking for heap crashes. Your Serial.print's use a bunch of memory on their own (see F() function.)
 
My guess is you have a memory problem. How much free RAM is the compiler reporting? Anything less than 80% and you are asking for heap crashes. Your Serial.print's use a bunch of memory on their own (see F() function.)

+1

Try ditching the PID for a less than/greater than function to reduce your code. I recently did, and literally dropped memory usage ~50%. Also, my temps hold within a tighter range without the PID anyways.
 
The PID code should not be that big. Something is not right, even on an UNO.

Agreed. The PID library isn't that big. I was using a modified Sous Vide code, and scrapping the PID idea allowed me to delete a lot of code that went with the PID. For me, trying to tune the PID was a royal PITA...
 
Tuning is generally much more effort than it is worth. Our sytems move so slowly the I and D don't matter that much. In fact, the I can cause problems because or integral windup.

Empirical values should work well enough. Say for typical 5 gallon batch loads, coefficients like kP = 30, kI = 5, kI = 10 are pretty good.
 
I tried my best to wrap my head around implementing the PID, just seemed to never get it right. I got to the point where I would tune the PID super aggressive, and just turn the element off via control panel switch when I wanted it to stop heating. That's how I figured out that my system has literally no overshoot. Element off, temp stops rises. The element control function now is simply if (element temp < target && mash temp < target ) {heat}. With my lack of programming skills this worked out perfectly, and it will hold my temps +/- 0.5F of target.
 
Understood - the math of PID is simple, but the implementation and real-world response... notsomuch.

No surprise basic ON/OFF works - there is no real "momentum" of a liquid heating system. Like you said, turn relay off, heat stops rising immediately. Anyway, as long as you found a working solution, thats all that matters!
 
...as long as you found a working solution, thats all that matters!

+1

I'm not going to lie though, I hope that one day my system is half as b.a. as yours...please tell me your profession has to do with technology!?
 
One day I'll get there I suppose. My system is slowly advancing, just still pretty Fred Flintstone compared to yours and many others here. I just wait around for you smart folk to do something cool, so I can attempt to copy it....NAILED IT! haha

Your engineering background definitely shines through in your build. Gotta feed the beast.
 
Back
Top