RTC DS1307 auto aggionamento ora

ciao a tutti, volevo chiedervi se c'è la possibilità di far aggiornare l'ora dell'rtc in oggetto tramite internet o altro.

la scheda verrà chiusa in una scatola di derivazione quindi mi verrà troppo scomodo andare ad aggiornare l'ora al cambiamento dell'ora solare/legale. A meno che non lo faccia già da solo ma non ho trovato nulla a riguardo googlando.

Ovviamente la scheda sarà connessa ad internet con la ethernet shield.

Grazie in anticipo, ciao!

Certo, lo puoi sincronizza da una qualunque fonte: Server NTP, Segnale GPS o DCF77. Naturalmente per ognuna di queste dovrai avere lo shield adatto.

EDIT: Per il server NTP c'è uno sketch di esempio nell'IDE. Devi solo convertire il timestamp del server in data e ora. Trovi una funzione simile all'interno della libreria swRTC di Leo. (http://arduino.cc/forum/index.php/topic,73496.0.html)

ciao, io ho l'ethernet shiled originale...quindi deduco che mi servirà il server ntp. Sai consigliarmi qualche codice?

x iscrizione, devo fare la stessa cosa.
Proprio oggi in metro guardavo lo sketch di esempio per l’NTP nell’ide.

Ciao!

Se nell'ide di arduino va su examples-ethernet-udpntpclient trovi uno sketch bello e pronto... come timeserver potete usare in alternativa l'italiano IEN: http://www.inrim.it/ntp/index_i.shtml

ma se prendo l'ora dal server ntp poi l'arduino me la tiene tipo per 1 giorno? o serve sempre l'rtc? non ho capito questa cosa scusate :)

La precisione dell'Arduino non è molto elevata. L'accuratezza dipende dalle tolleranze del risonatore ceramico. C'è poi il problema che se ti salta l'alimentazione, ti salta anche l'ora.

fracrema: ma se prendo l'ora dal server ntp poi l'arduino me la tiene tipo per 1 giorno? o serve sempre l'rtc? non ho capito questa cosa scusate :)

No non serve rtc, la libreria swRTC è stata pensata per chi non possiede l'rtc, altrimenti non servirebbe. Tale libreria utilizza i timer interrupt del controller, è in grado di tenere un orario con uno scarto di pochi secondi al giorno, la connessione all'NTP serve solo per correggere l'errore e ripartire, non fare troppe richieste al server ntp in poco tempo o rischi il ban dell'ip.

ciao

fracrema: ma se prendo l'ora dal server ntp poi l'arduino me la tiene tipo per 1 giorno? o serve sempre l'rtc? non ho capito questa cosa scusate :)

Come ti hanno detto, se l'arduino si spegne perde l'ora. L'RTC con la sua batteria conserva l'orario anche se l'arduino si spegne. Sia l'arduino sia l'RTC perdono secondi strada facendo... sul lungo periodo è quindi meglio risincronizzarli.

La soluzione "tecnicamente migliore" a parer mio è sincronizzare l'RTC con l'NTP ogni tot... così hai un indicatore di tempo preciso che resta anche se l'arduino si resetta.

Luca proprio oggi stavo guardando quell'esempio, sembra abbastanza semplice, ora devo capire come settare l'RTC usando la risposta del server. Ma in sti gg sono fusa per altre questioni :)

Grazie mille per le risposte!! Non mi serve sta gran precisione, anzi, mi basta che riconosca la mattina dalla sera :grin:

Vi chiedevo se faceva le veci di un rtc perchè utilizzerò un display i2c 20x4 e se riuscivo a stamparci data e ora lo riempivo un po' di più.

Grazie mille, provo un po' e poi caso mai ritorno eheh! Buona giornata!

Questo è il mio sketch per sincronizzare la swRTC:

#include <SPI.h>         
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <swRTC.h>

// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {  
  0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA };

unsigned int localPort = 8888;      // local port to listen for UDP packets

