Arduino library upang suriin ang petsa at temperatura ng pinagsamang DS3231 sa pamamagitan ng I2C
UPDATED: Bisitahin din ang bago library upang pamahalaan ang petsa at oras gamit ang DS3231 RTC module at Arduino na may mga pagpapabuti tulad ng pana-panahong oras.
Ang pagpapatakbo ng IC de mga real time na orasan (RTC) pinakasikat na kinokontrol gamit ang bus I2C Kadalasan ito ay halos magkatulad. Bukod sa Wire library de Arduino lubos na pinapasimple ang mga komunikasyon sa mga device Dalawang-wire na Serial Interface (TWI), I2C, tiyak.
Sa malawak na pagsasalita, ang proseso ay binubuo ng
-
Magsimula ng mga komunikasyon bilang alipin o master (itinakda bilang default) gamit ang function Wire.begin(address). Kung ang "address" ay tinanggal, ang mga komunikasyon ay magsisimula sa μC ang guro ng bus I2C.
-
I-activate ang komunikasyon I2C gamit ang device sa pamamagitan ng memory address kung saan ito matatagpuan, gamit ang command Wire.beginTransmission(address).
-
Sumulat ng isang order sa bus I2C upang sabihin sa device ang operasyon na gusto mong gawin nito, gamit Wire.write(command), kung saan ang "order" ay ang operation code.
-
Huwag paganahin ang mga komunikasyon upang mailabas ang bus I2C may pagpapaandar Wire.endTransmission ().
-
Hilingin sa device na magpadala ng isang tiyak na halaga ng data na tumutugma sa operasyon na hiniling (sa kasong ito, ang petsa at oras) kasama ang function Wire.requestFrom(address,halaga).
-
Maghintay para sa data na hiniling na may function na magagamit para sa pagbabasa Magagamit ang wire (), na nagbabalik ng bilang ng data na natanggap na at maaaring basahin.
-
Basahin ang data na ipinadala ng device (ang real time na orasan, sa kasong ito) gamit ang function Wire.read() kasing dami ng mga byte na ipinahiwatig Magagamit ang wire () magagamit na iyon
-
Karaniwang ipinapadala ang data sa mga napaka-compact na format kaya malamang na kakailanganing bigyang-kahulugan ang data na natanggap sa paraang tumutugma sa representasyon ng data na ginawa sa program na gumagamit ng device.
Tungkol sa DS3231 (at mga katugma sa parehong serye, tulad ng DS3232) at ang interpretasyon ng data, ayon sa mga pagtutukoy ng pinagsamang, halimbawa, ang mga halaga ng iba't ibang mga digit na kumakatawan sa oras ay kinakatawan sa binary coded decimal (BCD) na magiging mas maginhawang ipahayag bilang isang decimal na halaga (a byte) na gagamitin sa Arduino
Sa parehong linya, ang temperatura ay ipinahayag bilang isang byte in pandagdag ng dalawa para sa bahaging integer at dalawang bit para sa hakbang, na may resolusyon na isang quarter ng isang degree, ng bahaging decimal. Ang mga ito at iba pang aspeto ng representasyon ng data sa orasan ay lubusang tinalakay sa code ng library sa ibaba. DS3231
Upang suriin ang temperatura sa library na ito, gamitin lamang ang pamamaraan read_temperature() ng bagay DS3231 instantiated sa simula. Upang basahin ang petsa at oras, unang nilo-load ang mga ito at pagkatapos ay hinihiling sa isa sa mga format (compact, human...) na available para sa iba't ibang gamit, na nakadokumento sa header na dokumento ng code library sa ibaba.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | //DS3231.h #if defined(ARDUINO) && ARDUINO>=100 #include “Arduino.h” #else #include “WProgram.h” #endif #include <Wire.h> #define TEMPERATURA_MAXIMA_DS3231 85.0 // Máxima temperatura que se puede medir con un DS3231 (70 grados en la versión comercial / no-industrial) #define TEMPERATURA_MINIMA_DS3231 -40.0 // Mínima temperatura que se puede medir con un DS3231 (0 grados en la versión comercial / no-industrial) #define DIRECCION_DS3231 B1101000 // Según datasheet #define TIMEOUT_I2C_DS3231 200 // Máximo tiempo de espera del bus I2C del DS3231 #define NUMERO_ELEMENTOS_FECHA 7 // Número de elementos (un byte por elemento) que tiene la matriz con los datos de la fecha #define NUMERO_BYTES_TEMPERATURA 2 // Número de bytes con los que se representa la temperatura (uno para la parte entera y el signo y otro para la parte decimal representada con una resolución de 0.25 grados) #define RESOLUCION_DECIMALES_DS3231 0.25 // Grados de cada paso de la parte decimal #define ROTACION_DECIMALES 6 // Rotación necesaria hasta llegar a los bits que contienen la parte que representa los decimales de la temperatura (rotar 6 corresponde a atender a los bits 7 y 8) #define MASCARA_DECIMALES B11000000 // Máscara para eliminar con una operación and la parte no significativa. En el caso el DS3231 no hace nada ya que al rotar queda sólo la parte relevante. class DS3231 { private: char valor_fecha_hora_DS3231[7]; // Matriz de valores numéricos (7 char) de la fecha y la hora. El índice 0 representa los segundos, el 1 los minutos, el 2 las horas (en formato de 24), el 3 el día de la semana empezando en el domingo que es 1, el 4 el día del mes, el 5 el número del mes y el 6 los dos últimos dígitos del año char hora_humana_DS3231[11]; // Hora en el formato hh:mm:ss siendo hh la hora (en formato de 24) representada con 2 dígitos, mm los minutos con 2 dígitos y ss los segundos con 2 dígitos char fecha_humana_DS3231[11]; // Fecha en formato DD/MM/AAAA siendo DD el día representado con 2 dígitos, MM el mes con 2 dígitos y AAAA el año con 4 dígitos char fecha_hora_MySQL_DS3231[20]; // Fecha en formato AAAA-MM-DD hh:mm:ss (estilo MySQL) siendo AAAA el año representado con 4 dígitos, MM el mes con 2 dígitos, DD el día con 2 dígitos, hh la hora (en formato de 24) con 2 dígitos, mm los minutos con 2 dígitos y ss los segundos con 2 dígitos char fecha_hora_compacta_DS3231[13]; // Fecha en formato compacto (como la anterior pero sin adornos y dos dígitos para el año) para escribir en log y en bases de datos char bcd_a_decimal(char bcd); // Convertir de BCD a decimal char decimal_a_bcd(char decimal); // Convertir de decimal a BCD protected: public: DS3231(); ~DS3231(); void cargar_fecha_hora(); void grabar_fecha_hora(char *fecha); char *valor_fecha_hora(); char *hora_humana(); // Hora en el formato hh:mm:ss siendo hh la hora (en formato de 24) representada con 2 dígitos, mm los minutos con 2 dígitos y ss los segundos con 2 dígitos unsigned int reloj_4_digitos_7_segmentos(); // La hora tal como la esperan la mayoría de relojes de cuatro dígitos LED de 7 segmentos char *fecha_humana(); // Fecha en formato DD/MM/AAAA siendo DD el día representado con 2 dígitos, MM el mes con 2 dígitos y AAAA el año con 4 dígitos char numero_dia_semana(); // Eso y empezando en domingo que es el 1 char *fecha_hora_MySQL(); // Fecha en formato AAAA-MM-DD hh:mm:ss (estilo MySQL) siendo AAAA el año representado con 4 dígitos, MM el mes con 2 dígitos, DD el día con 2 dígitos, hh la hora (en formato de 24) con 2 dígitos, mm los minutos con 2 dígitos y ss los segundos con 2 dígitos char *fecha_hora_compacta(); // Fecha en formato compacto (como la anterior pero sin adornos y dos dígitos para el año) para escribir en log y en bases de datos double leer_temperatura(); double temperatura_minima(); double temperatura_maxima(); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | //DS3231.cpp #include “DS3231.h” DS3231::DS3231() { //Wire.begin(); // Dependiendo de la versión del IDE puede ser maestro por defecto o no y habrá que activar en la librería o en la aplicación que la use (en el ejemplo se activa desde la aplicación, lo que permite un uso más genérico) } DS3231::~DS3231() { } void DS3231::cargar_fecha_hora() { unsigned long timeout_i2c; byte contador; Wire.beginTransmission(DIRECCION_DS3231); // Comunicar con el DS3231 en la dirección correspondiente Wire.write(0x00); // pedir registros desde la primera dirección Wire.endTransmission(); // Liberar el bus I2C Wire.requestFrom(DIRECCION_DS3231,NUMERO_ELEMENTOS_FECHA); // esperar NUMERO_ELEMENTOS_FECHA bytes timeout_i2c=millis()+TIMEOUT_I2C_DS3231; while(Wire.available()<NUMERO_ELEMENTOS_FECHA&&millis()<timeout_i2c){} // Esperar a que lleguen los datos o a que pase el tiempo mínimo de espera // Para usar sin espera: if(Wire.available()) for(contador=0;contador<NUMERO_ELEMENTOS_FECHA;contador++) { valor_fecha_hora_DS3231[contador]= Wire.read(); // Leer todos los datos sin discriminar aunque luego tendrán distinto tratamiento } valor_fecha_hora_DS3231[0]=bcd_a_decimal(valor_fecha_hora_DS3231[0]); // segundos en BCD valor_fecha_hora_DS3231[1]=bcd_a_decimal(valor_fecha_hora_DS3231[1]); // minutos en BCD valor_fecha_hora_DS3231[2]=((valor_fecha_hora_DS3231[2]&B00110000)>>4)*10+(valor_fecha_hora_DS3231[2]&B00001111); // BCD en modo de 24 horas valor_fecha_hora_DS3231[3]=valor_fecha_hora_DS3231[3]&B00000111; // Número de día de la semana empezando en 1 que es domingo valor_fecha_hora_DS3231[4]=((valor_fecha_hora_DS3231[4]&B00110000)>>4)*10+(valor_fecha_hora_DS3231[4]&B00001111); // Número del día del mes valor_fecha_hora_DS3231[5]=((valor_fecha_hora_DS3231[5]&B00010000)>>4)*10+(valor_fecha_hora_DS3231[5]&B00001111); // Número de mes (sin MSB) valor_fecha_hora_DS3231[6]=bcd_a_decimal(valor_fecha_hora_DS3231[6]); // Año en BCD (dos últimos dígitos) } char *DS3231::valor_fecha_hora() { //cargar_fecha_hora(); return valor_fecha_hora_DS3231; } char *DS3231::hora_humana() { //cargar_fecha_hora(); sprintf ( hora_humana_DS3231, “%02d:%02d:%02d”, valor_fecha_hora_DS3231[2], valor_fecha_hora_DS3231[1], valor_fecha_hora_DS3231[0] ); return hora_humana_DS3231; } unsigned int DS3231::reloj_4_digitos_7_segmentos() { //cargar_fecha_hora(); return (int)valor_fecha_hora_DS3231[2]*100+(int)valor_fecha_hora_DS3231[1]; } char *DS3231::fecha_humana() { //cargar_fecha_hora(); sprintf ( fecha_humana_DS3231, “%02d/%02d/20%02d”, // Formato de fecha y hora estilo español ¡Olé! valor_fecha_hora_DS3231[4], valor_fecha_hora_DS3231[5], valor_fecha_hora_DS3231[6] ); return fecha_humana_DS3231; } char DS3231::numero_dia_semana() { //cargar_fecha_hora(); return valor_fecha_hora_DS3231[3]; } char *DS3231::fecha_hora_compacta() { //cargar_fecha_hora(); sprintf ( fecha_hora_compacta_DS3231, “%02d%02d%02d%02d%02d%02d”, // Formato de fecha y hora compacta para log y base de datos valor_fecha_hora_DS3231[6], valor_fecha_hora_DS3231[5], valor_fecha_hora_DS3231[4], valor_fecha_hora_DS3231[2], valor_fecha_hora_DS3231[1], valor_fecha_hora_DS3231[0] ); return fecha_hora_compacta_DS3231; } char *DS3231::fecha_hora_MySQL() { //cargar_fecha_hora(); sprintf ( fecha_hora_MySQL_DS3231, “20%02d-%02d-%02d %02d:%02d:%02d”, // Formato de fecha y hora estilo MySQL valor_fecha_hora_DS3231[6], valor_fecha_hora_DS3231[5], valor_fecha_hora_DS3231[4], valor_fecha_hora_DS3231[2], valor_fecha_hora_DS3231[1], valor_fecha_hora_DS3231[0] ); return fecha_hora_MySQL_DS3231; } void DS3231::grabar_fecha_hora(char *fecha) { byte contador; Wire.beginTransmission(DIRECCION_DS3231); // Comunicar con el DS3231 en la dirección correspondiente Wire.write(0x00); // Empezar el envío en la primera dirección for(contador=0;contador<NUMERO_ELEMENTOS_FECHA;contador++) { Wire.write(decimal_a_bcd(fecha[contador])); // Escribir cada valor expresándolo en BCD } Wire.endTransmission(); // Liberar el bus I2C } double DS3231::leer_temperatura() { byte msb; // El byte más significativo contiene la parte entera de la temperatura (en complemento a 2 para poder representar temperaturas bajo cero) byte lsb; // El byte menos significatico contiene la parte decimal con una resolución de un cuarto de grado float temperatura=TEMPERATURA_MAXIMA_DS3231+1.0; // Un número mayor que el máximo como aviso de que algo va mal boolean negativo=false; // Inicialmente se considera postivo unsigned long timeout_i2c; Wire.beginTransmission(DIRECCION_DS3231); // Preparar el dispositivo Wire.write(0x11); // Solicitar temperatura (empieza en 11h y termina en 12h) Wire.endTransmission(); Wire.requestFrom(DIRECCION_DS3231,NUMERO_BYTES_TEMPERATURA); // Esperar temperatura: pedir dos bytes en la dirección del integrado timeout_i2c=millis()+TIMEOUT_I2C_DS3231; while(Wire.available()<NUMERO_BYTES_TEMPERATURA&&millis()<timeout_i2c){}// Esperar a que lleguen los datos o pase el tiempo de espera máximo // Para usar sin espera: if(Wire.available()) msb=Wire.read(); // parte entera con signo en complemento a dos lsb=Wire.read(); // parte fraccional con resolución de 0.25 grados negativo=msb>B01111111; // Es negativo si el primer dígito es uno temperatura=msb&B01111111; // revertir complemento a dos temperatura+=((lsb&MASCARA_DECIMALES)>>ROTACION_DECIMALES)*RESOLUCION_DECIMALES_DS3231; // atender sólo a los bits que contienen la parte decimal (7 y 8), multiplicar por el paso de la resolución y sumar a la parte entera de la temperatura if(negativo) { temperatura*=–1; // Cambiar el signo } return temperatura; } double DS3231::temperatura_minima() { return TEMPERATURA_MINIMA_DS3231; } double DS3231::temperatura_maxima() { return TEMPERATURA_MAXIMA_DS3231; } char DS3231::bcd_a_decimal(char bcd) // Convertir de BCD a decimal { return ((bcd&B11110000)>>4)*10+(bcd&B00001111); } char DS3231::decimal_a_bcd(char decimal) // Convertir de decimal a BCD { return decimal/10*16+(decimal%10); } |
Ang sumusunod ay halimbawang code upang ipakita kung paano gamitin ang library. Tulad ng nabanggit sa itaas, ang temperatura ay binabasa lamang gamit ang function
ng object ng klase ngunit, upang huwag pansinin ang mga error sa pagbabasa, dalawang constants ang ginagamit na nag-iimbak, ayon sa pagkakabanggit, ang maximum at minimum na temperatura ng device ayon sa data sheet at kung saan ay binabasa na may kaukulang mga function.Ang pagbabasa ng temperatura ay isinasagawa sa dalawang yugto: una ang halaga ay na-load, upang ang iba't ibang paggamit ng petsa o oras ay magiging pare-pareho (hindi sila magpapakita ng mas mataas na halaga sa mga hindi kanais-nais na mga kaso) at pangalawa ito ay ginagamit ayon sa format na ay kailangan. Ang halimbawang programa (na hindi masyadong praktikal, bagama't ipinapaliwanag nito ang lahat ng mga posibilidad) ay nagpapakita ng lahat ng magagamit na mga format
-
Ang pag-andar tangke (bytes) na naglalaman ng pitong numerical values na kumakatawan sa petsa at oras sa isang orasan DS3231 na-convert sa decimal (sila ay nasa BCD sa device)
na nagbabalik ng isang pointer sa isang array -
Gamit ang function
Ang isang halaga ay nakuha na tumutugma sa bilang ng araw ng linggo simula sa Linggo. Upang ipakita ito bilang teksto, isang array ang ginagamit at ang isa ay ibabawas upang magsimula sa index zero, Linggo. -
Upang kumonsulta sa petsa sa isang "lokal" (Spanish) na format, gamitin ang function
, na nagbabalik ng pointer sa isang string kung saan ang petsa ay kinakatawan sa DD/MM/YYYY na format, kung saan ang DD ay ang araw na kinakatawan ng 2 digit, MM ang buwan na may 2 digit at YYYY ang taon na may 4 na digit. -
Ang pag-andar
ibinabalik ang oras sa format na hh:mm:ss, na ang hh ay ang oras (sa 24 na format) na kinakatawan ng 2 digit, ang mm ay ang mga minuto na may 2 digit at ss ang mga segundo na may 2 digit. -
Upang madaling gamitin ang petsa at oras sa log file ang function ay na-program , na naghahatid ng halaga ng petsa at oras sa format na YYMMDDhhmmss na ang AA ay ang taon na kinakatawan ng huling 2 digit, MM ang buwan na may 2 digit, DD ang araw na may 2 digit, hh ang oras (sa 24 na format) na may 2 digit, mm ang minuto na may 2 digit at ss ang mga segundo na may 2 digit. Ang format na ito, kahit na ito ay teksto, ay tumatagal ng kaunting espasyo at nagbibigay-daan para sa napakasimpleng pagkakasunud-sunod ng alpabeto.
-
Ang pag-andar MySQL (o ang bago at mas malaya MariaDB) YYYY-MM-DD hh:mm:ss, kung saan ang YYYY ay ang taon na kinakatawan ng 4 na digit, ang MM ay ang buwan na may 2 digit, ang DD ay ang araw na may 2 digit, ang hh ay ang oras (sa 24 na format) na may 2 digit , mm ay ang mga minuto na may 2 digit at segundo na may 2 digit.
nagsisilbing ipakita ang petsa at oras sa format na ginamit ng database manager
Bagama't maraming mga format kung saan kinakatawan ang petsa at oras, maaaring wala doon ang kailangan mo, ngunit tiyak na batay sa isa sa mga umiiral na at gamit ito bilang isang halimbawa, magiging madaling magdagdag ng bagong paraan ayon sa iba pang mga pagtutukoy. Mangyaring, kung magdadagdag ka ng mga bagong function, ibahagi ang code (ilabas ito!) at ipaliwanag sa amin kung paano ito gumagana, upang paunti-unti naming mapahusay ang library. Salamat!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | #define INTERVALO_MEDICION 100000 // Medir temperatura cada 100 segundos (se renueva internamente en el DS3231 cada 64 segundos) #define ESPERA_ERROR 1000 // Tiempo de espera antes de volver a medir si se ha producido un error #define ELEMENTOS_MATRIZ_FECHA 7 #include “DS3231.h” #include <Wire.h> char buffer_fecha[ELEMENTOS_MATRIZ_FECHA]; char *puntero_fecha; float temperatura; unsigned long cronometro; byte contador; String dia_semana[]={“lunes”,“martes”,“miércoles”,“jueves”,“viernes”,“sábado”,“domingo”}; DS3231 reloj; void setup() { Serial.begin(9600); Wire.begin(); // Inicializar Wire sólo si no se hace dentro del constructor (de la librería) Este método, hacerlo en la aplicación, supone que se usa Wire para comunicar con otros dispositivos, no sólo con el DS3231 cronometro=0; // para que empiece inmediatamente } void loop() { if(millis()>cronometro) { temperatura=reloj.leer_temperatura(); if(temperatura>reloj.temperatura_maxima()||temperatura<reloj.temperatura_minima()) { cronometro=millis()+ESPERA_ERROR; } else { cronometro=millis()+INTERVALO_MEDICION; reloj.cargar_fecha_hora(); puntero_fecha=reloj.valor_fecha_hora(); for(contador=0;contador<ELEMENTOS_MATRIZ_FECHA;contador++) { buffer_fecha[contador]=*(puntero_fecha+contador); Serial.println(“Contenido de la posición “+String(contador,DEC)+” del buffer de la fecha -> “+String(int(buffer_fecha[contador]),DEC)); } Serial.print(“El día “); Serial.print(reloj.fecha_humana()); Serial.print(“, “); Serial.print(dia_semana[reloj.numero_dia_semana()–1]); Serial.print(“, a las “); Serial.println(reloj.hora_humana()); Serial.print(“(“); Serial.print(reloj.reloj_4_digitos_7_segmentos()); Serial.print(” en un reloj de 4 dígitos y “); Serial.print(reloj.fecha_hora_MySQL()); Serial.print(” según MySQL o “); Serial.print(reloj.fecha_hora_compacta()); Serial.println(” abreviadamente)”); Serial.print(“la temperatura era de “); Serial.print(temperatura); // Mostrar la temperatura Serial.println(” grados centígrados”); } } } |
Ang output ng halimbawang programa sa itaas ay maaaring katulad ng ipinapakita sa sumusunod na larawan: isang listahan ng 7 halaga (segundo, minuto, oras, araw ng linggo, araw ng buwan, buwan at taon) ang petsa at ang oras na ipinahayag sa paraang "tao" (ayon sa istilong Espanyol) ang oras bilang isang buong numero sa apat na digit na format ng orasan, ang petsa at oras sa format ng database MySQL, petsa at oras sa compact na format (para sa mga tala) at ang panloob na temperatura ng DS3231.
Post Komento