Piscine : quelques sondes

Hello,

j’ai une nouvelle piscine et j’ai vu qu’il y avait quelques tests à faire régulièrement :

  • Taux de sel
  • Taux de Chlore
  • PH

J’aurais aimé faire un petit projet pour avoir ces valeurs remontées automatiquement vers un serveur en ajoutant éventuellement ces 2 autres sondes :

  • Température de l’eau
  • éventuellement la pression avant la pompe histoire de voir si le skimmer n’est pas plein, mais ça, c’est vraiment optionnel.

Le plus important pour moi, c’est surtout d’avoir chlore et PH. Ensuite vient le taux de sel et la température. Je note que je ne souhaite pas du tout contrôler les équipements aujourd’hui, simplement avoir les valeurs.

J’ai vu sur le net qu’il existe des sondes de ce type :

A priori, il faut ajouter ça aussi :

Je ne sais pas trop à quoi correspond le « potentiel de réduction d’oxydation », mais j’aurais déjà le PH.

J’ai vu qu’il existait des objets connectés qui permettent d’obtenir toutes ces valeurs sur un téléphone via une app. Le problème, c’est qu’ils sont assez chers à l’achat, mais surtout en maintenance (il faut changer régulièrement les sondes et éventuellement la batterie selon les modèles).

Il faudrait déjà que je choisisse le type d’installation voulue. 2 solutions :
1)
On peut en effet mettre tout ça dans le local, il y a une alimentation, et j’ai juste à faire tremper les sondes à travers les tuyaux. C’est un peu ça qui m’embête, je ne souhaite pas trop modifier l’installation actuelle, et je ne suis pas trop plombier ou pisciniste. Je ne sais pas comment on procède pour utiliser ces sondes à travers des tuyaux…

Ou sinon, je fais un système autonome qui serait dans le skimmer et qui contiendrait toutes ces sondes. Par contre, il va falloir trouver un moyen d’alimenter le raspberry avec des piles ou autres, et ça, je ne sais pas si c’est facile…

Avez vous des conseils sur la méthode ? Je dois dire que la 2ème me plait plus car il n’y a pas de modification sur l’installation actuelle.

Je trouve que les sondes sont déjà un peu chères, donc je ne sais pas si c’est la meilleure méthode…

Salut il y a un sujet similaire je crois sur le forum, ça peut peut-être te donner des infos…

Finalement, j’ai fait un système autonome sur pile qui se met dans le skimmer. Il est assez étanche et il envoie les infos vers un serveur qui récupère les valeurs. J’ai utilisé un arduino, l’autonomie est très bonne car les piles durent quelques mois.
https://forum.jeedom.com/viewtopic.php?f=151&t=35487&sid=c94637ab54a0f8860d0ed28896a13833&start=380#p770766

Salut,
Super idée ton montage, pourrais tu stp le documenter avec la mise à jour du schéma électrique et le code ?
On est plusieurs à être très intéressés, c’est le seul montage autonome.
Merci d’avance

Pour le schéma, tout est expliqué sur le lien un peu plus haut.

Pour le code, je pourrai fournir ça, il faut que je remette la main dessus. Mais en gros, j’ai utilisé le même protocole que pour les sonde température Oregon. Et après, j’ai retraité l’info coté serveur. Pour la réception, j’ai utilisé un rflink 433MHz, mais je l’avais fait aussi avec un récepteur Telldus, et ça doit aussi fonctionner avec un récepteur classique sur un Arduino ou raspberry direct.

Malheureusement, je suis meilleur en informatique et même en électronique qu’en plomberie, et mon système a finit par prendre l’eau… Alors, je retenterai peut être l’expérience, mais avec un boitier hors piscine et juste une sonde qui va dans la skimmer.

Oui ça serait top si tu pouvais fournir le code, je vais tenter de reproduire le montage avec des porte sondes directement sur la tuyauterie.

#include <LowPower.h>
 
/* Dépendance pour le bus 1-Wire */
#include <OneWire.h>

// Configuration Oregon
#define MODE_0 0 // Temperature only [THN132N]
#define MODE_1 1 // Temperature + Humidity [THGR2228N]
#define MODE_2 2 // Temperature + Humidity + Baro() [BTHR918N]

#define PIN_ORP         A0
#define PIN_PH        A1

#define MODE MODE_1
const byte TX_PIN = 9;
 
 
/* Broche du bus 1-Wire */
const byte BROCHE_ONEWIRE = 2;

const byte DONE_PIN = 5;



/* Code de retour de la fonction getTemperature() */
enum DS18B20_RCODES {
  READ_OK,  // Lecture ok
  NO_SENSOR_FOUND,  // Pas de capteur
  INVALID_ADDRESS,  // Adresse reçue invalide
  INVALID_SENSOR  // Capteur invalide (pas un DS18B20)
};


/** ********************************************************** */
/** ************ Fonction de préparation Oregon ************** */
/** ********************************************************** */
#define SEND_HIGH() digitalWrite(TX_PIN, HIGH)
#define SEND_LOW() digitalWrite(TX_PIN, LOW)
const unsigned long TIME = 512;
const unsigned long TWOTIME = TIME*2;
// Buffer for Oregon message
#if MODE == MODE_0
 byte OregonMessageBuffer[8];
#elif MODE == MODE_1
 byte OregonMessageBuffer[9];
#elif MODE == MODE_2
 byte OregonMessageBuffer[11];
#else
 #error mode unknown
#endif

/* Création de l'objet OneWire pour manipuler le bus 1-Wire */
OneWire ds(BROCHE_ONEWIRE);


 /**
* \brief Send logical "0" over RF
* \details azero bit be represented by an off-to-on transition
* \ of the RF signal at the middle of a clock period.
* \ Remenber, the Oregon v2.1 protocol add an inverted bit first 
*/
inline void sendZero(void){
 SEND_HIGH();
 delayMicroseconds(TIME);
 SEND_LOW();
 delayMicroseconds(TWOTIME);
 SEND_HIGH();
 delayMicroseconds(TIME);
}

/**
* \brief Send logical "1" over RF
* \details a one bit be represented by an on-to-off transition
* \ of the RF signal at the middle of a clock period.
* \ Remenber, the Oregon v2.1 protocol add an inverted bit first 
*/
inline void sendOne(void){
 SEND_LOW();
 delayMicroseconds(TIME);
 SEND_HIGH();
 delayMicroseconds(TWOTIME);
 SEND_LOW();
 delayMicroseconds(TIME);
}

/**
* Send a bits quarter (4 bits = MSB from 8 bits value) over RF
*
* @param data Source data to process and sent
*/

/**
* \brief Send a bits quarter (4 bits = MSB from 8 bits value) over RF
* \param data Data to send
*/
inline void sendQuarterMSB(const byte data){
 (bitRead(data, 4)) ? sendOne() : sendZero();
 (bitRead(data, 5)) ? sendOne() : sendZero();
 (bitRead(data, 6)) ? sendOne() : sendZero();
 (bitRead(data, 7)) ? sendOne() : sendZero();
}

/**
* \brief Send a bits quarter (4 bits = LSB from 8 bits value) over RF
* \param data Data to send
*/
inline void sendQuarterLSB(const byte data){
 (bitRead(data, 0)) ? sendOne() : sendZero();
 (bitRead(data, 1)) ? sendOne() : sendZero();
 (bitRead(data, 2)) ? sendOne() : sendZero();
 (bitRead(data, 3)) ? sendOne() : sendZero();
}

/**
* \brief Send a buffer over RF
* \param data Data to send
* \param size size of data to send
*/
void sendData(byte *data, byte size){
 for(byte i = 0; i < size; ++i)
 {
 sendQuarterLSB(data[i]);
 sendQuarterMSB(data[i]);
 }
}

/**
* \brief Send an Oregon message
* \param data The Oregon message
*/
void sendOregon(byte *data, byte size){
 sendPreamble();
//sendSync();
 sendData(data, size);
 sendPostamble();
}

/**
* \brief Send preamble
* \details The preamble consists of 16 "1" bits
*/
inline void sendPreamble(void){
 byte PREAMBLE[]={0xFF,0xFF};
 sendData(PREAMBLE, 2);
}

/**
* \brief Send postamble
* \details The postamble consists of 8 "0" bits
*/
inline void sendPostamble(void){
#if MODE == MODE_0
 sendQuarterLSB(0x00);
#else
 byte POSTAMBLE[]={0x00};
 sendData(POSTAMBLE, 1);
#endif
}

/**
* \brief Send sync nibble
* \details The sync is 0xA. It is not use in this version since the sync nibble
* \ is include in the Oregon message to send.
*/
inline void sendSync(void){
 sendQuarterLSB(0xA);
}

