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

Nauwkeurige klok alleen met behulp van een Arduino

Componenten en benodigdheden

Arduino Nano R3
Ik heb een Nano gebruikt, maar zou met elke Arduino moeten werken
× 1
Alfanumeriek LCD-scherm, 16 x 2
Elk scherm zou moeten werken, ik heb dit gebruikt https://www.amazon.co.uk/ gp/product/B00N8K2BYM/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1
× 1
Tactiele schakelaar, bovenkant bediend
× 3
Trimmerpotentiometer, 10 kohm
Elke 10k trimmer is voldoende
× 1
Jumperdraden
× 1

Over dit project

Ik begon dit als een academische oefening, maar eindigde met een zeer nauwkeurige klok. Na 5 dagen te hebben gelopen, had het geen tijd verloren of gewonnen.

Het belangrijkste probleem met het gebruik van alleen een Arduino is dat de interne kloksnelheid niet 100% nauwkeurig is. Dus als u hier gewoon op vertrouwt, zal de telling van de verstreken milliseconden met een klein percentage afwijken en zal de klok die u aan het maken bent, tijd verliezen of winnen. Mijn aanpak was om de nauwkeurigheid van de Arduino die ik gebruikte te testen en te bepalen hoeveel milliseconden het per uur verloor of won. Het enige dat toen nodig was, was het programmeren van een snelheidsaanpassing om dit verschil elk uur op te tellen bij of af te trekken van de intern bijgehouden milliseconden.

Mijn andere zorg was of de Arduino-klok consequent onnauwkeurig was, maar zoals aangegeven, heeft de klok die ik heb geprogrammeerd de tijd gedurende 5 dagen zeer nauwkeurig aangehouden, dus het lijkt erop dat de onnauwkeurigheid consistent is.

Het tweede probleem is dat de interne millis()-functie zichzelf elke 50 dagen of zo reset en dat je het aantal milliseconden niet kunt manipuleren. Daarom was het antwoord om de millis()-interrupt te vervangen door een teller die ik kon manipuleren en die de milliseconden vanaf middernacht zou tellen, waarbij elke dag opnieuw werd ingesteld en alle runtime-beperkingen werden verwijderd.

De onnauwkeurigheid beoordelen

Om de onnauwkeurigheid te beoordelen, ging ik ervan uit dat mijn computerklok, en dus de millis() in Processing nauwkeurig was. Ik heb daarom een ​​programma voor de Arduino gemaakt om het aantal milliseconden dat is verstreken sinds de handshake elke 2 seconden naar Processing te sturen en een script voor Processing om dit te lezen en te vergelijken met de verstreken milliseconden die een realtime resultaat weergeven en het verschil na een uur had verstreken. Dit gaf het aantal milliseconden dat verloren of gewonnen was in een uur en dus de waarde die moest worden gebruikt voor de snelheidsaanpassing in het klokprogramma.

De code voor het Arduino-programma en het Processing-script vindt u hieronder.

Als Processing niet is geïnstalleerd, gaat u naar https://processing.org waar u het kunt downloaden en er meer over te weten kunt komen.

De klokcode

De belangrijkste interessegebieden in de klokcode zijn de instelling van de interrupt, hoe deze wordt gebruikt en de manier waarop de datum wordt vastgehouden en gemanipuleerd.

De onderbreking

De volgende code stelt een interrupt in die elke milliseconde wordt geactiveerd. Dit leidt de interrupt die wordt gebruikt om millis() te onderhouden om, zodat millis() en delay() niet langer werken.

 // Tijdonderbreking instellen - millis() rolt over na 50 dagen, dus // we gebruiken onze eigen millisecondenteller die we aan het einde van elke dag kunnen resetten // Stel de CTC-modus in Vergelijk tijd en trigger-interrupt TCCR0A =(1 < 

Dit is de code die elke seconde wordt aangeroepen:

// Dit is een interrupt die wordt aangeroepen wanneer de vergelijkingstijd is bereikt// wordt daarom één keer per milliseconde aangeroepen op basis van de // OCR0A-registerinstelling.ISR(TIMER0_COMPA_vect) {if (currentMode!=SET_TIME) huidigeTijd++; verstreken++;}  

currentTime en elapsed zijn niet-ondertekende lange variabelen. Merk op dat deze als vluchtig worden gekwalificeerd wanneer ze worden gedefinieerd, aangezien we ook de variabelen in de hoofdcode manipuleren. Dit dwingt het systeem om de variabele elke keer dat deze wordt gebruikt te lezen en geen waarde in de cache te gebruiken.

