Controle serveur ubuntu

Bonjour tout le monde !!!

Voila il y'a quelque année je me suis lancée dans ce formidable monde de la programmation. j'ai commencé par apprendre le python et puis l'arduino. J'ai pu ainsi acquérir de l'expérience et aboutir à un projet final que je tient à vous présenter.

Mon but était de contrôler un ventilateur d'ordinateur selon des température relevée devant le cpu, à la sortie d'air et enfin à l'entrée d'air ou avec un potentiomètre. Ainsi que d'allumer/eteindre mon serveur par un push button (je vous direz plus tard pourquoi je n'est pas mis de momentary push button)

Tout d'abord le premier problème qui ce pose c'est l'alimentation de l'arduino en effet si le serveur est éteint l'alim du pc ne délivre plus de 5v ou de 12v. j'ai donc opté pour une alimentation externe de 12v que j'abaisse à 5v via un pont diviseur. Ainsi je prend aussi cette alimentation 12v extérieur pour alimenter mon ventilateur.

Ensuite pour éviter des bourdes et d'éteindre le serveur alors que l'on ne voulait pas je vérifie que les tension du 5v et 12v de l'alimentation du serveur ne sont pas à 0 (cf: problème d'alimentation). pour cela j'ai trouvé ce site qui explique comment faire: http://www.electroschematics.com/9351/arduino-digital-voltmeter/

C'est ici que l'on voit l'intérêt d'un push button. En effet on ne peut éteindre le serveur que par ce bouton car si l'arduino détecte que le bouton est sur la position "on" alors que les deux voltage de l'alimentation sont à 0 il activera le relais jusqu'à ce que l'ordinateur s'allume.

De plus on peut dés lors anticiper l'arrêt de l'ordinateur et envoyer un mail aux utilisateurs du serveur.

Enfin un des problèmes qui ma donné beaucoup de fil a retordre à était le contrôle du ventilateur par une sortie PWM. En effet si je ne me trompe pas l'arduino produit un signal PWM à une fréquence d'environ 490KHz alors que le ventilateur néssescite un signal pwm à 25KHz. Et malgré mon expérience je ne me sentais pas capable de manipuler les registres des timer de l'arduino. Au terme des recherches que j'ai effectuée grâce à notre amis Google je suis finalement tombé sur cette publication :PWM frequency library - Libraries - Arduino Forum
Je l'avoue, ca m'a bien aidé pour contrôler le ventilateur.

voici le schémas du montage:

Bon je ne vais pas le décrire, Vous pourrez toujours me posez des question si vous en avez.

Ensuite voici le code arduino qui permet d'animer la bête ^^

# include <PWM.h>

#include <dht.h>

#define pin_cpu A4
#define pin_in A5
#define pin_out A2

#define R1 100000.0 // resistance of R1 (100K) -see text
#define R2 10000.0 // resistance of R2 (10K) - see text!

#define pin_12_ext A6
#define pin_12_pc A0
#define pin_5 A1

#define pin_vent 9

#define pin_pot A3

#define pin_relais 11

#define pin_1_interupt 7
#define pin_2_interupt 8

#define interupt_g 10

String inString = "";

dht dht_cpu;
dht dht_in;
dht dht_out;

int temp_cpu;
int temp_in;
int temp_out;

int hum_cpu;
int hum_in;
int hum_out;

float vout = 0.0;  //valeur pour le voltmetre arduino 
float vin = 0.0;
int value = 0;

float alim_5 = 0.0;
float alim_12 = 0.0;
float alim_ext = 0.0;
boolean state = true;     //variable pour l'état du pc true=allumé false=éteint
boolean _verbose = true; //verbose du programme (pour avoir l'humidité ou non

boolean manual;     //variable pour définir si l'on utilise le potenciométre ou non pour regler la vitesse du ventilateur
int fan_speed = 0;
int32_t frequency = 25000; //frequency (in Hz)

