Created
July 7, 2016 03:19
-
-
Save GeorgeHahn/97c5c67b64c53ee8c6de7f5c1343c833 to your computer and use it in GitHub Desktop.
Tweaked code from Jens Jensen to run in ESP8266 Arduino environment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/***************************************** | |
accurite 5n1 weather station decoder | |
for arduino and 433 MHz OOK RX module | |
Note: use superhet (with xtal) rx board | |
the regen rx boards are too noisy | |
Jens Jensen, (c)2015 | |
*****************************************/ | |
// pulse timings | |
// SYNC | |
#define SYNC_HI 675 | |
#define SYNC_LO 550 | |
#define HIGH 1 | |
#define LONG_HI 475 | |
#define LONG_LO 350 | |
#define LOW 0 | |
#define SHORT_HI 275 | |
#define SHORT_LO 150 | |
#define RESETTIME 10000 | |
// other settables | |
#define LED 13 | |
#define PIN D2 // data pin from 433 RX module | |
#define MAXBITS 65 // max framesize | |
//#define DEBUG 1 // uncomment to enable debugging | |
#define DEBUGPIN D1 // pin for triggering logic analyzer | |
#define METRIC_UNITS 0 // select display of metric or imperial units | |
// sync states | |
#define RESET 0 // no sync yet | |
#define INSYNC 1 // sync pulses detected | |
#define SYNCDONE 2 // complete sync header received | |
volatile unsigned int pulsecnt = 0; | |
volatile unsigned long risets = 0; // track rising edge time | |
volatile unsigned int syncpulses = 0; // track sync pulses | |
volatile byte state = RESET; | |
volatile byte buf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; // msg frame buffer | |
volatile bool reading = false; // have valid reading | |
volatile bool flag = false; | |
unsigned int raincounter = 0; | |
// wind directions: | |
// { "NW", "WSW", "WNW", "W", "NNW", "SW", "N", "SSW", | |
// "ENE", "SE", "E", "ESE", "NE", "SSE", "NNE", "S" }; | |
const float winddirections[] = { 315.0, 247.5, 292.5, 270.0, | |
337.5, 225.0, 0.0, 202.5, | |
67.5, 135.0, 90.0, 112.5, | |
45.0, 157.5, 22.5, 180.0 }; | |
// wx message types | |
#define MT_WS_WD_RF 49 // wind speed, wind direction, rainfall | |
#define MT_WS_T_RH 56 // wind speed, temp, RH | |
void setup() | |
{ | |
// put your setup code here, to run once: | |
Serial.begin(115200); | |
Serial.println(F("Starting Acurite5n1 433 WX Decoder v0.3 ...")); | |
pinMode(PIN, INPUT); | |
raincounter = 0; | |
#ifdef DEBUG | |
// setup a pin for triggering logic analyzer for debugging pulse train | |
pinMode(DEBUGPIN, OUTPUT); | |
digitalWrite(DEBUGPIN, HIGH); | |
#endif | |
attachInterrupt(PIN, My_ISR, CHANGE); | |
} | |
void loop() | |
{ | |
doloop(); | |
if (reading) { | |
// reading found | |
noInterrupts(); | |
if (acurite_crc(buf, sizeof(buf))) { | |
// passes crc, good message | |
digitalWrite(LED, HIGH); | |
#ifdef DEBUG | |
int i; | |
for (i = 0; i < 8; i++) { | |
Serial.print(buf[i], HEX); | |
Serial.print(" "); | |
} | |
Serial.println(F("CRC OK")); | |
#endif | |
float windspeedkph = getWindSpeed(buf[3], buf[4]); | |
Serial.print("windspeed: "); | |
if (METRIC_UNITS) { | |
Serial.print(windspeedkph, 1); | |
Serial.print(" km/h, "); | |
} | |
else { | |
Serial.print(convKphMph(windspeedkph), 1); | |
Serial.print(" mph, "); | |
} | |
int msgtype = (buf[2] & 0x3F); | |
if (msgtype == MT_WS_WD_RF) { | |
// wind speed, wind direction, rainfall | |
float rainfall = 0.00; | |
unsigned int curraincounter = getRainfallCounter(buf[5], buf[6]); | |
if (raincounter > 0) { | |
// track rainfall difference after first run | |
rainfall = (curraincounter - raincounter) * 0.01; | |
} | |
else { | |
// capture starting counter | |
raincounter = curraincounter; | |
} | |
float winddir = getWindDirection(buf[4]); | |
Serial.print("wind direction: "); | |
Serial.print(winddir, 1); | |
Serial.print(", rain gauge: "); | |
if (METRIC_UNITS) { | |
Serial.print(convInMm(rainfall), 1); | |
Serial.print(" mm"); | |
} | |
else { | |
Serial.print(rainfall, 2); | |
Serial.print(" inches"); | |
} | |
} | |
else if (msgtype == MT_WS_T_RH) { | |
// wind speed, temp, RH | |
float tempf = getTempF(buf[4], buf[5]); | |
int humidity = getHumidity(buf[6]); | |
bool batteryok = ((buf[2] & 0x40) >> 6); | |
Serial.print("temp: "); | |
if (METRIC_UNITS) { | |
Serial.print(convFC(tempf), 1); | |
Serial.print(" C, "); | |
} | |
else { | |
Serial.print(tempf, 1); | |
Serial.print(" F, "); | |
} | |
Serial.print("humidity: "); | |
Serial.print(humidity); | |
Serial.print(" %RH, battery: "); | |
if (batteryok) { | |
Serial.print("OK"); | |
} | |
else { | |
Serial.print("LOW"); | |
} | |
} | |
else { | |
Serial.print("unknown msgtype: "); | |
for (int i = 0; i < 8; i++) { | |
Serial.print(buf[i], HEX); | |
Serial.print(" "); | |
} | |
} | |
// time | |
unsigned int timesincestart = millis() / 60 / 1000; | |
Serial.print(", mins since start: "); | |
Serial.print(timesincestart); | |
Serial.println(); | |
} | |
else { | |
// failed CRC | |
#ifdef DEBUG | |
Serial.println(F("CRC BAD")); | |
#endif | |
} | |
digitalWrite(LED, LOW); | |
reading = false; | |
interrupts(); | |
} | |
delay(0); | |
} | |
bool acurite_crc(volatile byte row[], int cols) | |
{ | |
// sum of first n-1 bytes modulo 256 should equal nth byte | |
cols -= 1; // last byte is CRC | |
int sum = 0; | |
for (int i = 0; i < cols; i++) { | |
sum += row[i]; | |
} | |
if (sum != 0 && sum % 256 == row[cols]) { | |
return true; | |
} | |
else { | |
return false; | |
} | |
} | |
float getTempF(byte hibyte, byte lobyte) | |
{ | |
// range -40 to 158 F | |
int highbits = (hibyte & 0x0F) << 7; | |
int lowbits = lobyte & 0x7F; | |
int rawtemp = highbits | lowbits; | |
float temp = (rawtemp - 400) / 10.0; | |
return temp; | |
} | |
float getWindSpeed(byte hibyte, byte lobyte) | |
{ | |
// range: 0 to 159 kph | |
int highbits = (hibyte & 0x7F) << 3; | |
int lowbits = (lobyte & 0x7F) >> 4; | |
float speed = highbits | lowbits; | |
// speed in m/s formula according to empirical data | |
if (speed > 0) { | |
speed = speed * 0.23 + 0.28; | |
} | |
float kph = speed * 60 * 60 / 1000; | |
return kph; | |
} | |
float getWindDirection(byte b) | |
{ | |
// 16 compass points, ccw from (NNW) to 15 (N), | |
// { "NW", "WSW", "WNW", "W", "NNW", "SW", "N", "SSW", | |
// "ENE", "SE", "E", "ESE", "NE", "SSE", "NNE", "S" }; | |
int direction = b & 0x0F; | |
return winddirections[direction]; | |
} | |
int getHumidity(byte b) | |
{ | |
// range: 1 to 99 %RH | |
int humidity = b & 0x7F; | |
return humidity; | |
} | |
int getRainfallCounter(byte hibyte, byte lobyte) | |
{ | |
// range: 0 to 99.99 in, 0.01 increment rolling counter | |
int raincounter = ((hibyte & 0x7f) << 7) | (lobyte & 0x7F); | |
return raincounter; | |
} | |
float convKphMph(float kph) | |
{ | |
return kph * 0.62137; | |
} | |
float convFC(float f) | |
{ | |
return (f - 32) / 1.8; | |
} | |
float convInMm(float in) | |
{ | |
return in * 25.4; | |
} | |
void My_ISR() | |
{ | |
flag = true; | |
} | |
unsigned long timestamp; | |
unsigned long duration; | |
void doloop() | |
{ | |
if (!flag) | |
return; | |
flag = false; | |
// decode the pulses | |
timestamp = micros(); | |
if (digitalRead(PIN) == HIGH) { | |
// going high, start timing | |
if (timestamp - risets > RESETTIME) { | |
// detect reset condition | |
state = RESET; | |
syncpulses = 0; | |
pulsecnt = 0; | |
} | |
risets = timestamp; | |
return; | |
} | |
// going low | |
duration = timestamp - risets; | |
if (state == RESET || state == INSYNC) { | |
// looking for sync pulses | |
if ((SYNC_LO) < duration && duration < (SYNC_HI)) { | |
// start counting sync pulses | |
state = INSYNC; | |
syncpulses++; | |
if (syncpulses > 3) { | |
// found complete sync header | |
state = SYNCDONE; | |
syncpulses = 0; | |
pulsecnt = 0; | |
#ifdef DEBUG | |
// quick debug to trigger logic analyzer at sync | |
digitalWrite(DEBUGPIN, LOW); | |
#endif | |
} | |
return; | |
} | |
else { | |
// not interested, reset | |
syncpulses = 0; | |
pulsecnt = 0; | |
state = RESET; | |
#ifdef DEBUG | |
digitalWrite(DEBUGPIN, HIGH); //return trigger | |
#endif | |
return; | |
} | |
} | |
else { | |
// SYNCDONE, now look for message | |
// detect if finished here | |
if (pulsecnt > MAXBITS) { | |
state = RESET; | |
pulsecnt = 0; | |
reading = true; | |
return; | |
} | |
// stuff buffer with message | |
byte bytepos = pulsecnt >> 3; | |
byte bitpos = 7 - (pulsecnt % 8); // reverse bitorder | |
if (LONG_LO < duration && duration < LONG_HI) { | |
bitSet(buf[bytepos], bitpos); | |
pulsecnt++; | |
} | |
else if (SHORT_LO < duration && duration < SHORT_HI) { | |
bitClear(buf[bytepos], bitpos); | |
pulsecnt++; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment