Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Ultimate Kegerator

Komponenten und Verbrauchsmaterialien

SparkFun Arduino Pro Mini 328 - 3.3V/8MHz
× 7
SparkFun Wägezellenverstärker - HX711
× 1
Leviton 47603-12B 4x12 Telefonverteilungsplatine
× 1
Raspberry Pi 2 Model B
× 1
AC 100-120V 200-220V 5V 6A 30W Swich-Netzteil
× 1
IR-Bewegungssensor
× 1
10.6 cu. ft. Gefriertruhe
× 1
Smart Weight Digitale Briefwaage
× 5
Rii K12 Ultra Slim 2,4 GHz Tastatur
× 1
Rasbperry Pi WLAN-Dongle
× 1
Arduino DC 5V Relaismodul
× 1
DHT11 Temperatur- und Feuchtigkeitssensor (4 Pins)
× 2
DHT22-Temperatursensor
× 3
HomeBrewStuff Doppelfass-Bierturm aus Edelstahl
× 2
Bierturmkühler
× 1
0-30 PSI I2C-Drucksensor (3,3V) ABPMANN030PG2A3
× 1

Notwendige Werkzeuge und Maschinen

Holzbearbeitungswerkzeuge
Werkzeuge zum Anpassen der Außenseite des Gefrierschranks
Drahtschneider
Lötkolben (generisch)
Schrumpfschlauch

Über dieses Projekt

Haftungsausschluss: Zunächst einmal fördert dieses Projekt in keiner Weise den Konsum oder Missbrauch von Alkohol, es liegt ganz bei den Benutzern, welche Getränke den Inhalt dieses Kegerators bilden.

Dieses Projekt entstand aus dem Wunsch heraus, den Inhalt eines Kegerators besser zu verwalten. Ein Kegerator arbeitet nach dem Grundprinzip, ein Getränk kalt zu halten und die Getränke auf einem bestimmten PSI kohlensäurehaltig zu halten. Außerdem haben Sie beim Einschenken eines kalten Getränks keine Ahnung, wie viel noch im Fass ist. Es wäre eine Schande, Leute zu einem sonntäglichen Fußballspiel zu haben und nach der Hälfte des Spiels kein Root Beer mehr zu haben.



Die Ziele dieses Projekts sind also:

  1. Halten Sie eine konstante Temperatur der Getränke aufrecht, stellen Sie sicher, dass die Getränke nicht zu warm oder zu kalt werden und frieren Sie sie ein
  2. Stellen Sie sicher, dass dem Fass eine akzeptable Menge Kohlensäure zugeführt wird, um den optimalen Geschmack zu erhalten
  3. Behalten Sie die Getränkemenge in jedem Fass im Auge und geben Sie visuelles Feedback, um sicherzustellen, dass für das große Spiel genügend Getränke zur Verfügung stehen.
  4. Behalten Sie die verbleibende CO2-Menge im Tank im Auge, die zum Karbonisieren der Getränke verwendet wird

Die grundlegenden elektronischen Komponenten und ihre Verwendung:

  1. Eine Gefriertruhe dient als Kühleinheit und als Rahmen für ein schönes Möbelstück
  2. Raspberry PI 2, auf dem Windows 10 IoT Core ausgeführt wird, wird als das Gehirn des Betriebs verwendet
  3. Kleine Briefwaagen werden verwendet, um das Gewicht jedes Fasses sowie des CO2-Tanks zu messen. Bei diesen Briefwaagen wurde die Elektronik entfernt und ein Wägezellenverstärker und ein kleiner Arduino in die Waage eingebaut. Diese Waagen werden über I2C mit dem Raspberry PI 2 kommunizieren (dazu später mehr)
  4. Es gibt 5 digitale Temperatursensoren, die am Gerät installiert sind, einer an der Unterseite der Gefriertruhe, einer an der Unterseite der Oberseite, jeweils einer in den Türmen, wo sich die Wasserhahngriffe befinden (mehr dazu später .) ) und einer an der Außenseite des Geräts installiert, um die Umgebungstemperatur zu messen. Diese Temperatursensoren sind mit einem kleinen Arduino verbunden und kommunizieren auch mit dem Raspberry PI 2 über I2C
  5. Ein Honeywell-Drucksensor ist an den Luftleitungen angebracht, die verwendet werden, um die Fässer mit Kohlensäure zu versorgen. Obwohl die Einstellung des PSI (vorerst) manuell erfolgt, bietet dies einen genauen Hinweis darauf, wie viel CO2 den Fässern zugeführt wird.
  6. Ein 5V-Netzteil wird verwendet, um den Raspberry PI2 mit Strom zu versorgen. Es wurde eine größere Version (mit bis zu 6 Ampere) gewählt, damit sie auch einen adressierbaren LED-Streifen mit Strom versorgen kann.
  7. Ein einfaches Relais wird in Reihe mit der Stromversorgung des Kompressors geschaltet. Über dieses Relais kann der Kompressor mit Strom versorgt und wieder getrennt werden, der Kompressor regelt dann wiederum die Temperatur des Kegerators (dazu später mehr)

