稀土掘金技术社区 前天 09:52
Android串口,USB,打印机,扫码枪,支付盒子,键盘,鼠标,U盘等开发使用一网打尽
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文详细介绍了Android智能嵌入式设备如何接入各种外设,包括串口、USB设备(如打印机)、扫码枪、支付盒子以及键盘鼠标等。文章针对Android系统下这些外设的接入和使用进行了重点讲解,提供了实际开发中所需的代码示例和配置方法。通过本文,开发者可以快速掌握在Android设备上集成这些常用外设的技巧,从而扩展Android设备的适用范围。

⚙️ 串口接入:通过下载串口包并导入`libprt_serial_port.so`库,使用`SerialPort`类进行串口通信。需要配置串口路径和波特率,并从串口获取输入输出流进行数据读写。数据的解析需要参考硬件厂商提供的格式文档。

🖨️ USB打印机接入:首先在AndroidManifest中添加USB使用配置,并注册USB插拔监听广播。通过`UsbManager`检索USB设备,找到接口类型值为7的打印机设备。获取USB权限后,连接打印机并获取`UsbEndpoint`,最后通过`bulkTransfer`方法写入打印数据,可以使用ESC打印命令进行格式控制。

💳 扫码枪和支付盒子接入:扫码枪和支付盒子等USB设备插入Android设备后,可以通过两种方式获取扫码内容。方式一是注册扫码广播,在广播接收器中获取扫码结果;方式二是在Activity的`onKeyDown`方法中监听按键事件,解析`keyCode`对应的数字值。

原创 Wgllss 2025-05-19 08:30 重庆

本文重点介绍了Android 智能嵌入式设备,接入串口,USB,打印机,扫码枪支付盒子,键盘鼠标等,接入的简单开发。

点击关注公众号,“技术干货” 及时达!

众里寻他千百度,蓦然回首,那人却在灯火阑珊处

一、前言

在 Android 智能设备开发过程中, 难免会遇到串口,USB, 扫码枪,支付盒子,打印机,键盘,鼠标等接入场景,其实这些很简单,只是大多数情况下,大家都在做手机端的 App 开发,接触这方面的很少。本文重点介绍下这些在 Android 系统下是怎么接入使用的。

二 、串口接入使用

1. 可以到官网下载串口包
里面含有 

libprt_serial_port.so

 这个库, 下载下来按照

so使用方式

接入就行了,还有 

SerialPort

 类: 如下:

    public class SerialPort {

       private static final String TAG = "SerialPort";

       /*

        * Do not remove or rename the field mFd: it is used by native method close();

        */

       private FileDescriptor mFd;

       private FileInputStream mFileInputStream;

       private FileOutputStream mFileOutputStream;

       public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {

          mFd = open(device.getAbsolutePath(), baudrate, flags);

          if (mFd == null) {

             Log.e(TAG, "native open returns null");

             throw new IOException();

          }

          mFileInputStream = new FileInputStream(mFd);

          mFileOutputStream = new FileOutputStream(mFd);

       }

       // Getters and setters

       public InputStream getInputStream() {

          return mFileInputStream;

       }

       public OutputStream getOutputStream() {

          return mFileOutputStream;

       }

       // JNI

       private native static FileDescriptor open(String path, int baudrate, int flags);

       public native void close();

       static {

          System.loadLibrary("serial_port");

       }

    }

    2. 使用串口读取或者写入数据
    需要配置串口路径和波特率,如下:

    路径为:/dev/ttyS4, 波特率为9600

    , 这 2 个参数是硬件厂商约定好的。

      val serialPort = SerialPort(File("/dev/ttyS4"), 96000);

      读写数据需要从串口里面拿到 「输入输出流」

        inputStream = serialPort.inputStream  //

        outputStream = serialPort.outputStream

        比如读取数据:

          val length = inputStream!!.available()

          val bytes = new byte[length];

          inputStream.read(bytes);

          到此,串口的使用基本就完成了。

          至于串口读取后的数据怎么解析?需要看串口数据的文档,不同硬件设备读取的不同内容出来格式不一样,按照厂商给的格式文档解析就完了,比如,串口连接的是秤,秤厂商硬件那边约定好的数据格式是怎样的,数据第 1 位什么意思,第 2 到第 X 位什么意思,xxx 位什么意思,这不同的厂商不同的,如果串口连接的不是秤,是其他硬件,约定的格式可能又不一样。

          同理: 串口写数据,使用 

          outputStream

          流写入就行了, 写的具体内容,具体硬件厂商会有写入的文档,写入哪个数据是干什么用的,都在文档里面有。不同的写入功能,对应不同的写入内容命令。

          三 、USB 接入使用

          1、在 AndroidManifest 中添加 USB 使用配置

            <uses-permission android:name="android.permission.USB_PERMISSION" />

            <uses-feature android:name="android.hardware.usb.host" />

            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />

            <meta-data android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />

            2、防止 USB 插入拔出导致 Activity 生命周期发生变化需要在 Activity 下添加配置

              android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"

              3、代码中具体使用:

              比如接入 USB 打印机:

                //拿到USB管理器

                mUsbManager = context.getSystemService(Context.USB_SERVICE) as UsbManager

                mPermissionIntent = PendingIntent.getBroadcast(context, 0, Intent(ACTION_USB_PERMISSION), 0)

                val filter = IntentFilter(USBPrinter.ACTION_USB_PERMISSION)

                filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)

                //注册监听USB插入拔出监听广播

                context.registerReceiver(mUsbDeviceReceiver, filter)

                //开始检索出已经连接的USB设备

                setUsbDevices()

                找到打印机设备,打印机设备的接口类型值固定式为 7

                (usbInterface.interfaceClass)

                  /**

                   * 检索usb打印设备

                   */

                  private fun setUsbDevices() {

                      // 列出所有的USB设备,并且都请求获取USB权限

                      mUsbManager?.deviceList?.let {

                          for (device in it.values) {

                              val usbInterface = device.getInterface(0)

                              if (usbInterface.interfaceClass == 7) {

                                  //连接了多个USB打印机设备需要 判断vid,pid,(硬件厂商会给这个值的)来确定哪一个打印机

                                  //检查该USB设备是否有权限

                                  if (!mUsbManager!!.hasPermission(device)) {

                                      //申请该打印机USB权限

                                      mUsbManager!!.requestPermission(device, mPermissionIntent)

                                  } else {

                                      connectUsbPrinter(device)

                                  }

                                  break

                              }

                          }

                      }

                  }

                  USB 权限广播 action 收到后,就可以连接打印了

                    private val mUsbDeviceReceiver = object : BroadcastReceiver() {

                        override fun onReceive(context: Context, intent: Intent) {

                            val action = intent.action

                            if (ACTION_USB_PERMISSION == action) {

                                synchronized(this) {

                                    val usbDevice = intent.getParcelableExtra<UsbDevice>(UsbManager.EXTRA_DEVICE)

                                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {

                                        //UsbDevice:在Android开发中用于表示连接到Android设备的USB设备  

                                        mUsbDevice = usbDevice

                                        if (mUsbDevice != null) {

                                            connectUsbPrinter(mUsbDevice)

                                        }

                                    } else {

                                        WLog.e(this"Permission denied for device $usbDevice")

                                    }

                                }

                            } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED == action) {

                               //USB插入了       

                            } else if (UsbManager.ACTION_USB_DEVICE_DETACHED == action) {

                               //USB拔出了

                                if (mUsbDevice != null) {

                                    WLog.e(this"Device closed")

                                    if (mUsbDeviceConnection != null) {

                                        mUsbDeviceConnection!!.close()

                                    }

                                }

                            }

                        }

                    }

                    四、打印机的使用

                    Android 上面的打印机大多数是 USB 连接的打印机,还有蓝牙打印机。下面重点介绍 USB 打印机的使用: 在前面代码里找到 USB 打印设备后,我们需要拿到打印机的 

                    UsbEndpoint

                    ,如下:

                      //UsbEndpoint:表示USB设备的单个端点。USB协议中,端点是用于发送和接收数据的逻辑

                      private var printerEp: UsbEndpoint? = null

                      private var usbInterface: UsbInterface? = null

                      fun connectUsbPrinter(mUsbDevice: UsbDevice?) {

                          if (mUsbDevice != null) {

                              usbInterface = mUsbDevice.getInterface(0)

                              for (i in 0 until usbInterface!!.endpointCount) {

                                  val ep = usbInterface!!.getEndpoint(i)

                                  if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) {

                                      if (ep.direction == UsbConstants.USB_DIR_OUT) {

                                          mUsbManager?.let {

                                              //与USB设备建立连接

                                              mUsbDeviceConnection = mUsbManager!!.openDevice(mUsbDevice)

                                              //拿到USB设备的端点

                                              printerEp = ep //拿到UsbEndpoint

                                          }

                                      }

                                  }

                              }

                          }

                      }

                      开始打印:写入打印数据:

                        /**

                         * usb写入

                         *

                         * @param bytes

                         */

                        fun write(bytes: ByteArray) {

                            if (mUsbDeviceConnection != null) {

                                try {

                                    mUsbDeviceConnection!!.claimInterface(usbInterface, true)

                                    //注意设定合理的超时值,以避免长时间阻塞

                                    val b = mUsbDeviceConnection!!.bulkTransfer(printerEp, bytes, bytes.size, USBPrinter.TIME_OUT)

                                    mUsbDeviceConnection!!.releaseInterface(usbInterface)

                                } catch (e: Exception) {

                                    e.printStackTrace()

                                }

                            }

                        }

                        一般通用 USB 打印命令都是 ESC 打印命令如下:

                        初始化打印机指令

                          //初始化打印机

                          public static byte[] init_printer() {

                              byte[] result = new byte[2];

                              result[0] = ESC;

                              result[1] = 0x40;

                              return result;

                          }

                          打印位置设置为居左对齐指令

                             /**

                                 * 居左

                                 */

                                public static byte[] alignLeft() {

                                    byte[] result = new byte[3];

                                    result[0] = ESC;

                                    result[1] = 97;

                                    result[2] = 0;

                                    return result;

                                }

                            打印位置设置为居中对齐指令

                                  /**

                                   * 居中对齐

                                   */

                                  public static byte[] alignCenter() {

                                      byte[] result = new byte[3];

                                      result[0] = ESC;

                                      result[1] = 97;

                                      result[2] = 1;

                                      return result;

                                  }

                              打印位置设置居右对齐指令

                                    /**

                                     * 居右

                                     */

                                    public static byte[] alignRight() {

                                        byte[] result = new byte[3];

                                        result[0] = ESC;

                                        result[1] = 97;

                                        result[2] = 2;

                                        return result;

                                    }

                                打印结束切刀指令

                                      //切刀

                                      public static byte[] cutter() {

                                          byte[] box = new byte[6];

                                          box[0] = 0x1B;

                                          box[1] = 0x64;

                                          box[2] = 0x01;

                                          box[3] = 0x1d;

                                          box[4] = 0x56;

                                          box[5] = 0x31;

                                  //        byte[] data = new byte[]{0x1d, 0x56, 0x01};

                                          return box;

                                      }

                                  打印文字

                                    /**

                                     * 打印文字

                                     *

                                     * @param msg

                                     */

                                    ///**

                                    //     * 安卓9.0之前

                                    //     * 只要你传送的数据不大于16384 bytes,传送不会出问题,一旦数据大于16384 bytes,也可以传送,

                                    //     * 只是大于16384后面的数据就会丢失,获取到的数据永远都是前面的16384 bytes,

                                    //     * 所以,android USB Host 模式与HID使用bulkTransfer(endpoint,buffer,length,timeout)通讯时

                                    //     * buffer的长度不能超过16384。

                                    //     * <p>

                                    //     * controlTransfer( int requestType, int request , int value , int index , byte[] buffer , int length , int timeout)

                                    //     * 该方法通过0节点向此设备传输数据,传输的方向取决于请求的类别,如果requestType 为 USB_DIR_OUT 则为写数据 , USB _DIR_IN ,则为读数据

                                    //     */

                                    fun printText(msg: String) {

                                        try {

                                            write(msg.toByteArray(charset("gbk")))

                                        } catch (e: UnsupportedEncodingException) {

                                            e.printStackTrace()

                                        }

                                    }

                                    打印图片,条码,二维码,可以将图片条码二维码转化为 bitmap, 然后再打印

                                      //光栅位图打印

                                      public static byte[] printBitmap(Bitmap bitmap) {

                                          byte[] bytes1 = new byte[4];

                                          bytes1[0] = GS;

                                          bytes1[1] = 0x76;

                                          bytes1[2] = 0x30;

                                          bytes1[3] = 0x00;

                                          byte[] bytes2 = getBytesFromBitMap(bitmap);

                                          return byteMerger(bytes1, bytes2);

                                      }

                                      蓝牙打印机,放在下一篇文章介绍吧,一起介绍蓝牙,及蓝牙打印

                                      五、扫码枪、支付盒子、键盘、鼠标使用

                                      扫码枪,支付盒子,键盘,鼠标都是 USB 连接设备,只需要插入 Android 设备即可,前提是 Android 设备硬件含有 USB 接口,比如智能硬件 收银机,收银秤,车载插入 U 盘等

                                      收银机 「扫码枪、支付盒子」 怎么扫码的?大家知道,我们的支付码,条码,其实是一串数字内容的,扫到后是怎么解析的? 有两种方式的

                                      「方式 1:广播接收如下:」

                                      1. 「先注册扫码广播」

                                        <receiver android:>

                                            <intent-filter>

                                                <!-- 这里的 "SCAN_ACTION" 是扫码枪触发的action,需要替换为实际的值 -->

                                                <action android: />

                                            </intent-filter>

                                        </receiver>

                                        2. 「在广播接收器里面拿到扫码内容」

                                          import android.content.BroadcastReceiver;

                                          import android.content.Context;

                                          import android.content.Intent;

                                          public class ScanGunReceiver extends BroadcastReceiver {

                                              @Override

                                              public void onReceive(Context context, Intent intent) {

                                                  // 获取扫码内容,这里的 "SCAN_RESULT" 是扫码枪提供的action,具体可能不同

                                                  String scanContent = intent.getStringExtra("SCAN_RESULT");

                                                  // 处理扫码内容

                                                  if (scanContent != null) {

                                                      // 扫码内容非空,执行相关逻辑

                                                  }

                                              }

                                          }

                                          「方式 2:在 Activity 的

                                          onKeyDown

                                          方法中监听,或者在

                                          Dialog.setOnKeyListener

                                          里面

                                          onKey

                                          中接收」

                                          1. Activity 中 onKeyDown:中解析每一个 keyCode 对应的数字值

                                            override fun onKeyDown(keyCode: Int, event: KeyEvent?)Boolean {

                                                if (KeyUtils.doNotSwitchViewPagerByKey(keyCode)) {

                                                    //按了键盘上 左右键 tab 键

                                                    return true

                                                }

                                                scanHelpL.get().acceptKey(this, keyCode) {

                                                    viewModel.scanByBarcode(it)

                                                }

                                                return super.onKeyDown(keyCode, event)

                                            }

                                            2. keyCode 值与具体对照值如下:

                                              object KeyUtils {

                                                  //控制按键 左右 tab 键 不切换 viewpage

                                                  fun doNotSwitchViewPagerByKey(keyCode: Int) = keyCode == KeyEvent.KEYCODE_DPAD_UP || keyCode == KeyEvent.KEYCODE_DPAD_DOWN || keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT || keyCode == KeyEvent.KEYCODE_TAB

                                                  /**

                                                   * keyCode转换为字符

                                                   */

                                                  fun keyCodeToChar(code: Int, isShift: Boolean): String{

                                                      return when (code) {

                                                          KeyEvent.KEYCODE_SHIFT_LEFT -> ""

                                                          KeyEvent.KEYCODE_0 -> if (isShift) ")" else "0"

                                                          KeyEvent.KEYCODE_1 -> if (isShift) "!" else "1"

                                                          KeyEvent.KEYCODE_2 -> if (isShift) "@" else "2"

                                                          KeyEvent.KEYCODE_3 -> if (isShift) "#" else "3"

                                                          KeyEvent.KEYCODE_4 -> if (isShift) "$" else "4"

                                                          KeyEvent.KEYCODE_5 -> if (isShift) "%" else "5"

                                                          KeyEvent.KEYCODE_6 -> if (isShift) "^" else "6"

                                                          KeyEvent.KEYCODE_7 -> if (isShift) "&" else "7"

                                                          KeyEvent.KEYCODE_8 -> if (isShift) "*" else "8"

                                                          KeyEvent.KEYCODE_9 -> if (isShift) "(" else "9"

                                                          KeyEvent.KEYCODE_A -> if (isShift) "A" else "a"

                                                          KeyEvent.KEYCODE_B -> if (isShift) "B" else "b"

                                                          KeyEvent.KEYCODE_C -> if (isShift) "C" else "c"

                                                          KeyEvent.KEYCODE_D -> if (isShift) "D" else "d"

                                                          KeyEvent.KEYCODE_E -> if (isShift) "E" else "e"

                                                          KeyEvent.KEYCODE_F -> if (isShift) "F" else "f"

                                                          KeyEvent.KEYCODE_G -> if (isShift) "G" else "g"

                                                          KeyEvent.KEYCODE_H -> if (isShift) "H" else "h"

                                                          KeyEvent.KEYCODE_I -> if (isShift) "I" else "i"

                                                          KeyEvent.KEYCODE_J -> if (isShift) "J" else "j"

                                                          KeyEvent.KEYCODE_K -> if (isShift) "K" else "k"

                                                          KeyEvent.KEYCODE_L -> if (isShift) "L" else "l"

                                                          KeyEvent.KEYCODE_M -> if (isShift) "M" else "m"

                                                          KeyEvent.KEYCODE_N -> if (isShift) "N" else "n"

                                                          KeyEvent.KEYCODE_O -> if (isShift) "O" else "o"

                                                          KeyEvent.KEYCODE_P -> if (isShift) "P" else "p"

                                                          KeyEvent.KEYCODE_Q -> if (isShift) "Q" else "q"

                                                          KeyEvent.KEYCODE_R -> if (isShift) "R" else "r"

                                                          KeyEvent.KEYCODE_S -> if (isShift) "S" else "s"

                                                          KeyEvent.KEYCODE_T -> if (isShift) "T" else "t"

                                                          KeyEvent.KEYCODE_U -> if (isShift) "U" else "u"

                                                          KeyEvent.KEYCODE_V -> if (isShift) "V" else "v"

                                                          KeyEvent.KEYCODE_W -> if (isShift) "W" else "w"

                                                          KeyEvent.KEYCODE_X -> if (isShift) "X" else "x"

                                                          KeyEvent.KEYCODE_Y -> if (isShift) "Y" else "y"

                                                          KeyEvent.KEYCODE_Z -> if (isShift) "Z" else "z"

                                                          else -> ""

                                                      }

                                                  }

                                              }

                                              3. 扫码枪和支付盒子扫完,最后一位是回车键:检测到回车键值时候,就可以将扫到的码的内容 提交出去处理支付等操作。如下:

                                                private fun acceptKey(keyCode: Int, block: (resultString) -> Unit) {

                                                    //监听扫码广播

                                                    if (keyCode != KeyEvent.KEYCODE_ENTER) {

                                                        if (isDeleteStringBuilder) {

                                                            val tmp: String = KeyUtils.keyCodeToChar(keyCode, hasShift)

                                                            stringBuilder.append(tmp)

                                                            hasShift = keyCode == KeyEvent.KEYCODE_SHIFT_LEFT

                                                        }

                                                    } else if (keyCode == KeyEvent.KEYCODE_ENTER) {

                                                        if (isDeleteStringBuilder) {

                                                            isDeleteStringBuilder = false

                                                            if (!TextUtils.isEmpty(stringBuilder.toString())) {

                                                                block?.invoke(stringBuilder.toString())

                                                            }

                                                            stringBuilder.delete(0, stringBuilder.length)

                                                            isDeleteStringBuilder = true

                                                        }

                                                    }

                                                }

                                                需要注意的是,扫码枪,支付盒子,键盘都是输入设备,要避免 UI 视图上面 控件焦点设置为 false, 同时界面不能有 EditText 控件,否则会将扫到的内容自动填入 EditText 控件里面去。

                                                六、总结

                                                本文重点介绍了 Android 智能嵌入式设备,接入串口,USB, 打印机,扫码枪支付盒子,键盘鼠标等,接入的简单开发。当然涉及到的蓝牙,蓝牙打印机,分屏这些会在后面的文章中进行介绍。


                                                关注更多AI编程资讯请去AI Coding专区:https://juejin.cn/aicoding

                                                ""~

                                                阅读原文

                                                跳转微信打开

                                                Fish AI Reader

                                                Fish AI Reader

                                                AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

                                                FishAI

                                                FishAI

                                                鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

                                                联系邮箱 441953276@qq.com

                                                相关标签

                                                Android 嵌入式设备 串口通信 USB打印机 扫码枪
                                                相关文章