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

Genaue Uhr nur mit einem Arduino

Komponenten und Verbrauchsmaterialien

Arduino Nano R3
Ich habe einen Nano verwendet, sollte aber mit jedem Arduino funktionieren
× 1
Alphanumerisches LCD, 16 x 2
Jede Anzeige sollte funktionieren, ich habe diese verwendet https://www.amazon.co.uk/ gp/product/B00N8K2BYM/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1
× 1
Tastschalter, von oben betätigt
× 3
Trimmerpotentiometer, 10 kOhm
Jeder 10.000-Trimmer reicht aus
× 1
Überbrückungskabel
× 1

Über dieses Projekt

Ich begann dies als akademische Übung, endete aber mit einer sehr genauen Uhr. Nachdem es 5 Tage gelaufen war, hatte es keine Zeit verloren oder gewonnen.

Das Hauptproblem bei der Verwendung nur eines Arduino ist, dass seine interne Taktrate nicht 100% genau ist. Wenn Sie sich also nur darauf verlassen, wird die Zählung der verstrichenen Millisekunden um einen kleinen Prozentsatz überschritten und die von Ihnen erstellte Uhr verliert entweder an Zeit oder gewinnt an Zeit. Mein Ansatz bestand darin, die Genauigkeit des von mir verwendeten Arduino zu testen und festzustellen, wie viele Millisekunden es pro Stunde verloren oder gewonnen hat. Dann musste nur noch eine Geschwindigkeitsanpassung programmiert werden, um diese Differenz zu den intern erfassten Millisekunden pro Stunde hinzuzufügen oder abzuziehen.

Meine andere Sorge war, ob die Arduino-Uhr durchweg ungenau war, aber wie angegeben, hat die von mir programmierte Uhr über 5 Tage eine sehr genaue Zeit beibehalten, so dass die Ungenauigkeit anscheinend konsistent ist.

Das zweite Problem ist, dass sich die interne millis()-Funktion alle 50 Tage oder so selbst zurücksetzt und Sie die Millisekundenzahl nicht manipulieren können. Daher bestand die Antwort darin, den millis()-Interrupt durch einen Zähler zu ersetzen, den ich manipulieren konnte und der die Millisekunden ab Mitternacht zählen würde und jeden Tag zurücksetzte, um alle Laufzeitbeschränkungen zu beseitigen.

Bewertung der Ungenauigkeit

Um die Ungenauigkeit zu beurteilen, ging ich davon aus, dass meine Computeruhr und damit millis() in Processing genau waren. Ich habe daher ein Programm für den Arduino erstellt, um die Anzahl der seit dem Handshaking verstrichenen Millisekunden alle 2 Sekunden an Processing zu senden, und ein Skript für Processing, um dies zu lesen und mit seinen verstrichenen Millisekunden zu vergleichen, die ein Echtzeitergebnis und die Differenz nach einer Stunde anzeigen verstrichen. Dies ergab die Anzahl der Millisekunden, die in einer Stunde verloren oder gewonnen wurden, und damit den Wert, der für die Geschwindigkeitsanpassung im Uhrprogramm verwendet werden sollte.

Der Code für das Arduino-Programm und das Processing-Skript sind unten aufgeführt.

Wenn Sie Processing nicht installiert haben, besuchen Sie https://processing.org, wo Sie es herunterladen und mehr darüber erfahren können.

Der Uhrcode

Die Hauptinteressensgebiete des Uhrcodes sind die Einstellung des Interrupts, seine Verwendung und die Art und Weise, wie das Datum gehalten und manipuliert wird.

Die Unterbrechung

Der folgende Code setzt einen Interrupt, der jede Millisekunde ausgelöst wird. Dies leitet den Interrupt um, der verwendet wird, um millis() aufrechtzuerhalten, sodass millis() und delay() nicht mehr funktionieren.

 // Zeitunterbrechung einrichten - millis() rollt nach 50 Tagen über, also // verwenden wir unseren eigenen Millisekundenzähler, den wir am // Ende jedes Tages zurücksetzen können // CTC-Modus Vergleich einstellen Zeit- und Triggerunterbrechung TCCR0A =(1 < 

Dies ist der Code, der jede Sekunde aufgerufen wird:

