Outils pour utilisateurs

Outils du site


issue203:micro-ci_micro-la

Greetings fellow Beings. I hope you have already read my How-To Python in the Real World #149 in this issue, because that is the actual beginning of this article. Consider this the second half of it. So if not, please jump back to the contents page (usually page #2) and read that first. I’ll wait. Ready now? GREAT! We’re talking about Time change from Standard Time to Daylight Saving time and back to Standard time. Why is that important? Well, if you have a Microcontroller that is dealing with time sensitive logging or needs to do something at a particular time, your device needs to know what the actual correct time is at all times. You might be thinking to yourself that this isn’t a big deal, especially if you use Thonny as your IDE for programming your device. Have you ever noticed that when you get the current time on your device is correct? That’s because, by default, Thonny sets the time on the device and more times than not, it is also set, again by default, to use the local time from the computer you are running Thonny on. That is a good thing, isn’t it? Well, yes. Most of the time.

Salutations, chers amis. J'espère que vous avez déjà lu mon tutoriel Python dans le monde réel n° 149 dans ce numéro, parce que c'est le début de cet article. Considérez ceci comme la deuxième moitié de l'article. Si ce n'est pas le cas, je vous invite à retourner au sommaire (généralement la page 2) et à le lire en premier. J'attendrai.

Vous êtes prêt ? C'EST GÉNIAL ! Nous parlons du passage de l'heure normale à l'heure d'été, puis à l'heure normale. Pourquoi est-ce important ? Eh bien, si vous avez un microcontrôleur qui traite des données sensibles au temps ou qui doit faire quelque chose à un moment donné, votre appareil doit savoir l'heure exacte à tout moment.

Vous vous dites peut-être que ce n'est pas très grave, surtout si vous utilisez Thonny comme IDE pour programmer votre appareil. Avez-vous déjà remarqué que lorsque vous obtenez l'heure actuelle sur votre appareil, celle-ci est correcte ? C'est parce que, par défaut, Thonny règle l'heure sur l'appareil et, la plupart du temps, il est également réglé, toujours par défaut, pour utiliser l'heure locale de l'ordinateur sur lequel vous exécutez Thonny.

C'est une bonne chose, n'est-ce pas ? Oui, bien sûr. En général.

Let’s say that you have a program that you are going to test that requires the time and date to be set. You test it under Thonny and it runs just fine. Now, you save the program to the device as main.py so that it will run when the device is powered on. Now you close Thonny and unplug then replug in the device. Assuming the device and your program have some sort of display showing the time, the time will display “00:00:00”. Even if you’ve connected to the network, unless you have connected to an NTP server (time server), the time is not set. That’s why having Thonny auto set the time and date isn’t always a good idea. How do we keep that from happening? There are four options that can be turned on or off by clicking in the small squares (Radiobuttons in the Tkinter world). Click on the two center options (‘Synchronize device’s real time clock’ and ‘Use local time in the real time clock’) to turn off the options. Then restart Thonny. Originally, I had planned on a full project including code for this article. However, there have been many stumbling blocks keeping me from doing this. Not the least of which is a problem in the ntptime function of MicroPython. It seems that this is across all the types of Microcontrollers, not just the RPi-PicoW. So, my fallback plan is to explain how it SHOULD be done, then try to lay out a plan on how it should be implemented (at least until things get settled in the MicroPython world) and give you the project code next month.

Supposons que vous souhaitiez tester un programme qui nécessite le réglage de l'heure et de la date. Vous le testez sous Thonny et il fonctionne parfaitement. Maintenant, vous sauvegardez le programme sur l'appareil sous le nom de main.py afin qu'il s'exécute lorsque l'appareil est mis sous tension. Maintenant, vous fermez Thonny et vous débranchez puis rebranchez l'appareil. En supposant que l'appareil et votre programme disposent d'une sorte d'affichage de l'heure, l'heure affichera « 00:00:00 ». Même si vous vous êtes connecté au réseau, à moins que vous ne vous soyez connecté à un serveur NTP (serveur de temps), l'heure n'est pas réglée. C'est pourquoi faire en sorte que Thonny règle automatiquement l'heure et la date n'est pas toujours une bonne idée.

