最近遇到在安卓上通过串口连接地磅的需求, 基本上实现了读数和判断地磅稳定状态 , 这里分享出来实现过程以方便大家开发.我们开发用的打印机是XK3190-A9+ ,需要这个打印机参数文档的可以耀华官网下载:http://www.yaohua.com.cn/companyfile/16/
地磅是通过串口连接的,连接地磅主要参数有串口名称、波特率、一帧长度、以及是否校验,校验位等其他参数;
我这边做的参数有:串口名称、波特率、一帧长度以及通讯方式;
一、地磅参数
1、串口名称
在我安卓工控机上是已接入是RS232,命名为ttfS1和ttfS3两个串口;
RS232、TTL、COM 是不同电平和逻辑的串口,百度一下大概有常用的电平标准有TTL、CMOS、LVTTL、LVCMOS、ECL、PECL、LVPECL、RS232、RS485等,还有一些速度比较高的 LVDS、GTL、PGTL、CML、HSTL、SSTL等
2、波特率
波特率即调制速率,指的是有效数据讯号调制载波的速率,即单位时间内载波调制状态变化的次数。
XK3910-A9+支持的波特率有:600、1200、2400、4800、9600;我这里统一选择了9600
3、一帧长度
是指一次信号携带的数据长度 ,耀华是8位,其中开后1位是“=”号连接位,后面7位是数据位。
4、通讯方式
意思是说地磅跟其他机器的通讯方式,可以选择连续发数据,但不接收数据、发送一次和等待接收、连续发送8位、连续发送9位这四种;我选择的是连续发送8位。
地磅上相关参数的设置方步骤和方式:
更多设置可以去下载打印机文档.
二、配置参数
1、外设参数配置,先配置上面的默认参数
String port = “/dev/ttyS1”;//串口名字,默认ttfS1
int baudRate = 9600;//波特率,默认9600
int dateLengthPerFrame = 8;//每一帧的数据长度 ,耀华是8位
2、增加上一些本地的参数配置
int stableTimeMills = 1000;//地磅稳定时间,超过这个时间数据不曾修改则表示地磅稳定,耀华的是1秒
int receiveTimeOut = 1000;//地磅接收超时,一般地磅会毫秒内发送数据,超过这个时间则表示地磅链接失败,默认是1秒没内收到数据就表示地磅链接失败
int receiveTimeCountdownInterval = 500;//地磅接收超时,倒计时 ,默认50毫数一次
double recentlyValidWeightNum = 0.0;//最近一次有效读磅数
3、自定义地磅状态
public enum PoundStatus {
UN_CONNECTED(-1),//未连接
UNSTABLE(0),//已连接,不稳定
STABLE(1);//已连接,已稳定
int code;
PoundStatus(int code) {
this.code = code;
}
public String getStatusStr() {
return code == -1 ? "未连接" : (code == 0 ? "不稳定" : "稳定");
}
}
三、连接地磅代码解析
1、增加一个串口管理依赖
gradle 下添加依赖 implementation 'com.github.547394:SerialPortManager:1.0.10'
2、新建一个PoundManager.java 增加上面的相关配置参数
public class PoundManager {
static PoundManager instance;//唯一实例
SerialPortManager serialPort;//端口管理器
SerialPortProtocol protocol = new SerialPortProtocol();//端口协议
String port = "/dev/ttyS1";//串口名字,默认ttfS1
int baudRate = 9600;//波特率,默认9600
int stableTimeMills = 1000;//地磅稳定时间,超过这个时间数据不曾修改则表示地磅稳定,耀华的是1秒
CountDownTimer countDownTimer;//地磅信号接收超时倒计时
int receiveTimeOut = 1000;//地磅接收超时,一般地磅会毫秒内发送数据,超过这个时间则表示地磅链接失败,默认是1秒没内收到数据就表示地磅链接失败
int receiveTimeCountdownInterval = 500;//地磅接收超时,倒计时 ,默认50毫数一次,
int dateLengthPerFrame = 8;//每一帧的数据长度 ,耀华是8位
PoundStatusListener poundStatusListener;//地磅状态监听器
PoundStatus poundStatus = PoundStatus.UN_CONNECTED;//地磅状态,默认未连接
double recentlyValidWeightNum = 0.0;//最近一次有效读磅数
long lastValidWeightNumTimeMills;//最后一次有效读数时间
long lastDataReceiveTime;//最后一次监听到数据输入的时间
public static PoundManager getInstance() {
if (instance == null) {
instance = new PoundManager();
}
return instance;
}
/**
* 地磅信息
*/
public interface PoundStatusListener {
void onTick(double lastValidWeightNum, PoundStatus status);
}
/**
* 地磅状态
*/
public enum PoundStatus {
UN_CONNECTED(-1),//未连接
UNSTABLE(0),//已连接,不稳定
STABLE(1);//已连接,已稳定
int code;
PoundStatus(int code) {
this.code = code;
}
public String getStatusStr() {
return code == -1 ? "未连接" : (code == 0 ? "不稳定" : "稳定");
}
}
}
3、初始化端口连接
//初始化端口设置
public void init() {
outPutInfo("正在初始化地磅设置");
initSettingFromSP();
serialPort = new SerialPortManager();
protocol.setFixedLength(dateLengthPerFrame);
serialPort.setBufferSize(1024);
serialPort.setProtocol(protocol);
serialPort.setReceivedTimeout(receiveTimeOut);
serialPort.enableDebug(true);//是否开启日志
serialPort.setOnDataListener(new OnDataListener() {
@Override
public void onDataReceived(byte[] bytes) {
// dealReceiveDataOld(bytes);
dealReceiveData(new String(bytes));
}
@Override
public void onDataSend(byte[] bytes) {
super.onDataSend(bytes);
}
});
serialPort.setOnOpenListener(new OnOpenListener() {
@Override
public void onSuccess(File device) {
super.onSuccess(device);
// poundStatus = PoundStatus.UNSTABLE;
// outPutInfo("地磅连接成功,开始监听数据返回");
startListeningPortCountDown();
}
@Override
public void onFailure(File device, SerialPortError error) {
super.onFailure(device, error);
outPutInfo("地磅连接失败:" + error.toString());
startListeningPortCountDown();
}
});
outPutInfo("正在链接地磅");
serialPort.open(port, baudRate);
}
4、处理接受到的数据
StringBuilder tempReceiveStr;//为了防止重复new,定义一个自由成员变量
private void dealReceiveData(String bytes) {
try {
if (bytes.length() <= 1) return;
tempReceiveStr = new StringBuilder(bytes).delete(0,1).reverse();
Log.v("wztest", "tempStr=" +tempReceiveStr.toString());
try {
if (tempReceiveStr.length() != 0) {
//有数据
if (Double.parseDouble(tempReceiveStr.toString()) != recentlyValidWeightNum) {
//数据不同,表示数据有变动,不稳定
recentlyValidWeightNum = Double.parseDouble(tempReceiveStr.toString());
lastValidWeightNumTimeMills = System.currentTimeMillis();
poundStatus = PoundStatus.UNSTABLE;
} else {
//数据不变,判断是否稳定
poundStatus = System.currentTimeMillis() - lastValidWeightNumTimeMills > stableTimeMills ? PoundStatus.STABLE : PoundStatus.UNSTABLE;
}
}
} catch (Exception e) {
e.printStackTrace();
recentlyValidWeightNum = 0.0;
poundStatus = PoundStatus.UNSTABLE;
// Log.v("wztest", "数据中包含了无效数字");
}
lastDataReceiveTime = System.currentTimeMillis();
//输入日志和回调
outPutInfo("收到地磅数据,最近一条有效数据为:" + recentlyValidWeightNum);
} catch (Exception e) {
outPutInfo("地磅信息解析错误:" + new String(bytes));
e.printStackTrace();
lastValidWeightNumTimeMills = System.currentTimeMillis();
poundStatus = PoundStatus.UNSTABLE;
}
}
5、监听者模式+倒计时返回设备实时状态
//监听串口数据读入倒计时
//50年有效
private void startListeningPortCountDown() {
countDownTimer = new CountDownTimer(1000 * 60 * 60 * 24 * 365 * 50, 500) {
@Override
public void onTick(long millisUntilFinished) {
if (System.currentTimeMillis() - lastDataReceiveTime > receiveTimeOut) {
poundStatus = PoundStatus.UN_CONNECTED;
recentlyValidWeightNum = 0.0;
outPutInfo("地磅连接超时");
}
}
@Override
public void onFinish() {
//50年未关机?
}
}.start();
}
public void onDestroy() {
if (countDownTimer != null) countDownTimer.cancel();
if (instance != null) instance = null;
}
public PoundStatusListener getPoundStatusListener() {
return poundStatusListener;
}
public PoundManager setPoundStatusListener(PoundStatusListener poundStatusListener) {
this.poundStatusListener = poundStatusListener;
return this;
}
/**
* 输出日志和回调
*
* @param message
*/
void outPutInfo(String message) {
Log.v("PoundManager", message == null ? "" : message);
if (poundStatusListener != null) {
poundStatusListener.onTick(recentlyValidWeightNum, poundStatus);
}
}
6、最后调用使用
PoundManager.getInstance().setPoundStatusListener(new PoundManager.PoundStatusListener() {
@Override
public void onTick(double lastValidWeightNum, PoundManager.PoundStatus status) {
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_num.setText(String.valueOf(lastValidWeightNum));
tv_hint.setText(status.getStatusStr());
tv_num.setTextColor(status == PoundManager.PoundStatus.STABLE ? Color.rgb(0, 255, 0) : Color.rgb(255, 0, 0));
tv_hint.setTextColor(status == PoundManager.PoundStatus.STABLE ? Color.rgb(0, 255, 0) : Color.rgb(255, 0, 0));
}
});
}
}).init();
这样就正常接入了.
四、过程中遇到的问题和解决方案
1、第一个问题是地磅连接电脑可以互相接收数据,工控机连接电脑可以互相接收数据,但是地磅连接工控机就无法接收数据。
这个问题造成的原因是工控机串口线接错了 ,RS232串口线一般2是输出信号,3是输入信号,工控机那边接错线,把输入和输出接错了。
按道理工控机接错了,则工控机的输出变成了输入,那么连接到电脑,应该是无法互相接收数据的(电脑端的输入没信号了)
但是非常非常巧合的是,我买了一条双目数据线正好也是接错的,结果负负得正,反而把数据转换过来了!!!
最后拆了三个串口线发现了上面的原理,改一下针口解决了。
2、数据一次性接收的不是8位 , 而是多串数据在一次接收中
这个主要是因为没有在协议上设置一次接收数据长度,
protocol.setFixedLength(dateLengthPerFrame);
完整代码:
package com.jda.app.main.utils;
import android.content.Context;
import android.os.CountDownTimer;
import android.util.Log;
import com.tim.serialportlib.OnDataListener;
import com.tim.serialportlib.OnOpenListener;
import com.tim.serialportlib.SerialPortError;
import com.tim.serialportlib.SerialPortManager;
import com.tim.serialportlib.SerialPortProtocol;
import java.io.File;
/**
* CreateInfo:
* wz created on 2021/1/27;
* Email 37717239@qq.com.
* <p>
* Description:
* gradle 下添加依赖 implementation 'com.github.547394:SerialPortManager:1.0.10'
* 耀华地磅管理工具
*/
public class PoundManager {
static PoundManager instance;//唯一实例
SerialPortManager serialPort;//端口管理器
SerialPortProtocol protocol = new SerialPortProtocol();//端口协议
String port = "/dev/ttyS1";//串口名字,默认ttfS1
int baudRate = 9600;//波特率,默认9600
int stableTimeMills = 1000;//地磅稳定时间,超过这个时间数据不曾修改则表示地磅稳定,耀华的是1秒
CountDownTimer countDownTimer;//地磅信号接收超时倒计时
int receiveTimeOut = 1000;//地磅接收超时,一般地磅会毫秒内发送数据,超过这个时间则表示地磅链接失败,默认是1秒没内收到数据就表示地磅链接失败
int receiveTimeCountdownInterval = 500;//地磅接收超时,倒计时 ,默认50毫数一次,
int dateLengthPerFrame = 8;//每一帧的数据长度 ,耀华是8位
PoundStatusListener poundStatusListener;//地磅状态监听器
PoundStatus poundStatus = PoundStatus.UN_CONNECTED;//地磅状态,默认未连接
double recentlyValidWeightNum = 0.0;//最近一次有效读磅数
long lastValidWeightNumTimeMills;//最后一次有效读数时间
long lastDataReceiveTime;//最后一次监听到数据输入的时间
public static PoundManager getInstance() {
if (instance == null) {
instance = new PoundManager();
}
return instance;
}
//初始化端口设置
public void init() {
outPutInfo("正在初始化地磅设置");
initSettingFromSP();
serialPort = new SerialPortManager();
protocol.setFixedLength(dateLengthPerFrame);
serialPort.setBufferSize(1024);
serialPort.setProtocol(protocol);
serialPort.setReceivedTimeout(receiveTimeOut);
serialPort.enableDebug(true);//是否开启日志
serialPort.setOnDataListener(new OnDataListener() {
@Override
public void onDataReceived(byte[] bytes) {
// dealReceiveDataOld(bytes);
dealReceiveData(new String(bytes));
}
@Override
public void onDataSend(byte[] bytes) {
super.onDataSend(bytes);
}
});
serialPort.setOnOpenListener(new OnOpenListener() {
@Override
public void onSuccess(File device) {
super.onSuccess(device);
// poundStatus = PoundStatus.UNSTABLE;
// outPutInfo("地磅连接成功,开始监听数据返回");
startListeningPortCountDown();
}
@Override
public void onFailure(File device, SerialPortError error) {
super.onFailure(device, error);
outPutInfo("地磅连接失败:" + error.toString());
startListeningPortCountDown();
}
});
outPutInfo("正在链接地磅");
serialPort.open(port, baudRate);
}
StringBuilder tempReceiveStr;//为了防止重复new,定义一个自由成员变量
private void dealReceiveData(String bytes) {
try {
if (bytes.length() <= 1) return;
tempReceiveStr = new StringBuilder(bytes).delete(0,1).reverse();
Log.v("wztest", "tempStr=" +tempReceiveStr.toString());
try {
if (tempReceiveStr.length() != 0) {
//有数据
if (Double.parseDouble(tempReceiveStr.toString()) != recentlyValidWeightNum) {
//数据不同,表示数据有变动,不稳定
recentlyValidWeightNum = Double.parseDouble(tempReceiveStr.toString());
lastValidWeightNumTimeMills = System.currentTimeMillis();
poundStatus = PoundStatus.UNSTABLE;
} else {
//数据不变,判断是否稳定
poundStatus = System.currentTimeMillis() - lastValidWeightNumTimeMills > stableTimeMills ? PoundStatus.STABLE : PoundStatus.UNSTABLE;
}
}
} catch (Exception e) {
e.printStackTrace();
recentlyValidWeightNum = 0.0;
poundStatus = PoundStatus.UNSTABLE;
// Log.v("wztest", "数据中包含了无效数字");
}
lastDataReceiveTime = System.currentTimeMillis();
//输入日志和回调
outPutInfo("收到地磅数据,最近一条有效数据为:" + recentlyValidWeightNum);
} catch (Exception e) {
outPutInfo("地磅信息解析错误:" + new String(bytes));
e.printStackTrace();
lastValidWeightNumTimeMills = System.currentTimeMillis();
poundStatus = PoundStatus.UNSTABLE;
}
}
/****
* 第一版处理数据方式 ,通过=号分割处理,
* 临时备份, 准备第二个更加有效率的方式处理
* @param bytes
*/
private void dealReceiveDataOld(byte[] bytes) {
try {
if (bytes.length == 0) return;
String[] splitList = new String(bytes).split("=");//耀华的称重数据解析
// Log.v("wztest", "收到消息" + new String(bytes) + " 分割数组长度" + splitList.length);
boolean containsDifferentNum = false;//数据段是否包含与最近称重不同数据
for (int i = 0; i < splitList.length; i++) {
// Log.v("wztest", "第" + i + "个数据" + splitList[i]);
try {
if (!splitList[i].equals("")) {
if (Double.parseDouble(splitList[i]) != recentlyValidWeightNum) {//发现不同数据,表示数据有变动
containsDifferentNum = true;
break;
// Log.v("wztest", "数据中发现了不同数字");
}
}
} catch (Exception e) {
e.printStackTrace();
containsDifferentNum = true;
// Log.v("wztest", "数据中包含了无效数字");
}
}
if (containsDifferentNum) {//如果数据不一致,则刷新最近一条有效信息的时间为当前时间,并且将数据置为不稳定
lastValidWeightNumTimeMills = System.currentTimeMillis();
poundStatus = PoundStatus.UNSTABLE;
try {
recentlyValidWeightNum = Double.parseDouble(splitList[splitList.length - 1]);
// Log.v("wztest", "设置有效数据为最后一个数据" + recentlyValidWeightNum);
} catch (Exception e) {
e.printStackTrace();
recentlyValidWeightNum = 0.0;
// Log.v("wztest", "最后一个数据不是有效数字");
}
} else {
//数据与以前一致,则判断最近一次时间与当前时间是否超过了稳定期,超过则表示稳定,未超过则表示不稳定
poundStatus = System.currentTimeMillis() - lastValidWeightNumTimeMills > stableTimeMills ? PoundStatus.STABLE : PoundStatus.UNSTABLE;
}
lastDataReceiveTime = System.currentTimeMillis();
//输入日志和回调
outPutInfo("收到地磅数据,最近一条有效数据为:" + recentlyValidWeightNum);
} catch (Exception e) {
outPutInfo("地磅信息解析错误:" + new String(bytes));
e.printStackTrace();
lastValidWeightNumTimeMills = System.currentTimeMillis();
poundStatus = PoundStatus.UNSTABLE;
}
}
//监听串口数据读入倒计时
//50年有效
private void startListeningPortCountDown() {
countDownTimer = new CountDownTimer(1000 * 60 * 60 * 24 * 365 * 50, 500) {
@Override
public void onTick(long millisUntilFinished) {
if (System.currentTimeMillis() - lastDataReceiveTime > receiveTimeOut) {
poundStatus = PoundStatus.UN_CONNECTED;
recentlyValidWeightNum = 0.0;
outPutInfo("地磅连接超时");
}
}
@Override
public void onFinish() {
//50年未关机?
}
}.start();
}
public void onDestroy() {
if (countDownTimer != null) countDownTimer.cancel();
if (instance != null) instance = null;
}
public PoundStatusListener getPoundStatusListener() {
return poundStatusListener;
}
public PoundManager setPoundStatusListener(PoundStatusListener poundStatusListener) {
this.poundStatusListener = poundStatusListener;
return this;
}
/**
* 输出日志和回调
*
* @param message
*/
void outPutInfo(String message) {
Log.v("PoundManager", message == null ? "" : message);
if (poundStatusListener != null) {
poundStatusListener.onTick(recentlyValidWeightNum, poundStatus);
}
}
//这里执行一些地磅设置缓存读取,比如波特率修改、端口修改
private void initSettingFromSP() {
}
public boolean checkPoundEnvironment(Context context) {
return false;
}
public void setBaudRate(int baudRate) {
this.baudRate = baudRate;
}
public int getBaudRate() {
return this.baudRate;
}
public void setPort(String port) {
this.port = port;
}
public String getPort() {
return this.port;
}
/**
* 地磅信息
*/
public interface PoundStatusListener {
void onTick(double lastValidWeightNum, PoundStatus status);
}
/**
* 地磅状态
*/
public enum PoundStatus {
UN_CONNECTED(-1),//未连接
UNSTABLE(0),//已连接,不稳定
STABLE(1);//已连接,已稳定
int code;
PoundStatus(int code) {
this.code = code;
}
public String getStatusStr() {
return code == -1 ? "未连接" : (code == 0 ? "不稳定" : "稳定");
}
}
}