Mike Mackes
Published © GPL3+

Pool Controller

Windows 10 IoT Core project to control pool components for example pool pump, waterfall and solar heater.

AdvancedFull instructions provided90,688
Pool Controller

Things used in this project

Hardware components

Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
×1
PNY 16GB Turbo MicroSDXC CL10
×1
Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1
SainSmart 5V 4-Channel Solid State Relay Board
×1
Tolako 5v Relay Module for Arduino
×1
DS18b20 Waterproof Temperature Sensors
×1
4.7k Ohm Resistors - 1/4 Watt - 5% - 4K7 (10 Pieces)
×1
Raspberry Pi USB WIFI Dongle
×1
A Male To A Female Extension 1-Feet Usb
×1
American Valve CL40PK6 Number 40 Clamp, 6-Pack
×1
J-B Weld 8272 MarineWeld Marine Epoxy - 2 oz
×1
Seat Washer
×2
Micro USB Power Supply Wall Charger AC Adapter
×1

Software apps and online services

Windows 10 IoT Core
Microsoft Windows 10 IoT Core
Visual Studio 2015
Microsoft Visual Studio 2015
Microsoft IIS
Arduino IDE
Arduino IDE
OneWire Library
Dallas Temperature Library
openHAB open source home automation software

Hand tools and fabrication machines

Printrbot Simple
Used to create enclosure and sensor mounts
Ftdi Usb to Ttl Serial Adapter Module for Arduino Mini Port
Used to upload sketch to Arduino Mini Pro

Story

Read more

Custom parts and enclosures

Air temperature sensor mount

Mount to hold DS18B20 waterproof sensor to monitor air temperature

Water temperature sensor mount

Mount to hold DS18B20 waterproof sensor on to standard pool piping

Bottom of Raspberry Pi and relay enclosure

Enclosure for Raspberry Pi and Relays

Top of Raspberry Pi and relay enclosure

Enclosure for Raspberry Pi and Relays

Schematics

Pool Controller AC Components

Schematic showing how to connect Raspberry Pi to AC relays. Controls pool pump, waterfall, pool lights and AC yard lights

Pool Controller DC Components

Schematic showing how to connect Raspberry Pi to DC relay. Controls the solar water valve.

Pool Controller Arduino Mini Pro

Schematic showing how to connect Raspberry Pi to Arduino Mini Pro and temperature sensors. Monitors pool water, solar heater water and outside air temperatures.

Code

Arduino Sketch for Temperature Sensors using I2C

Java
Code to read the DS18b20 temperature sensors and send data when requested over the I2C interface.
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#define SLAVE_ADDRESS 0x40 

//Define GPIO pin constants
const int POOL_PIN = 3;
const int SOLAR_PIN = 5;
const int OUTSIDE_PIN = 7;

//Define the length of our buffer for the I2C interface
const int I2C_BUFFER_LEN = 24;  //IMPORTANT MAX is 32!!!

//Load OneWire - proprietary dallas semiconductor sensor protocol - no license required
OneWire poolTemp(POOL_PIN);
OneWire solarTemp(SOLAR_PIN);
OneWire outsideTemp(OUTSIDE_PIN);

//Load Dallas - proprietary dallas sensor protocol utilizing onewire - no license required
DallasTemperature poolSensor(&poolTemp);
DallasTemperature solarSensor(&solarTemp);
DallasTemperature outsideSensor(&outsideTemp);

//Define I2C buffer
char data[I2C_BUFFER_LEN];
String temperatureData;

//Define variable for timer
long prevMillis = 0;
long interval = 1000;

void setup(void) {
  //Connect to temperature sensor buses
  poolSensor.begin();
  solarSensor.begin();
  outsideSensor.begin();
  //Start the I2C interface
  Wire.begin(SLAVE_ADDRESS);
  Wire.onRequest(requestEvent);
}

void loop(void) {
  //Monitor time to read temperature sensors once every defined interval
  //Don't read them faster than every 1 second. they can't respond that fast 
  unsigned long currMillis = millis();
  if (currMillis - prevMillis > interval) {
    prevMillis = currMillis;
    readTemperatures();
  }
}

