Часы на DS3231 с автоматической коррекцией по GPS

kraus

✩✩✩✩✩✩✩
20 Апр 2019
20
1
В интернете полно схем и проектов часов на DS3231.
Так же полно часов на GPS модулях.
Но часов на часах реального времени с автоматической коррекцией по GPS нет совсем.
Если вдруг найдёте, то подскажите где посмотреть. Весь инет перелопатил - ничего не нашёл.
Хотелось бы в машину такие поставить, а то постоянно приходится часы поправлять вручную. Убегают быстро.
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,799
579
44
Плохо искал, по "arduino gps time sync" выдает просто кучу ссылок на примеры.
Да и NMEA формат достаточно прост если вручную его парсить.

 

kraus

✩✩✩✩✩✩✩
20 Апр 2019
20
1
Вы правы - ссылок море..... только везде часы либо на модуле RTC, либо на GPS . Часы на RTC + GPS только здесь .
Глянул ихний скетч, но как изменить на обычную маленькую матрицу (матрица обычных автомобильных часов).......... не совсем понял.
C++:
#include <TimeLib.h>
#include <SPI.h>
#include <DS3232RTC.h>    //http://github.com/JChristensen/DS3232RTC
#include <MsTimer2.h>
#include <SoftwareSerial.h>
#include <TinyGPS.h>
#include <Bounce.h>
#include <OneWire.h>
#include <EEPROM.h>

#define defaultTimezone  3; // Сюда установить часовую зону по умолчанию.
                            // Часовую зону всегда можно оперативно откорректировать. Для вызова меню установки часовой зоны,
                            // часы надо включить при одновременно зажатых кнопках перевода Часов и Минут.
                            // Для выхода из режима установки можно просто выключить часы и включить их снова в штатном режие.
                            // Одновременное нажатие кнопок часов и минут также приводит к выходу из режима установки часовой зоны

float LATITUDE = 55.5, LONGITUDE = 37.5; // первоначальная позиция для Москвы, район аэропорта Остафьево.
int MaxTimeToSync=500;       // Синхронизация каждые 500 секунд
#define MaxScreenTimer 114   // С какой полусекунды каждой минуты будет отображаться температура. Значение 114 -> с 57-й секунды до конца минуты.

// Подключение часов реальноговремени
// DS3231  -> Arduino
// D (SDA) -> A4
// C (SCL) -> A5


#define HourPin 4   //  Это контакт кнопки перевода часов
#define MinutePin 8 //  Это контакт кнопки перевода минут

//
//  |----/.---- pin 4
//  |
//  +----/.---- pin 8
//  |
// Gnd

Bounce HourButt = Bounce(HourPin,40 );
Bounce MinuteButt = Bounce(MinutePin,40 );


#define DotPin 5     //  К этому пину подключены светодиоды в виде двоеточия между часами и минутами
byte DotNightBright = 9;  // Это значение яркости зажжённого двоеточия в ночном режиме.

//
//    pin 5 o------+-----+
//                 |,,   |,,
//                 V     V
//                 -     -
//                 |     |
//                | |   | |
//                |R|   |R| Значения R подбираются в зависимости от яркости светодиодов, от 100 ом до 6кОм.
//                | |   | |
//                 |     |
//                 |     |
//  Gnd o----------+-----+



enum { REG_SELECT = 10 }; // пин, управляющий защёлкой (SS в терминах SPI)

//        0
//     =======
//   ||       ||
// 5 ||       || 1
//   ||   6   ||
//     =======
//   ||       ||
// 4 ||       || 2
//   ||   3   ||
//     =======   o 7
//
// Module         Arduino
//   Vcc o----------- +5v
//   Gnd o----------- Gnd
//   Clk o----------- pin 13 SCK  (52)
//   Stb o----------- pin 10  SS  (53)
//  S_in o----------- pin 11 MOSI (51)
//

#define BrightnessPin 3
//          10k
//      --|  R |---*---------------*-- + 7.5 V
//     |           |               |             _______      Узел изменения яркости дисплея.
//     |     |-----  S            | |            \   o  \     Питание семисегментных матриц подаётся
// o---*----||--^       IRF9Z14   |=| 740         \  ____\__  на клеммные колодки DC8V на платах индикаторов
//pin3    G  |-----  D            | |              \/\      \
//                 |               |                \ \      \
//                 |               |                 \ \______\
//                  ---------------*--> +DC8v         \/______/
//                                                       \ \ \
//                  ------------------> -DC8V             G D S
//                 |
//                 -

TinyGPS gps;
SoftwareSerial ss(9, 7);  // К этим пинам подключается GPS-приёмник. Tx->9, Rx->7

// Module         Arduino
//   Vcc o----------- +5v
//    Rx o----------- 7
//    Tx o----------- 9
//   Gnd o----------- Gnd


#define LightSensorPin 2
// Module         Arduino           Сенсор освещённости
//   Vcc o----------- +5v
//   Gnd o----------- Gnd
//   D0 o----------- pin 2

#define DSdatapin 6  // К этому пину подключена линия данных датчика DS18B20
#define DSpowerpin 14 // С этого пина подаётся питание на датчик DS18B20

OneWire ds (DSdatapin);


int CurMins;

byte font[23] = {0b11000000, 0b11111001, 0b10100100, 0b10110000, 0b10011001, 0b10010010, 0b10000010, 0b11111000, 0b10000000, 0b10010000,    // 0 - 9
                 0b01000000, 0b01111001, 0b00100100, 0b00110000, 0b00011001, 0b00010010, 0b00000010, 0b01111000, 0b00000000, 0b00010000,    // 0. - 9.
                 0b10111111,0b10011100,0xff};  // -, °

char TIMEZONE = defaultTimezone;
byte Blinking = false;
byte TempPresent = false;
byte DSstatus,DSphase=0;
byte CRC;
byte tempdata[12];
unsigned int tempraw ;
float CurrentTemp, DSTemp;


byte DarkNight = 0;

int Synced;
int TimeToSync=0;

int t = 0;
int sc,mm,hh,dd,mn,ye;
byte bb,bo=0;
byte dot=true;


char GPSchar;
float flat, flon;
unsigned long age;
int GPSyear;
char GPSmonth, GPSday, GPShour, GPSminute, GPSsecond, GPShundredths;
unsigned long GPSage;
byte GPSpresent = false;
byte NeedToShowScreen = false;
byte ScreenPage=0;
int ScreenTimer;
unsigned long dotstart;

byte sm[9];

void TickEvery05s() // Эта процедура вызывается каждые полсекунды
{
dotstart= millis();
if (dot==true)
{if (Blinking ==true) {Blinking=false;analogWrite(DotPin,0);} else  {Blinking=true; if (DarkNight==1) analogWrite(DotPin,DotNightBright); else analogWrite(DotPin,255);}} else analogWrite(DotPin,0);
if (ScreenTimer<120) ScreenTimer++; else ScreenTimer=0;

NeedToShowScreen = true;

if (DSphase==0) {digitalWrite(DSpowerpin, HIGH); } else
if (DSphase==1) {
   ds.reset();
   ds.write(0xCC);   
   ds.write(0x44);} else
if (DSphase==4) {
   DSstatus=ds.reset();
   ds.write(0xCC);     
   ds.write(0xBE);
   CRC=0;
   for (byte i = 0; i < 9; i++) tempdata[i] = ds.read ();
   digitalWrite(DSpowerpin, LOW);} else
if (DSphase==5) {CRC=ds.crc8(tempdata,9);} else
if (DSphase==6) {
   tempraw =  (tempdata[1] << 8) | tempdata[0];  // Пересчитываем в температуру
   int signBit = tempraw & 0x8000;               // Проверяем самый левый бит: 0x8000= 0b10000000 00000000
    if (signBit)                                 // Если там единица - число отрицательное и его надо преобразовать
    {                                            // Стандартное преобразование отрицательного числа, которое в микроконтроллере в дополнительной кодировке
        tempraw = (tempraw ^ 0xffff) + 1;        // Путем исключающего ИЛИ плюс единица
    }
   float celsius =  (float)tempraw / 16.0;
   if (signBit) celsius=-celsius;                // Если отрицательное число
   CurrentTemp=celsius;
   if (CRC==0) TempPresent=true; else TempPresent=false;
   if (DSstatus==0) TempPresent=false;
}

DSphase++;
if (DSphase>20) DSphase=0;

}


int encodeDigit(char c)
{
  int m;
 
    if (c=='-') m=20;  else
    if ((c>='0') and (c<='9')) m=c-48; else
    m=22;
    return m;
}


void Screen()
{
String b;
char s[16];

if (TempPresent==true)
{
b = dtostrf(CurrentTemp, 3, 0, s);
if (b==" -0") b="  0";
if (b!="  0") { if (b.charAt(1)==' ') b.setCharAt(1,' '); else if ((b.charAt(0)==' ') && (b.charAt(1)!='-')) b.setCharAt(0,' ');}
} else
{
  b="---";
  ScreenPage=0;
  }

switch(ScreenPage) {
case 1: {
  dot=false;
  analogWrite(DotPin,0);
  sm[0]=font[encodeDigit(b.charAt(0))];
  sm[1]=font[encodeDigit(b.charAt(1))];
  sm[2]=font[encodeDigit(b.charAt(2))];
  sm[3]=font[21];
  break;}
case 0:{
  dot=true;
  sm[3]=font[(byte)minute()%10];
  sm[2]=font[(byte)minute()/10];
  sm[1]=font[(byte)hour()%10];
  sm[0]=font[(byte)hour()/10];
  break;}}

digitalWrite (BrightnessPin, DarkNight); 
digitalWrite(REG_SELECT, LOW);
SPI.transfer(sm[3]);
SPI.transfer(sm[2]);
SPI.transfer(sm[1]);
SPI.transfer(sm[0]);
digitalWrite(REG_SELECT, HIGH);

NeedToShowScreen = false;
}

void ScreenTZ()
{
  dot=false;
  analogWrite(DotPin,0);
  sm[3]=font[(byte)abs(TIMEZONE)%10];
  sm[2]=font[(byte)abs(TIMEZONE)/10];
  if (TIMEZONE<0) sm[1]=font[20]; else sm[1]=font[22];
  sm[0]=font[22];

digitalWrite(REG_SELECT, LOW);
SPI.transfer(sm[3]);
SPI.transfer(sm[2]);
SPI.transfer(sm[1]);
SPI.transfer(sm[0]);
digitalWrite(REG_SELECT, HIGH);

}


void Synctime()
{
byte monthes [13] = {0,31,28,31,30,31,30,31,31,30,31,30,31};  // Сколько дней по месяцам в невисокосном году.

if (Synced>0) Synced=0 ;


gps.f_get_position(&flat, &flon, &GPSage);
if ((flat !=TinyGPS::GPS_INVALID_F_ANGLE) && (flon !=TinyGPS::GPS_INVALID_F_ANGLE)) {GPSpresent=true;} else GPSpresent=false;
gps.crack_datetime(&GPSyear, &GPSmonth, &GPSday, &GPShour, &GPSminute, &GPSsecond, &GPShundredths, &GPSage);
if ((GPSage!=TinyGPS::GPS_INVALID_AGE) && (GPSpresent==true) && ((GPShour+GPSminute)>6))
{
  GPShour=GPShour+TIMEZONE;
  if (GPShour>23) {  // Корректируем время с GPS под часовые зоны восточной долготы.
        GPShour=GPShour-24;
    if (((GPSyear % 4 == 0) && (GPSyear % 100 != 0)) || (GPSyear % 400 == 0)) {monthes[2]=29;} // Корректируем февраль, если год високосный
    GPSday++;
    if (GPSday>monthes[GPSmonth])
      {
        GPSday=1;
        GPSmonth++;       
        if (GPSmonth>12) {GPSmonth=1; GPSyear++;}               
      }
    }
    if (GPShour<0) {  // Корректируем время с GPS под часовые зоны западной долготы.
        GPShour=GPShour+24;
    GPSday--;
    if (GPSday<1)
      {     
        GPSmonth--;       
        if (GPSmonth<1) {GPSmonth=12; GPSyear--;}
        if (((GPSyear % 4 == 0) && (GPSyear % 100 != 0)) || (GPSyear % 400 == 0)) {monthes[2]=29;} // Корректируем февраль, если год високосный
        GPSday=monthes[GPSmonth];               
      }
    }
  if (flat == TinyGPS::GPS_INVALID_F_ANGLE) {flat=0.0;} else {flat=round(flat*100); flat=flat/100;}
  if (flon == TinyGPS::GPS_INVALID_F_ANGLE) {flon=0.0;} else {flon=round(flon*100); flon=flon/100;}
  if ((flat !=TinyGPS::GPS_INVALID_F_ANGLE) && (flon !=TinyGPS::GPS_INVALID_F_ANGLE))
  {
  LATITUDE = flat, LONGITUDE = flon;
  if (GPSage>1500) {;} else {
  setTime(GPShour, GPSminute, GPSsecond, GPSday, GPSmonth, GPSyear);
  RTC.set(now());   
  Synced=2 ;
}
  }
}}



