Compteur d’utilisation CPU/RAM

Bonjour à vous !

Pour ce premier article de blog je vais vous faire découvrir mon nouveau petit gadget. Petit gadget qui consiste à afficher sur un bargraphe circulaire le pourcentage d’utilisation du processeur et de la ram.

DSC_0676_resize

Petit sommaire avant de commencer !

  1. Le matériel

  2. Explication du compteur CPU/RAM

  3. Le code Python

  4. Le code Arduino

  5. Démonstration

  6. Les problèmes rencontrés (dans un prochain article)

Amusez-vous bien 😉

1) Le matériel

Pour cela j’ai utilisé « NeoPixel Ring – 24 x » conçu par Adafruit et disponible juste ici, chez notre confrère français Lextronic ce qui vous évitera d’éventuels frais de douane.

Une fois celui-ci en main, plusieurs questions se posent pour arriver jusqu’au résultat final. Quel matériel utiliser ? Quelle technologie/langage pour récupérer les informations de votre ordinateur ?

Pour ma part, j’ai fait au plus simple pour le matériel physique, et ayant déjà travaillé avec, j’ai opté pour l’Arduino.
On peut y coder de chouette programme avec un langage proche du C, allumer des LEDs, des moteurs, utiliser des interrupteurs ou encore communiquer avec le port série (un des points qui va nous intéresser).

J’opte donc pour un petit Arduino Nano V3. J’ai par le passé commencé mes bidouilles avec un Arduino Uno V3 puis un Arduino Mega 2560. J’ai donc voulu acheter un plus petit micro-contrôleur pour éventuellement réaliser un produit propre et fini.

ArduinoNanoFront_3_sm

Arduino nano (source : arduino.cc)

Adafruit NeoPixel Ring - 24x

Adafruit NeoPixel Ring – 24x (source : adafruit.com)

 

 

 

 

 

 

 

 

Ok ! Nous avons le gros du matériel ! En résumé, j’ai utilisé au total :

  • Un microcontrôleur Arduino nano R3 (que j’ai acheté ici, soit ~7.5€ pièce);
  • Un bargraphe Adafruit NeoPixelRing – 24x (acheté ici ~20€);
  • Deux mini breadboards (ou une seul grande , ~8€);
  • Un interrupteur ( moins d’un euro);
  • Un condensateur 10nF ; (quelque centimes);
  • Une résistance 10kOhm ; (quelque centimes);
  • Trois fils à breadboard mâles (pour connecter le bouton. Quelques euros);
  • Trois fils (pour relier le bargraphe. Quelques euros);

On s’en sort avec une quarantaine d’euros en arrondissant bien, à notez que j’ai commandé certains éléments sur Ebay par un vendeur Chinois à des prix vraiment très intéressant (il faut juste savoir être patient et il n’y a pas de frais de douane). Vendeur avec lequel je n’ai eux aucun soucis avec mes commandes et mes articles. Si vous voulez la jouer frenchie, vous trouverez tout facilement sur des sites français comme Lextronic, site de qualité avec un réel soins du packaging des commandes.

Maintenant passons à la partie récupération des données. Là c’était le terrain inconnu pour moi. Je savais déjà que l’on passerai par la liaison série pour communiquer les données, mais aucune idée comment récupérer le pourcentage RAM et CPU sur l’ordinateur.

2) Explication du compteur CPU/RAM

Mettons les choses au clair avec ce petit schéma pour savoir qui fait quoi :

Schéma du système

Schéma du système

 

  1. En gros, l’ordinateur communique les informations du pourcentage CPU et RAM via le port série (l’ordinateur et l’Arduino étant connecté via l’USB) ;
  2. L’Arduino nano interprète les chiffres reçu, il va s’occuper de transformer par exemple « 20% CPU et 30% RAM » en « allumer 2 LED bleu et allumer 3 LED verte ». avec un temps de rafraîchissement ;
  3. Un interrupteur pour changer de mode d’affichage. Et pour ce qui concerne le câblage du bouton à l’Arduino, j’ai suivi ce tutoriel ;
  4. Le bargraphe circulaire fait son job, c’est a dire s’allumer quand il faut avec la couleur qu’il faut. Il est alimenté via deux câbles le +5V et le GND, et reçoit ses informations via le câble de DATA, l’alimentation vient de l’Arduino et elle même du 5V délivré par le câble USB.

3) Le code Python

Nous allons donc nous tout d’abord nous intéresser à la partie récupération des données avant envoi sur le port série. Après quelques recherche sur comment récupérer celle-ci, je me suis orienté vers le langage Python. Pourquoi celui-ci et pas un autre ? Et bien par se qu’il est gratuit, sous licence libre, et compatible avec tout systèmes d’exploitation Windows , Linux et Mac OS rendant notre futur gadget « cross plateform ».

Ceci étant fait j’opte pour Python 2.7 et j’intègre les différentes bibliothèques « serial » , « psutil » et « time » qui nous permettrons respectivement de communiquer via le port série, récupérer les informations CPU/RAM et gérer le temps d’envoi des données.

import serial # for serial connection to Arduino
import psutil # to retrieve computer information
import time   # to delay information

Il nous reste à initialiser la connexion série, sur le port de notre choix, pour moi le COM3 (Vous pouvez trouver sur quel port est relié votre Arduino grâce au logiciel même de programmation Arduino ou bien le gestionnaire de périphérique Windows)

ser = serial.Serial()
#ser.baudrate = 9600
ser.baudrate = 115200
ser.port = 2 # corresponding to COM3
ser.open()

Après plusieurs essais, j’ai orienté mon choix sur une vitesse de 115200 bauds, celle-ci étant plus rapide et corrigeant des problèmes de synchronisation/affichage du résultat final.

Une autre question m’est venue à l’esprit : « Sous quel forme j’envoie mes informations ? »
Rappelons nous, nous avons deux informations à communiquer le pourcentage de la RAM et du CPU. Un pourcentage c’est entre 0 et 100 et nous en avons deux à envoyer.

Ne manipulant que des nombres, je décide de concaténer le CPU et la RAM en rajoutant un « 1 » devant, en guise de début de message. ça nous donnerais par exemple 12050 pour 20% de CPU utilisé et 50% de RAM utilisé. Vient ensuite une contrainte à gérer. Dans le cas où l’un des deux viendrait à 100% on aurait 1100100 et du coup notre nombre à envoyer serait beaucoup plus grand, j’ai donc décidé, dans le cas où l’on atteignait 100% de tricher en assignant 99%, ce qui ne changera rien à notre affichage une fois sur les LED et notre entier ne dépassera jamais 5 caractères.

Nous avons donc un entier pouvant se balader de 10000 à 19999, le tout étant de boucler à l’infini en récupérant les deux nombres que l’on veut, tronquer les valeurs (car nous ne voulons que 2 digits par info RAM et CPU) et d’envoyer cela sur le port série, toutes les secondes.

On obtient le code complet suivant :

import serial # for serial connection to Arduino
import psutil # to retrieve computer information
import time   # to delay information

ser = serial.Serial()
#ser.baudrate = 9600
ser.baudrate = 115200
ser.port = 2 # correspond au port COM3
ser.open()

while True :
    
    #cast XX,X as XX
    cpupercent = '%(number)02d' % \
        {"language": "Python", "number": psutil.cpu_percent()}

    #force the cpupercent to not exceed 99
    if(cpupercent == '100'):
        cpupercent = '99'

    #cast XX,X as XX
    rampercent = '%(number)02d' % \
        {"language": "Python", "number": psutil.virtual_memory().percent}

    #force the cpupercent to not exceed 99
    if(rampercent == '100'):
        rampercent = '99'

    total = '1' + cpupercent + rampercent # concatenation of each information

    print total # print information in console
    ser.write(total) #sending to the serial port
    time.sleep(1) # waiting for 1 second

Et voilà, pour ma part je suis allé au plus simple, le code est dans un fichier « .py » qui s’exécute par défaut avec Python 2.7 installé sur Windows. Le code est bien sur améliorable, on pourrait rajouter une saisie utilisateur pour renseigner nous-même au lancement de l’application le port série sur lequel on souhaite transmettre les informations.
(l’intégralité ici) !

4) le code Arduino

Bien, nos infos sont envoyées, et si on interprétait tout cela ?

Pour faire simple, dans notre loop(), (La boucle infinie) on rafraichi les LEDs, on écoute le port série, on vérifie si l’entier reçu correspond bien à ce que l’on attend (c’est à dire entre 10000 et 19999) et si c’est le cas on attribue les pourcentages aux LEDs respectives avec la méthode setPercents(recieved_value):

  void loop() {

    cleanLED();
 
    recieved_integer = 0;
    
    while ( Serial.available() ) {
      byte_read = Serial.read();
      if ( is_a_number(byte_read) ) {   
            recieved_integer = ascii2int(recieved_integer, byte_read);
      }
      
      if( recieved_integer >= 10000 && recieved_integer <= 19999 ){
        recieved_value = recieved_integer;
      }
    }
 
    setPercents(recieved_value);
    
    pixels.show();
    delay(250);
  }

La méthode setPercents(recieved_value) va attribuer les pourcentages reçus aux LEDs. Souvenez-vous, j’ai placé un bouton pour changer de mode d’affichage. J’ai donc opté pour trois modes différents :

  • Le bargraphe que je possède dispose de 24 LEDs, le premier mode partage le cercle en deux moitiés, une pour le CPU et l’autre pour la RAM. Ce qui nous donne 12 LEDs chacune. J’ai choisi bleu pour le CPU et vert pour la RAM avec deux LEDs blanches à la 6ème LED délimitant les 50% d’utilisation et 12 pour 100%
  • Le mode 2 consiste à n’afficher que le pourcentage CPU sur l’ensemble des 24 LEDs avec quatre LEDs blanche à 25, 50,75 et 100% d’utilisation
  • De même pour le mode 3 avec la RAM en vert.

OK, affichons tout ça, on transforme le int reçu en string pour « substringuer » les caractères qui nous intéressent en deux variables, CPU et RAM.

On écoute le bouton : par défaut on n’appuie pas dessus, le mode est donc à 0.
On va donc appeler la méthode printstat(int , int) deux fois, en passant en argument le segment à afficher et le pourcentage.

  void setPercents(int recieved_integer){
 
    //--------------------12345
    //integer is formed : 1XXYY
    // 1 the start of the message
    // 2 and 3 represent the CPU percent
    // 4 and 5 represent the RAM percent
    
    recieved_string = (String)recieved_integer;
    
    int CPU = (recieved_string.substring(1,3)).toInt();
    int RAM = (recieved_string.substring(3,5)).toInt();
    
    //LISTEN BUTTON
    myButton_state = digitalRead(myButton); // LISTEN BUTTON STATE
    
    //Si le bouton a un état différent que celui enregistré ET que cet état est "appuyé"
    if((myButton_state != myButton_memory) && (myButton_state == LOW))
    {
        mode = mode + 1;
        if(mode == 3)
          mode = 0;
    }
    myButton_memory = myButton_state; //on enregistre l'état du bouton pour le tour suivant
    
   switch(mode){
     case 0:
     {
       printStat( 0 , CPU);
       printStat( 1 , RAM);
     }
     case 1:
       printStat( 2 , CPU);
     case 2:
       printStat( 3 , RAM);
    }

  }

OK, donc dans printStat(int , int) on gère un seul pourcentage à la fois, souvenez-vous, dans le code précédent on l’appelle deux fois, en commençant par le CPU. on se retrouve donc avec un 83% qu’il faut adapter sur un maximum de 12 LEDs car on est sur le mode 1. On sort donc la calculette pour faire notre produit en croix :

100% → 12 LEDs
83 % → ?

Donc (83 * 12) / 100 donne 9.96… euh 9.96 LEDs ? … des LEDs à virgules ? …

Non nous allons tout simplement arrondir notre nombre à 0.5 près.
Et après avoir longuement cherché comment arrondir mon nombre à 0.5 près en cherchant sur Internet une bibliothèque où une méthode compliquée me permettant de faire cela (Car j’ai seulement comment tronquer et non arrondir), je me remémore tout simplement, un de mes cours de math de BTS où nous avions de manière simple arrondi une valeur à 0.5 près sur un programme de calculette. Et oui les maths ne servent pas à rien !

Pour cela rien de plus simple, rajoutez tout simplement 0.5 à votre nombre à virgule et tronquez le ensuite :
10.4 + 0.5 donnera 10.9 et sera tronqué en 10
46.7 + 0.5 donnera 47.2 et sera tronqué en 47

Et voila ! nos 83% donnerons donc 10 LEDs !

  //transforme le pourcentage en nombre de led avant d'etre envoyé au segment corespondant.
  //int segment : represent the segment to show pourcentage
  //float pourcent : represent thhe pourcentage to show
  void printStat( int segment , float pourcent){
    
    int diviseur = 2; // DEFAULT
    if(segment == 2 || segment == 3)
      diviseur = 1; // ALL CIRCLE need all leds
      
    Serial.print("Diviseur :");
    Serial.println(diviseur);
    
    float nbLed = ( pourcent * (24.00 / diviseur) ) / 100.00; // convert pourcent to led
    /*Serial.print("Valeur avant arrondi :");
    Serial.println(nbLed);*/
    
    nbLed = nbLed + 0.50; // rounded to up (ex : 4.6 -> 5.1)
    /*Serial.print("Valeur APRES arrondi :");
    Serial.println(nbLed); // trunked to unity (ex 5.1 -> 5)*/
    
    int trunkedLed = (int)(nbLed);
    /*Serial.print("Valeur APRES TRONCKAGE :");
    Serial.println(trunkedLed);*/
    
    switch(segment){
      case 0: // LEFT
        printLeftSegment(trunkedLed);
      case 1: // RIGHT
        printRightSegment(trunkedLed);
      case 2: // ALL CIRCLE CPU
        printAllCircle(trunkedLed , 0 , 0 , 5); // BLUE
      case 3: // ALL CIRCLE RAM
        printAllCircle(trunkedLed , 0 , 5 , 0); // GREEN
    }
    
  }

Et vu que segment reçu vaut 0 (pour notre pourcentage de CPU) on est dans le case 0 et on appelle donc printLeftSegment(trunkedLed) pour allumer les LEDs ! Le case 1 correspond à l’affichage de RAM, le case 2 au mode 2 avec seulement l’affichage du CPU et case 3 au mode 3 avec seulement la RAM.

Regardons maintenant la fonction printLeftSegment(trunkedLed)
Assez courte, mais pas besoin de bien plus, celle-ci va boucler entre 0 et le nombre de LEDs reçues. Ainsi 10 LEDs reçues en allumera dix vertes de 0 à 9; les LEDs 6 et 12, si elles sont atteintes (en dépassant 50% et ou en atteignant 100%) seront allumées en blanc.

  //affiche les le pourcentage converti en nombre de led sur le segment gauche.
  void printLeftSegment( int nbLed ){
    
    for(int i=0;i<nbLed ;i++){
      if(i == 5 || i == 11)
        pixels.setPixelColor(i, pixels.Color(5,5,5));
      else
        pixels.setPixelColor(i, pixels.Color(0,0,5));
    }
  }

La fonction printRightSegment(trunkedLed) ressemble beaucoup à celle du code précédent sauf que l’on part de la 24ème LED et non de la 1ère jusqu’à la 13ème, avec une couleur verte et les LEDs 12 et 18 en blanc.

Pour le mode 2 et 3 c’est à dire « seulement le CPU » ou « seulement la RAM », ça sera la même fonction appelée dans les deux cas.
Celle-ci reçoit également le nombre de LEDs mais également la couleur en paramètres.
4 leds seront blanches pour 25, 50, 75 et 100%

  void  printAllCircle(int nbLed , int Red , int Green , int Blue ){
      for(int i=0;i<nbLed ;i++){
      if(i == 5 || i == 11 || i == 17 ||i ==23)
        pixels.setPixelColor(i, pixels.Color(5,5,5)); // marker at 25,50,75 and 100%
      else
        pixels.setPixelColor(i, pixels.Color( Red , Green , Blue ));
    }

Et voilà, on a fait le tour pour le code Arduino. Je ne vous ai pas présenté la totalité des fonctions, mais vous avez le code dans les grandes lignes d’expliqué, je vous laisse le découvrir l’intégralité ici.

5) Démonstration

Voici une petite vidéo et galerie de photos !

arduino nano + adafruit neopixel ring - 24x

RAM mod

CPU mod

twice CPU/RAM mod

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *