Well, I have not did very good about keeping a project log. Here is the (hopefully) final project.
Figure 1: The internet enabled smoke alarm on the benchtop
Figure 2: The internet enabled smoke alarm installed on the ceiling.
Figure 3: Inside of the Hammond enclosure
Oh, this is the misbehaving smoke alarms I mentioned in the project description.
When reverse engineering part of the commercial FireX smoke alarm schematic, I took digital images of the top and bottom side of the PCB. I then over layed the images in http://paint.net. Then, the opacity was adjusted to see both the traces on the bottom and the components on the top side. (This technique has been written about on various sites)
There are three wires going to each smoke alarm:
The 120VAC and common are incredibly fortunate. This makes it easy to power the ESP8266 module with a 120VAC to 3.3VDC wall pack adapter. The network line connects all 6 smoke alarms. Normally, the network line is at the same level as common. But, when the smoke alarms are sounding an alert, the network line rises about 9 volts above common. This causes all 6 smoke alarms to sound an alert. This network wire is connected to the ESP8266, through an optocoupler. The optocoupler has several advantages:
1) The optocoupler is high impedance, so it will not interfere with the smoke alarm operation.
2) The optocoupler electrically isolates the smoke alarm from the ESP8266.
3) The optocoupler is one directional. So, if some evil-hacker guy gets into the ESP8266, he could not set off the fire alarm. (I'm not 100% sure if the 3.3V logic line from the ESP8266 would set off the smoke alarm)
Now that it is known how the network line operates, here is a schematic of the smoke alarm modification.
Figure 4: Schematic to add internet connectivity to a smoke alarm.
There really isn't too much to the hardware. When the network line goes to 9V above common, the optocoupler turns on. This changes the state of the I/O line going to the ESP8266. There is a two wire cable going to the white LED. The LED blinks when there the wireless router is not found. The 3.3V wall pack powers the ESP8266. The schematic has a few details noted on it.
The software I used for the ESP8266 module is written in Lua. The NodeMCU firmware was loaded in the module. The module is running version 20150318 of NodeMCU.
Here is the code:
-- Matt Meerian -- Date: 5-16-2015 -- Origin: This started as an example from the internet (there are several modified snippets) -- Target: ESP8266 module -- Connection to target: USB to 9600 baud, 3.3V -- Downloader: ESP8266 LuaLoader 0.83 or later -- Firmware running on the ESP8266: NodeMCU 0.9.5 build 20150318 powered by Lua 5.1.4 -- Description: (NOTE: This firmware should be the only file on the ESP8266 module when run) -- This script will send an email to my yahoo account when the networked smoke alarms sound an alert. -- There are 6 networked smoke alarms in the house: Kiddie i4618. I have reverse engineered -- part of the schematic. There are three wires going to the smoke alarm: 120VAC and a -- "network" wire. The network wire is normally at 0V. When the alarm is going off, the network -- line goes up to 9V. This then sets off the other smoke alarms on the network. I have -- used an optocoupler to tap off of this network line and trigger the input of the ESP8266 module. -- You need to connect to a wireless router (AP) before you run the "dofile": -- wifi.setmode(wifi.STATION) -- wifi.sta.config("NSA_mobile_surveillance","frn...") -- You can then get your wifi by: = wifi.sta.getip() -- NOTE: The email alert is sent by pushingbox. -- When running a new lua script in LuaLoader: -- Press the "Upload File" button. (wait) -- Press the "Restart" button. -- Press the "dofile" button to run the script. -- How to flash lua firmware into the ESP8266: -- Use esp8266_flasher.exe Follow the instructions there led_output_pin=4 --This is the pin for the "wifi not connected" LED blink opto_input_pin = 3 --The optocoupler input pin debug_cntTick = 0 timer_between_emails=0 timer_minute=0 --This is used to count up to a minute wifi_gone_alert=0 email_sent_timeout=0 const_min_time=30 --this is a "constant", if timer is at 2 seconds, then a value of 30 would be 1 min const_email_series_delay=200 --if a series of emails has started, do not send anymore for an hour (if the timer is 2 seconds, then the value should be 1800) host="http://api.pushingbox.com" --This is the address to send an alert email ipnr=0 --holds the IP address CntTimer=1 --allows the code in the "send_pushingbox_email" routine to be run twice gpio.mode(opto_input_pin,gpio.INPUT,gpio.PULLUP) --make the optocoupler pin an input gpio.mode(led_output_pin,gpio.OUTPUT) --make the "no wifi" LED alert pin an output. gpio.write(led_output_pin,gpio.LOW) --This timer happens every four seconds tmr.alarm(0, 4000, 1, function() --print("testing...") check_smoke_alarm() -- debug_cntTick=debug_cntTick+1 --counts the number of times through the timer print(debug_cntTick) if debug_cntTick > 50000 then --reset the counter if the number is getting large debug_cntTick=1 --reset the value end check_wifi_status() --blink an LED if there is no wifi signal timer_minute=timer_minute+1 if timer_minute>const_min_time then --This timer happens every minute. timer_minute=0 --reset the minute counter end end ) --This function looks to see if there is a wifi signal. If not, it blinks a red LED. function check_wifi_status() if wifi.sta.getip()==nil then --are we attached to a wireless router? print("no IP address") --no wifi connection, start blinking an LED if wifi_gone_alert==0 then --Since we do not have an internet connection, start blinking a red LED wifi_gone_alert=1 --next time through, turn off the red LED gpio.write(led_output_pin,gpio.HIGH) --turn on the red LED, we will be blink'n else wifi_gone_alert=0 --next time through, turn on the red LED gpio.write(led_output_pin,gpio.LOW) --turn off the red alert LED, we will be blink'n end else --we do not need to blink the alert LED, so turn if off --print("we have an ip address") wifi_gone_alert=0 --we do not need any more red LED blinking gpio.write(led_output_pin,gpio.LOW) --make sure the red LED is not on end end --This function looks at an input pin (through an opto coupler), to see if the smoke alarm is going off -- If the alarm is going off, it sends an email function check_smoke_alarm() if gpio.read(opto_input_pin)==1 then --The smoke alarm is not going off --print("-no alarm-") --this is run when the smoke alarm is off if email_sent_timeout==0 then --only allow an email to be sent if an email has not been sent recently timer_between_emails=0 --The alarm is not sounding, so reset the email counter end else --The smoke alarm is going off print("smoke alarm sounding!") --this is run when the optocoupler sees 9V (alarm of on the smoke alarm is active) if timer_between_emails==0 then print("First email sent") send_pushingbox_email() email_sent_timeout=const_email_series_delay --if we have sent an email recently, don't allow another series of emails to be sent out right away end if timer_between_emails==20 then --is it time to send the second email? print("second email sent") send_pushingbox_email() end if timer_between_emails==40 then --is it time to send the third email? The house has probably burned down by now. fuck. print("third email sent") send_pushingbox_email() end if timer_between_emails<100 then --the smoke alarms must have been on for a long time! timer_between_emails=timer_between_emails+1 --counting up to the next email that can be sent out end end if email_sent_timeout>0 then --if we have sent an email recently, don't allow another series of emails to be sent out right away email_sent_timeout=email_sent_timeout-1 --counting down to allow another series of emails to be sent out end end --This routine uses a "get" command to have the Pushingbox web site send an email. --This code snippet was copied from the internet function send_pushingbox_email() CntTimer=1 --we have to go through this routine twice in order for the email to be sent tmr.alarm(1, 4000,1, function() print("CntTimer: ") --debug print(CntTimer) --debug CntTimer=CntTimer+1 if CntTimer>2 then --allows the timer to only run through twice tmr.stop(1) --stop the timer after two times through end sk=net.createConnection(net.TCP, 0) print("A") --debug sk:dns(host,function(conn,ip) ipnr=ip print("B") --debug end) conn=net.createConnection(net.TCP, 0) print("C") --debug conn:on("receive", function(conn, payload) print(payload) end ) print("D") --debug conn:connect(80,ipnr) print("E") --debug conn:send("GET /pushingbox?devid=vD60F1 HTTP/1.1\r\nHost: "..host.."\r\nConnection: keep-alive\r\nAccept: */*\r\n\r\n") --use your own V print("F") --debug end) end
This is my first Lua program. I had to borrow heavily from forum examples in http://esp8266.com. There are a bunch of really talented people there.
There are a couple of things I would like to do in the future:
1) Recess all of the electronics into the ceiling. The Hammond enclosure hanging down from the ceiling is somewhat of an eye sore.
2) Create a web server on the ESP8266. It would be nice to hold debugging information in a web page. Then, when an email is not sent, I can look back at the history. It would also be nice to change the wifi access point and password remotely.
3) Make a custom circuit board for the ESP8266. I used perf-board with point to point wiring in the prototype. It would be more tidy to do a PCB layout and send out the gerbers for a professional board.
There is lots of room for improvement. Anyway, let me know if you have questions and I will try to get to them.