Well, it’s been a good while since our last post but there is a good excuse for it, there is a new product coming soon in the shop, a new temperature and humidity sensor so keep visiting us..
Now, for today I am going to make public a piece of code that was worked on by Olivier Lebrun of http://connectingstuff.net. He managed to put the code together to send in an Oregon Scientific v 2.1 format information that can be received and decoded by an Arduino RF shield. As we know up to know if you searched the Internet you will find loads and loads of examples of code that will receive RF signals from Oregon sensors but no code to transmit, well he did it so fair play to him.
What we did we just added an Sension SHT temperature/Humidity Sensor and some lines of code and now we had a fully working Arduino Based Oregon like sensor.
Here is the code produced by adding to Olivier’s code:
/*
* connectingStuff, Oregon Scientific v2.1 Emitter
* http://connectingstuff.net/blog/encodage-protocoles-oregon-scientific-sur-arduino/
*
* Copyright (C) 2013 olivier.lebrun@gmail.com
* Sensirion data added 03/2013
* 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.
*/
int SHT_clockPin = 2; // pin used for clock
int SHT_dataPin = 3; // pin used for data
#undef THN132N
const byte TX_PIN = 11;
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(0×00);
#else
byte POSTAMBLE[]={0×00};
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 (0×10, 0×20, 0×30)
*/
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] = 0×00;
}
/**
* \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] = 0×08;
temp *= -1;
}
else
{
data[6] = 0×00;
}
// 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);
pinMode(13, 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, 0×20);
setId(OregonMessageBuffer, 0xBB);
}
void loop()
{
float temperature = getTemperature();
float humidity = getHumidity();
Serial.print(” | “);
Serial.print(temperature);
Serial.print(” | “);
Serial.println(humidity);
// Get Temperature, humidity and battery level from sensors
// (ie: 1wire DS18B20 for température, …)
setBatteryLevel(OregonMessageBuffer, 1); // 0 : low, 1 : high
setTemperature(OregonMessageBuffer, temperature);
#ifndef THN132N
// Set Humidity
setHumidity(OregonMessageBuffer, humidity);
#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 copy 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();
digitalWrite(13, LOW);
delay(30000);
digitalWrite(13, HIGH);
}
float getTemperature(){
//Return Temperature in Celsius
SHT_sendCommand(B00000011, SHT_dataPin, SHT_clockPin);
SHT_waitForResult(SHT_dataPin);
int val = SHT_getData(SHT_dataPin, SHT_clockPin);
SHT_skipCrc(SHT_dataPin, SHT_clockPin);
return (float)val * 0.01 – 40; //convert to celsius
}
float getHumidity(){
//Return Relative Humidity
SHT_sendCommand(B00000101, SHT_dataPin, SHT_clockPin);
SHT_waitForResult(SHT_dataPin);
int val = SHT_getData(SHT_dataPin, SHT_clockPin);
SHT_skipCrc(SHT_dataPin, SHT_clockPin);
return -4.0 + 0.0405 * val + -0.0000028 * val * val;
}
void SHT_sendCommand(int command, int dataPin, int clockPin){
// send a command to the SHTx sensor
// transmission start
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
digitalWrite(dataPin, HIGH);
digitalWrite(clockPin, HIGH);
digitalWrite(dataPin, LOW);
digitalWrite(clockPin, LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(dataPin, HIGH);
digitalWrite(clockPin, LOW);
// shift out the command (the 3 MSB are address and must be 000, the last 5 bits are the command)
shiftOut(dataPin, clockPin, MSBFIRST, command);
// verify we get the right ACK
digitalWrite(clockPin, HIGH);
pinMode(dataPin, INPUT);
if (digitalRead(dataPin)) Serial.println(“ACK error 0″);
digitalWrite(clockPin, LOW);
if (!digitalRead(dataPin)) Serial.println(“ACK error 1″);
}
void SHT_waitForResult(int dataPin){
// wait for the SHTx answer
pinMode(dataPin, INPUT);
int ack; //acknowledgement
//need to wait up to 2 seconds for the value
for (int i = 0; i < 1000; ++i){
delay(2);
ack = digitalRead(dataPin);
if (ack == LOW) break;
}
if (ack == HIGH) Serial.println(“ACK error 2″);
}
int SHT_getData(int dataPin, int clockPin){
// get data from the SHTx sensor
// get the MSB (most significant bits)
pinMode(dataPin, INPUT);
pinMode(clockPin, OUTPUT);
byte MSB = shiftIn(dataPin, clockPin, MSBFIRST);
// send the required ACK
pinMode(dataPin, OUTPUT);
digitalWrite(dataPin, HIGH);
digitalWrite(dataPin, LOW);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
// get the LSB (less significant bits)
pinMode(dataPin, INPUT);
byte LSB = shiftIn(dataPin, clockPin, MSBFIRST);
return ((MSB << 8) | LSB); //combine bits
}
void SHT_skipCrc(int dataPin, int clockPin){
// skip CRC data from the SHTx sensor
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
digitalWrite(dataPin, HIGH);
digitalWrite(clockPin, HIGH);
digitalWrite(clockPin, LOW);
}
Now, there are a few small things to be done, one is to change the code and add the posibility to change the channel easily with a switch?
But who wants to use an Arduino Uno just to have a sensor working?
Let’s make it work on batteries as well. Curious to know how ? Visit us again soon.
The sensor was tested and is being received with and RFXCOM receiver.
Get in touch if you have discovered something more related to this sensor protocol.