// Dieser Interrupt wird aufgerufen, wenn die Vergleichszeit erreicht wurde // wird daher einmal pro Millisekunde basierend auf der // OCR0A-Registereinstellung aufgerufen.ISR(TIMER0_COMPA_vect) { if (currentMode !=SET_TIME) aktuelleZeit++; abgelaufen++;}  

currentTime und elapsed sind Long-Variablen ohne Vorzeichen. Beachten Sie, dass diese bei ihrer Definition als flüchtig eingestuft werden, da wir auch die Variablen im Hauptcode manipulieren. Dies zwingt das System, die Variable bei jeder Verwendung zu lesen und keinen zwischengespeicherten Wert zu verwenden.

currentTime speichert die Anzahl der Millisekunden seit Mitternacht und es gibt Routinen, um dies in HH:MM:SS umzuwandeln und es zurückzusetzen, wenn Sie die Zeit einstellen.

Nach Ablauf von 24 Stunden zieht das System die Anzahl der Millisekunden eines Tages von der Uhrzeit ab und erhöht das Datum um 1 Tag. Die Uhr wird daher nicht durch den maximalen Wert beeinflusst, den die Variable speichern kann, im Gegensatz zu millis().

 // Wenn am Ende des Tages die Zeit zurückgesetzt und das Datum erhöht wird if ((currentMode ==SHOW_TIME) &&(currentTime> millisecondsInADay)) { //Nächster Tag // Unterbrechungen während der Reset-Zeit stoppen noInterrupts(); currentTime -=MillisekundenInADay; unterbricht(); aktuellesDatum++; } 

Beachten Sie, dass wir Interrupts deaktivieren, während wir die Variable currentTime manipulieren, andernfalls könnte der Interrupt-Aufruf mitten in der Berechnung ausgelöst werden, um MillisekundenInADay abzuziehen und die Berechnung zu beschädigen.

Nachdem jede Stunde verstrichen ist, passt das System die Anzahl der Millisekunden an, die durch die zuvor berechnete Geschwindigkeitsanpassung verstrichen sind, und passt die aktuelle Uhrzeit an, um die schnelle oder langsame interne Uhr zu kompensieren.

 // Passen Sie am Ende jeder Stunde die verstrichene Zeit für // die Ungenauigkeit in der Arduino-Uhr an if (elapsed>=millisecondsInHour) { noInterrupts (); // Zeit für langsam / schnell laufende Arduino-Uhr anpassen currentTime + =speedCorrection; // Zurücksetzen, um die nächste verstrichene Stunde zu zählen =0; unterbricht(); } 

Datumsspeicherung und -berechnung

Das Datum wird als julianischen Datum gehalten, das die Anzahl der Tage ist, die seit Montag, dem 1. Januar 4713 v. Chr., vergangen sind. Es sind Routinen enthalten, um das Julianische Datum zu berechnen und es zurück in den Gregorianischen Kalender zu konvertieren.

float JulianDate(int iday, int imonth, int iyear) { // Berechne das julianische Datum (getestet bis zum Jahr 20.000) unsigned long d =iday; unsigned long m =1Monat; unsigned long y =iyear; wenn (m <3) {m =m + 12; y =y - 1; } unsigned long t1 =(153 * m - 457) / 5; vorzeichenloses langes t2 =365 * y + (y / 4) - (y / 100) + (y / 400); return 1721118.5 + d + t1 + t2;}void GregorianDate(float jd, int &iday, int &imonth, int &iyear) { // Hinweis 2100 ist das nächste übersprungene Schaltjahr - kompensiert übersprungene Schaltjahre unsigned long f =jd + 68569.5; vorzeichenloses langes e =(4,0 * f) / 146097; vorzeichenloses langes g =f - (146097 * e + 3) / 4; unsigned long h =4000ul * (g + 1) / 1461001; vorzeichenloses langes t =g - (1461 * h / 4) + 31; vorzeichenloses langes u =(80ul * t) / 2447; vorzeichenloses langes v =u / 11; iJahr =100 * (e - 49) + h + v; im Monat =u + 2 - 12 * v; iday =t - 2447 * u / 80;} 

Die Einstellknöpfe

Die Modustaste schaltet den aktuellen Modus von Show Time zu Set Time, Set Year, Set Date, Set Speed ​​Adjustment und zurück zu Show Time. Diese sind jeweils selbsterklärend und verwenden Sie die anderen 2 Tasten, um die aktuelle Einstellung anzupassen.

Sobald die Uhr läuft und Zeit gewinnt oder verliert, können Sie die Geschwindigkeitseinstellung ändern, indem Sie auf den Modus zur Einstellung der Geschwindigkeitseinstellung zugreifen und diese mit den Auf- und Ab-Tasten um jeweils 5 Sekunden erhöhen oder verringern.

Code

  • Uhrprogramm
  • Arduino-Timer-Programm
  • Verarbeitung des Timer-Testskripts
UhrprogrammArduino
Genaue Uhr mit Datum nur mit Arduino
 // Paul Brace - Feb 2021 // Einfache Uhr mit Datum, die nur mit einem Arduino erstellt wurde - kein RTC-Modul // Das Programm enthält eine Zeitkorrektureinstellung zum Ausgleich der internen // Taktgeschwindigkeit nicht 100% genau. // Sobald die richtige Geschwindigkeitseinstellung eingestellt ist, ist die Uhr überraschend genau. // In meinem Test hat sie über einen Zeitraum von 5 Tagen keine Zeit verloren oder gewonnen. // Zeigt die Zeit auf einem 16x2-LCD-Display an // Tasten zum Einstellen der Zeit// Modustaste (Pin 2) schaltet zwischen Zeiteinstellung, Datum und Lauf um// Taste 1 (Pin 3) erhöht Minuten und Monat und verringert Jahr/Geschwindigkeitseinstellung// Taste 2 (Pin 4) erhöht Stunde und Tag und erhöht Jahr./Geschwindigkeitsanpassung// 24-Stunden-Anzeige // Binden Sie den Bibliothekstreiber für die Anzeige ein:#include // LiquidCrystal lcd(RS, EN, D4,D5, D6, D7)LiquidCrystal lcd(12, 13 , 6, 7, 8, 9); // ein LCD-Objekt erstellen und die Pins zuweisen // Tasten und Summerverbindungen definieren#define MODE_BUTTON 2#define HOUR_BUTTON 3 // Gleiche Tastendefinitionen wie#define UP_BUTTON 3 // Code verständlicher machen #define DAY_BUTTON 3#define MINUTE_BUTTON 4 // Gleiche Taste, unterschiedliche Definitionen für #define DOWN_BUTTON 4 // Code verständlicher machen #define MONTH_BUTTON 4// Aktuelle Moduseinstellungen#define SHOW_TIME 1 // 1 =läuft - Zeit anzeigen#define SET_TIME 2 // 2 =Zeit eingestellt #define SET_YEAR 3 // 3 =Jahr einstellen#define SET_DATE 4 // 4 =Tag/Monat einstellen #define SET_SPEED_ADJ 5 // 5 =speedCorrection-Variable ändernint speedCorrection =3545; // Anzahl der Millisekunden, die meine Nano-Uhr pro Stunde langsam läuft // negative Zahl hier, wenn sie schnell läuft // an Ihre Arduino anpassen // Volatile Variablen, wie sie in einem Interrupt geändert wurden, und wir // müssen das System zwingen, die zu lesen tatsächliche Variable // wenn sie außerhalb des Interrupts verwendet wird und keine zwischengespeicherte Versionvolatile verwendet wird unsigned long currentTime; // Dauer in Millisekunden ab Mitternachtunsigned long lastTime =-1000; // lastTime, die ShowTime aufgerufen wurde, initialisiert auf -1000, zeigt also sofortvolatile unsigned long elapsed; // Timer für Verzögerung und Stundenzählung verwendetunsigned long millisecondsInADay; // Millisekunden in 24 Stundenunsigned long millisecondsInHour; // Millisekunden in 1 Stundeint currentMode; // 1 =läuft - Uhrzeit anzeigen // 2 =eingestellte Uhrzeit // 3 =eingestelltes Jahr // 4 =Tag/Monat setfloat currentDate; // Julian datefloat lastDate =0.0; // letztes Datum, an dem ShowDate aufgerufen wurdeint currentDay;int currentMonth;int currentYear;char *dayArray[] ={ "Di. ", // Zeigt eine Compiler-Warnung an, funktioniert aber einwandfrei "Mi.", "Do.", "Fr . ", "Sa. ", "So. ", "Mo. " };void setup() { // Zeitunterbrechung einrichten - millis() rollt nach 50 Tagen über // wir verwenden unseren eigenen Millisekundenzähler, der wir können am // Ende jedes Tages zurückgesetzt werden TCCR0A =(1 < millisecondsInADay)) { //Next Tag // Stoppt Interrupts während der Reset-Zeit noInterrupts(); currentTime -=MillisekundenInADay; unterbricht(); aktuellesDatum++; } // Am Ende jeder Stunde die verstrichene Zeit für // die Ungenauigkeit in der Arduino-Uhr anpassen if (elapsed>=millisecondsInHour) { noInterrupts (); // Zeit für langsam / schnell laufende Arduino-Uhr anpassen currentTime + =speedCorrection; // Zurücksetzen, um die nächste verstrichene Stunde zu zählen =0; unterbricht(); } // Prüfen, ob Schaltflächen gedrückt wurden CheckButtons(); // Anzeige basierend auf aktuellem Moduswechsel anzeigen (currentMode) { case SHOW_TIME:// Aktuelle Uhrzeit und Datum anzeigen ShowTime(currentTime); ShowDate (aktuelles Datum); brechen; case SET_TIME:// Bildschirm zum Einstellen der Zeit ShowTimeSet(currentTime); brechen; case SET_YEAR:// Bildschirm zum Einstellen des Jahres ShowYearSet(currentDate); brechen; case SET_DATE:// Bildschirm zum Einstellen von Tag und Monat ShowDDMMSet(currentDate); brechen; case SET_SPEED_ADJ:// Bildschirm zum Anpassen der Geschwindigkeitskorrektur ShowSpeedSet(); brechen; } Wait(150);} // Dieser Interrupt wird aufgerufen, wenn die Vergleichszeit erreicht wurde // wird daher einmal pro Millisekunde basierend auf der // OCR0A-Registereinstellung aufgerufen. ISR(TIMER0_COMPA_vect) { if (currentMode !=SET_TIME ) aktuelleZeit++; elapsed++;}float JulianDate(int iday, int imonth, int iyear) { // Julian Date berechnen (getestet bis zum Jahr 20.000) unsigned long d =iday; unsigned long m =1Monat; unsigned long y =iyear; wenn (m <3) {m =m + 12; y =y - 1; } unsigned long t1 =(153 * m - 457) / 5; vorzeichenloses langes t2 =365 * y + (y / 4) - (y / 100) + (y / 400); return 1721118.5 + d + t1 + t2;}void GregorianDate(float jd, int &iday, int &imonth, int &iyear) { // Hinweis 2100 ist das nächste übersprungene Schaltjahr - kompensiert übersprungene Schaltjahre unsigned long f =jd + 68569.5; vorzeichenloses langes e =(4,0 * f) / 146097; vorzeichenloses langes g =f - (146097 * e + 3) / 4; unsigned long h =4000ul * (g + 1) / 1461001; vorzeichenloses langes t =g - (1461 * h / 4) + 31; vorzeichenloses langes u =(80ul * t) / 2447; vorzeichenloses langes v =u / 11; iJahr =100 * (e - 49) + h + v; im Monat =u + 2 - 12 * v; iday =t - 2447 * u / 80;}void SplitTime(unsigned long curr, unsigned long &ulHour, unsigned long &ulMin, unsigned long &ulSec) { // HH:MM:SS aus Millisekundenzahl berechnen ulSec =curr / 1000; ulMin =ulSek / 60; ulStunde =ulMin / 60; ulMin -=ulStunde * 60; ulSec =ulSec - ulMin * 60 - ulHour * 3600;}unsigned long SetTime(unsigned long ulHour, unsigned long ulMin, unsigned long ulSec) { // Setzt die Anzahl der Millisekunden von Mitternacht bis zur aktuellen Uhrzeit zurück (ulHour * 60 * 60 * 1000) + (ulMin * 60 * 1000) + (ulSec * 1000);}void Wait(unsigned long value) { // Erstellen Sie unsere eigene Dealy-Funktion // Wir haben unseren eigenen Interrupt auf TCCR0A gesetzt // daher millis() und delay() funktioniert nicht mehr unsigned long startTime =elapsed; while ((elapsed - startTime)  12) { iMonth =1; } // Gespeichertes Datum basierend auf aktuellen Einstellungen setzen currentDate =JulianDate(iDay, iMonth, iYear); aufrechtzuerhalten. Wenn (digitalRead (DAY_BUTTON) ==LOW) {// Vortag int iDay; int iMonth; int iJahr; GregorianDate (aktuelles Datum, iDay, iMonth, iYear); iDay++; if (iDay> 31) { iDay =1; } if (((iMonth ==4) || (iMonth ==6) || (iMonth ==9) || (iMonth ==11)) &&(iDay> 30)) { iDay =1; } if ((iMonth ==2) &&(iDay> 29)) { iDay =1; } if ((iMonth ==2) &&((iYear % 4) !=0) &&(iDay> 28)) { iDay =1; } // Gespeichertes Datum basierend auf aktuellen Einstellungen einstellen // Wenn anschließend der Monat so angepasst wird, dass der Tag nicht gültig ist // dann springt die Anzeige zum nächsten gültigen Datum currentDate =JulianDate(iDay, iMonth, iYear); } brechen; case SET_SPEED_ADJ:// Korrektur um 5 Millisekunden erhöhen oder verringern if (digitalRead (UP_BUTTON) ==LOW) { speedCorrection +=5; aufrechtzuerhalten. Wenn (digitalRead (DOWN_BUTTON) ==LOW) {speedCorrection -=5; } brechen; } }}String FormatNumber(int value) { // Um ​​eine führende 0 hinzuzufügen, falls erforderlich if (value <10) { return "0" + String(value); aufrechtzuerhalten. Sonst { Return String (Wert); }}void ShowTime(unsigned long value) { // Anzeige einmal pro Sekunde aktualisieren // oder wenn über Mitternacht rollt if ((value> lastTime + 1000) || (value  
Arduino-Timer-ProgrammArduino
Dieses Programm sendet die Anzahl der verstrichenen Millisekunden an die serielle Schnittstelle um 2 Sekunden.
// Paul Brace Feb 2021// Zur Verwendung mit dem entsprechenden Verarbeitungsskript// um millis() von hier mit millis() in // Verarbeitung zu vergleichen unter Verwendung der Computeruhrint inByte =0;unsigned long firstReading =100000; // millis () beim ersten Lesen von sentvoid setup () { Serial.begin (9600); // Senden Sie das Hello-Byte an Processing sayHello();}void loop() { // wenn ein Byte an der seriellen Schnittstelle empfangen wird // dann lesen und verwerfen und den aktuellen // Wert von millis() senden if (Serial. verfügbar ()> 0) { // eingehendes Byte abrufen inByte =Serial.read (); // Sendezeit seit der ersten Leseverarbeitung Serial.print (millis () - firstReading); Serial.print ('E'); // alle 2 Sekunden wiederholen Verzögerung (2000); }}void sayHello () {// Warten Sie, bis der serielle Port verfügbar ist // senden Sie dann ein Hallo-Byte, um den Handshake zu starten, während (Serial.available () <=0) { Serial.print ('Z'); // Z zur Verarbeitung senden, um Hallo zu sagen Verzögerung (200); } firstReading =millis();}
Verarbeitung des Timer-TestskriptsVerarbeitung
Dies ist das Skript für die Verarbeitung, das die vom Arduino gesendeten Millisekunden liest und mit den verstrichenen Millisekunden bei der Verarbeitung vergleicht.
// Paul Brace Feb 2021// Skript zum Akzeptieren von millis() von Arduino// und vergleicht es mit interne millis() um // die Ungenauigkeit der Arduino-Uhr zu bewerten. // Geht davon aus, dass die Computeruhr genau ist // -ve =Arduino läuft langsam, also als +ve-Einstellung in das Uhrenprogramm eingeben // +ve =Arduino is läuft schnell, also geben Sie als -ve-Anpassung ein, um den Takt zu verlangsamen. Import Processing.serial.*;Serial theSerialPort; // den seriellen Port erstellen objectint[] serialBytesArray =new int[15]; // Array zum Speichern eingehender Bytesint bytesCount =0; // aktuelle Anzahl der empfangenen Bytesboolean init =false; // false bis der Handshake durch den Empfang des Zeichens abgeschlossen wurde Zint fillColor =255; // Definieren der anfänglichen Füllung colorlong mills =0; // letzte Lesung Receivedlong first =0; // Zeitpunkt der ersten erhaltenen Mühlen, damit wir jetzt die Differenz über eine Stunde berechnen können; // Anzahl der Millis, die seit dem Empfang der ersten Mühlen vergangen sindlong firstReading =100000; // millis () bei der Verarbeitung der ersten Nachricht von Arduinolong DiffPerHour =0; // die Differenz nach Ablauf der ersten Stunde int inByte; // letztes Byte readvoid setup () { // Definiere einige Canvas- und Zeichenparameter size (500, 500); Hintergrund (70); noStroke(); // die Liste aller seriellen Geräte ausgeben, damit Sie wissen, welches für das Arduino eingestellt werden muss // muss das Programm ausführen und bearbeiten, wenn der richtige Port nicht unter printArray (Serial.list ()) eingestellt ist; // instanziieren Sie den seriellen Kommunikationsstring thePortName =Serial.list () [1]; theSerialPort =new Serial(this, thePortName, 9600);}void draw() {// Anzeige der Zeiteinstellungen background(70); fill(fillColor); Textgröße (25); text(Stunde() + ":" + Minute() + ":" + Sekunde(), 50, 50); // die letzten gelesenen Millis, die vom Arduino-Text gesendet wurden ("Incoming elapsed:" + Mills, 50, 100); // der Strom, der seit dem ersten Einlesen vergangen ist Processing text("Local elapsed:" + (jetzt - firstReading), 50, 150); // Anzeige der aktuellen Differenz text("Diff:" + (mills - (now - firstReading)), 50, 200); // Prüfen, ob 1 Stunde vergangen ist und ob die erste Stunde die Differenz speichern if (((now - firstReading)>=3600000) &&(DiffPerHour ==0)){ DiffPerHour =Mills - (now - firstReading); } // Anzeige der ersten Differenz und der Differenz nach der ersten Stunde text("Diff after 1 hour:" + DiffPerHour, 50, 300);}void serialEvent(Serial myPort) { // ein Byte vom seriellen Port lesen inByte =meinPort.read(); if (init ==false) { // Wenn noch kein Handshake durchgeführt wurde, sehen Sie, ob das Handshake-Byte if (inByte =='Z') { // wenn das gelesene Byte Z ist myPort.clear(); // den Puffer der seriellen Schnittstelle löschen init =true; // Speichern Sie die Tatsache, dass wir das erste Hallo hatten myPort.write('Z'); // dem Arduino sagen, dass er mehr senden soll if (first ==0) { first =millis (); } } } else { // wenn es bereits das erste Hallo gab // Füge das letzte Byte vom seriellen Port zum Array hinzu if (inByte !=69) { // Prüfe nicht das Ende des Nachrichtenzeichens E if (bytesCount <14) { serialBytesArray[bytesCount] =inByte; bytesAnzahl++; } } if (inByte ==69) { // Ende der Nachricht // jetzt verstrichene lokale Zeit speichern =millis(); // ankommende Millis berechnen () Mühlen =0; for (int i =1; i <=bytesCount; i++) { Mills +=(serialBytesArray[i - 1] - 48) * pow(10, (bytesCount - i)); } // Sagen wir, wir sind bereit, die nächste Nachricht zu akzeptieren // wenn dies der erste Messwert ist, dann setze den ersten Unterschied if (firstReading ==100000) { firstReading =now; } myPort.write('Z'); // bytesCount zurücksetzen:bytesCount =0; } }}

Schaltpläne


Herstellungsprozess

  1. Arduino-Pov-Vision-Uhr
  2. DTMF-Decoder, der nur Arduino verwendet
  3. Erstellen von Monitor-Ambilight mit Arduino
  4. Einfache Wanduhr mit Adafruit 1/4 60 Ring Neopixel
  5. Einfache Wordclock (Arduino)
  6. Arduino-Uhr mit islamischen Gebetszeiten
  7. Hauptuhr
  8. DIY-Voltmeter mit Arduino und Smartphone
  9. Herzfrequenzmesser mit IoT
  10. WebServerBlink mit Arduino Uno WiFi