Add modbus RTU to modbus cli
parent
fd80c7e99a
commit
ccd656c3e6
|
|
@ -34,31 +34,88 @@
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
#include <QSerialPort>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
|
#include <QSerialPortInfo>
|
||||||
#include <QModbusTcpClient>
|
#include <QModbusTcpClient>
|
||||||
|
#include <QModbusRtuSerialMaster>
|
||||||
|
|
||||||
|
void sendRequest(quint16 modbusServerAddress, QModbusDataUnit::RegisterType registerType, quint16 registerAddress, quint16 length, const QByteArray &writeData, QModbusClient *client);
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
QCoreApplication application(argc, argv);
|
QCoreApplication application(argc, argv);
|
||||||
application.setApplicationName("nymea-modbus-cli");
|
application.setApplicationName("nymea-modbus-cli");
|
||||||
application.setOrganizationName("nymea");
|
application.setOrganizationName("nymea");
|
||||||
application.setApplicationVersion("1.0.0");
|
application.setApplicationVersion("1.1.0");
|
||||||
|
|
||||||
|
QString description = QString("\nTool for testing and reading Modbus TCP or RTU registers.\n\n");
|
||||||
|
description.append(QString("Copyright %1 2016 - 2023 nymea GmbH <contact@nymea.io>\n\n").arg(QChar(0xA9)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
description.append("TCP\n");
|
||||||
|
description.append("-----------------------------------------\n");
|
||||||
|
description.append("Example reading 2 holding registers from address 1000:\n");
|
||||||
|
description.append("nymea-modbus-cli -a 192.168.0.10 -p 502 -r 1000 -l 2\n\n");
|
||||||
|
|
||||||
|
|
||||||
|
description.append("RTU\n");
|
||||||
|
description.append("-----------------------------------------\n\n");
|
||||||
|
|
||||||
|
description.append("Typical baudrates:\n");
|
||||||
|
description.append("- 1200\n");
|
||||||
|
description.append("- 2400\n");
|
||||||
|
description.append("- 4800\n");
|
||||||
|
description.append("- 9600\n");
|
||||||
|
description.append("- 19200\n");
|
||||||
|
description.append("- 38400\n");
|
||||||
|
description.append("- 57600\n");
|
||||||
|
description.append("- 115200\n\n");
|
||||||
|
|
||||||
|
description.append("Example reading 2 holding registers from address 1000:\n");
|
||||||
|
description.append("nymea-modbus-cli --serial /dev/ttyUSB0 --baudrate 9600 -r 1000 -l 2\n\n");
|
||||||
|
|
||||||
QString description = QString("\nTool for testing and reading Modbus TCP registers.\n\n");
|
|
||||||
description.append(QString("Copyright %1 2016 - 2022 nymea GmbH <contact@nymea.io>\n").arg(QChar(0xA9)));
|
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.addHelpOption();
|
parser.addHelpOption();
|
||||||
parser.addVersionOption();
|
parser.addVersionOption();
|
||||||
parser.setApplicationDescription(description);
|
parser.setApplicationDescription(description);
|
||||||
|
|
||||||
QCommandLineOption addressOption(QStringList() << "a" << "address", QString("The IP address of the modbus TCP server."), "address");
|
// TCP
|
||||||
|
QCommandLineOption addressOption(QStringList() << "a" << "address", QString("TCP: The IP address of the modbus TCP server."), "address");
|
||||||
parser.addOption(addressOption);
|
parser.addOption(addressOption);
|
||||||
|
|
||||||
QCommandLineOption portOption(QStringList() << "p" << "port", QString("The port of the modbus TCP server. Default is 502."), "port");
|
QCommandLineOption portOption(QStringList() << "p" << "port", QString("TCP: The port of the modbus TCP server. Default is 502."), "port");
|
||||||
portOption.setDefaultValue("502");
|
portOption.setDefaultValue("502");
|
||||||
parser.addOption(portOption);
|
parser.addOption(portOption);
|
||||||
|
|
||||||
|
// RTU
|
||||||
|
QCommandLineOption serialPortOption(QStringList() << "serial", QString("RTU: The serial port to use for the RTU communication."), "port");
|
||||||
|
parser.addOption(serialPortOption);
|
||||||
|
|
||||||
|
QCommandLineOption baudrateOption(QStringList() << "baudrate", QString("RTU: The baudrate for the RTU communication. Default is 19200."), "baudrate");
|
||||||
|
baudrateOption.setDefaultValue("19200");
|
||||||
|
parser.addOption(baudrateOption);
|
||||||
|
|
||||||
|
QCommandLineOption parityOption(QStringList() << "parity", QString("RTU: The parity for the RTU communication. Allowed values are [none, even, odd, space, mark]. Default is none."), "parity");
|
||||||
|
parityOption.setDefaultValue("none");
|
||||||
|
parser.addOption(parityOption);
|
||||||
|
|
||||||
|
QCommandLineOption dataBitsOption(QStringList() << "databits", QString("RTU: The amount of data bits for the RTU communication. Allowed values are [5, 6, 7, 8]. Default is 8."), "databits");
|
||||||
|
dataBitsOption.setDefaultValue("8");
|
||||||
|
parser.addOption(dataBitsOption);
|
||||||
|
|
||||||
|
QCommandLineOption stopBitsOption(QStringList() << "stopbits", QString("RTU: The amount of stop bits for the RTU communication. Allowed values are [1, 1.5, 2]. Default is 1."), "stopbits");
|
||||||
|
stopBitsOption.setDefaultValue("1");
|
||||||
|
parser.addOption(stopBitsOption);
|
||||||
|
|
||||||
|
|
||||||
|
QCommandLineOption listSerialPortsOption(QStringList() << "list-serials", QString("List the available serial ports on this host."));
|
||||||
|
parser.addOption(listSerialPortsOption);
|
||||||
|
|
||||||
|
// General
|
||||||
|
|
||||||
QCommandLineOption modbusServerAddressOption(QStringList() << "m" << "modbus-address", QString("The modbus server address on the bus (slave ID). Default is 1."), "id");
|
QCommandLineOption modbusServerAddressOption(QStringList() << "m" << "modbus-address", QString("The modbus server address on the bus (slave ID). Default is 1."), "id");
|
||||||
modbusServerAddressOption.setDefaultValue("1");
|
modbusServerAddressOption.setDefaultValue("1");
|
||||||
parser.addOption(modbusServerAddressOption);
|
parser.addOption(modbusServerAddressOption);
|
||||||
|
|
@ -85,6 +142,24 @@ int main(int argc, char *argv[])
|
||||||
bool verbose = parser.isSet(debugOption);
|
bool verbose = parser.isSet(debugOption);
|
||||||
if (verbose) qDebug() << "Verbose debug print enabled";
|
if (verbose) qDebug() << "Verbose debug print enabled";
|
||||||
|
|
||||||
|
if (parser.isSet(listSerialPortsOption)) {
|
||||||
|
foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts())
|
||||||
|
qInfo().noquote() << serialPortInfo.systemLocation() << "|" << serialPortInfo.description() << "|" << serialPortInfo.serialNumber() << "|" << serialPortInfo.manufacturer();
|
||||||
|
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure we have either RTU, or TCP, not both or none
|
||||||
|
if (parser.isSet(addressOption) && parser.isSet(serialPortOption)) {
|
||||||
|
qCritical() << "Error: invalid paramter combination. Use either TCP connection by defining the \"address\" or RTU by defining the \"serial\" paramter, not both.";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!parser.isSet(addressOption) && !parser.isSet(serialPortOption)) {
|
||||||
|
qCritical() << "Error: unknown protocol. Use either TCP connection by specifying the \"address\" or RTU by specifying the \"serial\" port.";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
QModbusDataUnit::RegisterType registerType = QModbusDataUnit::RegisterType::Invalid;
|
QModbusDataUnit::RegisterType registerType = QModbusDataUnit::RegisterType::Invalid;
|
||||||
QString registerTypeString = parser.value(registerTypeOption);
|
QString registerTypeString = parser.value(registerTypeOption);
|
||||||
if (registerTypeString.toLower() == "input") {
|
if (registerTypeString.toLower() == "input") {
|
||||||
|
|
@ -96,31 +171,26 @@ int main(int argc, char *argv[])
|
||||||
} else if (registerTypeString.toLower() == "coils") {
|
} else if (registerTypeString.toLower() == "coils") {
|
||||||
registerType = QModbusDataUnit::RegisterType::Coils;
|
registerType = QModbusDataUnit::RegisterType::Coils;
|
||||||
} else {
|
} else {
|
||||||
qCritical() << "Invalid register type:" << parser.value(registerTypeOption) << "Please select on of the valid register types: input, holding, discrete, coils";
|
qCritical() << "Error: invalid register type:" << parser.value(registerTypeOption) << "Please select on of the valid register types: input, holding, discrete, coils";
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool valueOk = false;
|
bool valueOk = false;
|
||||||
quint16 modbusServerAddress = parser.value(modbusServerAddressOption).toUInt(&valueOk);
|
quint16 modbusServerAddress = parser.value(modbusServerAddressOption).toUInt(&valueOk);
|
||||||
if (modbusServerAddress < 1 || !valueOk) {
|
if (modbusServerAddress < 1 || !valueOk) {
|
||||||
qCritical() << "Error: Invalid modbus server address (slave ID):" << parser.value(modbusServerAddressOption);
|
qCritical() << "Error: invalid modbus server address (slave ID):" << parser.value(modbusServerAddressOption);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 registerAddress = parser.value(registerOption).toUInt(&valueOk);
|
quint16 registerAddress = parser.value(registerOption).toUInt(&valueOk);
|
||||||
if (!valueOk) {
|
if (!valueOk) {
|
||||||
qCritical() << "Error: Invalid register number:" << parser.value(registerOption);
|
qCritical() << "Error: invalid register number:" << parser.value(registerOption);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 length = parser.value(lengthOption).toUInt(&valueOk);
|
quint16 length = parser.value(lengthOption).toUInt(&valueOk);
|
||||||
if (!valueOk) {
|
if (!valueOk) {
|
||||||
qCritical() << "Error: Invalid register length number:" << parser.value(lengthOption);
|
qCritical() << "Error: invalid register length number:" << parser.value(lengthOption);
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!parser.isSet(addressOption)) {
|
|
||||||
qWarning() << "Error: please specify the IP address of the modbus TCP server you want connect to. Modbus RTU is not implemented yet so you need to specify it.";
|
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -130,16 +200,18 @@ int main(int argc, char *argv[])
|
||||||
qDebug() << "Write data:" << writeData;
|
qDebug() << "Write data:" << writeData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TCP
|
||||||
|
if (parser.isSet(addressOption)) {
|
||||||
// TCP connection
|
// TCP connection
|
||||||
QHostAddress address = QHostAddress(parser.value(addressOption));
|
QHostAddress address = QHostAddress(parser.value(addressOption));
|
||||||
if (address.isNull()) {
|
if (address.isNull()) {
|
||||||
qCritical() << "Error: Invalid address:" << parser.value(addressOption);
|
qCritical() << "Error: invalid address:" << parser.value(addressOption);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
quint16 port = parser.value(portOption).toUInt();
|
quint16 port = parser.value(portOption).toUInt();
|
||||||
|
|
||||||
qInfo() << "Connecting to" << QString("%1:%2").arg(address.toString()).arg(port) << "Modbus server address:" << modbusServerAddress;
|
qInfo().noquote() << "Connecting to" << QString("%1:%2").arg(address.toString()).arg(port) << "modbus server address:" << modbusServerAddress;
|
||||||
QModbusTcpClient *client = new QModbusTcpClient(nullptr);
|
QModbusTcpClient *client = new QModbusTcpClient(nullptr);
|
||||||
client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, address.toString());
|
client->setConnectionParameter(QModbusDevice::NetworkAddressParameter, address.toString());
|
||||||
client->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
|
client->setConnectionParameter(QModbusDevice::NetworkPortParameter, port);
|
||||||
|
|
@ -148,11 +220,113 @@ int main(int argc, char *argv[])
|
||||||
|
|
||||||
QObject::connect(client, &QModbusTcpClient::stateChanged, &application, [=](QModbusDevice::State state){
|
QObject::connect(client, &QModbusTcpClient::stateChanged, &application, [=](QModbusDevice::State state){
|
||||||
if (verbose) qDebug() << "Connection state changed" << state;
|
if (verbose) qDebug() << "Connection state changed" << state;
|
||||||
if (state == QModbusDevice::ConnectedState) {
|
if (state != QModbusDevice::ConnectedState)
|
||||||
qDebug() << "Connected successfully to" << QString("%1:%2").arg(address.toString()).arg(port);
|
return;
|
||||||
|
|
||||||
|
qDebug() << "Connected successfully to" << QString("%1:%2").arg(address.toString()).arg(port);
|
||||||
|
sendRequest(modbusServerAddress, registerType, registerAddress, length, writeData, client);
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(client, &QModbusTcpClient::errorOccurred, &application, [=](QModbusDevice::Error error){
|
||||||
|
qWarning() << "Modbus error occurred:" << error << client->errorString();
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!client->connectDevice()) {
|
||||||
|
qWarning() << "Error: could not connect to" << QString("%1:%2").arg(address.toString()).arg(port);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.isSet(serialPortOption)) {
|
||||||
|
|
||||||
|
QString serialPortName = parser.value(serialPortOption);
|
||||||
|
|
||||||
|
quint32 baudrate = parser.value(baudrateOption).toUInt();
|
||||||
|
|
||||||
|
QSerialPort::Parity parity = QSerialPort::NoParity;
|
||||||
|
QString parityString = parser.value(parityOption);
|
||||||
|
if (parityString.toLower() == "none") {
|
||||||
|
parity = QSerialPort::NoParity;
|
||||||
|
} else if (parityString.toLower() == "even") {
|
||||||
|
parity = QSerialPort::EvenParity;
|
||||||
|
} else if (parityString.toLower() == "odd") {
|
||||||
|
parity = QSerialPort::OddParity;
|
||||||
|
} else if (parityString.toLower() == "space") {
|
||||||
|
parity = QSerialPort::SpaceParity;
|
||||||
|
} else if (parityString.toLower() == "mark") {
|
||||||
|
parity = QSerialPort::MarkParity;
|
||||||
|
} else {
|
||||||
|
qCritical() << "Error: invalid parit type:" << parser.value(parityOption) << "Please select on of the valid values: [none, even, odd, space, mark].";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSerialPort::StopBits stopBits = QSerialPort::OneStop;
|
||||||
|
QString stopBitsString = parser.value(stopBitsOption);
|
||||||
|
if (stopBitsString == "1") {
|
||||||
|
stopBits = QSerialPort::OneStop;
|
||||||
|
} else if (stopBitsString == "1.5") {
|
||||||
|
stopBits = QSerialPort::OneAndHalfStop;
|
||||||
|
} else if (stopBitsString == "2") {
|
||||||
|
stopBits = QSerialPort::TwoStop;
|
||||||
|
} else {
|
||||||
|
qCritical() << "Error: invalid stop bits:" << parser.value(stopBitsOption) << "Please select on of the valid values: [1, 1.5, 2].";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
QSerialPort::DataBits dataBits = QSerialPort::Data8;
|
||||||
|
QString dataBitsString = parser.value(dataBitsOption);
|
||||||
|
if (dataBitsString == "5") {
|
||||||
|
dataBits = QSerialPort::Data5;
|
||||||
|
} else if (dataBitsString == "6") {
|
||||||
|
dataBits = QSerialPort::Data6;
|
||||||
|
} else if (dataBitsString == "7") {
|
||||||
|
dataBits = QSerialPort::Data7;
|
||||||
|
} else if (dataBitsString == "8") {
|
||||||
|
dataBits = QSerialPort::Data8;
|
||||||
|
} else {
|
||||||
|
qCritical() << "Error: invalid data bits:" << parser.value(dataBitsOption) << "Please select on of the valid values: [5, 6, 7, 8].";
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModbusRtuSerialMaster *client = new QModbusRtuSerialMaster(nullptr);
|
||||||
|
client->setConnectionParameter(QModbusDevice::SerialPortNameParameter, serialPortName);
|
||||||
|
client->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, baudrate);
|
||||||
|
client->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, dataBits);
|
||||||
|
client->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, stopBits);
|
||||||
|
client->setConnectionParameter(QModbusDevice::SerialParityParameter, parity);
|
||||||
|
client->setNumberOfRetries(3);
|
||||||
|
client->setTimeout(500);
|
||||||
|
|
||||||
|
QObject::connect(client, &QModbusTcpClient::stateChanged, &application, [=](QModbusDevice::State state){
|
||||||
|
qDebug() << "Connection state changed" << state;
|
||||||
|
if (state != QModbusDevice::ConnectedState)
|
||||||
|
return;
|
||||||
|
|
||||||
|
qDebug() << "Connected successfully to" << serialPortName << baudrate << dataBits << stopBits << parity << "modbus server address:" << modbusServerAddress;
|
||||||
|
sendRequest(modbusServerAddress, registerType, registerAddress, length, writeData, client);
|
||||||
|
});
|
||||||
|
|
||||||
|
QObject::connect(client, &QModbusRtuSerialMaster::errorOccurred, &application, [=](QModbusDevice::Error error){
|
||||||
|
qWarning() << "Error occurred for modbus RTU master" << error << client->errorString();
|
||||||
|
if (error != QModbusDevice::NoError) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!client->connectDevice()) {
|
||||||
|
qWarning() << "Error: failed not connect to" << serialPortName << client->errorString();
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return application.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendRequest(quint16 modbusServerAddress, QModbusDataUnit::RegisterType registerType, quint16 registerAddress, quint16 length, const QByteArray &writeData, QModbusClient *client)
|
||||||
|
{
|
||||||
if (writeData.isEmpty()) {
|
if (writeData.isEmpty()) {
|
||||||
qDebug() << "Reading from modbus server address" << modbusServerAddress << registerTypeString << "register:" << registerAddress << "Length:" << length;
|
qDebug() << "Reading from modbus server address" << modbusServerAddress << registerType << "register:" << registerAddress << "Length:" << length;
|
||||||
QModbusDataUnit request = QModbusDataUnit(registerType, registerAddress, length);
|
QModbusDataUnit request = QModbusDataUnit(registerType, registerAddress, length);
|
||||||
QModbusReply *reply = client->sendReadRequest(request, modbusServerAddress);
|
QModbusReply *reply = client->sendReadRequest(request, modbusServerAddress);
|
||||||
if (!reply) {
|
if (!reply) {
|
||||||
|
|
@ -228,17 +402,3 @@ int main(int argc, char *argv[])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
QObject::connect(client, &QModbusTcpClient::errorOccurred, &application, [=](QModbusDevice::Error error){
|
|
||||||
qWarning() << "Modbus error occurred:" << error << client->errorString();
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!client->connectDevice()) {
|
|
||||||
qWarning() << "Error: could not connect to" << QString("%1:%2").arg(address.toString()).arg(port);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
return application.exec();
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue