Oregon WMR300 on a Raspberry Pi

Since back in the time when I purchased the brand-new weatherstation (sadly without a deeper research beforehand), no software running on linux was available to communicate with the Oregon WMR300, I had to reverse engineer all the protocols used. I will try to guide you through the set-up of the required software systems to communicate in this part, and go into detail on how to decode and store the received data in the next post. The first part of accessing the Oregon's data is to connect the WMR300 base station (which hopefully is located near a suitable place for a Raspberry Pi with network connection) via USB.

The next part involves basic knowledge of the Pi's installed operating system/distribution and requires a bit of programming knowledge in C++.

Since the communication protocol that the bundled software called "Weather OS PRO" has not been made public, I had to analyze the ongoing USB communication first.

This reveals that

  • firstly, the base station is defined by
    • a vendor ID of 0xfde and a product ID of 0xca08
  • the initial handshaking is based on sending 4 reports to the base station, namely
    1. 0x41,0x43,0x4B,0xD3,0x01,0x89 // requests the first live data
    2. 0x41,0x43,0x4B,0xDC,0x01,0x89 // tells the base station to not send old history data first
    3. 0xA6,0x91,0xCA,0x45,0x52,0x00 // ??
    4. 0x73,0xE5,0x0A,0x26,0x88,0x8B // ??
How are we going to access the device via USB? Gladly, there are libraries out there that enable us to add some layers of abstraction. The following example uses this HID library by Alan Ott.
#include <stdio.h>
#include <wchar.h>
#include <string.h>
#include <stdlib.h>
#include "hidapi.h"


// Headers needed for sleeping.
#ifdef _WIN32
	#include <windows.h>

	void usleep(__int64 usec)
	{
		HANDLE timer;
		LARGE_INTEGER ft;

		ft.QuadPart = -(10 * usec); // Convert to 100 nanosecond interval, negative value indicates relative time

		timer = CreateWaitableTimer(NULL, TRUE, NULL);
		SetWaitableTimer(timer, &ft, 0, NULL, NULL, 0);
		WaitForSingleObject(timer, INFINITE);
		CloseHandle(timer);
	}
#else
	#include <unistd.h>
#endif

int main(int argc, char* argv[])
{
	int res;
	unsigned char buf[256];
	hid_device *handle;
	int i;
	int x;
	int y;
	FILE *fp;

	//Initialize the HIDAPI Library
	hid_init();

	// Try to open the HID device.
	handle = hid_open(0xfde, 0xca08, NULL);
	if (!handle) {
		printf("unable to open device\n");
		return 1;
	}

	// Set the hid_read() function to be blocking.
	hid_set_nonblocking(handle, 1);

	while(true) {
		// Set up the command buffer. This has to be done around every 200-300 times data is received in order to avoid a time-out of the base station.
		memset(buf, 0x00, sizeof(buf));
		// Send commands to start data streaming
		buf[0] = 0x41;
		buf[1] = 0x43;
		buf[2] = 0x4B;
		buf[3] = 0xD3;
		buf[4] = 0x01;
		buf[5] = 0x89;
		res = hid_write(handle, buf, 17);
		if (res < 0) {
			printf("hid_write failed.\n");
		}
		buf[0] = 0x41;
		buf[1] = 0x43;
		buf[2] = 0x4B;
		buf[3] = 0xDC;
		buf[4] = 0x01;
		buf[5] = 0x89;
		res = hid_write(handle, buf, 17);
		if (res < 0) {
			printf("hid_write failed.\n");
		}
		buf[0] = 0xA6;
		buf[1] = 0x91;
		buf[2] = 0xCA;
		buf[3] = 0x45;
		buf[4] = 0x52;
		buf[5] = 0x00;
		res = hid_write(handle, buf, 17);
		if (res < 0) {
			printf("hid_write failed.\n");
		}
		buf[0] = 0x73;
		buf[1] = 0xE5;
		buf[2] = 0x0A;
		buf[3] = 0x26;
		buf[4] = 0x88;
		buf[5] = 0x8B;
		res = hid_write(handle, buf, 17);
		if (res < 0) {
			printf("hid_write failed.\n");
		}

		// Here comes the tricky part. Basically, each of the outer for loops reads 100 lines of data via the HID bus.
		for (x = 0; x < 100; x++) {
			res = 0;

			// And the inner loop reads one "report" from the device, containing one line of encrypted raw data.
			while (res == 0) {
				res = hid_read(handle, buf, sizeof(buf));
				if (res == 0)
					printf("waiting...count = %i\n", x);
				if (res < 0)
					printf("Unable to read()\n");
				//Pause 0.1 second
				usleep(1000*100);
			}

			fflush(stdout);

			for (i = 0; i < res; i++) {
				// Now we print out the raw data to stdout as hex numbers with a space in between.
				printf("%02hhx ", buf[i]);
			}

			printf("\n");
		}

	}

	hid_close(handle);
	hid_exit();
	return 0;
}
So, what does this code snippet demonstrate, and how can we use it? After you followed the "Build" instructions on the above website, you should be able to
  • Create a new .cpp file containing the above source in a new directory, could be called "hidtest"
  • Run make on it, including the above-build libraries in the path
  • Be able to run the programm with sudo ../path/to/hidtest. Usually, sudo or root rights are needed because the low-level hardware communication is needed.
If everything is working, the programm should generate an output containing 64 bytes per line. Now that we are able to get the data from the WMR300 base station, it is time to process and decode it in the second part.
Facebooktwittergoogle_plusredditpinterestlinkedinmail

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.