I'm trying to build a serial over radio transmitter module using two Feather M0 boards with RFM69HCW chips on board. As a test I'm trying to run a slip IP over serial session. This seems to work consistantly for UDP sessions (in my case ICMP ping requests) but fails for TCP sessions (in my case a simple HTTP request).

Here is the code:

#include <RFM69.h>
#include <RFM69_ATC.h>
#include <SPI.h>
#include <string>
// a simple off the shelf CRC8 implementation
#include <crc.h>

// defined for one of the two boards
// #define B

#ifdef B
#define NODEID 1
#define RECEIVER 2
#ifndef B
#define NODEID 2
#define RECEIVER 1

#define MAX_BYTES 56

#define NETWORKID 100
#define FREQUENCY RF69_433MHZ
#define IS_RFM69HW_HCW

RFM69_ATC radio(8, 3, true);

char buf[512];

void setup()
  // initialize random using receiver address
  // this guarantees that both nodes have different initial random values


  radio.initialize(FREQUENCY, NODEID, NETWORKID);

int8_t receiver_last_id = -1;

bool read_packet(bool override = false)
  if (radio.receiveDone() || override)
    int cnt = radio.DATALEN - 2;
    int crc = radio.DATA[0];
    int8_t packet_id = radio.DATA[1];

    memcpy(buf, radio.DATA + 2, cnt);
    uint8_t crc_data = crc8(0, (uint8_t*)buf, cnt);

    if (radio.ACKRequested())
      if (crc == crc_data)
        // sometimes duplicates seem to occure
        // in case this happens just ignore the packet
        if (packet_id != receiver_last_id)
          Serial.write(buf, cnt);
          receiver_last_id = packet_id;
          return true;
        return false;
        radio.ACK_REQUESTED = 0;
        return false;

    return false;
  return false;

int8_t sender_packet_id = 0;

bool send_packet()
  int serial_cnt = Serial.available();

  if (serial_cnt > 0 && radio.canSend())
    // packet structure:
    // 1. byte: CRC8 of DATA
    // 2. byte: ID of packet (to prevent duplicates)
    // variable length: DATA

    int payload_size = min(MAX_BYTES, serial_cnt);
    int total_size = payload_size + 2;

    Serial.readBytes((char *)buf + 2, payload_size);
    buf[0] = crc8(0, (uint8_t*)(buf + 2), payload_size);
    buf[1] = sender_packet_id++;

    while (!radio.sendWithRetry(RECEIVER, buf, total_size, 1, 5U))
      // radio.receiveDone() switches the
      // radio into receive mode
      if (radio.receiveDone())
        // if receive is done instantly
        // process the packet and move on

      // wait random time in case
      // a collision happened
      delay(random(5, 30));


      // wait for radio to be ready to send
      while (!radio.canSend()) ;

    return true;

  return false;

void loop()


I also have a network capture of both sides.

Here is the capture for the Client: https://ufile.io/v945jl8i

Here is the capture for the Server: https://ufile.io/yn36rjw4

I've figured out that it happens somewhere in the while (!radio.sendWithRetry(RECEIVER, buf, total_size, 1, 5U)) loop but I can't figure out what exactly goes wrong and how I can fix it.

Any help would be greatly appreciated!

Edit: I'm running a simple python HTTP server (python 3 -m http.server 8080). And as client i'm using curl (curl <server address>:8080)

Here are the Commands I use for starting slip:

modprobe slip
slattach -vL -s 115200 -p slip /dev/ttyACM0
ifconfig sl0 pointtopoint up

I switched the two IP addresses for the other side.

Also interestingly it sometimes seems to randomly work for one or two requests, so i'm guessing some kind of race condition.

I also made sure that i'm connecting to the correct serial ports.

PS: i'm kind of new to C++ so any recommendations on best practices are also appreciated

  • Are you running an actual server on the receiver?
    – Nick S.
    Dec 2, 2023 at 3:05
  • edited the question to add the info. TLDR: yes, I have a server running
    – Lukas
    Dec 2, 2023 at 12:55


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.