• Please visit and share your knowledge at our sister communities:
  • If you have not, please join our official Homebrewing Facebook Group!

    Homebrewing Facebook Group

Real Time Online Fermentation Temperature Monitor for ~$60

Homebrew Talk

Help Support Homebrew Talk:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.
None at all. You'll just need to expand the 2D sensor array to hold the extra roms and add some extra sensor names. Then in int main() you'll need a few extra

Code:
int16_t rawTemperature2 = ReadSensor(1);  // Read the second sensor

and finally you'll need to modify BuildPut to string together the extra sensor data

I suppose I could make the sensor names a 2D array and design the code to automatically loop through all sensor ROMs and string the data together. Or I could make a data type to hold the sensor ROM and name and just loop through an array of those...
 
If you're really interested in saving code space, you could reduce the precision to 9 bits, still be within the +-0.5 deg C accuracy of the chip, and speed up your chip read times by a factor of almost 5 (I use 125ms for 9 bit vs. 750mc for 12 bit). I mean seriously, when is a ten-thousandth of a degree going to make a difference?? :confused:
 
If you're really interested in saving code space, you could reduce the precision to 9 bits, still be within the +-0.5 deg C accuracy of the chip, and speed up your chip read times by a factor of almost 5 (I use 125ms for 9 bit vs. 750mc for 12 bit). I mean seriously, when is a ten-thousandth of a degree going to make a difference?? :confused:

Yeah, ±0.5°C (±0.9°F) isn't nearly precise enough for me. A 12-bit reading still only reads to the nearest 0.1125°F which really means that I should only be reporting temperatures to the nearest 0.1°F. I was always taught the precision you attach to a measurement is the same as that of the most significant digit of the error associated with that measurement. Besides, in my experiments taking a temperature reading every second really did cause internal heating of the DS18B20s. At a reading every 3-5 seconds I don't observe any extra heating. The spike in my carboy temperature at 21:15:00 is nothing more than a result of polling the temperature sensor too frequently. As soon as I stopped pestering it, it cooled back down to the true temperature. I care more about reading the real temperature than reading it every second; nothing is changing very quickly in my system anyway.

EDIT: I forgot the mention that I shaved another 400 bytes of program memory by tweaking my itoa and RawTemperatureToString routines.
 
If you're really interested in saving code space, you could reduce the precision to 9 bits, still be within the +-0.5 deg C accuracy of the chip, and speed up your chip read times by a factor of almost 5 (I use 125ms for 9 bit vs. 750mc for 12 bit). I mean seriously, when is a ten-thousandth of a degree going to make a difference?? :confused:

For brewing purposes though, really whats the point of 125ms delay vs 750ms delay? We aren't sending this thing to be on the International Space Station where milliseconds matter for life and death. The worst any of these sensors are going to see is a keezer or ferm chamber :)

Realistically even polling every 5 seconds like we are is way overkill, you could do every minute and still get the same usefulness out of the data.

Because we arent trying to push the sensors delay to the limit, i'd rather sacrifice speed that we dont care about for sensitivity, which we do. Granted having .11F accuracy is way overkill too.
 
In my case, I'm polling up to 36 separate devices, so a 750ms delay each starts to add up.

I'm also not sitting there in a delay() either. I set a timer per device, and do a read when the timer expires.
 
In my case, I'm polling up to 36 separate devices, so a 750ms delay each starts to add up.

I'm also not sitting there in a delay() either. I set a timer per device, and do a read when the timer expires.

First of all bravo Sir! ANY project which takes 36 temperature sensors must be chocked full of win! Just making sure but you do know that writing STARTCONVO onto the OneWire bus makes all the sensors on the bus take a temperature reading and store the results in their EEPROM (scratchpad) right? Just wait 750 ms and then poll each of the 36 one after the other with no further delay. I hacked my code to take 36 readings and it completes them in 300 ms total!

