一.Modbus协议
1.Modbus是由Modicon(现为施耐德电气公司的一个品牌)在1979年发明的,是全球第一个真正用于工业现场的总线协议。
ModBus网络是一个工业通信系统,由带智能终端的可编程序控制器和计算机通过公用线路或局部专用线路连接而成。其系统结构既包括硬件、亦包括软件。它可应用于各种数据采集和过程监控。
ModBus网络只有一个主机,所有通信都由他发出。网络可支持247个之多的远程从属控制器,但实际所支持的从机数要由所用通信设备决定。采用这个系统,各PC可以和中心主机交换信息而不影响各PC执行本身的控制任务。
2.Modbus具有以下几个特点:
(1)标准、开放,用户可以免费、放心地使用Modbus协议,不需要交纳许可证费,也不会侵犯知识产权。目前,支持Modbus的厂家超过400家,支持Modbus的产品超过600种。
(2)Modbus可以支持多种电气接口,如RS-232、RS-485等,还可以在各种介质上传送,如双绞线、光纤、无线等。
(3)Modbus的帧格式简单、紧凑,通俗易懂。用户使用容易,厂商开发简单。
3.ModBus功能码
01 READ COIL STATUS
02 READ INPUT STATUS
03 READ HOLDING REGISTER
04 READ INPUT REGISTER
05 WRITE SINGLE COIL
06 WRITE SINGLE REGISTER
15 WRITE MULTIPLE COIL
16 WRITE MULTIPLE REGISTER
二.Modbus4J简介
[Git](https://github.com/infiniteautomation/modbus4j)
以下是Git上Modbus4J的简介:
modbus4j
========
A high-performance and ease-of-use implementation of the Modbus protocol written in Java by Infinite Automation Systems and Serotonin Software. Supports ASCII, RTU, TCP, and UDP transports as slave or master, automatic request partitioning and response data type parsing.
Commercial licenses are available from www.InfiniteAutomation.com
Modbus4J是一个由英菲尼迪自动化系统和Serotonin软件一起推出的高性能且易于使用的基于java语言的Modbus协议的实现。支持主/从站,ASCII,RTU,TCP和UDP传输方式,具有自动化的请求分割与响应数据解析的功能。
三.在安卓下使用Modbus4J
/**
* 初始化Modbus主站
*/
public static void initModbusMaster() {
String plcIpAddress= AppSP.getStringValue(AppSP.PLC_IP_ADDRESS,"");
String plcPort= AppSP.getStringValue(AppSP.PLC_PORT,"502");//PLC端口号默认502
plcNO=Integer.parseInt(AppSP.getStringValue(AppSP.PLC_NO,"1"));//默认PLC站号为1
if(plcIpAddress.equals("")||plcPort.equals("")){
return;
}
IpParameters ipParameters=new IpParameters();
ipParameters.setHost(plcIpAddress);
ipParameters.setPort(Integer.parseInt(plcPort));
if(master!=null){
if (master.isInitialized()){
master.destroy();
}
}
ModbusFactory modbusFactory = new ModbusFactory();
master = modbusFactory.createTcpMaster(ipParameters,true);
new Thread(){
@Override
public void run() {
super.run();
try {
master.init();
} catch (ModbusInitException e) {
e.printStackTrace();
}finally {
master.destroy();
}
}
}.start();
}
在安卓上使用Modbus4J,Modbus可以使用串口或者以太网通信,这里我们使用的是以太网通信方式:
public class ModbusBridge {
/**
* 读保持寄存器上的内容
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param len 待读寄存器的个数
* @param listener 回调监听
*/
public static void readHRegs(final ModbusMaster master, final int slaveId, final int start, final int len, final ModbusResponseListener listener) {
if(master==null){
return;
}
new AsyncTask<Object[],Long,ModbusResponse>(){
@Override
protected ModbusResponse doInBackground(Object[]... params) {
ReadHoldingRegistersRequest request = null;
ReadHoldingRegistersResponse response=null;
try {
request = new ReadHoldingRegistersRequest(slaveId, start, len);
response = (ReadHoldingRegistersResponse) master.send(request);
return response;
} catch (ModbusTransportException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(ModbusResponse response) {
super.onPostExecute(response);
if(response==null){
onFail(response);
return;
}
listener.onResponse(response);
}
}.execute();
}
/**
* 批量写数据到保持寄存器
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址的偏移量
* @param values 待写数据
* @param listener 回调监听
*/
public static void writeHRegs(final ModbusMaster master, final int slaveId, final int start, final short[] values, final ModbusResponseListener listener) {
if(master==null){
return;
}
new AsyncTask<Object[],Long,ModbusResponse>(){
@Override
protected ModbusResponse doInBackground(Object[]... params) {
WriteRegistersRequest request ;
WriteRegistersResponse response;
try {
request = new WriteRegistersRequest(slaveId, start, values);
response = (WriteRegistersResponse) master.send(request);
return response;
}
catch (ModbusTransportException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(ModbusResponse response) {
super.onPostExecute(response);
if(response==null){
onFail(response);
return;
}
listener.onResponse(response);
}
}.execute();
}
/**
* 同步方式批量写数据到保持寄存器
* @param master
* @param slaveId
* @param start
* @param values
*/
public static int writeHRegs(final ModbusMaster master, final int slaveId, final int start, final short[] values){
if(master==null){
return -1;
}
try {
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, start, values);
WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);
if(!response.isException()){
return 0;
}else {
return -1;
}
}
catch (ModbusTransportException e) {
e.printStackTrace();
}
return -1;
}
/************************************************************************************************************/
/**
* 带掩码写数据到保持寄存器
* @param master 主站
* @param slaveId 从站地址
* @param start 起始地址
* @param andMask The andMask determines which bits we want to change
* @param orMask The orMask determines what value a bit will have after writing
* @param listener 回调监听
*/
public static void writeMaskReg(final ModbusMaster master, final int slaveId, final int start, final int andMask, final int orMask, final ModbusResponseListener listener) {
if(master==null){
return;
}
readHRegs(master, slaveId, start, 1, new ModbusResponseListener() {
@Override
public void onResponse(ModbusResponse response) {
if(!response.isException()){
short temp=((ReadHoldingRegistersResponse)response).getShortData()[0];
Log.d("读取数据",temp+"");
Log.d("准备数据1",(temp&andMask)+"");
Log.d("准备数据2",((temp&andMask)|orMask)+"");
int result=((temp&andMask)|orMask);
short[] data={(short)result};
writeHRegs(master,slaveId,start,data,listener);
}
}
});
}
private static void onFail(ModbusResponse response){
if(response==null){
Toast.makeText(MainApplication.getContext(),"ModbusTransportException",Toast.LENGTH_LONG).show();
}else {
Toast.makeText(MainApplication.getContext(),"Fail:"+response.getExceptionMessage(),Toast.LENGTH_LONG).show();
}
}
/********************************************************************************************************************/
/**
* 写线圈寄存器
* @param master
* @param slaveId
* @param addr
* @param value
* @param listener
*/
public static void writeCoilRegister(final ModbusMaster master, final int slaveId, final int addr, final boolean value, final ModbusResponseListener listener){
if(master==null){
return;
}
new AsyncTask<Object[],Long,ModbusResponse>(){
@Override
protected ModbusResponse doInBackground(Object[]... params) {
WriteCoilRequest request ;
WriteCoilResponse response;
try {
request = new WriteCoilRequest(slaveId,addr,value);
response = (WriteCoilResponse) master.send(request);
return response;
}
catch (ModbusTransportException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(ModbusResponse response) {
super.onPostExecute(response);
if(response==null){
onFail(response);
return;
}
listener.onResponse(response);
}
}.execute();
}
/**
* 读线圈寄存器
* @param master
* @param slaveId
* @param addr
* @param listener
*/
public static void readCoilRegister(final ModbusMaster master, final int slaveId, final int addr, final ModbusResponseListener listener){
if(master==null){
return;
}
new AsyncTask<Object[],Long,ModbusResponse>(){
@Override
protected ModbusResponse doInBackground(Object[]... params) {
ReadCoilsRequest request = null;
ReadCoilsResponse response=null;
try {
request = new ReadCoilsRequest(slaveId,addr,1);
response = (ReadCoilsResponse) master.send(request);
return response;
} catch (ModbusTransportException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(ModbusResponse response) {
super.onPostExecute(response);
if(response==null){
onFail(response);
return;
}
listener.onResponse(response);
}
}.execute();
}
/**
* 同步写线圈寄存器
* @param master
* @param slaveId
* @param addr
* @param value
*/
public static int writeCoilRegister(final ModbusMaster master,int slaveId,int addr,boolean value){
if(master==null){
return -1;
}
try {
WriteCoilRequest request=new WriteCoilRequest(slaveId,addr,value);
WriteCoilResponse response= (WriteCoilResponse) master.send(request);
if(!response.isException()){
return 1;
}else {
return -1;
}
} catch (ModbusTransportException e) {
e.printStackTrace();
}
return -1;
}
/**
* 同步读线圈寄存器
* @param master
* @param slaveId
* @param addr
*/
public static boolean readCoilRegister(final ModbusMaster master,int slaveId,int addr){
if(master==null){
return false;
}
try {
ReadCoilsRequest request=new ReadCoilsRequest(slaveId,addr,1);
ReadCoilsResponse response= (ReadCoilsResponse) master.send(request);
if(!response.isException()){
return response.getBooleanData()[0];
}
return false;
} catch (ModbusTransportException e) {
e.printStackTrace();
}
return false;
}
public static void read(final ModbusMaster master,int slaveId,int addr){
try {
ReadCoilsRequest request2=new ReadCoilsRequest(slaveId,addr,8);
ReadCoilsResponse response2= (ReadCoilsResponse) master.send(request2);
if(!response2.isException()){
Log.e("ReadInputRegisters",response2.getFunctionCode()+" "+Arrays.toString(response2.getBooleanData()));
}
} catch (ModbusTransportException e) {
e.printStackTrace();
}
}
}