Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Контроллер пула

Компоненты и расходные материалы

Raspberry Pi 2, модель B
× 1
PNY 16 ГБ Turbo MicroSDXC CL10
× 1
SparkFun Arduino Pro Mini 328 - 5 В / 16 МГц
× 1
4-канальная плата твердотельных реле SainSmart 5 В
× 1
Модуль реле 5v Толако для Arduino
× 1
Водонепроницаемые датчики температуры DS18b20
× 1
Резисторы 4,7 кОм - 1/4 Вт - 5% - 4K7 (10 шт.)
× 1
USB-ключ WIFI Raspberry Pi
× 1
От мужчины к женскому удлинителю на 1 фут USB
× 1
American Valve CL40PK6 Number 40 Clamp, 6-Pack
× 1
Морская эпоксидная смола J-B Weld 8272 MarineWeld - 2 унции
× 1
Шайба сиденья
× 2
Сетевой адаптер переменного тока для настенного зарядного устройства Micro USB
× 1

Необходимые инструменты и машины

Printrbot Simple
Используется для создания корпуса и крепления датчиков
Модуль последовательного адаптера Ftdi Usb в Ttl для мини-порта Arduino
Используется для загрузки эскиза в Arduino Mini Pro

Приложения и онлайн-сервисы

Microsoft Windows 10 IoT Core
Microsoft Visual Studio 2015
Microsoft IIS
IDE Arduino
Библиотека OneWire
Библиотека температур Далласа
ПО openHAB для домашней автоматизации с открытым исходным кодом

Об этом проекте

Автоматизированный контроллер пула

Дважды за три месяца таймер моего насоса для бассейна выходил из строя. Это вдохновило меня на создание этого проекта. Стоимость замены этих таймеров составила более 120 долларов, и все, что мне нужно было показать, это таймер, который давал мне очень мало контроля и высокую частоту отказов. У меня также вышел из строя датчик температуры на моем солнечном водонагревателе, который стоил дополнительно 30 долларов.

Я знал, что могу создать экономичный автоматизированный контроллер бассейна, который даст мне гораздо больше контроля над тем, когда мой насос для бассейна будет работать. Я хотел иметь больше переменных относительно того, когда насос работает, вместо простого времени и дня существующего таймера. Я также хотел иметь возможность не только автоматизировать мой насос для бассейна, но и отслеживать состояние различных аспектов среды моего бассейна. Еще одна цель заключалась в том, чтобы иметь возможность выполнять эти задачи из любого места с любого устройства.

Созданный мной проект очень рентабелен, поскольку в нем используется Raspberry Pi под управлением Windows 10 IoT Core, Relays, Arduino Mini Pro, а также датчики температуры, проводка и компоненты, напечатанные на 3D-принтере. Я выполнил этот проект за гораздо меньшие деньги, чем я заплатил за два предыдущих таймера и датчик солнечной температуры.

Управление насосом бассейна (компоненты переменного тока)

Я начал свой проект с управления твердотельными реле с моего Raspberry Pi под управлением Windows 10 IoT Core. Эти реле позволяют мне управлять моими компонентами переменного тока (переменного тока), такими как насос для бассейна. Твердотельные реле управляют существующими реле переменного тока на 30 А, которые использовались в старом таймере. После разработки и тестирования схемы насоса для бассейна я создал дополнительные функции для управления другими компонентами переменного тока, такими как водопад в бассейне, а также освещение для бассейна и двора. Разработав эту часть проекта, я мог управлять всеми этими элементами удаленно. Мне и членам моей семьи больше не нужно физически открывать блок управления, чтобы включить водопад, включить освещение бассейна или двора или установить таймер для насоса бассейна.

Корпус контроллера пула

Мой сын спроектировал корпус Pool Controller и создал его с помощью нашего 3D-принтера и обеспечил надежную посадку Raspberry Pi и твердотельных реле в коробке контроллера.

Датчики температуры

Одной из целей проекта myproject было обеспечение контроля на основе переменных в дополнение к дню и времени. Я хотел иметь возможность учитывать температуру наружного воздуха, а также температуру воды в солнечном водонагревателе и бассейне, чтобы определить, когда насос должен работать, а когда он должен быть остановлен. Одним из примеров того, когда этот тип работы будет иметь решающее значение, является ситуация, когда температура наружного воздуха очень низкая и близка к замерзанию. Если температура воды в бассейне также близка к температуре замерзания, мне нужно убедиться, что мой бассейн и водопадные насосы работают, чтобы предотвратить замерзание труб и повреждение систем. С помощью этого проекта я смогу это сделать, даже когда меня нет дома. Чтобы реализовать это, я встроил в свой проект датчики температуры. Я читал эти датчики, используя Arduino Mini Pro, который отправляет эти данные на тот же Raspberry Pi, который управляет насосами бассейна и водопада через интерфейс I2C.

Датчик температуры наружного воздуха

Датчик температуры наружного воздуха был первым датчиком, который я встроил. Опять же, мой сын разработал и напечатал крепление датчика на нашем 3D-принтере. Он пробовал как PLA, так и ABS, ABS действительно работает лучше, поскольку он более устойчив к погодным условиям и имеет более высокую температуру стеклования, что делает его более термостойким. Убедитесь, что вы печатаете с заполнением не менее 75% . Датчик был подключен, как описано выше на схеме.

Датчики температуры воды

Затем я подключил датчики температуры воды в бассейне и солнечного нагревателя. Это позволит проекту собрать данные о температуре воды, которые будут отображаться для пользователя, а также предоставить дополнительные переменные для определения того, когда определенные компоненты работают или находятся в состоянии покоя. Сначала было разработано и напечатано на 3D-принтере крепление для датчика. Как упоминалось ранее, на самом деле ABS работает лучше благодаря лучшей погодо- и термостойкости. Также убедитесь, что вы используете заполнение не менее 75% .

Сборка и установка датчика температуры воды

После печати крепления датчика температуры воды я использовал сверло с зенковкой, чтобы создать область 45 градусов вокруг отверстия датчика. Это позволило бы JB Weld иметь большую поверхность для приклеивания. Я предпочел использовать сверло, чтобы сделать это, вместо того, чтобы менять дизайн 3D-печати, поскольку грубая резка сверла, казалось, давала JB Weld лучшую удерживающую способность.

Следующим шагом было вставить датчик температуры в крепление так, чтобы он выступал примерно на 3/4 дюйма из нижней части крепления. Добавьте шайбу седла, чтобы удерживать его на месте.

Затем залейте верхнюю часть крепления JB Weld и дайте высохнуть в течение 24 часов.

Подождав не менее 24 часов, пока JB Weld высохнет, пришло время установить датчики температуры воды.

ВАЖНОЕ примечание: Перед установкой датчиков температуры воды убедитесь, что все насосы выключены!

Убедившись, что все водяные насосы отключены, рекомендуется открыть все клапаны, которые могут снять давление воды в области, в которой вы устанавливаете датчики температуры воды. Это значительно упростит установку (а также сохранит вас сухим).

Просверлите отверстие 5/16 дюйма в трубопроводе бассейна. Установите датчик температуры воды и используйте 2 зажима, чтобы надежно удерживать его на месте. Не совершайте ту же ошибку, что и я, и перетягивайте зажимы, чрезмерное затягивание приведет к раздавливанию крепления датчика. клапаны и включите насосы. Проверьте на герметичность.

Управление клапаном солнечного нагревателя

После установки датчиков температуры я мог спроектировать и установить регулятор клапана солнечного водонагревателя. В солнечном нагревателе используется постоянное напряжение, а не переменное напряжение, используемое в других компонентах бассейна, упомянутых ранее. Это потребовало от меня управления реле постоянного тока вместо реле переменного тока. Концепция аналогична, но требуемые реле разные. Убедитесь, что реле, которые вы используете для своего проекта, будут контролировать правильный тип напряжения, используемого устройством, которым вы управляете.

