Raspberry Pi Pico als analoge Uhr - AZ-Delivery

Comme le Raspberry Pi Pico est encore assez nouveau pour moi et que j'ai besoin de rafraîchir mes connaissances sur Python, j'utilise toujours de petits "projets standard" pour me familiariser avec un nouveau matériel ou logiciel. Vous l'avez déjà vu dans mon dernier blog Pico comme station météo et j'aimerais continuer ici en réalisant un horloge analogique à l'aide d'un RealtTimeClock et d'un écran OLED.

Je partage le blog en deux parties :

  1. Un RealtTimeClock de type DS3231 sur le Pico Mise en service
  2. Afficher l'heure de RealTimeClock sur l'écran OLED

L'horloge devrait pouvoir distinguer simultanément l'heure d'été et l'heure d'hiver. Un composant RTC est nécessaire, car le Raspberry Pi Pico ne possède pas de RTC interne.

Matériel et logiciel nécessaires

Le matériel nécessaire pour ce montage expérimental est simple, voir tableau 1.

Nombre Composant matériel Note
1 Raspberry Pi Pico
1 0,96 pouce OLED I2C Display 128 x 64
1 Breadboard Kit - 3x Jumper Wire
1 Horloge en temps réel RTC DS3231 I2C

Alternativement: DS1302 RTC

Tableau 1 : composants matériels pour horloge analogique

Pour le logiciel, comme il s'agira d'un programme avec MicroPython, vous utiliserez Thonny Python IDE, qui est déjà disponible avec l'image Raspbian et peut être installé pour tous les systèmes d'exploitation courants.

De plus, une fois l'IDE Thonny Python installé et le Pico connecté, vous aurez besoin des bibliothèques urtc et microypthon-oled, qui seront ensuite transférées sur le Pico. Pour savoir comment installer les bibliothèques, consultez également l'article sur la station météo Pico.

Mettre en service RTC DS3231

C'est justement lorsque vous vous procurez un nouveau RTC DS3231 dans notre boutique ou lorsque la pile du RTC est déchargée que le RTC DS3231 doit être réglé à nouveau. C'est ce que nous allons faire dans la première partie de ce blog. Pour cela, il faut d'abord câbler le Raspberry Pi Pico avec le RCT DS3231, voir figure 1.

Figure 1: Câblage RTC DS3231 avec pico

Figure 1 : Câblage RTC DS3231 avec Pico

Pour l'ensemble du montage, il faut en fait quatre fils et les deux composants mentionnés précédemment. Ensuite, si vous avez déjà installé Thonny Python IDE, connectez le Raspberry Pi Pico au PC ou au Raspberry Pi et téléchargez les bibliothèques nécessaires. A ce stade, je suppose que le firmware est présent sur le Pico. Téléchargez ensuite le code 1 sur le Pico. Pour savoir comment installer le firmware, consultez cette Quickstart Guide.

