Accueil > Forum > Informatique > Programmation > boucle gourmande en cpu script python

boucle gourmande en cpu script python

JissJisseone JissJisseone 79 Messages
Bonjour à toutes et tous. Voila, j'ai bricolé un lecteur de badge RFID couplé à ma domotique maison pour pouvoir tout fermer ou tout ouvrir lorsque je pars ou je rentre à la dite maison. Le badgeur est derrière une cloison à l’entrée de la maison et chaque fois que je m'en vais, je badge: les lumières s’éteignent, les capteurs PIR  et de portes s'activent, ainsi que les notifications d'alerte  sur mon portable et une notification verbal sur mon google home.
Bon mon installation fonctionne, elle tourne sur un raspberry3 et mon script est en python. Mais ce script est particulièrement gourmand en ram. Mon proc tourne à plus de 30%, alors qu'il devrait plafonner à 5-10%
Après quelques recherches, mon script émet un loop (une boucle) qui fait travailler le PI en permanence, alors qu'il  devrait travailler seulement au moment ou il est sollicité (par le passage du badge) .
Le problème c'est que je n'ai qu'une connaissance TRES limitée des scripts. Je sais les lire, les bidouiller (un tout petit peu), mais incapable de les écrire. Je les recupere sur le net, et les traficote. Alors j'ai bien compris qu'il y a un TIMESLEEP (x) à placer quelque part pour mettre la boucle en veille, mais quelque soit mes essais, j'ai une erreur qui apparait. Je vous sollicite donc pour voir si un œil plus expert détecte une erreur grossière  .
Je vous fais suivre le script en question. Merci à vous.


#!/usr/bin/env python
# -*- coding: utf8 -*-
 
import RPi.GPIO as GPIO
import MFRC522
import signal
import time
import requests
import urllib
import json
############# Parametres #################################


#~~~~~~~~~~ Parametres Domoticz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
domoticz_ip="192.168.1.17"
domoticz_port="8080"
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# IDX de Domoticz
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

# remplacer XXX par votre idx de votre widget
dummy_idx={'tagjisse': 195}

dummy_idx1={'tagmarius': 242}

continue_reading = True

# Capture SIGINT for cleanup when the script is aborted
def end_read(signal,frame):
    global continue_reading
    print ("Ctrl+C captured, ending read.")
    continue_reading = False
    GPIO.cleanup()
 
# Hook the SIGINT
signal.signal(signal.SIGINT, end_read)
 
# Create an object of the class MFRC522
MIFAREReader = MFRC522.MFRC522()
 
# Welcome message
print ("Lancement script autorisation")
print ("Press Ctrl-C to stop.")
 
# This loop keeps checking for chips. If one is near it will get the UID and authenticate
while continue_reading:
    
    # Scan for cards    
    (status,TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
 
    # If a card is found
    if status == MIFAREReader.MI_OK:
        print ("Card detected")
    
    # Get the UID of the card
    (status,uid) = MIFAREReader.MFRC522_Anticoll()
 
    # If we have the UID, continue
    if status == MIFAREReader.MI_OK:
 
        # Print UID
        print ("Card read UID: "+str(uid[0])+","+str(uid[1])+","+str(uid[2])+","+str(uid[3])+','+str(uid[4]))  
        # This is the default key for authentication
        key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]
        
                
        # Select the scanned tag
        MIFAREReader.MFRC522_SelectTag(uid)
       

        
        #ENTER Your Card UID here
        my_uid = [xxx,xx,xx,x,41] 
        my_uid1 =[xxx,xx,xxx,xxx,189]
        
                
        #Check to see if card UID read matches your card UID
        if uid == my_uid:                #OK if matching UIDs
                 requete='http://xxx.xxx.x.x@'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx['tagjisse'])+'&switchcmd=On&level=0'
                 urllib.urlopen(requete)

                 print("Access validé")
                 print("lancement de la requete")
                 time.sleep(5)
                 print("pret")
                                        
                                       
        

        #Check to see if card UID read matches your card UID
        if uid == my_uid1:                #OK if matching UIDs
                 requete='http://'+domoticz_ip+':'+domoticz_port+'/json.htm?type=command¶m=switchlight&idx='+str(dummy_idx1['tagmarius'])+'&switchcmd=On&level=0'
                 urllib.urlopen(requete)

                 print("Access validé")
                 print("lancement de la requete")
                 time.sleep(5)
                 print("pret")

        
