221 lines
5.4 KiB
C++
221 lines
5.4 KiB
C++
/*
|
|
* RemoteSensor library v1.0.2 (20130601) for Arduino 1.0
|
|
*
|
|
* This library encodes, encrypts en transmits data to
|
|
* remote weather stations made by Hideki Electronics..
|
|
*
|
|
* Copyright 20112-2013 by Randy Simons http://randysimons.nl/
|
|
*
|
|
* Parts of this code based on Oopsje's CrestaProtocol.pdf, for which
|
|
* I thank him very much!
|
|
*
|
|
* License: GPLv3. See license.txt
|
|
*/
|
|
|
|
#include <SensorReceiver.h>
|
|
|
|
byte SensorReceiver::halfBit = 0;
|
|
word SensorReceiver::clockTime;
|
|
boolean SensorReceiver::isOne;
|
|
unsigned long SensorReceiver::lastChange=0;
|
|
SensorReceiverCallback SensorReceiver::callback;
|
|
byte SensorReceiver::data[14];
|
|
byte SensorReceiver::packageLength;
|
|
word SensorReceiver::duration;
|
|
boolean SensorReceiver::enabled;
|
|
|
|
void SensorReceiver::init(int8_t interrupt, SensorReceiverCallback callbackIn) {
|
|
callback = callbackIn;
|
|
|
|
enable();
|
|
|
|
if (interrupt >= 0) {
|
|
attachInterrupt(interrupt, interruptHandler, CHANGE);
|
|
}
|
|
}
|
|
|
|
void SensorReceiver::interruptHandler() {
|
|
if (!enabled) {
|
|
return;
|
|
}
|
|
|
|
/* I'll follow CrestaProtocol documentation here. However, I suspect it is inaccurate at some points:
|
|
* - there is no stop-bit after every byte. Instead, there's a start-bit (0) before every byte.
|
|
* - Conversely, there is no start-bit "1" before every byte.
|
|
* - An up-flank is 0, down-flank is 1, at least with both my receivers.
|
|
*
|
|
* However, since the first start-bit 0 is hard to distinguish given the current clock-detecting
|
|
* algorithm, I pretend there *is* a stop-bit 0 instead of start-bit. However, this means the
|
|
* last stop-bit of a package must be ignored, as it simply isn't there.
|
|
*
|
|
* This manchester decoder is based on the principle that short edges indicate the current bit is the
|
|
* same as previous bit, and that long edge indicate that the current bit is the complement of the
|
|
* previous bit.
|
|
*/
|
|
|
|
static byte halfBitCounter = 255;
|
|
unsigned long currentTime=micros();
|
|
duration=currentTime-lastChange; // Duration = Time between edges
|
|
|
|
lastChange=currentTime;
|
|
|
|
if (halfBit==0) {
|
|
// Automatic clock detection. One clock-period is half the duration of the first edge.
|
|
clockTime = duration >> 1;
|
|
|
|
// Some sanity checking, very short (<200us) or very long (>1000us) signals are ignored.
|
|
if (clockTime < 200 || clockTime > 1000) {
|
|
return;
|
|
}
|
|
isOne = true;
|
|
}
|
|
else {
|
|
// Edge is not too long, nor too short?
|
|
if (duration < (clockTime >> 1) || duration > (clockTime << 1) + clockTime) { // read as: duration < 0.5 * clockTime || duration > 3 * clockTime
|
|
// Fail. Abort.
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
// Only process every second half bit, i.e. every whole bit.
|
|
if (halfBit & 1) {
|
|
byte currentByte = halfBit / 18;
|
|
byte currentBit = (halfBit >> 1) % 9; // nine bits in a byte.
|
|
|
|
if (currentBit < 8) {
|
|
if (isOne) {
|
|
// Set current bit of current byte
|
|
data[currentByte] |= 1 << currentBit;
|
|
}
|
|
else {
|
|
// Reset current bit of current byte
|
|
data[currentByte] &= ~(1 << currentBit);
|
|
}
|
|
}
|
|
else {
|
|
// Ninth bit must be 0
|
|
if (isOne) {
|
|
// Bit is 1. Fail. Abort.
|
|
reset();
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (halfBit == 17) { // First byte has been received
|
|
// First data byte must be x75.
|
|
if (data[0] != 0x75) {
|
|
reset();
|
|
return;
|
|
}
|
|
}
|
|
else if (halfBit == 53) { // Third byte has been received
|
|
// Obtain the length of the data
|
|
byte decodedByte = data[2]^(data[2]<<1);
|
|
packageLength = (decodedByte >> 1) & 0x1f;
|
|
|
|
// Do some checking to see if we should proceed
|
|
if (packageLength < 6 || packageLength > 11) {
|
|
reset();
|
|
return;
|
|
}
|
|
|
|
halfBitCounter = (packageLength + 3) * 9 * 2 - 2 - 1; // 9 bits per byte, 2 edges per bit, minus last stop-bit (see comment above)
|
|
}
|
|
|
|
// Done?
|
|
if (halfBit >= halfBitCounter) {
|
|
if (halfBit == halfBitCounter) {
|
|
// Yes! Decrypt and call the callback
|
|
if (decryptAndCheck()) {
|
|
(callback)(data);
|
|
}
|
|
}
|
|
|
|
// reset
|
|
halfBit = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Edge is long?
|
|
if (duration > clockTime + (clockTime >> 1)) { // read as: duration > 1.5 * clockTime
|
|
// Long edge.
|
|
isOne = !isOne;
|
|
// Long edge takes 2 halfbits
|
|
halfBit++;
|
|
}
|
|
}
|
|
|
|
halfBit++;
|
|
return;
|
|
}
|
|
|
|
void SensorReceiver::reset() {
|
|
halfBit = 1;
|
|
clockTime = duration >> 1;
|
|
isOne = true;
|
|
}
|
|
|
|
boolean SensorReceiver::decryptAndCheck() {
|
|
byte cs1,cs2,i;
|
|
|
|
cs1=0;
|
|
cs2=0;
|
|
for (i=1; i<packageLength+2; i++) {
|
|
cs1^=data[i];
|
|
cs2 = secondCheck(data[i]^cs2);
|
|
data[i] ^= data[i] << 1;
|
|
}
|
|
|
|
if (cs1) {
|
|
return false;
|
|
}
|
|
|
|
if (cs2 != data[packageLength+2]) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
byte SensorReceiver::secondCheck(byte b) {
|
|
byte c;
|
|
|
|
if (b&0x80)
|
|
b^=0x95;
|
|
c = b^(b>>1);
|
|
if (b&1)
|
|
c^=0x5f;
|
|
if (c&1)
|
|
b^=0x5f;
|
|
|
|
return b^(c>>1);
|
|
}
|
|
|
|
void SensorReceiver::enable() {
|
|
halfBit = 0;
|
|
enabled = true;
|
|
}
|
|
|
|
void SensorReceiver::disable() {
|
|
enabled = false;
|
|
}
|
|
|
|
void SensorReceiver::decodeThermoHygro(byte *data, byte &channel, byte &randomId, int &temp, byte &humidity) {
|
|
channel = data[1] >> 5;
|
|
|
|
// Internally channel 4 is used for the other sensor types (rain, uv, anemo).
|
|
// Therefore, if channel is decoded 5 or 6, the real value set on the device itself is 4 resp 5.
|
|
if (channel >= 5) {
|
|
channel--;
|
|
}
|
|
|
|
randomId = data[1] & 0x1f;
|
|
|
|
temp = 100 * (data[5] & 0x0f) + 10 * (data[4] >> 4) + (data[4] & 0x0f);
|
|
// temp is negative?
|
|
if (!(data[5] & 0x80)) {
|
|
temp = -temp;
|
|
}
|
|
|
|
humidity = 10 * (data[6] >> 4) + (data[6] & 0x0f);
|
|
} |