A friend of mine is an avid all-grain home beer brewer. He prefers to keep things as manual as possible, but one thing bugs him: the thermometer is a delicate piece of equipment, and the mashing process requires careful monitoring of the temperature. Of course, digital thermometers come a dime a dozen, but how about a thermometer that can be programmed for a target temperature and tells him to increase or decrease the heat? It's a thermostat, yes, but for a DYI guy like him, a home-made one is better than anything you can find at a store.
This is a job for the PICaxe 08M, which has built-in support for the Dallas Semiconductors DS18B20 1-wire temperature probe.
The first version is quite simple: the user sets the target temperature using an IR remote, and the 08M writes back the entry on a two-digit display. Next, the 08M continuously measures the temperature via the DS18B20, and lights an LED if the heater must be turned up. The user may change the target temperature any time using the remote. The IR device is a TSOP 1138.
The hardware is about as simple as it can be. Note that the veroboard strips must be broken in five places. A jumper is used to determine whether input pin should be used for programming (jumper position closest to the processor), or for resetting the display (covered shortly) once the circuit is running.
Future revisions may involve the use of a servo motor for turning up or down the heater with only very minor modifications of the hardware and software.
The display makes use of two 4026 decade counters with built-in 7-segment drivers. Only two wires are required to display two digits: one reset signal that resets the decade counters to 00. The other is a clock that is pulsed to count to the desired output. The counting is fast enough to cause only a small fluttering of the display as the desired output is reached:
The 08M is stalled while executing the infrain2 instruction. In order to allow continuous temperature measurement, instead the 08M polls for activity on the IR pin. If any activity is detected, it enters an input mode. Similarly, the 08M is stalled while reading the DS18B20. Hence, rather than measuring repeatedly, in order to accept IR input the 08M must be explicitly told to do nothing for a while (i.e., doing nothing but polling for IUR input) so that the risk of entering an input just when the 08M reads the DS18B20 is kept at a tolerable level. The program works its way somewhat around the stalls by reading the DS18B20 only about every 15 seconds.
The DS18B20 is mounted in a copper pipe with brass ends, allowing it to be submerged in the warm or boiling wort during the entire brewing process:
The 08M is programmed with the following program by placing the jumper closest to the processor:
' Beer Brew Temperature Control ' Usage: ' NN: Set the target temperature, which is displayed for
' one second. Then the display shows the current
' temperature. #picaxe 08m #com /dev/ttyUSB0 ' Preload the EEPROM with defaults: ' Value: Last target temperature. eeprom 0, ( 60 ) ' Pin 0: Reset 4026-based 7-segment counter. ' Pin 1: Input from sensor. ' Pin 2: Output to heater indicator. ' Pin 3: IR input. ' Pin 4: Count a 4026-based 7-segment counter. input 1 output 0, 2, 4 symbol i = b0 symbol two_digits = b1 symbol targettemperature = b2 symbol currenttemperature = b3 symbol lasttemperature = b4 symbol difference = b5 symbol waitcounter = w3 ' b6:b7 symbol irreceiverpin = pin3 ' symbol infra = b13 (these are synonymous) ' Turn off heater. low 2 ' Restore the settings from EEPROM. read 0, targettemperature lasttemperature = -127 main: waitcounter = 15000 ' Wait for a key. Poll for a key in order to let the temperature be
' updated repeatedly.
poll: if irreceiverpin = 0 then infrain2 ' If it's a digit key, then it's the target temperature. if infra < 10 then ' Decode the first key, bypassing wait and validation. gosub getonedigit_nowait ' Get the second digit of the two-digit number. gosub getseconddigit targettemperature = two_digits ' Store the target temperature in EEPROM. write 0, targettemperature ' Show the target temperature for two seconds before
' showing the current temperature. pause 1000 lasttemperature = -127 goto sampletemperature endif endif ' Count down until about half a minute before sampling the temperature. waitcounter = waitcounter - 1 if waitcounter > 0 then poll ' Read the temperature. We won't compensate for impossible
' negative temperatures. sampletemperature: readtemp 1, currenttemperature ' Show the current temperature again if it has changed. if currenttemperature <> lasttemperature then lasttemperature = currenttemperature two_digits = currenttemperature gosub showtwodigits endif gosub adjustheater goto main ' Turn heater on or off, depending on temperature. This could be
' modified to, e.g., set a servo motor to open or close a valve. In
' this case, it indicates whether the temperature is high or low. adjustheater: difference = targettemperature - currenttemperature ' Too hot: Turn off the heater. if difference > 128 then low 2 ' Too cold: Turn on the heater. else if difference >= 1 then high 2 endif return ' Read one digit key from the remote. getonedigit: trydigitagain: infrain2 ' Keep trying until a valid number key has been entered. if infra > 9 then trydigitagain ' Label used to bypass waiting and validating the digit. getonedigit_nowait: ' Keys '1' through '9' have values 0 through 8, and ' key '0' has value 9. Add one, and perform a modulus 10
' to wrap the '0' key around, and we have the true value
' of the key. infra = infra + 1 infra = infra // 10 ' Wait until the key has probably been released. pause 500 return ' Read two digit keys from the remote and return the
' two-digit value. gettwodigits: gosub getonedigit ' Label used to bypass waiting for the first digit. getseconddigit: ' Move the first digit to the tens position. two_digits = infra * 10 gosub getonedigit ' Add the lower digit to the ones position. two_digits = two_digits + infra ' Display the entered value. gosub showtwodigits return ' Show the value of "two_digits" by resetting the display
' and counting to the display value. showtwodigits: ' Reset the counters. pulsout 0, 1 ' 10 us pulse ' Count to the display value. if two_digits = 0 then endshow for i = 1 to two_digits pulsout 4, 1 ' 10 us pulse next i endshow: return
After programming, move the jumper to the opposite position, and the thermometer is ready.