Android 串口和USB通信開發(fā)

最近涉及到android串口和usb的開發(fā)商虐,花費不少時間找文章參考。主要時間花費在串口連接上,由于計算機實際中沒有串口的設備接口缀壤,只能用 虛擬串口 進行通信;且 USB 連接模擬器又是一番費神操作纠亚。
用到的工具 提取碼: djss


搭建虛擬環(huán)境(有實際串口接口可忽略)
  • 打開 Genymotion 安裝android虛擬機

    添加虛擬機

    安裝完成后

  • 打開android studio 添加 Genymotion 插件

    AS添加插件

    安裝完成重啟AS后會看到此圖標

  • 打開 Oracle VM VirtualBox 添加 Oracle_VM_VirtualBox_Extension_Pack 插件

    全局設定

    添加擴展支持

添加此支持在使用USB掛載android模擬器時必須使用塘慕,否則android模擬設備無法識別連接設備。

  • 打開 Configure Virtual Serial Port Driver 添加虛擬串口

    添加COM2和COM3串口

    成功回顯

    此處添加的虛擬串口是成對出現(xiàn)蒂胞。

  • Oracle VM VirtualBox 開啟使用虛擬串口

    android模擬設備開啟串口

由于 成功回顯圖 模擬的串口名為COM2和COM3图呢,此處僅開啟COM2的映射即可。

  • Oracle VM VirtualBox 開啟使用USB設備

    usb設備映射

    此處選擇自己開發(fā)的相應設備即可骗随。

  • 使用 串口調(diào)試精靈 模擬數(shù)據(jù)的發(fā)送與接收

    開啟COM3

    上圖為 開啟狀態(tài) 蛤织,默認是關閉的

  • 最后就是接下來要說的開啟 android模擬器 并編寫的程序進行通信

一、串口通信

參考鏈接 主要參考其使用谷歌的串口so庫加載打開串口類

搭建項目
  • 復制so庫至android項目中

復制so庫

這里有個7.0系統(tǒng)會報錯鸿染,so庫版本問題指蚜,直接去谷歌開源拿比較穩(wěn)

  • 設置so庫目錄
  android { 
      ...
      sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
      }
    ...
  }
  • 復制包及SerialPort類
串口打開工具類

注:android_serialport_api 包名不可更改

  • 串口基本操作(打開,關閉牡昆,發(fā)送姚炕,接收)
//isConConnectionStatus  串口的連接狀態(tài)
 /**
   * 打開串口
   * @param pathname  串口路徑
   * @param baudrate  波特率
   */
  public boolean openSerialPort(String pathname, int baudrate) {
      boolean isopen = false;
       try {
            serialPort = new SerialPort(new File(pathname), baudrate, 0);
            inputStream = serialPort.getInputStream();
            outputStream = serialPort.getOutputStream();
            receive();//接收函數(shù)摊欠,主要實現(xiàn)開啟子線程并讀取數(shù)據(jù)
            isConConnectionStatus = isopen = true;
        } catch (IOException e) {
            Log.e(TAG, "打開串口異常" + e);
        } catch (SecurityException e) {
             Log.e(TAG, "無串口操作權限" + e);
        } catch (UnsatisfiedLinkError e) {
             Log.e(TAG, "so文件無法加載" + e);
        }
      return isopen;
  }
  
   /**
     * 接收串口數(shù)據(jù)
     */
    public void receive() {
        if (receiveThread != null && !isConConnectionStatus) {
            return;
        }
        receiveThread = new Thread() {
            @Override
            public void run() {
                while (isConConnectionStatus) {
                    try {
                        byte[] readData = new byte[32];
                        if (inputStream == null) {
                            return;
                        }
                        int size = inputStream.read(readData);
                        if (size > 0 && isConConnectionStatus) {
                            //回調(diào)數(shù)據(jù)
                            Log.i(TAG, "readData:" + Arrays.toString(readData));
                        }
                    } catch (IOException e) {
                        Log.e(TAG, "讀取數(shù)據(jù)失敗..." + e);
                    }
                }
            }
        };
        receiveThread.start();
    }

   /**
     * 發(fā)送串口指令
     */
    public boolean sendData(byte[] data) {
        boolean result = false;
        if (isConConnectionStatus) {
           try {
               outputStream.write(data);
               outputStream.flush();
               result  = true;
            } catch (IOException e) {
               Log.e(TAG, "串口數(shù)據(jù)發(fā)送失敗:" + e);
            }
        }
        return result;
    }

 /**
   * 關閉串口
   */
  public boolean closeSerialPort() {
      boolean isclose = false;
      if (isConConnectionStatus) {
           try {
               if (inputStream != null) {
                  inputStream.close();
               }
               if (outputStream != null) {
                  outputStream.close();
               }
               isConConnectionStatus = false;
               isClose = true;
            } catch (IOException e) {
                Log.e(TAG, "關閉串口異常" + e);
            }
       }
      return isclose ;
  }

