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

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Komponenten und Zubehör

Komponente Menge
Raspberry Pi 2 Model B 1
PNY 16GB Turbo MicroSDXC CL10 1
SparkFun Arduino Pro Mini 328 – 5V/16MHz 1
SainSmart 5V 4-Kanal Solid-State-Relaisplatine 1
Tolako 5V Relaismodul für Arduino 1
DS18B20 wasserdichte Temperatursensoren 1
4,7 kOhm Widerstände – 1/4 Watt – 5% (10 Stück) 1
Raspberry Pi USB WLAN-Dongle 1
USB-Verlängerungskabel A-Stecker auf A-Buchse, 30 cm 1
American Valve CL40PK6 Schlauchschellen Nr. 40, 6er-Pack 1
J-B Weld 8272 MarineWeld Epoxidharz – 56 g 1
Sitzdichtung (Seat Washer) 2
Micro-USB-Netzteil (Wandladegerät) 1

Benötigte Werkzeuge und Maschinen

Apps und Online-Dienste

Über das Projekt

Automatisierter Pool-Controller

Innerhalb von drei Monaten fiel die Zeitschaltuhr meiner Poolpumpe zweimal aus. Die Ersetzungskosten beliefen sich auf über 120 $, und das Ergebnis war eine unzuverlässige Steuerung mit minimalen Funktionen. Hinzu kam der Ausfall des Temperatursensors meiner Solarwassererwärmung (weitere 30 $).

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Ich entwickelte eine kostengünstige, automatisierte Poolsteuerung, die weit mehr Kontrolle bietet als eine einfache Zeitschaltuhr. Ziel war es, die Pumpenlaufzeit von zusätzlichen Variablen abhängig zu machen: Außentemperatur, Solarkollektor- und Poolwassertemperatur. Besonders wichtig: Bei Frostgefahr müssen Pool- und Wasserfallpumpen laufen, um Rohrbrüche zu vermeiden – auch wenn man nicht zu Hause ist. Das System sollte von jedem Gerät weltweit bedienbar sein.

Die Lösung basiert auf einem Raspberry Pi mit Windows 10 IoT Core, Relaisplatinen, einem Arduino Pro Mini, Temperatursensoren, Verkabelung und 3D-gedruckten Komponenten. Die Gesamtkosten lagen weit unter den Ausgaben für die beiden defekten Timer und den Solar-Sensor.

Poolpumpen-Steuerung (AC-Komponenten)

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Der Einstieg erfolgte über Solid-State-Relais (SSR), die vom Raspberry Pi (GPIO) angesteuert werden. Diese SSRs schalten die vorhandenen 30 A-AC-Relais der alten Zeitschaltuhr. Nach erfolgreichem Test der Poolpumpensteuerung wurde die Funktionalität auf Wasserfall, Pool- und Gartenbeleuchtung erweitert. Alle Elemente lassen sich nun ferngesteuert bedienen – kein Öffnen des Steuerkastens mehr nötig.

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Gehäuse des Pool-Controllers

Mein Sohn entwarf das Gehäuse und druckte es auf unserem 3D-Drucker. Raspberry Pi und SSRs sitzen sicher und passgenau im Gehäuse.

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Temperatursensoren

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Drei DS18B20-Sensoren erfassen: Außentemperatur, Poolwassertemperatur und Solarkollektor-Temperatur. Der Arduino Pro Mini liest die Sensoren über 1-Wire aus und sendet die Daten per I²C an den Raspberry Pi. So können Entscheidungen auf Basis echter Umgebungsdaten getroffen werden – z. B. Pumpenlauf bei Frostgefahr.

Außentemperatursensor

Der Außensensor wurde in einem 3D-gedruckten Halter montiert. ABS-Filament eignet sich besser als PLA (wetterbeständiger, höhere Glasübergangstemperatur). Wichtig: Mindestens 75 % Infill drucken. Der Sensor wird wie im Schaltplan gezeigt angeschlossen.

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Wassertemperatursensoren