Code:
reset();
    write(0xCC);
    write(STARTCONVO);
    _delay_ms(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20(s)
    reset();
    
    // Read the temperatures previously requested
    int16_t rawTemperature1, rawTemperature2;
    long startTime, endTime;
    startTime = millis();
    for (uint8_t i=0;i<=18;i++) {
      rawTemperature1 = ReadSensor(0);  // Read the first sensor
      rawTemperature2 = ReadSensor(1);  // Read the second sensor
    }
    endTime = millis();
    Serial.print("36 temperature readings took ");
    Serial.print(endTime-startTime);
    Serial.println(" ms");
 
First of all bravo Sir! ANY project which takes 36 temperature sensors must be chocked full of win!

Actually it can be a mix of DS18B20 Temp Sensors and DS2406+ digital switches. I'm also looking at other possible 1-Wire devices as well. The project is here, and my website is here. I'm currently adding hardware and code to support multiple 4x20 HD44780-compatible LCDs over an I2C interface.

Just making sure but you do know that writing STARTCONVO onto the OneWire bus makes all the sensors on the bus take a temperature reading and store the results in their EEPROM (scratchpad) right? Just wait 750 ms and then poll each of the 36 one after the other with no further delay. I hacked my code to take 36 readings and it completes them in 300 ms total!

I appreciate you pointing that out, I re-checked the datasheets for both devices and there doesn't appear to be any overlapping commands that might collide with one another, but I haven't checked any of the other devices yet. For the time being, I think using MATCHROM is a better approach.:rockin:
 
I got one a while back, could not get it to work to my satisfaction. It may be OK if all you're doing is pushing info to COSM, but it took a lot of overhead to use the Arduino as a web-server.
 
Good to know that you've got at least some code for it. I found an EtherShield libraray which it seems like is the most developed/functional for that chip. I'm hoping between the datasheet and the chunks of code that I can find that I can do for it what I did for the w5100. I've read that this chip is really only appropriate for client situations rather than as a server.
 
I came across Atmel AVR4027: Tips and Tricks to Optimize Your C Code for 8-bit AVR Microcontrollers and applied what I could to my fermentation monitor code to tremendous effect. As I speculated I was doing some things stupidly especially global variables which burn up SRAM big time! My newest code now takes up 2,812 bytes of program memory and 121 bytes of SRAM. Before applying the tips I used 2,856 bytes of program memory (not a big difference here) and 333 bytes of SRAM (HUGE difference here). I also added some escapes from a few infinite loops which periodically caused the code to hang until board reset.
 
I used one 4.7k on each of my sensors as well Fuzze. I later learned that I just need one per grouping but my project has been working just fine with one per DS18B20.

By the way, I'm working on compacting sketch size and SRAM usage by stuffing just those parts of DallasTemperature.cpp and DallasTemperature.h into my sketch that I actually need. I know the compiler is supposed to remove unreferenced code but so far I was able to shave almost 2k off my sketch size just be deep sixing Dallas. I may give the same treatment to OneWire later this week if I'm feeling ambitious. This is my very simple code to read a 12 bit temperature off a DS18B20 and spit it to the serial monitor in F.

Code:
#include <OneWire.h>

// Data wire is plugged into pin 3 on the Arduino
#define ONE_WIRE_BUS 3
#define STARTCONVO      0x44  // Tells device to take a temperature reading and put it on the scratchpad
#define READSCRATCH     0xBE  // Read EEPROM
#define TEMP_LSB        0
#define TEMP_MSB        1
uint8_t testTherm[8]= { 0x28, 0x83, 0xF5, 0x83, 0x04, 0x00, 0x00, 0x16 };
uint8_t scratchPad[8];

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

void setup()
{
  // This code will only run once, after each powerup or reset of the board
  Serial.begin(9600);
  Serial.print("Setup");
}

void loop() {
 
  // Request temperatures function from Dallas
  //Serial.print("Requesting\n");
  oneWire.reset();
  oneWire.skip();
  oneWire.write(STARTCONVO, false);
  
  // Read the temperature from the DS18B20
  delay(750);  // Wait 750ms to allow for a 12-bit precision temperature reading to be taken by the DS18B20
  //Serial.print("Reading\n");
  oneWire.reset();
  oneWire.select(testTherm);
  oneWire.write(READSCRATCH);
  scratchPad[TEMP_LSB] = oneWire.read();
  scratchPad[TEMP_MSB] = oneWire.read();
  oneWire.reset();
  int16_t rawTemperature = (((int16_t)scratchPad[TEMP_MSB]) << 8) | scratchPad[TEMP_LSB];
  Serial.print( (float)rawTemperature * 0.0625*1.8+32,4);
  Serial.write(176);
  Serial.print("F\n");
  delay(5000);

}

I'm going through this trouble because DallasTemperature is way bigger than what we need it to be and I want to have plenty of room on my chip for doing cooler stuff like output to an LCD and controlling stuff. Of course this sketch would be a heck of a lot smaller if I wasn't using the Serial monitor. With that commented out the sketch size is 1,738 bytes and uses 39 bytes of SRAM!

The OneWire protocol has been looking a little portly to me for a while now so I went one step further with it and rewrote all the read_bit, read, write_bit, write, and reset functions to twiddle ports directly rather than doing it through the #defines that OneWire uses. I removed a little of the flexibility in that now one must run their OneWire bus on Port D (digital pins 2-7). I took the code that reads a temperature all the way down to 504 bytes of program memory and 8 bytes of SRAM usage which is crazy given a blank Arduino sketch takes 444 and 9!
 
So I tried this. I bought an UNO, Ethershield, and two super cheap DS18B20's from deal extreme. (I have plenty of wire and resistors lying around the house. Grand total was like $30.)

It worked beautifully. Both temp probes were surprisingly accurate. I calibrated them against my thermopen, and found I only needed a .2F offset on one and a .6F offset on the other.

The problem came when I climbed into my attic to run an Ethernet cord to the garage -- I'm not that motivated. Wireless it is.

WIFI sheilds are expensive, so I decided to drop $35 on a Raspberry Pi, and hook up a $5 USB wifi nic I have around. Updates to come on that front.

For now, here are the two temp probes monitoring my closet temp. https://cosm.com/feeds/120160
 
Ok, the raspberry pi arrived. i bought a $2 ribbon cable for its GPIO pins, and shoved my temp probe wires directly into the other end of the ribbon cable.

Raspbian is an easy install, and the wifi card was supported out of the box.

After a couple hours of learning some rudimentary python, I had it pushing temps to COSM again.

The last piece of the puzzle was getting it to auto-login and, auto launch VNC, and auto-run my script. At this point, should the power ever drop, or the Pi reboots, it'll fire right back up.

I'll post some brief instructions and include my Python scripts tomorrow. If I ever get around to it, I'll write blog post that really details all the packages you need and what not.

https://cosm.com/feeds/120160

There's my feed. That's the current temp of my Stone Enjoy By IPA clone
 
Alright, I had some more fun today. I threw a new page together on my blog. I'm polling both my datastreams from COSM for the current datapoint with a little script. The script updates every 10 seconds, so the text value should always be up-to-date. The graphs are embed from the Graph Builder on COSM. Those update on every page refresh.

http://www.bertusbrewery.com/p/temp-monitor.html

Here's my python code below. You'd first need to install the python-dev, python-pip, and EEML packages. I'll eventually do a full write up that includes more of a step-by-step.

The Raspberry Pi was definitely more involved that the Arduino, but it's a much more powerful little box with more options (cheap wifi, desktop environment, VNC, etc).
Code:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import glob
import time
import eeml

# Setting up the temp probes
os.system('modprobe w1-gpio')
os.system('modprobe w1-therm')

# The two 28-XXXX numbers below are my two temp probes
# You'll need to replace those with your own
base_dir = '/sys/bus/w1/devices/'
device1_folder = glob.glob(base_dir + '28-00000480c6e8')[0]
device1_file = device1_folder + '/w1_slave'
device2_folder = glob.glob(base_dir + '28-000004837542')[0]
device2_file = device2_folder + '/w1_slave'

# Insert your API KEY below.
API_KEY = 'PutYourAPIKeyHere,LeaveTheApostrophes' 
# Replace the XX's with your FEED ID, e.g. 120160
FEED = XXXXX

API_URL = '/v2/feeds/{feednum}.xml' .format(feednum = FEED)
def read_temp_raw1():
    f = open(device1_file, 'r')
    lines1 = f.readlines()
    f.close()
    return lines1

def read_temp_raw2():
    f = open(device2_file, 'r')
    lines2 = f.readlines()
    f.close()
    return lines2


def read_temp1():
    lines1 = read_temp_raw1()
    while lines1[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines1 = read_temp_raw1()
    equals_pos = lines1[1].find('t=')
    if equals_pos != -1:
        temp_string = lines1[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        temp_f = temp_c * 9.0 / 5.0 + 32
# temp_f is our temp in F, but it's a floating integer. 
# we want a rounded number. Replace the number 1 below with 2
# if you want your value in hundreths not teths.
        temp_r = "%.1f" % round(temp_f,1)
        return temp_r

def read_temp2():
    lines2 = read_temp_raw2()
    while lines2[0].strip()[-3:] != 'YES':
        time.sleep(0.2)
        lines2 = read_temp_raw2()
    equals_pos = lines2[1].find('t=')
    if equals_pos != -1:
        temp_string = lines2[1][equals_pos+2:]
        temp_c = float(temp_string) / 1000.0
        temp_f = temp_c * 9.0 / 5.0 + 31.77
# Same deal as above, we're rounding our reading for probe2
        temp_r = "%.1f" % round(temp_f,1)
        return temp_r

# Doing two things here, first printing the temps in the
# terminal window. Secondly, sending the data to COSM
while True:
	print(read_temp1(), read_temp2())	
        pac = eeml.Pachube(API_URL, API_KEY)
# Change 'FermentationTemp' to whatever you want
        pac.update ([eeml.Data('FermentationTemp', read_temp1(), unit=eeml.Fahrenheit())])
# FridgeTemp is my second Stream name, change as well
        pac.update ([eeml.Data('FridgeTemp', read_temp2(), unit=eeml.Fahrenheit())])
        pac.put()	
# This polls every 7 seconds. You could go longer if you like. 
        time.sleep(7)
 
Finally getting around to turning this into a full fledged temperature controller.

Bought this thing called a Powerswitch Tail II, which can switch up to 120V @ 15A and doesnt require any real wiring but takes in the Microcontroller outputs from Arduino...the 15A being key obviously as many freezers spike up to 10A or more when the compressor comes on, so this should give a bit of margin so the SSR doesnt get super hot or blown out...
PowerSwitch%20Tail%20II.jpg


For $25 bucks its not bad, considering its an all in 1 solution with the connectors, and all the protection circuitry...even though i feel fairly confident playing with 110V mains voltage, every time you wire something up you always have(or should have) that voice in your head telling you not to screw up and fry yourself.

I think in most cases though for a ferm fridge you only use one, either its plugged into the fridge or its plugged into a light source...never both at the same time. So this should work really well.
 
Well isnt this just lovely.

I go to bed last night and everything is fine.

This morning i wake up and COSM does not exist, they are now known as Xively or something....No emails prior..nothing. Just one email at 6am this morning saying that they are transitioning to Xively...what the hell is that. I know its a free service and what not, but why the hell didnt they send this information out weeks or months ago so that we could prepare our feeds? Both of my feeds are stagnant now presumably failing to upload data, and im stuck at work so i cant fix them for the day...bah. It looks like Scottlands is still functioning, so maybe i just need to restart my Arduino to get it to reconnect.

My data stream isnt working, and their new webpage looks like crap.
Im not sure if i can customize the graphs better, but overall i dont like their new setup it seems inferior....im seriously kinda pissed right now ;)
 
Odd, my feeds are still working. They were pachube before COSM, so it's not a shocker to me. The URLs and APIs redirect, so I wonder why it's not working for you?
 
Well isnt this just lovely.

I go to bed last night and everything is fine.

This morning i wake up and COSM does not exist, they are now known as Xively or something....No emails prior..nothing. Just one email at 6am this morning saying that they are transitioning to Xively...what the hell is that. I know its a free service and what not, but why the hell didnt they send this information out weeks or months ago so that we could prepare our feeds? Both of my feeds are stagnant now presumably failing to upload data, and im stuck at work so i cant fix them for the day...bah. It looks like Scottlands is still functioning, so maybe i just need to restart my Arduino to get it to reconnect.

My data stream isnt working, and their new webpage looks like crap.
Im not sure if i can customize the graphs better, but overall i dont like their new setup it seems inferior....im seriously kinda pissed right now ;)

That is why I chose to store my own data and create my own graphs.
 

That's just another step that not everyone needs to or wants to take which is why i made this project. This is more for complete DIY computer *******, start telling computer illiterate people about SQL Database's and HTML code and they will cry ;)