串口的操作就在這里完結撒花柱宦。

二些椒、USB通信

  • 設置靜態(tài)權限
<uses-feature android:name="android.hardware.usb.host" />
  • USB基本操作(打開、關閉掸刊、接收免糕、發(fā)送)
  public void open() {
      usbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        if (usbManager != null) {
            UsbDevice result = null;
            HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList();
            for (String key : deviceList.keySet()) {
                UsbDevice usbDevice = deviceList.get(key);
                if (usbDevice != null &&
                        usbDevice.getProductId() == PID &&
                        usbDevice.getVendorId() == UID) {
                    result = usbDevice;
                    break;
                }
            }
            if (result != null) {
                if (usbManager.hasPermission(result)) {
                    onUsbPermissionAllow(result);//權限通過
                } else {
                    //申請USB權限
                    mUsbPermissionActionReceiver = new UsbPermissionActionReceiver();
                    mUsbPermissionActionReceiver.setListener(this);
                    Intent intent = new Intent(UsbPermissionActionReceiver.ACTION_USB_PERMISSION);
                    PendingIntent mPermissionIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
                    IntentFilter permissionFilter = new IntentFilter(UsbPermissionActionReceiver.ACTION_USB_PERMISSION);
                    context.registerReceiver(mUsbPermissionActionReceiver, permissionFilter);
                    usbManager.requestPermission(result, mPermissionIntent);
                }
            } else {
                onUsbPermissionAllow(null);
            }
        } else {
            onUsbPermissionFail();//未獲取到權限
        }
  }
  
  public void onUsbPermissionFail() {
    if (mUsbPermissionActionReceiver != null && context != null) {
        context.unregisterReceiver(mUsbPermissionActionReceiver);
    }
  }

  public void onUsbPermissionAllow() {
    usbDeviceConnection = usbManager.openDevice(usbDevice);//獲取usb連接
    if (usbDeviceConnection != null) {
         if (usbRequest == null) {
             usbRequest = new UsbRequest();
         }
         usbRequest.initialize(usbDeviceConnection, usbEndpointIn);
         isConConnectionStatus = true;
         new ReceiveThread().start();
     }
    if (mUsbPermissionActionReceiver != null && context != null) {
        context.unregisterReceiver(mUsbPermissionActionReceiver);
    }
  }

  /**
    * 接收線程
    */
  private class ReceiveThread extends Thread {
        @Override
        public void run() {
            super.run();
            while (isConConnectionStatus) {
                try {
                    int maxSize = usbEndpointIn.getMaxPacketSize();
                    receiveBytes = new byte[maxSize];
                    boolean result = false;
                    if (usbDeviceConnection != null && usbInterface != null) {
                          usbDeviceConnection.claimInterface(usbInterface, true);//獨占接口
                          result = usbDeviceConnection.bulkTransfer(usbEndpointIn, receiveBytes, receiveBytes.length, DEFAULT_TIMEOUT) != -1;
                    }
                    if (result) {
                        //獲取成功
                        Log.e(TAG, "data:" +  e);
                    } else {
                            Log.e(TAG, "讀取數(shù)據(jù)失敗忧侧!\n" +  e);
                    }
                } catch (InterruptedException e) {
                    Log.e(TAG, "數(shù)據(jù)讀取異常:" + e);
                }
            }
        }
    }

  /**
     * 發(fā)送串口指令(字符串)
     *
     * @param sendBytes 需要發(fā)送的字節(jié)數(shù)據(jù)
     */
    private boolean sendData(final byte[] sendBytes) {
        boolean sendResult = false;
        if (usbEndpointOut != null && sendBytes != null && sendBytes.length > 0) {
            sendResult = usbDeviceConnection.bulkTransfer(usbEndpointOut, sendBytes, sendBytes.length, DEFAULT_TIMEOUT) != -1;
        }
        Log.i(TAG, "sendData:" + sendResult);
        return sendResult;
    }

  /**
    * 關閉USB連接
    */
  public boolean close() {
    boolean result = false;
        if (isConConnectionStatus) {
            if (usbDeviceConnection != null && usbInterface != null) {
                isConConnectionStatus = false;
                usbDeviceConnection.releaseInterface(usbInterface);
                usbDeviceConnection.close();
                result = true;
            }
        }
    return result;
  }