/**
* \brief Set the sensor type
* \param data Oregon message
* \param type Sensor type
*/
inline void setType(byte *data, byte* type){
 data[0] = type[0];
 data[1] = type[1];
}

/**
* \brief Set the sensor channel
* \param data Oregon message
* \param channel Sensor channel (0x10, 0x20, 0x30)
*/
inline void setChannel(byte *data, byte channel){
 data[2] = channel;
}

/**
* \brief Set the sensor ID
* \param data Oregon message
* \param ID Sensor unique ID
*/
inline void setId(byte *data, byte ID){
 data[3] = ID;
}

/**
* \brief Set the sensor battery level
* \param data Oregon message
* \param level Battery level (0 = low, 1 = high)
*/
void setBatteryLevel(byte *data, byte level){
 if(!level) data[4] = 0x0C;
 else data[4] = 0x00;
}
 
/**
 * Fonction de lecture de la température via un capteur DS18B20.
 */
byte getTemperature(float *temperature, byte reset_search) {
  byte data[9], addr[8];
  // data[] : Données lues depuis le scratchpad
  // addr[] : Adresse du module 1-Wire détecté
  
  /* Reset le bus 1-Wire ci nécessaire (requis pour la lecture du premier capteur) */
  if (reset_search) {
    ds.reset_search();
  }
 
  /* Recherche le prochain capteur 1-Wire disponible */
  if (!ds.search(addr)) {
    // Pas de capteur
    Serial.print(F("pas de capteur"));
    Serial.println();
    return NO_SENSOR_FOUND;
  }
  
  /* Vérifie que l'adresse a été correctement reçue */
  if (OneWire::crc8(addr, 7) != addr[7]) {
    // Adresse invalide
    Serial.print(F("mauvaise addr"));
    Serial.print(OneWire::crc8(addr, 7), 2);
    Serial.print(addr[7], 2);
    Serial.println();
    return INVALID_ADDRESS;
  }
 
  /* Vérifie qu'il s'agit bien d'un DS18B20 */
  if (addr[0] != 0x28) {
    // Mauvais type de capteur
    Serial.print(F("mauvais sensor"));
    Serial.print(addr[0], 2);
    Serial.println();
    return INVALID_SENSOR;
  }
 
  /* Reset le bus 1-Wire et sélectionne le capteur */
  ds.reset();
  ds.select(addr);
  
  /* Lance une prise de mesure de température et attend la fin de la mesure */
  ds.write(0x44, 1);
  delay(800);
  
  /* Reset le bus 1-Wire, sélectionne le capteur et envoie une demande de lecture du scratchpad */
  ds.reset();
  ds.select(addr);
  ds.write(0xBE);
 
 /* Lecture du scratchpad */
  for (byte i = 0; i < 9; i++) {
    data[i] = ds.read();
  }
   
  /* Calcul de la température en degré Celsius */
  *temperature = (int16_t) ((data[1] << 8) | data[0]) * 0.0625; 
  
  // Pas d'erreur
  return READ_OK;
}

/**
* \brief Set the sensor temperature
* \param data Oregon message
* \param temp the temperature
*/
void setTemperature(byte *data, float temp){
// Set temperature sign
 if(temp < 0)
 {
 data[6] = 0x08;
 temp *= -1; 
 }
 else
 {
 data[6] = 0x00;
 }



// Determine decimal and float part
 int tempInt = (int)temp;
 int td = (int)(tempInt / 10);
 int tf = (int)round((float)((float)tempInt/10 - (float)td) * 10);

int tempFloat = (int)round((float)(temp - (float)tempInt) * 10);

// Set temperature decimal part
 data[5] = (td << 4);
 data[5] |= tf;

// Set temperature float part
 data[4] |= (tempFloat << 4);
}

 /**
* \brief Set the sensor humidity
* \param data Oregon message
* \param hum the humidity
*/
void setHumidity(byte* data, byte hum){
 data[7] = (hum/10);
 data[6] |= (hum - data[7]*10) << 4;
}

/**
* \brief Set the sensor temperature
* \param data Oregon message
* \param temp the temperature
*/
void setPressure(byte *data, float pres){
 if ((pres > 850) && (pres < 1100)) {
 data[8] = (int)round(pres) - 856;
 data[9] = 0xC0; 
 }
}

/**
* \brief Sum data for checksum
* \param count number of bit to sum
* \param data Oregon message
*/
int Sum(byte count, const byte* data){
 int s = 0;

for(byte i = 0; i<count;i++)
 {
 s += (data[i]&0xF0) >> 4;
 s += (data[i]&0xF);
 }

if(int(count) != count)
 s += (data[count]&0xF0) >> 4;

return s;
}

/**
* \brief Calculate checksum
* \param data Oregon message
*/
void calculateAndSetChecksum(byte* data){
#if MODE == MODE_0
 int s = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);

data[6] |= (s&0x0F) << 4; data[7] = (s&0xF0) >> 4;
#elif MODE == MODE_1
 data[8] = ((Sum(8, data) - 0xa) & 0xFF);
#else
 data[10] = ((Sum(10, data) - 0xa) & 0xFF);
#endif
}

float getRedox() {
    float value, valORP;
    int buf[10],temp;
    unsigned long int avgValue;
  
   //value = (float)analogRead(PIN_ORP); // raw value between 0 and 1023
   //Serial.print("ORP brute: ");Serial.println(value);
    //value = map(value,0,1023,0,5000); // mV
    /*valORP = (2500-value)/1.037;

    Serial.print("ORP: ");Serial.println(valORP);*/
    for(int i=0;i<10;i++)
    { 
      buf[i]=analogRead(PIN_ORP); delay(30);
    }
    for(int i=0;i<9;i++)
    {
      for(int j=i+1;j<10;j++) {
        if(buf[i]>buf[j]) {
          temp=buf[i]; 
          buf[i]=buf[j]; 
          buf[j]=temp;
        }
      }
    }
    avgValue=0;
    for(int i=2;i<8;i++) {
      avgValue+=buf[i];
    }
    value=(float)avgValue/6.0;
    return value;
}

float getPH() {
    float value, valPH;
    int buf[10],temp;
    unsigned long int avgValue;
    
   //value = (float)analogRead(PIN_PH); // raw value between 0 and 1023
   /*Serial.print("PH brut: ");Serial.println(value);
    value = map(value,0,1023,0,5000); // mV
    valPH = (2500-value)/1.037;

    Serial.print("PH: ");Serial.println(valPH);
  return valPH;*/
    for(int i=0;i<10;i++)
    { 
      buf[i]=analogRead(PIN_PH); delay(30);
    }
    for(int i=0;i<9;i++)
    {
      for(int j=i+1;j<10;j++) {
        if(buf[i]>buf[j]) {
          temp=buf[i]; 
          buf[i]=buf[j]; 
          buf[j]=temp;
        }
      }
    }
    avgValue=0;
    for(int i=2;i<8;i++) {
      avgValue+=buf[i];
    }
    value=(float)avgValue/6.0;
    return value;
}
 
 
/** Fonction setup() **/
void setup() {
 pinMode(DONE_PIN, OUTPUT);
 pinMode(TX_PIN, OUTPUT);
 Serial.begin(9600);
 Serial.println("\n[Oregon V2.1 encoder]");
 SEND_LOW(); 
 #if MODE == MODE_0
 // Create the Oregon message for a temperature only sensor (THN132N)
 byte ID[] = {0xEA,0x4C};
 #elif MODE == MODE_1
 // Create the Oregon message for a temperature/humidity sensor (THGR2228N)
 byte ID[] = {0x1A,0x2D};
 #else
 // Create the Oregon message for a temperature/humidity/barometer sensor (BTHR918N)
 byte ID[] = {0x5A, 0x6D};
 #endif

 setType(OregonMessageBuffer, ID);
 setChannel(OregonMessageBuffer, 0x20);
 setId(OregonMessageBuffer, 0xCB);
}


 
 