void setup()
{
  setSyncProvider(RTC.get);
  if(timeStatus() != timeSet) Synced=-1 ; else Synced=0 ;
  byte TZ=EEPROM.read(5);
  if (TZ>24) TIMEZONE=defaultTimezone else TIMEZONE=TZ-12;
  EEPROM.write(5, TIMEZONE+12);
  pinMode(DotPin, OUTPUT);  analogWrite(DotPin,0);
  pinMode(BrightnessPin, OUTPUT);  digitalWrite (BrightnessPin, 1); 
  pinMode(LightSensorPin, INPUT);
  SPI.begin();
  pinMode(REG_SELECT, OUTPUT);
  DarkNight=digitalRead(LightSensorPin);
  Screen();
 
  pinMode(HourPin, INPUT_PULLUP);
  pinMode(MinutePin, INPUT_PULLUP);
  pinMode(DSpowerpin, OUTPUT); digitalWrite(DSpowerpin, HIGH);
 
  CurMins=hour()*60+minute();
  DSphase=0;
  ScreenPage=0;
  ScreenTimer=0;
  NeedToShowScreen = true;
  CurrentTemp=85;
  TempPresent=false;

  MsTimer2::set(500, TickEvery05s); // 500ms period
  MsTimer2::start();
  ss.begin(9600);
  HourButt.update();MinuteButt.update();
  if ((HourButt.read()== LOW) && (MinuteButt.read()== LOW)) {
    ScreenTZ();
    while ((HourButt.read()== LOW) || (MinuteButt.read()== LOW)){ HourButt.update();MinuteButt.update();}
    byte ExiteMenu=false;
    while (ExiteMenu==false) {
    ScreenTZ();
    if (HourButt.  update()) {if (HourButt.  read()== LOW) {TIMEZONE++; if (TIMEZONE> 12) TIMEZONE= 12; EEPROM.write(5, TIMEZONE+12); ScreenTZ(); HourButt  .rebounce(500);}}
    if (MinuteButt.update()) {if (MinuteButt.read()== LOW) {TIMEZONE--; if (TIMEZONE<-12) TIMEZONE=-12; EEPROM.write(5, TIMEZONE+12); ScreenTZ(); MinuteButt.rebounce(500);}}
    if ((HourButt.read()== LOW) && (MinuteButt.read()== LOW)) ExiteMenu=true;
    delay(100);
    }
  }
}

time_t prevDisplay = 0;

void loop()
{
bool newData = false; 
  if (ss.available()){
char GPSchar = ss.read();
  if (gps.encode(GPSchar)) newData = true;}
if (newData==true)
  {
gps.f_get_position(&flat, &flon, &GPSage);
if ((flat !=TinyGPS::GPS_INVALID_F_ANGLE) && (flon !=TinyGPS::GPS_INVALID_F_ANGLE)) {GPSpresent=true; NeedToShowScreen=true;} else GPSpresent=false;

  }
if (TempPresent==true) {if (ScreenTimer>=MaxScreenTimer) {ScreenPage=1; dot=false; analogWrite(DotPin,0);} else {ScreenPage=0; dot=true;}} else {ScreenPage=0; dot=true;}

// Опрос кнопок
if (HourButt.  update()) {if (HourButt.  read()== LOW) {hh=hour();mm=minute();sc=second();dd=day();mn=month();ye=year(); hh++; if (hh==24) hh=0; setTime(hh, mm, sc, dd, mn, ye); RTC.set(now()); Synced=1; ScreenTimer=0; ScreenPage=0; Screen(); HourButt  .rebounce(500);}}
if (MinuteButt.update()) {if (MinuteButt.read()== LOW) {hh=hour();mm=minute();sc=second();dd=day();mn=month();ye=year(); mm++; if (mm==60) mm=0; setTime(hh, mm, sc, dd, mn, ye); RTC.set(now()); Synced=1; ScreenTimer=0; ScreenPage=0; Screen(); MinuteButt.rebounce(500);}}
 
if (now() != prevDisplay) {
      prevDisplay = now();
      CurMins=hour()*60+ minute();
      
      TimeToSync++;
      if (TimeToSync >= MaxTimeToSync) {
      Synctime();
      TimeToSync=0;}
      }
      
if ((TimeToSync%10==0) && (Synced < 1)) Synctime();  // Если время ещё не синхронизировано, то пытаемся его синхронизировать чаще.

DarkNight=digitalRead(LightSensorPin);
if (NeedToShowScreen == true) Screen();
if (Synced<0) if (millis()-dotstart>30) analogWrite(DotPin,0);
delay(1);

}
...
 

Геннадий П

★★★★★★✩
14 Апр 2021
1,799
579
44
Так а что мешает взять часы на RTC и дополнить их подключив GPS. Раз в допустим час берешь время с GPS и меняешь время в RTC, ну или каждую минуту если совсем-совсем точное время нужно.
 
  • Лойс +1
Реакции: kraus и bort707