My Brief
Build a smoke alarm that sends an email and text message when it goes off.
The Device
After a fe
w days of investigation into possible solutions, I opted to build one using a Raspberry PI and a cheap smoke alarm, linked by radio signal.
When the Smoke Alarm is triggered, it will send a signal using the radio transmitter to its receiver, which is connected to the Raspberry PI. When the Raspberry detects a constant signal for more 10 seconds, it then sends me an email and text message.
The end result
This video demonstrates it working. The first blip is a false alarm. The second lasts for more than 10 seconds and sends the messages.
Equipment Used
The Raspberry PI
I decided to use the Raspberry PI micro-computer because it’s nice and small (credit card sized), doesn’t require a lot of power and is really simple to program. It also contains general-purpose input/output (GPIO) pins that can be configured to receive signals from external devices (the radio receiver).
The Smoke Alarm
I tried a few smoke alarms before settling on the Aico Ei100L. Ideally I wanted the radio transmitter to be powered using the alarm’s battery. The transmitter sleeps at a low voltage; but once up and running none of the other alarms had enough power for it, and the alarm to work properly. The Aico was perfect, as uses very basic electronics and two 9v batteries.
Radio Gear
I was trying to build the system on a budget, so I ended up buying a cheap AM Radio Transmitter/Receiver from Maplin. I later found out I might have been wiser buy a slightly better system, as I found myself constantly trying to resolve interference issues. It was sensitive to other electronics, which apparently this is quite common with these cheap components.
I had originally planned on powering the receiver using its power output, but it ended up with its power supply, as even the Raspberry caused interference issues.
99% of the interference issues were resolved once it was placed in its case and the software fixed the rest.
Items used:
- 1 x Smoke Alarm Aico Ei100L
- 1 x Raspberry PI
- 2 x Cases
- 2 x Power Supplies
- 1 x Radio transmitter and receiver
- Cables and other electronics
Electronics
Transmitter Circuit
Software
I programmed the software using Python 3. I’m a C# developer by trade, and found Python simple to learn. Although, if you are prototyping on Windows machine, watch out for case sensitive code.
Receiving the Signal
To check for an input from the radio receiver I used the Python’s RPi.GPIO module.
As mentioned above, I didn’t want the system to send messages until it had received a signal for at least 10 seconds. This would allow me to test the battery each month and stop if from sending false alarms when I burn my toast! To do this I added a timing loop and signal counter. If the signal received 20 times in a 10 second period, the messages are sent. The count of 20 stops the system from sending messages when there’s receiver interference.
To ensure the system doesn’t need resetting once it’s triggered, I’ve encompassed the logic in an infinite loop.
def listen(self): print("Listening for Signal") while True: fire = False while fire == False: time.sleep(0.5) number = self.checkForSignal() if (number == 1): start = time.time() counter = 0; while True: number = self.checkForSignal() counter += number elapsed = time.time()-start time.sleep(0.1) if (elapsed < 10): continue if (counter > 20): fire = True else: print('False alarm') break else: counter = 0 self.sendEmail() self.sendSms()
Sending the SMS Text message
The Raspberry can be connected to a GPRS modem, but they’re expensive. After reading Matt Hawkins’ post, I ended up using Text Local’s SMS web service instead. Simple to use, cheap and there’s no monthly renewal fee!
def sendSms(self): values = {'test' : 0, 'uname' : self.smsUsername, 'hash' : self.smsHash, 'message' : self.smsMessage, 'from' : self.smsSender, 'selectednums' : (self.smsNumbers) } url = 'http://www.txtlocal.com/sendsmspost.php' postdata = urllib.urlencode(values) binary_data = postdata.encode('ascii') req = urllib2.Request(url, binary_data) print('Attempt to send SMS ...') try: response = urllib2.urlopen(req) response_url = response.geturl() if response_url == url: print('SMS sent!') except urllib.error.URLError as e: print('Send failed!') print(e.reason)
Sending the email
For this I connected my Gmail account’s SMTP server.
def sendEmail(self): print('Sending Email') subject = "Smoke Detected Possible Fire" text = self.smsMessage message = """\From: %s\nTo: %s\nSubject: %s\n\n%s """ % (self.emailFrom, ", ".join(self.emailTo), subject, text) try: server = smtplib.SMTP(self.smtpServerHost, self.smtpServerPort) if (self.smtpTls == True): server.ehlo() server.starttls() server.login(self.smtpUsername, self.smtpPassword) server.sendmail(self.emailFrom, [self.emailTo], message) server.close() print('Successfully sent the mail') except smtplib.SMTPException as e: print(e) print("Failed to send mail")
Full Code
import time import random import smtplib import RPi.GPIO as GPIO import urllib import urllib2 import os import ConfigParser class SmokeDetector: def __init__(self): GPIO.setmode(GPIO.BOARD) GPIO.setup(11, GPIO.IN) config = ConfigParser.ConfigParser() config.read("config.cfg") self.smsMessage = config.get('alerter', 'smsMessage') self.smsUsername = config.get('alerter', 'smsUsername') self.smsSender = config.get('alerter', 'smsSender') self.smsHash = config.get('alerter', 'smsHash') self.smsNumbers = config.get('alerter', 'smsNumbers') self.smtpUsername = config.get('alerter', 'smtpUsername') self.smtpPassword = config.get('alerter', 'smtpPassword') self.smtpServerHost = config.get('alerter', 'smtpServerHost') self.smtpServerPort = config.get('alerter', 'smtpServerPort') self.smtpTls = config.getboolean('alerter', 'tls') self.emailFrom = config.get('alerter', 'from') self.emailTo = config.get('alerter', 'to') def listen(self): print("Listening for Signal") while True: fire = False while fire == False: time.sleep(0.05) number = self.checkForSignal() if (number == 1): start = time.time() counter = 0; while True: number = self.checkForSignal() counter += number elapsed = time.time()-start time.sleep(0.1) if (elapsed < 10): continue if (counter > 20): fire = True else: print('False alarm') break else: counter = 0 self.sendEmail() self.sendSms() def sendEmail(self): print('Sending Email') subject = "Smoke Detected Possible Fire" text = self.smsMessage message = """\From: %s\nTo: %s\nSubject: %s\n\n%s """ % (self.emailFrom, ", ".join(self.emailTo), subject, text) try: server = smtplib.SMTP(self.smtpServerHost, self.smtpServerPort) if (self.smtpTls == True): server.ehlo() server.starttls() server.login(self.smtpUsername, self.smtpPassword) server.sendmail(self.emailFrom, [self.emailTo], message) server.close() print('Successfully sent the mail') except smtplib.SMTPException as e: print(e) print("Failed to send mail") def sendSms(self): values = {'test' : 0, 'uname' : self.smsUsername, 'hash' : self.smsHash, 'message' : self.smsMessage, 'from' : self.smsSender, 'selectednums' : (self.smsNumbers) } url = 'http://www.txtlocal.com/sendsmspost.php' postdata = urllib.urlencode(values) binary_data = postdata.encode('ascii') req = urllib2.Request(url, binary_data) print('Attempt to send SMS ...') try: response = urllib2.urlopen(req) response_url = response.geturl() if response_url == url: print('SMS sent!') except urllib.error.URLError as e: print('Send failed!') print(e.reason) def checkForSignal(self): return GPIO.input(11); sa = SmokeDetector() sa.listen()
Starting system on boot
To get the code running on boot I created a Cron job to run my script file.
Testing the device
I’ve tested the device over two floors, with a distance of 12 metres between the alarm and Raspberry. It would go further, if the transmitter and receiver aerials were extended.
The device has been running now for 10 days without any false alarms.
For more detail: Raspberry PI connected Wireless Smoke Alarm