If i wanted to go that far i'd look at something like your project or BrewPi. For me atleast, me storing my own data does me no good unless i plan to actually do something based on the data, like control SSR's.

I updated using their Xively API and its working again, they just changed all the api calls from Cosmclient to Xivelyclient, etc...annoying but only took me 30 mins to fix.

Just a bit pissed that going from COSM as a "Beta" to Xively as a "release" it now has less features. How the hell is that even possible from a software company where the release has less features than the beta lol...ugh. I'd get fired for that for software at work lol.
 
Ya, to do anything, it forced me to more or less create a new feed. So I went ahead, and tore through my python script on my pi. Updated the API address to API.xively everywhere, and updated my new API key and feed number. Then on my blog updated the jscript to pull from the new Xively API with the new key and feed number.

The annoying thing is, the custom graph feature is completely gone. The old API still works, but oddly enough, you have to remove the time zone setting from the URL for it to graph any data.

So I have graphs again (YAY!!), but the time on my graphs isn't accurate (oh well).
 
I already emailed their support with a feature request to re-enable the custom graphs that you could get URL's to for embedding.

I highly suggest you do the same, if we can get multiple people all telling them how retarded they are for removing it they may speed up.
They replied that they noticed a few bugs and omissions and are working on them, but more people complaining may help them prioritize..

