Refactor firmware for clarity; no functional changes

- main.c: file-header comment describing the hardware and operation, logic
  split into named phases (sleep_until_interrupt, wake_peripheral_rails,
  take_counts_snapshot, send_wheel_counts_report, handle_minute_alarm,
  init_all_hardware); shared state renamed to say what it is and made static.
- rfm69.c: reorganized into six labeled sections; cond_1/2/3 and hash scratch
  globals replaced by a reply_acknowledges() helper with clear locals; packet
  layout and every init register write documented.
- LOG() macro (compiled out when DO_UART is off) replaces the #if DO_UART
  blocks that obscured the logic.
- Drivers: file-header comments; named RTC_REG_*/RTC_ALM_MASK_BIT constants;
  EEPROM spool scheme documented; ADC_CHANNEL_BANDGAP named; repeated pin
  if/else helpers collapsed to SET_PIN_TO().
- Removed unused globals/buffers and commented-out code; ran clang-format
  with the project style.

Register writes and radio protocol are byte-identical. Builds clean under
-Wall -Wextra on gnu17 and c23; flash 13028 -> 12830 B, static RAM
1038 -> 999 B.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVJKatfeMJjAmuH9KYiLuv
This commit is contained in:
2026-08-31 23:04:00 -04:00
parent ed476473b6
commit bbdcc1e623
25 changed files with 825 additions and 1798 deletions
+288 -270
View File
@@ -1,142 +1,19 @@
// RFM69 packet radio driver, plus the node's send-with-acknowledgement
// protocol and the wheel-counts packet builder.
//
// Layout of this file:
// 1. Register access over SPI
// 2. Mode control and status waits
// 3. Raw packet write/read (FIFO)
// 4. Acknowledged send protocol
// 5. Wheel-counts packet builder and hashes
// 6. Radio configuration (rfm69_init)
#include "rfm69.h"
uint32_t msg_hash;
uint8_t p_hash_1;
uint8_t p_hash_2;
uint8_t p_hash_3;
uint8_t c_hash_1;
uint8_t c_hash_2;
uint8_t c_hash_3;
bool cond_1;
bool cond_2;
bool cond_3;
DATA_SEND_STATUS send_message(tx_rx_data_struct tx_data)
{
rfm69_write_msg(tx_data);
p_hash_1 = tx_data.msg[57];
p_hash_2 = tx_data.msg[58];
p_hash_3 = tx_data.msg[59];
for (uint8_t i = 0; i < 10; i++) {
bool result = wait_rx_payload_ready_timeout(50);
if (result) {
RX_DATA = rfm69_read_msg();
#if DO_UART
uart_sendString("RX DATA\n");
uart_print_tx_rx_data(RX_DATA);
#endif
c_hash_1 = RX_DATA.msg[0];
c_hash_2 = RX_DATA.msg[1];
c_hash_3 = RX_DATA.msg[2];
cond_1 = (p_hash_1 == c_hash_1) && (p_hash_2 == c_hash_2) && (p_hash_3 == c_hash_3);
cond_2 = (tx_data.from == RX_DATA.to) && (tx_data.to == RX_DATA.from);
cond_3 = cond_1 && cond_2;
if (cond_3 && (RX_DATA.flags == MSG_RECV_COUNTS_SUCCESS) && (RX_DATA.msg[3] == 0xFF)) {
#if DO_UART
uart_sendString(" RX DATA SUCCESS\n");
#endif
return DATA_SEND_SUCCESS;
}
#if DO_UART
else if (
cond_3 && (RX_DATA.flags == MSG_RECV_COUNTS_FAIL) && (RX_DATA.msg[3] == 0x00)) {
uart_sendString(" RX DATA FAILED\n");
} else {
uart_sendString(" RX DATA ANOTHER ERROR\n");
}
#endif
}
}
return DATA_NOT_SENT;
}
void uart_print_tx_rx_data(tx_rx_data_struct tx_rx_print)
{
// uart_print_uint8(tx_rx_print.len, "LEN; ");
// uart_print_uint8(tx_rx_print.to, "TO;");
// uart_print_uint8(tx_rx_print.from, "FROM;");
// uart_print_uint8(tx_rx_print.dtype, "DTYPE;");
// uart_print_uint8(tx_rx_print.flags, "FLAGS;");
DATA_BUFFER_7[0] = tx_rx_print.len;
DATA_BUFFER_7[1] = tx_rx_print.to;
DATA_BUFFER_7[2] = tx_rx_print.from;
DATA_BUFFER_7[3] = tx_rx_print.dtype;
DATA_BUFFER_7[4] = tx_rx_print.flags;
uart_sendString(" ");
uart_print_uint8_array(DATA_BUFFER_7, 5, "LEN,TO,FROM,DTYPE,FLAGS\n");
uart_sendString(" ");
uart_sendStringArray(tx_rx_print.msg, 20);
uart_sendChar('\n');
uart_sendString(" ");
uart_print_uint8_array(tx_rx_print.msg, tx_rx_print.len, "\n");
}
void rfm69_write_msg(tx_rx_data_struct txrxd)
{
// TxStart is configured as FifoNotEmpty, so the radio begins transmitting
// the moment the first byte lands. Fill the FIFO from standby and only then
// switch to TX, otherwise the packet goes out ahead of its own payload.
set_rfm69_standby();
spi_rfm69_select(true);
spi_write(REG_FIFO | RFM69_SPI_WRITE);
if (txrxd.len > (60)) {
txrxd.len = 60;
}
spi_write(txrxd.len + 4);
spi_write(txrxd.to);
spi_write(txrxd.from);
spi_write(txrxd.dtype);
spi_write(txrxd.flags);
for (uint8_t x = 0; x < txrxd.len; x++) {
spi_write(txrxd.msg[x]);
}
spi_rfm69_select(false);
set_rfm69_tx_mode();
wait_tx_sent();
set_rfm69_rx_mode();
}
tx_rx_data_struct rfm69_read_msg(void)
{
memset(RX_DATA.msg, ' ', sizeof(RX_DATA.msg));
spi_rfm69_select(true);
spi_write(REG_FIFO);
uint8_t raw_len = spi_read();
RX_DATA.to = spi_read();
RX_DATA.from = spi_read();
RX_DATA.dtype = spi_read();
RX_DATA.flags = spi_read();
// The length byte comes off the air and is not trustworthy: below 4 it
// underflows to ~252, above 60 it walks off the end of msg[].
uint8_t len_f = (raw_len < 4) ? 0 : (uint8_t)(raw_len - 4);
if (len_f > sizeof(RX_DATA.msg)) {
len_f = sizeof(RX_DATA.msg);
}
RX_DATA.len = len_f;
for (uint8_t idx_f = 0; idx_f < len_f; idx_f++) {
RX_DATA.msg[idx_f] = spi_read();
}
spi_rfm69_select(false);
set_rfm69_idle();
return RX_DATA;
}
// ---------------------------------------------------------------------------
// 1. Register access over SPI ("_rt" = register transfer)
// ---------------------------------------------------------------------------
uint8_t spi_read_rfm69_rt(uint8_t reg)
{
@@ -167,95 +44,40 @@ uint8_t spi_write_rfm69_multiple_rt(uint8_t reg, const char* vals, uint8_t len)
return data_init;
}
// name (max 10), 10
// diameter (max 10), 20
// battery_value 16-bit, 22
// time_reading (min) 23
// time_reading (hour) 24
// time_reading (day) 25
// time_reading (month) 26
// time_reading (year) 27
// 15 * per-min +30 57
// three byte hash check 3
// ---------------------------------------------------------------------------
// 2. Mode control and status waits
//
// Every wait has a bail-out: an absent or unpowered radio must not hang the
// firmware, since no watchdog reset is armed.
// ---------------------------------------------------------------------------
tx_rx_data_struct generate_wheel_counts_message(
identifier_results idd, time_struct time, uint16_t battery_value, volatile uint16_t counts[15])
void reset_rfm69(void)
{
reset_txrx_struct(&TX_DATA);
memcpy(TX_DATA.msg, idd.name_str, MIN(10, idd.name_len));
memcpy(TX_DATA.msg + 10, idd.diameter_str, MIN(10, idd.diameter_len));
TX_DATA.msg[20] = battery_value & 0xFF;
TX_DATA.msg[21] = (battery_value >> 8) & 0xFF;
TX_DATA.msg[22] = time.Minute;
TX_DATA.msg[23] = time.Hour;
TX_DATA.msg[24] = time.Day;
TX_DATA.msg[25] = time.Month;
TX_DATA.msg[26] = time.Year;
for (uint8_t idx = 0; idx < 15; idx++) {
TX_DATA.msg[26 + (2 * idx + 1)] = counts[idx] & 0xFF; // LSB first
TX_DATA.msg[26 + (2 * idx + 2)] = (counts[idx] >> 8) & 0xFF; // MSB second
}
msg_hash = hash_3bytes(TX_DATA.msg, 57);
TX_DATA.msg[57] = msg_hash & 0xFF;
TX_DATA.msg[58] = (msg_hash >> 8) & 0xFF;
TX_DATA.msg[59] = (msg_hash >> 16) & 0xFF;
TX_DATA.len = sizeof(TX_DATA.msg);
TX_DATA.flags = MSG_SEND_COUNTS;
TX_DATA.from = idd.hashed;
TX_DATA.to = 255;
TX_DATA.dtype = MSG_TYPE_BINARY;
return TX_DATA;
}
uint32_t hash_3bytes(unsigned const char* str, uint8_t str_len)
{
uint32_t hash = 0;
for (uint8_t i = 0; i < str_len; i++) {
hash = (hash * 31 + str[i]) % 0xFFFFFF;
}
return hash;
}
void set_rfm69_power_amp_boost(void)
{
spi_write_rfm69_rt(REG_OCP, VAL_OCP_OFF);
spi_write_rfm69_rt(REG_TEST_PA1, VAL_TEST_PA1_BOOST);
spi_write_rfm69_rt(REG_TEST_PA2, VAL_TEST_PA2_BOOST);
}
void set_rfm69_power_amp_normal(void)
{
spi_write_rfm69_rt(REG_TEST_PA1, VAL_TEST_PA1_NORMAL);
spi_write_rfm69_rt(REG_TEST_PA2, VAL_TEST_PA2_NORMAL);
spi_write_rfm69_rt(REG_OCP, VAL_OCP_ON);
}
void reset_txrx_struct(tx_rx_data_struct* s)
{
s->len = 0;
s->to = 255;
s->from = 255;
s->dtype = 0;
s->flags = 0;
memset(s->msg, ' ', 60);
rfm69_reset_state(true); // Reset line is active high
_delay_ms(10);
rfm69_reset_state(false);
_delay_ms(10);
}
void set_rfm69_mode(uint8_t target_mode)
{
uint8_t mode = spi_read_rfm69_rt(REG_OP_MODE);
mode &= ~VAL_OPMODE_MASK;
mode |= (target_mode & VAL_OPMODE_MASK);
spi_write_rfm69_rt(REG_OP_MODE, mode);
}
bool wait_rfm69_mode_ready(void)
{
for (uint16_t attempts = 0; attempts < RFM69_TIMEOUT_MS; attempts++) {
if (MODE_READY) {
return true;
}
_delay_ms(1);
}
return false;
}
bool wait_tx_sent(void)
{
for (uint16_t attempts = 0; attempts < RFM69_TIMEOUT_MS; attempts++) {
@@ -267,18 +89,6 @@ bool wait_tx_sent(void)
return false;
}
uint8_t hash(const char* str, uint8_t min, uint8_t max)
{
unsigned int hash = 0;
while (*str) {
hash = (hash * 31) + (unsigned char)(*str);
str++;
}
unsigned int range = max - min + 1;
return (hash % range) + min;
}
bool wait_rx_payload_ready_timeout(uint16_t attempts)
{
set_rfm69_rx_mode();
@@ -293,20 +103,22 @@ bool wait_rx_payload_ready_timeout(uint16_t attempts)
return RX_PAYLOAD_READY != 0;
}
bool wait_rx_payload_ready(void)
bool wait_rx_payload_ready(void) { return wait_rx_payload_ready_timeout(RFM69_TIMEOUT_MS); }
// The PA boost registers are only allowed during TX; OCP must be off for the
// +20 dBm path, per the datasheet's high-power sequence.
void set_rfm69_power_amp_boost(void)
{
return wait_rx_payload_ready_timeout(RFM69_TIMEOUT_MS);
spi_write_rfm69_rt(REG_OCP, VAL_OCP_OFF);
spi_write_rfm69_rt(REG_TEST_PA1, VAL_TEST_PA1_BOOST);
spi_write_rfm69_rt(REG_TEST_PA2, VAL_TEST_PA2_BOOST);
}
bool wait_rfm69_mode_ready(void)
void set_rfm69_power_amp_normal(void)
{
for (uint16_t attempts = 0; attempts < RFM69_TIMEOUT_MS; attempts++) {
if (MODE_READY) {
return true;
}
_delay_ms(1);
}
return false;
spi_write_rfm69_rt(REG_TEST_PA1, VAL_TEST_PA1_NORMAL);
spi_write_rfm69_rt(REG_TEST_PA2, VAL_TEST_PA2_NORMAL);
spi_write_rfm69_rt(REG_OCP, VAL_OCP_ON);
}
void set_rfm69_tx_mode(void)
@@ -337,61 +149,267 @@ void set_rfm69_sleep(void)
wait_rfm69_mode_ready();
}
void set_rfm69_idle(void)
// "Idle" between packets is just standby
void set_rfm69_idle(void) { set_rfm69_standby(); }
// ---------------------------------------------------------------------------
// 3. Raw packet write/read
//
// On-air packet layout (variable-length mode, CRC on):
// [len] [to] [from] [dtype] [flags] [msg bytes ...]
// where len counts everything after itself, so msg length + 4 header bytes.
// ---------------------------------------------------------------------------
#define PACKET_HEADER_LEN 4
void reset_txrx_struct(tx_rx_data_struct* s)
{
set_rfm69_power_amp_normal();
set_rfm69_mode(VAL_OPMODE_STDBY);
wait_rfm69_mode_ready();
s->len = 0;
s->to = 255;
s->from = 255;
s->dtype = 0;
s->flags = 0;
memset(s->msg, ' ', sizeof(s->msg));
}
void reset_rfm69(void)
void rfm69_write_msg(tx_rx_data_struct txrxd)
{
rfm69_reset_state(true);
_delay_ms(10);
rfm69_reset_state(false);
_delay_ms(10);
// TxStart is configured as FifoNotEmpty, so the radio begins transmitting
// the moment the first byte lands. Fill the FIFO from standby and only then
// switch to TX, otherwise the packet goes out ahead of its own payload.
set_rfm69_standby();
if (txrxd.len > sizeof(txrxd.msg)) {
txrxd.len = sizeof(txrxd.msg);
}
spi_rfm69_select(true);
spi_write(REG_FIFO | RFM69_SPI_WRITE);
spi_write(txrxd.len + PACKET_HEADER_LEN);
spi_write(txrxd.to);
spi_write(txrxd.from);
spi_write(txrxd.dtype);
spi_write(txrxd.flags);
for (uint8_t x = 0; x < txrxd.len; x++) {
spi_write(txrxd.msg[x]);
}
spi_rfm69_select(false);
set_rfm69_tx_mode();
wait_tx_sent();
set_rfm69_rx_mode();
}
tx_rx_data_struct rfm69_read_msg(void)
{
memset(RX_DATA.msg, ' ', sizeof(RX_DATA.msg));
spi_rfm69_select(true);
spi_write(REG_FIFO);
uint8_t raw_len = spi_read();
RX_DATA.to = spi_read();
RX_DATA.from = spi_read();
RX_DATA.dtype = spi_read();
RX_DATA.flags = spi_read();
// The length byte comes off the air and is not trustworthy: below 4 it
// underflows to ~252, above 60 it walks off the end of msg[].
uint8_t msg_len = (raw_len < PACKET_HEADER_LEN) ? 0 : (uint8_t)(raw_len - PACKET_HEADER_LEN);
if (msg_len > sizeof(RX_DATA.msg)) {
msg_len = sizeof(RX_DATA.msg);
}
RX_DATA.len = msg_len;
for (uint8_t idx = 0; idx < msg_len; idx++) {
RX_DATA.msg[idx] = spi_read();
}
spi_rfm69_select(false);
set_rfm69_idle();
return RX_DATA;
}
// ---------------------------------------------------------------------------
// 4. Acknowledged send protocol
//
// Every counts packet ends in a 3-byte hash of its payload. The base station
// echoes that hash back in its reply, so a reply is accepted only when the
// echoed hash matches what we sent and the addresses are ours reversed.
// ---------------------------------------------------------------------------
#define SEND_ACK_ATTEMPTS 10
#define ACK_WAIT_MS 50
static bool reply_acknowledges(const tx_rx_data_struct* tx_data)
{
bool hash_echo_matches = (tx_data->msg[57] == RX_DATA.msg[0])
&& (tx_data->msg[58] == RX_DATA.msg[1]) && (tx_data->msg[59] == RX_DATA.msg[2]);
bool addresses_are_ours_reversed
= (tx_data->from == RX_DATA.to) && (tx_data->to == RX_DATA.from);
return hash_echo_matches && addresses_are_ours_reversed;
}
DATA_SEND_STATUS send_message(tx_rx_data_struct tx_data)
{
rfm69_write_msg(tx_data);
for (uint8_t attempt = 0; attempt < SEND_ACK_ATTEMPTS; attempt++) {
if (!wait_rx_payload_ready_timeout(ACK_WAIT_MS)) {
continue;
}
RX_DATA = rfm69_read_msg();
LOG("RX DATA\n");
#if DO_UART
uart_print_tx_rx_data(RX_DATA);
#endif
if (!reply_acknowledges(&tx_data)) {
LOG(" RX DATA ANOTHER ERROR\n");
continue;
}
if ((RX_DATA.flags == MSG_RECV_COUNTS_SUCCESS) && (RX_DATA.msg[3] == 0xFF)) {
LOG(" RX DATA SUCCESS\n");
return DATA_SEND_SUCCESS;
}
LOG(" RX DATA FAILED\n");
}
return DATA_NOT_SENT;
}
// ---------------------------------------------------------------------------
// 5. Wheel-counts packet builder and hashes
//
// 60-byte msg layout:
// [0..9] name (padded)
// [10..19] wheel diameter (padded)
// [20..21] battery reading, little endian
// [22..26] timestamp: minute, hour, day, month, year
// [27..56] 15 x uint16 per-minute counts, little endian
// [57..59] 24-bit hash of bytes 0..56
// ---------------------------------------------------------------------------
tx_rx_data_struct generate_wheel_counts_message(
identifier_results idd, time_struct time, uint16_t battery_value, volatile uint16_t counts[15])
{
reset_txrx_struct(&TX_DATA);
memcpy(TX_DATA.msg, idd.name_str, MIN(10, idd.name_len));
memcpy(TX_DATA.msg + 10, idd.diameter_str, MIN(10, idd.diameter_len));
TX_DATA.msg[20] = battery_value & 0xFF;
TX_DATA.msg[21] = (battery_value >> 8) & 0xFF;
TX_DATA.msg[22] = time.Minute;
TX_DATA.msg[23] = time.Hour;
TX_DATA.msg[24] = time.Day;
TX_DATA.msg[25] = time.Month;
TX_DATA.msg[26] = time.Year;
for (uint8_t idx = 0; idx < 15; idx++) {
TX_DATA.msg[26 + (2 * idx + 1)] = counts[idx] & 0xFF; // LSB first
TX_DATA.msg[26 + (2 * idx + 2)] = (counts[idx] >> 8) & 0xFF; // MSB second
}
uint32_t msg_hash = hash_3bytes(TX_DATA.msg, 57);
TX_DATA.msg[57] = msg_hash & 0xFF;
TX_DATA.msg[58] = (msg_hash >> 8) & 0xFF;
TX_DATA.msg[59] = (msg_hash >> 16) & 0xFF;
TX_DATA.len = sizeof(TX_DATA.msg);
TX_DATA.flags = MSG_SEND_COUNTS;
TX_DATA.from = idd.hashed;
TX_DATA.to = 255;
TX_DATA.dtype = MSG_TYPE_BINARY;
return TX_DATA;
}
// 24-bit payload checksum carried in the last three message bytes
uint32_t hash_3bytes(unsigned const char* str, uint8_t str_len)
{
uint32_t hash = 0;
for (uint8_t i = 0; i < str_len; i++) {
hash = (hash * 31 + str[i]) % 0xFFFFFF;
}
return hash;
}
// Hash a NUL-terminated string into [min, max]; used to derive the node's
// radio address from its name.
uint8_t hash(const char* str, uint8_t min, uint8_t max)
{
unsigned int hash = 0;
while (*str) {
hash = (hash * 31) + (unsigned char)(*str);
str++;
}
unsigned int range = max - min + 1;
return (hash % range) + min;
}
void uart_print_tx_rx_data(tx_rx_data_struct tx_rx_print)
{
DATA_BUFFER_7[0] = tx_rx_print.len;
DATA_BUFFER_7[1] = tx_rx_print.to;
DATA_BUFFER_7[2] = tx_rx_print.from;
DATA_BUFFER_7[3] = tx_rx_print.dtype;
DATA_BUFFER_7[4] = tx_rx_print.flags;
uart_sendString(" ");
uart_print_uint8_array(DATA_BUFFER_7, 5, "LEN,TO,FROM,DTYPE,FLAGS\n");
uart_sendString(" ");
uart_sendStringArray(tx_rx_print.msg, 20);
uart_sendChar('\n');
uart_sendString(" ");
uart_print_uint8_array(tx_rx_print.msg, tx_rx_print.len, "\n");
}
// ---------------------------------------------------------------------------
// 6. Radio configuration
// ---------------------------------------------------------------------------
void rfm69_init(void)
{
reset_rfm69();
_delay_ms(100);
set_rfm69_idle();
// Carrier: 434.0 MHz (see the VAL_FREQ_* derivation in rfm69.h)
spi_write_rfm69_rt(REG_FREQ_MSB, VAL_FREQ_433MHz_MSB);
spi_write_rfm69_rt(REG_FREQ_MIDDLE_SB, VAL_FREQ_433MHz_MID_SB);
spi_write_rfm69_rt(REG_FREQ_LSB, VAL_FREQ_433MHz_LSB);
spi_write_rfm69_rt(
REG_FIFO_THRESH,
VAL_TX_START_FIFO_NOT_EMPTY | VAL_FIFO_LEVEL_INTERRUPT); // TX condition
spi_write_rfm69_rt(REG_TEST_DAGC,
VAL_TEST_DAGC_DEFAULT); // Fading margin improvement
// Start transmitting as soon as the FIFO has data (rfm69_write_msg relies
// on filling the FIFO in standby because of this)
spi_write_rfm69_rt(REG_FIFO_THRESH, VAL_TX_START_FIFO_NOT_EMPTY | VAL_FIFO_LEVEL_INTERRUPT);
spi_write_rfm69_rt(REG_TEST_DAGC, VAL_TEST_DAGC_DEFAULT); // Fading margin improvement
// 2-byte sync word shared with the base station
char sync_words[] = { 0x2d, 0xd4 };
spi_write_rfm69_multiple_rt(REG_SYNC_VALUE_1, sync_words, 2);
spi_write_rfm69_rt(REG_SYNC_CONFIG, VAL_SYNCWORDS_ON | VAL_SYNCWORDS_SIZE_2_BYTES);
spi_write_rfm69_rt(REG_DATA_MODUL,
VAL_DATA_PACKET_MODE | VAL_DATA_MODUL_FSK
| VAL_MODUL_SHAPING_GAUSS_BT_1_0); // RegDataModul
spi_write_rfm69_rt(REG_BITRATE_MSB,
VAL_BITRATE_250kbps_MSB); // RegBitrateMSB
spi_write_rfm69_rt(REG_BITRATE_LSB,
VAL_BITRATE_250kbps_LSB); // RegbBitrateLSB
spi_write_rfm69_rt(REG_FDEV_MSB, VAL_FDEV_MSB); // RegFdevMSB (0x05)
spi_write_rfm69_rt(REG_FDEV_LSB, VAL_FDEV_LSB); // RegFdevLSB (0x06)
spi_write_rfm69_rt(REG_RX_BW, 0xE0); // RegRxBw
spi_write_rfm69_rt(REG_AFC_BW, 0xE0); // RegAfcBw
// FSK packet mode, Gaussian shaping, 250 kbps, 25 kHz deviation
spi_write_rfm69_rt(
REG_PACKET_CONFIG_1,
VAL_PACKET_VARIABLE_LENGTH | VAL_PACKET_WHITENING | VAL_PACKET_CRCON); // RegPacketConfig1
REG_DATA_MODUL, VAL_DATA_PACKET_MODE | VAL_DATA_MODUL_FSK | VAL_MODUL_SHAPING_GAUSS_BT_1_0);
spi_write_rfm69_rt(REG_BITRATE_MSB, VAL_BITRATE_250kbps_MSB);
spi_write_rfm69_rt(REG_BITRATE_LSB, VAL_BITRATE_250kbps_LSB);
spi_write_rfm69_rt(REG_FDEV_MSB, VAL_FDEV_MSB);
spi_write_rfm69_rt(REG_FDEV_LSB, VAL_FDEV_LSB);
spi_write_rfm69_rt(REG_PREAMBLE_MSB, 0x00); // RegPreambleMSB
spi_write_rfm69_rt(REG_PREAMBLE_LSB, 0x04); // RegPreambleLSB
// Widest RX/AFC bandwidth settings
spi_write_rfm69_rt(REG_RX_BW, 0xE0);
spi_write_rfm69_rt(REG_AFC_BW, 0xE0);
spi_write_rfm69_rt(REG_PA_LEVEL,
VAL_PA_PA1_ON | VAL_PA_PA2_ON | VAL_PA_20dB); // RegPaLevel
// Variable-length packets with whitening and CRC
spi_write_rfm69_rt(
REG_PACKET_CONFIG_1, VAL_PACKET_VARIABLE_LENGTH | VAL_PACKET_WHITENING | VAL_PACKET_CRCON);
// 4-byte preamble
spi_write_rfm69_rt(REG_PREAMBLE_MSB, 0x00);
spi_write_rfm69_rt(REG_PREAMBLE_LSB, 0x04);
// Both PA stages on, maximum output power
spi_write_rfm69_rt(REG_PA_LEVEL, VAL_PA_PA1_ON | VAL_PA_PA2_ON | VAL_PA_20dB);
}