Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Manufacturing Technology >> Proceso de manufactura

Controlador de piscina

Componentes y suministros

Raspberry Pi 2 Modelo B
× 1
PNY 16GB Turbo MicroSDXC CL10
× 1
SparkFun Arduino Pro Mini 328 - 5V / 16MHz
× 1
Tarjeta de relé de estado sólido de 4 canales SainSmart 5V
× 1
Módulo de relé Tolako 5v para Arduino
× 1
Sensores de temperatura a prueba de agua DS18b20
× 1
Resistencias de 4.7k Ohm - 1/4 Watt - 5% - 4K7 (10 piezas)
× 1
Llave USB WIFI Raspberry Pi
× 1
Una extensión macho a hembra 1-Feet Usb
× 1
Abrazadera American Valve CL40PK6 Número 40, paquete de 6
× 1
J-B Weld 8272 MarineWeld Marine Epoxy - 2 oz
× 1
Arandela de asiento
× 2
Fuente de alimentación micro USB Cargador de pared Adaptador de CA
× 1

Herramientas y máquinas necesarias

Printrbot Simple
Se utiliza para crear gabinetes y montajes de sensores
Ftdi Usb to Ttl Serial Adapter Module para Arduino Mini Port
Se usa para cargar bocetos en Arduino Mini Pro

Aplicaciones y servicios en línea

Microsoft Windows 10 IoT Core
Microsoft Visual Studio 2015
Microsoft IIS
Arduino IDE
Biblioteca OneWire
Biblioteca de temperatura de Dallas
software de automatización del hogar de código abierto openHAB

Acerca de este proyecto

Controlador de piscina automatizado

Dos veces, en tres meses, falló el temporizador de la bomba de mi piscina. Esto me inspiró a crear este proyecto. El costo de reemplazar esos temporizadores fue de más de $ 120 y todo lo que tuve que mostrar fue un temporizador que me dio muy poco control y una alta tasa de fallas. También sufrí una falla en el sensor de temperatura de mi calentador de agua solar que costó $ 30 adicionales.

Sabía que podía crear un controlador de piscina automatizado rentable que me diera mucho más control sobre cuándo funcionaba la bomba de mi piscina. Quería tener más variables en cuanto a cuándo funcionaba la bomba en lugar de la hora y el día simples del temporizador existente. También quería poder no solo automatizar la bomba de mi piscina, sino también monitorear el estado de varios aspectos del entorno de mi piscina. Otro objetivo era poder realizar estas tareas desde cualquier lugar utilizando cualquier dispositivo.

El proyecto que he creado es muy rentable ya que utiliza una Raspberry Pi con Windows 10 IoT Core, relés, Arduino Mini Pro, así como sensores de temperatura, cableado y componentes impresos en 3D. Completé este proyecto por mucho menos dinero del que había pagado por los dos temporizadores anteriores y el sensor de temperatura solar.

Control de bomba de piscina (componentes de CA)

Comencé mi proyecto controlando los relés de estado sólido desde mi Raspberry Pi con Windows 10 IoT Core. Estos relés me permiten controlar mis componentes de CA (corriente alterna), como la bomba de la piscina. Los relés de estado sólido controlan los relés de CA de 30 amperios existentes que el antiguo temporizador había utilizado. Después de diseñar y probar el circuito de la bomba de la piscina, creé una funcionalidad adicional para controlar otros componentes de CA, como la cascada de mi piscina y las luces de mi piscina y jardín. Con esta parte del proyecto diseñada, pude controlar todos estos elementos de forma remota. Los miembros de mi familia o yo ya no tendríamos que abrir físicamente la caja de control para encender la cascada, encender las luces de la piscina o del jardín o configurar el temporizador para la bomba de la piscina.

Caja del controlador de piscina

Mi hijo diseñó el gabinete del controlador de piscina y lo creó con nuestra impresora 3D y se aseguró de que tanto la Raspberry Pi como los relés de estado sólido encajaran de forma segura dentro de la caja del controlador.

Sensores de temperatura

Uno de los objetivos de diseño de myproject era permitir el control basado en variables además del día y la hora. Quería poder tener en cuenta la temperatura del aire exterior, así como el calentador de agua solar y la temperatura del agua de la piscina para determinar cuándo debe funcionar la bomba y cuándo debe estar en reposo. Un ejemplo de cuándo este tipo de operación sería crucial es cuando la temperatura del aire exterior es muy fría y cercana al punto de congelación. Si la temperatura del agua de la piscina también está cerca de congelarse, necesito asegurarme de que mi piscina y las bombas de cascada estén funcionando para evitar que las tuberías se congelen y dañen los sistemas. Con este proyecto podré lograrlo incluso cuando no esté en casa. Para implementar esto, incorporé sensores de temperatura en mi proyecto. Leí esos sensores utilizando un Arduino Mini Pro que envía esos datos a la misma Raspberry Pi que controla las bombas de la piscina y la cascada a través de la interfaz I2C.

Sensor de temperatura del aire exterior

El sensor de temperatura del aire exterior fue el primer sensor que incorporé. Una vez más, mi hijo diseñó e imprimió el soporte del sensor en nuestra impresora 3D. Probó tanto PLA como ABS, el ABS en realidad funciona mejor ya que es más resistente a la intemperie y tiene una temperatura de transición vítrea más alta, lo que lo hace más resistente al calor. Asegúrese de imprimir con al menos un 75% de relleno. El sensor se conectó como se describe arriba en el esquema.

Sensores de temperatura del agua

Luego incorporé los sensores de temperatura del agua de la piscina y del calentador solar. Esto permitiría que el proyecto recopilara datos de temperatura del agua que se mostrarían a un usuario, así como también proporcionaría más variables para determinar cuándo ciertos componentes funcionaron o estaban en reposo. Primero, se diseñó e imprimió en 3D un soporte de sensor. Como se mencionó anteriormente, el ABS en realidad funciona mejor debido a una mejor resistencia al clima y al calor. Además, asegúrese de utilizar al menos un 75% de relleno .

Construcción e instalación del sensor de temperatura del agua

Después de imprimir el soporte del sensor de temperatura del agua, utilicé una broca avellanada para crear un área de 45 grados alrededor del orificio del sensor. Esto permitiría que JB Weld tuviera más superficie a la que adherirse. Preferí usar una broca para hacer esto en lugar de cambiar el diseño de impresión 3D, ya que el corte preliminar de la broca parecía darle a JB Weld un mejor poder de sujeción.

El siguiente paso fue insertar el sensor de temperatura en el soporte hasta que se extienda aproximadamente 3/4 "fuera de la parte inferior del soporte. Agregue la arandela del asiento para mantenerlo en su lugar.

A continuación, llene la parte superior del soporte con JB Weld y deje que se seque durante 24 horas.

Después de esperar al menos 24 horas para que se seque JB Weld, llegó el momento de instalar los sensores de temperatura del agua.

Nota IMPORTANTE: ¡Asegúrese de que todas las bombas estén apagadas antes de intentar instalar los sensores de temperatura del agua!

Después de asegurarse de que todas las bombas de agua estén apagadas, es una buena idea abrir cualquier válvula que pueda eliminar la presión del agua del área en la que está instalando los sensores de temperatura del agua. Esto facilitará enormemente la instalación (y también lo mantendrá seco).

Taladre un orificio de 5/16 "en la tubería de la piscina. Instale el sensor de temperatura del agua y utilice 2 abrazaderas para sujetarlo firmemente en su lugar. No cometa el mismo error que yo y apriete demasiado las abrazaderas; si aprieta demasiado, aplastará el soporte del sensor. Cerrar válvulas y encienda las bombas. Compruebe si hay fugas.

Control de la válvula del calentador solar

Después de tener los sensores de temperatura en su lugar, pude diseñar e instalar el control de la válvula del calentador de agua solar. El calentador solar utiliza voltaje de CC en lugar de los voltajes de CA utilizados con los otros componentes de la piscina mencionados anteriormente. Esto requirió que controlara un relé de CC en lugar de un relé de CA. El concepto es similar, pero los relés necesarios son diferentes. Asegúrese de que los relés que utiliza para su proyecto controlarán el tipo correcto de voltajes utilizados por el dispositivo que está controlando.

Este control me permite dirigir el agua de la piscina hacia los paneles solares de mi techo. Solo quiero dirigir el agua a los paneles cuando la temperatura del aire exterior esté por encima de los 60 grados. Una vez que el agua se desvía a los paneles, asegúrese de que el agua de retorno esté al menos 2 grados más caliente que el agua de la piscina. De lo contrario, es un desperdicio de energía bombear el agua a los paneles.