Für Poolwasser und Solarerwärmer wurden baugleiche Sensorhalter gedruckt (ebenfalls ABS, ≥75 % Infill).

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Aufbau und Einbau der Wassertemperatursensoren

  1. Nach dem Druck den Sensorloch mit einem Senkbohrer (45°) aufweiten – raue Oberfläche verbessert die Haftung von JB Weld.
  2. Sensor in den Halter schieben, bis er ca. 19 mm (¾ Zoll) herausragt. Sitzdichtung aufsetzen.
  3. Halter oben mit JB Weld auffüllen, 24 Stunden aushärten lassen.
  4. WICHTIG: Alle Pumpen ausschalten, bevor die Sensoren in die Rohrleitung eingebaut werden!
  5. Druck ablassen (Ventile öffnen), 8 mm (5/16 Zoll) Loch in das Poolrohr bohren, Sensor mit zwei Schlauchschellen fixieren – nicht zu fest anziehen, sonst bricht der Halter.
  6. Ventile schließen, Pumpen starten, auf Dichtheit prüfen.
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Solarerwärmer-Ventilsteuerung

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Das Solarventil arbeitet mit Gleichspannung (DC), daher wurde ein DC-Relais statt eines AC-Relais verwendet. Die Logik: Wasser wird nur zum Solarkollektor geleitet, wenn die Außentemperatur > 16 °C (60 °F) beträgt. Zudem muss das Rücklaufwasser mind. 2 °C wärmer sein als das Poolwasser – sonst lohnt die Umwälzung nicht. Der Schaltplan findet sich im Abschnitt „Schematics“.

Anwendungsentwicklung

Nach Installation von Windows 10 IoT Core stellte sich die Frage, ob der integrierte Webserver eine reduzierte IIS-Version ist und sich für REST-Services nutzen ließe. Recherchen ergaben: nicht machbar. Stattdessen wurde das Beispiel „Blinky Web Server“ und ein Artikel auf „Druss Blog“ als Basis genommen. Entstanden ist eine kopflose Windows 10 IoT Core Background Application, die als simpler HTTP-Server auf GET/POST reagiert. Innerhalb weniger Tage lief ein funktionsfähiger Prototyp.

Bereitstellung der Anwendung

Die Bereitstellung bereitete Schwierigkeiten: Im Debug-Modus (Visual Studio 2015) lief alles, aber nach dem Wechsel auf Release und Start über den AppX Manager erschien nur „Application failed to initialize“. Lösung: Vorhandene Bereitstellung löschen, dann die App direkt über den AppX Manager installieren. Auch danach stürzte die App nach dem ersten HTTP-Request ab. Der Workaround: Architektur wie beim „Blinky Web Server“ – eine UI-App startet den Webserver im Hintergrund und empfängt Statusmeldungen per AppService. Dieser Ansatz läuft stabil.

Benutzeroberfläche

Eine HTML-Steuerseite läuft auf praktisch jedem Gerät und ermöglicht Steuerung (Pumpe, Wasserfall, Beleuchtung) sowie Überwachung aller Sensoren von überall.

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Zusätzlich wurde eine openHAB-Sitemap erstellt, die eine alternative Oberfläche bietet.

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Vielen Dank fürs Lesen – viel Spaß beim Nachbauen!

Code

Arduino Sketch für Temperatursensoren (I²C)

C++

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>

#define SLAVE_ADDRESS 0x40
const int POOL_PIN = 3;
const int SOLAR_PIN = 5;
const int OUTSIDE_PIN = 7;
const int I2C_BUFFER_LEN = 24; // MAX 32!

OneWire poolTemp(POOL_PIN);
OneWire solarTemp(SOLAR_PIN);
OneWire outsideTemp(OUTSIDE_PIN);

DallasTemperature poolSensor(&poolTemp);
DallasTemperature solarSensor(&solarTemp);
DallasTemperature outsideSensor(&outsideTemp);

char data[I2C_BUFFER_LEN];
String temperatureData;
long prevMillis = 0;
long interval = 1000;

void setup() {
  poolSensor.begin();
  solarSensor.begin();
  outsideSensor.begin();
  Wire.begin(SLAVE_ADDRESS);
  Wire.onRequest(requestEvent);
}

void loop() {
  unsigned long currMillis = millis();
  if (currMillis - prevMillis > interval) {
    prevMillis = currMillis;
    readTemperatures();
  }
}

void readTemperatures() {
  poolSensor.requestTemperatures();
  solarSensor.requestTemperatures();
  outsideSensor.requestTemperatures();
  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 += " ";
  return inStr;
}

void requestEvent() {
  temperatureData.toCharArray(data, I2C_BUFFER_LEN);
  Wire.write(data);
}

PoolWebServer – BackgroundTask.cs

C#


