Приглашаю к тестированию системы коррекции хода часов
- Войдите на сайт для отправки комментариев
Большая радость пришла в наш чум - вчера наконец-то удалось прорваться через злобных роботов-тестировщиков и опубликовать на Google Play (пока что в версии для открытого тестирования) свое первое Android-приложение RTC Synchro, предназначенное для определения скорости ухода часов реального времени (+/- секунд за сутки) и настройки коррекции этого ухода. Поставленная цель - уйти от сумасшедшей погрешности модуля DS1307 (в моем случае это 31 секунда) до более/менее приемлемых 1-2 сек.
------------------------------
Всех, владеющих смартфоном и модулями Bluetooth+DS1307, приглашаю к тестированию вышеупомянутого аппа (еще раз ссылка на страничку в магазине Google Play - ТУТА) и обсуждению Arduino-части системы.
А вот и скетч, работающий в связке с вышеупомянутым аппом:
#include <SoftwareSerial.h> // библиотека, автоматизирующая процесс создания управляющих строковых последовательностей //#include <mdwriter.h> //#include <mdsender_bt.h> // Работа с часами DS1307 #include <Wire.h> #include "RTClib.h" #define _WITH_TIMECORRECTION RTC_DS1307 rtc; // *** используются куски из примера ds1307 пакета RTClib *** DateTime d; SoftwareSerial mySerial(7, 6); // RX, TX //MD_Sender_BT mdScout; String cmdBuffer,sResponse; short sceneNum; char cSign=0; // Коррекция только при -1 или +1 unsigned long interval,nextCorr,lastCorr,dd; void setup() { // Open serial communications and wait for port to open: Serial.begin(9600); while (!Serial) { ; // wait for serial port to connect. Needed for native USB port only } Serial.println("+------------== avr_MD_test_MoClck ==------------+"); Serial.println("| testing Clock tool for setting clock and |"); Serial.println("| define correction values |"); Serial.println("+------------------------------------------------+"); // set the data rate for the SoftwareSerial port mySerial.begin(9600); sResponse = ""; cmdBuffer = ""; sceneNum = 1; if (! rtc.begin()) { Serial.println("Couldn't find RTC"); while (1); } if (! rtc.isrunning()) { Serial.println("RTC is NOT running!"); // following line sets the RTC to the date & time this sketch was compiled //rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // *************************************** // This line sets the RTC with an explicit date & time, for example to set // January 21, 2014 at 3am you would call: // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); } lastCorr=millis(); interval = 36990; } void loop() { // run over and over char c; String s; if (mySerial.available()) { c = (char)mySerial.read(); Serial.write(c); if (c==0x0A || c==0x0D) interpreteCommand(); else cmdBuffer=cmdBuffer+c; } if (Serial.available()) { //mySerial.write((char)Serial.read()); c = (char)Serial.read(); //mySerial.write(); if (c==0x0A || c==0x0D) interpreteCommand(); else cmdBuffer=cmdBuffer+c; } if(millis()-lastCorr>=interval) { TimeSpan ts; dd = lastCorr; lastCorr=millis(); d = rtc.now(); s = get2dec(d.hour()) + ":" + get2dec(d.minute()) + ":" + get2dec(d.second()); if(cSign==1) { // Перевод часов на одну секунду вперед #ifdef WITH_TIMECORRECTION ts=1; rtc.adjust(d+ts); #endif } else if(cSign==-1) { // Перевод часов на одну секунду назад #ifdef WITH_TIMECORRECTION ts=1; rtc.adjust(d-ts); #endif } s = s + " - " + get2dec(d.hour()) + ":" + get2dec(d.minute()) + ":" + get2dec(d.second()); Serial.println(s); Serial.print(lastCorr); Serial.print("-"); Serial.print(dd); Serial.print("="); Serial.print(lastCorr-dd); Serial.print(" ("); Serial.print(d.unixtime()); Serial.println(")"); } } void sendString() { for(int i=0;i<sResponse.length();i++) { mySerial.write(sResponse.charAt(i)); Serial.write(sResponse.charAt(i)); } mySerial.write('\r'); mySerial.write('\n'); Serial.write('\r'); Serial.write('\n'); sResponse = ""; } void sendString(String s) { Serial.print("Send string via bluetooth: "); for(int i=0;i<s.length();i++) { mySerial.write(s.charAt(i)); Serial.write(s.charAt(i)); } mySerial.write('\r'); mySerial.write('\n'); Serial.write('\r'); Serial.write('\n'); sResponse = ""; } String getYear(uint16_t y) { char cYear[5]; sprintf(cYear,"%d",y); return cYear; } String get2dec(byte d) { char dgt[]="0123456789",d2[]="00"; d2[0]=dgt[d/10]; d2[1]=dgt[d%10]; String s2(d2); return s2; } String getDateTime(bool withDofW) { DateTime now = rtc.now(); String s = getYear(now.year()) + "/" + get2dec(now.month()) + "/" + get2dec(now.day()) + " "; s = s + get2dec(now.hour()) + ":" + get2dec(now.minute()) + ":" + get2dec(now.second()); return s; } void setDate(String s, bool bDebug) { int p, pslash, newMonth, newDay; int newYear; String sY, sM, sD; p = s.indexOf(" "); if(p>=0) { pslash = s.indexOf("/",p+1); if (bDebug) { Serial.print("p="); Serial.print(p); Serial.print(", pslash ="); Serial.print(pslash); } sY = s.substring(p+1,pslash); Serial.println(" sYear: " + sY); newYear = s.substring(p+1,pslash).toInt(); p = pslash; pslash = s.indexOf("/",p+1); if (bDebug) { Serial.print("p="); Serial.print(p); Serial.print(", pslash ="); Serial.print(pslash); } sM = s.substring(p+1,pslash); Serial.println(" sMonth: " + sM); newMonth = s.substring(p+1,pslash).toInt(); p = pslash; if (bDebug) { Serial.print("p="); Serial.print(p); Serial.print(", pslash ="); Serial.print(pslash); } sD = s.substring(p+1); Serial.println(" sDay: " + sD); newDay = s.substring(p+1).toInt(); if (bDebug) { Serial.print("Year: "); Serial.print(newYear); Serial.print(", Month: "); Serial.print(newMonth); Serial.print(", Day: "); Serial.println(newDay); } DateTime d = rtc.now(); rtc.adjust(DateTime(newYear, newMonth, newDay, d.hour(), d.minute(), d.second())); } sendString("datetime="+getDateTime(false)); } void setTime(String s, bool bDebug) { int p, pslash, newHour, newMinute; int newSecond; String sH, sM, sS; p = s.indexOf(" "); // поиск пробела между командой ("settime") и подстрокой с новым временем ("hh:mm:ss") if(p>=0) { // выделяем часы pslash = s.indexOf(":",p+1); if (bDebug) { Serial.print("p="); Serial.print(p); Serial.print(", pslash ="); Serial.print(pslash); } sH = s.substring(p+1,pslash); Serial.println(" sHour: " + sH); newHour = s.substring(p+1,pslash).toInt(); p = pslash; // выделяем минуты pslash = s.indexOf(":",p+1); if (bDebug) { Serial.print("p="); Serial.print(p); Serial.print(", pslash ="); Serial.print(pslash); } sM = s.substring(p+1,pslash); Serial.println(" sMinute: " + sM); newMinute = s.substring(p+1,pslash).toInt(); p = pslash; pslash = s.indexOf("|",p+1); if(pslash<0) { // строка завершилась - только установка времени if (bDebug) { Serial.print("p="); Serial.print(p); Serial.print(", pslash ="); Serial.print(pslash); } sS = s.substring(p+1); Serial.println(" sSecond: " + sS); newSecond = s.substring(p+1).toInt(); } else { // найдено продолжение - блок с прараметрами коррекции ухода часов s = s.substring(pslash+1); Serial.println("call setCorrection("+s+")"); setCorrection(" "+s,bDebug); } if (bDebug) { Serial.print(" Hour: "); Serial.print(newHour); Serial.print(", Minute: "); Serial.print(newMinute); Serial.print(", Second: "); Serial.println(newSecond); } // а теперь собственно настройка времени #ifdef WITH_TIMECORRECTION DateTime d = rtc.now(); rtc.adjust(DateTime(d.year(), d.month(), d.day(), newHour, newMinute, newSecond)); #endif } sendString("datetime="+getDateTime(false)); } void setCorrection(String s, bool bDebug) { // Задание параметров коррекции хода часов. Два параметра (направление коррекции и интервал коррекции) // передаются в формате <sign><interval>, где sign - направление коррекции ('+' для отстающих часов, // т.е. подвод часов на 1 секунду, '-' для спешащих часов, т.е. отвод часов на 1 секунду назад), аinterval // задает интервал времени, по истечении которого необходимо выполнять коррекцию хода char p,c; p = s.indexOf(" "); #ifdef WITH_TIMECORRECTION if(s[p+1]=='+') cSign=1; else cSign=-1; interval = s.substring(p+2).toInt(); if(bDebug) {Serial.print(" sign: "); Serial.println(cSign, DEC);} if(bDebug) {Serial.print("interval: "); Serial.println(interval);} #else if(s[p+1]=='+') c=1; else c=-1; /*if(bDebug)*/ {Serial.print(" sign: "); Serial.println(c, DEC);} /*if(bDebug)*/ {Serial.print("interval: "); Serial.println(s.substring(p+2).toInt());} #endif } void interpreteCommand() { if(cmdBuffer.length()==0) return; if(cmdBuffer.equalsIgnoreCase("getprotocol")) sendString("protocol=RTClock"); else if(cmdBuffer.equalsIgnoreCase("getdatetime")) sendString("datetime="+getDateTime(false)); else if(cmdBuffer.indexOf("setdate")>=0) setDate(cmdBuffer, false); else if(cmdBuffer.indexOf("settime")>=0) setTime(cmdBuffer, false); else if(cmdBuffer.indexOf("setcorr")>=0) setCorrection(cmdBuffer, true); else { Serial.print("Unknown command: <"); Serial.print(cmdBuffer); Serial.println(">"); } cmdBuffer = ""; }Немного о схеме:
Модуль Arduino Nano, к которому подключены модуль часов DS1307 (A4->SDA, A5->SCL) и Bluetooth (software serial: D7->RX, D6-> TX)
Программный сериал в вышеприведенном скетче используется потому, что аппаратный занят под вывод отладочной информации. Такая конфигурация позволяет проверять работу алгоритма и без связи со смартфоном - все поступающие от смартфона команды можно ввести в терминале, отправляемая на смартфон информация параллельно выводится в терминал.
Бредовая идея.
У ds1307 нет термокомпенсации.
У каждой из микросхем будет свой уход в ту или иную сторону, в зависимости от температуры.
Я на работе часы на ней собрал(там сильно точность не нужна).
Летом и зимой по минуте в неделю в одну сторону убегает, осенью/весной(когда уже холодно а отопления ещё нет) в другую.
Для DS3231 более актуально
А NTP использовать не пробовали. А вы идете из Питера в Москву через Владивосток.
Это всё решается покупкой нормального кварца за 15 рублей, дрейфующего максимум на 20 минут в год после всех температурных поправок.
Это всё решается покупкой нормального кварца за 15 рублей, дрейфующего максимум на 20 минут в год после всех температурных поправок.
3231 проще и наверное даже дешевле, учитывая то, что не нуждается в дополнительных решениях.
А NTP использовать не пробовали. А вы идете из Питера в Москву через Владивосток.
NTP с ардуинки, оснащенной лишь блютусом? Научите.
Ну через uart время с устройства получить проблем нет никаких.
Чем будет отличатся та же процедура для Bluetooth ?
Пару раз в год - да даже пару раз в месяц - потратить десять секунд на соединение с МК, сравнение времени смартфона и МК и нажатие на кнопку коррекции хода в случае необходимости...
Ничто не мешает адаптировать скетч к любым часам, на которые найдется библиотека для управления ими. Для приложения требуется лишь сравнить два-три раза время на эталонном и корректируемом устройстве - тут зависимости от типа микросхемы никакой.
Это эталонное устройство должно либо висеть на проводках, либо ручками подключаться к ним. А смартфон лежит в кармане. Мимо контролируемого устройства прохожу, запускаю приложение, вижу, что часы тикают с допустимым разбегом - успокаиваюсь. Нет - давлю на кнопку, одновременно и устанавливая на МК и точное время и актуальную для сезона коррекцию хода.
А зачем на нано точное время?
Если она никуда не подключается, то и абсолютная точность не нужна.
Если это часы, и нужно более точное время - проще на ESP8266 сделать, цена на том же уровне, но и Wi-Fi уже есть, можно и время получать и данными обмениваться.
А зачем на нано точное время?
Точное - незачем. А относительно точное, с уходом 1-2 секунды в сутки вместо 30-40, вполне может оказаться полезным. Задачи перед людьми стоят разные, методы их решения тоже весьма разнообразны.
Если это часы, и нужно более точное время - проще на ESP8266 сделать, цена на том же уровне, но и Wi-Fi уже есть, можно и время получать и данными обмениваться.
А если это не часы? Если это что-то типа кухонного/дачного/еще какого таймера с привязкой к календарю и каким-то внешним событиям, происходящим в определенное время? Как сказано выше,люди разные, задачи перед ними стоят непохожие, а та же пресловутая DS1307 до сих пор продается наряду с более точной DS3231...
Цель обсуждаемого приложения не создание турбийона из часов с кукушкой, а автоматизация процесса корректировки хода, приведение погрешности из области неприличных значений к удобоваримым. Переход от +/- 30 сек в сутки к 1-2. Или на базе года - от 3-4 часов накопившейся погрешности к 5-6 минутам.
А вы идете из Питера в Москву через Владивосток.
Запятая тут решает ))
Так время же относительно :-), (цитата)
Просто ЦНХ с DS1307 без стабильного кварца и термостабилизации, тема так себе.
Тогда еще и коррекцию ЦНХ по температуре нужно закладывать.
Вот и возникает вопрос, может более точный DS3231, или NTP в ESP? Это же даже дешевле чем нано и BT получится.
Я Цифровую Настройку Хода (ЦНХ) делал под Win на компе в 90-ых, это было сделано, если GPS будет недоступен, (плата GPS тогда ставилась в комп (редкая новинка была) для получения точного времени, точность была нужна до секунды, координаты были не нужны, объект был под землей), а точность обеспечить было нужно. Ну сделал ЦНХ, ну месяцами ходили более менее точно, время по модему рассылали на несколько потребителей.
А сейчас дома часы с ESP8266 и синхронизацией по NTP в каждой комнате. В датчиках тоже ESP, как же без mqtt?
Время там незачем, время на сервере.
Нет, есть еще старые самопальные термостаты, в них есть время, можно посмотреть время минимумов и максимумов температуры за последние сутки. Локально на ЖКИ. Но они подключены к мастеру по RS485, и он обновляет в них время из NTP.
Вот скрин из моей проги из 90-ых:
Не видно что-то скрина.(
Да замучился загружать..., супер недружественный интерфейс...
Блин, увидел скрин и чуть слезу не пустил. Ностальжи. Эх, было время и мы были молодыми....
А в чем проблема?
Я до сих пор такие интерфейсы делаю.