Como la Raspberry Pi Pico todavía es bastante nueva para mí y necesito refrescar mis conocimientos sobre Python, siempre uso "pequeños proyectos estándar" que me permitan familiarizarme con un nuevo hardware o software. Ya lo ha visto en mi último blog Estación meteorológica con Raspberry Pi Pico y pantalla OLED y me gustaría continuar aquí construyendo un Reloj Analógico utilizando un RealTimeClock y una pantalla OLED.
Dividiré el blog en dos partes:
- Un RealTimeClock tipo DS3231 con puesta en marcha en la Pico.
- Mostrar la hora del RealTimeClock en la pantalla OLED.
El reloj debe ser capaz de distinguir entre el horario de verano y el de invierno al mismo tiempo. Se requiere un componente RTC, ya que la Raspberry Pi Pico tiene un RTC instalado internamente.
Hardware y Software requeridos
El hardware para este esquema experimental es sencillo, véase la Tabla 1.
Cantidad | Hardware | Anotación |
---|---|---|
1 | Raspberry Pi Pico | |
1 | 0,96 pulgadas OLED SSD1306 Display 128x64 I2C | |
1 | Placa de pruebas y Cables de Puente (Jumper Wire) | |
1 | Reloj en Tiempo Real RTC DS3231 I2C Alternativamente: DS1302 Reloj en Tiempo Real |
Tabla 1: Piezas de hardware para el Reloj Analógico
Para el software, ya que será un programa con Micropython, utilice Thonny Python IDE, que ya está disponible con la imagen de Raspbian y se puede instalar para todos los sistemas operativos comunes.
Además, una vez instalado el Thonny Python IDE y conectada la Pico, necesitará las bibliotecas urtc y micropython-oled, que se transferirán a la Pico. También puede encontrar cómo instalar las bibliotecas en la publicación Estación meteorológica con Raspberry Pi Pico y pantalla OLED.
Puesta en marcha del RTC DS3231
En el momento en que obtiene un nuevo RTC DS3231 de nuestra tienda, o si la batería del RTC se ha agotado, entonces el RTC DS3231 debe ser reiniciado. De esto se trató en la primera parte de este blog. Para este propósito, primero se debe conectar la Raspberry Pi Pico con el RCT DS3231, véase la Figura 1.
Figura 1: Cableado RTC DS3231 con Pico
Para toda la construcción, se necesitan básicamente cuatro cables y los dos componentes que se mencionaron previamente. Después, si ya ha instalado Thonny Python IDE, conecte la Raspberry Pi Pico al PC o a la Raspberry Pi y descargue las bibliotecas necesarias. En este punto, supongo que el firmware está disponible en la Pico. A continuación, cargue el código 1 en la Pico. Puede leer cómo instalar el firmware en esta guía de inicio rápido.
"""
// leer / escribir un DS3231-RTC a través de I2C
// y la salida al terminal de escritura
// Autor: Joern Camino
// Licencia: GNU GPL 3.0
// Creado: 03. Nov 2021
// actualización: 11 Nov 2021
"""
de máquina importar Código PIN,I2C
de uti importar dormir
importar URTC #Lib de RTC
Se necesita #init interfaz I2C
I2c_port = 0
I2C_SDA = Código PIN(0)
I2C_SCL = Código PIN(1)
I2C = I2C(I2c_port, SDA=I2C_SDA, SCL=I2C_SCL, frec=40000)
#Write en direcciones encontradas Terminal
I2cscan = I2C.escanear()
Encimera = 0
impresión('-----------------')
impresión(Encontrado I2C-Dispositivos ')
por I en I2cscan:
impresión("Dirección I2C" + F '{Contador: 03d}' + " : "+maleficio(I).superior())
Encimera+=1
#Sleep (5) #make como comentario, si la fecha / hora debe ser conjunto
#Init RTC-DS3231
RTC = URTC.DS3231(I2C)
Arrayday = ["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"];
"" Tiempo #Establecer de RTC
#UNCOMMENT SÓLO PARA Establecer hora para RTC
#Create tupla con Nueva Fecha POR EJEMPLO Miércoles 03 de noviembre 2021 07:30:00 en
DateTime = URTC.DATETIME_TUPLE (Año = 2021, Mes = 11, día = 3,
Día de la semana = 5, hora = 10, minuto = 34,
Segundo = 0, Millisegundo = 0)
RTC.DATETIMIENTE (DATETIMIENTO) #WRITE Nueva Fecha / Hora a RTC
"""#End tiempo de espera
Ultimo segundo = -1
muecero Cierto: #Infinit bucle para leer la fecha / hora
t = Rtc.fecha y hora() # Fecha / hora de RTC
SI Ultimo segundo != En t(t[6]): #Upado solo si las segundas cambiaron
impresión('-----------------')
#Print (LastSecond) #show Último segundo
#Print (rtc.datetime ()) #show tuple con información de fecha-hora
# Fecha y hora actuales de salida de RTC a Terminal
impresión('Fecha:' + F '{t [2]: 02d}' + '/' + F '{t [1]: 02d}' + '/' + F '{t [0]: 02d}')
impresión('Día:' + Arroyday[t[3]-1])
impresión('Tiempo:' + F '{t [4]: 02d}'+':'+F '{t [5]: 02d}'+':'+F '{t [6]: 02d}')
Ultimo segundo = En t(t[6])
Código 1: Configuración del RTC DS3231
Para asegurarse de que el RTC DS3231 también está ajustado, debe borrar la línea precedente ""#SET TIME FROM RTC y ""# END SET TIME" e ingresar una nueva fecha y hora. El Día de la semana es un poco confuso en este punto porque aquí se utiliza el formato horario americano y la semana comienza con el día domingo. Por lo tanto, el domingo es 1 y el miércoles es 4 en el ejemplo. Cuando se ejecuta el script, el RTC DS3231 se reinicia. Inmediatamente después, verá la hora y la fecha actuales del RTC cada segundo en la línea de comandos, véase la Figura 2.
Figura 2: Fecha y hora actuales de RTC DS3231
Inicie la Pico nuevamente, entonces en el caso actual el tiempo se restablecerá, por lo que debe insertar el “““ de nuevo en los lugares apropiados y dejar que el código se cargue.
Mientras la batería de botón esté insertada en el RTC DS 3231 y no esté vacía, el reloj seguirá funcionando.
Reloj analógico con OLED y RTC DS3231
Ahora viene el verdadero proyecto, el reloj analógico en la pantalla OLED con el resto de la información horaria. El cableado, dado que básicamente sólo se ha agregado un componente más, no se ha complicado mucho, véase la Figura 3.
Figura 3: Cableado de la pantalla OLED y el RTC DS3231 con Pico
Dado que el RTC DS3231 y la pantalla OLED se comunican con i2c, pero tienen direcciones diferentes, se pueden colocar en una línea de bus. Una vez realizado el trabajo de cableado, puede transferir el código 2 a la Pico.
"""
// leer un DS3231-RTC a través de I2C y
// Escribir salida al terminal y OLED
// Autor: Joern Way
// Licencia: GNU GPL 3.0
// creado: 04. nov 2021
// Actualización: 05. Nov 2021
"""
de máquina importar Código PIN,I2C
importar uti
de Oled importar SSD1306_I2C,Gfx,Escribiendo
de Oled.fuente importar ubuntu_mo_12, ubuntu_mo_15
importar Urtc #Lib para rtc
importar Matemáticas # Todo el mundo ama las matemáticas;)
bprintdiag = Falso #Bool para mostrar diag de la terminal. ADVERTENCIA CONJUNTO PARA EL RENDIMIENTO DE COSTO VERDADERA
#Init necesitado interfaz I2C
I2c_port = 0
I2c_sda = Código PIN(0)
I2c_scl = Código PIN(1)
I2C = I2C(I2c_port, Sda=I2c_sda, Scl=I2c_scl, freq=40000)
#WRITE EN LA TERMINAL DIRECTORES ENCONTRADAS
SI bprintdiag:
I2cscan = I2C.escanear()
Encimera = 0
impresión('-----------------')
impresión('Encontrado i2c-dispositivos')
por I en I2cscan:
impresión("Dirección I2C" + F '{Counter: 03D}' + " : "+maleficio(I).superior())
Encimera+=1
#Init rtc-ds3231
Rtc = Urtc.DS3231(I2C)
Arroyday = ["Domingo","Lunes","Martes","Miércoles","Jueves","Viernes","Sábado"];
#DEFINICIONES Y INIT PARA PANTALLA OLED
Ancho = 128
Hight = 64
Oled = SSD1306_I2C(Ancho, Hight, I2C)
gráficos = Gfx.Gfx(Ancho,Hight,Oled.píxel)
#Definiciones para el reloj
Xcenter = 32
Yecenter = 32
radio = 30
#Definición para la fuente
fuente = Escribiendo(Oled,ubuntu_mo_12)
Intro = Escribiendo(Oled,ubuntu_mo_15)
"" #Set hora de RTC
#Comentario solo para poner tiempo a RTC
#Create tupla con fecha nueva por ejemplo. Miércoles 03 nov. 2021 07:30:00 en
DateTime = urTC.DataTime_Tuple (Año = 2021, Mes = 11, Día = 3,
Día de la semana = 4, hora = 7, minuto = 30,
Segundo = 0, Millisegundo = 0)
RTC.DATETIMIENTE (DATETIMIENTO) #WRITE Nueva Fecha / Hora a RTC
"""#End tiempo de sincronización de RTC
"""
Función: calc_summer_winter_time
Descripción: Calcular la diferencia horaria.
Para el verano o el invierno.
En hora: hora actual
En minuto: minuto actual
En segundo: Curent Second
En el día: día actual
En mes: mes actual
En el año: año en curso
"""
def Calc_summer_winter_time(hora,minuto,segundo,día,mes,año):
Hhmarch = utime.mktime((año,3 ,(14-(En t(5*año/4+1))%7),1,0,0,0,0,0)) # Tiempo de marzo cambia a DST
Hhnovember = utime.mktime((año,10,(7-(En t(5*año/4+1))%7),1,0,0,0,0,0)) # Tiempo de noviembre CAMBIAR A EST
#Print (HHNovember)
ahora=utime.mktime((año,mes,día,hora,minuto,segundo,0,0))
si ahora < Hhmarch :
dst=0
elif ahora < Hhnovember : # Estamos antes del último domingo de octubre.
dst=1
demás: # estamos después del domingo pasado de octubre
dst=0
regreso(dst)
"""
Función: DRAW_CLOCK
Descripción: Dibuja el reloj analógico OLED
En hora: hora actual
En minuto: minuto actual
"""
def DRAW_CLOCK(hora,minuto):
gráficos.circulo(xcenter,eCenter,radio,1)
gráficos.Fill_circle(xcenter,eCenter,2,1)
oled.texto('12',25,6)
oled.texto('3',52,30)
oled.texto('6',29,50)
oled.texto('9',5,28)
Minuga = 180 - minuto * 6
Incifre = 5 * En t(minuto/10)
#Impresión (minuto)
si hora>=0 y hora<=12:
Bodega = 180 - hora * 30
demás:
Bodega = 180 - (hora-12) * 30
Bodega-=Incifre
#Print (aroma)
#Obtain coordenadas para un mango de minuto
Shift_min_x = 0.8 * radio * Matemáticas.pecado(Matemáticas.radianes(Minuga))
Shift_min_y = 0.8 * radio * Matemáticas.cos(Matemáticas.radianes(Minuga))
gráficos.línea(xcenter,eCenter,ronda(xcenter+Shift_min_x),ronda(eCenter+Shift_min_y),1)
#Obtain coordenadas durante la manija de la hora
Shift_hour_x = 0.6 * radio * Matemáticas.pecado(Matemáticas.radianes(Bodega))
Shift_hour_y = 0.6 * radio * Matemáticas.cos(Matemáticas.radianes(Bodega))
gráficos.línea(xcenter,eCenter,ronda(xcenter + Shift_hour_x),ronda(eCenter + Shift_hour_y),1)
"""
Función: calc_summer_winter_time
Descripción: Calcular la diferencia horaria.
Para el verano o el invierno.
En hora: hora actual
En minuto: minuto actual
En segundo: Curent Second
En May: Nombre escrito
En el día: día actual
En mes: mes actual
En el año: año en curso
"""
def Print_date_time(hora,minuto,segundo,nombramiento,día,mes,año):
ypos = 1
yshift = 13
XPOS = xcenter+radio+6
fuente.texto('---Fecha---', XPOS, ypos)
ypos+=yshift
fuente.texto(f '{day: 02d}' + '/' + F '{mes: 02d}' + '/' + F '{Año: 02D}', XPOS, ypos)
ypos+=yshift
fuente.texto(F '{dayname: ^ 10}', XPOS, ypos) #Dayname en el centro
ypos+=yshift
fuente.texto('---Tiempo---', XPOS, ypos)
ypos+=yshift
fuente.texto(' ' + f '{hora: 02d}' + ':' + F '{Minuto: 02D}' + ':' + f '{segundo: 02d}', XPOS, ypos)
ultimo segundo = -1 # Actualizar texto en OLED
último minuto = -1 #Apdate reloj analógico
ultima hora = -1 # Para revisar el sombrero y el invierno
Cambio de hora = 0 # Para el verano y el invierno
#Show una introducción en OLED durante 5 segundos
oled.llenar(0)
gráficos.Fill_RECT(0, 5, 128, 15, 1)
Intro.texto("Reloj analogo", 20, 5, bgcolor=1, color=0)
Intro.texto("(C) Joern Weise", 10, 25)
Intro.texto("Para AZ-Entrega", 10, 45)
oled.show()
#Print intro corto en terminal
impresión('----------------')
impresión(f '{"reloj analógico": ^ 16}')
impresión(F '{"(c) Joern Weise": ^ 16}')
impresión(F '{"para AZ-Entrega": ^ 16}')
impresión('----------------')
utime.dormir(5)
tiempo Cierto: #Infinit bucle para leer la fecha / hora
t = Rtc.fecha y hora() # Fecha / hora de RTC
si ultimo segundo != En t(t[6]): #Upado solo si los segundos cambiaron
si ultima hora != t[4]: #Compruebe el verano o el invierno
Cambio de hora = Calc_summer_winter_time(t[4],t[5],t[6],t[2],t[1],t[0])
#Print ('Nuevo tiempo de tiempo:' + STR (Timeshift))
ultima hora = t[4]
correcto = t[4] + Cambio de hora
# Fecha y hora actuales de salida de RTC a Terminal
si bprintdiag:
impresión('-----------------')
impresión('Fecha: ' + F '{t [2]: 02d}' + '/' + F '{t [1]: 02d}' + '/' + F '{t [0]: 02d}')
impresión('Día:' + Arroyday[t[3]-1])
impresión('Tiempo:' + F '{Correcthour: 02d}'+':'+F '{t [5]: 02d}'+':'+F '{t [6]: 02d}')
SI último minuto != t[5]:
Oled.llenar(0)
Draw_clock(Correcthour,t[5])
último minuto = t[5]
Print_date_time(Correcthour,t[5],t[6],Arroyday[t[3]-1],t[2],t[1],t[0])
Oled.show()
Ultimo segundo = En t(t[6])
Código 2: Reloj analógico con pantalla OLED y el RTC DS3231
Me gustaría explicar brevemente este código fuente en algunos puntos. En primer lugar, añadí una bandera Diag a mi código fuente bPrintDiag. Como la hora completa se imprime cada segundo en el terminal, la Pico funciona un poco lento con él. Como las salidas Diag son sólo necesarias para la depuración, las he hecho conmutables en encendido y apagado. A continuación, sigue la definición de la función Calc_Summer_Winter_Time, que comprueba si es invierno o verano. Esto devuelve el desplazamiento de la hora, que se utiliza en el bucle while como corrección de la hora del RTC DS3231. Además, para mantener el código más claro, he externalizado el dibujo del reloj analógico y la escritura del texto en la pantalla OLED a las funciones Draw_Clock y Print_Date_Time. La primera función sólo se llama cuando ha habido un cambio en los minutos, la segunda cuando ha habido un cambio en los segundos.
Draw_Clock contiene las matemáticas para dibujar las manecillas y la esfera del reloj. Como soy un fanático de que todo funcione exactamente como con un reloj analógico real, el comportamiento de la aguja de las horas también se reproduce correctamente. En el código fuente inicial, la aguja horaria era rígida en la hora actual. Esto no ocurre con un reloj analógico real, sino que se pasa a la hora siguiente poco a poco. Me di cuenta de ello calculando el ángulo de la aguja de las horas con el minuto actual. Dado que faltan algunas funciones de dibujo en la biblioteca estándar para el OLED, utilizo la biblioteca GFX para dibujar. Esto siempre llama a la función del píxel del objeto OLED para dibujar. La definición se puede encontrar al principio del código fuente, véase detrás del comentario #Definitions y init para la pantalla OLED.
Print_Date_Time escribe entonces la hora como la fecha en la parte derecha de la pantalla OLED. Como ya se ha mencionado, esto se hace cada segundo. Sin embargo, todavía hay un pequeño truco. La fuente por defecto del OLED es bastante grande, por lo que existe la fuente objeto, que se crea al principio del código fuente. Esto carga la fuente ubuntu_mono_12, que se suministra en la biblioteca micropython-oled.
El bucle while al final del código fuente hace el resto. Se encarga de los controles correspondientes. Puede ver el resultado en la Figura 4.
Figura 4: La fecha y la hora actuales con la Pico
Resumen
La Pico es todavía bastante nueva en nuestra gama de productos, por lo que muchos proyectos "antiguos" se pueden reconstruir. Especialmente en términos de microcontroladores por poco dinero, la Pico es una alternativa real a un Nano V3.0 o a la placa de microcontroladores ATmega328. La diferencia es de "sólo" 2 euros, pero en términos de principio, la Raspberry Pi Pico no es en absoluto inferior a la original.
En lo personal, cada vez estoy más entusiasmado con la Pico, especialmente por el lenguaje de scripting MicroPython. Aunque las bibliotecas para la Pico aun no son tan extensas como en el caso del Arduino IDE, hay que tener en cuenta que la Raspberry Pi Pico no se presentó hasta el 21 de enero de 2021 y que la distribución mundial del hardware también ha llevado su tiempo. La Nano V3.0 o la placa microcontrolador ATMega328 llevan mucho más tiempo en el mercado y, por lo tanto, ya se ha avanzado más en el ámbito del desarrollo de bibliotecas.
Para personalizar aun más el proyecto, podría añadir un segundero en la esfera del reloj, por ejemplo, pero éste también tendría que rediseñarse cada segundo. También puede modificar los punteros con un poco de matemáticas, como usted prefiera.
Nota: si utiliza el Módulo Reloj en Tiempo Real RTC DS3231 I2C en lugar del Módulo Reloj en tiempo real RTC DS1302 Serial, puede probar esta biblioteca de programas.
Puede encontrar más proyectos míos para AZ-Delivery en https://github.com/M3taKn1ght/Blog-Repo.
8 comentarios
Peter Nilsson
Hi, I can’t get it to work with the code for
“DS3231-RTC via I2C and write output to terminal and OLED”
I get this output in Thonny:
>>> %run -c $EDITOR_CONTENT
Traceback (most recent call last):
File “”, line 29, in
File “/lib/oled/ssd1306.py”, line 127, in init
File “/lib/oled/ssd1306.py”, line 47, in init
File “/lib/oled/ssd1306.py”, line 74, in init_display
File “/lib/oled/ssd1306.py”, line 99, in show
File “/lib/oled/ssd1306.py”, line 137, in write_framebuf
OSError: [Errno 110] ETIMEDOUT
This is line 29: oled = SSD1306_I2C(WIDTH, HIGHT, i2c)
I guess that the firs error has to be fixed first.
It worked with the code for
“Read/Write a DS3231-RTC via I2C and write output to terminal”
Ralf Kastmann
Hallo allerseits.
Eine runde Sache wäre es noch, wenn man z.B. ein DCF77-Empfangsmodul hinzufügen würde, welches einmal pro Tag die Uhr synchronisiert. Dann entfällt auch die mühsame Berechnung von Sommer/Winterzeit. Und noch ein Hinweis zum Display: ein TFT-Display ist m.A. nach besser geeignet, da das OLED-Display schon nach recht kurzer Zeit ein Einbrennverhalten zeigt. Das bedeutet, dass alle konstant aktivierten Pixel ihre Leuchtkraft verlieren.
Und mit einem TFT-Display kann man noch ein weinig mit Farben das Gesamtbild auffrischen.
Freundliche Grüsse
Konrad
Hallo,
wo kann ich die urtc Lib gerunterladen?
Mit sonnigen Grüßen,
Konrad
Gerhard Uhlhorn
Viel interessanter wäre doch einen Zeitserver abzufragen, damit sich die Uhr selbst stellt. Das scheint aber mangels Netzwerk nicht so einfach zu sein (ich suche gerade nach so einer Möglichkeit).
Alternativ könnte man beim starten des Picos vom Rechner oder bei der Scriptübergabe vielleicht die Rechnerzeit übergeben oder bei bestehender USB-Verbindung zum Rechner die Uhrzeit des Rechners auslesen. Dann bräuchte man ihn nur 2x im Jahr kurz mal in den Rechner einstecken und er stellt sich automatisch.
Hmm, ich glaube es wird wohl doch ein Arduino mit Ethernet oder WLAN.
Slyfox
Hallo,
Function: Calculates for a given date time if it is summer or winter time (MEZ/MESZ) Methode Find the last Sunday in March and October and substract the number of Days from the end of the month until that last Sunday Input Date & Time (hour,minute,second,day,month,year) Output 0 = Wintertime 1 = Summertimehier ein Codevorschlag für Sommer Winterzeit-berechnung für MEZ/MESZ
def Calc_Summer_Winter_Time(hour,minute,second,day,month,year):
HHMarch = utime.mktime((year,3,31,1,0,0,0,0,0)) # int time in sec since
HHMarch2 = utime.localtime(HHMarch) # generate tuple of time
ds = (HHMarch26 + 1)%7 # take the Day of the week 6 (int) of localtime Monday = 0, add one to make Monday the first day of the week ==> Sunday becomes 7 and with modulus 7 Sunday becomes 0
HHMarch = utime.mktime((year,3,31 – ds,1,0,0,0,0,0)) # Substract day of the week from 31 and generate time in sec since for start of summertime HHNovember = utime.mktime((year,10,31,1,0,0,0,0,0)) HHNovember2 = utime.localtime(HHNovember) dw = (HHNovember26 + 1) % 7 HHNovember = utime.mktime((year,10,31-dw,1,0,0,0,0,0)) now=utime.mktime((year,month,day,hour,minute,second,0,0)) if now < HHMarch : dst = 0 elif now < HHNovember : # we are before last sunday of october dst = 1 else: dst = 0 return(dst)
Andreas Wolter
@Slyfox: wir schauen uns das an
@Manfred sen.: dieser Beitrag war gedacht dafür zu zeigen, wie der Pico mit den Komponenten programmiert werden kann.
Es gibt bereits ein ähnliches Projekt mit ESP32 und dem AZ-Touch Mod:
https://www.az-delivery.de/blogs/azdelivery-blog-fur-arduino-und-raspberry-pi/az-touch-mod-als-analoge-uhr-mit-anzeige-fur-sonnenauf-und-untergang
Das zeigt, wie es in der Arduino IDE programmiert werden kann.
Grüße,
Andreas Wolter
Manfred sen.
Hallo
Scheint ja ein tolles Projekt zu sein, aber leider muss man wieder eine neue Hardware kaufen und eine neue Programmiersprache lernen.
Gibt es die Möglichkeit das Projekt auch für einen Uno oä. zu schreiben?
Das wäre richtig toll.
Gruß an den Programmierer
Slyfox
Hallo,
nettes Projekt.
Die Sommer/Winterzeit Umschaltung ist hier aber in DST und die Formeln ergeben die amerikanischen Daten welches für die Sommerzeitumstellung der zweite Sonntag im März ist und nicht wie in Europa der letzte Sonntag im März.
USA 14.3.2021 – 7.11.2021 (1. Sonntag im November) Deutschland 28.3 – 31.10 letzter Sonntag im Oktober. Auf die Schnelle keine einfache Umstellung der Formel gefunden die für D passen würde denn der spätestens beim Übergang 2023 23.3. auf 2024 31.3 passt das nicht. Vielleicht habe ich aber auch nur den Wald vor lauter Bäumen nicht gesehen.