使用 Windows 10 UWP 應(yīng)用程序的強大功能來控制和監(jiān)控啤酒桶,讓您的飲料保持涼爽、新鮮和隨時可用。
注意:首先,這個項目絕不會提倡使用或濫用酒精,這完全取決于用戶在這個 kegerator 中的內(nèi)容是什么飲料。
這個項目的誕生是為了更好地管理扎啤機。扎啤機的基本原理是保持飲料低溫以及將飲料碳酸保持在一定的 PSI 下。此外,僅僅給自己倒一杯冷飲,你根本不知道桶里還剩多少。
所以這個項目的目標是:
保持飲料的一致溫度,確保飲料不會過熱或過冷并凍結(jié)
確保將可接受的碳酸化量應(yīng)用于小桶以保持最佳風味
跟蹤每個桶中的飲料量并提供視覺反饋,以確保為大型比賽準備充足的飲料。
跟蹤用于碳酸飲料的罐中剩余的二氧化碳量
基本電子元件及其用途:
冷藏柜用于冷卻裝置并提供框架以制作精美的家具
Raspberry PI 2 Running Windows 10 IoT core 被用作操作的大腦
小型郵資秤用于測量每個小桶和二氧化碳罐的重量,這些郵資秤移除了電子設(shè)備,并在秤中內(nèi)置了稱重傳感器放大器和小型 Arduino。這些秤將通過 I2C 與 Raspberry PI 2 通信(稍后會詳細介紹)
設(shè)備上安裝了 5 個數(shù)字溫度傳感器,一個在冷凍柜的底部,一個安裝在頂部的下側(cè),一個安裝在水龍頭把手所在的塔架上(稍后會詳細介紹)和一個安裝在設(shè)備外部以測量環(huán)境溫度。這些溫度傳感器連接到一個小型 Arduino,還通過 I2C 與 Raspberry PI 2 通信
霍尼韋爾壓力傳感器連接到用于為小桶提供碳酸化作用的空氣管路上。雖然 PSI 的調(diào)整是手動的(目前),但這將提供一個準確的計量器來確定桶中的二氧化碳量。
5V 電源用于為 Raspberry PI2 供電。選擇了更大的版本(提供高達 6 安培),因此它也可以為可尋址的 LED 燈條供電。
一個簡單的繼電器與壓縮機電源串聯(lián)。使用這個繼電器,可以從壓縮機施加和斷開電源,然后壓縮機將依次控制 kegerator 的溫度(稍后會詳細介紹)
云連接
Ultimate Kegerator 包含一個 Web 服務(wù)器,允許通過 REST 服務(wù)進行遠程配置以及當前狀態(tài)的簡單靜態(tài)視圖。可以通過http://slsys.homeip.net:9501訪問該網(wǎng)站 。
此外,Ultimate Kegerator 將其重要統(tǒng)計數(shù)據(jù)上傳到 Windows Azure 事件中心。雖然無法使用標準 Nuget 包與事件中心對話,但是你可以使用 Windows Embedded MVP Paolo Patierno提供的易于實現(xiàn)的庫,網(wǎng)址為
https://www.nuget.org/packages/AzureSBLite/
數(shù)據(jù)被推送到WindowsAzure上的事件中心:
通過流分析進行最終處理
WindowsAzure上的流分析正在處理的數(shù)據(jù):
流分析的最終計劃是:
1) 監(jiān)控并通知溫度是否過熱或過冷
2) 監(jiān)測并通知 CO2 罐何時過低
3) 監(jiān)測并通知 CO2 罐是否檢測到泄漏(重量逐漸減輕)
以下是組裝過程的一些附加圖片:
電子秤組裝
稱重傳感器:
測試+5VDC 穩(wěn)壓器:
在測試壓縮機繼電器時準備連接:
電源:
CPU:
連接溫度傳感器:
Arduino的最終封裝:
壓力傳感器及其+5VDC至+3.3VDC穩(wěn)壓器:
最終成果:
Kegerator Class:
using LagoVista.Common.Commanding;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
namespace LagoVista.IoT.Common.Kegerator
{
public class Kegerator : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Models.Keg _keg1;
private Models.Keg _keg2;
private Models.Keg _keg3;
private Models.Keg _keg4;
private CO2.CO2Tank _co2Tank;
private Kegerator() { }
public List
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool Set
{
if (object.Equals(storage, value)) return false;
storage = value;
this.RaisePropertyChanged(propertyName);
return true;
}
byte[] _scalesAddresses = { 0x43, 0x41, 0x40, 0x42 };
private const string I2C_CONTROLLER_NAME = "I2C1";
private Thermo.Temperatures _temperatures;
private Thermo.Controller _tempController;
private Scales.Scale _co2Scale;
private Dictionary
private CO2.PressureSensor _pressureSensor;
private LED.LEDManager _ledManager;
private REST.KegeratorServices _kegServices;
private static Kegerator _kegerator = new Kegerator();
public static Kegerator Instance { get { return _kegerator; } }
private CloudServices.EventHubClient _eventHubClient;
System.Threading.Timer _timer;
private bool _initialized = false;
public async Task Init()
{
if (!_initialized)
{
_initialized = true;
var selector = I2cDevice.GetDeviceSelector(I2C_CONTROLLER_NAME); /* Find the selector string for the I2C bus controller */
var deviceInfo = (await DeviceInformation.FindAllAsync(selector)).FirstOrDefault(); /* Find the I2C bus controller device with our selector string */
var deviceId = deviceInfo == null ? (string)null : deviceInfo.Id;
_temperatures = new Thermo.Temperatures(0x48);
await _temperatures.Init(deviceId);
_devices.Add(_temperatures);
_tempController = new Thermo.Controller();
_tempController.Init(_temperatures);
_devices.Add(_tempController);
_pressureSensor = new CO2.PressureSensor();
await _pressureSensor.Init(deviceId, TimeSpan.FromSeconds(1));
_devices.Add(_pressureSensor);
_co2Scale = new Scales.Scale(0x44);
await _co2Scale.Init(deviceId, TimeSpan.FromSeconds(1));
_devices.Add(_co2Scale);
_co2Tank = new CO2.CO2Tank(_co2Scale, TimeSpan.FromSeconds(2));
_co2Tank.Load();
_devices.Add(_co2Tank);
_kegScales = new Dictionary
_eventHubClient = new CloudServices.EventHubClient(this, TimeSpan.FromSeconds(2));
_devices.Add(_eventHubClient);
for (var idx = 0; idx < 4; ++idx)
{
var scale = new Scales.Scale(_scalesAddresses[idx]);
await scale.Init(deviceId, TimeSpan.FromMilliseconds(500));
_kegScales.Add(idx, scale);
_devices.Add(scale);
}
_keg1 = new Models.Keg(1, _kegScales[0], TimeSpan.FromMilliseconds(500));
_keg1.Load();
_devices.Add(_keg1);
_keg2 = new Models.Keg(2, _kegScales[1], TimeSpan.FromMilliseconds(500));
_keg2.Load();
_devices.Add(_keg2);
_keg3 = new Models.Keg(3, _kegScales[2], TimeSpan.FromMilliseconds(500));
_keg3.Load();
_devices.Add(_keg3);
_keg4 = new Models.Keg(4, _kegScales[3], TimeSpan.FromMilliseconds(500));
_keg4.Load();
_devices.Add(_keg4);
DateInitialized = DateTime.Now.ToString();
Web.WebServer.Instance.StartServer();
_kegServices = new REST.KegeratorServices() { Port = 9500 };
_kegServices.EventContent += _kegServices_EventContent;
_kegServices.StartServer();
_timer = new System.Threading.Timer((state) =>
{
Refresh();
}, null, 0, 250);
}
}
private void _kegServices_EventContent(object sender, string e)
{
var parts = e.Split('/');
if (parts.Count() > 0)
{
switch (parts[1])
{
case "zero":
{
var scaleIndex = Convert.ToInt32(parts[2]);
_kegScales[scaleIndex].StoreOffset();
}
break;
case "cal":
{
var scaleIndex = Convert.ToInt32(parts[2]);
_kegScales[scaleIndex].CalibrationWeight = Convert.ToDouble(parts[3]);
_kegScales[scaleIndex].Calibrate();
}
break;
}
}
}
public void Refresh()
{
foreach (var device in _devices)
{
if (DateTime.Now > (device.LastUpdated + device.UpdateInterval))
device.Refresh();
}
LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() =>
{
CurrentTimeDisplay = DateTime.Now.ToString();
RaisePropertyChanged("CurrentTimeDisplay");
});
}
public Thermo.Temperatures Temperatures { get { return _temperatures; } }
public Thermo.Controller TemperatureController { get { return _tempController; } }
private String _statusMessage;
public String StatusMessage
{
get { return _statusMessage; }
set { Set(ref _statusMessage, value); }
}
public List
{
get { return _kegScales.Values.ToList(); }
}
public void ToggleCompressor()
{
if (_tempController.IsCompressorOn)
_tempController.CompressorOff();
else
_tempController.CompressorOn();
}
public String DateInitialized
{
get;
set;
}
public String CurrentTimeDisplay
{
get;
set;
}
public Scales.Scale CO2Scale
{
get { return _co2Scale; }
}
public CO2.PressureSensor PressureSensor
{
get { return _pressureSensor; }
}
public Models.Keg Keg1 { get { return _keg1; } }
public Models.Keg Keg2 { get { return _keg2; } }
public Models.Keg Keg3 { get { return _keg3; } }
public Models.Keg Keg4 { get { return _keg4; } }
public CO2.CO2Tank CO2Tank { get { return _co2Tank; } }
public RelayCommand ToggleCompressorCommand { get { return new RelayCommand(ToggleCompressor); } }
}
}
-
WINDOWS
+關(guān)注
關(guān)注
4文章
3549瀏覽量
88792 -
應(yīng)用程序
+關(guān)注
關(guān)注
37文章
3276瀏覽量
57732 -
DIY設(shè)計
+關(guān)注
關(guān)注
2文章
59瀏覽量
19824
發(fā)布評論請先 登錄
相關(guān)推薦
評論