Skip to content

Instantly share code, notes, and snippets.

@RouquinBlanc
Created August 13, 2015 10:42
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RouquinBlanc/5cb6ff88cd02e68d48ea to your computer and use it in GitHub Desktop.
Save RouquinBlanc/5cb6ff88cd02e68d48ea to your computer and use it in GitHub Desktop.
Send Temperature, Humidity and Pressure data as an Oregon BTHR918N Sensor using Arduino and 433MHz Transmitter
Based on:
- original work from found on this place:
http://www.connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
- Oregon Protocol description:
http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf
- BTHR918N frame description from this site:
http://syno.haeflinger.com/index.php/Domotique#BTHR918N
I was able to get a correct signal emitted from a simple RF433MHz transmitter plugged on an Arduino nano,
decoded by the RFXCOM Rfxtrx433e module, and then correctly displayed on Domoticz.
/*
* connectingStuff, Oregon Scientific v2.1 Emitter
* http://www.connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
*
* Copyright (C) 2013 olivier.lebrun@gmail.com
* Modified by Jonathan Martin <therouquinblanc@gmail.com>, August 2015
*
* 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 MODE_0 0 // Temperature only [THN132N]
#define MODE_1 1 // Temperature + Humidity [THGR2228N]
#define MODE_2 2 // Temperature + Humidity + Baro() [BTHR918N]
#define MODE MODE_2
const byte TX_PIN = 6;
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
#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()
{
// Get Temperature, humidity and battery level from sensors
// (ie: 1wire DS18B20 for température, ...)
setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
setTemperature(OregonMessageBuffer, 11.2);
#if MODE != MODE_0
// Set Humidity
setHumidity(OregonMessageBuffer, 52);
#endif
#if MODE == MODE_2
// Set Pressure
setPressure(OregonMessageBuffer, 1013);
#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);
}
@RouquinBlanc
Copy link
Author

New version of this code as an Arduino library available here: ArduinOregon

@Mahrc
Copy link

Mahrc commented May 17, 2016

Bonjour,
C'est super pratique d'avoir trouvé votre sketch, pour mon projet, ça me permet d'avancer beaucoup plus vite !
Comme vous, sur arduino nano, j'envoi les trames via RF433, et je reçois par un boîtier rfxcomm.

J'ai juste un petit problème, dans la rubrique humidité, je n'arrive pas à avoir le symbole "%", voici un copier/collé de l'affichage de la station reception:

Received = 0D 54 02 00 CB 02 00 6E 34 00 03 F5 01 79
Date/Time = 2016-05-17 23:57:05
Packet Length = 0D
Packettype = Temperature, humidity and barometric sensors
Subtype = BTHR918N, BTHR968
Seqnbr = 00
Id = CB02
Temperature = 11.0 C
Humidity = 52
Humidity Status = Normal
Barometric pressure = 1013 hPa
Forecast Status = Sunny
Signal level = 7

Battery = 9

Received = 0A 52 01 01 F9 01 00 94 54 03 59
Date/Time = 2016-05-17 23:57:18
Packet Length = 0A
Packettype = Temperature and humidity sensors
Subtype = THGN122/123, THGN132, THGR122/228/238/268
Seqnbr = 01
Id = F901
Temperature = 14.8 C
Humidity = 84%
Humidity Status = Wet
Battery = 9

Signal level = 5

Je galère pour essayer de régler le problème, est-ce que ça vous parle ?

@RouquinBlanc
Copy link
Author

Bonjour,

J’ai deja vu la sortie du RFXCOM ne pas être vraiment homogène, avec des différences selon le type de sensor connecté! Ici on voit bien que l’humidité est affichée sans % avec un BTHR918N, et avec % pour le THGN122! Pourtant les deux valeurs sont bien des pourcentages...

Je ne sais pas en quel languages vous comptez récupérer la sortie du rfxcom, mais il devrait être assez facile de récuperer la valeur, avec ou sans pourcentage. Par exemple en python, vous pouvez utiliser une expression régulière pour filtrer le pourcentage:

>>> import re
>>> m = re.search('Humidity = ([0-9]+)%?', 'Humidity = 84%')
>>> m.group(1)
'84'
>>> m = re.search('Humidity = ([0-9]+)%?', 'Humidity = 52')
>>> m.group(1)
'52'

@ReMiOS
Copy link

ReMiOS commented Mar 11, 2017

I used this code on my arduino Nano to send it to my Raspberry with Pilight installed

This works with my pilight compiled with v7_oregon and test_dev branch from https://github.com/wo-rasp/pilight/tree/test_dev
But the pressure gave me strange readings so i altered the setPressure since it swaps bytes
( for instance 5A6D1234567890 becomes 5A6D2143658709 )

With the code below it works fine:

void setPressure(byte *data, float pres)
{
data[8] = (((int)round(pres) & 0x0F00) >> 4 | ((int)round(pres) & 0xF000)>>12 );
data[9] = (((int)round(pres) & 0x000F) << 4 | ((int)round(pres) & 0x00F0)>>4 );
}

@iw9hlv
Copy link

iw9hlv commented Mar 18, 2017

ciao, many thanks for your work !

I've tried this code with different arduino's board, a nano and uno with a simple tx on 433,920 simulating a common THGR228N.
the problem is that another arduino can receive and decode the simulated transmission but no oregon bases are capable to receive it .
what I wrong ?
I fear going crazy ...

@michelinok
Copy link

ciao, many thanks for your work !

I've tried this code with different arduino's board, a nano and uno with a simple tx on 433,920 simulating a common THGR228N. the problem is that another arduino can receive and decode the simulated transmission but no oregon bases are capable to receive it . what I wrong ? I fear going crazy ...

Did you solve?

@RouquinBlanc
Copy link
Author

Sorry, this is too old in my memory I don't use this anymore... If you find a solution, please post here for reference!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment