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

Wirklich intelligente Box

Komponenten und Verbrauchsmaterialien

Acrylplatte (z. B. 3 mm Plexiglas)
× 1
Arduino MKR FOX 1200
× 1
SparkFun HX711 Wägezellenverstärker
× 1
5-kg-Wägezelle
× 2
Adafruit BME280
× 1

Notwendige Werkzeuge und Maschinen

Laserschneider (generisch)
3D-Drucker (generisch)

Apps und Onlinedienste

Tinamous
Sigfox

Über dieses Projekt

Die Really Smart Box-Plattform verwandelt eine Really Useful Box (tm) in eine intelligente, mit dem Internet verbundene Lagerbox für die Bestandsüberwachung. Basierend auf dem Sigfox Arduino MKR FOX 1200 erfasst es das Gewicht der in der Box aufbewahrten Dinge sowie Temperatur und Luftfeuchtigkeit und verwendet das Sigfox-Radio mit geringem Stromverbrauch, um diese Informationen weiterzugeben.

Anwendungsfall - 3D-Drucker-Filamentspeicherung:

Wenn Sie einen 3D-Drucker besitzen, wird es Ihnen höchstwahrscheinlich wichtig sein, wie Ihr Filament gelagert wird. Dies gilt nicht nur für Druckerfilamente, sondern viele andere Dinge müssen innerhalb akzeptabler Temperatur- und Feuchtigkeitsbereiche gelagert werden (z Temperaturen unter dem Gefrierpunkt).

Als einer der Personen, die für die Wartung der 3D-Drucker in meinem lokalen Herstellerbereich verantwortlich sind, muss ich sicherstellen, dass wir ausreichend Filament vorrätig haben und es trocken hält.

Mit der Really Smart Box bin ich in der Lage, das Gewicht des Filaments zu überwachen und so zu wissen, ob wir zu niedrig werden, zusammen mit der Überwachung der Luftfeuchtigkeit in der Box, um festzustellen, ob das Silikagel ersetzt werden muss.

Anwendungsfall - Verbrauchsmaterialbestandskontrolle:

Ein Reinigungsunternehmen möchte möglicherweise einen Vorrat an Lufterfrischer, Handseife oder anderen Verbrauchsmaterialien am Standort des Kunden aufbewahren. Der Kunde würde dem Auftragnehmer wahrscheinlich keinen WLAN-Zugang gewähren oder ein solches Gerät mit Strom versorgen, wenn es nicht anwesend ist, aber das Unternehmen des Auftragnehmers muss dies tun wissen, wann Sie neuen Bestand einsenden müssen, was die Zeit der Reinigungskräfte verlängert und zusätzlichen Papierkram hinzufügt, den niemand mag.

Die Really Smart Box-Plattform fällt einfach in eine Aufbewahrungsbox, da sie Sigfox verwendet, muss sie sich nicht mit dem Netzwerk des Clients verbinden und ist stromsparend, so dass sie mit einem Satz Batterien betrieben wird. Da sich der Inhalt der Box nur sehr selten ändert, kann das Arduino die meiste Zeit in einem Energiesparzustand gehalten werden, was die Batterielebensdauer verlängert.

Der Plattform kann das Gewicht des in der Box gelagerten Artikeltyps (d. h. der Lufterfrischer) mitgeteilt und so berechnet werden, wie viele sich in der Box befinden. Dieser kann dann an das Reinigungsunternehmen gesendet werden, um es zu benachrichtigen, wenn mehr Heizmaterial an den Standort des Kunden geliefert werden muss.

Erstellen der Plattform:

Die Plattform ist von einfacher Konstruktion, der Hauptkörper besteht aus zwei Teilen lasergeschnittenem Acryl (ich habe 3 mm verwendet, dicker wäre besser für schwere Gegenstände wie Druckerfilament) mit einem Paar Wägezellen dazwischen.

Ich habe die Schraubenlöcher für die Wägezelle manuell versenkt, um ein schöneres Finish zu erzielen. Ich habe noch einen Laserschneider gefunden, der das Senken macht!

Das Acryl kann auf jede beliebige Größe zugeschnitten werden, um der Box zu entsprechen, die Sie möchten, achten Sie jedoch auf die Positionierung der Wägezellen und die Länge des Drahts, da er ziemlich kurz ist. Die obere Acrylglasplatte ist etwas kleiner als die untere, damit sie nicht an den Seiten der Box hängen bleibt.

Die untere Platte hat einen Ausschnitt, damit die Elektronik ohne Abstandshalter auf dem Acryl montiert werden kann und die angelöteten Gerätebeine durchstehen. Ich habe einen M3-Hahn an den Eckabschnitten des Ausschnitts verwendet, um die Platine direkt daran zu schrauben. An den Ecken wurden auch 3D-gedruckte Füße angebracht, um sicherzustellen, dass Schrauben der Wägezelle, die nicht bündig mit dem Acryl waren, die Balance nicht beeinflussten.

Das Gewicht wird mit zwei 5 kg Wägezellen erfasst. Ein 4er-Set wird normalerweise in Personenwaagen verwendet und das wäre vielleicht besser gewesen, aber ich konnte keine schöne Möglichkeit finden, sie am Acryl zu befestigen und den erforderlichen Abstand für die Elektronik bereitzustellen.

Die Wägezellen benötigen oben und unten etwas Polsterung, um ein wenig Durchbiegen zu ermöglichen, und die eigentlichen DMS-Sensoren (das weiße Bit im Bild) der Wägezelle sind dicker als der Montageblock. Dies wird unter der Wägezelle mit den beiden "Really Smart Box" 3D-gedruckten Endplatten erreicht, die einen kleinen Block zum Anheben der Wägezelle haben, auf der Oberseite der Wägezelle befinden sich einige lasergeschnittene Acryl-Polsterblöcke.

Die Wägezellen sind an einen Wägezellenverstärker HX711 angeschlossen. Dies hat zwei Kanäle (A und B), die ausgewählt werden können, was für diese Verwendung perfekt ist.

Jede Wägezelle besteht aus Dehnungsmessstreifen in einer Wheatstone-Brückenkonfiguration, dies erzeugt ein unsymmetrisches Potenzialteilerpaar, wenn die Wägezelle unter Last gesetzt wird, ändert sich der Widerstand der Dehnungsmessstreifen und somit wird eine Differenz zwischen den beiden Potenzialteilern erzeugt , dies wird vom HX711 verstärkt und gemessen, der die Analog-Digital-Wandlung für uns durchführt.

