Comment rafraichir un ecran oled en recevant des instructions IR

Bonjour
je souhaite afficher l’heure sur un ecran OLED et en meme temps réagir aux actions d’une telecommande IR.
pour l’ecran j’ai créé une classe :

class Welcome():
  def __init__(self,  **Arguments):
      global time_var
      global date_var  
      draw=ImageDraw.Draw(image_bw) 
      
      while True:

        oled.image(image_bw)
        #efface le texte de la frame précédente
        draw.text((55,2),time_var,font=font1,size=1,fill=0)  
        draw.text((40,45),date_var,font=font2,size=1,fill=0)  
        set_time()
        #update la frame avec l'heure réactualisée
        draw.text((55,2),time_var,font=font1,size=1,fill=1)  
        draw.text((40,45),date_var,font=font2,size=1,fill=1)  
        oled.show()

ce code fonctionne bien. Dans la boucle while j’ai ajouté :

 event = dev.read_one()
 if (event):                
        print("You pressed: ")
        print(event.value)

le code réagit bien à une action de la télécommande mais il renvoie plusieurs events pour une action simple. De plus il renvoie des events parasites associés au code 0. Pour être plus clair un exemple. Si j’appuie sur la touche 24, le résultat est :

 You pressed: 24 
 You pressed: 0 
 You pressed: 24 
 You pressed: 0 
 You pressed: 24 
 You pressed: 0 

j’ai donc 3 évenements associés à la touche 24 et 3 associés à la touche 0 alors que je n’attends qu’un évènement unique associé à la touche 24. Ce comportement n’est pas dû à l’association avec le code d’affichage OLED car il se produit identiquement si je garde uniquement les 4 lignes précédentes dans une boucle while.

J’ai essayé l’autre méthode :

timer = 0.0
tolerance = 0.5 
for event in dev.read_loop():
    if timer == 0.0 or time.time() > timer + tolerance:
        timer = time.time()
        
        print(event.value)

qui fonctionne bien seule. si je l’ajoute dans la boucle while, bien entendu il reste indéfiniment dans la boucle dev.read_loop() et l’affichage n’est pas réactualisé.

@ lelvelKro j’ai lu ton post Principe d'affichage OLED mais je n’y ai pas trouvé la solution.

Les OLED de ce type, dans mes démos, ont besoin d’une image envoyé à ce dernier.

Le plus « lourd » pour le système sera de charger une image depuis un fichier ou créer la base d’une image vide.

Il faut alors commencer un script et utiliser un Draw créé au lancement. C’est un peu comme utiliser un « Écran Magique » ( Écran magique — Wikipédia ) au lieu de feuille blanche.

Alors tu dessine dans ce « Draw » ce que tu veux qu’a la fin du While (avant la répétition) soit afficher.

Sur chaque cycle, tu as au début la capture des touches (physique ou IR) et par la suite le travail du rendu attendu.

Prenons un exemple, afficher l’heure et la date. Dans cet exemple tu aurais un bouton physique et une commande via IR. le bouton physique passe l’heure du AM/PM au mode 24hr (et vice versa) et la commande IR affiche la date, ou l’heure, selon ce qui est présent.

  1. Tu démarre le script, tu établie l’action par défaut « display=time », et les valeur de design « time=24h » et crée une image vide (le Draw)
  2. Le While commence, vérifie les états du boutons et IR (tous non utilisé), alors il passe a l’action d’afficher l’heure, en format 24H, l’image est créé et à la fin du While, il l’envoie au OLED. Résultat : 13:24
  3. Le While recommence, cette fois tu as la touche physique pressé, alors vu que « time=24h » est utilisé, il passe en « time=ampm »,
  4. Vu que le « display=time », il affichera l’heure, mais vu que la valeur est « time=ampm », il va le mettre dans l’autre format pour le Draw, il effacera le Draw actuel et génère le nouveau, le résultat à la fin du While sera : 01:24pm
  5. La on touche plus a rien, alors il boucle toujours avec les mêmes valeurs, alors il va généré toujours la même image. Pour éviter du travail inutile, tu peux ajouter l’heure en valeur, si elle ne change pas, ainsi que les autres valeurs, il ne va pas regénérer l’image mais simplement la réutilisé.
  6. Tu appui sur la touche de ta commande IR, ce sera la même chose, il détecte l’état , alors il passe « display=time » à « display=date »
  7. L’action change, il efface l’intérieur du Draw, il doit afficher la date, alors il passera le code associé et va ajouter au Draw le « 29/10/2024 » et à la fin du While ce sera sa qui sera visible.
  8. Tous comme pour l’heure, si aucune variable change, il boucle sur le même dessin. Pour voir le changement de date, tout comme l’heure, tu enregistre cette valeur et si elle est différente de l’état actuel, seulement la il va recréer l’image.
  9. Petit ajout, si tu veux que la date soit affiché 5 secondes avant de repasser à l’heure, tu enregistre le unixtime du changement d’état à date, et à chaque While il vérifie la différence entre ce unixtime et celui actuel, quand il dépasse le temps désiré (5s) il change la valeur « display=date » à « display=time ».

Bref, chaque While sera ton rafraichissement de l’écran (vsync) et dans ce While tu as ;

  1. Détection des touches → Changement des valeurs (si il y a)
  2. Traitement des valeurs → Les actions
  3. Envoie du rendu généré par ce cycle

je crois que c’est ce que j’ai essayé de faire comme dans cet exemple :

class Welcome():
 def __init__(self,  **Arguments):
  global time_var
  global volume
  global date_var  
  global key
  draw=ImageDraw.Draw(image_bw) 
      
  while True:
    
    event = dev.read_one()
    if (event):                
            print("You pressed: ")
            print(event.value)    
            #traitement des actions sur la télécommande
            if event.value==3 :
                menu=Menu()
                self.logout
                break
            if event.value==51 :
                volume = max(volume - 10,0)
                player.audio_set_volume(volume)
            if event.value==43 :
                volume =  min(volume + 10,200)
                player.audio_set_volume(volume)
            # if event.value==0 :
                # veille=pause_sound()
                # self.logout
                # break
    else :
            #rafraichissement de l'écran
            oled.image(image_bw)
            draw.text((55,2),time_var,font=font1,size=1,fill=0)  
            draw.text((40,45),date_var,font=font2,size=1,fill=0)  
            set_time()
            draw.text((55,2),time_var,font=font1,size=1,fill=1)  
            draw.text((40,45),date_var,font=font2,size=1,fill=1)  
            oled.show()

la commande event = dev.read_one()
le pb c’est d’effectuer la capture des touches :

Sur chaque cycle, tu as au début la capture des touches (physique ou IR) et par la suite le travail du rendu attendu.

je n’arrive pas à faire ça . elle duplique chaque touche saisie en intercalant la saisie fictive de la touche zéro comme indiqué plus haut.

Vérifie plutôt l’état des bouton, si la commande attend une réponse, tu bloque assurément le script. Tandis que si tu y va par cycle, tu peux alors simplement vérifier leur état. De cette manière, tu peux également déterminer des bouton par longueur de pression.

Par exemple, dans ton cycle tu vérifie si le bouton physique X est pressé, si oui, tu set une variable comme butX=True (vu qu’il était False). après tu peux définir butXtime pour être le temps unix.

Dans le second loop, il redétecte la position, tu peux alors faire pour la détection de X si il est toujours pressé. SI il ne l’est plus, alors tu lance l’action. Mais tu peux ajouter une vérification pour savoir si le moment qui n’appuie plus sur X et le moment qu’il a commencé est plus grand de 5 secondes par exemple. Dans ce cas la il pourrait faire une commande différente. Si il ne change pas d’état, il continue…

Exemple concret
Si tu appui moins de 5 secondes, tu lance un reboot, si tu appui plus de 5 secondes, tu lance la fermeture complet.

Alors ton script, dans le while devras;

  1. Vérifier les états des boutons, et selon les contexte
  2. Exécuter une action selon l’état des variables
  3. Afficher le rendu

C’est ce que j’explique dans le post que tu as référé.

Faut voir l’usage dans une globalité, et ce à chaque « frame ». Une application GUI va généré un rendu et attendre la réponse dans des champs. Tandis que ici, tu n’as en fait aucune interface. Tous est généré à chaque cycle selon les valeurs.

Ton Script est le template du rendu, et chaque cycle prend les paramètres (valeurs) et ceci affecte le travail du template.

Si tu te base directement sur mon code, avec les modif pour voir l’état des boutons et IR selon ton système, tu arriverais à faire ce que j’explique, il te resterais qu’a ajuster selon tes besoin.

Visite Téléchargement - levelKro.net - Browser et tu trouveras mes codes zippé

  • python-oled-drivers.zip C’est mon pack du pilote Waveshare, mais aussi de smes codes. Dans el dossier olddrv, tu retrouveras l’essentiel de ce que je tente d’expliquer ici.
  • python-piButtons.zip Juste par référence, comment capturer les boutons sur RPi.
  • python-piMyKey.zip Mon projet intégrale. J’ai abandonné vu que je n’ai pas réussi à faire tout ce que je désirais, vu certains problèmes variés, mais ce qu’ont parle ici marche nickel.

ta méthode avec le flag boolean fonctionne. Pour le pb du zéro, je suis obligé de détecter la séquence 0 0 avec deux flags boolean capture01 et capture02 :

  capture01=0
  capture02=0
  capture3=0
  capture51=0
  capture43=0
    
  while True:
    
    
    event = dev.read_one()
    if (event) :                
            print("You pressed: ")
            print(event.value)   
            print(capture01)   
            print(capture02)   
            
            #traitement des actions sur la télécommande
            if event.value==0:
                 if capture01==0:
                    capture01=1
                 else:
                    capture02=1
            else:
                capture01=0
                capture02=0
                    
            if event.value==3 :
                capture3=1
            if event.value==51 :
                capture51=1
            if event.value==43 :
                capture43=1
                
            if capture3==1:
                menu=Menu()
                self.logout                
                break
                
            if capture51==1:
                volume = max(volume - 10,0)
                player.audio_set_volume(volume)
  
            if capture43==1:
                volume =  min(volume + 10,200)
                player.audio_set_volume(volume)
              
            if capture02==1 :
                player.audio_set_volume(0)

c’est bien bourrin. Il doit surement y avoir un moyen plus simple.

Ton seul problème est que tu attend un changement d’état pour savoir quel entrée il y a. Mais ce que tu dois faire, être de questionner tout les entrées pour savoir si il y a un changement d’état.

Dans ta manière de traiter, je ne crois pas que tu pourras par exemple prendre en charge le multi-pression. Ce que ma méthode permet.

C’est comme avoir 5 personnes devant soit, dans ta méthode, tu attend que l’un des cinq te parle pour actualisé le résultat. tandis que moi, je demande a chacun si y veux me parler et une fois demandé au 5 j’actualise la situation et je recommence.

C’est pour sa que ton résultat stop et c’est ça qui faut enlever. Ta manière de travaillé exige qu’a chaque loop une action est fait par l’utilisateur.


Pause ton code, et examine le mien, amuse toi avec ce que j’ai dans un but de test et comprendre comment il travail, je crois que de cette façon tu pourras mieux intégrer de quoi pour arrivé au résultat souhaité.

Ou du moins, commence par créer un affichage sans interaction, comme sa tu maitrisera déjà l’affichage. Après, intègre les actions, tu arriveras surement à comprendre l’intégration des deux.

ok merci je vais prendre le temps de lire tes sources.

je n’arrive pas à télécharger tes sources, j’essaierai plus tard.
Dans ton msg je ne comprends pas bien qd tu dis

tu attends un chgt d’état

.En fait l’instruction:

    event = dev.read_one()
    if (event) :

n’attend pas un chgt d’état, elle lit s’il y a un chgt d’état. S’il y a un chgt d’état event vaut TRUE et seulement ensuite elle cherche si l’une des touches utilisées est concernée par ce chgt d’état. S’il n’y a pas de chgt d’état (else) alors elle rafraîchit le display. C’est pour ça que dans mon esprit j’utilisais la même méthode que toi.

Je suis a travailler sur mon serveur pour certains trucs, c’est sur qui risque d’avoir des bugs parfois, le maudit FPM de PHP c’est mis sur le port de la radio et sa crée un conflit, je déteste me battre pour des connerie comme ça.

Devrais être ok la.


Bon si ton dev.read_one() n’attend pas, change alors ton if/else, car tu n’aurais pas de bon résultats. Tu dois afficher le rendu peut importe si une entré, ou non, est fait, car actuellement tu travail le event OU tu donne un affichage, et non les deux dans le même while (cycle), et ça, c’est pas bon. Alors par exemple si l’état est toujours True, tu n’auras jamais de rafraichissement de l’affichage.

oui je comprends ça.

j’ai regardé ton code oled.py
c’est bien comme tu l’expliques dans ton post mais tu n’utilises pas d’IR remote, mais juste des if GPIO.input. Les difficultés proviennent de la gestion des events IR liés à la lib evdev. J’ai pas d’autre choix pour gérer les interactions IR que les 2 commandes dev.read_one() ou
dev.read_loop().
Si l’état est toujours true ça signifie qu’on appuie sans cesse sur un bouton. Dans ce cas, vaut mieux pas updater le frame.

En effet, mon code ne prend en compte le IR.

Mais je te conseil de modifier le code quand même pour prendre en compte l’actualisation de l’affichage à chaque cycle, sauf si aucun changement est a y faire. Car par exemple, si tu laisse enfoncer pour changer le volume, a chaque cycle tu peux alors le modifier (le volume) et actualisé l’affichage. Et si l’utilisateur appui sur une touche qui ne fait aucune action, tu ne pénalise pas l’affichage, et aussi, tu assure d’apporter la modification a l’affichage rapidement.

Sinon il y a une autre solution, soit exécuter deux scripts en même temps, l’un pour l’affichage et l’autre pour le contrôle. Ton affichage vérifiera alors que les choses qui lui sont destiné. Tu peux utiliser une DB SQL ou en fichier pour faire passer des valeurs de l’un a l’autre.

Par exemple, tu as une horloge en display, et ta manette contrôle le volume et une lecture en arrière plan. Tu n’as pas besoin de l’affichage pour contrôler la musique, et cet affichage peut par divers moyens, savoir si une lecture est en court (process ou DB).

Il existe plusieurs manière de tout faire ça. A toi de trouver la meilleur pour ta situation. Mais je crois que tu as tout en main pour analyser et ajuster pour tes besoins.

Good luck.

merci bcp pour tous tes conseils utiles