void readTemperatures() {
  //Read all three temperature sensors
  poolSensor.requestTemperatures();
  solarSensor.requestTemperatures();
  outsideSensor.requestTemperatures();

  //Store temperature data in a string 
  //We pad right to the full length of the buffer to make sure to overwrite old data
  //Data is in the format "88.99|78.12|100.00" where "PoolTemp|SolarTemp|OutsideTemp"
  temperatureData = padRight(String(poolSensor.getTempFByIndex(0)) + "|" + 
                             String(solarSensor.getTempFByIndex(0)) + "|" + 
                             String(outsideSensor.getTempFByIndex(0)), I2C_BUFFER_LEN);
}

String padRight(String inStr, int inLen) {
  while (inStr.length() < inLen)
    inStr = inStr + " ";
  return inStr;
}

void requestEvent() {
  //sends data over I2C in the format "88.99|78.12|100.00" where "PoolTemp|SolarTemp|OutsideTemp"
  temperatureData.toCharArray(data,I2C_BUFFER_LEN);
  Wire.write(data);
}

PoolWebServer - BackgroundTask.cs

C#
Defines the HTTP Server that responds to HTTP POST and GET requests
// Copyright (c) Microsoft. All rights reserved.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Http;
using Windows.Foundation.Collections;
using Windows.ApplicationModel.Background;
using Windows.ApplicationModel.AppService;
using Windows.System.Threading;
using Windows.Networking.Sockets;
using System.IO;
using Windows.Storage.Streams;
using System.Threading.Tasks;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Devices.Gpio;

namespace WebServerTask
{
    public sealed class WebServerBGTask : IBackgroundTask
    {
        public void Run(IBackgroundTaskInstance taskInstance)
        {
            // Associate a cancellation handler with the background task. 
            taskInstance.Canceled += OnCanceled;

            // Get the deferral object from the task instance
            serviceDeferral = taskInstance.GetDeferral();

            var appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
            if (appService != null && appService.Name == "App2AppComService")
            {
                appServiceConnection = appService.AppServiceConnection;
                appServiceConnection.RequestReceived += OnRequestReceived;
            }
        }

        //Processes message resquests sent from PoolWebService App
        private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            var message = args.Request.Message;
            string command = message["Command"] as string;

