├── 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 | --------------------------------------------------------------------------------