I also requested that they add the ability to plot two graphs on the same graph. It would be great if i could just get both Ambient and Fermentation on a single graph since they have the same time deltas and units.
 
That's just another step that not everyone needs to or wants to take which is why i made this project. This is more for complete DIY computer *******, start telling computer illiterate people about SQL Database's and HTML code and they will cry ;)

I have already incorporated all of the code and database access into my software. I've tried to make the system as easy as possible to get started, including boards, so you can start as small or big as you want.

I've got a few folks that are "DIY computer *******" that have gotten the system up and running. :D
 
I've followed a tutorial on setting up the RPi (by Scottland, i think) and it's working fine except the script stops running after as few as 4 hours and no more than 12 h. I have it streaming data to Xively (formally COSM). Any additional code I can include to the script that will resolve this? thanks
 
Thats one reason i opted not to use a RPI much like many others have found it can crash a lot.

That said, it should be pretty trivial to set up a startup service that checks if the script is running every X minutes, if its not, it starts it. That way if the RPI crashes when it comes back up it should start again.
 
I have a question for this kind of build. What size board would be needed if I want to do 5 temp probes. I want to do 1 ambient and then 4 fermenters. Thanks in advance.
 
Well i have since moved away from this method, because the API changed and it was a bit of a pain...but in theory this should still work with the new Xively service.

If you mean what Arduino you just need an Uno, all of the sensors are wired to the same pin and are addressed by their internal address using the OneWire protocol.
 
Back
Top