added my-PV

This commit is contained in:
Boernsman 2020-01-22 16:58:48 +01:00
parent 59a9927f2c
commit 344239fe0d
12 changed files with 757 additions and 18 deletions

16
debian/control vendored
View File

@ -33,6 +33,22 @@ Description: nymea.io plugin for Drexel & Weiss heat pumps
This package will install the nymea.io plugin for Drexel & Weiss heat pumps
Package: nymea-plugin-mypv
Architecture: any
Depends: ${shlibs:Depends},
${misc:Depends},
nymea-plugins-translations,
libmodbus,
Description: nymea.io plugin for my-pv heating rods
The nymea daemon is a plugin based IoT (Internet of Things) server. The
server works like a translator for devices, things and services and
allows them to interact.
With the powerful rule engine you are able to connect any device available
in the system and create individual scenes and behaviors for your environment.
.
This package will install the nymea.io plugin for my-pv
Package: nymea-plugin-wallbe
Architecture: any
Depends: ${shlibs:Depends},

1
debian/nymea-plugin-mypv.install.in vendored Normal file
View File

@ -0,0 +1 @@
usr/lib/@DEB_HOST_MULTIARCH@/nymea/plugins/libnymea_devicepluginmypv.so

View File

@ -171,12 +171,10 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
discoverModbusSlaves(modbus, slave);
info->finish(Device::DeviceErrorNoError);
return;
} else {
info->finish(Device::DeviceErrorActionTypeNotFound);
}
info->finish(Device::DeviceErrorActionTypeNotFound);
return;
}
if (device->deviceClassId() == x2luDeviceClassId) {
} else if (device->deviceClassId() == x2luDeviceClassId) {
Device *parentDevice = myDevices().findById(device->parentId());
if (!parentDevice) {
qWarning(dcDrexelUndWeiss()) << "Could not find the parent device";
@ -210,12 +208,10 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
}
m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::Betriebsart, data), info);
return;
} else {
info->finish(Device::DeviceErrorActionTypeNotFound);
}
info->finish(Device::DeviceErrorActionTypeNotFound);
return;
}
if (device->deviceClassId() == x2wpDeviceClassId) {
} else if (device->deviceClassId() == x2wpDeviceClassId) {
Device *parentDevice = myDevices().findById(device->parentId());
if (!parentDevice) {
qWarning(dcDrexelUndWeiss()) << "Could not find modbus interface";
@ -235,18 +231,17 @@ void DevicePluginDrexelUndWeiss::executeAction(DeviceActionInfo *info)
int data = static_cast<int>(qRound(targetTemp * 1000));
m_pendingActions.insert(modbus->writeHoldingRegister(slave,ModbusRegisterX2::RaumSoll, data), info);
return;
}
if (action.actionTypeId() == x2wpTargetWaterTemperatureActionTypeId) {
} else if (action.actionTypeId() == x2wpTargetWaterTemperatureActionTypeId) {
qreal targetWaterTemp = action.param(x2wpTargetWaterTemperatureActionTargetWaterTemperatureParamTypeId).value().toDouble();
int data = static_cast<int>(qRound(targetWaterTemp * 1000));
m_pendingActions.insert(modbus->writeHoldingRegister(slave, ModbusRegisterX2::BrauchwasserSolltermperatur, data), info);
return;
} else {
info->finish(Device::DeviceErrorActionTypeNotFound);
}
info->finish(Device::DeviceErrorActionTypeNotFound);
return;
} else {
info->finish(Device::DeviceErrorDeviceClassNotFound);
}
info->finish(Device::DeviceErrorDeviceClassNotFound);
return;
}

View File

@ -51,8 +51,6 @@ public:
void setupDevice(DeviceSetupInfo *info) override;
void postSetupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
public slots:
void executeAction(DeviceActionInfo *info) override;
private:

5
mypv/README.md Normal file
View File

@ -0,0 +1,5 @@
# mypv
--------------------------------
Description of the plugin...

261
mypv/devicepluginmypv.cpp Normal file
View File

@ -0,0 +1,261 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "plugininfo.h"
#include "devicepluginmypv.h"
#include <QUdpSocket>
#include <QHostAddress>
DevicePluginMyPv::DevicePluginMyPv()
{
}
void DevicePluginMyPv::discoverDevices(DeviceDiscoveryInfo *info)
{
QUdpSocket *searchSocket = new QUdpSocket(this);
// Note: This will fail, and it's not a problem, but it is required to force the socket to stick to IPv4...
searchSocket->bind(QHostAddress::AnyIPv4, 16124);
QByteArray discoveryString;
discoveryString.resize(19);
discoveryString.fill(0);
discoveryString.insert(0, QByteArray::fromHex("86d93efc"));
discoveryString.insert(4, "AC ELWA-E");
qCDebug(dcMypv()) << "Send datagram:" << discoveryString << "length: " << discoveryString.length();
qint64 len = searchSocket->writeDatagram(discoveryString, QHostAddress("255.255.255.255"), 16124);
if (len != discoveryString.length()) {
searchSocket->deleteLater();
qCWarning(dcMypv()) << "Error sending discovery";
return;
}
QTimer::singleShot(2000, this, [this, searchSocket, info](){
QList<DeviceDescriptor> descriptorList;
while(searchSocket->hasPendingDatagrams()) {
char buffer[1024];
QHostAddress senderAddress;
int len = searchSocket->readDatagram(buffer, 1024, &senderAddress);
QByteArray data = QByteArray::fromRawData(buffer, len);
qCDebug(dcMypv()) << "Have datagram:" << data;
if (data.length() < 64) {
continue;
}
//Device Id AC•THOR = 0x4e84
//Device Id Power = 0x4e8e
//Device Id AC ELWA-E = 0x3efc
qCDebug(dcMypv()) << "device Id:" << data.mid(2, 2);
if (data.mid(2, 2) == QByteArray::fromHex("3efc")) {
qCDebug(dcMypv()) << "Found Device: AC ElWA-E";
} else if (data.mid(2, 2) == QByteArray::fromHex("0x4e8e")) {
qCDebug(dcMypv()) << "Found Device: Powermeter";
} else if (data.mid(2, 2) == QByteArray::fromHex("0x4e84")) {
qCDebug(dcMypv()) << "Found Device: AC Thor";
} else {
qCDebug(dcMypv()) << "Failed to parse discovery datagram from" << senderAddress << data;
continue;
}
quint32 uiAddress = 0;
for (int i=0; i<4; i++) {
uiAddress |= data.at(7-i) << (i*8);
}
QHostAddress address(uiAddress);
QByteArray serialNumber = data.mid(8, 16);
bool existing = false;
foreach (Device *existingDev, myDevices()) {
if (existingDev->deviceClassId() == info->deviceClassId() && existingDev->paramValue(elwaDeviceIpAddressParamTypeId).toString() == address.toString()) {
existing = true;
}
}
if (existing) {
qCDebug(dcMypv()) << "Already have device" << senderAddress << "in configured devices. Skipping...";
continue;
}
DeviceDescriptor deviceDescriptors(info->deviceClassId(), "AC ELWA-E", address.toString());
ParamList params;
params << Param(elwaDeviceIpAddressParamTypeId, address.toString());
params << Param(elwaDeviceSerialNumberParamTypeId, serialNumber);
deviceDescriptors.setParams(params);
descriptorList << deviceDescriptors;
}
info->addDeviceDescriptors(descriptorList);;
searchSocket->deleteLater();
});
}
void DevicePluginMyPv::setupDevice(DeviceSetupInfo *info)
{
Device *device = info->device();
if(device->deviceClassId() == elwaDeviceClassId) {
QHostAddress address = QHostAddress(device->paramValue(elwaDeviceIpAddressParamTypeId).toString());
ModbusTCPMaster *modbusTcpMaster = new ModbusTCPMaster(address, 502, this);
if (!modbusTcpMaster->createInterface()) {
modbusTcpMaster->deleteLater();
return;
}
m_modbusTcpMasters.insert(device, modbusTcpMaster);
} else {
Q_ASSERT_X(false, "setupDevice", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8());
}
}
void DevicePluginMyPv::postSetupDevice(Device *device)
{
if (device->deviceClassId() == elwaDeviceClassId) {
update(device);
}
}
void DevicePluginMyPv::deviceRemoved(Device *device)
{
if (device->deviceClassId() == elwaDeviceClassId) {
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.take(device);
modbusTCPMaster->deleteLater();
}
}
void DevicePluginMyPv::executeAction(DeviceActionInfo *info)
{
Device *device = info->device();
Action action = info->action();
if (device->deviceClassId() == elwaDeviceClassId) {
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(device);
if (action.actionTypeId() == elwaHeatingPowerActionTypeId) {
int heatingPower = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toInt();
if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::Power, heatingPower)){
return info->finish(Device::DeviceErrorHardwareFailure);
}
return;
} else if (action.actionTypeId() == elwaPowerActionTypeId) {
bool power = action.param(elwaHeatingPowerActionHeatingPowerParamTypeId).value().toBool();
if(power) {
if(!modbusTCPMaster->setRegister(0xff, ElwaModbusRegisters::ManuelStart, 1)){
return info->finish(Device::DeviceErrorHardwareFailure);
}
}
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled actionTypeId: %1").arg(action.actionTypeId().toString()).toUtf8());
}
} else {
Q_ASSERT_X(false, "executeAction", QString("Unhandled deviceClassId: %1").arg(device->deviceClassId().toString()).toUtf8());
}
}
void DevicePluginMyPv::onRefreshTimer(){
foreach (Device *device, myDevices()) {
update(device);
}
}
void DevicePluginMyPv::update(Device *device) {
if (device->deviceClassId() == elwaDeviceClassId)
{
ModbusTCPMaster *modbusTCPMaster = m_modbusTcpMasters.value(device);
int data;
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Status, &data)) {
switch (data) {
case Heating: {
device->setStateValue(elwaStatusStateTypeId, "Heating");
device->setStateValue(elwaPowerStateTypeId, true);
break;
}
case Standby:{
device->setStateValue(elwaStatusStateTypeId, "Standby");
device->setStateValue(elwaPowerStateTypeId, false);
break;
}
case Boosted:{
device->setStateValue(elwaStatusStateTypeId, "Boosted");
device->setStateValue(elwaPowerStateTypeId, true);
break;
}
case HeatFinished:{
device->setStateValue(elwaStatusStateTypeId, "Heat finished");
device->setStateValue(elwaPowerStateTypeId, false);
break;
}
case Setup:{
device->setStateValue(elwaStatusStateTypeId, "Setup");
device->setStateValue(elwaPowerStateTypeId, false);
break;
}
case ErrorOvertempFuseBlown:{
device->setStateValue(elwaStatusStateTypeId, "Error Overtemp Fuse blown");
break;
}
case ErrorOvertempMeasured:{
device->setStateValue(elwaStatusStateTypeId, "Error Overtemp measured");
break;
}
case ErrorOvertempElectronics:{
device->setStateValue(elwaStatusStateTypeId, "Error Overtemp Electronics");
break;
}
case ErrorHardwareFault:{
device->setStateValue(elwaStatusStateTypeId, "Error Hardware Fault");
break;
}
case ErrorTempSensor:{
device->setStateValue(elwaStatusStateTypeId, "Error Temp Sensor");
break;
}
default:
device->setStateValue(elwaStatusStateTypeId, "Unknown");
}
device->setStateValue(elwaConnectedStateTypeId, true);
} else {
device->setStateValue(elwaConnectedStateTypeId, false);
}
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::WaterTemperature, &data)) {
device->setStateValue(elwaTemperatureStateTypeId, data/10.00);
}
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::TargetWaterTemperature, &data)) {
device->setStateValue(elwaTargetWaterTemperatureStateTypeId, data/10.00);
}
if (modbusTCPMaster->getRegister(0xff, ElwaModbusRegisters::Power, &data)) {
device->setStateValue(elwaHeatingPowerStateTypeId, data);
}
}
}

87
mypv/devicepluginmypv.h Normal file
View File

@ -0,0 +1,87 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef DEVICEPLUGINMYPV_H
#define DEVICEPLUGINMYPV_H
#include "devices/deviceplugin.h"
#include "plugintimer.h"
#include "modbustcpmaster.h"
#include <QUdpSocket>
class DevicePluginMyPv: public DevicePlugin
{
Q_OBJECT
Q_PLUGIN_METADATA(IID "io.nymea.DevicePlugin" FILE "devicepluginmypv.json")
Q_INTERFACES(DevicePlugin)
public:
explicit DevicePluginMyPv();
void discoverDevices(DeviceDiscoveryInfo *info) override;
void setupDevice(DeviceSetupInfo *info) override;
void postSetupDevice(Device *device) override;
void deviceRemoved(Device *device) override;
void executeAction(DeviceActionInfo *info) override;
private:
enum ElwaModbusRegisters {
Power= 1000,
WaterTemperature = 1001,
TargetWaterTemperature = 1002,
Status = 1003,
ManuelStart = 1012
};
enum ElwaStatus {
Heating = 2,
Standby = 3,
Boosted = 4,
HeatFinished = 5,
Setup = 9,
ErrorOvertempFuseBlown = 201,
ErrorOvertempMeasured = 202,
ErrorOvertempElectronics = 203,
ErrorHardwareFault = 204,
ErrorTempSensor = 205
};
PluginTimer *m_refreshTimer = nullptr;
QHash<Device *, ModbusTCPMaster *> m_modbusTcpMasters;
void update(Device *device);
private slots:
void onRefreshTimer();
};
#endif // DEVICEPLUGINMYPV_H

