Manufaktur industri
Industri Internet of Things | bahan industri | Pemeliharaan dan Perbaikan Peralatan | Pemrograman industri |
home  MfgRobots >> Manufaktur industri >  >> Manufacturing Technology >> Proses manufaktur

Membuat Jam Presisi dengan Arduino Nano dan LCD 16x2

Komponen dan perlengkapan

Tentang proyek ini

Saya memulai ini sebagai latihan akademis, tetapi akhirnya dengan jam yang sangat akurat. Setelah berjalan selama 5 hari, ia tidak kehilangan atau menambah waktu sama sekali.

Masalah utama dengan menggunakan hanya Arduino adalah kecepatan jam internalnya tidak 100% akurat. Oleh karena itu jika Anda hanya mengandalkan ini, hitungan milidetik yang telah berlalu akan keluar dengan persentase kecil dan jam yang Anda buat akan kehilangan atau menambah waktu. Pendekatan saya adalah untuk menguji akurasi Arduino yang saya gunakan dan menentukan berapa milidetik yang hilang atau ditambah per jam. Yang kemudian diperlukan adalah untuk memprogram penyesuaian kecepatan untuk menambah atau mengurangi selisih ini dari milidetik yang diinternal setiap jam.

Khawatir lain saya adalah apakah jam Arduino secara konsisten tidak akurat, tetapi seperti yang ditunjukkan, jam yang saya program telah mempertahankan waktu yang sangat akurat selama 5 hari jadi tampaknya ketidakakuratan konsisten.

Masalah kedua adalah fungsi millis() internal mereset dirinya sendiri setiap 50 hari atau lebih dan Anda tidak dapat memanipulasi hitungan milidetik. Oleh karena itu, jawabannya adalah untuk mengganti interrupt millis() menggunakan counter yang dapat saya manipulasi dan yang akan menghitung milidetik dari tengah malam, mereset setiap hari menghapus batasan waktu berjalan.

Menilai ketidakakuratan

Untuk menilai ketidakakuratan, saya membuat asumsi bahwa jam komputer saya, dan consequently millis() dalam Processing akurat. Saya oleh karena itu membuat program untuk Arduino untuk mengirimkan jumlah milidetik yang telah berlalu sejak handshaking ke Processing sekali setiap 2 detik dan skrip untuk Processing untuk membaca ini dan membandingkannya dengan milidetik yang telah berlalu menampilkan hasil real time dan selisih setelah satu jam telah berlalu. Ini memberikan jumlah milidetik yang telah hilang atau ditambah dalam satu jam dan oleh karena itu nilai untuk digunakan untuk penyesuaian kecepatan dalam program jam.

Kode untuk program Arduino dan skrip Processing disediakan di bawah ini.

Jika Anda tidak memiliki Processing yang terpasang, kunjungi https://processing.org tempat Anda dapat mengunduh dan mempelajarinya.

Kode jam

Area utama minat dalam kode jam adalah pengaturan interrupt, bagaimana ini digunakan dan cara tanggal disimpan dan dimanipulasi.

Interrupt

Kode berikut akan mengatur interrupt yang akan terpicu setiap milidetik. Ini mengalihkan interrupt yang digunakan untuk memelihara millis() jadi millis() dan delay() tidak akan bekerja lagi.

 // Set up time interrupt - millis() rolls over after 50 days so // we are using our own millisecond counter which we can reset at // the end of each day //Set the CTC mode Compare time and trigger interrupt TCCR0A = (1 << WGM01); //Set value for time to compare to ORC0A for 1ms = 249 (8 bits so max is 256) //[(Clock speed/Prescaler value)*Time in seconds] - 1 //[(16,000,000/64) * .001] - 1 = 249 = 1 millisecond OCR0A = 0xF9; //set timer compare interrupt TIMSK0 |= (1 << OCIE0A); //Set the prescale 1/64 clock // ie 110 for last 3 bits TCCR0B |= (1 << CS01); TCCR0B |= (1 << CS00); //initialize counter value to 0 TCNT0 = 0; //Enable interrupt sei(); 

Ini adalah kode yang akan dipanggil setiap detik:

