Uniapp 串口通信原生插件开发指南(零基础版)

开发前的准备工作

首先,你需要安装以下工具:

  1. HBuilderX - 这是开发 Uniapp 应用和原生插件的主要工具
  2. Android Studio - 用于编写和编译 Android 原生代码
  3. Java JDK - 确保安装 JDK 8 或以上版本
  4. Android SDK - 通过 Android Studio 安装

创建插件项目

1. 在 HBuilderX 中创建原生插件项目
  1. 打开 HBuilderX,选择菜单:文件 -> 新建 -> 原生插件
  2. 填写插件信息:
    • 插件名称:UniSerialPort
    • 插件ID:UniSerialPort
    • 选择"仅Android平台"
  3. 点击"创建",项目将被创建在 HBuilderX 的项目目录中
2. 导入项目到 Android Studio
  1. 打开 Android Studio
  2. 选择"Open an existing Android Studio project"
  3. 导航到 HBuilderX 创建的插件项目目录,选择 android 文件夹
  4. 等待 Android Studio 导入并构建项目

实现串口通信功能

1. 添加 Android 串口库

首先,我们需要添加 Android 串口通信库到项目中:

  1. 在 Android Studio 中,打开 android/build.gradle 文件
  2. dependencies 块中添加以下依赖:
dependencies {
    implementation 'com.github.licheedev:AndroidSerialPort:1.3.1'
}
2. 创建串口管理类

在 Android Studio 中,创建以下类文件:

  1. SerialPortManager.java - 负责串口的打开、关闭、读写操作
// SerialPortManager.java
package com.uniserialport;

import com.licheedev.serialworker.core.SerialConfig;
import com.licheedev.serialworker.core.SerialWorker;
import com.licheedev.serialworker.worker.ByteDataListener;
import com.licheedev.serialworker.worker.Worker;
import java.util.HashMap;
import java.util.Map;

public class SerialPortManager {
    private static Map<String, Worker> workers = new HashMap<>();
    
    // 打开串口
    public static Worker openSerialPort(String path, int baudrate, int dataBits, 
                                        int stopBits, String parity, int flowControl, 
                                        int delay) throws Exception {
        if (workers.containsKey(path)) {
            closeSerialPort(path);
        }
        
        // 配置串口参数
        SerialConfig config = new SerialConfig(path, baudrate);
        config.setDataBits(dataBits);
        config.setStopBits(stopBits);
        
        // 设置校验位
        switch (parity.toLowerCase()) {
            case "odd":
                config.setParity(android_serialport_api.SerialPort.PARITY_ODD);
                break;
            case "even":
                config.setParity(android_serialport_api.SerialPort.PARITY_EVEN);
                break;
            default:
                config.setParity(android_serialport_api.SerialPort.PARITY_NONE);
        }
        
        // 设置流控
        config.setFlowControl(flowControl);
        
        // 创建串口Worker
        Worker worker = new SerialWorker(config);
        worker.setReceiveDelay(delay); // 设置粘包处理延迟
        workers.put(path, worker);
        return worker;
    }
    
    // 关闭串口
    public static void closeSerialPort(String path) {
        Worker worker = workers.get(path);
        if (worker != null) {
            worker.release();
            workers.remove(path);
        }
    }
    
    // 发送数据
    public static void sendData(String path, byte[] data) throws Exception {
        Worker worker = workers.get(path);
        if (worker != null) {
            worker.send(data);
        }
    }
    
    // 开始监听数据
    public static void startListening(String path, DataCallback callback) {
        Worker worker = workers.get(path);
        if (worker != null) {
            worker.setDataListener(new ByteDataListener() {
                @Override
                public void onDataReceived(byte[] data) {
                    if (callback != null) {
                        callback.onDataReceived(data);
                    }
                }
            });
        }
    }
    
    // 数据回调接口
    public interface DataCallback {
        void onDataReceived(byte[] data);
    }
}
3. 创建 JS 接口模块

创建 SerialPortModule.java 文件,这是插件与 JS 交互的桥梁:

// SerialPortModule.java
package com.uniserialport;

import com.alibaba.fastjson.JSONObject;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;

public class SerialPortModule extends UniModule {
    
    // 打开串口
    @UniJSMethod(uiThread = false)
    public void open(Map<String, Object> options, UniJSCallback callback) {
        try {
            String path = (String) options.get("path");
            int baudrate = (Integer) options.get("baudrate");
            int dataBits = (Integer) options.getOrDefault("dataBits", 8);
            int stopBits = (Integer) options.getOrDefault("stopBits", 1);
            String parity = (String) options.getOrDefault("parity", "none");
            int flowControl = (Integer) options.getOrDefault("flowControl", 0);
            int delay = (Integer) options.getOrDefault("delay", 20);
            
            SerialPortManager.openSerialPort(path, baudrate, dataBits, stopBits, parity, flowControl, delay);
            callback.invokeSuccess();
        } catch (Exception e) {
            callback.invokeError(e.getMessage());
        }
    }
    
