├── images ├── WMR86.JPG ├── header.png ├── DualTrace.png ├── hardwired.jpg ├── schematic.png ├── AGC_Starting.png ├── RGB_Status.jpg ├── bitwaveform.png └── manchester.png ├── .gitignore ├── LICENSE ├── Extensions.md ├── DebugMan.md ├── DebugManchester.ino ├── Debug_Auto.ino ├── README.md ├── SampleDump.txt └── MainWeather_09 └── MainWeather_09.ino /images/WMR86.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/WMR86.JPG -------------------------------------------------------------------------------- /images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/header.png -------------------------------------------------------------------------------- /images/DualTrace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/DualTrace.png -------------------------------------------------------------------------------- /images/hardwired.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/hardwired.jpg -------------------------------------------------------------------------------- /images/schematic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/schematic.png -------------------------------------------------------------------------------- /images/AGC_Starting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/AGC_Starting.png -------------------------------------------------------------------------------- /images/RGB_Status.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/RGB_Status.jpg -------------------------------------------------------------------------------- /images/bitwaveform.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/bitwaveform.png -------------------------------------------------------------------------------- /images/manchester.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robwlakes/ArduinoWeatherOS/HEAD/images/manchester.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | 23 | # Temp files 24 | *~ 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Rob Ward 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Extensions.md: -------------------------------------------------------------------------------- 1 | Weather Station Extension 2 | ========================= 3 | ### Extension of the Project 4 | Several features became desirable after the original system had been running for several years. 5 | The main concern was a lack of Battery Level indicators and also no direct feedback from the Arduino module 6 | of the signals being received. The solution was tackled in two ways, but involving similar parts of the program. 7 | 8 | ### Battery Levels 9 | 10 | The Battery Levels were a harder one to crack. I checked the WMR86 manual for mention of a battery level indicator and also checked the LCD screen supplied with it for any indicators that referred to battery levels. 11 | I could not find any. So I devised this strategy. Should any sensor not be logged within a minute (ie between sending strings of data to the WWW server) then a number is incremented for that Sensor. As soon as that 12 | sensor is detected the number is reset to Zero. However if the sensor is not detected for 20 minutes then a flag bit is set for that Sensor. This "detection" number is sent to the WWW server along with all the other 13 | data every minute. If it remains 0 then the sensors are OK, if it is non-zero, then it means one of the three sensors have experienced a "continuous down time" of at least 20 minutes. The WWW server can check this and send out an Email 14 | at midnight of that day warning of which sensor(s) have been missed. However the warning could arise for a number of reasons, the first obvious one is the batteries are going flat and the second is that something maybe 15 | effecting either the transmitter or receiver. Eg the Antenna may have changed position on the Rx and suddenly 2 outof the three are being received. Or maybe some object has been placed bewteen the Tx and Rx and 16 | that has knocked out the signal. However, just because the signal drops out it may not be because of the Batteries. The system should be sensitive enough to warn of early stages of battery failure. Even if it begins by 17 | small periods of time overnight in the cool night air. 18 | 19 | ### Status Indicator 20 | The RGB LED should give a quick indication if an antenna position change for example is still allowing the sensors to be received. 21 | An RGB LED was added to the board to indicate which sensor transmitter was detected. By combining Red, Blue and/or Green I could easily produce 8 recognisable states from the LED. 22 | Simply observing the board would indicate that the different sensors were being logged. This gave a quick indication, for example after a restart that the Arduino was functioning properly, 23 | and so were the 4 Oregon sensors 24 | #### Colour coding was: 25 | #### Red = Temperature and Humidity 26 | #### Green = Wind Direction and Speed 27 | #### Blue = Rain Gauge 28 | #### Yellow = UV Light Sensor 29 | #### Purple = Experimental 30 | 31 | ![alt text](images/RGB_Status.jpg?raw=true "RGB Status LED") 32 | 33 | 34 | ### Software Version 35 | 36 | The version to use these features is MainWeather_09.ino. Please note the order of the sensors in the output string has also been changed. So if you have a system reading this string and processing it from the previous version 37 | you will have to alter it as well to use this program as it is. 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /DebugMan.md: -------------------------------------------------------------------------------- 1 | General Manchester Debugger Ver8 2 | ================================ 3 | 4 | Hi, here are some rough notes for the moment to accompany the Manchester Debug Program Version 10. 5 | 6 | Only a few things need to be changed and tested early, others much later. Pretty much an easy recipe... 7 | 8 | > // Variables for Manchester Receiver Logic: 9 | 10 | > word sDelay = 245; //Small Delay about 1/4 of bit duration begin with 250 (alter early) 11 | 12 | > word lDelay = 390; //Long Delay about 1/2 of bit duration begin with 500, 1/4 + 1/2 = 3/4 (alter early) 13 | 14 | The above delays are the first things to work on. They do not need to be very exact as the timing can be out by about +-15 and the encoding will tolerate it ok. However a CRO, or something like Audacity used as simple storage CRO, can get you into the right ball park much quicker. This can also help you decide whether the polarity is positive or negative Manchester. Either are equally likely and it is just a matter of knowing what your system uses. Again some sort of CRO is a help here. As there are only two possibilities, there is no problem with trial and error here. So beginning by just playing with the sDelay, lDelay and the Polarity settings can be enough to get you some decoded signal. You can fine tune other parts as time goes on. 15 | 16 | > byte polarity = 1; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start (alter early) 17 | 18 | > byte tempBit = 1; //Reflects the required transition polarity (no need to alter) for the following data bit. 19 | 20 | > byte bitState ; //State of the RxPin 3/4 way through Bit Waveform 21 | 22 | > byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 23 | 24 | The variable tempBit simply follows the raw data coming in, but is exclusive-OR'ed with Polarity to invert it (if Neg Polarity is selected) before being packed into the byte array. 25 | 26 | > boolean firstZero = false;//flags when the first '0' is found. (no need to alter) 27 | 28 | > boolean noErrors =true; (no need to alter) 29 | 30 | These two variables above need not be changed. 31 | 32 | > //variables for Header detection 33 | 34 | > byte headerBits = 10; //The number of ones expected to make a valid header (alter later) 35 | 36 | > byte headerHits = 0; //Counts the number of "1"s to determine a header (no need to alter) 37 | 38 | Your CRO will also give you an idea of how many header bits are part of the protocol. If you think there are about 20 header bits (1's) then set the headerBits variable to say half, in this example 10 headerBits are counted before it is accepted as a valid header. 39 | 40 | > //Variables for Byte storage 41 | 42 | > byte discards =0; //Expecting all bits to be inside byte boundaries, set to number of leading bits to be dumped (alter later) 43 | 44 | You may need to set the variable discards (eg =0 means no discards, sync zero is included in the packet, or =1, discard the sync zero from the packet, or =2 discards the first two bits) depending on how the byte boundaries line up. You will need to be aware when trying to process the data that it maybe not alligned for the easiest processing if the sync zero, for example, is included in the first byte or excluded. 45 | 46 | > byte dataByte = 0; //Accumulates the bit information (no need to alter) 47 | 48 | > byte nosBits = 0; //Counts to 8 bits within a dataByte (no need to alter) 49 | 50 | > byte maxBytes = 6; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error (alter later) 51 | 52 | Set maxBytes to quite low to begin with until you can get very stable packet reception (eg many the same or "explainably" different), then experiment with how high you can set the number and still get "valid" packets (this maybe hard to test until you have sorted repetition or checksum out as well). NB If you set the number of bytes too high you will not see packets at all, as any scrambled data received after the valid bits/bytes will cause the program to exit, and not show you anything. Some protocols eg Oregon Scientific have different packet lengths for various sensors. This has to be detected on the fly and maxBytes changed before the packet reception has finished. So a fair bit of modification would be require to accomodate this, but not difficult. 53 | 54 | > byte nosBytes = 0; //Counter stays within 0 -> maxBytes (no need to alter) 55 | 56 | > //Variables for multiple packets 57 | 58 | > byte bank = 0; //Points to the array of 0 to 3 banks of results from up to 4 separate, but sequential, data downloads (alter later, if required) 59 | 60 | Some Manchester protocols do not have a checksum, they merely quickly repeat the packet say three of four times and each packet repeated has to be stored into seperate banks and later compared. If all four banks are the same then the packet is declared valid. In this program only one bank is used, but there are four defined if you find you need to go down that path. 61 | 62 | > byte nosRepeats = 0; //Number of times the header/data is fetched at least once or up to 4 times (alter later) 63 | 64 | > //Banks for multiple packets if required (at least one will be needed), four provided here... 65 | 66 | > byte manchester[4][20]; //Stores 4 banks of manchester pattern decoded on the fly (alter later) 67 | 68 | So in a nutshell 69 | 70 | >1. Get an idea of what sDelay and lDelay ought to be (lDelay is usually 2*sDelay) 71 | 72 | >2. Get an idea of how many header bits you want to look for (10 is OK to begin with) 73 | 74 | >3. Experiment with polarity setting 75 | 76 | You should aim for steady, repeatable reception at this stage... then 77 | 78 | >1. See if packets are repeated for validation (then use banks for validation code) 79 | 80 | >2. Experiment with how many bytes are used (build up gradually) 81 | 82 | >3. Work out whether all bits are included in, or some are excluded from, the packet bytes 83 | 84 | After all this, you need to begin processing the data bytes in the first Manchester array to see if you can replicate the readings from your console. You will need to keep the discard bits problem in mind here (how many leading bits included or excluded in the packet?). Data may not make sense easily if this is not sorted out (ie binary overlapping into adjacent bytes can be messy to decode!) 85 | 86 | To improve reliability of the reception you will also need to be able reject bad packets. This will probably be by either repetition of packets (ie get 4 in a row the same, and its valid!!) or a single packet with a simple checksum (or a more complicated cyclic redundancy polynomial algorithm). Manchester encoding does not have inherent data error checking, just waveform checking. It is quite tolerant of timing varations, but any error detection in the data must be added at the next level after the raw bytes have been received. Good luck! 87 | 88 | Rob Ward 89 | 90 | PS A very interesting observation for many people who have problems with Weather Stations connected by RF, eg 433MHz, is that if there are sensors close by that are a relatively strong transmitter they can upset the AGC of the simpler 433MHz Rx'ers to the point where a more remote sensor will not be received. The AGC does not recover quick enough to correctly respond to the weaker signal and probably causes the header to timeout (not enough hits) or just prone to errors. So it may be advantageous in some situations to move stronger signals further away. The Rain Fall sensor Tx with the Oregon Scientifics is much stronger than the Temperature Tx for example. Buying a good modern receiver brand like Dorji can make all the difference here. eg RF-DRA886RX-S, the Dorji 433MHZ Transceiver 13dBm SMA CONN 91 | 92 | -------------------------------------------------------------------------------- /DebugManchester.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_10, 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 | boolean firstZero = false;//has it processed the first zero yet? This a "sync" bit. 24 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 25 | //variables for Header detection 26 | byte headerBits = 10; //The number of ones expected to make a valid header 27 | byte headerHits = 0; //Counts the number of "1"s to determine a header 28 | //Variables for Byte storage 29 | byte dataByte = 0; //Accumulates the bit information 30 | byte nosBits = 0; //Counts to 8 bits within a dataByte 31 | byte maxBytes = 5; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 32 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 33 | //Variables for multiple packets 34 | byte bank = 0; //Points to the array of 0 to 3 banks of results from up to 4 last data downloads 35 | byte nosRepeats = 0; //Number of times the header/data is fetched at least once or up to 4 times 36 | //Banks for multiple packets if required (at least one will be needed) 37 | byte manchester[4][20]; //Stores 4 banks of manchester pattern decoded on the fly 38 | 39 | /* Sample Printout, Binary for every packet, but only combined readings after three of the different packets have been received 40 | This is an example where the Sync '0' is inside the byte alignment (ie always a zero at the start of the packet) 41 | 42 | Using a delay of 1/4 bitWaveform 245 uSecs 1/2 bitWaveform 490 uSecs 43 | Positive Polarity 44 | 10 bits required for a valid header 45 | Sync Zero inside Packet 46 | D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 90 22223333 47 | D 5F 01011111 14 00010100 28 00101000 C5 11000101 01 00000001 //Oregon Scientific Temperature 48 | D 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 //Oregon Scientific Rainfall 49 | D 58 01011000 91 10010001 20 00100000 52 01010010 13 00010011 //Oregon Scientific Anemometer/Wind direction 50 | These are just raw test dumps. The data has to be processed and require 10-11 bytes for all the packet to be seen 51 | Oregon Scientific works on nibbles and these need to reversed ie ABCD become DCBA in each nibble 52 | Also the OS protocol has a simple checksum to assist with validation as the packets are only sent once a cycle 53 | */ 54 | 55 | void setup() { 56 | Serial.begin(115200);//make it fast so it dumps quick! 57 | pinMode(RxPin, INPUT); 58 | pinMode(ledPin, OUTPUT); 59 | Serial.println("Debug Manchester Version 09"); 60 | lDelay=2*sDelay;//just to make sure the 1:2 ratio is established. They can have some other ratio if required 61 | Serial.print("Using a delay of 1/4 bitWaveform ");// +-15% and they still seem to work ok, pretty tolerant! 62 | Serial.print(sDelay,DEC); 63 | Serial.print(" uSecs 1/2 bitWaveform ");//these may not be exactly 1:2 ratio as processing also contributes to these delays. 64 | Serial.print(lDelay,DEC); 65 | Serial.println(" uSecs "); 66 | if (polarity){ 67 | Serial.println("Negative Polarity hi->lo=1"); 68 | } 69 | else{ 70 | Serial.println("Positive Polarity lo->hi=1"); 71 | } 72 | Serial.print(headerBits,DEC); 73 | Serial.println(" bits expected for a valid header"); 74 | if (discards){ 75 | Serial.print(discards,DEC); 76 | Serial.println(" leading bits discarded from Packet"); 77 | } 78 | else{ 79 | Serial.println("All bits inside the Packet"); 80 | } 81 | Serial.println("D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 90 22223333"); 82 | //if packet is repeated then best to have non matching numbers in the array slots to begin with 83 | //clear the array to different nos cause if all zeroes it might think that is a valid 3 packets ie all equal 84 | eraseManchester(); //clear the array to different nos cause if all zeroes it might think that is a valid 3 packets ie all equal 85 | }//end of setup 86 | 87 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 88 | void loop(){ 89 | tempBit=polarity^1; //these begin the opposites for a packet 90 | noErrors=true; 91 | firstZero=false; 92 | headerHits=0; 93 | nosBits=0; 94 | nosBytes=0; 95 | while (noErrors && (nosBytes0, or 0->1 111 | //data transition detection must swap, so it loops for the opposite transitions 112 | tempBit = tempBit^1; 113 | }//end of detecting no transition at end of bit waveform, ie end of previous bit waveform same as start of next bitwaveform 114 | 115 | //Now process the tempBit state and make data definite 0 or 1's, allow possibility of Pos or Neg Polarity 116 | if(bitState==1){ //1 data could be header or packet 117 | if(!firstZero){ 118 | headerHits++; 119 | if (headerHits==headerBits){ 120 | digitalWrite(ledPin,1); //valid header accepted, minimum required found 121 | //Serial.print("H"); 122 | } 123 | } 124 | else{ 125 | add(bitState);//already seen first zero so add bit in 126 | } 127 | }//end of dealing with ones 128 | else{ //bitState==0 could first error, first zero or packet 129 | // if it is header there must be no "zeroes" or errors 130 | if(headerHits=headerBits)){ //if first zero, it has not been found previously 137 | firstZero=true; 138 | }//end of finding first zero 139 | add(bitState); 140 | }//end of dealing with a zero 141 | }//end of dealing with zero's (in header, first or later zeroes) 142 | }//end of first error check 143 | }//end of while noErrors=true and getting packet of bytes 144 | digitalWrite(ledPin,0); //data processing exited, look for another header 145 | }//end of mainloop 146 | 147 | //Read the binary data from the bank and apply conversions where necessary to scale and format data 148 | void analyseData(){ 149 | } 150 | 151 | void add(byte bitData){ 152 | if (discards>0){ //if first one, it has not been found previously 153 | discards--; 154 | } 155 | else{ 156 | dataByte=(dataByte<<1)|bitData; 157 | nosBits++; 158 | if (nosBits==8){ 159 | nosBits=0; 160 | manchester[bank][nosBytes]=dataByte; 161 | nosBytes++; 162 | //Serial.print("B"); 163 | } 164 | if(nosBytes==maxBytes){ 165 | hexBinDump();//for debug purposes dump out in hex and binary 166 | //analyseData();//later on develop your own analysis routines 167 | } 168 | } 169 | } 170 | 171 | void hexBinDump(){ 172 | //Print the fully aligned binary data in manchester[bank] array 173 | Serial.print("D "); 174 | for( int i=0; i < maxBytes; i++){ 175 | byte mask = B10000000; 176 | if (manchester[bank][i]<16){ 177 | Serial.print("0"); //Pad single digit hex 178 | } 179 | Serial.print(manchester[bank][i],HEX); 180 | Serial.print(" "); 181 | for (int k=0; k<8; k++){ 182 | if (manchester[bank][i] & mask){ 183 | Serial.print("1"); 184 | } 185 | else{ 186 | Serial.print("0"); 187 | } 188 | mask = mask >> 1; 189 | } 190 | Serial.print(" "); 191 | } 192 | Serial.println(); 193 | } 194 | 195 | void eraseManchester(){ 196 | //Clear the memory to non matching numbers across the banks 197 | //If there is only one packet, with no repeats this is not necessary. 198 | for( int j=0; j < 4; j++){ 199 | for( int i=0; i < 20; i++){ 200 | manchester[j][i]=j+i; 201 | } 202 | } 203 | } 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /Debug_Auto.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Manchester Decoding, reading by delay rather than interrupt 3 | Rob Ward Agust 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 ReadMe: DebugMan.md 8 | 9 | Please leave comment or suggestions in the "Issues" feature on the GIT-Hub site 10 | 11 | DebugAuto, 28th July 2014 12 | 13 | This version steps automatically through a series of delays 10uS at a time each 5mins 14 | and attempts to decode Manchester encoded signals. The Polarity of the code can be either 15 | positive or negative. This program detects longer intervals bewtween transitions 16 | as 'faulty' zeroes and automatically reverses the Polarity setting for subsequent 17 | packets. Because it starts off at a fairly unlikely setting for a Weather Station 18 | (low durations between samples) few packets will be initially decoded, if any at all. 19 | You will have to be patient and wait until the timings get closer and data finally 20 | begins to appear. This may involve looking at a static screen for quite a while, 21 | as it steps through the 10uS increments. When the data does begin to appear then 22 | note down the Polarity and what delay is best. You should also allow it to go through 23 | to longer and longer delays to there the packets begin to reduce in numbers so you know 24 | you have the best mid point for the sDelay setting. 25 | 26 | The aim of this program it to provide experimenters and hackers a simple to use tool that 27 | will reveal the basic settings to receive a particular systems codes. The program can 28 | be easily modified to become the basis of permanent installations when an experimenter 29 | adds in their own calculations and procedures. I have overdone the comments but hopefully 30 | experimenters, especially beginners will appreciate the overflow! Experts delete them! 31 | For actual installations the program can be stripped back to its bare essentials by 32 | deleting all the debug and incrementing stuff etc and putting chosen final values for all 33 | the parameters. 34 | 35 | Considerable lattitude is allowed in choosing a sDelay value to set up the Manchester 36 | decoding and experimenters may find there are a range of values of sDelay to choose from. 37 | Generally aim for the mididle ground to give you a bit of room to handle errors on either 38 | side. Variations up to +-10-15% are tolerated due to the program syncing each bit 39 | to the middle transition event that always occurs in proper Manchester encoding. So 40 | small errors are contirnually corrected and do not accumulate. 41 | 42 | So expect to see a fairly broad band of sDelays that will work OK. Once you 43 | are satisfied you have the sDelay and Polarity worked out (only two settings!) 44 | you can begin pulling the program apart for your own purposes and building your 45 | own version, eg what bits get discarded, how many bytes, packet repeat etc 46 | 47 | The knowledge for this program came from wanting to intercept the signals from 48 | a Bios (or Thermor) weather station to feed into my 24/7 weather www page. 49 | I had to get to get to grips with the Manchester encoding first and after many 50 | false starts I succeeded. After that came the decryption of the packets and 51 | calculating of meaningful values. My old Bios broke down and I swapped to an Oregon 52 | Scientific WS, and had to do it all over again. Both quite different animals, so to 53 | speak, but both used Manchester Encoding. Second time through was a lot quicker 54 | but after having tried to help people do the same on the Arduino Forums I figured 55 | I needed to publish some good stuff for people to read up on. 56 | 57 | After a while I wanted to streamline my Manchester decoding logic and the result 58 | of that is here in this program. With better logic and a much tighter procedure 59 | (instead of one routine for the header and another for the packet data, this routine 60 | can do it all in one!) design, I have been able to make it almost automatic to 61 | at least get some Manchester data from something like a weather station. If the 62 | bit waveform has a duration very much above or below 1mS the automatic part will 63 | will fall down. If the duration gets very short where the delay caused by the 64 | processing becomes similar in size to the sDelay then program will probably work 65 | but will need fairly inspired alterations. The simple relationship that 66 | lDelay=2*sDelay will probably break down. Then the sDelay and lDelay may need to 67 | to be determined independently. For longer delays the 1:2 relation will work fine 68 | but the auto detection will need the loopCount and timeout turned into a word 69 | variable as it is only a byte varaible at the moment to maximise the speed of the 70 | loopCount++ counting. As the delays get bigger counting in words will not matter. 71 | timeout=lDelay/5; may need to change as well to keep the timeout (the number 72 | of loop++'s) to be about 1.5 times the duration of the sDelay duration. 73 | 74 | Obviously once the validation of the packets is working ie checked by repeats or 75 | a numerical checksum, then if this process is repeated the best timings will become 76 | even more evident. This checking will eliminate packets that don't have any 77 | obvious Manchester decoding errors, but do in fact have internal errors. 78 | 79 | Please read the other documents on my GIT-Hub site that accompany this program for 80 | a fuller understanding of the Manchester concepts so you can "do you own thing!!" 81 | 82 | I hope you enjoy your own personal journey as I have enjoyed mine and this program 83 | heklps you enjoy even more :-) Best wishes for your progress! 84 | 85 | Rob Ward 86 | 87 | */ 88 | 89 | //Interface Definitions 90 | int RxPin = 8; //The number of signal from the Rx 91 | int ledPin = 13; //The number of the onboard LED pin 92 | 93 | // Variables for Manchester Receiver Logic: 94 | word sDelay = 200; //Small Delay about 1/4 of bit duration try like 220 to 480 95 | word lDelay ; //Long Delay about 1/2 of bit duration try like 440 to 880, 1/4 + 1/2 = 3/4 96 | byte polarity = 0; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start 97 | byte tempBit ; //Reflects the required transition polarity 98 | byte bitState ; //State of the RxPin 3/4 way through Bit Waveform 99 | //Variables for Error detection 100 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 101 | byte timeout ; //Maximum loops allowed to look for the next bit transition 102 | byte loopCount ; //Incremented inside the loop looking for next bit transition 103 | //variables for Header detection 104 | byte headerBits = 10; //The number of ones expected to make a valid header 105 | byte headerHits = 0; //Counts the number of "1"s to determine a header 106 | boolean firstZero = false;//has it processed the first zero yet? This a "sync" bit. 107 | word nosHits = 0; //number hex dumps achieved (not necessarily error free, just hits!) 108 | //Variables for Byte storage 109 | byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 110 | byte dataByte = 0; //Accumulates the bit information 111 | byte nosBits = 0; //Counts to 8 bits within a dataByte 112 | byte maxBytes = 5; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 113 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 114 | //Variables for multiple packets 115 | byte bank = 0; //Points to the array of 0 to 3 banks of results from up to 4 last data downloads 116 | byte nosRepeats = 0; //Number of times the header/data is fetched at least once or up to 4 times 117 | //Banks for multiple packets if required (at least one will be needed) 118 | byte manchester[4][20]; //Stores 4 banks of manchester pattern decoded on the fly 119 | 120 | word seconds =0;//used in the interrupt 121 | 122 | /* Sample Printout, Binary for every packet, but only combined readings after three of the different packets have been received 123 | This is an example where the Sync '0' is inside the byte alignment (ie always a zero at the start of the packet) 124 | 125 | Using a delay of 1/4 bitWaveform 245 uSecs 1/2 bitWaveform 490 uSecs 126 | Positive Polarity 127 | 10 bits required for a valid header 128 | Sync Zero inside Packet 129 | D 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 90 22223333 130 | D 5F 01011111 14 00010100 28 00101000 C5 11000101 01 00000001 //Oregon Scientific Temperature 131 | D 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 //Oregon Scientific Rainfall 132 | D 58 01011000 91 10010001 20 00100000 52 01010010 13 00010011 //Oregon Scientific Anemometer/Wind direction 133 | These are just raw test dumps. The data has to be processed and require 10-11 bytes for all the packet to be seen 134 | Oregon Scientific works on nibbles and these need to reversed ie ABCD become DCBA in each nibble 135 | Also the OS protocol has a simple checksum to assist with validation as the packets are only sent once a cycle 136 | */ 137 | 138 | void setup() { 139 | Serial.begin(115200);//make it fast so it dumps quick! 140 | pinMode(RxPin, INPUT); 141 | pinMode(ledPin, OUTPUT); 142 | 143 | // Initialize Timer1 for a 1 second interrupt 144 | // Thanks to http://www.engblaze.com/ for this section, see their Interrupt Tutorial 145 | cli(); // disable global interrupts 146 | TCCR1A = 0; // set entire TCCR1A register to 0 147 | TCCR1B = 0; // same for TCCR1B 148 | // set compare match register to desired timer count: 149 | OCR1A = 15624; 150 | // turn on CTC mode: 151 | TCCR1B |= (1 << WGM12); 152 | // Set CS10 and CS12 bits for 1024 prescaler: 153 | TCCR1B |= (1 << CS10); 154 | TCCR1B |= (1 << CS12); 155 | // enable timer compare interrupt: 156 | TIMSK1 |= (1 << OCIE1A); 157 | // enable global interrupts: 158 | sei(); 159 | 160 | 161 | Serial.println("Debug Manchester Version GIT-Hub V03"); 162 | lDelay=2*sDelay;//just to make sure the 1:2 ratio is established. They can have some other ratio if required 163 | Serial.print("Using a delay of 1/4 bitWaveform ");// +-15% and they still seem to work ok, pretty tolerant! 164 | Serial.print(sDelay,DEC); 165 | Serial.print(" uSecs 1/2 bitWaveform ");//these may not be exactly 1:2 ratio as processing also contributes to these delays. 166 | Serial.print(lDelay,DEC); 167 | Serial.println(" uSecs "); 168 | if (polarity){ 169 | Serial.println("Negative Polarity hi->lo=1"); 170 | } 171 | else{ 172 | Serial.println("Positive Polarity lo->hi=1"); 173 | } 174 | Serial.print(headerBits,DEC); 175 | Serial.println(" bits expected for a valid header"); 176 | if (discards){ 177 | Serial.print(discards,DEC); 178 | Serial.println(" leading bits discarded from Packet"); 179 | } 180 | else{ 181 | Serial.println("All bits inside the Packet"); 182 | } 183 | timeout=lDelay/5;//Rough number to indicate max loops allowed, if exceeded usually wrong polarity chosen, heals itself if wrong 184 | Serial.print(timeout,DEC); 185 | Serial.println(" initial timeout to detect wrong Polarity"); 186 | Serial.println("P 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 90 22223333"); 187 | Serial.print("sDelay ="); 188 | Serial.println(sDelay,DEC); 189 | //if packet is repeated then best to have non matching numbers in the array slots to begin with 190 | //clear the array to different nos cause if all zeroes it might think that is a valid 3 packets ie all equal 191 | eraseManchester(); //clear the array to different nos cause if all zeroes it might think that is a valid 3 packets ie all equal 192 | }//end of setup 193 | 194 | //Routine Driven by Interrupt, trap 1 second interrupts, and output every minute 195 | ISR(TIMER1_COMPA_vect){ 196 | seconds++; 197 | if (seconds == 300){//make 300 for each output ie 5 minutes 198 | sDelay = sDelay+10; 199 | seconds = 0; 200 | Serial.print("Nos Hits =");//Number of successful Hex dumps (not necessarily fre of errors) 201 | Serial.println(nosHits,DEC); 202 | nosHits=0; 203 | Serial.print("sDelay =");//So the progress of the increments on sDelay can be monitored 204 | Serial.println(sDelay,DEC); 205 | lDelay = sDelay*2;//Assume the sampling delays are ratio 1:2 206 | timeout=lDelay/5; //Rough number to indicate max loops allowed, if exceeded usually wrong polarity chosen, heals itself if wrong 207 | } 208 | } //end of interrupt routine 209 | 210 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 211 | void loop(){ 212 | tempBit=polarity^1; //these begin as opposites for each packet 213 | noErrors=true; 214 | firstZero=false; 215 | headerHits=0; 216 | nosBits=0; 217 | nosBytes=0; 218 | while (noErrors && (nosBytes0, or 0->1 238 | //data transition detection must swap, so it loops for the opposite transition in the next bit waveform 239 | tempBit = tempBit^1; 240 | }//end of detecting no transition at end of bit waveform 241 | //when looping to detect the transition in middle of bit waveform it must never take too long 242 | if(loopCount>timeout){ 243 | noErrors=false;//not allowed to sync on an incorrect transition 244 | //if in the header phase and looking for the sync 0 245 | if ((!firstZero)&&(headerHits>headerBits)){ 246 | //ending here means the zero was found but polarity was wrong 247 | //Serial.println(loopCount,DEC);//enable for debugging 248 | polarity = polarity^1;//switch polarity and try for next packet 249 | //Serial.print("Polarity now ="); 250 | //Serial.println(polarity,DEC); 251 | } 252 | } 253 | 254 | //Now process the tempBit state and interept data as definite 0 or 1's, (Polarity corrected by now) 255 | if(bitState==1){ //1 data could be header or packet 256 | if(!firstZero){ 257 | headerHits++; 258 | if (headerHits==headerBits){ 259 | digitalWrite(ledPin,1); //valid header accepted, minimum required found 260 | //Serial.print("H"); 261 | } 262 | } 263 | else{ 264 | add(bitState);//already seen first zero so add bit in 265 | } 266 | }//end of dealing with ones 267 | else{ //bitState==0 could first error, first zero or packet 268 | // if it is header there must be no "zeroes" or errors 269 | if(headerHits=headerBits)){ //if first zero, it has not been found previously 276 | firstZero=true; 277 | //Serial.print("!"); 278 | }//end of finding first zero 279 | add(bitState); 280 | }//end of dealing with a zero 281 | }//end of dealing with zero's (in header, first or later zeroes) 282 | }//end of first error check 283 | }//end of while noErrors=true and getting packet of bytes 284 | digitalWrite(ledPin,0); //data processing exited, look for another header 285 | }//end of mainloop 286 | 287 | void add(byte bitData){ 288 | if (discards>0){ //if first one, it has not been found previously 289 | discards--; 290 | } 291 | else{ 292 | dataByte=(dataByte<<1)|bitData; 293 | nosBits++; 294 | if (nosBits==8){ 295 | nosBits=0; 296 | manchester[bank][nosBytes]=dataByte; 297 | nosBytes++; 298 | //Serial.print("B"); 299 | } 300 | if(nosBytes==maxBytes){ 301 | nosHits++;//keep track 302 | hexBinDump();//for debug purposes dump out in hex and bainary 303 | //analyseData();//later on develop your own analysis routines 304 | } 305 | } 306 | } 307 | 308 | //Read the binary data from the bank and apply conversions where necessary to scale and format data 309 | void analyseData(){ 310 | } 311 | 312 | void hexBinDump(){ 313 | //Print the fully aligned binary data in manchester[bank] array 314 | Serial.print(polarity,DEC);//show what polarity was used to reveal the packet 315 | Serial.print(" "); 316 | for( int i=0; i < maxBytes; i++){ 317 | byte mask = B10000000; 318 | if (manchester[bank][i]<16){ 319 | Serial.print("0"); //Pad single digit hex 320 | } 321 | Serial.print(manchester[bank][i],HEX); 322 | Serial.print(" "); 323 | for (int k=0; k<8; k++){ 324 | if (manchester[bank][i] & mask){ 325 | Serial.print("1"); 326 | } 327 | else{ 328 | Serial.print("0"); 329 | } 330 | mask = mask >> 1; 331 | } 332 | Serial.print(" "); 333 | } 334 | Serial.println(); 335 | } 336 | 337 | void eraseManchester(){ 338 | //Clear the memory to non matching numbers across the banks 339 | //If there is only one packet, with no repeats this is not necessary. 340 | for( int j=0; j < 4; j++){ 341 | for( int i=0; i < 20; i++){ 342 | manchester[j][i]=j+i; 343 | } 344 | } 345 | } 346 | 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Weather Stations 2 | ================ 3 | 4 | ### Arduino Uno, 433MhzRx and Oregon Scientific WMR86 Weather Station 5 | 6 | This project allows the 433MHz signals from an Oregon Scientific WMR86 weather station to be intercepted and decoded into simple decimal values using an Arduino Uno. The values for Wind Direction, Wind Speed, Temperature, Humidity, UV Light and Rainfall are then sent via the USB/Serial port on the Arduino to the host computer. In my case I use a Python program to interpret this CSV formatted string of characters and plot the above parameters for display on my website http://www.laketyersbeach.net.au/weather.html (Please note the DHT22 Humidity/Temperature sensor on the Arduino shield is not used in the my final data stream, but is decoded in the software, and so is the temperature read from the BMP05 air pressure detector. The externally mounted OS Temperature/Humidity sensor is used in my final data string. This can be modified to suit your own purposes). 7 | 8 | ### The hardware 9 | 10 | ![alt text](images/hardwired.jpg?raw=true "Arduino+433MHzRx+Humidity+Barometer") 11 | 12 | The Arduino listens continually for the three WMR86 sensor's broadcasts (the UV Light sensor must be purchased seperately) and merges them every minute for an output of a CSV string. There are three transmitters in the WMR86 package, Wind Direction+Wind Speed (average and gusts), Temperature+Humidity, and Rainfall (cumulative total and rate). They each have different periods between transmission eg Wind every 14 Seconds, Temp/Hum and Rainfall at longer periods. This does cause overlap of sensor transmissions and inevitable corruption of some packets, however the protocol does have a simple arithmetic checksum based on nybbles that helps eliminate most bad data. Admittedly the chance of substitution errors causing bad data to go un-detected is much higher than if a higher bit CRC based on a polynomial process was used. Range validation would be necessary (in the Python or Arduino) to check if the resulting readings were sensible to improve reliability. This program reports every minute for all three sensors whether a good packet has been received within that minute or not, so the Python program can sum the good and bad packets each minute and report the relative numbers each week in an email. 13 | 14 | The Wiring: A generic prototyping shield was used. 15 | 16 | ![alt text](images/schematic.png?raw=true "Arduino Schematic") 17 | 18 | #### Manchester Protocol 19 | 20 | The Ardunio algorithm to decode the Manchester protocol uses timed delays to sample the waveform from the 433MHz Rx and not interrupts and direct measurement of waveform transitions. This has a number of implications. The main one is that Arduino is continually sampling and analysing the incoming waveform. As it is a dedicated processor in this application and has no other function, this is not a problem. The benefit is that it does simplify the reception and analysis of the waveforms. The simple decoding strategy should also be worth studying by anyone else attempting to leverage other systems that use Manchester encoding. To that end a fairly lengthy explanation is offered below. 21 | 22 | The Manchester protocol is a bias neutral protocol where each bit is composed of a high and low signal of equal length (or close to it). This means that a transmitter can be totally keyed off and on (ie the 433MHz signal is not partially modulated, but is either fully transmitting or 'silent')and no matter what sequences of data, 1's and 0's, are transmitted, on average the Tx signal will consequently be on for the same time as it is off. This means the switching point, or bias level, for the receiver amplifier remains steady. A data bit in a Manchester waveform always has a high and low signal component for encoding both 1's and 0's. So a Bit Waveform for a Data 1 is a high signal followed by a low signal (hi->lo), and a Bit Waveform for a Data 0 is a low signal followed by a high signal (lo->hi). For the OS Weather station each high and low signal duration are both about 430uS. So a full Bit Waveform will last about 860uS. (As will be seen later, the tolerance for decoding these timings is quite wide). 23 | 24 | The cheap 433MHz Rx's available have a simple Automatic Gain Control and Bias Detection built in. The AGC allows the sensitivity to be maximised with low signals, and adjusted back when a stronger 433MHz signal is received. The Bias Detection allows the average of the output signal voltage to be determined and applied to one half of a comparator, the other comparator input has the received signal. With Manchester protocol the on/off ratio is equal, so the Bias Detection will be at the average voltage, ie approximately half way, consequently the transitions of the signal from on to off and back again, can produce quite clean, symmetrical logic signals with simple circuitry. This Manchester decoding program relies on the timing of transitions, rather than the exact shape of the waveform (ie again it does not have to be a very clean square wave shape to work, as long as the transitions are accurate). 25 | 26 | In other words, the decoding program needs to be able to determine whether it has seen a 430uS 'no signal' followed by a 430uS 'on signal' and so has received a 0, OR has received a 430uS 'on signal" followed by a 430uS 'no signal' and so received a 1. 'on signal' makes the Rx amplifier output go 'hi' and 'no signal' makes the Rx output go 'lo'. 27 | 28 | #### How is this decoded? 29 | 30 | The first very important prior knowledge to have is which of the two polarity conventions for Manchester encoding this system uses. Arguments are put forward for both possibilities as being correct, but either polarity works as good as the other, and with a simple audio sampler, such as Audacity, is simple to work out. The transition polarity used by OS is that a Data 1 is hi->lo and Data 0 is lo->hi. 31 | 32 | However any hi->lo transition could be either the middle of a Data 1 Bit Waveform and mean a 1 has been sent, or possibly just the hi->lo transition between two Bit Waveforms, and as such not indicate anything. Similarly any lo->hi could indicate the middle of a Data 0 Bit, or again just a meaningless transition between two Bit Waveforms. The following diagram illustrates the two Bit Waveforms. When these are connected up into a bit stream, then the red dots may or may not require transitions to connect them for the Bit Waveforms to reflect the data bits. A simple example is provided underneath with the 4 possible bit sequences (10,00,01,11) shown and the incidental transitions marked in blue. If you are writing a Manchester Encoded transmitter then this is all you need to know to get started. 33 | 34 | ![alt text](images/bitwaveform.png?raw=true "Manchester Encoding Bit Waveform") Diag 1 35 | 36 | Curiously a long string of Data 1's will have the same looking waveform as a long string of Data 0's. Whether they are 1's or 0's depends on where we begin to analyse, we would need some sort of marker, or unambiguous beginning point. And also surprising to begin with, is the sequences of data bits 1 to 0, and 0 to 1, do not have any signal change between Bit Waveforms. More information on that later... 37 | 38 | Critically the only guaranteed meaningful regular transition of states occurs in the middle of the Bit Waveform. Consequently is essential to concentrate the decoding algorithm around the center of the Bit Waveform to keep it properly synchronised. 39 | 40 | #### RF practicalities... 41 | 42 | Part of the practical application of the Manchester protocol combined with simple 433MHz Tx/Rx combo's is to use a header sequence of bits, usually 30 or so 1's. This quickly stablizes the Rx AGC and establishes the Bias Detection point so the simple 433MHz Rx has a good chance of settling down and producing a clean logic waveform after say 10 on/off transmissions. The decoding program can then sample the Bit Waveform by synchronising (ie looping and waiting) the program to an hi->lo transition, which is the midway point of a Bit Waveform for a 1. This polarity is how it works for the OS protocol, hi->lo==1, lo->hi==0 (Wikipedia says the opposite polarity convention is also used by other systems, and both are just as valid). 43 | 44 | ![alt text](images/AGC_Starting.png?raw=true "Rx AGC kicking in") Graphic 1: The AGC is stabilising fairly quickly here. (Audacity Sample) 45 | 46 | This Diagram 2 below is showing a stream on 1's as the header. The algorithm is expecting a stream of Data 1's and to begin with, and is looking for any hi to lo transition on the Rx and assuming it is the middle of a Data 1 bit Waveform. After detecting any hi->lo transition it begins to check the subsequent waveform. Graphic 1 is sections A,B&C in Diagram 2. 47 | 48 | ![alt text](images/header.png?raw=true "Header Manchester Encoding") Diag 2 49 | 50 | Diagram 2 illustrates the process of detecting a signal and locking into the data in the packet. From the lefthand side, A is showing static, or noise, and when a signal begins in B the AGC on the 433MHz receiver is stabilizing. Note that early on the program will try to lock onto the negative going edge as shown with the downward black arrow at the start of Phase C. Phase C is the stream of 1's known as the header. Here I have illustrated that receiving fifteen 1's will indicate we have a valid header. Phase D indicates that we have more header bits than we need (this allows for some drift about the AGC starting point). So any excess 1's are then dumped until the bit pattern changes, when at Phase E the bit pattern for 0 arrives. From then on, Phase F (the remainder of the packet, NB not all shown) any number of data bits, 1&0's, can be received. Sometimes the E&F Phase are within the Byte pattern, and sometimes the start bit, E is excluded and it just flags the start, and the bytes begin at Phase F. 51 | 52 | #### Extracting data from the bit stream 53 | 54 | How does it know they are properly formed and timed Bit Waveforms? (Please refer to diagram 3 below, NB 7 Bit Waveforms are shown, only alternative ones are labelled). To filter out noise, the input is sampled until a hi->lo event is detected eg at (B) and then again the program re-samples the Rx output about a 1/4 of a Bit Waveform later at (E), to see if it is still lo. If is lo (as the diagram shows) then it is possibly a genuine middle of a 1 Bit Waveform, however if it is not a lo then it was not a genuine hi->lo middle of a 1 Bit Waveform, and the algorithm begins the search for another hi->lo transition all over again. However if this preliminary test is true, it has possibly sampled a midpoint of a 1, so it waits for another half a Bit Waveform (F). This timing is actually 1/4 of the way into the next Bit Waveform, and because we know we are looking for another 1, then the signal should have gone hi by then. If is not hi, then the original sample, that was possibly the mid point of a 1 Bit Waveform is rejected, as overall, it has not followed the 'Bit Waveform rules', and the search (looping) for then next hi->lo transition begins at the start, all over again. Here is a diagram to highlight the previous ideas. 55 | 56 | ![alt text](images/manchester.png?raw=true "Manchester Encoding of data bits") Diag 3 57 | 58 | The pink lines are the signal arriving from the 433MHzRx. The long vertical blue lines (eg at A & C) are indicating the start and end of some Bit Waveforms. These are partially covered by the pink signal trace if they coincide. This diagram show 7 bit patterns. B is positioned at the middle of a Bit Waveform, and these are also indicated by the M's. The data contained in each bit pattern is determined by the direction of the transition at M, the middle of the Bit Waveform, and not by what happens at the finish and start of each Bit Waveform. 59 | 60 | The Diagram 3 shows the four possible bit sequences, 0->1, 1->0, 1->1, 0->0 and what happens in between each combinations of Bit Waveforms (marked by the orange #1-7 Bit Waveforms). 61 | 62 | This simple filtering (delay-check-delay-check etc) allows the program to detect and count the number of successfully detected Data 1's received, and once a minimum has been counted in sequence, then the program can assume it has a valid header coming in. This sampling, by looking for transitions and waiting periods of time to sample, is also applied equally to all subsequent 1's and 0's received and can eliminate badly formed packets if the waveform pattern of the Manchester encoding is not followed (say due to interference or a weak signal). Hence it forms a simple but effective digital filter that greatly reduces spurious results from random or unwanted 433MHz signals. 63 | 64 | The transitions at the orange 1-7 do not carry data, but if transitions do occur they are to make sure the subsequent transitions that must occur at the Mid points match the data bit stream correctly (you can think of 1-7 setting up the data transition for the next Mid). The critical aspect of this processing is that this program is always syncing to the Mid point of the Bit Waveform, where the critical data indicating transitions occur. This makes the decoding of the bit stream very tolerant to any errors (data rate wow or flutter in old terms) in sampling the waveforms. This allows the calculations between receiving bits to take different lengths of time (after the middle M of the Bit Waveform) and still not effect the reliability. The delays can be altered plus or minus 15% (or more) and still get reliable reception. 65 | 66 | The synchronising 0 bit and may or may not be included in the byte boundaries. Some implementations send a single 0 bit then all the bytes of information, whereas the OS example we are dealing with here just makes sure the first bit in the first byte sent, is always a 0. 67 | 68 | #### Capturing the data 69 | 70 | Once this "0" is detected then the data stream can be considered synchronised with the detector. This program to decode the Oregon Scientific Weather Station's sensors then decodes a sequence of received bytes that have data encoded in both straight binary and other times, BCD. The number of bytes per packet for each transmitter can also vary. The program must be able to recognise each transmitter early and change the number of subsequent bytes expected for each type. To do this the program begins downloading all bytes (more on that later) and identifies the ID bytes within the first 2 bytes, and when a particular sensor is detected it immediately sets the exact number of bytes expected. So temp/Humidity decoding requires 10 bytes (but only uses 9), Anemometer decoding requires 10 bytes and uses them all, and the Rainfall sensor decoding requires 11 bytes but only needs the four most significant bits of the last byte. If 11 bytes were accepted all the time, the Rainfall would work, but the other two would fail as their signals return to random noise after 10 bytes and this would cause them to be rejected. 71 | 72 | Once a data packet is received it is given a checksum check before being declared valid. We now need to diverge, and return back to the way the bits arrive in the data stream and how they are best stored. The easiest way to explain this is to give an example. Let's number the raw Rf incoming bits in order, zero arrives first - 73 | 74 | `0 1 2 3 4 5 6 7` , and this how it is best to rearrange those bits for OS comptibility 75 | 76 | `3 2 1 0 7 6 5 4` , so the first bit 0 is actually moved to the fourth position, the next bit 1 is moved to the third position, and so on, and, as this wraps around, all the incoming bits are stored in new positions in a temporary byte, and then stacked into a byte array. It essentially reverses the place values inside each nybble. This properly reflects the environmental values sampled. 77 | 78 | Why Oregon Scientific chose this rearrangement is best left up to them to explain, but applying this swapping of positions makes all the data in the stored bytes array so much more logical as well. Binary numbers are found in the correct ascending order etc. Plus when it comes to the Checksum, it is also simpler to calculate as well. Take each 4 bit nybble in the data packet (excluding the checksum byte) and add them up as an 8 bit result. This will result in a byte that can be compared to the last byte in the packet, the checksum byte. The number of nybbles for Temp/Humidity is 16, Anemometer 18, and rainfall 19 (NB rainfall Check Sum byte, is made up of nybbles 20 and 21, ie it bridges the byte boundary). 79 | 80 | #### Processing and exporting the data 81 | 82 | Once the bits and bytes are stored in this fashion then the checksum becomes trivial, just add up the nybbles and compare to the checksum byte. Reject any packet that does not workout. A simple checksum like this is prone to substitution errors getting through undetected (ie one byte has an error bit, but is balanced out by an inverted error bit in another byte with the same place value. Cyclic redundancy methods for checking data validity are much more robust than the simple arithmetic checksums, but they are not used on the OS Sensors. However by the time the two bytes for the sensor type ID, and the rolling ID code for a particular sensor are put aside, the critical data that changes is down to 5-7 bytes, and probably the checksum for such a small sample is quite acceptable (though if you intend to use any of these sensors for mission critical stuff you may like to disagree with that opinion). Fortunately for me it was easy to program. 83 | 84 | Once the bytes for a particular valid packet are stored in the array, they are processed. Some have conversion factors applied, such as binary weighted data for Rain is turned into floating point decimals, and the Anemometer wind speed is converted from metres per second to kilometers per hour. Others such as relative Humidity are provided directly in two BCD characters. Eventually a selection of the numerical data is formatted into ASCII values in a CSV string and sent out via the serial port (over the USB connection). The program has a 1 second interrupt that is used to to send the CSV string every 60 seconds. My Python program on the www server then further processes this string into averages and graphs etc 85 | 86 | Before any CSV is sent though, this program checks that it has received a valid sample from each of the three sensors, and only begins sending CSV's when Therm/Hum, Anemometer, UV and Rainfall have all been logged in for valid values. This avoids some values being valid and other being at zero at start up. 87 | 88 | This description should give you a good idea of how the OS V3.0 protocol works and how my program tackles decoding that protocol. It is not very sophisticated, when it is all shown now, but was quite a headache to work through originally. The Oregon Scientific Sensors are a good balance of quality engineering, accessible protocols and reasonable price. Really getting a grip on the Manchester protocol was been a major hurdle, but now is well under control. I am hoping my program and this presentation above will help others tackle this protocol in their own projects. 89 | 90 | ![alt text](images/WMR86.JPG?raw=true "Oregon Scientific WMR86 Sensors") http://au.oregonscientific.com/ WMR86 to get your own :-) 91 | 92 | #### Manchester Debugger 93 | 94 | There is now a support Debug Program you can use to develop your own applications in this repository. Edit three values, recipe like, and you could be receiving Manchester encoded bytes in next to no time. Then over to you! Check it out. 95 | 96 | I hope you enjoy using them as much as I do, cheers, Rob 97 | 98 | 99 | Acknowledgements: There are many people, and other publications, I have referenced that have assisted greatly in arriving at this solution and these are listed in the documentation of this program, and explanations given to where they are relevant. 100 | 101 | Weather Station Extension 102 | ========================= 103 | ### Extension of the Project 104 | Several features became desirable after the original system had been running for several years. 105 | The main concern was a lack of Battery Level indicators and also no direct feedback from the Arduino module 106 | of the signals being received. The solution was tackled in two ways, but involving similar parts of the program. 107 | 108 | ### Battery Levels 109 | 110 | The Battery Levels were a harder one to crack. I checked the WMR86 manual for mention of a battery level indicator and also checked the LCD screen supplied with it for any indicators that referred to battery levels. 111 | I could not find any. So I devised this strategy. Should any sensor not be logged within a minute (ie between sending strings of data to the WWW server) then a number is incremented for that Sensor. As soon as that 112 | sensor is detected the number is reset to Zero. However if the sensor is not detected for 20 minutes then a flag bit is set for that Sensor. This "detection" number is sent to the WWW server along with all the other 113 | data every minute. If it remains 0 then the sensors are OK, if it is non-zero, then it means one of the three sensors have experienced a "continuous down time" of at least 20 minutes. The WWW server can check this and send out an Email 114 | at midnight of that day warning of which sensor(s) have been missed. However the warning could arise for a number of reasons, the first obvious one is the batteries are going flat and the second is that something maybe 115 | effecting either the transmitter or receiver. Eg the Antenna may have changed position on the Rx and suddenly 2 outof the three are being received. Or maybe some object has been placed bewteen the Tx and Rx and 116 | that has knocked out the signal. However, just because the signal drops out it may not be because of the Batteries. The system should be sensitive enough to warn of early stages of battery failure. Even if it begins by 117 | small periods of time overnight in the cool night air. 118 | 119 | ### Status Indicator 120 | The RGB LED should give a quick indication if an antenna position change for example is still allowing the sensors to be received. 121 | An RGB LED was added to the board to indicate which sensor transmitter was detected. By combining Red, Blue and/or Green I could easily produce 8 recognisable states from the LED. 122 | Simply observing the board would indicate that the different sensors were being logged. This gave a quick indication, for example after a restart that the Arduino was functioning properly, 123 | and so were the 4 Oregon sensors 124 | #### Colour coding was: 125 | #### Red = Temperature and Humidity 126 | #### Green = Wind Direction and Speed 127 | #### Blue = Rain Gauge 128 | #### Yellow = UV Light Sensor 129 | #### Purple = Experimental 130 | 131 | ![alt text](images/RGB_Status.jpg?raw=true "RGB Status LED") RGB Status LED 132 | 133 | 134 | ### Software Version 135 | 136 | The version to use these features is MainWeather_09.ino. Please note the order of the sensors in the output string has also been changed. So if you have a system reading this string and processing it from the previous version 137 | you will have to alter it as well to use this program as it is. 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /SampleDump.txt: -------------------------------------------------------------------------------- 1 | Below is a dump of the output from an Arduino running the DebugAuto.ino program. 2 | It was made using a Dorji 433MHz receiver and there were three separate transmitters 3 | Rainfall (repeats 47sec approx), Wind Speed+Direction (repeats 14sec approx) and 4 | Temperature+Humidity (repeats 50secs approx). 5 | 6 | These create plenty of traffic to observe and all three work well with delays at 7 | sDelay = 230-260uSec so the timing is very tolerant, yet robust. In this example 8 | run there is a requirement of 15 header hits, and 9 bytes for each packet to run the 9 | system a bit harder to show up any bad timings up quicker. Early samples show only 10 | one transmitter being received, and last samples show bad data, so best to pick 11 | around the middle of the best packets, in this case 245uS. 12 | 13 | If you use this program I suggest you start off with a lower value for header hits, 14 | say 10 and maybe about 5 bytes. You can make it more demanding when you get your 15 | sDelay in range and know what polarity is working, and trying them in the 16 | DebugManchester.ino version and building your own system from there. 17 | 18 | If you wanted a more critical test and you know how your system validates packets eg 19 | multiple repeats or checksums, then you could program that into the DebugAuto.ino 20 | version so that any errors are also reported. The packets below do not have any 21 | checking on them. All this means is that the program has considered that they have 22 | met the Manchester waveform satisfactorily over duration of the packet. There 23 | could easily bit errors at either end of the delay setting and of course from time 24 | to time just from normal Rx errors from noise etc 25 | 26 | Here we go.... 27 | 28 | Debug Manchester Version GIT-Hub V03 29 | Using a delay of 1/4 bitWaveform 200 uSecs 1/2 bitWaveform 400 uSecs 30 | Positive Polarity lo->hi=1 31 | 15 bits expected for a valid header 32 | All bits inside the Packet 33 | 80 initial timeout to detect wrong Polarity 34 | P 00 00001111 01 22223333 02 44445555 03 66667777 04 88889999 05 AAAABBBB 06 CCCCDDDD 07 EEEEFFFF 08 00001111 90 22223333 35 | sDelay =200 36 | Nos Hits =0 37 | sDelay =210 38 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 98 10011000 04 00000100 80 10000000 39 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 02 00000010 80 10000000 40 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 88 10001000 02 00000010 80 10000000 41 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 90 10010000 02 00000010 80 10000000 42 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 88 10001000 04 00000100 80 10000000 43 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 04 00000100 80 10000000 44 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 E0 11100000 04 00000100 80 10000000 45 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 0C 00001100 80 10000000 46 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 0C 00001100 80 10000000 47 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 28 00101000 0C 00001100 80 10000000 48 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 08 00001000 00 00000000 80 10000000 49 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 04 00000100 00 00000000 80 10000000 50 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 88 10001000 00 00000000 80 10000000 51 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 88 10001000 08 00001000 80 10000000 52 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 08 00001000 80 10000000 53 | Nos Hits =15 54 | sDelay =220 55 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 60 01100000 08 00001000 80 10000000 56 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 57 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 A0 10100000 08 00001000 80 10000000 58 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 48 01001000 00 00000000 80 10000000 59 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 00 00000000 80 10000000 60 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E8 11101000 00 00000000 80 10000000 61 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 62 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 63 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 28 00101000 00 00000000 80 10000000 64 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 18 00011000 0C 00001100 80 10000000 65 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 A8 10101000 0C 00001100 80 10000000 66 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 67 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 0C 00001100 80 10000000 68 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 69 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 0C 00001100 80 10000000 70 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E8 11101000 02 00000010 80 10000000 71 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 72 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 08 00001000 02 00000010 80 10000000 73 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 74 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 60 01100000 02 00000010 80 10000000 75 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 02 00000010 80 10000000 76 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 88 10001000 09 00001001 00 00000000 77 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 78 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 09 00001001 00 00000000 79 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 80 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 88 10001000 09 00001001 00 00000000 81 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 18 00011000 09 00001001 00 00000000 82 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 83 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 09 00001001 00 00000000 84 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 04 00000100 80 10000000 85 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 86 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 A8 10101000 04 00000100 80 10000000 87 | Nos Hits =32 88 | sDelay =230 89 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 04 00000100 80 10000000 90 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 91 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E8 11101000 04 00000100 80 10000000 92 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 04 00000100 80 10000000 93 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 94 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 88 10001000 04 00000100 80 10000000 95 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 96 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 04 00000100 80 10000000 97 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 28 00101000 04 00000100 80 10000000 98 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 99 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 06 00000110 03 00000011 88 10001000 08 00001000 80 10000000 100 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 101 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 A8 10101000 08 00001000 80 10000000 102 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 88 10001000 08 00001000 80 10000000 103 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 08 00001000 80 10000000 104 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 105 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 90 10010000 08 00001000 80 10000000 106 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 107 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 04 00000100 80 10000000 108 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 18 00011000 04 00000100 80 10000000 109 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 04 00000100 80 10000000 110 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 111 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 01 00000001 88 10001000 08 00001000 E3 11100011 32 00110010 112 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 04 00000100 80 10000000 113 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 48 01001000 0C 00001100 80 10000000 114 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 90 10010000 0C 00001100 80 10000000 115 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 116 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 90 10010000 0C 00001100 80 10000000 117 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 01 00000001 88 10001000 08 00001000 E3 11100011 32 00110010 118 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 0C 00001100 80 10000000 119 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 48 01001000 09 00001001 00 00000000 120 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 90 10010000 09 00001001 00 00000000 121 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 122 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 09 00001001 00 00000000 123 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 01 00000001 88 10001000 08 00001000 E3 11100011 32 00110010 124 | Nos Hits =35 125 | sDelay =240 126 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 09 00001001 00 00000000 127 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 00 00000000 80 10000000 128 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 129 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 28 00101000 00 00000000 80 10000000 130 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 01 00000001 88 10001000 08 00001000 E3 11100011 32 00110010 131 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 00 00000000 80 10000000 132 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E0 11100000 00 00000000 80 10000000 133 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 134 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 88 10001000 00 00000000 80 10000000 135 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 60 01100000 00 00000000 80 10000000 136 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 137 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 A0 10100000 00 00000000 80 10000000 138 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 60 01100000 00 00000000 80 10000000 139 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 140 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 60 01100000 00 00000000 80 10000000 141 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 08 00001000 06 00000110 00 00000000 142 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 143 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 A0 10100000 06 00000110 00 00000000 144 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 145 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 60 01100000 06 00000110 00 00000000 146 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 06 00000110 00 00000000 147 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 08 00001000 06 00000110 00 00000000 148 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 149 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 150 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 C8 11001000 06 00000110 00 00000000 151 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 88 10001000 06 00000110 00 00000000 152 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 08 00001000 06 00000110 00 00000000 153 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 03 00000011 03 00000011 C8 11001000 06 00000110 00 00000000 154 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 155 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 156 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 08 00001000 80 10000000 157 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E0 11100000 08 00001000 80 10000000 158 | Nos Hits =32 159 | sDelay =250 160 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 A8 10101000 08 00001000 80 10000000 161 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 162 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 08 00001000 E3 11100011 A2 10100010 163 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 98 10011000 08 00001000 80 10000000 164 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 A8 10101000 0A 00001010 80 10000000 165 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 C8 11001000 0A 00001010 80 10000000 166 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 68 01101000 0A 00001010 80 10000000 167 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 168 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 00 00000000 48 01001000 00 00000000 E0 11100000 1C 00011100 169 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0E 00001110 03 00000011 68 01101000 0A 00001010 80 10000000 170 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 C8 11001000 02 00000010 80 10000000 171 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 90 10010000 02 00000010 80 10000000 172 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 173 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 60 01100000 02 00000010 80 10000000 174 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 175 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 02 00000010 80 10000000 176 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 08 00001000 80 10000000 177 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 178 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 C8 11001000 08 00001000 80 10000000 179 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 08 00001000 80 10000000 180 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 181 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 A8 10101000 08 00001000 80 10000000 182 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 18 00011000 08 00001000 80 10000000 183 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 184 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 0A 00001010 80 10000000 185 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 09 00001001 88 10001000 08 00001000 E3 11100011 B2 10110010 186 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E0 11100000 0A 00001010 80 10000000 187 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 18 00011000 0A 00001010 80 10000000 188 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 189 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 0A 00001010 80 10000000 190 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 44 01000100 0C 00001100 80 10000000 191 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 192 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 84 10000100 0C 00001100 80 10000000 193 | Nos Hits =33 194 | sDelay =260 195 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 44 01000100 0C 00001100 80 10000000 196 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 98 10011000 06 00000110 80 10000000 197 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 01 00000001 88 10001000 08 00001000 E3 11100011 32 00110010 198 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 44 01000100 06 00000110 80 10000000 199 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 200 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 88 10001000 06 00000110 80 10000000 201 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 84 10000100 06 00000110 80 10000000 202 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 06 00000110 80 10000000 203 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 204 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E8 11101000 0A 00001010 80 10000000 205 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 0A 00001010 80 10000000 206 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 98 10011000 0A 00001010 80 10000000 207 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 208 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 88 10001000 0A 00001010 80 10000000 209 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 01 00000001 88 10001000 08 00001000 E3 11100011 32 00110010 210 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 2C 00101100 09 00001001 80 10000000 211 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 A8 10101000 09 00001001 80 10000000 212 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 84 10000100 09 00001001 80 10000000 213 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 214 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 215 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 28 00101000 09 00001001 80 10000000 216 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 28 00101000 0E 00001110 80 10000000 217 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 18 00011000 0E 00001110 80 10000000 218 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 219 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 C8 11001000 0E 00001110 80 10000000 220 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 221 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 0E 00001110 80 10000000 222 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 68 01101000 0C 00001100 80 10000000 223 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 224 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 C8 11001000 0C 00001100 80 10000000 225 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0D 00001101 03 00000011 A8 10101000 0C 00001100 80 10000000 226 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 227 | Nos Hits =32 228 | sDelay =270 229 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 0D 00001101 03 00000011 24 00100100 0C 00001100 80 10000000 230 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 A8 10101000 0C 00001100 80 10000000 231 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 232 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 E0 11100000 02 00000010 80 10000000 233 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 90 10010000 02 00000010 80 10000000 234 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 235 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 C8 11001000 02 00000010 80 10000000 236 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 60 01100000 00 00000000 80 10000000 237 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 28 00101000 00 00000000 80 10000000 238 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 239 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 240 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 90 10010000 00 00000000 80 10000000 241 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 90 10010000 09 00001001 00 00000000 242 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 243 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E8 11101000 09 00001001 00 00000000 244 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 245 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 05 00000101 03 00000011 60 01100000 09 00001001 00 00000000 246 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 C8 11001000 09 00001001 00 00000000 247 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 48 01001000 09 00001001 00 00000000 248 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 249 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 250 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 01 00000001 03 00000011 A0 10100000 00 00000000 80 10000000 251 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E8 11101000 00 00000000 80 10000000 252 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 E0 11100000 00 00000000 80 10000000 253 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 254 | 1 5F 01011111 14 00010100 28 00101000 CB 11001011 0E 00001110 88 10001000 08 00001000 E3 11100011 D2 11010010 255 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 90 10010000 0C 00001100 80 10000000 256 | Nos Hits =27 257 | sDelay =280 258 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 259 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F8 11111000 AF 10101111 F6 11110110 FE 11111110 260 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F9 11111001 6F 01101111 F6 11110110 FE 11111110 261 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F9 11111001 6F 01101111 F6 11110110 FF 11111111 262 | 1 58 01011000 91 10010001 20 00100000 D9 11011001 09 00001001 03 00000011 68 01101000 04 00000100 80 10000000 263 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 264 | 0 41 01000001 D7 11010111 AE 10101110 69 01101001 F2 11110010 EF 11101111 EE 11101110 39 00111001 5B 01011011 265 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 F5 11110101 F8 11111000 EF 11101111 F6 11110110 FE 11111110 266 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F9 11111001 6F 01101111 F6 11110110 FE 11111110 267 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F8 11111000 EF 11101111 E6 11100110 FF 11111111 268 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F9 11111001 EF 11101111 E6 11100110 FE 11111110 269 | 1 17 00010111 C5 11000101 0A 00001010 32 00110010 C3 11000011 A2 10100010 02 00000010 38 00111000 F4 11110100 270 | 1 01 00000001 62 01100010 44 01000100 83 10000011 64 01100100 24 00100100 0F 00001111 80 10000000 38 00111000 271 | 1 54 01010100 98 10011000 20 00100000 A0 10100000 00 00000000 00 00000000 0E 00001110 2A 00101010 C8 11001000 272 | Nos Hits =14 273 | sDelay =290 274 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 E3 11100011 F8 11111000 3F 00111111 FE 11111110 FE 11111110 275 | 0 41 01000001 D7 11010111 AE 10101110 69 01101001 E2 11100010 EF 11101111 EE 11101110 38 00111000 5B 01011011 276 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F9 11111001 EF 11101111 FE 11111110 FF 11111111 277 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F8 11111000 EF 11101111 FE 11111110 FE 11111110 278 | 0 45 01000101 BB 10111011 7C 01111100 9B 10011011 FB 11111011 F2 11110010 7F 01111111 FD 11111101 FD 11111101 279 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F9 11111001 FF 11111111 DB 11011011 FC 11111100 280 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F8 11111000 6F 01101111 ED 11101101 FF 11111111 281 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F8 11111000 DF 11011111 ED 11101101 FE 11111110 282 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F9 11111001 FF 11111111 ED 11101101 FD 11111101 283 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F8 11111000 5F 01011111 ED 11101101 FC 11111100 284 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 ED 11101101 F8 11111000 EF 11101111 EE 11101110 FE 11111110 285 | 0 4E 01001110 DD 11011101 BE 10111110 4D 01001101 FD 11111101 F8 11111000 EF 11101111 EE 11101110 FF 11111111 286 | Nos Hits =12 287 | sDelay =300 288 | Nos Hits =0 289 | sDelay =310 290 | Nos Hits =0 291 | sDelay =320 292 | Nos Hits =0 293 | sDelay =330 294 | 295 | -------------------------------------------------------------------------------- /MainWeather_09/MainWeather_09.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Oregon Scientific Weather Station and Manchester Decoding, (reading by delay rather than interrupt) 3 | Rob Ward 2015 4 | This example code is in the public domain. 5 | Use at your own risk, I will take no responsibility for any loss whatsoever for its deployment. 6 | Visit https://github.com/robwlakes/ArduinoWeatherOS for the latest version and 7 | documentation. Filename: MainWeather_09.ino 8 | 9 | Main_Weather, 24 Dec 2017 (works with Protocol Version 3.0) 10 | 11 | The aim of this version is use the new Manchester decoding routine and incorporate detection 12 | of a Oregon compatible sensor built to cover novel environmental sensors. 13 | For example we could have remote temperature sampling, bat detectors, lightning detectors, 14 | solar power detectors, and earthquake detectors, all reporting back to the WWW server. 15 | The advantage is that by using the Oregon Scientific Protocol a person can incorporate 16 | their excellent weather sensors into the overall design and largely keep the Arduino Rx software 17 | design relatively simple, ie also use their Manchester implementation for our own sensors. 18 | 19 | It may seem this program is encouraging people to build their own replacements for the 20 | Oregon Scientific sensors, however it is quite the reverse. The program is designed 21 | encourage people to purchase the Oregon gear to get the basic wind, temperature, rain 22 | and humidity sensors and then extend the range with original designs as suggested above. 23 | If the Oregon sensors are not used, then it does not make sense to use their protocol, 24 | and any RF other protocol could be as good, or maybe even better in some circumstances. 25 | 26 | Before I discovered the Oregon products, and designed a receiver for it, I was seriously 27 | considering building my own basic weather sensors. Now I am glad I did not try as the 28 | quality and price of the Oregon sensors are a very good economic and reliable alternative. 29 | 30 | Have we made the Oregon base station with the LCD redundant? Well no, not at all. I have 31 | it in the kitchen and it used by members of the family from day to day and does not take 32 | anything extra to have it performing a useful monitoring job as well. However if you want to 33 | build a system that uses weather data, and combines that with other sensors, and then performs 34 | other tasks such as switching pumps, or moving blinds then adding an Arduino based 433MHz Rx 35 | makes an incredibly versatile platform to work with. 36 | 37 | Have we made the Oregon USB to PC models redundant? Again no, unless you want to take a long time 38 | writing your own logging and graphing routines. This program does not even begin to replace 39 | that level of functionality. 40 | 41 | Have we made the Anywhere OS Internet connection system redundant? Finally, again no! 42 | This simple Arduino system does not report live to the internet and it would also take a lot 43 | of effort to even go close to include the features of the OS internet connection system. 44 | 45 | I would encourage anyone looking for a weather sensor based project to consider the Oregon 46 | Scientific products as they produce a wide range of economically priced but solidly built 47 | weather sensors. The mechanical engineering is very good and hard to replicate by the "Geek 48 | in the Street". However the scope to extend the usefulness of this data in our designs, by 49 | combining it with our own sensors (eg soil moisture, or dew point sensors) is truly amazing. 50 | 51 | Have a look here: http://www.oregonscientificstore.com/ 52 | 53 | So get some OS gear, an Arduino and get into it :-) 54 | 55 | NB: This program only works with Protocol 3.0 sensors. 56 | 57 | Reference Material: 58 | Thanks to these authors, they all helped in some way, especially the last one Brian!!!! 59 | http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf (1) 60 | http://jeelabs.net/projects/cafe/wiki/Decoding_the_Oregon_Scientific_V2_protocol (2) 61 | https://github.com/phardy/WeatherStation (3) 62 | http://lucsmall.com/2012/04/27/weather-station-hacking-part-1/ (4) 63 | http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ (5) 64 | http://lucsmall.com/2012/04/29/weather-station-hacking-part-2/ (6) 65 | http://www.mattlary.com/2012/06/23/weather-station-project/(7) 66 | https://github.com/lucsmall/WH2-Weather-Sensor-Library-for-Arduino (8) 67 | http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com 68 | 69 | Most of all thanks to Oregon Scientific, who have produced an affordable, high quality product. 70 | I can now have my LCD Home Base in the kitchen to enjoy, with the Arduino in the garage also 71 | capturing data for WWW Weather pages. Lovely!!!! http://www.oregonscientific.com 72 | Very Highly recommended equipment. Rob Ward 73 | 74 | The Oregon Scientific UV Detector UVN-800 has been added into to this code as a sensor. 75 | It appears to have power problems and does not seem to connect as easy as the other sensors. 76 | However I was foreced to buy it as I could not find a UV transparent material for a window 77 | to use a cheaper Adafruit sensor (Analog UV Light Sensor Breakout-GUVA-S12SD). Otherwise 78 | I was not able to protect the circuit. 79 | SI1145 Digital UV Index / IR / Visible Light Sensor could also be useful as it reports SPI 80 | and can actually give predicted UV levels by measuring Visible light and IR. 81 | 82 | Special thanks to Darko in Italy who corrected an error when decoding negative temperatures. Grazie ragazzi!!! 83 | 84 | The Future: 85 | The Solar Power sensor (a silicon cell's open voltage under sunlight) has been added 86 | A lightning strike detector has been added with nos of strikes per 45 seconds 87 | */ 88 | 89 | #include "DHT.h" 90 | #include 91 | #include 92 | 93 | #define DHTTYPE DHT22 // DHT 22 (AM2302) 94 | #define DHTPIN 2 // what pin we're connected to 95 | 96 | Adafruit_BMP085 bmp; 97 | DHT dht(DHTPIN, DHTTYPE); 98 | 99 | //Interface Definitions 100 | int RxPin = 8; //The number of signal from the Rx 101 | int ledPin = 13; //The number of the onboard LED pin 102 | int RedPin = 7; //The Red LED, turn on for a valid Temp sensor packet 103 | int GrePin = 6; //The Green LED, turn on for a valid Wind sensor packet 104 | int BluPin = 5; //The Blue LED, turn on for a valid Rain sensor packet 105 | 106 | // Variables for Manchester Receiver Logic: 107 | word sDelay = 250; //Small Delay about 1/4 of bit duration try like 250 to 500 108 | word lDelay = 500; //Long Delay about 1/2 of bit duration try like 500 to 1000, 1/4 + 1/2 = 3/4 109 | byte polarity = 1; //0 for lo->hi==1 or 1 for hi->lo==1 for Polarity, sets tempBit at start 110 | byte tempBit = 1; //Reflects the required transition polarity 111 | byte discards = 0; //how many leading "bits" need to be dumped, usually just a zero if anything eg discards=1 112 | byte discNos = 0; //Counter for the Discards 113 | boolean firstZero = false; //has it processed the first zero yet? This a "sync" bit. 114 | boolean noErrors = true; //flags if signal does not follow Manchester conventions 115 | //variables for Header detection 116 | byte headerBits = 15; //The number of ones expected to make a valid header 117 | byte headerHits = 0; //Counts the number of "1"s to determine a header 118 | //Variables for Byte storage 119 | byte dataByte = 0; //Accumulates the bit information 120 | byte nosBits = 0; //Counts to 8 bits within a dataByte 121 | byte maxBytes = 9; //Set the bytes collected after each header. NB if set too high, any end noise will cause an error 122 | byte nosBytes = 0; //Counter stays within 0 -> maxBytes 123 | //Bank array for packet (at least one will be needed) 124 | byte manchester[12]; //Stores manchester pattern decoded on the fly 125 | //Oregon bit pattern, causes nibble rotation to the right, ABCDabcd becomes DCBAdcba 126 | byte oregon[] = { 127 | 16, 32, 64, 128, 1, 2, 4, 8 128 | }; 129 | byte csIndex = 0; //counter for nibbles needed for checksum 130 | //Weather Variables 131 | byte quadrant = 0; //used to look up 16 positions around the compass rose 132 | double avWindspeed = 0.0; 133 | double gustWindspeed = 0.0; //now used for general anemometer readings rather than avWindspeed 134 | float rainTotal = 0.0; 135 | float rainRate = 0.0; 136 | double temperature = 0.0; 137 | int humidity = 0; 138 | double intTemp = 0; 139 | double intHumi = 0; 140 | double intPres = 0; 141 | byte intSolar = 0; //eg Solar power 142 | byte intLightning = 0; //eg Lightning Strikes 143 | byte intUV = 0; //eg UV Light Levels 144 | const char windDir[16][4] = { 145 | "N ", "NNE", "NE ", "ENE", "E ", "ESE", "SE ", "SSE", "S ", "SSW", "SW ", "WSW", "W ", "WNW", "NW ", "NNW" 146 | }; 147 | 148 | byte scan = 0; // &7!=0 means that all three sensors has been detected, so it reports all three with meaningful figures first up (not the latest addition though) 149 | byte seconds = 0; // Counter to trigger the 60 seconds worth of data. 150 | byte batStat = 0; // bit1= temp, bit2=wind bit3=rain, bit4=exp if true then that sensor has not been logged for 20 minutes, its battery probably getting flat 151 | byte logTemp = 0; // Counter for number of minutes a sensor reading is missed 152 | byte logWind = 0; // Counter for number of minutes a sensor reading is missed 153 | byte logRain = 0; // Counter for number of minutes a sensor reading is missed 154 | byte logUV = 0; // Counter for number of minutes a sensor reading is missed 155 | byte logExp = 0; // Counter for number of minutes a sensor reading is missed 156 | int aday = 0; // Counts the number of minutes in a day and clears battery status every 24hrs 157 | 158 | void setup() { 159 | Serial.begin(115200); 160 | pinMode(RxPin, INPUT); 161 | pinMode(ledPin, OUTPUT); 162 | //Strobe colours in to indicate a reboot, TriColour LED is Common Anode, so requires active low to trigger. 163 | pinMode(RedPin, OUTPUT); 164 | pinMode(GrePin, OUTPUT); 165 | pinMode(BluPin, OUTPUT); 166 | //make the RGB LED flash a signature heartbeat on start up 167 | digitalWrite(RedPin, 1); 168 | digitalWrite(GrePin, 0); 169 | digitalWrite(BluPin, 0); //Flash Cyan 170 | delay(1000); 171 | digitalWrite(RedPin, 0); 172 | digitalWrite(GrePin, 0); //Flash Yellow 173 | digitalWrite(BluPin, 1); 174 | delay(1000); 175 | digitalWrite(RedPin, 0); 176 | digitalWrite(GrePin, 1); 177 | digitalWrite(BluPin, 0); //Flash Purple 178 | delay(1000); 179 | digitalWrite(BluPin, 1); //all off 180 | digitalWrite(RedPin, 1); 181 | delay(1000); 182 | /* 183 | // Enable these if attempting to debug the program or the circuit 184 | Serial.println("Debug Manchester Version 18"); 185 | Serial.print("Using a delay of 1/4 bitWaveform "); 186 | Serial.print(sDelay,DEC); 187 | Serial.print(" uSecs 1/2 bitWaveform "); 188 | Serial.print(lDelay,DEC); 189 | Serial.println(" uSecs "); 190 | if (polarity){ 191 | Serial.println("Negative Polarity hi->lo=1"); 192 | } 193 | else{ 194 | Serial.println("Positive Polarity lo->hi=1"); 195 | } 196 | Serial.print(headerBits,DEC); 197 | Serial.println(" bits expected for a valid header"); 198 | if (discards){ 199 | Serial.print(discards,DEC); 200 | Serial.println(" leading bits discarded from Packet"); 201 | } 202 | else{ 203 | Serial.println("All bits inside the Packet"); 204 | } 205 | 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"); 206 | */ 207 | 208 | //Tutorial on using the BMP05 Press/Temp transducer https://www.sparkfun.com/tutorials/253 209 | bmp.begin(); //start the barometer and temp packages 210 | 211 | // Initialize Timer1 for a 1 second interrupt 212 | // Thanks to http://www.engblaze.com/ for this section, see their Interrupt Tutorial 213 | cli(); // disable global interrupts 214 | TCCR1A = 0; // set entire TCCR1A register to 0 215 | TCCR1B = 0; // same for TCCR1B 216 | // set compare match register to desired timer count: 217 | OCR1A = 15624; 218 | // turn on CTC mode: 219 | TCCR1B |= (1 << WGM12); 220 | // Set CS10 and CS12 bits for 1024 prescaler: 221 | TCCR1B |= (1 << CS10); 222 | TCCR1B |= (1 << CS12); 223 | // enable timer compare interrupt: 224 | TIMSK1 |= (1 << OCIE1A); 225 | // enable global interrupts: 226 | sei(); 227 | } 228 | 229 | //Routine Driven by Interrupt, trap 1 second interrupts, and output every minute 230 | ISR(TIMER1_COMPA_vect) { 231 | seconds++; 232 | if (seconds == 60) { //make 60 for each output 233 | seconds = 0; 234 | //Serial.print("One second ..."); 235 | usbData();//comment this out to temporarily disable data every minute for debug 236 | } 237 | } //end of interrupt routine 238 | 239 | // Main routines, find header, then sync in with it, get a packet, and decode data in it, plus report any errors. 240 | void loop() { 241 | tempBit = polarity ^ 1; 242 | noErrors = true; 243 | firstZero = false; 244 | headerHits = 0; 245 | nosBits = 0; 246 | maxBytes = 15; //too big for any known OS signal 247 | nosBytes = 0; 248 | discNos = discards; 249 | manchester[0] = 0; 250 | digitalWrite(ledPin, 0); 251 | while (noErrors && (nosBytes < maxBytes)) { 252 | while (digitalRead(RxPin) != tempBit) { 253 | } 254 | delayMicroseconds(sDelay); 255 | if (digitalRead(RxPin) != tempBit) { 256 | noErrors = false; 257 | } 258 | else { 259 | byte bitState = tempBit ^ polarity; 260 | delayMicroseconds(lDelay); 261 | if (digitalRead(RxPin) == tempBit) { 262 | tempBit = tempBit ^ 1; 263 | } 264 | if (bitState == 1) { 265 | if (!firstZero) { 266 | headerHits++; 267 | if (headerHits == headerBits) { 268 | //digitalWrite(ledPin,1); 269 | } 270 | } 271 | else { 272 | add(bitState); 273 | } 274 | } 275 | else { 276 | if (headerHits < headerBits) { 277 | noErrors = false; 278 | } 279 | else { 280 | if ((!firstZero) && (headerHits >= headerBits)) { 281 | firstZero = true; 282 | //digitalWrite(ledPin,1); 283 | 284 | } 285 | add(bitState); 286 | } 287 | } 288 | } 289 | } 290 | digitalWrite(ledPin, 0); 291 | } 292 | 293 | void add(byte bitData) { 294 | if (discNos > 0) { 295 | discNos--;//discard bits before real data 296 | } 297 | else { 298 | //the incoming bitstream has bytes placed in reversed nibble order on the fly, then the CS is done. 299 | if (bitData) { 300 | //if it is a '1' OR it in, others leave at a '0' 301 | manchester[nosBytes] |= oregon[nosBits];//places the reversed low nibble, with hi nibble, on the fly!!! 302 | } 303 | //Oregon Scientific sensors have specific packet lengths 304 | //Maximum bytes for each sensor must set once the sensor has been detected. 305 | if (manchester[0] == 0xA2) { 306 | maxBytes = 11; //rain 307 | csIndex = 19; 308 | } 309 | if (manchester[0] == 0xA1) { 310 | maxBytes = 10; //wind 311 | csIndex = 18; 312 | } 313 | if (manchester[0] == 0xAF) { 314 | maxBytes = 9; //temp 315 | csIndex = 16; 316 | } 317 | if (manchester[0] == 0xAD) { 318 | maxBytes = 8; //UV Light Detector 319 | csIndex = 14; //CS byte begins at 14 nibble 320 | } 321 | if (manchester[0] == 0xA3) { 322 | maxBytes = 10; //experimental, Solar,Strikes,UV 323 | csIndex = 18; //CS byte begins at 18 nibble 324 | } 325 | nosBits++; 326 | //Pack the bits into 8bit bytes 327 | if (nosBits == 8) { 328 | nosBits = 0; 329 | nosBytes++; 330 | manchester[nosBytes] = 0; //next byte to 0 to accumulate data 331 | } 332 | //Check the bytes for a valid packet once maxBytes received 333 | if (nosBytes == maxBytes) { 334 | //hexBinDump(); 335 | digitalWrite(ledPin, 1); 336 | //Check Checksum first 337 | if (ValidCS(csIndex)) { 338 | //Process the byte array into Human readable numbers 339 | analyseData(); 340 | } 341 | noErrors = false; //make it begin again from the start 342 | } 343 | } 344 | } 345 | 346 | //Useful to invoke to debug the byte Array 347 | void hexBinDump() { 348 | //Serial.println("T A3 10100011 07 00000111 02 00000010 AA 10101010 F0 11110000 06 00000110 FF 11111111 07 00000111 33 00110011 60 01100000"); 349 | Serial.print("D "); 350 | for ( int i = 0; i < maxBytes; i++) { 351 | byte mask = B10000000; 352 | if (manchester[i] < 16) { 353 | Serial.print("0"); 354 | } 355 | Serial.print(manchester[i], HEX); 356 | Serial.print(" "); 357 | for (int k = 0; k < 8; k++) { 358 | if (manchester[i] & mask) { 359 | Serial.print("1"); 360 | } 361 | else { 362 | Serial.print("0"); 363 | } 364 | mask = mask >> 1; 365 | } 366 | Serial.print(" "); 367 | } 368 | Serial.println(); 369 | } 370 | 371 | //Support Routines for Nybbles and CheckSum 372 | 373 | // http://www.lostbyte.com/Arduino-OSV3/ (9) brian@lostbyte.com 374 | // Directly lifted, then modified from Brian's work. Now nybble's bits are pre-processed into standard order, ie MSNybble + LSNybble 375 | // CS = the sum of nybbles, 1 to (CSpos-1), then compared to CSpos nybble (LSNybble) and CSpos+1 nybble (MSNybble); 376 | // This sums the nybbles in the packet and creates a 1 byte number, and this is compared to the two nybbles beginning at CSpos 377 | // Note that Temp 9 bytes and anemometer 10 bytes, but rainfall uses 11 bytes per packet. (NB Rainfall CS spans a byte boundary) 378 | bool ValidCS(int CSPos) { 379 | boolean ok = false; 380 | byte cs = 0; 381 | for (int x = 1; x < CSPos; x++) { 382 | byte test = nyb(x); 383 | cs += test; 384 | } 385 | //do it by nybbles as some CS's cross the byte boundaries eg rainfall 386 | byte check1 = nyb(CSPos); 387 | byte check2 = nyb(CSPos + 1); 388 | byte check = (check2 << 4) + check1; 389 | /* 390 | if (manchester[0]==0xA2){ 391 | Serial.print(check1,HEX); //dump out the LSNybble Checksum 392 | Serial.print("(LSB), "); 393 | Serial.print(check2,HEX); //dump out the MSNybble Checksum 394 | Serial.print("(MSB), "); 395 | Serial.print(check,HEX); //dump out the Rx'ed predicted byte Checksum 396 | Serial.print("(combined), calculated = "); 397 | Serial.println(cs,HEX); //dump out the calculated byte Checksum 398 | //Serial.print(" "); //Space it out for the next printout 399 | } 400 | */ 401 | if (cs == check) { 402 | ok = true; 403 | } 404 | return ok; 405 | } 406 | // Get a nybble from manchester bytes, short name so equations elsewhere are neater :-) 407 | // Enables the byte array to be indexed as an array of nybbles 408 | byte nyb(int nybble) { 409 | int bite = nybble / 2; //DIV 2, find the byte 410 | int nybb = nybble % 2; //MOD 2 0=MSB 1=LSB 411 | byte b = manchester[bite]; 412 | if (nybb == 0) { 413 | b = (byte)((byte)(b) >> 4); 414 | } 415 | else { 416 | b = (byte)((byte)(b) & (byte)(0xf)); 417 | } 418 | return b; 419 | } 420 | 421 | //enable debug dumps if formatted data required 422 | void analyseData() { 423 | if (manchester[0] == 0xaf) { //detected the Thermometer and Hygrometer (every 53seconds) 424 | scan = scan | 1; 425 | logTemp = 0; //reset missing reads to zero 426 | thermom(); 427 | //dumpThermom(); 428 | digitalWrite(RedPin, 0); //Temperature is Red 429 | digitalWrite(GrePin, 1); 430 | digitalWrite(BluPin, 1); 431 | 432 | } 433 | if (manchester[0] == 0xa1) { //detected the Anemometer and Wind Direction (every 14seconds) 434 | scan = scan | 2; 435 | logWind = 0;//reset missing reads to zero 436 | anemom(); 437 | //dumpAnemom(); 438 | digitalWrite(RedPin, 1); 439 | digitalWrite(GrePin, 0); //Wind is Green 440 | digitalWrite(BluPin, 1); 441 | } 442 | if (manchester[0] == 0xa2) { //detected the Rain Gauge (every 47seconds) 443 | scan = scan | 4; 444 | logRain = 0;//reset missing reads to zero 445 | rain(); 446 | //dumpRain(); 447 | digitalWrite(RedPin, 1); 448 | digitalWrite(GrePin, 1); 449 | digitalWrite(BluPin, 0); //Rain is Blue 450 | } 451 | if (manchester[0] == 0xad) { //detected the UV Light Sensor (every 73seconds) 452 | scan = scan | 8; //not checked at the moment 453 | logUV = 0;//reset missing reads to zero 454 | UV(); 455 | //dumpUV(); 456 | digitalWrite(RedPin, 0); 457 | digitalWrite(GrePin, 0); 458 | digitalWrite(BluPin, 1); //UV is Yellow 459 | } 460 | if (manchester[0] == 0xa3) { //detected an original Sensor designed by Us every 45 seconds!!! 461 | //totally experimental 462 | scan = scan | 8; //not checked at the moment 463 | logExp=0;//reset missing reads to zero 464 | totExp(); 465 | //This code is not used by the three sensors in the WMR86 product. It may clash with other 466 | //other OS Sensors and a different value chosen in the Tx and then Rx 467 | digitalWrite(RedPin, 0); 468 | digitalWrite(GrePin, 1); 469 | digitalWrite(BluPin, 0); //Experimental is Purple!!! or Magenta??? 470 | 471 | } 472 | //Serial.println(scan,DEC); 473 | eraseManchester(); 474 | } 475 | 476 | //Calculation Routines 477 | /*The following bit has been identified by Xander Zimmerman as a 'battery low' indicator 478 | He used a variable voltage power supply to test signals from rainfall, temp/hum and anemometer eg 479 | - no more messages: < 2.1 V 480 | - switch to low < 2.6 V 481 | - only 1 Bit used: Byte 4; Bit 6: high => Bat low (Nyb8, Bit 2) 482 | This aligns with the nybble I suspected contained the Battery level indicator 483 | However it does not tally with my experiments. However I used resistors to lower the voltage to the sensor, 484 | rather than a precise and steady voltage. I am inclined to accept Xander's opinion here but reluctant 485 | to change my present strategy of notification if the sensor is not detected at all for 20 minutes. 486 | This strategy allows for instant collapse of the sensor whether it be low batteries, or the cold, or phyiscal damage 487 | or the transmitter suddenly being shielded or a lightning strike or any other quick demise. The Oregon LCD base console 488 | does not have icons that signal a low battery, or the absence of a signal. So if the "low battery" transmissions are 489 | missed then the sensor can drop out before action is taken. 490 | In the case of some batteries, I have them go "low" overnight when it has been very cold only to act normal again the next 491 | day as the air temperature rose. Some sort of flag system in the server code would need to capture and reain that status. 492 | However I am sure if you see value in using the "battery low" bit, then it will be easy enough to incorporate. 493 | Xander is also a fan of http://www.openhab.org/ for organising his home systems and also https://github.com/wfrog/wfrog 494 | for rendering the Oregon information into graphs etc. 495 | */ 496 | 497 | /* PCR800 Rain Gauge Sample Data: 498 | // 0 1 2 3 4 5 6 7 8 9 A 499 | // A2 91 40 50 93 39 33 31 10 08 02 500 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 4 5 501 | // 10100010 10010001 01000000 01010000 10010011 00111001 00110011 00110001 00010000 00001000 00000010 502 | // -------- ------- bbbb--- RRRRRRRR 88889999 AAAABBBB CCCCDDDD EEEEFFFF 00001111 2222CCCC cccc 503 | 504 | // byte(0)_byte(1) = Sensor ID????? 505 | // bbbb = Battery indicator??? (7) My investigations on the anemometer would disagree here. (However check above for Xander's contribution). 506 | // After exhaustive low battery tests these bbbb bits did not change 507 | // RRRRRRRR = Rolling Code Byte 508 | // 222211110000.FFFFEEEEDDDD = Total Rain Fall (inches) 509 | // CCCCBBBB.AAAA99998888 = Current Rain Rate (inches per hour) 510 | // ccccCCCC = 1 byte Checksum cf. sum of nybbles 511 | // Message length is 20 nybbles so working in inches 512 | Three tips caused the following 513 | 1 tip=0.04 inches or 1.1mm (observed off the LCD) 514 | My experiment 515 | Personally I don't like this value. I think mult by 25 and divide by 1000 IS closer to return mm directly. 516 | 0.127mm per tip??? It looks close the above. Again can't vouch 100% for this, any rigorous assistance would be appreciated. 517 | Over the last 6 months this has proved to be Ok 518 | */ 519 | void rain() { 520 | rainTotal = float(((nyb(18) * 100000) + (nyb(17) * 10000) + (nyb(16) * 1000) + (nyb(15) * 100) + (nyb(14) * 10) + nyb(13)) * 25) / 1000.0; 521 | //Serial.println((nyb(18)*100000)+(nyb(17)*10000)+(nyb(16)*1000)+(nyb(15)*100)+(nyb(14)*10)+nyb(13),DEC); 522 | rainRate = float(((nyb(8) * 10000) + (nyb(9) * 1000) + (nyb(10) * 100) + (nyb(11) * 10) + nyb(12)) * 25) / 1000.0; 523 | //Serial.println((nyb(8)*10000)+(nyb(9)*1000)+(nyb(10)*100)+(nyb(11)*10)+nyb(12),DEC); 524 | } 525 | void dumpRain() { 526 | Serial.print("Total Rain "); 527 | Serial.print(rainTotal); 528 | Serial.print(" mm, "); 529 | Serial.print("Rain Rate "); 530 | Serial.print(rainRate); 531 | Serial.println(" mm/hr "); 532 | } 533 | 534 | //UVN800 UV Light Sensor 535 | // D AD 10101101 87 10000111 41 01000001 AB 10101011 00 00000000 00 00000000 70 01110000 D3 11010011 536 | // D AD 10101101 87 10000111 41 01000001 AB 10101011 00 00000000 0A 00001010 70 01110000 74 01110100 537 | 538 | void UV() { 539 | //maximum readings appear ot be about 130-140 units (not sure how they rate them) 540 | intUV = int((nyb(9)*16)+nyb(11)); 541 | } 542 | void dumpUV() { 543 | Serial.print("UV Level "); 544 | Serial.println(intUV); 545 | } 546 | 547 | 548 | // WGR800 Wind speed sensor 549 | // Sample Data: 550 | // 0 1 2 3 4 5 6 7 8 9 551 | // A1 98 40 8E 00 0C 70 04 00 34 552 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 553 | // 10100001 10011000 01000000 10001110 00000000 00001100 01110000 00000100 00000000 00110100 554 | // -------- -------- bbbb---- NRRRRRRR xxxx9999 xxxxxxxx CCCCDDDD xxxxFFFF 0000---- CCCCcccc 555 | // Av Speed 0.4000000000m/s Gusts 0.7000000000m/s Direction: N 556 | 557 | // byte(0)_byte(1) = Sensor ID????? 558 | // bbbb = Battery indicator??? (7) My investigations would disagree here. After exhaustive low battery tests these bits did not change 559 | // 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 560 | // 9999 = Direction 561 | // DDDD.CCCC = Gust Speed (m per sec) 562 | // 0000.FFFF = Avg Speed(m per sec) 563 | // multiply by 3600/1000 for km/hr 564 | // ccccCCCC = 1 byte checksum cf. sum of nybbles 565 | // packet length is 20 nybbles 566 | 567 | void anemom() { 568 | //D A1 98 40 8E 08 0C 60 04 00 A4 569 | avWindspeed = ((nyb(16) * 10) + nyb(15)) * 3.6 / 10; 570 | double gust = ((nyb(13) * 10) + nyb(12)) * 3.6 / 10; 571 | // after every minute, reset gustWindspeed to avWindspeed and then take the highest gust after that (approx4 readings a minute) 572 | if (gust > gustWindspeed) { 573 | gustWindspeed = gust; 574 | } 575 | quadrant = nyb(9) & 0xF; 576 | } 577 | void dumpAnemom() { 578 | Serial.print("Av Speed "); 579 | Serial.print(avWindspeed); 580 | Serial.print(" km/hr, Gusts "); 581 | Serial.print(gustWindspeed); 582 | Serial.print(" km/hr, Direction: "); 583 | Serial.print(quadrant); 584 | Serial.print(" -> "); 585 | Serial.println(windDir[quadrant]); 586 | } 587 | 588 | // THGN800 Temperature and Humidity Sensor 589 | // 0 1 2 3 4 5 6 7 8 9 Bytes 590 | // 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 3 nybbles 591 | // 01011111 00010100 01000001 01000000 10001100 10000000 00001100 10100000 10110100 01111001 Bits 592 | // -------- -------- bbbbcccc RRRRRRRR 88889999 AAAABBBB SSSSDDDD EEEE---- CCCCcccc -------- Explanation 593 | // byte(0)_byte(1) = Sensor ID????? 594 | // bbbb = Battery indicator??? (7), My investigations on the anemometer would disagree here. After exhaustive low battery tests these bits did not change 595 | // RRRRRRRR = Rolling code byte 596 | // nybble(5) is channel selector c (Switch on the sensor to allocate it a number) 597 | // BBBBAAAA.99998888 Temperature in BCD 598 | // SSSS sign for negative (- is !=0) 599 | // EEEEDDDD Humidity in BCD 600 | // ccccCCCC 1 byte checksum cf. sum of nybbles 601 | // Packet length is 18 nybbles and indeterminate after that 602 | // H 00 01 02 03 04 05 06 07 08 09 Byte Sequence 603 | // D AF 82 41 CB 89 42 00 48 85 55 Real example 604 | // Temperature 24.9799995422 degC Humidity 40.0000000000 % rel 605 | void thermom() { 606 | temperature = (double)((nyb(11) * 100) + (nyb(10) * 10) + nyb(9)) / 10; //accuracy to 0.1 degree seems unlikely 607 | //The following line has been corrected by Darko in Italy, thank you, Grazie ragazzi!!! 608 | if (nyb(12) == 8) { // Trigger a negative temperature 609 | temperature = -1.0 * temperature; 610 | } 611 | humidity = (nyb(14) * 10) + nyb(13); 612 | } 613 | void dumpThermom() { 614 | Serial.print("Temperature "); 615 | Serial.print(temperature); 616 | Serial.print(" degC, Humidity "); 617 | Serial.print(humidity); 618 | Serial.println("% Rel"); 619 | } 620 | //The novel added extra sensors, extracting and applying any necessary conversions 621 | void totExp() { 622 | //No complicated conversions required here 623 | intSolar = byte(manchester[2]); //EG To apply a Solar Cell to the input? 624 | intLightning = byte(manchester[3]); //EG Lightning Strikes 625 | } 626 | 627 | void dumpExp() { 628 | Serial.print("Solar Power ="); 629 | Serial.println(intSolar, DEC); 630 | Serial.print("Lightning Strikes ="); 631 | Serial.println(intLightning, DEC); 632 | } 633 | 634 | // Formating routine for interface to host computer, output once a minute once all three sesnors have been detected 635 | void usbData() { 636 | // Stn Id, Packet Type, Wind Quadrant, Wind Speed, Rain Tips, Ext temp, Int Temp, Int Pressure, Int Humidity 637 | intHumi = (double)dht.readHumidity(); //DHT22 readings %Humidity 638 | intTemp = (double)bmp.readTemperature(); //internal temperature 639 | intPres = (double)bmp.readPressure() / 100.0; //Pa reduced to mBar 640 | //leave this check at 7 until the other readings are stabilized 641 | if ((scan&7)==7) { //scan==15 means all 4 readings now have a valid value, ready for output on Serial 642 | //Battery/Signal status, OR in the the four status values for the signal connections. 643 | logTemp++; 644 | if (logTemp>40){ 645 | batStat = batStat | 1; 646 | } 647 | logWind++; 648 | if (logWind>40){ 649 | batStat = batStat | 2; 650 | } 651 | logRain++; 652 | if (logRain>40){ 653 | batStat = batStat | 4; 654 | } 655 | logUV++; 656 | if (logUV>40){ 657 | batStat = batStat | 8; 658 | } 659 | logExp++; 660 | if (logExp>40){ 661 | batStat = batStat | 16; 662 | } 663 | //reset the batStat to 0 every 24hours 664 | aday++; 665 | if (aday>1440){ 666 | batStat=0; 667 | aday=0; 668 | } 669 | // Order: Battery Status, Quadrant, Wind Gust, Rainfall, Temperature, InternalTemp, Internal Pressure, Int Humidity, Solar Power, Lightning, UV Radiation 670 | Serial.print(batStat,DEC); //Send out the number to indicate if a sensor is not transmitting for maore than 20 mins. Low Battery or other damage. 671 | Serial.print(","); 672 | Serial.print(quadrant); //0-15 in 22.5 degrees steps clockwise 673 | Serial.print(","); 674 | Serial.print(gustWindspeed, 1); //Gust windspeed km/hr, not average windspeed (graphing over 10 samples gives Average) 675 | Serial.print(","); 676 | gustWindspeed = avWindspeed; //reset gust to average, then take the larger next reading 677 | Serial.print(rainTotal, 1); //currently considered to checked for a good calibration to mm 678 | Serial.print(","); 679 | Serial.print(temperature, 2); // OS Temperature Centigrade 680 | Serial.print(","); 681 | Serial.print(intTemp, 2); //BMP085 temperature (used for compensation reading) Centigrade 682 | Serial.print(","); 683 | Serial.print(intPres, 2); //BMP085 pressure reading milli-bars 684 | Serial.print(","); 685 | Serial.print(intHumi); //Digital DHT22 seems better than the OS in Temp/Hum sensor % relative 686 | Serial.print(","); 687 | Serial.print(intSolar,DEC); //A reading from experimental Solar Power sensor 688 | Serial.print(","); 689 | Serial.print(intLightning,DEC); //A reading from the nos of Lightning Strikes 690 | Serial.print(","); 691 | Serial.print(intUV, DEC); //UV Sensor 692 | Serial.println(); 693 | 694 | //Indicate the transmission of this one second data with a white light set up R+B+G!! 695 | digitalWrite(RedPin, 0); //White, all colours on, data has been logged to the Server 696 | digitalWrite(GrePin, 0); 697 | digitalWrite(BluPin, 0); 698 | } 699 | } 700 | 701 | void eraseManchester(){ 702 | for( int i=0; i < 15; i++){ 703 | manchester[i]=0; 704 | } 705 | } 706 | 707 | 708 | --------------------------------------------------------------------------------