Decode radio transmissions from devices on the ISM bands in OCaml
1(* Tests for rtl433 library *)
2
3module Util = Rtl433.Util
4module Bitbuffer = Rtl433.Bitbuffer
5module Data = Rtl433.Data
6module Device = Rtl433.Device
7
8(* Test CRC-8 calculation *)
9let test_crc8 () =
10 let data = Bytes.of_string "\x51\x12\x34\x56\x05\x32\x01\x00\x00\x05\x00\x00" in
11 let crc = Util.crc8 ~poly:0x31 ~init:0x00 data 12 in
12 Printf.printf "CRC-8 of test data: 0x%02x\n" crc;
13 assert (crc >= 0 && crc <= 255)
14
15(* Test add_bytes checksum *)
16let test_add_bytes () =
17 let data = Bytes.of_string "\x01\x02\x03\x04\x05" in
18 let sum = Util.add_bytes data 5 in
19 Printf.printf "Sum of bytes 1+2+3+4+5: %d (expected 15)\n" sum;
20 assert (sum = 15)
21
22(* Test bitbuffer operations *)
23let test_bitbuffer () =
24 let bb = Bitbuffer.create () in
25 (* Add 8 bits to create a row *)
26 for i = 0 to 7 do
27 Bitbuffer.add_bit bb (if i mod 2 = 0 then 1 else 0)
28 done;
29 Printf.printf "Bitbuffer test: created buffer with %d rows, %d bits in row 0\n"
30 (Bitbuffer.num_rows bb)
31 (Bitbuffer.bits_per_row bb 0);
32 assert (Bitbuffer.num_rows bb > 0);
33 assert (Bitbuffer.bits_per_row bb 0 = 8)
34
35(* Add a byte to the bitbuffer, MSB first *)
36let add_byte_to_bitbuffer bb byte =
37 for j = 7 downto 0 do
38 Bitbuffer.add_bit bb ((byte lsr j) land 1)
39 done
40
41(* Create a valid WH51 packet for testing *)
42let create_wh51_packet ~device_id ~moisture ~battery_mv =
43 let data = Bytes.create 14 in
44 (* Family code *)
45 Bytes.set_uint8 data 0 0x51;
46 (* Device ID (24 bits) *)
47 Bytes.set_uint8 data 1 ((device_id lsr 16) land 0xff);
48 Bytes.set_uint8 data 2 ((device_id lsr 8) land 0xff);
49 Bytes.set_uint8 data 3 (device_id land 0xff);
50 (* Battery status (OK) + counter *)
51 Bytes.set_uint8 data 4 0x05;
52 (* Moisture *)
53 Bytes.set_uint8 data 5 moisture;
54 (* AD raw value *)
55 Bytes.set_uint8 data 6 0x01;
56 Bytes.set_uint8 data 7 0x00;
57 (* Boost *)
58 Bytes.set_uint8 data 8 0x00;
59 (* Battery voltage raw *)
60 let battery_raw = battery_mv / 2 in
61 Bytes.set_uint8 data 9 ((battery_raw lsr 8) land 0xff);
62 Bytes.set_uint8 data 10 (battery_raw land 0xff);
63 (* Sequence *)
64 Bytes.set_uint8 data 11 0x00;
65 (* CRC-8 *)
66 let crc = Util.crc8 ~poly:0x31 ~init:0x00 data 12 in
67 Bytes.set_uint8 data 12 crc;
68 (* Checksum *)
69 let sum = Util.add_bytes data 13 in
70 Bytes.set_uint8 data 13 sum;
71 data
72
73(* Test WH51 decoder with synthetic packet *)
74let test_wh51_decoder () =
75 Printf.printf "\n=== Testing WH51 decoder ===\n";
76
77 (* Create a valid WH51 packet *)
78 let packet = create_wh51_packet ~device_id:0x123456 ~moisture:50 ~battery_mv:3000 in
79
80 Printf.printf "Created WH51 packet: ";
81 for i = 0 to 13 do
82 Printf.printf "%02x " (Bytes.get_uint8 packet i)
83 done;
84 Printf.printf "\n";
85
86 (* Create bitbuffer with preamble + packet *)
87 let bb = Bitbuffer.create () in
88
89 (* Add preamble: 0xAA 0x2D 0xD4 *)
90 add_byte_to_bitbuffer bb 0xAA;
91 add_byte_to_bitbuffer bb 0x2D;
92 add_byte_to_bitbuffer bb 0xD4;
93
94 (* Add packet data *)
95 for i = 0 to 13 do
96 add_byte_to_bitbuffer bb (Bytes.get_uint8 packet i)
97 done;
98
99 Printf.printf "Created bitbuffer with %d rows, %d bits in row 0\n"
100 (Bitbuffer.num_rows bb)
101 (Bitbuffer.bits_per_row bb 0);
102
103 (* Create decoder and run it *)
104 let decoders = Rtl433.builtin_decoders () in
105 let wh51_decoder = List.hd decoders in
106 Printf.printf "Running decoder: %s\n" wh51_decoder.Device.name;
107
108 let result = wh51_decoder.Device.decode () bb in
109
110 (match result with
111 | Device.Decoded n ->
112 Printf.printf "SUCCESS: Decoded %d message(s)\n" n;
113 (match Data.get_last_output () with
114 | Some data ->
115 Printf.printf "Output JSON: %s\n" (Data.to_json data);
116 Data.clear_last_output ()
117 | None ->
118 Printf.printf "WARNING: No output data captured\n")
119 | Device.Abort_length ->
120 Printf.printf "FAILED: Abort_length (not enough bits)\n"
121 | Device.Abort_early ->
122 Printf.printf "FAILED: Abort_early (no preamble found)\n"
123 | Device.Fail_sanity ->
124 Printf.printf "FAILED: Fail_sanity (invalid family code)\n"
125 | Device.Fail_mic ->
126 Printf.printf "FAILED: Fail_mic (CRC or checksum error)\n");
127
128 Printf.printf "=== WH51 decoder test complete ===\n"
129
130(* Test JSON output *)
131let test_json_output () =
132 Printf.printf "\n=== Testing JSON output ===\n";
133 let data = Data.make [
134 ("model", None, Data.String "Test-Device");
135 ("id", None, Data.String "abc123");
136 ("temperature_C", Some "Temperature", Data.Float 23.5);
137 ("humidity", Some "Humidity", Data.Int 65);
138 ] in
139 let json = Data.to_json data in
140 Printf.printf "JSON output: %s\n" json;
141 assert (String.length json > 0);
142 Printf.printf "=== JSON output test complete ===\n"
143
144let () =
145 Printf.printf "rtl433 library tests\n";
146 Printf.printf "====================\n\n";
147
148 test_crc8 ();
149 test_add_bytes ();
150 test_bitbuffer ();
151 test_json_output ();
152 test_wh51_decoder ();
153
154 Printf.printf "\nAll tests passed!\n"