IPAddress timeServer(192, 43, 244, 18); // time.nist.gov NTP server 192, 43, 244, 18

const int NTP_PACKET_SIZE= 48; // NTP time stamp is in the first 48 bytes of the message

byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets 

long interval = 3600000;    // interval to control NTP Server
unsigned long previousMillis = 0;        // will store last time LED was updated

// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
swRTC rtc;

void setup() 
{
  delay(2000);
  unsigned long ts = 0;
  Serial.begin(9600);
  Serial.print("Serial Start! at ");
  Serial.print(millis());
  Serial.println(" microsecond.");
  AvviaEthernet();
  Serial.println("Ethernet started.");

  while (ts==0)
  {
    Serial.println("Check Server NTP...");
    ts=GetNTPtime();
    if (ts==0) {
      Serial.println("Wait 5 seconds..."); 
      delay(5000);
    }   
  }
  rtc.stopRTC();
  rtc.setClockWithTimestamp(ts, 1970);
  rtc.startRTC();  
  Serial.println("swRTC synchronized."); 
}

void loop()
{
  int deltaT = 0;
  unsigned long currentMillis = millis();
  stampa();
  delay(10000); 
  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;   
    // Controlla il server NTP
    deltaT = GetNTPtime() - rtc.getTimestamp();
    Serial.print("DeltaT : "); 
    Serial.println(deltaT);
  } 
  // else{
  //   Serial.println("Millis: "); 
  //   Serial.println(currentMillis); 
  //   Serial.println(previousMillis); 
  //   Serial.println(interval);
  //   Serial.println(" ");  
  // }

}

void AvviaEthernet()
{ 
  // start Ethernet and UDP
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP.");
    // no point in carrying on, so do nothing forevermore:
    for(;;)
      ;
  }
  else
  {
    Serial.println("Ethernet configured using DHCP.");
    Serial.print("Local IP: ");
    Serial.println(Ethernet.localIP());
    Serial.print("SubnetMask: ");
    Serial.println(Ethernet.subnetMask());
    Serial.print("Gateway: ");
    Serial.println(Ethernet.gatewayIP()); 
    Serial.print("DNS Server: ");
    Serial.println(Ethernet.dnsServerIP());
  }
  Udp.begin(localPort);
}

// send an NTP request to the time server at the given address 
unsigned long sendNTPpacket(IPAddress& address)
{
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE); 
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49; 
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp: 		   
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket(); 
}

unsigned long GetNTPtime(){
  sendNTPpacket(timeServer); // send an NTP packet to a time server
  // wait to see if a reply is available
  delay(1000);  
  if ( Udp.parsePacket() ) {  
    // We've received a packet, read the data from it
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer
    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:
    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);  
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;  
    Serial.print("Seconds since Jan 1 1900 = " );
    Serial.println(secsSince1900);               
    // now convert NTP time into everyday time:
    Serial.print("Unix time = ");
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;     
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears;  
    // print Unix time:
    Serial.println(epoch);                               
    // print the hour, minute and second:
    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
    Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
    Serial.print(':');  
    if ( ((epoch % 3600) / 60) < 10 ) {
      // In the first 10 minutes of each hour, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
    Serial.print(':'); 
    if ( (epoch % 60) < 10 ) {
      // In the first 10 seconds of each minute, we'll want a leading '0'
      Serial.print('0');
    }
    Serial.println(epoch %60); // print the second
    return epoch;
  } 
  else { 
    Serial.println("NTP Server unavailable.");
    return 0;
  }
}

void stampa() {
  Serial.print(rtc.getHours(), DEC);
  Serial.print(":");
  Serial.print(rtc.getMinutes(), DEC);
  Serial.print(":");
  Serial.print(rtc.getSeconds(), DEC);
  Serial.print("  ");
  Serial.print(rtc.getDay(), DEC);
  Serial.print("/");
  Serial.print(rtc.getMonth(), DEC);
  Serial.print("/");
  Serial.println(rtc.getYear(), DEC);
  Serial.println(rtc.getTimestamp(), DEC);
}

