Sales and Advice: 0800 955 3600

Raspberry PI connected Wireless Smoke Alarm

Posted on 27 of September 2013

How it works

My Brief

Build a smoke alarm that sends an email and text message when it goes off.

The Device

After a few 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

Raspberry PI connected Wireless Smoke Detector

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

Receiver 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.

Pitfalls

This certainly something you wouldn't want to put into production as there are a few pitfalls. The system won't work if:

  • Wi-fi router causes the fire
  • The Raspberry PI causes the fire
  • The Raspberry PI crashes
  • It's an electrical fire and the fuse box trips
  • The broadband connection fails
  • The email and SMS service fails
  • The Battery runs low or dies

What Next?

I've had good fun learning what the Raspberry PI can and can't do, but it's a bit over-kill for this type of application. I am currently building version 2, which connects smoke alarm directly to my home Wi-fi and communicates via web service.

If have any questions or suggestions please feel free to contact me on twitter or .

Share this page...