By: Lasse Jespersen
The Timelord is a time control device that you can customize to automate practically anything. Make it. Set it. Forget it.
In this tutorial, we will learn how to:
- Use a DHT22 sensor to sense temperature and humidity
- Use a RTC module to set and keep time
- Use a PIR sensor to detect movement
- Use a 16×2 LCD to print useful data
- Use a piezoelectric buzzer for auditory feedback
- Drive a relay with a single NPN transistor
- Perform soft reset of our Arduino Nano
- What can the Timelord do?
- Sense temperature and humidity, and act on either or both
- Know the time, and act on it
- Control relay ( 30VDC/10A or 250VAC/10A )
- Output time over serial to other devices
- Sense motion
- Display data of interest
- Build the ultimate time control device for automation! (insert evil villain laugh)
Yet Another Clock?
Timekeeping devices are not exactly scarce. To know the time, you need only look at your phone, laptop, or media center. For children of the new millennium, wristwatches are only worn as ornaments. Stopwatch, alarm and calendar functionality have all been displaced into smart devices. And timekeeping has become much more precise after this – while many clock radios have DCF77 modules to pull the time from a radio signal, and even more DAB+ radios just pull it from the superabundant DAB+ signals, smart devices which are online can just query a timeserver, get a reply, and be set until it’s time to ask again. Hallelujah!
Regardless of this modernity, in many scenarios, you need a low-energy, compact controller for keeping time, reading temperature and humidity, and Doing_Thingstm when pre-defined conditions are met. The Timelord device we show you in this article is a template system for this. It can readily be reprogrammed to switch on a relay at specific times, temperatures, humidities, while also providing time data over a serial interface, without losing any of its functionality as a regular desktop clock. It even has a PIR motion sensor, so its LCD only lights up when there is movement nearby. Every bit as monstrously entertaining as Dr. Who, but much more useful. To set time, just uncomment a single line, compile and upload the firmware – then comment the line out, compile and upload again.
For my personal use, this is a desktop clock – but also a controller for an air conditioner. I also have one of those old Raspberry Pis on a shelf near my desk – every time I reboot it, it will forget what time it is, and since its wireless connection is terribly unreliable, it can go for days thinking it’s travelled many years into the past. I have to log in to the damned thing, unplug the wireless adapter, check connectivity, ugh… I suspect it’s related to some power saving done on the wireless link by default, but I will never bother to fix it.
Now that Raspberry Pi can just pull its time data over its serial port, and do whatever it is it is supposed to do. You may not need this feature at all – but if you do, the Timelord can deliver. You can use PuTTY or Tera Term to view serial output — here’s a sample of the data sent out over serial, viewed with putty:
— snip —
201704241759.13 25.4C 33.6H
201704241759.16 25.4C 33.6H
201704241759.18 25.4C 33.7H
201704241759.20 25.4C 33.7H
201704241759.22 25.4C 33.7H
201704241759.25 25.4C 33.7H
— snip —
Using the companion script, queryTimelord.py, the output becomes much more palatable. This script can be easily modified to do other things – in its present form it merely prints some parsed data to your screen.
— snip —
$ py queryTimelord.py
Connected to /dev/ttyAMA0 at 9600 baud. Hit CTRL+C to exit at any time …
[*] Sent ‘PING!’, waiting for reply…
Environment: 28.8C 24.5H
[*] Sent ‘PING!’, waiting for reply…
Environment: 29.0C 24.1H
[*] Sent ‘PING!’, waiting for reply…
Environment: 29.1C 23.8H
— snip —
Refer to this queryTimelord.py script for an example of how to pull time and environment data from the Timelord – I run a slightly different version of this on my old Raspberry Pi every time it boots up – if setting up its serial port in /boot/config.txt is cumbersome, or you need that port for something else, you can just use a USB to TTL-RS232 cable with 5V logic from the Pi to the Timelord.
Now on to more interesting applications.
Consider this simple list of conditions, which can trigger the Timelord to do something truly practical – remember that it can be programmed ad-hoc to meet the needs of any particular situation. The firmware is 14.9KB now, with about 1KB of RAM free, so there’s room for MUCH more.
// If on vacation, turn on some lights automatically
if( hour >= 18 && ( hour < 23 && minute < 30 ) ) driveLights( true ) ;
else driveLights( false ) ; // or turn them off
— OR —
// If home, and temperature exceeds 25C, drive AC
if ( temperature >= 25 && hour > 16 ) driveAirconditioner( true ) ;
else driveAirconditioner( false ) ;
— OR —
// Drive a dehumidifier, often necessary in basements
if( humidity > 35 ) driveDehumidifier( true ) ;
else if( humidity < 30 ) driveDehumidifier( false ) ;
Similarly, we can drive Morai’s micro linear actuators to open windows or valves to allow airflow when needed. The Timelord lends itself naturally to having a carbon dioxide sensor integrated into the build. The effects of even medium CO2 levels indoors are devastating, affecting general well-being, concentration levels, and much more. This is particularly important in schools where students should not lack oxygen. This can be accomplished elegantly by simply turning on the actuators to open windows when CO2 levels are above a certain threshold, and closing them when the indoor air has become sufficiently saturated with oxygen – this is a doable addition to the Timelord in its current form, since there is a 5V powered relay on the board… see the relayControl function in the Arduino sketch.
Less interesting is the piezoelectric buzzer – but it just has to be there. It provides an auditory feedback, and can either wake you up (use a 100 ohms resistor if you want that kind of pain), or alert you if there is some reason to pay attention. A 150 ohms resistor in series with the buzzer provides a fairly comfortable bleep amplitude. I do not currently use it, but you can call the bleep function if you need to. I get enough problems from my better half, just because the harmless clock lights up whenever she moves near it. She calls it torture… Besides, I added no buttons to disable a wakeup alarm, so the thing would just have to bleep for a fixed period. I do not know what she would do to the clock if she couldn’t turn off the bleeping in the morning… I just know she wouldn’t unplug it like a rational person. She is the blunt-object-pummel-until-it-stops-bleeping type :/
Onward and upward – to build this device, you will need the following:
- 1 x DHT22 sensor
- 1 x 16×2 LCD with FC-113 ( or similar ) module for I2C
- 1 x DS3231 RTC ( remember to disconnect the diode to prevent charging of battery )
- 1 x 3V3 CR2032 coin-cell lithium battery.
- 3 x 4K7 ohms resistor
- 1 x 220 ohms resistor
- 1 x 2N7000 (S-G-D) N-channel MOSFET ( or similar )
- 1 x Arduino Nano
- 1 x HC-SR501 PIR sensor
- 1 x BC239 (C-B-E) NPN transistor ( or similar )
- 1 x 5V relay
- 1 x diode
- 1 x piezoelectric buzzer
- 1 x 150 ohms resistor for buzzer ( 100 ohms if you like it loud )
And the following libraries installed into Arduino IDE:
- RTClib (by Adafruit)
- NewliquidCrystal (replace the old LiquidCrystal library in the Libraries folder with this one )
You should grab them now, install them into your Libraries folder and restart your Arduino IDE.
Descriptions of Components
It is worth knowing a more about the ICs that we will be using – a DHT22 sensor, a DS3231 RTC, a 16×2 character LCD, and a HC-SR501 PIR sensor.
The DHT22 is a temperature- and humidity sensor. It is fairly precise, with a +/- 2 degrees celsius margin of error on temperature readings, and +/-5 percent margin on humidity readings. It should be polled at intervals of 2 seconds or more, and in my test feeding it 5V caused it to heat up slightly, so I used 3.3 volts instead. I have correlated its temperature readings with both a multimeter that can read temperatures, and a laser thermometer – they match up pretty well – at most a +/- 1 degree celsius divergence. It’s a nice little thermometer/hygrometer for the price, and perfect for this application.
The DS3231 RTC is an awesome little IC. It drifts nowhere near as much as the DS1307 RTC, and the 3V3 coin cell can keep time nicely for years without the need for correction. You can easily pull year, month, day, hour, minute, second and temperature from this device. Feed the DS3231 3.3 volts from the 3V3 pin on your Arduino and it will keep ticking for decades. Many of these modules will try to charge the coin-cell battery, which is not ideal, and completely unnecessary. If the module is wired to charge, you have to unsolder or cut the tiny SMD diode which at its cathode is directly connected to the positive pole of the battery. Use the continuity test on your multimeter if you can’t spot it. If you let it charge, the battery will at some point die, and may damage the DS3231. It’s not going to go ‘bang!’ or start a fire though – usually it just bulges a few millimeters. But better safe than sorry.
The 16×2 LCD you use should have a smaller module soldered on – in my case, this was a FC-113 IC, which lets me control it with … I2C. Only using two wires + the power wires is immensely helpful. The LCD doesn’t use much power; only about 25mA when lit up (and blank). You’ll see how to write to it later on. It’s worth your while to replace the stock LiquidCrystal library in your Libraries folder with fmalpartida’s NewliquidCrystal library (linked above). Just back up the old folder, rename the extracted folder to LiquidCrystal, and restart the Arduino IDE. When you construct the text strings that you wish to display on the LCD, sprintf() is my preferred method. The 32 positions on the LCD are addressed individually, so use something like this to simplify building more complex text strings:
L0: hh:mm dd.dC
L1: MM-DD dd.dH
Coordinates range from 0,0 to 0,15 for line 1, and 1,0 to 1,15 for line 2.
By using lcd.setCursor( 0, 0 ), we move to the beginning of the first line, and print our (max) 16 byte string with lcd.print( line1 ). With lcd.setCursor( 0, 1 ) we move to the beginning of the second line, and print another (max) 16 byte string with lcd.print( line2 ). Other useful methods for our ‘lcd’ object are write, clear, backlight, noBacklight, and home. There’s a bit of sprintfery stuffed away in the formatStrings function, but you don’t need that. Use the String type for building strings if sprintf() annoys you. It will probably cost you a few bytes more RAM, but you can afford it.
The PIR (passive infrared) sensor is more interesting than the other ICs. It has a small hemisphere with hexagonal prisms (the sensor ‘face’), two potentiometers for setting trigger time and sensitivity, and a jumper for auto-reset and presumably not-auto-reset. The one I used was just called HC-SR501, and both the potentiometers were labeled ‘1’. After a bit of fiddling, I had both the right sensitivity and trigger time. I feed it 5V, and its signal pin (labeled “S+”) will fire a 3V3 signal when it detects motion. It does so unerringly, and one can find many applications for such a sensor, even in a not-so-smart home.
We use an external interrupt here (INT0 on pin 2) to register the fact that the PIR has detected motion, and react in the loop function if appropriate. You can read more about interrupts elsewhere, but suffice to say that the atmega328p/168 Arduinos (Uno, Nano, Pro Mini 3V3/5V, etc.) have two external interrupts – INT0 on pin 2 and INT1 on pin 3. These let us interrupt the Arduino (however briefly), usually just to have it register that “something” has happened by setting a variable to a specific value that can be checked later, and acted upon. That’s basically what we do with the PIR. If the PIR has seen something move, the interrupt calls the reactRisingPIR function to set a variable, which is used in the loop function to enable the LCD backlight. It’s meant to an annoying always-on backlight as much as just to prolong the life of the LCD backlight itself.
16×2 LCD w. FC-113 I2C module:
- Wire A4 to SDA
- Wire A5 to SCL
- Wire 5V to VCCTimelord.ino
- Wire GND to GND
DHT22 environment sensor:
- Wire pin 1 to 3V3 ( 5V causes the sensor to heat up and yield inaccurate temperatures )
- Wire pin 2 to arduino D6
- Wire a 10K pull-up from pin 2 to pin 1
- Pin 3 is not connected (NC)
- Wire pin 4 to GND
- Wire SCL to A5
- Wire SDA to A4
- Wire VCC to 3V3
- Wire GND to GND
- Ignore 32K pin
- Ignore SQW pin
2N7000 (SGD) for softReset function:
- Wire DRAIN to RST
- Wire SOURCE to GND
- Wire GATE to D4
- Wire GATE across a 4K7 resistor to GND
HC-SR501 PIR sensor:
- Wire VCC to 5V
- Wire S+ (3V3 when tripped) to D2 (INT0)
- Wire GND to GND
- Wire BC239 NPN driver transistor (CBE) BASE to D5 across a 220 ohm resistor
- Wire 5V to relay positive terminal
- Wire relay negative terminal to BC239 COLLECTOR pin
- Wire BC239 EMITTER pin to GND
- Wire diode with anode on relay negative terminal, cathode on positive terminal
- Wire a 150 ohm resistor in series with either negative or positive terminal
- Wire one wire to D9
- Wire the other to D10
This project does not require you to use every module, or use every feature. Play around with it, and add the stuff you need. Then change the code to suit your needs.
The most important thing is to gain a practical understanding of how to bolt a device together. On my own Timelord sDevice (‘s’ is for serial!), I have not even enclosed it in a plastic box. And I use it every time I look over at my desk, more than any other desktop clock I’ve owned.
If I had had the room, I would have added a few buttons and some code to run a timer (countdown), an alarm clock… and lasers. Frickin’ lasers!
Summer has arrived – stay tuned for a MLA driven solar tracker that is both portable and awesome in other ways. Going camping, like Bear Grylls? You will benefit greatly from its 12V Pb charging capability, which lets you charge every electronic device you own… With this, you can charge your phone and order Chinese food, instead of eating earthworms!