首页 > 代码库 > Tower 实战一:MavLink的连接与通信

Tower 实战一:MavLink的连接与通信

1 废话不多说,Tower的连接方式之一是通过android通过手机自带蓝牙模块,和遥控器蓝牙通信,最后在连接飞空,飞空是一块单片机,里面也有一套程序处理逻辑,然后遥控器坐传输媒介

这样做的好处就是把一些用户操作放到android上面,比如航点规划,客服端控制飞机的起飞,降落等,最后飞空来控制无人机玩成一系列的操作

连接方式如图

技术分享

2 今天主要说一下Mavlink的连接首先在Tower的源码里构建了一个类

public class MAVLinkClient implements MAVLinkStreams.MAVLinkOutputStream {
    public MAVLinkClient(Context context, MAVLinkStreams.MavlinkInputStream listener) {
        this.listener = listener;
    }

    private void connectMAVConnection() {
}

    public void sendMavPacket(MAVLinkPacket pack) {
}

    @Override
    public void toggleConnectionState() {
        if (isConnected()) {
            closeConnection();
        } else {
            openConnection();
        }
    }
}

这个了主要实习了 连接,切换连接,发送数据包,和注入一个listener , 这个listener主要是接受到数据的时候回调,所以这个类主要就是实习这个几个功能,Tower向无人机发送数据基本都是通过这个sendMavPacket方法去实现的

3 我们看看listener是个什么东西

    public interface MavlinkInputStream {
        void notifyConnected();

        void notifyDisconnected();

        void notifyReceivedData(MAVLinkMessage m);
    }

这个listener的声明 ,他是用来处理收到消息的 ,调用notifyReceivedData 接受一个MavLinkmessage

4  接来我们看MavLinkClient的初始化,差看源码可以看到,在Application的初始化OnCreatef 方法里面 MAVLinkClient MAVClient = new MAVLinkClient(this, this); 可以看到Application 实现了MavlinkInputStream 接口,并且实现了 notifyReceivedData 方法

    @Override
    public void notifyReceivedData(MAVLinkMessage msg) {
        mavLinkMsgHandler.receiveData(msg);
    }

 

5 这样你就很容易看到在哪里处理 MAVLinkMessage 了,so , MavLinkMsgHandler 就是 MAVLinkMessage 的处类了,你可以看到关于多种MAVLinkMessage 的处理,这些消息有心跳消息,事件消息等。。。

6 现在条理是不是很清楚了,那我们就来看android如果通过蓝牙连接飞控的,首先你必须会蓝牙连接的基础知识,网上资料很多

7 我们看连接函数

    private void connectMAVConnection() {
        bluetoothConnection.addMavLinkConnectionListener("blue",mConnectionListener);
        Toast.makeText(parent, R.string.status_connecting, Toast.LENGTH_SHORT).show();
        if (bluetoothConnection.getConnectionStatus() == MavLinkConnection.MAVLINK_DISCONNECTED) {
            bluetoothConnection.connect();
        }
    }