currentTime slaat het aantal milliseconden sinds middernacht op en er zijn routines om dit om te zetten naar UU:MM:SS en het opnieuw in te stellen wanneer u de tijd instelt.

Als er 24 uur zijn verstreken, trekt het systeem het aantal milliseconden in een dag van de tijd af en verhoogt de datum met 1 dag. De klok wordt daarom niet beïnvloed door de maximale waarde die de variabele kan opslaan, in tegenstelling tot millis().

 // Als aan het einde van de dag de tijd wordt gereset en de datum wordt verhoogd if ((currentMode ==SHOW_TIME) &&(currentTime> millisecondsInADay)) { //Volgende dag // Stop onderbreekt tijdens resettijd noInterrupts(); currentTime -=millisecondenInADay; onderbreekt(); huidigeDatum++; } 

Houd er rekening mee dat we interrupts uitschakelen terwijl we de currentTime-variabele manipuleren, anders kan de interrupt-aanroep in het midden van de berekening worden geactiveerd om millisecondenInADay af te trekken die de berekening corrumperen.

Nadat elk uur is verstreken, past het systeem het aantal milliseconden aan dat is verstreken door de snelheidsaanpassing die we eerder hebben berekend, waarbij de huidige tijd wordt aangepast om de snelle of langzame interne klok te compenseren.

 // Pas aan het einde van elk uur de verstreken tijd aan voor // de onnauwkeurigheid in de Arduino-klok if (elapsed>=millisecondsInHour) { noInterrupts(); // Pas de tijd aan voor langzaam/snel lopende Arduino-klok currentTime +=speedCorrection; // Reset om het volgende verstreken uur te tellen =0; onderbreekt(); } 

Datumopslag en berekening

De datum wordt gehouden als een Juliaanse datum, het aantal dagen dat is verstreken sinds maandag 1 januari 4713 v.Chr. Er zijn routines opgenomen om de Juliaanse datum te berekenen en terug te zetten naar de Gregoriaanse kalender.

