Buongiorno, qualcuno mi sa dire se sull'RTC di arduino uno R4 è possibile settare solo un dato (esempio: solo ora o minuti)
Sfogliato la libreria RTC non ho trovato nulla
Grazie in anticipo
Pare di no. Devi per forza leggere il time corrente e poi cambiare solo quello che ti serve
RTCTime currentTime;
RTC.getTime(currentTime);
..
currentTime.tm_hour = 12; // cambio solo ora
...
RTC.setTime(currentTime);
QUI la documentazione relativa al RTC della UNO R4 ... tieni presente però che, se ti serve "precisione", è meglio usare una schedina esterna con un DS3231 piuttosto che l'RTC integrato difatti, come ho già scritto in altra discussione ... NON hanno previsto il cristallo esterno da 32'768 KHz e si sono invece affidati all'oscillatore interno "LOCO" che però, da prove effettuate, può sbagliare anche +/- 1 sec. al minuto
Entro un certo limite, è possibile aggiustare (sperimentalmente) la cosa in funzione della propria scheda ...
... libreria RTC della UNO R4, file RTC.cpp, linea 455, è la seguente:
.freq_compare_value_loco = 255,
... ecco aumentando o diminuendo leggermente quel valore si può correggere l'errore, ma, di certo, NON si arriva mai alla precisione di un oggetto con il quarzo o meglio di un oggetto come il DS3231.
Guglielmo
P.S.: Solo per puntualizzare, quel campo è un uint32_t: uint32_t freq_compare_value_loco;
che si trova definito dentro FspTransfer.h nel "core". Mi raccomando solo di fare piccole variazioni, qualche unità, non decine
Grazie mille per le risposte
Provata la risposta di nid69ita
ma non funziona,
Se non funziona stai sbagliando qualche cosa ... come detto
- studiati il testo relativo alla RTC che ti ho già indicato
- studiati i SORGENTI della libreria e gli esempi ad essa allegati
Guglielmo
Ovvero ? Non compila? Probabile. Dell'oggetto di classe RTCTime non c'e' accesso ai dati membro interni. Bisogna usare le sue funzioni (di quella classe)
setDayOfMonth(int day); /* day from 1 to 31 */
setMonthOfYear(Month m); /* month from 1 (January) to 12 (December) */
setYear(int year); /* the year 1989 or 2022 */
setHour(int hour); /* from 0 (midnight) to 23 */
setMinute(int minute); /* from 0 to 59 */
setSecond(int second); /* from 0 to 59 */
Prova cosi (a me funziona):
RTCTime currentTime;
RTC.getTime(currentTime);
..
currentTime.setHour(12); // cambio solo ora
...
RTC.setTime(currentTime);
Esempio completo che mi funziona:
#include "RTC.h"
void setup() {
Serial.begin(9600);
while (!Serial) { }
RTC.begin();
RTCTime mytime(25, Month::DECEMBER, 2023, 11, 22, 33, DayOfWeek::THURSDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setTime(mytime);
RTCTime currentTime;
RTC.getTime(currentTime);
currentTime.setHour(15); // cambio solo ora
RTC.setTime(currentTime);
}
void loop() {
RTCTime currenttime;
RTC.getTime(currenttime);
Serial.print("Current time: ");
Serial.print(currenttime.getDayOfMonth()); Serial.print("/"); Serial.print(Month2int(currenttime.getMonth())); Serial.print("/"); Serial.print(currenttime.getYear());
Serial.print(" - ");
Serial.print(currenttime.getHour()); Serial.print(":"); Serial.print(currenttime.getMinutes()); Serial.print(":"); Serial.println(currenttime.getSeconds());
delay(1000);
}
Non ho ancora preso nessuna R4, ma effettivamente questa scelta mi pare abbastanza stupida da parte dei progettisti, non capisco perché... Non è che usare un quarzo sia una cosa costosa né difficile, e viste le novità introdotte nella R4 potevano anche farlo, per cui non capisco...
Mah ... scelte (sbagliate) di progetto ... vai a capire ... ancora peggio poi sulla R4 WiFi dove hanno pure previsto due pin per attaccarci la batteria che alimenta tale RTC interno quando la scheda non è alimentata e che quindi sarebbe comodissimo ... invece hanno lasciato i due pin P214 e P215 (XCOUT e XCIN) non collegati
Guglielmo
Grazie mille, funziona
Riprendo questa discussione per segnalare che, da prove fatte, quella di correggere il parametro .freq_compare_value_loco = 255,
NON è una strada percorribile. Anche piccole variazioni unitarie di tale parametro, provocano grossi scompensi a livello errore RTC.
Peccato, era una possibilità abbastanza semplice da applicare, ma NO, non va bene.
Guglielmo
Ciao anch`io ho lo stesso problema sono andato alla ricerca di una soluzione
- Come prima cosa ho cercato di capire se il clock interno LOCO 32,768KHz fosse preciso ma non accurato in modo da fare una calibrazione(qui sotto quello che intendo)
Come prima cosa ho cercato nel datasheet delle informazioni e ho trovato solo quelle relative alla fLOCO...
R01DS0355EJ0110 fLOCO min=27.8528KHz: max=32.6832KHz
Mi sono detto terrano in considerazione tutto il range di temperatura, ma per le mie applicazioni sarano per uso interno da 18°C a 35°C, poi un'altro fattore da tenere in considerazione è la tensione a cui è alimentato... ma per questo nessun problema in quando il modulo RTC è alimentato da un LDO interno 3V3 che alimenta il pin 4 VBAT.
Come primo approccio ho cercato di valutare la precisione confrontando l'ora del PC con l 'ora di Arduino R4.
Ho fatto un codice che ogni 6 minuti stampa l'ora usando la funzione della libreria RTC.hRTC.getTime(currentTime)
e confrontando con il timestamp della seriale(...ho avuto parecchi problemi con il copia e incolla della Seriale IDE 2.2.1 di Arduino perchè copia solo parte del log selezionato, sono passato al timestamp utilizzando Realterm). Estratto i dati dei log effettuati in 2 giorni diversi per 12 ore ho ottenuto un buon risultato:
Dalle mie misure ottengo che il mio LOCO guadagna +0.018815s al secondo quindi 1.018815s corrisponde a 1.846753336% che su 32,768KHz corrisponde a 33,373KHz
- A questo punto ho pensato ad un algoritmo di compensazione. L'algoritmo a cui ho pensato compensa ogni >=540s + i secondi necessari per arrivare al trentesimo secondo in modo da non incappare a muovere correggere anche minuti,ore ecc... Il parametro che passo per compensare è la variazione al minuto nel mio caso 1.12s/min
rtc_secondAtminute
#include "RTC.h"
volatile bool irqFlag = false;
//Variabili per funzione RTC_compensation()
RTCTime currentTime;
int lastSecond = -1;
float rtc_secondAtminute = 1.12;// +1,12s/min
float rtc_min_com = 0.0;
float rtc_min_com_mod_memory = 0.0;
float rtc_min_com_mod_current = 0.0;
int rtccomp_counter =0;
bool rtccomp_counter_stop = false;
int memSecond = -1 ;
/*--- Prototipi di funzioni ---*/
void RTC_compensation();
/*---FINE Prototipi di funzioni---*/
void setup() {
delay(1000);
Serial.begin(9600);
/*##### Inizialized RTC Time. #####*/
RTC.begin();
RTCTime mytime(30, Month::JUNE, 2023, 11, 54, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setTime(mytime);
if (!RTC.setPeriodicCallback(periodicCallback, Period::ONCE_EVERY_1_SEC)) {
Serial.println("ERROR: periodic callback not set");
}
Serial.print("Start");
}
void loop(){
if(irqFlag){
//Inserire qui il codice eseguito ogni secondo
RTC.getTime(currentTime);
RTC_compensation(rtc_secondAtminute,memSecond);
irqFlag = false;
}
}
void periodicCallback()
{
irqFlag = true;
}
/***********************************************
* Time compensation s/min
***********************************************/
void RTC_compensation(float rtc_secondAtminute,int rtc_secondi){
/*
* "float rtc_secondAtminute" = s/min da compensare.
* "rtc_secondi" = volore secondi attuale.
*
* Calcolo il tempo da compensare ogni secondo esempio:
* +1,12s/min -> +0.018666667 s/s
* Impongo che voglio compensare ogni 540s(9:00) ma
* voglio che il tempo venga cambiato al 30s. Mi calcolo
* quanti secondi occorrono per arrivare al 30esimo secondo
* dopo 9min esempio sono le 00:10:00 sommo 9 min = 00:19:00
* quindi per arrivare al 30s occorrono 9min + 30sec
* Calcolo il tempo da compensare es:
* (0.018666667*570)= 10.64s compenso 10 secondi e
* conservo 0.64s a quale li sommo alla prossima compensazione.
* 00:19:20 + 9min 00:28:20 per arrivare al 30esimo occorrono 10
* per un totale di 9:10 = 550s (0.018666667*550)= 10.26666685
* al quale sommo 0.64s ->10.90666685s compenso 10 e 0.9066 li
* aggiungo alla prossima compensazione.
* Inserire questa funzione in una routine richiamata ogni
* secondo.
*/
//Contatore secondi
rtccomp_counter ++;
//Aspetto ad azzerare il contatore per arricare al 30esimo sec.
if(rtccomp_counter >= 540 && rtc_secondi == 30 ){
rtccomp_counter_stop = true; Serial.println("stop");
RTCTime currentTime;
RTC.getTime(currentTime);
Serial.print("RTC.getTime=");Serial.println(currentTime);
}
/* Se il contatore arrica a 540s + il tempo necessario per arrivare
* al 30esimo secondo eseguo quanto sotto.*/
if(rtccomp_counter_stop){
rtccomp_counter_stop = false;
Serial.println("rtccomp_counter");Serial.println(rtccomp_counter);
//Conversione offset-secondi al minuto in offset-secondi al secondo * rtccomp_counter
float rtc_secondoTosecond = ((rtc_secondAtminute / 60)*rtccomp_counter);
Serial.print("rtc_secondoTosecond=");Serial.println(rtc_secondoTosecond);
//Cambio il segno.
int rtc_sign=-1;
//Aggiungo il resto precedendete al tempo di compensazione
rtc_min_com = rtc_secondoTosecond + rtc_min_com_mod_memory ;
Serial.print("rtc_min_com=");Serial.println(rtc_min_com);
//Azzero la parte decimale dell`offest
rtc_min_com_mod_memory = 0.0;
//Estraggo la parte intera dell`offest es: 10.64s diventa 10s
int rtc_min_com_int=(int)rtc_min_com;
//Estraggo l`offest decimale attuale es: 0.64s
rtc_min_com_mod_current = rtc_min_com - rtc_min_com_int ;
Serial.print("rtc_min_com_mod_current=");Serial.println(rtc_min_com_mod_current);
//Sommo l`offeset decimale attuale con l`eventale parte decimale es 0.64s+0.64s=1.28s
rtc_min_com_mod_memory = rtc_min_com_mod_memory + rtc_min_com_mod_current;
Serial.print("rtc_min_com_mod_memory=");Serial.println(rtc_min_com_mod_memory);
//Se l'offset supera il secondo correggo il tempo
if(rtc_min_com_int >= 1 || rtc_min_com_int <= -1 ){
Serial.print("rtc_min_com_int=");Serial.println(rtc_min_com_int);
RTCTime currentTime;
RTC.getTime(currentTime);
Serial.print("RTC.getTime=");Serial.println(currentTime);
memSecond=((rtc_min_com_int*(rtc_sign))+30); // Corregge il tempo nella routine 1s
currentTime.setSecond(((rtc_min_com_int*(rtc_sign))+30));
RTC.setTime(currentTime);
Serial.print("RTC.setTime=");Serial.println(currentTime);
}
//Azzera contatore
rtccomp_counter = 0;
}
}
Queste sono le stampe di debug:
19:18:10.938 -> Start
19:27:30.565 -> stop
19:27:30.565 -> RTC.getTime=2023-06-30T13:46:30
19:27:30.565 -> rtccomp_counter
19:27:30.565 -> 570
19:27:30.565 -> rtc_secondoTosecond=10.64
19:27:30.565 -> rtc_min_com=10.64
19:27:30.565 -> rtc_min_com_mod_current=0.64
19:27:30.565 -> rtc_min_com_mod_memory=0.64
19:27:30.565 -> rtc_min_com_int=10
19:27:30.565 -> RTC.getTime=2023-06-30T13:46:30
19:27:30.565 -> RTC.setTime=2023-06-30T13:46:20
19:36:30.608 -> stop
19:36:30.608 -> RTC.getTime=2023-06-30T13:55:30
19:36:30.608 -> rtccomp_counter
19:36:30.608 -> 550
19:36:30.608 -> rtc_secondoTosecond=10.27
19:36:30.608 -> rtc_min_com=10.91
19:36:30.608 -> rtc_min_com_mod_current=0.91
19:36:30.608 -> rtc_min_com_mod_memory=0.91
19:36:30.608 -> rtc_min_com_int=10
19:36:30.608 -> RTC.getTime=2023-06-30T13:55:30
19:36:30.608 -> RTC.setTime=2023-06-30T13:55:20
19:45:18.594 -> stop
19:45:18.594 -> RTC.getTime=2023-06-30T14:04:30
19:45:18.594 -> rtccomp_counter
19:45:18.594 -> 550
19:45:18.594 -> rtc_secondoTosecond=10.27
19:45:18.594 -> rtc_min_com=11.17
19:45:18.594 -> rtc_min_com_mod_current=0.17
19:45:18.594 -> rtc_min_com_mod_memory=0.17
19:45:18.594 -> rtc_min_com_int=11
19:45:18.594 -> RTC.getTime=2023-06-30T14:04:30
19:45:18.594 -> RTC.setTime=2023-06-30T14:04:19
19:54:19.753 -> stop
19:54:19.753 -> RTC.getTime=2023-06-30T14:13:30
19:54:19.753 -> rtccomp_counter
19:54:19.753 -> 551
19:54:19.753 -> rtc_secondoTosecond=10.29
19:54:19.753 -> rtc_min_com=10.46
19:54:19.753 -> rtc_min_com_mod_current=0.46
19:54:19.753 -> rtc_min_com_mod_memory=0.46
19:54:19.753 -> rtc_min_com_int=10
19:54:19.753 -> RTC.getTime=2023-06-30T14:13:30
19:54:19.753 -> RTC.setTime=2023-06-30T14:13:20
20:03:19.970 -> stop
20:03:19.970 -> RTC.getTime=2023-06-30T14:22:30
20:03:19.970 -> rtccomp_counter
20:03:19.970 -> 550
20:03:19.970 -> rtc_secondoTosecond=10.27
20:03:19.970 -> rtc_min_com=10.73
20:03:19.970 -> rtc_min_com_mod_current=0.73
20:03:19.970 -> rtc_min_com_mod_memory=0.73
20:03:19.970 -> rtc_min_com_int=10
20:03:19.970 -> RTC.getTime=2023-06-30T14:22:30
20:03:19.970 -> RTC.setTime=2023-06-30T14:22:20
20:12:20.240 -> stop
20:12:20.240 -> RTC.getTime=2023-06-30T14:31:30
20:12:20.240 -> rtccomp_counter
20:12:20.240 -> 550
20:12:20.240 -> rtc_secondoTosecond=10.27
20:12:20.240 -> rtc_min_com=10.99
20:12:20.240 -> rtc_min_com_mod_current=0.99
20:12:20.240 -> rtc_min_com_int=10
20:12:20.240 -> RTC.getTime=2023-06-30T14:31:30
20:12:20.240 -> RTC.setTime=2023-06-30T14:31:20
20:21:20.480 -> stop
20:21:20.480 -> RTC.getTime=2023-06-30T14:40:30
20:21:20.480 -> rtccomp_counter
20:21:20.480 -> 550
20:21:20.480 -> rtc_secondoTosecond=10.27
20:21:20.480 -> rtc_min_com=11.26
20:21:20.480 -> rtc_min_com_mod_current=0.26
20:21:20.480 -> rtc_min_com_mod_memory=0.26
20:21:20.480 -> rtc_min_com_int=11
20:21:20.480 -> RTC.getTime=2023-06-30T14:40:30
20:21:20.480 -> RTC.setTime=2023-06-30T14:40:19
20:30:21.709 -> stop
20:30:21.709 -> RTC.getTime=2023-06-30T14:49:30
20:30:21.709 -> rtccomp_counter
20:30:21.709 -> 551
20:30:21.709 -> rtc_secondoTosecond=10.29
20:30:21.709 -> rtc_min_com=10.54
20:30:21.709 -> rtc_min_com_mod_current=0.54
20:30:21.709 -> rtc_min_com_mod_memory=0.54
20:30:21.709 -> rtc_min_com_int=10
20:30:21.709 -> RTC.getTime=2023-06-30T14:49:30
20:30:21.709 -> RTC.setTime=2023-06-30T14:49:20
20:39:21.916 -> stop
20:39:21.916 -> RTC.getTime=2023-06-30T14:58:30
20:39:21.916 -> rtccomp_counter
20:39:21.916 -> 550
20:39:21.916 -> rtc_secondoTosecond=10.27
20:39:21.916 -> rtc_min_com=10.81
20:39:21.916 -> rtc_min_com_mod_current=0.81
20:39:21.916 -> rtc_min_com_mod_memory=0.81
20:39:21.916 -> rtc_min_com_int=10
20:39:21.916 -> RTC.getTime=2023-06-30T14:58:30
20:39:21.916 -> RTC.setTime=2023-06-30T14:58:20
20:48:22.147 -> stop
20:48:22.147 -> RTC.getTime=2023-06-30T15:07:30
20:48:22.147 -> rtccomp_counter
20:48:22.147 -> 550
20:48:22.147 -> rtc_secondoTosecond=10.27
20:48:22.147 -> rtc_min_com=11.08
20:48:22.147 -> rtc_min_com_mod_current=0.08
20:48:22.147 -> rtc_min_com_mod_memory=0.08
20:48:22.147 -> rtc_min_com_int=11
20:48:22.147 -> RTC.getTime=2023-06-30T15:07:30
20:48:22.147 -> RTC.setTime=2023-06-30T15:07:19
20:57:23.334 -> stop
20:57:23.334 -> RTC.getTime=2023-06-30T15:16:30
20:57:23.334 -> rtccomp_counter
20:57:23.334 -> 551
20:57:23.334 -> rtc_secondoTosecond=10.29
20:57:23.334 -> rtc_min_com=10.36
20:57:23.334 -> rtc_min_com_mod_current=0.36
20:57:23.334 -> rtc_min_com_mod_memory=0.36
20:57:23.334 -> rtc_min_com_int=10
20:57:23.334 -> RTC.getTime=2023-06-30T15:16:30
20:57:23.334 -> RTC.setTime=2023-06-30T15:16:20
21:06:23.429 -> stop
21:06:23.429 -> RTC.getTime=2023-06-30T15:25:30
21:06:23.429 -> rtccomp_counter
21:06:23.429 -> 550
- Come seconda soluzione volevo provare a modificare questo parametro da voi citato
.freq_compare_value_loco = 255
richiamando l'API ma non sono riuscito senza modificare la libreria soprattutto a mio parere perchè è unaconst
.
A questo punto guardando il "Renesas RA4M1 Group User’s Manual: Hardware" noto che il registro PRTC.RFRL dovrebbe corrispondere
Quindi io ho misurato la frequenza LOCO corrispondete a 33,373KHz mi calcolo il valore del registro e ottengo:
RFC[15:0] = (33,373KHz) / 128 - 1 = 259,7265 approssimando a 260 ( 0x104).
Qui sotto vi mostro come intervenire su i registri attenzione non consiglio questo approccio ma in quanto scrivere nel posto sbagliato potrebbe rompere la board.
#include "RTC.h"
void setup() {
delay(1000);
Serial.begin(9600);
RTC.begin();
RTCTime mytime(30, Month::JUNE, 2023, 11, 54, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setTime(mytime);
//PRTC.RFRL 4004 402Ch
uint16_t *pPRTC_RFRL = (uint16_t*)0x4004402C;
*pPRTC_RFRL =0x104;
}
void loop(){
}
Dalle misure successive vedo che con tutti e due le soluzioni l`orologio è nel minuto in un giorno.
Concludo dicendo che potrebbero inserire nella libreria RTC.h la compensazione come funzione(senza agire nei registri) e nella prossima revisione hardware lasciare la possibilità di mettere un cristallo esterno (aggiungendo anche la funzione in RTC.h).
PS: per sapere la frequenza LOCO si potrebbe usare anche un pin digitale comandato da ISR RTC.setPeriodicCallback(periodicCallback, Period::ONCE_EVERY_256_SEC)
e misurare la frequenza con un frequenzimetro(ho provato ma servirebbe acquisire tanti campioni per farne una media affidabile), ci sarebbe anche la possibilità di portare direttamente fuori il contatore ma il pin RTCOUT è usato dalla USB.
Tutte prove fatte e rifatte assieme ad altre ... la risposta è sempre la stessa, INUTILIZZABILE, usare un DS3231 esterno con la sua batteria.
E comunque, anche con un quarzo esterno, ho idea che l'RTC del RA4M1 lasci comunque a desiderare (se comparato con i DS3231), inoltre sia il disegno della scheda che quello del "core" ha dei problemi con l'RTC.
Ho abbandonato l'idea di usarlo (salvo non rimetterlo ogni N ore usando NTP).
Guglielmo
Per inciso, su GitHub c'è anche un'interessante "issue" aperta ...
After each pcb RST (POR or not), the RTC is resetted, because VBTCR1.BPWSWSTP bit is set to 1 after each pcb reset (renesas datasheet section 11.2.1).
I suspect Arduino settings to automatically set this bit at each pcb power restart. How to deactivate it so we can use the RTC function normally ?
We need this VBTCR1.BPWSWSTP bit to be setting at 0 when PCB powering on, otherwise, RTC function is unusable.
In pratica sembrerebbe (NON ho verificato) che il bit che abilita l'utilizzo di VBAT da parte del "core" NON sia in realtà messo al corretto valore e quindi ... quando si dovesse togliere l'alimentazione si perderebbe il contenuto del RTC
Guglielmo
P.S.: si parla di "datasheet", in verità quel registro è descritto si al punto 11.2.1 ma del "Renesas RA4M1 User’s Manual" e non del datasheet.
Per le mie esigenze mi accontento di avere una precisione nel minuto al giorno e di perdere il contenuto del RTC ad ogni reset/power up... ma seguiranno altre prove.
Credo che utilizzare il quarzo esterno non lascia a desiderare perchè venga fatta una divisione(2^15) di frequenza intera senza introdurre errore ad esempio 32,768KHz diviso 2^15 fa sempre 1Hz a meno che ci siano problemi con i divisori.
Volevo valutare la possibilità di alimentare il pin di VBAT collegando una batteria o un supercapacitor per mantenere i dati RTC, grazie prenderò in considerazione la discussione "issue "
A beh ... allora ti potevi accontentare anche di millis() ... che probabilmente è anche più precisa del RTC
Tanto hai sempre NTP per sistemare l'ora esatta ...
Guglielmo
Avendo una scheda Arduino UNO R4 Minima non posso . Confermo millis() è più preciso . Uso la libreira RTC.h perchè è più comoda per gestire il calendario ecc..
Vorrei vedere fino a che punto è migliorabile la RTC, chissà forse miglioreranno la parte RTC in una nuova revision.
Mah ... se pensi che per risparmiare? ... quanto? ... sulla MINIMA ci sono i pad ma NON c'è montato né il quarzo , né i condensatori ... e, sulla WiFi, hanno usato i due pin per il quarzo per pilotare la LED Matrix ...
Quindi, come la metti la metti, NON c'è quarzo sull'RTC e NON c'è quarzo nemmeno sul clock primario ... bah ...
Guglielmo
Proseguono le mie prove, in questi giorni ho voluto valutare la precisione del LOCO interno paraganandolo all'oscillatore interno(48MHz) da cui si ricava millis().
Questa volta ho scelto di usare per acquisire i tempi un modulo ethernet Wiznet W5500 da cui acquisivo l`ora NTP, una modulo SD,uno schermo OLED(SSD1306 opzionale). Vi lascio qui sotto il codice(non molto bello da vedere ma funzionale) con il quale acquisisco ogni circa 5min i vari orari LOCO,millis() e NTP.
#include <SPI.h>
#include <SD.h>
#include "RTC.h"
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
/*OLED Display Initialized i2c*/
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3c ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = {
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
};
unsigned int localPort = 8888; // local port to listen for UDP packets
const char timeServer[] = "ntp1.inrim.it"; // time.nist.gov NTP server
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
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
String date_ntp= "";
String dpoint = ":";
String virgola = ";";
const int Analog_pin_vusb = A0;
float VUSB ;
const int chipSelect = 10;
volatile bool irqFlag = false;
unsigned long sec_since_rtc = 0;
unsigned long sec_since_ntp = 0;
unsigned long sec_start_ntp = 0;
bool acquire_start = true;
long diff_time_mem = 0;
unsigned long start_millis = 0 ;
unsigned long sec_since_millis = 0 ;
const char* filename = "rtc.txt";
void periodicCallback()
{
sec_since_rtc++;
}
void setup() {
delay(1000);
// Open serial communications and wait for port to open:
Serial.begin(9600);
//OLED
/*##### Inizialized OLED Display #####*/
// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
}
display.clearDisplay();// Clear the buffer
display.display();//Print screan
delay(2000);
//ETH INIT
Ethernet.init(5); // CS for ethernet shield
// start Ethernet and UDP
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// Check for Ethernet hardware present
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
Serial.println("Ethernet shield was not found. Sorry, can't run without hardware. :(");
} else if (Ethernet.linkStatus() == LinkOFF) {
Serial.println("Ethernet cable is not connected.");
}
// no point in carrying on, so do nothing forevermore:
}
delay(1000);
display.setCursor(0, 1);
display.setTextSize(1);
display.setTextColor(WHITE);
Udp.begin(localPort);
auto link = Ethernet.linkStatus();
switch (link) {
case Unknown:
Serial.println("LInk Unknown; no divice recognized");
display.println("no divice recognized");display.display();
break;
case LinkON:
Serial.println("LInk UP");
display.println("LInk UP");display.display();
break;
case LinkOFF:
Serial.println("LInk Down");
display.println("LInk Down");display.display();
break;
}
Serial.println(Ethernet.localIP());
display.println(Ethernet.localIP());display.display();
delay(100);
Serial.print("Initializing SD card...");
display.println("Initializing SD card...");display.display();
delay(100);
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
display.println("Card failed, or not present");display.display();
// don't do anything more:
}
delay(100);
Serial.println("card initialized.");
display.println("card initialized.");display.display();
//You want remove the file
pinMode(4, INPUT_PULLUP);
if ( digitalRead(4) == 0) {
SD.remove(filename);
Serial.println("File removed successfully.");
display.println("SD log removed.");display.display();
}
// append or new
delay(100);
if (SD.exists(filename)) {
Serial.println("SD log append.");
display.println("SD log append.");display.display();
} else {
Serial.println("New file create.");
display.println("SD new file create.");display.display();
}
RTC.begin();
RTCTime startTime(30, Month::JUNE, 2023, 13, 37, 00, DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setTime(startTime);
if (!RTC.setPeriodicCallback(periodicCallback, Period::ONCE_EVERY_1_SEC)) {
Serial.println("ERROR: periodic callback not set");
}
//Print VUSB
analogReference(AR_INTERNAL);
delay(4000);
//Display print OLED
display.clearDisplay();
display.display();
}
void loop() {
/////////////////////////////////////////////////////////////////////////////////////////////////
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, extract 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;
// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
const unsigned long seventyYears = 2208988800UL;
if (secsSince1900 != 0 && acquire_start == true){
sec_start_ntp = secsSince1900;
acquire_start = false;
sec_since_rtc = 0;
start_millis = millis();
}
sec_since_ntp = secsSince1900-sec_start_ntp;
// subtract seventy years:
unsigned long epoch = secsSince1900 - seventyYears;
// print Unix time:
// print the hour, minute and second:
String ore_ntp = String((epoch % 86400L) / 3600);
String min_ntp = String((epoch % 3600) / 60);
String sec_ntp = String(epoch % 60);
date_ntp= "NTP:"+ ore_ntp + dpoint + min_ntp + dpoint + sec_ntp + virgola;
//Serial.print(date_ntp);
Ethernet.maintain();
//////////////////////////////////////////////////////////////
RTCTime currentTime;
// Get current time from RTC
RTC.getTime(currentTime);
// Print out date (DD/MM//YYYY)
String day_rtc = String(currentTime.getDayOfMonth());
String ore_rtc = String(currentTime.getHour());
String min_rtc = String(currentTime.getMinutes());
String sec_rtc = String(currentTime.getSeconds());
String date_rtc= "RTC:"+ day_rtc + dpoint + ore_rtc + dpoint + min_rtc + dpoint + sec_rtc + virgola;
//////// VUSB /////////////////////////
//Serial.print(date_rtc);
//////////////////////////////////////////////////////////////
// Serial.print(" sec_since_rtc:");
// Serial.print(sec_since_rtc);
// Serial.print(": sec_since_ntp:");
// Serial.print(sec_since_ntp);
// Serial.print(": sec_since_mills:");
sec_since_millis = (millis() - start_millis) / 1000;
// Serial.print(sec_since_millis);
// Serial.print(": Diff RTC-NTC time:");
long diff_time=sec_since_rtc - sec_since_ntp;
// Serial.print(diff_time);
// Serial.print(": Diff millis-NTC time:");
long diff_time_millis = sec_since_millis - sec_since_ntp;
// Serial.print(diff_time_millis);
// Serial.print(": Diff RTC-NTC sector:");
diff_time_mem= diff_time-diff_time_mem;
// Serial.println(diff_time_mem);
String since_rtc = " sec_since_rtc:" + String(sec_since_rtc) ;
String since_ntp = "; sec_since_ntp:" + String(sec_since_ntp);
String since_millis = "; sec_since_millis:" + String(sec_since_millis);
String diff = "; Diff RTC-NTC time:" + String(diff_time);
String diff_millis = "; Diff millis-NTC time:" + String(diff_time_millis);
String diff_sector = "; Diff RTC-NTC sector:" + String(diff_time_mem);
VUSB= ((analogRead(Analog_pin_vusb))*0.001416)* 4.3;
String VCC= "; VCC:" + String(VUSB);
//OLDE PRINT
display.clearDisplay();
display.setCursor(0, 1);
display.setTextSize(1);
display.setTextColor(WHITE);
display.print("RTC_since:");
display.println(sec_since_rtc);
display.print("NTP_since:");
display.println(sec_since_ntp);
display.print("Millis_since:");
display.println(sec_since_millis);
display.print("RTC-NTP:");
display.println(diff_time);
display.print("MILLIS-NTP:");
display.println(diff_time_millis);
display.println("");
display.print("RTC-sector:");
display.println(diff_time_mem);
display.print("VCC:");
display.println(VUSB);
display.display();
diff_time_mem = diff_time;
//SD
// make a string for assembling the data to log:
File dataFile = SD.open(filename, FILE_WRITE);
String save_sd = date_rtc + date_ntp + since_rtc + since_ntp + since_millis + diff + diff_millis + diff_sector + VCC;
Serial.println(save_sd);
if (dataFile) {
dataFile.println(save_sd);
dataFile.close();
// print to the serial port too:
//Serial.println(date_rtc);
}
// if the file isn't open, pop up an error:
else {
Serial.println("error opening datalog.txt");
}
}
delay(300000);
}
void sendNTPpacket(const char * 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();
}
Dalle acquisizioni ho notato che il tempo millis() variava di qualche secondo all'ora al variare dalla tensione VUSB, questo non si sposa con il mio progetto perchè dovrò alimentarlo con USB-C, ma utilizzando come alimentazione il connettore DC JACK dove in cascata c'è un alimentatore stabilizzato l'utilizzo di millis come fonte di clock potrebbe essere una strada. Discorso diverso per l'oscillatore del RTC LOCO il quale è alimentato da un LDO 3V3 interno quindi poco o quasi indipendete dalla tensione VUSB.
Qui sotto una acquisizione fatta di 36ore dove ho voluto mettere a confronto la precisione dei due oscillatori:
Posso confermare anche da altre acquisizioni non riportate che il clock LOCO è preciso ma non accurato e anche millis (con il vincolo di usare di alimentare la scheda Arduino o dal DC Jack per avere sempre la stessa deviazione).
Il mio obbiettivo è di qualche secondo al giorno, quindi nei prossimi giorni utilizzerò la funzione creata in precedenza vedi Sopra nel quale inserirò la compensazione e vedrò la precisione giornaliera, mettendo qui i risultati.
Saluti
Ma hai visto QUESTA discussione? ...
... perché ho già fatto qualche altra cosa ed è piuttosto precisa
Guglielmo