El cableado y las conexiones de este control se proporcionan en el esquema de Componentes de CC del controlador de la piscina.

Desarrollo de aplicaciones

Después de instalar Windows 10IoT Core en mi Raspberry Pi, me di cuenta de que tiene un servidor web integrado que se usa para administrarlo. Me preguntaba si se trataba de una versión simplificada de IIS. Si es así, podría simplemente escribir excelentes servicios de descanso en IIS y llamarlos para este proyecto. Después de muchas búsquedas en la web y mucha investigación, no parecía posible. Ese enfoque sería uno que preferiría, pero no parece factible en este momento.

Tomando un enfoque diferente, revisé el ejemplo "Blinky Web Server" y un artículo en el "Druss Blog". Decidí crear una aplicación de fondo de Windows 10 IoT Core sin cabeza que actúa como un servidor web HTTP simple que responde a las solicitudes HTTP GET y POST .

A los pocos días, tenía un prototipo funcional. Esto me dio mucha confianza en que mi proyecto podría tener éxito. Entonces decidí seguir adelante con esta arquitectura. Después de probar minuciosamente mi código a través del depurador de Visual Studio 2015, tuve la impresión de que podía implementar fácilmente mi aplicación.

Implementación de la aplicación

Este es un punto con el que luché, así que espero mostrarte cómo evitar tal dificultad. Debido a que mi aplicación se probó a fondo en el depurador de Visual Studio 2015, tenía la impresión de que podía cambiar el modo de depuración a versión para implementar mi aplicación. Probé este enfoque y, de hecho, implementó mi aplicación y la inició en modo Depuración. Luego detuve la depuración e intenté ejecutar la aplicación desde AppX Manager. No tuve éxito cuando intenté esto, solo obtenía un error genérico que decía, "La aplicación no se pudo inicializar".

La solución a este problema es eliminar la implementación actual y luego instalar la aplicación desde AppX Manager. Esto me costó mucho tiempo, así que espero que esto te ayude a evitar ese problema.

Aunque la aplicación se ejecutó sin problemas en el modo de depuración de Visual Studio 2015, moriría después de recibir la primera solicitud HTTP. Pasé mucho tiempo tratando de solucionar este problema y todavía no sé por qué ocurre.

Sintiendo la presión de completar este proyecto, decidí cambiar mi proyecto para que fuera como el ejemplo de “Blinky Web Server”. En mi implementación, no vi la necesidad de una aplicación de pantalla de Windows 10 IoT Core debido al hecho de que planeé que el servidor web controlara los pines GPIO y leyera la interfaz I2C (no una aplicación de pantalla). Lo que hice en mi proyecto fue que la aplicación de pantalla iniciara el servidor web. Luego, el servidor web envía mensajes a la aplicación de pantalla para que pueda ver qué llamada HTTP recibió mi servidor. Este enfoque parece ser sólido como una roca y es exactamente el mismo código que usé en mi intento original.

Interfaz de usuario

Finalmente, construí un programa de control HTML que se ejecutará prácticamente en cualquier dispositivo. Esto me permite no solo controlar la bomba de la piscina, la cascada y las luces de la piscina, sino también monitorear los sensores adicionales desde cualquier lugar.

Más tarde, utilicé OpenHAB y creé un mapa del sitio que me proporcionó esta interfaz adicional.

Espero que hayas disfrutado leyendo sobre mi proyecto tanto como yo creándolo. Gracias.

Enlace de YouTube, Vimeo o Vine y presione Entrar

Código

  • Bosquejo de Arduino para sensores de temperatura usando I2C
  • PoolWebServer - BackgroundTask.cs
  • PoolWebServer - Devices.cs
  • PoolWebServer - Sensors.cs
  • PoolWebService- MainPage.xaml.cs
  • PoolWebService - App.xaml.cs
  • Mapa del sitio de OpenHAB
  • Elementos de OpenHAB