Этот элемент управления позволяет мне направлять воду из бассейна к солнечным панелям на моей крыше. Я хочу направлять воду на панели только тогда, когда температура наружного воздуха выше 60 градусов. После того, как вода будет отводиться к панелям, убедитесь, что возвращающаяся вода по крайней мере на 2 градуса теплее, чем вода в бассейне. В противном случае закачка воды к панелям будет пустой тратой энергии.

Электропроводка и соединения этого элемента управления представлены на схеме компонентов постоянного тока контроллера пула.

Разработка приложений

После установки Windows 10IoT Core на мой Raspberry Pi я понял, что у него есть встроенный веб-сервер, который используется для управления им. Мне было интересно, была ли это урезанная версия IIS? Если так, я мог бы просто написать классные успокаивающие сервисы на IIS и позвонить им для этого проекта. После множества поисков в Интернете и множества исследований это оказалось невозможным. Я бы предпочел такой подход, но на данный момент он не представляется возможным.

Взяв другой подход, я рассмотрел пример «Blinky Web Server» и статью в «Druss Blog». Я решил создать автономное фоновое приложение Windows 10 IoT Core, которое действует как простой веб-сервер HTTP, отвечающий на запросы HTTP GET и POST. .

Через несколько дней у меня был рабочий прототип. Это вселило во меня уверенность в том, что мой проект может быть успешным. Поэтому я решил двигаться дальше с этой архитектурой. После тщательного тестирования моего кода с помощью отладчика Visual Studio 2015 у меня сложилось впечатление, что я могу легко развернуть свое приложение.

Развертывание приложения

Это момент, с которым я боролся, поэтому я надеюсь показать вам, как избежать таких трудностей. Поскольку мое приложение было тщательно протестировано в отладчике Visual Studio 2015, у меня создалось впечатление, что я могу просто изменить режим с «Отладка» на «Выпуск», чтобы развернуть свое приложение. Я попробовал этот подход, и он действительно развернул мое приложение и запустил его в режиме отладки. Затем я остановил отладку и попытался запустить приложение из AppX Manager. У меня не было успеха, когда я пытался это сделать, я просто получал общую ошибку, которая гласила:«Не удалось инициализировать приложение».

Решение этой проблемы - удалить текущее развертывание, а затем вместо этого установить приложение из AppX Manager. Это стоило мне много времени, поэтому я надеюсь, что это поможет вам избежать этой проблемы.

Несмотря на то, что приложение безупречно работало в режиме отладки Visual Studio 2015, оно умирало после получения первого HTTP-запроса. Я потратил много времени, пытаясь решить эту проблему, и до сих пор не знаю, почему это происходит.

Чувствуя необходимость завершить этот проект, я решил изменить свой проект так, чтобы он был похож на пример «Blinky Web Server». В моей реализации я не видел необходимости в экранном приложении Windows 10 IoT Core из-за того, что я планировал, чтобы веб-сервер управлял контактами GPIO и считывал интерфейс I2C (а не экранное приложение). В моем проекте я заставил экранное приложение запускать веб-сервер. Затем веб-сервер отправляет сообщения обратно экранному приложению, чтобы я мог видеть, какой HTTP-вызов был получен моим сервером. Этот подход кажется надежным, и это точно такой же код, который я использовал в своей первоначальной попытке.

Пользовательский интерфейс

Наконец, я создал управляющую программу HTML, которая будет работать практически на любом устройстве. Это позволяет мне не только управлять насосом бассейна, водопадом и освещением бассейна, но и контролировать дополнительные датчики из любого места.

Позже я использовал OpenHAB и создал карту сайта, которая дала мне этот дополнительный интерфейс.

Надеюсь, вам понравилось читать о моем проекте так же, как и мне. Спасибо.

Ссылка на YouTube, Vimeo или Vine и нажмите Enter