Ich habe für dieses Projekt zwei 5-kg-Wägezellen verwendet. Sie können verschiedene Bewertungen (z. B. 1 kg und 10 kg) erhalten, die genau gleich funktionieren, jedoch mit unterschiedlicher Empfindlichkeit.

Achten Sie beim Aufsetzen der Wägezellen darauf, dass der Pfeil am Ende der Wägezelle nach unten (in Richtung der Last) zeigt. Die Zellen haben M5-Gewindebohrungen an einem Ende (in der Regel das feste Ende) und M4 am anderen (der Seite, auf der Sie die Last platzieren).

Die rot/schwarzen Drähte sind Strom, dieser speist die oberen und unteren Bereiche der Potenzialteiler und wird von den beiden Wägezellen geteilt. Grün und weiß sind die Sense-Drähte von der Mitte der Potenzialteiler, diese sind mit Kanal A und B des HX711 verbunden.

Der HX711 unterstützt 3 Verstärkungsfaktoren, diese werden jedoch auch für die Kanalauswahl verwendet. Auf dem A-Kanal stehen Verstärkungen von 128 und 64 zur Verfügung, während die Auswahl einer Verstärkung von 32 den B-Kanal auswählt. Dies bedeutet, dass unser zweiter Kanal nicht so empfindlich ist wie der Hauptkanal, das ist für diese Anwendung in Ordnung.

Der HX711 kann an beliebige digitale Pins des Arduino angeschlossen werden, ich habe D0 (Data) und D1 (Clock) verwendet, dann muss der Verstärker nur noch an die 3v3-Versorgung des Arduino angeschlossen werden.

Sie können mehr über Wägezellen und den HX711 in SparkFuns hervorragendem Wägezellen-Tutorial lesen.

Schließlich wird ein BME280 an den I2C-Bus angeschlossen und verwendet, um die Temperatur und Luftfeuchtigkeit im Inneren der Box zu messen. Dies kann auch verwendet werden, um den Druck zu messen, aber dies ist wahrscheinlich von geringem Interesse und wir haben nur 12 Byte Sigfox-Daten zum Spielen, also ist es nicht gemeldet.

Die Elektronik ist auf einem ThingySticks Arduino Prototype Board montiert, ich habe einen Batteriehalter hinzugefügt (Hotmelt auf die untere Acrylplatte geklebt) und die Antenne angeschlossen, die ein schönes flaches Design hat und perfekt für die Plattform funktioniert.

Kalibrierung der Wägezelle:

Bevor wir die Plattform verwenden können, müssen die Wägezellen kalibriert werden. Jede Wägezelle ist ein Unikat. Sie wird hergestellt, indem Dehnungsmessstreifen an einem Metallblock befestigt und Löcher darin gebohrt werden, um eine ausreichende Biegung zu gewährleisten, die erfasst werden kann, ohne dass sie reißt. Aus diesem Grund müssen wir jede Wägezelle auf ihre Reaktion auf das Gewicht kalibrieren .

Nach der Kalibrierung wenden wir die Gleichung y=mx+c auf den gemessenen ADC-Wert (x) an, um das tatsächliche Gewicht (y) zu erhalten. Also müssen wir c (den Offset) und m (die Steigung) für unsere Wägezelle finden.

