Brewduino
So, I need a way to control the temperature in my fermenting vessel.
Home brew is quite forgiving in it's temperature range, but my spare room is just too chilly for it. So, I need a heat mat. I'm using a heat mat that's about 3 feet long and normally used in a reptile vivarium. I wrap it around the vessel with a piece of string.
Just call me Macguyver.
But the heat mat is always on. I need a way to check the temperature of the vessel, but without putting anything in there. The Arduino with a solid state relay and a DHT22 temperature/humidity sensor would be just the thing.
But I also fancy logging the data. So, we'll need an ESP8266 module that'll do wifi. Nice!
Let's get to it!
DHT22 + ESP8266 + ThingSpeak = Stuff!
I’ve connected the DHT22 module using the three pins. One wire to +5V, one to ground, and the other to pin 6 on the Arduino MEGA that I'm using.
Other than the serial monitor there’s no way to see the temperature. I prefer visuals, so I’m going to hook up the ESP8266 (WiFi) module and send my results to ThingSpeak. The ESP8266 uses five of the eight pins that it has. One for +3.3V (NOT +5V or you will fry it) shown as a yellow wire in the diagram, one for ground (black wire), one each for RX and TX (green and blue) and a reset pin (white wire) which also goes to +3.3V.
The code for the DHT22 is pretty straightforward, so we’ll look at that next. The ESP8266 is more tricky, and is quite often prone to not responding. If you think your code is right, and the ESP8266 isn’t responding, try unplugging/plugging the two 3.3V wires from the breadboard. I find that gives a good reset to the module. When the code is running you’ll see the RX/TX lights flash on the Arduino and at that same time you should see a blue light on the ESP8266 flash.
The code is attached here.
For the DHT22 I’m using the two libraries from here: http://playground.arduino.cc/Main/DHTLib
You’ll need to copy/paste the text for the two files to new files and name them accordingly. All the instructions for doing this are at that link.
I start by including the DHT library and define the pin for the DHT22. I then define my wifi SSID and password.
Now we’re on to the main setup. I use two serial begin commands:
Serial.begin(115200); Serial2.begin(9600);
The Serial.begin is for the DHT22, so that I could get results to the serial monitor before I added the wifi module. The Serial2.begin is for the wifi module. If you’re using an Arduino other than the Mega then you may only have one serial RX/TX and need to modify the code accordingly. Now I set up the ESP8266.
//WiFi setup
Serial2.println("AT"); Serial.println("AT sent - checking..."); delay(5000); char okcheck[]="OK"; if(Serial2.find(okcheck)){ Serial.println("OK, found. Connecting"); connectWiFi(); Serial.println("Yay! Should be connected now.");} else{ Serial.println("NOT CONNECTED TO WIFI"); }
When using Serial2 I’m talking to the ESP8266. When using just Serial I’m talking (or printing to) the serial monitor. I’m sending the command AT to the ESP8266 and printing text to the serial monitor to show me what is happening in the background. I wait five seconds then run a Serial2.find to see if I got a reply of OK. If I did then I’m going to try and connect (by jumping to connectWiFi() ). If not then I display the text to say not connected.
Connecting to a wifi router needs the SSID, password and several commands.
boolean connectWiFi(){
Serial2.println("AT+CWMODE=1"); delay(2000); String cmd="AT+CWJAP=\""; // add SSID and password cmd+=SSID; cmd+="\",\""; cmd+=PASS; cmd+="\""; // send string Serial2.println(cmd); delay(5000); // was the login accepted? char okcheck[]="OK"; if(Serial2.find(okcheck)){ Serial.println("Login accepted"); return true; }else{ Serial.println("Login not accepted."); return false; } }
So, I send the command AT+CWMODE=1 to the ESP8266. Wait two seconds, create a string called cmd and start with AT+CWJAP=\” then add to the string with the SSID, password, then send the completed string to the ESP8266. Again, I check for a reply of OK (or not) with an appropriate message to the serial monitor.
For the main loop I first jump to TempHum() to get my temperature info from the DHT22.
Serial.print("DHT22, \t");
int chk = DHT.read22(DHT22_PIN); switch (chk) { case DHTLIB_OK: Serial.print("OK,\t"); break; case DHTLIB_ERROR_CHECKSUM: Serial.print("Checksum error,\t"); break; case DHTLIB_ERROR_TIMEOUT: Serial.print("Time out error,\t"); break; default: Serial.print("Unknown error,\t"); break; } // DISPLAY DATA Serial.print(DHT.humidity, 1); Serial.print(",\t\t"); Serial.println(DHT.temperature, 1);
This is simply creating an integer called chk and reading the DHT22 pin. I check the status of the DHT22 (the switch, which I’ve snipped that code here to save space), and print the temperature and humidity to the serial monitor.
String SendTempLevel = String((float)DHT.temperature, 0);The two Strings are holders for the temperature and humidity and is what I’ll send to ThingSpeak.
String SendHumLevel = String((float)DHT.humidity, 0);
I jump to updateTemp() taking those two strings with me. Now it’s time to send to ThingSpeak. You’ll obviously need to create a free account with ThingSpeak, create a channel, have two fields (for temperature and humidity) and obtain your API key.
String cmd = "AT+CIPSTART=\"TCP\",\"";
cmd += "184.106.153.149"; // api.thingspeak.com cmd += "\",80"; Serial2.println(cmd);
Like last time I create a string called cmd and send it AT codes, add the ThingSpeak IP and port then send it to the ESP8266.
Again, I do a check to see if I’m send an error or not.
String getStr = "GET /update?api_key=";
getStr += "8KS0CVMQ12A7D817"; getStr += "&field1="; getStr += String(SendTempLevel); getStr += "&field2="; getStr += String(SendHumLevel); getStr += "\r\n\r\n";
A new string, getStr, is created with a GET command (with my ThingSpeak API key) and temperature and humidity.
String cmd is created again with an AT command and the getStr.length will tell us now long the getStr is. This is required for sending to ThingSpeak and send to the ESP8266.
Like previous sends we check for a reply. The greater than character (>) means good. Anything else is bad and we send AT+CIPCLOSE to close the connection. Again, info is sent to the serial monitor to tell us what’s happening.
Uploading the gist code to the MEGA should be error free and, when running, the serial monitor will tell you what is happening.
Downloads
LCD + Relay = MORE Stuff!
In previous builds I used an LCD screen which, while it worked, would need about a dozen wires and a potentiometer to control the screen brightness (see photo, top board). This time I’ve managed to get some much simpler LCD screens that require only four wires (same photo, bottom board). The wires are VCC, GND, SDA and SCL. These should be marked on your Arduino, but on UNO boards I believe it’s A4 and A5. On my MEGA it’s 20 and 21 and they’re marked as such. But you need to put both those lines through a 4.7K resistor from 5V.
These newer LCD screens are I2C which means they have a little controller board on the back. It also means they require a newer LCD library (https://bitbucket.org/fmalpartida/new-liquidcrystal) in the code.
Before adding LCD code, we need to do a scan of the LCD screen to get its I2C address. Different models have different addresses. So, grab the code from: http://arduino.cc/playground/Main/I2cScanner and run it to see your LCD I2C address in the serial monitor. Mine is 0x3F.
LCD Code
I need to include that new library:
#include <LiquidCrystal_I2C.h>
Define various pins (I2C stuff that you shouldn’t need to touch) but insert my I2C address:
#define I2C_ADDR 0x3F // <<----- Add your address here.
#define BACKLIGHT_PIN 3 #define En_pin 2 #define Rw_pin 1 #define Rs_pin 0 #define D4_pin 4 #define D5_pin 5 #define D6_pin 6 #define D7_pin 7 LiquidCrystal_I2C lcd(I2C_ADDR,En_pin,Rw_pin,Rs_pin,D4_pin,D5_pin,D6_pin,D7_pin);
In my setup I initialise the screen as 16X2, turn on the backlight, and print a message and put a delay to allow reading it.
lcd.begin (16,2); // LCD is 16x2
lcd.setBacklightPin(BACKLIGHT_PIN,POSITIVE); // Switch on the backlight lcd.setBacklight(HIGH); lcd.home(); // go home lcd.print("Screen OK"); // print to screen delay(2000);
I now have my temperature, humidity, and some of my serial monitor outputs going to the LCD screen using the lcd.print() command.
lcd.setCursor(0,2); lcd.print("Checking... ");
lcd.setCursor() is for placing the text at a particular position on the screen.
NOTE: if you don’t see anything on your screen on first run, try turning the potentiometer on the back of the LCD screen to adjust the brightness of the screen.
WARNING: This time we’re heading into the big league with controlling
household voltage devices. It should go without saying, but I’ll say it anyway, you must make sure your 110/240V devices are unplugged before chopping cables and double check your wiring before you do put that plug back into the wall outlet.
Solid State Relay
First, you can use a relay switch with this circuit, but you’ll get a physical clicking sound as the relay switches back and forth. No big deal, but a solid state relay is digital and without sound. Be absolutely sure that the relay switch (solid state or not) is rated to do 110/240V.
Before I chop into the cable of my beloved heat mat I want to make sure this circuit will work. So, instead, I’m going to chop into an unused desk lamp.
With it unplugged I chopped through the cable and paired off the two inner cables (live and neutral). I then twisted the two live cables together and left the two neutral cables detached. I also put some electrical tape over that twist to cover it up and keep things somewhat safe.
The solid state relay (SSR for short) has two DC inputs (in the photo, at that’s bottom left, is the red +5v and blue ground). It also has (depending on your SSR) one or more channels. My SSR has two channels. This is where your Arduino (yellow wire in the photo) will tell the SSR to go HIGH or LOW. The final two inputs are for the cable you wish to open/close. In this case the neutral (top wires in the photo) from my lamp. Now, it didn’t help that my inputs are labelled wrongly. The one marked ‘Ch1’ is actually for SSR channel two. So if you try this and it doesn’t work, try switching your wire from Ch1 to Ch2. You should see a light come on on the SSR to show which channel is HIGH/LOW.
With those things in place it’s time to write some code.
To test the SSR I’ll flash the lamp on/off. The first new lines of code are:
#define ssr1 53int state = LOW; unsigned long previousMillis= 0; const long interval= 1000;
This defines pin 53 on the Arduino as the control pin for the SSR. The state is to keep track of whether the lamp is currently on/off. The previousMillis and interval are for the flash. I’m going to try and use millis rather than delays as millis is more preferable to the program halting delay command.
I begin with the usual pinMode and then set the SSR to the initial state of LOW. In other words, off. This SSR is ‘low level trigger’ which means LOW is on, and HIGH is off.
pinMode(ssr1, OUTPUT); digitalWrite(ssr1, state);
In the setup I create:
unsigned long currentMillis = millis();
as this will be used in the if/then to switch on/off the lamp.
The only other additional code of note is the switching:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis; // using millis() for light flash delay // flash light if (state == LOW) { state=HIGH; // want to turn the SSR off } else { state=LOW; // want to turn the SSR on } digitalWrite(ssr1, state); // turn the SSR on/off }
currentMillis and previousMillis will keep track of how many milliseconds the loop is running for. When it reaches the interval (set up at the start) then the loop ends. Note that this means the code never grinds to a halt like it would with a delay. I can still put stuff to do inside that HIGH/LOW if statement.
The inner if/else is just checking to see if it’s currently off, let’s put it on, if it’s currently on, let’s put it off. And, finally, do the on/off.
With the interval set at 1000 this means the lamp will turn on/off every second.
The only downside of removing the delays is that I’m now hammering ThingSpeak every minute or less, but I’ll fix that later. It’s working. That’s the main thing!
The code for this is attached.
To check for a specific temperature it’s as simple as putting in two if statements.
if (DHT.temperature > 24 ) { state=LOW; }
if (DHT.temperature < 24) { state=HIGH; }
If the temperature goes above 24, the lamp comes on. If the temperature falls below 24, the lamp goes off.
Downloads
The End Is Nigh
Not much left to do on the Brewduino now.
I’ve added some LEDs just to indicate what’s going on. The green LED is to show that the device is on and OK. The red LED shows that the switch is on and, thus, the heat mat is on and heating up the fermenting vessel. Finally, the blue LED shows that the switch, and heat mat, are off and that the vessel is cooling down to room temperature.
The best way, I found, to test the whole device is to have the DHT22 under the lamp as when the lamp comes on the heat from the bulb will bring the temperature up and the whole system goes into an on/off cycle. The lamp heating the DHT, the lamp goes off, the DHT cools down, threshold is reached and the lamp comes back on again. And so on.
The only adjustments to the code are to define the LED pins:
int power = 43; int red = 47; int blue = 39;
Then, set the modes to output, and put the (power) green LED on:
pinMode(power, OUTPUT); // LED
pinMode(red, OUTPUT); // LED pinMode(blue, OUTPUT); // LED digitalWrite(power, HIGH); // power on LED
The temperature check statement now includes the states for the red and blue LEDs:
if (DHT.temperature > 24 ) {
state=HIGH; // switch heat mat on digitalWrite(red, LOW); // heating on LED digitalWrite(blue, HIGH); // heating off LED } if (DHT.temperature < 24) { state=LOW; // switch heat mat off digitalWrite(red, HIGH); // heating on LED digitalWrite(blue, LOW); // heating off LED }
You’ll note that I’ve also fixed the SSR state so that the light is LOW (on) when the temperature is below 24, and HIGH (off) when above 24. This is the correct procedure for the heat mat.
Well, we’ve pretty much come to the end of the Brewduino. Apart from swapping out the lamp for the heat mat, everything is in place and working. Obviously, everything will need to go in a nice box, and the Arduino will need a power supply (as I need my laptop!) but everything is working, and that’s the main thing.
No doubt I’ll be tinkering with the code between writing this up, and you reading it, but the code referred to here is attached.