Project tutorial
Smart Smokes

Smart Smokes © GPL3+

Alexa-enabled smoke, flame alarm with emergency lighting.

  • 1,314 views
  • 0 comments
  • 3 respects

Components and supplies

Necessary tools and machines

09507 01
Soldering iron (generic)
3drag
3D Printer (generic)
Electric Drill

Apps and online services

About this project

INTRODUCTION

This project shows how to build an Alexa-enabled smart smoke alarm that can detect flame, dangerous gases (including smoke) and temperature. It also has a led light ring that is used both as an alarm indicator, emergency/convenience lighting, and status indicator. A user can ask Alexa to give the location and type of alarm ("Alexa, ask smart smokes which alarm is going off?"), silence a specific alarm ("Alexa, ask Smart Smokes to tell Alarm one that I'm just cooking!"), check the temperature at a particular alarm ("Alexa, ask smart smokes what's the temperature at alarm 1?", and turn on and off the emergency/convenience lighting ("Alexa, tell smart smokes to turn on the emergency lights) .

Full Demonstration Video

Once you get through this project you'll see that adding further features, sensors and tricks to the Smart Smokes platform is fairly simple and limited only by your imagination. You'll also find that the final product is significantly cheaper and more flexible than a store bought smart smoke alarm.

OVERVIEW

The Smart Smokes system is made up of a base station and up to six smoke alarms (hereinafter referred to as "smokes" or "nodes"). The base station is built around an Arduino Uno receiver, which can communicate simultaneously with up to six smokes using the venerable nRF2401 transceiver.

Integrating Alexa starts with creating an Alexa Skill on the Amazon Developer Website. The Alexa Sill then communicates with a Raspberry Pi 3 equipped with a little Python program that uses package called Flask-Ask. Flask-Ask is Python framework created by John Wheeler that you can install on Python "that makes building Alexa skills for the Amazon Echo easier and much more fun." Thanks John Wheeler! The Pi3 then talks with the Arduino base station through the serial port. Easy Peasy.

We'll start with building a smoke and a base station. Once we get them talking, we'll install the smart smokes Python program on a Raspberry Pi 3 and plug it into the base station. Lastly, we'll head over to Amazon and create custom Alexa Skill and test some voice commands. Let's start with the smoke.

STEP 1: WIRE UP THE SMOKE

First get your 3d printer going on the Smoke Top and then the Smoke Body and Smoke Base of the smart smoke in that order because you'll need the Smoke Top first Now, fire up your soldering iron and mount your Arduino Nano into two parallel, fifteen-pin, female headers on a 40x60 prototype board. Then mount a twenty-pin male header on one side of the Nano and a two by four (2x4) female header on the other side. Here's what mine looked like:

Now we start connecting all the jumper wires. For your reference, here is the big picture idea on a breadboard schematic:

Let's start wiring in the jumpers for the nRF24L01. The two by four (2x4) header will be used to connect the nRF24L01 to the Nano. I wired it using jumper wires from the two by four (2x4) header to the Nano on the the bottom of the prototype board. Here's the pinout for soldering your jumper wires:

Next, we'll prepare the twenty-pin male header for all the sensors by connecting jumpers from the header to the Nano on the bottom of the prototype board. Here's the pinout for soldering your jumper wires from the twenty-pin header to the Nano:

Finally, wire your power source to the Nano Vin and Gnd. (Note: I did use a 9V battery, but this project chows them pretty quickly. I would recommend a 5v regulated supply instead.)

Here's how it all looks on the bottom of the prototyping board when I was done:

The closest header to you in the picture is the two by four (2x4) female header, then the Nano pins, then the twenty-pin header is farthest away. I know . . . its impossible to tell what is really going on, but I want you to see what I meant by using jumper wires on the bottom of the prototype board. Look at the other schematics to direct you as to what goes to where.

Now, let's use longer hookup wires to start connecting our components to the headers. Again, let's start with the nRF24L01 transceiver. You'll notice in the schematic (Figure 2) that I wired in a 3.3v power supply to the nRF24L01. Why not just hook this up to the 3.3v pin on the Nano? The reason is that the 3.3v pin on a Nano does not have sufficient current to power the nRF24L01 at anything over the minimum power setting. A convenient solution to this is to buy the nRF24L01 with some breakout boards that have a built in regulated 3.3v power supply with sufficient current. Then you just connect the power on the breakout board to the 5v pin on the Nano, which has plenty of current to handle various power settings on the nRF24L01. Here's a picture of the combination I used, with the breakout board on the top and the nRF24L01 on the bottom:

And here is the nRF24L01 plugged into the breakout board:

After you plug the nRF24L01 into the breakout board, just use some male to female hookup wire to hook it into the header. Then secure the header connections and protect the nRF24L01 with some shrink tubing as shown here:

And here is the nRF24L01 attached to the two by four (2x4) header:

Next, connect each of your components to the twenty-pin header as shown in Figure 2 and Table 2. When your done it should look something like this:

My cheap way of doing a header is to protect the connections with shrink wrap. You can see the 5v header is on the right, which I created by laying a solid piece of 22 gauge wire across 5 pins, soldering them together and then connected the pins to the 5v on the Nano with a jumper wire. I used the same process for the GND header on the left. The four open pins are not connected to anything, which corresponds to Table 2 above. Ok, tough part is done. Now let's go check on the 3d print.

STEP 2: MOUNT THE SENSORS

When your 3d print of the Smoke Top is done you should have a print that looks something like this:

To prep the top for mounting, you'll want "tap" the existing9 holes in the mounting pillars for the gas sensor and the active buzzer shown in Figure above. Really, it is not necessary to use an actual tap because the 3d printed part is forgiving. I just used 5/64 inch drill bit (or roughly 2mm) and found that short M2.5 screws held pretty well.

Now you can start mounting your sensors on the Smoke Top. Mount the Neopixel 12 first by gently pressing it in the ring. It should clip in without too much pressure as shown here:

You'll notice I put a couple blobs of hot glue around the edges just to be sure the led ring stays put.

Mount the flame sensor on the vertical plate so that the actual IR Sensor protrudes through the provided hole in the Smoke Top. Then put a M3 screw through the holes in the flame sensor and the vertical plate. Put a nut a nut on the M3 screw to hold it in place. Like this:

Next you can mount the active buzzer with 2 M2.5 screws in the pillars so it is centered over the hole in the Smoke Top mount like this:

Mount the gas sensor over the grate with at least two M2.5 screws in the pillars, like so:

You can also see on the left of Figure 11 above that the temperature sensor (TMP36) should fit nicely through the 5mm hole in the Smoke Top. If you need to, you can bore it out with a 5mm drill bit. Then put the thermistor through the hole and put a little dab of hot glue over it to hold it in place. After everything is in place, flip the Smoke Top over and make sure everything is lined up, like this:

On the bottom you can see the active buzzer, on the top you can see the temperature sensor, on the right the gas sensor is barely visible through the grate, on left the IR sensor is poking through, and of course the Neopixel ring is in the middle.

STEP 3: ASSEMBLE THE BASE STATION

Now on to the base station, which is considerably simpler. All we are going to do is hook up an nRF24L01 radio to an Arduino Uno as shown in this pinout and schematic:

Now we will want to upload the appropriate sketches to our base and smokes, but first let's set up the Raspberry Pi 3 so we can use it to upload these sketches.

STEP 4: PREPARE THE RASPBERRY PI 3

First thing you'll want to do is get your Raspberry Pi 3 fired up and install a fresh Rasbian image. I did this using NOOBS. Click here for instructions on installing NOOBS. Makes sure you select the Raspbian Operating system in the NOOBS setup. (Don't forget to change your keyboard to United States if that's where you are located)

(Note, that you could use any number of computers or operating systems to communicate with the base station. I used a Pi 3 because my mac was giving me fits with security settings. Also, note that this tutorial uses Python 3.)

First you'll want to make sure you have your Pi3 connected to the internet. Click here to see the documentation.

Now, open a terminal and enter the following commands.

sudo apt-get update
sudo apt get upgrade
sudo apt-get install arduino

Each of these commands is separate and the Upgrade command will take some time to complete. Be patient. The third line installs the Arduino IDE, which we will use to upload the sketches to our base station and smokes.

Now you need to download a couple of Libraries for the Arduino sketches to work. First let's get the libraries to operate the nRF24L02.

Download the nRF24L02 library here:

Scroll Down to here and click Download as indicated in figure 17:

After the download is complete, you will have a file named RF24-master.zip in your home/pi/downloads directory. Extract the file to the downloads folder. Then rename the extracted folder to "RF24". You're downloads folder should look like this:

The red arrow is pointing to the RF24 folder. Now you want to add the RF24 folder to your Arduino library by opening applications menu selecting the Arduino IDE, then clicking on Sketch ---> Import Library--->Add Library:

Once you click Add Library, navigate to your home/pi/Downloads folder, open the RF24 folder, and click OK as follows:

Now go back to your Arduino IDE and check that the library was properly added by selecting Sketch-->Import Library. You should see RF24 down at the bottom of the menu like so:

Now do this same process for the following two libraries:

  • "Ellapsed millis" library: Download, and extract into a folder named "ellapsedMillis". Documentation here.
  • Adafruit Neopixel Library: Download, and extract into a folder named "Adafruit_NeoPixel". Documentation here.

Please note, that without exception, when you extract the above .zip files, they will be placed automatically in a file that has the appropriate folder named followed by "_master". Just erase the "_master" and your folder will be named correctly.

STEP 5: UPLOAD THE ARDUINO SKETCHES

You should now be ready to compile and upload the sketches. There are two Arduino sketches included in the downloads section of this tutorial: SmartSmokesBase.ino, which gets loaded into the base, and SmartSmokesTx.ino, which gets loaded into each of the smart smokes alarms.

Let's start with the base. Download the SmartSmokesBase.ino and open it up in your Arduino IDE (Arduino may ask you to create a folder of the same name. Just say yes). You should have this on your screen:

Now plug in your Arduino Uno Base we created in Step 3 to one of the USB ports on the Pi 3. Check that your Tools-->Board is set to the "Uno" and that "Tools-->Serial Port is set to the Uno (mine is dev/tty/ACMO, but yours may be different). Here's the screen shots:

If everything looks good, then click "verify" then "upload". If everything goes smoothly, your Arduino IDE status bar should say "Done Uploading." Now click on the serial monitor. These three steps are shown here:

Once your serial monitor is open, entER "1" in the dialogue and press "send." You should get back a "999." This means the base station is monitoring the serial port for incoming messages. Here's what you should see:

Ok, if all that went well, its time to move on to uploading software to your smokes!

This is almost the exact same process as uploading to the base station, with one important difference. We need to slightly modify each upload of SmartSmokesTx.ino to correspond to each unique smoke or node. Download the SmartSmokesTx.ino and open it in the Arduino IDE. Look at the code and notice a line that says "#define WHICH_NODE 1:" (the one in figure 27 below says WHICH_NODE 3)

You must change this number to a number between 1 and 6 to correspond with each smoke alarm or node. So for example, if you only have two smokes, then for the first you would designate WHICH_NODE 1, save the sketch, and then upload the sketch to your smoke. Next, designate WHICH_NODE 2, save the sketch, and upload to your second smoke. That's It! Go ahead and upload your Arduino IDE to your smokes. For this project I only created two smokes, just as described in this paragraph. But you could do up to 6 smokes.

Once you have uploaded your software, go ahead and take any normal lighter and squirt a little butane right into the grate that covers the gas sensor as I did in the demonstration video. You should get a loud beep and red lights as shown in the demo video.

Keep in mind that your smoke will act funny when you first power it up. It may beep and indicate an alarm. This is because the the gas sensor has a element that must heat up before it can read correctly. When the gas sensor is cold it gives a false alarm If you look closely at the code you'll see I tried to account for this warmup period, but it was still a little buggy when the deadline was up for this project. Something to keep working on . . . in any event, after the gas sensor warms up the smoke will operate normally.

If your smokes work, then its time to go on to configuring Alexa to talk to your Smart Smokes system.

STEP 5: INSTALL FLASK-ASK

Installing flask ask is very simple, just open an terminal on the Raspberry Pi 3 and type the following command .

sudo pip3 install flask-ask

Answer yes when asked to install.

STEP 6: INSTALL NGROK

Follow these instructions to install NGrok (thanks again to John Wheeler):

Click here to the ngrok client for your operating system. Make sure you choose the Linux (ARM) version if your using the Raspberry Pi 3:

  • Unzip it to a location you can remember. (I just extracted mine in the /home/pi/downloads folder)
  • Open up a new terminal, cd into the location, and enter:
./ngrok http 5000

The terminal should now look something like this:

Write down the bottom most "Forwarding" address. In Figure 29 above it is https://a1409a8.ngrok.io. Yours will almost certainly be different. Save that address for later and leave the terminal open. If you close the terminal, then you close the ngrok session and Smart Smokes and Alexa won't be able to communicate. Now let's move on to configuring Alexa.

STEP 7: CONFIGURE THE ALEXA SKILL

First you will need to register for an Amazon Developer account. Click here to go to the registration page. Once you'v registered, sign in and the screen should look something like this

Now, click on the Alexa tab as indicated by the red arrow in Figure 30. The next screen should look something like this:

Now, click on the Get Started button under "Alexa Skills Kit" as represented by the red arrow in Figure 31. The next screen should look something like this:

You'll note in Figure 32 that I already have a skill named "smart smokes." That's because this is my account. You'll want to create this skill in your account. Do so by clicking "Add New Skill" as indicated by the red arrow in Figure 32 above. The next screen should look something like this:

Make sure your screen looks like this one, including, clicking the "Custom Interaction Model" radio button, entering English as the appropriate language, and using "smart smokes" as both the skill name and the invocation name. Now click Save, as indicated by the red arrow above. The next screen will look something like this:

Click Next, as indicated by the red arrow above. The next screen should look something like this:

Now you are into the meat of the skill: the interaction model. This is where you put together your intents, slots and utterances. There are many good tutorials on building a interaction model. I'm just going to cut to the chase and suggest you just copy my code so we can get this thing built. You can go back and play with the interaction model later. So click on the "Code Editor" button as indicated by the red arrow above. You should see this screen:

Now, you'll see the screen above. Go ahead and drag and drop the InteractionModel.txt file included in the code section. If it works, your screen should look something like this:

Keep in mind, that the stuff on the left side of the screen in these shots will probably not show up on yours. Again, this is my existing skill, and I am just backtracking through it for instruction purposes. But the code in the main editor should look the same or very similar to what you see above. If it does, then click "Apply Changes", then "Build Code" (this one will take awhile), then "Save Model".

Now the model is built, so we can configure it. Click "Configuration" (as shown above). You're screen should look as follows:

This is where we tell Alexa where to send the instructions to operate our skill. In this case we want to click the HTTPS button and then enter the NGROK address that you saved above in step 6. The Ngrok address you see in Figure 38 above is just an example. You must enter your own from Step 6. Scroll the screen down and you should see this:

Fill out the remainder of the screen as shown above. Most, if not all, should be default values. Then click next as shown above. Now you should see this:

Click on the radio button corresponding to "My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority." Save if necessary, then click "Next", all as indicated above.

Congrats! You're ready to test the skill.

STEP 8: TESTING THE SKILL

Download the SerialChooser.py program and run it in your Python 3 IDE. This video shows how to do it:

Video 2

And here is a full demonstration of most of the capabilities of Smart Smokes:

Smart Smokes Video Demonstration

Lastly, you can also get the current temperature from any particular node by asking "Alexa, tell me the temperature on Node ___." Enjoy!

Code

SerialChooser.pyPython
This is the Python 3/Flask-Ask program that you use to connect your Raspberry Pi to the base station and Alexa to the Raspberry Pi.
import tkinter as tk
import tkinter.ttk as ttk
import serial.tools.list_ports
import serial as Cheerios
import time


import logging
import os

from flask import Flask
from flask_ask import Ask, request, session, question, statement, convert_errors

app = Flask(__name__)
ask = Ask(app, "/")

logging.getLogger('flask_ask').setLevel(logging.DEBUG)

# --- functions ---

@ask.intent('EmergencyLightsOn')
def emergencyLightsOn():
    ser.flushInput()
    msg=40
    msgst=str(msg).encode()
    print(msgst)
    ser.write(msgst)
    return statement("Emergency lights on.")

@ask.intent('EmergencyLightsOff')
def emeregencyLightsOff():
    ser.flushInput()
    msg=50
    msgst=str(msg).encode()
    ser.write(msgst)
    return statement("Emergency lights off.")
@ask.intent('SilenceAlarm',convert={'node' : int} )
def silenceAlarm(node):
    ser.flushInput()
    msg=30 + node
    msgst=str(msg).encode()
    ser.write(msgst)
    return statement("Node {} has been silenced".format(node))

@ask.intent('GetStatus')
def getStatus():
    ser.flushInput()
    speech_text=""
    counter=0
    nodeAlarmList=[[False, False], [False, False], [False, False], [False, False]]  #(smoke, flame)
                   
    for i in range(0, 10):    #collect a sample from the smokes and store in list
        status=ser.readline().strip()
        status=status.decode('ascii')
        
        if status:
            nodeNumber=int(status[3])
            if status[:3]=='777':   #reporting smoke/gas
                nodeAlarmList[nodeNumber][0]=True
            if status[:3]=='888':   #reporting flame
                nodeAlarmList[nodeNumber][1]=True
    for t in nodeAlarmList:
        
        if (t[0] and t[1]):
            speech_text += " Node" + str(counter) + " is reporting a flame and smoke or dangerous gases,"
        elif t[0]:
            speech_text += " Node" + str(counter) + " is reporting smoke or dangerous gases."
        elif t[1]:
            speech_text += " Node" + str(counter) + " is reporting a flame."
        counter=counter+1
    if (speech_text):
        return statement(speech_text)
    else:
        speech_text = "No alarms reported"
        return statement(speech_text)
    

@ask.intent('TestBuzzer', convert={'node' : int})
def testBuzz(node):
    ser.flushInput()
    if node is None:
        return question("Please repeat your request and make sure you  specify a node.")
    msg=20 + node
    msgst=str(msg).encode()
    ser.write(msgst)
    return statement("Buzzer Test Done on node {}".format(node))

@ask.intent('Hello')
def hello_world():
    speech_text = 'Hello, welcome to Smart Smokes'
    return statement(speech_text)

@ask.intent('GetTemp', convert={'node' : int})
def getTemp(node):
    ser.flushInput()
    msg= 10 + node
    msgst=str(msg).encode()
    ser.write(msgst)
    time.sleep(2)
    temp=ser.readline().strip()
    if temp:
        speech_text= "The temperature on node {} is ".format(node) + str(temp.decode('ascii')) + "Degrees farenheit"
        return statement(speech_text)

@ask.launch
def launch():
    speech_text = 'Hello, Welcome to Smart Smokes'
    return statement(speech_text)


def serial_ports():
    portVar1=serial.tools.list_ports.comports()
    MenuList = []
    for x in range(len(portVar1)):
        MenuList.append(portVar1[x].device)
    return MenuList
    

def on_select_port(event=None):
     portVar = PortSelect.get()
     global ser
     ser=serial.Serial(portVar, timeout=.5) 
     

def on_start_flask_ask(event=None):
     if __name__ == '__main__':
        if 'ASK_VERIFY_REQUESTS' in os.environ:
            verify = str(os.environ.get('ASK_VERIFY_REQUESTS', '')).lower()
            if verify == 'false':
                app.config['ASK_VERIFY_REQUESTS'] = False
        app.run()
        
    

# --- main ---

root = tk.Tk()
root.title("Smart Smokes")
           
mainframe = ttk.Frame(root, padding='1i')
mainframe.grid(column=5, row=5)
mainframe.columnconfigure(0, weight=1)
mainframe.rowconfigure(0, weight=1)

StartFlask = ttk.Button(mainframe, text= "Start Flask", command=on_start_flask_ask)
StartFlask.grid(column=2, row=2)
           
PortSelect = ttk.Combobox(mainframe, values=serial_ports())
PortSelect.grid(column=2, row=1)

PortSelect.bind('<<ComboboxSelected>>', on_select_port)


                    
root.mainloop()
           

                
SmartSmokesBaseArduino
Upload this Arduino Sketch to the Base Station.
//Radio Setup

#include <SPI.h> //Call SPI library so you can communicate with the nRF24L01+
#include <nRF24L01.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/
#include <RF24.h> //nRF2401 libarary found at https://github.com/tmrh20/RF24/

const int pinCE = 7; //This pin is used to set the nRF24 to standby (0) or active mode (1)
const int pinCSN = 8; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
RF24 radio(pinCE, pinCSN); // Declare object from nRF24 library (Create your wireless SPI)

//Create up to 6 pipe addresses P0 - P5;  the "LL" is for LongLong type
const uint64_t rAddress[] = {0x7878787878LL, 0xB3B4B5B6F1LL, 0xB3B4B5B6CDLL, 0xB3B4B5B6A3LL, 0xB3B4B5B60FLL, 0xB3B4B5B605LL };

byte daNumber = 0; //The number that the transmitters are trying to guess
char receivedChar;
boolean newData = false;
void setup()   
{
  
  Serial.begin(9600);  //start serial to communication
  
  
  radio.begin();  //Start the nRF24 module

  radio.setPALevel(RF24_PA_LOW);  // "short range setting" - increase if you want more range AND have a good power supply
  radio.setChannel(108);          // the higher channels tend to be more "open"

  // Open up to six pipes for PRX to receive data
  radio.openReadingPipe(0,rAddress[0]);
  radio.openReadingPipe(1,rAddress[1]);
  radio.openReadingPipe(2,rAddress[2]);
  radio.openReadingPipe(3,rAddress[3]);
  radio.openReadingPipe(4,rAddress[4]);
  radio.openReadingPipe(5,rAddress[5]);
  
  radio.startListening();                 // Start listening for messages
}

void loop()  
{   
   byte pipeNum = 0; //variable to hold which reading pipe sent data
    int16_t msg; //used to store payload from transmit module


 //This part of the program listens for incoming alerts from the Smart SMokes Tx
    while(radio.available(&pipeNum)){ //Check if received data
     radio.read( &msg, sizeof(msg) ); //read one byte of data and store it in gotByte variable
     
     
     switch (msg)
    {
      case 1:
        Serial.print(777); //cue for fire alarm
        Serial.println(pipeNum + 1); //print which pipe or transmitter this is from

        break;
      case 2:
        Serial.print(888); //cue for flame alarm
        Serial.println(pipeNum + 1); //print which pipe or transmitter this is from
        break;
    }
                        }  

//This part of the code listens for a serial command.  If one is available it sends to smokes and waits for appropriate response
int outMsg;


  if (Serial.available() >0 ){

      outMsg = Serial.parseInt();
      //Serial.println(outMsg);
      int Buzz=2; 
      int Temp=1;
      int Silence=3;
      int Lights=4;
      int LightsOff=5;
      
      switch(outMsg){

        case 11:     //Get Temp from node 1
          radio.stopListening();
          radio.openWritingPipe(rAddress[0]);
          radio.write(&Temp, sizeof(Temp));

          Serial.println(recieveTemp());  // send temp value back to python
      
          break;
          
        case 12:    // Get temp from node 2
          radio.stopListening();
          radio.openWritingPipe(rAddress[1]);
          radio.write(&Temp, sizeof(Temp));

          Serial.println(recieveTemp());  // send temp value back to python
      
          break;
        
        case 13:    //Get temp from node 3
           radio.stopListening();
          radio.openWritingPipe(rAddress[2]);
          radio.write(&Temp, sizeof(Temp));

          Serial.println(recieveTemp());  // send temp value back to python
      
          break;
        
        case 21:  //test buzzer on node 1
            
            radio.stopListening();
            radio.openWritingPipe(rAddress[0]);
            radio.write(&Buzz, sizeof(Buzz));
            radio.startListening();
            break; 
            
        case 22:  //test buzzer on node 2
            
            radio.stopListening();
            radio.openWritingPipe(rAddress[1]);
            radio.write(&Buzz, sizeof(Buzz));
            radio.startListening();
            break;
            
        case 23:  //test buzzer on node 3
            
            radio.stopListening();
            radio.openWritingPipe(rAddress[2]);
            radio.write(&Buzz, sizeof(Buzz));
            radio.startListening();
            break;
            
         case 31: //silence alarm on Node 1
            radio.stopListening();
            radio.openWritingPipe(rAddress[0]);
            radio.write(&Silence, sizeof(Silence));
            radio.startListening();
            break;
            
         case 32: //silence alarm on Node 2
            radio.stopListening();
            radio.openWritingPipe(rAddress[1]);
            radio.write(&Silence, sizeof(Silence));
            radio.startListening();
            break;
            
         case 33: //silence alarm on Node 3
            radio.stopListening();
            radio.openWritingPipe(rAddress[2]);
            radio.write(&Silence, sizeof(Silence));
            radio.startListening();
            break;  
            
         case 40:  //turn on emergency lights
            radio.stopListening();
            radio.openWritingPipe(rAddress[0]);
            radio.write(&Lights, sizeof(Lights));
            radio.openWritingPipe(rAddress[1]);
            radio.write(&Lights, sizeof(Lights));
            radio.openWritingPipe(rAddress[2]);
            radio.write(&Lights, sizeof(Lights));
            radio.startListening();
            break;
         
         case 50:  //turn off emergency lights
            radio.stopListening();
            radio.openWritingPipe(rAddress[0]);
            radio.write(&LightsOff, sizeof(LightsOff));
            radio.openWritingPipe(rAddress[1]);
            radio.write(&LightsOff, sizeof(LightsOff));
            radio.openWritingPipe(rAddress[2]);
            radio.write(&LightsOff, sizeof(LightsOff));
            radio.startListening();
            break;
            
         default:

            Serial.println(999);   //error code
           }
          
      }
      

  }



          
int recieveTemp(){
    int temp;
    unsigned long started_waiting_at = millis();
    bool timeout = false;
    radio.startListening();
    started_waiting_at = millis();
    timeout = false;
    while ( ! radio.available() && ! timeout ){
      if (millis() - started_waiting_at > 5000 ){
          timeout = true;}}
            // Describe the results
             if ( timeout )
                 {
                     temp=0;
                 }
               else
                  {
                    radio.read( &temp, sizeof(temp) );
                  }
     return temp;
}

void recvOneChar() {
 if (Serial.available() > 0) {
 receivedChar = Serial.read();
 newData = true;
 }
}

void showNewData() {
 if (newData == true) {
 Serial.print("This just in ... ");
 Serial.println(receivedChar);
 newData = false;
 }
}
SmartSmokesTxArduino
This is the Arduino sketch you upload to each of the smokes. Don't forget to edit the "WHICH_NODE" parameter to correspond to your smoke.
//Each Smart Smoke Uses the same code below.  Just change the WHich_Node to a unique number between 1 and 6


#include <elapsedMillis.h>  //use for timer on alarm silencing feature

elapsedMillis sinceSilence;


//Set up the NRF24**************************

  #include <SPI.h>
  #include <RF24_config.h>
  #include <printf.h>
  #include <nRF24L01.h>
  #include <RF24.h>

  const int pinCE = 7; //This pin is used to set the nRF24 to standby (0) or active mode (1)
  const int pinCSN = 8; //This pin is used to tell the nRF24 whether the SPI communication is a command or message to send out
  
  RF24 radio(pinCE, pinCSN); // Create your nRF24 object or wireless SPI connection

  #define WHICH_NODE 1     // must be a number from 1 - 6 identifying the PTX node

  const uint64_t wAddress[] = {0x7878787878LL, 0xB3B4B5B6F1LL, 0xB3B4B5B6CDLL, 0xB3B4B5B6A3LL, 0xB3B4B5B60FLL, 0xB3B4B5B605LL};
  const uint64_t PTXpipe = wAddress[ WHICH_NODE - 1 ];   // Pulls the address from the above array for this node's pipe
  byte counter = 1; //used to count the packets sent
  bool done = false; //used to know when to stop sending packets



//Sensor Pin Variables
  int tempsensorPin = A0; //the analog pin the TMP36's Vout (sense) pin is connected to
                        //the resolution is 10 mV / degree centigrade with a
                        //500 mV offset to allow for negative temperatures
 
  int buzzer = 2;//the pin of the active buzzer

  int gasPin = A1; //smoke alarm pin

  int flamePin = 5;

  boolean silenceAlarm = false;
  boolean emergencyLightsOn=false;
  
//setup flame sensor


//setup Neopixel 12 on pin 6
  #include <Adafruit_NeoPixel.h>
  #define NeoPIN            6
  #define NUMPIXELS      16
  Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUMPIXELS, NeoPIN, NEO_GRB + NEO_KHZ800);


//setup Alarm variables


boolean warmedUp=false;


void setup()
{
  
  //Radio Initialize
  
  radio.begin();            //Start the nRF24 module
  radio.setPALevel(RF24_PA_HIGH);  // "short range setting" - increase if you want more range AND have a good power supply
  radio.setChannel(108);          // the higher channels tend to be more "open"
  radio.openReadingPipe(0,PTXpipe);  //open reading or receive pipe
  radio.stopListening(); //go into transmit mode



  //Initialize Serial 
  
  Serial.begin(9600);  //Start the serial connection with the computer
     
                      //to view the result open the serial monitor 
                      
  //INitialize Sensors
                       
  pinMode(buzzer,OUTPUT);//initialize the buzzer pin as an output
  digitalWrite(buzzer,HIGH);  //High is silent on the buzzer
  pinMode(gasPin, INPUT);//Smoke alarm Initialze
  pinMode(flamePin, INPUT); //Flame alarm initialize
   strip.begin(); // This initializes the NeoPixel library.
   strip.show();

   
}
 
void loop(){

if (warmedUp){//This won't let the loop start until the heating element is up temp, which is judged by the analog read.




if (emergencyLightsOn){
   colorWipe(strip.Color(127, 127, 127), 50); // White;
     strip.show();
}
else {
  colorWipe(strip.Color(0, 0, 0), 50); // lights off
  strip.show();
}


if (silenceAlarm and (sinceSilence > 60000)) {//allows alexa to silence the alarm for 60 seconds even if an alarm is being detected
  silenceAlarm = false;
}

//request from base station section
radio.startListening();  //wait for a request from the base station
if (radio.available()){  //if a request is available call handleRequest() function
   handleRequest();}
   
//alarm detecting section

if ((smokeAlarm() or flameAlarm())){  //if the Tx detects any alarm AND the alrm is not silenced, start buzzing, send signal to base and turn on red lights
  if (!silenceAlarm){//condtion the buzzer on the alarm NOT being silenced
    makeBuzz();}
  colorWipe(strip.Color(255, 0, 0), 50); // Red
  sendAlarm();
}
else{
  silenceBuzz();
  if (!emergencyLightsOn){colorWipe(strip.Color(0, 0, 0), 50); // lights off
  strip.show();}
  else {
     colorWipe(strip.Color(127, 127, 127), 50); // White;
     strip.show();
  }

}
}
else {
  warmupSmoke();

  makeBuzz();  
  warmedUp=true;}
  
}
   








void sendAlarm(){
  int msg;
  radio.stopListening(); //go into transmit mode
  radio.openWritingPipe(PTXpipe); //open writing or transmit pipe 

  if (smokeAlarm()){
    msg = 1;
    radio.write( &msg, sizeof(msg));

   }

  if(flameAlarm()){
    msg = 2;
    radio.write( &msg, sizeof(msg));
  }
    

}

void handleRequest(){
    int request;
    
    while (radio.available()) {                          // While there is data ready
        radio.read(&request, sizeof(request) );             // Get the payload
      }
        int temp;

     switch (request)
      { 
        case 1:                                           //1 is a temperature request
         temp = (int) getTemp();
          Serial.println(temp);
          radio.stopListening(); //go into transmit mode
          radio.openWritingPipe(PTXpipe);   //open writing or transmit pipe 
          radio.write(&temp, sizeof(temp));
          break;
        
        case 2:                                           //2 is a buzzer test
          for (int i=0; i <=20000; i++){
          makeBuzz();}
          break;

        case 3:
  
          silenceAlarm=true;
          sinceSilence=0;
          silenceBuzz();
          break;
          
        case 4:
          emergencyLightsOn=true;
          Serial.println(emergencyLightsOn);
          break;
          
        case 5:
          
           emergencyLightsOn=false;
            Serial.println(emergencyLightsOn);
           break;
          

      }
          
   
  }

  


void warmupSmoke(){

  while (smokeAlarm()){
      theaterChase(strip.Color(0, 0, 127), 50); // Blue
  }
}

  
float getTemp()
{
   //getting the voltage reading from the temperature sensor
 int reading = analogRead(tempsensorPin);  

 
 // converting that reading to voltage, for 3.3v arduino use 3.3
 float voltage = reading * 4.27;
 voltage /= 1024.0; 
 

 
 // now print out the temperature
 float temperatureC = (voltage - 0.5) * 100 ;  //converting from 10 mv per degree wit 500 mV offset
                                               //to degrees ((voltage - 500mV) times 100)
 //Serial.print(temperatureC); Serial.println(" degrees C");
 
 // now convert to Fahrenheit
 float temperatureF = (temperatureC * 9.0 / 5.0) + 32.0;
 //Serial.print(temperatureF); Serial.println(" degrees F");
 
 return temperatureF;
 
}

void makeBuzz()
{

digitalWrite(buzzer,LOW);

}

void silenceBuzz(){

  digitalWrite(buzzer, HIGH);
}
  
boolean smokeAlarm()
{
 if (analogRead(gasPin)> 250){
  return true;
}
else {
  return false;
}
}

//NeoPixelFunctions

void colorWipe(uint32_t c, uint8_t wait) {
  for(uint16_t i=0; i<strip.numPixels(); i++) {
    strip.setPixelColor(i, c);
    strip.show();
    delay(wait);
  }
}

boolean flameAlarm()
{
  int Flame = digitalRead(flamePin);

  if (Flame==LOW){
    return true;
  }
  else{
    return false;
  }
  
  
}

void rainbow(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256; j++) {
    for(i=0; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel((i+j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t wait) {
  uint16_t i, j;

  for(j=0; j<256*5; j++) { // 5 cycles of all colors on wheel
    for(i=0; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

//Theatre-style crawling lights.
void theaterChase(uint32_t c, uint8_t wait) {
  for (int j=0; j<10; j++) {  //do 10 cycles of chasing
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, c);    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

//Theatre-style crawling lights with rainbow effect
void theaterChaseRainbow(uint8_t wait) {
  for (int j=0; j < 256; j++) {     // cycle all 256 colors in the wheel
    for (int q=0; q < 3; q++) {
      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, Wheel( (i+j) % 255));    //turn every third pixel on
      }
      strip.show();

      delay(wait);

      for (uint16_t i=0; i < strip.numPixels(); i=i+3) {
        strip.setPixelColor(i+q, 0);        //turn every third pixel off
      }
    }
  }
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

void fullWhite() {
  
    for(uint16_t i=0; i<strip.numPixels(); i++) {
        strip.setPixelColor(i, strip.Color(0,0,0, 255 ) );
    }
      strip.show();
  }
Interaction ModelJSON
Drag and drop this file into the Alexa Skill as described in the tutorial.
{
  "languageModel": {
    "intents": [
      {
        "name": "AMAZON.CancelIntent",
        "samples": []
      },
      {
        "name": "AMAZON.HelpIntent",
        "samples": []
      },
      {
        "name": "AMAZON.StopIntent",
        "samples": []
      },
      {
        "name": "EmergencyLightsOff",
        "samples": [
          "turn off emergency light",
          "turn off the light",
          "shut off the emergency lighting",
          "turn off the lights",
          "shut off the light",
          "turn off the emergency light",
          "shut off the emergency light"
        ],
        "slots": []
      },
      {
        "name": "EmergencyLightsOn",
        "samples": [
          "turn on the lights",
          "turn on the emergency lights",
          "turn on all the lights"
        ],
        "slots": []
      },
      {
        "name": "GetStatus",
        "samples": [
          "which alarm is going off",
          "which alarm is that",
          "what is the status of the alarms",
          "what is the status",
          "which alarm is buzzing",
          "what alarm is buzzing",
          "are there any alarms buzzing",
          "are there any alarm",
          "is there any flames",
          "is there any smoke",
          "are any alarms going off",
          "tell me what is happening",
          "tell me what's happening"
        ],
        "slots": []
      },
      {
        "name": "GetTemp",
        "samples": [
          "what's the temperature on node {node}",
          "what is the temperature on node {node}",
          "give me the temperature on node {node}",
          "how is the temperature on node {node}",
          "tell me the temperature on  node {node}",
          "read the temperature on node {node}",
          "for the temperature on node {node}",
          "for the temperature on alarm {node}",
          "what's the temperature on alarm {node}",
          "what is the temperature on alarm {node}",
          "give me the temperature on alarm {node}"
        ],
        "slots": [
          {
            "name": "node",
            "type": "AMAZON.NUMBER"
          }
        ]
      },
      {
        "name": "Hello",
        "samples": [
          "Hello",
          "Hey",
          "Tell smart smokes I said hello",
          "tell smart smokes hello"
        ],
        "slots": []
      },
      {
        "name": "SilenceAlarm",
        "samples": [
          "Silence node {node}",
          "Stop the alarm on node {node}",
          "tell node {node} to be quiet",
          "tell alarm {node} that I'm just cooking",
          "silence alarm {node}",
          "stop alarm {node}"
        ],
        "slots": [
          {
            "name": "node",
            "type": "AMAZON.NUMBER"
          }
        ]
      },
      {
        "name": "TestBuzzer",
        "samples": [
          "test the buzzer on {node}",
          "buzz {node}",
          "try the buzzer on {node}",
          "test {node} alarm",
          "try the alarm on {node}",
          "test the buzzer on alarm {node}",
          "try the buzzer on alarm {node}"
        ],
        "slots": [
          {
            "name": "node",
            "type": "AMAZON.NUMBER"
          }
        ]
      }
    ],
    "invocationName": "smart smokes"
  }
}

Custom parts and enclosures

Smoke Top STL
This is the mounting plate for all the sensors.
Smoke Body
This is the body of the alarm.
Smoke Base
This is the Base of the alarm.

Schematics

Base Station Schematic
This is how you wire the nRF24L01 radio to the Arduino Uno to create the base station for the smart smokes.
Base station schematic anabljtehn
Smart Smokes Schematics
This is the breadboard diagram of the smart smoke alarms.
Smartsmoke tx bb nauj21otho

Comments

Similar projects you might like

Rampiot - Cool Smart Lock

Project tutorial by Robinson Mesino

  • 6,550 views
  • 3 comments
  • 38 respects

Smart Battery Charger Multiplexer with Smart Display

Project tutorial by leonzak

  • 5,873 views
  • 8 comments
  • 19 respects

Alexa Smart Mailbox

Project tutorial by Team CodersCafe

  • 2,660 views
  • 4 comments
  • 16 respects

Hygge Home - Alexa Smart Bath

Project tutorial by J Howard

  • 6,635 views
  • 2 comments
  • 22 respects

Smart Home - Smart Rules using ARTIK Cloud & Photon

Project tutorial by Raghavendra Ponuganti

  • 5,286 views
  • 3 comments
  • 17 respects

Alexa Smart Power Strip with Temperature

Project tutorial by Muhammad Afzal

  • 2,105 views
  • 0 comments
  • 11 respects
Add projectSign up / Login