Ich habe die Hauptplattformoberseite entfernt und an jeder Wägezelle der Reihe nach ein kleines Acrylquadrat befestigt und die gemessenen Werte überwacht (es gibt ein Routing in der Firmware dazu, das durch Senden eines "c" an die serielle Schnittstelle gestartet werden kann.

Anfänglich wurde der Messwert für eine leere Plattform gemessen, dies ergibt den Offset (c)-Wert, dann wird eine Last mit einem bekannten Gewicht auf die Zelle gelegt, die Differenz der Messwerte gibt uns die Steigung an.

Neigung =(gemessen - Offset) / Gewicht (g).  

Ich habe sowohl eine kleine Lufterfrischer-Dose (ca. 230 g) als auch eine Spule Druckerfilament (ca. 1,5 kg) verwendet, um die Werte zu überprüfen, beide ergaben ungefähr die gleiche Steigung, was beruhigend war.

Natürlich unterscheidet sich der mit dem kleinen Acryl-Pad gemessene Offset von dem mit dem vollen Topsheet, ebenso gibt es auch einen kleinen Steigungsunterschied, wenn beide Wägezellen verwendet werden, so dass eine zweite Kalibrierung erforderlich ist. Im Moment wird ein Nullpunkt-Offset (Tara) verwendet, das in der Firmware eingestellt ist, aber auch über die serielle USB-Verbindung oder über eine Sigfox-Downlink-Nachricht nach der Bereitstellung eingestellt werden kann.

Sigfox-Verbindung:

Mit der Really Smart Box-Verkabelung habe ich anfangs den seriellen USB-Port verwendet, um die Ausgabe zu überwachen, um das Debuggen und Tunen des Systems zu unterstützen. Auf diese Weise können Sie die einzelnen Wägezellen, Veränderungen und Geräusche sehen. Dies funktioniert jedoch nicht für eine bereitgestellte Box, da sie vollständig drahtlos sein muss.

Mit Sigfox können wir bis zu 140 mal täglich 12 Byte Daten an unseren Onlinedienst senden, das ist mehr als genug für die Really Smart Box. Die folgende Datenstruktur wird im Arduino verwendet und beschreibt, wie wir die 12 Byte verwenden.

typedef struct __attribute__ ((gepackt)) sigfox_message { uint8_t status; // Statusflags int8_t Feuchtigkeit; // Feuchtigkeit::int:8 - einige Sensoren (HTU21D) lesen -ve Feuchtigkeit) int8_t Temperatur; // temperature::int:8 (keine Dezimalstellen). int16_t Nullgewicht; // zeroWeight::int:16:little-endian int16_t Gewicht; // Gewicht::int:16:little-endian int16_t itemCount; // itemCount::int:16:little-endian (100x tatsächliche Artikelanzahl um 2.01 zu berücksichtigen (da das Gewicht nicht genau übereinstimmt) int8_t driftCorrection; // Hier gibt es nichts zu sehen, weiter.... int8_t lastStatus; // Letzter sigfox-Status } SigfoxMessage;  

Das erste Byte (Status) ist in Bit-Flags aufgeteilt, um Probleme anzuzeigen:

// status::uint:8 -> Split to 8 bits // B7 - Erster Lauf // B6 - HX711 Fehler // B5 - BME280 Fehler // B4 - Temperaturalarm // B3 - Feuchtigkeitsalarm // B2 - Gewichtsalarm // B1 - Niedriger Lagerbestand // B0 - Ersatz  

Diese Struktur wird auf 12 Bytes komprimiert, wir müssen sie jedoch am Sigfox-Ende entpacken, um auf Tinamous zu pushen. Wir verwenden dafür eine benutzerdefinierte Payload-Konfiguration und es ist am besten, dies herauszufinden, während die Datenstruktur definiert ist. Unsere ist:

firstRun::bool:7 hx711Fault::bool:6 bmeFault::bool:5 temperatureAlarm::bool:4 FeuchteAlarm::bool:3 weightAlarm::bool:2 lowStock::bool:1 b0::bool:0 status::int:8 Luftfeuchtigkeit::int:8 temperature::int:8 zeroWeight::int:16:little-endian weight::int:16:little-endian itemCount::int:16:little -endian  

Die benutzerdefinierte Nutzlast teilt unsere 12 Bytes beim Parsen auf.

Beachten Sie, dass wir die Little-Endian-Natur von allem angeben müssen, das größer als 1 Byte ist, da Sigfox standardmäßig Big-Endian verwendet und Arduino Little-Endian verwendet (d.

Beachten Sie auch, dass das Aufteilen der booleschen Flags im ersten Byte den Byte-Marker nicht wie bei allen anderen Lesevorgängen vorantreibt, sodass das Statusbyte, das alle Flags enthält, ebenfalls gelesen wird, um das erste Byte zu überspringen.

In den Flaggen sind Alarmflaggen für Temperatur, Luftfeuchtigkeit und Gewichtsbereich enthalten. Wir könnten einen Online-Dienst (zB Tinamous) verwenden, um Temperatur, Luftfeuchtigkeit und Gewicht außerhalb des Bereichs zu überwachen. Diese können jedoch kurzlebig sein (einige Stunden) und unsere Box werden möglicherweise selten gesendet (ein- oder zweimal täglich), die daraus resultierenden möglichen schädlichen Umgebungsbedingungen könnten leicht übersehen werden, sodass sie auf dem Gerät gekennzeichnet und gesendet werden (und nach einem erfolgreichen Senden zurückgesetzt werden).

Die Artikelanzahl ist tatsächlich auf das 100-fache der tatsächlichen Artikelanzahl eingestellt. Ich wollte Werte wie 2,2 Artikel (aufgrund von Gewichtsfehlern oder anderen Artikeln in der Box) berücksichtigen, ohne eine Rundung zu erzwingen, ebenso könnten 2,95 auf 2 abgerundet werden, wenn wir nicht aufpassen, und es wäre eher ein Hinweis auf 3 Artikel in die Box und ein kleiner Fehler. Ich wollte auch keinen Float verwenden, der mehr Platz benötigt, also habe ich ein 16-Bit-Wort verwendet und einen Faktor angewendet, um eine einfache Konvertierung zu ermöglichen (es ist auch mit einem Vorzeichen versehen, um einen Null-Fehler zuzulassen, der zu einem Lagerbestand führen kann von -1 oder -2 usw.).

Es muss nur sehr wenig getan werden, um die Sigfox-Kommunikation zu ermöglichen. Innerhalb des Arduino wird die Sigfox-Bibliothek hinzugefügt und entsprechende Funktionen aufgerufen, um die Daten gemäß den Arduino-Beispielen für die Sigfox-Bibliothek zu übertragen, jedoch müssen wir unser Gerät bei Sigfox registrieren.

Durch Senden eines „s“ an die serielle Schnittstelle der Really Smart Box werden die Sigfox-ID und der PAC-Code gedruckt, mit denen das Gerät am Sigfox-Backend aktiviert wird. Wir gehen dann zum Sigfox-Backend-Aktivierungsdienst und folgen dem Assistenten, wählen zuerst unser Gerät, dann Land/Anbieter und dann einige Details aus.

Und endlich ist unser Gerät aktiviert und aufgelistet:

Sigfox ordnet Geräte der Gerätetyp-Gruppierung zu, was sinnvoll ist, da Sie normalerweise viele (Hunderte, Tausende usw.) desselben Gerätetyps haben, die Sie als Gruppe bearbeiten möchten. Wenn der Gerätetyp definiert ist, können wir einen benutzerdefinierten Rückruf konfigurieren, um die empfangenen Daten an unseren Online-Dienst weiterzuleiten. Ich verwende dafür Tinamous (Hinweis:siehe meinen Profilnamen - ich könnte bei meiner Auswahl voreingenommen sein).

Verwenden der Really Smart Box:

Einmal verkabelt, verschraubt, Firmware geflasht und Batterien eingelegt, wird die Plattform einfach in eine wirklich nützliche Box (tm) geworfen und ist einsatzbereit.

Die Stromversorgung sollte so spät wie möglich erfolgen, da das Gerät nach dem Einschalten die erste Sigfox-Nachricht nach 2 Minuten sendet und damit Downlink-Daten anfordert. Diese Daten können einen "Null"-Befehl enthalten, um das Gewicht der Plattform auf Null zu setzen. Andernfalls wird entweder eine serielle USB-Verbindung benötigt oder auf die nächste Downlink-Anfrage gewartet - diese erfolgt alle 12 Stunden.

Nach der Inbetriebnahme veröffentlicht die Plattform alle 15 Minuten Sigfox-Nachrichten, um das Gewicht, die Artikelanzahl, die Temperatur, die Luftfeuchtigkeit und die Alarmzustände zu senden. Temperatur, Feuchtigkeit und Gewicht werden jede Minute gemessen, um sicherzustellen, dass diese nicht außerhalb des Bereichs liegen, und Alarme werden für die nächste Übertragung markiert, wenn sie ausgelöst wurden.

Überwachung mit Tinamous:

Tinamous unterstützt benutzerdefinierte Sigfox-Rückrufe, indem es unserem Konto einen "Sigfox-Bot" hinzufügt. Anweisungen dazu finden Sie in meinem Hackster.io-Tutorial "Get Your Sigfox On".

Wenn Sie Ihrem Tinamous-Konto einen Sigfox-Bot hinzufügen und die API-Einstellungen einbeziehen, sucht der Sigfox-Bot nach Ihren Geräten und fügt sie Ihrem Tinamous-Konto hinzu. Dies ist jedoch nicht erforderlich, da das Gerät automatisch hinzugefügt wird, wenn Daten wird veröffentlicht.

Wenn Sie den Bot hinzugefügt haben, wird uns ein Callback-Konfigurationsbildschirm angezeigt, der Sie bei der Einrichtung der Sigfox-Callbacks unterstützt.

Sie können dann bei Sigfox einen benutzerdefinierten Rückruf erstellen. Beachten Sie, dass die Really Smart Box DATA -> BIDIR-Rückruf verwendet, der den normalen UPLINK-Rückruf und die BIDIR-Rückrufe (Up- und Downlink) verarbeitet.

Hier ist die benutzerdefinierte Nutzlast von vorhin praktisch, füge diese aus dem Quellcode in die benutzerdefinierte Nutzlast ein und aktualisiere den Abschnitt mit den Feldern, um dies widerzuspiegeln.

Lat und Lng werden in diesem Rückruf angegeben, der einen ungefähren Standort angibt. Sigfox auf dem Arduino unterstützt jedoch eine verbesserte Standorteinstellung, dies erfordert jedoch einen zweiten Rückruf. Wenn Sie die Geo-Ortungsfunktion verwenden, geben Sie in dieser Nachricht keine Lat/Lng an, da sich die Really Smart Box zwischen den Standorten zu bewegen scheint.

Sobald dies konfiguriert ist, muss es auch für Downlink aktiviert werden. Dies ist standardmäßig deaktiviert, obwohl BIDIR eingestellt wurde.

Beachten Sie, dass der Screenshot unter der Downlink-Option "markiert" ist, dies muss manuell erfolgen und ist möglicherweise nicht verfügbar, wenn der Gerätetyp für Downlink-Daten nicht auf "CALLBACK" eingestellt wurde (Gerätetyp -> Bearbeiten -> Downlink-Daten). .

Bei einem Downlink-Callback möchten wir auch einen SERVICE -> ACKNOWLEDGE-Callback angeben, um zu wissen, dass unser Gerät die Downlink-Daten empfangen hat. Durch Klicken auf den Sigfox Bot in Tinamous werden andere Callback-Konfigurationen angezeigt, folgen Sie den Anweisungen für die ACKNOWLEDGE- und GEOLOC-Callbacks.

Beachten Sie, dass Sie den Autorisierungs-Header aus dem ersten Uplink/Bidir-Callback kopieren müssen, da dies ein Passwort ist, das in Tinamous unidirektional verschlüsselt ist und nicht mehr angezeigt werden kann.

Mit unseren Rückrufen sollten die von unserem Gerät veröffentlichten Daten nun an Tinamous gesendet werden. Wir könnten bei Sigfox auch E-Mail-Rückrufe hinzufügen, die helfen können, zu bestätigen, dass die Daten durchkommen (aber auch sehr schnell sehr laut werden können).

Konfiguration des Tinamous-Geräts:

Sobald das Sigfox-Gerät bei Tinamous gesehen wurde (entweder durch die API-Suche oder einen Rückruf), wird es auf der Seite Geräte angezeigt, von hier aus können wir die Eigenschaften bearbeiten. Felder werden automatisch hinzugefügt, wenn sie vom Sigfox-Callback kommen. Warten Sie also am besten, bis das Gerät Daten veröffentlicht.

Ich setze die Zeit für "Nicht melden nach" auf 1 Stunde (wird derzeit alle 15 Minuten veröffentlicht), damit ich feststellen kann, ob das Gerät defekt ist, und optional darüber benachrichtigt werde.

Ich wollte nicht alle vom Gerät gesendeten Felder auf der Diagramm- / Detailseite sehen (es sind viele, wenn Sie alle Flags einschließen), daher ist Tinamous so konfiguriert, dass nur Gewicht und Artikelanzahl angezeigt werden. Auch hier wurden menschenfreundliche Etiketten und Einheiten angebracht.

Das Feld Artikelanzahl ist das 100-fache der tatsächlichen Artikelanzahl, daher wird eine Kalibrierung auf dieses Feld angewendet, um es um das 100-fache zu reduzieren.

Einige Downlink-Daten werden eingestellt, die dazu führen, dass die Really Smart Box auf Null geht und Temperatur- und Feuchtigkeitsbereichsgrenzen anwendet, wenn sie das nächste Mal eine Downlink-Nachricht anfordert (2 Minuten nach dem Einschalten, dann einmal alle 12 Stunden).

Anzeigen der Really Smart Box-Informationen:

Jetzt sind die Gerätefelder konfiguriert und wir können sie über die Gerätedetailseite überwachen (Beachten Sie, dass ich die Plattform zu diesem Zeitpunkt nicht auf Null gesetzt habe, daher denkt es, dass eine halbe Einheit vorhanden ist - ich habe auch die Acryloberseite durch eine 5 mm² ersetzt Version, die schwerer ist, aber Druckerfilamente besser verarbeitet).

Wir können auch die Sigfox-Callback-Interaktionen aus dem Sigfox-Bereich sehen. Beachten Sie hier, dass die Downlink-Daten gesendet und bestätigt werden, das Arduino jedoch einen Fehler meldet. Mehr dazu am Ende.

Auf der Registerkarte Standort können wir auch sehen, wo sich unsere Really Smart Box befindet, was nützlich sein könnte, wenn Sie vergessen, bei welchem ​​Kunden sie sich befindet oder ob sie sich in einem Lieferwagen befindet.

Und natürlich möchten wir eine schöne Dashboard-Ansicht unserer Really Smart Box. Die folgende zeigt das Gewicht des Kartoninhalts, die geschätzten Einheiten und die Anzahl der Geräte, die sich nicht melden, damit wir feststellen können, ob eine defekt ist.

Empfangen von Benachrichtigungen mit Tinamous:

Als nächstes richte ich Tinamous so ein, dass es eine E-Mail und SMS sendet, wenn die Anzahl der Elemente niedrig ist. Ich habe dies getan, indem ich einen Arbeitsbereich von 3 - 300 für das Feld für die Artikelanzahl angegeben habe. Wenn der Wert außerhalb dieses Bereichs liegt, wird sogar eine Messung außerhalb des Bereichs ausgelöst.

Wenn wir Tinamous eine Benachrichtigung hinzufügen, können wir benachrichtigt werden, wenn dies passiert.

Wir könnten nur das Feld angeben, an dem wir interessiert sind, aber wenn wir dieses Feld leer lassen, erhalten wir Benachrichtigungen für alle Felder, die außerhalb des zulässigen Bereichs liegen.

Ebenso bei Geräten, lassen Sie es für alle Geräte leer (d. h. das einzige, das wir derzeit haben!)

Stellen Sie die Benachrichtigungen wiederholen so ein, dass sie nur einmal ausgelöst werden, bis sie zurückgesetzt werden (jeden Tag), sonst werden Benachrichtigungen alle 15 Minuten sehr schnell nervig!

Wählen Sie dann aus, wie Sie benachrichtigt werden möchten, ich stelle es für E-Mail und SMS ein und erstelle dann die Benachrichtigung:

Fazit:

Ich kann jetzt die Really Smart Box einsetzen und (hoffentlich) vergessen. Wenn der Lagerbestand niedrig wird, werde ich benachrichtigt und kann auf dem Dashboard nachsehen, wie es läuft. Mit Sigfox muss ich mich außer einem gelegentlichen Batteriewechsel nicht um die Stromversorgung des Geräts kümmern, und es ist keine WLAN-Einrichtung vor Ort erforderlich, was die Bereitstellung extrem einfach macht.

Ich plane, dieses Gerät in einer unserer Filament-Lagerboxen in Cambridge Makespace einzusetzen, um die Filament-Lagerbestände zu überwachen.

Zu lösende Probleme:

Unnötig zu erwähnen, dass dies kein abgeschlossenes Projekt in Produktionsqualität ist, einige Probleme müssen noch gelöst werden:

Sigfox-Downlink:

Sigfox ermöglicht das Senden von 4 Nachrichten pro Tag als Antwort auf eine Uplink-Nachricht. Die Really Smart Box verwendet diese, um die Waage neu auf Null zu stellen, den oberen und unteren Temperatur- und Feuchtigkeitsbereich sowie das Artikelgewicht einzustellen. Beim Versuch, dies zum Laufen zu bringen, meldet das Arduino jedoch einen Statusfehler von 62, der keinem Fehler-Flag zugeordnet ist, obwohl die Downlink-Nachricht gesendet und bestätigt zu werden scheint (wie im Sigfox-Backend gezeigt). Bedingungen, die für den ATA8520-Chip aufgelistet sind, verwendet der Befehl eine Downlink-Anfrage, die ebenfalls nicht im Datenblatt aufgeführt ist, und untersucht die Treiber, sodass weitere Untersuchungen erforderlich sind.

Nur Debug:

Das Ausführen der Sigfox-Kommunikation mit deaktiviertem Debug führt dazu, dass die Low-Power-Einstellung des Arduino aktiviert wird, wobei der serielle USB-Port beendet wird.

Energiesparmodus:

Wie für die Sigfox-Debug-Einstellung beschrieben, führt die Verwendung der Low-Power-Bibliothek von Arduino dazu, dass die USB-Seriell abfällt, sodass dies zu diesem Zeitpunkt nicht aktiviert ist.

Drift:

Es wurde keine Driftkompensation hinzugefügt, die Wägezellen werden zweifellos driften, wenn sie unter konstanter Last gehalten werden.

Geräusch-/Nicht-vertikale Messungen:

Es ist möglich, dass sich die Really Smart Box hinten in einem Lieferwagen befindet (z. B. Reinigungskraft, Zimmermann, Klempner usw.). Es wäre gut, der Plattform einen Beschleunigungsmesser hinzuzufügen und Messzyklen zu überspringen, wenn die Box nicht stabil ist. Wenn die Box nicht vertikal steht, wird das Gewicht nicht wie erwartet durch die Wägezellen geleitet.

Code

  • Wirklich Smart Box Arduino-Code
Really Smart Box Arduino CodeArduino
Fügen Sie Bibliotheken für Arduino MKR FOX 1200, HX711, AdaFruit BME280, Arduino Low Power hinzu. Verwenden Sie die Arduino IDE, um wie gewohnt zu programmieren.
 // Really Smart Box // Misst das Gewicht des Inhalts einer wirklich intelligenten Box // Hergestellt aus zwei Acrylplatten mit 2 Wägezellen dazwischen // in einer wirklich Smart Box.// Enthält auch ein BME280 zur Messung von Temperatur und Druck in der Box.// Autor:Stephen Harrison// Lizenz:MIT#include #include #include  #include #include // ---------------------------------- ----// BME280 am I2C-Port.Adafruit_BME280 bme; // -------------------------------------------------- // HX711 Wägezellenverstärker.// 0 :D0 - DOUT// 1:D1 - CLK// Anfangsverstärkung von 128.HX711-Waagen (0, 1, 128); // Arrays für Wägezellen. Index 0 ==Kanal A, Index 1 ==Kanal B.float gain[] ={128,32};// Kalibrierfaktoren.// wir verwenden y =mx + c (c =offset, m =scaleFactor)./ / um den gemessenen Wert in ein Gewicht umzuwandeln.// Setzen Sie dies auf den von den Wägezellen gemeldeten Offset.// ohne Gewicht darauf.float offset[] ={0,54940}; // Setze dies auf den Faktor, der berechnet wird, wenn ein Gewicht auf die Waage gelegt wird. // Setze zuerst den Offset, flashe das arduiono neu damit dies wirksam wird // lege ein Gewicht auf die Waage und dividiere den Rohmesswert durch die Gewicht.// mit scaleFactor =Messwert / Gewicht.float scaleFactor[] ={378.f,260.9f};// ---------------------- -----------------// Sigfox// Dies ist die Datenstruktur, die wir an Sigfox veröffentlichen.// Teile die Bits als Bool-Flags aus dem ersten Statusbyte heraus, aber das Byte braucht noch to be // included otherwise humidity becomes the status// firstRun::bool:7 hx711Fault::bool:6 bmeFault::bool:5 temperatureAlarm::bool:4 humidityAlarm::bool:3 weightAlarm::bool:2 lowStock::bool:1 b0::bool:0// status::int:8 humidity::int:8 temperature::int:8 zeroWeight::int:16:little-endian weight::int:16:little-endian itemCount::int:16:little-endiantypedef struct __attribute__ ((packed)) sigfox_message { uint8_t status; // status::uint:8 -> Split to 8 bits // B7 - First run, B6 - HX711 fault, B5 BME280 fault, B4 Temperature alarm, B3 - Humidity alarm, B2 - weight alarm, B1 - Low stock, B0 - spare int8_t humidity; // humidity::int:8 (yes some sensors (HTU21D read -ve humidity) int8_t temperature; // temperature::int:8 (no decimal places). int16_t zeroWeight; // zeroWeight::int:16:little-endian int16_t weight; // weight::int:16:little-endian int16_t itemCount; // itemCount::int:16:little-endian (100x actual item count to allow for 2.01 (as weight won't match exactly) int8_t driftCorrection; // Drift Correction for changes in zero weight applied to the scales. int8_t filler; int8_t lastStatus; // Last sigfox status} SigfoxMessage;// Time the last Sigfox message was published atlong lastPublish =0;// Time the last Sigfox downlink was requested.// Allowed max 4 per day of these.long lastDownlink =0;uint8_t lastSigfoxStatus =0;// --------------------------------------// Application/state// If the fist cycle (after a reset) for the measure/publish// cycle (this is used to request a downlink message from Sigfox).// Note that only 4 of them are allowed per day so becareful// when deploying code.bool isFirstCycle =true;// Application mode// 0:Normal// 1:Calibrationint mode =0;// Which channel should be read during calibration.int calibrate_channel =0;// The last average value measured for each channel.float lastAverage[] ={0,0};// The current weight of the contents of the boxfloat currentWeight =0;// The weight of the units the box will hold.// Updatable via Sigfox downlink message.float unitWeight =238;// Different to tare as it would be a manual// zero'd at a set reading from scales// This will most likely change with drift (time/temperature/etc)// and should be set once the scale is in place but not loaded.// Updatable via Sigfox downlink message.float zeroWeight =0;bool bmeOk =true;bool hx711Ok =true;// Alarms and alarm rangesfloat minTemperature =5.f;float maxTemperature =60.f;float minHumidity =0.f;float maxHumidity =60.f;float maxWeight =10000; // 10kgbool temperatureAlarm =false;bool humidityAlarm =false;bool weightAlarm =false;float currentTemperature =0;float currentHumidity =0;float stockLevel =0;bool lowStock =false;float minStock =5;// Setup the Arduino.void setup() { pinMode(LED_BUILTIN, OUTPUT); //Initialize serial:Serial.begin(9600); // NB:The sensor I'm using (from random eBay seller) // does not use the default address. bmeOk =bme.begin(0x76); if (!bmeOk) { Serial.println("Could not find a valid BME280 sensor!"); } // Delay for USB Serial connect and for the BME's first reading. Verzögerung (5000); Serial.println("Really Smart Box..."); printHeader();}int delayCounter =0;void loop() { switch (mode) { case 0:measureAndPublish(); //Sleep for 1 minutes // Causing problems with USB connected. //LowPower.sleep(1 * 60 * 1000); delay(60 * 1000); break; case 1:calibrate(); Verzögerung (1000); break; } // Check for user input via the serial port. checkSerial(); // measure is done on RTC timer tick (once per minute) delay(100);}void measureAndPublish() { // turn the LED on to indicate measuring. digitalWrite(LED_BUILTIN, HIGH); printBmeValues(); measureTemperatureAndHumidity(); measureWeight(true); // Weight, temperature and humidity are read every minute // however we only publish occasionally. if (shouldPublish()) { publishMeasurements(); } digitalWrite(LED_BUILTIN, LOW); }// Main measurement loop. Reads the weight from the load cells// and stores if no noise from the previous read.void measureWeight(bool printDetails) { scales.power_up(); Verzögerung (500); float delta =readDelta(printDetails); if (printDetails) { Serial.print("\t"); Serial.print(delta, 2); } // If the delta is between -1 and 1 (i.e. no noise) // update the change in overall weight and units contained // otherwise ignore and try again later on. // This ensures we use only stable readings when both channels have not changed for // two sets of measurements. if (delta <1.f &&delta> -1.f) { // Remember the previous measured weight so we can get a delta. float lastWeight =currentWeight; // Compute the weight. Take the weight of both load cells // added together then subtract the zero'd weight. currentWeight =lastAverage[0] + lastAverage[1] - zeroWeight; // Compute the difference in weight of the items in the box // compated to the last time we had a stable reading. float itemsWeightDelta =currentWeight - lastWeight; updateStockLevels(); if (printDetails) { Serial.print("\t"); Serial.print("\t"); Serial.print(currentWeight, 2); Serial.print("\t"); // divide by unit weight to estimate the stock level in the box Serial.print(currentWeight / unitWeight, 2); Serial.print("\t"); // the change in weight, (i.e. the weight if the items added) Serial.print(itemsWeightDelta, 2); Serial.print("\t"); // divide by unit weight to estimate the units removed/added Serial.print(itemsWeightDelta / unitWeight, 2); } } checkWeightLimits(); if (printDetails) { Serial.println(); } // put the ADC in sleep mode and switch // off the LED now we're done measuring. scales.power_down(); }void updateStockLevels() { stockLevel =currentWeight / unitWeight; // Unlike other alarms the low stock level // is reset if the stock is re-stocked. lowStock =stockLevel  maxWeight ) { weightAlarm =true; } if (lastAverage[0]> (maxWeight /2)) { weightAlarm =true; } if (lastAverage[1]> (maxWeight /2)) { weightAlarm =true; }}// Read the difference in weight from the last // average to this time across both load cells.// average value is stored in the lastAverage array.float readDelta(bool printDetails) { float aDelta =readChannel(0, true); if (printDetails) { Serial.print("\t"); } float bDelta =readChannel(1, true); return aDelta + bDelta;}// Read the weight from a channel. Stores the measured value in // the lastAverage array and retuns the delta of the measured value// from the previous lastAverage. This allows us to know if the weight// has changed.// channel 0 =A// channel 1 =Bfloat readChannel(int channel, bool printDetails) { // Gain:// Channel A supports 128 or 64. Default 128 // Channel B supports 32 // Select Channel B by using gain of 32. scales.set_gain(gain[channel]); // HX711 library only has one set of offset/scale factors // which won't work for use as we use both channels and they // have different gains, so each needs to have it's offset/scale set // before reading the value. scales.set_offset(offset[channel]); scales.set_scale(scaleFactor[channel]); // force read to switch to gain. scales.read(); scales.read(); float singleRead =scales.get_units(); float average =scales.get_units(10); float delta =average - lastAverage[channel]; if (printDetails) { Serial.print(singleRead, 1); Serial.print("\t"); Serial.print(average, 1); Serial.print("\t"); Serial.print(delta, 1); Serial.print("\t"); } lastAverage[channel] =average; return delta;}// print the header for the debug data pushed out when measuring.void printHeader() { Serial.print("BME280\t\t\t\t\t"); Serial.print("Channel A\t\t\t"); Serial.print("Channel B\t\t\t"); Serial.print("\t\t"); Serial.print("Totals \t\t\t"); Serial.println(""); Serial.print("Temp\t"); Serial.print("Pressure\t"); Serial.print("Humidity\t"); Serial.print("read\t"); Serial.print("average\t"); Serial.print("delta\t"); Serial.print("\t"); Serial.print("read\t"); Serial.print("average\t"); Serial.print("delta\t"); Serial.print("\t"); Serial.print("sum\t"); Serial.print("\t"); Serial.print("weight\t"); Serial.print("items\t"); Serial.print("change\t"); Serial.print("items added"); Serial.println("");}// Calibration - reads/prints selected channel values.void calibrate() { digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level) scales.set_gain(gain[calibrate_channel]); scales.set_offset(offset[calibrate_channel]); scales.set_scale(scaleFactor[calibrate_channel]); // force read to switch to gain Serial.print("\t|CH:\t"); Serial.print(calibrate_channel,1); Serial.print("\traw:\t"); Serial.print(scales.read(),1); Serial.print("\t| raw:\t"); Serial.print(scales.read(),1); Serial.print("\t| units:\t"); Serial.print(scales.get_units(), 1); Serial.print("\t| gain:\t"); Serial.print(gain[calibrate_channel], 1); Serial.print("\t| factor:\t"); Serial.println(scaleFactor[calibrate_channel], 1); digitalWrite(LED_BUILTIN, LOW);}// check the serial port for input from a console to allow us to alter // the device mode etc.void checkSerial() { if(Serial.available()) { char instruction =Serial.read(); switch (instruction) { case '0':calibrate_channel =0; Serial.println("Channel 0 (A) Selected"); break; case '1':calibrate_channel =1; Serial.println("Channel 1 (B) Selected"); break; case 'm':// Measurement mode mode =0; Serial.println("Measurement Mode"); printHeader(); break; case 'c':// Calibration mode mode =1; Serial.println("Calibration Mode"); break; case 't':// Tare. Teset the scale to 0 Serial.println("Taring"); scales.power_up(); Verzögerung (500); scales.tare(5); // Need to do this for each channel // and update our stored offset. Serial.println("Not properly Tared!"); break; case 'h':printHeader(); break; case 'z':zeroScales(); break; case 's':printSigfoxModelDetails(); break; default:Serial.println("Unknown instruction. Select:0, 1, m, c, t, h, z, or s"); Serial.println("m - measurement mode"); Serial.println("c - Calibration mode"); Serial.println("0 - Channel 0 (A) Calibration"); Serial.println("1 - Channel 1 (B) Calibration"); Serial.println("t - Tare (scale)"); Serial.println("z - Zero (Weight)"); Serial.println("h - print Header"); Serial.println("s - print Sigfox model details"); break; } }}// Measure (and record) the temperature and humidity levels// Sets alarms if out of rage (we can't use limits on Internet service side// as the messages may only be sent a few times a day and a brief (maybe hours)// out of range temperature/humidity could easily be missed between// message publishing.void measureTemperatureAndHumidity() { if (!bmeOk) { return; } currentTemperature =bme.readTemperature(); if (currentTemperature  maxTemperature) { temperatureAlarm =true; } currentHumidity =bme.readHumidity(); if (currentHumidity  maxHumidity) { humidityAlarm =true; }}// Print the values read from the BME280 sensorvoid printBmeValues() { //Serial.print("Temperature ="); Serial.print(bme.readTemperature(), 1); Serial.print("\t"); Serial.print(bme.readPressure() / 100.0F, 0); Serial.print("\t\t"); Serial.print(bme.readHumidity(),1); Serial.print("\t\t ");}// =============================================================// Sigfox handing// =============================================================// Determine if we should publish the Sigfox message.// We may also wish to publish if the stock level has// changed (or a significant weight level has changed)// but we would need to be careful of exceeding the // 140 messages per day for a noisy system.bool shouldPublish() { // Publish every 15 minutes // this doesn't really need to be this often // but whilst developing it helps keep an eye on the system. int messageIntervalMinutes =15; // On first run after reset // allow a 2 minute delay for the platform to be placed into // the box and stabalise before doing first publish // which is also expected to include a check for zeroing the platform. if (isFirstCycle) { messageIntervalMinutes =2; Serial.println("First cycle"); } // How long ago we last publish a Sigfox message long millisAgo =millis() - lastPublish; return millisAgo> (messageIntervalMinutes * 60 * 1000);}// Publish our measurements (weight, temperature, humidity etc)// to Sigfox.void publishMeasurements() { Serial.println("Sending via Sigfox..."); bool useDownlink =shouldUseDownlink(); if (useDownlink) { Serial.println("Using Sigfox downlink..."); } // stub for message which will be sent SigfoxMessage msg =buildMessage(); SigFox.begin(); SigFox.debug(); // Wait at least 30mS after first configuration (100mS before) delay(100); // Clears all pending interrupts SigFox.status(); delay(1); SigFox.beginPacket(); SigFox.write((uint8_t*)&msg, 12); // endPacket actually sends the data. uint8_t statusCode =SigFox.endPacket(useDownlink); printSigfoxStatus(statusCode); // Status =0 for a successful send, otherwise indicates // a failure. // Store when we last published a Sigfox message // to allow for timed message sending. if (statusCode ==0) { resetAlarms(); } // Update the last publish/downlink times // even if an error resonse was received to prevent // repeated publishing lastPublish =millis(); isFirstCycle =false; if (useDownlink) { parseDownlinkData(statusCode); lastDownlink =lastPublish; } // Store the status value lastSigfoxStatus =statusCode; SigFox.end();}void printSigfoxStatus(uint8_t statusCode) { Serial.print("Response status code :0x"); Serial.println(statusCode, HEX); if (statusCode !=0) { Serial.print("Sigfox Status:"); Serial1.println(SigFox.status(SIGFOX)); Serial1.println(); Serial.print("Atmel Status:"); Serial1.println(SigFox.status(ATMEL)); Serial1.println(); }}// Create the message to be publish to Sigfox.SigfoxMessage buildMessage() { SigfoxMessage message; message.status =getStatusFlags(); message.humidity =(int8_t )currentHumidity; message.temperature =(int8_t)currentTemperature; message.zeroWeight =(int16_t)zeroWeight; message.weight =(int16_t)currentWeight; message.itemCount =(int16_t)(stockLevel * 100); message.driftCorrection =0; // TODO message.filler =0; message.lastStatus =lastSigfoxStatus; return message;}// Get the status flags for the Sigfox message.byte getStatusFlags() { byte status =0; // B7 - First run, // B6 - HX711 fault // B5 - BME280 fault // B4 - Temperature alarm // B3 - Humidity alarm // B2 - weight alarm // B1 - Low stock // B0 - spare // Upper Nibble (Charging/Battery) // Battery flat if (isFirstCycle) { status |=0x80; // 1000 0000 } // HX711 fault. // we don't have a way to check this yet. if (!hx711Ok) { status |=0x40; // 0100 0000 } // BME280 fault if (!bmeOk) { status |=0x20; // 0010 0000 } // Over/Under temperature alarm if (temperatureAlarm> 0) { status |=0x10; // 0001 0000 } // Over/Under humidity alarm if (humidityAlarm) { status |=0x08; // 0000 1000 } // Over/under? weight alarm if (weightAlarm) { status |=0x04; // 0000 0100 } // if computed stock level low. if (lowStock) { status |=0x02; // 0000 0010 } return status;}// Determine if we are requesting a downlink message.bool shouldUseDownlink() { // When debugging uncomment this so as to not keep requesting // downlink //return false; // On first run we want to request a downlink // message to help with zero'ing and setup. if (isFirstCycle) { return true; } // How long ago we last did a downlink message. long millisAgo =millis() - lastDownlink; // try every 12 hours, this keeps us under the // maximum 4 per day. return millisAgo> (12 * 60 * 60 * 1000);}// Parse downlinked data.void parseDownlinkData(uint8_t statusMessage) { if (statusMessage> 0) { Serial.println("No transmission. Status:" + String(statusMessage)); return; } // Max response size is 8 bytes // set-up a empty buffer to store this. (0x00 ==no action for us.) uint8_t response[] ={0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // Expect... // Byte 0:Flags // B7:Zero scales // B6:Set Temperature range (ignore min/max temp if 0) // B5:Set Humidity range (ignore min/max humidity if 0) // B4:Set tolerance? // B3:Set ??? // B2:Update unit weight (ignore update if 0) // B1:// B0:// Byte 1:Min T // Byte 2:Max T // Byte 3:Min Humidity // byte 4:Max Humidity // byte 5:Read tolerence??? (+/- x) // byte 6 &7:Unit weight // Parse the response packet from Sigfox if (SigFox.parsePacket()) { Serial.println("Response from server:"); // Move the response into local buffer. int i =0; while (SigFox.available()) { Serial.print("0x"); int readValue =SigFox.read(); Serial.println(readValue, HEX); response[i] =(uint8_t)readValue; i++; } // byte 0 - flags. // 0b 1000 0000 if (response[0] &0x80 ==0x80) { zeroScales(); } // 0b 0100 0000 if (response[0] &0x40 ==0x40) { updateTemperatureAlarm(response[1], response[2]); } // 0b 0010 0000 if (response[0] &0x20 ==0x20) { updateHumidityAlarm(response[3], response[4]); } // 0b 0000 0100 if (response[0] &0x04 ==0x04) { // Little Endian format. (ff dd -> 0xddff uint16_t weight =response[7] <<8 &response[6]; updateUnitWeight(weight); } } else { Serial.println("No response from server"); } Serial.println();}void printSigfoxModelDetails() { if (!SigFox.begin()) { Serial.println("Shield error or not present!"); return; } // Output the ID and PAC needed to register the // device at the Sigfox backend. String version =SigFox.SigVersion(); String ID =SigFox.ID(); String PAC =SigFox.PAC(); // Display module informations Serial.println("MKRFox1200 Sigfox configuration"); Serial.println("SigFox FW version " + version); Serial.println("ID =" + ID); Serial.println("PAC =" + PAC); Serial.println(""); Serial.print("Module temperature:"); Serial.println(SigFox.internalTemperature()); Serial.println("Register your board on https://backend.sigfox.com/activate with provided ID and PAC"); delay(100); // Send the module to the deepest sleep SigFox.end();}// =============================================================// General helper methods// =============================================================// Reset the alarms after they have been published.void resetAlarms() { temperatureAlarm =false; humidityAlarm =false; weightAlarm =false;}void zeroScales() { zeroWeight =lastAverage[0] + lastAverage[1]; Serial.print("Zero'd:"); Serial.print(zeroWeight, 1); Serial.println();}void updateTemperatureAlarm(int8_t lower, int8_t upper) { Serial.print("Setting temperature alarm. Min:"); Serial.print(lower); Serial.print(", Max:"); Serial.println(upper); minTemperature =lower; maxTemperature =upper;}void updateHumidityAlarm(int8_t lower, int8_t upper) { Serial.print("Setting humidity alarm. Min:"); Serial.print(lower); Serial.print(", Max:"); Serial.println(upper); minHumidity =lower; maxHumidity =upper;}void updateUnitWeight(uint16_t weight) { Serial.print("Setting unit weight:"); Serial.println(weight); unitWeight =weight;}
Really Smart Box Github Repository
https://github.com/Tinamous/ReallySmartBox

Kundenspezifische Teile und Gehäuse

Use this to cut the top and bottom acrylic sheets. cuttingguide_e7GNHf980M.svgThis sits between the lower acrylic sheet and load cell to raise it up a little and provide a edge to the platformsPrint 4 of these for each corner of the lower sheet if needed

Schaltpläne

Nothing to complex.

Herstellungsprozess

  1. Saftbox
  2. Blackbox
  3. Solarzelle
  4. Smart Barkeeper
  5. Intelligente Ampel
  6. Intelligenter Abfallbehälter
  7. Raspberry Pi CD-Box-Roboter
  8. UVC-Box einen DIY-UV-Sterilisator
  9. Smart Plant IoT
  10. Smart Blinds