// This is interrupt is called when the compare time has been reached // hence will be called once a millisecond based on the // OCR0A register setting. ISR(TIMER0_COMPA_vect) { if (currentMode != SET_TIME) currentTime++; elapsed++; } 

currentTime dan elapsed adalah variabel unsigned long. Perhatikan bahwa ini diformulasikan sebagai volatile saat didefinisikan karena kita juga memanipulasi variabel dalam kode utama. Ini memaksa sistem untuk membaca variabel setiap kali digunakan dan tidak menggunakan nilai cache.

currentTime menyimpan jumlah milidetik sejak tengah malam dan ada rutinitas untuk mengonversi ini ke HH:MM:SS dan meresetnya saat Anda mengatur waktu.

Ketika 24 jam telah berlalu, sistem mengurangi jumlah milidetik dalam satu hari dari waktu dan menambah tanggal sebesar 1 hari. Jam tidak terpengaruh oleh nilai maksimum yang dapat disimpan oleh variabel, tidak seperti millis().

 // If at end of the day reset time and increase date if ((currentMode == SHOW_TIME) && (currentTime > millisecondsInADay)) { //Next day // Stop interrupts while reset time noInterrupts(); currentTime -= millisecondsInADay; interrupts(); currentDate++; } 

Perhatikan bahwa kita menonaktifkan interrupt saat memanipulasi variabel currentTime sebaliknya panggilan interrupt dapat terpicu di tengah perhitungan untuk mengurangi millisecondsInADay merusak perhitungan.

Setiap jam telah berlalu, sistem menyesuaikan jumlah milidetik yang telah berlalu dengan penyesuaian kecepatan yang kita hitung sebelumnya, menyesuaikan waktu saat ini untuk mengakomodasi jam internal yang cepat atau lambat.

 // At the end of each hour adjust the elapsed time for // the inacuracy in the Arduino clock if (elapsed >= millisecondsInHour) { noInterrupts(); // Adjust time for slow/fast running Arduino clock currentTime += speedCorrection; // Reset to count the next hour elapsed = 0; interrupts(); } 

Penyimpanan dan perhitungan tanggal

Tanggal disimpan sebagai tanggal Julian, yang merupakan jumlah hari yang telah berlalu sejak Senin, 1 Januari 4713 SM. Rutinitas disertakan untuk menghitung tanggal Julian dan mengonversinya kembali ke kalender Gregorius.