            switch (command)
            {
                case "Initialize":
                    {
                        Sensors.InitSensors();
                        Devices.InitDevices();

                        var messageDeferral = args.GetDeferral();
                        //Set a result to return to the caller
                        var returnMessage = new ValueSet();
                        //Define a new instance of our HTTPServer on Port 8888
                        HttpServer server = new HttpServer(8888, appServiceConnection);
                        IAsyncAction asyncAction = Windows.System.Threading.ThreadPool.RunAsync(
                            (workItem) =>
                            {   //Start the Sever
                                server.StartServer();
                            });

                        //Respond back to PoolWebService with a Status of Success 
                        returnMessage.Add("Status", "Success");
                        var responseStatus = await args.Request.SendResponseAsync(returnMessage);
                        messageDeferral.Complete();
                        break;
                    }

                case "Quit":
                    {
                        //Service was asked to quit. Give us service deferral
                        //so platform can terminate the background task
                        serviceDeferral.Complete();
                        break;
                    }
            }
        }
        private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason)
        {
            //Clean up and get ready to exit
        }

        BackgroundTaskDeferral serviceDeferral;
        AppServiceConnection appServiceConnection;
    }

    //Class to define the HTTP WebServer
    public sealed class HttpServer : IDisposable
    {
        //Create a buffer to read HTTP data
        private const uint BufferSize = 8192;
        //Port to listen on
        private int port = 8888;
        //Listener to
        private readonly StreamSocketListener listener;
        //Connection to send status information back to PoolControllerWebService
        private AppServiceConnection appServiceConnection;

        public HttpServer(int serverPort, AppServiceConnection connection)
        {
            listener = new StreamSocketListener();
            port = serverPort; 
            appServiceConnection = connection;
            //Add event handler for HTTP connections
            listener.ConnectionReceived += (s, e) => ProcessRequestAsync(e.Socket);
        }

        //Call to start the listner 
        public void StartServer()
        {
#pragma warning disable CS4014
            listener.BindServiceNameAsync(port.ToString());
#pragma warning restore CS4014
        }

        public void Dispose()
        {
            listener.Dispose();
        }


        private async void ProcessRequestAsync(StreamSocket socket)
        {
            try
            {
                StringBuilder request = new StringBuilder();
                //Get the incomming data
                using (IInputStream input = socket.InputStream)
                {
                    byte[] data = new byte[BufferSize];
                    IBuffer buffer = data.AsBuffer();
                    uint dataRead = BufferSize;
                    //Read all the incomming data
                    while (dataRead == BufferSize)
                    {
                        await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
                        request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
                        dataRead = buffer.Length;
                    }
                }

                //Got the data start processing a response
                using (IOutputStream output = socket.OutputStream)
                {
                    string requestMethod = request.ToString();
                    string[] requestParts = { "" };
                    if (requestMethod != null)
                    {
                        //Beakup the request into it parts
                        requestMethod = requestMethod.Split('\n')[0];
                        requestParts = requestMethod.Split(' ');
                    }
                    //We only respond HTTP GETS and POST methods
                    if (requestParts[0] == "GET")
                        await WriteGetResponseAsync(requestParts[1], output);
                    else if (requestParts[0] == "POST")
                        await WritePostResponseAsync(requestParts[1], output);
                    else
                        await WriteMethodNotSupportedResponseAsync(requestParts[1], output);
                }
            }
            catch (Exception) { }
        }

        //Handles all HTTP GET's
        private async Task WriteGetResponseAsync(string request, IOutputStream os)
        {
            bool urlFound = false;
            byte[] bodyArray = null;
            string responseMsg = "";
            //See if the request it matches any of the valid requests urls and create the response message
            switch (request.ToUpper())
            {
                case "/SENSORS/POOLTEMP":
                    responseMsg = Sensors.PoolTemperature;
                    urlFound = true;
                    break;
                case "/SENSORS/SOLARTEMP":
                    responseMsg = Sensors.SolarTemperature;
                    urlFound = true;
                    break;
                case "/SENSORS/OUTSIDETEMP":
                    responseMsg = Sensors.OutsideTemperature;
                    urlFound = true;
                    break;
                case "/DEVICES/POOLPUMP/STATE":
                    responseMsg = Devices.PoolPumpState;
                    urlFound = true;
                    break;
                case "/DEVICES/WATERFALLPUMP/STATE":
                    responseMsg = Devices.PoolWaterfallState;
                    urlFound = true;
                    break;
                case "/DEVICES/POOLLIGHTS/STATE":
                    responseMsg = Devices.PoolLightsState;
                    urlFound = true;
                    break;
                case "/DEVICES/YARDLIGHTS/STATE":
                    responseMsg = Devices.YardLightsState;
                    urlFound = true;
                    break;
                case "/DEVICES/POOLSOLAR/STATE":
                    responseMsg = Devices.PoolSolarValveState;
                    urlFound = true;
                    break;
                default:
                    urlFound = false;
                    break;
            }

            bodyArray = Encoding.UTF8.GetBytes(responseMsg);
            await WriteResponseAsync(request.ToUpper(), responseMsg, urlFound, bodyArray, os);
        }

        //Handles all HTTP POST's
        private async Task WritePostResponseAsync(string request, IOutputStream os)
        {
            bool urlFound = false;
            byte[] bodyArray = null;
            string responseMsg = "";
            //See if the request it matches any of the valid requests urls and create the response message
            switch (request.ToUpper())
            {
                case "/DEVICES/POOLPUMP/OFF":
                    Devices.PoolPumpPinValue = GpioPinValue.Low;
                    bodyArray = Encoding.UTF8.GetBytes("OFF");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/POOLPUMP/ON":
                    Devices.PoolPumpPinValue = GpioPinValue.High;
                    bodyArray = Encoding.UTF8.GetBytes("ON");
                    responseMsg = "ON";
                    urlFound = true;
                    break;
                case "/DEVICES/WATERFALLPUMP/OFF":
                    Devices.PoolWaterfallPinValue = GpioPinValue.Low;
                    bodyArray = Encoding.UTF8.GetBytes("OFF");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/WATERFALLPUMP/ON":
                    Devices.PoolWaterfallPinValue = GpioPinValue.High;
                    bodyArray = Encoding.UTF8.GetBytes("ON");
                    responseMsg = "ON";
                    urlFound = true;
                    break;
                case "/DEVICES/POOLLIGHTS/OFF":
                    Devices.PoolLightsPinValue = GpioPinValue.Low;
                    bodyArray = Encoding.UTF8.GetBytes("OFF");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/POOLLIGHTS/ON":
                    Devices.PoolLightsPinValue = GpioPinValue.High;
                    bodyArray = Encoding.UTF8.GetBytes("ON");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/YARDLIGHTS/OFF":
                    Devices.YardLightsPinValue = GpioPinValue.Low;
                    bodyArray = Encoding.UTF8.GetBytes("OFF");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/YARDLIGHTS/ON":
                    Devices.YardLightsPinValue = GpioPinValue.High;
                    bodyArray = Encoding.UTF8.GetBytes("ON");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/POOLSOLAR/OFF":
                    Devices.PoolSolarValvePinValue = GpioPinValue.Low;
                    bodyArray = Encoding.UTF8.GetBytes("OFF");
                    responseMsg = "OFF";
                    urlFound = true;
                    break;
                case "/DEVICES/POOLSOLAR/ON":
                    Devices.PoolSolarValvePinValue = GpioPinValue.High;
                    bodyArray = Encoding.UTF8.GetBytes("ON");
                    responseMsg = "ON";
                    urlFound = true;
                    break;
                default:
                    bodyArray = Encoding.UTF8.GetBytes("");
                    urlFound = false;
                    break;
            }

            await WriteResponseAsync(request.ToUpper(), responseMsg, urlFound,bodyArray, os);
        }

        //Write the response for unsupported HTTP methods
        private async Task WriteMethodNotSupportedResponseAsync(string request, IOutputStream os)
        {
            bool urlFound = false;
            byte[] bodyArray = null;
            bodyArray = Encoding.UTF8.GetBytes("");
            await WriteResponseAsync(request.ToUpper(), "NOT SUPPORTED", urlFound, bodyArray, os);
        }

        //Write the response for HTTP GET's and POST's 
        private async Task WriteResponseAsync(string RequestMsg, string ResponseMsg, bool urlFound, byte[] bodyArray, IOutputStream os)
        {
          try //The appService will die after a day or so.  Let's try catch it seperatly so the http server will still respond
          {
            var updateMessage = new ValueSet();
            updateMessage.Add("Request", RequestMsg);
            updateMessage.Add("Response", ResponseMsg);
            var responseStatus = await appServiceConnection.SendMessageAsync(updateMessage);
          }
          catch (Exception) {}

          try
          {
            MemoryStream bodyStream = new MemoryStream(bodyArray);
            using (Stream response = os.AsStreamForWrite())
            {
                string header = GetHeader(urlFound, bodyStream.Length.ToString());
                byte[] headerArray = Encoding.UTF8.GetBytes(header);
                await response.WriteAsync(headerArray, 0, headerArray.Length);
                if (urlFound)
                    await bodyStream.CopyToAsync(response);
                await response.FlushAsync();
            }
          }
          catch(Exception) {}
        }

        //Creates the HTTP header text for found and not found urls
        string GetHeader(bool urlFound, string bodyStreamLength)
        {
            string header;
            if (urlFound)
            {
                header = "HTTP/1.1 200 OK\r\n" +
                           "Access-Control-Allow-Origin: *\r\n" +
                           "Content-Type: text/plain\r\n" +
                           "Content-Length: " + bodyStreamLength + "\r\n" +
                           "Connection: close\r\n\r\n";
            }
            else
            {
                header = "HTTP/1.1 404 Not Found\r\n" +
                         "Access-Control-Allow-Origin: *\r\n" +
                         "Content-Type: text/plain\r\n" +
                         "Content-Length: 0\r\n" +
                         "Connection close\r\n\r\n";
            }
            return header;
        }
    }
}