void setup() {
  // put your setup code here, to run once:
  pinMode(11, OUTPUT);
  digitalWrite(11, HIGH);
  pinMode(10, INPUT);
  pinMode(A1, INPUT);
  pinMode(A0, INPUT);
  pinMode(A3, INPUT);
  pinMode(A6, INPUT);
  Serial.begin(9600);

  delay(100);
  //initialize all timers except for 0, to save time keeping functions
  InitTimersSafe();

  //sets the frequency for the specified pin
  bool success = SetPinFrequency(pin_vent, frequency);
  //if the pin frequency was set successfully, turn pin 13 on

}


void loop() {
  // on récupére les tension de l'alimentation du pc (12V et 5V) ainsi que l'alimentation exterieur.
  alim_ext = get_V(pin_12_ext);
  alim_12 = get_V(pin_12_pc);
  alim_5 = get_V(pin_5);
  

  if (alim_12 == 0 && alim_5 == 0) {
    // on vérifie l'état du serveur
    state = false;
    pwmWrite(pin_vent, 10); //la valeur 10 correspond à l'arret du ventilateur
  }
  else {
    state = true;
  }
  
  if (digitalRead(interupt_g) == HIGH) {
    /*c'est ce bout de programme qui permet à moins de mettre le push button sur off de rallumer le server 
     * En effet lorsque l'on verifie l'état du serveur si il est éteint la variable passera a false
     * or ici si le push button est sur on et que state=false le relais démarre l'ordinateur
     */
    if (state == false) {
      //allumage du serveur via relais 5v bas niveau
      state = true;
      power(true);
      digitalWrite(pin_relais, LOW);
      delay(250);
      digitalWrite(pin_relais, HIGH);
      delay(120000);    //corespond a 2 minutes le temps que le serveur démarre;
    }
  }
  else {
    if (digitalRead(interupt_g) == LOW) {
      if (state == true) {
        power(false);
        //extinction de l'ordinateur via commande executé grace à l'interface python
        //cf code python fonction shutdown()
        state = false;
        Serial.println("0");
      }
    }
  }
  if (state == true) {
    //ma routine de controle des temperature et d'ajustement de la vitesse du ventilateur
    read_dht(_verbose);
    send_data(_verbose);


    if (digitalRead(pin_1_interupt) == HIGH) {
      manual = true;
    }
    else {
      if (digitalRead(pin_2_interupt == HIGH)) {
        manual = false;
      }
    }


    if (manual == true) {
      //ici on régle la vitesse du ventilateur grace au potenciometre
      fan_speed = get_pot(pin_pot) / 4;
      if (fan_speed < 10) {
        fan_speed = 10;
      }
    }


    else {
        if (Serial.available() > 0) {
          //ce qui suit n'est pas de moi mais à était trouvé sur le site d'arduino: https://www.arduino.cc/en/Tutorial/StringToIntExample
          while (Serial.available() > 0) {
            int inChar = Serial.read();
            if (isDigit(inChar)) {
              //convert the incoming byte to a char and add it to the string:
              inString += (char)inChar;
            }
            //if you get a newline, print the string, then the string's value:
          }
          fan_speed = inString.toInt();
          //clear the string for new input:
          inString = "";
        }

        
        //fan_speed=x.toInt();
        if (fan_speed == 0) {
          fan_speed = 10; //10 est la valeur d'arret du ventilateur
        }
      
    }
    pwmWrite(pin_vent, fan_speed);
  }
  delay(1000);
}

void read_dht(boolean read_plus) {
  //dans cette fonction on releve les temperature des dht11
  dht_cpu.read11(pin_cpu);
  dht_in.read11(pin_in);
  dht_out.read11(pin_out);

  temp_cpu = dht_cpu.temperature ;
  temp_in = dht_in.temperature;
  temp_out = dht_out.temperature;

  if (read_plus) {
    //et si on en veut plus on peut écrire l'humidités relevé par les dht11
    hum_cpu = dht_cpu.humidity;
    hum_in = dht_in.humidity;
    hum_out = dht_out.humidity;
  }

}
int get_pot(int pin) {
  //fonction relevant la valeur du potentiometre
  int an_pot = analogRead(pin);
  return an_pot;
}

float get_V(int pin) {
  //prend les mesure des tension sur le pin passé en argument
  value = analogRead(pin);
  vout = (value * 4.88) / 1024.0; // see text
  vin = vout / (R2 / (R1 + R2));
  if (vin < 0.09) {
    vin = 0.0; //statement to quash undesired reading !
  }
  return vin;
}

void send_data(bool _verbose) {
  //"protocole" d'envoie des données pour interfacer avec le programme python
  if (_verbose) {
    Serial.print(temp_cpu);
    Serial.print("_");
    Serial.print(temp_out);
    Serial.print("_");
    Serial.print(temp_in);
    Serial.print("|");
    Serial.print(hum_in);
    Serial.print("_");
    Serial.print(hum_out);
    Serial.print("_");
    Serial.print(hum_cpu);
    Serial.print("|");
    Serial.print(alim_5);
    Serial.print("_");
    Serial.print(alim_12);
    Serial.print("_");
    Serial.print(alim_ext);
  }
  else {

    Serial.print(temp_cpu);
    Serial.print("_");
    Serial.print(temp_out);
    Serial.print("_");
    Serial.print(temp_in);
    Serial.print("|");
    Serial.print("no_hum");
    Serial.print("_");
    Serial.print("no_hum");
    Serial.print("_");
    Serial.print("no_hum");
    Serial.print("|");
    Serial.print(alim_5);
    Serial.print("_");
    Serial.print(alim_12);
    Serial.print("_");
    Serial.print(alim_ext);
  }
  Serial.println();
}

Si vous lisez le code vous pourrez trouver avec les quelques commentaires ce que fait telle ou telle ligne ^^

Ensuite j'ai crée un programme python qui permet de récupérer les valeurs des dht11 et de passer la valeur de la vitesse du ventilateur. elle génère aussi une page html ou figure deux tableaux : un pour les valeurs des dht11 et un autre pour les tensions.

voici une capture d'écran:

et voici le code python:

from serial import *
from math import ceil
from os import system
from time import time,localtime

first_time=0

iteration=False
test=True
mail=["pierre@gmail.com","paul@gmail.com","jacque@gmail.com","admin@gmail.com"]
admin="admin@gmail.com"

def check_time():
 #limite l'envoi du message de temperature de rentrée d'air élevée
    global iteration,first_time
#86400 sec en 1 journée
    if time()>first_time+86400:
        first_time=0
        iteration=False

def send_alert(txt,all_):
 #fonction renvoyant les alerte mail soit juste à l'administrateur soit à tout les utilisateur
    if all_:
        for i in mail:
            system("echo '"+txt+"' | msmtp "+i)
    else:
        system("echo '"+txt+"' | msmtp "+admin)

def write_html(DATA):
 #fonction écrivant la page html
    #fichier=open("/opt/lampp/htdocs/projet-site-serveur/info.html","w")
    fichier=open('test.html','w')
    fichier.write('<!DOCTYPE html><html><head><meta charset="UTF-8"><title>info serveur</title><link rel="stylesheet" type="text/css" href="test.css"></head><h1>Information du serveur</h1><body><table><tr><th></th><th>température</th><th>humidité</th></tr><tr><td>cpu</td><td>'+DATA[0][0]+'</td><td>'+DATA[1][2]+'</td></tr><tr><td>entrée</td><td>'+DATA[0][2]+'</td><td>'+DATA[1][0]+'</td></tr><tr><td>sortie</td><td>'+DATA[0][1]+'</td><td>'+DATA[1][1]+'</td></tr></table><table><tr><th></th><th>tension</th></tr><tr><td>12V alimentation serveur
</td><td>'+DATA[2][1]+'</td></tr><tr><td>5V alimentation serveur</td><td>'+DATA[2][0]+'</td></tr><tr><td>12V alimentation arduino et ventilateur
</td><td>'+DATA[2][2]+'</td></tr></table></body></html> ')
    fichier.close()

def connect(port,bauds,time):
 #fonction qui ouvre le port série
    ser=Serial(port,bauds,EIGHTBITS,PARITY_NONE,STOPBITS_ONE,timeout=time)
    return ser

def write(txt,serial_connection):
 #fonction pour écrire des données
    serial_connection.write(txt.encode('utf-8'))

def ready(serial_connection):
 #fonction pour lire les données
    return serial_connection.readlines()

def shut_down():
 #fonction pour eteindre l'ordinatuer 60 secondes aprés la demande
    if test:
        alert_txt="'ceci est un test pour le message pre-extinction du serveur \n si vous recevez ce message veuillez renvoyer un mail de confirmation à admin@gmail.com \n En vous remerciant, admin'"
    else:
        alert_txt="'extinction du serveur non prevue dans 1 minutes, En m'excusant du dérangement. admin'"

    send_alert(alert_txt,False)
    time_stop=localtime(time()+60)
    hour=time_stop[3]
    minute=time_stop[4]
    system("shutdown -t "+str(hour)+":"+str(minute))

def send_pwm(temperature,serial_connection):
 #fonction qui calcule la valeur selon les temperature
    global iteration,first_time
    if int(temperature[2])>30:
        if not(iteration):
            iteration=True
            send_alert("temperature de rentrée d air élevée",False)
            first_time=time()
    if int(temperature[0])>40:
        write("255",serial_connection)
    elif int(temperature[1])>30:
        write("255",serial_connection)
    else:
        total=0
        for i in temperature:
            total+=int(i)
        total=total/3
        total=ceil(total*7.65)+10
        while total%10!=0: #arrondi à la dizaine supérieur pour eviter les fluctuation
            total+=1
        if total>255:
            total=255
        elif total<10:
            total=10
        write(str(total),serial_connection)
        
            

def traitement(DATA,x):
 #fonction qui permet que la chaine de caractére qui est envoyé par l'arduino soit mise en liste selon le schemas suivant
 # [[temperature cpu,temperature sortie d'air,temperature rentrée air],[humidité rentrée d'air,humidité sortie air,humidité cpu],[5V alim,12V alim
 #,12V alim externe]]
    if DATA=="0":
        shut_down()
    else:
        DATA=DATA.split("|")
        for i in range(len(DATA)):
            DATA[i]=DATA[i].split("_")
 
        send_pwm(DATA[0],x)
        write_html(DATA)
        

x=connect("/dev/ttyUSB0",9600,1)
while True:
    txt=ready(x)
    if txt!=[]:
        for line in txt:
            DATA=str(line)[2:-5]
        traitement(DATA,x)

    if iteration:
        check_time()

Et voila maintenant il n'y a plus qu'a faire tourner le programme ^^ personnellement j'utilise un petit script bash pour éviter que le programme ne s'arrête pour x ou y raison

until python3 ~/Desktop/fan.py; do
    echo "Server 'fan control' crashed with exit code $?. Respawning..." | msmtp admin@gmail.com
    sleep 1
done

Et voila ^^ Surtout si vous avez des questions ou des améliorations n'hésitez pas à en faire part :slight_smile:

Schemas_arduino_fan_control.pdf (32.6 KB)

Pour le schémas je vous conseille de le télécharger au format pdf car il est beaucoup plus visible. Encore désolé de cette inconvénient

Bonjour tout le monde voila j'ai complétement automatisée le processus, en effet lorsque je démarre mon serveur le programme, à l'aide du script bash, démarre. Pour cela rien de plus simple, du moins une fois que l'on à finie et comprit ^^

(Attention je suis sous linux ubuntu 16.04)

premiére étape éditer la crontab

user@nom_ordinateur:~$ crontab -e

et on rajoute cette ligne:

@reboot setsid /lien/vers/script/sh >/lien/vers/sortie/log 2>&1 < /lien/vers/sortie/log &

une fois fait, le programme démarre automatiquement mais on ne peut pas éteindre le pc avec le bouton: en effet la commande shutdown néscessite l'utilisateur root. Pour cela il faut faire :

user@nom_ordinateur:~$ sudo visudo

et rajouter à la fin la ligne :

user ALL = NOPASSWD: /sbin/shutdown

ou user est votre nom d'utilisateur.

Et surtout merci à ceux qui mon lu et n'hesitez pas à poser vos question ^^