Connecting Stuff

Protocoles Oregon Scientific et Arduino : Encodage

Maintenant que l’on décode les protocoles Oregon Scientific (le v2 du moins car n’ayant pas de sonde en v3, je n’ai pas pu tester mais certains commentaires laissent à penser que ça fonctionne :) ), on va s’attaquer à l’émission de trames Oregon Scientific v2 depuis un arduino. Je pense que ça va en intéresser plus d’un et vous aurez compris l’idée derrière cela … réaliser des sondes pas chères et surtout compatibles avec les équipements du marché tel que le RFXCom ou la Zibase.
MAJ 10/02/2013 : suite à un problème avec l’éditeur de wordpress, le code était foireux à la ligne 264, c’est corrigé. Merci à catalin.

Note: Je parle de protocole version 2, il s’agit du 2.1 à réalité.

Le protocole Oregon Scientific v2

Codage

Je suis parti des specs que l’on trouve ici: http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf.

Note : Il y a des différences entre la spec et ce que fait le décodeur des articles précédant, notamment au niveau des ID des sondes.
En effet, si on prend l’exemple d’une sonde THN132N, la spec indique qu’elle renvoie 0xEC40 comme ID, alors que nous on attend 0xEA4C. le « A » de 0xEA4C semble venir en fait de 4bits de syncho envoyés par toutes les sondes. Du coup, même si le décodeur fonctionne, je pense qu’il ne doit pas reconnaitre toutes les sondes correctement (à tester, maintenant que l’on peut simuler n’importe qu’elle sonde).

Les protocoles 2 et 3 transmettent à un débit d’environ 1024Hz et utilisent un codage manchester. Un bit 0 est représenté par une transition du niveau bas vers le niveau haut et un bit 1 par une transition du niveau haut vers le niveau bas. La transition se produit au milieu d’une pulsation.

Particularités du protocole Oregon Scientific 2

La version 2 du protocole Oregon Scientific présente quelques particularités. Chaque bit est envoyé 4 fois. D’abords, les bits sont doublés. Pour un bit 1, on envoie « 01″ et pour un bit 0, « 10″. Puis le message complet est envoyé 2 fois.

Structure d’une trame Oregon Scientific

Les protocoles v2 et v3 ont une structure similaire :

[ Préambule (16/24) ] [ Sync (4) ] [ Data (taille variable) ] [ Postambule (taille variable) ]

  1. Un préambule qui permet de reconnaitre une trame Oregon. Elle consiste en une série de 24bits « 1″ pour la version 3 et 16bits « 1″ pour la version 2. N’oubliez pas que dans la version 2, chaque bit est doublé par son inverse. On se retrouve donc pour la v2 avec 32bits « 01010101… » .
  2. 4 bits de synchronisation : 0101, le fameux « A » (voir note plus haut). Les bits sont envoyés bit de poids faible en premier (A = 1010 -> tramis à l’envers : 0101). Pour la v2 : 10011001, Vous suivez toujours ?
  3. Les datas (voir plus bas)
  4. un postambule variable.

Analyse de la trame à « l’oscilloscope »

Forcement, ça n’a pas fonctionné du premier coup et pour vérifier ce que mon émetteur envoyait et le comparer avec une vrai sonde un oscilloscope aurait été bien pratique. Il y a plusieurs méthodes pour se dépatouiller sans oscillo.

On peut brancher un récepteur RF433 sur l’entrée ligne d’une carte son et utiliser un soft comme audacity pour afficher le signal comme le « ferait » un oscillo. Malheureusement je n’ai pas d’entrée ligne sur mon portable.

J’ai trouvé une autre méthode à base d’arduino : xoscillo. C’est un oscilloscope logiciel. Le signal reçu par un Arduino équipé d’un récepteur RF est envoyé via l’USB sur le PC qui fait tourner le soft xoscillo et qui affiche une représentation graphique du signal. Ca ne remplace pas un vrai oscillo bien sûr mais dans notre cas c’est suffisant et en tout cas, ça m’a bien dépanné :)

Sur l’image, une trame Oregon complète (le deuxième envoi) en haut. En bas, On voit clairement les 16 bits du préambule par exemple et les 4bits de synchro.