PoolWebServer - Devices.cs

C#
Class the defines all devices and what GPIO pins they are connected to
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Gpio;


namespace WebServerTask
{
    //Class the defines all devices and what GPIO pins they are connected to.
    public static class Devices
    {
        //Define the GPIO pins numbers
        private const int POOL_PUMP_PIN = 12;
        private const int POOL_WATERFALL_PIN = 13;
        private const int POOL_LIGHTS_PIN = 16;
        private const int YARD_LIGHTS_PIN = 18;
        private const int POOL_SOLAR_VALVE_PIN = 22;

        //Define the GPIO pins 
        private static GpioPin poolPumpPin;
        private static GpioPin poolWaterfallPin;
        private static GpioPin poolLightsPin;
        private static GpioPin yardLightsPin;
        private static GpioPin poolSolarValvePin;

        //Property for GPIO Pin assigned to the Pool Pump
        public static GpioPinValue PoolPumpPinValue
        {
            get
            {
                return poolPumpPin.Read();  //Read the Pin returns High or Low
            }

            set
            {
                if (poolPumpPin.Read() != value) //Only set the pin if is changing
                    poolPumpPin.Write(value);
            }
        }

        //Property to read status of the Pool Pump ON or OFF
        public static string PoolPumpState
        {
            get
            {
                return GetState(PoolPumpPinValue, GpioPinValue.High);  //Get the state
            }
        }

