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

终极酒桶

组件和用品

SparkFun Arduino Pro Mini 328 - 3.3V/8MHz
× 7
SparkFun 称重传感器放大器 - HX711
× 1
Leviton 47603-12B 4x12 电话分配板
× 1
Raspberry Pi 2 B 型
× 1
AC 100-120V 200-220V 5V 6A 30W 开关电源
× 1
红外运动传感器
× 1
10.6 立方厘米。英尺冷冻柜
× 1
智能重量数字邮资秤
× 5
Rii K12 超薄 2.4 GHz 键盘
× 1
Rasbperry Pi WiFi Dongle
× 1
Arduino DC 5V 继电器模块
× 1
DHT11 温湿度传感器(4 针)
× 2
DHT22 温度传感器
× 3
HomeBrewStuff 不锈钢双生啤酒塔
× 2
啤酒塔冷却器
× 1
0-30 PSI I2C 压力传感器 (3.3V) ABPMANN030PG2A3
× 1

必要的工具和机器

木工工具
自定义冰箱外部的工具
线切割器
烙铁(通用)
热缩管

关于这个项目

免责声明: 首先,这个项目不提倡使用或滥用酒精,完全取决于用户将用什么饮料来组成这个小酒壶的内容。

这个项目的诞生是为了更好地管理发酵罐的内容。发酵罐的工作原理是使饮料保持低温以及在特定 PSI 下保持饮料的碳酸化。此外,只要给自己倒一杯冷饮,你都不知道小桶里还剩多少。让人们参加周日足球比赛并在比赛进行到一半时用完根啤酒将是一种耻辱。



所以这个项目的目标是:

  1. 保持饮料的温度一致,确保饮料不会过热或过冷而结冰
  2. 确保向小桶施加可接受的碳酸化量以保持最佳风味
  3. 跟踪每个小桶中的饮料数量并提供视觉反馈,以确保有足够的饮料供大型比赛使用。
  4. 跟踪用于给饮料充碳酸气的罐中剩余的二氧化碳量

基本电子元件及其用途:

  1. 冷冻柜用于冷却装置并提供框架以制作精美的家具
  2. 运行 Windows 10 IoT 核心的 Raspberry PI 2 用作操作的大脑
  3. 小型邮资秤用于测量每个小桶和二氧化碳罐的重量,这些邮资秤已拆除电子设备,并在秤中内置了称重传感器放大器和小型 Arduino。这些秤将通过 I2C 与 Raspberry PI 2 通信(稍后会详细介绍)
  4. 装置上安装了 5 个数字温度传感器,一个在冷冻柜底部,一个连接在顶部下方,每个安装在水龙头把手所在的塔中(稍后会详细介绍) ) 和一个安装在设备外部以测量环境温度。这些温度传感器连接到一个小型 Arduino 并通过 I2C 与 Raspberry PI 2 通信
  5. 霍尼韦尔压力传感器连接到用于为小桶提供碳酸化作用的空气管线。虽然 PSI 的调整是手动的(目前),但这将提供一个准确的计量有多少 CO2 施加到小桶。
  6. 使用 5V 电源为 Raspberry PI2 供电。选择了更大的版本(提供高达 6 安培),因此它还可以为可寻址 LED 灯条供电。
  7. 一个简单的继电器与压缩机的电源串联。使用这个继电器,可以给压缩机通电和断电,然后压缩机将依次控制发酵罐的温度(稍后会详细介绍)

云连接

Ultimate Kegerator 包含一个 Web 服务器,允许通过 REST 服务进行远程配置以及当前状态的简单静态视图。可以通过 http://slsys.homeip.net:9501 访问该网站。

此外,Ultimate Kegerator 将其重要统计数据上传到 Windows Azure 事件中心。您将无法使用标准 Nuget 包与事件中心通信,但是您可以使用 Windows Embedded MVP Paolo Patierno 提供的易于实现的库,可在

https://www.nuget.org/packages/AzureSBLite/

用于流分析的最终处理

流分析的最终计划是:

1) 监控并通知温度是否太热或太冷

2) CO2 罐过低时监测并通知

3) 监测并通知 CO2 罐是否有泄漏(重量逐渐减少)


以下是组装过程的一些附加图片:


-twb

代码

  • 酒桶类
  • 音阶班
  • Kegerator 类
