├── DebugVersion_12_BestDebugger └── DebugVersion_12_BestDebugger.ino ├── DebugVersion_12_BestRaw └── DebugVersion_12_BestRaw.ino ├── DebugVersion_16_NextStep └── DebugVersion_16_NextStep.ino ├── DebugVersion_21_Final └── DebugVersion_21_Final.ino ├── README.md ├── Transmitter9Temp └── Transmitter9Temp.ino └── github_button.png /DebugVersion_12_BestDebugger/DebugVersion_12_BestDebugger.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Manchester Decoding, reading by delay rather than interrupt 3 | Rob Ward August 2014 4 | This example code is in the public domain. 5 | Use at your own risk, I will take no responsibility for any loss whatsoever its deployment. 6 | Visit https://github.com/robwlakes/ArduinoWeatherOS for the latest version and 7 | documentation. Filename: DebugManchester.ino 8 | 9 | DebugVersion_12_BestDebugger, 1stAugust 2014 10 | 11 | */ 12 | 13 | //Interface Definitions 14 | int RxPin = 8; //The number of signal from the Rx 15 | int ledPin = 13; //The number of the onboard LED pin 16 | 17 | // Variables for Manchester Receiver Logic: 18 | word sDelay = 250; //Small Delay about 1/4 of bit duration try like 230 to 500 19 | word lDelay = 500; //Long Delay about 1/2 of bit duration try like 500 to 1000, 1/4 + 1/2 = 3/4 20 | byte polarity = 1; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start 21 | byte tempBit = 1; //Reflects the required transition polarity 22 | byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 23 | byte discNos = 0; //Counter for the Discards 24 | boolean firstZero = false;//has it processed the first zero yet? This a "sync" bit. 25 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 26 | //variables for Header detection 27 | byte headerBits = 15; //The number of ones expected to make a valid header 28 | byte headerHits = 0; //Counts the number of "1"s to determine a header 29 | //Variables for Byte storage 30 | byte dataByte = 0; //Accumulates the bit information 31 | byte nosBits = 0; //Counts to 8 bits within a dataByte 32 | byte maxBytes = 9; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 33 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 34 | //Variables for multiple packets 35 | byte bank = 0; //Points to the array of 0 to 3 banks of results from up to 4 last data downloads 36 | byte nosRepeats = 0; //Number of times the header/data is fetched at least once or up to 4 times 37 | //Banks for multiple packets if required (at least one will be needed) 38 | byte manchester[12]; //Stores 4 banks of manchester pattern decoded on the fly 39 | 40 | void setup() { 41 | Serial.begin(115200); 42 | pinMode(RxPin, INPUT); 43 | pinMode(ledPin, OUTPUT); 44 | Serial.println("Debug Manchester Version 12 Best Debug- Dumps Raw bit pattern of different length packets"); 45 | Serial.print("Using a delay of 1/4 bitWaveform "); 46 | Serial.print(sDelay,DEC); 47 | Serial.print(" uSecs 1/2 bitWaveform "); 48 | Serial.print(lDelay,DEC); 49 | Serial.println(" uSecs "); 50 | if (polarity){ 51 | Serial.println("Negative Polarity hi->lo=1"); 52 | } 53 | else{ 54 | Serial.println("Positive Polarity lo->hi=1"); 55 | } 56 | Serial.print(headerBits,DEC); 57 | Serial.println(" bits expected for a valid header"); 58 | if (discards){ 59 | Serial.print(discards,DEC); 60 | Serial.println(" leading bits discarded from Packet"); 61 | } 62 | else{ 63 | Serial.println("All bits inside the Packet"); 64 | } 65 | Serial.println("D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 90 22223333 A0 44445555"); 66 | eraseManchester(); 67 | } 68 | 69 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 70 | void loop(){ 71 | tempBit=polarity^1;//toggle flag to track what the next data transition to expect 72 | noErrors=true;//flag that causes a restart of the scan if false 73 | firstZero=false;//flag that reflects whether the synch 0 has been detected 74 | headerHits=0;//counter for the number of 1's found in the header 75 | nosBits=0;//counter for the number of bits shifted in 76 | nosBytes=0;//for for the number of bytes required in the payload 77 | discNos = discards;//how many leading bits are discarded from valid bit stream 78 | eraseManchester(); 79 | maxBytes = 8; //This is the minimum number of bytes expected, this may need to be reduced or increased for other applications 80 | //The following code is a generalised Manchester decoding routine that may be of use to others... 81 | //Please use and adapt it your your own purposes, just acknowledge the author you got it from 82 | while (noErrors && (nosBytes0, or 0->1 96 | //data transition detection must swap, so it loops for the opposite transitions 97 | tempBit = tempBit^1;//toggle the next bit to look for 98 | } 99 | //process a one 100 | if(bitState==1){//could be a header bit or part of data payload 101 | if(!firstZero){//if first zero not been detected, then is must be a header bit 102 | headerHits++; 103 | if (headerHits==headerBits){//valid header accepted, minimum required found 104 | //digitalWrite(ledPin,1);//indicate a header detected 105 | } 106 | } 107 | 108 | else{ 109 | add(bitState);//shift a one into the data payload 110 | } 111 | } 112 | //process a zero, or possibly an error waveform in header 113 | else{ 114 | if(headerHits0){ 136 | discNos--; 137 | } 138 | else{ 139 | dataByte=(dataByte<<1)|bitData; 140 | nosBits++; 141 | if (nosBits==8){ 142 | nosBits=0; 143 | manchester[nosBytes]=dataByte; 144 | nosBytes++; 145 | //Serial.print("?"); 146 | if(nosBytes==maxBytes){ 147 | hexBinDump(); 148 | nosBytes=0; 149 | eraseManchester(); 150 | maxBytes=15; 151 | } 152 | } 153 | //Oregon Scientific specific packet lengths 154 | if(manchester[0]==0x54){ 155 | maxBytes=11;//rain 156 | } 157 | if(manchester[0]==0x58){ 158 | maxBytes=10;//wind 159 | } 160 | if(manchester[0]==0x5F){ 161 | maxBytes=9;//temp 162 | } 163 | if(manchester[0]==0x5C){ 164 | maxBytes=10;//Experimental 165 | } 166 | } 167 | } 168 | 169 | void hexBinDump(){ 170 | Serial.print("D "); 171 | for( int i=0; i < maxBytes; i++){ 172 | byte mask = B10000000; 173 | if (manchester[i]<16){ 174 | Serial.print("0"); 175 | } 176 | Serial.print(manchester[i],HEX); 177 | Serial.print(" "); 178 | for (int k=0; k<8; k++){ 179 | if (manchester[i] & mask){ 180 | Serial.print("1"); 181 | } 182 | else{ 183 | Serial.print("0"); 184 | } 185 | mask = mask >> 1; 186 | } 187 | Serial.print(" "); 188 | } 189 | Serial.println(); 190 | } 191 | 192 | void eraseManchester(){ 193 | for( int i=0; i < maxBytes; i++){ 194 | manchester[i]=0; 195 | } 196 | } 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /DebugVersion_12_BestRaw/DebugVersion_12_BestRaw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Manchester Decoding, reading by delay rather than interrupt 3 | Rob Ward August 2014 4 | This example code is in the public domain. 5 | Use at your own risk, I will take no responsibility for any loss whatsoever its deployment. 6 | Visit https://github.com/robwlakes/ArduinoWeatherOS for the latest version and 7 | documentation. 8 | 9 | This program dumps out binary numbers for any Manchester encoded signal that fits the parameters you have entered 10 | 11 | It should handle: 12 | Checking waveform conforms to Machester encoding 13 | Any length header (currently set to 10) 14 | Find the Synching zero 15 | Discard the zero (and any other bits) if required (currently included in bytes) 16 | Any number of bytes (Currently set to 8) 17 | 18 | What it does not do: 19 | Is any error checking of data 20 | Any processing of data (eg reversal of significance within nibbles) 21 | Calculations to change the data into human redable form. 22 | Compose messages to the outside world to summarise incoming data. 23 | 24 | Filename: DebugVersion_12_BestRaw, 1stAugust 2014 25 | 26 | Most of the parameters can be changed to investigate other Manchester encoded signals. 27 | Readers/users are invited to use the core decoding part of this code to interface with 28 | other products that use Manchester encoding, or if necessary invent your own with a 29 | Manchester encoding transmitter. 30 | 31 | 32 | */ 33 | 34 | //Interface Definitions 35 | int RxPin = 8; //The number of signal from the Rx 36 | int ledPin = 13; //The number of the onboard LED pin 37 | 38 | // Variables for Manchester Receiver Logic: 39 | word sDelay = 250; //Small Delay about 1/4 of bit duration try like 250 to 500 40 | word lDelay = 500; //Long Delay about 1/2 of bit duration try like 500 to 1000, 1/4 + 1/2 = 3/4 41 | byte polarity = 1; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start 42 | byte tempBit = 1; //Reflects the required transition polarity 43 | byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 44 | byte discNos = 0; //Counter for the Discards 45 | boolean firstZero = false;//has it processed the first zero yet? This a "sync" bit. 46 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 47 | //variables for Header detection 48 | byte headerBits = 10; //The number of ones expected to make a valid header 49 | byte headerHits = 0; //Counts the number of "1"s to determine a header 50 | //Variables for Byte storage 51 | byte dataByte = 0; //Accumulates the bit information 52 | byte nosBits = 0; //Counts to 8 bits within a dataByte 53 | byte maxBytes = 8; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 54 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 55 | //Variables for multiple packets 56 | byte bank = 0; //Points to the array of 0 to 3 banks of results from up to 4 last data downloads 57 | byte nosRepeats = 0; //Number of times the header/data is fetched at least once or up to 4 times 58 | //Banks for multiple packets if required (at least one will be needed) 59 | byte manchester[12]; //Stores 4 banks of manchester pattern decoded on the fly 60 | 61 | void setup() { 62 | Serial.begin(115200); 63 | pinMode(RxPin, INPUT); 64 | pinMode(ledPin, OUTPUT); 65 | Serial.println("Debug Manchester Version 12 - Raw bits in an 8 byte packet"); 66 | Serial.print("Using a delay of 1/4 bitWaveform "); 67 | Serial.print(sDelay,DEC); 68 | Serial.print(" uSecs 1/2 bitWaveform "); 69 | Serial.print(lDelay,DEC); 70 | Serial.println(" uSecs "); 71 | if (polarity){ 72 | Serial.println("Negative Polarity hi->lo=1"); 73 | } 74 | else{ 75 | Serial.println("Positive Polarity lo->hi=1"); 76 | } 77 | Serial.print(headerBits,DEC); 78 | Serial.println(" bits expected for a valid header"); 79 | if (discards){ 80 | Serial.print(discards,DEC); 81 | Serial.println(" leading bits discarded from Packet"); 82 | } 83 | else{ 84 | Serial.println("All bits inside the Packet"); 85 | } 86 | Serial.println("D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF"); 87 | eraseManchester(); 88 | } 89 | 90 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 91 | void loop(){ 92 | tempBit=polarity^1;//toggle flag to track what the next data transition to expect 93 | noErrors=true;//flag that causes a restart of the scan if false 94 | firstZero=false;//flag that reflects whether the synch 0 has been detected 95 | headerHits=0;//counter for the number of 1's found in the header 96 | nosBits=0;//counter for the number of bits shifted in 97 | nosBytes=0;//for for the number of bytes required in the payload 98 | discNos = discards;//how many leading bits are discarded from valid bit stream 99 | eraseManchester(); 100 | maxBytes = 8; //This is the minimum number of bytes expected, this may need to be reduced or increased for other applications 101 | //The following code is a generalised Manchester decoding routine that may be of use to others... 102 | //Please use and adapt it your your own purposes, just acknowledge the author you got it from 103 | while (noErrors && (nosBytes0, or 0->1 117 | //data transition detection must swap, so it loops for the opposite transitions 118 | tempBit = tempBit^1;//toggle the next bit to look for 119 | } 120 | //process a one 121 | if(bitState==1){//could be a header bit or part of data payload 122 | if(!firstZero){//if first zero not been detected, then is must be a header bit 123 | headerHits++; 124 | if (headerHits==headerBits){//valid header accepted, minimum required found 125 | //digitalWrite(ledPin,1);//indicate a header detected 126 | } 127 | } 128 | 129 | else{ 130 | add(bitState);//shift a one into the data payload 131 | } 132 | } 133 | //process a zero, or possibly an error waveform in header 134 | else{ 135 | if(headerHits0){ //some manchester encodings strip off the first zero or more 157 | discNos--;//discards some bits, not always used 158 | } 159 | else{ 160 | dataByte=(dataByte<<1)|bitData;//OR in the data bit 161 | nosBits++;//count 'em!! 162 | if (nosBits==8){ 163 | nosBits=0; 164 | manchester[nosBytes]=dataByte; 165 | nosBytes++; 166 | if(nosBytes==maxBytes){ 167 | hexBinDump(); 168 | nosBytes=0; 169 | eraseManchester(); 170 | } 171 | } 172 | } 173 | } 174 | 175 | void hexBinDump(){ 176 | Serial.print("D "); 177 | for( int i=0; i < maxBytes; i++){ 178 | byte mask = B10000000; 179 | if (manchester[i]<16){ 180 | Serial.print("0"); 181 | } 182 | Serial.print(manchester[i],HEX); 183 | Serial.print(" "); 184 | for (int k=0; k<8; k++){ 185 | if (manchester[i] & mask){ 186 | Serial.print("1"); 187 | } 188 | else{ 189 | Serial.print("0"); 190 | } 191 | mask = mask >> 1; 192 | } 193 | Serial.print(" "); 194 | } 195 | Serial.println(); 196 | } 197 | 198 | void eraseManchester(){ 199 | for( int i=0; i < maxBytes; i++){ 200 | manchester[i]=0; 201 | } 202 | } 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | -------------------------------------------------------------------------------- /DebugVersion_16_NextStep/DebugVersion_16_NextStep.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Manchester Decoding, reading by delay rather than interrupt 3 | Rob Ward August 2014 4 | This example code is in the public domain. 5 | Use at your own risk, I will take no responsibility for any loss whatsoever its deployment. 6 | Visit https://github.com/robwlakes/ArduinoWeatherOS for the latest version and 7 | documentation. Filename: DebugManchester.ino 8 | 9 | DebugVersion_16, 1stAugust 2014 10 | 11 | */ 12 | 13 | //Interface Definitions 14 | int RxPin = 8; //The number of signal from the Rx 15 | int ledPin = 13; //The number of the onboard LED pin 16 | 17 | // Variables for Manchester Receiver Logic: 18 | word sDelay = 250; //Small Delay about 1/4 of bit duration try like 250 to 500 19 | word lDelay = 500; //Long Delay about 1/2 of bit duration try like 500 to 1000, 1/4 + 1/2 = 3/4 20 | byte polarity = 1; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start 21 | byte tempBit = 1; //Reflects the required transition polarity 22 | byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 23 | byte discNos = 0; //Counter for the Discards 24 | boolean firstZero = false;//has it processed the first zero yet? This a "sync" bit. 25 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 26 | //variables for Header detection 27 | byte headerBits = 15; //The number of ones expected to make a valid header 28 | byte headerHits = 0; //Counts the number of "1"s to determine a header 29 | //Variables for Byte storage 30 | byte dataByte = 0; //Accumulates the bit information 31 | byte nosBits = 0; //Counts to 8 bits within a dataByte 32 | byte maxBytes = 9; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 33 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 34 | //Array for packet data, if required (at least one will be needed) 35 | byte manchester[12]; //Stores 4 banks of manchester pattern decoded on the fly 36 | //Oregon bit pattern, causes a 4 bit rotation to the right, otherwise in Oregon data the LSB precedes MSB 37 | byte oregon[] = { 38 | 16,32,64,128,1,2,4,8 39 | }; 40 | byte csIndex = 0; //how many nibbles needed for checksum (2 less than nos of nibbles, as last two is CS 41 | //Weather Variables 42 | byte quadrant = 0; //used to look up 16 positions around the compass rose 43 | double avWindspeed = 0.0; 44 | double gustWindspeed = 0.0; //now used for general anemometer readings rather than avWinspeed 45 | float rainTotal = 0.0; 46 | float rainRate = 0.0; 47 | double temperature = 0.0; 48 | int humidity = 0; 49 | const char windDir[16][4] = { 50 | "N ", "NNE", "NE ", "ENE", "E ", "ESE", "SE ", "SSE", "S ", "SSW", "SW ", "WSW", "W ", "WNW", "NW ", "NNW"}; 51 | 52 | void setup() { 53 | Serial.begin(115200); 54 | pinMode(RxPin, INPUT); 55 | pinMode(ledPin, OUTPUT); 56 | Serial.println("Debug Manchester Version 11 - Printout sensors in Human readable format."); 57 | Serial.print("Using a delay of 1/4 bitWaveform "); 58 | Serial.print(sDelay,DEC); 59 | Serial.print(" uSecs 1/2 bitWaveform "); 60 | Serial.print(lDelay,DEC); 61 | Serial.println(" uSecs "); 62 | if (polarity){ 63 | Serial.println("Negative Polarity hi->lo=1"); 64 | } 65 | else{ 66 | Serial.println("Positive Polarity lo->hi=1"); 67 | } 68 | Serial.print(headerBits,DEC); 69 | Serial.println(" bits expected for a valid header"); 70 | if (discards){ 71 | Serial.print(discards,DEC); 72 | Serial.println(" leading bits discarded from Packet"); 73 | } 74 | else{ 75 | Serial.println("All bits inside the Packet"); 76 | } 77 | Serial.println("D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 09 22223333 0A 44445555"); 78 | } 79 | 80 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 81 | void loop(){ 82 | tempBit=polarity^1;//toggle flag to track what the next data transition to expect 83 | noErrors=true;//flag that causes a restart of the scan if false 84 | firstZero=false;//flag that reflects whether the synch 0 has been detected 85 | headerHits=0;//counter for the number of 1's found in the header 86 | nosBits=0;//counter for the number of bits shifted in 87 | nosBytes=0;//counter for number of bytes stored in the payload. 88 | discNos = discards;//how many leading bits are discarded from valid bit stream 89 | maxBytes = 10; //This is the maximum number of bytes expected, this may need to be reduced or increased for other applications 90 | manchester[0]=0; 91 | //The following code is a generalised Manchester decoding routine that may be of use to others... 92 | //Please use and adapt it your your own purposes, just acknowledge the author you got it from 93 | while (noErrors && (nosBytes0, or 0->1 107 | //data transition detection must swap, so it loops for the opposite transitions 108 | tempBit = tempBit^1;//toggle the next bit to look for 109 | } 110 | //process a one 111 | if(bitState==1){//could be a header bit or part of data payload 112 | if(!firstZero){//if first zero not been detected, then is must be a header bit 113 | headerHits++; 114 | if (headerHits==headerBits){//valid header accepted, minimum required found 115 | //digitalWrite(ledPin,1);//indicate a header detected 116 | } 117 | } 118 | 119 | else{ 120 | add(bitState);//shift a one into the data payload 121 | } 122 | } 123 | //process a zero, or possibly an error waveform in header 124 | else{ 125 | if(headerHits0){ 144 | discNos--; 145 | } 146 | else{ 147 | if (bitData){ 148 | manchester[nosBytes]|=oregon[nosBits]; //Set the bit 149 | } 150 | //Oregon Scientific specific packet lengths 151 | nosBits++; 152 | if (nosBits==8){ 153 | if(manchester[0]==0xA2){ 154 | maxBytes=11;//rain 155 | csIndex =19; 156 | } 157 | if(manchester[0]==0xA1){ 158 | maxBytes=10;//wind 159 | csIndex=18; 160 | } 161 | if(manchester[0]==0xAF){ 162 | maxBytes=9;//temp 163 | csIndex=16; 164 | } 165 | if(manchester[0]==0xA3){ 166 | maxBytes=10;//experimental, 9 data bytes and 1 byte CS 167 | csIndex =18;//CS byte begins 18 nibble 168 | digitalWrite(ledPin,1); 169 | } 170 | nosBits=0; 171 | nosBytes++; 172 | manchester[nosBytes]=0; //next byte to 0 to accumulate data 173 | //Serial.print("?"); 174 | } 175 | if(nosBytes==maxBytes){ 176 | //hexBinDump(); 177 | if (ValidCS(csIndex)){ 178 | analyseData(); 179 | } 180 | noErrors=false;//make it begin from the start 181 | } 182 | } 183 | } 184 | 185 | void hexBinDump(){ 186 | Serial.print("D "); 187 | for( int i=0; i < maxBytes; i++){ 188 | byte mask = B10000000; 189 | if (manchester[i]<16){ 190 | Serial.print("0"); 191 | } 192 | Serial.print(manchester[i],HEX); 193 | Serial.print(" "); 194 | for (int k=0; k<8; k++){ 195 | if (manchester[i] & mask){ 196 | Serial.print("1"); 197 | } 198 | else{ 199 | Serial.print("0"); 200 | } 201 | mask = mask >> 1; 202 | } 203 | Serial.print(" "); 204 | } 205 | Serial.println(); 206 | } 207 | 208 | //Support Routines for Nybbles and CheckSum 209 | 210 | // http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com 211 | // Directly lifted, then modified from Brian's work, as nybbles bits are now in convenient order, ie MSNybble + LSNybble 212 | // CS = the sum of nybbles, 1 to (CSpos-1), compared to CSpos byte (LSNybble) and CSpos+1 byte (MSNybble); 213 | // This sums the nybbles in the packet and creates a 1 byte number, and then compared to the two nybbles beginning at CSpos 214 | // Note that Temp and anemometer uses only 10 bytes but rainfall use 11 bytes per packet. (Rainfall CS spans a byte boundary) 215 | bool ValidCS(int CSPos){ 216 | bool ok = false; 217 | byte cs = 0; 218 | for (int x=1; x "); 346 | Serial.println(windDir[quadrant]); 347 | } 348 | 349 | // THGN800 Temperature and Humidity Sensor 350 | // 0 1 2 3 4 5 6 7 8 9 Bytes 351 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 nybbles 352 | // 01011111 00010100 01000001 01000000 10001100 10000000 00001100 10100000 10110100 01111001 Bits 353 | // -------- -------- bbbbcccc RRRRRRRR 88889999 AAAABBBB SSSSDDDD EEEE---- CCCCcccc -------- Explanation 354 | // byte(0)_byte(1) = Sensor ID????? 355 | // bbbb = Battery indicator??? (7), My investigations on the anemometer would disagree here. After exhaustive low battery tests these bits did not change 356 | // RRRRRRRR = Rolling code byte 357 | // nybble(5) is channel selector c (Switch on the sensor to allocate it a number) 358 | // BBBBAAAA.99998888 Temperature in BCD 359 | // SSSS sign for negative (- is !=0) 360 | // EEEEDDDD Humidity in BCD 361 | // ccccCCCC 1 byte checksum cf. sum of nybbles 362 | // Packet length is 18 nybbles and indeterminate after that 363 | // H 00 01 02 03 04 05 06 07 08 09 Byte Sequence 364 | // D AF 82 41 CB 89 42 00 48 85 55 Real example 365 | // Temperature 24.9799995422 degC Humidity 40.0000000000 % rel 366 | void thermom(){ 367 | temperature = (double)((nyb(11)*100)+(nyb(10)*10)+nyb(9))/10; //accuracy to 0.01 degree seems unlikely 368 | if(nyb(12)==1){// Trigger a negative temperature 369 | temperature = -1.0*temperature; 370 | } 371 | humidity = (nyb(14)*10)+nyb(13); 372 | } 373 | void dumpThermom(){ 374 | Serial.print("Temperature "); 375 | Serial.print(temperature); 376 | Serial.print(" degC, Humidity "); 377 | Serial.print(humidity); 378 | Serial.println("% Rel"); 379 | } 380 | 381 | void totExp(){ 382 | Serial.print("Total Experiment Delay="); 383 | int tot = manchester[nyb(3)*256+nyb(4)]; 384 | Serial.print(tot,DEC); 385 | Serial.print(", Incrementing "); 386 | Serial.print(manchester[5],DEC); 387 | Serial.print(", Decrementing "); 388 | Serial.println(manchester[6],DEC); 389 | } 390 | 391 | void eraseManchester(){ 392 | for( int i=0; i < maxBytes; i++){ 393 | manchester[i]=0; 394 | } 395 | } 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | -------------------------------------------------------------------------------- /DebugVersion_21_Final/DebugVersion_21_Final.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Manchester Decoding, (reading by delay rather than interrupt) 3 | Rob Ward 2014 4 | This example code is in the public domain. 5 | Use at your own risk, I will take no responsibility for any loss whatsoever its deployment. 6 | Visit https://github.com/robwlakes/ArduinoWeatherOS for the latest version and 7 | documentation. Filename: ZZZZZZZ.ino 8 | 9 | Main_Version_20, 5 November 2014 10 | 11 | Pressure and Humidity added in 12 | 13 | The aim of this version is use the new Manchester decoding routine and incorporate detection 14 | of a Oregon compatible sensor built to cover novel environmental sensors. 15 | For example we could have remote temperature sampling, bat detectors, lightning detectors, 16 | solar power detectors, and earthquake detectors, all reporting back to the WWW server. 17 | The advantage is that by using the Oregon Scientific Protocol a person can incorporate 18 | their excellent weather sensors into the overall design and largely keep the Arduino Rx software 19 | design relatively simple, ie also use their Manchester implementation for our own sensors. 20 | 21 | It may seem this program is encouraging people to build their own replacements for the 22 | Oregon Scientific WRMR86 sensors, however it is quite the reverse. The program is designed 23 | encourage people to purchase the Oregon WRMR86 gear to get the basic wind, temperature, rain 24 | and humidity sensors and then extend the range with original designs as suggested above. 25 | If the Oregon sensors are not used, then it does not make sense to use their protocol, 26 | and any RF other protocol could be as good, or maybe even better in some circumstances. 27 | 28 | Before I discovered the Oregon products, and designed a receiver for it, I was seriously 29 | considering building my own basic weather sensors. Now I am glad I did not try as the 30 | quality and price of the Oregon sensors are a very good economic and reliable alternative. 31 | 32 | Have we made the Oregon base station with the LCD redundant? Well no, not at all. I have 33 | it in the kitchen and it used by members of the family from day to day and does not take 34 | anything extra to have it performing a useful monitoring job as well. However if you want to 35 | build a system that uses weather data, and combines that with other sensors, and then performs 36 | other tasks such as switching pumps, or moving blinds then adding an Arduino based 433MHz Rx 37 | makes an incredibly versatile platform to work with. 38 | 39 | Have we made the USB to PC models redundant? Again no, unless you want to take a long time 40 | writing your own logging and graphing routines. This program does not even begin to replace 41 | that level of functionality. 42 | 43 | Have we made the Anywhere OS Internet connection system redundant? Finally, again no! 44 | This simple Arduino system does not report live to the internet and it would also take a lot 45 | of effort to even go close to include the features of the OS internet connection system. 46 | 47 | I would encourage anyone looking for a weather sensor based project to consider the Oregon 48 | Scientific products as they produce a wide range of economically priced but solidly built 49 | weather sensors. The mechanical engineering is very good and hard to replicate by the "Geek 50 | in the Street". However the scope to extend the usefulness of this data in our designs, by 51 | combining it with our own sensors (eg soil moisture, or dew point sensors) is truly amazing. 52 | 53 | Have a look here: http://www.oregonscientificstore.com/ 54 | 55 | So get some OS WRMR86 gear, an Arduino and get into it :-) 56 | 57 | NB: This program only works with Protocol 3.0 sensors. 58 | 59 | Reference Material: 60 | Thanks to these authors, they all helped in some way, especially the last one Brian!!!! 61 | http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf (1) 62 | http://jeelabs.net/projects/cafe/wiki/Decoding_the_Oregon_Scientific_V2_protocol (2) 63 | https://github.com/phardy/WeatherStation (3) 64 | http://lucsmall.com/2012/04/27/weather-station-hacking-part-1/ (4) 65 | http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ (5) 66 | http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ (6) 67 | http://www.mattlary.com/2012/06/23/weather-station-project/(7) 68 | https://github.com/lucsmall/WH2-Weather-Sensor-Library-for-Arduino (8) 69 | http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com 70 | 71 | Most of all thanks to Oregon Scientific, who have produced an affordable, high quality product. 72 | I can now have my LCD Home Base in the kitchen to enjoy, with the Arduino in the garage also 73 | capturing data for WWW Weather pages. WRMR86 Lovely!!!! http://www.oregonscientific.com 74 | Very Highly recommended equipment. Rob Ward 75 | */ 76 | 77 | #include 78 | #include "DHT.h" 79 | #include "Adafruit_BMP085.h" 80 | 81 | #define DHTTYPE DHT22 // DHT 22 (AM2302) 82 | #define DHTPIN 2 // what pin we're connected to 83 | 84 | Adafruit_BMP085 bmp; 85 | DHT dht(DHTPIN, DHTTYPE); 86 | 87 | 88 | //Interface Definitions 89 | int RxPin = 8; //The number of signal from the Rx 90 | int ledPin = 13; //The number of the onboard LED pin 91 | 92 | // Variables for Manchester Receiver Logic: 93 | word sDelay = 250; //Small Delay about 1/4 of bit duration try like 250 to 500 94 | word lDelay = 500; //Long Delay about 1/2 of bit duration try like 500 to 1000, 1/4 + 1/2 = 3/4 95 | byte polarity = 1; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start 96 | byte tempBit = 1; //Reflects the required transition polarity 97 | byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 98 | byte discNos = 0; //Counter for the Discards 99 | boolean firstZero = false; //has it processed the first zero yet? This a "sync" bit. 100 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 101 | //variables for Header detection 102 | byte headerBits = 15; //The number of ones expected to make a valid header 103 | byte headerHits = 0; //Counts the number of "1"s to determine a header 104 | //Variables for Byte storage 105 | byte dataByte = 0; //Accumulates the bit information 106 | byte nosBits = 0; //Counts to 8 bits within a dataByte 107 | byte maxBytes = 9; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 108 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 109 | //Bank array for packet (at least one will be needed) 110 | byte manchester[12]; //Stores manchester pattern decoded on the fly 111 | //Oregon bit pattern, causes nibble reflection, left to right, ABCDabcd becomes DCBAdcba 112 | byte oregon[] = { 113 | 16,32,64,128,1,2,4,8}; 114 | byte csIndex = 0; //counter for nibbles needed for checksum 115 | //Weather Variables 116 | byte quadrant = 0; //used to look up 16 positions around the compass rose 117 | double avWindspeed = 0.0; 118 | double gustWindspeed = 0.0; //now used for general anemometer readings rather than avWindspeed 119 | float rainTotal = 0.0; 120 | float rainRate = 0.0; 121 | double temperature = 0.0; 122 | int humidity = 0; 123 | byte byte0 = 0; 124 | byte byte1 = 0; 125 | byte byte2 = 0; 126 | double intTemp; 127 | double intHumi; 128 | double intPres; 129 | const char windDir[16][4] = { 130 | "N ", "NNE", "NE ", "ENE", "E ", "ESE", "SE ", "SSE", "S ", "SSW", "SW ", "WSW", "W ", "WNW", "NW ", "NNW"}; 131 | 132 | byte scan =0; //==7 means that all three sensors has been detected, so it reports all three with meaningful figures first up 133 | byte seconds =0; //Counter to trigger the 60 seconds worth of data. 134 | 135 | void setup() { 136 | Serial.begin(115200); 137 | pinMode(RxPin, INPUT); 138 | pinMode(ledPin, OUTPUT); 139 | 140 | /* Enable these if attempting to debug the program or the circuit 141 | Serial.println("Debug Manchester Version 18"); 142 | Serial.print("Using a delay of 1/4 bitWaveform "); 143 | Serial.print(sDelay,DEC); 144 | Serial.print(" uSecs 1/2 bitWaveform "); 145 | Serial.print(lDelay,DEC); 146 | Serial.println(" uSecs "); 147 | if (polarity){ 148 | Serial.println("Negative Polarity hi->lo=1"); 149 | } 150 | else{ 151 | Serial.println("Positive Polarity lo->hi=1"); 152 | } 153 | Serial.print(headerBits,DEC); 154 | Serial.println(" bits expected for a valid header"); 155 | if (discards){ 156 | Serial.print(discards,DEC); 157 | Serial.println(" leading bits discarded from Packet"); 158 | } 159 | else{ 160 | Serial.println("All bits inside the Packet"); 161 | } 162 | Serial.println("D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 09 22223333 0A 44445555"); 163 | */ 164 | 165 | //Tutorial on using the BMP05 Press/Temp transducer https://www.sparkfun.com/tutorials/253 166 | bmp.begin(); //start the barometer and temp packages 167 | 168 | // Initialize Timer1 for a 1 second interrupt 169 | // Thanks to http://www.engblaze.com/ for this section, see their Interrupt Tutorial 170 | cli(); // disable global interrupts 171 | TCCR1A = 0; // set entire TCCR1A register to 0 172 | TCCR1B = 0; // same for TCCR1B 173 | // set compare match register to desired timer count: 174 | OCR1A = 15624; 175 | // turn on CTC mode: 176 | TCCR1B |= (1 << WGM12); 177 | // Set CS10 and CS12 bits for 1024 prescaler: 178 | TCCR1B |= (1 << CS10); 179 | TCCR1B |= (1 << CS12); 180 | // enable timer compare interrupt: 181 | TIMSK1 |= (1 << OCIE1A); 182 | // enable global interrupts: 183 | sei(); 184 | } 185 | 186 | //Routine Driven by Interrupt, trap 1 second interrupts, and output every minute 187 | ISR(TIMER1_COMPA_vect){ 188 | seconds++; 189 | if (seconds == 60){//make 60 for each output 190 | seconds = 0; 191 | usbData();//comment this out to temporarily disable data every minute for debug 192 | } 193 | } //end of interrupt routine 194 | 195 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 196 | void loop(){ 197 | tempBit=polarity^1; 198 | noErrors=true; 199 | firstZero=false; 200 | headerHits=0; 201 | nosBits=0; 202 | maxBytes=15;//too big for any known OS signal 203 | nosBytes=0; 204 | discNos = discards; 205 | manchester[0]=0; 206 | digitalWrite(ledPin,0); 207 | while (noErrors && (nosBytes=headerBits)){ 237 | firstZero=true; 238 | //digitalWrite(ledPin,1); 239 | 240 | } 241 | add(bitState); 242 | } 243 | } 244 | } 245 | } 246 | digitalWrite(ledPin,0); 247 | } 248 | 249 | void add(byte bitData){ 250 | if (discNos>0){ 251 | discNos--;//discard bits before real data 252 | } 253 | else{ 254 | if (bitData){ 255 | //if it is a '1' OR it in, others leave at a '0' 256 | manchester[nosBytes]|=oregon[nosBits]; 257 | } 258 | //Oregon Scientific sensors have specific packet lengths 259 | //Maximum bytes for each sensor must set once the sensor has been detected. 260 | if(manchester[0]==0xA2){ 261 | maxBytes=11;//rain 262 | csIndex =19; 263 | } 264 | if(manchester[0]==0xA1){ 265 | maxBytes=10;//wind 266 | csIndex=18; 267 | } 268 | if(manchester[0]==0xAF){ 269 | maxBytes=9;//temp 270 | csIndex=16; 271 | } 272 | if(manchester[0]==0xA3){ 273 | maxBytes=10;//experimental, 9 data bytes and 1 byte CS 274 | csIndex =18;//CS byte begins 18 nibble 275 | } 276 | nosBits++; 277 | //Pack the bits into 8bit bytes 278 | if (nosBits==8){ 279 | nosBits=0; 280 | nosBytes++; 281 | manchester[nosBytes]=0; //next byte to 0 to accumulate data 282 | } 283 | //Check the bytes for a valid packet once maxBytes received 284 | if(nosBytes==maxBytes){ 285 | //hexBinDump(); 286 | digitalWrite(ledPin,1); 287 | //Check Checksum first 288 | if (ValidCS(csIndex)){ 289 | //Process the byte array into Human readable numbers 290 | analyseData(); 291 | } 292 | noErrors=false;//make it begin again from the start 293 | //Throw in our own Arduino sensors as well (normally in the OS Base Station). 294 | intHumi=(double)dht.readHumidity(); //DHT22 readings %Humidity 295 | intTemp=(double)bmp.readTemperature(); //internal temperature 296 | intPres=(double)bmp.readPressure()/100.0; //Pa reduced to mBar 297 | } 298 | } 299 | } 300 | 301 | //Useful to invoke to debug the byte Array 302 | void hexBinDump(){ 303 | //Serial.println("T A3 10100011 07 00000111 02 00000010 AA 10101010 F0 11110000 06 00000110 FF 11111111 07 00000111 33 00110011 60 01100000"); 304 | Serial.print("D "); 305 | for( int i=0; i < maxBytes; i++){ 306 | byte mask = B10000000; 307 | if (manchester[i]<16){ 308 | Serial.print("0"); 309 | } 310 | Serial.print(manchester[i],HEX); 311 | Serial.print(" "); 312 | for (int k=0; k<8; k++){ 313 | if (manchester[i] & mask){ 314 | Serial.print("1"); 315 | } 316 | else{ 317 | Serial.print("0"); 318 | } 319 | mask = mask >> 1; 320 | } 321 | Serial.print(" "); 322 | } 323 | Serial.println(); 324 | } 325 | 326 | //Support Routines for Nybbles and CheckSum 327 | 328 | // http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com 329 | // Directly lifted, then modified from Brian's work. Now nybble's bits are pre-processed into standard order, ie MSNybble + LSNybble 330 | // CS = the sum of nybbles, 1 to (CSpos-1), then compared to CSpos nybble (LSNybble) and CSpos+1 nybble (MSNybble); 331 | // This sums the nybbles in the packet and creates a 1 byte number, and this is compared to the two nybbles beginning at CSpos 332 | // Note that Temp 9 bytes and anemometer 10 bytes, but rainfall uses 11 bytes per packet. (NB Rainfall CS spans a byte boundary) 333 | bool ValidCS(int CSPos){ 334 | boolean ok = false; 335 | byte cs = 0; 336 | for (int x=1; x> 4); 369 | } 370 | else{ 371 | b = (byte)((byte)(b) & (byte)(0xf)); 372 | } 373 | return b; 374 | } 375 | 376 | //enable debug dumps if formatted data required 377 | void analyseData(){ 378 | if (manchester[0]==0xaf){ //detected the Thermometer and Hygrometer (53seconds) 379 | scan = scan | 1; 380 | thermom(); 381 | //dumpThermom(); 382 | } 383 | if (manchester[0]==0xa1){ //detected the Anemometer and Wind Direction (14seconds) 384 | scan = scan | 2; 385 | anemom(); 386 | //dumpAnemom(); 387 | } 388 | if (manchester[0]==0xa2){ //detected the Rain Gauge (47seconds) 389 | scan = scan | 4; 390 | rain(); 391 | //dumpRain(); 392 | } 393 | if (manchester[0]==0xa3){ //detected an original Sensor designed by Us!!! 394 | //scan = scan | 8; 395 | //totally experimental 396 | totExp(); 397 | //dumpExp(); 398 | //This code is not used by the three sensors in the WMR86 product. It may clash with other 399 | //other OS Sensors and a different value chosen in the Tx and then Rx 400 | } 401 | 402 | } 403 | 404 | //Calculation Routines 405 | 406 | /* PCR800 Rain Gauge Sample Data: 407 | // 0 1 2 3 4 5 6 7 8 9 A 408 | // A2 91 40 50 93 39 33 31 10 08 02 409 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 410 | // 10100010 10010001 01000000 01010000 10010011 00111001 00110011 00110001 00010000 00001000 00000010 411 | // -------- ------- bbbb--- RRRRRRRR 88889999 AAAABBBB CCCCDDDD EEEEFFFF 00001111 2222CCCC cccc 412 | 413 | // byte(0)_byte(1) = Sensor ID????? 414 | // bbbb = Battery indicator??? (7) My investigations on the anemometer would disagree here. 415 | // After exhaustive low battery tests these bbbb bits did not change 416 | // RRRRRRRR = Rolling Code Byte 417 | // 222211110000.FFFFEEEEDDDD = Total Rain Fall (inches) 418 | // CCCCBBBB.AAAA99998888 = Current Rain Rate (inches per hour) 419 | // ccccCCCC = 1 byte Checksum cf. sum of nybbles 420 | // Message length is 20 nybbles so working in inches 421 | Three tips caused the following 422 | 1 tip=0.04 inches or 1.1mm (observed off the LCD) 423 | My experiment 424 | Personally I don't like this value. I think mult by 30 (I tried a few, 42, then 25) and divide by 1000 IS closer to return mm directly. 425 | 0.127mm per tip??? It looks close to the above. Again can't vouch 100.000% for this, any rigorous assistance would be appreciated. 426 | */ 427 | void rain(){ 428 | rainTotal = float(((nyb(18)*100000)+(nyb(17)*10000)+(nyb(16)*1000)+(nyb(15)*100)+(nyb(14)*10)+nyb(13))*30/1000.0); 429 | //Serial.println((nyb(18)*100000)+(nyb(17)*10000)+(nyb(16)*1000)+(nyb(15)*100)+(nyb(14)*10)+nyb(13),DEC); 430 | rainRate = float(((nyb(8)*10000)+(nyb(9)*1000)+(nyb(10)*100)+(nyb(11)*10)+nyb(12))*30/1000.0); 431 | //Serial.println((nyb(8)*10000)+(nyb(9)*1000)+(nyb(10)*100)+(nyb(11)*10)+nyb(12),DEC); 432 | } 433 | void dumpRain(){ 434 | Serial.print("Total Rain "); 435 | Serial.print(rainTotal); 436 | Serial.print(" mm, "); 437 | Serial.print("Rain Rate "); 438 | Serial.print(rainRate); 439 | Serial.println(" mm/hr "); 440 | } 441 | 442 | // WGR800 Wind speed sensor 443 | // Sample Data: 444 | // 0 1 2 3 4 5 6 7 8 9 445 | // A1 98 40 8E 00 0C 70 04 00 34 446 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 447 | // 10100001 10011000 01000000 10001110 00000000 00001100 01110000 00000100 00000000 00110100 448 | // -------- -------- bbbb---- NRRRRRRR xxxx9999 xxxxxxxx CCCCDDDD xxxxFFFF 0000---- CCCCcccc 449 | // Av Speed 0.4000000000m/s Gusts 0.7000000000m/s Direction: N 450 | 451 | // byte(0)_byte(1) = Sensor ID????? 452 | // bbbb = Battery indicator??? (7) My investigations would disagree here. After exhaustive low battery tests these bits did not change 453 | // NRRRRRRR = Rolling Code Byte, the N bit is set to 1 for 64 cycles to indicate it is reset or new to the Rx box 454 | // 9999 = Direction 455 | // DDDD.CCCC = Gust Speed (m per sec) 456 | // 0000.FFFF = Avg Speed(m per sec) 457 | // multiply by 3600/1000 for km/hr 458 | // ccccCCCC = 1 byte checksum cf. sum of nybbles 459 | // packet length is 20 nybbles 460 | 461 | void anemom(){ 462 | //D A1 98 40 8E 08 0C 60 04 00 A4 463 | avWindspeed = ((nyb(16)*10)+ nyb(15))*3.6/10; 464 | double gust =((nyb(13)*10)+nyb(12))*3.6/10; 465 | // after every minute, reset gustWindspeed to avWindspeed and then take the highest gust after that (approx4 readings a minute) 466 | if (gust>gustWindspeed){ 467 | gustWindspeed = gust; 468 | } 469 | quadrant = nyb(9) & 0xF; 470 | } 471 | void dumpAnemom(){ 472 | Serial.print("Av Speed "); 473 | Serial.print(avWindspeed); 474 | Serial.print(" km/hr, Gusts "); 475 | Serial.print(gustWindspeed); 476 | Serial.print(" km/hr, Direction: "); 477 | Serial.print(quadrant); 478 | Serial.print(" -> "); 479 | Serial.println(windDir[quadrant]); 480 | } 481 | 482 | // THGN800 Temperature and Humidity Sensor 483 | // 0 1 2 3 4 5 6 7 8 9 Bytes 484 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 nybbles 485 | // 01011111 00010100 01000001 01000000 10001100 10000000 00001100 10100000 10110100 01111001 Bits 486 | // -------- -------- bbbbcccc RRRRRRRR 88889999 AAAABBBB SSSSDDDD EEEE---- CCCCcccc -------- Explanation 487 | // byte(0)_byte(1) = Sensor ID????? 488 | // bbbb = Battery indicator??? (7), My investigations on the anemometer would disagree here. After exhaustive low battery tests these bits did not change 489 | // RRRRRRRR = Rolling code byte 490 | // nybble(5) is channel selector c (Switch on the sensor to allocate it a number) 491 | // BBBBAAAA.99998888 Temperature in BCD 492 | // SSSS sign for negative (- is !=0) 493 | // EEEEDDDD Humidity in BCD 494 | // ccccCCCC 1 byte checksum cf. sum of nybbles 495 | // Packet length is 18 nybbles and indeterminate after that 496 | // H 00 01 02 03 04 05 06 07 08 09 Byte Sequence 497 | // D AF 82 41 CB 89 42 00 48 85 55 Real example 498 | // Temperature 24.9799995422 degC Humidity 40.0000000000 % rel 499 | void thermom(){ 500 | temperature = (double)((nyb(11)*100)+(nyb(10)*10)+nyb(9))/10; //accuracy to 0.01 degree seems unlikely 501 | if(nyb(12)==1){// Trigger a negative temperature 502 | temperature = -1.0*temperature; 503 | } 504 | humidity = (nyb(14)*10)+nyb(13); 505 | } 506 | void dumpThermom(){ 507 | Serial.print("Temperature "); 508 | Serial.print(temperature); 509 | Serial.print(" degC, Humidity "); 510 | Serial.print(humidity); 511 | Serial.println("% Rel"); 512 | } 513 | //The novel added extra sensors, extracting and applying any necessary conversions 514 | void totExp(){ 515 | byte0 = manchester[2]; //*190/255; //EG To apply a Solar Cell to the input 516 | byte1 = manchester[3]; 517 | byte2 = manchester[4]; 518 | } 519 | 520 | void dumpExp(){ 521 | Serial.print("Solar Percentage ="); 522 | Serial.println(byte0,DEC); 523 | Serial.print("Lightning Strikes ="); 524 | Serial.println(byte1,DEC); 525 | Serial.print("Earthquakes ="); 526 | Serial.println(byte2,DEC); 527 | Serial.print("Incrementing "); //When testing the Tx, these were made to change every transmission to check reception 528 | Serial.print(manchester[5],DEC); 529 | Serial.print(", Decrementing "); 530 | Serial.println(manchester[6],DEC); 531 | } 532 | 533 | // Formating routine for interface to host computer, output once a minute once all three sesnors have been detected 534 | void usbData(){ 535 | // Stn Id, Packet Type, Wind Quadrant, Wind Speed, Rain Tips, Ext temp, Int Temp, Int Pressure, Int Humidity 536 | if (scan == 7){ //scan==7 means all readings now have a valid value, output on Serial is no OK 537 | Serial.print(byte0); //A reading from the original design for an Oregon sensor byte[2] 538 | Serial.print(","); 539 | Serial.print(byte1); //A reading from the original design for an Oregon sensor byte[3] 540 | Serial.print(","); 541 | //I have a suspicion they may use bad checksums or some such to detect when the Tx is getting low. However that idea has a 542 | //weakness as any other Tx on 433 can scramble a packet, and each WMR86 has three Tx's all transmitting at different intervals 543 | //and will inevitably cause corrupted/bad packets when they overlap. 544 | Serial.print(quadrant); //0-15 in 22.5 degrees steps clockwise 545 | Serial.print(","); 546 | Serial.print(gustWindspeed,1); //Gust windspeed km/hr, not average windspeed (graphing over 10 samples gives Average) 547 | Serial.print(","); 548 | gustWindspeed=avWindspeed; //reset gust to average, then take the larger next reading 549 | Serial.print(rainTotal,1); //checked for calibration to mm, appears to be OK 550 | Serial.print(","); 551 | Serial.print(temperature,2); // OS Temperature Centigrade 552 | Serial.print(","); 553 | //The following two sensors are to be added but for simplicity sake they are not included in this version. 554 | //Only the preset globals are reported here, ie zeroes!!! 555 | Serial.print(intTemp,2); //BMP085 temperature (used for compensation of Pressure reading) Centigrade 556 | Serial.print(","); 557 | Serial.print(intPres,2); //BMP085 pressure reading milli-bars 558 | Serial.print(","); 559 | Serial.print(intHumi); //Digital DHT22 seems better than the OS in Temp/Hum sensor % relative 560 | Serial.println(); 561 | } 562 | } 563 | 564 | 565 | 566 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Oregon Scientific Weather station WMR86 using a 433MHx receiver and an Arduino Uno 2 | ================================================================================== 3 | 4 | The concept is presented in four stages where the first program (BestRaw) simply captures and displays samples of the sensors' raw packet data from the Oregon Scientific sensors. The second version (BestDebugger) further decodes the OS protocol and captures the three main types of packets (rainfall data, wind data and temp/humidity data). The third program (NextStep) recognises the different packet sizes, uses a checksum, and converts the data to human readable format. The fourth program (Final) develops the receiver further by collecting pressure and humidity data directly off the Ardunio, and every minute sending out a serial string of characters summarising all the external weather sensors' data. In my case I use this data to plot information for my 24/7 web weather site. 5 | 6 | ###Manchester Decoding Algorithm 7 | Anybody who has worked with Manchester encoding/decoding may be interested in studying the algorithm used to decode incoming signals. My previous attempts had two very similar sections, with the first detecting a header stream of one's and then the second very similar code detecting the payload of data. I have been able to eliminate the repetition in the code and come up with one unified algorithm. It may be useful to adapt to other applications. 8 | 9 | ###Extending the types of Sensors 10 | One of the drawbacks of not building one's own weather station from scratch is that using a commercial station usually means being stuck with what ever they offer as extra sensors or functions in their consoles. The power of the Arduino is drawn on here to also be able to design your original OS style sensors. The code to generate Manchester encoding is much simpler than receiving it. 11 | 12 | A short example program (Transmitter9) is shown here that sends off an OS protocol compatible signal and allows the Arduino receiver to decode it quite easily, alongside the other OS signals it is getting. It means that novel sensors can be added to the system and expand it well beyond a good weather station. The example provided below is a temperature sensor. However this is not meant to be a "cheap" alternative to buying OS sensors (in fact a standard OS console will not fully understand the structure and meaning of this program's packets hence not respond to them, even though the internal byte structure conforms to the OS format, the packet data does not match any standard particular OS sensor eg a THGR810). The main advantage of brewing one's own sensor is that the Dallas sensor has a TO-3 form factor (ie it looks like a transistor) and can be sealed in glue and stuck in places that a normal sized OS sensor package would never fit (like the inside of the tube that goes into my solar hotwater tank!!). 13 | 14 | Note though that any new sensor you design will need to be well planned out as these programs here have not been developed exhaustively to eliminate possible conflicts with other OS sensors. So a fair degree of hacking and programming skill is required to build any extra sensors that will not conflict with the OS system you have bought. In fact none of the programs use the "rolling code" concept and as a result do not eliminate the possibility of other people's neighboring OS stations being recieved (or for example, a person's setup may want to read more than one wind sensor and also be able to identify each sensor). The code offered here in many cases is the simple bare minimum, and should you want more complete features you will have to add them yourself. Be warned! 15 | 16 | ###Novel Sensors 17 | Inventing sensors also opens up other possibilities such as a lighting detector, soil moisture detector, pet food status, solar power detector, and so on. My favourite idea is to build a Bat Detector. I have constructed a hand held detector (drives earphones), and if I can get it to work locally I will have a go at extending my environmental reporting. I definitely intend to be able to do sampling of the temperature in our lake with a portable temperature recorder that I can dunk in the water, then log it in via 433 when I return home and hence report the lake's temperature. 18 | 19 | ###Sensors plus Arduino versatility 20 | The weather station sensors, along with the Arduino in control, also open a myriad of other possibilities of intelligent switching of devices in response to weather. For example low wind speed and low temperatures often combine as a frost situation inland. These conditions could be detected and the Arduino could turn on sprinklers to save a grape harvest from frost damage. 21 | 22 | ###How to add weather sensors to your project 23 | If you have an Arduino project that looks as though weather conditions may be a factor in its success, then I can recommend buying an Oregon Scientific weather station (I used the WMR86 protocol V3.0) as a great starting point. These programs are not just compile run and sit back, but are offered to serious experimenters who can program and build on other people's ideas. If you are inspired by these programs to do something exciting please let me know. 24 | NB I receive no financial, or any other benefits from promoting Oregon Scientific equipment. 25 | 26 | Rob 27 | 28 | **Initial upload of Arduino+433MHz Rx base station routines:** 29 | 30 | **BestRaw** (grabs Manchester encoded data, first 8bytes of every packet) 31 | 32 | **BestDebugger** (get Manchester encoded data, recognises variable packet length) 33 | 34 | **NextStep** (Decodes Oregon Scientific sensors to human readable format) 35 | 36 | **Final** (gets data from Oregon Scientific Weather sensors and decodes them, reports every minute) 37 | 38 | **Transmitter9** (sends temp to Arduino Base using protocol similar to OS (NB Not OS base compatible)) 39 | -------------------------------------------------------------------------------- /Transmitter9Temp/Transmitter9Temp.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Manchester Encoding, reading by delay rather than interrupt 3 | Rob Ward September 2014 4 | This example code is in the public domain. 5 | Use at your own risk, I will take no responsibility for any loss whatsoever its deployment. 6 | Visit https://github.com/robwlakes/433MHz_Tx_Rx 7 | for the latest version and documentation. 8 | 9 | The following format relates to the Weather stations, mainly because I wanted to incorporate 10 | extra data from other remote RF sensors into my WWW server's weather pages. eg daily solar power 11 | I currently use the Oregon Scientific line of Weather Stations, using the V3.0 protocol. 12 | Hence I wanted to be able to make my Tx use a compatible protocol to simplify the receiver program logic 13 | that decodes the OS protocol. As the Tx mimics a sensor, it would also need to alert the receiver to be 14 | able to distinguish multiple transmitters/sensors apart. ie different ID number's etc so they don't 15 | clash with the existing OS transmitters. The set up below has not been tested apart from with my WMR86 16 | The example here is a temperature sensor based on a Dallas DS18B20. My aim is to be able to log the 17 | temperature in my solar hot water system. The tank has a sensor port let into the side of the tank with 18 | a diameter of 5mm. Obviously trying to fit a standard Oregon Scientific temperature sensor into this is 19 | totally out of the question, however the Dallas TO-3 style case is fine. 20 | Other sensors, like conductivity in soil, tank water levels, animal feed levels, pet behaviour are just a 21 | few examples of sensors that could be built and still incorporate the Oregon Scientific weather equipment. 22 | The huge advantage is that the convenient weather station sensors add quality data to the other measurements. 23 | Hugely important questions like: Does my cat go outside when the wind is from the east?? Could be solved.... 24 | Just joking on that one. No doubt there are more worthwhile serious challenges and 25 | obviously other users may like to determine their own sensor packet format(s), 26 | or adapt it in a major way to their own unique weather station protocols, 27 | ie build a weather station from scratch. 28 | */ 29 | 30 | #include 31 | #define DS18S20_ID 0x10 32 | #define DS18B20_ID 0x28 33 | 34 | const int TxPin = 8; // the TxPin applied to the 433MHz modulator pin 35 | const int ledPin = 13; // the number of the onboard LED pin 36 | 37 | byte thisBit; //reflects the current data bit polarity, 38 | 39 | byte manchester[10]; //stores a bank of data to be manchester encoded on the fly, say first 8 bytes 40 | byte bytePointer = 0; //increments through manchester[data] 41 | byte maxBytes = 10; //number of bytes transmitted 42 | byte oregon[]={ 43 | 16,32,64,128,1,2,4,8}; //Oregon bit mask, protocol is Hi Nibble followed by Lo nibble, but bits in nibble in reverse order 44 | word sDelay = 550; //1/2 of bit pattern duration, begin with 500uS (a common choice, data bit length = 1ms) 45 | int seconds =0; 46 | 47 | byte data[12]; 48 | float temp; 49 | 50 | // DS18S20 Temperature chip i/o 51 | OneWire ds(10); // on pin 10 52 | /* 53 | Pins from Left to right 54 | 1 = Ground 55 | 2 = Digital data 56 | 3 = +5V 57 | Also connect 2&3 with a 4K7 Ohm resistor (pull-up) 58 | */ 59 | 60 | void setup(){ 61 | pinMode(TxPin, OUTPUT); 62 | pinMode(ledPin, OUTPUT); 63 | //Dummy data in the Manchester Byte Array (can be used just to test it out). 64 | manchester[0] = B10100011; //0xA3; //eg Brand/Sensor ID, can be from 0 to 255 65 | manchester[1] = B00000111; //eg Packet type (wind, rain, solar, UV etc) 7? 66 | manchester[2] = 0; //First byte data eg sunlight or temperature 67 | manchester[3] = 0; //Second byte data 68 | manchester[4] = B11110000; //dummy data, bit patterns 69 | manchester[5] = B00000000; //dummy data, incremented 70 | manchester[6] = B11111111; //dummy data, decremented 71 | manchester[7] = B10101010; //dummy data, bit patterns 72 | manchester[8] = B00110011; //dummy data, bit patterns 73 | manchester[9] = 0; //checksum, calculated from preceding data 74 | 75 | Serial.begin(115200); 76 | //while the serial stream is not open, do nothing: 77 | while (!Serial) ; 78 | //Serial.println("Transmitter On Air....");//Just to check if you like 79 | } 80 | 81 | void loop(){ 82 | if (getTemperature()){ 83 | Serial.print("Temperature is ("); 84 | manchester[2] = data[1]; 85 | manchester[3] = data[0]; 86 | temp = float( (data[1] << 8) + data[0] )/16; 87 | //1 LSB bit change = 0.06 of a degree, nice!!! 88 | Serial.print(data[1]);//Binary version MSB 89 | Serial.print("*256 + "); 90 | Serial.print(data[0]);//Binary version LSB 91 | Serial.print(")/16 = "); 92 | Serial.print(temp,2);//Float version 93 | Serial.println("C"); 94 | transmit(); 95 | } 96 | //Wait for 47 Seconds before next transmission 97 | for (int second=0;second<47;second++){ 98 | delay(1000); //a Second 99 | } 100 | } 101 | void transmit(){ 102 | digitalWrite(ledPin,HIGH); 103 | manchester[5]++; //just tickle over the bits in the number to show it can change 104 | manchester[6]--; //as above 105 | //hexBinDump(); 106 | ValidCS(18); 107 | digitalWrite(TxPin,0); //begin on Signal Off 108 | for (int j=0;j<30;j++){ //header 30 of 1's, probably excessive, 20 could be enough 109 | Tx1(); //Send 1's as it stabilises the Rx's AGC the quickest 110 | } 111 | //Oregon has the start bit within the Byte boundaries so is in the manchester Array 112 | //Tx0(); //put in a single 0 as the first "Start Bit", indicates end of Header bit stream of 1's 113 | bytePointer = 0; //Point to start of manchester array 114 | //Serial.print("R "); 115 | for (bytePointer =0;bytePointer> 4); 166 | } 167 | else{ 168 | b = (byte)((byte)(b) & (byte)(0xf)); 169 | } 170 | return b; 171 | } 172 | 173 | //handy for any debugging 174 | void hexBinDump(){ 175 | Serial.print("D "); 176 | for( int i=0; i < maxBytes; i++){ 177 | byte mask = B10000000; 178 | if (manchester[i]<16){ 179 | Serial.print("0"); 180 | } 181 | Serial.print(manchester[i],HEX); 182 | Serial.print(" "); 183 | for (int k=0; k<8; k++){ 184 | if (manchester[i] & mask){ 185 | Serial.print("1"); 186 | } 187 | else{ 188 | Serial.print("0"); 189 | } 190 | mask = mask >> 1; 191 | } 192 | Serial.print(" "); 193 | } 194 | Serial.println(); 195 | } 196 | 197 | boolean getTemperature(){ 198 | byte i; 199 | byte present = 0; 200 | //byte data[12]; 201 | byte addr[8]; 202 | //find a device 203 | int junk = ds.search(addr); 204 | /* 205 | //Not required if only one sensor to be found 206 | if (!ds.search(addr)) { 207 | ds.reset_search(); 208 | Serial.println("Bad Search"); 209 | return false; 210 | } 211 | */ 212 | if (OneWire::crc8( addr, 7) != addr[7]) { 213 | Serial.println("Bad CRC"); 214 | return false; 215 | } 216 | if ((addr[0] != DS18S20_ID) && (addr[0] != DS18B20_ID)) { 217 | Serial.println("Bad ID"); 218 | return false; 219 | } 220 | ds.reset(); 221 | ds.select(addr); 222 | // Start conversion 223 | ds.write(0x44, 1); 224 | // Wait some time... 225 | delay(850); 226 | present = ds.reset(); 227 | ds.select(addr); 228 | // Issue Read scratchpad command 229 | ds.write(0xBE); 230 | // Receive 9 bytes 231 | for ( i = 0; i < 9; i++) { 232 | data[i] = ds.read(); 233 | } 234 | // Calculate temperature value 235 | temp = float( (data[1] << 8) + data[0] )/16; 236 | return true; 237 | } 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | -------------------------------------------------------------------------------- /github_button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/Weather-Station-OS-Sensors/f07bd8d41fc45c8fe1965160fe8517a46fa0bb31/github_button.png --------------------------------------------------------------------------------