更新记录
1.0.0(2026-07-20)
新增
- 首次发布
sendHex(options)- 发送 HEX 指令到 PLC,返回 HEX 响应字符串testConnection(host, port, timeout)- 测试 PLC TCP 连通性- Android 端基于
java.net.Socket的短连接实现 - 网络操作在 IO 线程执行,不阻塞 UI 线程
- HEX 字符串与 ByteArray 互转工具函数
- 支持 uni-app (vue3) 和 uni-app x 的 Android 平台
平台兼容性
uni-app(3.8.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | iOS | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|
| - | √ | - | - | - | - | 5.0 | - | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(3.91)
| Chrome | Safari | Android | iOS | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|
| - | - | 5.0 | - | - | - |
modbus-tcp
Android 端 Modbus TCP 直连通信 UTS 插件。
通过 java.net.Socket 实现 Modbus TCP 短连接通信,App 无需代理服务器即可直接连接 PLC。
特性
- 纯原生 TCP Socket 通信,不依赖 HTTP 代理
- 短连接模式:每次请求新建 Socket,发送报文 → 读取响应 → 关闭连接
- 支持发送任意 HEX 格式的 Modbus TCP 指令
- 网络操作在 IO 线程执行,不阻塞 UI
- 返回 HEX 格式响应字符串,便于业务层解析
支持平台
| 平台 | 支持情况 |
|---|---|
| uni-app App-Android (vue3) | ✓(minSdk 21 / Android 5.0+) |
| uni-app App-iOS | ✗ |
| uni-app H5 | ✗(浏览器不支持原生 TCP) |
| uni-app 小程序 | ✗ |
| uni-app x App-Android | ✓ |
注意:UTS 插件需要打自定义基座才能运行,标准基座下不生效。
目录结构
uni_modules/modbus-tcp/
├── package.json
├── README.md
├── CHANGELOG.md
└── utssdk/
├── interface.uts # 接口类型声明
└── app-android/
├── index.uts # Android 原生实现
└── config.json # 权限与依赖配置
API
SendHexOptions
发送 HEX 指令的参数类型。
| 属性 | 类型 | 必填 | 说明 |
|---|---|---|---|
| host | string | 是 | PLC 的 IP 地址 |
| port | number | 是 | PLC 的端口号(Modbus TCP 默认 502) |
| hex | string | 是 | HEX 指令字符串,可含空格,如 "00 01 00 00 00 06 01 06 00 00 00 01" |
| timeout | number | 是 | 超时时间(毫秒) |
sendHex(options)
发送 HEX 指令到 PLC,返回 PLC 的 HEX 响应字符串。
function sendHex(options: SendHexOptions): Promise<string>
- 成功:
resolve(HEX响应字符串),格式为大写空格分隔,如"00 01 00 00 00 06 01 06 00 00 00 01" - 失败:
reject(Error),如连接超时、PLC 无响应等
testConnection(host, port, timeout)
测试 PLC 的 TCP 连通性(建立连接后立即关闭,不发送数据)。
function testConnection(host: string, port: number, timeout: number): Promise<boolean>
- 成功:
resolve(true) - 失败:
resolve(false)(注意:不会 reject,失败时返回 false)
使用示例
1. 导入插件
// #ifdef APP-PLUS
import { sendHex, testConnection } from '@/uni_modules/modbus-tcp'
// #endif
2. 测试 PLC 连通性
// #ifdef APP-PLUS
const reachable = await testConnection('192.168.0.100', 502, 3000)
console.log('PLC连接:', reachable ? '成功' : '失败')
// #endif
3. 发送指令 - 写单个寄存器(功能码 06)
Modbus TCP 写单个寄存器指令格式:
事务ID(2) + 协议ID(2) + 长度(2) + 从站地址(1) + 功能码(1) + 寄存器地址(2) + 写入值(2)
// #ifdef APP-PLUS
// 向地址 0x0000 写入值 0x0001(触发继电器1)
const writeCmd = '00 01 00 00 00 06 01 06 00 00 00 01'
const response = await sendHex({
host: '192.168.0.100',
port: 502,
hex: writeCmd,
timeout: 5000
})
console.log('PLC响应:', response)
// 响应: 00 01 00 00 00 06 01 06 00 00 00 01(原样回显表示写入成功)
// #endif
4. 发送指令 - 读离散输入(功能码 02)
读取 PLC 输入端子状态,常用于检测按钮、传感器信号。
// #ifdef APP-PLUS
// 读取第13脚输入(地址 0x0D = 13,施耐德 M221 的 I13)
// 事务ID: 00 03 | 协议: 00 00 | 长度: 00 06 | 从站: 01 | 功能码: 02 | 起始地址: 00 0D | 数量: 00 01
const readCmd = '00 03 00 00 00 06 01 02 00 0D 00 01'
const response = await sendHex({
host: '192.168.0.100',
port: 502,
hex: readCmd,
timeout: 3000
})
console.log('PLC响应:', response)
// 解析响应(功能码02,读取1个离散输入)
// 响应格式: 事务ID(2) + 协议(2) + 长度(2) + 从站(1) + 功能码(1) + 字节数(1) + 数据(1)
// 示例: 00 03 00 00 00 04 01 02 01 01
// ↑ parts[9] = 数据字节
const parts = response.split(' ')
if (parts.length >= 10) {
const dataByte = parseInt(parts[9], 16)
const isActive = (dataByte & 0x01) !== 0 // 最低位即输入状态
console.log('I13输入状态:', isActive ? '高电平' : '低电平')
}
// #endif
5. 发送指令 - 读输入寄存器(功能码 04)
// #ifdef APP-PLUS
// 读取地址 0x0000 的 1 个输入寄存器
const readCmd = '00 04 00 00 00 06 01 04 00 00 00 01'
const response = await sendHex({
host: '192.168.0.100',
port: 502,
hex: readCmd,
timeout: 3000
})
console.log('PLC响应:', response)
// 解析响应(功能码04,读取1个寄存器)
// 响应格式: 事务ID(2) + 协议(2) + 长度(2) + 从站(1) + 功能码(1) + 字节数(1) + 数据(2)
// 示例: 00 04 00 00 00 05 01 04 02 00 01
// ↑↑ parts[9-10] = 寄存器值(大端序)
const parts = response.split(' ')
if (parts.length >= 11) {
const hi = parseInt(parts[9], 16)
const lo = parseInt(parts[10], 16)
const value = hi * 256 + lo
console.log('寄存器值:', value)
}
// #endif
6. 封装工具类(推荐用法)
参考实际项目中的封装方式,将插件调用封装为通用工具类:
/**
* Modbus TCP 工具类
*/
// #ifdef APP-PLUS
import { sendHex, testConnection } from '@/uni_modules/modbus-tcp'
// #endif
let plcHost = '192.168.0.100'
let plcPort = 502
/** 设置 PLC 地址 */
export function setPlcAddress(host, port) {
plcHost = host
plcPort = port
}
/**
* 发送 HEX 指令
* @param {string} hexCommand - HEX 指令字符串
* @param {number} timeout - 超时时间(ms)
* @returns {Promise<string>} PLC 响应的 HEX 字符串
*/
export function sendRawHexCommand(hexCommand, timeout = 5000) {
// #ifdef APP-PLUS
return sendHex({
host: plcHost,
port: plcPort,
hex: hexCommand,
timeout: timeout
})
// #endif
// #ifndef APP-PLUS
return Promise.reject(new Error('当前平台不支持 Modbus TCP 直连'))
// #endif
}
/**
* 检查 PLC 是否可连接
* @returns {Promise<boolean>}
*/
export function checkConnection() {
// #ifdef APP-PLUS
return testConnection(plcHost, Number(plcPort), 3000)
// #endif
// #ifndef APP-PLUS
return Promise.resolve(false)
// #endif
}
/**
* 读取 PLC 离散输入状态(功能码 02)
* @param {number} inputNo - 输入路号(从1开始)
* @returns {Promise<boolean>} 输入是否为高电平
*/
export async function readPlcInput(inputNo) {
const addr = inputNo - 1
const addrHi = (addr >> 8).toString(16).padStart(2, '0')
const addrLo = (addr & 0xFF).toString(16).padStart(2, '0')
const hexCmd = `00 03 00 00 00 06 01 02 ${addrHi} ${addrLo} 00 01`
const response = await sendRawHexCommand(hexCmd)
const parts = response.split(' ')
if (parts.length >= 10) {
const dataByte = parseInt(parts[9], 16)
return (dataByte & 0x01) !== 0
}
return false
}
7. 轮询读取 PLC 输入(唤醒场景示例)
import { setPlcAddress, readPlcInput } from './modbusUtil'
let pollTimer = null
let polling = false
/** 开始轮询 PLC 输入 */
function startPolling(plcHost, plcPort, inputNo, onActive) {
setPlcAddress(plcHost, plcPort)
pollTimer = setInterval(async () => {
if (polling) return
polling = true
try {
const isActive = await readPlcInput(inputNo)
if (isActive) {
onActive() // 输入为高电平,执行回调
}
} catch (e) {
console.log('读取失败:', e)
} finally {
polling = false
}
}, 500)
}
/** 停止轮询 */
function stopPolling() {
if (pollTimer) {
clearInterval(pollTimer)
pollTimer = null
}
}
// 使用:每隔 500ms 读取 I13,高电平时触发回调
startPolling('192.168.0.100', 502, 14, () => {
console.log('I13 触发!')
stopPolling()
})
Modbus TCP 指令格式速查
Modbus TCP 报文 = MBAP 头(7字节) + PDU
┌─────────┬─────────┬─────────┬──────────┬──────────┐
│ 事务ID │ 协议ID │ 长度 │ 从站地址 │ PDU │
│ 2字节 │ 2字节 │ 2字节 │ 1字节 │ N字节 │
│ (任意) │(00 00) │ │ (通常01) │功能码+...│
└─────────┴─────────┴─────────┴──────────┴──────────┘
常用功能码指令模板
| 功能码 | 操作 | 指令模板(HEX) | 说明 |
|---|---|---|---|
| 06 | 写单个寄存器 | XX XX 00 00 00 06 01 06 AA AA VV VV |
AA=地址, VV=值 |
| 02 | 读离散输入 | XX XX 00 00 00 06 01 02 AA AA NN NN |
AA=起始地址, NN=数量 |
| 04 | 读输入寄存器 | XX XX 00 00 00 06 01 04 AA AA NN NN |
AA=起始地址, NN=数量 |
| 03 | 读保持寄存器 | XX XX 00 00 00 06 01 03 AA AA NN NN |
AA=起始地址, NN=数量 |
| 05 | 写单个线圈 | XX XX 00 00 00 06 01 05 AA AA FF 00 |
AA=地址, FF00=ON/0000=OFF |
事务ID(XX XX)可任意取值,PLC 会原样回显。
已验证 PLC 型号
| 品牌 | 型号 | 说明 |
|---|---|---|
| 施耐德 | TM221CE24R | I0~I13 对应 Modbus 离散输入地址 0~13 |
| 更多 | - | 欢迎补充 |
注意事项
- 自定义基座:UTS 插件在标准基座下不生效,需打自定义基座运行。
- Android 最低版本:需 Android 5.0(API 21)及以上。
- 网络权限:插件已声明
INTERNET权限,无需额外配置。 - 明文流量:原生 TCP Socket 不受
usesCleartextTraffic限制。 - H5 不支持:浏览器环境无法建立 TCP 连接,请使用条件编译
#ifdef APP-PLUS隔离。 - 短连接模式:每次
sendHex调用都会新建 TCP 连接,适用于低频控制场景。如需高频通信,请评估连接开销。 - 超时设置:建议读取操作超时设 3000ms,写入操作设 5000ms。超时过短可能导致 PLC 响应不完整。

收藏人数:
购买普通授权版(
试用
赞赏(0)
下载 7
赞赏 0
下载 12441222
赞赏 1934
赞赏
京公网安备:11010802035340号