// 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) {
      taskInstance.Canceled += OnCanceled;
      serviceDeferral = taskInstance.GetDeferral();
      var appService = taskInstance.TriggerDetails as AppServiceTriggerDetails;
      if (appService != null && appService.Name == "App2AppComService") {
        appServiceConnection = appService.AppServiceConnection;
        appServiceConnection.RequestReceived += OnRequestReceived;
      }
    }

    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();
          var returnMessage = new ValueSet();
          HttpServer server = new HttpServer(8888, appServiceConnection);
          Windows.System.Threading.ThreadPool.RunAsync((workItem) => { server.StartServer(); });
          returnMessage.Add("Status", "Success");
          await args.Request.SendResponseAsync(returnMessage);
          messageDeferral.Complete();
          break;
        }
        case "Quit": {
          serviceDeferral.Complete();
          break;
        }
      }
    }

    private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { }
    BackgroundTaskDeferral serviceDeferral;
    AppServiceConnection appServiceConnection;
  }

  public sealed class HttpServer : IDisposable {
    private const uint BufferSize = 8192;
    private int port = 8888;
    private readonly StreamSocketListener listener;
    private AppServiceConnection appServiceConnection;

    public HttpServer(int serverPort, AppServiceConnection connection) {
      listener = new StreamSocketListener();
      port = serverPort;
      appServiceConnection = connection;
      listener.ConnectionReceived += (s, e) => ProcessRequestAsync(e.Socket);
    }

    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();
        using (IInputStream input = socket.InputStream) {
          byte[] data = new byte[BufferSize];
          IBuffer buffer = data.AsBuffer();
          uint dataRead = BufferSize;
          while (dataRead == BufferSize) {
            await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial);
            request.Append(Encoding.UTF8.GetString(data, 0, data.Length));
            dataRead = buffer.Length;
          }
        }
        using (IOutputStream output = socket.OutputStream) {
          string requestMethod = request.ToString();
          string[] requestParts = { "" };
          if (requestMethod != null) {
            requestMethod = requestMethod.Split('\n')[0];
            requestParts = requestMethod.Split(' ');
          }
          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) { }
    }

    private async Task WriteGetResponseAsync(string request, IOutputStream os) {
      bool urlFound = false;
      byte[] bodyArray = null;
      string responseMsg = "";
      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);
    }

    private async Task WritePostResponseAsync(string request, IOutputStream os) {
      bool urlFound = false;
      byte[] bodyArray = null;
      string responseMsg = "";
      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);
    }

    private async Task WriteMethodNotSupportedResponseAsync(string request, IOutputStream os) {
      byte[] bodyArray = Encoding.UTF8.GetBytes("");
      await WriteResponseAsync(request.ToUpper(), "NOT SUPPORTED", false, bodyArray, os);
    }

    private async Task WriteResponseAsync(string RequestMsg, string ResponseMsg, bool urlFound, byte[] bodyArray, IOutputStream os) {
      try {
        var updateMessage = new ValueSet();
        updateMessage.Add("Request", RequestMsg);
        updateMessage.Add("Response", ResponseMsg);
        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) { }
    }

    string GetHeader(bool urlFound, string bodyStreamLength) {
      if (urlFound) {
        return "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 {
        return "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";
      }
    }
  }
}

PoolWebServer – Devices.cs

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Gpio;

namespace WebServerTask {
  public static class Devices {
    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;

    private static GpioPin poolPumpPin;
    private static GpioPin poolWaterfallPin;
    private static GpioPin poolLightsPin;
    private static GpioPin yardLightsPin;
    private static GpioPin poolSolarValvePin;

    public static GpioPinValue PoolPumpPinValue {
      get { return poolPumpPin.Read(); }
      set { if (poolPumpPin.Read() != value) poolPumpPin.Write(value); }
    }
    public static string PoolPumpState { get { return GetState(PoolPumpPinValue, GpioPinValue.High); } }

    public static GpioPinValue PoolWaterfallPinValue {
      get { return poolWaterfallPin.Read(); }
      set { if (poolWaterfallPin.Read() != value) poolWaterfallPin.Write(value); }
    }
    public static string PoolWaterfallState { get { return GetState(PoolWaterfallPinValue, GpioPinValue.High); } }

    public static GpioPinValue PoolLightsPinValue {
      get { return poolLightsPin.Read(); }
      set { if (poolLightsPin.Read() != value) poolLightsPin.Write(value); }
    }
    public static string PoolLightsState { get { return GetState(PoolLightsPinValue, GpioPinValue.High); } }

    public static GpioPinValue PoolSolarValvePinValue {
      get { return poolSolarValvePin.Read(); }
      set { if (poolSolarValvePin.Read() != value) poolSolarValvePin.Write(value); }
    }
    public static string PoolSolarValveState { get { return GetState(PoolSolarValvePinValue, GpioPinValue.High); } }

    public static GpioPinValue YardLightsPinValue {
      get { return yardLightsPin.Read(); }
      set { if (yardLightsPin.Read() != value) yardLightsPin.Write(value); }
    }
    public static string YardLightsState { get { return GetState(YardLightsPinValue, GpioPinValue.High); } }