112
mypv/devicepluginmypv.json Normal file
View File

@ -0,0 +1,112 @@
{
"name": "Mypv",
"displayName": "Mypv",
"id": "73c7efcc-80d5-4166-ad97-2cbbeb129d91",
"vendors": [
{
"name": "myPV",
"displayName": "my-PV",
"id": "1f17597f-e0d0-459b-858d-ec9cbcd10b2c",
"deviceClasses": [
{
"name": "elwa",
"displayName": "AC Elwa E",
"id": "19ac4c7c-9c0a-4998-a8f0-c77d940cbb08",
"createMethods": ["Discovery"],
"interfaces": ["connectable", "temperaturesensor", "heating"],
"paramTypes": [
{
"id": "ae66596f-f6c7-4d9c-9eee-b9190616a9e1",
"name":"ipAddress",
"displayName": "IP address",
"type": "QString"
},
{
"id": "b31a263a-2fdc-4a88-88ec-9e182025da8f",
"name":"serialNumber",
"displayName": "Serial number",
"type": "QString"
}
],
"stateTypes":[
{
"id": "a5afaad5-78bf-4cac-b98d-7eae31aac518",
"name": "connected",
"displayName": "Connected",
"displayNameEvent": "Connected changed",
"type": "bool",
"defaultValue": false,
"cached": false
},
{
"id": "e6b0260b-f255-4f17-8ac1-bc87a950f449",
"name": "power",
"displayName": "Power",
"displayNameEvent": "Power changed",
"displayNameAction": "Change power",
"type": "bool",
"defaultValue": 0,
"writable": true
},
{
"id": "2eb3c40c-1b43-4b64-82e2-6558f0b8817e",
"name": "heatingPower",
"displayName": "Power consumption",
"displayNameEvent": "Power consumption changed",
"displayNameAction": "Change power consumption",
"type": "int",
"unit": "Watt",
"minValue": 0,
"maxValue": 3600,
"defaultValue": 0,
"writable": true
},
{
"id": "60006f93-8852-433b-bbc0-f10cc3939eeb",
"name": "temperature",
"displayName": "Water temperature",
"displayNameEvent": "Water temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "2b089c93-6411-41f7-96aa-f78d5cf910cb",
"name": "targetWaterTemperature",
"displayName": "Target water temperature",
"displayNameEvent": "Target water temperature changed",
"type": "double",
"unit": "DegreeCelsius",
"defaultValue": 0
},
{
"id": "d0a7065e-7773-47d7-b474-ce8d21d55aa7",
"name": "status",
"displayName": "Status",
"displayNameEvent": "Status changed",
"type": "QString",
"defaultValue": "Unknown",
"possibleValues": [
"Unknown",
"Heat",
"Standby",
"Boost heat",
"Heat finished",
"Setup",
"Error Overtemp Fuse blown",
"Error Overtemp measured",
"Error Overtemp Electronics",
"Error Hardware Fault",
"Error Temp Sensor"
]
}
]
}
]
}
]
}

181
mypv/modbustcpmaster.cpp Normal file
View File

