├── README.md
└── SecuritySensor4ToMQTT.ino
/README.md:
--------------------------------------------------------------------------------
1 | # Security Sensor (4-channel) to MQTT
2 |
3 | Developed by [SuperHouse Automation Pty Ltd](http://www.superhouse.tv/)
4 |
5 | ## Description
6 |
7 | Reads the state of up to 4 security sensors using End-of-Line resistors connected to analog inputs, and publishes state changes to an MQTT server.
8 |
9 | Can optionally display events locally using a Freetronics 128x128 colour OLED module.
10 |
11 | See more at http://www.superhouse.tv/
12 |
13 | ## Required Libraries
14 |
15 | 1. [PubSubClient.h](https://github.com/knolleary/pubsubclient)
16 |
17 | 2. [FTOLED.h](https://github.com/freetronics/FTOLED) NOTE: Only required when using the OLED display option.
18 |
19 | ## Installation
20 |
21 | 1. Change directory to Arduino's main directory
22 | 2. mkdir SecuritySensor4ToMQTT
23 | 3. cd SecuritySensor4ToMQTT
24 | 4. git clone https://github.com/superhouse/SecuritySensor4ToMQTT.git .
25 | 5. Start Arduino IDE.
26 | 6. Go to File--->Sketchbook--->SecuritySensor4ToMQTT
27 |
28 | ## License
29 | Copyright (C) 2015 SuperHouse Automation Pty Ltd
30 |
31 | This program is free software: you can redistribute it and/or modify
32 | it under the terms of the GNU General Public License as published by
33 | the Free Software Foundation, either version 3 of the License, or
34 | (at your option) any later version.
35 |
36 | This program is distributed in the hope that it will be useful,
37 | but WITHOUT ANY WARRANTY; without even the implied warranty of
38 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 | GNU General Public License for more details.
40 |
41 | You should have received a copy of the GNU General Public License
42 | along with this program. If not, see .
43 |
--------------------------------------------------------------------------------
/SecuritySensor4ToMQTT.ino:
--------------------------------------------------------------------------------
1 | /**
2 | Security Sensor (4 channel) to MQTT
3 |
4 | Copyright 2015 SuperHouse Automation Pty Ltd
5 |
6 | This program is free software: you can redistribute it and/or modify
7 | it under the terms of the GNU General Public License as published by
8 | the Free Software Foundation, either version 3 of the License, or
9 | (at your option) any later version.
10 |
11 | This program is distributed in the hope that it will be useful,
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 | GNU General Public License for more details.
15 |
16 | You should have received a copy of the GNU General Public License
17 | along with this program. If not, see .
18 | */
19 | /*--------------------------- Configuration ------------------------------*/
20 | /* Network config */
21 | #define ENABLE_DHCP true // true/false
22 | #define ENABLE_MAC_ADDRESS_ROM true // true/false
23 | #define MAC_I2C_ADDRESS 0x50 // Microchip 24AA125E48 I2C ROM address
24 | static uint8_t mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; // Set if no MAC ROM
25 | static uint8_t ip[] = { 192, 168, 1, 35 }; // Use if DHCP disabled
26 |
27 | /* Display config */
28 | #define ENABLE_OLED false // true/false. Enable if you have a Freetronics OLED128 connected
29 | const long oled_timeout = 20; // Seconds before screen blanks after last activity
30 |
31 | // Panel-specific configuration:
32 | int panelId = 15; // A unique identifier for this panel
33 |
34 | const long seconds_start_delay = 60; // Pause during startup to let PIRs settle down
35 |
36 | /*------------------------------------------------------------------------*/
37 |
38 | #include
39 | #include "Ethernet.h"
40 | #include
41 |
42 | /* Required for OLED */
43 | #include "Wire.h"
44 | #include
45 | #include
46 | #include
47 |
48 | #define STATE_SHORT 0
49 | #define STATE_NORMAL 1
50 | #define STATE_TAMPER 2
51 | #define STATE_ALARM 3
52 | #define STATE_ALARM_TAMPER 4
53 | #define STATE_CUT 5
54 | #define STATE_UNKNOWN 6
55 |
56 | /* Analog readings corresponding to different sensor states */
57 | struct input_ranges {
58 | int sensor_state;
59 | const String label;
60 | int range_bottom;
61 | int range_optimum;
62 | int range_top;
63 | };
64 | const input_ranges ranges[] {
65 | { STATE_SHORT, "Shorted", 0, 0, 162 }, // 0
66 | { STATE_NORMAL, "Normal", 163, 326, 409 }, // 1
67 | { STATE_TAMPER, "Tamper", 410, 495, 551 }, // 2
68 | { STATE_ALARM, "Alarm", 552, 609, 641 }, // 3
69 | { STATE_ALARM_TAMPER, "Alarm+Tamper", 642, 675, 848 }, // 4
70 | { STATE_CUT, "Cut", 849, 1023, 1023 }, // 5
71 | { STATE_UNKNOWN, "Unknown", 1024, 1024, 1024 }, // 6. Range beyond analog in because this is never read
72 | };
73 |
74 | /* Connected sensors */
75 | struct sensor {
76 | const String label;
77 | byte analog_input;
78 | byte status_output;
79 | byte last_state;
80 | };
81 | sensor sensors[] {
82 | { "A", 0, 4, STATE_UNKNOWN },
83 | { "B", 1, 5, STATE_UNKNOWN },
84 | { "C", 2, 6, STATE_UNKNOWN },
85 | { "D", 3, 7, STATE_UNKNOWN },
86 | };
87 |
88 | /* Use 128x128 pixel OLED module ("OLED128") to display status */
89 | const byte pin_cs = 48;
90 | const byte pin_dc = 49;
91 | const byte pin_reset = 53;
92 | OLED oled(pin_cs, pin_dc, pin_reset);
93 | OLED_TextBox box(oled);
94 | long lastActivityTime = 0;
95 |
96 | byte server[] = { 192, 168, 1, 111 }; // MQTT server
97 |
98 | char messageBuffer[100];
99 | char topicBuffer[100];
100 | char clientBuffer[20];
101 |
102 | /**
103 | */
104 | void callback(
105 | char* topic,
106 | byte* payload,
107 | int length) {
108 |
109 | Serial.print("Received: ");
110 | for (int index = 0; index < length; index ++) {
111 | Serial.print(payload[index]);
112 | }
113 | Serial.println();
114 | }
115 |
116 | // Instantiate MQTT client
117 | PubSubClient client(server, 1883, callback);
118 |
119 |
120 | /**
121 | * Initial configuration
122 | */
123 | void setup()
124 | {
125 | if ( ENABLE_OLED == true )
126 | {
127 | oled.begin();
128 | oled.selectFont(SystemFont5x7);
129 | box.setForegroundColour(DODGERBLUE);
130 |
131 | box.println(F(" SuperHouse.TV "));
132 | box.println(F(" Security Sensor v1.0 "));
133 | box.println(F("Getting MAC address: "));
134 | box.print (F(" "));
135 | }
136 |
137 | if (seconds_start_delay > 0)
138 | {
139 | Serial.print("Pausing ");
140 | Serial.print( seconds_start_delay );
141 | Serial.println(" seconds to let sensors settle");
142 |
143 | if ( ENABLE_OLED == true )
144 | {
145 | box.print(F("Pausing "));
146 | box.print(seconds_start_delay, DEC);
147 | box.println(" seconds");
148 | box.println("to let sensors settle");
149 | }
150 |
151 | delay(seconds_start_delay * 1000);
152 | }
153 |
154 | Wire.begin(); // Wake up I2C bus
155 | Serial.begin(9600); // Use the serial port to report back readings
156 |
157 | if ( ENABLE_MAC_ADDRESS_ROM == true )
158 | {
159 | Serial.print(F("Getting MAC address from ROM: "));
160 | mac[0] = readRegister(0xFA);
161 | mac[1] = readRegister(0xFB);
162 | mac[2] = readRegister(0xFC);
163 | mac[3] = readRegister(0xFD);
164 | mac[4] = readRegister(0xFE);
165 | mac[5] = readRegister(0xFF);
166 | } else {
167 | Serial.print(F("Using static MAC address: "));
168 | }
169 | // Print the IP address
170 | char tmpBuf[17];
171 | sprintf(tmpBuf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
172 | Serial.println(tmpBuf);
173 |
174 | if ( ENABLE_OLED == true )
175 | {
176 | box.println(tmpBuf);
177 | }
178 |
179 | // setup the Ethernet library to talk to the Wiznet board
180 | if ( ENABLE_DHCP == true )
181 | {
182 | Ethernet.begin(mac); // Use DHCP
183 | } else {
184 | Ethernet.begin(mac, ip); // Use static address defined above
185 | }
186 |
187 | // Print IP address:
188 | Serial.print(F("My IP: http://"));
189 | for (byte thisByte = 0; thisByte < 4; thisByte++) {
190 | // print the value of each byte of the IP address:
191 | Serial.print(Ethernet.localIP()[thisByte], DEC);
192 | if ( thisByte < 3 )
193 | {
194 | Serial.print(".");
195 | }
196 | }
197 | Serial.println();
198 |
199 | if ( ENABLE_OLED == true )
200 | {
201 | box.println(F("My IP:"));
202 | box.print(" ");
203 | for (byte thisByte = 0; thisByte < 4; thisByte++) {
204 | // print the value of each byte of the IP address:
205 | box.print(Ethernet.localIP()[thisByte], DEC);
206 | if ( thisByte < 3 )
207 | {
208 | box.print(".");
209 | }
210 | }
211 | box.println();
212 | }
213 |
214 | String clientString = "Arduino-" + String(Ethernet.localIP());
215 | clientString.toCharArray(clientBuffer, clientString.length() + 1);
216 | if (client.connect(clientBuffer)) {
217 | Serial.println("MQTT connected");
218 | client.publish("events", "Starting up");
219 | //client.subscribe("test/2");
220 | }
221 |
222 | Serial.println("Ready.");
223 | }
224 |
225 |
226 | /**
227 | * Main program loop
228 | */
229 | void loop()
230 | {
231 | if ( ENABLE_OLED == true )
232 | {
233 | if ( millis() > (lastActivityTime + (1000 * oled_timeout)))
234 | {
235 | oled.setDisplayOn(false);
236 | }
237 | }
238 |
239 | client.loop();
240 | byte i;
241 | for ( i = 0; i < 4; i++) {
242 | processSensor( i );
243 | }
244 | }
245 |
246 |
247 | /**
248 | */
249 | void processSensor( byte sensorId )
250 | {
251 | int sensorReading = analogRead( sensors[sensorId].analog_input );
252 | //Serial.print(sensorId, DEC);
253 | //Serial.print(": ");
254 | //Serial.println(sensorReading, DEC);
255 |
256 | byte sensorState = STATE_UNKNOWN;
257 |
258 | // This should check all entries in the states struct:
259 | if ( (sensorReading >= ranges[STATE_SHORT].range_bottom) && (sensorReading <= ranges[STATE_SHORT].range_top) ) {
260 | sensorState = STATE_SHORT;
261 | } else if ( (sensorReading >= ranges[STATE_NORMAL].range_bottom) && (sensorReading <= ranges[STATE_NORMAL].range_top) ) {
262 | sensorState = STATE_NORMAL;
263 | } else if ( (sensorReading >= ranges[STATE_TAMPER].range_bottom) && (sensorReading <= ranges[STATE_TAMPER].range_top) ) {
264 | sensorState = STATE_TAMPER;
265 | } else if ( (sensorReading >= ranges[STATE_ALARM].range_bottom) && (sensorReading <= ranges[STATE_ALARM].range_top) ) {
266 | sensorState = STATE_ALARM;
267 | } else if ( (sensorReading >= ranges[STATE_ALARM_TAMPER].range_bottom) && (sensorReading <= ranges[STATE_ALARM_TAMPER].range_top) ) {
268 | sensorState = STATE_ALARM_TAMPER;
269 | } else if ( (sensorReading >= ranges[STATE_CUT].range_bottom) && (sensorReading <= ranges[STATE_CUT].range_top) ) {
270 | sensorState = STATE_CUT;
271 | } else {
272 | sensorState = STATE_UNKNOWN;
273 | }
274 |
275 | // Compare to previous value
276 | if ( sensorState != sensors[sensorId].last_state )
277 | {
278 | // It changed!
279 | byte lastSensorState = sensors[sensorId].last_state;
280 | Serial.print("Sensor ");
281 | Serial.print(sensorId);
282 | Serial.print(" changed from ");
283 | Serial.print(ranges[lastSensorState].label);
284 | Serial.print(" to ");
285 | Serial.println(ranges[sensorState].label);
286 | sensors[sensorId].last_state = sensorState;
287 | String messageString = String(panelId) + '-' + sensors[sensorId].label + '-' + String(sensorState);
288 |
289 | Serial.println(messageString);
290 |
291 | messageString.toCharArray(messageBuffer, messageString.length() + 1);
292 | String topicString = "sensors";
293 | topicString.toCharArray(topicBuffer, topicString.length() + 1);
294 | client.publish("sensors", messageBuffer);
295 |
296 | if ( ENABLE_OLED == true )
297 | {
298 | box.setForegroundColour(LIMEGREEN);
299 | box.print(F("Event: "));
300 | box.println(messageString);
301 | }
302 | }
303 | }
304 |
305 |
306 |
307 | /**
308 | * Required to read the MAC address ROM
309 | */
310 | byte readRegister(byte r)
311 | {
312 | unsigned char v;
313 | Wire.beginTransmission(MAC_I2C_ADDRESS);
314 | Wire.write(r); // Register to read
315 | Wire.endTransmission();
316 |
317 | Wire.requestFrom(MAC_I2C_ADDRESS, 1); // Read a byte
318 | while (!Wire.available())
319 | {
320 | // Wait
321 | }
322 | v = Wire.read();
323 | return v;
324 | }
325 |
--------------------------------------------------------------------------------