Industriële fabricage
Industrieel internet der dingen | Industriële materialen | Onderhoud en reparatie van apparatuur | Industriële programmering |
home  MfgRobots >> Industriële fabricage >  >> Manufacturing Technology >> Productieproces

Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding

Onderdelen en benodigdheden

SparkFun Arduino Pro Mini 328 - 3.3V/8MHz × 7
SparkFun Load Cell Amplifier - HX711 × 1
Leviton 47603-12B 4x12 Telefoonverdeelbord × 1
Raspberry Pi 2 Model B × 1
AC 100-120V 200-220V 5V 6A 30W Schakelvoeding × 1
IR-bewegingssensor × 1
10.6 cu. ft. Vrieskist × 1
Smart Weight Digitale Postzegelschaal × 5
Rii K12 Ultra Slim 2.4 GHz Toetsenbord × 1
Raspberry Pi WiFi Dongle × 1
Arduino DC 5V Relaismodule × 1
DHT11 Temperatuur- & Vochtigheidssensor (4 pinnen) × 2
DHT22 Temperatuursensor × 3
HomeBrewStuff RVS Dubbele Bierkraantoren × 2
Bierkraantoren Koeler × 1
0-30 PSI I2C Druksensor (3.3V) ABPMANN030PG2A3 × 1

Benodigde tools en machines

  • Houtbewerkingsgereedschappen (voor aanpassing buitenkant vrieskist)
  • Draadknipper
  • Soldeerbout (standaard)
  • Krimpkous

Over dit project

Disclaimer: Allereerst bevordert dit project op geen manier het gebruik of misbruik van alcohol, het is volledig aan de gebruikers welke dranken de inhoud van deze kegerator zullen vormen.

Dit project ontstond uit de wens om de inhoud van een kegerator beter te beheren. Een kegerator werkt op het basisprincipe van het koud houden van een drankje alsook het koolzuurgas-houden van de dranken op een bepaalde PSI. Daarnaast heb je door het schenken van een koud drankje geen idee hoeveel er nog in het vat zit. Het zou jammer zijn als je gasten hebt voor een zondagse voetbalwedstrijd en halverwege het spel het root beer op is.

De doelen van dit project zijn:

  1. Een consistente temperatuur van de dranken handhaven, ervoor zorgen dat de dranken niet te warm of te koud worden en bevriezen
  2. Zorgen dat een acceptabele hoeveelheid koolzuur wordt toegepast op het vat om optimale smaak te behouden
  3. Bijhouden hoeveel drank er in elk vat zit en visuele feedback geven om ervoor te zorgen dat er voldoende drank is voor de grote wedstrijd
  4. Bijhouden hoeveel CO2 er nog in de tank zit die gebruikt wordt om de dranken te carbonatiseren

De basis elektronische componenten en hun gebruik:

  1. Een vrieskist wordt gebruikt als koeleenheid en om een frame te bieden voor een mooi meubelstuk
  2. Raspberry PI 2 met Windows 10 IoT Core is de 'hersenen' van de operatie
  3. Kleine postzegelschaalen meten het gewicht van elk vat en de CO2-tank. De elektronica is verwijderd en een load cell-versterker en kleine Arduino ingebouwd. Deze schalen communiceren met de Raspberry PI 2 via I2C
  4. Er zijn 5 digitale temperatuursensors geïnstalleerd: een op de bodem van de vrieskist, een aan de onderkant van het deksel, één in elke toren waar de kraanhandvatten zitten, en één aan de buitenkant om de omgevingstemperatuur te meten. Deze sensors zijn verbonden met een kleine Arduino en communiceren ook via I2C met de Raspberry PI 2
  5. Een Honeywell-druksensor is aangesloten op de luchtleidingen die koolzuur leveren aan de vaten. Hoewel de PSI-instelling handmatig is (voor nu), biedt dit een nauweweegschaal van hoeveel CO2 op de vaten wordt toegepast
  6. Een 5V-voeding voedt de Raspberry PI2. Een zwaardere versie (tot 6 ampere) werd gekozen zodat deze ook een adresserbare LED-strip kan voeden
  7. Een eenvoudig relais wordt in serie met de voeding van de compressor geplaatst. Met dit relais kan de stroom aan/uitgeschakeld worden, wat de temperatuur van de kegerator regelt

Cloud Connectiviteit

De Ultieme Kegerator bevat een webserver voor externe configuratie via REST-services en een eenvoudige statische weergave van de huidige status. Deze website is bereikbaar op https://slsys.homeip.net:9501.

Daarnaast uploadt de Ultieme Kegerator de vitale statistieken naar een Windows Azure Event Hub. Je kunt het standaard Nuget-pakket niet gebruiken voor communicatie met de Event Hub, maar de eenvoudig te implementeren bibliotheek van Windows Embedded MVP Paolo Patierno, beschikbaar op NuGet.

Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding

Voor uiteindelijke verwerking door Stream Analytics:

Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding

Toekomstige plannen voor Stream Analytics:

  1. Monitoren en notificeren als temperaturen te warm of te koud worden
  2. Monitoren en notificeren wanneer de CO2-tank te laag is
  3. Monitoren en notificeren bij een lekkage in de CO2-tank (geleidelijke afname in gewicht)

Hier zijn enkele extra foto's van het assemblageproces:

Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding
Ultieme DIY Kegerator: Complete Componenten & Bouwhandleiding

-twb

Code

Keg Class C#
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;
        TimeSpan _updateInterval;
        private Scales.Scale _scale;
        public Keg(int idx, Scales.Scale scale, TimeSpan updateInterval) {
            _idx = idx;
            _updateInterval = UpdateInterval;
            _scale = 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 - ContainerWeightLb)) * 100);
                else
                    PercentFull = 0;
                PercentFull = Math.Min(PercentFull, 100);
                if (GlassSizeOz > 0)
                    QtyRemaining = Convert.ToInt32((ContentsWeight * 16) / GlassSizeOz);
                else
                    QtyRemaining = 0;
                RaisePropertyChanged("PercentFullHeight");
                RaisePropertyChanged("PercentFullDisplay");
            });
        }
        public Scales.Scale Scale {
            get { return _scale; }
        }
        #region Calculated Properties
        private int _qtyRemaining;
        public int QtyRemaining {
            get { return _qtyRemaining; }
            set { Set(ref _qtyRemaining, value); }
        }
        private DateTime? _installDate;
        public DateTime? InstallDate {
            get { return _installDate; }
            set { Set(ref _installDate, value); }
        }
        private int _percentFull;
        public int PercentFull {
            get { return _percentFull; }
            set { Set(ref _percentFull, value); }
        }
        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 Entered Properties
        private bool _isEmpty;
        public bool IsEmpty {
            get { return _isEmpty; }
            set { _isEmpty = value; RaisePropertyChanged(); }
        }
        private double _glassSize;
        public double GlassSizeOz {
            get { return _glassSize; }
            set { Set(ref _glassSize, value); }
        }
        private DateTime? _bornDate;
        public DateTime? BornDate {
            get { return _bornDate; }
            set { Set(ref _bornDate, value); }
        }
        double _containerWeight;
        public double ContainerWeightLb {
            get { return _containerWeight; }
            set { Set(ref _containerWeight, value); }
        }
        double _contentsWeight;
        public double ContentsWeight {
            get { return _contentsWeight; }
            set { Set(ref _contentsWeight, value); }
        }
        double _fullContentsWeight;
        public double FullContentsWeightLb {
            get { return _fullContentsWeight; }
            set { Set(ref _fullContentsWeight, value); }
        }
        private String _contentsName;
        public String ContentsName {
            get { return _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"));
            var bornDate = GetSetting("KEG{0}_BORN_DATE", String.Empty);
            if (!String.IsNullOrEmpty(bornDate))
                BornDate = DateTime.Parse(bornDate);
            else
                BornDate = null;
            var installDate = GetSetting("KEG{0}_INSTALL_DATE", String.Empty);
            if (!String.IsNullOrEmpty(installDate))
                InstallDate = DateTime.Parse(installDate);
            else
                InstallDate = null;
        }
        public async void SaveFullWeight() {
            FullContentsWeightLb = await Scale.GetAverageWeight();
            Save();
        }
        public RelayCommand SaveFullWeightCommand {
            get { return new RelayCommand(() => SaveFullWeight()); }
        }
    }
}
Scale Class C#
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;
        double? _calibrationFactor = null;
        private TimeSpan _updateInterval;
        byte _address;
        public Scale(byte address) {
            _address = address;
        }
        private void WriteValue(byte address, int value) {
            if (!IsDemoMode) {
                var offsetBuffer = new byte[5];
                offsetBuffer[0] = address;
                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 = 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)]);
                    try {
                        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";
                });
            }
        }
        int? _lastRaw = null;
        private int GetRaw() {
            try {
                var inbuffer = new byte[4];
                _scaleI2CChannel.Write(new byte[] { (byte)0x11 });
                _scaleI2CChannel.Read(inbuffer);
                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;
                return thisRaw;
            } catch (Exception) {
                return -1;
            }
        }
        public override void Refresh() {
            LastUpdated = DateTime.Now;
            int rawResult = 0;
            var isOnline = true;
            try {
                var inbuffer = new byte[4];
                var statusBuffer = new byte[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("Staritng Zero Process");
            long zeroSum = 0;
            for (var idx = 0; idx < CALIBRATION_COUNT; ++idx) {
                await Task.Delay(250);
                zeroSum += GetRaw();
            }
            _countOffset = (int)(zeroSum / CALIBRATION_COUNT);
            WriteValue((byte)'O', _countOffset);
            Debug.WriteLine(String.Format("Finished Zero Process {0:X}", _countOffset));
            if (Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey(String.Format("{0:X}.OFFSET", _address)))
                Windows.Storage.ApplicationData.Current.LocalSettings.Values.Remove(String.Format("{0:X}.OFFSET", _address));
            Windows.Storage.ApplicationData.Current.LocalSettings.Values.Add(String.Format("{0:X}.OFFSET", _address), _countOffset);
            Debug.WriteLine("Finished Zero Process");
            LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => {
                Status = "Zeroed";
            });
        }
        public async void Calibrate() {
            Status = "Calibrating";
            LagoVista.Common.PlatformSupport.Services.BindingHelper.RefreshBindings();
            long countSum = 0;
            for (var idx = 0; idx < CALIBRATION_COUNT; ++idx) {
                await Task.Delay(250);
                countSum += GetRaw() - _countOffset;
            }
            _calibrationFactor = CalibrationWeight / (countSum / CALIBRATION_COUNT);
            if (Windows.Storage.ApplicationData.Current.LocalSettings.Values.ContainsKey(String.Format("{0:X}.CALIBRATION", _address)))
                Windows.Storage.ApplicationData.Current.LocalSettings.Values.Remove(String.Format("{0:X}.CALIBRATION", _address));
            Windows.Storage.ApplicationData.Current.LocalSettings.Values[String.Format("{0:X}.CALIBRATION", _address)] = _calibrationFactor;
        }
        public async Task<double> GetAverageWeight(int pointCount = 5) {
            var weightSum = 0.0;
            for (var idx = 0; idx < pointCount; ++idx) {
                weightSum += Weight;
                await Task.Delay(UpdateInterval);
            }
            return weightSum / pointCount;
        }
        private int _fullRaw;
        public int FullRaw {
            get { return _fullRaw; }
            set { Set(ref _fullRaw, value); }
        }
        private double _weight;
        public double Weight {
            get { return _weight; }
            set { _weight = value; }
        }
        private String _weightDisplay;
        public String WeightDisplay {
            get { return _weightDisplay; }
            set { Set(ref _weightDisplay, value); }
        }
        private int _raw;
        public int Raw {
            get { return _raw; }
            set { RaisePropertyChanged("RawDisplay"); Set(ref _raw, value); }
        }
        public String AddressDisplay {
            get { return String.Format("0x{0:X}", _address); }
        }
        public String RawDisplay {
            get { return String.Format("{0:X}", Raw); }
        }
        private int _tareRaw;
        public int TareRaw {
            get { return _tareRaw; }
            set { Set(ref _tareRaw, value); }
        }
        private int _beerCount;
        public int BeerCount {
            get { return _beerCount; }
            set { Set(ref _beerCount, value); }
        }
        private double _percentFull;
        public double PercentFull {
            get { return _percentFull; }
            set { Set(ref _percentFull, value); }
        }
        public override TimeSpan UpdateInterval {
            get { return _updateInterval; }
        }
        private double _calibrationWeight;
        public double CalibrationWeight {
            get { return _calibrationWeight; }
            set { _calibrationWeight = value; }
        }
        public RelayCommand ZeroCommand {
            get { return new RelayCommand(() => StoreOffset()); }
        }
        public RelayCommand CalibrationCommand {
            get { return new RelayCommand(() => Calibrate()); }
        }
    }
}
Kegerator Class C#
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<DeviceBase> _devices = new List<DeviceBase>();
        private void RaisePropertyChanged([CallerMemberName] string propertyName = null) {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null) {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        private bool Set<T>(ref T storage, T value, string columnName = null, [CallerMemberName] string propertyName = null) {
            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<int, Scales.Scale> _kegScales;
        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);
                var deviceInfo = (await DeviceInformation.FindAllAsync(selector)).FirstOrDefault();
                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<int, Scales.Scale>();
                _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<Scales.Scale> KegScales {
            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); }
        }
    }
}

Schema's

Hoog-niveau systeemcomponentendiagram

Productieproces

  1. AI-gestuurd obstakelvermijdingssysteem met Arduino en ultrasone sensoren
  2. Interactieve gyroscoop LED-display met NeoPixel-ring en Arduino
  3. DIY Arduino USB Gaming Controller – Bouw Je Eigen High-Performance Gamepad
  4. DIY Wolk Kostuum: LED-strip, Arduino Nano & SparkFun Onderdelen
  5. Industriële Lijnvolgende Robot voor Materiaaltransport
  6. Pixie: Een Arduino-gebaseerde NeoPixel-polshorloge – Modern, DIY LED-tijdstuk
  7. Slimme Arduino-waterfles – realtime monitoring & herinneringen
  8. DIY Kerst Schaduwtheater: Bouw een lichtshow met Arduino 101 en adresseerbare LED-strip
  9. Draagbare Remote Surveillance Camera Robot
  10. Slimme gebaar-naar-tekst handskoen voor gebarentaal
  11. Top Wire EDM machines: precisiesnijoplossingen uit Taiwan