    // 关闭串口
    @UniJSMethod(uiThread = false)
    public void close(String path, UniJSCallback callback) {
        try {
            SerialPortManager.closeSerialPort(path);
            callback.invokeSuccess();
        } catch (Exception e) {
            callback.invokeError(e.getMessage());
        }
    }
    
    // 发送数据
    @UniJSMethod(uiThread = false)
    public void send(Map<String, Object> options, UniJSCallback callback) {
        try {
            String path = (String) options.get("path");
            String data = (String) options.get("data");
            byte[] bytes = data.getBytes("UTF-8");
            SerialPortManager.sendData(path, bytes);
            callback.invokeSuccess();
        } catch (Exception e) {
            callback.invokeError(e.getMessage());
        }
    }
    
    // 开始监听数据
    @UniJSMethod(uiThread = false)
    public void startListening(String path, final UniJSCallback callback) {
        try {
            SerialPortManager.startListening(path, new SerialPortManager.DataCallback() {
                @Override
                public void onDataReceived(byte[] data) {
                    try {
                        String hexData = bytesToHexString(data);
                        JSONObject result = new JSONObject();
                        result.put("data", hexData);
                        callback.invoke(result);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            
            callback.invokeSuccess();
        } catch (Exception e) {
            callback.invokeError(e.getMessage());
        }
    }
    
    // 工具方法:字节数组转16进制字符串
    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            String hex = String.format("%02X", b);
            sb.append(hex);
        }
        return sb.toString();
    }
}
4. 配置插件信息

编辑 package.json 文件,配置插件信息:

{
  "name": "UniSerialPort",
  "id": "UniSerialPort",
  "description": "Uniapp 串口通信插件",
  "version": "1.0.0",
  "platforms": ["android"],
  "modules": {
    "SerialPort": {
      "class": "com.uniserialport.SerialPortModule",
      "methods": ["open", "close", "send", "startListening"]
    }
  },
  "android": {
    "permissions": [
      "<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"/>",
      "<uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"/>",
      "<uses-permission android:name=\"android.permission.INTERNET\"/>"
    ]
  }
}

打包和使用插件

1. 打包插件
  1. 在 HBuilderX 中,右键点击插件项目
  2. 选择"原生插件" -> “制作本地插件”
  3. 选择 Android 平台
  4. 点击"制作"按钮,HBuilderX 将自动调用 Android Studio 编译插件
  5. 编译完成后,插件将被打包到 nativeplugins/UniSerialPort/dist 目录
2. 在 Uniapp 项目中使用插件
  1. 在你的 Uniapp 项目中,创建 nativeplugins/UniSerialPort 目录
  2. 将刚才打包好的插件文件复制到该目录
  3. manifest.json 中配置插件:
{
  "app-plus": {
    "nvueStyleCompiler": "uni-app",
    "usingComponents": true,
    "nativePlugins": {
      "UniSerialPort": {
        "version": "1.0.0",
        "provider": "local"
      }
    }
  }
}
3. 在 JS 代码中调用插件
// 引入插件
const serialPort = uni.requireNativePlugin('UniSerialPort');

// 打开串口
serialPort.open({
  path: '/dev/ttyS1',  // 串口设备路径
  baudrate: 9600,      // 波特率
  dataBits: 8,         // 数据位
  stopBits: 1,         // 停止位
  parity: 'none',      // 校验位:none, odd, even
  flowControl: 0,      // 流控
  delay: 50            // 粘包处理延迟时间(ms)
}, (res) => {
  if (res.code === 0) {
    console.log('串口打开成功');
    // 开始监听数据
    startListening();
  } else {
    console.error('串口打开失败:', res.message);
  }
});

// 监听数据
function startListening() {
  serialPort.startListening('/dev/ttyS1', (res) => {
    console.log('收到数据:', res.data);
    // 处理接收到的16进制数据
  });
}

// 发送数据
function sendData() {
  serialPort.send({
    path: '/dev/ttyS1',
    data: 'Hello, Serial Port!'
  }, (res) => {
    if (res.code === 0) {
      console.log('数据发送成功');
    } else {
      console.error('数据发送失败:', res.message);
    }
  });
}

// 关闭串口
function closeSerialPort() {
  serialPort.close('/dev/ttyS1', (res) => {
    if (res.code === 0) {
      console.log('串口关闭成功');
    } else {
      console.error('串口关闭失败:', res.message);
    }
  });
}

常见问题和解决方案

  1. 找不到串口设备

    • 确认设备上串口的实际路径
    • 检查应用是否有访问串口的权限
  2. 无法打开串口

    • 检查串口是否被其他应用占用
    • 确认串口参数配置正确
  3. 接收数据不完整

    • 调整粘包处理的延迟时间
    • 检查数据解析逻辑
  4. 应用崩溃

    • 检查 AndroidManifest.xml 中是否添加了必要的权限
    • 确保所有的串口操作都在 try-catch 块中进行

通过以上步骤,你就可以完成一个功能完整的 Uniapp 串口通信原生插件。即使你没有安卓原生开发经验,按照这个指南一步步操作,也能够成功实现这个插件。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值