Why I Wanted My Own Climate Feed
I like numbers more than guesses. More than five winters ago I put a pair of TFA Dostmann temperature‑humidity sensors—one in the living room, one in the basement—just to see how quickly the house cooled overnight. The €20 plastic pucks had LCDs, but walking around recording values with a notebook got old fast. I needed the data in one place, continuously, and preferably on a dashboard.
The obvious solution—Wi‑Fi IoT gadgets—felt wrong: higher cost, batteries that last months not years, and proprietary clouds. Then I found baycom/tfrec, a tiny open‑source program by Georg Acher & Deti Fliegl that decodes the 868 MHz packets those sensors already broadcast. All it needs is the same RTL2832U DVB‑T dongle many of us have in a parts drawer. This tool decodes data sent by the KlimaLogg Pro and similar temperature sensors made by TFA Dostmann or other Technoline/LaCrosse-compatible sensors. Have a look at their sensors.txt, a wide range is supported, including weather sensors like wind or rain.
That was the eureka moment: Cheap hardware I already trust, software I can compile, and battery‑friendly sensors I can buy in any electronics store.
Bill of Materials
Item | Typical price | Notes |
---|---|---|
RTL2832U DVB‑T USB stick (with R820T tuner) | €20–30 | Acts as a general‑purpose Software Defined Radio (SDR) receiver. |
868 MHz temp/humidity sensor (TFA Dostmann / Technoline / La Crosse) | €15–20 | LCD read‑out, 2x AA batteries that last 4-5 years. Models for pools, wind, rain available. |
Linux host (Raspberry Pi, old laptop, VM) | from €15 (Pi Zero 2 W) | Any system that can run rtl_sdr. |
Optional: better 868 MHz antenna | €5–10 | The stock whip works across most houses; a bigger external antenna buys extra metres. |
I started with two sensors. When that worked, I added some more units over the years to cover most rooms in my house—twelve total today. The RF range comfortably covers my two‑floor + basement brick house with the stock DVB‑T antenna on a shelf. A great addition to my weather station.
Since a wide range of sensors is supported, you'll always find a suiting one for your needs.
Installing tfrec
sudo apt install git build-essential rtl-sdr # Debian/Ubuntu; Arch users know the drill
git clone https://github.com/baycom/tfrec
cd tfrec
make
sudo cp tfrec /usr/bin/ # or anywhere in $PATH
What tfrec does:
- Listens to raw I/Q samples from rtl_sdr.
- Detects the on‑off‑keying pattern used by KlimaLogg Pro‑compatible sensors.
- Prints each packet’s sensor ID, temperature °C, humidity %, RSSI, battery flag, and timestamp.
- Optionally executes a command, passing those values as arguments.
No kernel modules, no black magic—just libusb.
Running It Manually (First Sanity Check)
Run it with -d
for debug mode:
sudo /opt/bin/tfrec -d
You should see lines like:
16b4 +23.8 41 27 0 -60
92af +22.1 45 18 0 -63
ID + Temp + Humidity + Sequence + BatteryLow + SignalStrength(dB).
If nothing appears, move the antenna away from USB noise or check the sensor battery / error output.
Automating with systemd
I want:
- 60 s “listening windows” every 30 min
- automatic restart if rtl_sdr hangs (rare but happened a few times over the years, timeout is 180 s = 3 min)
- everything logged via
journalctl
Service Unit
We create a simple systemd service that runs our custom tfrec script at /etc/systemd/system/tfrec.service
:
[Unit]
Description=Receive 868 MHz climate data and pass to handler
[Service]
Type=simple
User=root
ExecStart=/bin/bash /opt/tfrec.sh
TimeoutStartSec=180
Timer Unit
And we create a suiting systemd timer which calls our service every 30 minutes (minute 0 and 30 of every hour) at /etc/systemd/system/tfrec.timer
:
[Unit]
Description=Run tfrec every 30 minutes
[Timer]
OnCalendar=*:0,30
AccuracySec=1min
Persistent=true
[Install]
WantedBy=timers.target
Enable both:
sudo systemctl daemon-reload
sudo systemctl enable --now tfrec.timer
The Wrapper Script
In our example, we put this script at /opt/tfrec.sh
. Put it anywhere logical and accessible.
#!/usr/bin/env bash
/usr/bin/tfrec -q -w 60 -e "php /var/www/cli.php climate:add"
exit 0
-q
quiet (no human output)-w 60
listen for 60 s-e
execute the PHP handler for every packet
Don't forget to chmod +x tfrec.sh
after saving it, so we can execute it.
Storing Readings with PHP
If you prefer Python or another language, skip to the next heading—the logic is the same: Have a script which accepts the arguments in order as tfrec adds them to your script, process them, ignore invalid entries and store / publish them.
My code is written for the Charm Framework but it's very close to Laravel and Symfony, you'll easily understand it.
CLI Command climate:add
I created a simple CLI command based on symfony/console. The full code is below. Highlights:
- Checks
Climate::$sensors
so unknown IDs are ignored. - Drops duplicates within 15 minutes (tfrec can see the same packet twice in one window).
- Saves to MySQL (or SQLite/PostgreSQL—Laravel's Eloquent abstracts it).
- Publishes fresh readings on MQTT via php-mqtt/client.
use App\Models\Climate;
use Carbon\Carbon;
use Charm\Bob\Command;
use Charm\Vivid\C;
use PhpMqtt\Client\ConnectionSettings;
use PhpMqtt\Client\MqttClient;
use Symfony\Component\Console\Input\InputArgument;
class ClimateAdd extends Command
{
protected function configure()
{
$this->setName("climate:add")
->setDescription("Store tfrec sensor reading")
->addArgument('id', InputArgument::REQUIRED)
->addArgument('temp', InputArgument::REQUIRED)
->addArgument('hum', InputArgument::REQUIRED)
->addArgument('seq', InputArgument::REQUIRED)
->addArgument('batfail', InputArgument::REQUIRED)
->addArgument('rssi', InputArgument::REQUIRED)
->addArgument('timestamp',InputArgument::OPTIONAL);
}
public function main(): bool
{
$id = $this->io->getArgument('id');
$temp = (double)$this->io->getArgument('temp');
$hum = (int)$this->io->getArgument('hum');
$batfail = (bool)$this->io->getArgument('batfail');
$rssi = (int)$this->io->getArgument('rssi');
$timestamp = $this->io->getArgument('timestamp');
// Validate sensor
if (!array_key_exists($id, Climate::$sensors)) {
return true;
}
// 15‑minute duplicate check
$recent = Climate::where('sensor_id', $id)
->where('time', '>=', Carbon::now()->subMinutes(15))
->first();
if ($recent) {
return true;
}
$c = new Climate();
$c->sensor_id = $id;
$c->temperature = str_replace("+", "", $temp);
$c->humidity = $hum;
$c->rssi = $rssi;
$c->low_battery = $batfail;
$c->time = Carbon::createFromTimestamp($timestamp);
$c->save();
$this->publishViaMqtt($c);
return true;
}
private function publishViaMqtt(Climate $c)
{
$client = new MqttClient(
C::Config()->get('connections:mqtt.server'),
C::Config()->get('connections:mqtt.port'),
C::Config()->get('connections:mqtt.client_id')
);
$settings = (new ConnectionSettings())
->setUsername(C::Config()->get('connections:mqtt.username'))
->setPassword(C::Config()->get('connections:mqtt.password'));
$client->connect($settings, true);
$client->publish("/tfrec/{$c->sensor_id}/temp", $c->temperature);
$client->publish("/tfrec/{$c->sensor_id}/hygro", $c->humidity);
$client->disconnect();
}
}
Database Model
class Climate extends Model
{
protected $table = 'tfrec';
protected $casts = ['time' => 'datetime'];
public $timestamps = false;
public static $sensors = [
'16b4' => 'Entrance',
'92af' => 'Kitchen',
'5d43' => 'Livingroom',
'6ac9' => 'Bedroom',
];
public static function getTableStructure(): \Closure
{
return function (Blueprint $table) {
$table->increments('id');
$table->string('sensor_id')->index();
$table->decimal('temperature', 4, 1);
$table->decimal('humidity', 4, 1);
$table->tinyInteger('rssi')->nullable();
$table->tinyInteger('low_battery')->nullable();
$table->dateTime('time');
};
}
}
Feel free to swap in SQLite for a lightweight setup or to just only publish the data via MQTT.
MQTT Integration: Why It Makes Life Easy
No matter how your script looks like, it makes sense that it pushes the climate data to MQTT, so you can use it easily across your apps:
- OpenHAB / Home Assistant subscribe to
/tfrec/#
and auto‑discover sensors. - Node‑RED can add thresholds, alerts, or InfluxDB logging with a drag‑and‑drop flow.
- Grafana graphs look great on a wall‑mounted tablet.
- Any language on any device can listen—MQTT is just lightweight TCP.
After import and a bit of configuration, my OpenHAB dashboard now shows the temperature and humidity on the room cards, provides graphs and more.
Reliability After 5+ Years
Aspect | Observation |
---|---|
Sensor battery | Batteries last 1–3 years, rarely need replacements (indoor, 20 °C). |
Packet loss | 1–2 drops per week on the farthest sensor; always reappears next interval. |
tfrec stability | One rtl_sdr hang every ~8-12 weeks—systemd watchdog handles it silently. |
Range | Stock antenna covers 15 m through two brick walls, more than enough for me. |
When a sensor does disappear it’s usually a weak battery; swapping it fixes reception instantly.
Why RTL‑SDR + DVB‑T Sticks Shine for This Job
- They’re everywhere. Any electronics hobby shop stocks them.
- Flexible. The same dongle can read ADS‑B aircraft beacons, pager traffic, or weather satellites—just change software.
- Cheap to replace. If it burns out, €20 buys a new one.
- Driver support.
rtl-sdr
is in every mainstream Linux distro, maintained, and well‑documented.
Wrap‑Up
With a handful of parts and an afternoon of tinkering, you can:
- Capture room‑level temperature & humidity continuously.
- Store data in your own database—no vendor lock‑in.
- Feed dashboards or automations through MQTT.
- Expand at will: pool temperature, wind speed, even rain gauges—tfrec already supports them.
It started as a two‑sensor experiment and grew into a house‑wide climate feed I trust. If you give it a try, let me know how it works out—and what you end up building on top. Reliable data has a habit of inspiring new ideas.
Happy hacking!
This post was created by myself with support from AI (GPT o3). Illustrations were generated by myself with Sora. Explore how AI can inspire your content – Neoground GmbH.
Noch keine Kommentare
Kommentar hinzufügen