小桶类C#
在 GitHub 上发布完整源代码之前的源代码预览。如果您想抢先体验或想帮助做出贡献,请联系该项目的作者
using LagoVista.Common.Commanding;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System .Threading.Tasks;using Windows.UI.Xaml;namespace LagoVista.IoT.Common.Kegerator.Models{ public class Keg :DeviceBase { int _idx;时间跨度_updateInterval;私人 Scales.Scale _scale;公共小桶(int idx,Scales.Scale scale,TimeSpan updateInterval){_idx =idx; _updateInterval =更新间隔; _scale =比例; } public override TimeSpan UpdateInterval { get { return _updateInterval; } } public override void Refresh() { LastUpdated =DateTime.Now; LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { ContentsWeight =Scale.Weight - ContainerWeightLb; if (FullContentsWeightLb> 0) PercentFull =Convert.ToInt32((ContentsWeight / (FullContentsWeightLb -)); *Container else PercentFull =0; PercentFull =Math.Min(PercentFull, 100); if (GlassSizeOz> 0) QtyRemaining =Convert.ToInt32((ContentsWeight * 16)/ GlassSizeOz); else QtyRemaining =0; RaisePropertyChanged("PercentFull); ("PercentFullDisplay"); }); } public Sc​​ales.Scale Scale { get { return _scale; } } #region 计算属性 private int _qtyRemaining;公共 int QtyRemaining { 获取 { 返回 _qtyRemaining; } set { Set(ref _qtyRemaining, value);私人日期时间? _安装日期;公共日期时间?安装日期 { 获取 { 返回 _installDate; } 设置 { 设置(参考 _installDate,值); } } 私有整数_percentFull;公共 int PercentFull { 获取 { 返回 _percentFull; } 设置 { 设置(参考 _percentFull,值); } } public String PercentFullDisplay { get { return String.Format("{0}%", Convert.ToInt32(PercentFull)); } } public double PercentFullHeight { get { return Convert.ToDouble(_percentFull * 2); } } public int KegIndex { get { return _idx; } } #endregion #region 进入属性私有 bool _isEmpty; public bool IsEmpty { get { return _isEmpty; } 设置 { _isEmpty =值; RaisePropertyChanged();私人双 _glassSize;公共双 GlassSizeOz { 得到 { 返回 _glassSize; } 设置 { 设置(参考 _glassSize,值);私人日期时间? _出生日期;公共日期时间?出生日期 { 得到 { 返回 _bornDate; } 设置 { 设置(参考 _bornDate,值);双 _containerWeight;公共双ContainerWeightLb { 得到{ 返回_containerWeight; } set { Set(ref _containerWeight, value);双 _contentsWeight; public double ContentsWeight { get { return _contentsWeight; } set { Set(ref _contentsWeight, value);双 _fullContentsWeight;公共双 FullContentsWeightLb { 得到 { 返回 _fullContentsWeight; } set { Set(ref _fullContentsWeight, value); } } 私有字符串_contentsName;公共字符串内容名称 { 获取 { 返回 _内容名称; } set { Set(ref _contentsName, value); } } #endregion public void Save() { LagoVista.Common.PlatformSupport.Services.BindingHelper.RefreshBindings(); PutSetting(String.Format("KEG{0}_CONTENTS", _idx), ContentsName); PutSetting(String.Format("KEG{0}_IS_EMPTY", _idx), IsEmpty.ToString()); PutSetting(String.Format("KEG{0}_CONTAINER_WEIGHT", _idx), String.Format("{0:0.00}", ContainerWeightLb)); PutSetting(String.Format("KEG{0}_GLASS_SIZE", _idx), String.Format("{0:0.00}", GlassSizeOz)); PutSetting(String.Format("KEG{0}_FULL_CONTENTS_WEIGHT", _idx), String.Format("{0:0.00}", FullContentsWeightLb)); if (BornDate.HasValue) PutSetting(String.Format("KEG{0}_BORN_DATE", _idx), BornDate.Value.ToString()); else RemoveSetting(String.Format("KEG{0}_BORN_DATE", _idx)); if(InstallDate.HasValue) PutSetting(String.Format("KEG{0}_INSTALL_DATE", _idx), InstallDate.Value.ToString()); else RemoveSetting(String.Format("KEG{0}_INSTALL_DATE", _idx)); } public void Load() { ContentsName =GetSetting(String.Format("KEG{0}_CONTENTS", _idx), "?"); ContainerWeightLb =Convert.ToDouble(GetSetting(String.Format("KEG{0}_CONTAINER_WEIGHT", _idx), "10.0")); GlassSizeOz =Convert.ToDouble(GetSetting(String.Format("KEG{0}_GLASS_SIZE", _idx), "12.0")); FullContentsWeightLb =Convert.ToDouble(GetSetting(String.Format("KEG{0}_FULL_CONTENTS_WEIGHT", _idx), "0.0")); IsEmpty =Convert.ToBoolean(GetSetting(String.Format("KEG{0}_IS_EMPTY", _idx), "True")); varbornDate =GetSetting("KEG{0}_BORN_DATE", String.Empty); if (!String.IsNullOrEmpty(bornDate)) BornDate =DateTime.Parse(bornDate);其他出生日期=空; var installDate =GetSetting("KEG{0}_INSTALL_DATE", String.Empty); if (!String.IsNullOrEmpty(installDate)) InstallDate =DateTime.Parse(installDate);否则安装日期 =空; } public async void SaveFullWeight() { FullContentsWeightLb =await Scale.GetAverageWeight();保存(); } public RelayCommand SaveFullWeightCommand { get { return new RelayCommand(() => SaveFullWeight()); } } }}
Scale ClassC#
在 GitHub 上发布完整源代码之前的源代码预览。如果您想抢先体验或想帮助做出贡献,请联系该项目的作者
using LagoVista.Common.Commanding;using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System .Text;using System.Threading.Tasks;using Windows.Devices.I2c;namespace LagoVista.IoT.Common.Kegerator.Scales{ public class Scale :DeviceBase { Windows.Devices.I2c.I2cDevice _scaleI2CChannel; int _countOffset;双倍的? _calibrationFactor =null;私人时间跨度_updateInterval;字节_地址;公共规模(字节地址){ _address =地址; } private void WriteValue(byte address, int value) { if (!IsDemoMode) { var offsetBuffer =new byte[5]; offsetBuffer[0] =地址; offsetBuffer[1] =(byte)(value>> 24); offsetBuffer[2] =(byte)(value>> 16); offsetBuffer[3] =(byte)(value>> 8); offsetBuffer[4] =(byte)(value); _scaleI2CChannel.Write(offsetBuffer); } } public async Task Init(String i2cDeviceId, TimeSpan updateInterval) { var settings =new I2cConnectionSettings(_address) { BusSpeed =I2cBusSpeed.StandardMode, SharingMode =I2cSharingMode.Shared }; _updateInterval =更新间隔; IsDemoMode =String.IsNullOrEmpty(i2cDeviceId); if (!IsDemoMode) { _scaleI2CChannel =await Windows.Devices.I2c.I2cDevice.FromIdAsync(i2cDeviceId, settings); if (Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey(String.Format("{0:X}.OFFSET", _address))) { _countOffset =Convert.ToInt32(Windows.Storage.ApplicationData.Current.LocalSettings .Values[String.Format("{0:X}.OFFSET", _address)]);尝试 { WriteValue((byte)'O', _countOffset); } catch (Exception ex) { Debug.WriteLine("Scale offline "); } } if (Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey(String.Format("{0:X}.CALIBRATION", _address))) { _calibrationFactor =Convert.ToDouble(Windows.Storage.ApplicationData.Current .LocalSettings.Values[String.Format("{0:X}.CALIBRATION", _address)]); LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { Status ="Ready"; }); } } else { LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { Status ="Ready"; });整数? _lastRaw =空; private int GetRaw() { try { var inbuffer =new byte[4]; _scaleI2CChannel.Write(new byte[] { (byte)0x11 }); _scaleI2CChannel.Read(inbuffer); /* 注意比例尺,这是一个 long(64 位),这里是一个 int(64 位)*/ var thisRaw =(int)(inbuffer[0] <<24 | inbuffer[1] <<16 | inbuffer[ 2] <<8 | inbuffer[3]); if (_lastRaw.HasValue) { if (Math.Abs​​(_lastRaw.Value - thisRaw)> 0xFFFF) return _lastRaw.Value; } else _lastRaw =thisRaw;返回这个Raw; } catch (Exception) { return -1; } } public override void Refresh() { LastUpdated =DateTime.Now; int rawResult =0; var isOnline =true;尝试 { var inbuffer =new byte[4]; var statusBuffer =新字节[1]; if (!IsDemoMode) { _scaleI2CChannel.Write(new byte[] { (byte)0x0A }); _scaleI2CChannel.Read(statusBuffer); rawResult =GetRaw(); } if (_calibrationFactor.HasValue) { Weight =(rawResult - _countOffset) * _calibrationFactor.Value; Debug.WriteLine(String.Format("0x{0:X} WEIGHT VALUE => {1:0.00} lbs", _address, Weight)); } else if (_countOffset> 0) Debug.WriteLine(String.Format("0x{0:X} ZEROED VALUE => {1}", _address, rawResult - _countOffset)); else Debug.WriteLine(String.Format("0x{0:X} RAW VALUE => 0x{1:X}", _address, rawResult)); } catch (Exception ex) { rawResult =-1; isOnline =false; Debug.WriteLine(ex.Message); } LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { Raw =rawResult; IsOnline =isOnline; if (!IsOnline) { Status ="Offline"; WeightDisplay ="?"; } else { if (_calibrationFactor .HasValue) { Status ="Ready"; WeightDisplay =String.Format("{0}lb {1:00}oz", Math.Truncate(Weight), ((Weight % 1.0) * 16.0)); } else { WeightDisplay ="?"; Status ="Not Calibrated"; } } RaisePropertyChanged("LastUpdateDisplay"); }); } const int CALIBRATION_COUNT =10; public async void StoreOffset() { LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { Status ="Zeroing"; }); Debug.WriteLine("启动零进程");长零和 =0; for (var idx =0; idx  { Status ="Zeroed"; }); } public async void Calibrate() { Status ="Calibrating"; LagoVista.Common.PlatformSupport.Services.BindingHelper.RefreshBindings();长计数和 =0; for (var idx =0; idx  GetAverageWeight(int pointCount =5) { var weightSum =0.0; for(var idx =0; idx  StoreOffset()); } } public RelayCommand CalibrationCommand { get { return new RelayCommand(() => Calibrate()); } } }}
Kegerator 类C#
在 GitHub 上发布完整源代码之前的源代码预览。如果您想抢先体验或想帮助做出贡献,请联系该项目的作者
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 { 公共事件 PropertyChangedEventHandler PropertyChanged;私人 Models.Keg _keg1;私人 Models.Keg _keg2;私人 Models.Keg _keg3;私人 Models.Keg _keg4;私人 CO2.CO2Tank _co2Tank; private Kegerator() { } public List _devices =new List(); private void RaisePropertyChanged([CallerMemberName] string propertyName =null) { var eventHandler =this.PropertyChanged; if (eventHandler !=null) { eventHandler(this, new PropertyChangedEventArgs(propertyName)); } } private bool Set(ref T storage, T value, string columnName =null, [CallerMemberName] string propertyName =null) { if (object.Equals(storage, value)) return false;存储=价值; this.RaisePropertyChanged(propertyName);返回真; } byte[] _scalesAddresses ={ 0x43, 0x41, 0x40, 0x42 };私有常量字符串 I2C_CONTROLLER_NAME ="I2C1";私人 Thermo.Temperatures _温度;私人 Thermo.Controller _tempController;私人 Scales.Scale _co2Scale;私人字典 _kegScales;私人 CO2.PressureSensor _pressureSensor;私人 LED.LEDManager _ledManager;私有 REST.KegeratorServices _kegServices;私有静态 Kegerator _kegerator =new Kegerator();公共静态 Kegerator 实例 { 获取 { 返回 _kegerator;私有 CloudServices.EventHubClient _eventHubClient; System.Threading.Timer _timer;私人布尔_初始化=假;公共异步任务 Init() { if (!_initialized) { _initialized =true; var selector =I2cDevice.GetDeviceSelector(I2C_CONTROLLER_NAME); /* 查找 I2C 总线控制器的选择器字符串 */ var deviceInfo =(await DeviceInformation.FindAllAsync(selector)).FirstOrDefault(); /* 使用我们的选择器字符串查找 I2C 总线控制器设备 */ var deviceId =deviceInfo ==null ? (string)null :deviceInfo.Id; _温度 =新的 Thermo.Temperatures(0x48);等待 _temperatures.Init(deviceId); _devices.Add(_温度); _tempController =new Thermo.Controller(); _tempController.Init(_温度); _devices.Add(_tempController); _pressureSensor =new CO2.PressureSensor();等待 _pressureSensor.Init(deviceId, TimeSpan.FromSeconds(1)); _devices.Add(_pressureSensor); _co2Scale =new Scales.Scale(0x44);等待 _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]);等待 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 =新 REST.KegeratorServices() { 端口 =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(); } 休息; case "cal":{ var scaleIndex =Convert.ToInt32(parts[2]); _kegScales[scaleIndex].CalibrationWeight =Convert.ToDouble(parts[3]); _kegScales[scaleIndex].Calibrate(); } 休息; } } } 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 温度 { get { return _temperatures; } } public Thermo.Controller TemperatureController { get { return _tempController; } } 私有字符串_statusMessage;公共字符串状态消息 { 获取 { 返回 _statusMessage; } set { Set(ref _statusMessage, value); } } public List KegScales { get { return _kegScales.Values.ToList(); } } public void ToggleCompressor() { if (_tempController.IsCompressorOn) _tempController.CompressorOff();否则 _tempController.CompressorOn(); } public String DateInitialized { get;放; } 公共字符串 CurrentTimeDisplay { 获取;放; } public Sc​​ales.Scale CO2Scale { get { return _co2Scale; } } public CO2.PressureSensor 压力传感器 { 得到 { 返回 _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); } } }}

示意图

高级系统组件图

制造工艺

  1. 使用人工智能避障
  2. NeoPixel Ring 的陀螺仪乐趣
  3. Arduino 游戏控制器
  4. 云服装
  5. 供应材料的工业线跟随器
  6. Pixie:基于 Arduino 的 NeoPixel 手表
  7. Arduino 供电的水壶
  8. 假日皮影戏
  9. 移动远程监控摄像头
  10. 智能手套
  11. IoT4Car
  12. 台湾终极线切割机