Skip to content

Commit 6487006

Browse files
icockettmerbanan
authored andcommitted
Add support for RFM69 LowPowerLab Moteino
1 parent a8f1541 commit 6487006

5 files changed

Lines changed: 134 additions & 1 deletion

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,7 @@ See [CONTRIBUTING.md](./docs/CONTRIBUTING.md).
405405
[315] Alps FWB1U545 Car Remote
406406
[316] Continental KR5V2X Car Remote (-f 313.8M -s 1024k)
407407
[317] Code Alarm FRDPC2002 Car Remote
408+
[318] RFM69 LowPowerLab Moteino board (-s 1000k)
408409
409410
* Disabled by default, use -R n or a conf file to enable
410411

conf/rtl_433.example.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,7 @@ convert si
556556
protocol 315 # Alps FWB1U545 Car Remote
557557
protocol 316 # Continental KR5V2X Car Remote (-f 313.8M -s 1024k)
558558
protocol 317 # Code Alarm FRDPC2002 Car Remote
559+
protocol 318 # RFM69 LowPowerLab Moteino board (-s 1000k)
559560

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

include/rtl_433_devices.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,7 @@
325325
DECL(alps_fwb1u545_car_remote) \
326326
DECL(continental_car_remote) \
327327
DECL(code_alarm_frdpc2000_car_remote) \
328-
328+
DECL(rfm69_lowpowerlab_moteino) \
329329
/* Add new decoders here. */
330330

331331
#define DECL(name) extern r_device name;

src/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ add_library(r_433 STATIC
232232
devices/regency_fan.c
233233
devices/revolt_nc5462.c
234234
devices/revolt_zx7717.c
235+
devices/rfm69_lowpowerlab_moteino.c
235236
devices/rftech.c
236237
devices/risco_agility.c
237238
devices/rojaflex.c
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/** @file
2+
RFM69 decoder as used on LowPowerLabs Moteino boards
3+
4+
Copyright (C) 2025 Ian Cockett <cockettian@gmail.com>
5+
6+
This program is free software; you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation; either version 2 of the License, or
9+
(at your option) any later version.
10+
*/
11+
12+
#include "decoder.h"
13+
14+
/**
15+
16+
Generic decoder for RFM69 radio modules as used on LowPowerLab.com Moteino boards.
17+
18+
rtl_433 -s 1000k
19+
20+
Test data captured with sample sketch https://github.com/LowPowerLab/RFM69/blob/master/Examples/Node/Node.ino
21+
22+
Encryption must be disabled in the sketch (comment out #define ENCRYPTKEY)
23+
Data captures from 433MHz RFM69HW_HCW board, but 868MHz models should be similar
24+
25+
Protocol description:-
26+
27+
- Preamble aaaaaa
28+
- Sync word 2d64
29+
- Header byte 1 - Length Byte
30+
- Header byte 2 - Dest Address
31+
- Header byte 3 - Src Address
32+
- Header byte 4 - Control byte (not sure what this does)
33+
- n bytes variable length message.
34+
- CRC16 checksum
35+
36+
*/
37+
38+
#define LENGTH_POS 5
39+
#define NODE_ID_POS 7
40+
#define DATA_START_POS 9
41+
42+
#define HEADER_LENGTH 6
43+
#define MAX_LENGTH 65
44+
#define BUF_LENGTH 72
45+
46+
static int rfm69_fsk_callback(r_device *decoder, bitbuffer_t *bitbuffer)
47+
{
48+
data_t *data;
49+
50+
uint8_t search = 0x2d; // sync byte to scan for
51+
52+
uint8_t message[BUF_LENGTH]; // max size of header + payload + terminator
53+
uint8_t payload[MAX_LENGTH]; // max size of payload + terminator
54+
55+
unsigned posn = bitbuffer_search(bitbuffer, 0, 0, &search, 8);
56+
57+
if ((posn < 24) || (posn > 28))
58+
return 0; // Can't find bit position of sync word
59+
60+
bitbuffer_extract_bytes(bitbuffer, 0, posn - 24, (uint8_t *)&message, (MAX_LENGTH) * 8); // Extract out full into our aligned buffer. Include 3x8 bits of sync word before the preamble
61+
62+
uint8_t payload_len = message[LENGTH_POS];
63+
64+
if (payload_len > MAX_LENGTH)
65+
return 0; // message junk
66+
67+
bitbuffer_extract_bytes(bitbuffer, 0, posn + 16, (uint8_t *)&payload, (payload_len + 1) * 8); // we need to include length byte in CRC calc
68+
69+
// found the polynomial values in an old Semtech application note.
70+
uint16_t crc = ~crc16(payload, (payload_len + 1), 0x1021, 0x1d0f) & 0xffff;
71+
72+
if (((crc >> 8) != message[HEADER_LENGTH + payload_len + 0]) ||
73+
((crc & 0x00ff) != message[HEADER_LENGTH + payload_len + 1])) {
74+
fprintf(stderr, "CRC NOT OK!\n");
75+
return 0;
76+
}
77+
78+
if (message[NODE_ID_POS] == 0x02) {
79+
message[HEADER_LENGTH + payload_len] = 0x00; // remove the checksum which is still at the end of the message buffer
80+
81+
char strNode[2];
82+
char strGateway[2];
83+
char strMessage[32];
84+
85+
sprintf(strGateway, "%d", (message[HEADER_LENGTH]));
86+
sprintf(strNode, "%d", (message[NODE_ID_POS]));
87+
sprintf(strMessage, "%.30s", &message[DATA_START_POS]);
88+
89+
/* clang-format off */
90+
data = data_make(
91+
"model", "Model", DATA_STRING, "Moteino-RFM69",
92+
"id", "Node Id ", DATA_STRING, strNode,
93+
"gateway_id", "Gateway Id", DATA_STRING, strGateway,
94+
"msg", "Message", DATA_STRING, strMessage,
95+
"mic", "Integrity", DATA_STRING, "CRC",
96+
NULL);
97+
/* clang-format on */
98+
99+
decoder_output_data(decoder, data);
100+
101+
return 1;
102+
}
103+
104+
if (message[NODE_ID_POS] == 0xff) // your node id
105+
{
106+
// Add your own stuff
107+
}
108+
109+
return 0;
110+
}
111+
112+
static char const *const output_fields[] = {
113+
"model",
114+
"gateway_id",
115+
"id",
116+
"msg",
117+
"mic",
118+
NULL,
119+
};
120+
121+
r_device const rfm69_lowpowerlab_moteino = {
122+
.name = "RFM69 LowPowerLab Moteino board (-s 1000k)",
123+
.modulation = FSK_PULSE_PCM,
124+
.short_width = 18,
125+
.long_width = 18,
126+
.reset_limit = 400,
127+
.decode_fn = &rfm69_fsk_callback,
128+
.fields = output_fields,
129+
130+
};

0 commit comments

Comments
 (0)