Код

  • Эскиз Arduino для датчиков температуры с использованием I2C
  • PoolWebServer - BackgroundTask.cs
  • PoolWebServer - Devices.cs
  • PoolWebServer - Sensors.cs
  • PoolWebService- MainPage.xaml.cs
  • PoolWebService - App.xaml.cs
  • Карта сайта OpenHAB
  • Элементы OpenHAB
Эскиз Arduino для датчиков температуры с использованием I2C 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; // Определяем длину нашего буфера для интерфейса I2Cconst int I2C_BUFFER_LEN =24; // ВАЖНОЕ МАКСИМАЛЬНОЕ значение 32 !!! // Загрузка OneWire - проприетарного протокола полупроводникового датчика Далласа - лицензия не требуется OneWire poolTemp (POOL_PIN); OneWire solarTemp (SOLAR_PIN); OneWire outsideTemp (OUTSIDE_PIN); onewire - лицензия не требуется void setup (void) {// Подключаемся к шинам датчиков температуры poolSensor.begin (); solarSensor.begin (); outsideSensor.begin (); // Запускаем интерфейс I2C Wire.begin (SLAVE_ADDRESS); Wire.onRequest (requestEvent);} void loop (void) {// Отслеживать время считывания датчиков температуры один раз в определенный интервал // Не считывать их быстрее, чем каждые 1 секунду. они не могут ответить так быстро, как долго без знака currMillis =millis (); если (currMillis - prevMillis> интервал) {prevMillis =currMillis; readTemperas (); }} void readTemperatures () {// Считываем все три датчика температуры poolSensor.requestTemperatures (); solarSensor.requestTemperatures (); outsideSensor.requestTemperatures (); // Сохраняем данные о температуре в виде строки // Мы дополняем до полной длины буфера, чтобы гарантировать перезапись старых данных // Данные в формате «88.99 | 78.12 | 100.00», где «PoolTemp | SolarTemp | OutsideTemp» temperatureData =padRight (String (poolSensor.getTempFByIndex (0)) + «|» + String (solarSensor.getTempFByIndex (0)) + «|» + String (outsideSensor.getTempFByIndex (0)), I2C_BUFFER_LEN);} в StringStr padr , int inLen) {в то время как (inStr.length ()  
PoolWebServer - BackgroundTask.cs C #
Определяет HTTP-сервер, который отвечает на запросы HTTP POST и GET
 // Copyright (c) Microsoft. Все права защищены. 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; использование 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 {общедоступный запечатанный класс WebServerBGTask:IBackgroundTask {public void Run (IBackgroundTaskInstance taskInstance) {// Свяжите обработчик отмены с фоновой задачей. taskInstance.Canceled + =OnCanceled; // Получить объект отсрочки из экземпляра задачи serviceDeferral =taskInstance.GetDeferral (); var appService =taskInstance.TriggerDetails as AppServiceTriggerDetails; если (appService! =null &&appService.Name =="App2AppComService") {appServiceConnection =appService.AppServiceConnection; appServiceConnection.RequestReceived + =OnRequestReceived; }} // Обрабатывает повторные запросы сообщений, отправленные из приложения PoolWebService private async void OnRequestReceived (отправитель AppServiceConnection, аргументы AppServiceRequestReceivedEventArgs) {var message =args.Request.Message; строка command =сообщение ["Command"] как строка; переключатель (команда) {case "Инициализировать":{Sensors.InitSensors (); Devices.InitDevices (); var messageDeferral =args.GetDeferral (); // Устанавливаем результат для возврата вызывающей стороне var returnMessage =new ValueSet (); // Определить новый экземпляр нашего HTTPServer на порту 8888 HttpServer server =new HttpServer (8888, appServiceConnection); IAsyncAction asyncAction =Windows.System.Threading.ThreadPool.RunAsync ((workItem) => {// Запускаем сервер server.StartServer ();}); // Ответить на PoolWebService со статусом успеха returnMessage.Add ("Status", "Success"); var responseStatus =ждать аргументов.Request.SendResponseAsync (returnMessage); messageDeferral.Complete (); ломать; } case "Quit":{// Служба попросила завершить работу. Предоставьте нам отсрочку обслуживания // чтобы платформа могла завершить фоновую задачу serviceDeferral.Complete (); ломать; }}} private void OnCanceled (отправитель IBackgroundTaskInstance, причина BackgroundTaskCancellationReason) {// Очистите и приготовьтесь к выходу} BackgroundTaskDeferral serviceDeferral; AppServiceConnection appServiceConnection; } // Класс для определения открытого закрытого класса HTTP WebServer HttpServer:IDisposable {// Создание буфера для чтения закрытых данных HTTP const uint BufferSize =8192; // Порт для прослушивания частного int port =8888; // Слушатель частного только для чтения StreamSocketListener listener; // Соединение для отправки информации о статусе обратно в PoolControllerWebService private AppServiceConnection appServiceConnection; общедоступный HttpServer (int serverPort, соединение AppServiceConnection) {listener =new StreamSocketListener (); порт =serverPort; appServiceConnection =соединение; // Добавляем обработчик событий для прослушивателя HTTP-соединений .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 (); } частный async void ProcessRequestAsync (сокет StreamSocket) {попробуйте {StringBuilder request =new StringBuilder (); // Получить входящие данные с помощью (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; }} // Получены данные, запускающие обработку ответа с помощью (IOutputStream output =socket.OutputStream) {string requestMethod =request.ToString (); строка [] requestParts ={""}; if (requestMethod! =null) {// Добавляем запрос в его части requestMethod =requestMethod.Split ('\ n') [0]; requestParts =requestMethod.Split (''); } // Мы отвечаем только на методы HTTP GETS и POST if (requestParts [0] =="GET") await WriteGetResponseAsync (requestParts [1], output); иначе if (requestParts [0] =="POST") await WritePostResponseAsync (requestParts [1], output); иначе ждите WriteMethodNotSupportedResponseAsync (requestParts [1], output); }} catch (Exception) {}} // Обрабатывает все приватные асинхронные задачи HTTP GET WriteGetResponseAsync (строковый запрос, IOutputStream os) {bool urlFound =false; byte [] bodyArray =ноль; строка responseMsg =""; // Проверяем, соответствует ли запрос любому из допустимых URL-адресов, и создаем переключатель ответного сообщения (request.ToUpper ()) {case "/ SENSORS / POOLTEMP":responseMsg =Sensors.PoolTemperature; urlFound =true; ломать; case "/ ДАТЧИКИ / СОЛНЕЧНАЯ ТЕМПЕРАТУРА":responseMsg =Sensors.SolarTemperature; urlFound =true; ломать; case "/ SENSORS / OUTSIDETEMP":responseMsg =Sensors.OutsideTemperature; urlFound =true; ломать; case «/ DEVICES / POOLPUMP / STATE»:responseMsg =Devices.PoolPumpState; urlFound =true; ломать; case "/ DEVICES / WATERFALLPUMP / STATE":responseMsg =Devices.PoolWaterfallState; urlFound =true; ломать; case "/ DEVICES / POOLLIGHTS / STATE":responseMsg =Devices.PoolLightsState; urlFound =true; ломать; case "/ DEVICES / YARDLIGHTS / STATE":responseMsg =Devices.YardLightsState; urlFound =true; ломать; case "/ DEVICES / POOLSOLAR / STATE":responseMsg =Devices.PoolSolarValveState; urlFound =true; ломать; по умолчанию:urlFound =false; ломать; } bodyArray =Encoding.UTF8.GetBytes (responseMsg); ждать WriteResponseAsync (request.ToUpper (), responseMsg, urlFound, bodyArray, os); } // Обрабатывает все приватные асинхронные задачи HTTP POST WritePostResponseAsync (строковый запрос, IOutputStream os) {bool urlFound =false; byte [] bodyArray =ноль; строка responseMsg =""; // Проверяем, соответствует ли запрос любому из действительных URL-адресов запросов, и создаем переключатель сообщения ответа (request.ToUpper ()) {case "/ DEVICES / POOLPUMP / OFF":Devices.PoolPumpPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("ВЫКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / POOLPUMP / ON":Devices.PoolPumpPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ВКЛ"); responseMsg ="ВКЛ"; urlFound =true; ломать; case "/ DEVICES / WATERFALLPUMP / OFF":Devices.PoolWaterfallPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("ВЫКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / WATERFALLPUMP / ON":Devices.PoolWaterfallPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ВКЛ"); responseMsg ="ВКЛ"; urlFound =true; ломать; case "/ DEVICES / POOLLIGHTS / OFF":Devices.PoolLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("ВЫКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / POOLLIGHTS / ON":Devices.PoolLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ВКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / YARDLIGHTS / OFF":Devices.YardLightsPinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("ВЫКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / YARDLIGHTS / ON":Devices.YardLightsPinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ВКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / POOLSOLAR / OFF":Devices.PoolSolarValvePinValue =GpioPinValue.Low; bodyArray =Encoding.UTF8.GetBytes ("ВЫКЛ"); responseMsg ="ВЫКЛ"; urlFound =true; ломать; case "/ DEVICES / POOLSOLAR / ON":Devices.PoolSolarValvePinValue =GpioPinValue.High; bodyArray =Encoding.UTF8.GetBytes ("ВКЛ"); responseMsg ="ВКЛ"; urlFound =true; ломать; по умолчанию:bodyArray =Encoding.UTF8.GetBytes (""); urlFound =false; ломать; } await WriteResponseAsync (request.ToUpper (), responseMsg, urlFound, bodyArray, os); } // Записываем ответ для неподдерживаемых HTTP-методов private async Task WriteMethodNotSupportedResponseAsync (строковый запрос, IOutputStream os) {bool urlFound =false; byte [] bodyArray =ноль; bodyArray =Encoding.UTF8.GetBytes (""); ожидание WriteResponseAsync (request.ToUpper (), «НЕ ПОДДЕРЖИВАЕТСЯ», urlFound, bodyArray, os); } // Записываем ответ для приватной асинхронной задачи HTTP GET и POST WriteResponseAsync (строка RequestMsg, строка ResponseMsg, bool urlFound, byte [] bodyArray, IOutputStream os) {try // Служба appService умрет примерно через день. Давайте попробуем поймать его отдельно, чтобы http-сервер все равно отвечал {var updateMessage =new ValueSet (); updateMessage.Add («Запрос», RequestMsg); updateMessage.Add («Ответ», ResponseMsg); var responseStatus =ждать appServiceConnection.SendMessageAsync (updateMessage); } catch (Exception) {} попробуйте {MemoryStream bodyStream =new MemoryStream (bodyArray); используя (Ответ потока =os.AsStreamForWrite ()) {заголовок строки =GetHeader (urlFound, bodyStream.Length.ToString ()); byte [] headerArray =Encoding.UTF8.GetBytes (заголовок); ждать ответа.WriteAsync (headerArray, 0, headerArray.Length); если (urlFound) ждать bodyStream.CopyToAsync (ответ); ждать ответа.FlushAsync (); }} catch (Exception) {}} // Создает текст HTTP-заголовка для найденных и не найденных URL-адресов 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 "+" Соединение:закрыть \ 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.cs C #
Класс определяет все устройства и контакты 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 GpioPin poolPumpPin; частный статический пул GpioPinWaterfallPin; 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"; вернуть состояние; }}} 
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. Контроллер умного дома 433 МГц с Sensorflare и RaspberryPi
  5. Отслеживание Raspberry Pi Ball
  6. Универсальный пульт дистанционного управления Raspberry Pi
  7. Датчик движения с использованием Raspberry Pi
  8. Кусочек Raspberry Pi
  9. Cycle Chaser
  10. Датчик влажности почвы Raspberry Pi