Hardware is very basic: Arduino Nanao, Rx module, Tx module. Same as in WD project.
RX data connected to D3, RX led (with apropriate resistor) connected to D4, TX data connected to A0.
Only Oregon V2 RF protocol is decoded (not all sensors).
Code:
// 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 $
#include <Wire.h>
//#include <LiquidCrystal.h>
#include <LiquidCrystal_I2C.h> // Library for LCD
#include <VirtualWire.h> // library for RF RX/TX
//LiquidCrystal lcd(15, 14, 7, 6, 5, 4);
//LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
LiquidCrystal_I2C lcd(0x27, 20, 4);
#define StationID 0xA1
char DataPacket[32];
int T_Out;
int H_Out;
bool new_data;
int GustNow=-999;
int AverageNow=-999;
int DirectionNow=-999;
int BarometerNow=-999;
int WindBat=100;
int RainRateNow, RainBat;
int RainTotal = -1;
const char ExtraSensors[6][9]= { "Shirla:","Etage:","Chamb.:","Cave:","Cellier:","Ext.:" };
//const byte ExtraSensor[5][2] = ExtraSensors;
int T_Ext[10];
int H_Ext[10];
byte count = 0;
unsigned long last_UpdateDisplay;
class DecodeOOK {
protected:
byte total_bits, Min_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() {
Min_bits = 160;
}
// 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(2 == pos)
{
// 80 * 2
Min_bits = 160;
if(0xEA == data[0]) Min_bits = 136;
if(0x5A == data[0]) Min_bits = 176;
//if(0x2A == data[0]) Min_bits = 168;
}
if (pos >= sizeof data) {
resetDecoder();
return;
}
state = OK;
}
virtual char decode (word width) {
if (200 <= width && width < 1200) {
byte w = width >= 700;
switch (state) {
case UNKNOWN:
if (w != 0) {
// Long pulse
++flip;
}
else if (16 <= 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 {
return -1;
}
return total_bits == Min_bits ? 1: 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;
}
float temperature(const byte* data)
{
int sign = (data[6]&0x8) ? -1 : 1;
float temp = ((data[5]&0xF0) >> 4)*10 + (data[5]&0xF) + (float)(((data[4]&0xF0) >> 4) / 10.0);
return sign * temp;
}
byte humidity(const byte* data)
{
return (data[7]&0xF) * 10 + ((data[6]&0xF0) >> 4);
}
int barometer(const byte* data)
{
return (data[8])+856+28;//28=correction altitude
}
// Ne retourne qu'un apercu de l'etat de la baterie : 10 = faible
byte battery(const byte* data)
{
return (data[4] & 0x4) ? 10 : 90;
}
byte channel(const byte* data)
{
byte channel;
switch (data[2])
{
case 0x10:
channel = 1;
break;
case 0x20:
channel = 2;
break;
case 0x40:
channel = 3;
break;
}
return channel;
}
void reportSerial_new (const char* s, class DecodeOOK& decoder)
{
byte bid = 0;
byte bid1 = 0;
byte PacketID, UnitID;
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();
UnitID = 2;
PacketID = UnitID << 4 | 1; // Hi nibble UnitID, Lo nibble SensorID
// Inside Temp-Hygro : THGR228N,...
if(data[0] == 0x1A && data[1] == 0x2D)
{
switch (data[3])
{
case 0xA3:
{
T_Ext[0] = int(temperature(data) * 100.0);
H_Ext[0] = int(humidity(data) * 100.0);
sendData(StationID, PacketID, T_Ext[0], H_Ext[0], 3, 0);
Serial.print("Shirla, Id:");
break;
}
case 0xE4:
{
T_Ext[1] = int(temperature(data) * 100.0);
H_Ext[1] = int(humidity(data) * 100.0);
Serial.print("Etage, Id:");
break;
}
case 0xF1:
{
T_Ext[2] = int(temperature(data) * 100.0);
H_Ext[2] = int(humidity(data) * 100.0);
sendData(StationID, PacketID, T_Ext[2], H_Ext[2], 2, 0);
Serial.print("Chambre, Id:");
break;
}
case 0x32:
{
T_Ext[3] = int(temperature(data) * 100.0);
H_Ext[3] = int(humidity(data) * 100.0);
sendData(StationID, PacketID, T_Ext[3], H_Ext[3], 0, 0);
Serial.print("Cave, Id:");
break;
}
case 0x7D:
{
T_Ext[4] = int(temperature(data) * 100.0);
H_Ext[4] = int(humidity(data) * 100.0);
sendData(StationID, PacketID, T_Ext[4], H_Ext[4], 1, 0);
Serial.print("Cellier, Id:");
break;
}
case 0x45:
{
T_Ext[5] = int(temperature(data) * 100.0);
H_Ext[5] = int(humidity(data) * 100.0);
Serial.print("Extérieur, Id:");
break;
}
case 0xC3:
{
T_Ext[6] = int(temperature(data) * 100.0);
H_Ext[6] = int(humidity(data) * 100.0);
sendData(StationID, PacketID, T_Ext[6], H_Ext[6], 4, 0);
Serial.print("Soute, Id:");
break;
}
}
//Serial.print("[THGR228N,...] Id:");
Serial.print(data[3], HEX);
Serial.print(" ,Channel:");
Serial.print(channel(data));
Serial.print(" ,temp:");
Serial.print(temperature(data));
Serial.print(" ,hum:");
Serial.print(humidity(data));
Serial.print(" ,bat:");
Serial.print(battery(data));
new_data = true;
Serial.println();
}
// inside Temp-Hygro-Baro : BTHR918
else if (data[0] == 0x5A && data[1] == 0x6D)
{
Serial.print("Rdc, Id:");
//Serial.print("[BTHR918,...] Id:");
Serial.print(data[3], HEX);
Serial.print(" ,Channel:");
Serial.print(channel(data));
Serial.print(" ,temp:");
Serial.print(temperature(data));
Serial.print(" ,hum:");
Serial.print(humidity(data));
Serial.print(" ,baro:");
Serial.print(barometer(data));
Serial.print(" ,bat:");
Serial.print(battery(data));
Serial.println();
T_Ext[7] = int(temperature(data) * 100.0);
H_Ext[7] = int(humidity(data) * 100.0);
BarometerNow = barometer(data);
new_data = true;
}
// outside Temp-Hygro : THGR918,...
else if(data[0] == 0x1A && data[1] == 0x3D)
{
Serial.print("EXT Id:");
Serial.print(data[3], HEX);
Serial.print(" ,Channel:");
Serial.print(channel(data));
Serial.print(" ,temp:");
Serial.print(temperature(data));
Serial.print(" ,hum:");
Serial.print(humidity(data));
Serial.print(" ,bat:");
Serial.print(battery(data));
Serial.println();
T_Out = int(temperature(data) * 100.0);
H_Out = int(humidity(data) * 100.0);
}
// Rain
else if(data[0] == 0x2A && data[1] == 0x1D)
{
bid = 8;
Serial.print("[RGR918,...] Id:");
Serial.print(data[3], HEX);
Serial.print(" Rr:");
RainRateNow = ((data[5]>>4) * 100) + ((data[5] & 0x0F) * 10) + (data[4] >> 4);
Serial.print(RainRateNow);
RainTotal = ((data[8] & 0x0F) * 1000)+ ((data[7] >> 4) * 100) + ((data[7] & 0x0F) * 10)+ (data[6]>>4);
Serial.print(" ,To:");
Serial.print(RainTotal);
Serial.print("mm ,bat:");
RainBat=battery(data);
Serial.print(RainBat);
Serial.println();
}
// Wind
else if(data[0] == 0x3A && data[1] == 0x0D)
{
Serial.print("Vent Id:");
Serial.print(data[3], HEX);
Serial.print(" Direction ");
DirectionNow = ((data[5]>>4) * 100) + ((data[5] & 0x0F) * 10) + (data[4] >> 4);
Serial.print(DirectionNow);
Serial.print(" degrees Rafale:");
GustNow = ((data[7] & 0x0F) * 100) + ((data[6]>>4) * 10) + ((data[6] & 0x0F)) ;
Serial.print(float(GustNow)/10,1);
Serial.print("m/s Moyenne:");
AverageNow = ((data[8]>>4) * 100) + ((data[8] & 0x0F) * 10)+((data[7] >>4)) ;
Serial.print(float(AverageNow)/10,1);
Serial.print("m/s Bat:");
WindBat=battery(data);
Serial.print(WindBat);
//Serial.println("%");
Serial.println();
}
// THN132
else if(data[0] == 0xEA && data[1] == 0x4C)
{
switch (data[3])
{
case 0xBB:
{
T_Ext[8] = int(temperature(data) * 100.0);
//sendData(StationID, PacketID, T_Ext[8], 0, 7, 0);
Serial.print("Congel, Id:");
break;
}
}
Serial.print("[THN132,...] Id:");
Serial.print(data[3], HEX);
Serial.print(" ,Channel:");
Serial.print(channel(data));
Serial.print(" ,temp:");
Serial.print(temperature(data));
Serial.print(" ,bat:");
Serial.print(battery(data));
Serial.println();
}
decoder.resetDecoder();
}
// -----------------------------------------------------------------------------------------
// -------- Function to send data to RF Module ---------------------------------------------
void sendData (int data0, int data1, long data2, long data3, long data4, long data5)
{
memset(DataPacket,0,sizeof(DataPacket)); // clean str array
sprintf(DataPacket, "%d,%d,%ld,%ld,%ld,%ld", data0, data1, data2, data3, data4, data5);
PORTC |= B10; //digitalWrite(TX_Power_PIN, HIGH); - Turn on power from TX module
delay(50); // Wait for power to stabilize
vw_send((uint8_t *)DataPacket, strlen(DataPacket));
vw_wait_tx();
}
// ---------------------- Update Display -------------------------
void Update_Display()
{
byte PacketID, UnitID;
lcd.setCursor(0, 0);
lcd.print(" ");
lcd.setCursor(0, 0);
lcd.print(ExtraSensors[count]);
lcd.print(T_Ext[count]/100.0,1);
lcd.write(B11011111);
lcd.print(";");
lcd.print(H_Ext[count]/100);
switch (count){
case 0 :
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Out:");
lcd.print(T_Out/100.0,1);
lcd.write(B11011111);
lcd.print(";");
lcd.print(H_Out/100);
break;
}
case 1 :
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Pression:");
lcd.print(BarometerNow,1);
break;
}
case 2 :
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Vent:");
lcd.print(DirectionNow);
lcd.print(";");
lcd.print(GustNow/10.0,1);
lcd.print("/");
lcd.print(AverageNow/10.0,1);
lcd.print(" m/s");
break;
}
case 3 :
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Pl.:");
if (RainTotal != -1){
lcd.print(RainTotal);
lcd.print("mm; ");
lcd.print(RainRateNow);
}
else{
lcd.print("NADA");
}
break;
}
case 5 :
{
break;
}
}
count ++;
if (count > 4) count = 0;
last_UpdateDisplay = millis();
}
// ---------------------------------------------------------------
void setup ()
{
Serial.begin(115200);
Serial.println("\n[ookDecoder]");
attachInterrupt(1, ext_int_1, CHANGE); // 0=>pin2 1=> pin3
//------------------------------------------------------------
// Setup the transmitter
//------------------------------------------------------------
//DDRC = DDRC | B11; // pinMode(TX_Power_PIN, OUTPUT); & pinMode(TX_PIN, OUTPUT);
pinMode(14, OUTPUT);
pinMode(4, OUTPUT);
vw_set_ptt_pin(4); // Transmitter ptt connected to pin A1
vw_set_tx_pin(14); // Transmitter connected to pin A0
vw_setup(1000); // Bits per second
//------------------------------------------------------------
lcd.begin();
lcd.print("Coucou");
delay(1000);
lcd.setCursor(0, 0);
lcd.print(" ");
}
void loop () {
static int i = 0;
cli();
word p = pulse;
pulse = 0;
sei();
if (p != 0)
{
if (orscV2.nextPulse(p))
reportSerial_new("OSV2", orscV2);
}
//if (millis() - last_UpdateDisplay > 4000)
//Update_Display();
}
reportSerial_new subroutine you have select which sensor type and ID you like to send to WD. The function call is :
To see the OS V2 sensors that are decoded, just use a serial monitoring software (115200 bauds). you have to retain the ID of the sensors you want to send to WeatherDuino.
highlighted is a new OS temp sensor not yet recognised by the software.
that happens when battery is changed.
I use SensorID 1 in RX software to write the T° and RH % in loopdata to have Extra sensor 1 to 5 available in Cumulus.