Jun 202012


I have owned a Lacrosse Technology weather station (model WS-3600) for a number of years, which was purchased from Jaycar Electronics here in Australia.  This model can be connected to a PC via a RS232 port, but the software that was supplied with it was buggy and unreliable, as well as this the serial interface uses non-standard signalling that can’t be understood by any terminal software.

I had been thinking that it must be possible to intercept the radio signal from the outdoor sensor, and upon searching found that it had already been done for another Lacrosse Technology weather station (model WS-2355).  This gave me a very good starting point for me to start to understand the communications. The original project can be found at http://www.practicalarduino.com/projects/weather-station-receiver. Please note that any information regarding the WS-2355 in this article was derived from the code at the previously referenced link as I don’t have access to this device.

I don’t use Arduino, but instead use the native development environment for Atmel microcontrollers (AVR Studio).  The first step was to build up a circuit on a breadboard and then port the Arduino code to work with it and AVR Studio. This all turned out to be relatively straightforward, however reverse engineering the packet format was substantially more complicated and took a substantial amount of time.

Sensor Differences



While the WS-2355 uses the outdoor sensor model WS-2300-25, the WS-3600 uses a TX13 sensor.

I found there to be a few differences in the RF transmissions of these two models of sensor.  These are:

  1. While both sensors use the same signalling system, the timing used is different.  A ‘0’ bit is represented by sending a ‘long’ high signal followed by a ‘long’ low signal, while a ‘1’ bit is represented by sending a ‘short’ high signal followed by a ‘long’ low signal.  The length of the ‘long’ and ‘short’ periods is different between the two sensors.  See the table below for the timings.

    Model Long Short
    WS-2300-25 1200uS 600uS
    TX13 1400uS 300uS
  2. The sensors use a different preamble/sync byte to begin the transmission of a packet.  This first byte has wrongly being identified in many places as being a sensor type identifier.  This is wrong and while they may vary between sensors it is not an identifier.  The transmission starts by sending a few 0’s to wake up and sync the RF module in the receiver.  The receiver may or may miss some of these 0’s as it may take a few bits to sync.  The number of bits missed can vary between makes/models of receiver used and must be catered for in software.  After these initial 0’s a pattern to allow the receiver to identify where the end of the first byte should be is sent.
    For the TX13 sensor this initial preamble/sync byte is binary 00000110 (0x06), while the WS-2300-25 uses binary 00001001 (0x09).

  3. The TX13 sensor sends an additional packet containing wind gust speed and direction.  The WS-2300-25 does not send this.

Packet Format

This information is based on work done with a TX13 sensor. Much of this information is probably also relevant to the WS-2300-25 sensor.  The packets are transmitted by the Lacrosse weather sensors over the 433.92 Mhz band (this frequency may be different in other countries).  The sensor uses ASF modulation and employs OOK (On-Off-Keying) to encode and transmit the data.

The sensor sends groups of packets at regular intervals.  For the TX13 this period is 128 seconds when the average wind speed is below 2.8m/S, and at 32 second intervals when the average wind speed is 2.8m/S or more.  When the sensor transmits data it sends multiple packets in a burst of up to 10 packets.  There are five types of packet that contain different types of data, these are Temperature, Humidity, Rain, Wind Average and Wind Gust.  The group of up to 5 packets are sent and then immediately resent to create a stream of up to 10 packets. There is a gap of approximately 160mS between these packets.

When first powered up the sensor selects a random identification number and begins transmitting groups of packets every 4S, this interval is maintained for the first 15 minutes before reverting back to either 32/128S.  During this first 15 minutes every type of packet is transmitted in each group.

Rainfall is only transmitted in every 5th group of packets (at least this is the case when it is not raining).

The following table shows the structure used within each of these packets, followed by a description of the various fields.

Nibble 0 1 2 3 4 5 6 7 8 9 10 11 12
Bit 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
Function I7..I0 G X P1..P0 S7..S0 T4..T0 F1..F0 D12..D0 Q11..Q4 C3..C0

The next table shows the meaning of the various fields within the packets.

Bits Description

Preamble/sync.  This never changes.

WS3600 = 00000110 (06 hex)
G For weather stations that report wind gusts this bit is set to 1 while P1:P0 is set to 11 to indicate that this is a wind gust packet.
For all other combinations of P1:P0 this bit is set to 0.
X This bit is used as part of the error checking.  This bit when XOR’d with D0-D12 and F0-F1 will equal 1 if the data is OK.

This is the indication of the type of data contained in the packet.

00 = Temperature
01 = Humidity
10 = Rain
11 = Windspeed and direction (if G is 0 this is average data. If G is 1 this is gust data)
S7:S0 This is populated with the random sensor identification that is generated when the sensor is powered up.  If the sensor loses power a new id will be generated when power is restored.

This is populated with flags indicating which packets are included in this group of packets.  For each packet in the group this will therefore be the same value.  The meaning of the bits is:

T4 = if 1 then wind gust packets are being sent (confirmed)
T3 = if 1 then wind average packets are being sent (not confirmed, always 1)
T2 = if 1 then rain packets are being sent (confirmed)
T1 = if 1 then humidity packets are being sent (not confirmed, always 1)
T0 = if 1 then temperature packets are being sent (not confirmed, always 1)

This field indicates the period that the sensor is going to wait before sending the next group of packets.

00 = 4 seconds
01 = 32 seconds
10 = 128 seconds
11 = unused (not confirmed, never seen)

For temperature the sensor reports the temperature in increments of 0.1C from -40 to 59.9.

Temperate = ((D11:D8*10) + (D7:D4) + (D3:D0/10)) – 40
nb. D12 is not used and is always 0

For Humidity the sensor reports the humidity as an integer.

Humidity = (D11:D8*10) + (D7:D4)
D3:D1 = NOT S3:S0
nb. D12 is not used and is always 0

For Rain the sensor reports the number of tips of the seesaw in the range of 0-4095.  Once the count reaches 4095 it wraps back to 0.  This count gets reset to 0 if the sensor loses power.  Each tip of the seesaw is 0.508mm.

Rain = D11:D0
nb. D12 is not used and is always 0

For Wind (both average and gust) the data is reported in increments of 0.1m/S from 0 to 50.

Wind speed = D12:D4 (for gust a value of 510 means no gust)
Wind Direction = D3:D0 (0000=N, 0001=NNE, 0010=NE, 0011=ENE, 0100=E, 0101=ESE, 0110=SE, 0111=SSE, 1000=S, 1001=SSW, 1010=SW, 1011=WSW, 1100=W, 1101=WNW, 1110=NW, 1111=NNW)

Inverted data.  This provides for another check for valid packets.

Q11:Q4 = NOT D11:D4

This is the checksum of the packet.

C3:C0 = The sum of nibbles 0 to 11


Although I originally started with a port of the Arduino code I have since discarded it and started rewriting my own code from scratch. The main reason behind this decision was that the Arduino version does way too much processing inside an interrupt routine. I have it working, but still have a few improvements on the todo list. Once complete I will happily return the code to the community.

Hopefully someone out there finds some of this information useful.