Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Точные часы, просто используя Arduino

Компоненты и расходные материалы

Arduino Nano R3
Я использовал Nano, но должен работать с любым Arduino
× 1
Буквенно-цифровой ЖК-дисплей, 16 x 2
Любой дисплей должен работать, я использовал этот https://www.amazon.co.uk/ gp / product / B00N8K2BYM / ref =ppx_yo_dt_b_asin_title_o02_s00? ie =UTF8 &psc =1
× 1
Тактильный переключатель, срабатывающий сверху
× 3
Подстроечный потенциометр, 10 кОм
Подойдет любой триммер 10k
× 1
Перемычки
× 1

Об этом проекте

Я начал это как академическое упражнение, но в итоге получил очень точные часы. После 5 дней работы он не потерял и не выиграл время.

Основная проблема с использованием только Arduino заключается в том, что его внутренняя тактовая частота не является точной на 100%. Следовательно, если вы просто полагаетесь на это, то счетчик прошедших миллисекунд будет на небольшой процент, а часы, которые вы создаете, будут либо терять, либо увеличивать время. Мой подход заключался в том, чтобы проверить точность Arduino, которую я использовал, и определить, сколько миллисекунд он терял или выигрывал в час. Все, что тогда требовалось, - это запрограммировать регулировку скорости, чтобы добавить или вычесть эту разницу из внутренне отслеживаемых миллисекунд каждый час.

Другая моя проблема заключалась в том, были ли часы Arduino постоянно неточными, но, как указано, часы, которые я запрограммировал, поддерживали очень точное время в течение 5 дней, поэтому кажется, что неточность постоянна.

Вторая проблема заключается в том, что внутренняя функция millis () сбрасывается каждые 50 дней или около того, и вы не можете управлять счетчиком миллисекунд. Следовательно, ответ заключался в том, чтобы заменить прерывание millis () с помощью счетчика, которым я мог бы манипулировать и который бы считал миллисекунды от полуночи, сбрасывая каждый день, удаляя любые ограничения времени выполнения.

Оценка неточности

Чтобы оценить неточность, я предположил, что часы моего компьютера и, следовательно, millis () в Processing были точными. Поэтому я создал программу для Arduino, которая отправляет количество миллисекунд, прошедших с момента установления связи с обработкой, каждые 2 секунды и скрипт для Processing, чтобы прочитать это и сравнить его с прошедшими миллисекундами, отображая результат в реальном времени, и разница через час прошло. Это дало количество миллисекунд, которые были потеряны или получены за час, и, следовательно, значение, используемое для регулировки скорости в программе часов.

Код программы Arduino и скрипта обработки приведены ниже.

Если у вас не установлена ​​обработка, посетите https://processing.org, где вы можете загрузить и узнать о ней.

Код часов

Основные области, представляющие интерес в коде часов, - это настройка прерывания, то, как оно используется, а также способ удержания даты и управления ею.

Прерывание

Следующий код установит прерывание, которое будет срабатывать каждую миллисекунду. Это перенаправляет прерывание, используемое для обслуживания millis (), поэтому millis () и delay () больше не будут работать.

  // Настраиваем прерывание по времени - millis () переключается через 50 дней, поэтому // мы используем наш собственный счетчик миллисекунд, который мы можем сбрасывать // в конце каждого дня // Устанавливаем режим CTC Сравнить время и триггер прерывания TCCR0A =(1 < 

Это код, который будет вызываться каждую секунду:

  // Это прерывание вызывается при достижении времени сравнения // следовательно, будет вызываться один раз в миллисекунду на основе // настройки регистра OCR0A. ISR (TIMER0_COMPA_vect) {if (currentMode! =SET_TIME) currentTime ++; elapsed ++;}  

currentTime и elapsed - длинные переменные без знака. Обратите внимание, что они квалифицируются как изменчивые, когда они определены, поскольку мы также манипулируем переменными в основном коде. Это заставляет систему читать переменную каждый раз, когда она используется, и не использовать кэшированное значение.

currentTime хранит количество миллисекунд, прошедших с полуночи, и есть процедуры для преобразования этого значения в ЧЧ:ММ:СС и сброса его при установке времени.

По прошествии 24 часов система вычитает количество миллисекунд в день из времени и увеличивает дату на 1 день. Таким образом, на часы не влияет максимальное значение, которое может хранить переменная, в отличие от millis ().

  // Если в конце дня сбросить время и увеличить дату if ((currentMode ==SHOW_TIME) &&(currentTime> millisecondsInADay)) {// Следующий день // Остановить прерывания на время сброса noInterrupts (); currentTime - =миллисекундыInADay; прерывания (); currentDate ++; }  

Обратите внимание, что мы отключаем прерывания при манипулировании переменной currentTime, иначе вызов прерывания может быть запущен в середине вычисления, чтобы вычесть миллисекундыInADay, искажающие вычисление.

По истечении каждого часа система корректирует количество миллисекунд, прошедшее с помощью рассчитанной нами ранее регулировки скорости, корректируя текущее время для компенсации быстрых или медленных внутренних часов.

  // В конце каждого часа корректируем прошедшее время для // неточности в часах Arduino if (elapsed> =millisecondsInHour) {noInterrupts (); // Настраиваем время для медленного / быстрого запуска часов Arduino currentTime + =speedCorrection; // Сброс для подсчета прошедшего часа =0; прерывания (); }  

Хранение и расчет даты

Дата считается юлианской датой, которая представляет собой количество дней, прошедших с понедельника 1 января 4713 года до нашей эры. Включены процедуры для расчета даты по юлианскому календарю и ее обратного преобразования в григорианский календарь.

  float JulianDate (int iday, int imonth, int iyear) {// Вычислить дату по юлиану (проверено до 20 000 года) unsigned long d =iday; беззнаковый длинный m =imonth; беззнаковый длинный y =iyear; если (m <3) {m =m + 12; у =у - 1; } long без знака t1 =(153 * m - 457) / 5; беззнаковое длинное 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) {// Примечание 2100 - следующий пропущенный високосный год - компенсирует пропущенные високосные годы unsigned long f =jd + 68569.5; беззнаковое длинное e =(4.0 * f) / 146097; беззнаковое длинное g =f - (146097 * e + 3) / 4; беззнаковый длинный h =4000ul * (g + 1) / 1461001; беззнаковое длинное t =g - (1461 * h / 4) + 31; беззнаковое длинное u =(80ul * t) / 2447; беззнаковое длинное v =u / 11; iyear =100 * (e - 49) + h + v; imonth =u + 2 - 12 * v; iday =t - 2447 * u / 80;}  

Кнопки регулировки

Кнопка «Режим» переводит текущий режим с «Показать время» на «Установить время», «Установить год», «Установить дату», «Установить скорость» и обратно на «Показать время». Каждый из них говорит сам за себя, и для настройки текущего параметра используются другие 2 кнопки.

Когда часы идут, если они набирают или теряют время, вы можете изменить настройку скорости, войдя в режим настройки скорости и используя кнопки вверх и вниз, чтобы увеличивать или уменьшать ее на 5 секунд за раз.

Код

  • Программа часов
  • Программа таймера Arduino
  • Сценарий тестирования таймера обработки
Программа часов Arduino
Точные часы с датой только с использованием и Arduino
 // Paul Brace - февраль 2021 // Простые часы с датой, созданные только с использованием Arduino - без модуля RTC // Программа включает корректировку времени для компенсации внутренней // тактовой скорости неточность на 100%. // После установки правильной регулировки скорости часы становятся на удивление точными. // В моем тесте часы не теряли и не выигрывали за 5 дней. для установки времени // Кнопка режима (контакт 2) переключает время, дату и запуск // Кнопка 1 (контакт 3) увеличивает минуты и месяц и уменьшает регулировку года / скорости // Кнопка 2 (контакт 4) увеличивает часы и день и увеличивает Year./speed adj // 24-часовой дисплей // Включите драйвер библиотеки для отображения:#include  // LiquidCrystal lcd (RS, EN, D4, D5, D6, D7) LiquidCrystal lcd (12, 13 , 6, 7, 8, 9); // создать ЖК-объект и назначить контакты // Определить кнопки и подключения зуммера # определить MODE_BUTTON 2 # определить HOUR_BUTTON 3 // Для одной и той же кнопки разные определения # определить UP_BUTTON 3 // сделать код более понятным #define DAY_BUTTON 3 # define MINUTE_BUTTON 4 // Одна и та же кнопка, разные определения # определить DOWN_BUTTON 4 // сделать код более понятным #define MONTH_BUTTON 4 // Настройки текущего режима # определить SHOW_TIME 1 // 1 =работает - показать время # определить SET_TIME 2 // 2 =установить время #define SET_YEAR 3 // 3 =год установлен # define SET_DATE 4 // 4 =день / месяц установлен #define SET_SPEED_ADJ 5 // 5 =изменить переменную speedCorrection int speedCorrection =3545; // Количество миллисекунд, в которых мои часы Nano работают медленно в час // отрицательное число, если они работают быстро // измените, чтобы соответствовать вашему Arduino // Изменчивые переменные, измененные в прерывании, и нам // нужно заставить систему прочитать фактическая переменная // при использовании вне прерывания и без использования кэшированной версии volatile без знака long currentTime; // Продолжительность в миллисекундах от полуночиunsigned long lastTime =-1000; // lastTime, в который был вызван метод ShowTime, инициализирован значением -1000, поэтому показывает сразу жеvolatile unsigned long elapsed; // Таймер, используемый для задержки и отсчета часовunsigned long millisecondsInADay; // Миллисекунды в 24 часаunsigned long millisecondsInHour; // Миллисекунды в 1 час int currentMode; // 1 =запущено - показать время // 2 =время установлено // 3 =год установлено // 4 =день / месяц setfloat currentDate; // Юлианский datefloat lastDate =0.0; // последняя дата вызова ShowDate int currentDay; int currentMonth; int currentYear; char * dayArray [] ={"Tue.", // Будет отображаться предупреждение компилятора, но работает нормально "Wed.", "Thur.", "Fri . "," Sat. "," Sun. "," Mon. "}; void setup () {// Устанавливаем прерывание по времени - millis () переключается через 50 дней, поэтому // мы используем наш собственный счетчик миллисекунд, который мы можем сбрасывать // в конце каждого дня TCCR0A =(1 < millisecondsInADay)) {// Далее day // Остановить прерывания во время сброса noInterrupts (); currentTime - =миллисекундыInADay; прерывания (); currentDate ++; } // В конце каждого часа настраиваем прошедшее время для // неточности в часах Arduino if (elapsed> =millisecondsInHour) {noInterrupts (); // Настраиваем время для медленного / быстрого запуска часов Arduino currentTime + =speedCorrection; // Сброс для подсчета прошедшего часа =0; прерывания (); } // Проверяем, были ли нажаты какие-либо кнопки CheckButtons (); // Показать отображение на основе переключателя текущего режима (currentMode) {case SHOW_TIME:// Отображение текущего времени и даты ShowTime (currentTime); ShowDate (currentDate); ломать; case SET_TIME:// Отображение экрана для установки времени ShowTimeSet (currentTime); ломать; case SET_YEAR:// Отображение экрана для установки года ShowYearSet (currentDate); ломать; case SET_DATE:// Отображение экрана для установки дня и месяца ShowDDMMSet (currentDate); ломать; case SET_SPEED_ADJ:// Отображение экрана для настройки коррекции скорости ShowSpeedSet (); ломать; } Wait (150);} // Это прерывание вызывается по достижении времени сравнения // поэтому будет вызываться один раз в миллисекунду на основе // настройки регистра OCR0A. ISR (TIMER0_COMPA_vect) {if (currentMode! =SET_TIME ) currentTime ++; elapsed ++;} float JulianDate (int iday, int imonth, int iyear) {// Вычислить дату по юлиану (проверено до 20 000 года) unsigned long d =iday; беззнаковый длинный m =imonth; беззнаковый длинный y =iyear; если (m <3) {m =m + 12; у =у - 1; } long без знака t1 =(153 * m - 457) / 5; беззнаковое длинное 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) {// Примечание 2100 - следующий пропущенный високосный год - компенсирует пропущенные високосные годы unsigned long f =jd + 68569.5; беззнаковое длинное e =(4.0 * f) / 146097; беззнаковое длинное g =f - (146097 * e + 3) / 4; беззнаковый длинный h =4000ul * (g + 1) / 1461001; беззнаковое длинное t =g - (1461 * h / 4) + 31; беззнаковое длинное u =(80ul * t) / 2447; беззнаковое длинное 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) {// Вычислить HH:MM:SS по счетчику миллисекунд 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) {// Создаем нашу собственную функцию dealy // Мы установили собственное прерывание на TCCR0A // следовательно, millis () и delay () больше не будет работать unsigned long startTime =elapsed; while ((elapsed - startTime)  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 ++; если (iDay> 31) {iDay =1; } if (((iMonth ==4) || (iMonth ==6) || (iMonth ==9) || (iMonth ==11)) &&(iDay> 30)) {iDay =1; } если ((iMonth ==2) &&(iDay> 29)) {iDay =1; } if ((iMonth ==2) &&((iYear% 4)! =0) &&(iDay> 28)) {iDay =1; } // Устанавливаем сохраненную дату на основе текущих настроек // Если впоследствии изменить месяц так, чтобы день был недействительным // тогда дисплей перейдет к следующей действительной дате currentDate =JulianDate (iDay, iMonth, iYear); } ломать; case SET_SPEED_ADJ:// увеличить или уменьшить коррекцию на 5 миллисекунд if (digitalRead (UP_BUTTON) ==LOW) {speedCorrection + =5; } if (digitalRead (DOWN_BUTTON) ==LOW) {speedCorrection - =5; } ломать; }}} String FormatNumber (int value) {// Чтобы добавить начальный 0, если требуется if (value <10) {return "0" + String (value); } else {return String (значение); }} void ShowTime (unsigned long value) {// Обновлять отображение раз в секунду // или при переходе через полночь if ((value> lastTime + 1000) || (value  
Программа таймера Arduino Arduino
Эта программа отправляет количество прошедших миллисекунд на последовательный порт через каждые 2 секунды.
 // Paul Brace, февраль 2021 г. // Для использования с соответствующим скриптом обработки // для сравнения millis () отсюда с millis () в // Обработке с использованием часов компьютера int inByte =0; unsigned long firstReading =100000; // millis () при первом чтении sentvoid setup () {Serial.begin (9600); // Отправляем приветственный байт в Обработку sayHello ();} void loop () {// если байт получен на последовательном порте // тогда считываем и отбрасываем его и отправляем текущее // значение millis () if (Serial. available ()> 0) {// получаем входящий байт inByte =Serial.read (); // время отправки, прошедшее с момента обработки первого чтения Serial.print (millis () - firstReading); Serial.print ("E"); // повторять каждые 2 секунды delay (2000); }} void sayHello () {// Подождите, пока станет доступен последовательный порт // затем отправьте байт приветствия, чтобы начать рукопожатие while (Serial.available () <=0) {Serial.print ('Z'); // Отправляем Z в обработку, чтобы сказать Hello delay (200); } firstReading =millis ();} 
Скрипт тестирования таймера обработки Processing
Это сценарий для обработки, который считывает миллисекунды, отправленные из Arduino, и сравнивает их с прошедшими миллисекундами обработки.
 // Paul Brace, февраль 2021 г. // Сценарий для приема millis () от Arduino // и сравнения его с internal millis () для // оценки неточности часов Arduino. // Предполагается, что часы компьютера точны // -ve =Arduino работает медленно, поэтому введите в программе часов как + ve корректировку // + ve =Arduino is работает быстро, поэтому введите настройку -ve для замедления тактовой частоты import processing.serial. *; Serial theSerialPort; // создаем объект последовательного порта int [] serialBytesArray =new int [15]; // массив для хранения входящих байтов int bytesCount =0; // текущее количество полученных байтовboolean init =false; // false, пока рукопожатие не завершится получением символа Zint fillColor =255; // определение начального цвета заливки long mills =0; // получено последнее чтение long first =0; // время получения первых фрезеров, поэтому теперь мы можем рассчитать разницу за час; // количество миллисекунд, прошедших с момента получения первого фрезерования long firstReading =100000; // millis () при обработке первого сообщения, полученного от Arduinolong DiffPerHour =0; // разница по прошествии первого часа int inByte; // последний байт readvoid setup () {// определяем некоторые параметры холста и рисования size (500, 500); фон (70); noStroke (); // распечатываем список всех последовательных устройств, чтобы вы знали, какое из них установить для Arduino // нужно будет запустить программу и отредактировать, если правильный порт не установлен ниже printArray (Serial.list ()); // мгновенное создание строки последовательной связи thePortName =Serial.list () [1]; theSerialPort =new Serial (this, thePortName, 9600);} void draw () {// Отображение фона настроек времени (70); заливка (fillColor); textSize (25); текст (час () + ":" + минута () + ":" + секунда (), 50, 50); // последний миллисекунд чтения, отправленный Arduino text ("Incoming elapsed:" + mills, 50, 100); // текущее время, прошедшее с момента первого чтения в тексте обработки ("Local elapsed:" + (now - firstReading), 50, 150); // отображаем текущий текст разницы ("Diff:" + (mills - (now - firstReading)), 50, 200); // Проверяем, прошел ли 1 час и сохраняется ли разница в первый час 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') {// если был прочитан байт Z myPort.clear (); // очищаем буфер последовательного порта init =true; // сохраняем факт первого приветствия myPort.write ('Z'); // скажем Arduino отправить больше if (first ==0) {first =millis (); }}} else {// если уже было первое приветствие // Добавляем последний байт из последовательного порта в массив if (inByte! =69) {// Проверяем не конец символа E сообщения if (bytesCount <14) {serialBytesArray [bytesCount] =inByte; bytesCount ++; }} if (inByte ==69) {// Конец сообщения // Сохранение прошедшего местного времени =millis (); // вычисляем входящие 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:bytesCount =0; }}} 

Схема


Производственный процесс

  1. Часы видения Arduino pov
  2. Декодер DTMF с использованием только Arduino
  3. Создание монитора Ambilight с помощью Arduino
  4. Простые настенные часы с использованием Adafruit 1/4 60 Ring Neopixel
  5. Простые часы со словами (Arduino)
  6. Часы Arduino с исламским временем молитв
  7. Основные часы
  8. Вольтметр своими руками с использованием Arduino и смартфона
  9. Монитор сердечного ритма с использованием Интернета вещей
  10. WebServerBlink с использованием Arduino Uno WiFi