        //Property for GPIO Pin assigned to the Waterfall Pump
        public static GpioPinValue PoolWaterfallPinValue
        {
            get
            {
                return poolWaterfallPin.Read();
            }

            set
            {
                if (poolWaterfallPin.Read() != value)
                    poolWaterfallPin.Write(value);
            }
        }

        //Property to read status of the Waterfall Pump ON or OFF
        public static string PoolWaterfallState
        {
            get
            {
                return GetState(PoolWaterfallPinValue, GpioPinValue.High);
            }
        }

        //Property for GPIO Pin assigned to the Pool Lights
        public static GpioPinValue PoolLightsPinValue
        {
            get
            {
                return poolLightsPin.Read();
            }

            set
            {
                if (poolLightsPin.Read() != value)
                    poolLightsPin.Write(value);
            }
        }

        //Property to read status of the Pool Lights ON or OFF
        public static string PoolLightsState
        {
            get
            {
                return GetState(PoolLightsPinValue, GpioPinValue.High);
            }
        }

        //Property for GPIO Pin assigned to the valve to turn Solar on and off
        public static GpioPinValue PoolSolarValvePinValue
        {
            get
            {
                return poolSolarValvePin.Read();
            }

            set
            {
                if (poolSolarValvePin.Read() != value)
                    poolSolarValvePin.Write(value);
            }
        }

        //Property to read status of the Solar valve ON or OFF
        public static string PoolSolarValveState
        {
            get
            {
                return GetState(PoolSolarValvePinValue, GpioPinValue.High);
            }
        }

        //Property for GPIO Pin assigned to the Yard Lights
        public static GpioPinValue YardLightsPinValue
        {
            get
            {
                return yardLightsPin.Read();
            }

            set
            {
                if (yardLightsPin.Read() != value)
                    yardLightsPin.Write(value);
            }
        }

        //Property to read status of the Yard Lights ON or OFF
        public static string YardLightsState
        {
            get
            {
                return GetState(YardLightsPinValue, GpioPinValue.High);
            }
        }



        //Intialize all GPIO pin used
        public static void InitDevices()
        {
            var gpio = GpioController.GetDefault();
            if (gpio != null)
            {
                //These pins are on an active high relay.  We set everything to OFF when we start
                poolPumpPin = gpio.OpenPin(POOL_PUMP_PIN);
                poolPumpPin.Write(GpioPinValue.Low);
                poolPumpPin.SetDriveMode(GpioPinDriveMode.Output);

                poolWaterfallPin = gpio.OpenPin(POOL_WATERFALL_PIN);
                poolWaterfallPin.Write(GpioPinValue.Low);
                poolWaterfallPin.SetDriveMode(GpioPinDriveMode.Output);

                poolLightsPin = gpio.OpenPin(POOL_LIGHTS_PIN);
                poolLightsPin.Write(GpioPinValue.Low);
                poolLightsPin.SetDriveMode(GpioPinDriveMode.Output);

                yardLightsPin = gpio.OpenPin(YARD_LIGHTS_PIN);
                yardLightsPin.Write(GpioPinValue.Low);
                yardLightsPin.SetDriveMode(GpioPinDriveMode.Output);

                poolSolarValvePin = gpio.OpenPin(POOL_SOLAR_VALVE_PIN);
                poolSolarValvePin.Write(GpioPinValue.Low);
                poolSolarValvePin.SetDriveMode(GpioPinDriveMode.Output);
            }
        }

        //Gets the state of a device based upon it ActiveState
        //ActiveState means what required to turn the device on High or Low on the GPIO pin 
        private static string GetState(GpioPinValue value, GpioPinValue ActiveState)
        {
            string state = "OFF";
            if (value == ActiveState)
                state = "ON";
            return state;
        }

    }
}

PoolWebServer - Sensors.cs

C#
Class that defines all temperature sensors and the I2C interface used to read them
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;

