Kit Pengontrol Kolam Komprehensif – Raspberry Pi 2 + Arduino, Papan Relai, Sensor Suhu & Aksesori
Komponen dan Perlengkapan
Raspberry Pi 2 Model B
×
1
PNY 16GB Turbo MicroSDXC Kelas 10
×
1
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×
1
Papan Relai Status Padat 4-Saluran 5V SainSmart
×
1
Modul Relai 5V Tolako untuk Arduino
×
1
Sensor Suhu Tahan Air DS18b20
×
1
Resistor 4.7k Ohm - 1/4 Watt - 5% - 4K7 (10 Buah)
×
1
Dongle USB WiFi Raspberry Pi
×
1
Kabel Perpanjang USB A-Male ke A-Female 1 Kaki
×
1
Klem Number 40 American Valve CL40PK6, Kemasan 6 Buah
×
1
Epoxi Laut J-B Weld 8272 MarineWeld - 2 ons
×
1
Cakram Duduk
×
2
Adapter Charger Dinding Micro USB AC
×
1
Alat dan Mesin yang Diperlukan
Printrbot Simple
Digunakan untuk membuat kotak pelindung dan pemegang sensor
Modul Adaptor Serial USB ke TTL Ftdi untuk Port Arduino Mini
Digunakan untuk mengunggah sketsa ke Arduino Mini Pro
Aplikasi dan Layanan Online
Microsoft Windows 10 IoT Core
Microsoft Visual Studio 2015
Microsoft IIS
Arduino IDE
OneWire Library
Dallas Temperature Library
openHAB perangkat lunak automasi rumah sumber terbuka
Tentang Proyek Ini
Pengontrol Kolam Otomatis
Dua kali dalam waktu tiga bulan, timer pompa kolam saya gagal. Hal ini mendorong saya untuk membuat proyek ini. Biaya penggantian timer tersebut melebihi $120, dan yang saya dapatkan hanyalah timer yang memberikan kontrol sangat terbatas dan tingkat kegagalan yang tinggi. Saya juga mengalami kegagalan sensor suhu pada pemanas air surya saya yang menimbi biaya tambahan sebesar $30.
Saya tahu saya dapat membuat pengontrol kolam otomatis yang hemat biaya yang memberikan saya kontrol yang jauh lebih besar atas kapan pompa kolam berjalan. Saya ingin memiliki lebih banyak variabel mengenai kapan pompa berjalan, bukan hanya waktu dan hari sederhana dari timer yang ada. Saya juga ingin dapat mengotomatiskan tidak hanya pompa kolam saya, tetapi juga memantau status berbagai aspek lingkungan kolam saya. Tujuan lebih lanjut adalah dapat menyelesaikan tugas-tugas ini dari mana saja menggunakan perangkat apa pun.
Proyek yang saya buat sangat hemat biaya karena menggunakan Raspberry Pi yang menjalankan Windows 10 IoT Core, Relai, Arduino Mini Pro, serta sensor suhu, kabel, dan komponen cetak 3D. Saya menyelesaikan proyek ini dengan biaya jauh lebih rendah dari yang telah saya bayar untuk dua timer sebelumnya dan sensor suhu surya.
Kontrol Pompa Kolam (Komponen AC)
Saya memulai proyek saya dengan mengontrol relai status padat dari Raspberry Pi saya yang menjalankan Windows 10 IoT Core. Relai ini memungkinkan saya untuk mengontrol komponen AC (Arus Bolak-balik) saya seperti pompa kolam. Relai status padat mengontrol relai AC 30 Ampere yang telah digunakan oleh timer lama. Setelah merancang dan menguji rangkaian untuk pompa kolam, saya membuat fungsionalitas tambahan untuk mengontrol komponen AC lainnya seperti air terjun kolam saya, dan lampu kolam serta halaman saya. Dengan bagian proyek ini yang dirancang, saya dapat mengontrol semua elemen ini secara jarak jauh. Tidak lagi anggota keluarga atau saya yang perlu membuka kotak kontrol secara fisik untuk menyalakan air terjun, menyalakan lampu kolam atau halaman, atau mengatur timer untuk pompa kolam.
Kotak Pelindung Pengontrol Kolam
Putra saya merancang Kotak Pelindung Pengontrol Kolam dan membuatnya menggunakan printer 3D kami, dan memastikan bahwa baik Raspberry Pi maupun relai status padat memiliki pasangan yang aman dalam kotak kontrol.
Sensor Suhu
Salah satu tujuan desain proyek saya adalah untuk mengizinkan kontrol berdasarkan variabel selain Hari dan Waktu. Saya ingin dapat mempertimbangkan suhu udara luar, serta suhu pemanas air surya dan air kolam untuk menentukan kapan pompa harus berjalan dan kapan harus istirahat. Salah satu contoh saat jenis operasi ini sangat krusial adalah ketika suhu udara luar sangat dingin dan mendekati titik beku. Jika suhu air kolam juga mendekati titik beku, saya perlu memastikan pompa kolam dan air terjun saya berjalan untuk mencegah pipa membeku dan merusak sistem. Dengan proyek ini, saya akan dapat menyelesaikan hal ini bahkan saat saya tidak ada di rumah. Untuk mengimplementasikan ini, saya memasukkan sensor suhu ke dalam proyek saya. Saya membaca sensor tersebut menggunakan Arduino Mini Pro yang mengirim data tersebut ke Raspberry Pi yang sama yang mengontrol pompa kolam dan air terjun melalui antarmuka I2C.
Sensor Suhu Udara Luar
Sensor suhu udara luar adalah sensor pertama yang saya masukkan. Lagi, putra saya merancang dan mencetak pemegang sensor pada printer 3D kami. Dia mencoba PLA dan ABS, ABS sebenarnya bekerja lebih baik karena lebih tahan cuaca dan memiliki suhu transisi kaca yang lebih tinggi, membuatnya lebih tahan panas. Pastikan Anda mencetak dengan setidaknya 75% infill. Sensor dihubungkan seperti yang dijelaskan di atas dalam skematik.
Sensor Suhu Air
Selanjutnya, saya memasukkan sensor suhu air kolam dan pemanas surya. Ini akan memungkinkan proyek untuk mengumpulkan data suhu air yang akan ditampilkan kepada pengguna serta memberikan variabel lebih lanjut untuk menentukan kapan komponen tertentu berjalan atau istirahat. Pertama, pemegang sensor dirancang dan dicetak 3D. Seperti yang disebutkan sebelumnya, ABS sebenarnya bekerja lebih baik karena ketahanan cuaca dan panas yang lebih baik. Juga pastikan Anda menggunakan setidaknya 75% infill.
Membangun dan Memasang Sensor Suhu Air
Setelah mencetak pemegang sensor suhu air, saya menggunakan bor countersink untuk membuat area 45 derajat di sekitar lubang sensor. Ini akan memungkinkan JB Weld untuk memiliki lebih banyak permukaan untuk menempel. Saya lebih suka menggunakan bor untuk melakukan ini daripada mengubah desain cetak 3D karena potongan kasar bor tampaknya memberikan JB Weld daya tahan yang lebih baik.
Langkah selanjutnya adalah memasukkan sensor suhu ke dalam pemegang hingga terentang sekitar 3/4 inci dari bawah pemegang. Tambahkan cakram duduk untuk memegangnya pada tempatnya.
Selanjutnya, isi bagian atas pemegang dengan JB Weld dan biarkan kering selama 24 jam.
Setelah menunggu setidaknya 24 jam untuk JB Weld kering, saatnya untuk memasang sensor suhu air.
CATATAN PENTING:Pastikan semua pompa mati sebelum mencoba memasang sensor suhu air!
Setelah memastikan semua pompa air mati, ide bagus untuk membuka setiap klep yang dapat mengurangi tekanan air dari area tempat Anda memasang sensor suhu air. Ini akan memudahkan pemasangan secara besar (dan menjaga Anda tetap kering juga).
Buat lubang 5/16 inci pada pipa kolam. Pasang sensor suhu air dan gunakan 2 klem untuk memegangnya dengan kuat. Jangan melakukan kesalahan yang sama seperti saya dan megencet klem terlalu kencang, megencet terlalu kencang akan menghancurkan pemegang sensor. Tutup klep dan nyalakan pompa. Periksa kebocoran.
Kontrol Klep Pemanas Surya
Setelah sensor suhu terpasang, saya kemudian dapat merancang dan memasang kontrol Klep Pemanas Air Surya. Pemanas Surya menggunakan tegangan DC, berbeda dengan tegangan AC yang digunakan dengan komponen kolam lainnya yang disebutkan sebelumnya. Ini mengharuskan saya untuk mengontrol relai DC alih-alih relai AC. Konsepnya serupa, tetapi relai yang diperlukan berbeda. Pastikan relai yang Anda gunakan untuk proyek Anda akan mengontrol jenis tegangan yang benar yang digunakan oleh perangkat yang Anda kontrol.
Kontrol ini memungkinkan saya untuk mengarahkan air kolam ke panel Surya di atap saya. Saya hanya ingin mengarahkan air ke panel ketika suhu udara luar di atas 60 derajat. Setelah air diarahkan ke panel, pastikan air yang kembali setidaknya 2 derajat lebih hangat dari air kolam. Jika tidak, ini akan sia-sia energi untuk memompa air ke panel.
Kabel dan koneksi kontrol ini disediakan dalam skematik Komponen DC Pengontrol Kolam.
Pengembangan Aplikasi
Setelah menginstal Windows 10 IoT Core pada Raspberry Pi saya, saya menyadari bahwa memiliki web server bawaan yang digunakan untuk mengelolanya. Saya bertanya apakah ini versi yang disederhanakan dari IIS? Jika demikian, saya hanya bisa menulis beberapa layanan RESTful di IIS dan memanggilnya untuk proyek ini. Setelah banyak pencarian web dan banyak penelitian, tampaknya tidak mungkin. Pendekatan ini akan menjadi yang saya sukai, tetapi tampaknya tidak feasible pada saat ini.
Mengambil pendekatan yang berbeda, saya meninjau contoh "Blinky Web Server" dan artikel pada "Druss Blog". Saya memutuskan untuk membangun Aplikasi Latar Belakang Windows 10 IoT Core tanpa layar yang bertindak sebagai Web Server HTTP sederhana yang merespons permintaan HTTP GET dan POST.
Dalam beberapa hari, saya sudah memiliki prototipe yang berfungsi. Ini memberikan saya banyak kepercayaan bahwa proyek saya bisa berhasil. Jadi saya memutuskan untuk melanjutkan dengan arsitektur ini. Setelah menguji kode saya secara menyeluruh melalui debugger Visual Studio 2015, saya memiliki kesan bahwa saya dapat dengan mudah menyebarkan aplikasi saya.
Menyebarkan Aplikasi
Ini adalah titik yang saya kesulitkan, jadi saya berharap dapat menunjukkan kepada Anda cara menghindari kesulitan semacam itu. Karena Aplikasi saya telah diuji secara menyeluruh dalam debugger Visual Studio 2015, saya memiliki kesan bahwa saya hanya dapat mengubah mode dari Debug ke Release untuk menyebarkan aplikasi saya. Saya mencoba pendekatan ini dan sebenarnya menyebarkan aplikasi saya dan memulainya dalam mode Debug. Saya kemudian berhenti mencoba dan mencoba menjalankan aplikasi dari AppX Manager. Saya tidak berhasil saat mencoba ini, saya hanya mendapatkan kesalahan umum yang menyatakan, "Aplikasi gagal diinisialisasi."
Solusi untuk masalah ini adalah menghapus penyebaran saat ini dan kemudian menginstal aplikasi dari AppX Manager. Ini membuang banyak waktu saya, jadi saya berharap ini membantu Anda menghindari masalah itu.
Meski aplikasi berjalan sempurna dalam mode debug Visual Studio 2015, ia akan mati setelah menerima permintaan HTTP pertama. Saya menghabiskan banyak waktu untuk mencoba memecahkan masalah ini dan masih tidak tahu mengapa ini terjadi.
Merasa tekanan untuk menyelesaikan proyek ini, saya memutuskan untuk mengubah proyek saya menjadi seperti contoh "Blinky Web Server". Dalam implementasi saya, saya tidak melihat kebutuhan untuk aplikasi layar Windows 10 IoT Core karena fakta bahwa saya merencanakan web server untuk mengontrol pin GPIO dan membaca antarmuka I2C (bukan aplikasi layar). Apa yang saya lakukan dalam proyek saya adalah untuk membuat aplikasi layar memulai web server. Web Server kemudian mengirim pesan kembali ke aplikasi layar sehingga saya dapat melihat panggilan HTTP apa yang diterima oleh server saya. Pendekatan ini tampaknya sangat stabil dan itu adalah kode yang sama yang saya gunakan dalam percobaan asli saya.
Antarmuka Pengguna
Akhirnya, saya membangun program kontrol HTML yang akan berjalan pada hampir semua perangkat. Ini memungkinkan saya untuk tidak hanya mengontrol pompa kolam, air terjun, dan lampu kolam, tetapi juga memantau sensor tambahan dari mana saja.
Nanti, saya menggunakan OpenHAB dan membuat peta situs yang memberikan saya antarmuka tambahan ini.
Saya harap Anda menikmati membaca tentang proyek saya sebanyak saya menikmati membuatnya. Terima kasih.
YouTube, Vimeo atau Vine link, dan tekan Enter
Kode
Sketsa Arduino untuk Sensor Suhu menggunakan I2C
PoolWebServer - BackgroundTask.cs
PoolWebServer - Devices.cs
PoolWebServer - Sensors.cs
PoolWebService- MainPage.xaml.cs
PoolWebService - App.xaml.cs
OpenHAB Sitemap
OpenHAB Items
Sketsa Arduino untuk Sensor Suhu menggunakan I2C Java
Kode untuk membaca sensor suhu DS18b20 dan mengirim data saat diminta melalui antarmuka I2C.
#include <OneWire.h> #include <DallasTemperature.h> #include <Wire.h> #define SLAVE_ADDRESS 0x40 //Define GPIO pin constants const int POOL_PIN = 3; const int SOLAR_PIN = 5; const int OUTSIDE_PIN = 7; //Define the length of our buffer for the I2C interface const int I2C_BUFFER_LEN = 24; //IMPORTANT MAX is 32!!! //Load OneWire - proprietary dallas semiconductor sensor protocol - no license required OneWire poolTemp(POOL_PIN); OneWire solarTemp(SOLAR_PIN); OneWire outsideTemp(OUTSIDE_PIN); //Load Dallas - proprietary dallas sensor protocol utilizing onewire - no license required DallasTemperature poolSensor(&poolTemp); DallasTemperature solarSensor(&solarTemp); DallasTemperature outsideSensor(&outsideTemp); //Define I2C buffer char data[I2C_BUFFER_LEN]; String temperatureData; //Define variable for timer long prevMillis = 0; long interval = 1000; void setup(void) { //Connect to temperature sensor buses poolSensor.begin(); solarSensor.begin(); outsideSensor.begin(); //Start the I2C interface Wire.begin(SLAVE_ADDRESS); Wire.onRequest(requestEvent); } void loop(void) { //Monitor time to read temperature sensors once every defined interval //Don't read them faster than every 1 second. they can't respond that fast unsigned long currMillis = millis(); if (currMillis - prevMillis > interval) { prevMillis = currMillis; readTemperatures(); } } void readTemperatures() { //Read all three temperature sensors poolSensor.requestTemperatures(); solarSensor.requestTemperatures(); outsideSensor.requestTemperatures(); //Store temperature data in a string //We pad right to the full length of the buffer to make sure to overwrite old data //Data is in the format "88.99|78.12|100.00" where "PoolTemp|SolarTemp|OutsideTemp" temperatureData = padRight(String(poolSensor.getTempFByIndex(0)) + "|" + String(solarSensor.getTempFByIndex(0)) + "|" + String(outsideSensor.getTempFByIndex(0)), I2C_BUFFER_LEN); } String padRight(String inStr, int inLen) { while (inStr.length() < inLen) inStr = inStr + " "; return inStr; } void requestEvent() { //sends data over I2C in the format "88.99|78.12|100.00" where "PoolTemp|SolarTemp|OutsideTemp" temperatureData.toCharArray(data,I2C_BUFFER_LEN); Wire.write(data); }
PoolWebServer - BackgroundTask.csC#
Menentukan Server HTTP yang merespons permintaan HTTP POST dan GET.
// Copyright (c) Microsoft. All rights reserved. using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Http; using Windows.Foundation.Collections; using Windows.ApplicationModel.Background; using Windows.ApplicationModel.AppService; using Windows.System.Threading; using Windows.Networking.Sockets; using System.IO; using Windows.Storage.Streams; using System.Threading.Tasks; using System.Runtime.InteropServices.WindowsRuntime; using Windows.Foundation; using Windows.Devices.Gpio; namespace WebServerTask { public sealed class WebServerBGTask : IBackgroundTask { public void Run(IBackgroundTaskInstance taskInstance) { // Associate a cancellation handler with the background task. taskInstance.Canceled += OnCanceled; // Get the deferral object from the task instance serviceDeferral = taskInstance.GetDeferral(); var appService = taskInstance.TriggerDetails as AppServiceTriggerDetails; if (appService != null && appService.Name == "App2AppComService") { appServiceConnection = appService.AppServiceConnection; appServiceConnection.RequestReceived += OnRequestReceived; } } //Processes message resquests sent from PoolWebService App private async void OnRequestReceived(AppServiceConnection sender, AppServiceRequestReceivedEventArgs args) { var message = args.Request.Message; string command = message["Command"] as string; switch (command) { case "Initialize": { Sensors.InitSensors(); Devices.InitDevices(); var messageDeferral = args.GetDeferral(); //Set a result to return to the caller var returnMessage = new ValueSet(); //Define a new instance of our HTTPServer on Port 8888 HttpServer server = new HttpServer(8888, appServiceConnection); IAsyncAction asyncAction = Windows.System.Threading.ThreadPool.RunAsync( (workItem) => { //Start the Sever server.StartServer(); }); //Respond back to PoolWebService with a Status of Success returnMessage.Add("Status", "Success"); var responseStatus = await args.Request.SendResponseAsync(returnMessage); messageDeferral.Complete(); break; } case "Quit": { //Service was asked to quit. Give us service deferral //so platform can terminate the background task serviceDeferral.Complete(); break; } } } private void OnCanceled(IBackgroundTaskInstance sender, BackgroundTaskCancellationReason reason) { //Clean up and get ready to exit } BackgroundTaskDeferral serviceDeferral; AppServiceConnection appServiceConnection; } //Class to define the HTTP WebServer public sealed class HttpServer : IDisposable { //Create a buffer to read HTTP data private const uint BufferSize = 8192; //Port to listen on private int port = 8888; //Listener to private readonly StreamSocketListener listener; //Connection to send status information back to PoolControllerWebService private AppServiceConnection appServiceConnection; public HttpServer(int serverPort, AppServiceConnection connection) { listener = new StreamSocketListener(); port = serverPort; appServiceConnection = connection; //Add event handler for HTTP connections listener.ConnectionReceived += (s, e) => ProcessRequestAsync(e.Socket); } //Call to start the listner public void StartServer() { #pragma warning disable CS4014 listener.BindServiceNameAsync(port.ToString()); #pragma warning restore CS4014 } public void Dispose() { listener.Dispose(); } private async void ProcessRequestAsync(StreamSocket socket) { try { StringBuilder request = new StringBuilder(); //Get the incomming data using (IInputStream input = socket.InputStream) { byte[] data = new byte[BufferSize]; IBuffer buffer = data.AsBuffer(); uint dataRead = BufferSize; //Read all the incomming data while (dataRead == BufferSize) { await input.ReadAsync(buffer, BufferSize, InputStreamOptions.Partial); request.Append(Encoding.UTF8.GetString(data, 0, data.Length)); dataRead = buffer.Length; } } //Got the data start processing a response using (IOutputStream output = socket.OutputStream) { string requestMethod = request.ToString(); string[] requestParts = { "" }; if (requestMethod != null) { //Beakup the request into it parts requestMethod = requestMethod.Split('\n')[0]; requestParts = requestMethod.Split(' '); } //We only respond HTTP GETS and POST methods if (requestParts[0] == "GET") await WriteGetResponseAsync(requestParts[1], output); else if (requestParts[0] == "POST") await WritePostResponseAsync(requestParts[1], output); else await WriteMethodNotSupportedResponseAsync(requestParts[1], output); } } catch (Exception) { } } //Handles all HTTP GET's private async Task WriteGetResponseAsync(string request, IOutputStream os) { bool urlFound = false; byte[] bodyArray = null; string responseMsg = ""; //See if the request it matches any of the valid requests urls and create the response message switch (request.ToUpper()) { case "/SENSORS/POOLTEMP": responseMsg = Sensors.PoolTemperature; urlFound = true; break; case "/SENSORS/SOLARTEMP": responseMsg = Sensors.SolarTemperature; urlFound = true; break; case "/SENSORS/OUTSIDETEMP": responseMsg = Sensors.OutsideTemperature; urlFound = true; break; case "/DEVICES/POOLPUMP/STATE": responseMsg = Devices.PoolPumpState; urlFound = true; break; case "/DEVICES/WATERFALLPUMP/STATE": responseMsg = Devices.PoolWaterfallState; urlFound = true; break; case "/DEVICES/POOLLIGHTS/STATE": responseMsg = Devices.PoolLightsState; urlFound = true; break; case "/DEVICES/YARDLIGHTS/STATE": responseMsg = Devices.YardLightsState; urlFound = true; break; case "/DEVICES/POOLSOLAR/STATE": responseMsg = Devices.PoolSolarValveState; urlFound = true; break; default: urlFound = false; break; } bodyArray = Encoding.UTF8.GetBytes(responseMsg); await WriteResponseAsync(request.ToUpper(), responseMsg, urlFound, bodyArray, os); } //Handles all HTTP POST's private async Task WritePostResponseAsync(string request, IOutputStream os) { bool urlFound = false; byte[] bodyArray = null; string responseMsg = ""; //See if the request it matches any of the valid requests urls and create the response message switch (request.ToUpper()) { case "/DEVICES/POOLPUMP/OFF": Devices.PoolPumpPinValue = GpioPinValue.Low; bodyArray = Encoding.UTF8.GetBytes("OFF"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/POOLPUMP/ON": Devices.PoolPumpPinValue = GpioPinValue.High; bodyArray = Encoding.UTF8.GetBytes("ON"); responseMsg = "ON"; urlFound = true; break; case "/DEVICES/WATERFALLPUMP/OFF": Devices.PoolWaterfallPinValue = GpioPinValue.Low; bodyArray = Encoding.UTF8.GetBytes("OFF"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/WATERFALLPUMP/ON": Devices.PoolWaterfallPinValue = GpioPinValue.High; bodyArray = Encoding.UTF8.GetBytes("ON"); responseMsg = "ON"; urlFound = true; break; case "/DEVICES/POOLLIGHTS/OFF": Devices.PoolLightsPinValue = GpioPinValue.Low; bodyArray = Encoding.UTF8.GetBytes("OFF"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/POOLLIGHTS/ON": Devices.PoolLightsPinValue = GpioPinValue.High; bodyArray = Encoding.UTF8.GetBytes("ON"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/YARDLIGHTS/OFF": Devices.YardLightsPinValue = GpioPinValue.Low; bodyArray = Encoding.UTF8.GetBytes("OFF"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/YARDLIGHTS/ON": Devices.YardLightsPinValue = GpioPinValue.High; bodyArray = Encoding.UTF8.GetBytes("ON"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/POOLSOLAR/OFF": Devices.PoolSolarValvePinValue = GpioPinValue.Low; bodyArray = Encoding.UTF8.GetBytes("OFF"); responseMsg = "OFF"; urlFound = true; break; case "/DEVICES/POOLSOLAR/ON": Devices.PoolSolarValvePinValue = GpioPinValue.High; bodyArray = Encoding.UTF8.GetBytes("ON"); responseMsg = "ON"; urlFound = true; break; default: bodyArray = Encoding.UTF8.GetBytes(""); urlFound = false; break; } await WriteResponseAsync(request.ToUpper(), responseMsg, urlFound,bodyArray, os); } //Write the response for unsupported HTTP methods private async Task WriteMethodNotSupportedResponseAsync(string request, IOutputStream os) { bool urlFound = false; byte[] bodyArray = null; bodyArray = Encoding.UTF8.GetBytes(""); await WriteResponseAsync(request.ToUpper(), "NOT SUPPORTED", urlFound, bodyArray, os); } //Write the response for HTTP GET's and POST's private async Task WriteResponseAsync(string RequestMsg, string ResponseMsg, bool urlFound, byte[] bodyArray, IOutputStream os) { try //The appService will die after a day or so. Let's try catch it seperatly so the http server will still respond { var updateMessage = new ValueSet(); updateMessage.Add("Request", RequestMsg); updateMessage.Add("Response", ResponseMsg); var responseStatus = await appServiceConnection.SendMessageAsync(updateMessage); } catch (Exception) {} try { MemoryStream bodyStream = new MemoryStream(bodyArray); using (Stream response = os.AsStreamForWrite()) { string header = GetHeader(urlFound, bodyStream.Length.ToString()); byte[] headerArray = Encoding.UTF8.GetBytes(header); await response.WriteAsync(headerArray, 0, headerArray.Length); if (urlFound) await bodyStream.CopyToAsync(response); await response.FlushAsync(); } } catch(Exception) {} } //Creates the HTTP header text for found and not found urls string GetHeader(bool urlFound, string bodyStreamLength) { string header; if (urlFound) { header = "HTTP/1.1 200 OK\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: " + bodyStreamLength + "\r\n" + "Connection: close\r\n\r\n"; } else { header = "HTTP/1.1 404 Not Found\r\n" + "Access-Control-Allow-Origin: *\r\n" + "Content-Type: text/plain\r\n" + "Content-Length: 0\r\n" + "Connection close\r\n\r\n"; } return header; } } }
PoolWebServer - Devices.csC#
Kelas yang menentukan semua perangkat dan pin GPIO mana tempat mereka terhubung.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Windows.Devices.Gpio; namespace WebServerTask { //Class the defines all devices and what GPIO pins they are connected to. public static class Devices { //Define the GPIO pins numbers private const int POOL_PUMP_PIN = 12; private const int POOL_WATERFALL_PIN = 13; private const int POOL_LIGHTS_PIN = 16; private const int YARD_LIGHTS_PIN = 18; private const int POOL_SOLAR_VALVE_PIN = 22; //Define the GPIO pins private static GpioPin poolPumpPin; private static GpioPin poolWaterfallPin; private static GpioPin poolLightsPin; private static GpioPin yardLightsPin; private static GpioPin poolSolarValvePin; //Property for GPIO Pin assigned to the Pool Pump public static GpioPinValue PoolPumpPinValue { get { return poolPumpPin.Read(); //Read the Pin returns High or Low } set { if (poolPumpPin.Read() != value) //Only set the pin if is changing poolPumpPin.Write(value); } } //Property to read status of the Pool Pump ON or OFF public static string PoolPumpState { get { return GetState(PoolPumpPinValue, GpioPinValue.High); //Get the state } } //Property for GPIO Pin assigned to the Waterfall Pump public static GpioPinValue PoolWaterfallPinValue { get { return poolWaterfallPin.Read(); } set { if (poolWaterfallPin.Read() != value) poolWaterfallPin.Write(value); } } //Property to read status of the Waterfall Pump ON or OFF public static string PoolWaterfallState { get { return GetState(PoolWaterfallPinValue, GpioPinValue.High); } } //Property for GPIO Pin assigned to the Pool Lights public static GpioPinValue PoolLightsPinValue { get { return poolLightsPin.Read(); } set { if (poolLightsPin.Read() != value) poolLightsPin.Write(value); } } //Property to read status of the Pool Lights ON or OFF public static string PoolLightsState { get { return GetState(PoolLightsPinValue, GpioPinValue.High); } } //Property for GPIO Pin assigned to the valve to turn Solar on and off public static GpioPinValue PoolSolarValvePinValue { get { return poolSolarValvePin.Read(); } set { if (poolSolarValvePin.Read() != value) poolSolarValvePin.Write(value); } } //Property to read status of the Solar valve ON or OFF public static string PoolSolarValveState { get { return GetState(PoolSolarValvePinValue, GpioPinValue.High); } } //Property for GPIO Pin assigned to the Yard Lights public static GpioPinValue YardLightsPinValue { get { return yardLightsPin.Read(); } set { if (yardLightsPin.Read() != value) yardLightsPin.Write(value); } } //Property to read status of the Yard Lights ON or OFF public static string YardLightsState { get { return GetState(YardLightsPinValue, GpioPinValue.High); } } //Intialize all GPIO pin used public static void InitDevices() { var gpio = GpioController.GetDefault(); if (gpio != null) { //These pins are on an active high relay. We set everything to OFF when we start poolPumpPin = gpio.OpenPin(POOL_PUMP_PIN); poolPumpPin.Write(GpioPinValue.Low); poolPumpPin.SetDriveMode(GpioPinDriveMode.Output); poolWaterfallPin = gpio.OpenPin(POOL_WATERFALL_PIN); poolWaterfallPin.Write(GpioPinValue.Low); poolWaterfallPin.SetDriveMode(GpioPinDriveMode.Output); poolLightsPin = gpio.OpenPin(POOL_LIGHTS_PIN); poolLightsPin.Write(GpioPinValue.Low); poolLightsPin.SetDriveMode(GpioPinDriveMode.Output); yardLightsPin = gpio.OpenPin(YARD_LIGHTS_PIN); yardLightsPin.Write(GpioPinValue.Low); yardLightsPin.SetDriveMode(GpioPinDriveMode.Output); poolSolarValvePin = gpio.OpenPin(POOL_SOLAR_VALVE_PIN); poolSolarValvePin.Write(GpioPinValue.Low); poolSolarValvePin.SetDriveMode(GpioPinDriveMode.Output); } } //Gets the state of a device based upon it ActiveState //ActiveState means what required to turn the device on High or Low on the GPIO pin private static string GetState(GpioPinValue value, GpioPinValue ActiveState) { string state = "OFF"; if (value == ActiveState) state = "ON"; return state; } } }
PoolWebServer - Sensors.csC#
Kelas yang menentukan semua sensor suhu dan antarmuka I2C yang digunakan untuk membacanya.
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#
Halaman utama aplikasi yang memulai 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 { /// <summary> /// Provides application-specific behavior to supplement the default Application class. /// </summary> sealed partial class App : Application { /// <summary> /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). /// </summary> public App() { InitializeComponent(); Suspending += OnSuspending; } /// <summary> /// Invoked when the application is launched normally by the end user. Other entry points /// will be used such as when the application is launched to open a specific file. /// </summary> /// <param name="e">Details about the launch request and process.</param> protected override void OnLaunched(LaunchActivatedEventArgs e) { #if DEBUG if (System.Diagnostics.Debugger.IsAttached) { DebugSettings.EnableFrameRateCounter = true; } #endif Frame rootFrame = Window.Current.Content as Frame; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active if (rootFrame == null) { // Create a Frame to act as the navigation context and navigate to the first page rootFrame = new Frame(); // Set the default language rootFrame.Language = Windows.Globalization.ApplicationLanguages.Languages[0]; rootFrame.NavigationFailed += OnNavigationFailed; if (e.PreviousExecutionState == ApplicationExecutionState.Terminated) { //TODO: Load state from previously suspended application } // Place the frame in the current Window Window.Current.Content = rootFrame; } if (rootFrame.Content == null) { // When the navigation stack isn't restored navigate to the first page, // configuring the new page by passing required information as a navigation // parameter rootFrame.Navigate(typeof(MainPage), e.Arguments); } // Ensure the current window is active Window.Current.Activate(); } /// <summary> /// Invoked when Navigation to a certain page fails /// </summary> /// <param name="sender">The Frame which failed navigation</param> /// <param name="e">Details about the navigation failure</param> void OnNavigationFailed(object sender, NavigationFailedEventArgs e) { throw new Exception("Failed to load Page " + e.SourcePageType.FullName); } /// <summary> /// Invoked when application execution is being suspended. Application state is saved /// without knowing whether the application will be terminated or resumed with the contents /// of memory still intact. /// </summary> /// <param name="sender">The source of the suspend request.</param> /// <param name="e">Details about the suspend request</param> private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); //TODO: Save application state and stop any background activity deferral.Complete(); } } }
OpenHAB SitemapJavaScript
Contoh sitemap yang digunakan dalam konfigurasi openHAB.
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
Contoh item konfigurasi openHAB.
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((.*?))]"}
Repositori Proyek GitHub
Proyek lengkap Visual Studio 2015 Pool Controller: https://github.com/mmackes/Windows-10-IoT-PoolController
Bagian dan Kotak Pelindung Khusus
Pemegang untuk memegang sensor tahan air DS18B20 untuk memantau suhu udara. Pemegang untuk memegang sensor tahan air DS18B20 pada pipa kolam standar. Kotak pelindung untuk Raspberry Pi dan Relai. Kotak pelindung untuk Raspberry Pi dan Relai.
Skematik
Skematik yang menunjukkan cara menghubungkan Raspberry Pi ke relai AC. Mengontrol pompa kolam, air terjun, lampu kolam, dan lampu halaman AC.
Skematik yang menunjukkan cara menghubungkan Raspberry Pi ke relai DC. Mengontrol klep air pemanas surya.
Skematik yang menunjukkan cara menghubungkan Raspberry Pi ke Arduino Mini Pro dan sensor suhu. Memantau suhu air kolam, air pemanas surya, dan udara luar.