Comment éviter cela ?

Il y a quatre options qui peuvent être activées ou désactivées en cliquant sur les petits carrés (Radiobuttons dans le monde Tkinter). Cliquez sur les deux options centrales (« Synchroniser l'horloge temps réel de l'appareil » et « Utiliser l'heure locale dans l'horloge temps réel ») pour désactiver les options. Redémarrez ensuite Thonny.

A l'origine, j'avais prévu un projet complet incluant le code pour cet article. Cependant, de nombreux écueils m'ont empêché de le faire. L'un d'entre eux, et non des moindres, est un problème dans la fonction ntptime de MicroPython. Il semble que ce problème soit présent sur tous les types de microcontrôleurs, et pas seulement sur le RPi-Pico W. Donc, mon plan de repli est d'expliquer comment cela DEVRAIT être fait, puis d'essayer d'établir un plan sur la façon dont il devrait être mis en œuvre (au moins jusqu'à ce que les choses se règlent dans le monde de MicroPython) et de vous donner le code du projet le mois prochain.

NTPTIME The MicroPython firmware has a module called ntptime that, assuming your device supports internet communication, will go out to a special Internet address and ask for the time. HOWEVER, the time that is returned is not what you would think it is. It is a very large integer that specifies the number of seconds since midnight Jan 1, 1900, and then an offset is applied to that to adjust it to either the number of seconds from Jan 1, 1970 or Jan 1, 2000. This is known as the EPOCH. This depends on the Microcontroller you are using. You really shouldn’t have to worry about the deep-down internal calculations, since the ntptime module should deal with that for you. What you DO need to worry about is that there are two times that are kept on the system. One is gmtime and one is localtime. As you might guess, gmtime is UTC (Universal Coordinated Time) or Greenwich Mean Time. This number never gets Daylight Saving Time applied to it. Now you would think that localtime would have at least the timezone applied to it and at best the DST value applied to it. As John Belushi used to say… “BUT NOOOOOOOOOOOOOOOOOOO”. In the world of MicroPython (at least currently), gmtime and localtime are both the same. This has caused a large discussion that you can follow if you wish at https://github.com/orgs/micropython/discussions/12378. As I said just above, time zones are not currently implemented in MicroPython.

NTPTIME

Le micrologiciel de MicroPython dispose d'un module appelé ntptime qui, en supposant que votre appareil supporte la communication Internet, se rendra à une adresse Internet spéciale et demandera l'heure. CEPENDANT, l'heure renvoyée n'est pas celle que l'on croit. Il s'agit d'un très grand nombre entier qui indique le nombre de secondes écoulées depuis le 1er janvier 1900 à minuit, puis un décalage est appliqué pour l'ajuster au nombre de secondes écoulées depuis le 1er janvier 1970 ou le 1er janvier 2000. C'est ce qu'on appelle l'EPOCH. Cela dépend du microcontrôleur que vous utilisez. Vous ne devriez pas avoir à vous préoccuper des calculs internes profonds, puisque le module ntptime devrait s'en charger pour vous. Ce dont vous devez vous préoccuper, c'est qu'il y a deux temps qui sont conservés dans le système. L'un est gmtime et l'autre est localtime. Comme vous pouvez le deviner, gmtime est l'UTC (Universal Coordinated Time - Temps universel coordonné), ou heure moyenne de Greenwich. Ce chiffre n'est jamais soumis à l'heure d'été. On pourrait penser que l'heure locale est au moins associée au fuseau horaire et au mieux à la valeur de l'heure d'été. Comme le disait John Belushi… « MAIS NOOOOOOOOOOONNNN ». Dans le monde de MicroPython (du moins actuellement), gmtime et localtime sont identiques. Cela a donné lieu à une grande discussion que vous pouvez suivre si vous le souhaitez à l'adresse https://github.com/orgs/micropython/discussions/12378. Comme je l'ai dit ci-dessus, les fuseaux horaires ne sont actuellement pas implémentés dans MicroPython.

To make matters a bit crazier, there are two versions of gmtime and localtime. There is one set in the time module and one in the utime module. Currently they all point to the same values. So, if you are interested in getting the correct time for wherever your Microcontroller is located at that moment, you have to play a bunch of games. So, since there is no consensus on this subject, let’s look at the games we can play to get the values that we want/need, at least for now. The VERY simplest solution at this point is to write your own localtime function that will take the timezone information and return a proper localtime. However, you really don’t need to do that, since I found one for you. User MrEbbinghaus posted a quick solution on August 31, 2023 at https://github.com/orgs/micropython/discussions/11173#discussioncomment-6876888 a one-line solution (not counting comments) that works pretty well. I copied the small function (since I already have time imported in my code) and named it “localtime2”. That way, there is no naming conflict.

Pour compliquer les choses, il existe deux versions de gmtime et localtime. L'une est définie dans le module time et l'autre dans le module utime. Actuellement, elles pointent toutes vers les mêmes valeurs.

Ainsi, si vous souhaitez obtenir l'heure correcte quel que soit l'endroit où se trouve votre microcontrôleur à ce moment-là, vous devez jouer à un tas de jeux.

Puisqu'il n'y a pas de consensus sur ce sujet, examinons les jeux auxquels nous pouvons jouer pour obtenir les valeurs voulues/nécessaires, du moins pour l'instant.

La solution la plus simple à ce stade est d'écrire votre propre fonction d'heure locale qui prendra l'information sur le fuseau horaire et renverra une heure locale correcte. Cependant, vous n'avez pas besoin de faire cela, puisque j'en ai trouvé une pour vous.

L'utilisateur MrEbbinghaus a posté une solution rapide le 31 août 2023 à l'adresse https://github.com/orgs/micropython/discussions/11173#discussioncomment-6876888, une solution en une ligne (sans compter les commentaires) qui fonctionne plutôt bien.

J'ai copié la petite fonction (puisque j'ai déjà importé le temps dans mon code) et je l'ai nommée « localtime2 ». De cette façon, il n'y a pas de conflit de nom.