"""
 // Read/Write a DS3231-RTC via I2C
 // and write output to terminal
 // Autor:   Joern Weise
 // License: GNU GPl 3.0
 // Created: 03. Nov 2021
 // Update: 11. Nov 2021
 """
 
 from machine import Pin,I2C
 from utime import sleep
 import urtc #Lib for RTC
 
 #Init needed I2C-Interface
 I2C_PORT = 0
 I2C_SDA = Pin(0)
 I2C_SCL = Pin(1)
 i2c = I2C(I2C_PORTsda=I2C_SDAscl=I2C_SCLfreq=40000)
 
 #Write in terminal found addresses
 i2cScan = i2c.scan()
 counter = 0
 print('-----------------')
 print('Found I2C-Devices')
 for i in i2cScan:
     print("I2C Address " + f'{counter:03d}' + " : "+hex(i).upper())
     counter+=1
 #sleep(5) #Make as comment, if date/time should be set
 #Init RTC-DS3231
 RTC = urtc.DS3231(i2c)
 arrayDay = ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"];
 
 """#SET TIME FROM RTC
 #Uncomment only to set time to RTC
 #Create tuple with new date e.g. Wednesday 03 Nov. 2021 07:30:00 AM
 datetime = urtc.datetime_tuple(year=2021, month=11, day=3,
                                weekday=5, hour=10, minute=34,
                                second=0, millisecond=0)
 RTC.datetime(datetime) #Write new date/time to RTC
 """#END setting time
 
 lastSecond = -1
 while True#Infinit loop to read date/time
     t = RTC.datetime() #Get date/time from RTC
     if lastSecond !int(t[6]): #Update only if seconds changed
         print('-----------------')
         #print(lastSecond) #Show last second
         #print(RTC.datetime()) #Show tuple with date-time information
         #Output current date and time from RTC to terminal
         print('Date: ' + f'{t[2]:02d}' + '/' + f'{t[1]:02d}' + '/' + f'{t[0]:02d}')
         print('Day: ' + arrayDay[t[3]-1])
         print('Time: ' + f'{t[4]:02d}'+':'+f'{t[5]:02d}'+':'+f'{t[6]:02d}')
         lastSecond = int(t[6])

Code 1 : Réglage du RTC DS3231

Pour que le RTC DS3231 soit réglé, il faut supprimer les """ précédents dans les lignes """#SET TIME FROM RTC et """#End settime et entrer une nouvelle date et une nouvelle heure. Weekday est un peu déroutant à cet endroit, car le format d'heure américain est utilisé ici et la semaine commence par dimanche. Le dimanche est donc le 1 et le mercredi le 4 dans l'exemple. Lors de l'exécution du script, l'horloge RTC DS3231 est réinitialisée. Immédiatement après, l'heure et la date actuelles du RTC s'affichent toutes les secondes sur la ligne de commande, voir figure 2.

Figure 2: Date et heure actuelles de RTC DS3231

Figure 2 : Date et heure actuelles de l'horloge RTC DS3231.

Si vous redémarrez le Pico, dans le cas actuel, l'heure sera à nouveau réinitialisée, il faut donc remettre les """ aux endroits appropriés et laisser le code se charger.

Tant que la pile bouton est insérée dans la RTC DS 3231 et qu'elle n'est pas vide, l'horloge continue sa marche.

Horloge analogique avec OLED et RTC DS3231

Nous en arrivons maintenant au projet proprement dit, l'horloge analogique sur l'écran OLED avec toutes les autres indications de temps. Le câblage, puisqu'il ne s'agit en fait que d'un composant supplémentaire, n'est pas devenu beaucoup plus compliqué, voir figure 3.

Figure 3: Affichage OLED de câblage et RTC DS3231 avec pico

Figure 3 : Câblage de l'écran OLED et de l'horloge RTC DS3231 avec Pico