直接调用了bluetoothConnection.connect(),  MavLinkConnection bluetoothConnection是一个抽象类,也住无人机地面站里面的一个比较核心的类,

  /**
     * Listen for incoming data on the mavlink connection.
     */
    private final Runnable mConnectingTask = new Runnable() {

        @Override
        public void run() {
            Thread sendingThread = null, loggingThread = null;
            try {
                // Open the connection
                openConnection();
                mConnectionStatus.set(MAVLINK_CONNECTED);
                // Launch the ‘Sending‘, and ‘Logging‘ threads
                sendingThread = new Thread(mSendingTask, "MavLinkConnection-Sending Thread");
                sendingThread.start();

                final Parser parser = new Parser();
                parser.stats.mavlinkResetStats();
          
                final byte[] readBuffer = new byte[READ_BUFFER_SIZE];
                while (mConnectionStatus.get() == MAVLINK_CONNECTED&&!isAndroidfor4_4()) {
                    int bufferSize = readDataBlock(readBuffer);
                    if (readBuffer==null||readBuffer.length==0){
                        continue;
                    }
                    handleData(parser, bufferSize, readBuffer);
                }
            } catch (Exception e) {
                Log.i("===>>","disconent");
                if (loggingThread != null && loggingThread.isAlive()) {
                    loggingThread.interrupt();
                }

                if (sendingThread != null && sendingThread.isAlive()) {
                    sendingThread.interrupt();
                }
                mConnectionStatus.set(MAVLINK_DISCONNECTED);
                disconnect();
            }
        }

8 这个类有几个核心的方法 1打开连接 openConnection(),2 sendingThread不停地发送数据 3  handleData()处理数据,在其实现类里面主要也是完成这3个方法,

public Bluetoothfor2_0Connection(Context parentContext) {
        prefs = new DroidPlannerPrefs(parentContext);
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        if (mBluetoothAdapter == null) {
            Log.d(BLUE, "Null adapters");
        }
    }

    protected void openAndroidConnection() throws IOException {
        Log.d(BLUE, "Connect");

        // Reset the bluetooth connection
        resetConnection();

        // Retrieve the stored device
        BluetoothDevice device = null;
        final String addressName = prefs.getBluetoothDeviceAddress();

        if (addressName != null) {
            // strip name, use address part - stored as <address>;<name>
            final String part[] = addressName.split(";");
            try {
                device = mBluetoothAdapter.getRemoteDevice(part[0]);
            } catch (IllegalArgumentException ex) {
                // invalid configuration (device may have been removed)
                // NOP fall through to ‘no device‘
            }
        }
        // no device
        if (device == null)
            device = findSerialBluetoothBoard();

        Log.d(BLUE, "Trying to connect to device with address " + device.getAddress());
        Log.d(BLUE, "BT Create Socket Call...");
        bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(UUID
                .fromString(UUID_SPP_DEVICE));

        Log.d(BLUE, "BT Cancel Discovery Call...");
        mBluetoothAdapter.cancelDiscovery();

        Log.d(BLUE, "BT Connect Call...");
        bluetoothSocket.connect(); // Here the IOException will rise on BT
                                    // protocol/handshake error.

        Log.d(BLUE, "## BT Connected ##");
        out = bluetoothSocket.getOutputStream();
        in = bluetoothSocket.getInputStream();
    }

    @Override
    protected int readDataBlock(byte[] buffer) throws IOException {
        return in.read(buffer);

    }

    @Override
    protected void sendBuffer(byte[] buffer) throws IOException {
        if (out != null) {
            out.write(buffer);
        }
    }

9 handledata 方法就是回调之前的 listener.notifydatareceiver(),是不是之前地方了,至于发送数据

 private final Runnable mSendingTask = new Runnable() {
        @Override
        public void run() {
            int msgSeqNumber = 0;
            try {
                while (mConnectionStatus.get() == MAVLINK_CONNECTED) {
                    final MAVLinkPacket packet = mPacketsToSend.take();
                    packet.seq = msgSeqNumber;
                    byte[] buffer = packet.encodePacket();
                        if (buffer==null|| buffer.length==0){
                            continue;
                        }
                    try {
                        sendBuffer(buffer);
                        queueToLog(packet);
                    } catch (IOException e) {
                        reportComError(e.getMessage());
                        mLogger.logErr(TAG, e);
                    }

                    msgSeqNumber = (msgSeqNumber + 1) % (MAX_PACKET_SEQUENCE + 1);
                }
            } catch (InterruptedException e) {
         
    };

 

 发送数据其实也是Mavclient的 sendMavPacket方法,里面有一个阻塞队列的东西,线程安全的双端队列,可能需要你看看,思路很清晰,2可能会讲 MavlinkMessage

    @Override
    public void sendMavPacket(MAVLinkPacket pack) {
        if (!isConnected()) {
            return;
        }
        bluetoothConnection.sendMavPacket(pack);
    }

 

Tower 实战一:MavLink的连接与通信