|
| 1 | +// Adafruit IO House: Security System |
| 2 | +// |
| 3 | +// Learn Guide: https://learn.adafruit.com/adafruit-io-home-security |
| 4 | +// |
| 5 | +// Adafruit invests time and resources providing this open source code. |
| 6 | +// Please support Adafruit and open source hardware by purchasing |
| 7 | +// products from Adafruit! |
| 8 | +// |
| 9 | +// Written by Brent Rubell for Adafruit Industries |
| 10 | +// Copyright (c) 2018 Adafruit Industries |
| 11 | +// Licensed under the MIT license. |
| 12 | +// |
| 13 | +// All text above must be included in any redistribution. |
| 14 | + |
| 15 | +/************************** Configuration ***********************************/ |
| 16 | + |
| 17 | +// edit the config.h tab and enter your Adafruit IO credentials |
| 18 | +// and any additional configuration needed for WiFi, cellular, |
| 19 | +// or ethernet clients. |
| 20 | +#include "config.h" |
| 21 | + |
| 22 | +// include the NeoPixel library |
| 23 | +#include "Adafruit_NeoPixel.h" |
| 24 | + |
| 25 | +// include the SGP30 library |
| 26 | +#include <Wire.h> |
| 27 | +#include "Adafruit_SGP30.h" |
| 28 | + |
| 29 | +/************************ Example Starts Here *******************************/ |
| 30 | + |
| 31 | +// delay the main `io.run()` loop |
| 32 | +#define LOOP_DELAY 3000 |
| 33 | +// delay for each sensor send to adafruit io |
| 34 | +#define SENSOR_DELAY 1000 |
| 35 | + |
| 36 | +// PIR sensor input pin |
| 37 | +#define pirPin 13 |
| 38 | +// reed switch input pin |
| 39 | +#define doorPin 2 |
| 40 | +// piezo (alarm) buzzer |
| 41 | +#define piezoPin 14 |
| 42 | + |
| 43 | +/**** Time Setup ****/ |
| 44 | +// set the hour to start at |
| 45 | +int startingHour = 1; |
| 46 | +// set the second to start at |
| 47 | +int seconds = 56; |
| 48 | +// set the minutes to start at |
| 49 | +int minutes = 56; |
| 50 | +// set the hour at which the motion alarm should trigger |
| 51 | +int alarmHour = 16; |
| 52 | + |
| 53 | +unsigned long currentTime = 0; |
| 54 | +unsigned long prevTime = 0; |
| 55 | +int currentHour = startingHour; |
| 56 | + |
| 57 | + |
| 58 | +/*********NeoPixel Setup*********/ |
| 59 | +// pin the NeoPixel strip and jewel are connected to |
| 60 | +#define NEOPIXEL_PIN 12 |
| 61 | +// amount of neopixels on the NeoPixel strip |
| 62 | +#define STRIP_PIXEL_COUNT 60 |
| 63 | +#define JEWEL_PIXEL_COUNT 7 |
| 64 | +// type of neopixels used by the NeoPixel strip and jewel. |
| 65 | +#define PIXEL_TYPE NEO_GRB + NEO_KHZ800 |
| 66 | +// init. neoPixel Strip |
| 67 | +Adafruit_NeoPixel strip = Adafruit_NeoPixel(STRIP_PIXEL_COUNT, NEOPIXEL_PIN, PIXEL_TYPE); |
| 68 | + |
| 69 | +// sketch starts assuming no motion is detected |
| 70 | +int pirState = LOW; |
| 71 | +// sketch starts assuming the the door is closed |
| 72 | +int doorState = LOW; |
| 73 | +// alarm state |
| 74 | +bool isAlarm = false; |
| 75 | +// variable for reading the pin status |
| 76 | +int pirRead = 0; |
| 77 | +// SGP30 Sensor Object |
| 78 | +Adafruit_SGP30 sgp; |
| 79 | + |
| 80 | +/*** Adafruit IO Feed Setup ***/ |
| 81 | +// 'indoor-lights' feed |
| 82 | +AdafruitIO_Feed *indoorLights = io.feed("indoor-lights"); |
| 83 | +// `outdoor-lights` feed |
| 84 | +AdafruitIO_Feed *outdoorLights = io.feed("outdoor-lights"); |
| 85 | +// `front-door` feed |
| 86 | +AdafruitIO_Feed *frontDoor = io.feed("front-door"); |
| 87 | +// `motion-detector` feed |
| 88 | +AdafruitIO_Feed *motionFeed = io.feed("motion-detector"); |
| 89 | +// `home-alarm` feed |
| 90 | +AdafruitIO_Feed *alarm = io.feed("home-alarm"); |
| 91 | +// 'tvoc' feed |
| 92 | +AdafruitIO_Feed *tvocFeed = io.feed("tvoc"); |
| 93 | +// 'eco2' feed |
| 94 | +AdafruitIO_Feed *eco2Feed = io.feed("eco2"); |
| 95 | + |
| 96 | +void setup() { |
| 97 | + // start the serial connection |
| 98 | + Serial.begin(115200); |
| 99 | + |
| 100 | + // wait for serial monitor to open |
| 101 | + while(! Serial); |
| 102 | + Serial.println("Adafruit IO Home: Security"); |
| 103 | + |
| 104 | + Serial.println("Connecting to Adafruit IO"); |
| 105 | + // start MQTT connection to io.adafruit.com |
| 106 | + io.connect(); |
| 107 | + |
| 108 | + // attach a message handler for the `home-alarm` feed |
| 109 | + alarm->onMessage(handleAlarm); |
| 110 | + // subscribe to lighting feeds and register message handlers |
| 111 | + indoorLights->onMessage(indoorLightHandler); |
| 112 | + outdoorLights->onMessage(outdoorLightHandler); |
| 113 | + |
| 114 | + // wait for an MQTT connection |
| 115 | + // NOTE: when blending the HTTP and MQTT API, always use the mqttStatus |
| 116 | + // method to check on MQTT connection status specifically |
| 117 | + while(io.mqttStatus() < AIO_CONNECTED) { |
| 118 | + Serial.print("."); |
| 119 | + delay(500); |
| 120 | + } |
| 121 | + // we are connected |
| 122 | + Serial.println(); |
| 123 | + Serial.println(io.statusText()); |
| 124 | + |
| 125 | + // declare PIR sensor as input |
| 126 | + pinMode(pirPin, INPUT); |
| 127 | + // declare reed switch as input |
| 128 | + pinMode(doorPin, INPUT); |
| 129 | + // set up the SGP30 sensor |
| 130 | + setupSGP30(); |
| 131 | + // init the neopixel strip and set to `off` |
| 132 | + strip.begin(); |
| 133 | + strip.show(); |
| 134 | +} |
| 135 | + |
| 136 | +void loop(){ |
| 137 | + // io.run(); is required for all sketches. |
| 138 | + // it should always be present at the top of your loop |
| 139 | + // function. it keeps the client connected to |
| 140 | + // io.adafruit.com, and processes any incoming data. |
| 141 | + io.run(); |
| 142 | + |
| 143 | + getTime(); |
| 144 | + Serial.println("* read door sensor..."); |
| 145 | + readDoorSensor(); |
| 146 | + Serial.println("* read motion detector"); |
| 147 | + readPIR(); |
| 148 | + Serial.println("* reading SGP30..."); |
| 149 | + readSGP30(); |
| 150 | + |
| 151 | + // check if the alarm toggle is armed from the dashboard |
| 152 | + if (isAlarm == true) { |
| 153 | + if (doorState == HIGH || (currentHour>alarmHour && pirState == HIGH)) { |
| 154 | + playAlarmAnimation(); |
| 155 | + } |
| 156 | + } |
| 157 | +} |
| 158 | + |
| 159 | +void playAlarmAnimation() { |
| 160 | +// plays the alarm piezo buzzer and turn on/off neopixels |
| 161 | + Serial.println("ALARM TRIGGERED!"); |
| 162 | + tone(piezoPin, 220, 2); |
| 163 | + for(int i=0; i<JEWEL_PIXEL_COUNT; ++i) { |
| 164 | + strip.setPixelColor(i, 255, 0, 0); |
| 165 | + } |
| 166 | + strip.show(); |
| 167 | + delay(500); |
| 168 | + for(int i=0; i<JEWEL_PIXEL_COUNT; ++i) { |
| 169 | + strip.setPixelColor(i, 0, 0, 0); |
| 170 | + } |
| 171 | + strip.show(); |
| 172 | +} |
| 173 | + |
| 174 | +void readDoorSensor() { |
| 175 | +// reads the status of the front door and sends to adafruit io |
| 176 | + doorState = digitalRead(doorPin); |
| 177 | + if (doorState == LOW) { |
| 178 | + Serial.println("* Door Closed"); |
| 179 | + frontDoor->save(1); |
| 180 | + } else { |
| 181 | + Serial.println("* Door Open"); |
| 182 | + frontDoor->save(3); |
| 183 | + } |
| 184 | + delay(SENSOR_DELAY); |
| 185 | +} |
| 186 | + |
| 187 | +void readPIR() { |
| 188 | + // check if motion is detected in front of the home |
| 189 | + pirRead = digitalRead(pirPin); |
| 190 | + if (pirRead == HIGH) { |
| 191 | + if (pirState == LOW) { |
| 192 | + // we have just turned on |
| 193 | + Serial.println("* Motion detected in front of home!"); |
| 194 | + motionFeed->save(3); |
| 195 | + pirState = HIGH; |
| 196 | + } |
| 197 | + } |
| 198 | + else { |
| 199 | + if (pirState == HIGH) { |
| 200 | + Serial.println("* Motion ended."); |
| 201 | + motionFeed->save(0); |
| 202 | + pirState = LOW; |
| 203 | + } |
| 204 | + } |
| 205 | + delay(SENSOR_DELAY); |
| 206 | +} |
| 207 | + |
| 208 | +void readSGP30() { |
| 209 | +// reads the SGP30 sensor and sends data to Adafruit IO |
| 210 | + if (! sgp.IAQmeasure()) { |
| 211 | + Serial.println("Measurement failed"); |
| 212 | + return; |
| 213 | + } |
| 214 | + Serial.print("TVOC "); Serial.print(sgp.TVOC); Serial.print(" ppb\t"); |
| 215 | + Serial.print("eCO2 "); Serial.print(sgp.eCO2); Serial.println(" ppm"); |
| 216 | + tvocFeed->save(int(sgp.TVOC)); |
| 217 | + delay(SENSOR_DELAY/2); |
| 218 | + eco2Feed->save(int(sgp.eCO2)); |
| 219 | + delay(SENSOR_DELAY/2); |
| 220 | +} |
| 221 | + |
| 222 | +/*** MQTT messageHandlers ***/ |
| 223 | +void handleAlarm(AdafruitIO_Data *data) { |
| 224 | +// handle the alarm toggle on the Adafruit IO Dashboard |
| 225 | + String toggleValue = data->toString(); |
| 226 | + Serial.print("> rcv alarm: "); |
| 227 | + Serial.println(toggleValue); |
| 228 | + if(toggleValue == String("ON")) { |
| 229 | + Serial.println("* Alarm Set: ON"); |
| 230 | + isAlarm = true; |
| 231 | + } else { |
| 232 | + Serial.println("* Alarm Set: OFF"); |
| 233 | + isAlarm = false; |
| 234 | + } |
| 235 | +} |
| 236 | + |
| 237 | +// handles the indoor light colorpicker on the Adafruit IO Dashboard |
| 238 | +void indoorLightHandler(AdafruitIO_Data *data) { |
| 239 | + Serial.print("-> indoor light HEX: "); |
| 240 | + Serial.println(data->value()); |
| 241 | + long color = data->toNeoPixel(); |
| 242 | + // set the color of each NeoPixel in the jewel |
| 243 | + for(int i=0; i<JEWEL_PIXEL_COUNT; ++i) { |
| 244 | + strip.setPixelColor(i, color); |
| 245 | + } |
| 246 | + // 'set' the neopixel jewel to the new color |
| 247 | + strip.show(); |
| 248 | +} |
| 249 | + |
| 250 | +// handles the outdoor light colorpicker on the Adafruit IO Dashboard |
| 251 | +void outdoorLightHandler(AdafruitIO_Data *data) { |
| 252 | + Serial.print("-> outdoor light HEX: "); |
| 253 | + Serial.println(data->value()); |
| 254 | + long color = data->toNeoPixel(); |
| 255 | + // set the color of each NeoPixel in the strip |
| 256 | + for(int i=JEWEL_PIXEL_COUNT; i<STRIP_PIXEL_COUNT+JEWEL_PIXEL_COUNT; ++i) { |
| 257 | + strip.setPixelColor(i, color); |
| 258 | + } |
| 259 | + // 'set' the neopixel strip to the new color |
| 260 | + strip.show(); |
| 261 | +} |
| 262 | + |
| 263 | + |
| 264 | +void setupSGP30(){ |
| 265 | +// sets up the SGP30 Sensor |
| 266 | + if (! sgp.begin()) { |
| 267 | + Serial.println("Sensor not found :("); |
| 268 | + while (1); |
| 269 | + } |
| 270 | + Serial.print("Found SGP30 serial #"); |
| 271 | + Serial.print(sgp.serialnumber[0], HEX); |
| 272 | + Serial.print(sgp.serialnumber[1], HEX); |
| 273 | + Serial.println(sgp.serialnumber[2], HEX); |
| 274 | +} |
| 275 | + |
| 276 | +void getTime() { |
| 277 | + currentTime = millis()/1000; |
| 278 | + seconds = currentTime - prevTime; |
| 279 | + |
| 280 | + if (seconds == 60) { |
| 281 | + prevTime = currentTime; |
| 282 | + minutes += 1; |
| 283 | + } |
| 284 | + if (minutes == 60) { |
| 285 | + minutes = 0; |
| 286 | + currentHour += 1; |
| 287 | + } |
| 288 | + if (currentHour == 24) { |
| 289 | + currentHour = 0; |
| 290 | + } |
| 291 | + |
| 292 | + Serial.print("Time:"); |
| 293 | + Serial.print(currentHour); |
| 294 | + Serial.print(":"); |
| 295 | + Serial.print(minutes); |
| 296 | + Serial.print(":"); |
| 297 | + Serial.println(seconds); |
| 298 | +} |
0 commit comments