Search code examples
javaserial-communicationraspberry-pi3

Raspberry Pi getting data from MultiWii


I have a Raspberry Pi 3B and a CRIUS All in One Pro (v2.0) MultiWii flight controller. I'm using the MultiWii 2.4 version and the latest NOOBS. I was able to set up both fine, and now I am trying to get the Raspberry Pi to communicate with the MultiWii through a USB/Micro USB cable that connects the two boards.

At the moment, the MultiWii isn't returning any data back, and I'm not sure why. From what I can see, I have the protocol correct. I've looked at several working code repos (written in Python of Java for Arduino), and have followed the MultiWii documentation and read through the associated forum post.

Here is the client code that I wrote.

package com.jmace.MaceDrone.msp;


import com.pi4j.io.serial.Baud;
import com.pi4j.io.serial.DataBits;
import com.pi4j.io.serial.FlowControl;
import com.pi4j.io.serial.Parity;
import com.pi4j.io.serial.Serial;
import com.pi4j.io.serial.SerialConfig;
import com.pi4j.io.serial.SerialDataEvent;
import com.pi4j.io.serial.SerialDataEventListener;
import com.pi4j.io.serial.SerialFactory;
import com.pi4j.io.serial.StopBits;
import java.io.IOException;
import java.math.BigInteger;

public class MultiWiiClient {

    private final Serial serial;

    //The preamble is defined by the protocol.
    //Every message must begin with the characters $M
    private static final String PREAMBLE = "$M";
    //Character that denotes information being passed to the MultiWii
    private static final char TO_MUTLIWII = '<';
    //Character that denotes information being requested from by the MultiWii
    private static final char FROM_MUTLIWII = '>';

    public MultiWiiClient(String usbPort) {
        SerialConfig config = new SerialConfig();
        config.device(usbPort)
              .baud(Baud._115200)
              .dataBits(DataBits._8)
              .parity(Parity.NONE)
              .stopBits(StopBits._1)
              .flowControl(FlowControl.NONE);

        this.serial = SerialFactory.createInstance();

        serial.addListener(new SerialDataEventListener() {
            @Override
            public void dataReceived(SerialDataEvent event) {
                try {
                    System.out.println("[HEX DATA]   " + event.getHexByteString());
                    System.out.println("[ASCII DATA] " + event.getAsciiString());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        try {
            this.serial.open(config);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public String sendRequest(MultiWiiRequest request) throws IllegalStateException, IOException {
        String message = createMessage(request.getId(), false, null);
        //////////////////////////////////////////////////////////////////////////////////
        System.out.println(message);
        System.out.println(String.format("%040x", new BigInteger(1, message.getBytes())));
        //////////////////////////////////////////////////////////////////////////////////
        return sendMessage(message);
    }


    public String sendCommand(MultiWiiCommand command, String payload) throws IllegalStateException, IOException {
        String message = createMessage(command.getId(), true, payload);
        return sendMessage(message);
    }

    /**
     * This method creates the message that will be sent to the MultiWii
     * 
     * Message format is as follows:
     * +--------+---------+----+-------+----+---+
     * |preamble|direction|size|command|data|crc|
     * +--------+---------+----+-------+----+---+
     * 
     * Preamble (2 bytes):
     *      Marks the start of a new message; always "$M"
     * 
     * Direction (1 byte):
     *      Either '<' for a command going to the MultiWii or '>' for
     *      information being requested from the MultiWii
     * 
     * Size (1 byte):
     *      The number of bytes in the payload
     * 
     * Command (1 byte):
     *      The message ID of the command, as defined in the protocol
     *      100's for requesting data, and 200's for requesting an action
     * 
     * Data (variable bytes):
     *      The data to pass along with the command
     * 
     * CRC (1 byte):
     *      Calculated with an XOR of the size, command, and each byte of data 
     */
    private String createMessage(int mutliWiiCommandnumber, boolean isCommand, String payload) {
        StringBuilder message = new StringBuilder(PREAMBLE);
        byte checksum=0;

        //Get the direction of the message
        if (isCommand) {
            message.append(TO_MUTLIWII);
        } else {
            message.append(FROM_MUTLIWII);
        }

        int datalength = (payload != null) ? payload.length() : 0;

        message.append((char) datalength);
        checksum ^= datalength;

        message.append((char) mutliWiiCommandnumber);
        checksum ^= ((int) mutliWiiCommandnumber);

        if (payload != null) {
            for (char c : payload.toCharArray()){ 
                message.append(c);
                checksum ^= (int) c;
            }
        }

        message.append((char) checksum);
        return message.toString();
    }


    private String sendMessage(String message) throws IllegalStateException, IOException {
        serial.write(message.getBytes());

        serial.flush();
        System.out.println("TESTING ------------------");

        return "";
    }

}

I am using "/dev/ttyUSB0" to connect, which I confirmed is the correct location and it seems to be working (no errors when I run it; and if I start running and then disconnect the USB, it throws an exception because it lost the connection).

When running, I get the following output (sending command 100, MSP_IDENT):

$M>dd
00000000000000000000000000244d3e006464
TESTING ------------------

See my Git repo for more code context.

EDIT: Fixed the checksum code in my post


Solution

  • I turns out that the problem was in my direction bit. I though that there '<' was used for sending commands and '>' was use for requesting data. It turns out that both in both cases, '<' should be used. The '>' character is used only when the MultiWii responds.