Trame oregon à l'oscilloscope

Simulation d’une sonde Oregon Scientific V2 avec un Arduino

Le sketch ci dessous permet de simuler une sonde de température Oregon Scientific THN132N ou une sonde de température/humidité THGR2228N.

Le code envoie la même trame toutes les 30 secondes. Libre à vous ensuite d’ajouter un capteur de température et/ou d’humidité pour envoyer de vrais informations.

Je me suis inspiré du code de skyduino pour l’envoi de trame blyss. Je l’ai testé sur Arduino Duemilanove et l’IDE 1.0.1 (linux).

/*
 * connectingStuff, Oregon Scientific v2.1 Emitter
 * http://connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
 *
 * Copyright (C) 2013 olivier.lebrun@gmail.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#define THN132N

const byte TX_PIN = 4;

const unsigned long TIME = 512;
const unsigned long TWOTIME = TIME*2;

#define SEND_HIGH() digitalWrite(TX_PIN, HIGH)
#define SEND_LOW() digitalWrite(TX_PIN, LOW)

// Buffer for Oregon message
#ifdef THN132N
  byte OregonMessageBuffer[8];
#else
  byte OregonMessageBuffer[9];
#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)
{
#ifdef THN132N
  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    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)
{
#ifdef THN132N
    int s = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);

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

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

void setup()
{
  pinMode(TX_PIN, OUTPUT);

  Serial.begin(9600);
  Serial.println("\n[Oregon V2.1 encoder]");

  SEND_LOW();  

#ifdef THN132N  
  // Create the Oregon message for a temperature only sensor (TNHN132N)
  byte ID[] = {0xEA,0x4C};
#else
  // Create the Oregon message for a temperature/humidity sensor (THGR2228N)
  byte ID[] = {0x1A,0x2D};
#endif  

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

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

#ifndef THN132N
  // Set Humidity
  setHumidity(OregonMessageBuffer, 52);
#endif  

  // Calculate the checksum
  calculateAndSetChecksum(OregonMessageBuffer);

  // Show the Oregon Message
  for (byte i = 0; i < sizeof(OregonMessageBuffer); ++i)   {     Serial.print(OregonMessageBuffer[i] >> 4, HEX);
    Serial.print(OregonMessageBuffer[i] & 0x0F, HEX);
  }

  // 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);
}

Compatibilité et conclusion

J’ai testé avec succès la réception avec le décodeur des articles précédant et avec un ancien RFXCom USB (2010). J’attends vos retours pour savoir si ça passe bien sur les nouveaux RFXcom ou sur une Zibase, voir sur une station Oregon Scientific directement.

J’avais commencé une librairie Oregon pour Arduino que je vais du coup essayer de terminer et de publier.

 

, , , , ,

52 thoughts on “Protocoles Oregon Scientific et Arduino : Encodage

  • Franck dit :

    Bonsoir,
    Je viens du forum touteladomotique.com qui m’a conseillé votre site.
    J’ai des sondes Oregon, un Arduino Uno.

    Pensez vous qu’il soit possible de lire les températures des sondes avec l’Arduino et ensuite de récupérer les données sous forme de courbe depuis un navigateur web (Pc, Mac, tablette, iPhone)?

    En vous remerciant pour le temps que vous prendrez à me répondre :)

    • Olivier dit :

      Salut,
      C’est moi qui t’es laissé le message sur le forum ;)
      Oui tout a fait c’est le but de cette série d’article. Si tu regarde ici : décodage oregon sur arduino 3/3 tu auras déjà le code pour réceptionner et décoder les infos des sondes 2.1 (quel modèle de sonde possèdes tu ?). Il faudra ensuite coder le transfert des données vers ton serveur. Il existe plein de moyen pour le faire (ethernet, usb, serie, rf …) et plein de façons différentes de le faire (http, xpl, …)

      Si tu es patient, je ferai un article avec cette partie là.

      • franck dit :

        Je saurait faire preuve de patience dans ce cas ;)

        Mes sondes sont des THGR122N et la dernière est une THGR122NX, je sais pas où ce trouve la différence?

        Par contre je ne sais pas si ce sont des V2 ou V3?

        Pour commencer avec les trois premier articles, il me manque donc un récepteur 433Mhz, j’en est trouvé mais pas à deux euros ;)

        Lequel prendre?

        http://www.planete-domotique.com/module-slave-recepteur-rf-433-92mhz-antenne-rfxcom.html

        http://www.lextronic.fr/P865-recepteur-43392-mhz-rrq3-433.html

        Tu as dit:
        « Il faudra ensuite coder le transfert des données vers ton serveur. Il existe plein de moyen pour le faire (ethernet, usb, serie, rf …) et plein de façons différentes de le faire (http, xpl, …) »

        L’Arduino ne peu pas faire office de serveur?
        Je pensais qu’il suffisait de lui adjoindre un shield Ethernet pour qu’il soit accessible via un navigateur internet.

        Mon serveur actuel, qui me sert pour les sauvegardes, bibliothéques iTunes est un Mac mini server avec OSX 10.6.

        Je ne sais pas si il peu être utilisé pour faire sa?

        • Olivier dit :

          Salut Frank,

          Je t’ai déjà répondu sur le forum de touteladomotique mais je partage les réponse ici aussi du coup.

          Je dirai que tu devrais pouvoir utiliser le code que j’ai fournis dans les articles précédant puisqu’elles partagent le même codage que les THGR228N pour lesquels j’ai fournis les fonctions nécessaires.

          J’ai acheté le mien ici (ce n’est pas le même que sur la photo par contre) : http://www.watterott.com/en/RF-Link-2400bps-Receiver-434MHz il me semblait l’avoir payé moins cher à l’époque.

          Si l’arduino peut faire office de serveur. On pourrait stocker les données sur la carte SD par exemple mais pour générer des graphs ça risque d’être limité. Je préfère utiliser l’arduino juste comme « passerelle », il s’occupe de la réception/décodage et renvoi les données décodées au serveur, chacun son boulot :)
          Oui tu peux utiliser ton MAc mini, pas de soucis.

  • Franck dit :

    Je suppose qu’il faudra un module Rfx com pour lire les sondes Oregon?

    • Olivier dit :

      Tu peux le faire avec le rfxcom mais aussi avec un arduino + un module récepteur RF433 à 2€. Cf le commentaire au dessus: jette un oeil sur les trois articles précédant à propos du décodage du protocole oregon.

  • catalin dit :

    Hi,

    there is a problem in your code and iit will not compile, the problem relates to the sum byte part there is something missing in the code and it will not compile on line 264..

    Thank you

  • catalin dit :

    Please rectify the code on the page, I have found the problem.
    this is the correct code:
    int Sum(byte count, const byte* data)
    {
    int s = 0;

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

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

    return s;
    }

  • franck dit :

    Je viens de trouver sa:

    http://www.mon-club-elec.fr/pmwiki_reference_arduino/pmwiki.php?n=Main.DebuterModuleArduinoEthernet

    Dans le cas où mon Mac Mini Server ne pourrais pas être utilisé, mais j’attend ta réponse à mon précédent post ;)

  • catalin dit :

    Thank you for all your work, I have managed to use your code together with an 433.92 rf transmitter and an ShT22 sensor and i have a fully working replica of an Oregon Scientific sensor now, I am using it in Homeseer with RFXCom.
    I plan to have it ported outside arduino on a separate AVR platform with atmega 8 and make my own compatible temperature sensors.

    • Olivier dit :

      Great :) I’m glad it’s working for you !
      I plan to use a ATtiny or something like that too to reduce cost. I’ll write a post when it’s ok.

      Share with us if you do it ! :)

  • catalin dit :

    sure it will be shared :)

  • Robert dit :

    Bonjour,

    J’ai une série de thermomètres THR128 Oregon.

    Aucuns des décodeurs que j’ai pu tester ne fonctionne. Il semble qu’ils utilisent le protocole V1.

    Je n’ai pas trouvé de sketch Arduino pour cette version. Il y a-t-il une quelqu’un qui a réalisé un décodeur pour la version 1.

    Merci d’avance
    Robert

  • Leon dit :

    Bonjour,

    Try to play with your sketch (including bufix line 264) to emulate sensor.
    I have at serial output 1A2D20BB0C500006F6, and nothing on Oregon screen.
    Could you please specify where is problem?
    Many thanks in advance for reply.

    Data from my real Oregon sensor
    OSV2 1A2D408E700870834B41

  • engel dit :

    Bonjour, et merci pour cet excellent article, néanmoins je m’interroge… dans le document que vous citez en référence : http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf , les ID’s sont différents des votres, est-ce lié à un encodage particulier de vos données ?

  • catalin dit :

    Hi,
    the code works fine with rfxcom but it is not recognised by any Oregon Scientific stations, i have tested it with BAR800U and BAR206.
    I believe there is an issue with the checksum or postamble, hard to say..

    • Olivier dit :

      Hi,
      Are they oregonv2.1 sensors ?
      If yes, you can have a look to xpl-perl source code (old version) to see if the checksum code is correct.
      I’ll check when I have some time.

  • engel dit :

    bonjour,

    j’ai fait quelques essais avec ma station wmr86 et des capteurs en version 3. Il semble que le postamble soit en fait le crc 8 des données placées avant le cheksum, je n’ai pas de transmetteur RF 433 pour tester mais en ajoutant le crc, je pense que l’on devrait pouvoir afficher les données sur la station.

    Un petit sketch pour vérifier, ( les paquets RF sont ceux enregistrés depuis mes capteurs ).


    #include

    #define mByte(i) (RFPacket[i])
    #define NIBBLE(i) ((mByte((i)>>1) >> (((i)&1)<<2))&0xf)

    // WGR800
    // 1A8904BBA0C004000046E2 Anemo = Sync + longueur utile (17) + CheckSum + CRC = 22 nibbles
    //uint8_t RFPacket[]={0x1A, 0x89, 0x04, 0xBB, 0xA0, 0xC0, 0x04, 0x00, 0x00, 0x46, 0xE2};

    // PCR800
    //2A19047D0000204500F05208 Pluvio Sync + longueur utile (18) + CheckSum + CRC = 23 nibbles
    //uint8_t RFPacket[]={0x2A, 0x19, 0x04, 0x7D, 0x00, 0x00, 0x20, 0x45, 0x00, 0xF0, 0x52, 0x08};

    // THGN800
    //AF8241750570028CC4054 thermo Hygro = Sync + longueur utile (15) + CheckSum + CRC = 20 nibbles
    uint8_t RFPacket[]={0xFA, 0x28, 0x14, 0x57, 0x50, 0x07, 0x20, 0xC8, 0x4C, 0x50};

    void checkSumAndCRC(const uint8_t len)
    {
    uint8_t i,j,c,CRC,SUM;

    CRC =0x00;
    SUM =0x00;

    uint8_t CCIT_POLY = 0x07;

    for (j=1; j<=len; j++)
    {
    c = NIBBLE(j);
    Serial.print(c, HEX);Serial.print(" ");
    SUM += c;
    CRC ^= c;
    for(i = 0; i<4; i++)
    if(CRC & 0x80 )
    CRC = (CRC << 1) ^ CCIT_POLY;
    else
    CRC <<= 1;
    CRC &= 0xff;
    }
    for(i = 0; i<4; i++)
    if(CRC & 0x80 )
    CRC = (CRC << 1) ^ CCIT_POLY;
    else
    CRC <<= 1;
    Serial.print("SUM: ");Serial.print(SUM, HEX); Serial.print(" CRC:"); Serial.print(CRC, HEX);Serial.println("");
    }

    void setup()
    {
    Serial.begin(38400,4);
    Serial.print("Start\n");
    }

    void loop()
    {
    checkSumAndCRC(15);
    delay(10000);
    }

    • gribouille dit :

      bonjour engel,

      j’essaye de comprendre votre code de calcul du post-ambul
      mais je bloque sur la compréhension

      ne manque t il pas de { } pour les boucles for(i = 0; i<4; i++)?
      car sans eux, le for ne sert a rien?

      pour le SUM , pas de probleme, j'ai bien compris et il est bien corroboré par mes trames brutes issus de capteur V2 (non via un recepteur UHF)

      ex : A1D3015717220418 73 8B
      le SUM vaut bien 37
      mais j'arrive pas a calculé le B8

      merci de votre aide

      • engel dit :

        Les accolades ne sont pas nécessaires, le for est exécuté sur l’instruction if suivante,( l’indentation n’a pas été préservée ). enfin bon je reconnais que l’on peut les mettre, ca ne gâche rien :)

        je n’ai pas de capteurs V2. mais un truc intéressant à faire à mon avis c’est de calculer le CRC en excluant le rolling Code du capteur 0×57 dans votre cas.

        • gribouille dit :

          bon j’ai testé en enlevant le 57 , mais c’est pas ça :D
          ou alors c’est mon code qui ne colle pas au votre

          voila ce que j’ai ecrit sous visualbasic en m’inspirant du votre
          ai je bon ?

          plainText recupere chaque nibble de la trame
          dans la trame du dessus
          plainText[0] = 1
          plainText[1] = D
          plainText[2] = 3
          etc…

          ********************************************************
          Dim i, j, c, CRC, SUM As UInteger

          CRC = &H0
          SUM = &H0

          Dim CCIT_POLY As UInteger = &H7

          For j = 0 To (Input1.TextLength – 1)
          c = plainText(j)
          SUM = SUM + c
          CRC = CRC Xor c

          For i = 0 To 3
          If CRC And &H80 Then
          CRC = (CRC << 1) Xor CCIT_POLY
          Else
          CRC = CRC << 1
          End If
          Next i
          CRC = CRC And &HFF
          Next j

          For i = 0 To 3
          If CRC And &H80 Then
          CRC = (CRC << 1) Xor CCIT_POLY
          Else
          CRC = CRC << 1
          End If
          Next i
          ********************************************************

    • gribouille dit :

      bonjour engel,

      j’essaye de comprendre votre code de calcul du CRC8
      ni connaissant rien en arduino, j’essaye de faire la translation du code en C pour tenter de le comprendre

      dans votre code , ne manque t il pas de { } pour la boucle for(i = 0; i<4; i++)?
      car sans eux , la boucle for tourne sur elle meme?

      merci de votre aide pour avoir quelques explications sur le calcul du CRC

      merci

    • pascal dit :

      Bonjour engel
      Avez vous pu confirmer qu’il s’agit bien d’un CRC 8 ? Si oui avec quel polynome ?
      J’ai testé avec 0×07 comme indiqué dans votre code mais ca ne fonctionne pas. Je vais en tester d’autres mais si je peux éviter de partir sur une mauvaise piste c’est aussi bien…
      Si vous avez d’autres informations qui expliquent ce préambule vous me seriez d’une grande aide.
      Si je trouve quelque chose de mon coté je publierai le résultat.
      Merci d’avance

  • catalin dit :

    Hi engel, I am willing to send you a free RF 433 transmitter to test your code, if you continue to share the code, get in touch with me and send me your details.

  • Florent dit :

    Bonjour Olivier et quel super travail réalisé.
    J’arrive à capter toutes les infos de mes sondes (THN132N & THGR228N) :-)

    Par contre ma THN132N étant en train de lacher, je souhaite la remplacer par un montage à base d’Arduino + sonde DS18B20.

    En récupérant ce code, je capte bien les messages sur un Arduino + récepteur RF mais ma station Oregon ne capte pas le message.

    Quelqu’un a-t’il déjà réussi à émuler une sonde et faire afficher la température sur une station Oregon?

    Par avance, merci de vos réponses

    • gribouille dit :

      j’ai testé , mais helas sans le post-ambule
      impossible qu’une station oregon reconnaisse la trame :-(

      faut reussir à trouver le calcul du post-ambul sur un capteur V2

      • Olivier dit :

        Salut,

        Je n’ai pas de station oregon, le rfxcom doit être est plus permissif (?)
        J’essaierai de regarder de ce coté à l’occasion mais pour le moment le tps me manque un peut :/

        • gribouille dit :

          disons que le rfxcom se moque complétement du post-ambule vu qu’il a deja toutes les infos dont il a besoin avant (meme si elles peuvent etre erroné suite à un parasite, quoique y’a un minimum avec le CRC ^_^ )

  • Julien dit :

    Salut,

    Merci beaucoup pour ce code ! C’est cool ! Je n’ai pas pour habitude de laisser des messages sur internet, mais je voulais faire partager mes expériences. Tout cela me paraissait assez compliqué, si bien que je doutais de l’efficacité de toutes ces lignes…
    Je viens tout juste de programmer un Atmega328 muni d’une sonde temp/hygro DHT22 et d’un émetteur 433MHz (les moins chers sur eBay). J’avais déjà essayé ces deux petits objets pour d’autres projets et ça marchait très bien. Pour le DHT22, j’utilise la librairie du même nom.
    Ce qui m’intéressait, c’était que la zibase capte toute seule les ondes et que je n’aie pas à passer par le port série branché sur un Raspberry (je lance un script PHP à l’aide de php_serial.class pour récupérer ce que le récepteur 433 a lui-même récupéré sur le Uno). D’une part, c’est plus pratique (et ça soulage la Zibase et le Raspberry), et d’autre part, je vais enfin pouvoir utiliser de manière optimale la librairie LowPower pour mettre en powerDown en boucle et ne réveiller la bête que toutes les tant de minutes… Cela m’économisera les piles. A vrai dire, c’est surtout pour le montage d’une sonde à ultrasons HC-SR04 que j’ai placée dans ma cuve de récupération d’eau de pluie, qui est au fond du jardin, que tout ceci m’intéressait… Mais je vais en profiter pour me faire une autre sonde temp/hygro sans fil, une « Oregon fake » !
    Merci encore !
    (Donc ça marche super bien avec la Zibase ! Voici ce qu’elle m’envoie : Received radio ID (433Mhz Noise=2004 Level=5.0/5 Oregon Generic Temp-Hygro/THGR228N Ch=2 T=+27.5°C (+81.5°F) Humidity=49% Batt=Ok): OS439204610
    Autant dire que c’est parfait : avec un petit fil de 17,3cm, je capte à 100% alors que je suis à l’étage…)
    NB : pour le code, j’ai remplacé #ifndef THN132N par #ifdef THGR2228N

    • Olivier dit :

      Salut,
      Merci pour le retour, ça fait plaisir :)

    • Leon dit :

      Salut,

      Can you share your code here? Merci.

      • Julien dit :

        Le code pour la sonde Temp/hygro : (pour le byte ID 0x1A,0x2D, on peut le remplacer par 0xCA,0x2C (THGR328N), il y aura peut-être moins de risques sur la limitation des canaux s’il y a plusieurs sondes) (j’ai aussi celui de l’ultrason pour ceux qui veulent)

        #include "LowPower.h" // Appel pour la maise en veille

        #include

        // Data wire is plugged into port 7 on the Arduino
        // Connect a 4.7K resistor between VCC and the data pin (strong pullup)
        #define DHT22_PIN 2

        // Setup a DHT22 instance
        DHT22 myDHT22(DHT22_PIN);

        #define THGR228N

        const byte TX_PIN = 12;

        const unsigned long TIME = 512;
        const unsigned long TWOTIME = TIME*2;

        #define SEND_HIGH() digitalWrite(TX_PIN, HIGH)
        #define SEND_LOW() digitalWrite(TX_PIN, LOW)

        // Buffer for Oregon message
        #ifdef THN132N
        byte OregonMessageBuffer[8];
        #else
        byte OregonMessageBuffer[9];
        #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)
        {
        #ifdef THN132N
        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 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> 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)
        {
        #ifdef THN132N
        int s = ((Sum(6, data) + (data[6]&0xF) - 0xa) & 0xff);

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

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

        void setup()
        {

        pinMode(TX_PIN, OUTPUT);

        Serial.begin(9600);
        Serial.println("\n[Oregon V2.1 encoder]");

        SEND_LOW();

        #ifdef THN132N
        // Create the Oregon message for a temperature only sensor (TNHN132N)
        byte ID[] = {
        0xEA,0x4C };
        #else
        // Create the Oregon message for a temperature/humidity sensor (THGR228N)
        byte ID[] = {
        0x1A,0x2D };
        #endif

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

        void loop()
        {
        // Get Temperature, humidity and battery level from sensors
        // (ie: 1wire DS18B20 for température, ...)

        DHT22_ERROR_t errorCode;

        // The sensor can only be read from every 1-2s, and requires a minimum
        // 2s warm-up after power-on.
        delay(2000);

        float t = myDHT22.getTemperatureC();
        float h = myDHT22.getHumidity();

        int hum = h * 10;
        int temp = t * 10;

        errorCode = myDHT22.readData();

        switch(errorCode)
        {
        case DHT_ERROR_NONE:
        // Alternately, with integer formatting which is clumsier but more compact to store and
        // can be compared reliably for equality:
        //
        char buf[128];
        sprintf(buf, "T%03hi%03hi ", temp, hum);

        Serial.println(buf);

        // vw_send((uint8_t *)buf, strlen(buf));
        // vw_wait_tx(); // Wait until the whole message is gone
        break;
        case DHT_ERROR_CHECKSUM:
        Serial.print("check sum error ");
        Serial.print(myDHT22.getTemperatureC());
        Serial.print("C ");
        Serial.print(myDHT22.getHumidity());
        Serial.println("%");
        break;
        case DHT_BUS_HUNG:
        Serial.println("BUS Hung ");
        break;
        case DHT_ERROR_TOOQUICK:
        Serial.println("Polled to quick ");
        break;
        }

        setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
        setTemperature(OregonMessageBuffer, t);

        #ifdef THGR228N
        // Set Humidity
        setHumidity(OregonMessageBuffer, h);
        #endif

        // Calculate the checksum
        calculateAndSetChecksum(OregonMessageBuffer);

        // Show the Oregon Message
        for (byte i = 0; 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();
        int i =0;
        while (i++ < 70) {
        // Sleep for 8 s with ADC module and BOD module off x 70 = 560 s
        LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
        }

        }

        • yoon dit :

          Bonjour,
          petit déterrage de topic, cependant je suis fortement intéressé par le code pour le capteur à ultrason si toujours disponible :D
          Merci d’avance ;)

  • David Pilling dit :

    Hi, many thanks for this code.

    I have a comment, after working out what follows, I’ve found my discovery is something that is well known, but I’ll repeat it here to save others effort.

    I got Ook_OSV2 from Jee Labs Cafe, written by Dominique Pierre. Worked well with my Oregon sensors.

    I then set up this encoder – would not work. The Ook code demands 32 bits in the preamble, and this code seems to generate 31..

    I have found that subsequent versions of the Ook code drop the preamble requirement to 24 bits, so the problem goes away if you’re using the latest version.

    • Olivier dit :

      Hi, you’re welcome :)
      Yes, Dominique Pierre explain me that sometimes the OokDecoder doesn’t « see » all 32 bits of the preamble. I have this problem with my THN132N sensors (31bits only are reported).

      However, if my encoder send only 31 bits, It’s a mistake … I need to check, thanks for pointing that out.

  • dnis dit :

    Salut,
    Peut-on créer des sondes à bas cout en utilisant seulement la puce de l’Ardui (Atmega328) comme le montre Idleman , pour les utiliser sur Domoticz via RFXcom.
    merci d’avance.

    • Olivier dit :

      Salut,
      Oui c’est possible et c’était le but à l’origine des articles. Je ne m’en suis pas encore occupé mais j’ai tout ce qu’il faut pour le faire.
      J’aimerai être sous les 5€ boitier compris.

      • Galen dit :

        il semble que erwan ai déjà tenté l’expérience :)

        http://labalec.fr/erwan/?p=1489

        https://forum.jeedom.fr/viewtopic.php?f=36&t=1832

        hormis la conso électrique qui me semble a optimiser pour se rapprocher de celle des oregons officielles cela semble être un bon plan :)

        Apres va falloir que je potasse sérieusement l’arduino :)

        • Olivier dit :

          Salut,

          Oui j’ai vu le post d’Erwan, il ne reste plus qu’à supprimer l’arduino pour être un peut moins cher et plus petit :)

          Le coté conso n’est pas un problème je pense. Les sondes officielles émettent environ toutes les 30s mais tu peux monter à 1min, 5 min voir plus pour monitorer la température d’une pièce c’est largement suffisant. Ensuite, il suffit de mettre l’atmega en veille le reste du temps.
          On doit pouvoir tenir facilement plus d’un 1an à ce rythme avec une ou deux piles LR6.

          • Galen dit :

            C’est clair que les 30-40 secondes d’origine ne sont pas nécessaires :)

            Merci pour la réponse rapide

  • […] comes the rf433_sendOOK. Note that that I am re using code from connectingstuff.net to send oregon 2.1 compatible […]

  • Ludo dit :

    Bonjour à tous,

    Je souhaite faire un répéteur pour les sondes oregon dans le but d’augmenter la portée à partir d’un Uno. La partie réception fonctionne. Par contre la partie émission, j’ai plus de pb et en plus je débute avec Arduino.

    Si j’utilise le sketch d’Olivier, pas d’erreur de compilation par contre la zibase ne reçoit pas les ordres.

    En utilisant la correction de catalin, j’ai une erreur de compilation.

    int Sum(byte count, const byte* data)
    {
    int s = 0;

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

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

    return s;
    }

    Si quelqu’un pouvait m’aider…

    Merci

    • Olivier dit :

      Salut,
      Le code de l’article est correct, je l’avais corrigé suite au commentaire de catalin. Par contre dans son commentaire il y a une erreur (due à wordpress qui à supprimé un bout de code à l’enregistrement du commentaire :/) c’est pour cela que tu as une erreur de compilation.

  • kaa dit :

    salut,

    Tout d’abord merci pour ton boulot de reverse.

    J’ai réussi à faire un module avec un DHT22+ Microview (arduino like)+Emetteur. ça fonctionne à merveille et est reconnu par domoticz comme un « THGN122/123, THGN132, THGR122/228/238/268″.

    j’ai essayé d’y ajouter un capteur BMP180 pour la pression et du coup, j’ai essayé de trouver quelle sonde Oregon faisait ça pour pouvoir l’émuler.
    il semble que la BTHR968/BTHR918N fait ça.

    J’ai essayé de l’émuler mais sans succès et même en émettant la trame soit disant de test du site http://www.mattlary.com/2012/06/23/weather-station-project/ ça ne fonctionne pas. le RFXCOM ne la voit pas.

    une idée sur le codage de cette sonde ?

  • Michel Thevenot dit :

    Bonjour Olivier, bonjour à tous,

    Vraiment un grand bravo et un grand merci pour ce travail et cette mise à dispo, ça marche du tonnerre en réception comme en émission.

    Juste une petite remarque pour la partie réception, à l’intégration de la partie 3/3 j’ai eu une erreur de compilation avec « max_bits = 160; »que j’ai du déclarer ainsi « int max_bits; » en début du sketch.

    J’utilise un Tx/RX 433 vraiment pas cher (ce modèle : avec un fil de 17,5cm comme antenne, je me ballade dans toute la maison).

    Reste à traiter les infos, j’aimerais pouvoir interfacer avec Domoticz, à suivre…

    Merci encore
    Cordialement
    Michel

  • DD91 dit :

    Bonjour

    deja bravo pour toute ces realisations qui font rever des novice comme moi :)

    je souhaite poser une petite question :)

    j’ai commencé a me pencher sur la domotique il y a peut et au regard du prix d’un z-wave et d’un rfxcom je trouve ca bete de mettre peux cher dans un rasp pour devoir payer a prix d’or une interface :)

    le module avec l’arduino presenté dans le post est-il compatible avec le logiciel domoticz?

    est ce que par exemple les prises radiocommandé d’idleman sont compatible avec par exemple?

    j’ai une sonde de temperature exterieure en 433mhz (vendu avec une horloge de base) est ce que ca peut etre compatible?

    merci d’avance de vos reponses

    DD91

Laisser un commentaire

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

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>