mavlink: Unable to get any kind of data from UDP connection

Hi, I have an issue trying to get data with UDP connection to an arducopter >= 3.7.

I’m following these steps:

  • create mavlink connection
  • start to send CGS Heartbeat
  • request Data Stream
  • get messages from mavlinkConnection

I set up some wrapper and functions but these steps can be simplified in this way.

—create mavlink connection

Socket socket = new Socket(host, port, false);
socket.setSoTimeout(10000);

MavlinkConnection connection = MavlinkConnection.builder(socket.getInputStream(), socket.getOutputStream())
          .dialect(MavAutopilot.MAV_AUTOPILOT_GENERIC, new StandardDialect())
          .dialect(MavAutopilot.MAV_AUTOPILOT_ARDUPILOTMEGA, newArdupilotmegaDialect()).build();

  • start to send CGS Heartbeat (sent every second)
      Heartbeat msg = Heartbeat.builder()
          .type(MavType.MAV_TYPE_GCS)
          .autopilot(MavAutopilot.MAV_AUTOPILOT_INVALID)
          .systemStatus(MavState.MAV_STATE_UNINIT)
          .mavlinkVersion(3)
          .build();
      try {
        connection.send2(255, 1, msg);
      } catch (IOException e) {
        Logger.logException(TAG, e);
      }
  • request Data Stream
int heartbeatStreamId = 0;
int batteryStatusStreamId = 147;
CommandLong hbCmd = commandLongBuilder.command(MavCmd.MAV_CMD_SET_MESSAGE_INTERVAL).param1(heartbeatStreamId).param2(0).targetSystem(0).targetComponent(0).build();
CommandLong batteryCmd = commandLongBuilder.command(MavCmd.MAV_CMD_SET_MESSAGE_INTERVAL).param1(heartbeatStreamId).param2(0).targetSystem(0).targetComponent(0).build();
connection.send2(255, 1, hbCmd);
connection.send2(255, 1, batteryCmd);
  • get messages from mavlinkConnection
      while (mavlinkConnection != null) {
        MavlinkMessage message = null;
        try {
          message = mavlinkConnection.next();
          ......
        } catch (Throwable e) {
          message = null;
          ......
        }
      }

I never get any kind of message from this connection, I always get timeout. What am I doing wrong?

Is there any issue in the create of mavlink connection? Do I have to do something else to start the mavlink connection in addition to sending the heartbeat and requesting the data stream?

Regarding the connection, I know that this kind of socket constructor is deprecated but I don’t know any other way to create a mavlink connection with a UDP socket. Can you suggest a better one?

Many thanks in advance

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Comments: 26 (6 by maintainers)

Most upvoted comments

In terms of reading there should not be an issue. Try calling udpOut.flush() right after connection.send, so that packets are sent out immediately, perhaps that is preventing you from seeing traffic.

On Mon, Apr 19, 2021, 18:19 OctavianIonel @.***> wrote:

@benbarkay https://github.com/benbarkay I tried the code above with the UDP and it is not working, what could be the issue? Thanks for your help!

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/dronefleet/mavlink/issues/35#issuecomment-822552620, or unsubscribe https://github.com/notifications/unsubscribe-auth/AA4DXUU2BWOPQZ4ABN7I6YDTJRCWRANCNFSM4JCI23AQ .

This is a Java issue but I suppose a ready-made solution would be nice to have because of the friction it creates in terms of starting to use dronefleet/mavlink with existing UDP-based tools like mavproxy. Perhaps this can be implemented in a separate library.

Here’s a snippet that might help for now. I only tested this lightly, but it may help with direction:

// The address we listen on to receive packets
SocketAddress localAddress = new InetSocketAddress(
        "127.0.0.1", 1234);

// The address to which we send packets
SocketAddress remoteAddress = new InetSocketAddress(
        "127.0.0.1", 4321);

// The size we use for buffers. 65535 should be able to carry any
// UDP packet of a standard length.
int bufferSize = 65535;

// This binds to the listen address, we can later use this datagram socket
// to receive packets and mark packets with source addresses when sending.
DatagramSocket datagramSocket = new DatagramSocket(localAddress);

// The input/output streams that will be exposed for use. These are what you'd let
// MavlinkConnection use.
PipedInputStream udpIn = new PipedInputStream();
OutputStream udpOut = new OutputStream() {
    // We're buffering data that is written to the output stream,  and then sending the
    // data as a UDP packet when flushing.
    
    final byte[] buffer = new byte[bufferSize];
    int position = 0;

    @Override
    public void write(int i) throws IOException {
        write(new byte[] { (byte)i }, 0, 1);
    }

    @Override
    public synchronized void write(byte[] b, int off, int len) throws IOException {
        // If the buffer is full, we flush
        if ((position + len) > buffer.length) {
            flush();
        }
        System.arraycopy(b, off, buffer, position, len);
        position += len;
    }

    @Override
    public synchronized void flush() throws IOException {
        DatagramPacket packet = new DatagramPacket(buffer, 0, position, remoteAddress);
        datagramSocket.send(packet);
        position = 0;
    }
};

// We connect `udpIn` to a corresponding PipedOutputStream (`appOut`). Data that we write to
// `appOut` will be available to read from `udpIn`.
// We instantiate/connect here rather than within the reading thread so that `udpIn` can be used immediately.
PipedOutputStream appOut = new PipedOutputStream(udpIn);

// We create a thread pool with 1 thread which we use for receiving packets.
// We keep reading incoming packets and pushing them into `appOut`, so that they
// are available to read from `udpIn`.
ExecutorService service = Executors.newSingleThreadExecutor();
service.execute(() -> {
    try {
        DatagramPacket packet = new DatagramPacket(new byte[bufferSize], bufferSize);
        while (!datagramSocket.isClosed()) {
            datagramSocket.receive(packet);
            appOut.write(packet.getData(), packet.getOffset(), packet.getLength());
            appOut.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            appOut.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!datagramSocket.isClosed()) {
            datagramSocket.close();
        }
        if (!service.isShutdown()) {
            service.shutdown();
        }
    }
});

// We are now left with udpIn and udpOut, with which we can use to read/write

Here’s a simple relay example (writes whatever it receives to the remote endpoint):

byte[] receiveBuffer = new byte[bufferSize];
int readLength;
while ((readLength = udpIn.read(receiveBuffer)) != -1) {
    udpOut.write(receiveBuffer, 0, readLength);
    udpOut.flush();
}

Hi there.

Socket uses TCP, so it’s likely failing to communicate with your UDP destination. To make UDP connections you will have to look into DatagramSocket. Since DatagramSocket doesn’t provide input/output streams, you will also need to make these for MavlinkConnection to consume. I suggest using PipedInputStream and PipedOutputStream.

Good luck!