Arduino Sketch para sensores de temperatura usando I2C Java
Código para leer los sensores de temperatura DS18b20 y enviar datos cuando se solicite a través de la interfaz I2C.
 #include  #include  #include  #define SLAVE_ADDRESS 0x40 // Define el pin GPIO constantsconst int POOL_PIN =3; const int SOLAR_PIN =5; const int OUTSIDE_PIN =7; // Defina la longitud de nuestro búfer para la interfaz I2Cconst int I2C_BUFFER_LEN =24; // ¡¡¡IMPORTANTE MÁXIMO es 32 !!! // Cargar OneWire:protocolo de sensor de semiconductores de dallas patentado:no se requiere licencia OneWire poolTemp (POOL_PIN); OneWire solarTemp (SOLAR_PIN); OneWire outsideTemp (OUTSIDE_PIN); // Load Dallas:protocolo de sensor de dallas propietario que utiliza onewire - no se requiere licenciaDallasTemperature poolSensor (&poolTemp); DallasTemperature solarSensor (&solarTemp); DallasTemperature outsideSensor (&outsideTemp); // Definir I2C bufferchar data [I2C_BUFFER_LEN]; String temperatureData; // Definir variable para temporizadorlong prevMillis =0; void setup (void) {// Conectarse a los buses del sensor de temperatura poolSensor.begin (); solarSensor.begin (); outsideSensor.begin (); // Inicie la interfaz I2C Wire.begin (SLAVE_ADDRESS); Wire.onRequest (requestEvent);} void loop (void) {// Controle el tiempo para leer los sensores de temperatura una vez en cada intervalo definido // No los lea más rápido que cada segundo. no pueden responder tan rápido sin firmar currMillis =millis (); if (currMillis - prevMillis> intervalo) {prevMillis =currMillis; readTemperatures (); }} void readTemperatures () {// Leer los tres sensores de temperatura poolSensor.requestTemperatures (); solarSensor.requestTemperatures (); outsideSensor.requestTemperatures (); // Almacenamos los datos de temperatura en una cadena // Acumulamos hasta la longitud total del búfer para asegurarnos de sobrescribir los datos antiguos // Los datos tienen el formato "88.99 | 78.12 | 100.00" donde "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 ()  
PoolWebServer - BackgroundTask.cs C #
Define el servidor HTTP que responde a las solicitudes HTTP POST y GET
 // Copyright (c) Microsoft. Todos los derechos reservados.usando System; usando System.Collections.Generic; usando System.Linq; usando System.Text; usando System.Net.Http; usando Windows.Foundation.Collections; usando Windows.ApplicationModel.Background; usando Windows.ApplicationModel. AppService; usando Windows.System.Threading; usando Windows.Networking.Sockets; usando System.IO; usando Windows.Storage.Streams; usando System.Threading.Tasks; usando System.Runtime.InteropServices.WindowsRuntime; usando Windows.Foundation; usando Windows.Devices.Gpio; namespace WebServerTask {clase pública sellada WebServerBGTask:IBackgroundTask {public void Run (IBackgroundTaskInstance taskInstance) {// Asocia un controlador de cancelación con la tarea en segundo plano. taskInstance.Canceled + =OnCanceled; // Obtener el objeto de aplazamiento de la instancia de tarea serviceDeferral =taskInstance.GetDeferral (); var appService =taskInstance.TriggerDetails como AppServiceTriggerDetails; if (appService! =null &&appService.Name =="App2AppComService") {appServiceConnection =appService.AppServiceConnection; appServiceConnection.RequestReceived + =OnRequestReceived; }} // Procesa las solicitudes de mensajes enviadas desde la aplicación PoolWebService private async void OnRequestReceived (AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) {var message =args.Request.Message; comando de cadena =mensaje ["Comando"] como cadena; cambiar (comando) {caso "Inicializar":{Sensores.InitSensors (); Devices.InitDevices (); var messageDeferral =args.GetDeferral (); // Establecer un resultado para devolver al llamador var returnMessage =new ValueSet (); // Definir una nueva instancia de nuestro HTTPServer en el puerto 8888 HttpServer server =new HttpServer (8888, appServiceConnection); IAsyncAction asyncAction =Windows.System.Threading.ThreadPool.RunAsync ((workItem) => {// Inicie el servidor Servidor.StartServer ();}); // Responde a PoolWebService con un estado de éxito returnMessage.Add ("Status", "Success"); var responseStatus =await args.Request.SendResponseAsync (returnMessage); messageDeferral.Complete (); descanso; } case "Quit":{// Se solicitó el cierre del servicio. Danos el aplazamiento del servicio // para que la plataforma pueda finalizar la tarea en segundo plano serviceDeferral.Complete (); descanso; }}} private void OnCanceled (remitente de IBackgroundTaskInstance, motivo BackgroundTaskCancellationReason) {// Limpia y prepárate para salir} BackgroundTaskDeferral serviceDeferral; AppServiceConnection appServiceConnection; } // Clase para definir la clase sellada pública HTTP WebServer HttpServer:IDisposable {// Crear un búfer para leer datos HTTP const uint privado BufferSize =8192; // Puerto para escuchar en el puerto int privado =8888; // Oyente al oyente StreamSocketListener privado de solo lectura; // Conexión para enviar información de estado a PoolControllerWebService private AppServiceConnection appServiceConnection; HttpServer público (int serverPort, conexión AppServiceConnection) {listener =new StreamSocketListener (); puerto =servidorPort; appServiceConnection =conexión; // Agregue un controlador de eventos para el detector de conexiones HTTP.ConnectionReceived + =(s, e) => ProcessRequestAsync (e.Socket); } // Llamar para iniciar el listner public void StartServer () {#pragma warning disable CS4014 listener.BindServiceNameAsync (port.ToString ()); # pragma warning restore CS4014} public void Dispose () {listener.Dispose (); } privado async void ProcessRequestAsync (conector StreamSocket) {prueba {solicitud de StringBuilder =new StringBuilder (); // Obtener los datos entrantes usando (IInputStream input =socket.InputStream) {byte [] data =new byte [BufferSize]; IBuffer buffer =data.AsBuffer (); uint dataRead =BufferSize; // Leer todos los datos entrantes while (dataRead ==BufferSize) {await input.ReadAsync (buffer, BufferSize, InputStreamOptions.Partial); request.Append (Encoding.UTF8.GetString (data, 0, data.Length)); dataRead =buffer.Length; }} // Obtuve los datos, comience a procesar una respuesta usando (IOutputStream output =socket.OutputStream) {string requestMethod =request.ToString (); string [] requestParts ={""}; if (requestMethod! =null) {// Transmitir la solicitud en sus partes requestMethod =requestMethod.Split ('\ n') [0]; requestParts =requestMethod.Split (''); } // Solo respondemos los métodos HTTP GETS y POST si (requestParts [0] =="GET") await WriteGetResponseAsync (requestParts [1], salida); else if (requestParts [0] =="POST") await WritePostResponseAsync (requestParts [1], salida); si no, aguarda WriteMethodNotSupportedResponseAsync (requestParts [1], salida); }} catch (Exception) {}} // Maneja todas las tareas asincrónicas privadas de HTTP GET WriteGetResponseAsync (solicitud de cadena, IOutputStream os) {bool urlFound =false; byte [] bodyArray =null; string responseMsg =""; // Vea si la solicitud coincide con alguna de las URL de solicitudes válidas y cree el interruptor de mensaje de respuesta (request.ToUpper ()) {case "/ SENSORS / POOLTEMP":responseMsg =Sensors.PoolTemperature; urlFound =verdadero; descanso; case "/ SENSORS / SOLARTEMP":responseMsg =Sensors.SolarTemperature; urlFound =verdadero; descanso; case "/ SENSORS / OUTSIDETEMP":responseMsg =Sensors.OutsideTemperature; urlFound =verdadero; descanso; case "/ DEVICES / POOLPUMP / STATE":responseMsg =Devices.PoolPumpState; urlFound =verdadero; descanso; case "/ DEVICES / WATERFALLPUMP / STATE":responseMsg =Devices.PoolWaterfallState; urlFound =verdadero; descanso; case "/ DEVICES / POOLLIGHTS / STATE":responseMsg =Devices.PoolLightsState; urlFound =verdadero; descanso; case "/ DEVICES / YARDLIGHTS / STATE":responseMsg =Devices.YardLightsState; urlFound =verdadero; descanso; case "/ DEVICES / POOLSOLAR / STATE":responseMsg =Devices.PoolSolarValveState; urlFound =verdadero; descanso; predeterminado:urlFound =false; descanso; } bodyArray =Encoding.UTF8.GetBytes (responseMsg); esperar WriteResponseAsync (request.ToUpper (), responseMsg, urlFound, bodyArray, os); } // Maneja todas las tareas asíncronas privadas de HTTP POST WritePostResponseAsync (solicitud de cadena, IOutputStream os) {bool urlFound =false; byte [] bodyArray =null; string responseMsg =""; // Vea si la solicitud coincide con alguna de las URL de solicitudes válidas y cree el mensaje de respuesta switch (request.ToUpper ()) {case "/ DEVICES / POOLPUMP / OFF":Devices.PoolPumpPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("APAGADO"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / POOLPUMP / ON":Devices.PoolPumpPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ON"); responseMsg ="ENCENDIDO"; urlFound =verdadero; descanso; case "/ DEVICES / WATERFALLPUMP / OFF":Devices.PoolWaterfallPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("APAGADO"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / WATERFALLPUMP / ON":Devices.PoolWaterfallPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ON"); responseMsg ="ENCENDIDO"; urlFound =verdadero; descanso; case "/ DEVICES / POOLLIGHTS / OFF":Devices.PoolLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("APAGADO"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / POOLLIGHTS / ON":Devices.PoolLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ON"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / YARDLIGHTS / OFF":Devices.YardLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("APAGADO"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / YARDLIGHTS / ON":Devices.YardLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ON"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / POOLSOLAR / OFF":Devices.PoolSolarValvePinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("APAGADO"); responseMsg ="APAGADO"; urlFound =verdadero; descanso; case "/ DEVICES / POOLSOLAR / ON":Devices.PoolSolarValvePinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ON"); responseMsg ="ENCENDIDO"; urlFound =verdadero; descanso; predeterminado:bodyArray =Encoding.UTF8.GetBytes (""); urlFound =falso; descanso; } await WriteResponseAsync (request.ToUpper (), responseMsg, urlFound, bodyArray, os); } // Escribe la respuesta para métodos HTTP no admitidos private async Task WriteMethodNotSupportedResponseAsync (solicitud de cadena, IOutputStream os) {bool urlFound =false; byte [] bodyArray =null; bodyArray =Encoding.UTF8.GetBytes (""); aguardar WriteResponseAsync (request.ToUpper (), "NO SOPORTADO", urlFound, bodyArray, os); } // Escribe la respuesta para HTTP GET's y POST's private async Task WriteResponseAsync (string RequestMsg, string ResponseMsg, bool urlFound, byte [] bodyArray, IOutputStream os) {try // El appService morirá después de un día más o menos. Intentemos capturarlo por separado para que el servidor http todavía responda {var updateMessage =new ValueSet (); updateMessage.Add ("Solicitud", RequestMsg); updateMessage.Add ("Respuesta", ResponseMsg); var responseStatus =aguardar appServiceConnection.SendMessageAsync (updateMessage); } catch (Exception) {} try {MemoryStream bodyStream =new MemoryStream (bodyArray); usando (Respuesta de secuencia =os.AsStreamForWrite ()) {encabezado de cadena =GetHeader (urlFound, bodyStream.Length.ToString ()); byte [] headerArray =Encoding.UTF8.GetBytes (encabezado); aguardar respuesta.WriteAsync (headerArray, 0, headerArray.Length); si (urlFound) aguarda bodyStream.CopyToAsync (respuesta); aguardar respuesta.FlushAsync (); }} catch (Exception) {}} // Crea el texto del encabezado HTTP para las URL encontradas y no encontradas 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" + " Longitud del contenido:"+ bodyStreamLength +" \ r \ n "+" Conexión:cerrar \ 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 -Longitud:0 \ r \ n "+" Cierre de conexión \ r \ n \ r \ n "; } encabezado de retorno; }}} 
PoolWebServer - Devices.cs C #
La clase define todos los dispositivos y a qué pines GPIO están conectados
 usando System; usando System.Collections.Generic; usando System.Linq; usando System.Text; usando System.Threading.Tasks; usando Windows.Devices.Gpio; namespace WebServerTask {// Class the define todos los dispositivos y los pines GPIO a los que están conectados. Dispositivos de clase estática pública {// Defina los números de los pines GPIO. private const int POOL_PUMP_PIN =12; const privado int POOL_WATERFALL_PIN =13; const privado int POOL_LIGHTS_PIN =16; const privado int YARD_LIGHTS_PIN =18; const privado int POOL_SOLAR_VALVE_PIN =22; // Definir los pines GPIO private static GpioPin poolPumpPin; poolWaterfallPin estático privado de GpioPin; poolLightsPin estático privado de GpioPin; GpioPin yardLightsPin estático privado; poolSolarValvePin estático privado de GpioPin; // Propiedad para el Pin GPIO asignado a Pool Pump public static GpioPinValue PoolPumpPinValue {get {return poolPumpPin.Read (); // Leer el Pin devuelve Alto o Bajo} set {if (poolPumpPin.Read ()! =Value) // Solo establece el pin si está cambiando 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.csC#
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.csC#
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.csC#
// 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{ ///  /// Provides application-specific behavior to supplement the default Application class. ///  sealed partial class App :Application { ///  /// 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(). ///  public App() { InitializeComponent(); Suspending +=OnSuspending; } ///  /// 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. ///  /// Details about the launch request and process. 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(); } ///  /// Invoked when Navigation to a certain page fails ///  /// The Frame which failed navigation /// Details about the navigation failure void OnNavigationFailed(object sender, NavigationFailedEventArgs e) { throw new Exception("Failed to load Page " + e.SourcePageType.FullName); } ///  /// 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. ///  /// The source of the suspend request. /// Details about the suspend request. private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral =e.SuspendingOperation.GetDeferral(); //TODO:Save application state and stop any background activity deferral.Complete(); }}} 
OpenHAB SitemapJavaScript
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 ItemsPlain text
Sample items openHAB configuration
Switch PoolPump "Pool Pump"  (grp1) {http=">[ON:POST:http:///DEVICES/POOLPUMP/ON]>[OFF:POST:http:///DEVICES/POOLPUMP/OFF] <[http:///DEVICES/POOLPUMP/STATE:1500:REGEX((.*?))]", autoupdate="true"}Switch WaterFall "Water Fall"  (grp1) {http=">[ON:POST:http:///DEVICES/WATERFALLPUMP/ON]>[OFF:POST:http:///DEVICES/WATERFALLPUMP/OFF] <[http:///DEVICES/WATERFALLPUMP/STATE:1500:REGEX((.*?))]", autoupdate="true"}Switch PoolLights "Pool Lights" (grp1) {http=">[ON:POST:http:///DEVICES/POOLLIGHTS/ON]>[OFF:POST:http:///DEVICES/POOLLIGHTS/OFF] <[http:///DEVICES/POOLLIGHTS/STATE:1500:REGEX((.*?))]", autoupdate="true"}Number pooltemp "Pool Water Temp [%.2f F]"  (grp1) {http="<[http:///SENSORS/POOLTEMP:30000:REGEX((.*?))]"}Number solartemp "Solar Water Temp [%.2f F]"  (grp1) {http="<[http:///SENSORS/SOLARTEMP:30000:REGEX((.*?))]"}Number outsidetemp "Outside Air Temp [%.2f F]"  (grp1) {http="<[http:///SENSORS/OUTSIDETEMP:30000:REGEX((.*?))]"}
GitHub project repository
Full Visual Studio 2015 Pool Controller projecthttps://github.com/mmackes/Windows-10-IoT-PoolController

Piezas y carcasas personalizadas

Mount to hold DS18B20 waterproof sensor to monitor air temperatureMount to hold DS18B20 waterproof sensor on to standard pool pipingEnclosure for Raspberry Pi and RelaysEnclosure for Raspberry Pi and Relays

Esquemas

Schematic showing how to connect Raspberry Pi to AC relays. Controls pool pump, waterfall, pool lights and AC yard lights Schematic showing how to connect Raspberry Pi to DC relay. Controls the solar water valve. Schematic showing how to connect Raspberry Pi to Arduino Mini Pro and temperature sensors. Monitors pool water, solar heater water and outside air temperatures.

Proceso de manufactura

  1. Monitoreo de temperatura en Raspberry Pi
  2. Estación meteorológica Raspberry Pi 2
  3. Supervisión de la temperatura con Raspberry Pi
  4. 433MHz Smart Home Controller con Sensorflare y RaspberryPi
  5. Seguimiento de Raspberry Pi Ball
  6. Mando a distancia universal Raspberry Pi
  7. Sensor de movimiento con Raspberry Pi
  8. Un trozo de Raspberry Pi
  9. Cycle Chaser
  10. Sensor de humedad del suelo Raspberry Pi
  11. Detector de ladrones Raspberry Pi