Décodage des protocoles Oregon Scientific sur Arduino (1/3)
Cet article est le premier d’une petite série. Voulant me passer de mon Rfxcom, je cherche à implémenter les protocoles qu’il décode, du moins ceux que j’utilise, dans un Arduino.
J’ai commencé par les protocoles Oregon Scientific v2 (et v3) qui permettent de recevoir les informations des sondes (température, humidité, vent, etc) du même nom.
Un Décodeur Oregon Scientific pour Arduino
Je n’ai pas implémenté le décodeur moi même, j’ai utilisé celui-ci : http://jeelabs.net/projects/11/wiki/Decoding_the_Oregon_Scientific_V2_protocol (Vous y trouverez un sketch Arduino qui contient d’autres décodeurs, dont celui du protocole v3 que je n’ai pas testé, n’ayant pas de sonde v3 pour le moment).
Malheureusement, lors de mes premiers tests, impossible de recevoir la moindre donnée de mes sondes THN132N (protocole Oregon v2) :/
Deux problèmes
Du coup j’ai lu les specs des protocoles Oregon Scientific ici : http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf . J’y ai trouvé deux problèmes à l’origine de mes soucis. Le premier, c’est que le décodeur ne décode que les trames de 80bits de longueur … or la spec indique que les trames sont de taille non fixe. La plupart des sondes renvoient effectivement 80bits mais d’autres non. Les THN132N ne renvoient par exemple que 68bits.
Le deuxième problème c’est que le décodeur attends 32bits de préambule (et c’est bien ce que la spec indique) mais avec mes sondes, je n’en reçois que 31 ? :/ … sur ce coup là aucune explications. J’ai contacté l’auteur du décodeur qui m’a répondu très vite (merci à lui
et m’explique qu’il arrive que le récepteur « loupe » les premiers bits (si j’ai bien compris) mais que l’on peu considérer qu’au bout d’au moins 24bits de préambule la trame qui suit est bien une trame Oregon v2. Je modifie (en surbrillance voir ci-dessous) donc le décodeur et tout marche maintenant
Le code du décodeur Oregon Scientific modifié
class OregonDecoderV2 : public DecodeOOK {
public:
OregonDecoderV2() {}
// add one bit to the packet data buffer
virtual void gotBit (char value) {
if(!(total_bits & 0x01))
{
data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
}
total_bits++;
pos = total_bits >> 4;
if (pos >= sizeof data) {
Serial.println("sizeof data");
resetDecoder();
return;
}
state = OK;
}
virtual char decode (word width) {
if (200 <= width && width < 1200) {
//Serial.println(width);
byte w = width >= 700;
switch (state) {
case UNKNOWN:
if (w != 0) {
// Long pulse
++flip;
} else if (w == 0 && 24 <= flip) {
// Short pulse, start bit
flip = 0;
state = T0;
} else {
// Reset decoder
return -1;
}
break;
case OK:
if (w == 0) {
// Short pulse
state = T0;
} else {
// Long pulse
manchester(1);
}
break;
case T0:
if (w == 0) {
// Second short pulse
manchester(0);
} else {
// Reset decoder
return -1;
}
break;
}
} else if (width >= 2500 && pos >= 8) {
return 1;
} else {
return -1;
}
return 0;
}
};
Le hardware
Coté hardware, il suffit de connecter un récepteur RF433 sur l’Arduino. J’utilise celui-ci pour mes tests : http://www.watterott.com/en/RF-Link-2400bps-Receiver-434MHz mais l’auteur du décodeur qui en a testé plusieurs m’a conseillé celui ci : http://www.lextronic.fr/P865-recepteur-43392-mhz-rrq3-433.html (moins sensible au bruit).
Vcc sur le 5v de l’Arduino, Gnd sur gnd Arduino, data sur la pin 3 (interruption 1) et une antenne de 17.3 cm.
La portée n’est pas terrible lors de mes tests mais nous nous occuperons de cela dans le prochain article.
Exemple
le sketch complet de réception et affichage de la trame reçue :
// Oregon V2 decoder modfied - Olivier Lebrun
// Oregon V2 decoder added - Dominique Pierre
// New code to decode OOK signals from weather sensors, etc.
// 2010-04-11 <jcw@equi4.com> http://opensource.org/licenses/mit-license.php
// $Id: ookDecoder.pde 5331 2010-04-17 10:45:17Z jcw $
class DecodeOOK {
protected:
byte total_bits, bits, flip, state, pos, data[25];
virtual char decode (word width) =0;
public:
enum { UNKNOWN, T0, T1, T2, T3, OK, DONE };
DecodeOOK () { resetDecoder(); }
bool nextPulse (word width) {
if (state != DONE)
switch (decode(width)) {
case -1: resetDecoder(); break;
case 1: done(); break;
}
return isDone();
}
bool isDone () const { return state == DONE; }
const byte* getData (byte& count) const {
count = pos;
return data;
}
void resetDecoder () {
total_bits = bits = pos = flip = 0;
state = UNKNOWN;
}
// add one bit to the packet data buffer
virtual void gotBit (char value) {
total_bits++;
byte *ptr = data + pos;
*ptr = (*ptr >> 1) | (value << 7);
if (++bits >= 8) {
bits = 0;
if (++pos >= sizeof data) {
resetDecoder();
return;
}
}
state = OK;
}
// store a bit using Manchester encoding
void manchester (char value) {
flip ^= value; // manchester code, long pulse flips the bit
gotBit(flip);
}
// move bits to the front so that all the bits are aligned to the end
void alignTail (byte max =0) {
// align bits
if (bits != 0) {
data[pos] >>= 8 - bits;
for (byte i = 0; i < pos; ++i)
data[i] = (data[i] >> bits) | (data[i+1] << (8 - bits));
bits = 0;
}
// optionally shift bytes down if there are too many of 'em
if (max > 0 && pos > max) {
byte n = pos - max;
pos = max;
for (byte i = 0; i < pos; ++i)
data[i] = data[i+n];
}
}
void reverseBits () {
for (byte i = 0; i < pos; ++i) {
byte b = data[i];
for (byte j = 0; j < 8; ++j) {
data[i] = (data[i] << 1) | (b & 1);
b >>= 1;
}
}
}
void reverseNibbles () {
for (byte i = 0; i < pos; ++i)
data[i] = (data[i] << 4) | (data[i] >> 4);
}
void done () {
while (bits)
gotBit(0); // padding
state = DONE;
}
};
class OregonDecoderV2 : public DecodeOOK {
public:
OregonDecoderV2() {}
// add one bit to the packet data buffer
virtual void gotBit (char value) {
if(!(total_bits & 0x01))
{
data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
}
total_bits++;
pos = total_bits >> 4;
if (pos >= sizeof data) {
Serial.println("sizeof data");
resetDecoder();
return;
}
state = OK;
}
virtual char decode (word width) {
if (200 <= width && width < 1200) {
//Serial.println(width);
byte w = width >= 700;
switch (state) {
case UNKNOWN:
if (w != 0) {
// Long pulse
++flip;
} else if (w == 0 && 24 <= flip) {
// Short pulse, start bit
flip = 0;
state = T0;
} else {
// Reset decoder
return -1;
}
break;
case OK:
if (w == 0) {
// Short pulse
state = T0;
} else {
// Long pulse
manchester(1);
}
break;
case T0:
if (w == 0) {
// Second short pulse
manchester(0);
} else {
// Reset decoder
return -1;
}
break;
}
} else if (width >= 2500 && pos >= 8) {
return 1;
} else {
return -1;
}
return 0;
}
};
OregonDecoderV2 orscV2;
volatile word pulse;
void ext_int_1(void)
{
static word last;
// determine the pulse length in microseconds, for either polarity
pulse = micros() - last;
last += pulse;
}
void reportSerial (const char* s, class DecodeOOK& decoder) {
byte pos;
const byte* data = decoder.getData(pos);
Serial.print(s);
Serial.print(' ');
for (byte i = 0; i < pos; ++i)
{
Serial.print(data[i] >> 4, HEX);
Serial.print(data[i] & 0x0F, HEX);
}
Serial.println();
decoder.resetDecoder();
}
void setup ()
{
Serial.begin(115200);
Serial.println("\n[ookDecoder]");
attachInterrupt(1, ext_int_1, CHANGE);
//DDRE &= ~_BV(PE5); //input with pull-up
//PORTE &= ~_BV(PE5);
}
void loop () {
static int i = 0;
cli();
word p = pulse;
pulse = 0;
sei();
if (p != 0)
{
if (orscV2.nextPulse(p))
reportSerial("OSV2", orscV2);
}
}
Un exemple de trames émisent par mes sondes THN132N :
OSV2 EA4C20725C21D083 OSV2 EA4C40F85C21D0D4
Conclusion
Nous avons le décodeur qui nous affiche les trames brutes reçues. Dans le prochain article nous ajouterons le décodage de ces trames (ça en fait des décodeurs…) afin de récupérer les informations qui nous intéresse à savoir : l’id de la sonde, son type, le type d’information reçue, sa valeur, le niveau de batterie, etc.
Je n’ai pour l’instant testé le décodeur qu’avec des sondes THN132N. Si vous possédez d’autres sondes, faites moi un petit retour sur le fonctionnement! (vous pouvez coller les trames reçues en commentaire). Merci à vous.
La suite c’est ici -> Decodeur Oregon Scientific Partie 2
Transformer XBMC en SqueezeBox Décodage des protocoles Oregon Scientific sur Arduino (2/3)
Information supplementaire, ce code est pour un arduino mega, ne fonctionne pas sur un arduino uno.
je reprend le code de jeelabs pour le faire marcher sur un arduino uno mais je n’arrive pas a comprendre quel est le port d’entrée…. je reposte mes tests ensuite
Effectivement, je n’ai testé que sur un mega mais normalement l’interruption 1 est branché sur la meme pin (la 3)
Je test des que j’ai le temps
Tu aurais moyen de réessayer le stech ci dessus sur ton UNO en utilisant la pin digital 3 (c’est l’interruption 1) comme sortie du récepteur (pin data) ?
car je ne suis pas sur que ma condition de sortie fonctionne pour toutes les sondes : le « else if (width >= 2500 && pos >= 8) »
dans le setup tu utilises le port E du mega , pas de port E sur uno.
j’ai modifié les lignes du decodage comme toi, ca decrypte toujours si en ligne 168 j’ai return total_bits == 160 ? 1: 0; sinon ca decode pas.
le code de jeelabs est bon sur arduino uno sur la patte ADC1 (AnalogIn 2)
voici la reception avec une sonde THGR228N (thermo/hydro)
OSV2 1A2D20BB1C26008348A7
OSV2 1A2D20BB8C2500834EE0
OSV2 1A2D20BB6C2500834C24
OSV2 1A2D20BB4C2500834A08
Encore Merci Olivier
vivement la suite de l’article….
Super
merci Michael.
J’essai de poster la suite d’ici demain. Il y aura le décodage des THN132N et de ta sonde THGR228N du coup ainsi que les informations pour décoder les autres sondes.
Pour info, quel récepteur utilise tu ?
Ce fut plus long que prévu :/ mais il est enfin là
-> Décodage des protocoles Oregon Scientific sur Arduino (2/3)
pour le recepteur, c’est un premier prix comme celui que tu utilises, j’en ai deux autres a tester ….
MERRRRRRRRRRCIIIII !!!
Un grand merci à toi ! Je m’avance un peu, peut-être, effectivement je viens de lire ton article mais je ne pourrai tester que ce soir si cela fonctionne (pas de raison j’ai tout à l’identique je crois) .
Quelle explication ! J’avais imprimé la doc Oregon mais elle ne me parlait pas du tout v___v
Comme je vois que tu as dans le bain des décodages de sondes sur du 433Mhz, j’en profite pour t’exposé un autre de mes hacks en cours (enfin qui est au point O et qui risque pas de bougé car les informations sont rare) .
il s’agit d’une sonde de courant , je m’explique , c’est une prise qui se met en serie avec un appareillage electrique (frigo…etc) afin de connaitre sa consommation . C’est appelé : un consomètre sans fil, de marque Otio , modèle 12BB10T et cela émet entre 433.05 et 434.79Mhz, je l’ai démonté je pourrais joindre la photo plus tard, il semble que le MicroControlleur soit un : SONIX SN8P2604A Doc Constructeur .
Je sais qu’il est possible de capturer les trames emises via un arduino mais je n’ai pas le schema de branchement. Merci encore pour ton travail! ++Benny
Salut BennyBoom,
, n’hésite pas à faire un retour si ça marche (ou pas:/ ).
Content que ça puisse aider
Pour le wattmètre Otio, on doit pouvoir choper des données mais ils faut ensuite trouver quel codage est utilisé pour reconstituer les trames puis décoder les données :/ … Skywodd t’en parlait dans une de ses réponses à ton commentaire (sur skyduino). Il a réaliser ce genre de chose avec d’autres membres du forum fr arduino pour le matériel blyss : Reverse engineering des interrupteurs domotique Blyss.
A moins qu’il y ai déjà des infos à propos du protocole utilisé, je pense que c’est au dessus de mes compétences :/
Salut Olivier,
Je viens de rentrer ton sketch dans mon Arduino UNO , et après avoir changé l’entrée Data qui était souvent sur les pin11/12/13 (selon les sketch trouvé sur internet), sur la pin3, j’ai des TRAMES!!!! VICTOIRE!
Voici ce que je chope :
[ookDecoder]
OSV2 1A2D103742197005378E
OSV2 1A2D103742197005378E
OSV2 1A2D103742197005378E
OSV2 1A2D103742197005378E
OSV2 1A2D103742197005378E
OSV2 1A2D103742197005378E
Par contre pour compiler j’ai du commenter les lignes suivantes :
//DDRE &= ~_BV(PE5);
//PORTE &= ~_BV(PE5);
qui me remontaient l’erreur :
In function ‘void setup()’:
error: ‘DDRE’ was not declared in this scope
error: ‘PE5′ was not declared in this scope
error: ‘PORTE’ was not declared in this scope
Ceux sont des variables à définir sur une arduino mega?
Autre question, j’ai flingué un récepteur Oregon (tombé du bar, les cristaux liquide ont pleuré xD) donc j’ai du en racheter un et donc j’ai à présent 2 sondes THN132N , un switch permet de choisir le numéro de la sonde entre 1 , 2 et 3, sais tu si dans la trame l’identifiant est stipulé ? (Je suppose que oui mais je voulais savoir si tu avais d’autres infos sur ca)
Merci également pour ta réponse sur le wattmètre, j’ai vu le tuto de Skyduino, effectivement le travail réalisé est monstrueux , je me disais que peut etre tu avais déjà planché dessus
Je viens de voir que tu viens de publier la suite du tuto sur oregonV2 je vais de ce pas lire ton billet, j’ai pour but de transmettre les infos de mes sondes via ethernet à un nas qui stockera les métriques et les publiera via des graphes RRD servit par une interfaceweb.
Je te souhaite bonne continuation , la domotique m’intéresse beaucoup surtout que tout comme toi j’ai le projet d’un homecinema dans les cartons
++Benny
Super
, je t’ai répondu sur l’autre billet.
Le but final cette série d’articles est semblable au tiens: Réaliser un matériel autonome connecté au réseau qui envoi les infos des sondes oregon via xPL sur le réseau
(et intérogeable en HTML)
Ensuite tu peux récupérer ça comme tu veux. Dans une interface perso (c’est ce que je fais, avec RRD justement), dans domogik, etc …
Bonne chance pour ta salle HC, c’est vraiment un chouette projet
un switch permet de choisir le numéro de la sonde entre 1 , 2 et 3, sais tu si dans la trame l’identifiant est stipulé ?
Il permet de choisir le canal. Il est bien dans la trame mais n’est pas l’identifiant de la sonde. L’identifiant est généré aléatoirement à chaque changement de pile (qui est aussi dans la trame).
Hello,
Pour la salle HomeCinema j’ai pas trouvé de post sur ton site qui parle de la tienne? Si tu as des billes a me filer coté domotique ou meme matos Hifivideo je suis preneur! Merci encore
++
Benny
Salut,
J’ai vendu ma maison début juin, la salle ciné et tout le matériel :/ Peut etre que j’en ferai un petit post (à titre posthume :’)
Si tu cherche des sources d’inspiration je te conseil l’excellent forum de homecinema-fr.com notamment la section Installations dédiées . J’y est trouvé à l’époque tout ce dont j’avais besoin (j’y ai un post sur ma salle de l’époque).
Pour le matériel je ne saurais pas te guider je ne suis pas vraiment connaisseur.
Quelques coneils en vrac quand meme :
Quand j’ai commencé la salle je n’avais au début d’un ancien kit de salon JBL 5.1 et un vieil ampli HK AVR2000, et bien le son était déjà vraiment bon (pour mon niveau en tout cas).
Pour l’écran je te conseille la toile que vend l’un des membres du forum. C’est une toile transsonore (elle te permet de positionner les enceintes cachées derrière l’écran sans perdre en qualité d’écoute. En plus de ça, elle n’est pas chère.
Je ne sais pas où se trouve la pièce que tu as choisis ? moi elle se trouvait sous ma terrasse. Je n’ai pas eu besoin de forcer sur l’isolation phonique, on entendait rien dans la maison meme à fort niveau sonore. Si elle se trouve dans la maison, n’hésite pas à mettre le paquet sur l’isolation pour pouvoir en profiter (surtout le soir qd les enfants font dodo
. La meilleure technique est de faire de nouveaux murs,sol et plafond découplés des anciens (tu as des exemples sur le forum).
Placer les élements électroniques hors du champ de vision.
Mettre un canapé plutot que des sièges ciné, c’est moins classe mais tellement plus confortable
Pour la domotique, un tableau élec à part rien que pour la salle. Les lumières étaient pilotées par un arduino. J’utilisais un PCHC linux/XBMC qui envoyait des ordres HTTP à l’arduino sur réception des ordres de la télecommande (ex : sur play, il ordonnait à l’arduino d’éteindre progressivement les lumières, etc)
voila voila. Si tu veux plus de détails hésite pas à me contacter par mail, ça sera plus simple
Article sitôt lu, sitôt mis en application !
Je suis reparti du sketch original de Jeelabs, adapté pour une Arduino Uno (PD2, INT0). Le récepteur est un ST‐RX07A‐ASK de chez Summitech Technology.
La sonde est une Oregon Scientific THGR122NX qui renvoie ce genre de trames (avec décodage) :
OSV2 1A2D1072512080E73F2C[THGR228N,...] Id:72 ,Channel:1 ,temp:20.50 ,hum:78 ,bat:90
OSV2 1A2D1072412080E73E3A[THGR228N,...] Id:72 ,Channel:1 ,temp:20.40 ,hum:78 ,bat:90
OSV2 1A2D1072312080E73D58[THGR228N,...] Id:72 ,Channel:1 ,temp:20.30 ,hum:78 ,bat:90
OSV2 1A2D1072212080E73C4E[THGR228N,...] Id:72 ,Channel:1 ,temp:20.20 ,hum:78 ,bat:90
OSV2 1A2D1072112060E739A2[THGR228N,...] Id:72 ,Channel:1 ,temp:20.10 ,hum:76 ,bat:90
Super merci pour ton retour
Sur le conseils de l’auteur du sketch original (très sympa cela dit en passant), j’ai ajouté la détection du type de sonde pendant la réception des trames de manière à connaitre la longueur de la trame. Ca fonctionne bien et du coup toutes les sondes devraient être détectées sans problèmes.
J’ai aussi ajouté le calcul de la checksum pour vérifier la cohérence des trames reçues et le décodage des infos de toutes les sondes reconnues … bientôt en ligne
J’ai modifié (et testé) le sketch pour qu’il fonctionne indifféremment sur mega et duemilanove, je n’ai pas de UNO pour tester.
Il suffit effectivement de supprimer/commenter :
DDRE &= ~_BV(PE5);
PORTE &= ~_BV(PE5);
qui sont des commandes de manipulation de port (du mega en l’occurrence). Si je ne dis pas de conneries, c’est redondant avec la fonction attachInterrupt()
La nouvelle version fonctionne aussi sur Arduino Uno.
Par contre les lignes commentées ne sont pas redondantes avec la fonction attachInterrupt. Même si elles ne sont pas obligatoires dans ce cas précis…
La ligne DDRE &= ~_BV(PE5); configure le port PE5 en entrée. Par défaut, elle l’est déjà au démarrage de l’ATmega. De plus le flag d’interruption sera levé à n’importe quel changement d’état de la broche configurée en interruption, qu’elle soit en entrée ou en sortie.
La ligne PORTE &= ~_BV(PE5); désactive la résistance de pull-up de l’entrée PE5. Par défaut, elle l’est déjà au démarrage de l’ATmega.
Cependant sur certains projets, cette entrée sera peut-être utilisée d’une manière différente et il me semble préférable de prendre la peine de la configurer en conservant les deux lignes. Quitte à les adapter ou à gérer les différents cas avec une compilation conditionnelle…
Merci Julien pour les explications, je vais corriger ça.
sur le sujet ?
Connaitrais tu une bonne doc (compréhensible
De rien Olivier
Pour les documentations, la bible est la documentation technique de chaque microcontrôleur, plus particulièrement les chapitres « External Interrupts » et ‘I/O Ports ». Mais ce n’est pas forcément accessible et peut rapidement devenir indigeste…
Sinon il y aussi la note d’application AVR1200: Using External Interrupts for megaAVR Devices.
Enfin on trouve pas mal d’autres ressources mais pas aussi complètes…
Bonjour
Merci pour ce super travail
De mon coté j ai reussi à avoir les trames oregonV3 d une thgr810.
J ai mis la data de la carte reception sur A1
Voici un exemple de trame recue sur arduino :
OSV3 FA2814A93022304443BE
Maintenant reste la partie emission vers un rfxtrx433 à partir de l arduino !!
Bonsoir,
Je n’ai pas de sondes v3, tu as utilisé le code sur jeelabs.net ? Il fonctionne directement, sans changements ?
> Maintenant reste la partie emission vers un rfxtrx433 à partir de l arduino !!
Voila c’est fait
-> Emission de trames Oregon Scientific à partir d’un arduino
[...] des protocoles Oregon Scientific sur Arduino (2/3) | Connecting Stuff Décodage des protocoles Oregon Scientific sur Arduino (1/3) | Connecting Stuff Le senseur à effet Hall US5881LUA Un senseur à effet Hall est sensible au champ magnétique. [...]