c’è qualche Serial.print di troppo e il codice non è molto commentato ed è basato sull’esempio dell’IDE 1.0 di Arduino.
Non avendo un RTC hardware non l’ho sviluppato in tal senso. (Mi dovrebbe arrivare a breve insieme al ricevitore DCF77)

Ciao.
Paolo.

EDIT: Avendo spazio puoi implementare anche la risoluzione del nome DNS del Server NTp in modo sia sempre disponibile, ad esempio utilizzando it.pool.ntp.org che ha disponibili circa 26 server.
Se ne parla in un altro topic: http://arduino.cc/forum/index.php/topic,92553.0.html

grazie mille!!! proverò un po' tutto e vi farò sapere!!!

Non ho aperto io questo topic ma ne approfitto perché sto facendo la medesima cosa... Allora. Ho implementato il client NTP senza problemi. Devo ancora vedere quale server scegliere ma questo lo guardo poi. Quello che ora francamente non so è come settare l'RTC hardware!

Qualcuno sa qual'è l'equivalente di queste tre righe:

  rtc.stopRTC();
  rtc.setClockWithTimestamp(ts, 1970);
  rtc.startRTC();

Per un rtc hardware gestito con la libreria "RTClib.h" di adafruit? Io di solito regolavo l'RTC tramite due righe di codice che settavano l'ora di compilazione dello sketch... non ho mai visto, ne trovo altrove, i comandi per settare dall'interno l'RTC.

Ogni aiuto sarà apprezzato :)

Allora,
ts è la variabile che contiene il Timestamp che nei server NTP è riferito al 1970.
Il codice della funzione è questo: (preso dalla libreria)

// set the internal clock using a timestamp using the epoch passed as argument
byte swRTC::setClockWithTimestamp(unsigned long timeT, int yearRef) {
	unsigned long dayT;
	
	timeT+=86400; //days in the calendar start from Jan., 1 not from Jan., 0
	dayT = timeT/(60L*60L*24L);
	float remaining = timeT-dayT*(60L*60L*24L);
	int yearT = (dayT / 365.2422);
	float dayRemaining = dayT-yearT*365.2422;
	
	if (yearRef==NULL) {
		yearRef=1970;
	} else if (yearRef<1900) {
		yearRef=1900;
	} else if (yearRef>1970) {
		yearRef=1970;
	} else if ((yearRef!=1900) && (yearRef!=1970)) {
		yearRef=1970;
	}
	
	yearT+=yearRef;
	if (dayRemaining>=365.2422)
		return 1;//my math is wrong!
	if (yearT<yearRef)
		return 2;//year not supported!
	int monthT=0;
	while (dayRemaining > daysPerMonth[monthT]){
		dayRemaining -= daysPerMonth[monthT];
		monthT++;
	}

	monthT++;//because month 0 doesn't exist
	if (monthT>12)
		return 3;//my math is wrong!
	if (dayRemaining>=(60L*60L*24L))
		return 4;//my math is wrong!
	dayT=dayRemaining;
	if (dayRemaining-dayT>0){ //add partial day!
		dayT++;
	}
	int hoursT = remaining/(60L*60L);
	remaining = remaining-hoursT*(60L*60L);
	if (remaining>=(60L*60L))
		return 5;//my math is wrong!
	int minutesT = remaining/(60L);
	remaining = remaining-minutesT*(60L);
	if (remaining>=60)
		return 6;//my math is wrong!

	year=yearT;
	month = monthT;
	day = dayT;
	hours = hoursT;
	minutes = minutesT;
	seconds = remaining;
	return 0;
}

L’ultima parte del codice è quella che devi modificare:

	year=yearT;
	month = monthT;
	day = dayT;
	hours = hoursT;
	minutes = minutesT;
	seconds = remaining;

