Skip to content

Add protocol 278 'omni.c' device driver for microcontroller multisensor protocol #3278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 43 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
75ccbbf
Add Omni protocol
hdtodd May 10, 2025
3f0fdf9
Add Omni protocol
hdtodd May 10, 2025
5dc7fe3
Merge branch 'master' of https://github.com/merbanan/rtl_433
hdtodd May 18, 2025
f686aa3
Update comments for light intensity
hdtodd May 18, 2025
8e81fb5
Fix coding style
hdtodd May 18, 2025
6c8d665
Fix coding style
hdtodd May 18, 2025
ca701dc
Fix coding style
hdtodd May 18, 2025
b84c5ed
Correct 'const r_device omni'
hdtodd May 19, 2025
8474d4a
Correct 'const r_device omni'
hdtodd May 19, 2025
6f89084
Correct 'const r_device omni'
hdtodd May 19, 2025
e369f5a
Correct 'const r_device omni'
hdtodd May 19, 2025
3db0926
Correct 'const r_device omni'
hdtodd May 19, 2025
641f909
Correct 'const r_device omni'
hdtodd May 19, 2025
dfdc55d
Correct 'const r_device omni'
hdtodd May 19, 2025
be47e4a
Correct 'const r_device omni'
hdtodd May 19, 2025
9324d13
Correct 'const r_device omni'
hdtodd May 19, 2025
e72c02d
Correct 'const r_device omni'
hdtodd May 19, 2025
a61f3ef
Correct 'const r_device omni'
hdtodd May 19, 2025
a58873f
Correct 'const r_device omni'
hdtodd May 19, 2025
23d2b75
Correct 'const r_device omni'
hdtodd May 19, 2025
a996b24
Reinsert missing code for format 01
hdtodd May 25, 2025
b0ef345
remove tab char
hdtodd May 25, 2025
440cf9a
Merge branch 'merbanan:master' into master
hdtodd May 28, 2025
7b86fc1
Fix issues with PR
hdtodd Jun 1, 2025
07563fe
fixpull
hdtodd Jun 1, 2025
a5109f7
Address pull issues
hdtodd Jun 1, 2025
8c6bf45
remove trailing blanks
hdtodd Jun 1, 2025
8d70b53
Fix Pull #2
hdtodd Jun 2, 2025
b6b5bbd
Fix Pull #2
hdtodd Jun 2, 2025
fc27065
Fix Pull #2
hdtodd Jun 2, 2025
1329e29
Fix Pull #2
hdtodd Jun 2, 2025
b34dd96
Fix Pull #3
hdtodd Jun 3, 2025
80dee29
Fix Pull #3
hdtodd Jun 3, 2025
55276bc
Merge branch 'master' into master
hdtodd Jun 3, 2025
b9f21f7
merge with merbanan master
hdtodd Jun 3, 2025
d977e3d
Merge branch 'master' of http://github.com/hdtodd/rtl_433
hdtodd Jun 3, 2025
f9c9096
fixpull3
hdtodd Jun 3, 2025
195ede7
fixpull3
hdtodd Jun 3, 2025
33b32e0
Merge branch 'master' of https://github.com/merbanan/rtl_433
hdtodd Jun 4, 2025
ae15445
fixpull4
hdtodd Jun 4, 2025
da849fc
fixpull4
hdtodd Jun 4, 2025
523b81e
Correct >>4 shift in revised itemp calc
hdtodd Jun 7, 2025
4b2bd8d
Merge branch 'master' of https://github.com/merbanan/rtl_433
hdtodd Jun 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
[275] GM-Aftermarket TPMS
[276] RainPoint HCS012ARF Rain Gauge sensor
[277] Apator Metra E-RM 30
[278] Omni Multisensor

* Disabled by default, use -R n or a conf file to enable

Expand All @@ -372,7 +373,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
[-d <RTL-SDR USB device index>] (default: 0)
[-d :<RTL-SDR USB device serial (can be set with rtl_eeprom -s)>]
To set gain for RTL-SDR use -g <gain> to set an overall gain in dB.
SoapySDR device driver is available.
SoapySDR device driver is not available.
[-d ""] Open default SoapySDR device
[-d driver=rtlsdr] Open e.g. specific SoapySDR device
To set gain for SoapySDR use -g ELEM=val,ELEM=val,... e.g. -g LNA=20,TIA=8,PGA=2 (for LimeSDR).
Expand Down
1 change: 1 addition & 0 deletions conf/rtl_433.example.conf
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,7 @@ convert si
protocol 275 # GM-Aftermarket TPMS
protocol 276 # RainPoint HCS012ARF Rain Gauge sensor
protocol 277 # Apator Metra E-RM 30
protocol 278 # Omni Multisensor

## Flex devices (command line option "-X")

Expand Down
2 changes: 1 addition & 1 deletion include/rtl_433_devices.h
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@
DECL(tpms_gm) \
DECL(rainpoint_hcs012arf) \
DECL(apator_metra_erm30) \

DECL(omni) \
/* Add new decoders here. */

