### Send data to weather station over 433.92 MHz

Send temperature and humidity data to a weather station over 433.92 MHz, OOK modulated with FS1000A module

In a previous post I used a software defined radio (SDR) to analyze and decode data transmission over 433.92 MHz of a simple weather station. As I mentioned then, the indoor unit can receive data from up to three outdoor units. I found that outdoor units use basic OOK modulation to send data to indoor unit. Knowing this I can make my own outdoor unit using a 433 MHz transmitter module controlled by an Arduino.

Obviously, I had to use a temperature and humidity sensor such as DHT11, DHT22, AM2302 to get environment parameters. I emulated full original outdoor unit functionality by adding a display and a push button to trigger immediate transmission of data to indoor unit.

 Arduino based data transmission device

## Electronics

The device is controlled by an Arduino Nano compatible board. For the simplicity of the schematic, I used an alphanumeric LCD fitted with I2C adapter. I also added the red LED to signal RF transmission, as it is on original unit.

 Schematic of prototype

The outdoor units can be configured to transmit data on three channels. The “channel” is a bit in the data stream. I included a jumper to set channel. The RF module is part of a RX-TX kit. I got acceptable results with this TX module (FS1000A), yet I would recommend you use other type. There are some problems with it.

Output power is way lower than the original unit of the weather station. Note that I powered FS1000A from 5 V, yet it accepts up to 12 V. Maybe, at 12 V it would provide more output power. I built a simple antenna using some copper wire, after instructions of Ben Schueler.

Another thing to mention is that frequency drifts mostly because of no shielding. It should be quite stable, since it uses a SAW resonator, R433S type.

 Schematic of FS1000A module

Speaking of SAW resonator, according to datasheet, it has a frequency of 433.92 MHz. More or less (75 kHz more or less). I found mine to oscillate on 433.86 MHz (which is within the specifications). The RF signal can still be received by main unit. But, given the lower output power and this frequency shift, TX range is severely reduced.

 Frequency of original outdoor unit vs. FS1000A module

While making some signal recordings using the SDR of original outdoor unit and my device, I also found this FS1000A has (probably unintended) slow start and stop. When switching data pin to HIGH, it gradually increases the output power until it reaches maximum. Gradual decrease is also noted when switching back to LOW. The oscillator is off when data input is LOW.

To overcome this effect, I had to alter bit times to make my signal understandable for the weather station.

## Code

Controlling this with Arduino is pretty easy. RF transmission of FS1000A module can only be turned on and off using DATA pin. This makes it suitable for OOK modulation only (on-off keying). As I shown in the previous post, the original device encodes data using pulse distance modulation as follows: a 500 µs carrier burst followed by 1000 µs pause represents bit 0, while a 500 µs carrier burst followed by 2000 µs pause represents bit 1. There is also a space (or a sync bit) represented by 500 µs carrier burst followed by 4000 µs pause. This is used between data packets. Starting with the basics, this is the function which generates the signal for bit 0 or 1:

// ***** Generate the pulse for a single bit *****
void tx_bit(byte onPin, bool b)
{
digitalWrite(onPin, HIGH);
delayMicroseconds(PULSE_HIGH);
digitalWrite(onPin, LOW);
if (b == true)
delayMicroseconds(PULSE_ONE);
else
delayMicroseconds(PULSE_ZERO);
}

Next we need to generate the 36-bit packet containing 8-bit device ID (house code), battery status bit, channel bits, 12-bit signed temperature multiplied by 10, 4-bit always 1111 and 8-bit humidity.

 Data bits

While analyzing the signal of the original unit, I discovered it skips first 4 bits of device ID on every first packet transmission. This is why the next function takes an argument which is set to true only on first packet transmission to skip ID bits.

// ***** Generate bitstream (36 bits) *****
void tx_data(byte onPin, bool first = false)
{
int idx;

// send ID
for (first ? idx = 3 : idx = 7; idx >= 0; idx--)
{
tx_bit(onPin, dev_id & (0x1 << idx));
}

// send battery state
tx_bit(onPin, dev_batt);

// send zero
tx_bit(onPin, LOW);

// send channel
tx_bit(onPin, dev_ch & 0x2);
tx_bit(onPin, dev_ch & 0x1);

// send temperature (12 bits)
int16_t t12 = t * 10.0f;
for (idx = 11; idx >= 0; idx--)
{
tx_bit(onPin, t12 & (0x1 << idx));
}

// send 1111
tx_bit(onPin, HIGH);
tx_bit(onPin, HIGH);
tx_bit(onPin, HIGH);
tx_bit(onPin, HIGH);

// send humidity
uint8_t h8 = (uint8_t)h;
for (idx = 7; idx >= 0; idx--)
{
tx_bit(onPin, h8 & (0x1 << idx));
}
}

The above generates a single data packet. In needs to be repeated 10 times, including sync bit between each packet. Note the first packet should skip ID bits. This is what the next function does.

// ***** Generate full signal (36 bits repeated 10 times) *****
{
#ifdef COMPLETE_TX
tx_data(onPin, false);
#else
tx_data(onPin, true);
#endif

// sync bit
digitalWrite(onPin, HIGH);
delayMicroseconds(PULSE_HIGH);
digitalWrite(onPin, LOW);
delayMicroseconds(PULSE_SYNC);

for (int i = 1; i < repeat; i++)
{
tx_data(onPin);

#ifndef COMPLETE_TX
if (i + 1 == repeat)
break; // do not send sync after last TX
#endif

// sync bit
digitalWrite(onPin, HIGH);
delayMicroseconds(PULSE_HIGH);
digitalWrite(onPin, LOW);
delayMicroseconds(PULSE_SYNC);
}
}

Also, after last packet there is no sync bit. I included a preprocessor directive in the code which allows sending complete data packets (COMPLETE_TX). For this device, this should not be defined. The 10 packets are sent every 56.75 seconds. This is a strange time interval, but this is what I measured on recorded signals from original unit.

According to rtl_433 signals database, the device ID (house code) is randomly changed every time batteries are changed in the unit. I did set it using random() but it seems to always default to 110.

Note that at the beginning of the code there are the slightly modified pulse times. I had to increase carrier burst to 550 µs then decrease space times a bit to get a proper output signal. Full source code for Arduino framework over PlatformIO can be downloaded here.