@ -0,0 +1,181 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "modbustcpmaster.h"
#include "extern-plugininfo.h"
#include <QLoggingCategory>
Q_DECLARE_LOGGING_CATEGORY(dcModbus)
Q_LOGGING_CATEGORY(dcModbus, "Modbus")
ModbusTCPMaster::ModbusTCPMaster(QHostAddress IPv4Address, int port, QObject *parent) :
QObject(parent),
m_IPv4Address(IPv4Address),
m_port(port)
{
}
ModbusTCPMaster::~ModbusTCPMaster()
{
if (m_mb != NULL) {
modbus_close(m_mb);
}
modbus_free(m_mb);
}
bool ModbusTCPMaster::createInterface() {
// TCP connction to target device
qCDebug(dcModbus()) << "Setting up TCP connecion" << m_IPv4Address.toString() << "port:" << m_port;
const char *address = m_IPv4Address.toString().toLatin1().data();
m_mb = modbus_new_tcp(address, m_port);
if(m_mb == nullptr){
qCWarning(dcMypv()) << "Error modbus TCP: " << modbus_strerror(errno) ;
return false;
}
// Extend the timeout to 3 seconds
//struct timeval response_timeout;
//response_timeout.tv_sec = 3;
//response_timeout.tv_usec = 0;
//modbus_set_response_timeout(m_mb, &response_timeout);
if(modbus_connect(m_mb) == -1){
qCWarning(dcMypv()) << "Error connecting modbus:" << modbus_strerror(errno) ;
return false;
}
return true;
}
int ModbusTCPMaster::port()
{
return m_port;
}
bool ModbusTCPMaster::setIPv4Address(QHostAddress ipv4Address)
{
m_IPv4Address = ipv4Address;
if (!createInterface()) {
return false;
}
return true;
}
bool ModbusTCPMaster::setPort(int port)
{
m_port = port;
if (!createInterface()) {
return false;
}
return true;
}
QHostAddress ModbusTCPMaster::ipv4Address()
{
return m_IPv4Address;
}
bool ModbusTCPMaster::setCoil(int slaveAddress, int coilAddress, bool status)
{
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
if (modbus_write_bit(m_mb, coilAddress, status) == -1) {
qCWarning(dcMypv()) << "Could not write Coil" << coilAddress << "Reason:" << modbus_strerror(errno);
return false;
}
return true;
}
bool ModbusTCPMaster::setRegister(int slaveAddress, int registerAddress, int data)
{
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
if (modbus_write_register(m_mb, registerAddress, data) == -1) {
qCWarning(dcMypv()) << "Could not write Register" << registerAddress << "Reason:" << modbus_strerror(errno);
return false;
}
return true;
}
bool ModbusTCPMaster::getCoil(int slaveAddress, int coilAddress, bool *result)
{
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
uint8_t status;
if (modbus_read_bits(m_mb, coilAddress, 1, &status) == -1){
qCWarning(dcMypv()) << "Could not read bits" << coilAddress << "Reason:"<< modbus_strerror(errno);
return false;
}
*result = (bool)status;
return true;
}
bool ModbusTCPMaster::getRegister(int slaveAddress, int registerAddress, int *result)
{
uint16_t data;
if (m_mb == nullptr) {
if (!createInterface())
return false;
}
if(modbus_set_slave(m_mb, slaveAddress) == -1){
qCWarning(dcMypv()) << "Error setting slave ID" << slaveAddress << "Reason:" << modbus_strerror(errno) ;
return false;
}
if (modbus_read_registers(m_mb, registerAddress, 1, &data) == -1){
qCWarning(dcMypv()) << "Could not read register" << registerAddress << "Reason:" << modbus_strerror(errno);
return false;
}
*result = data;
return true;
}

63
mypv/modbustcpmaster.h Normal file
View File

@ -0,0 +1,63 @@
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*
* Copyright 2013 - 2020, nymea GmbH
* Contact: contact@nymea.io
*
* This file is part of nymea.
* This project including source code and documentation is protected by copyright law, and
* remains the property of nymea GmbH. All rights, including reproduction, publication,
* editing and translation, are reserved. The use of this project is subject to the terms of a
* license agreement to be concluded with nymea GmbH in accordance with the terms
* of use of nymea GmbH, available under https://nymea.io/license
*
* GNU Lesser General Public License Usage
* Alternatively, this project may be redistributed and/or modified under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation; version 3.
* this project is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this project.
* If not, see <https://www.gnu.org/licenses/>.
*
* For any further details and any questions please contact us under contact@nymea.io
* or see our FAQ/Licensing Information on https://nymea.io/license/faq
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#ifndef MODBUSTCPMASTER_H
#define MODBUSTCPMASTER_H
#include <QObject>
#include <QHostAddress>
#include <modbus/modbus.h>
class ModbusTCPMaster : public QObject
{
Q_OBJECT
public:
explicit ModbusTCPMaster(QHostAddress IPv4Address, int port, QObject *parent = 0);
~ModbusTCPMaster();
bool createInterface();
bool getCoil(int slaveAddress, int coilAddress, bool *result);
bool getRegister(int slaveAddress, int registerAddress, int *result);
bool setCoil(int slaveAddress, int coilAddress, bool status);
bool setRegister(int slaveAddress, int registerAddress, int data);
QHostAddress ipv4Address();
int port();
bool setIPv4Address(QHostAddress IPv4Address);
bool setPort(int port);
private:
modbus_t *m_mb;
QHostAddress m_IPv4Address;
int m_port;
signals:
public slots:
};
#endif // MODBUSTCPMASTER_H

19
mypv/mypv.pro Normal file
View File

@ -0,0 +1,19 @@
include(../plugins.pri)
TARGET = $$qtLibraryTarget(nymea_devicepluginmypv)
QT += \
network \
serialbus \
LIBS += -lmodbus
SOURCES += \
devicepluginmypv.cpp \
modbustcpmaster.cpp \
HEADERS += \
devicepluginmypv.h \
modbustcpmaster.h \

View File

@ -2,6 +2,7 @@ TEMPLATE = subdirs
PLUGIN_DIRS = \
drexelundweiss \
mypv \
wallbe \
message(============================================)