Hors-série. Du code. Partie 2.

Section un peu plus technique pour les intéressés, qui vient compléter l’article précédent.

Je met à disposition l’ensemble des codes sources dont je me suis servi / dont je me sers ainsi que des portions de code annexes que je mettrais aussi probablement en suivant. Il faut garder en tête que c’est une base de travail, des premiers jets, des expérimentations etc… et non du code pro (un pro se gausserait surement d’ailleurs, mais c’est en apprenant / testant que l’on progresse !). Mais ça peut faire gagner du temps si on souhaite partir d’une base ou prendre le train en marche sans devoir tout réécrire depuis 0. Ils peuvent aussi servir de tests de montages. En effet, si on a un montage déjà fait, il suffit de brancher les voies logiques correspondantes, envoyer le code et ça fonctionne.

Je suis moi-même parti de 0 et j’aurais gagné du temps si j’étais tombé sur ce genre de fichiers, donc peut-être que d’autres sont dans mon cas ? Le code est maison, donc, il est imparfait, oui, mais je l’ai placé sous la licence la plus permissive possible à ma connaissance, donc n’importe qui peut en faire ce qu’il veut (y compris ne rien en faire et aller boire un thé au lieu de le lire).

C’est ici : https://github.com/pierre-ec/raito

Je partage donc le dossier Raspberry Pi, qui contient du code en Python. On y trouve par exemple le fichier Interface Graphique qui contient un programme minimal permettant de contrôler en direct, en temps réel, la désynchronisation entre le temps d’allumage et d’extinction des LED. En pratique ce sont deux scales, aux teintes de gris splendides, qui ravira n’importe quel amateur d’art j’en suis sûr, des widgets de Tkinter, qui envoient leur valeur sur une fonction calée sur deux time.sleep().

On peut se demander en voyant le led_thread, mais pourquoi créer une classe qui fait tourner un booléen en parallèle ? Pourquoi pas faire tourner le while direct ?

Tout simplement parce que faire tourner un while directement, même calé sur un compteur que l’on incrémente fera freezer, et c’est logique, l’interface graphique, et on ne pourra pas modifier la valeur du scale en temps réel. Le slider va défreezer à la sortie de la boucle, 5000 tours de pistes après. Il y a mieux, en terme de temps réel.

L’astuce donc, c’est de se servir du threading. Il faut en pratique créer une classe, et définir au moins trois fonctions dans cette classe, une pour initialiser, une pour faire tourner le while, et une pour stopper le thread. Il faut définir un booléen qui, tant qu’il sera vrai, fera tourner la fonction qui prendra en paramètre les deux inputs des sliders. Il faut bien penser au DoubleVar() puisque ce seront des flottants. J’ai mis une résolution de 0.0001 et j’ai allongé les sliders pour être tranquille mais ces paramètres se gèrent à souhait, ce sont des simples paramètres du scale. Pour finir, il faut simplement appeler, via un bouton par exemple, une fonction qui va lancer le thread en question.

C’est à optimiser (beaucoup), c’est à corriger, et il existe d’autres méthodes pour arriver à ce résultat, mais cela reste un outil assez pratique pour tester rapidement, sans modifier du code, à la volée, certaines transitions ou certaines fréquences, en gardant en tête qu’on ne travaille ici qu’avec des approximations. Bien sûr, ce que je nomme ici « fréquence d’allumage » et « fréquence extinction » sont des termes impropres au sens strict, mais ils sont assez imagés, et ça fait le café, pour du test rapide.

Dans tous les codes, même sur l’Arduino, pour l’instant, il n’y a pas (encore?) de correction de la dérive temporelle. Je n’ai pas encore implémenté cette correction. C’est assez corsé, mais pas impossible, il faut se servir du timer interne et penser un peu différemment le flicker.

Concernant le code, pour l’Arduino notamment, vous constaterez si vous ouvrez un des programmes que j’ai nommé une série de fonction avec un suffixe « cen » (desync_cen, extcen, extcen_2…). J’ai tout simplement implanté une led centrale dans le montage et ce sont des fonctions qui jouent avec. Le montage le permet tout à fait, tout est indépendant en terme logique grâce aux transistors.

Dans le cas de l’Arduino, pour gérer l’intensité de la led centrale on sert du PWM. Il est implémenté facilement avec un analogWrite(). En définissant une variable qui se baladera entre 0 (intensité minimum) et 255 (intensité maximum, en fait, un 255 est un digitalWrite(pin, HIGH)) – dans mon cas « lumen », même si bien évidemment, elle ne prend pas en paramètres les vrais lumens mesurés, c’est une image – et en choisissant une led adaptée on peut gérer des fades souples, juste au code, sans potentiomètre physique.