    public static void InitDevices() {
      var gpio = GpioController.GetDefault();
      if (gpio != null) {
        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);
      }
    }

    private static string GetState(GpioPinValue value, GpioPinValue ActiveState) {
      return (value == ActiveState) ? "ON" : "OFF";
    }
  }
}

PoolWebServer – Sensors.cs

C#

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 {
  public static class Sensors {
    private static I2cDevice Device;
    private static Timer periodicTimer;
    private static int ReadInterval = 4000; // 4 Sekunden

    private static string poolTemperature = "--.--";
    private static string solarTemperature = "--.--";
    private static string outsideTemperature = "--.--";

    public static string PoolTemperature {
      get { lock (poolTemperature) { return poolTemperature; } }
      set { lock (poolTemperature) { poolTemperature = value; } }
    }
    public static string SolarTemperature {
      get { lock (solarTemperature) { return solarTemperature; } }
      set { lock (solarTemperature) { solarTemperature = value; } }
    }
    public static string OutsideTemperature {
      get { lock (outsideTemperature) { return outsideTemperature; } }
      set { lock (outsideTemperature) { outsideTemperature = value; } }
    }

    async public static void InitSensors() {
      var settings = new I2cConnectionSettings(0x40);
      settings.BusSpeed = I2cBusSpeed.StandardMode;
      string aqs = I2cDevice.GetDeviceSelector("I2C1");
      var dis = await DeviceInformation.FindAllAsync(aqs);
      Device = await I2cDevice.FromIdAsync(dis[0].Id, settings);
      periodicTimer = new Timer(TimerCallback, null, 0, ReadInterval);
    }

    private static void TimerCallback(object state) {
      byte[] RegAddrBuf = new byte[] { 0x40 };
      byte[] ReadBuf = new byte[24];
      try { Device.Read(ReadBuf); } catch (Exception) { }
      char[] cArray = System.Text.Encoding.UTF8.GetString(ReadBuf, 0, 23).ToCharArray();
      String c = new String(cArray).Trim();
      string[] data = c.Split('|');
      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#

// 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.";
      appServiceConnection = new AppServiceConnection();
      appServiceConnection.PackageFamilyName = "PoolWebServer_hz258y3tkez3a";
      appServiceConnection.AppServiceName = "App2AppComService";
      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 {
  sealed partial class App : Application {
    public App() { InitializeComponent(); Suspending += OnSuspending; }
    protected override void OnLaunched(LaunchActivatedEventArgs e) {
#if DEBUG
      if (System.Diagnostics.Debugger.IsAttached) DebugSettings.EnableFrameRateCounter = true;
#endif
      Frame rootFrame = Window.Current.Content as Frame;
      if (rootFrame == null) {
        rootFrame = new Frame();
        rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0];
        rootFrame.NavigationFailed += OnNavigationFailed;
        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { /* Load state */ }
        Window.Current.Content = rootFrame;
      }
      if (rootFrame.Content == null) rootFrame.Navigate(typeof(MainPage), e.Arguments);
      Window.Current.Activate();
    }
    void OnNavigationFailed(object sender, NavigationFailedEventArgs e) {
      throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
    }
    private void OnSuspending(object sender, SuspendingEventArgs e) {
      var deferral = e.SuspendingOperation.GetDeferral(); deferral.Complete();
    }
  }
}

openHAB Sitemap

JavaScript

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

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

GitHub Repository: Windows-10-IoT-PoolController

3D-Druck-Teile & Gehäuse

Schaltpläne

Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör
Umfassendes Pool-Controller-Kit – Raspberry Pi 2 + Arduino, Relaisplatinen, Temperatursensoren & Zubehör

Herstellungsprozess

  1. Professionelle Temperaturüberwachung mit Raspberry Pi und DS18B20
  2. Outdoor-Wetterstation mit Raspberry Pi 2 & ADS-WS1 bauen
  3. Präzise Temperaturüberwachung im Server-Schrank mit Raspberry Pi
  4. 433 MHz Smart-Home-Controller mit Sensorflare und Raspberry Pi selbst bauen
  5. Fortgeschrittenes Tischtennisball-Tracking mit Raspberry Pi und OpenCV
  6. Professionelle Raspberry Pi Universalfernbedienung mit LIRC bauen
  7. LED mit PIR-Bewegungsmelder am Raspberry Pi steuern
  8. RF zu DC mit Raspberry Pi wandeln: Hüllkurvendetektor bauen & troubleshooten
  9. Cycle Chaser: Fahrrad zur nächtlichen Lichtshow mit Raspberry Pi verwandeln
  10. Günstiger Raspberry Pi Bodenfeuchtesensor für smarte Bewässerung