#define DECL(name) extern r_device name;
Expand Down
2 changes: 1 addition & 1 deletion man/man1/rtl_433.1
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ RTL\-SDR device driver is available.
To set gain for RTL\-SDR use \-g <gain> to set an overall gain in dB.
.RE
.RS
SoapySDR device driver is available.
SoapySDR device driver is not available.
.RE
.TP
[ \fB\-d\fI ""\fP ]
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ add_library(r_433 STATIC
devices/oil_standard.c
devices/oil_watchman.c
devices/oil_watchman_advanced.c
devices/omni.c
devices/opus_xt300.c
devices/oregon_scientific.c
devices/oregon_scientific_sl109h.c
Expand Down
243 changes: 243 additions & 0 deletions src/devices/omni.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
/** @file
Omni Multisensor.

Copyright (C) 2025 H. David Todd <[email protected]>

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
*/
/**
The 'sensor' is actually a programmed microcontroller, such as
Raspberry Pi Pico 2 or similar, with multiple possible data-sensor
attachments. A message 'format' field indicates the format of the data
packet being sent. The protocol, and this decoder, support up to
16 different message formats:
- Single transmission protocol
- Flexible 64-bit data payload field structure
- Two initial formats, extensible to a total of 16 possible formats.

NOTE: the rtl_433 decoder, omni.c, uses the "fmt" or "Format" field
here, as transmitted by omni.c, to decode the incoming message.
But, through omni.c, rtl_433 reports the packet format-field value
as "channel" in its published reporting (JSON, for example),
in keeping with the standard nomenclature and order of field-name
precedence used within rtl_433 for data fields.

The omni protocol is OOK modulated PWM with fixed period of 600μs
for data bits, preambled by four long startbit pulses of fixed period equal
to 1200μs. It is similar to the Lacrosse TX141TH-BV2.

A single data packet looks as follows:
1) preamble - 600μs high followed by 600μs low, repeated 4 times:

---- ---- ---- ----
| | | | | | | |
---- ---- ---- ----

2) a train of 80 data pulses with fixed 600μs period follows immediately:

--- -- -- --- --- -- ---
| | | | | | | | | | | | | |
-- --- --- -- -- --- -- ....

A logical 0 is 400μs of high followed by 200μs of low.
A logical 1 is 200μs of high followed by 400μs of low.

Thus, in the example pictured above the bits are 0 1 1 0 0 1 0 ...

The omni microcontroller sends 4 of identical packets of
4-pulse preamble followed by 80 data bits in a single burst, for a
total of 336 bits requiring ~212μs.

The last packet in a burst is followed by a postamble low
of at least 1250μs.

These 4-packet bursts repeat every 30 seconds.

The message in each packet is 10 bytes / 20 nibbles:

[fmt] [id] 16*[data] [crc8] [crc8]

- fmt is a 4-bit message data format identifier
- id is a 4-bit device identifier
- data are 16 nibbles = 8 bytes of data payload fields,
interpreted according to 'fmt'
- crc8 is 2 nibbles = 1 byte of CRC8 checksum of the first 9 bytes:
polynomial 0x97, init 0xaa

A format=0 message simply transmits the core temperature and input power
voltage of the microcontroller and is the format used if no data
sensor is present. For format=0 messages, the message
nibbles are to be read as:

fi tt t0 00 00 00 00 00 vv cc

f: format of datagram, 0-15
i: id of device, 0-15
t: Pico 2 core temperature: °C *10, 12-bit, 2's complement integer
0: bytes should be 0 (but aren't required to be)
v: (VCC-3.00)*100, as 8-bit integer, in volts: 3V00..5V55 volts
c: CRC8 checksum of bytes 1..9, initial remainder 0xaa,
divisor polynomial 0x97, no reflections or inversions

A format=1 message format is provided as a more complete example.
It uses the Bosch BME688 environmental sensor as a data source.
It is an indoor-outdoor temperature/humidity/pressure sensor, and the
message packet has the following fields:
indoor temp, outdoor temp, indoor humidity, outdoor humidity,
barometric pressure, sensor power VCC.
The data fields are binary values, 2's complement for temperatures.
For format=1 messages, the message nibbles are to be read as:

fi 11 12 22 hh gg pp pp vv cc

f: format of datagram, 0-15
i: id of device, 0-15
1: sensor 1 temp reading (e.g, indoor), °C *10, 12-bit, 2's complement integer
2: sensor 2 temp reading (e.g, outdoor), °C *10, 12-bit, 2's complement integer
h: sensor 1 humidity reading (e.g., indoor), %RH as 8-bit integer
g: sensor 2 humidity reading (e.g., outdoor), %RH as 8-bit integer
p: barometric pressure * 10, in hPa, as 16-bit integer, 0..6553.5 hPa
v: (VCC-3.00)*100, as 8-bit integer, in volts: 3V00..5V55 volts
c: CRC8 checksum of bytes 1..9, initial remainder 0xaa,
divisor polynomial 0x97, no reflections or inversions
*/

#include "decoder.h"

#define initCRC 0xaa
#define OMNI_MSGFMT_00 0x00
#define OMNI_MSGFMT_01 0x01

static int omni_decode(r_device *decoder, bitbuffer_t *bitbuffer)
{
// Find a row that's a candidate for decoding
int r = bitbuffer_find_repeated_row(bitbuffer, 2, 80);

if (r < 0 || bitbuffer->bits_per_row[r] > 82) {
decoder_log(decoder, 1, __func__, "omni: Invalid message");
return DECODE_ABORT_LENGTH;
};

// OK, that's our message buffer for decoding
uint8_t *b = bitbuffer->bb[r];

// Validate the packet against the CRC8 checksum
if (crc8(b, 9, 0x97, initCRC) != b[9]) {
decoder_log(decoder, 1, __func__, "omni: CRC8 checksum error");
return DECODE_FAIL_MIC;
}

// OK looks like we have a valid packet. What format?
int message_fmt = b[0] >> 4;
int id = b[0] & 0x0F;
char hexstring[50];
char *ptr;
data_t *data;

// Decode that format, if we know it
switch (message_fmt) {

// Default to fmt=00 so unformatted message data are reported in hex
case OMNI_MSGFMT_00:
ptr = &hexstring[0];
// print just the 8 data bytes as payload data
for (int ij = 1; ij < 9; ij++)
ptr += sprintf(ptr, "%02x", b[ij]);
double itemp_c = (double)(((int16_t)( b[1]<<8 | b[2] )) >> 4) * 0.10;
double volts = ((double)(b[8])) * 0.01 + 3.00;
// Make the data descriptor
/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Omni-Multisensor",
"id", "Id", DATA_INT, id,
"channel", "Format", DATA_INT, message_fmt,
"temperature_C" , "Core Temperature", DATA_FORMAT, "%.2f ˚C", DATA_DOUBLE, itemp_c,
"voltage_V", "VCC voltage", DATA_FORMAT, "%.2f V", DATA_DOUBLE, volts,
"payload", "Payload", DATA_STRING, hexstring,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
break;

case OMNI_MSGFMT_01:
itemp_c = (double)(((int16_t)( b[1]<<8 | b[2] )) >> 4) * 0.10;
double otemp_c = (double) (((int16_t)(b[2]<<12 | b[3]<<4 )) >> 4) * 0.10;
double ihum = (double)b[4];
double light = (double)b[5];
double press = (double)(((uint16_t)(b[6] << 8)) | b[7]) * 0.10;
volts = ((double)(b[8])) * 0.01 + 3.00;
// Make the data descriptor
/* clang-format off */
data = data_make(
"model", "", DATA_STRING, "Omni-Multisensor",
"id", "Id", DATA_INT, id,
"channel", "Format", DATA_INT, message_fmt,
"temperature_C" , "Indoor Temperature", DATA_FORMAT, "%.2f ˚C", DATA_DOUBLE, itemp_c,
"temperature_2_C", "Outdoor Temperature",DATA_FORMAT, "%.2f ˚C", DATA_DOUBLE, otemp_c,
"humidity", "Indoor Humidity", DATA_FORMAT, "%.0f %%", DATA_DOUBLE, ihum,
"light_pct", "Light", DATA_FORMAT, "%.0f %%", DATA_DOUBLE, light,
"pressure_hPa", "BarometricPressure", DATA_FORMAT, "%.1f hPa", DATA_DOUBLE, press,
"voltage_V", "VCC voltage", DATA_FORMAT, "%.2f V", DATA_DOUBLE, volts,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
/* clang-format on */
break;

default:
// Print the whole 10 bytes in hex, with id and fmt explicit
ptr = &hexstring[0];
// print just the 8 data bytes as payload data
for (int ij = 1; ij < 9; ij++)
ptr += sprintf(ptr, "%02x", b[ij]);
// Make the data descriptor
data = data_make(
"model", "", DATA_STRING, "Omni-Multisensor",
"id", "Id", DATA_INT, id,
"channel", "Format", DATA_INT, message_fmt,
"payload", "Payload", DATA_STRING, hexstring,
"mic", "Integrity", DATA_STRING, "CRC",
NULL);
break;

/* New decoders follow here */
break;

};

// And output the field values
decoder_output_data(decoder, data);
return 1; // if we got here, one valid message was returned
}

// List the output fields for various message types
static char const *const output_fields[] = {
"model",
"fmt",
"id",
"temperature_C",
"temperature_2_C",
"humidity",
"pressure_hPa",
"light_pct",
"voltage_V",
"payload",
"mic",
NULL,
};

/* clang-format off */
r_device const omni = {
.name = "Omni Multisensor",
.modulation = OOK_PULSE_PWM,
.short_width = 200, // short pulse is ~200 us
.long_width = 400, // long pulse is ~400 us
.sync_width = 600, // sync pulse is ~600 us
.gap_limit = 500, // long gap (with short pulse) is ~400 us, sync gap is ~600 us
.reset_limit = 1250, // maximum gap is 1250 us (long gap + longer sync gap on last repeat)
.decode_fn = &omni_decode,
.fields = output_fields,
};
/* clang-format on */
Loading