Aimar N.
Published © GPL3+

Vacation Automation

Entry for the Microsoft and Hackster home automation contest

AdvancedWork in progress2,520
Vacation Automation

Things used in this project

Hardware components

1N4001 diode
×1
Resistor 10k ohm
Resistor 10k ohm
×1
2N2222 Transistor
×1
12 Volt Relay
×1
Assorted Wires
×1
Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
×1
Adafruit HUZZAH ESP8266 Breakout
Adafruit HUZZAH ESP8266 Breakout
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Wire Stripper

Story

Read more

Schematics

Relay circuit

This circuit is what triggers the relay to pass the 120 volts from your outlet to your light. We use an ESP8266 in place of an Arduino, so we can connect it to the Raspberry Pi

Code

Raspberry Pi code

C#
Code (1/2), this code is the interface for the user and what connects to the ESP8266. Universal Windows Application programmed in C sharp, runs on the Pi's Windows 10 IoT Core.
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using SDKTemplate;
using System;
using System.Collections.Generic;
using Windows.ApplicationModel.Core;
using Windows.Networking;
using Windows.Networking.Connectivity;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace HomeAutomationProject
{
    /// <summary>
    /// A page for fifth scenario.
    /// </summary>
    public sealed partial class Scenario5 : Page
    {
        // A pointer back to the main page.  This is needed if you want to call methods in MainPage such
        // as NotifyUser()
        private MainPage rootPage = MainPage.Current;
        
        private DatagramSocket listenerSocket = null;

        public Scenario5()
        {
            this.InitializeComponent();
        }

        private void CloseListenerSocket()
        {
            if (listenerSocket != null)
            {
                // DatagramSocket.Close() is exposed through the Dispose() method in C#.
                // The call below explicitly closes the socket, freeing the UDP port that it is currently bound to.
                listenerSocket.Dispose();
                listenerSocket = null;
            }
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // Only Broadcast Scenario will be used for Vacation Automation.
            // I leave the Multicast option in the code for future enhancements.
            // MulticastRadioButton.IsChecked = true;
            SetupBroadcastScenarioUI();
        }

        /// <summary>
        /// Invoked immediately before the Page is unloaded and is no longer the current source of a parent Frame.
        /// </summary>
        /// <param name="e">Event data representative of the navigation that will unload the current Page.</param>
        protected override void OnNavigatingFrom(NavigatingCancelEventArgs e)
        {
            CloseListenerSocket();
        }

        // Sets up the UI to display the multicast scenario options.
        private void SetupMulticastScenarioUI()
        {
            RemoteAddressLabel.Text = "Multicast Group:";
            StartListenerButton.Content = "Start listener and join multicast group";
            RemoteAddress.Text = "224.3.0.5";
            RemoteAddress.IsEnabled = false;
            StartListenerButton.IsEnabled = true;
            SendMessageButton.IsEnabled = false;
            CloseListenerButton.IsEnabled = false;
            SendOutput.Text = "";
        }

        // Sets up the UI to display the broadcast scenario options.
        private void SetupBroadcastScenarioUI()
        {
            RemoteAddressLabel.Text = "Broadcast Address:";
            StartListenerButton.Content = "Start Main Application";
            SendMessageButton.Content = "Send Message";
            CloseListenerButton.Content = "Close Main Application";
            RemoteAddress.Text = "255.255.255.255";
            TextToSend.Text = "Please choose sequence...";
            ServiceName.IsEnabled = false;
            RemoteAddress.IsEnabled = false;
            TextToSend.IsEnabled = false;
            StartListenerButton.IsEnabled = true;
            Sequence1RadioButton.IsEnabled = false;
            Sequence2RadioButton.IsEnabled = false;
            Sequence3RadioButton.IsEnabled = false;
            AllOffRadioButton.IsEnabled = false;
            AllOnRadioButton.IsEnabled = false;
            FullyAutomaticRadioButton.IsEnabled = false;
            SendMessageButton.IsEnabled = false;
            CloseListenerButton.IsEnabled = false;
            SendOutput.Text = "";
            ServiceName.IsReadOnly = true;
            RemoteAddress.IsReadOnly = true;
            TextToSend.IsReadOnly = true;
        }

        // private void MulticastRadioButton_Checked(object sender, RoutedEventArgs e)
        // {
        //     CloseListenerSocket();
        //     SetupMulticastScenarioUI();
        // }

        // private void MulticastRadioButton_Unchecked(object sender, RoutedEventArgs e)
        // {
        //     CloseListenerSocket();
        //     SetupBroadcastScenarioUI();
        // }

        private void Sequence1RadioButton_Checked(object sender, RoutedEventArgs e)
        {
            TextToSend.Text = "Run Sequence 1";
            {
                rootPage.NotifyUser(
                    "Sequence 1 has been selected by the user.",
                    NotifyType.StatusMessage);
            }
        }

        private void Sequence2RadioButton_Checked(object sender, RoutedEventArgs e)
        {
            TextToSend.Text = "Run Sequence 2";
            {
                rootPage.NotifyUser(
                    "Sequence 2 has been selected by the user.",
                    NotifyType.StatusMessage);
            }
        }

        private void Sequence3RadioButton_Checked(object sender, RoutedEventArgs e)
        {
            TextToSend.Text = "Run Sequence 3";
            {
                rootPage.NotifyUser(
                    "Sequence 3 has been selected by the user.",
                    NotifyType.StatusMessage);
            }
        }

        private void FullyAutomaticRadioButton_Checked(object sender, RoutedEventArgs e)
        {
            TextToSend.Text = "Automatic Mode";
            {
                rootPage.NotifyUser(
                    "Fully automatic mode has been selected by the user.",
                    NotifyType.StatusMessage);
            }
        }

        private void AllOffRadioButton_Checked(object sender, RoutedEventArgs e)
        {
            TextToSend.Text = "All OFF";
            {
                rootPage.NotifyUser(
                    "All automation equipment has been turned off by the user.",
                    NotifyType.StatusMessage);
            }
        }
        private void AllOnRadioButton_Checked(object sender, RoutedEventArgs e)
        {
            TextToSend.Text = "All ON";
            {
                rootPage.NotifyUser(
                    "All automation equipment has been turned on by the user.",
                    NotifyType.StatusMessage);
            }
        }

        /// <summary>
        /// This is the click handler for the 'StartListener' button.
        /// </summary>
        /// <param name="sender">Object for which the event was generated.</param>
        /// <param name="e">Event's parameters.</param>
        private async void StartListener_Click(object sender, RoutedEventArgs e)
        {
            if (String.IsNullOrEmpty(ServiceName.Text))
            {
                rootPage.NotifyUser("Please provide a service name.", NotifyType.ErrorMessage);
                return;
            }

            if (listenerSocket != null)
            {
                rootPage.NotifyUser("A listener socket is already set up.", NotifyType.ErrorMessage);
                return;
            }

            bool isMulticastSocket = false;
            listenerSocket = new DatagramSocket();
            listenerSocket.MessageReceived += MessageReceived;

            if (isMulticastSocket)
            {
                // DatagramSockets conduct exclusive (SO_EXCLUSIVEADDRUSE) binds by default, effectively blocking
                // any other UDP socket on the system from binding to the same local port. This is done to prevent
                // other applications from eavesdropping or hijacking a DatagramSocket's unicast traffic.
                //
                // Setting the MulticastOnly control option to 'true' enables a DatagramSocket instance to share its
                // local port with any Win32 sockets that are bound using SO_REUSEADDR/SO_REUSE_MULTICASTPORT and
                // with any other DatagramSocket instances that have MulticastOnly set to true. However, note that any
                // attempt to use a multicast-only DatagramSocket instance to send or receive unicast data will result
                // in an exception being thrown.
                //
                // This control option is particularly useful when implementing a well-known multicast-based protocol,
                // such as mDNS and UPnP, since it enables a DatagramSocket instance to coexist with other applications
                // running on the system that also implement that protocol.
                listenerSocket.Control.MulticastOnly = true;
            }

            // Start listen operation.
            try
            {
                await listenerSocket.BindServiceNameAsync(ServiceName.Text);

                if (isMulticastSocket)
                {
                    // Join the multicast group to start receiving datagrams being sent to that group.
                    listenerSocket.JoinMulticastGroup(new HostName(RemoteAddress.Text));

                    rootPage.NotifyUser(
                        "Listening on port " + listenerSocket.Information.LocalPort + " and joined to multicast group",
                        NotifyType.StatusMessage);
                }
                else
                {
                    rootPage.NotifyUser(
                        "Main application has started, and it is listening on port " + listenerSocket.Information.LocalPort + ".",
                        NotifyType.StatusMessage);
                }

                // Disable StartListenerButton after active listening socket has been created.
                StartListenerButton.IsEnabled = false;
                // Enable scenario steps that require us to have an active listening socket.
                Sequence1RadioButton.IsEnabled = true;
                Sequence2RadioButton.IsEnabled = true;
                Sequence3RadioButton.IsEnabled = true;
                AllOffRadioButton.IsEnabled = true;
                AllOnRadioButton.IsEnabled = true;
                FullyAutomaticRadioButton.IsEnabled = true;
                SendMessageButton.IsEnabled = true;
                CloseListenerButton.IsEnabled = true;
            }
            catch (Exception exception)
            {
                listenerSocket.Dispose();
                listenerSocket = null;

                // If this is an unknown status it means that the error is fatal and retry will likely fail.
                if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
                {
                    throw;
                }

                rootPage.NotifyUser(
                    "Start listening failed with error: " + exception.Message,
                    NotifyType.ErrorMessage);
            }
        }

        /// <summary>
        /// This is the click handler for the 'SendMessage' button.
        /// </summary>
        /// <param name="sender">Object for which the event was generated.</param>
        /// <param name="e">Event's parameters.</param>
        private async void SendMessage_Click(object sender, RoutedEventArgs e)
        {
            SendOutput.Text = "";

            try
            {
                IOutputStream outputStream;
                HostName remoteHostname = new HostName(RemoteAddress.Text);

                // GetOutputStreamAsync can be called multiple times on a single DatagramSocket instance to obtain
                // IOutputStreams pointing to various different remote endpoints. The remote hostname given to
                // GetOutputStreamAsync can be a unicast, multicast or broadcast address.
                outputStream = await listenerSocket.GetOutputStreamAsync(remoteHostname, ServiceName.Text);

                // Send out some multicast or broadcast data. Datagrams generated by the IOutputStream will use
                // <source host, source port> information obtained from the parent socket (i.e., 'listenSocket' in
                // this case).

                string stringToSend = TextToSend.Text;
                DataWriter writer = new DataWriter(outputStream);
                writer.WriteString(stringToSend);
                await writer.StoreAsync();

                SendOutput.Text = "\"" + stringToSend + "\" sent successfully.";
            }
            catch (Exception exception)
            {
                // If this is an unknown status it means that the error is fatal and retry will likely fail.
                if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
                {
                    throw;
                }

                rootPage.NotifyUser("Send failed with error: " + exception.Message, NotifyType.ErrorMessage);
            }
        }

        /// <summary>
        /// This is the click handler for the 'CloseListener' button.
        /// </summary>
        /// <param name="sender">Object for which the event was generated.</param>
        /// <param name="e">Event's parameters.</param>
        private void CloseListener_Click(object sender, RoutedEventArgs e)
        {
            CloseListenerSocket();

            // Enable StartListenerButton after active listening socket has been closed.
            StartListenerButton.IsEnabled = true;

            // Disable scenario steps that require us to have an active listening socket.
            Sequence1RadioButton.IsEnabled = false;
            Sequence2RadioButton.IsEnabled = false;
            Sequence3RadioButton.IsEnabled = false;
            AllOffRadioButton.IsEnabled = false;
            AllOnRadioButton.IsEnabled = false;
            FullyAutomaticRadioButton.IsEnabled = false;
            SendMessageButton.IsEnabled = false;
            CloseListenerButton.IsEnabled = false;
            SendOutput.Text = "";

            rootPage.NotifyUser("Main Application closed", NotifyType.StatusMessage);
        }

        /// <summary>
        /// Message received handler
        /// </summary>
        /// <param name="socket">The socket object</param>
        /// <param name="eventArguments">The datagram event information</param>
        void MessageReceived(DatagramSocket socket, DatagramSocketMessageReceivedEventArgs eventArguments)
        {
            try
            {
                // Interpret the incoming datagram's entire contents as a string.
                uint stringLength = eventArguments.GetDataReader().UnconsumedBufferLength;
                string receivedMessage = eventArguments.GetDataReader().ReadString(stringLength);

                NotifyUserFromAsyncThread(
                    "Received data from remote peer (Remote Address: " +
                    eventArguments.RemoteAddress.CanonicalName +
                    ", Remote Port: " +
                    eventArguments.RemotePort + "): \"" +
                     receivedMessage + "\"",
                    NotifyType.StatusMessage);
            }
            catch (Exception exception)
            {
                SocketErrorStatus socketError = SocketError.GetStatus(exception.HResult);

                if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
                {
                    throw;
                }

                rootPage.NotifyUser(
                    "Error happened when receiving a datagram:" + exception.Message,
                    NotifyType.ErrorMessage);
            }
        }

        /// <summary>
        /// Notifies the user from a non-UI thread
        /// </summary>
        /// <param name="strMessage">The message</param>
        /// <param name="type">The type of notification</param>
        private void NotifyUserFromAsyncThread(string strMessage, NotifyType type)
        {
            var ignore = Dispatcher.RunAsync(
                CoreDispatcherPriority.Normal, () => rootPage.NotifyUser(strMessage, type));
        }

        private void TextBlock_SelectionChanged(object sender, RoutedEventArgs e)
        {

        }
    }
}

