WMR200 PROTOCOL | ||||||||||
|
Version 3.0
Introduction
==============================================================================================
WMR200 Data Protocol v3.0
Oregon Scientific WMR200 Weather Station USB Protocol
Compiled by: Rainer Finkeldeh .... rainer@bashewa.com
Please email me the corrections or additions. Thanks.
==============================================================================================
Oregon Scientific Weather Stations HID ID:
Vendor Id: 0fde
Product Id: ca01
PC FRAMES:
Commands to be issued to the WMR200 Console i.e. by the PC to the Console.
NOTE:
o There does not seem to be a way to only request historic data from the WMR200's data logger.
o If the console has historic data available after issuing a [D0] command, it will respond by
sending a [D1] packet otherwise it'll start sending live data.
o If you don't receive any data within 30 seconds then you should issue a [D0] frame again.
o If you don't acknowledge historic data packets with [DA] that you have received the WMR200
console will stop sending them.
o If you don't send a heartbeat [D0] frame periodically the WMR200 stops pumping live data
after 30 seconds, and then goes into history logging mode.
o Normally the WMR200 sends data for most of the sensors within 30 seconds after a [D0] data
retrieval request.
o For historic data retrieval you need to send a [D0] on startup and then wait for a [D1].
This you need to ACK with a [DA] to trigger [D2] history record retrieval.
o If you are not interested in historic data retrieval then only issue a [D0] frame at startup,
don't ACK the [D1]'s and perhaps send a [DB] command to clear the data logger.
D0 .. Heartbeat to Console ........ Console starts streaming [d3 - d9] records for 30 seconds.
This is a heartbeat to let the console know that your
application is still active. You should implement a timer
that pumps out [D0] frames on 30 second intervals. If the
WMR console does not receive a heartbeat from your
application within 30 seconds it will switch to data
logging mode.
DA .. Request historic data ....... This should be sent in response to [D1] and [D2] packets.
It triggers the retrieval of the next historic data record.
DB .. Erase WMR200 data logger .... Clears the historic data from WMR200's data logger memory
and when done the console will respond with a [DB].
DF .. Stop communication .......... Stops communication between PC and WMR200 Console and puts
the WMR200 into data logging mode. The console responds
with a [DF] in return if the command has been honoured.
NOTE: USB frames are made up of 9 bytes where the second byte indicates the length of the
command within the frame. Unused bytes in the frame should be padded with [00]'s. For
instance to issue a [D0] command you would send [00 01 D0 00 00 00 00 00 00] to the
USB port.
CONSOLE FRAMES:
Packets received by the PC i.e. returned by the WMR200 Console in response to one of the above
commands.
D1 .. Historic Data Notification .. Normally received after the first [D0] command has been
issued at startup if historic data is available in the
WMR200's data logger. Should you encounter such a packet
during normal execution then the data logger has gathered
some historic data. In such a case immediatly respond with
a [DA] command to retrieve the record.
D2 .. Historic Data ............... Issue [DA] immediately when received for the next historic
data record retrieval.
D3 .. Wind Data ................... Delivered during hearbeat period.
D4 .. Rain Data ................... Delivered during hearbeat period.
D5 .. UVI Data .................... Delivered during hearbeat period.
D6 .. Pressure Data ............... Delivered during hearbeat period.
D7 .. Temperature/Humidity Data ... Delivered during hearbeat period.
D8 .. Spare ....................... Not in use or not known at this stage.
D9 .. Battery & Sensor Status ..... Delivered during hearbeat period.
DB .. Erase Acknowledgment ........ By the console that erase data logger [DB] command has
been honoured.
DF .. Stop Acknowledgment ......... By the console that an issued stop communication [DF]
command has been honoured.
NOTATION USED IN FORMULAS:
byte(09) .. byte number 9
byte(09:H) .. byte number 9 high nibble
byte(09:L) .. byte number 9 low nibble
[#0C] ....... hex number
[0011] ...... bits within a byte
UOM CONVERSIONS:
°C to °F: (tC * 1.8) + 32
°F to °C: (tF - 32) / 1.8
1 inch = 25.4 mm
1 millimeter = 0.0393700787 inch
1 m/s = 2.23693629 mph
1 m/s = 3.60000000 kph
1 m/s = 1.94384449 kts
Observations & Notes
OBSERVATIONS:
o The WMR200 Console stops sending data if it does not receive any D0/DA commands within
30 seconds. This is a safeguard against data loss. In this case the WMR200 will start
collecting data in it's data logger, up to 29 days if set for 1 minute interval logging,
or longer depending on the data collection interval which is settable to 1, 2, 5, 10 or
15 minutes.
o Best practice, if history data is crucial to your operation, is to always send a [DA]
frame after every history data packet received to trigger the next history data retrieval.
o History records can only be extracted if you acknowledge the previous history record
received. They are not streamed out of the WMR200 Console like live data packets are.
o History records are streamed in chronological order i.e. oldest records arrive first.
o Estimated history records extraction at 80 records per minute:
1,440 records i.e. 1 days history will take ....... 18 minutes
10,080 records i.e. 1 week history will take 2 hours 06 minutes
41,760 records i.e. 29 days history will take 8 hours
WARNING:
o When you start collecting data from the WMR200 Console it stops logging data.
Make sure that you catch the live data during history extraction and add that later
to the end of the history in your database.
o Live data, [D3] to [D9] packets, will arrive mixed in between the historic [D2] packets
once historic data extraction begins.
o As soon as the USB connection is terminated, either by sending [DF] or abnormal
termination, the WMR200 Console will stop sending data and then will start to collect
data in the Data Logger.
o The WMR200 sometimes may collect Historic [D2] Data during normal operation due to your
program not functioning correctly. Make provision for this in your application. This may
also happen if another WMR200 application uses the same USB port and issues a stop
communication [DF] command. You need to reset and restart when you see a [DF] arriving in
your code.
o When adjusting the WMR200 Clock make sure your program is connected so as not to create
historic [D2] records in the WMR200's data logger that might get bogus timestamps during
your date/time adjustment.
o For live data logging use your PC's time as that is more accurate than the WMR200 Console's
one minute resolution. Also see note below about 1 minute data collection scheme in the
"YOUR DATABASE" section in this document.
o For historic data timestamp corrections see the note below about
"CONSOLE CLOCK OUT OF SYNC WITH PC PROBLEM".
o Note that measurements in the packets are in various units-of-measure i.e. metric and/or
imperial. Rain for instance is in inches while temperature is in Celsius and windchill
is in Fahrenheit etc. What was OS thinking when they designed this unit.
SHARED MODE:
o If you want other WMR applications to run concurrently with yours then you must check
regularly for WMR connection as other WMR application might have issued a [DF] stop
communication request. This check can be done by checking if you have received any data
at all in the last 30 seconds. You then need to re-establish the connection if necessary
i.e. you need to send a [DO] and/or [DA] command.
o You should implement an option to be able to share the USB with other WMR applications.
In shared mode you should not issue any commands. The master application will be
responsible for communicating with the WMR200 Console. You'll merely be jogging along and
you'll be left to the mercy of the master.
o Don't run your WMR application together with VWS as it floods the WMR Console with [DA]
and [D0] commands. They use brute force to get data extracted from the WMR200 for some
obscure reason.
CONSOLE CLOCK OUT OF SYNC WITH PC PROBLEM:
The problem with the Console date/time being out of sync with the PC can sometimes results
in corrupt data. Lets say your WMR200 Console clock is 10 minutes faster than your PC, then
all the history [D2] records would normally be logged 10 minutes into the future in your PC
logs. This is not what you want, right. Well there is a way to correct the historic data
timestamps by doing the following:
1) Only issue a [D0] at startup which will retrieve the first live records.
2) Compare the timestamp on the live data received with your PC clock and find the
difference (in whole minutes).
3) Once you have the difference issue a [DA] frame which will trigger historic data
retrieval.
4) When you process the historic [D2] records you now add/subtract the difference from the
packets' timestamp for logging purposes.
Your WMR200 clock can be out by days, or even years, and you'll still be logging your data
correctly if you implement this neat little trick.
YOUR DATABASE:
The problem is no so much in the WMR 200 protocol but in how everybody has implemented their
applications before the event of data loggers. Data was merely appended to the end of their
databases because it was always live. Now with data loggers that is no longer possible and
everybody tries to find a workaround to the problem by protocol manipulation. Instead they
should rather look at and change their way of thinking and perhaps change the database
instead. Wx applications should be able to handle out of sync data. Thus there would not
be a need for detecting end of history data streams.
o Everybody seem to implement an "APPEND to end of database" scheme ... this poses various
problems. If you by mistake, or due to the PC and WMR200 clocks not being in sync, add a
historic record onto the database that is in the past, you end up with data that is out
of sync and your database will be corrupted.
o It is recommended to store records in a 1 minute resolution as WD has attempted. However
you should only log one, and only one, record per minute. Thus you should end up with 1440
records per day exactly and no more and nothing less.
HANDLING LIVE DATA FOR LOGGING PUPOSES:
o If you implement the above proposed 1 minute interval logging in the database it is
recommended to collect live data in a current "master record".
o While receiving live data you would then save the highs or lows in this "master record".
o This "master record" should then be written to the database in 1 minute intervals
which can be triggered by a timer.
COMMON PITFALLS:
Normally these common mistakes are made:
o If you are getting bogus temperature values and spikes in your graph etc. then please
re-check the specs of the individual data packets and make sure you have not mixed up
metric and imperial units-of-measure.
o To use brute force in obtaining data from the WMR200 by issuing 1000's of [D0] and/or [DA]
commands per minute is going to get you in trouble. You will flood the USB port and end up
with unsuccessful packet delivery which will result in "stalled PID" as is the case with
Virtual Weather Station (VWS).
o If you have a STOP button on your application make sure you issue a stop comms [DF] command
and stop your heartbeat timer or else you'll loose valuable weather data when you put your
application into hibernation.
o Since historic data extraction can take hours, most applications fail in catching the live
data during this retrieval process i.e. it is ignored. This means that you might end up
with huge "gaps" in your database.
o Check-sums should be calculated and compared with the last 2 bytes of a packet to ensure
data integrity.
o Some of the USB/HID libraries available have a flaw in them in that they sometimes strip
the 1st byte off the packets. This you need to be aware of and you should not blame the
WMR200 for sending you corrupt data.
USB LIBRARIES:
You need a USB/HID library for communicating with the WMR200 Console. Here are some links:
o VB ........ http://www.lvr.com/hidpage.htm#MyExampleCode
o C# ........ http://www.florian-leitner.de/index.php/2007/08/03/hid-usb-driver-library/
o C++ ....... http://www.lvr.com/hidpage.htm#MyExampleCode
o .NET ...... http://hidlibrary.codeplex.com/
o C# .NET ... http://www.codeproject.com/useritems/USB_HID.asp
o Delphi .... http://www.soft-gems.net/index.php?option=com_content&task=view&id=30&Itemid=33
RESOURCES:
o WMR200 User Manual ....... http://www.oregonscientific.co.uk/ulimages/manuals2/WMR200_EN_R0.pdf
http://www2.os-weather.com/um/UserManual_English_WMR200.pdf
o WMR100 N User Manual ..... http://www.oregonscientific.co.uk/ulimages/manuals2/WMR100_EN_86L4515-017.pdf
o HID FAQ .................. http://www.lvr.com/hidfaq.htm
o USB Developers FAQ ....... http://www.lvr.com/usbfaq.htm
o USB 3.0 Developers FAQ ... http://www.lvr.com/usb3faq.htm
o USB SNIFFER .............. http://www.usblyzer.com/ (±$200 / 30 day trial)
o HID API for Linux, Mac OS X and Windows ... http://www.signal11.us/oss/hidapi/
RECOMMENDED WMR200 SOFTWARE:
WMR200 program tests done in Dec 2010.
o WUHU .... Handles historic data retrieval flawlessly and the best by far in doing so of
all WX apps available. Quite fast in data retrieval. WUHU can interface to
most popular Weather Stations like the Davis VP range, OS WMR's, LA Crosse
and many others.
PRICE: Currently FREE for non-commercial use.
http://home.comcast.net/~wuhu_software/
o WD ...... By far the most popular of all WX application available but not recommended if
the historic data in your WMR200's data logger is crucial to you. Has a tendancy
to loose historic data during data retrieval. Otherwise it has a host of
features not found in other WS applications. Available for MAC and Windows and
can interface to almost any weather station on the market.
PRICE: ±$70
http://www.weather-display.com/index.php
o WSDL .... Weather Station Data Logger (WSDL) has only implemented historic data retrieval
recently and I've not had a chance to test it yet. It has a very nice GUI.
Also has hooks for a custom built console using the Ardoino platform. Only
interfaces to WMR100, WMR200, RMS300 and Radio Shack 63-256 Weather Stations.
PRICE: Currently FREE for non-commercial use with source code in C#.
http://wmrx00.sourceforge.net/index.html
o VWS ..... Not recommended. Does not handle the WMR200 protocol well at all. Sometimes
issues Clear DB commands without your approval. Interfaces to most of the
popular weather stations if not all on the market.
PRICE: ±$50 to $100 depending on your requirements.
http://www.ambientweather.com/virtualstation.html
o Others .. Most other software, excluding the above, have not sucessfully implemented
the WMR200 data protocol while testing took place in Dec 2010.
FORUMS:
o http://www.wxforum.net/
WMR100N DATA PROTOCOL:
o https://github.com/ejeklint/WLoggerDaemon/blob/master/Station_protocol.md
o http://www.dg1sfj.de/hardware/hw_wmr100_protokoll.html
OS FAQ:
o http://au.oregonscientific.com/service/faq.asp?t=1&cid=3
C# Code Snippet
//=========================================================================================\\
// WMR200 Data Protocol v3.0 \\
// Oregon Scientific WMR200 Weather Station USB Protocol \\
// \\
// Compiled by: Rainer Finkeldeh .... rainer@bashewa.com \\
// Please email me the corrections or additions. Thanks. \\
//==========================================================================================\\
// NOTE:
// o The code below is written in C# (similar to JavaScript).
// o This code snippet only deals with USB data collection and not data storage.
// o This code makes provision for data packet overflow in USB frames and timestamp correction
// on history packets.
// o To determine packet timestamp correction we wait for a live packet before we start
// extracting history data.
// o This code has been tested with 3 days worth of history records and it works perfect.
// o USB Data frames are 9 bytes.
//
// CODE SUMMARY:
// o We send a RESET and a [D0] to the WMR200 at startup.
// o The WMR200 responds with a [D1], which means it's got history data stored, but we don't
// ACK this because we wait for a live packet for timestamp correction on history packets.
// o A live packet should arrive within a few seconds which we then take the timestamp from
// and compare it to the PC's time. We determine the time difference and then use this to
// make corrections to the history packet timestamps.
// o After this we send a [DA] and the WMR200 starts sending us the 1st history record.
// o After that we issue [DA]'s after every history and live packet we receive.
// o When the WMR200 stops sending us [D2]'s we start counting 10 live packets for us to
// determine end of history data.
// o Once we have determined that we're finished with history data we start sending a [D0]
// heartbeat every 30 seconds (otherwise the WMR200 stops sending us live data).
//============================================================================================
// GLOBAL VARIABLES
bool connected = false; // Have we got a connection to the WMR200?
bool gotHistory = true; // We always assume that we'll get history data at startup.
bool stopIssued = false; // Set to true when we issue a DF stop command to the WMR200.
bool gotTimeFix = false; // Set to true when we've determined the time difference
// between PC and the WMR200.
int iHist = iLive = 0; // Counts for history and live packets received.
byte[] rcv_data = new byte[128]; // We collect USB data in this array until we've received a
// full WMR packet (1st byte contains length).
byte[] wmr_data = new byte[128]; // We port a full packet into this array and then ship it for
// further processing (1st byte contains length).
decimal wmr_time_fix = 0; // Packet timestamp correction in whole minutes as determined
// from 1st live packet received.
DateTime now_time = new DateTime();
DateTime wmr_time = new DateTime();
Timer timer_heartbeat = new Timer(); // timer for sending 30 second [D0] heartbeats.
//============================================================================================
public partial class WMR200_Data_Logger : Form
(
public WMR200_Data_Logger()
{
InitializeComponent();
timer_heartbeat.Tick += new System.EventHandler(timer_heartbeat_Tick);
}
// BUTTONS
// START BUTTON
private void btn_start_Click(object sender, EventArgs e)
{
cmd_start();
}
// STOP BUTTON
private void btn_stop_Click(object sender, EventArgs e)
{
cmd_stop();
}
// ERASE BUTTON
private void btn_erase_Click(object sender, EventArgs e)
{
cmd_erase();
}
// WMR200 COMMANDS
// USB CONNECT COMMAND
private void cmd_connect()
{
try
{
usb.ProductId = 51713; // 0xCA 0x01
usb.VendorId = 4062; // 0x0F 0xDE
usb.CheckDevicePresent(); // USB DLL library function
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
// START [D0] COMMAND
private void cmd_start()
{
if (!connected)
cmd_connect(); // we'll get a "usb_OnSpecifiedDeviceArrived" event if successfull
else
{
cmd_reset();
cmd_sendD0();
}
}
// STOP [DF] COMMAND
private void cmd_stop()
{// NOTE: The stop sometimes responds with a last packet and then confirms with DF
stopIssued = true;
}
// ERASE [DB] COMMAND (erases the history in the WMR200 data logger)
private void cmd_erase()
{
heartbeat_stop();
cmd_reset();
send_command("01 DB");
stopIssued = true;
}
// RESET COMMAND
private void cmd_reset()
{
heartbeat_stop();
iLive = 0;
iHist = 0;
gotHistory = true;
gotTimeFix = false;
stopIssued = false;
send_command("20 00 08 01");
rcv_dataClear(); // clear the data collector array
}
// SEND [D0] COMMAND
private void cmd_sendD0()
{
if (stopIssued) return;
heartbeat_start();
send_command("01 D0");
}
// SEND [DA] COMMAND
private void cmd_sendDA()
{
if (stopIssued)
{
heartbeat_stop();
send_command("01 DF");
}
else
{
heartbeat_reset();
send_command("01 DA");
}
}
// USB SEND COMMAND FRAME
private void send_command(string cmd)
{
try
{
cmd += " ";
cmd.Trim();
if (cmd == null) return;
string[] command = cmd.Split(' ');
byte[] data = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
for (int idx = 0; idx < command.Length; idx++)
if (command[idx] != "")
{
int value = Int32.Parse(command[idx], NumberStyles.HexNumber);
data[idx+1] = Convert.ToByte(value);
}
if (usb.SpecifiedDevice != null)
usb.SpecifiedDevice.SendData(data); // USB DLL function
else
MessageBox.Show("Sorry but the WMR200 is not present. Plug it in!! ");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
// USB DEVICE ARRIVED EVENT
private void usb_OnSpecifiedDeviceArrived(object sender, EventArgs e)
{// WMR200 device has been found .. now start sending
cmd_reset();
cmd_sendD0();
connected = true;
}
// USB ON DATA RECEIVED EVENT
private void usb_OnDataReceived(object sender, DataReceivedEventArgs args)
{
if (InvokeRequired) {
try {
Invoke(new DataReceivedEventHandler(usb_OnDataReceived), new object[] { sender, args });
}
catch (Exception ex) {
MessageBox.Show(ex.ToString());
}
}
else
{// expected 9 byte USB frame in "args.data": 0x00 0x03 0xD3 0x10 0x30 0x.. 0x.. 0x.. 0x.. 0x..
// If we're collecting a new packet make sure it's not junk (because in shared mode we
// might get half packets on startup).
if ( rcv_data[0] == 0x00 // the length in rcv_data[0] will be 0 if expecting a new packet
&& !(args.data[2] >= 0xD1 && args.data[2] <= 0xD7) // make sure 2nd byte is a valid packet
&& !(args.data[2] == 0xD9)
&& !(args.data[2] == 0xDB)
&& !(args.data[2] == 0xDF))
return; // junk ... we're not in sync with WMR200
// Collect and append the USB frames into "rcv_data"
Array.Copy(args.data, 2, rcv_data, rcv_data[0] + 1, args.data[1]);
rcv_data[0] += args.data[1]; // rcv_data[0] holds the count of valid USB data received
// Check that we have collected a full data packet before we proceed to process it.
if (rcv_data[2] != 0x00 // We must have at least received the pkt length to
// determine the logic following in this "if" condition.
&& rcv_data[0] >= rcv_data[2] // Collected length must be at least expected packet
// length or greater.
&& (rcv_data[1] >= 0xD2 && rcv_data[1] <= 0xD7 // Must also be one of the expected
|| rcv_data[1] == 0xD9)) // packet types [d2] to [d7] or [d9].
{
// Move the received packet for further processing into "wmr_data" (excluding any
// overflow ... we’ll handle overflow below).
Array.Copy(rcv_data, 1, wmr_data, 1, rcv_data[2]);
wmr_data[0] = wmr_data[2]; // set the array length to packet length
// Fix the timestamp on data packets (but only if the WMR200 clock is out)
if (gotTimeFix && wmr_time_fix != 0)
fix_pkt_timestamp(ref wmr_data);
else
if (!gotTimeFix && wmr_data[1] < 0xD9) // D9 packet has no timestamp ... skip it
{// calculate time difference between WMR200 and PC clock for timestamp correction.
now_time = DateTime.Now;
now_time = new DateTime( now_time.Year
, now_time.Month
, now_time.Day
, now_time.Hour
, now_time.Minute
, 0 // round this to the minute
);
wmr_time = new DateTime( wmr_data[7] + 2000
, wmr_data[6]
, wmr_data[5]
, wmr_data[4]
, wmr_data[3]
, 0
);
// substract the now_time from wmr_time to determine timespan difference
TimeSpan timeCorrection = now_time.Subtract(wmr_time);
wmr_time_fix = decimal.Ceiling(Convert.ToDecimal(timeCorrection.TotalMinutes));
gotTimeFix = true;
fix_pkt_timestamp(ref wmr_data); // Fix timestamp of this packet
}
// Check if we received overflow while collecting data. If so then don't ACK this
// wmr_data packet until we receive all of the frames for the overflow packet.
// Check if the received data is greater than the packet length.
if (rcv_data[0] > rcv_data[2]) // overflow?
{
// calculate length of overflow
byte lenOvr = Convert.ToByte((int)rcv_data[0] - (int)rcv_data[2]);
// move the overflow to the beginning of "rcv_data"
Array.Copy(rcv_data, rcv_data[2] + 1, rcv_data, 1, lenOvr);
rcv_data[0] = lenOvr;
// Clear the remainder of "rcv_data" so that we don't process half packets by
// mistake (important!)
Array.Clear(rcv_data, lenOvr + 1, rcv_data.Length - lenOvr - 1);
// Handle 1 byte overflow packets immediately and check for junk
if(!(rcv_data[1] >= 0xD1 && rcv_data[1] <= 0xD7)
&& !(rcv_data[1] == 0xD9)
&& !(rcv_data[1] == 0xDB)
&& !(rcv_data[1] == 0xDF)) // then we've got junk
{
rcv_dataClear();
}
else if (rcv_data[1] == 0xD1)
{
rcv_dataClear();
if (iHist > 0) // If not then wait for the 1st live to determine
cmd_sendDA(); // time difference between PC and WMR clock
}
else if (rcv_data[1] == 0xDF)
{
timer_timeout.Stop();
rcv_dataClear();
stopIssued = true;
}
}
// ACK the packets where necessary
else switch (wmr_data[1])
{
case 0xD2: // we always send an ACK after history packets
if (!stopIssued)
{
gotHistory = true; // Switch back to history extraction mode (so that we
cmd_sendDA(); // ACK live packets again)
}
iLive = 0;
iHist++;
rcv_dataClear();
break;
default:
if (gotHistory)
{ // after 10 consecutive live packets we assume end of history and start
// sending a heartbeat instead
if (iLive++ >= 10)
{ // now start a heartbeat instead of ACKing every live packet
if (!stopIssued)
{
heartbeat_start(); // start pumping [D0]'s every 30 seconds
send_command("01 D0");
}
gotHistory = false;
iLive = 0;
}
else if (gotTimeFix) // if no time fix then wait for the next live packet
{
cmd_sendDA();
}
}
rcv_dataClear();
break;
}
// now send off the received packet for further processing.
process_WMR_Packet(wmr_data);
} // end of valid data packets [d2] to [d7] and [d9]
else if (rcv_data[0] == 1) // 1 byte WMR packets are handled here ...[d1], [db] or [df]
{
switch (rcv_data[1])
{
case 0xD1:
rcv_dataClear();
if (iHist > 0 && !stopIssued)
{ // If not then wait for the 1st live packet to get time difference between
// PC and WMR clock
cmd_sendDA();
}
break;
case 0xDB:
timer_timeout.Stop();
rcv_dataClear();
break;
case 0xDF:
timer_timeout.Stop();
rcv_dataClear();
stopIssued = true;
break;
}
}
// else we keep on collecting USB frames until we have at least a full packet
}
}
// PROCESS WMR200 PACKETS
private void process_WMR_Packet(byte[] wmr_data) // wmr_data[0] is length of array.
{// NOTE: You need to collect live data while in history mode and append that to history
// when finished.
if (check_CRC(wmr_data)) // if CRC is OK then process packet
switch (wmr_data[1])
{// do whatever needs to be done with these
case 0xD2: process_History(wmr_data); break;
case 0xD3: process_Wind(wmr_data); break;
case 0xD4: process_Rain(wmr_data); break;
case 0xD5: process_UVI(wmr_data); break;
case 0xD6: process_Pressur(wmr_data); break;
case 0xD7: process_Temperature(wmr_data); break;
case 0xD9: process_Status(wmr_data); break;
}
}
// CRC CHECK
private bool check_CRC(byte[] wmr_data) // 1st byte of wmr_data contains length
{
int len = wmr_data[2];
ushort CRC = 0;
ushort pkt_CRC = Convert.ToUInt16((wmr_data[len] * 256) + wmr_data[len - 1]);
for (byte i = 1; i <= (wmr_data[2]-2); i++)
{
CRC += wmr_data[i];
}
return ((pkt_CRC + histTimeFix) == CRC);
}
// CLEAR THE PACKET COLLECTOR
private void rcv_dataClear()
{
Array.Clear(rcv_data, 0, rcv_data.Length); // Array.Clear(source , sourecindex, len)
}
// FIX PACKET TIMESTAMP
private void fix_pkt_timestamp(ref byte[] wmr_data)
{
if (wmr_data[1] == 0xD9) return; // D9 packet does not have a timestamp
// Get packet timestamp and correct it's time
DateTime wmr_time = new DateTime( wmr_data[7] + 2000
, wmr_data[6]
, wmr_data[5]
, wmr_data[4]
, wmr_data[3]
, 0
);
wmr_time = wmr_time.AddMinutes((long)wmr_time_fix); // apply the time correction
// fix packet timestamp
wmr_data[3] = Convert.ToByte(wmr_time.Minute);
wmr_data[4] = Convert.ToByte(wmr_time.Hour);
wmr_data[5] = Convert.ToByte(wmr_time.Day);
wmr_data[6] = Convert.ToByte(wmr_time.Month);
wmr_data[7] = Convert.ToByte(wmr_time.Year-2000);
}
// TIMER MANAGEMENT
// [D0] Heartbeat timer for the WMR200 to keep on pumping live data otherwise WMR will time
// out after 30 seconds.
private void heartbeat_start()
{
timer_heartbeat.Interval = 30000; // 30 seconds timeout trigger
timer_heartbeat.Start();
}
private void heartbeat_stop()
{
timer_heartbeat.Stop();
}
private void heartbeat_reset()
{
timer_heartbeat.Stop();
timer_heartbeat.Start();
}
private void timer_heartbeat_Tick(object sender, EventArgs e)
{
send_command("01 D0");
}
}
[D2] History
==============================================================================================
[D2] HISTORY
Rain = inch
Wind = m/s
Temp = °C
Dewp = °C
Heat Index = °F
Wind Chill = °F
Pressure = hPa
==============================================================================================
----> Time/Date----> RAIN---------------------------------> WIND--------------->
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 ...byte nr
D2 L mm-hh dd-mm-yy rL-rH hL-hH dL-dH aL-aH mm-hh dd-mm-yy xD .. gg aG-Aa WC AL ...notation
----- -------------- -------------------------------------- --------------------
D2 31 24 07 02 03 09 00 00 00 00 00 00 BC 0A 00 0C 01 01 07 04 0C 0D F0 00 00 20 ...data
--------------------------------------------------------------------------------
Continued: UV PRESSURE--> SC TEMP0--------------> TEMP1--------------> CHSUM
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 ...byte nr
xI SS-iS AA-xA SC tS TT-sT HU DD-sD HI tS TT-sT HU DD-sD HI cL-cH ...notation
-- ----------- -- -------------------- -------------------- -----
06 51 33 02 34 01 00 F4 00 2C 78 00 00 01 A6 00 51 82 00 00 11 07 ...data
-----------------------------------------------------------------
HEADER ---------------------------------------------------------------------------------------
Byte 00: [D2] Packet Id
Byte 01: [31] Packet Length 49 bytes ...(can be up to 112 bytes with all 10 external
sensors .. 7 extra bytes per sensor)
DATE/TIME ------------------------------------------------------------------------------------
Byte 02: (mm) Minute
Byte 03: (hh) Hour
Byte 04: (dd) Day
Byte 05: (mm) Month
Byte 06: (yy) Year
D4 RAIN --------------------------------------------------------------------------------------
Rainfall rate in inches (and not in millimeters) reported after 2nd tip.
Byte 07: (rL) Rainfall Rate low byte +
Byte 08: (rH) Rainfall Rate high byte
RainRate in inch/hr = ((rH*256) + rL) / 100)
RainRate in mm/hr = ((rH*256) + rL) / 100) * 25.4
Rain This Hour: activated when rain starts being measured.
Byte 09: (hL) Rainfall This Hour low byte +
Byte 10: (hH) Rainfall This Hour high byte
Rain_this_hour in inch = ((hH*256) + hL) / 100)
Rain_this_hour in mm = ((hH*256) + hL) / 100) * 25.4
Rain Past 24 Hours: excluding current hour
Byte 11: (dL) Rainfall Past 24H low byte +
Byte 12: (dH) Rainfall Past 24H high byte
Rain_last_24h in inch = ((dH*256) + dL) / 100)
Rain_last_24h in mm = ((dH*256) + dL) / 100) * 25.4
Accumulated rain since 12h00 1st January 2007
Byte 13: (aL) Rainfall Accumulated low byte +
Byte 14: (aH) Rainfall Accumulated high byte
Rain_accum in inch = ((aH*256) + aL) / 100)
Rain_accum in mm = ((aH*256) + aL) / 100) * 25.4
Date/time since accumulated rainfall (always 12h00 01-01-2007)
Byte 15: (mm) Rainfall Accumulated Minute
Byte 16: (hh) Rainfall Accumulated Hour
Byte 17: (dd) Rainfall Accumulated Day
Byte 18: (mm) Rainfall Accumulated Month
Byte 19: (yy) Rainfall Accumulated Year
D3 WIND --------------------------------------------------------------------------------------
Byte 20: (x) byte(20:H)
.. unknown / always [#0] ... maybe icon, alarm or sensor absent?
(D) byte(20:L)
Wind Direction = (D * 22.5) = degrees
0 = N (between 348.76° and 011.25°)
1 = NNE (between 011.26° and 033.75°)
2 = NE (between 033.76° and 056.25°)
3 = ENE (between 056.26° and 078.75°)
4 = E (between 078.76° and 101.25°)
5 = ESE (between 101.26° and 123.75°)
6 = SE (between 123.76° and 146.25°)
7 = SSE (between 146.26° and 168.75°)
8 = S (between 168.76° and 191.25°)
9 = SSW (between 191.26° and 213.75°)
10 = SW (between 213.76° and 236.25°)
11 = WSW (between 236.26° and 258.75°)
12 = W (between 258.76° and 281.25°)
13 = WNW (between 281.26° and 303.75°)
14 = NW (between 303.76° and 326.25°)
15 = NNW (between 326.26° and 348.75°)
Byte 21: (..) .. unknown / always [#0C] (0000x1100 = decimal 12)
Byte 22: (gg) byte(22) = Wind Gust low byte
Byte 23: (G) byte(23:L) = Wind Gust high byte : low nibble
(a) byte(23:H) = Wind Avrg low byte : low nibble
Byte 24: (a) byte(24:L) = Wind Avrg low byte : high nibble
(A) byte(24:H) = Wind Avrg high byte : low nibble
WIND GUST: [byte10:L + byte09]
WindGust in m/s = (((byte(23:L) * 256) + byte(22)) / 10)
WindGust in mph = (((byte(23:L) * 256) + byte(22)) / 10) * 2.23693629
WindGust in kph = (((byte(23:L) * 256) + byte(22)) / 10) * 3.60000000
WindGust in kts = (((byte(23:L) * 256) + byte(22)) / 10) * 1.94384449
WIND AVRG: [byte11:H + byte11:L + byte10:H]
WindAvrg in m/s = (((byte(24) * 16) + byte(23:H)) / 10)
WindAvrg in mph = (((byte(24) * 16) + byte(23:H)) / 10) * 2.23693629
WindAvrg in kph = (((byte(24) * 16) + byte(23:H)) / 10) * 3.60000000
WindAvrg in kts = (((byte(24) * 16) + byte(23:H)) / 10) * 1.94384449
Byte 25: (WC) WIND CHILL: (in Fahrenheit)
.... Reported when temp < 40°F (4.4°C) and wind > 3 mph (4.8 kph) otherwise
value is #00 ... to be confirmed.
.... According to OS Manual: temp reading has to be <= 10 deg & the wind
speed has to be >= 3 mph or 5 km/hr.
Wind Chill in °F = byte(25)
Wind Chill in °C = ((byte(25) - 32) / 1.8)°C
Byte 26: (AL) .. unknown / always [#20] = [0010 0000] = decimal 32
Wind Chill Alarm: [#00] = on, [#10] = ?, [#20] = off
D5 UV ----------------------------------------------------------------------------------------
Byte 27: (xI) Reports value of [#ff] in history [D2] records when out of order, not
installed or sensor is lost.
(x) byte(27:H) .. unknown / always [#0] .. maybe alarm or sensor absent?
(I) byte(27:L) .. UVI 0-15
... (0-2 low / 3-5 moderate / 6-7 high / 8-10 very high / 11+ extremely high)
D6 BARO --------------------------------------------------------------------------------------
Byte 28: (SS) byte(28) .. Station Pressure low byte +
Byte 29: (S) byte(29:L) .. Station Pressure high nibble ..
Station Pressure in hPa = ((byte(29:L)*256) + byte(28))
Station Pressure in inHg = ((byte(29:L)*256) + byte(28)) * 0.0295333727
(i) byte(29:H) .. Forecast Icon Nr: (12-24 hour weather forecast within 30-50 km)
[0000] = [#0] = 0: Partly Cloudy (day)
[0001] = [#1] = 1: Rainy
[0010] = [#2] = 2: Cloudy
[0011] = [#3] = 3: Sunny (day)
[0100] = [#4] = 4: Clear (night)
[0101] = [#5] = 5: Snowy
[0111] = [#6] = 6: Partly Cloudy (night)
Byte 30: (AA) byte(30) .. Altitude Pressure low byte +
Byte 31: (A) byte(31:L) .. Altitude Pressure high nibble ..
Altitude Pressure in hPa = ((byte(31:L)*256) + byte(30))
Altitude Pressure in inHg = ((byte(31:L)*256) + byte(30)) * 0.0295333727
(x) byte(31:H) .. unknown / always [#3] = [0011] .. maybe trend/alarm?
.. or maybe it's always sunny indoors ;-)
EXTERNAL SENSOR COUNT ------------------------------------------------------------------------
Byte 32: External sensor count (value 1 to 10)
This value will be 0 if your external sensor is not operational.
Use this to determine the number of extra sensor blocks in this history packet.
packetLengthD2 = 49 + ((byte(32)-1) * 7);
D7 CONSOLE ------- SENSOR #0 -----------------------------------------------------------------
Note the order of sensors might be reversed i.e. this might be sensor #1's data.
Make sure that you use the sensor number when posting this data.
Byte 33: (t) byte(33:H) .. Temp/Hum trend/icon.
[00..] = 0: Temperature ...... steady
[01..] = 1: Temperature ...... rising
[10..] = 2: Temperature ...... falling
[..00] = 0: Humidity ......... steady
[..01] = 1: Humidity ......... rising
[..10] = 2: Humidity ......... falling
tTemp = byte(33) >> 6 .......... or: tTemp = byte(33) / 64
tHum = byte(33) >> 4 & 0x03 ... or: tHum = (byte(33) / 16) & 0x03
(S) byte(33:L) .. Sensor Number
0 = WMR200 Console temp/humidity (indoor)
1 = Extra sensor #1 temp/humidity (outdoor)
. ... up to 10 sensors ...
10 = Extra sensor #10 temp/humidity
nSensor = byte(33) & 0x0F
Byte 34: (TT) byte(34) .. Temperature low byte +
Byte 35: (T) byte(35:L) .. Temperature high nibble
(s) byte(35:H) .. Temperature sign ...... #0 = positive / #8 = negative
Temp_sign = ((byte(35) >> 4)==0x08)? -1 : 1;
TEMPERATURE in °C = Temp_sign * (((byte(35:L)*256) + byte(34)) / 10)
TEMPERATURE in °F = ((TEMPERATURE in °C) * 1.8) + 32
Byte 36: (HU) Humidity %
Byte 37: (DD) byte(37) .. Dew Point low byte +
Byte 38: (D) byte(38:L) .. Dew Point high nibble
(s) byte(38:H) .. Dew Point sign ...... #0 = positif / #8 = negative
Dewp_sign = ((byte(38) >> 4)==0x08)? -1 : 1;
DEWPOINT in °C = Dewp_sign * (((byte(38:L)*256) + byte(37)) / 10)
DEWPOINT in °F = ((DEWPOINT in °C) * 1.8) + 32
Byte 39: (HI) Heat Index: (in Fahrenheit)
.... Reported when temp is > 80°F (26.7°C) otherwise value is [#00]
HEAT INDEX in °C = ((byte(39)-32) / 1.8)
HEAT INDEX in °F = byte(39)
D7 EXTERNAL ------ SENSOR #1 -----------------------------------------------------------------
Note the order of sensors might be reversed i.e. this might be sensor #2's data.
This block will be missing if sensor #1 has been lost i.e. external sensor count will be 0
in case of 1 extra external sensor.
Byte 40: (t) byte(40:H) .. Temp/Hum trend/icon.
[00..] = 0: Temperature ...... steady
[01..] = 1: Temperature ...... rising
[10..] = 2: Temperature ...... falling
[..00] = 0: Humidity ......... steady
[..01] = 1: Humidity ......... rising
[..10] = 2: Humidity ......... falling
tTemp = byte(40) >> 6 .......... or: tTemp = byte(40) / 64
tHum = byte(40) >> 4 & 0x03 ... or: tHum = (byte(40) / 16) & 0x03
(S) byte(40:L) .. Sensor Number
0 = WMR200 Console temp/humidity (indoor)
1 = Extra sensor #1 temp/humidity (outdoor)
. ... up to 10 sensors ...
10 = Extra sensor #10 temp/humidity
nSensor = byte(40) & 0x0F
Byte 41: (TT) byte(41) .. Temperature low byte +
Byte 42: (T) byte(42:L) .. Temperature high nibble
(s) byte(42:H) .. Temperature sign ...... #0 = positif / #8 = negative
Temp_sign = ((byte(42) >> 4)==0x08)? -1 : 1;
TEMPERATURE in °C = Temp_sign * (((byte(42:L)*256) + byte(41)) / 10)
TEMPERATURE in °F = ((TEMPERATURE in °C) * 1.8) + 32
Byte 43: (HU) Humidity %
Byte 44: (DD) byte(44) .. Dew Point low byte +
Byte 45: (D) byte(45:L) .. Dew Point high nibble
(s) byte(45:H) .. Dew Point sign ...... #0 = positif / #8 = negative
Dewp_sign = ((byte(45) >> 4)==0x08)? -1 : 1;
DEWPOINT in °C = Dewp_sign * (((byte(45:L)*256) + byte(44)) / 10)
DEWPOINT in °F = ((DEWPOINT in °C) * 1.8) + 32
Byte 46: (HI) Heat Index: (in Fahrenheit)
.... Only reported when temp is > 80°F (26.7°C) otherwise value is 0x00
HEAT INDEX in °C = ((byte(46)-32) / 1.8)
HEAT INDEX in °F = byte(46)
D7 EXTERNAL ------ SENSOR #n -----------------------------------------------------------------
Note the order of sensors might be reversed i.e. this might be sensor #1's data.
.. up to 9 blocks of 7 bytes each for additional external temp/humidity sensors.
.. this block will only be supplied if the sensor is operational i.e. if it transmits data.
.. byte(47:L) i.e. nSensor takes on the value of the channel number the sensor is set to.
CHECKSUM -------------------------------------------------------------------------------------
To get the check-sum, add up bytes 00 to 46 which must equal ((cH*256) + cL)
Byte 47: (cL) Check-sum low byte
Byte 48: (cH) Check-sum high byte
[D3] Wind
==============================================================================================
[D3] WIND (NOTE: in meters/second)
==============================================================================================
----> Time/Date----> WIND---------------> CHSUM
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 ...byte nr
ID LL mm-hh dd-mm-yy xD .. gg aG-Aa WC AL cL-cH ...notation
----- -------------- -- -- __ -_ -- -- -- -----
d3 10 0d 05 07 0c 0a 09 0c 12 b0 00 e6 00 c7 02 ...data
-----------------------------------------------
DECODED:
Packet Nr .......... [D3]
Length ............. [10] 16 bytes
Time ............... [0d:33] 05:13
Date ............... [07-0c-0a] 07-12-2010
Wind Direction ..... [9] SSW
Wind Speed Gust .... [012] 1.8 m/s
Wind Speed Avrg .... [00b] 1.1 m/s
Wind Chill ......... [e6] 23°F (-5.0°C)
Check-sum .......... [0711]
----------------------------------------------------------------------------------------------
HEADER ------
Byte 00: [D3] Packet Id
Byte 01: [10] Packet Length = 16 bytes
DATE/TIME ---
Byte 02: (mm) Minute
Byte 03: (hh) Hour
Byte 04: (dd) Day
Byte 05: (mm) Month
Byte 06: (yy) Year
D3 WIND -----
Byte 07: (x) byte(07:H)
.. unknown / always [#0] ... maybe icon/alarm?
(D) byte(07:L)
Wind Direction = (D * 22.5) = degrees
0 = N (between 348.76 and 011.25)
1 = NNE (between 011.26 and 033.75)
2 = NE (between 033.76 and 056.25)
3 = ENE (between 056.26 and 078.75)
4 = E (between 078.76 and 101.25)
5 = ESE (between 101.26 and 123.75)
6 = SE (between 123.76 and 146.25)
7 = SSE (between 146.26 and 168.75)
8 = S (between 168.76 and 191.25)
9 = SSW (between 191.26 and 213.75)
10 = SW (between 213.76 and 236.25)
11 = WSW (between 236.26 and 258.75)
12 = W (between 258.76 and 281.25)
13 = WNW (between 281.26 and 303.75)
14 = NW (between 303.76 and 326.25)
15 = NNW (between 326.26 and 348.75)
Byte 08: (..) .. unknown / always [#0C] (0000x1100 = decimal 12)
Byte 09: (gg) byte(09) = Wind Gust low byte
Byte 10: (G) byte(10:L) = Wind Gust high byte : low nibble
(a) byte(10:H) = Wind Avrg low byte : low nibble
Byte 11: (a) byte(11:L) = Wind Avrg low byte : high nibble
(A) byte(11:H) = Wind Avrg high byte : low nibble
WIND GUST: [byte10:L + byte09]
WindGust in m/s = (((byte(10:L) * 256) + byte(09)) / 10)
WindGust in mph = (((byte(10:L) * 256) + byte(09)) / 10) * 2.23693629
WindGust in kph = (((byte(10:L) * 256) + byte(09)) / 10) * 3.60000000
WindGust in kts = (((byte(10:L) * 256) + byte(09)) / 10) * 1.94384449
WIND AVRG: [byte11:H + byte11:Ln + byte10:H]
WindAvrg in m/s = (((byte(11) * 16) + byte(10:H)) / 10)
WindAvrg in mph = (((byte(11) * 16) + byte(10:H)) / 10) * 2.23693629
WindAvrg in kph = (((byte(11) * 16) + byte(10:H)) / 10) * 3.60000000
WindAvrg in kts = (((byte(11) * 16) + byte(10:H)) / 10) * 1.94384449
Stage 1: Light ......... 0- 8 mph ( 3-13 kph)
Stage 2: Moderate ...... 9-25 mph (14-41 kph)
Stage 3: Strong ........ 26-54 mph (42-97 kph)
Stage 4: Storm ......... >55 mph ( >88 kph)
Byte 12: (WC) WIND CHILL: (in Fahrenheit)
.... Reported when temp < 40°F (4.4°C) and wind > 3 mph (4.8 kph) otherwise
value is #00 ... to be confirmed.
.... According to OS Manual: temp reading has to be <= 10 deg & the wind
speed has to be >= 3 mph or 5 km/hr.
Wind Chill in °F = byte(12)
Wind Chill in °C = ((byte(12) - 32) / 1.8)°C
Byte 13: (AL) .. unknown / always [#20] = [0010 0000] = decimal 32
.. perhaps Wind Chill Alarm: [#00] = on, [#10] = ?, [#20] = off
CHECKSUM ---- To get the check-sum, add up bytes 00 to 13 == ((256*cH) + cL)
Byte 20: (cL) Check-sum low byte
Byte 21: (cH) Check-sum high byte
[D4] Rain
==============================================================================================
[D4] RAIN (NOTE: rainfall is in inches)
One tip according to OS is 0.04 inch = 1.016 mm (needs to be confirmed)
==============================================================================================
----> Time/Date----> Rain------------------> Rain Date----> CHSUM
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 ...byte nr
ID LL mm-hh dd-mm-yy rL-rH hL-hH dL-dH aL-aH mm-hh dd-mm-yy cL-cH ...notation
----- -------------- ----- ----- ----- ----- ----- -------- -----
d4 16 3b 15 08 0c 0a 0f 00 04 00 4b 00 7e 02 00 0c 01 01 07 4b 02 ...data
-----------------------------------------------------------------
DECODED:
Packet Nr .......... [D4]
Packet Length ...... [16] 22 bytes
Time ............... [15:3b] 21:59
Date ............... [07-0c-0a] 08-12-2010
Rain rate .......... [000f] 0.15 inch/hr
Rain last hour ..... [0004] 0.04 inch
Rain last 24hr ..... [004b] 0.75 inch
Rain accumulated ... [027e] 6.38 inch (162.052 mm)
Rain since time .... [0c:00] 12:00
Rain since date .... [01:01:07] 01-01-2007
Check-sum .......... [024b]
----------------------------------------------------------------------------------------------
HEADER ------
Byte 00: (ID) [D4] Packet Id
Byte 01: (LL) [16] Packet Length = 22 bytes
DATE/TIME ---
Byte 02: (mm) Minute
Byte 03: (hh) Hour
Byte 04: (dd) Day
Byte 05: (mm) Month
Byte 06: (yy) Year
D4 RAIN -----
Rainfall rate in inches (and not in millimeters)
Byte 07: (rL) Rainfall Rate low byte +
Byte 08: (rH) Rainfall Rate high byte
RainRate in inch/hr = ((256*rH) + rL) / 100)
RainRate in mm/hr = ((256*rH) + rL) / 100) * 25.4
Rain This Hour: activated when rain starts being measured
Byte 09: (hL) Rainfall This Hour low byte +
Byte 10: (hH) Rainfall This Hour high byte
Rain_this_hour in inch = ((256*hH) + hL) / 100)
Rain_this_hour in mm = ((256*hH) + hL) / 100) * 25.4
Rain Past 24 Hours: excluding current hour
Byte 11: (dL) Rainfall Past 24H low byte +
Byte 12: (dH) Rainfall Past 24H high byte
Rain_last_24h in inch = ((256*dH) + dL) / 100)
Rain_last_24h in mm = ((256*dH) + dL) / 100) * 25.4
Accumulated rain since 12h00 1st January 2007
Byte 13: (aL) Rainfall Accumulated low byte +
Byte 14: (aH) Rainfall Accumulated high byte
Rain_accum in inch = ((256*aH) + aL) / 100)
Rain_accum in mm = ((256*aH) + aL) / 100) * 25.4
Date/time since accumulated rainfall (always 12h00 01-01-2007)
Byte 15: (mm) Rainfall Accumulated Minute
Byte 16: (hh) Rainfall Accumulated Hour
Byte 17: (dd) Rainfall Accumulated Day
Byte 18: (mm) Rainfall Accumulated Month
Byte 19: (yy) Rainfall Accumulated Year
CHECKSUM ---- Add up the bytes 00 to 19 == ((256*cH) + cL)
Byte 20: (cL) Check-sum low byte
Byte 21: (cH) Check-sum high byte
[D5] UVI
==============================================================================================
[D5] UVI
==============================================================================================
----> Time/Date----> UV CHSUM
00 01 02 03 04 05 06 07 08 09 ...byte nr
ID LL mm-hh dd-mm-yy xI cL-cH ...notation
----- -------------- -- -----
D5 0a 24 07 02 03 09 06 11 07 ...data
----- -------------- -- -----
DECODED:
Packet Id .......... [D5]
Packet Length ...... [0a] 10 bytes
Time ............... [07:24] 07:36
Date ............... [02-03-09] 02-03-2009
UVI ................ [6] 6
Check-sum .......... [0711]
----------------------------------------------------------------------------------------------
HEADER ------
Byte 00: (ID) [D5] Packet Id
Byte 01: (LL) [0a] Packet Length = 10 bytes
DATE/TIME ---
Byte 02: (mm) Minute
Byte 03: (hh) Hour
Byte 04: (dd) Day
Byte 05: (mm) Month
Byte 06: (yy) Year
D5 UV -------
Byte 07: (xI) Reports value of [#ff] in history [D2] records when out of order, not
installed or sensor is lost.
(x) byte(07:H) .. unknown / always [0] .. maybe alarm?
(I) byte(07:L) .. UVI 0-15
... (0-2 low / 3-5 moderate / 6-7 high / 8-10 very high / 11+ extremely high)
CHECKSUM ---- Add up the bytes 00 to 07 == ((256*cH) + cL)
Byte 11: (cL) Check-sum low byte
Byte 12: (cH) Check-sum high byte
[D6] Baro
==============================================================================================
[D6] BARO (Pressure in hPa / mb)
==============================================================================================
----> Time/Date----> PRESSURE--> CHSUM
00 01 02 03 04 05 06 07 08 09 10 11 12 ...byte nr
ID LL mm-hh dd-mm-yy SS-iS AA-xA cL-cH ...notation
----- ----- -------- ----- ----- -----
D6 0d 06 12 04 0c 0a 4a 63 fa 33 ef 02 ...data
--------------------------------------
DECODED:
Packet Id .......... [D6]
Packet Length ...... [0d] 13 bytes
Time ............... [12:06] 18:06
Date ............... [04-0c-0a] 04-12-2010
Pressure/Station ... [34a] 842 hPa
Forecast Icon ...... [6] Partly Cloudy (Night)
Pressure/Altitude .. [3fa] 1018 hPa
Check-sum .......... [02ef]
----------------------------------------------------------------------------------------------
HEADER ------
Byte 00: (ID) [D6] Packet Id
Byte 01: (LL) [0d] Packet Length = 13 bytes
DATE/TIME ---
Byte 02: (mm) Minute
Byte 03: (hh) Hour
Byte 04: (dd) Day
Byte 05: (mm) Month
Byte 06: (yy) Year
D6 BARO -----
Byte 07: (SS) byte(07) .. Station Pressure low byte +
Byte 08: (S) byte(08:L) .. Station Pressure high nibble ..
Station Pressure in hPa = ((byte(08:L)*256) + byte(07))
Station Pressure in inHg = ((byte(08:L)*256) + byte(07)) * 0.0295333727
(i) byte(08:H) .. Forecast Icon Nr: (12-24 hour weather forecast within 30-50 km)
[0000] = [#0] = 0: Partly Cloudy (day)
[0001] = [#1] = 1: Rainy
[0010] = [#2] = 2: Cloudy
[0011] = [#3] = 3: Sunny (day)
[0100] = [#4] = 4: Clear (night)
[0101] = [#5] = 5: Snowy
[0110] = [#6] = 6: Partly Cloudy (night)
Byte 09: (AA) byte(09) .. Altitude Pressure low byte +
Byte 10: (A) byte(10:L) .. Altitude Pressure high nibble ..
Altitude Pressure in hPa = ((byte(10:L)*256) + byte(09))
Altitude Pressure in inHg = ((byte(10:L)*256) + byte(09)) * 0.0295333727
(x) byte(10:H) .. unknown / always [#3] = [0011] .. maybe trend/alarm?
.. or maybe it's always sunny indoors ;-)
CHECKSUM ---- Add up the bytes 00 to 10 == ((256*cH) + cL)
Byte 11: (cL) Check-sum low byte
Byte 12: (cH) Check-sum high byte
[D7] Temperature / Humidity
==============================================================================================
[D7] EXTERNAL - TEMP/HUM/DEWP/HI (Temp/Dewp in °C and Heat_Index in °F)
==============================================================================================
----> Time/Date----> TEMP/HUM/DEW-------> CHSUM
00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 ...byte nr
ID LL mm-hh dd-mm-yy tS TT-sT HU DD-sD HI cL-cH ...notation
----- -------------- -- ----- -- ----- -- -----
d7 10 2f 0d 06 0c 0a 91 22 01 1b 14 80 52 8a 02 ...data
-----------------------------------------------
DECODED:
Packet Id .......... [D7]
Packet Length ...... [10] 16 bytes
Time ............... [0d:2f] 13:47
Date ............... [06-0c-0a] 06-12-2010
Trend .............. [9] Temp falling / Humidity rising
Sensor Nr .......... [1] 1
Temperature ........ [0122] +21.1°C
Humidity ........... [1b] 27%
Dewpoint ........... [8014] -2.0°C
Heat Index ......... [52] 82°F (27.78°C)
Check-sum .......... [028a]
----------------------------------------------------------------------------------------------
HEADER ------
Byte 00: (ID) [D7] Packet Id
Byte 01: (LL) [10] Packet Length = 16 bytes
DATE/TIME ---
Byte 02: (mm) Minute
Byte 03: (hh) Hour
Byte 04: (dd) Day
Byte 05: (mm) Month
Byte 06: (yy) Year
TEMP/HUM ----
Byte 07: (t) byte(07:H) .. Temp/Hum trend/icon.
[00..] = [#0] = 0: Temperature ...... steady
[01..] = [#4] = 1: Temperature ...... rising
[10..] = [#8] = 2: Temperature ...... falling
[..00] = [#0] = 0: Humidity ......... steady
[..01] = [#1] = 1: Humidity ......... rising
[..10] = [#2] = 2: Humidity ......... falling
tTemp = byte(07) >> 6 .......... or: tTemp = byte(07) / 64
tHum = byte(07) >> 4 & 0x03 ... or: tHum = (byte(07) / 16) & 0x03
(S) byte(07:L) .. Sensor Number
0 = WMR200 Console temp/humidity (indoor)
1 = Extra sensor #1 temp/humidity (outdoor)
. ... up to 10 sensors ...
10 = Extra sensor #10 temp/humidity
nSensor = byte(07) & 0x0F
Byte 08: (TT) byte(08) .. Temperature low byte +
Byte 09: (T) byte(09:L) .. Temperature high nibble
(s) byte(09:H) .. Temperature sign ...... #0 = positif / #8 = negative
Temp_sign = ((byte(09) >> 8)==0x08)? -1 : 1;
TEMPERATURE in °C = Temp_sign * (((byte(09:L)*256) + byte(08)) / 10)
TEMPERATURE in °F = ((TEMPERATURE in °C) * 1.8) + 32
Byte 10: (HU) Humidity %
Byte 11: (DD) byte(11) .. Dew Point low byte +
Byte 12: (D) byte(12:L) .. Dew Point high nibble
(s) byte(12:H) .. Dew Point sign ...... #0 = positif / #8 = negative
Dewp_sign = ((byte(12) >> 4)==0x08)? -1 : 1;
DEWPOINT in °C = Dewp_sign * (((byte(12:L)*256) + byte(11)) / 10)
DEWPOINT in °F = ((DEWPOINT in °C) * 1.8) + 32
Byte 13: (HI) Heat Index: (in Fahrenheit)
.... Reported when temp is > 80°F (26.7°C) otherwise value is [#00]
HEAT INDEX in °C = ((byte(13)-32) / 1.8)
HEAT INDEX in °F = byte(13)
Stage 1: 80- 89°F (26.7°C to 32.0°C) ... Caution
Stage 2: 90-104°F (32.0°C to 40.0°C) ... Extreme Caution
Stage 3: 105-129°F (40.5°C to 53.8°C) ... Danger
Stage 4: 130-151°F (54.5°C to 66.1°C) ... Extreme Danger
CHECKSUM ---- Add up the bytes 00 to 13 = ((256*cH) + cL)
Byte 14: (cL) Check-sum low byte
Byte 15: (cH) Check-sum high byte
[D9] Status
==============================================================================================
[D9] Battery Low, Sensor Loss and RF Clock Signal
NOTE: Alarms are only sounded and reported about 15 minutes after loss of a sensor.
==============================================================================================
----> Status----> CHSUM
00 01 02 03 04 05 06 07 ...byte nr
ID LL aa bb cc dd cL-cH ...notation
----- -- -- -- -- -----
d9 08 00 00 00 00 17 00 ...data
-----------------------
HEADER ------
Byte 00: (ID) [D9] Packet Id
Byte 01: (LL) [08] Packet Length = 8 bytes
STATUS ------
[8421 8421]
Byte 02: (H) [1... ....] = [#80]
[.1.. ....] = [#40]
[..1. ....] = [#20]
[...1 ....] = [#10]
(L) [.... 1...] = [#08]
[.... .1..] = [#04]
[.... ..1.] = [#02] Sensor fault: Sensor 1 (temp/hum outdoor)
[.... ...1] = [#01] Sensor fault: Wind (---)
Byte 03: (H) [1... ....] = [#80]
[.1.. ....] = [#40]
[..1. ....] = [#20] Sensor fault: UV (--)
[...1 ....] = [#10] Sensor fault: Rain (---)
(L) [.... 1...] = [#08]
[.... .1..] = [#04]
[.... ..1.] = [#02]
[.... ...1] = [#01]
Byte 04: (H) [1... ....] = [#80] RF Signal Weak: Console Clock (time not synchronized)
[.1.. ....] = [#40]
[..1. ....] = [#20]
[...1 ....] = [#10]
(L) [.... 1...] = [#08]
[.... .1..] = [#04]
[.... ..1.] = [#02] Battery Low: Sensor 1 (temp/hum outdoor)
[.... ...1] = [#01] Battery Low: Wind ... to be confirmed)
Byte 05: (H) [1... ....] = [#80]
[.1.. ....] = [#40]
[..1. ....] = [#20] Battery Low: UV ..... (below 2.4v out of 3.0v)
[...1 ....] = [#10] Battery Low: Rain ... to be confirmed)
(L) [.... 1...] = [#08]
[.... .1..] = [#04]
[.... ..1.] = [#02]
[.... ...1] = [#01]
CHECKSUM ---- Add up the bytes 00 to 05 == ((256*cH) + cL)
Byte 06: (cL) Check-sum low byte
Byte 07: (cH) Check-sum high byte
| ||||||||||
|
|














