Els servidors NTP (Network Time Protocol) ens permeten saber l'hora actual, encara que amb un format una mica peculiar. Fent una connexió UDP amb els paràmetres apropiats a un d'aquests servidors obtindrem, entre altres dades, un nombre de quatre bytes que conté el nombre total de segons que han transcorregut des del dia 1 de gener de 1900 (en el meridià de Greenwich).
Amb la placa Arduino MKR WIFI 1010, hi ha una altra manera més simple d'obtenir l'hora i la data actuals fent servir la funció getTime.
Podem generar, a partir dels quatre bytes, el valor dels segons transcorreguts. A partir d'aquest valor podem calcular amb certa facilitat el nombre de segons transcorreguts del dia actual i, a partir d'aquest valor, l'hora. Amb una mica més de feina, atès que els anys tenen durades diferents, podem calcular també la data.
// Aquest programa està parcialment basat en els exemples de la pàgina // https://www.arduino.cc/en/Tutorial/LibraryExamples#wifi1010 #include <SPI.h> // Carreguem la biblioteca SPI #include <WiFiNINA.h> // Carreguem la biblioteca WiFiNINA #include <WiFiUdp.h> // Farem servir el protocol UDP
const char idXarxa[] = "xarxa-wifi"; // Nom del punt d'accés
const char contrasenya[] = "contrasenya-wifi"; // Contrasenya de connexió
int status = WL_IDLE_STATUS;
unsigned int localPort = 2390; // Port per a la recepció UDP
IPAddress timeServer(130, 206, 3, 166); // Servidor hora.rediris.es
const int NTP_PACKET_SIZE = 48; // Número de paquets necessaris per obtenir la informació de l'hora
byte packetBuffer[NTP_PACKET_SIZE]; // Buffer on guardarem els paquets
int hora, minuts, segons, dia, mes, any, diaSet;
String DiesSetm[] = {"Dilluns", "Dimarts", "Dimecres", "Dijous", "Divendres", "Dissabte", "Diumenge"};
WiFiUDP Udp; // Creem un objecte UDP
bool Traspas(int any){
bool bixest = false;
// Són anys de traspàs els múltiples de 4 excepte si són múltiples de 100 i no ho són de 400
if((any % 400 == 0) || ((any % 4 == 0) && (any % 100 != 0))){
bixest = true;
}
return bixest;
}
void calcData(unsigned long Segs){
// Cada dia té 86400 s
// El nombre de dies des de l'1-1-1900 és el quocient de dividir per aquesta quantitat
unsigned long diesTotals = Segs / 86400UL;
// Posem UL perquè ens ho guardi en un unsigned long (ja que no cap en un int)
unsigned long TotalDies = 0;
unsigned long AfegirDies;
// Anirem sumant els dies de cada any des de 1900 fins superar el nombre total de dies
int compt = 1899; // Comencem un any abans perquè sumarem 1
int DiesMes[]={31,28,31,30,31,30,31,31,30,31,30,31};
diaSet = 1 + diesTotals % 7; // Valor entre 1 i 7 (sumem 1 perquè no pugui valer 0)
// El dia 1-1-1900 era dilluns, per tant no cal cap correcció
while(TotalDies <= diesTotals){
compt++;
if(Traspas(compt)){
AfegirDies = 366;
} else {
AfegirDies = 365;
}
TotalDies += AfegirDies;
}
// Ja ens hem passat, restem el darrer any (que està incomplet)
TotalDies -= AfegirDies;
any = compt; // El valor del comptador és l'any actual
compt = 0; // Ara farem el mateix amb els mesos d'aquest any
while(TotalDies <= diesTotals){
compt++; // compt ara és el mes, d'1 a 12
AfegirDies = DiesMes[compt - 1]; // DiesMes va de 0 a 11
if((compt == 2) && Traspas(any)){ // Estem a febrer d'un any de traspàs?
AfegirDies++; // Afegim el 29
}
TotalDies += AfegirDies;
}
// Ja ens hem passat, restem el darrer mes (que està incomplet)
TotalDies -= AfegirDies;
mes = compt; // El valor del comptador és el mes actual
// El que queda és el nombre de dies complets (fins ahir)
dia = diesTotals - TotalDies + 1; // Sumem 1 per tenir avui
}
void escriuData() {
Serial.print(DiesSetm[diaSet - 1]);
Serial.print(", ");
Serial.print(dia);
Serial.print("-");
Serial.print(mes);
Serial.print("-");
Serial.println(any);
}
void calculHora(unsigned long Segs) {
// Cada dia té 86400 s
// El nombre de segons del dia d'avui és el residu de dividir per aquesta quantitat
Segs = Segs % 86400UL;
// Posem UL perquè ens ho guardi en un unsigned long (ja que no cap en un int)
hora = Segs / 3600;
Segs = Segs % 3600;
minuts = Segs / 60;
segons = Segs % 60;
}
void escriuHora() {
Serial.print(hora);
Serial.print(".");
if (minuts < 10) {
Serial.print("0"); // Afegim el 0 si és menor que 10
}
Serial.print(minuts);
Serial.print(".");
if (segons < 10) {
Serial.print("0"); // Afegim el 0 si és menor que 10
}
Serial.println(segons);
}
void setup() { // Inicialització
Serial.begin(9600); // Monitor sèrie
while (!Serial) {
; // Esperem que l'usuari obri el monitor sèrie
}
if (WiFi.status() == WL_NO_MODULE) {
Serial.println("No s'ha trobat el dispositiu Wi-Fi");
while (true); // Bloquegem el programa
}
String versio = WiFi.firmwareVersion();
if (versio < "1.0.0") {
Serial.println("Convindria actualitzar el firmware");
}
while (status != WL_CONNECTED) {
Serial.print("Connectant a la xarxa ");
Serial.println(idXarxa);
status = WiFi.begin(idXarxa, contrasenya);
delay(10000); // Ho tornarem a intentar passats 10 s
}
Serial.print("Connectat a ");
Serial.println(WiFi.SSID());
Serial.print("Estat de la connexió: ");
Serial.println(WiFi.status());
Serial.print("Adreça IP del dispositiu: ");
Serial.println(WiFi.localIP());
Serial.print("Intensitat del senyal: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
Serial.println();
Serial.println("Anem a connectar al servidor");
Udp.begin(localPort);
}
void loop() { // Programa que es repeteix indefinidament
// Preparem el paquet que cal enviar per llegir l'hora
for (int k = 0; k < NTP_PACKET_SIZE; k++) {
packetBuffer[k] = 0;
}
packetBuffer[0] = 0b11100011;
packetBuffer[1] = 0;
packetBuffer[2] = 6;
packetBuffer[3] = 0xEC;
packetBuffer[12] = 49;
packetBuffer[13] = 0x4E;
packetBuffer[14] = 49;
packetBuffer[15] = 52;
Udp.beginPacket(timeServer, 123); // La lectura de l'hora es fa pel port 123
Udp.write(packetBuffer, NTP_PACKET_SIZE); // Enviem el paquet
Udp.endPacket();
delay(1000);
if (Udp.parsePacket()) { // Ha arribat un paquet?
Serial.println("Nou paquet");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // Llegim el paquet
// Ens arriben quatre bytes de dades en les posicions 40-43
// Corresponen a un sol valor unsigned long que hem de construir
// D'entrada els guardarem en quatre variables
unsigned long Word1 = packetBuffer[40]; // byte més significatiu
unsigned long Word2 = packetBuffer[41];
unsigned long Word3 = packetBuffer[42];
unsigned long Word4 = packetBuffer[43]; // byte menys significatiu
unsigned long Temps1900 = Word1 << 24;
Temps1900 |= Word2 << 16;
Temps1900 |= Word3 << 8;
Temps1900 |= Word4;
Serial.print("Han passat ");
Serial.print(Temps1900);
Serial.println(" segons des de l'1-1-1900");
calcData(Temps1900);
escriuData();
Serial.print("L'hora GMT actual és ");
calculHora(Temps1900);
escriuHora();
Serial.print("L'hora oficial actual (hivern) és ");
calculHora(Temps1900 + 3600);
escriuHora();
Serial.print("L'hora oficial actual (estiu) és ");
calculHora(Temps1900 + 7200);
escriuHora();
// Esperem 15 s
delay(15000);
}
}
En aquest programa hi ha la condició que el monitor sèrie estigui obert per a que el programa comenci a funcionar. Això ens permet seguir tot el procés encara que triguem a obrir el monitor sèrie. Quan el microcontrolador hagi de funcionar de manera independent de l'ordinador caldrà eliminar les línies següents per evitar aquest bloqueig.
while (!Serial) {
; // Esperem que l'usuari obri el monitor sèrie
}

Aquesta obra d'Oriol Boix està llicenciada sota una llicència no importada Reconeixement-NoComercial-SenseObraDerivada 3.0.