/** Fonction loop() **/
void loop() {
  float temperature, temperatureCumul = 0.0f;
  float humidity = 59;
  float pressure = 998;
  int bat = 1;
  int loopNb = 10;
  int l = 0;
  float redox, redoxCumul = 0.0f;
  float ph, phCumul = 0.0f;

  while(l<loopNb) {
    
    /* Lit la température ambiante à ~1Hz */
    if (getTemperature(&temperature, true) != READ_OK) {
      //Serial.println(F("Erreur de lecture du capteur"));
      //return;
      bat = 0;
    }
    
    /* Affiche la température */
    Serial.print(F("Temperature : "));
    Serial.print(temperature, 2);
    Serial.write('C');
    Serial.println();
    
    //temperatureCumul += temperature;
    //if (l==loopNb-1) {
      setId(OregonMessageBuffer, 0xCB);
      //setTemperature(OregonMessageBuffer, temperatureCumul / loopNb);
      setTemperature(OregonMessageBuffer, temperature);
      setBatteryLevel(OregonMessageBuffer, bat); // 0 : low, 1 : high
    
      #if MODE != MODE_0
      // Set Humidity
      setHumidity(OregonMessageBuffer, l);
      #endif
      
      #if MODE == MODE_2
      // Set Pressure
      setPressure(OregonMessageBuffer, pressure);
      #endif
      
      // Calculate the checksum
      calculateAndSetChecksum(OregonMessageBuffer);
      
      // Show the Oregon Message
      Serial.println("Oregon -> ");
      for (byte i = 0; i < sizeof(OregonMessageBuffer); ++i) { 
        Serial.print(OregonMessageBuffer[i] >> 4, HEX);
        Serial.print(OregonMessageBuffer[i] & 0x0F, HEX);
      }
      Serial.println();
      
      // Send the Message over RF
      sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
      // Send a "pause"
      SEND_LOW();
      delayMicroseconds(TWOTIME*8);
      // Send a copie of the first message. The v2.1 protocol send the
      // message two time 
      sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
      
      // Wait for 30 seconds before send a new message 
      SEND_LOW();
    //}
    
        
    //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
    LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
    
    redox = getRedox();
    
    /* Affiche la température */
    Serial.print(F("redox raw value : "));
    Serial.print(redox, 2);
    Serial.println();

    redoxCumul += redox;
    //if (l==loopNb-1) {
      // we send redox in temperature now
      setId(OregonMessageBuffer, 0xCA);
      setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
      float fakeTemperatureRedox = redoxCumul/(l+1);
      if (fakeTemperatureRedox>999) fakeTemperatureRedox=999;
      fakeTemperatureRedox /= 10;
      setTemperature(OregonMessageBuffer, fakeTemperatureRedox);

      #if MODE != MODE_0
      // Set Humidity
      if (redox>999) redox = 999;
      setHumidity(OregonMessageBuffer, redox / 10);
      #endif
      
      // Calculate the checksum
      calculateAndSetChecksum(OregonMessageBuffer);
      
      // Send the Message over RF
      sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
      // Send a "pause"
      SEND_LOW();
      delayMicroseconds(TWOTIME*8);
      // Send a copie of the first message. The v2.1 protocol send the
      // message two time 
      sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
      
      // Wait for 30 seconds before send a new message 
      SEND_LOW();
    //}
      
    //LowPower.sleep(1000);
    //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
    LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
    
    ph = getPH();

    phCumul += ph;
    /* Affiche la température */
    Serial.print(F("ph raw value : "));
    Serial.print(ph, 2);
    Serial.println();
    
    //if (l==loopNb-1) {
      // we send ph in temperature now
      setId(OregonMessageBuffer, 0xC9);
      setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
      float fakeTemperaturePH = phCumul / (l+1);
      if (fakeTemperaturePH>999) fakeTemperaturePH=999-fakeTemperaturePH;
      fakeTemperaturePH /= 10;
      setTemperature(OregonMessageBuffer, fakeTemperaturePH);

      #if MODE != MODE_0
      // Set Humidity
      if (ph>999) ph = 999;
      setHumidity(OregonMessageBuffer, ph / 10);
      #endif
      
      // Calculate the checksum
      calculateAndSetChecksum(OregonMessageBuffer);
      
      // Send the Message over RF
      sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
      // Send a "pause"
      SEND_LOW();
      delayMicroseconds(TWOTIME*8);
      // Send a copie of the first message. The v2.1 protocol send the
      // message two time 
      sendOregon(OregonMessageBuffer, sizeof(OregonMessageBuffer));
      
      // Wait for 30 seconds before send a new message 
      SEND_LOW();
    //}
    
    //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
    LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
    l++;
  }

  digitalWrite(DONE_PIN, HIGH);
  //LowPower.idle(SLEEP_1S, ADC_OFF, TIMER4_OFF,TIMER3_OFF,TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF, USB_OFF);
  //LowPower.idle(SLEEP_1S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
  delay(5000);
  digitalWrite(DONE_PIN, LOW);
  
  //delay(10000);
  //for(int i = 0; i < 2; i++)
 //LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART1_OFF, TWI_OFF); 
}