UsbPermissionActionReceiver 類

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.util.Log;
import ehe.etsa.serialport.IETSAFactory;

public class UsbPermissionActionReceiver extends BroadcastReceiver {

    private OnUsbPermissionListener listener;
    public static final String ACTION_USB_PERMISSION = "com.demo.USB_PERMISSION";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (ACTION_USB_PERMISSION.equals(action)) {
            synchronized (this) {
                UsbDevice usbDevice = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                    if (null != usbDevice && listener != null) {
                        listener.onUsbPermissionAllow(usbDevice);
                    }
                } else {
                    Log.e(IETSAFactory.TAG, "Permission denied for device(獲取設備的權限被拒絕):\n" + usbDevice);
                    if (listener != null) {
                        listener.onUsbPermissionFail();
                    }
                }
            }
        }
    }

    public void setListener(OnUsbPermissionListener listener) {
        this.listener = listener;
    }

    public interface OnUsbPermissionListener {
        /** 當USB權限獲取失敗回調(diào) */
        void onUsbPermissionFail();

        /** 當權限通過回調(diào) */
        void onUsbPermissionAllow(UsbDevice usbDevice);
    }
}

到此Android 串口和USB的通信開發(fā)完結石窑。


沒有最好,只有最合適
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蚓炬,一起剝皮案震驚了整個濱河市松逊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肯夏,老刑警劉巖经宏,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異驯击,居然都是意外死亡烁兰,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門徊都,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沪斟,“玉大人,你說我怎么就攤上這事暇矫≈髦” “怎么了?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵李根,是天一觀的道長杀餐。 經(jīng)常有香客問我,道長朱巨,這世上最難降的妖魔是什么史翘? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮冀续,結果婚禮上琼讽,老公的妹妹穿的比我還像新娘。我一直安慰自己洪唐,他們只是感情好钻蹬,可當我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凭需,像睡著了一般问欠。 火紅的嫁衣襯著肌膚如雪肝匆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天顺献,我揣著相機與錄音旗国,去河邊找鬼。 笑死注整,一個胖子當著我的面吹牛能曾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肿轨,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼寿冕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了椒袍?” 一聲冷哼從身側(cè)響起驼唱,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎驹暑,沒想到半個月后曙蒸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡岗钩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了肖油。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片兼吓。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖森枪,靈堂內(nèi)的尸體忽然破棺而出视搏,到底是詐尸還是另有隱情,我是刑警寧澤县袱,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布浑娜,位于F島的核電站,受9級特大地震影響式散,放射性物質(zhì)發(fā)生泄漏筋遭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一暴拄、第九天 我趴在偏房一處隱蔽的房頂上張望漓滔。 院中可真熱鬧,春花似錦乖篷、人聲如沸响驴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豁鲤。三九已至秽誊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琳骡,已是汗流浹背锅论。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留日熬,地道東北人棍厌。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像竖席,于是被迫代替她去往敵國和親耘纱。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,955評論 2 355