Cloud-Konnektivität

Der Ultimate Kegerator enthält einen Webserver, der eine Fernkonfiguration über REST-Dienste sowie eine einfache statische Ansicht des aktuellen Status ermöglicht. Diese Website kann unter http://slsys.homeip.net:9501  erreicht werden.

Darüber hinaus lädt der Ultimate Kegerator seine wichtigen Statistiken in einen Windows Azure Event Hub hoch. Sie können jedoch nicht das Standard-Nuget-Paket verwenden, um mit dem Event Hub zu sprechen, Sie können jedoch die einfach zu implementierende Bibliothek verwenden, die von einem anderen Windows Embedded MVP Paolo Patierno bereitgestellt wird, verfügbar unter

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

Für die ultimative Verarbeitung durch Stream Analytics

Mögliche Pläne für Stream Analytics sind:

1) Überwachen und benachrichtigen, wenn die Temperaturen zu warm oder zu kalt werden

2) Überwachen und benachrichtigen, wenn der CO2-Tank zu niedrig wird

3) Überwachen und benachrichtigen, wenn ein Leck im CO2-Tank festgestellt wird (allmähliche Gewichtsabnahme)


Hier sind einige zusätzliche Bilder des Montageprozesses:


-twb

Code

  • Keg-Klasse
  • Klasse skalieren
  • Kegerator-Klasse