namespace WebServerTask
{
    //Class that defines all temperature sensors and the I2C interface used to read them them 
    public static class Sensors
    {
        private static I2cDevice Device;
        private static Timer periodicTimer;
        //How often to read temperature data from the Arduino Mini Pro
        private static int ReadInterval = 4000;  //4000 = 4 seconds

        //Variables to hold temperature data
        private static string poolTemperature = "--.--";
        private static string solarTemperature = "--.--";
        private static string outsideTemperature = "--.--";

        //Property to expose the Temperature Data
        public static string PoolTemperature
        {
            get
            {   //Lock the variable incase the timer is tring to write to it
                lock (poolTemperature)
                {
                    return poolTemperature;
                }
            }

            set
            {   //Lock the variable incase the HTTP Server is tring to read from it
                lock (poolTemperature)
                {
                    poolTemperature = value;
                }
            }
        }

        //Property to expose the Temperature Data
        public static string SolarTemperature
        {
            get
            {   //Lock the variable incase the timer is tring to write to it
                lock (solarTemperature)
                {
                    return solarTemperature;
                }
            }

            set
            {   //Lock the variable incase the HTTP Server is tring to read from it
                lock (solarTemperature)
                {
                    solarTemperature = value;
                }
            }
        }

        //Property to expose the Temperature Data
        public static string OutsideTemperature
        {
            get
            {   //Lock the variable incase the timer is tring to write to it
                lock (outsideTemperature)
                {
                    return outsideTemperature;
                }
            }

            set
            {   //Lock the variable incase the HTTP Server is tring to read from it
                lock (outsideTemperature)
                {
                    outsideTemperature = value;
                }
            }
        }

        //Initilizes the I2C connection and starts the timer to read I2C Data
        async public static void InitSensors()
        {
            //Set up the I2C connection the Arduino
            var settings = new I2cConnectionSettings(0x40); // Arduino address
            settings.BusSpeed = I2cBusSpeed.StandardMode;
            string aqs = I2cDevice.GetDeviceSelector("I2C1");
            var dis = await DeviceInformation.FindAllAsync(aqs);
            Device = await I2cDevice.FromIdAsync(dis[0].Id, settings);

            //Create a timer to periodicly read the temps from the Arduino
            periodicTimer = new Timer(Sensors.TimerCallback, null, 0, ReadInterval); 
        }

        //Handle the time call back
        private static void TimerCallback(object state)
        {
            byte[] RegAddrBuf = new byte[] { 0x40 };
            byte[] ReadBuf = new byte[24];
            //Read the I2C connection
            try
            {
                Device.Read(ReadBuf); // read the data
            }
            catch (Exception) { }

            //Parse the response
            //Data is in the format "88.99|78.12|100.00" where "PoolTemp|SolarTemp|OutsideTemp"
            char[] cArray = System.Text.Encoding.UTF8.GetString(ReadBuf, 0, 23).ToCharArray();  // Converte  Byte to Char
            String c = new String(cArray).Trim();
            string[] data = c.Split('|');

            //Write the data to temperature variables
            try
            {
                if (data[0].Trim() != "")
                    PoolTemperature = data[0];
                if (data[1].Trim() != "")
                    SolarTemperature = data[1];
                if (data[2].Trim() != "")
                    OutsideTemperature = data[2];
            }
            catch (Exception) { }
        }

    }
}

PoolWebService- MainPage.xaml.cs

C#
Main page of app that starts the WebServer
// Copyright (c) Microsoft. All rights reserved.

using System;
using Windows.ApplicationModel.AppService;
using Windows.Devices.Gpio;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

namespace PoolWebService
{
    public sealed partial class MainPage : Page
    {
        AppServiceConnection appServiceConnection;

        public MainPage()
        {
            InitializeComponent();
            InitializeAppSvc();
        }

        private async void InitializeAppSvc()
        {
            string WebServerStatus = "PoolWebServer failed to start. AppServiceConnectionStatus was not successful.";
            // Initialize the AppServiceConnection
            appServiceConnection = new AppServiceConnection();
            appServiceConnection.PackageFamilyName = "PoolWebServer_hz258y3tkez3a";
            appServiceConnection.AppServiceName = "App2AppComService";

            // Send a initialize request 
            var res = await appServiceConnection.OpenAsync();
            if (res == AppServiceConnectionStatus.Success)
            {
                var message = new ValueSet();
                message.Add("Command", "Initialize");
                var response = await appServiceConnection.SendMessageAsync(message);
                if (response.Status != AppServiceResponseStatus.Success)
                {
                    WebServerStatus = "PoolWebServer failed to start.";
                    throw new Exception("Failed to send message");
                }
                appServiceConnection.RequestReceived += OnMessageReceived;
                WebServerStatus = "PoolWebServer started.";
            }

            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                txtWebServerStatus.Text = WebServerStatus;
            });
        }

        private async void OnMessageReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args)
        {
            var message = args.Request.Message;
            string msgRequest = message["Request"] as string;
            string msgResponse = message["Response"] as string;

            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                txtRequest.Text = msgRequest;
                txtResponse.Text = msgResponse;
            });
        }
    }
}