Now, you would think that the TZ_OFFSET value should be simply the hour offset, either positive or negative based on your offset. Here where I live, “Standard” time is 6 hours behind UTC and “Daylight Saving Time” is 5 hours behind UTC. So I should use -6 as the TZ_OFFSET during “Standard” time and -5 as the offset during “Daylight Saving Time”, right? NOOOOOOOOOO You have to convert that hour offset to seconds first. WHAT? Yep. Now, we all know that there are 60 seconds in a minute and 60 minutes in an hour, so that would be 60*60*-6 (-21600) or 60*60*-5 (-18000). THOSE are the numbers you need to send into the “replacement” localtime function . To make matters worse, the ntptime module is NOT very user friendly. Nor is the NTP Timeserver. Many times, the ntptime server doesn’t return anything at all and sometimes it returns a value that will crash your program. When that happens, the “current thinking” is that the NTP server is sending you a KoD or “Kiss-’o-Death” packet that basically says you have made too many calls in too short a time.

On pourrait penser que la valeur TZ_OFFSET est simplement le décalage horaire, positif ou négatif en fonction de votre décalage. Là où je vis, l'heure « standard » est décalée de 6 heures par rapport à l'UTC et l'heure d'été est décalée de 5 heures par rapport à l'UTC. Je devrais donc utiliser -6 comme TZ_OFFSET pendant l'heure « standard » et -5 comme décalage pendant l'heure d'été, n'est-ce pas ?

NOOOOOONNNN

Il faut d'abord convertir ce décalage horaire en secondes. QUOI ?

Oui. Nous savons tous qu'il y a 60 secondes dans une minute et 60 minutes dans une heure, ce qui donne 60*60*-6 (-21600) ou 60*60*-5 (-18000). Ce sont ces nombres que vous devez envoyer à la fonction de « remplacement » de l'heure locale.

Pour ne rien arranger, le module ntptime n'est PAS très convivial. Il en va de même pour le serveur de temps NTP. Souvent, le serveur ntptime ne renvoie rien du tout et parfois il renvoie une valeur qui fera planter votre programme. Lorsque cela se produit, la « pensée actuelle » est que le serveur NTP vous envoie un paquet KoD ou « Kiss-'o-Death » ( baiser de la mort) qui dit en gros que vous avez fait trop d'appels dans un laps de temps trop court.

