Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Pool-Controller

Komponenten und Verbrauchsmaterialien

Raspberry Pi 2 Model B
× 1
PNY 16 GB Turbo MicroSDXC CL10
× 1
SparkFun Arduino Pro Mini 328 - 5V/16MHz
× 1
SainSmart 5V 4-Kanal Halbleiterrelaisplatine
× 1
Tolako 5v Relaismodul für Arduino
× 1
DS18b20 wasserdichte Temperatursensoren
× 1
4,7k Ohm Widerstände - 1/4 Watt - 5% - 4K7 (10 Stück)
× 1
Raspberry Pi USB WIFI Dongle
× 1
Eine männliche zu einer weiblichen Verlängerung 1-Fuß-USB
× 1
Amerikanisches Ventil CL40PK6 Nummer 40 Klemme, 6er-Pack
× 1
J-B Weld 8272 MarineWeld Marine Epoxy - 2 oz
× 1
Sitzscheibe
× 2
Micro-USB-Netzteil Wandladegerät AC-Adapter
× 1

Notwendige Werkzeuge und Maschinen

Printrbot Simple
Wird verwendet, um Gehäuse- und Sensorhalterungen zu erstellen
Ftdi-USB-auf-TTL-Seriell-Adaptermodul für Arduino Mini-Port
Wird verwendet, um eine Skizze auf Arduino Mini Pro hochzuladen

Apps und Onlinedienste

Microsoft Windows 10 IoT Core
Microsoft Visual Studio 2015
Microsoft IIS
Arduino-IDE
OneWire-Bibliothek
Dallas Temperaturbibliothek
openHAB Open-Source-Hausautomatisierungssoftware

Über dieses Projekt

Automatisierte Poolsteuerung

Zweimal, innerhalb von drei Monaten, ist der Timer für meine Poolpumpe ausgefallen. Dies hat mich zu diesem Projekt inspiriert. Die Kosten für den Austausch dieser Timer betrugen über 120 US-Dollar und alles, was ich dafür nachweisen musste, war ein Timer, der mir sehr wenig Kontrolle und eine hohe Ausfallrate gab. Ich erlitt auch einen Ausfall des Temperatursensors an meinem Solarwarmwasserbereiter, der zusätzliche 30 US-Dollar kostete.

Ich wusste, dass ich eine kostengünstige automatisierte Poolsteuerung entwickeln konnte, die mir viel mehr Kontrolle darüber gab, wann meine Poolpumpe lief. Ich wollte mehr Variablen haben, wann die Pumpe lief, anstatt die einfache Uhrzeit und den Tag des vorhandenen Timers. Ich wollte auch nicht nur die Poolpumpe automatisieren, sondern auch den Status verschiedener Aspekte meiner Poolumgebung überwachen. Ein weiteres Ziel war es, diese Aufgaben von überall und mit jedem Gerät erledigen zu können.

Das von mir erstellte Projekt ist sehr kostengünstig, da es einen Raspberry Pi mit Windows 10 IoT Core, Relais, Arduino Mini Pro sowie Temperatursensoren, Verkabelung und 3D-gedruckte Komponenten verwendet. Ich habe dieses Projekt für weit weniger Geld abgeschlossen, als ich für die beiden vorherigen Timer und den Solartemperatursensor bezahlt hatte.

Poolpumpensteuerung (AC-Komponenten)

Ich begann mein Projekt mit der Steuerung von Halbleiterrelais von meinem Raspberry Pi mit Windows 10 IoT Core. Diese Relais ermöglichen es mir, meine AC-Komponenten (Wechselstrom) wie die Poolpumpe zu steuern. Die Halbleiterrelais steuern die vorhandenen 30-A-Wechselstromrelais, die der alte Timer verwendet hatte. Nachdem ich die Schaltung für die Poolpumpe entworfen und getestet hatte, erstellte ich zusätzliche Funktionen zur Steuerung anderer AC-Komponenten wie meines Pool-Wasserfalls und meiner Pool- und Gartenbeleuchtung. Mit diesem Teil des Projekts konnte ich all diese Elemente aus der Ferne steuern. Meine Familienmitglieder oder ich müssten die Kontrollbox nicht mehr physisch öffnen, um den Wasserfall einzuschalten, die Pool- oder Gartenbeleuchtung einzuschalten oder den Timer für die Poolpumpe einzustellen.