float JulianDate(int iday, int imonth, int iyear) { // Calculate julian date (tested up to the year 20,000) unsigned long d = iday; unsigned long m = imonth; unsigned long y = iyear; if (m < 3) { m = m + 12; y = y - 1; } unsigned long 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) { // Note 2100 is the next skipped leap year - compensates for skipped leap years unsigned long f = jd + 68569.5; unsigned long e = (4.0 * f) / 146097; unsigned long 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; unsigned long v = u / 11; iyear = 100 * (e - 49) + h + v; imonth = u + 2 - 12 * v; iday = t - 2447 * u / 80; } 

Tombol penyesuaian

Tombol Mode memajukan mode saat ini dari Tampilkan Waktu, ke Atur Waktu, Atur Tahun, Atur Tanggal, Atur Penyesuaian Kecepatan dan kembali ke Tampilkan Waktu. Setiap ini self explanatory dan menggunakan 2 tombol lain untuk menyesuaikan pengaturan saat ini.

Setelah jam berjalan, jika ia mengalah atau kehilangan waktu, Anda dapat mengubah penyesuaian kecepatan, dengan mengakses mode Atur Penyesuaian Kecepatan dan menggunakan tombol naik dan turun untuk menambah atau mengurangi ini dengan 5 detik sekaligus.

Kode

Program jam

// Paul Brace - Feb 2021 // Simple Clock with Date created just using a Arduino - no RTC module // Program incorporates a time correction adjustment to compensate for the internal // clock speed not being 100% accurate. // Once correct speed adjustment set the clock is surprisingly accurate. // In my test it did not lose or gain any time over a 5 day period. // Displays time on a 16x2 LCD display // Buttons to set time // Mode button (pin 2) toggles set time, set date and run // Button 1 (pin 3) Increments Minutes and Month and decreases Year/speed adj // Button 2 (pin 4) Increments Hour and Day and increases Year./speed adj // 24 Hour display #include <LiquidCrystal.h> LiquidCrystal lcd(12, 13, 6, 7, 8, 9); #define MODE_BUTTON 2 #define HOUR_BUTTON 3 #define UP_BUTTON 3 #define DAY_BUTTON 3 #define MINUTE_BUTTON 4 #define DOWN_BUTTON 4 #define MONTH_BUTTON 4 #define SHOW_TIME 1 #define SET_TIME 2 #define SET_YEAR 3 #define SET_DATE 4 #define SET_SPEED_ADJ 5 int speedCorrection = 3545; volatile unsigned long currentTime; unsigned long lastTime = -1000; volatile unsigned long elapsed; unsigned long millisecondsInADay; unsigned long millisecondsInHour; int currentMode; float currentDate; float lastDate = 0.0; int currentDay; int currentMonth; int currentYear; char *dayArray[] = { "Tue. ", "Wed. ", "Thur. ", "Fri. ", "Sat. ", "Sun. ", "Mon. " }; void setup() { TCCR0A = (1 << WGM01); OCR0A = 0xF9; TIMSK0 |= (1 << OCIE0A); TCCR0B |= (1 << CS01); TCCR0B |= (1 << CS00); TCNT0 = 0; sei(); pinMode(MINUTE_BUTTON, INPUT_PULLUP); pinMode(HOUR_BUTTON, INPUT_PULLUP); pinMode(MODE_BUTTON, INPUT_PULLUP); currentTime = 0; currentDate = JulianDate(1, 1, 2021); elapsed = 0; millisecondsInADay = 24ul * 60 * 60 * 1000; millisecondsInHour = 60ul * 60 * 1000; currentMode = SHOW_TIME; lcd.begin(16, 2); lcd.noAutoscroll(); lcd.display(); lcd.clear(); ShowTime(currentTime); } void loop() { if ((currentMode == SHOW_TIME) && (currentTime > millisecondsInADay)) { noInterrupts(); currentTime -= millisecondsInADay; interrupts(); currentDate++; } if (elapsed >= millisecondsInHour) { noInterrupts(); currentTime += speedCorrection; elapsed = 0; interrupts(); } CheckButtons(); switch (currentMode) { case SHOW_TIME: ShowTime(currentTime); ShowDate(currentDate); break; case SET_TIME: ShowTimeSet(currentTime); break; case SET_YEAR: ShowYearSet(currentDate); break; case SET_DATE: ShowDDMMSet(currentDate); break; case SET_SPEED_ADJ: ShowSpeedSet(); break; } Wait(150); } ISR(TIMER0_COMPA_vect) { if (currentMode != SET_TIME) currentTime++; elapsed++; } float JulianDate(int iday, int imonth, int iyear) { unsigned long d = iday; unsigned long m = imonth; unsigned long y = iyear; if (m < 3) { m = m + 12; y = y - 1; } unsigned long 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) { unsigned long f = jd + 68569.5; unsigned long e = (4.0 * f) / 146097; unsigned long 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; unsigned long v = u / 11; iyear = 100 * (e - 49) + h + v; imonth = u + 2 - 12 * v; iday = t - 2447 * u / 80; } void SplitTime(unsigned long curr, unsigned long &ulHour, unsigned long &ulMin, unsigned long &ulSec) { 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) { return (ulHour * 60 * 60 * 1000) + (ulMin * 60 * 1000) + (ulSec * 1000); } void Wait(unsigned long value) { unsigned long startTime = elapsed; while ((elapsed - startTime) < value) { } } void CheckButtons() { if (digitalRead(MODE_BUTTON) == LOW) { switch (currentMode) { case SHOW_TIME: currentMode = SET_TIME; lcd.clear(); break; case SET_TIME: currentMode = SET_YEAR; lcd.clear(); break; case SET_YEAR: currentMode = SET_DATE; lcd.clear(); break; case SET_DATE: currentMode = SET_SPEED_ADJ; lcd.clear(); break; case SET_SPEED_ADJ: currentMode = SHOW_TIME; lcd.clear(); lastTime = 0; lastDate = 0.0; break; } } if (currentMode != SHOW_TIME) { switch (currentMode) { case SET_TIME: if (digitalRead(MINUTE_BUTTON) == LOW) { unsigned long iHours; unsigned long iMinutes; unsigned long iSeconds; SplitTime(currentTime, iHours, iMinutes, iSeconds); if (iMinutes < 59) { iMinutes++; } else { iMinutes = 0; } noInterrupts(); currentTime = SetTime(iHours, iMinutes, 0); elapsed = 0; interrupts(); } if (digitalRead(HOUR_BUTTON) == LOW) { unsigned long iHours; unsigned long iMinutes; unsigned long iSeconds; SplitTime(currentTime, iHours, iMinutes, iSeconds); if (iHours < 23) { iHours++; } else { iHours = 0; } noInterrupts(); currentTime = SetTime(iHours, iMinutes, 0); elapsed = 0; interrupts(); } break; case SET_YEAR: if (digitalRead(UP_BUTTON) == LOW) { int iDay; int iMonth; int iYear; GregorianDate(currentDate, iDay, iMonth, iYear); iYear++; currentDate = JulianDate(iDay, iMonth, iYear); } if (digitalRead(DOWN_BUTTON) == LOW) { int iDay; int iMonth; int iYear; GregorianDate(currentDate, iDay, iMonth, iYear); iYear--; currentDate = JulianDate(iDay, iMonth, iYear); } break; case SET_DATE: if (digitalRead(MONTH_BUTTON) == LOW) { int iDay; int iMonth; int iYear; GregorianDate(currentDate, iDay, iMonth, iYear); iMonth++; if (iMonth > 12) { iMonth = 1; } currentDate = JulianDate(iDay, iMonth, iYear); } if (digitalRead(DAY_BUTTON) == LOW) { int iDay; int iMonth; int iYear; GregorianDate(currentDate, 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; } currentDate = JulianDate(iDay, iMonth, iYear); } break; case SET_SPEED_ADJ: if (digitalRead(UP_BUTTON) == LOW) { speedCorrection += 5; } if (digitalRead(DOWN_BUTTON) == LOW) { speedCorrection -= 5; } break; } } } String FormatNumber(int value) { if (value < 10) { return "0" + String(value); } else { return String(value); } } void ShowTime(unsigned long value) { if ((value > lastTime + 1000) || (value < lastTime)) { lastTime = value; unsigned long iHours; unsigned long iMinutes; unsigned long iSeconds; SplitTime(value, iHours, iMinutes, iSeconds); lcd.setCursor(0, 0); lcd.print("Time: " + FormatNumber(iHours) + ":" + FormatNumber(iMinutes) + ":" + FormatNumber(iSeconds)); } } void ShowDate(float value) { if (lastDate != value) { lastDate = value; int iday; int imonth; int iyear; String currentDay; GregorianDate(value, iday, imonth, iyear); int dayOfWeek = (unsigned long)value % 7; lcd.setCursor(0, 1); lcd.print(dayArray[dayOfWeek]); lcd.print(FormatNumber(iday) + ":" + FormatNumber(imonth) + ":" + iyear); } } void ShowDDMMSet(float value) { int iday; int imonth; int iyear; String currentDay; GregorianDate(value, iday, imonth, iyear); lcd.setCursor(0, 0); lcd.print("Set day & month:"); lcd.setCursor(0, 1); lcd.print("Day:" + FormatNumber(iday) + " Month:" + FormatNumber(imonth)); } void ShowYearSet(float jd) { int iday; int imonth; int iyear; GregorianDate(jd, iday, imonth, iyear); lcd.setCursor(0, 0); lcd.print("Set year:"); lcd.setCursor(0, 1); lcd.print("Year: " + FormatNumber(iyear)); } void ShowTimeSet(unsigned long value) { unsigned long iHours; unsigned long iMinutes; unsigned long iSeconds; SplitTime(value, iHours, iMinutes, iSeconds); lcd.setCursor(0, 0); lcd.print("Set time:"); lcd.setCursor(0, 1); lcd.print("Hours:" + FormatNumber(iHours) + " Mins:" + FormatNumber(iMinutes)); } void ShowSpeedSet() { lcd.setCursor(0, 0); lcd.print("Set speed adj:"); lcd.setCursor(0, 1); lcd.print("Millis: "); lcd.print(speedCorrection); lcd.print(" "); } 

Program timer Arduino

// Paul Brace Feb 2021 // For use with corresponding Processing script // to compare millis() from here to millis() in // Processing using the computer clock int inByte = 0; unsigned long firstReading = 100000; void setup() { Serial.begin(9600); sayHello(); } void loop() { if (Serial.available() > 0){ inByte = Serial.read(); Serial.print(millis() - firstReading); Serial.print('E'); delay(2000); } } void sayHello(){ while (Serial.available() <=0){ Serial.print('Z'); delay(200); } firstReading = millis(); } 

Skrip uji timer Processing

// Paul Brace Feb 2021 // Script to accept millis() from Arduino // and compare it to internal millis() to // assess inaccuracy of the Arduino clock. // Assumes that the computer clock is accurate import processing.serial.*; Serial theSerialPort; int[] serialBytesArray = new int[15]; int bytesCount = 0; boolean init = false; int fillColor = 255; long mills = 0; long first = 0; long now; long firstReading = 100000; long DiffPerHour = 0; int inByte; void setup() { size(500, 500); background(70); noStroke(); printArray(Serial.list()); String thePortName = Serial.list()[1]; theSerialPort = new Serial(this, thePortName, 9600); } void draw() { background(70); fill(fillColor); textSize(25); text(hour() + ":" + minute() + ":" + second(), 50, 50); text("Incoming elapsed: " + mills, 50, 100); text("Local elapsed: " + (now - firstReading), 50, 150); text("Diff: " + (mills - (now - firstReading)), 50, 200); if (((now - firstReading)>= 3600000) && (DiffPerHour == 0)){ DiffPerHour = mills - (now - firstReading); } text("Diff after 1 hour: " + DiffPerHour, 50, 300); } void serialEvent(Serial myPort) { inByte = myPort.read(); if (init == false) { if (inByte == 'Z') { myPort.clear(); init = true; myPort.write('Z'); if (first == 0){ first = millis(); } } } else { if (inByte != 69) { if (bytesCount < 14) { serialBytesArray[bytesCount] = inByte; bytesCount++; } } if (inByte == 69) { now = millis(); mills = 0; for (int i = 1; i <= bytesCount; i++) { mills += (serialBytesArray[i - 1] - 48) * pow(10, (bytesCount - i)); } if (firstReading == 100000) { firstReading = now; } myPort.write('Z'); bytesCount = 0; } } } 

Skematik

Membuat Jam Presisi dengan Arduino Nano dan LCD 16x2Membuat Jam Presisi dengan Arduino Nano dan LCD 16x2

Proses manufaktur

  1. Jam POV Arduino: Buat Tampilan LED Persistensi Penglihatan
  2. Membangun DTMF Decoder Hanya Menggunakan Arduino – Tidak Perlu IC Tambahan
  3. Buat Sistem Ambilight Monitor Menakjubkan dengan Arduino
  4. Buat Jam Dinding Stylish dengan Neopixel Ring 60-LED Adafruit & Arduino
  5. Jam Kata Arduino DIY – Buat Tampilan Waktu Real-Time yang Elegan
  6. Membuat Jam Waktu Nyata Arduino untuk Waktu Sholat Islam
  7. Bangun Jam Master LED Kustom dengan Alarm – Terlihat dari Jarak 12 Meter
  8. Buat Voltmeter Cerdas dengan Arduino & Smartphone – Proyek DIY Mudah
  9. Pemantau Detak Jantung IoT Real-Time dengan Arduino & MAX30100
  10. Arduino Uno WiFi Web Server: Nyalakan/Matikan LED Melalui Browser