According to the RFC4430: Best Practices document for the NTP servers: “A client MUST NOT under any conditions use a poll interval less than 15 seconds.” Not something that is known by just everyone! But, ok. How can we handle this better than a try/Except loop? It seems that Peter Hinch (who I’ve mentioned more than once in my MTMT articles) has created a ‘portable’ NTP module of his own. You can find it at https://github.com/peterhinch/micropython-samples/blob/master/ntptime/ntptime.py . I would copy the code and place it in a module all on its own with a name like “port_ntptime”. The nice thing about Peter’s module, is that not only can you pass a timezone value (again in seconds) it also will return either the proper time value (in seconds) OR a 0 if the NTP Server failed to respond in time. There’s a discussion of this at https://github.com/peterhinch/micropython-samples/blob/master/README.md#414-ntp-time and a larger discussion at https://forum.micropython.org/viewtopic.php?t=12726.

Selon le document RFC4430 : Best Practices document for the NTP servers (Document des bonnes pratiques pour les serveurs NTP) « Un client NE DOIT EN AUCUN CAS utiliser un intervalle d'interrogation inférieur à 15 secondes ».

Ce n'est pas quelque chose qui est connu de tout le monde ! Mais d'accord. Comment pouvons-nous gérer cela mieux qu'une boucle try/Except ?

Il semble que Peter Hinch (que j'ai mentionné plus d'une fois dans mes articles sur MCML) ait créé son propre module NTP « portable ». Vous pouvez le trouver à l'adresse https://github.com/peterhinch/micropython-samples/blob/master/ntptime/ntptime.py.

Je copierais le code et le placerais dans un module à part entière avec un nom comme « port_ntptime ».

Ce qui est intéressant avec le module de Peter, c'est qu'il permet non seulement de passer une valeur de fuseau horaire (toujours en secondes), mais qu'il renvoie également soit la bonne valeur de temps (en secondes), soit un 0 si le serveur NTP n'a pas répondu à temps.

Il y a une discussion à ce sujet à https://github.com/peterhinch/micropython-samples/blob/master/README.md#414-ntp-time et une discussion plus longue à https://forum.micropython.org/viewtopic.php?t=12726.

Unfortunately, Peters module doesn’t actually set the time, it just gets the time. So, here is how you can use his code and still get the time set properly: trials=10 while trials > 0: lt=port_ntptime.time() Lt is the return value. Check to see if it is 0 or not. If it is, then call the ntptime.settime() function. If not, sleep for 15 seconds and then try again (top right). The ntptime.settime() function will force the corrected time into utime.gmtime. Again, we have the issue that gmtime and localtime are the same, so you should remember when you want to use localtime, use the localtime2 function. Hopefully, this will take the problems away from my code I want to present next month and allow me to get it tested completely without it crashing. Until next time, as always; stay safe, healthy, positive and creative!

Malheureusement, le module de Peters ne règle pas l'heure, il ne fait que l'obtenir. Voici donc comment vous pouvez utiliser son code tout en réglant correctement l'heure :

trials=10 while trials > 0 : lt=port_ntptime.time()

lt est la valeur de retour. Vérifiez si elle est égale à 0 ou non. Si c'est le cas, appelez la fonction ntptime.settime(). Si ce n'est pas le cas, attendez 15 secondes, puis réessayez (en haut à droite).

La fonction ntptime.settime() forcera le temps corrigé dans utime.gmtime. Une fois de plus, le problème est que gmtime et localtime sont identiques ; vous devez donc vous rappeler que lorsque vous voulez utiliser localtime, vous devez utiliser la fonction localtime2.

J'espère que cela va régler les problèmes du code que je veux présenter le mois prochain et me permettre de le tester complètement sans qu'il ne plante.

Jusqu'à la prochaine fois, comme toujours, restez en sécurité, en bonne santé, positifs et créatifs !

issue203/micro-ci_micro-la.txt · Dernière modification : 2024/04/03 12:38 de auntiee