Keg-KlasseC#
Vorschau des Quellcodes vor der Veröffentlichung des vollständigen Quellcodes auf GitHub. Wenn Sie einen frühen Zugriff wünschen oder einen Beitrag leisten möchten, wenden Sie sich bitte an den Autor dieses Projekts
mit LagoVista.Common.Commanding;using System;using System.Collections.Generic;using System.Linq;using System.Text;using System .Threading.Tasks;mit Windows.UI.Xaml;Namespace LagoVista.IoT.Common.Kegerator.Models{ public class Keg :DeviceBase { int _idx; TimeSpan _updateInterval; private Scales.Scale _scale; öffentliches Fass(int idx, Scales.Scale scale, TimeSpan updateInterval) { _idx =idx; _updateInterval =UpdateInterval; _scale =Skala; } public override TimeSpan UpdateInterval { get { return _updateInterval; } } öffentliche Überschreibung void Refresh() { LastUpdated =DateTime.Now; LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { ContentsWeight =Scale.Weight - ContainerWeightLb; if (FullContentsWeightLb> 0) PercentFull =Convert.ToInt32((ContentsWeight / (FullContentsWeightLb) - ContainerWeightLb) 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 Berechnete Eigenschaften private int _qtyRemaining; public int QtyRemaining { get { return _qtyRemaining; } set { Set (ref_qtyRemaining, value); } } private DateTime? _installDatum; öffentliches Datum/Uhrzeit? InstallDate { get { return _installDate; } set { Set (ref _installDate, Wert); } } private int _percentFull; public int PercentFull { get { return _percentFull; } set { Set (ref _percentFull, Wert); } } 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 Eingegebene Eigenschaften private bool _isEmpty; public bool IsEmpty { get { return _isEmpty; } set { _isEmpty =Wert; RaisePropertyChanged(); } } private double _glassSize; public double GlassSizeOz { get { return _glassSize; } set { Set (ref _glassSize, Wert); } } private DateTime? _Geburtsdatum; öffentliches Datum/Uhrzeit? BornDate { get { return _bornDate; } set { Set (ref _bornDate, Wert); } } double _containerWeight; public double ContainerWeightLb { get { return _containerWeight; } set { Set (ref _containerWeight, Wert); } } 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, Wert); } } private Zeichenfolge _contentsName; public String ContentsName { get { return _contentsName; } set { Set (ref _contentsName, Wert); } } #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); sonst BornDate =null; var installDate =GetSetting("KEG{0}_INSTALL_DATE", String.Empty); if (!String.IsNullOrEmpty(installDate)) InstallDate =DateTime.Parse(installDate); sonst InstallDate =null; } public async void SaveFullWeight() { FullContentsWeightLb =wait Scale.GetAverageWeight(); Speichern(); } public RelayCommand SaveFullWeightCommand { get { return new RelayCommand(() => SaveFullWeight()); } } }}
Scale-KlasseC#
Vorschau des Quellcodes vor der Veröffentlichung des vollständigen Quellcodes auf GitHub. Wenn Sie einen frühen Zugriff wünschen oder einen Beitrag leisten möchten, wenden Sie sich bitte an den Autor dieses Projekts
mit LagoVista.Common.Commanding;using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;using System .Text;mit System.Threading.Tasks;mit Windows.Devices.I2c;Namespace LagoVista.IoT.Common.Kegerator.Scale{ public class Scale :DeviceBase { Windows.Devices.I2c.I2cDevice _scaleI2CChannel; int _countOffset; doppelt? _calibrationFactor =null; private TimeSpan _updateInterval; Byte_Adresse; öffentliche Skala (Byteadresse) {_address =Adresse; } private void WriteValue(byte address, int value) { if (!IsDemoMode) { var offsetBuffer =new byte[5]; offsetBuffer[0] =Adresse; offsetBuffer[1] =(Byte)(Wert>> 24); offsetBuffer[2] =(Byte)(Wert>> 16); offsetBuffer[3] =(Byte)(Wert>> 8); offsetBuffer[4] =(Byte)(Wert); _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 =wait 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("Offline skalieren"); } } 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 ="Bereit"; }); } } else { LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { Status ="Bereit"; }); } } int? _lastRaw =null; private int GetRaw() { try { var inbuffer =neues Byte[4]; _scaleI2CChannel.Write (neues Byte[] { (Byte)0x11}); _scaleI2CChannel.Read (Inbuffer); /* Beachten Sie die Skala, dies ist ein langer (64-Bit) hier ist es ein int (64-Bit) */ var thisRaw =(int)(inbuffer[0] <<24 | inbuffer[1] <<16 | inbuffer[ 2] <<8 | Eingangspuffer[3]); if (_lastRaw.HasValue) { if (Math.Abs(_lastRaw.Value - thisRaw)> 0xFFFF) return _lastRaw.Value; } else _lastRaw =thisRaw; gib thisRaw zurück; } catch (Ausnahme) { return -1; } } öffentliche Überschreibung void Refresh() { LastUpdated =DateTime.Now; int rawResult =0; var isOnline =wahr; try { var Inbuffer =neues Byte[4]; var statusBuffer =neues Byte[1]; if (!IsDemoMode) { _scaleI2CChannel.Write(new byte[] { (byte)0x0A}); _scaleI2CChannel.Read(statusBuffer); rawResult =GetRaw(); } if (_calibrationFactor.HasValue) { Gewicht =(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} NULLWERT => {1}", _address, rawResult - _countOffset)); else Debug.WriteLine(String.Format("0x{0:X} RAW VALUE => 0x{1:X}", _address, rawResult)); } catch (Ausnahme 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 ="Nicht kalibriert"; } } RaisePropertyChanged("LastUpdateDisplay"); }); } const int CALIBRATION_COUNT =10; public async void StoreOffset() { LagoVista.Common.PlatformSupport.Services.DispatcherServices.Invoke(() => { Status ="Zeroing"; }); Debug.WriteLine("Null-Prozess starten"); lange Nullsumme =0; for (var idx =0; idx  { Status ="Zeroed"; }); } public async void Calibrate() { Status ="Kalibrieren"; LagoVista.Common.PlatformSupport.Services.BindingHelper.RefreshBindings(); lange Zählsumme =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-KlasseC#
Vorschau des Quellcodes vor der Veröffentlichung des vollständigen Quellcodes auf GitHub. Wenn Sie einen frühen Zugriff wünschen oder einen Beitrag leisten möchten, wenden Sie sich bitte an den Autor dieses Projekts
mit LagoVista.Common.Commanding;using System;using System.Collections.Generic;using System.Collections.ObjectModel;using System.ComponentModel; mit System.Linq;mit System.Runtime.CompilerServices;mit System.Text;mit System.Threading.Tasks;mit Windows.Devices.Enumeration;mit Windows.Devices.I2c;Namespace LagoVista.IoT.Common.Kegerator{ public class Kegerator :INotifyPropertyChanged { öffentliches Ereignis PropertyChangedEventHandler PropertyChanged; private Modelle.Keg _keg1; private Modelle.Keg _keg2; private Modelle.Keg _keg3; private Modelle.Keg _keg4; privater CO2.CO2Tank _co2Tank; privater 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; Speicher =Wert; this.RaisePropertyChanged(propertyName); true zurückgeben; } byte[]_scalesAddresses ={0x43, 0x41, 0x40, 0x42}; privater const-String I2C_CONTROLLER_NAME ="I2C1"; private Thermo.Temperaturen _temperatures; privater Thermo.Controller _tempController; private Scales.Scale _co2Scale; privates Wörterbuch _kegScale; privater CO2.PressureSensor _pressureSensor; privater LED.LEDManager _ledManager; private REST.KegeratorServices _kegServices; privater statischer Kegerator _kegerator =new Kegerator(); öffentliche statische Kegerator-Instanz { get { return _kegerator; } } private CloudServices.EventHubClient _eventHubClient; System.Threading.Timer _timer; private bool _initialized =false; public async Task Init() { if (!_initialized) { _initialized =true; Var-Selektor =I2cDevice.GetDeviceSelector(I2C_CONTROLLER_NAME); /* Finde den Selektor-String für den I2C-Bus-Controller */ var deviceInfo =(await DeviceInformation.FindAllAsync(selector)).FirstOrDefault(); /* Finden Sie das I2C-Bus-Controller-Gerät mit unserem Selektor-String */ var deviceId =deviceInfo ==null ? (string)null :deviceInfo.Id; _temperatures =neue Thermo.Temperatures(0x48); wait _temperatures.Init(deviceId); _devices.Add(_temperaturen); _tempController =neuer Thermo.Controller(); _tempController.Init(_temperaturen); _devices.Add(_tempController); _pressureSensor =neuer CO2.PressureSensor(); wait _pressureSensor.Init(deviceId, TimeSpan.FromSeconds(1)); _devices.Add(_pressureSensor); _co2Scale =new Scales.Scale(0x44); wait _co2Scale.Init(deviceId, TimeSpan.FromSeconds(1)); _devices.Add(_co2Scale); _co2Tank =new CO2.CO2Tank(_co2Scale, TimeSpan.FromSeconds(2)); _co2Tank.Load(); _devices.Add(_co2Tank); _kegScale =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]); wait scale.Init(deviceId, TimeSpan.FromMilliseconds(500)); _kegScale.Add(idx, Skala); _devices.Add(Skala); } _keg1 =new Models.Keg(1, _kegScale[0], TimeSpan.FromMilliseconds(500)); _keg1.Load(); _devices.Add(_keg1); _keg2 =neue Models.Keg(2, _kegScale[1], TimeSpan.FromMilliseconds(500)); _keg2.Load(); _devices.Add(_keg2); _keg3 =neues Models.Keg(3, _kegScale[2], TimeSpan.FromMilliseconds(500)); _keg3.Load(); _devices.Add(_keg3); _keg4 =neues Models.Keg(4, _kegScale[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); } } privat void _kegServices_EventContent(Objektsender, String e) {var parts =e.Split('/'); if (parts.Count()> 0) { switch (parts[1]) { case "null":{ var scaleIndex =Convert.ToInt32(parts[2]); _kegScale[scaleIndex].StoreOffset(); } brechen; case "cal":{ var scaleIndex =Convert.ToInt32(parts[2]); _kegScale[scaleIndex].CalibrationWeight =Convert.ToDouble(parts[3]); _kegScale[scaleIndex].Calibrate(); } brechen; } } } 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 Zeichenfolge _statusMessage; public String StatusMessage { get { return _statusMessage; } set { Set (ref _statusMessage, Wert); } } public List KegScale { get { return _kegScale.Values.ToList(); } } public void ToggleCompressor() { if (_tempController.IsCompressorOn) _tempController.CompressorOff(); sonst _tempController.CompressorOn(); } public String DateInitialized { get; einstellen; } public String CurrentTimeDisplay { get; einstellen; } public Scales.Scale CO2Scale { get { return _co2Scale; } } öffentlich 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; } } öffentlich CO2.CO2Tank CO2Tank { get { return _co2Tank; } } public RelayCommand ToggleCompressorCommand { get { return new RelayCommand(ToggleCompressor); } } }}

Schaltpläne

Übersicht über die Systemkomponenten

Herstellungsprozess

  1. Hindernisvermeidung durch künstliche Intelligenz
  2. Gyroskop-Spaß mit NeoPixel-Ring
  3. Arduino-Gamecontroller
  4. Wolkenkostüm
  5. Industrial Line Follower für die Materialversorgung
  6. Pixie:Eine Arduino-basierte NeoPixel-Armbanduhr
  7. Arduino-betriebene Wasserflasche
  8. Holiday Shadow Theatre
  9. Mobile Fernüberwachungskamera
  10. Ultimative Drahterodiermaschine in Taiwan