PoolWebService - App.xaml.cs

C#
// Copyright (c) Microsoft. All rights reserved.


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace PoolWebService
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            InitializeComponent();
            Suspending += OnSuspending;
        }

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {

#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            Frame rootFrame = Window.Current.Content as Frame;

            // Do not repeat app initialization when the Window already has content,
            // just ensure that the window is active
            if (rootFrame == null)
            {
                // Create a Frame to act as the navigation context and navigate to the first page
                rootFrame = new Frame();
                // Set the default language
                rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];

                rootFrame.NavigationFailed += OnNavigationFailed;

                if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
                    //TODO: Load state from previously suspended application
                }

                // Place the frame in the current Window
                Window.Current.Content = rootFrame;
            }

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate(typeof(MainPage), e.Arguments);
            }
            // Ensure the current window is active
            Window.Current.Activate();
        }

        /// <summary>
        /// Invoked when Navigation to a certain page fails
        /// </summary>
        /// <param name="sender">The Frame which failed navigation</param>
        /// <param name="e">Details about the navigation failure</param>
        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();
            //TODO: Save application state and stop any background activity
            deferral.Complete();
        }
    }
}

OpenHAB Sitemap

JavaScript
Sample sitemap used in openHAB configuration
sitemap default label="Windows 10 IoT"
{
    Frame label="" 
    {
      Text label="Pool" icon="swimmingpool" {
			Switch item=PoolPump mappings=[ON="ON", OFF="OFF"]
			Switch item=WaterFall mappings=[ON="ON", OFF="OFF"]
			Switch item=PoolLights mappings=[ON="ON", OFF="OFF"]
			Text item=pooltemp 
			Text item=solartemp 
			Text item=outsidetemp 
		}
    }       
}

OpenHAB Items

Plain text
Sample items openHAB configuration
Switch PoolPump "Pool Pump" <swimmingpool> (grp1) {http=">[ON:POST:http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/POOLPUMP/ON] >[OFF:POST:http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/POOLPUMP/OFF] <[http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/POOLPUMP/STATE:1500:REGEX((.*?))]", autoupdate="true"}
Switch WaterFall "Water Fall" <waterfall> (grp1) {http=">[ON:POST:http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/WATERFALLPUMP/ON] >[OFF:POST:http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/WATERFALLPUMP/OFF] <[http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/WATERFALLPUMP/STATE:1500:REGEX((.*?))]", autoupdate="true"}
Switch PoolLights "Pool Lights" (grp1) {http=">[ON:POST:http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/POOLLIGHTS/ON] >[OFF:POST:http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/POOLLIGHTS/OFF] <[http://<YOUR_IP_ADDRESS_AND_PORT>/DEVICES/POOLLIGHTS/STATE:1500:REGEX((.*?))]", autoupdate="true"}
Number pooltemp "Pool Water Temp [%.2f F]" <temperature> (grp1) {http="<[http://<YOUR_IP_ADDRESS_AND_PORT>/SENSORS/POOLTEMP:30000:REGEX((.*?))]"}
Number solartemp "Solar Water Temp [%.2f F]" <temperature> (grp1) {http="<[http://<YOUR_IP_ADDRESS_AND_PORT>/SENSORS/SOLARTEMP:30000:REGEX((.*?))]"}
Number outsidetemp "Outside Air Temp [%.2f F]" <temperature> (grp1) {http="<[http://<YOUR_IP_ADDRESS_AND_PORT>/SENSORS/OUTSIDETEMP:30000:REGEX((.*?))]"}

GitHub project repository

Full Visual Studio 2015 Pool Controller project

Credits

Mike Mackes

Mike Mackes

2 projects • 44 followers
Thanks to Noah Mackes.

Comments