Coté serveur, en utilisant un rflink 433MHz :

<?php

$dev = '/dev/ttyACM1';

date_default_timezone_set('Europe/Paris');    
    
echo "\n############################################################\n";

echo "\nrflink433_log";

sleep(1);

$fd = fopen ($dev, "r"); // ouverture du flux
if ($fd===FALSE) {
    echo "\nUnable to open file";
    exit();
}
$start_time = time();
$i = 0;
$line = '';

while ($i<100000) {
        $line =fgets($fd);
        //echo date("Y-m-d H:i:s");
        echo "\n".date("Y-m-d i:H:s")."\nLine: ".$line;
        if ($line === FALSE) {
                echo "\nUnable to read line...waiting";
                print_r(error_get_last());
        } else {
                $arr=explode(';',$line);
                //var_dump($arr);
                if (count($arr)> 2 && isset($arr[2]) && ($arr[2]=='Oregon Temp' || $arr[2]=='Oregon TempHygro')) {
                        $connexion = mysqli_connect ("127.0.0.1", "login", "password");
                        if (!$connexion) {
                                echo mysqli_error($connexion);
                        } else {
                                
                                $hsstatus = -1;
                                $temperature = -100;
                                $humidity = -1;
                                $bat = '';
                                $device_id = -1;
                                foreach($arr as $key => $value) {
                                        $arr_value = explode('=',$value);
                                        switch ($arr_value[0]) {
                                                case 'ID':
                                                        //echo "\nDEBUG ID:".$arr_value[1].' - '.hexdec($arr_value[1]);
                                                        $device_id = hexdec($arr_value[1]);
                                                        break;
                                                case 'TEMP':
                                                        $temperature = hexdec($arr_value[1])/10.0;
                                                        if (substr($arr_value[1],0,1) == "8") {
                                                                $temp_negative = "0".substr($arr_value[1],1,3);
                                                                $temperature = -hexdec($temp_negative)/10.0;
                                                        }
                                                        break;
                                                case 'HUM':
                                                        $humidity = $arr_value[1];
                                                        break;
                                                case 'HSTATUS':
                                                        $hsstatus = $arr_value[1];
                                                        break;
                                                case 'BAT':
                                                        $bat = $arr_value[1];
                                                        break;
                                        }
                                }
                                
                                $sql = "
                                                SELECT * FROM cedhome.log_temp 
                                                        WHERE id='".$device_id."' AND DATE_ADD(log_date, INTERVAL 15 MINUTE) > NOW() LIMIT 1";
                                echo "\n".$sql;
                                $res = mysqli_query($connexion, $sql) or die(mysqli_error($connexion)); ;
                                if (mysqli_num_rows($res)) {
                                        // wait for a newer value
                                } else {
                                        $sql = "INSERT INTO `cedhome`.`log_temp` 
                                                (`log_id`, `id`, `temperature`, `humidity`, `log_date`, `index_time`) 
                                                VALUES (NULL, ".(int)$device_id.", '".addslashes($temperature)."', '".addslashes($humidity)."', NOW(), '".(int)$hsstatus."')";
                                        echo "\n".$sql;
                                        $res = mysqli_query($connexion, $sql) ;
                                        if (!$res) {
                                                echo mysqli_error($connexion);
                                        }
                                
                                }
                                mysqli_close($connexion);
                        }
                }
        }
}

Et la config du port série du rflink :

<?php

$dev = '/dev/ttyACM1';

date_default_timezone_set('Europe/Paris');

function exec_and_write($str) {
    echo "\n".$str;
    return exec($str);
}

function GetPID($program)
{
    if(!$program) return -1;
    $c_pid = exec_and_write( "ps aux | grep ".$program." | grep -v grep | grep -v su | awk {'print $2'}");
    return $c_pid;
}

echo "\n############################################################\n";
echo "\nrflink433_control launched ".date("Y-m-d H:i:s")."\n";

if ($pid = GetPID("rflink_log.php")) {
    echo "\npid found : " .$pid;
} else {
    exec_and_write("/bin/stty -F ".$dev." 57600 sane -cstopb -parenb cs8 > /dev/null &");
    
    exec_and_write("chmod a+w ".$dev."");
    
    exec_and_write("php ~/Documents/rflink433/rflink_log.php > ~/Documents/rflink433/rflink433_log.txt & ");
}

echo "\n############################################################\n";


?>