Al posto delle variabili devi inserire il codice per settare l’RTC

Non so se gli RTC hw hanno anche il giorno della settimana che puoi ottenere così:

//return the day of the week 
byte swRTC::weekDay(unsigned long timestamp) {
  return ((timestamp / 86400UL) + 4 ) % 7; 
}

Ricorda che l’hanno è fisso al 1970 quindi puoi rimuovere tutti gli if che lo riguardano.

Ciao.
Paolo.

Edit: ti serve anche questo:

volatile byte daysPerMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

Spe paolo, probabilmente sono stonata io e mi sono espressa male... Ho già il codice per estrarre unixtime, anno, mese, giorno, ora ecc... Quello l'ho preso pari pari anche io dall'esempio dell'IDE :)

PaoloP: L'ultima parte del codice è quella che devi modificare:

  year=yearT;
    month = monthT;
    day = dayT;
    hours = hoursT;
    minutes = minutesT;
    seconds = remaining;

Al posto delle variabili devi inserire il codice per settare l'RTC

E' proprio quello che mi manca! Non ho idea di quale sia il codice per settare l'RTC con la libreria RTClib.h di adafruit! :)

Bastava guardare la libreria ]:smiley:

DateTime::DateTime (uint32_t t) {
  t -= SECONDS_FROM_1970_TO_2000;    // bring to 2000 timestamp from 1970

    ss = t % 60;
    t /= 60;
    mm = t % 60;
    t /= 60;
    hh = t % 24;
    uint16_t days = t / 24;
    uint8_t leap;
    for (yOff = 0; ; ++yOff) {
        leap = yOff % 4 == 0;
        if (days < 365 + leap)
            break;
        days -= 365 + leap;
    }
    for (m = 1; ; ++m) {
        uint8_t daysPerMonth = pgm_read_byte(daysInMonth + m - 1);
        if (leap && m == 2)
            ++daysPerMonth;
        if (days < daysPerMonth)
            break;
        days -= daysPerMonth;
    }
    d = days + 1;
}

Non c’è una funzione per disabilitare l’RTC ma solo per impostare la data e l’ora.
Alla fine ti bastano 2 righe

ts=GetNTPtime();
rtc.DateTime(ts);

Ciao.
Paolo.

p.s. GetNPTtime è facilissimo da implementare.

PaoloP: Bastava guardare la libreria ]:D rtc.DateTime(ts);

Paolo, ti giuro, la guardo adesso e comunque non la capisco :D Ho il cervello spento... vado a nanna e la guardo meglio domani :)

Notte ragazzi! :***

Paolo quelle due righe non compilano... Alla fine credo che il codice corretto sia:

RTC.adjust(DateTime(secondidal1900));

Ma non ho modo di provarlo prima di stasera. Secondi dal 900 è la variabile dove immagazzino la risposta ntp.

La libreria a cui faccio riferimento io è questa: https://github.com/adafruit/RTClib Credo che il codice possa essere:

RTC.adjust(RTC.DateTime(secondidal1970));

La variabile deve essere secondi dal 1970 e non dal 1900 ;) Che poi la libreria trasforma in secondi dal 2000. :astonished: Perchè ho scoperto che anche gli RTC attuali trattano l'anno a 2 cifre. :cold_sweat: Ma il Millennium Bug non ha insegnato niente. ]:D Possibile che non ci sia un byte in più per segnare il secolo? Boh!! Misteri!!

Ciao, Paolo.

Gli RTC trattano gli anni da 00 a 99, con 1 bit per indicare il secolo, se 1900 o 2000. Quindi quando chiedi l'anno in realtà non ottieni "2012" ma "12". 1 byte dal punto di vista del risparmio di memoria è sicuramente meglio di 2 per tenere il conto degli anni, tanto si presume che un circuito non campi più di 100 ]:D

PS1: se la lib di adafruit non vi piace, perché non ne usate un'altra? Mica è l'unica in circolazione. :P