Astuce vraiment pratique. La fonction analogWrite() ne fonctionnera donc logiquement que sur des broches PWM (j’ai mis la 11 dans mon cas, sur un Arduino Uno). C’est une fonction implémentée par les devs de l’Arduino qui calcule le duty cycle en prenant en paramètre le nombre que l’on insère (dans une longue suite de case / break), et l’on a ainsi pas à s’en préoccuper.

Sur un Pi, c’est un peu différent, il faut le faire à la main en servant de la sortie GPIO 12 (en notation BOARD, sinon c’est 18). Par exemple, avec un :

duty_cycle = 10.0
p = GPIO.PWM(pin, frequence_de_depart)
p.start(duty_cycle)
p.ChangeDutyCycle(nouveau_duty_cycle)
p.ChangeFrequency(nouvelle_frequence)

Avec un montage adapté, fonctionne tout aussi bien.


Pourquoi désynchroniser les time.sleep() ? N’a-t-on pas un meilleur effet sans désynchro ?

Expérimentalement, en tout cas, sans correction de la dérive (mais j’ai le sentiment que même en corrigeant la dérive, ceci reste vrai), et après de nombreux tests, je peux répondre clairement : non, on n’a pas de meilleur effet sans désynchro. Elle apporte visuellement beaucoup, et ce qui était au départ un sentiment se transforme en certitude : je pense que les devs de pando et de lucia (je ne connais que celles là) ont implémenté un système de désynchronisation et que les technos ne fonctionnent pas en ondes « symétriques » (avec un rapport de cycle de 50/50) en permanence, en tout cas, pas tout le temps, et pas sur tous les programmes. Ces technos ne sont pas de simples stroboscopes, ça serait trop simple…

La led centrale apporte-t-elle un plus ? Ne pourrait-on pas se contenter des leds periphériques simples ?

Sans détour : oui, un énorme plus. Un led centrale avec un blanc plus chaud que les leds periphériques modifie beaucoup les visuels, plus que ce que j’aurais pu penser. On peut surement contourner ou faire autrement, mais il faut je pense gérer dans le code une lumière de fond, avec un PWM par exemple. Je pense également qu’il y a un vrai plus à la désynchroniser par moments aussi. En fait, j’ai le sentiment qu’on y gagne à s’en servir, et pas seulement en fade / ambiance.

C’est pour cela que j’ai implémenté des fonctions du type :

void desync_cen(float fqa, float fqe)

{
taf=(1/fqa)*500;
tef=(1/fqe)*500;

analogWrite(LedCentrale, lumen);
delay(taf);

analogWrite(LedCentrale, lumen);
delay(tef);

}

void extcen(float fqa, float fqe)

{
taf=(1/fqa)*500;
tef=(1/fqe)*500;

digitalWrite(Led1, HIGH);
digitalWrite(Led2, HIGH);
digitalWrite(Led3, HIGH);
digitalWrite(Led4, HIGH);
digitalWrite(Led5, HIGH);
digitalWrite(Led6, HIGH);
digitalWrite(Led7, HIGH);
digitalWrite(Led8, HIGH);
digitalWrite(LedCentrale, LOW);
delay(taf);

digitalWrite(Led1, LOW);
digitalWrite(Led2, LOW);
digitalWrite(Led3, LOW);
digitalWrite(Led4, LOW);
digitalWrite(Led5, LOW);
digitalWrite(Led6, LOW);
digitalWrite(Led7, LOW);
digitalWrite(Led8, LOW);
digitalWrite(LedCentrale, HIGH);
delay(tef);
}

Pourquoi modifier l’ordre de grandeur de la fréquence sur le calcul du delay / time.sleep() [taf=(1/fqa)*500, etc…] ?

Initialement, c’était pour avoir une fréquence « vraie » sur la fonction swg puisque la variable temps était la même pour l’allumage et pour l’extinction. Donc je divisais par deux pour obtenir par exemple sur 1 Hz, donc un cycle allumage – pause 0,5 sec – extinction – pause 0,5 sec.

Ce n’est plus tellement justifié en calculant deux valeurs distinctes. Expérimentalement, pour s’y « retrouver », mais rien d’objectif donc.

Pourquoi n’as-tu implémenté que la fonction desync_full sur l’interface graphique ?

C’est un début ! Il a fallu faire un choix dans cette configuration, donc j’ai pris logiquement la desync_full. Quand (et si) j’en saurais plus, et que je serais plus à l’aise en Python, je ferais bien sûr différemment. J’ai beaucoup d’idées, mais en pratique, c’est difficile à implémenter à mon niveau, à l’heure où j’écris ces lignes.

Publicités

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s