Temperature-Controlled Cold Room With A Raspberry Pi Zero
by Philippe Olivier
Created 2021-08-18
1. Introduction
Under the porch of my house is a small concrete room. During the course of renovating my house, I transformed it into a cold room by heavily insulating it. I have a small heater that keeps the temperature at a constant 8°C during the winter. During the summer, however, the temperature can go as high as around 22°C, which is not ideal as I am also using this room to age wine.
I did not want to install an A/C unit for various reasons. Instead, I decided to build a system that would take the cold outside air (usually during the night) and circulate it through the room when the temperature delta between the inside and the outside of the room would allow it to be cooled. For this purpose, two pipes make a bridge between the inside of the room and the outside of the house, and an old bathroom fan is used to circulate the air. This fan is controlled by a Raspberry Pi Zero, which has a temperature sensor inside the room, and another one outside the house.
2. Hardware
This is the hardware that I ordered:
- Raspberry Pi Zero WH
- Power supply
- Case
- 64GB SD card
- 2x DHT22 temperature and humidity sensors (one plain DHT22, and one SEN0137)
- 10k Ohm resistor
- 5V relay module
3. Initial setup
First off, the initial setup of the RPi Zero is almost the same as described in a previous post (Raspberry Pi TV (Part 1)). The differences are that the stripped-down OS is chosen instead (Raspberry Pi OS Lite), and that the hostname is rpi-coldroom
. Furthermore, since the RPi Zero does not have an ethernet port, we must manually enable a wireless connection, as described below.
3.1. Wireless connection
In the /boot
partition, add a file wpa_supplicant.conf
with the following information:
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev update_config=1 country=CA network={ ssid="MyNetwork" psk="the_password" key_mgmt=WPA-PSK }
Note: The RPi Zero W does not support 5GHz.
4. Circuit diagrams
4.1. Temperature sensors
The wiring diagram of the SEN0137 is straightforward: The red wire goes to 5V, the black wire goes to GROUND, and the green wire goes to DATA.
For the plain DHT22, holding the module in front of you, starting from the left:
- The first pin is the power supply pin.
- The second pin is the data pin.
- The third pin is unused.
- The fourth pin is the ground pin.
|- 5V -------------------------| |- GND ------------------------| SEN0137 |- GPIO 18 --------------------| | |- 3V3 -----------------| PIN1 | RPi Zero | | | | 10k Ohm | | | | DHT22 |- GPIO 19 -------------| PIN2 | | | PIN3 | |- GND -----------------| PIN4 |
4.2. Relay
|- 5V --------| VCV | | COM |--- Fan ---| Wall socket RPi Zero |- GND -------| GND | Relay | NO |-----------| |- GPIO 21 ---| DATA | | NC |
4.3. Button
RPi Zero |- 3V3 -|- 10k Ohm -|- Button -|- GPIO 23 -| RPi Zero
4.4. LED
RPi Zero |- GPIO 16 -|- LED -|- 1k Ohm -|- GND -| RPi Zero
5. Software
We need to install the DHT package for Python:
$ sudo apt-get install libgpiod2 python3-pip $ pip3 install adafruit-circuitpython-dht
6. Testing the sensors
Let's test our two sensors to make sure that they are reading the temperature and humidity correctly.
import adafruit_dht import board import time inside_sensor = adafruit_dht.DHT22(board.D19) outside_sensor = adafruit_dht.DHT22(board.D18) while True: time.sleep(5) try: inside_temperature = inside_sensor.temperature inside_humidity = inside_sensor.humidity outside_temperature = outside_sensor.temperature outside_humidity = outside_sensor.humidity print(f'Ti:{inside_temperature},To:{outside_temperature},Hi:{inside_humidity},Ho:{outside_humidity}') except RuntimeError as error: print(error.args[0]) continue
7. Testing the relay
Let's test the relay.
import RPi.GPIO as GPIO import time relay_gpio = 23 GPIO.setmode(GPIO.BCM) GPIO.setup(relay_gpio, GPIO.OUT) while True: print ('ON') GPIO.output (relay_gpio, GPIO.LOW) time.sleep(2) print ('OFF') GPIO.output (relay_gpio, GPIO.HIGH) time.sleep(2)
Normally, the device connected to the relay should turn on/off every 2 seconds.
8. Main script
Let's put all of this together, along with the temperature policies we want for the cold room. Our policies are divided in two parts: operational policies, which are the regular cold room policies, and special policies, which take precedence over the operational policies.
Special policies:
- To ensure that there is some air circulation, the fan must run at least 5 minutes every 24 hours. If the fan has been off for more than 24 hours, it is turned on for 5 minutes.
- We want to prevent the fan from being turned on too often. If the fan is turned on, it must remain turned on for at least 1 minute.
- We don't want the fan to run for extended periods of time. If the fan has been on for more than 60 minutes, turn it off.
- We want to give a cooldown period for the fan. The fan must have been off for at least 30 minutes prior to being turned on.
- To prevent potential problems, if there is no valid reading for some time, exit the script.
These special policies ensure that at the low end, the fan will run at least 5 minutes every day, and at the high end, it will run 60 minutes out of every 90 minute interval.
Operational policies:
- If the inside temperature is less than 10°C, don't do anything.
- If it is too humid outside, don't do anything.
- If the outside temperature is less than the inside temperature (minus a small error factor), turn on the fan.
The complete script is:
import adafruit_dht import board import datetime import os import RPi.GPIO as GPIO import time # In case the RPi Zero shuts down improperly. GPIO.cleanup() # Note: All times are in seconds. # Interval duration between probing the sensors. PROBE_INTERVAL = 30 # Interval duration during which the user is allowed to press the poweroff button (when the light # flashes). POWEROFF_INTERVAL = 5 # How long the poweroff button must be pressed before registering. POWEROFF_HOLD = 1 # Maximum number of consecutive loops where no sensor reading is tolerated. MAX_NO_READINGS = 30 # Minimum temperature, i.e., don't cool the cold room if the temperature is below this level. MIN_TEMP = 10 # Maximum humidity, i.e., don't transfer air if the outside humidity is above this level. MAX_HUM = 80 # Error factor for the sensors. EPSILON = 1 # Some checks. assert(PROBE_INTERVAL >= POWEROFF_INTERVAL) assert(POWEROFF_INTERVAL >= POWEROFF_HOLD) GPIO.setmode(GPIO.BCM) # Sensors. inside_sensor = adafruit_dht.DHT22(board.D19) outside_sensor = adafruit_dht.DHT22(board.D18) # Relay. relay_gpio = 21 GPIO.setup(relay_gpio, GPIO.OUT) # LED indicator. led_gpio = 16 GPIO.setup(led_gpio, GPIO.OUT) # Button. button_gpio = 23 GPIO.setup(button_gpio, GPIO.IN, GPIO.PUD_DOWN) def fan_off(): GPIO.output(relay_gpio, GPIO.LOW) log('fan off') def fan_on(): GPIO.output(relay_gpio, GPIO.HIGH) log('fan on') def fan_active() -> bool: return bool(GPIO.input(relay_gpio)) def led_off(): GPIO.output(led_gpio, GPIO.LOW) def led_on(): GPIO.output(led_gpio, GPIO.HIGH) def log(line: str): print(line) with open('/home/pi/cold_room_log.txt', 'a') as f: f.write(line+'\n') f.close() def cleanup(): fan_off() led_off() GPIO.cleanup() os.system('sudo shutdown now') exit() # The routine starts with the fan running for 10 seconds, then turning off. led_on() fan_off() fan_on() time.sleep(10) fan_off() latest = {'fan on': time.time(), 'fan off': time.time()} consecutive_no_reads = 0 while True: # When the light flashes, the user can press the button to exit. button_counter = 0 for i in range(POWEROFF_INTERVAL*5): # If the button is held long enough, the script will end. if button_counter >= POWEROFF_HOLD*5: cleanup() # The LED flashes during the time when the button can be pressed. if i%2 == 0: led_off() if GPIO.input(button_gpio) == GPIO.HIGH: button_counter += 1 else: button_counter = 0 time.sleep(0.2) led_on() # Sleep for the rest of the probe interval. time.sleep(PROBE_INTERVAL-POWEROFF_INTERVAL) # Try to probe the sensors. try: inside_temp = inside_sensor.temperature inside_hum = inside_sensor.humidity outside_temp = outside_sensor.temperature outside_hum = outside_sensor.humidity log(f'{datetime.datetime.now().strftime("%Y-%m-%d-%H:%M:%S")},Ti:{inside_temp},To:{outside_temp},Hi:{inside_hum},Ho:{outside_hum}') consecutive_no_reads = 0 except RuntimeError as error: log(f'{error.args[0]}') consecutive_no_reads += 1 # If there are too many consecutive no readings of sensors, we assume that there is a # problem, and the script ends. if consecutive_no_reads > MAX_NO_READINGS: cleanup() continue time_now = time.time() ##################### # Special policies. # ##################### # The fan must turn on for at least 5 minutes every 24 hours. if time_now - latest['fan off'] >= 86400 and not fan_active(): fan_on() latest['fan on'] = time.time() time.sleep(300) fan_off() latest['fan off'] = time.time() continue # If the fan is turned on, it must remain turned on for at least 1 minute. if time_now - latest['fan on'] <= 60 and fan_active(): continue # If the fan has been on for more than 60 minutes, turn it off. if time_now - latest['fan on'] >= 3600 and fan_active(): fan_off() latest['fan off'] = time.time() continue # The fan must have been off for at least 30 minutes prior to being turned on. if not fan_active() and latest['fan off'] <= 1800: continue ######################### # Operational policies. # ######################### # If the cold room is too cold, don't cool it more. if inside_temp < MIN_TEMP: if fan_active(): fan_off() latest['fan off'] = time.time() continue # Don't transfer air from the outside if it is too humid. if outside_hum > MAX_HUM: if fan_active(): fan_off() latest['fan off'] = time.time() continue # Cool the cold room. if inside_temp > outside_temp + EPSILON: if not fan_active(): fan_on() latest['fan on'] = time.time() else: if fan_active(): fan_off() latest['fan off'] = time.time() cleanup()
9. Autorun
To ensure that this script autoruns when the RPi Zero is booted, do the following. Create a new unit file /lib/systemd/system/coldroom.service
with the following:
[Unit] Description=Cold room After=multi-user.target [Service] Type=idle User=pi Restart=no ExecStart=/usr/bin/python3 /home/pi/coldroom.py [Install] WantedBy=multi-user.target
Now enable the this new service:
$ sudo chmod 644 /lib/systemd/system/coldroom.service $ chmod +x /home/pi/coldroom.py $ sudo systemctl daemon-reload $ sudo systemctl enable coldroom.service