Приглашаю к тестированию системы коррекции хода часов

step962
Offline
Зарегистрирован: 23.05.2011

Большая радость пришла в наш чум - вчера наконец-то удалось прорваться через злобных роботов-тестировщиков и опубликовать на Google Play (пока что в версии для открытого тестирования) свое первое Android-приложение RTC Synchro, предназначенное для определения скорости ухода часов реального времени (+/- секунд за сутки) и настройки коррекции этого ухода. Поставленная цель - уйти от сумасшедшей погрешности модуля DS1307 (в моем случае это 31 секунда) до более/менее приемлемых 1-2 сек.

------------------------------

Всех, владеющих смартфоном и модулями Bluetooth+DS1307, приглашаю к тестированию вышеупомянутого аппа (еще раз ссылка на страничку в магазине Google Play - ТУТА) и обсуждению Arduino-части системы.

step962
Offline
Зарегистрирован: 23.05.2011

А вот и скетч, работающий в связке с вышеупомянутым аппом:

#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 = "";
}

 

step962
Offline
Зарегистрирован: 23.05.2011

Немного о схеме:

Модуль Arduino Nano, к которому подключены модуль часов DS1307 (A4->SDA, A5->SCL) и Bluetooth (software serial: D7->RX, D6-> TX)

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

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Бредовая идея.
У ds1307 нет термокомпенсации.
У каждой из микросхем будет свой уход в ту или иную сторону, в зависимости от температуры.
Я на работе часы на ней собрал(там сильно точность не нужна).
Летом и зимой по минуте в неделю в одну сторону убегает, осенью/весной(когда уже холодно а отопления ещё нет) в другую.
Для DS3231 более актуально

SAB
Offline
Зарегистрирован: 27.12.2016

А NTP использовать не пробовали. А вы идете из Питера в Москву через Владивосток.

rkit
Offline
Зарегистрирован: 23.11.2016

Это всё решается покупкой нормального кварца за 15 рублей, дрейфующего максимум на 20 минут в год после всех температурных поправок.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

rkit пишет:

Это всё решается покупкой нормального кварца за 15 рублей, дрейфующего максимум на 20 минут в год после всех температурных поправок.


3231 проще и наверное даже дешевле, учитывая то, что не нуждается в дополнительных решениях.

step962
Offline
Зарегистрирован: 23.05.2011

SAB пишет:

А NTP использовать не пробовали. А вы идете из Питера в Москву через Владивосток.

NTP с ардуинки, оснащенной лишь блютусом? Научите.

Kakmyc
Offline
Зарегистрирован: 15.01.2018

Ну через uart время с устройства получить проблем нет никаких.
Чем будет отличатся та же процедура для Bluetooth ?

step962
Offline
Зарегистрирован: 23.05.2011

Kakmyc пишет:
Бредовая идея. У ds1307 нет термокомпенсации.

Пару раз в год - да даже пару раз в месяц - потратить десять секунд на соединение с МК, сравнение времени смартфона и МК и нажатие на кнопку коррекции хода в случае необходимости...

Цитата:
Для DS3231 более актуально

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

step962
Offline
Зарегистрирован: 23.05.2011

Kakmyc пишет:
Ну через uart время с устройства получить проблем нет никаких. Чем будет отличатся та же процедура для Bluetooth ?

Это эталонное устройство должно либо висеть на проводках, либо ручками подключаться к ним. А смартфон лежит в кармане. Мимо контролируемого устройства прохожу, запускаю приложение, вижу, что часы тикают с допустимым разбегом - успокаиваюсь. Нет - давлю на кнопку, одновременно и устанавливая на МК и точное время и актуальную для сезона коррекцию хода.

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

А зачем на нано точное время? 

Если она никуда не подключается, то и абсолютная точность не нужна.

Если это часы, и нужно более точное время - проще на ESP8266 сделать, цена на том же уровне, но и Wi-Fi уже есть, можно и время получать и данными обмениваться.

step962
Offline
Зарегистрирован: 23.05.2011

SergeiL пишет:

А зачем на нано точное время?

Точное - незачем. А относительно точное, с уходом 1-2 секунды в сутки вместо 30-40, вполне может оказаться полезным. Задачи перед людьми стоят разные, методы их решения тоже весьма разнообразны.

Цитата:

Если это часы, и нужно более точное время - проще на ESP8266 сделать, цена на том же уровне, но и Wi-Fi уже есть, можно и время получать и данными обмениваться.

А если это не часы? Если это что-то типа кухонного/дачного/еще какого таймера с привязкой к календарю и каким-то внешним событиям, происходящим в определенное время? Как сказано выше,люди разные, задачи перед ними стоят непохожие, а та же пресловутая DS1307 до сих пор продается наряду с более точной DS3231...

Цель обсуждаемого приложения не создание турбийона из часов с кукушкой, а автоматизация процесса корректировки хода, приведение погрешности из области неприличных значений к удобоваримым. Переход от +/- 30 сек в сутки к 1-2. Или на базе года - от 3-4 часов накопившейся погрешности к 5-6 минутам.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

SAB пишет:

А вы идете из Питера в Москву через Владивосток.

Запятая тут решает ))

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Так время же относительно :-), (цитата)

Просто ЦНХ с DS1307 без стабильного кварца и термостабилизации, тема так себе.

Тогда еще и коррекцию ЦНХ по температуре нужно закладывать.

Вот и возникает вопрос, может более точный DS3231, или NTP  в ESP? Это же даже дешевле чем нано и BT получится.

Я  Цифровую Настройку Хода (ЦНХ) делал под Win на компе в 90-ых, это было сделано, если GPS будет недоступен, (плата GPS тогда ставилась в комп (редкая новинка была) для получения точного времени, точность была нужна до секунды, координаты были не нужны, объект был под землей),  а точность обеспечить было нужно. Ну сделал ЦНХ, ну месяцами ходили более менее точно, время по модему рассылали на несколько потребителей.

А сейчас дома часы с ESP8266 и синхронизацией по NTP в каждой комнате. В датчиках тоже ESP, как же без mqtt?

Время там незачем, время на сервере.

Нет, есть еще старые самопальные термостаты, в них есть время, можно посмотреть время минимумов и максимумов температуры за последние сутки. Локально на ЖКИ. Но они подключены к мастеру по RS485, и он обновляет в них время из NTP.

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Вот скрин из моей проги из 90-ых:

 

 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Не видно что-то скрина.(

SergeiL
SergeiL аватар
Offline
Зарегистрирован: 05.11.2018

Да замучился загружать..., супер недружественный интерфейс...

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Блин, увидел скрин и чуть слезу не пустил. Ностальжи. Эх, было время и мы были молодыми....

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

А в чем проблема? 

Я до сих пор такие интерфейсы делаю.