مكتبة Arduino للتحقق من تاريخ ودرجة حرارة DS3231 المدمج عبر I2C
محدث: قم أيضًا بزيارة الجديد مكتبة لإدارة التاريخ والوقت باستخدام وحدة DS3231 RTC وArduino مع تحسينات مثل الوقت الموسمي.
تشغيل IC de ساعات الوقت الحقيقي (RTC) الأكثر شيوعًا التي يتم التحكم فيها باستخدام الحافلة أنا2C عادة ما تكون مشابهة جدًا. إلى جانب ال مكتبة الأسلاك de اردوينو يبسط إلى حد كبير الاتصالات مع الأجهزة واجهة تسلسلية بسلكين (TWI), I2C، محدد.
بشكل عام، تتكون العملية من
-
ابدأ الاتصالات كعبد أو رئيسي (يتم تعيينه افتراضيًا) باستخدام الوظيفة سلك.ابدأ (العنوان). إذا تم حذف "العنوان"، تبدأ الاتصالات بـ درجة مئوية المعلم الحافلة أنا2C.
-
تفعيل التواصل I2C مع الجهاز من خلال عنوان الذاكرة حيث يوجد، وذلك باستخدام الأمر Wire.beginTransmission (العنوان).
-
اكتب أمرًا في الحافلة أنا2C لإخبار الجهاز بالعملية التي تريد أن يقوم بها، وذلك باستخدام سلك.كتابة (الأمر)، حيث "الترتيب" هو رمز العملية.
-
تعطيل الاتصالات للافراج عن الحافلة أنا2C مع الوظيفة Wire.endTransmission ().
-
اطلب من الجهاز إرسال كمية معينة من البيانات التي تتوافق مع العملية التي تم طلبها (في هذه الحالة، التاريخ والوقت) مع الوظيفة Wire.requestFrom(العنوان، المبلغ).
-
انتظر حتى تصبح البيانات المطلوبة مع الوظيفة متاحة للقراءة Wire.available ()، والذي يُرجع عدد البيانات التي تم تلقيها بالفعل ويمكن قراءتها.
-
قراءة البيانات المرسلة من الجهاز ( ساعة الوقت الحقيقي، في هذه الحالة) باستخدام الدالة سلك.قراءة () عدة مرات كما أشارت البايتات Wire.available () المتوفرة.
-
عادةً ما يتم إرسال البيانات بتنسيقات مضغوطة جدًا، لذا فمن المحتمل جدًا أنه سيكون من الضروري تفسير البيانات المستلمة بطريقة تتوافق مع تمثيل البيانات المقدمة في البرنامج الذي يستخدم الجهاز.
فيما يتعلق بـ DS3231 (والأجهزة المتوافقة في نفس السلسلة، مثل DS3232) وتفسير البيانات حسب المواصفات المتكاملة فمثلا يتم تمثيل قيم الارقام المختلفة التي تمثل الوقت في نظام عشري مشفر ثنائي (BCD) والتي ستكون أكثر ملاءمة للتعبير عنها كقيمة عشرية (أ بايت) للاستخدام في اردوينو
على نفس السطر، يتم التعبير عن درجة الحرارة بالبايت متمم ثنائي للجزء الصحيح وبتتين للخطوة بدقة ربع درجة للجزء العشري. تمت مناقشة هذه الجوانب وغيرها من جوانب تمثيل البيانات على مدار الساعة بشكل شامل في رمز المكتبة أدناه. DS3231
للتحقق من درجة الحرارة مع هذه المكتبة، فقط استخدم هذه الطريقة قراءة_درجة الحرارة() موضوع DS3231 مثيل في البداية. لقراءة التاريخ والوقت، يتم تحميلهما أولاً ثم طلبهما بأحد التنسيقات (مدمجة، بشرية...) المتاحة لاستخدامات مختلفة، موثقة في المستند الرئيسي لمكتبة الرموز أدناه.
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);
}
|
فيما يلي مثال على التعليمات البرمجية لإظهار كيفية استخدام المكتبة. كما ذكر أعلاه، يتم قراءة درجة الحرارة ببساطة باستخدام الوظيفة
من كائن الطبقة ولكن لتجاهل الأخطاء في القراءة، يتم استخدام ثابتين يخزنان، على التوالي، الحد الأقصى والحد الأدنى لدرجة حرارة الجهاز وفقًا لورقة البيانات والتي يتم قراءتها مع الوظائف المقابلة.تتم قراءة درجة الحرارة على مرحلتين: أولا يتم تحميل القيمة، بحيث تكون الاستخدامات المختلفة للتاريخ أو الوقت متسقة (لن تظهر قيمة أعلى في الحالات غير المواتية) وثانيا يتم استخدامها وفقا للتنسيق الذي وهناك حاجة. يعرض البرنامج النموذجي (وهو ليس عمليًا جدًا، على الرغم من أنه يشرح كل الاحتمالات) جميع التنسيقات المتاحة
-
الوظيفة دبابة (بايت) تحتوي على القيم الرقمية السبع التي تمثل التاريخ والوقت على الساعة DS3231 تم تحويلها إلى رقم عشري (هم في BCD على الجهاز)
الذي يقوم بإرجاع مؤشر إلى صفيف -
باستخدام الوظيفة
يتم الحصول على قيمة تتوافق مع عدد أيام الأسبوع بدءًا من يوم الأحد. لعرضه كنص، يتم استخدام مصفوفة ويتم طرح واحد منها للبدء عند الفهرس صفر، الأحد. -
للاطلاع على التاريخ بالتنسيق "المحلي" (الإسباني)، استخدم الوظيفة
، والذي يقوم بإرجاع مؤشر إلى سلسلة يتم فيها تمثيل التاريخ بتنسيق DD/MM/YYYY، حيث DD هو اليوم الممثل برقمين، وMM هو الشهر المكون من رقمين، وYYYY هو العام المكون من 2 أرقام. -
الوظيفة
إرجاع الوقت بالتنسيق hh:mm:ss، حيث hh هي الساعة (بتنسيق 24) ممثلة برقمين، وmm هي الدقائق المكونة من رقمين وss هي الثواني المكونة من رقمين. -
لاستخدام التاريخ والوقت بسهولة ملفات السجل تمت برمجة الوظيفة ، الذي يقدم قيمة التاريخ والوقت بالتنسيق YYMMDDhhmmss حيث AA هي السنة ممثلة بآخر رقمين، MM هو الشهر المكون من رقمين، DD هو اليوم المكون من رقمين، hh هي الساعة (بتنسيق 2 ) مكونة من رقمين، مم الدقائق مكونة من رقمين وss الثواني مكونة من رقمين. هذا التنسيق، على الرغم من كونه نصًا، إلا أنه يشغل مساحة صغيرة ويسمح بترتيب أبجدي بسيط جدًا.
-
الوظيفة MySQL (أو الجديد والأكثر حرية MariaDB ل) YYYY-MM-DD hh:mm:ss، حيث YYYY هي السنة ممثلة بأربعة أرقام، MM هو الشهر المكون من رقمين، DD هو اليوم المكون من رقمين، hh هي الساعة (بتنسيق 4) المكونة من رقمين ، مم هي الدقائق المكونة من رقمين والثواني المكونة من رقمين.
يعمل على تقديم التاريخ والوقت بالتنسيق الذي يستخدمه مدير قاعدة البيانات
على الرغم من وجود العديد من التنسيقات التي يمكنك من خلالها تمثيل التاريخ والوقت، إلا أن التنسيق الذي تحتاجه قد لا يكون موجودًا، ولكن بالتأكيد بناءً على أحد التنسيقات الموجودة واستخدامه كمثال، سيكون من السهل إضافة طريقة جديدة وفقًا لـ مواصفات أخرى. من فضلك، إذا قمت بإضافة وظائف جديدة، قم بمشاركة الكود (حرره!) واشرح لنا كيف يعمل، حتى نتمكن من تحسين المكتبة شيئًا فشيئًا. شكرًا لك!
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”);
}
}
}
|
يمكن أن تكون مخرجات البرنامج المثالى أعلاه شيئا مثل ما هو موضح في الصورة التالية: قائمة من 7 قيم (الثواني، الدقائق، الساعة، يوم من الأسبوع، يوم من الشهر، الشهر والسنة) التاريخ و الوقت المعبر عنه بطريقة "إنسانية" (وفقًا للأسلوب الإسباني) الوقت كرقم صحيح بتنسيق الساعة المكون من أربعة أرقام، التاريخ والوقت بتنسيق قاعدة البيانات MySQLوالتاريخ والوقت بتنسيق مضغوط (لـ الجذوع) ودرجة الحرارة الداخلية لل DS3231.
أكتب تعليق