I have built an intrusion detector that also measures barometer pressure and indoor and outdoor temperatures.
The pressure and indoor temperature is measured by a BMP280 and a number of DS18B20 sensors can be connected to measure the temperature in remote locations.
When movement is detected two red LEDs flash during 20 sec.
Everything is built into a frightening wooden mask, bought in Indonesia. The LEDs poke out through the nostrils.
Here is the sketch:
/**
* File name: MotionSensor_LED_2_temp_baro_280
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2015 Sensnology AB
* Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*
* REVISION HISTORY
* Version 1.0 - Henrik Ekblad
* Version 2.1 - Gunnar Blockmar includes PMB280 temp/baro sensor and DS18B20 temp sensor
*
* DESCRIPTION
* Motion Sensor example using HC-SR501
* http://www.mysensors.org/build/motion
*
* Now with 1 fading LEDs when sensor is triggered
* Measuring temp and pressure with BMP 280. Local pressure
* Measuring remote temp via DS18B20
*/
#include <Adafruit_BMP280.h>
#include <Adafruit_Sensor.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
unsigned long SLEEP_TIME = 60000; // Sleep time between reports (in milliseconds)
#define MY_DEBUG
#define DIGITAL_INPUT_SENSOR 3 // The digital input you attached your motion sensor. (Only 2 and 3 generates interrupt!)
#define INTERRUPT DIGITAL_INPUT_SENSOR-2 // Usually the interrupt = pin -2 (on uno/nano anyway)
#define CHILD_ID_MOT 0 // Id of the motion sensor child
#define CHILD_ID_ITEMP 1 // Id of indoor temperature sensor
#define CHILD_ID_BARO 2 // Id of pressure sensor
#define CHILD_ID_OTEMP 3 // Id of outdoor temperature sensor
#define RED_LED 5 // Pin for LED
#define RED_LED2 6 // Pin for LED2
#define DELAY 40 // Wait for brightness change
int fadeAmount = 5; // brightness change per step
#define ONE_WIRE_BUS 2 // Pin where dallas sensor is connected
#define MAX_ATTACHED_DS18B20 16
OneWire oneWire(ONE_WIRE_BUS); // Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
DallasTemperature sensors(&oneWire); // Pass the oneWire reference to Dallas Temperature.
int numSensors=0;
bool receivedConfig = false;
const char *weather[] = { "Stabilt", "Bättre", "Sämre", "Ostadigt", "Åska!", "Okänt" };
enum FORECAST
{
STABLE = 0, // "Stable Weather Pattern"
SUNNY = 1, // "Slowly rising Good Weather", "Clear/Sunny "
CLOUDY = 2, // "Slowly falling L-Pressure ", "Cloudy/Rain "
UNSTABLE = 3, // "Quickly rising H-Press", "Not Stable"
THUNDERSTORM = 4, // "Quickly falling L-Press", "Thunderstorm"
UNKNOWN = 5 // "Unknown (More Time needed)
};
Adafruit_BMP280 bmp; // Digital Pressure Sensor
float lastPressure = -1;
float lastTemp = -1;
int lastForecast = -1;
const int LAST_SAMPLES_COUNT = 5;
float lastPressureSamples[LAST_SAMPLES_COUNT];
// this CONVERSION_FACTOR is used to convert from Pa to kPa in forecast algorithm
// get kPa/h by dividing hPa by 10
#define CONVERSION_FACTOR (1.0/10.0)
int minuteCount = 0;
bool firstRound = true;
// average value is used in forecast algorithm.
float pressureAvg;
// average after 2 hours is used as reference value for the next iteration.
float pressureAvg2;
float dP_dt;
// Enable debug prints to serial monitor
// #define MY_DEBUG
// Enables and select radio type (if attached)
#define MY_RADIO_NRF24
//#define MY_RADIO_RFM69
#define MY_NODE_ID 41
#include <MySensors.h>
// Initialize motion message
MyMessage msgm(CHILD_ID_MOT, V_TRIPPED);
// Initialize temp baro and forecast messages
MyMessage msgit(CHILD_ID_ITEMP, V_TEMP);
MyMessage msgp(CHILD_ID_BARO, V_PRESSURE);
MyMessage forecastMsg(CHILD_ID_BARO, V_FORECAST);
MyMessage msgot(0, V_TEMP);
bool metric = true;
void before()
{
// Startup up the OneWire library
sensors.begin();
}
void setup()
{
pinMode(DIGITAL_INPUT_SENSOR, INPUT); // sets the motion sensor digital pin as input
pinMode(RED_LED, OUTPUT); // sets the pin for the LEDs as output
pinMode(RED_LED2, OUTPUT);
analogWrite(RED_LED, 0); // make sure LEDs are OFF
analogWrite(RED_LED2, 0);
bmp.begin();
if (!bmp.begin())
{
Serial.println("Could not find a valid BMP280 sensor, check wiring!");
while (1) {}
}
// requestTemperatures() will not block current thread
sensors.setWaitForConversion(false);
}
void presentation()
{
// Send the sketch version information to the gateway and Controller
sendSketchInfo("M. Sens_LED_2t/b/f 280", "2.2");
// Fetch the number of attached temperature sensors
numSensors = sensors.getDeviceCount();
// Register all sensors to gw (they will be created as child devices)
present(CHILD_ID_MOT, S_MOTION);
present(CHILD_ID_ITEMP, S_TEMP);
present(CHILD_ID_BARO, S_BARO);
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
present(i+CHILD_ID_OTEMP, S_TEMP);
}
}
void loop()
{
// Read digital motion value
boolean tripped = digitalRead(DIGITAL_INPUT_SENSOR) == HIGH;
Serial.println(tripped);
send(msgm.set(tripped?"1":"0")); // Send tripped value to gw
if (tripped) {
int brightness = 0; // brightness of LED
int count = 0; // number of half cycles to fade LED
while (count < 12) { // run 6 full fade cycles
analogWrite(RED_LED, brightness);
analogWrite(RED_LED2, brightness);
brightness = brightness + fadeAmount;
if (brightness == 0 || brightness == 255) {
fadeAmount = -fadeAmount; // change direction of fade
count ++;
}
wait (DELAY); //wait to see brightness change
}
analogWrite(RED_LED, 0); // turn off LEDs
analogWrite(RED_LED2, 0);
}
int long pressure = bmp.readPressure() / 100;
float temperature = bmp.readTemperature();
int forecast = sample(pressure);
send(msgit.set(temperature, 1));
send(msgp.set(pressure, 0));
send(forecastMsg.set(weather[forecast]));
// Fetch temperatures from Dallas sensors
sensors.requestTemperatures();
sleep (750);
// Read temperatures and send them to controller
for (int i=0; i<numSensors && i<MAX_ATTACHED_DS18B20; i++) {
// Fetch and round temperature to one decimal
float otemperature = static_cast<float>(static_cast<int>((sensors.getTempCByIndex(i)) * 10.)) / 10.;
// Only send data if no error
if (otemperature != -127.00 && otemperature != 85.00) {
// Send in the new temperature
send(msgot.setSensor(i+CHILD_ID_OTEMP).set(otemperature,1));
}
}
// Sleep until interrupt comes in on motion sensor but send update every minute.
tripped = false; //reset tripped
sleep(INTERRUPT,CHANGE, SLEEP_TIME);
}
float getLastPressureSamplesAverage()
{
float lastPressureSamplesAverage = 0;
for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
{
lastPressureSamplesAverage += lastPressureSamples[i];
}
lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
return lastPressureSamplesAverage;
}
// Algorithm found here
// http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
// Pressure in hPa --> forecast done by calculating kPa/h
int sample(float pressure)
{
// Calculate the average of the last n minutes.
int index = minuteCount % LAST_SAMPLES_COUNT;
lastPressureSamples[index] = pressure;
minuteCount++;
if (minuteCount > 185)
{
minuteCount = 6;
}
if (minuteCount == 5)
{
pressureAvg = getLastPressureSamplesAverage();
}
else if (minuteCount == 35)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change * 2; // note this is for t = 0.5hour
}
else
{
dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
}
}
else if (minuteCount == 65)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) //first time initial 3 hour
{
dP_dt = change; //note this is for t = 1 hour
}
else
{
dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
}
}
else if (minuteCount == 95)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 1.5; // note this is for t = 1.5 hour
}
else
{
dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
}
}
else if (minuteCount == 125)
{
float lastPressureAvg = getLastPressureSamplesAverage();
pressureAvg2 = lastPressureAvg; // store for later use.
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 2; // note this is for t = 2 hour
}
else
{
dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
}
}
else if (minuteCount == 155)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 2.5; // note this is for t = 2.5 hour
}
else
{
dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
}
}
else if (minuteCount == 185)
{
float lastPressureAvg = getLastPressureSamplesAverage();
float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
if (firstRound) // first time initial 3 hour
{
dP_dt = change / 3; // note this is for t = 3 hour
}
else
{
dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
}
pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
}
int forecast = UNKNOWN;
if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
{
forecast = UNKNOWN;
}
else if (dP_dt < (-0.25))
{
forecast = THUNDERSTORM;
}
else if (dP_dt > 0.25)
{
forecast = UNSTABLE;
}
else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
{
forecast = CLOUDY;
}
else if ((dP_dt > 0.05) && (dP_dt < 0.25))
{
forecast = SUNNY;
}
else if ((dP_dt >(-0.05)) && (dP_dt < 0.05))
{
forecast = STABLE;
}
else
{
forecast = UNKNOWN;
}
// uncomment when debugging
//Serial.print(F("Forecast at minute "));
//Serial.print(minuteCount);
//Serial.print(F(" dP/dt = "));
//Serial.print(dP_dt);
//Serial.print(F("kPa/h --> "));
//Serial.println(weather[forecast]);
return forecast;
}
and here are a couple of photos, front and back:
![alt text]( image url)DSC_0669 (1).jpg
![alt text]( image url)DSC_0668 (1).jpg