ESP8266 Code

C/C++
Code (2/2), This is the code uploaded to the ESP8266 internet module, this is what allows the project to be connected to the internet. Arduino IDE
/*
  WiFi UDP Send and Receive String

 This sketch wait an UDP packet on localPort using a WiFi ESP8266 module.
 When a packet is received an Acknowledge packet is sent to the client on port remotePort

 */

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <SPI.h>
#include <WiFiUdp.h>

    int sensorPin = 5;    // select the input pin for the light sensor
    int ledPin = 4;      // select the pin for the LED
//  int sensorValue = 0;  // variable to store the value coming from the sensor

    float sensorValue;

    // It indicates if LED must be on or off.
    boolean NewRequestedOutputState = LOW;
    
    
    String UDPString="";     // String to send via udp
    String StringPacketBuffer="";
    
    // We need a character array to compare.
    String Sequence01="Run Sequence 1";
    String Sequence02="Run Sequence 2";
    String Sequence03="Run Sequence 3";
    String AllOFF="All OFF";
    String AllON="All ON";
    String AutomaticMode="Automatic Mode";
    
    char CharSequence01[50];
    char CharSequence02[50];
    char CharSequence03[50];

    // Network keys... Put in your internet name and password here
    const char* ssid     = "xxxxxxxxxx";
    const char* password = "xxxxxxxxxx";

    int counter = 0;

    char myCharArray[16] = "Acknowledged # ";

    int status = WL_IDLE_STATUS;
    int keyIndex = 0;            // your network key Index number (needed only for WEP)

    unsigned int localPort = 11000;      // local port to listen on

    char packetBuffer[255]; //buffer to hold incoming packet
    char ReplyBuffer[16] = "acknowledged";       // a string to send back
    char charBuf[50]; // It sends the response to the Raspberry Pi.
    
    WiFiUDP Udp;

// =========================
//   Start "setup" section
// =========================

void setup() {
  
    pinMode(ledPin, OUTPUT);
    
// Initialize serial and wait for port to open:

    Serial.begin(115200);
    
// Connect to WiFi network using static IP set in variable "ip".
  
    WiFi.begin(ssid, password);
    
    Serial.print("\n\r \n\rWorking to connect to Home Network. Please, be patient.");
 
// attempt to connect to Wifi WPA/WPA2 network:

    while ( WiFi.status() != WL_CONNECTED) 
    {
      Serial.print("Attempting to connect to SSID: ");
      Serial.println(ssid);
      delay(500);
    }

    Serial.println("Connected to wifi");
    printWifiStatus();

    Serial.println("\nStarting connection to server...");
    
// if you get a connection, report back via serial:
    Udp.begin(localPort);

    Serial.println("Connection to server supposedly achieved");
}

// =======================
//   End "setup" section
// =======================


// ========================
//   Start "loop" section
// ========================

void loop() {

   // If there's data available, read a packet
   int packetSize = Udp.parsePacket();
  
  if (packetSize)
  {
    Serial.print("Received packet of size ");
    Serial.println(packetSize);
    Serial.print("From ");
    IPAddress remoteIp = Udp.remoteIP();
    Serial.print(remoteIp);
    Serial.print(", port ");
    Serial.println(Udp.remotePort());

    // read the packet into packetBufffer
    int len = Udp.read(packetBuffer, 255);
    if (len > 0) packetBuffer[len] = 0;
      Serial.println("Contents:");
      Serial.println(packetBuffer);

    // Concert "array" to "String" for comparison purposes.

    StringPacketBuffer = String(packetBuffer);
    Serial.println("PacketBuffer en modo string:");
    Serial.println(StringPacketBuffer);
    Serial.println(StringPacketBuffer.length());
    Serial.println("Variable Sequence01:");
    Serial.println(Sequence01.length());

    // Convert "string" to "array" for comparison purposes.
    Sequence01.toCharArray(CharSequence01, 50);
    Sequence02.toCharArray(CharSequence02, 50);
    Sequence03.toCharArray(CharSequence03, 50);



      // For debugging purposes:
      //Serial.println(CharSequence01);
      //Serial.println(CharSequence02);
      //Serial.println(CharSequence03);

    // Axel. Check the value received from the Raspberry Pi to see what sequence needs to be run.

    if (StringPacketBuffer == Sequence03 || StringPacketBuffer == AllON)
    {
      NewRequestedOutputState = HIGH;
      Serial.println(NewRequestedOutputState);
      SetDigitalOutputState(ledPin, NewRequestedOutputState);



          // Get the light value
    getlight();           // read sensor
    UDPString= "Relay 3 is ON..."+String((int)sensorValue);
   
    UDPString.toCharArray(charBuf, 50);


    // send a reply, to the IP address and port that sent us the packet we received
    // ReplyBuffer = myCharArray;
    Serial.println(UDPString);
    Serial.println(ReplyBuffer);
    Serial.println(charBuf);
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
    // Udp.write(ReplyBuffer);
    Udp.write(charBuf);
    Udp.endPacket();
    

    }
    else if (StringPacketBuffer == Sequence01)
    {
      NewRequestedOutputState = LOW;
      Serial.print(NewRequestedOutputState);
      SetDigitalOutputState(ledPin, NewRequestedOutputState);
    }
    else if (StringPacketBuffer == Sequence02)
    {
      NewRequestedOutputState = LOW;
      Serial.print(NewRequestedOutputState);
      SetDigitalOutputState(ledPin, NewRequestedOutputState);
    }
    else if (StringPacketBuffer == AllOFF)
    {
      NewRequestedOutputState = LOW;
      Serial.print(NewRequestedOutputState);
      SetDigitalOutputState(ledPin, NewRequestedOutputState);
    }
  }
}

void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm");
}

void getlight() {
  // read the value from the sensor:
  sensorValue = analogRead(5);
  // turn the ledPin on
//  digitalWrite(4, HIGH);
  // stop the program for <sensorValue> milliseconds:
  delay(400);
  // turn the ledPin off:
//  digitalWrite(4, LOW);
  return;
}

void SetDigitalOutputState(int PinNumber, boolean OutputState)
{
    digitalWrite(PinNumber, OutputState);
    return;
}

Credits

Aimar N.

Aimar N.

1 project • 1 follower
I like to make stuff

Comments