亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Manufacturing Technology >> 制造工艺

池控制器

组件和用品

Raspberry Pi 2 B 型
× 1
PNY 16GB Turbo MicroSDXC CL10
× 1
SparkFun Arduino Pro Mini 328 - 5V/16MHz
× 1
SainSmart 5V 4 通道固态继电器板
× 1
Tolako 5v Arduino 继电器模块
× 1
DS18b20 防水温度传感器
× 1
4.7k 欧姆电阻 - 1/4 瓦 - 5% - 4K7(10片)
× 1
Raspberry Pi USB WIFI Dongle
× 1
男对女延长线 1 英尺 Usb
× 1
美国阀门 CL40PK6 40 号夹子,6 件装
× 1
J-B Weld 8272 MarineWeld 船用环氧树脂 - 2 盎司
× 1
座垫
× 2
Micro USB 电源壁式充电器交流适配器
× 1

必要的工具和机器

Printrbot Simple
用于创建外壳和传感器安装
Ftdi Usb to Ttl Arduino Mini Port 串行适配器模块
用于上传草图到Arduino Mini Pro

应用和在线服务

Microsoft Windows 10 IoT Core
Microsoft Visual Studio 2015
微软 IIS
Arduino IDE
OneWire 库
达拉斯温度库
openHAB 开源家庭自动化软件

关于这个项目

自动池控制器

在三个月的时间里,我的泳池泵的计时器出现了两次故障。这启发了我创建这个项目。更换这些计时器的成本超过 120 美元,而我所要展示的只是一个给我很少控制和高故障率的计时器。我的太阳能热水器上的温度传感器也出现故障,额外花费了 30 美元。

我知道我可以创建一个具有成本效益的自动泳池控制器,让我可以更好地控制泳池泵的运行时间。我希望有更多关于泵何时运行的变量,而不是现有计时器的简单时间和日期。我还希望不仅能够使我的泳池泵自动化,而且还可以监控我的泳池环境各个方面的状态。另一个目标是能够从任何地方使用任何设备完成这些任务。

我创建的项目非常具有成本效益,因为它利用了运行 Windows 10 IoT Core、继电器、Arduino Mini Pro 以及温度传感器、布线和 3D 打印组件的 Raspberry Pi。我完成这个项目的成本远低于我为前两个计时器和太阳能温度传感器支付的费用。

池泵控制(交流组件)

我通过从运行 Windows 10 IoT Core 的 Raspberry Pi 控制固态继电器开始了我的项目。这些继电器允许我控制我的 AC(交流)组件,例如泳池泵。固态继电器控制旧计时器使用的现有 30Amp 交流继电器。在设计并测试了泳池泵的电路后,我创建了额外的功能来控制其他交流组件,例如泳池瀑布、泳池和庭院灯。通过设计项目的这一部分,我可以远程控制所有这些元素。我的家人或我不再需要亲自打开控制箱才能打开瀑布、打开泳池或庭院灯或设置泳池泵的计时器。

池控制器外壳

我儿子设计了池控制器外壳并使用我们的 3D 打印机创建它,并确保 Raspberry Pi 和固态继电器都安全地安装在控制器盒中。

温度传感器

myproject 的设计目标之一是允许基于除日期和时间之外的变量进行控制。我希望能够将室外气温以及太阳能热水器和泳池水温考虑在内,以确定泵何时运行以及何时停止。此类操作至关重要的一个示例是,当外部气温非常冷且接近冰点时。如果泳池水温也接近冰点,我需要确保我的泳池和瀑布泵正在运行,以防止管道冻结和损坏系统。使用这个项目,即使我不在家,我也能做到这一点。为了实现这一点,我在我的项目中加入了温度传感器。我使用 Arduino Mini Pro 读取这些传感器,它将数据发送到通过 I2C 接口控制游泳池和瀑布泵的同一个 Raspberry Pi。

室外空气温度传感器

室外空气温度传感器是我集成的第一个传感器。同样,我儿子在我们的 3D 打印机上设计并打印了传感器支架。他尝试了 PLA 和 ABS,实际上 ABS 效果更好,因为它更耐候,并且具有更高的玻璃化转变温度,使其更耐热。 确保您的打印填充率至少为 75%。 传感器的连接方式如上述示意图中所述。

水温传感器