##        # Authenticate
##        status = MIFAREReader.MFRC522_Auth(MIFAREReader.PICC_AUTHENT1A, 8, key, uid)
##
##        # Check if authenticated
##        if status == MIFAREReader.MI_OK:
##            MIFAREReader.MFRC522_Read(8)
##            MIFAREReader.MFRC522_StopCrypto1()
##        else:
##            print "Authentication error"
lobodol lobodol 699 Messages BIG BOSS
Salut JissJisseone,

Super ton installation ! Si l'envie de te prends de nous faire un tuto hésite pas, ça m'intéresse beaucoup.

Par contre je vois plusieurs choses à revoir d'un point de vue sécurité :

1. Ton Domoticz utilise le port 8080, j'en déduis donc que la connexion n'est pas sécurisée. Je t'invite donc à utiliser du SSL : https://www.domoticz.com/wiki/Native_secure_access_with_Lets_Encrypt

2. Ensuite, évite de publier les UUID de tes cartes sur le forum, tu dois garder ses informations secrètes.

3. En ce qui concerne la clé d'authentification, n'utilise pas la valeur par défaut car elle est connue de tous. Et pareil, ne publie pas ici sa valeur qui doit rester secrète.

4. D'après l'exemple du répo officiel (https://github.com/mxgxw/MFRC522-python/blob/master/Read.py) on dirait qu'il te manque la partie la plus importe à savoir l'authentification de la carte :


# Authenticate
status = MIFAREReader.MFRC522_Auth(MIFAREReader.PICC_AUTHENT1A, 8, key, uid)

if status == MIFAREReader.MI_OK:
    # Authentication success
else:
    print "Authentication error"        


5. Je ne vois pas de token ou quoi que ce soit qui sécurise la requête HTTP qui déclenche les actions Domoticz. Est-ce que ça veut dire que n'importe qui se trouvant sur ton réseau domestique peut déclencher ces actions juste en appelant l'url ?
Tu dois impérativement sécuriser l'API Domoticz, au minimum avec un token : https://www.domoticz.com/wiki/Domoticz_API/JSON_URL%27s#Authorization

my_uid et my_uid1 sont deux cartes différentes, c'est bien ça ? Et elles déclenchent toutes deux la même chose ? Du coup optimise ton code de la sorte :

if uid == my_uid1 or uid == my_uid1:

C'est un peu plus optimisé niveau perfs comme ça.

Sinon, je ne vois rien de choquant.
JissJisseone JissJisseone 79 Messages
Merci de ta réponse (si rapide). Alors effectivement toutes les info sont sous mon réseau "home", mais j'ai fais un report de port 8080 via un no ip pour les connections externes
j'ai un mot de passe et un pseudo pour me logger sur domoticz (les XXX.xxx.x.x que tu vois dans la requete http). Quant à mes cartes, le lecteur de badge est dans la maison, et il est juste la pour activer ou désactiver les scènes DEPART RETOUR a la maison (et différencier les personnes présentes ou absentes et lancer des scripts personnalises) , il fait office de déclencheur. Mais je te remercie de tes conseils (judicieux et avisés)et j'en prends bonnes notes. C'est vrai que dans mes requêtes je pourrais intégré un token...Et dans le coup: oui toute personne sur mon réseau domestique, peut envoyer des requêtes...mais bon ... craquer une protection WPA c'est pas donné à tous (meme avec du aircrack ou les outils KALI).
Pour le tuto, ça serait avec plaisir...
Pour l'optimisation..bin c'est de cela dont je parlais...je suis un peux léger au niveau programmation et surtout au niveau syntaxe...tu viens d'en voir les limites. Mais je teste les modif que tu me propose et te fais un retour rapide. Je te remercie de t’être penché sur mon problème. Je suis un grand fana de ton site.  J’échange beaucoup et avec plaisir.
Je te tien au courant avec tout ça . Bye. et merci
JissJisseone JissJisseone 79 Messages
j'ai fais une modif de mon post sur les uuid..
part contre, je ne comprends pas :

" En ce qui concerne la clé d'authentification, n'utilise pas la valeur
par défaut car elle est connue de tous. Et pareil, ne publie pas ici sa
valeur qui doit rester secrète"
,,???
donc mon script est gourmand en cpu  26% memoire vive et  28% en
processeur...(56 degres sur le rasp) pfff;;un tracteur le truc..
lobodol lobodol 699 Messages BIG BOSS
j'ai un mot de passe et un pseudo pour me logger sur domoticz (les XXX.xxx.x.x que tu vois dans la requete http)
Ok cool, c'est un bon début. Le souci que je vois avec ça c'est que tes credentials sont contenus dans l'URL et donc visibles par quelqu'un qui écouterait ton réseau. D'où l'intérêt d'utiliser un token passé en header de ta requête couplé à du HTTPS (sinon le token aussi transite en clair).
.mais bon ... craquer une protection WPA c'est pas donné à tous
Je vois beaucoup trop ce genre de justification au quotidien dans mon boulot :"on est une petite boite, personne ne va nous cibler", "s'introduire sur notre réseau ça risque pas de nous arriver", etc. Jusqu'au jour où ça arrive.

Le seul truc valable c'est : zero trust. Peu importe où se trouve ton système (sur un réseau privé, derrière un firewall, derrière une porte blindée), tu appliques toutes les règles de sécurité élémentaires et ne fait confiance à personne qui n'a pas montré patte blanche.

La clé d'authent c'est ça :


# This is the default key for authentication        
key = [0xFF,0xFF,0xFF,0xFF,0xFF,0xFF]


A voir dans la doc comment changer sa valeur.

donc mon script est gourmand en cpu  26% memoire vive et  28% en processeur...(56 degres sur le rasp) pfff;;un tracteur le truc..
C'est bizarre ça. T'es sûr qu'il n'y a pas un autre process qui bouffe les ressources CPU ?

Utilise la commande bash suivante pour voir quel processus est le plus gourmand :


top
JissJisseone JissJisseone 79 Messages
Effectivement j'avais remarqué qu'en http, les requêtes faisaient apparaitre le token. D’ailleurs j'utilise ifttt pour certaines choses, et tous mes codes sont visibles. (paas bien)
Part contre en https, je devais mal m'y prendre parce que j'avais des blocages, des erreurs et j’étais obligé de repasser en http pour simplifier et voir mes requêtes aboutir.

mais ok ..no trust, c'est la consigne.
pour la key d'authent, je croyais que c’était la structure de base du codage du badge (je ne sais pas si ce que je dis est clair ) qui était annoncé dans le code.
Est à dire que si j'encode le secteur 0 avec cette key, je crée un badge universel? comme un passe partout?

Et enfin, j'ai utilisé la commande top, et comme prévu, il m'annonce un processus appelé Python a 100%(qui correspond à mon script). Si je fais un Kill dessus, mon processeur redescend à 1%d'utilisation, c'est bien ce script (et sa structure) qui affole mon proc...
Il y a bien une m...de sur ce script . Et en essayant de trouver une solution, j'ai vu que je crée un Loop qui fait travailler le PI en permanence. Il faudrait que j’intègre un sleeptime quelque part pour pouvoir le mettre en attente...mais ou et comment...ça ça me depasse
lobodol lobodol 699 Messages BIG BOSS
Ah mais oui ! C'est évidemment la boucle qui bouffe tout le CPU. Il y a d'ailleurs une issue d'ouverte sur le repository : https://github.com/mxgxw/MFRC522-python/issues/17
Tu as eu le bon réflexe de vérifier les perf de ton Rpi en tout cas

L'idéal serait d'utiliser des interruptions mais j'ai pas l'impression que cette lib le permette. Si tu ne sais pas ce que sont les interruptions, tu peux jeter un œil à cet article.

Jette un œil à ce tuto, peut-être que ça fonctionnera mieux.
JissJisseone JissJisseone 79 Messages
Merci pour toutes ces infos...(effectivement sur le github,je ne suis pas le seul avec mon pbs de loop). C'est tout à fait ça...une interruption dans la boucle (après lecture de ton article). Mais encore une fois, je vais le faire à taton...je modifie mon script, je le lance, et j'attends de voir les messages d'erreurs...la dure vie du bidouilleur quoi...
Je fais monitorer mes pi par DOMOTICZ, j'ai donc toutes les infos de perf et de temperature...comme ça, je vois en permanence si il y a un truc anormal. Vraiment, Domoticz est un FREEWARE super. Il est dedié à la domotique, mais on peut l'utiliser juste pour ce fameux monitoring des PI.
SORy7tZ
JissJisseone JissJisseone 79 Messages
Comme tu peux le voir, j'ai fais un kill sur mon script et TADA le pross redescend à 2.4%
JissJisseone JissJisseone 79 Messages
Bonjour lobodol.Crois tu que tu aurais 5mn pour jeter un coup d’œil à mes recherches sur les interruptions, ET SURTOUT, me dire comment les integrer à mon script? merci bcp


#!/bin/python

import sys
import time
import signal

def cb_sigint_handler(signum, stack):
    global is_interrupted
    print "SIGINT received"
    is_interrupted = True

if __== "__main__":
    is_interrupted = False
    signal.signal(signal.SIGINT, cb_sigint_handler)
    while(1):
        # do stuff here 
        print "processing..."
        time.sleep(3)
        if is_interrupted:
            print "Exiting.."
            # do clean up
            sys.exit(0)
lobodol lobodol 699 Messages BIG BOSS
Salut, un peu de lecture sur la lib "signal" : https://docs.python.org/3/library/signal.html

L'interruption SIGINT est déclenchée lorsque tu fais Ctrl+C. Ici ça n'est pas ce que tu souhaites. Il me semble que cette librairie gère les signaux système.
Toi tu as besoin d'une interruption lorsqu'un badge est approché du lecteur rfid.
JissJisseone JissJisseone 79 Messages
salut, merci de ta réponse (rapide). Effectivement c'est ce dont j'ai besoin...une mise en attente du LOOP...de la boucle que fait le script..en fait, mon script travail tout le temps, alors que l’idéale  cela serait que mon script soit "en veille" (en attente) et qu'il se mette au boulot à l'approche d'un badge.
Donc le script lancé et le lecteur rfid non solicité, je devrais tourner a 3-4% de cpu et des que j'approche le badge et que le script est solicité, Là et seulement là, le cpu pourrait s'emballer... mais pour une courte periode, et revenir au repos après.   pffff pas sur que ce soit clair mon truc
JissJisseone JissJisseone 79 Messages
Bon après recherche, il semble que des qu'il y a une condition IF THEN et bien le rasp fait tourner cette boucle en permanence, attendant qu'une exception se présente (passage du badge)...mais  comment mettre en attente cette boucle infinie IF THEN...là c'est un mystère...je pensais que c'etait precisement l'inverse (comme je disais sur le precedent post  "Donc le script lancé et le lecteur rfid non solicité, je devrais tourner a 3-4% de cpu et des que j'approche le badge et que le script est
solicité, Là et seulement là, le cpu pourrait s'emballer...........etc etc")
Vous devez être connecté pour pouvoir répondre à ce sujet.
Utilisation des données

Afin d'améliorer ton expérience utilisateur, nous utilisons des cookies 🍪