Comme le RTC DS3231 et l'écran OLED communiquent tous deux avec i2c, mais ont des adresses différentes, ils peuvent être placés sur une seule ligne de bus. Une fois les travaux de câblage terminés, vous pouvez transférer le code 2 sur le Pico.

 """
 // Read a DS3231-RTC via I2C and
 // write output to terminal and OLED
 // Autor:   Joern Weise
 // License: GNU GPl 3.0
 // Created: 04. Nov 2021
 // Update: 05. Nov 2021
 """
 
 from machine import Pin,I2C
 import utime
 from oled import SSD1306_I2C,gfx,Write
 from oled.fonts import ubuntu_mono_12ubuntu_mono_15
 import urtc #Lib for RTC
 import math #Everybody loves math ;)
 
 bPrintDiag = False #Bool to show terminal diag. WARNING set to True cost performance
 #Init needed I2C-Interface
 I2C_PORT = 0
 I2C_SDA = Pin(0)
 I2C_SCL = Pin(1)
 i2c = I2C(I2C_PORTsda=I2C_SDAscl=I2C_SCLfreq=40000)
 
 #Write in terminal found addresses
 if bPrintDiag:
     i2cScan = i2c.scan()
     counter = 0
     print('-----------------')
     print('Found I2C-Devices')
     for i in i2cScan:
         print("I2C Address " + f'{counter:03d}' + " : "+hex(i).upper())
         counter+=1
 
 #Init RTC-DS3231
 RTC = urtc.DS3231(i2c)
 arrayDay = ["Sonntag","Montag","Dienstag","Mittwoch","Donnerstag","Freitag","Samstag"];
 
 #Definitions and init for OLED-Display
 WIDTH = 128
 HIGHT = 64
 oled = SSD1306_I2C(WIDTHHIGHTi2c)
 graphics = gfx.GFX(WIDTH,HIGHT,oled.pixel)
 
 #Definitions for clock
 xCenter = 32
 yCenter = 32
 radius = 30
 
 #Definition for Font
 font = Write(oled,ubuntu_mono_12)
 Intro = Write(oled,ubuntu_mono_15)
 
 """#SET TIME FROM RTC
 #Uncomment only to set time to RTC
 #Create tuple with new date e.g. Wednesday 03 Nov. 2021 07:30:00 AM
 datetime = urtc.datetime_tuple(year=2021, month=11, day=3,
                                weekday=4, hour=7, minute=30,
                                second=0, millisecond=0)
 RTC.datetime(datetime) #Write new date/time to RTC
 """#End Sync time from RTC
 
 """
 Function: Calc_Summer_Winter_Time
 Description: Calculate the time difference
 for summer or winter time
 IN hour: Current hour
 IN minute: Current minute
 IN second: Curent second
 IN day: Current day
 IN month: Current month
 In year: Current year
 """
 def Calc_Summer_Winter_Time(hour,minute,second,day,month,year):
     HHMarch = utime.mktime((year,3 ,(14-(int(5*year/4+1))%7),1,0,0,0,0,0)) #Time of March change to DST
     HHNovember = utime.mktime((year,10,(7-(int(5*year/4+1))%7),1,0,0,0,0,0)) #Time of November change to EST
     #print(HHNovember)
     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# we are after last sunday of october
         dst=0
     
     return(dst)
 
 """
 Function: Draw_Clock
 Description: Draw the analog clock on OLED
 IN hour: Current hour
 IN minute: Current minute
 """
 def Draw_Clock(hour,minute):
     graphics.circle(xCenter,yCenter,radius,1)
     graphics.fill_circle(xCenter,yCenter,2,1)
     oled.text('12',25,6)
     oled.text('3',52,30)
     oled.text('6',29,50)
     oled.text('9',5,28)
     MinutAngle = 180 - minute * 6
     MinShift = 5 * int(minute/10)
     #print(MinutAngle)
     if hour>=0 and hour<=12:
         HourAngle = 180 - hour * 30
     else:
         HourAngle = 180 - (hour-12* 30
     HourAngle-=MinShift
     #print(HourAngle)
     #Obtain coordinates for minute handle
     shift_min_x = 0.8 * radius * math.sin(math.radians(MinutAngle))
     shift_min_y = 0.8 * radius * math.cos(math.radians(MinutAngle))
     graphics.line(xCenter,yCenter,round(xCenter+shift_min_x),round(yCenter+shift_min_y),1)
     #Obtain coordinates for hour handle
     shift_hour_x = 0.6 * radius * math.sin(math.radians(HourAngle))
     shift_hour_y = 0.6 * radius * math.cos(math.radians(HourAngle))
     graphics.line(xCenter,yCenter,round(xCenter + shift_hour_x),round(yCenter + shift_hour_y),1)
 
 """
 Function: Calc_Summer_Winter_Time
 Description: Calculate the time difference
 for summer or winter time
 IN hour: Current hour
 IN minute: Current minute
 IN second: Curent second
 IN mday: Written day-name
 IN day: Current day
 IN month: Current month
 In year: Current year
 """
 def Print_Date_Time(hour,minute,second,dayName,day,month,year):
     yPos = 1
     yShift = 13
     xPos = xCenter+radius+6
     font.text('---Date---'xPosyPos)
     yPos+=yShift
     font.text(f'{day:02d}' + '/' + f'{month:02d}' + '/' + f'{year:02d}'xPosyPos)
     yPos+=yShift
     font.text(f'{dayName:^10}'xPosyPos#Dayname in center
     yPos+=yShift
     font.text('---Time---'xPosyPos)
     yPos+=yShift
     font.text(' ' + f'{hour:02d}' + ':' + f'{minute:02d}' + ':' + f'{second:02d}'xPosyPos)
     
     
 lastSecond = -1 # Update text on OLED
 lastMinute = -1 #Update Analog clock
 lastHour = -1 #To check sommer and winter time
 TimeShift = 0 #For summer and winter time
 #Show an intro on OLED for 5 seconds
 oled.fill(0)
 graphics.fill_rect(05128151)
 Intro.text("Analog Clock"205bgcolor=1color=0)
 Intro.text("(c) Joern Weise"1025)
 Intro.text("for Az-Delivery"1045)
 oled.show()
 
 #Print short intro in terminal
 print('----------------')
 print(f'{"Analog Clock":^16}')
 print(f'{"(c) Joern Weise":^16}')
 print(f'{"for Az-Delivery":^16}')
 print('----------------')
 
 utime.sleep(5)
 
 while True#Infinit loop to read date/time
     t = RTC.datetime() #Get date/time from RTC
     if lastSecond !int(t[6]): #Update only if seconds changed
         if lastHour !t[4]: #Check summer or winter time
             TimeShift = Calc_Summer_Winter_Time(t[4],t[5],t[6],t[2],t[1],t[0])
             #print('New timeshift: ' + str(TimeShift))
             lastHour = t[4]
         correctHour = t[4+ TimeShift
         #Output current date and time from RTC to terminal
         if bPrintDiag:
             print('-----------------')
             print('Date: ' + f'{t[2]:02d}' + '/' + f'{t[1]:02d}' + '/' + f'{t[0]:02d}')
             print('Day: ' + arrayDay[t[3]-1])
             print('Time: ' + f'{correctHour:02d}'+':'+f'{t[5]:02d}'+':'+f'{t[6]:02d}')
         if lastMinute !t[5]:
             oled.fill(0)
             Draw_Clock(correctHour,t[5])
             lastMinute = t[5]
         Print_Date_Time(correctHour,t[5],t[6],arrayDay[t[3]-1],t[2],t[1],t[0])
         oled.show()
         lastSecond = int(t[6])

Code 2 : Horloge analogique avec OLED et RTC DS3231.

J'aimerais expliquer brièvement ce code source à quelques reprises. Tout d'abord, j'ai ajouté un indicateur de diag dans mon code source bPrintDiag. Comme l'heure complète est affichée toutes les secondes dans le terminal, le Pico marche un peu au ralenti. Comme les sorties de diag ne sont nécessaires que pour le débogage, j'ai fait en sorte qu'elles soient activées ou désactivées. Vient ensuite la définition de la fonction Calc_Summer_Winter_Time, qui vérifie si l'on est en heure d'hiver ou d'été. Celle-ci renvoie le décalage de l'heure, qui est utilisé dans la boucle while comme correction de l'heure RTC DS3231. De plus, pour que le code reste plus clair, j'ai déplacé le dessin de l'horloge analogique et l'écriture du texte sur l'écran OLED dans les fonctions Draw_Clock et Print_Date_Time. La première fonction n'est appelée que s'il y a eu un changement dans les minutes, la seconde s'il y a eu un changement dans les secondes.

Draw_Clock contient les mathématiques permettant de dessiner les aiguilles et le visage de la montre. Comme je suis fan du fait que tout marche exactement comme sur une vraie montre analogique, le comportement de l'aiguille des heures est également reproduit correctement. Dans le code source initial, l'aiguille des heures était figée sur l'heure actuelle. Dans une vraie montre analogique, ce n'est pas le cas, mais elle se déplace petit à petit vers l'heure suivante. J'ai réalisé cela en calculant l'angle pour l'aiguille des heures avec la minute actuelle. Comme il manque quelques fonctions de dessin dans la bibliothèque standard pour l'OLED, j'utilise la bibliothèque GFX pour dessiner. Celle-ci appelle toujours la fonction du pixel de l'objet oled pour dessiner. La définition se trouve au début du code source, voir après le commentaire #Definitions and init for OLED-Display.

Print_Date_Time écrit alors aussi bien l'heure que la date sur le côté droit de l'écran OLED. Comme nous l'avons déjà mentionné, cela se fait toutes les secondes. Mais il y a encore un petit problème. La police standard de l'OLED est assez grande, c'est pourquoi il existe un objet font, qui est créé au début du texte source. Celui-ci charge la police ubuntu_mono_12, qui est fournie dans la bibliothèque microypthon-oled.

La boucle While à la fin du code source fait le reste. Elle se charge des vérifications correspondantes. Le résultat est présenté dans la figure 4.

Figure 4: La date actuelle et l'heure actuelle avec le Pico

Figure 4 : La date et l'heure actuelles avec le Pico

Résumé

Le Pico est encore très récent dans notre gamme de produits, ce qui permet de reconstruire de nombreux "vieux" projets. En ce qui concerne les microcontrôleurs à petit prix, le Pico est une véritable alternative à un Nano V3.0 ou à une carte microcontrôleur ATmega328. La différence n'est "que de 2 euros", mais le principe du Raspberry Pi Pico n'a rien à envier à son modèle.

Personnellement, je suis de plus en plus fasciné par le Pico, notamment grâce au langage de script MicroPython. Il est vrai que les bibliothèques pour le Pico ne sont pas encore aussi complètes que celles de l'IDE Arduino, mais il faut garder à l'esprit que le Raspberry Pi Pico n'a été présenté que le 21 janvier 2021 et qu'une distribution mondiale du matériel a également pris du temps. Le Nano V3.0 ou la carte microcontrôleur ATmega328 sont sur le marché depuis bien plus longtemps et, par conséquent, il s'est déjà passé plus de choses dans le domaine du développement de bibliothèques.

Pour personnaliser encore plus le projet, vous pouvez par exemple ajouter une aiguille des secondes sur le cadran de la montre, ce qui implique de le redessiner toutes les secondes. Vous pouvez également modifier les aiguilles avec un peu de mathématiques, comme vous le souhaitez.

Remarque : si vous utilisez le module RTC DS1302 au lieu du module RTC DS3231 I2C, vous pouvez essayer cette bibliothèque de programmes.

Vous trouverez d'autres projets pour AZ-Delivery de ma part sous https://github.com/M3taKn1ght/Blog-Repo

DisplaysProjekte für anfängerRaspberry pi

7 commentaires

Ralf Kastmann

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

Konrad

Hallo,
wo kann ich die urtc Lib gerunterladen?

Mit sonnigen Grüßen,
Konrad

Gerhard Uhlhorn

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

Slyfox

Hallo,
hier ein Codevorschlag für Sommer Winterzeit-berechnung für MEZ/MESZ

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 = Summertime
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

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.

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

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.

Laisser un commentaire

Tous les commentaires sont modérés avant d'être publiés

Articles de blog recommandés

  1. ESP32 jetzt über den Boardverwalter installieren - AZ-Delivery
  2. Internet-Radio mit dem ESP32 - UPDATE - AZ-Delivery
  3. Arduino IDE - Programmieren für Einsteiger - Teil 1 - AZ-Delivery
  4. ESP32 - das Multitalent - AZ-Delivery