float JulianDate(int iday, int imonth, int iyear) { // Bereken julian date (getest tot het jaar 20.000) unsigned long d =iday; niet ondertekend lang m =imaand; unsigned long y =iyear; als (m <3) { m =m + 12; y =y - 1; } niet-ondertekende lange t1 =(153 * m - 457) / 5; unsigned long 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) { // Opmerking 2100 is het volgende overgeslagen schrikkeljaar - compenseert voor overgeslagen schrikkeljaren unsigned long f =jd + 68569.5; niet-ondertekende lange e =(4,0 * f) / 146097; niet-ondertekende lange g =f - (146097 * e + 3) / 4; unsigned long h =4000ul * (g + 1) / 1461001; unsigned long t =g - (1461 * h / 4) + 31; unsigned long u =(80ul * t) / 2447; niet-ondertekende lange v =u / 11; ijaar =100 * (e - 49) + h + v; imaand =u + 2 - 12 * v; iday =t - 2447 * u / 80;} 

De aanpassingsknoppen

Met de knop Mode gaat de huidige modus van Show Time naar Set Time, Set Year, Set Date, Set Speed ​​Adjustment en terug naar Show Time. Elk van deze spreekt voor zich en gebruik de andere 2 knoppen om de huidige instelling aan te passen.

Als de klok eenmaal loopt en tijd wint of verliest, kunt u de snelheidsaanpassing wijzigen door naar de modus Snelheidsaanpassing instellen te gaan en de knop omhoog en omlaag te gebruiken om deze met 5 seconden per keer te verhogen of te verlagen.

Code

  • Klokprogramma
  • Arduino-timerprogramma
  • Testscript voor timer verwerken
KlokprogrammaArduino
Nauwkeurige klok met datum alleen met behulp van Arduino
// Paul Brace - februari 2021// Eenvoudige klok met datum gemaakt alleen met behulp van een Arduino - geen RTC-module// Programma bevat een tijdcorrectie-aanpassing om de interne// kloksnelheid te compenseren niet 100% nauwkeurig.// Zodra de juiste snelheidsaanpassing is ingesteld, is de klok verrassend nauwkeurig.// In mijn test verloor of won hij geen tijd over een periode van 5 dagen.// Geeft tijd weer op een 16x2 LCD-scherm// Knoppen om de tijd in te stellen// Modusknop (pin 2) schakelt tussen tijd instellen, datum instellen en run// Knop 1 (pin 3) Verhogen minuten en maand en verlagen Jaar/snelheid aanpassen// Knop 2 (pin 4) Verhogen uur en dag en verhoogt Year./speed adj// 24-uurs weergave// Voeg de bibliotheekdriver toe voor weergave:#include // LiquidCrystal lcd(RS, EN, D4,D5, D6, D7)LiquidCrystal lcd(12, 13 , 6, 7, 8, 9); // maak een lcd-object en wijs de pinnen toe// Definieer knoppen en zoemerverbindingen#define MODE_BUTTON 2#define HOUR_BUTTON 3 // Dezelfde knop verschillende definities voor#define UP_BUTTON 3 // maak code begrijpelijker #define DAY_BUTTON 3#define MINUTE_BUTTON 4 // Dezelfde knop verschillende definities om #define DOWN_BUTTON 4 // code begrijpelijker te maken #define MONTH_BUTTON 4// Huidige modus instellingen#define SHOW_TIME 1 // 1 =running - show time#define SET_TIME 2 // 2 =ingestelde tijd #define SET_YEAR 3 // 3 =jaar set#define SET_DATE 4 // 4 =dag/maand set #define SET_SPEED_ADJ 5 // 5 =verander de speedCorrection variableint speedCorrection =3545; // Aantal milliseconden dat mijn Nano-klok langzaam per uur loopt// negatief getal hier als het snel loopt// verander om overeen te komen met je Arduino// Vluchtige variabelen zoals gewijzigd in een interrupt en we// moeten het systeem dwingen om de werkelijke variabele// bij gebruik buiten de interrupt en geen gebruik van een in de cache opgeslagen versievluchtige niet-ondertekende lange currentTime; // Duur in milliseconden vanaf middernacht unsigned long lastTime =-1000; // lastTime dat ShowTime werd aangeroepen, geïnitialiseerd op -1000, dus toont onmiddellijk vluchtig niet-ondertekend lang verstreken; // Timer gebruikt voor vertraging en uren geteld, lange millisecondenInADay; // Milliseconden in 24 uur unsigned lange milliseconden InHour; // Milliseconden in 1 hourint currentMode; // 1 =lopend - toon tijd// 2 =ingestelde tijd// 3 =ingestelde jaar// 4 =dag/maand setfloat currentDate; // Julian datefloat lastDate =0.0; // laatste datum waarop ShowDate werd aangeroepen in currentDay;int currentMonth;int currentYear;char *dayArray[] ={ "Tue. ", // Geeft een compilerwaarschuwing weer maar werkt prima "Wed.", "Thur. ", "Fri . ", "Sat.", "Sun. ", "Mon. " };void setup() { // Time interrupt instellen - millis() gaat na 50 dagen over, dus // we gebruiken onze eigen millisecondenteller die we kunnen resetten op // het einde van elke dag TCCR0A =(1 < millisecondsInADay)) { //Next dag // Stop interrupts terwijl reset tijd noInterrupts(); currentTime -=millisecondenInADay; onderbreekt(); huidigeDatum++; } // Pas aan het einde van elk uur de verstreken tijd aan voor // de onnauwkeurigheid in de Arduino-klok if (elapsed>=millisecondsInHour) { noInterrupts(); // Pas de tijd aan voor langzaam/snel lopende Arduino-klok currentTime +=speedCorrection; // Reset om het volgende verstreken uur te tellen =0; onderbreekt(); } // Controleer of er knoppen zijn ingedrukt CheckButtons(); // Toon weergave op basis van huidige modusschakelaar (currentMode) {case SHOW_TIME:// Toon huidige tijd en datum ShowTime (currentTime); ShowDate(huidigeDatum); pauze; case SET_TIME:// Scherm voor het instellen van de tijd ShowTimeSet (currentTime); pauze; case SET_YEAR:// Scherm voor het instellen van het jaar ShowYearSet (currentDate); pauze; case SET_DATE:// Scherm voor het instellen van de dag en maand ShowDDMMSet(currentDate); pauze; case SET_SPEED_ADJ:// Scherm voor het aanpassen van de snelheidscorrectie ShowSpeedSet(); pauze; } Wait(150);}// Dit is een interrupt die wordt aangeroepen wanneer de vergelijkingstijd is bereikt// wordt daarom één keer per milliseconde aangeroepen op basis van de// OCR0A-registerinstelling.ISR(TIMER0_COMPA_vect) {if (currentMode!=SET_TIME ) currentTime++; elapsed++;}float JulianDate(int iday, int imonth, int iyear) { // Bereken julian date (getest tot het jaar 20.000) unsigned long d =iday; niet ondertekend lang m =imaand; unsigned long y =iyear; als (m <3) { m =m + 12; y =y - 1; } niet-ondertekende lange t1 =(153 * m - 457) / 5; unsigned long 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) { // Opmerking 2100 is het volgende overgeslagen schrikkeljaar - compenseert voor overgeslagen schrikkeljaren unsigned long f =jd + 68569.5; niet-ondertekende lange e =(4,0 * f) / 146097; niet-ondertekende lange g =f - (146097 * e + 3) / 4; unsigned long h =4000ul * (g + 1) / 1461001; unsigned long t =g - (1461 * h / 4) + 31; unsigned long u =(80ul * t) / 2447; niet-ondertekende lange v =u / 11; ijaar =100 * (e - 49) + h + v; imaand =u + 2 - 12 * v; iday =t - 2447 * u / 80;}void SplitTime(unsigned long curr, unsigned long &ulHour, unsigned long &ulMin, unsigned long &ulSec) { // Bereken HH:MM:SS vanaf milliseconde telling ulSec =curr / 1000; ulMin =ulSec / 60; ulHour =ulMin / 60; ulMin -=ulHour * 60; ulSec =ulSec - ulMin * 60 - ulHour * 3600;}unsigned long SetTime (unsigned long ulHour, unsigned long ulMin, unsigned long ulSec) { // Stelt het aantal milliseconden in van middernacht tot de huidige tijdretour (ulHour * 60 * 60 * 1000) + (ulMin * 60 * 1000) + (ulSec * 1000);}void Wait (unsigned long value) { // Creëer onze eigen dealy-functie // We hebben onze eigen interrupt ingesteld op TCCR0A // vandaar millis() en delay() werkt niet langer ongetekend long startTime =elapsed; while ((elapsed - startTime)  12) { iMonth =1; } // Stel opgeslagen datum in op basis van huidige instellingen currentDate =JulianDate (iDay, iMonth, iYear); } if (digitalRead (DAY_BUTTON) ==LAAG) { // Advance day int iDay; int iMaand; int iJaar; Gregoriaanse datum (huidige datum, iDay, iMonth, iYear); iDay++; als (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; } // Stel de opgeslagen datum in op basis van de huidige instellingen // Als vervolgens de maand wordt aangepast zodat de dag niet geldig is // dan gaat de weergave naar de volgende geldige datum currentDate =JulianDate (iDay, iMonth, iYear); } pauze; case SET_SPEED_ADJ:// verhoog of verlaag de correctie met 5 milliseconden als (digitalRead (UP_BUTTON) ==LAAG) { speedCorrection +=5; } if (digitalRead (DOWN_BUTTON) ==LAAG) { speedCorrection -=5; } pauze; } }}String FormatNumber(int value) { // Om indien nodig een voorloop 0 toe te voegen if (waarde <10) { return "0" + String(value); } else { return String (waarde); }}void ShowTime (unsigned long value) { // Werk de weergave eenmaal per seconde bij // of wanneer de middernacht voorbij is if ((value> lastTime + 1000) || (value  
Arduino timerprogrammaArduino
Dit programma stuurt het aantal verstreken milliseconden elke 2 seconden naar de seriële poort.
// Paul Brace Feb 2021// Voor gebruik met het bijbehorende Processing-script// om millis() van hier te vergelijken met millis() in // Processing met behulp van de computerklok inByte =0; unsigned long firstReading =100000; // millis () bij het voor het eerst lezen van sentvoid setup () { Serial.begin (9600); // Stuur de hallo byte naar Processing sayHello();}void loop() { // als een byte wordt ontvangen op de seriële poort // lees en gooi het dan weg en stuur de huidige // waarde van millis() if (Serial. available()> 0){ // get inkomende byte inByte =Serial.read(); // verzend tijd verstreken sinds eerste lezing verwerking Serial.print (millis () - firstReading); Serial.print('E'); // herhaal elke 2 seconden vertraging (2000); }}void sayHello(){ // Wacht tot de seriële poort beschikbaar is // stuur dan hallo byte om de handshake te starten while (Serial.available() <=0){ Serial.print('Z'); // Stuur Z naar de verwerking om Hallo vertraging (200) te zeggen; } firstReading =millis();}
Verwerking timer testscriptVerwerking
Dit is het script voor Processing dat de milliseconden leest die door de Arduino zijn verzonden en deze vergelijkt met de verstreken milliseconden tijdens de verwerking.
// Paul Brace Feb 2021// Script om millis() van Arduino te accepteren// en te vergelijken met interne millis() om// onnauwkeurigheid van de Arduino-klok te beoordelen.// Aangenomen dat de computerklok nauwkeurig is// -ve =Arduino loopt langzaam, dus voer in als een +ve-aanpassing in het klokprogramma// +ve =Arduino is loopt snel dus voer in als een -ve aanpassing om de klok te vertragen import processing.serial.*;Serial theSerialPort; // maak de seriële poort objectint[] serialBytesArray =new int[15]; // array om inkomende bytes op te slaanint bytesCount =0; // huidig ​​aantal ontvangen bytesboolean init =false; // false totdat de handdruk is voltooid door het teken Zint fillColor =255 te ontvangen; // definiëren van de initiële vulling colourlong mills =0; // laatste lezing ontvangen lang eerst =0; // tijd van de eerste molens die we hebben ontvangen, zodat we het verschil nu over een uur kunnen berekenen; // aantal verstreken milliseconden sinds de eerste maal dat de molens ontvingen, lange eerste uitlezing =100000; // millis() in verwerking van eerste bericht ontvangen van Arduinolong DiffPerHour =0; // het verschil nadat het eerste uur is verstreken int inByte; // last byte readvoid setup() {// definieer een aantal canvas- en tekenparameters size (500, 500); achtergrond(70); geen slag(); // print de lijst met alle seriële apparaten zodat je weet welke je moet instellen voor de Arduino // je moet het programma uitvoeren en bewerken als de juiste poort niet is ingesteld onder printArray(Serial.list()); // instanteer de seriële communicatiestring thePortName =Serial.list()[1]; theSerialPort =new Serial(this, thePortName, 9600);}void draw() { // Display time settings background(70); vul(vulKleur); tekstgrootte(25); text(hour() + ":" + minute() + ":" + second(), 50, 50); // de laatst gelezen millis verzonden door de Arduino-tekst ("Incoming elapsed:" + mills, 50, 100); // de huidige verstreken sinds de eerste keer gelezen in Tekst verwerken ("Lokale verstreken:" + (nu - eerste lezing), 50, 150); // toon de huidige verschiltekst ("Diff:" + (mills - (now - firstReading)), 50, 200); // Controleer of er 1 uur is verstreken en of het eerste uur het verschil opslaat if (((now - firstReading)>=3600000) &&(DiffPerHour ==0)){ DiffPerHour =mills - (now - firstReading); } // Toon het eerste verschil en het verschil na het eerste uur text("Diff after 1 hour:" + DiffPerHour, 50, 300);}void serialEvent(Serial myPort) { // lees een byte van de seriële poort inByte =mijnPort.lezen(); if (init ==false) { // indien nog niet de handshake byte if (inByte =='Z') { // als de gelezen byte Z is myPort.clear(); // wis de seriële poortbuffer init =true; // bewaar het feit dat we de eerste hallo myPort.write('Z') hadden; // vertel de Arduino om meer te sturen if (first ==0){ first =millis(); } } } else { // als de eerste hallo al was // Voeg de laatste byte van de seriële poort toe aan array if (inByte! =69) { // Controleer niet het einde van berichtteken E if (bytesCount <14) {serialBytesArray[bytesCount] =inByte; bytesCount++; } } if (inByte ==69) { // Einde van bericht // lokale tijd die nu verstreken is opslaan =millis(); // bereken inkomende millis() mills =0; for (int i =1; i <=bytesCount; i++) { mills +=(serialBytesArray[i - 1] - 48) * pow(10, (bytesCount - i)); } // Stel dat we klaar zijn om het volgende bericht te accepteren // als dit de eerste lezing is, stel dan het eerste verschil in if (firstReading ==100000) { firstReading =nu; } mijnPort.write('Z'); // Reset bytesCount:bytesCount =0; } }}

Schema's


Productieproces

  1. Arduino pov vision-klok
  2. DTMF-decoder met alleen Arduino
  3. Maak Monitor Ambilight met Arduino
  4. Eenvoudige wandklok met Adafruit 1/4 60 Ring Neopixel
  5. Eenvoudige Word Clock (Arduino)
  6. Arduino klok met islamitische gebedstijden
  7. Hoofdklok
  8. DIY voltmeter met Arduino en smartphone
  9. Hartslagmeter met IoT
  10. WebServerBlink met Arduino Uno WiFi
  11. 7-segment arrayklok