Homeautomation_control - a simple, cost-effective home control system
Create a simple home automation system using Arduino and an Android phone.
COMPONENTS AND SUPPLIES
Arduino uno
Relay
Arduino eathernet
Why this project?
Everybody loves automation. Telling your home to turn the light on or enter your garage tapping on the screen of your phone are amazing stuff. At least, I think so.
That said, I decided to try to make my own system to control my home devices from my smartphone, without buying expensive third-party wifi adapter, smart bulbs or anythings like that; so, it had to be cheap, easy to use and had to be controlled from local and mobile internet connection. I know there are tons of excellent working projects out there (just to name some, Cayenne or the one by Anurag Vasanwala); anyway, I decided to make my own to fit perfectly my requirements. I focused my work on two main parts: the hardware setup and the development of a decent Android app.
Hardware setup
The “hardware” is composed by three main components: Arduino, an Ethernet adapter and a relay board. All the devices I need to control - which at the moment are are some lights, the car gate, the main door and the garage door - are hooked up to the relay board; the board is controlled by the Arduino, which is then connected via an Ethernet shield to my router. Additionally, I found a 2€ RFID reader, and decided to give it a try: it's now connected to the Arduino and allows me to open/close the gate using a RFID tag; unnecessary, but nice to have.
Completed hardware setup
Here is how the finished setup looks like; pretty clean I think!
Communication protocol
All the instructions provided by the user inside the app are sent to the specified Arduino IP using the UDP protocol; the received string is then parsed and the desired device activated. The Arduino private IP and the router public IP are stored inside the app database; it's up to the application to decide which one to use, depending on the currently connected network. Once the Arduino has received the input, it sends back to the phone a confirmation code, to let the user know if the command has been run properly. The app also allows the user to input the router public IP in the form of an URL string: you can generate a unique URL using a dynamic dns service (such as no-ip.com) and register it in the router; this way you'll have a (sort of) static IP address, useful in the case your internet service provider doesn't provide you one - like mine.
In defining the communication protocol between the app and Arduino, the following requirements had to be taken into
accounts: it had to tell Arduino which action to perform (turn on/off/trigger/simulate button pressure...) and on which pin and had to carry a unique id string to prevent malicious users to gain control of the house. The syntax I came up with is the following:
?MT=codecode!pin!mode!del!+!pin!mode!del!+
Here ‘MT’ identifies a multiple action - or scene - and ‘codecode’ the 8 chars unique code. After the 8 chars code the first command string is sent; in case of a multiple action, more strings are sent using the ‘+’ char as a divider.
Surely somebody out there could do this way better, so if you have any suggestions about this feel free to post them, I’d be more than happy to improve the code.
User interface - Android app
Designing the app was another important point: I wanted it to be fully customizable, aesthetically pleasant and intuitive, for my family was going to use it. After designingand coding it, this is what it looks like:
App user interface
Certainly not a professional one, but I’m quite happy with it!
The app provides:
- Single devices creation, like
- door;
- Scenes (or “group actions”) which will run a list of predefined actions, based on existing devices;
- Customizable vocal commands;
- Multiple Arduinos management;
Arduino code
The Arduino sketch code is pretty simple:
- in checkUDP() the connection is checked; if any data is available it looks for the auth code, and once it has found it, the remaining string is sent to the corresponding processing method, depending if it is a single action, a multiple action (or “scene”), a connection check request or a sensor reading request.
- in checkRFID() Arduino checks the RFID reader; if any card is found, the read code is compared to the authorized ones and, if it is found between them, the corresponding action is launched.
- homotica.refresh() tells the homotica
Arduino code
The Arduino sketch code is pretty simple:
- in checkUDP() the connection is checked; if any data is available it looks for the auth code, and once it has found it, the remaining string is sent to the corresponding processing method, depending if it is a single action, a multiple action (or “scene”), a connection check request or a sensor reading request.
- in checkRFID() Arduino checks the RFID reader; if any card is found, the read code is compared to the authorized ones and, if it is found between them, the corresponding action is launched.
- homotica.refresh() tells the homotica library to check if enough time has passed for a certain pin to be pulled high or low; this is set when the user send a “push” command; the library is available on Github (link at the bottom of the page).
Here is the sketch code; as I said, suggestions are the welcome!
//Sketch for Homotica Android App #include <Homotica.h> #include <Ethernet.h> #include <SPI.h> #include <RFID.h> #include <EthernetUdp.h> #define DEBUG_MODE #define SKETCH_NAME "HomoticaSketch" #define SKETCH_VERSION "1.0" #define ACTIVE_LOW //delete the line if you're using active_high setup #ifdef ACTIVE_LOW #define MHIGH 0x0 #define MLOW 0x1 #else #define MHIGH 0x1 #define MLOW 0x0 #endif #ifdef DEBUG_MODE #define DEBUG_PRINT(x) Serial.print(x) #define DEBUG_PRINTLN(x) Serial.println(x) #else #define DEBUG_PRINT(x) #define DEBUG_PRINTLN(x) #endif #define SDA_PIN 9 #define RESET_PIN 8 IPAddress ip(192, 168, 1, 20); byte mac[] = {0x90, 0xA2, 0xDA, 0x0E, 0xBD, 0x21}; char Data_RX; int PIN, del, charsIndex[4], relayMode; static int startingPIN = 4, finishPIN = 7; unsigned const int localPort = 80; String msg, request, recivedCode; static String code = "abcdefgh"; char packetBuffer[200]; const char wrongAuth[] = "2"; const char positiveResponse[] = "1"; const char negativeResponse[] = "0"; EthernetUDP Udp; Homotica homotica; RFID mRFID(SDA_PIN, RESET_PIN); int lastRFIDAction = 0; static String allowedCards[] = {"6C69A3CU89", "77698D5R23"}; static String openDoor = "!7!2!1000!"; static String closeDoor = "!6!2!1000!"; void setup() { Ethernet.begin(mac, ip); Udp.begin(localPort); SPI.begin(); mRFID.init(); Serial.begin(9600); DEBUG_PRINT("UDP is at "); DEBUG_PRINTLN(Ethernet.localIP()); DEBUG_PRINTLN(""); for (int i = startingPIN; i < finishPIN + 1; i++) { pinMode(i, OUTPUT); digitalWrite(i, MLOW); } } void loop() { checkUDP(); checkRFID(); homotica.refresh(); } void checkUDP() { msg = ""; int packetSize = Udp.parsePacket(); if (packetSize) { DEBUG_PRINTLN(); DEBUG_PRINTLN("********************NEW INCOMING PACKET**************************"); Udp.read(packetBuffer, 200); msg = packetBuffer; recivedCode = msg.substring(msg.indexOf("=") + 1 , msg.indexOf("!")); request = msg.substring(msg.indexOf("?") + 1 , msg.indexOf("=")); if (recivedCode == code) { Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); String myString = msg.substring(msg.indexOf("!")); DEBUG_PRINT("Recived: "); DEBUG_PRINTLN(msg); DEBUG_PRINT("Substring: "); DEBUG_PRINTLN(myString); if (request == "QU") { charsIndex[0] = msg.indexOf("!"); charsIndex[1] = msg.indexOf("!", charsIndex[0] + 1); PIN = msg.substring(charsIndex[0] + 1 , charsIndex[1]).toInt(); String sensorReading = String(analogRead(PIN)); char output[sensorReading.length() + 1]; sensorReading.toCharArray(output, sensorReading.length() + 1); Udp.write(output); } else if (request == "ST") { Udp.write(positiveResponse); Udp.endPacket(); processSingleRunnable(myString); } else if (request == "MT") { Udp.write(positiveResponse); Udp.endPacket(); processMultipleRunnable(myString); } else if (request == "CH") { Udp.write(positiveResponse); Udp.endPacket(); } else { Udp.write(negativeResponse); Udp.endPacket(); } } else{ Udp.write(wrongAuth); Udp.endPacket(); } for ( int i = 0; i < sizeof(packetBuffer); ++i ) { packetBuffer[i] = (char)0; } } delay(10); } void checkRFID() { if (mRFID.isCard()) { mRFID.readCardSerial(); String code = ""; for (byte i = 0; i <= 4; i++) { code += String (mRFID.serNum[i], HEX); code.toUpperCase(); } DEBUG_PRINTLN("Read code:"); DEBUG_PRINTLN(code); DEBUG_PRINTLN(); for (String allowedCode : allowedCards) { if (allowedCode == code) { if (lastRFIDAction == 0) { //checks if the door has been closed or opened the last time processSingleRunnable(openDoor); lastRFIDAction = 1; } else if (lastRFIDAction == 1) { processSingleRunnable(closeDoor); lastRFIDAction = 0; } } } delay(2000); } } void processSingleRunnable(String msg) { charsIndex[0] = msg.indexOf("!"); charsIndex[1] = msg.indexOf("!", charsIndex[0] + 1); charsIndex[2] = msg.indexOf("!", charsIndex[1] + 1); charsIndex[3] = msg.indexOf("!", charsIndex[2] + 1); PIN = msg.substring(charsIndex[0] + 1 , charsIndex[1]).toInt(); relayMode = msg.substring(charsIndex[1] + 1, charsIndex[2]).toInt(); del = msg.substring(charsIndex[2] + 1, charsIndex[3]).toInt(); DEBUG_PRINTLN(); DEBUG_PRINTLN("- OPENING -"); DEBUG_PRINT("Pin: "); DEBUG_PRINT(String(PIN)); DEBUG_PRINT("; Mode: "); DEBUG_PRINT(String(relayMode)); DEBUG_PRINT("; Delay: "); DEBUG_PRINTLN(msg.substring(charsIndex[2] + 1, charsIndex[3])); DEBUG_PRINTLN("(0 --> ON, 1 --> OFF, 2 --> PUSH, 3 --> TOGGLE)"); if (0 <= relayMode <= 2 && startingPIN <= PIN <= finishPIN) { if (relayMode == 0) { digitalWrite(PIN, MHIGH); //relay on } else if (relayMode == 1) { digitalWrite(PIN, MLOW); } else if (relayMode == 2) { homotica.pushPin(PIN, del, digitalRead(PIN)); } else if (relayMode == 3) { digitalWrite(PIN, !digitalRead(PIN)); } } } void processMultipleRunnable(String msg) { char input[msg.length() + 1]; msg.toCharArray(input, msg.length() + 1); const char* divider = ("+"); int lastIndex = 0; for (int k = 0; k < sizeof(input); k++) { if (String(input[k]) == "+") { processSingleRunnable(msg.substring(lastIndex, k)); k += 1; lastIndex = k; } } }
- Comments(0)
- Likes(1)