Pool-Controller-Gehäuse

Mein Sohn entwarf das Pool-Controller-Gehäuse und erstellte es mit unserem 3D-Drucker und stellte sicher, dass sowohl der Raspberry Pi als auch die Halbleiterrelais einen sicheren Sitz in der Controller-Box hatten.

Temperatursensoren

Eines der Designziele für myproject war es, zusätzlich zu Tag und Uhrzeit eine Steuerung basierend auf Variablen zu ermöglichen. Ich wollte in der Lage sein, die Außenlufttemperatur sowie die Temperatur des Solarwarmwasserbereiters und des Poolwassers zu berücksichtigen, um zu bestimmen, wann die Pumpe laufen und wann sie in Ruhe sein sollte. Ein Beispiel dafür, wann diese Art des Betriebs entscheidend ist, ist, wenn die Außenlufttemperatur sehr kalt ist und nahe dem Gefrierpunkt ist. Wenn die Wassertemperatur im Pool ebenfalls nahe dem Gefrierpunkt liegt, muss ich sicherstellen, dass meine Pool- und Wasserfallpumpen laufen, um zu verhindern, dass die Rohre einfrieren und die Systeme beschädigen. Mit diesem Projekt kann ich dies auch erreichen, wenn ich nicht zu Hause bin. Um dies umzusetzen, habe ich Temperatursensoren in mein Projekt eingebunden. Ich habe diese Sensoren mit einem Arduino Mini Pro gelesen, der diese Daten an denselben Raspberry Pi sendet, der die Pool- und Wasserfallpumpen über die I2C-Schnittstelle steuert.

Außenlufttemperatursensor

Der Außenlufttemperatursensor war der erste Sensor, den ich eingebaut habe. Auch hier hat mein Sohn die Sensorhalterung auf unserem 3D-Drucker entworfen und gedruckt. Er hat sowohl PLA als auch ABS ausprobiert, ABS funktioniert tatsächlich besser, da es witterungsbeständiger ist und eine höhere Glasübergangstemperatur hat, was es hitzebeständiger macht. Stellen Sie sicher, dass Sie mit mindestens 75 % Füllung drucken. Der Sensor wurde wie oben im Schaltplan beschrieben angeschlossen.

Wassertemperatursensoren

Ich habe dann die Temperatursensoren für das Poolwasser und die Solarheizung integriert. Dies würde es dem Projekt ermöglichen, Wassertemperaturdaten zu sammeln, die einem Benutzer angezeigt würden, sowie weitere Variablen bereitzustellen, um zu bestimmen, wann bestimmte Komponenten liefen oder sich im Ruhezustand befanden. Zuerst wurde eine Sensorhalterung entworfen und in 3D gedruckt. Wie bereits erwähnt, funktioniert ABS aufgrund der besseren Wetter- und Hitzebeständigkeit tatsächlich besser. Außerdem achten Sie darauf, dass Sie mindestens 75 % Infill verwenden .

Aufbau und Installation des Wassertemperatursensors

Nach dem Drucken der Wassertemperatursensorhalterung habe ich einen Senkbohrer verwendet, um einen 45-Grad-Bereich um das Sensorloch herum zu schaffen. Dies würde es der JB Weld ermöglichen, eine größere Oberfläche zum Kleben zu haben. Ich zog es vor, dafür einen Bohrer zu verwenden, anstatt das 3D-Druckdesign zu ändern, da der grobe Schnitt des Bohrers dem JB Weld eine bessere Haltekraft zu verleihen schien.

Der nächste Schritt bestand darin, den Temperatursensor in die Halterung einzuführen, bis er etwa 3/4" aus der Unterseite der Halterung herausragt.  Fügen Sie die Sitzscheibe hinzu, um sie in Position zu halten. 

Füllen Sie anschließend die Oberseite der Halterung mit JB Weld und lassen Sie sie 24 Stunden trocknen.

Nachdem die JB Weld mindestens 24 Stunden getrocknet war, war es an der Zeit, die Wassertemperatursensoren zu installieren.

WICHTIGER Hinweis: Stellen Sie sicher, dass alle Pumpen ausgeschaltet sind, bevor Sie versuchen, die Wassertemperatursensoren zu installieren!

Nachdem Sie sichergestellt haben, dass alle Wasserpumpen ausgeschaltet sind, sollten Sie alle Ventile öffnen, die den Wasserdruck aus dem Bereich entfernen können, in dem Sie die Wassertemperatursensoren installieren. Dies erleichtert die Installation erheblich (und hält Sie auch trocken).

Bohren Sie ein 5/16-Zoll-Loch in die Poolleitung.  Installieren Sie den Wassertemperatursensor und verwenden Sie 2 Klemmen, um ihn fest zu halten.  Machen Sie nicht den gleichen Fehler wie ich und ziehen Sie die Klemmen zu fest an, da zu starkes Anziehen die Sensorhalterung zerquetscht.  Schließen Ventile und schalten Sie die Pumpen ein.  Auf Lecks prüfen.

Ventilsteuerung der Solarheizung

Nachdem ich die Temperatursensoren installiert hatte, konnte ich dann die Ventilsteuerung des Solarwarmwasserbereiters entwerfen und installieren. Die Solarheizung verwendet Gleichspannung im Gegensatz zu den Wechselspannungen, die bei den anderen zuvor erwähnten Poolkomponenten verwendet werden. Dies erforderte, dass ich ein DC-Relais anstelle eines AC-Relais steuerte. Das Konzept ist ähnlich, aber die benötigten Relais sind unterschiedlich. Stellen Sie sicher, dass die Relais, die Sie für Ihr Projekt verwenden, die richtige Art von Spannungen steuern, die von dem von Ihnen gesteuerten Gerät verwendet werden.

Diese Steuerung ermöglicht es mir, Poolwasser zu den Sonnenkollektoren auf meinem Dach zu leiten. Ich möchte nur dann Wasser auf die Paneele leiten, wenn die Außenlufttemperatur über 60 Grad liegt. Wenn das Wasser zu den Platten geleitet wird, stellen Sie sicher, dass das Rücklaufwasser mindestens 2 Grad wärmer ist als das Beckenwasser. Andernfalls ist es Energieverschwendung, das Wasser zu den Platten zu pumpen.

Die Verkabelung und Anschlüsse dieser Steuerung sind im Schaltplan der DC-Komponenten des Pool-Controllers enthalten.

Anwendungsentwicklung

Nachdem ich Windows 10IoT Core auf meinem Raspberry Pi installiert hatte, stellte ich fest, dass er über einen integrierten Webserver verfügt, der zur Verwaltung verwendet wird. Ich fragte mich, ob dies eine abgespeckte Version von IIS war. Wenn ja, könnte ich einfach einige erholsame Dienste auf IIS schreiben und sie für dieses Projekt aufrufen. Nach vielen Recherchen im Internet und vielen Recherchen schien es nicht möglich zu sein. Dieser Ansatz wäre einer, den ich bevorzugen würde, aber er scheint zum jetzigen Zeitpunkt nicht machbar zu sein.

Ich habe einen anderen Ansatz gewählt und mir das Beispiel "Blinky Web Server" und einen Artikel im "Druss Blog" angesehen. Ich beschloss, eine kopflose Windows 10 IoT Core-Hintergrundanwendung zu entwickeln, die als einfacher HTTP-Webserver fungiert, der auf HTTP-GET- und POST-Anfragen reagiert. . 

Innerhalb weniger Tage hatte ich einen funktionierenden Prototyp. Das hat mir viel Zuversicht gegeben, dass mein Projekt erfolgreich sein könnte. Also beschloss ich, mit dieser Architektur voranzukommen. Nachdem ich meinen Code mit dem Debugger von Visual Studio 2015 gründlich getestet hatte, hatte ich den Eindruck, dass ich meine Anwendung problemlos bereitstellen könnte.

Bereitstellen der Anwendung

Dies ist ein Punkt, mit dem ich zu kämpfen hatte, daher hoffe ich, Ihnen zu zeigen, wie Sie solche Schwierigkeiten vermeiden können. Da meine Anwendung im Debugger von Visual Studio 2015 gründlich getestet wurde, hatte ich den Eindruck, dass ich einfach den Modus von Debug auf Release ändern könnte, um meine Anwendung bereitzustellen. Ich habe diesen Ansatz ausprobiert und er hat tatsächlich meine Anwendung bereitgestellt und im Debug-Modus gestartet. Ich habe dann das Debuggen gestoppt und versucht, die Anwendung über AppX Manager auszuführen. Ich hatte keinen Erfolg, als ich dies versuchte, ich erhielt nur eine allgemeine Fehlermeldung, die besagte:"Anwendung konnte nicht initialisiert werden."

Die Lösung für dieses Problem besteht darin, die aktuelle Bereitstellung zu löschen und dann die Anwendung stattdessen über den AppX Manager zu installieren. Das hat mich viel Zeit gekostet. Ich hoffe, dies hilft Ihnen, dieses Problem zu vermeiden.

Obwohl die Anwendung im Debugmodus von Visual Studio 2015 einwandfrei lief, wurde sie nach Erhalt der ersten HTTP-Anforderung abgebrochen. Ich habe viel Zeit damit verbracht, dies zu beheben und weiß immer noch nicht, warum dies auftritt.

Da ich den Druck verspürte, dieses Projekt abzuschließen, beschloss ich, mein Projekt so zu ändern, dass es genau wie das Beispiel „Blinky Web Server“ aussieht. In meiner Implementierung sah ich keine Notwendigkeit für eine Windows 10 IoT Core-Bildschirmanwendung, da ich geplant hatte, dass der Webserver die GPIO-Pins steuert und die I2C-Schnittstelle ausliest (keine Bildschirmanwendung). Was ich in meinem Projekt getan habe, war, dass die Bildschirmanwendung den Webserver startet. Der Webserver sendet dann Nachrichten an die Bildschirmanwendung zurück, damit ich sehen kann, welcher HTTP-Aufruf von meinem Server empfangen wurde. Dieser Ansatz scheint grundsolide zu sein und ist genau derselbe Code, den ich bei meinem ursprünglichen Versuch verwendet habe.

Benutzeroberfläche

Schließlich habe ich ein HTML-Steuerungsprogramm erstellt, das auf praktisch jedem Gerät ausgeführt werden kann. Dadurch kann ich nicht nur die Poolpumpe, den Wasserfall und die Poolbeleuchtung steuern, sondern auch die zusätzlichen Sensoren von überall aus überwachen.

Später habe ich OpenHAB verwendet und eine Sitemap erstellt, die Methis zusätzliche Schnittstelle bietet.

Ich hoffe, es hat Ihnen genauso viel Spaß gemacht, über mein Projekt zu lesen, wie ich es gemacht habe. Danke.

YouTube-, Vimeo- oder Vine-Link und drücken Sie die Eingabetaste

Code

  • Arduino-Skizze für Temperatursensoren mit I2C
  • PoolWebServer - BackgroundTask.cs
  • PoolWebServer - Devices.cs
  • PoolWebServer - Sensoren.cs
  • PoolWebService- MainPage.xaml.cs
  • PoolWebService - App.xaml.cs
  • OpenHAB-Sitemap
  • OpenHAB-Elemente