然后我结合了泳池水和太阳能加热器温度传感器。这将允许该项目收集将显示给用户的水温数据,并提供进一步的变量以确定某些组件何时运行或处于静止状态。首先,设计并3D打印传感器安装座。如前所述,由于更好的耐候性和耐热性,ABS 实际上效果更好。另外确保您使用至少 75% 的填充物 .

构建和安装水温传感器

打印水温传感器安装座后,我使用埋头钻在传感器孔周围创建了一个 45 度的区域。这将使 JB Weld 有更多的表面积可以粘附。我更喜欢用钻头来做这个而不是改变 3D 打印设计,因为钻头的粗切似乎给了 JB Weld 更好的保持力。

下一步是将温度传感器插入底座,直到它从底座底部伸出约 3/4 英寸。添加座椅垫圈以将其固定到位。

接下来,用 JB Weld 填充底座的顶部并让其干燥 24 小时。

在等待 JB Weld 干燥至少 24 小时后,是时候安装水温传感器了。

重要提示: 在尝试安装水温传感器之前,确保所有泵都关闭!

在确保所有水泵都关闭后,最好打开任何可以从安装水温传感器的区域去除水压的阀门。这将大大简化安装(并保持干燥)。

在泳池管道上钻一个 5/16" 的孔。安装水温传感器并使用 2 个夹子将其牢牢固定到位。不要犯和我一样的错误并过度拧紧夹子,过度拧紧会压碎传感器安装座。关闭阀门并打开泵。检查是否有泄漏。

太阳能热水器阀门控制

安装好温度传感器后,我就可以设计和安装太阳能热水器阀门控制装置。太阳能热水器使用直流电压,而不是前面提到的其他池组件使用的交流电压。这需要我控制直流继电器而不是交流继电器。概念相似,但所需的继电器不同。确保您用于项目的继电器将控制您所控制的设备使用的正确类型的电压。

此控件允许我将池水引导至屋顶上的太阳能电池板。我只想在室外空气温度高于 60 度时将水引导到面板上。一旦水被转移到面板,确保返回的水至少比池水高 2 度。否则,将水抽到面板上是浪费能源。

池控制器直流组件示意图中提供了此控件的接线和连接。

应用开发

在我的 Raspberry Pi 上安装 Windows 10IoT Core 后,我意识到它有一个内置的 Web 服务器用于管理它。我想知道这是否是 IIS 的精简版?如果是这样,我可以在 IIS 上编写一些宁静的服务并为这个项目调用它们。经过多次网络搜索和大量研究,这似乎是不可能的。我更喜欢这种方法,但目前看来并不可行。

采取不同的方法,我查看了示例“Blinky Web Server”和一篇关于“Druss 博客”的文章。我决定构建一个无头的 Windows 10 IoT 核心后台应用程序,作为响应 HTTP GET 和 POST 请求的简单 HTTP Web 服务器.

几天之内,我就有了一个工作原型。这让我对我的项目能够成功充满信心。所以我决定继续推进这个架构。在通过 Visual Studio 2015 调试器彻底测试我的代码后,我的印象是我可以轻松部署我的应用程序。

部署应用程序

这是我纠结的一点,所以我希望向您展示如何避免这种困难。因为我的应用程序在 Visual Studio 2015 调试器中进行了全面测试,所以我的印象是我可以将模式从调试更改为发布来部署我的应用程序。我尝试了这种方法,它确实部署了我的应用程序并以调试模式启动它。然后我停止调试并尝试从 AppX 管理器运行应用程序。我尝试这样做时没有成功,我只会收到一个通用错误,指出“应用程序初始化失败。”

此问题的解决方案是删除当前部署,然后从 AppX 管理器安装应用程序。这花费了我很多时间,所以我希望这可以帮助您避免这个问题。

即使应用程序在 Visual Studio 2015 调试模式下完美运行,它也会在收到第一个 HTTP 请求后终止。我花了很多时间试图解决这个问题,但仍然不知道为什么会发生这种情况。

感到完成这个项目的压力,我决定将我的项目更改为就像“Blinky Web Server”示例一样。在我的实现中,我没有看到需要 Windows 10 IoT Core 屏幕应用程序,因为我计划让 Web 服务器控制 GPIO 引脚并读取 I2C 接口(不是屏幕应用程序)。我在我的项目中所做的是让屏幕应用程序启动 Web 服务器。然后,Web 服务器将消息发送回屏幕应用程序,以便我可以查看服务器接收到的 HTTP 调用。这种方法似乎坚如磐石,它与我在最初的尝试中使用的代码完全相同。

