Thermomètre RF avec Arduino Nano

J’utilise les sondes Oregon THGR 122 NX et Oregon THN 132 N pour récupérer différentes températures dans les différentes pièces de la maison ou à l’extérieur.
Ces sondes ont un coût important (Une vingtaine d’euros) même si elles apportent une très bonne précision et une compatibilité avec DomoticZ.

L’objectif était donc de réduire le coût des sondes, tout en garder une bonne précision et la possibilité de les intégrer sous DomoticZ à l’aide du récepteur RFXCom RFXtrx433E (Vous trouverez sur ce blog un tuto vous permettant de fabriquer cette passerelle à moindre coût à base d’Arduino).

Matériel :

Branchements :

Le programme que je vous propose utilise les branchements suivants :

  • Le capteur de température sur la pâte D3 de l’arduino
  • L’émetteur RF 433 sur la pâte D5 de l’arduino

Évidemment, ce sont les pâtes DATA des capteurs que vous devez branchez à ces endroits. Vous relierez ensuite les VCC des capteurs sur la pin 5V de l’Arduino et les GND des capteurs sur la pin GND de votre Arduino

Voici un aperçu de votre branchement sur Arduino.

 

Schéma de câblage

 

Le code source

A ce stade, il ne vous reste plus qu’à mettre le code dans votre Arduino. Pour le bon fonctionnement du code, vous aurez besoin des librairies LowPower et SimpleDHT

Quelques petites explications :

  • L’Arduino va envoyer un message formaté de la même manière que les sondes Oregon. C’est donc pleinement compatible avec DomoticZ
  • Si vous comptez mettre plusieurs sondes, pensez à bien changer l’ID (Ligne 300)  : setId(OregonMessageBuffer, 0xCB)
  • Il y a une implémentation de LowPower pour réduire la consommation de l’Arduino en veille (je n’ai pas encore mesurer la différence avec/sans)
#include <LowPower.h>
#include <SimpleDHT.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 MODE MODE_1
const byte TX_PIN = 3;

// Configuration Capteur Temp
int pinDHT22 = 5;
SimpleDHT22 dht22;

/** ********************************************************** */
/** ************ 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

/**
* \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;
}

/**
* \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
}

/******************************************************************/
/******************************************************************/
/******************************************************************/

void setup(){
 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);

}

void loop(){

float temperature = 0;
 float humidity = 0;
 digitalWrite(8, HIGH); //allumer L1
 digitalWrite(4, HIGH); //allumer L1
 delay(1000);
 dht22.read2(pinDHT22, &temperature, &humidity, NULL);
 digitalWrite(8, LOW); //allumer L1
 digitalWrite(4, LOW); //allumer L1



Serial.println("Temperature : "+(String)temperature);

// Get Temperature, humidity and battery level from sensors
 // (ie: 1wire DS18B20 for température, ...)
 setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
 setTemperature(OregonMessageBuffer, temperature);

#if MODE != MODE_0
 // Set Humidity
 setHumidity(OregonMessageBuffer, humidity);
 #endif

#if MODE == MODE_2
 // Set Pressure
 setPressure(OregonMessageBuffer, 1000);
 #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();


 //delay(30000);
 for(int i = 0; i < 8; i++)
 LowPower.idle(SLEEP_8S, ADC_OFF, TIMER2_OFF, TIMER1_OFF, TIMER0_OFF, SPI_OFF, USART0_OFF, TWI_OFF);
 
}

8 réflexions sur “Thermomètre RF avec Arduino Nano

  1. Merci pour le partage !
    Je vais essayer ça.
    Questions du débutant : comment vous alimentez tout ça ?
    Serait il possible d’avoir un schéma de câblage plus complet ?
    Merci d’avance.

    • Pour le moment, je n’ai pas trouvé de solution satisfaisante pour me séparer d’une prise. Du coup, c’est alimenté par USB via une prise électrique. Mes tests avec pile 9V et piles 1.5V n’ont pas une autonomie suffisante (1 semaine max.)

      Je vais faire le schéma de cablage et mettre à jour l’article rapidement.

      • Merci pour votre réponse.
        Je suis loin d’être un spécialiste mais il est possible que l’autonomie faible vient de l’alimentation en continue du module d’émission.
        Je n’arrive pas à changer la fréquence d’émission de la température. L’instruction « delay(30000) » est rendue inactive par les « // », et changer la valeur finale de la boucle qui suit ne modifie quasiment pas la fréquence.
        Je vais gratter un peu plus, mais si vous avez une idée la dessus, je suis preneur.

      • L’ajout d’un transistor pour n’alimenter les modules (RF et capteur de température) que quand nécessaire n’ont pas d’impact significatif sur l’autonomie. Je pense que c’est plutôt lié à toute l’électronique qui est intégré au Arduino qui consomme beaucoup. Le NANO n’étant pas réputé pour sa faible consommation d’ailleurs.

        Concernant le délai entre deux envois, il est possible de de-commenter le delay. La boucle de low_power n’ayant pas non plus d’impact significatif sur la consommation, elle peut être supprimée.

  2. Rectificatif : après un test plus rigoureux, changer la valeur finale de la boucle LowPower change bien la fréquence d’envoi des valeurs mesurées par paquet de 8s en gros. Autant pour moi.

  3. Bonjour.
    Je pense qu’il y a une petite erreur dans le schéma de cablage.
    Le +5Vcc du capteur et de l’émetteur sont reliés à la borne « reset » de l’Arduino et non à la borne 5V.

  4. Bonjour.
    Il y a une petite erreur dans le programme, ou dans le branchement, c’est selon.
    La sonde est reliée à D3 et la transmission à D5.
    Si le circuit est déjà fait, il faut changer la ligne 10 : const byte TX_PIN = 5;
    et la ligne 14 : int pinDHT22 = 3;

    • Merci pour votre commentaire.
      C’est jamais simple d’identifier les numéros de PIN avec la réalité et ça peut différer en fonction des versions d’Arduino.

      Votre commentaire sera donc peut-être utile à certain !

Laisser un commentaire

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