Arduino-Skizze für Temperatursensoren mit I2C Java
Code zum Lesen der DS18b20-Temperatursensoren und Senden von Daten bei Anforderung über die I2C-Schnittstelle.
#include #include #include #define SLAVE_ADDRESS 0x40 //Define GPIO pin constantsconst int POOL_PIN =3;const int SOLAR_PIN =5;const int OUTSIDE_PIN =7;//Definieren Sie die Länge unseres Puffers für die I2C-Schnittstelleconst int I2C_BUFFER_LEN =24; //WICHTIG MAX ist 32!!!//Load OneWire - proprietäres Dallas-Halbleitersensorprotokoll - keine Lizenz erforderlichOneWire poolTemp(POOL_PIN);OneWire solarTemp(SOLAR_PIN);OneWire outsideTemp(OUTSIDE_PIN);//Load Dallas - proprietäres Dallas-Sensorprotokoll unter Verwendung onewire - keine Lizenz erforderlichDallasTemperature poolSensor(&poolTemp);DallasTemperature solarSensor(&solarTemp);DallasTemperature outsideSensor(&outsideTemp);//Define I2C bufferchar data[I2C_BUFFER_LEN];String temperatureData;//Define Variable für Timerlong prevMillis =0;long interval =1000; Void setup (void) {// Verbindung mit Temperatursensorbussen poolSensor.begin (); solarSensor.begin(); OutsideSensor.begin(); // I2C-Schnittstelle starten Wire.begin(SLAVE_ADDRESS); Wire.onRequest(requestEvent);}void loop(void) {// Überwachen Sie die Zeit zum Lesen der Temperatursensoren einmal in jedem definierten Intervall // Lesen Sie sie nicht schneller als alle 1 Sekunde. sie können nicht so schnell antworten unsigned long currMillis =millis(); if (currMillis - prevMillis> Intervall) { prevMillis =currMillis; readTemperaturen(); }}void readTemperatures () {// Alle drei Temperatursensoren lesen poolSensor.requestTemperatures (); solarSensor.requestTemperatures(); OutsideSensor.requestTemperatures(); // Temperaturdaten in einer Zeichenfolge speichern // Wir füllen den Puffer bis zur vollen Länge auf, um sicherzustellen, dass alte Daten überschrieben werden // Die Daten haben das Format "88.99|78.12|100.00", wobei "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.csC#
Definiert den HTTP-Server, der auf HTTP-POST- und GET-Anfragen antwortet
// Copyright (c) Microsoft. Alle Rechte vorbehalten.Verwendung von System;Verwendung von System.Collections.Generic;Verwendung von System.Linq;Verwendung von System.Text;Verwendung von System.Net.Http;Verwendung von Windows.Foundation.Collections;Verwendung von Windows.ApplicationModel.Background;Verwendung von Windows.ApplicationModel. AppService;mit Windows.System.Threading;mit Windows.Networking.Sockets;mit System.IO;mit Windows.Storage.Streams;mit System.Threading.Tasks;mit System.Runtime.InteropServices.WindowsRuntime;mit Windows.Foundation;mit Windows.Devices.Gpio;namespace WebServerTask{ öffentliche versiegelte Klasse WebServerBGTask :IBackgroundTask { public void Run(IBackgroundTaskInstance taskInstance) { // Verknüpfen Sie einen Abbruchhandler mit der Hintergrundaufgabe. taskInstance.Canceled +=OnCanceled; // Hole das Verzögerungsobjekt von der Aufgabeninstanz serviceDeferral =taskInstance.GetDeferral(); var appService =taskInstance.TriggerDetails as AppServiceTriggerDetails; if (appService !=null &&appService.Name =="App2AppComService") { appServiceConnection =appService.AppServiceConnection; appServiceConnection.RequestReceived +=OnRequestReceived; } } //Verarbeitet Nachrichtenanfragen, die von PoolWebService App gesendet wurden privat async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var message =args.Request.Message; string Befehl =Nachricht["Befehl"] as string; switch (Befehl) { case "Initialize":{ Sensors.InitSensors(); Devices.InitDevices(); var messageDeferral =args.GetDeferral(); //Ein Ergebnis setzen, das an den Aufrufer zurückgegeben wird var returnMessage =new ValueSet(); //Definieren einer neuen Instanz unseres HTTPServers auf Port 8888 HttpServer server =new HttpServer(8888, appServiceConnection); IAsyncAction asyncAction =Windows.System.Threading.ThreadPool.RunAsync( (workItem) => { //Starte den Server server.StartServer(); }); //An PoolWebService mit einem Erfolgsstatus antworten returnMessage.Add("Status", "Success"); var responseStatus =wait args.Request.SendResponseAsync(returnMessage); messageDeferral.Complete(); brechen; } case "Quit":{ //Der Dienst wurde zum Beenden aufgefordert. Geben Sie uns Dienstaufschub // damit die Plattform die Hintergrundaufgabe beenden kann serviceDeferral.Complete(); brechen; } } } private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason Grund) { //Aufräumen und fertig zum Beenden} BackgroundTaskDeferral serviceDeferral; AppServiceConnection appServiceConnection; } //Klasse zum Definieren der öffentlichen versiegelten HTTP-Webserver-Klasse HttpServer :IDisposable { //Erzeuge einen Puffer zum Lesen von HTTP-Daten private const uint BufferSize =8192; //Port zum Abhören des privaten int-Ports =8888; //Listener to private readonly StreamSocketListener listener; //Verbindung zum Senden von Statusinformationen an PoolControllerWebService private AppServiceConnection appServiceConnection; public HttpServer(int serverPort, AppServiceConnection-Verbindung) { listener =new StreamSocketListener(); Port =ServerPort; appServiceConnection =Verbindung; //Ereignishandler für HTTP-Verbindungen hinzufügen listener.ConnectionReceived +=(s, e) => ProcessRequestAsync(e.Socket); } //Aufruf zum Starten des Listenners public void StartServer() {#pragma warning deaktivieren 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(); // Hole die eingehenden Daten mit (IInputStream input =socket.InputStream) { byte[] data =new byte[BufferSize]; IBuffer-Puffer =data.AsBuffer(); uint dataRead =BufferSize; //Alle eingehenden Daten lesen while (dataRead ==BufferSize) { wait input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial); request.Append(Encoding.UTF8.GetString(data, 0, data.Length)); dataRead =Puffer.Länge; } } //Die Daten starten mit der Verarbeitung einer Antwort mit (IOutputStream output =socket.OutputStream) { string requestMethod =request.ToString(); string[] requestParts ={ "" }; if (requestMethod !=null) { //Beakup die Anfrage in ihre Teile requestMethod =requestMethod.Split('\n')[0]; requestParts =requestMethod.Split(' '); } //Wir antworten nur auf HTTP GETS- und POST-Methoden, wenn (requestParts[0] =="GET") await WriteGetResponseAsync(requestParts[1], output); else if (requestParts[0] =="POST") wait WritePostResponseAsync(requestParts[1], Ausgabe); sonst erwarten WriteMethodNotSupportedResponseAsync(requestParts[1], Ausgabe); } } catch (Exception) { } } // Behandelt alle privaten asynchronen HTTP-GET-Aufgaben WriteGetResponseAsync(string request, IOutputStream os) { bool urlFound =false; byte[] bodyArray =null; string antwortMsg =""; // Prüfen Sie, ob die Anforderung mit einer der gültigen Anforderungs-URLs übereinstimmt, und erstellen Sie den Antwortnachrichtenschalter (request.ToUpper()) { case "/SENSORS/POOLTEMP":responseMsg =Sensors.PoolTemperature; urlFound =true; brechen; Fall "/SENSORS/SOLARTEMP":responseMsg =Sensors.SolarTemperature; urlFound =true; brechen; Fall "/SENSORS/OUTSIDETEMP":responseMsg =Sensors.OutsideTemperature; urlFound =true; brechen; Fall "/DEVICES/POOLPUMP/STATE":responseMsg =Devices.PoolPumpState; urlFound =true; brechen; Fall "/DEVICES/WATERFALLPUMP/STATE":responseMsg =Devices.PoolWaterfallState; urlFound =true; brechen; Fall "/DEVICES/POOLLIGHTS/STATE":responseMsg =Devices.PoolLightsState; urlFound =true; brechen; Fall "/DEVICES/YARDLIGHTS/STATE":responseMsg =Devices.YardLightsState; urlFound =true; brechen; Fall "/DEVICES/POOLSOLAR/STATE":responseMsg =Devices.PoolSolarValveState; urlFound =true; brechen; Standard:urlFound =false; brechen; } bodyArray =Encoding.UTF8.GetBytes(responseMsg); wait WriteResponseAsync(request.ToUpper(), responseMsg, urlFound, bodyArray, os); } //Verarbeitet alle privaten asynchronen HTTP-POST-Aufgaben WritePostResponseAsync(string request, IOutputStream os) { bool urlFound =false; byte[] bodyArray =null; string antwortMsg =""; //Überprüfen, ob die Anforderung mit einer der gültigen Anforderungs-URLs übereinstimmt, und erstellen Sie den Antwortnachrichtenschalter (request.ToUpper()) { case "/DEVICES/POOLPUMP/OFF":Devices.PoolPumpPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/POOLPUMP/ON":Devices.PoolPumpPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="ON"; urlFound =true; brechen; Fall "/DEVICES/WATERFALLPUMP/OFF":Devices.PoolWaterfallPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/WATERFALLPUMP/ON":Devices.PoolWaterfallPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="ON"; urlFound =true; brechen; Fall "/DEVICES/POOLLIGHTS/OFF":Devices.PoolLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/POOLLIGHTS/ON":Devices.PoolLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/YARDLIGHTS/OFF":Devices.YardLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/YARDLIGHTS/ON":Devices.YardLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/POOLSOLAR/OFF":Devices.PoolSolarValvePinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="OFF"; urlFound =true; brechen; Fall "/DEVICES/POOLSOLAR/ON":Devices.PoolSolarValvePinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="ON"; urlFound =true; brechen; Standardwert:bodyArray =Encoding.UTF8.GetBytes(""); urlFound =false; brechen; } wait WriteResponseAsync(request.ToUpper(), responseMsg, urlFound,bodyArray, os); } //Antwort für nicht unterstützte HTTP-Methoden schreiben private async Task WriteMethodNotSupportedResponseAsync(string request, IOutputStream os) { bool urlFound =false; byte[] bodyArray =null; bodyArray =Encoding.UTF8.GetBytes(""); wait WriteResponseAsync(request.ToUpper(), "NICHT UNTERSTÜTZT", urlFound, bodyArray, os); } //Schreibe die Antwort für die private asynchrone Aufgabe von HTTP GET und POST WriteResponseAsync(string RequestMsg, string ResponseMsg, bool urlFound, byte[] bodyArray, IOutputStream os) { try //Der AppService wird nach etwa einem Tag sterben. Versuchen wir es separat abzufangen, damit der http-Server immer noch antwortet { var updateMessage =new ValueSet(); updateMessage.Add("Request", RequestMsg); updateMessage.Add("Antwort", AntwortMsg); var responseStatus =wait appServiceConnection.SendMessageAsync(updateMessage); } catch (Ausnahme) {} try { MemoryStream bodyStream =new MemoryStream(bodyArray); using (Stream-Antwort =os.AsStreamForWrite()) { string header =GetHeader(urlFound, bodyStream.Length.ToString()); byte[] headerArray =Encoding.UTF8.GetBytes(header); Antwort erwarten.WriteAsync(headerArray, 0, headerArray.Length); if (urlFound) wait bodyStream.CopyToAsync(Antwort); Antwort erwarten.FlushAsync(); } } catch(Exception) {} } //Erzeugt den HTTP-Header-Text für gefundene und nicht gefundene 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" + " Inhalts-Länge:" + bodyStreamLength + "\r\n" + "Verbindung:schließen\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 -Länge:0\r\n" + "Verbindung schließen\r\n\r\n"; } Kopfzeile zurückgeben; } }}
PoolWebServer - Devices.csC#
Die Klasse definiert alle Geräte und mit welchen GPIO-Pins sie verbunden sind
mit System;mit System.Collections.Generic;mit System.Linq;mit System.Text;mit System.Threading.Tasks;mit Windows.Devices.Gpio; namespace WebServerTask{ //Klasse die definiert alle Geräte und mit welchen GPIO-Pins sie verbunden sind. öffentliche statische Klasse Geräte {//Definieren der GPIO-Pinsnummern 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; // Definiere die GPIO-Pins privates statisches GpioPin poolPumpPin; privater statischer GpioPin-PoolWaterfallPin; privater statischer GpioPin-PoolLightsPin; privater statischer GpioPin yardLightsPin; privater statischer GpioPin-PoolSolarValvePin; // Eigenschaft für den GPIO-Pin, der der Poolpumpe zugewiesen ist, öffentlicher statischer GpioPinValue PoolPumpPinValue { get { return poolPumpPin.Read (); // Den Pin lesen gibt High oder Low zurück} set { if (poolPumpPin.Read () ! =Wert) // Setze den Pin nur, wenn sich poolPumpPin.Write (Wert) ändert; } } //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

Kundenspezifische Teile und Gehäuse

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

Schaltpläne

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.

Herstellungsprozess

  1. Temperaturüberwachung auf dem Raspberry Pi
  2. Raspberry Pi 2 Wetterstation
  3. Überwachen der Temperatur mit Raspberry Pi
  4. 433 MHz Smart Home Controller mit Sensorflare und einem RaspberryPi
  5. Himbeer-Pi-Ball-Tracking
  6. Raspberry Pi Universal-Fernbedienung
  7. Bewegungssensor mit Raspberry Pi
  8. Ein Stück Himbeer-Pi
  9. Cycle Chaser
  10. Raspberry Pi Bodenfeuchtigkeitssensor