用户界面

最后,我构建了一个几乎可以在任何设备上运行的 HTML 控制程序。这让我不仅可以控制泳池泵、瀑布和泳池灯,还可以从任何地方监控额外的传感器。

后来,我使用 OpenHAB 并创建了一个站点地图,为我提供了这个额外的界面。

我希望你喜欢阅读我的项目,就像我创造它一样。谢谢。

YouTube、Vimeo 或 Vine 链接,然后按 Enter

代码

  • 使用 I2C 的温度传感器的 Arduino 草图
  • PoolWebServer - BackgroundTask.cs
  • PoolWebServer - Devices.cs
  • PoolWebServer - Sensors.cs
  • PoolWebService- MainPage.xaml.cs
  • PoolWebService - App.xaml.cs
  • OpenHAB 站点地图
  • OpenHAB 项目
使用 I2C 的温度传感器的 Arduino 草图 Java
读取 DS18b20 温度传感器并在通过 I2C 接口请求时发送数据的代码。
#include #include #include #define SLAVE_ADDRESS 0x40 //定义 GPIO 引脚constantsconst int POOL_PIN =3;const int SOLAR_PIN =5;const int OUTSIDE_PIN =7;//定义I2C接口的缓冲区长度const int I2C_BUFFER_LEN =24; //IMPORTANT MAX is 32!!!//加载 OneWire - 专有的达拉斯半导体传感器协议 - 无需许可OneWire poolTemp(POOL_PIN);OneWire solarTemp(SOLAR_PIN);OneWire outsideTemp(OUTSIDE_PIN);//加载达拉斯 - 专有的达拉斯传感器协议利用onewire - 无需许可DallasTemperature poolSensor(&poolTemp);DallasTemperature solarSensor(&solarTemp);DallasTemperature outsideSensor(&outsideTemp);//定义I2C bufferchar数据[I2C_BUFFER_LEN];String temperatureData;//定义变量为timelong prevMillis =100long interval; void setup(void) { //连接温度传感器总线 poolSensor.begin(); SolarSensor.begin();外部传感器。开始(); //启动I2C接口Wire.begin(SLAVE_ADDRESS); Wire.onRequest(requestEvent);}void loop(void) { //每定义的间隔监控读取温度传感器的时间 //不要超过每 1 秒读取一次。他们无法快速响应 unsigned long currMillis =millis();如果(currMillis - prevMillis> 间隔){ prevMillis =currMillis;读取温度(); }}void readTemperatures() { //读取所有三个温度传感器 poolSensor.requestTemperatures(); solarSensor.requestTemperatures();外部传感器.requestTemperatures(); //将温度数据存储在字符串中 //我们填充到缓冲区的全长以确保覆盖旧数据 //数据采用“88.99|78.12|100.00”格式,其中“PoolTemp|SolarTemp|OutsideTemp”温度数据=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#
定义响应 HTTP POST 和 GET 请求的 HTTP 服务器
// 版权所有 (c) Microsoft。保留所有权利。使用 System;使用 System.Collections.Generic;使用 System.Linq;使用 System.Text;使用 System.Net.Http;使用 Windows.Foundation.Collections;使用 Windows.ApplicationModel.Background;使用 Windows.ApplicationModel。 AppService;使用 Windows.System.Threading;使用 Windows.Networking.Sockets;使用 System.IO;使用 Windows.Storage.Streams;使用 System.Threading.Tasks;使用 System.Runtime.InteropServices.WindowsRuntime;使用 Windows.Foundation;使用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; } } //处理从PoolWebService App 私有异步发送的消息请求 void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var message =args.Request.Message;字符串命令 =消息["命令"] 作为字符串;开关(命令){ case "Initialize":{ Sensors.InitSensors(); Devices.InitDevices(); var messageDeferral =args.GetDeferral(); //设置一个结果返回给调用者 var returnMessage =new ValueSet(); //在端口 8888 上定义我们的 HTTPServer 的新实例 HttpServer server =new HttpServer(8888, appServiceConnection); IAsyncAction asyncAction =Windows.System.Threading.ThreadPool.RunAsync( (workItem) => { //启动服务器 server.StartServer(); }); //以成功的状态响应PoolWebService returnMessage.Add("Status", "Success"); var responseStatus =await args.Request.SendResponseAsync(returnMessage); messageDeferral.Complete();休息; } case "Quit":{ //服务被要求退出。给我们服务延迟 //这样平台就可以终止后台任务 serviceDeferral.Complete();休息; } } } private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { //清理并准备退出 } BackgroundTaskDeferral serviceDeferral; AppServiceConnection appServiceConnection; } //类来定义HTTP WebServer公共密封类HttpServer :IDisposable { //创建一个缓冲区来读取HTTP数据 private const uint BufferSize =8192; //监听私有的端口 int port =8888; //监听到私有只读StreamSocketListener监听器; //Connection 将状态信息发送回 PoolControllerWebService private AppServiceConnection appServiceConnection; public HttpServer(int serverPort, AppServiceConnection connection) { listener =new StreamSocketListener();端口 =服务器端口; appServiceConnection =连接; //为HTTP连接添加事件处理器 listener.ConnectionReceived +=(s, e) => ProcessRequestAsync(e.Socket); } //调用启动listner public void StartServer() {#pragma warning disable CS4014 listener.BindServiceNameAsync(port.ToString());#pragma warning restore CS4014 } public void Dispose() { listener.Dispose(); } private async void ProcessRequestAsync(StreamSocket socket) { try { StringBuilder request =new StringBuilder(); //使用 (IInputStream input =socket.InputStream) { byte[] data =new byte[BufferSize]; 获取传入数据IBuffer 缓冲区 =data.AsBuffer(); uint 数据读取 =缓冲区大小; //读取所有传入的数据 while (dataRead ==BufferSize) { await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial); request.Append(Encoding.UTF8.GetString(data, 0, data.Length)); dataRead =buffer.Length; } } //获取数据开始处理响应使用 (IOutputStream output =socket.OutputStream) { string requestMethod =request.ToString(); string[] requestParts ={ "" }; if (requestMethod !=null) { //将请求拆分成部分 requestMethod =requestMethod.Split('\n')[0]; requestParts =requestMethod.Split(' '); } //我们只响应HTTP的GETS和POST方法 if (requestParts[0] =="GET") await WriteGetResponseAsync(requestParts[1], output); else if (requestParts[0] =="POST") await WritePostResponseAsync(requestParts[1], output);否则等待 WriteMethodNotSupportedResponseAsync(requestParts[1], output); } } catch (Exception) { } } //处理所有HTTP GET的私有异步任务WriteGetResponseAsync(string request, IOutputStream os) { bool urlFound =false; byte[] bodyArray =null;字符串 responseMsg =""; //查看请求是否匹配任何有效的请求url并创建响应消息 switch (request.ToUpper()) { case "/SENSORS/POOLTEMP":responseMsg =Sensors.PoolTemperature; urlFound =真;休息; case "/SENSORS/SOLARTEMP":responseMsg =Sensors.SolarTemperature; urlFound =真;休息; case "/SENSORS/OUTSIDETEMP":responseMsg =Sensors.OutsideTemperature; urlFound =真;休息; case "/DEVICES/POOLPUMP/STATE":responseMsg =Devices.PoolPumpState; urlFound =真;休息; case "/DEVICES/WATERFALLPUMP/STATE":responseMsg =Devices.PoolWaterfallState; urlFound =真;休息; case "/DEVICES/POOLLIGHTS/STATE":responseMsg =Devices.PoolLightsState; urlFound =真;休息; case "/DEVICES/YARDLIGHTS/STATE":responseMsg =Devices.YardLightsState; urlFound =真;休息; case "/DEVICES/POOLSOLAR/STATE":responseMsg =Devices.PoolSolarValveState; urlFound =真;休息;默认值:urlFound =false;休息; bodyArray =Encoding.UTF8.GetBytes(responseMsg);等待 WriteResponseAsync(request.ToUpper(), responseMsg, urlFound, bodyArray, os); } //处理所有HTTP POST的私有异步任务WritePostResponseAsync(string request, IOutputStream os) { bool urlFound =false; byte[] bodyArray =null;字符串 responseMsg =""; //查看请求是否匹配任何有效请求url并创建响应消息 switch (request.ToUpper()) { case "/DEVICES/POOLPUMP/OFF":Devices.PoolPumpPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/POOLPUMP/ON":Devices.PoolPumpPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="开"; urlFound =真;休息; case "/DEVICES/WATERFALLPUMP/OFF":Devices.PoolWaterfallPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/WATERFALLPUMP/ON":Devices.PoolWaterfallPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="开"; urlFound =真;休息; case "/DEVICES/POOLLIGHTS/OFF":Devices.PoolLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/POOLLIGHTS/ON":Devices.PoolLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/YARDLIGHTS/OFF":Devices.YardLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/YARDLIGHTS/ON":Devices.YardLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/POOLSOLAR/OFF":Devices.PoolSolarValvePinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes("OFF"); responseMsg ="关闭"; urlFound =真;休息; case "/DEVICES/POOLSOLAR/ON":Devices.PoolSolarValvePinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes("ON"); responseMsg ="开"; urlFound =真;休息;默认值:bodyArray =Encoding.UTF8.GetBytes(""); urlFound =假;休息;等待 WriteResponseAsync(request.ToUpper(), responseMsg, urlFound,bodyArray, os); } //为不支持的HTTP方法写响应 private async Task WriteMethodNotSupportedResponseAsync(string request, IOutputStream os) { bool urlFound =false; byte[] bodyArray =null; bodyArray =Encoding.UTF8.GetBytes("");等待 WriteResponseAsync(request.ToUpper(), "NOT SUPPORTED", urlFound, bodyArray, os); } //为 HTTP GET 和 POST 的私有异步任务写响应 WriteResponseAsync(string RequestMsg, string ResponseMsg, bool urlFound, byte[] bodyArray, IOutputStream os) { try //appService 将在一天左右后死亡。让我们尝试单独捕获它,这样 http 服务器仍然会响应 { var updateMessage =new ValueSet(); updateMessage.Add("Request", RequestMsg); updateMessage.Add("Response", ResponseMsg); var responseStatus =await appServiceConnection.SendMessageAsync(updateMessage); } catch (Exception) {} try { MemoryStream bodyStream =new MemoryStream(bodyArray); using (Stream response =os.AsStreamForWrite()) { string header =GetHeader(urlFound, bodyStream.Length.ToString()); byte[] headerArray =Encoding.UTF8.GetBytes(header);等待 response.WriteAsync(headerArray, 0, headerArray.Length); if (urlFound) await bodyStream.CopyToAsync(response);等待 response.FlushAsync(); } } catch(Exception) {} } //为找到的和未找到的url创建HTTP头文本 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" + "内容长度:" + bodyStreamLength + "\r\n" + "连接:关闭\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 -长度:0\r\n" + "连接关闭\r\n\r\n"; } 返回标题; } }}
PoolWebServer - Devices.csC#
对定义的所有设备以及它们连接到的 GPIO 引脚进行分类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using Windows.Devices.Gpio; namespace WebServerTask{ // 类定义了所有设备以及它们连接到的 GPIO 引脚。 public static class Devices { //定义GPIO引脚编号 private const int POOL_PUMP_PIN =12;私有常量 int POOL_WATERFALL_PIN =13;私有常量 int POOL_LIGHTS_PIN =16;私有常量 int YARD_LIGHTS_PIN =18;私有常量 int POOL_SOLAR_VALVE_PIN =22; //定义GPIO管脚 private static GpioPin poolPumpPin;私有静态 GpioPin poolWaterfallPin;私有静态 GpioPin poolLightsPin;私有静态 GpioPin yardLightsPin;私有静态 GpioPin poolSolarValvePin; //分配给Pool Pump的GPIO Pin的属性 public static GpioPinValue PoolPumpPinValue { get { return poolPumpPin.Read(); //读取引脚返回高或低 } set { if (poolPumpPin.Read() !=value) //只有在改变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

定制零件和外壳

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

示意图

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.

制造工艺

  1. Raspberry Pi 上的温度监控
  2. Raspberry Pi 2 气象站
  3. 使用 Raspberry Pi 监测温度
  4. 带有 Sensorflare 和 RaspberryPi 的 433MHz 智能家居控制器
  5. Raspberry Pi Ball 跟踪
  6. Raspberry Pi Universal Remote
  7. 使用 Raspberry Pi 的运动传感器
  8. 一片树莓派
  9. Cycle Chaser
  10. Raspberry Pi 土壤湿度传感器
  11. Raspberry Pi Thief Detector
  12. Raspberry Pi 传感器