最近开发用到了串口通信,因为我用的是java,所以通信部分也只能用Java来实现了,虽然有点大材小用,但是呢还是来总结一下,希望能帮到和我一样遇到问题的朋友。
因为我开发用的是Springboot框架,而且我自己对通信也确实不太懂,所以直接把通信写在了启动类里面:
确定从站地址,波特率,以及连接端口:
端口确认,我的电脑,点击右键选择管理,进入后选择设备管理器,右侧栏目下查看端口,我这里的端口是COM3
然后确定从站地址,device id就是从站地址,对应我代码中的SLAVE_ADDRESS:
最后确定波特率,也就是对应我代码中的BAUD_RATE:
代码设置:
// 设定MODBUS网络上从站地址
private final static int SLAVE_ADDRESS = 201;
//串行波特率
private final static int BAUD_RATE = 9600;
因为我读出来的数据要写进数据库,所以并没有直接在启动类中实现读取功能,而是进行了二次封装:
/**
* 读保持寄存器上的内容
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param len 待读寄存器的个数
*/
private static String readHoldingRegistersTest(ModbusMaster master, int slaveId, int start, int len) {
String data = "";
try {
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, start, len);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse)master.send(request);
if (response.isException()) {
System.out.println("Exception response: message=" + response.getExceptionMessage());
} else {
data = Arrays.toString(response.getShortData());
System.out.println("read success"+ Arrays.toString(response.getShortData()));
}
} catch (ModbusTransportException e) {
e.printStackTrace();
}
return data;
}
调用读取功能,并对读取的数据进行操作后写进数据库:
public static void modbus(String[] args){
SerialPortWrapper serialParameters = new
SerialPortWrapperImpl("COM3", BAUD_RATE, 8, 1, 0, 0, 0);
//创建ModbusFactory工厂实例
ModbusFactory modbusFactory = new ModbusFactory();
//创建ModbusMaster实例
ModbusMaster master = modbusFactory.createRtuMaster(serialParameters);
//初始化
try {
//设置超时时长和尝试次数
/*master.setTimeout(3000);
master.setRetries(3);*/
master.init();
try (Connection conn = DriverManager.getConnection
(DB_URL, DB_USER, DB_PASSWORD)){
String sql = "update tracker_tracking set TARGET_ANGLE = ?,MEASURED_ANGLE = ?,ERROR_ANGLE = ? where COMMUNICATION_BOX_NAME='NCU1'";
//预编译
PreparedStatement stmt = conn.prepareStatement(sql);
String sql1 = "update tracker_tracking set TRACKING_DATE = ? where COMMUNICATION_BOX_NAME='NCU1'";
PreparedStatement stmt1 = conn.prepareStatement(sql1);
// 读取串口数据,并将其写入数据库
while (true) {
readHoldingRegistersTest(master, SLAVE_ADDRESS, 115, 1);
//目标角度,实际角度
String data =readHoldingRegistersTest(master, SLAVE_ADDRESS, 259, 2); // 读取串口数据
System.out.println("---------TARGET_ANGLE---------" + data);
ArrayList Arr = retData(data);
Double da = Double.parseDouble(Arr.get(0).toString()) * 0.01;
Double ta = Double.parseDouble(Arr.get(1).toString()) * 0.01;
Double ch = da - ta;
stmt.setDouble(1, da); // 将数据设置为SQL语句的参数
stmt.setDouble(2, ta); // 将数据设置为SQL语句的参数
stmt.setDouble(3, ch);
stmt.executeUpdate(); // 执行SQL语句,将数据插入到数据库中
//时间
String data1 = readHoldingRegistersTest(master, SLAVE_ADDRESS, 261, 6); // 读取串口数据
String Timedate = Date(data1);
stmt1.setString(1, Timedate);
stmt1.executeUpdate();
System.out.println("---------TARGET_ANGLE---------" + da);
System.out.println("---------MEASURED_ANGLE---------" + ta);
//分控箱当前角度
String data2 = readHoldingRegistersTest(master, SLAVE_ADDRESS, 11, 32); // 读取串口数据
System.out.println("---------data2---------" + data2);
/*String Timedate = Date(data1);
stmt1.setString(1, Timedate);
stmt1.executeUpdate();*/
break;
}
}catch (Exception e){
e.printStackTrace();
}
} catch (ModbusInitException e) {
e.printStackTrace();
} finally {
master.destroy();
}
}
读取出来的数据格式不能直接操作,所以对于我的数据格式还进行了操作,Date是对我读出来的日期的格式进行了处理,我读出来的日期格式是这样的:
public static ArrayList retData(String data){
ArrayList Arr = new ArrayList();
String[] dat = data.replace("[","").replace("]","").replace(",",",").replace(" ","").split(",");
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < dat.length; i ++ ){
stringBuilder.append(dat[i]);
Arr.add(dat[i]);
}
return Arr;
}
public static String Date(String data){
String[] dat = data.replace("[","").replace("]","").replace(",",",").replace(" ","").split(",");
String str="";
if (dat[0].length() == 2){
str = 20 + dat[0] + "-";
}
if (dat[1].length() == 1){
str = str + 0 + dat[1] + "-";
}else{
str = str + dat[1] + "-";
}
if(dat[2].length() == 1){
str = str + 0 +dat[2] + " ";
}else {
str = str + dat[2] + " ";
}
if (dat[3].length() == 2){
str = str + dat[3] + ":";
}else {
str = str + 0 + dat[3] + ":";
}
if (dat[4].length() == 2){
str = str + dat[4] + ":";
}else {
str = str + 0 + dat[4] + ":";
}
if(dat[5].length() == 1){
str = str + 0 +dat[5];
}else {
str = str + dat[5];
}
return str;
}
启动类中调用modbus,因为我读取的数据更新频率不用太高,所以设置了每5秒读取一次
public static void main(String[] args) {
SpringApplication.run(VersolApplication.class, args);
try {
Runtime.getRuntime().exec("cmd /c start http://localhost:8080/login");
final long timeInterval = 5000;// 5秒运行一次
new Thread(() -> {
while (true) {
// 要运行的程序
modbus(args);
try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
因为串口通信是单线程,读取的时候不能同时对他进行写入操作,所以我上面设置的每5秒读取一次,然后杀掉进程,就为写入的进程留出了通道,但是我的写进程是放在service层的,当我对数据库操作完成后在进行调用写的进程:
写进程二次封装:
// 设定MODBUS网络上从站地址
private final static int SLAVE_ADDRESS = 201;
//串行波特率
private final static int BAUD_RATE = 9600;
/**
* 批量写数据到保持寄存器
*
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param values 待写数据
*/
public static void writeRegistersTest(ModbusMaster master, int slaveId, int start, short[] values) {
try {
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, start, values);
WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);
if (response.isException()) {
System.out.println("Exception response: message = " + response.getExceptionMessage());
} else {
System.out.println("write success " + response);
}
} catch (ModbusTransportException e) {
e.printStackTrace();
}
}
调用写进程:
public static void modbus(int START,String LENG) {
SerialPortWrapper serialParameters = new
SerialPortWrapperImpl("COM3", BAUD_RATE, 8, 1, 0, 0, 0);
/* 创建ModbusFactory工厂实例 */
ModbusFactory modbusFactory = new ModbusFactory();
/* 创建ModbusMaster实例 */
ModbusMaster master = modbusFactory.createRtuMaster(serialParameters);
/* 初始化 */
try {
master.init();
//处理写入的数据
short LEN = Short.parseShort(LENG);
short[] list = {LEN};
//写入
writeRegistersTest(master, SLAVE_ADDRESS, START,list);
} catch (ModbusInitException e) {
e.printStackTrace();
} finally {
master.destroy();
}
}
调用modbus:
@Override
public int openAutomatic(String name) {
int result = 0;
result += vsdao.openAutomatic(name);
if (result == 1){
/*传递写的位置START,写的值name*/
modbus(115,name);
}
return result;
}
启动程序就发现,读取功能实现了:
写入功能需要前端操作成功后,后台再调用 。