Add basic structure for USB-RLY82
parent
750afb77f9
commit
f65bf428d9
|
|
@ -72,6 +72,7 @@ PLUGIN_DIRS = \
|
|||
udpcommander \
|
||||
unifi \
|
||||
usbrelay \
|
||||
usbrly82 \
|
||||
wakeonlan \
|
||||
wemo \
|
||||
ws2812fx \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
# USB-RLY82 - 2 channel USB relay
|
||||
|
||||
--------------------------------
|
||||
|
||||
This plugin allows you to control USB relays and receive digital and analog input values from the controller.
|
||||
|
||||
In order to set up the the controller in nymea, you only need to plug in the device. All digital and analog channels will be set up automatically.
|
||||
Each relay board has a unique serial number and therefore it can be clearly identified.
|
||||
|
||||
## Supported things
|
||||
|
||||
* USB-RLY82
|
||||
|
||||
This relay supports:
|
||||
|
||||
* 2 Digital outputs (Relays)
|
||||
* 8 Digital and analog inputs
|
||||
* the Resolution of the ADC can be configured
|
||||
|
||||
## More
|
||||
|
||||
Information about the USB relay hardware can be found [here](https://www.robot-electronics.co.uk/usb-rly82.html).
|
||||
The datasheet can be downloaded from [here](https://www.robot-electronics.co.uk/files/usb-rly82.pdf)
|
||||
|
|
@ -0,0 +1,254 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, 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 "integrationpluginusbrly82.h"
|
||||
|
||||
IntegrationPluginUsbRly82::IntegrationPluginUsbRly82()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::init()
|
||||
{
|
||||
m_monitor = new SerialPortMonitor(this);
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::discoverThings(ThingDiscoveryInfo *info)
|
||||
{
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::startMonitoringAutoThings()
|
||||
{
|
||||
// Start seaching for devices which can be discovered and added automatically
|
||||
connect(m_monitor, &SerialPortMonitor::serialPortAdded, this, &IntegrationPluginUsbRly82::onSerialPortAdded);
|
||||
connect(m_monitor, &SerialPortMonitor::serialPortRemoved, this, &IntegrationPluginUsbRly82::onSerialPortRemoved);
|
||||
|
||||
// Check the initial list of
|
||||
foreach (const SerialPortMonitor::SerialPortInfo &serialPortInfo, m_monitor->serialPortInfos()) {
|
||||
onSerialPortAdded(serialPortInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::setupThing(ThingSetupInfo *info)
|
||||
{
|
||||
Thing *thing = info->thing();
|
||||
qCDebug(dcUsbRly82()) << "Setup thing" << thing;
|
||||
|
||||
// Relay
|
||||
if (info->thing()->thingClassId() == usbRelayThingClassId) {
|
||||
|
||||
// Search for the serial port with the given serialnumber
|
||||
foreach (const SerialPortMonitor::SerialPortInfo &serialPortInfo, m_monitor->serialPortInfos()) {
|
||||
if (serialPortInfo.serialNumber == thing->paramValue(usbRelayThingSerialNumberParamTypeId).toString()) {
|
||||
qCDebug(dcUsbRly82()) << "Found serial port for" << thing << serialPortInfo;
|
||||
|
||||
UsbRly82 *relay = new UsbRly82(this);
|
||||
connect(relay, &UsbRly82::availableChanged, thing, [=](bool available){
|
||||
qCDebug(dcUsbRly82()) << thing << "available changed" << available;
|
||||
thing->setStateValue("connected", available);
|
||||
});
|
||||
|
||||
connect(relay, &UsbRly82::powerRelay1Changed, thing, [=](bool power){
|
||||
qCDebug(dcUsbRly82()) << thing << "relay 1 power changed" << power;
|
||||
thing->setStateValue(usbRelayPowerRelay1StateTypeId, power);
|
||||
});
|
||||
|
||||
connect(relay, &UsbRly82::powerRelay2Changed, thing, [=](bool power){
|
||||
qCDebug(dcUsbRly82()) << thing << "relay 2 power changed" << power;
|
||||
thing->setStateValue(usbRelayPowerRelay1StateTypeId, power);
|
||||
});
|
||||
|
||||
|
||||
connect(relay, &UsbRly82::digitalInputsChanged, thing, [=](){
|
||||
thing->setStateValue(usbRelayPowerRelay1StateTypeId, UsbRly82::checkBit());
|
||||
});
|
||||
|
||||
|
||||
if (!relay->connectRelay(serialPortInfo.systemLocation)) {
|
||||
qCWarning(dcUsbRly82()) << "Setup failed. Could not connect to relay" << thing;
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
relay->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
m_relays.insert(thing, relay);
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Does not seem to be available
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorSetupFailed);
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginUsbRly82::postSetupThing(Thing *thing)
|
||||
{
|
||||
qCDebug(dcUsbRly82()) << "Post setup thing" << thing;
|
||||
|
||||
// if (thing->thingClassId() == usbRelayConnectorThingClassId) {
|
||||
|
||||
// // Initialize the states
|
||||
// UsbRelay *relay = m_relays.key(thing);
|
||||
// if (!relay) {
|
||||
// qCWarning(dcUsbRly82()) << "Could not find relay in post setup.";
|
||||
// return;
|
||||
// }
|
||||
|
||||
// thing->setStateValue(usbRelayConnectorConnectedStateTypeId, relay->connected());
|
||||
|
||||
// // Check if we have to create child devices (relays)
|
||||
// if (myThings().filterByParentId(thing->id()).isEmpty()) {
|
||||
|
||||
// ThingDescriptors descriptors;
|
||||
// for (int i = 0; i < relay->relayCount(); i++) {
|
||||
// int relayNumber = i + 1;
|
||||
// ThingDescriptor descriptor(usbRelayThingClassId, QString("Relay %1").arg(relayNumber), QString(), thing->id());
|
||||
// ParamList params;
|
||||
// params.append(Param(usbRelayThingRelayNumberParamTypeId, relayNumber));
|
||||
// descriptor.setParams(params);
|
||||
// descriptors.append(descriptor);
|
||||
// }
|
||||
|
||||
// emit autoThingsAppeared(descriptors);
|
||||
// }
|
||||
// } else if (thing->thingClassId() == usbRelayThingClassId) {
|
||||
|
||||
// UsbRelay *relay = getRelayForDevice(thing);
|
||||
// if (!relay) return;
|
||||
|
||||
// // Set the current states
|
||||
// int relayNumber = thing->paramValue(usbRelayThingRelayNumberParamTypeId).toInt();
|
||||
// thing->setStateValue(usbRelayConnectedStateTypeId, relay->connected());
|
||||
// thing->setStateValue(usbRelayPowerStateTypeId, relay->relayPower(relayNumber));
|
||||
// }
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::thingRemoved(Thing *thing)
|
||||
{
|
||||
qCDebug(dcUsbRly82()) << "Remove thing" << thing;
|
||||
if (thing->thingClassId() == usbRelayThingClassId) {
|
||||
UsbRly82 *relay = m_relays.take(thing);
|
||||
if (!relay) return;
|
||||
delete relay;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void IntegrationPluginUsbRly82::executeAction(ThingActionInfo *info)
|
||||
{
|
||||
qCDebug(dcUsbRly82()) << "Executing action for thing" << info->thing() << info->action().actionTypeId().toString() << info->action().params();
|
||||
|
||||
if (info->thing()->thingClassId() == usbRelayThingClassId) {
|
||||
|
||||
Thing *thing = info->thing();
|
||||
UsbRly82 *relay = m_relays.value(thing);
|
||||
|
||||
if (!relay) {
|
||||
qCWarning(dcUsbRly82()) << "Could execute action because could not find USB relay for" << thing;
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!relay->available()) {
|
||||
qCWarning(dcUsbRly82()) << "Relay is not connected";
|
||||
info->finish(Thing::ThingErrorHardwareNotAvailable);
|
||||
return;
|
||||
}
|
||||
|
||||
if (info->action().actionTypeId() == usbRelayPowerRelay1ActionTypeId) {
|
||||
bool power = info->action().paramValue(usbRelayPowerRelay1ActionPowerRelay1ParamTypeId).toBool();
|
||||
UsbRly82Reply *reply = relay->setRelay1Power(power);
|
||||
connect(reply, &UsbRly82Reply::finished, info, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
return;
|
||||
} else if (info->action().actionTypeId() == usbRelayPowerRelay2ActionTypeId) {
|
||||
bool power = info->action().paramValue(usbRelayPowerRelay2ActionPowerRelay2ParamTypeId).toBool();
|
||||
UsbRly82Reply *reply = relay->setRelay2Power(power);
|
||||
connect(reply, &UsbRly82Reply::finished, info, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
info->finish(Thing::ThingErrorHardwareFailure);
|
||||
return;
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorNoError);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorActionTypeNotFound);
|
||||
}
|
||||
|
||||
info->finish(Thing::ThingErrorThingClassNotFound);
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::onSerialPortAdded(const SerialPortMonitor::SerialPortInfo &serialPortInfo)
|
||||
{
|
||||
if (serialPortInfo.vendorId == 0x04d8 && serialPortInfo.productId == 0xffee) {
|
||||
qCDebug(dcUsbRly82()) << "[+] Added" << serialPortInfo;
|
||||
Things alreadyAddedThings = myThings().filterByThingClassId(usbRelayThingClassId).filterByParam(usbRelayThingSerialNumberParamTypeId, serialPortInfo.serialNumber);
|
||||
if (alreadyAddedThings.isEmpty()) {
|
||||
qCDebug(dcUsbRly82()) << "New" << serialPortInfo.product << serialPortInfo.serialNumber << "showed up. Setting up a new thing for this.";
|
||||
ThingDescriptor descriptor(usbRelayThingClassId, "USB-RLY82");
|
||||
ParamList params;
|
||||
params.append(Param(usbRelayThingSerialNumberParamTypeId, serialPortInfo.serialNumber));
|
||||
descriptor.setParams(params);
|
||||
emit autoThingsAppeared(ThingDescriptors() << descriptor);
|
||||
} else {
|
||||
Thing *relayThing = alreadyAddedThings.first();
|
||||
if (relayThing) {
|
||||
qCDebug(dcUsbRly82()) << "Thing already set up for this controller" << relayThing;
|
||||
UsbRly82 *relay = m_relays.value(relayThing);
|
||||
if (relay) {
|
||||
relay->connectRelay(serialPortInfo.systemLocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IntegrationPluginUsbRly82::onSerialPortRemoved(const SerialPortMonitor::SerialPortInfo &serialPortInfo)
|
||||
{
|
||||
if (serialPortInfo.vendorId == 0x04d8 && serialPortInfo.productId == 0xffee) {
|
||||
qCDebug(dcUsbRly82()) << "[-] Removed" << serialPortInfo;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*
|
||||
* Copyright 2013 - 2022, 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 INTEGRATIONPLUGINUSBRLY82_H
|
||||
#define INTEGRATIONPLUGINUSBRLY82_H
|
||||
|
||||
#include "integrations/integrationplugin.h"
|
||||
#include "serialportmonitor.h"
|
||||
#include "usbrly82.h"
|
||||
|
||||
class IntegrationPluginUsbRly82: public IntegrationPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_PLUGIN_METADATA(IID "io.nymea.IntegrationPlugin" FILE "integrationpluginusbrly82.json")
|
||||
Q_INTERFACES(IntegrationPlugin)
|
||||
|
||||
|
||||
public:
|
||||
explicit IntegrationPluginUsbRly82();
|
||||
|
||||
void init() override;
|
||||
void startMonitoringAutoThings() override;
|
||||
void discoverThings(ThingDiscoveryInfo *info) override;
|
||||
void setupThing(ThingSetupInfo *info) override;
|
||||
void postSetupThing(Thing *thing) override;
|
||||
void thingRemoved(Thing *thing) override;
|
||||
void executeAction(ThingActionInfo *info) override;
|
||||
|
||||
private:
|
||||
SerialPortMonitor *m_monitor = nullptr;
|
||||
QHash<Thing *, UsbRly82 *> m_relays;
|
||||
|
||||
private slots:
|
||||
void onSerialPortAdded(const SerialPortMonitor::SerialPortInfo &serialPortInfo);
|
||||
void onSerialPortRemoved(const SerialPortMonitor::SerialPortInfo &serialPortInfo);
|
||||
|
||||
};
|
||||
|
||||
#endif // INTEGRATIONPLUGINUSBRLY82_H
|
||||
|
|
@ -0,0 +1,245 @@
|
|||
{
|
||||
"name": "UsbRly82",
|
||||
"displayName": "USB-RLY82",
|
||||
"id": "33d4ebad-104b-4f24-8089-6edd35ba2c95",
|
||||
"vendors": [
|
||||
{
|
||||
"displayName": "Robot electronics",
|
||||
"name": "robotElectronics",
|
||||
"id": "c3b972d1-85e6-436c-ae07-a04cbfae604e",
|
||||
"thingClasses": [
|
||||
{
|
||||
"name": "usbRelay",
|
||||
"displayName": "USB-RLY82",
|
||||
"id": "0ae5cd75-d42e-4993-8ce8-ced5ba315688",
|
||||
"setupMethod": "JustAdd",
|
||||
"createMethods": ["discovery", "auto"],
|
||||
"interfaces": [ "gateway" ],
|
||||
"paramTypes": [
|
||||
{
|
||||
"id": "5ac5b15f-34cf-4d42-8b9d-6e9866d8f2ed",
|
||||
"name": "serialNumber",
|
||||
"displayName": "Serial number",
|
||||
"type": "QString",
|
||||
"defaultValue": ""
|
||||
}
|
||||
],
|
||||
"stateTypes": [
|
||||
{
|
||||
"id": "b482b0ea-1901-4437-a309-be43833a1ad5",
|
||||
"name": "connected",
|
||||
"displayName": "Connected",
|
||||
"displayNameEvent": "Connected changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false,
|
||||
"cached": false
|
||||
},
|
||||
{
|
||||
"id": "9935ecba-bb56-4c6c-8be1-60bacc40abb7",
|
||||
"name": "powerRelay1",
|
||||
"displayName": "Power relay 1",
|
||||
"displayNameEvent": "Power relay 1 changed",
|
||||
"displayNameAction": "Set power relay 1",
|
||||
"type": "bool",
|
||||
"ioType": "digitalOutput",
|
||||
"defaultValue": false,
|
||||
"writable": true,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "d4b84a73-44dd-4bb9-a0d0-e45e6d2aaf7d",
|
||||
"name": "powerRelay2",
|
||||
"displayName": "Power relay 2",
|
||||
"displayNameEvent": "Power relay 2 changed",
|
||||
"displayNameAction": "Set power relay 2",
|
||||
"type": "bool",
|
||||
"ioType": "digitalOutput",
|
||||
"defaultValue": false,
|
||||
"writable": true,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "ad60f859-11e0-4a80-86fc-1810a5054fb6",
|
||||
"name": "digitalInputChannel1",
|
||||
"displayName": "Channel 1",
|
||||
"displayNameEvent": "Channel 1 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "5038008d-68c7-4cde-845d-335758a05a15",
|
||||
"name": "digitalInputChannel2",
|
||||
"displayName": "Channel 2",
|
||||
"displayNameEvent": "Channel 2 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "73a8c051-5ce8-4909-a75f-f0e88f1ceb61",
|
||||
"name": "digitalInputChannel3",
|
||||
"displayName": "Channel 3",
|
||||
"displayNameEvent": "Channel 3 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "11a3e692-cccc-435b-a9fd-721ae332c90c",
|
||||
"name": "digitalInputChannel4",
|
||||
"displayName": "Channel 4",
|
||||
"displayNameEvent": "Channel 4 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "932cb171-5ba2-4420-bee3-9016dca6498a",
|
||||
"name": "digitalInputChannel5",
|
||||
"displayName": "Channel 5",
|
||||
"displayNameEvent": "Channel 5 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "e10074d7-68cb-4f74-87ea-f0cdf193d207",
|
||||
"name": "digitalInputChannel6",
|
||||
"displayName": "Channel 6",
|
||||
"displayNameEvent": "Channel 6 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "f37aa320-ecfb-4896-bb31-8109606cc5b3",
|
||||
"name": "digitalInputChannel7",
|
||||
"displayName": "Channel 7",
|
||||
"displayNameEvent": "Channel 7 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "32aa1fbd-c128-4258-bb41-22b5b5e150de",
|
||||
"name": "digitalInputChannel8",
|
||||
"displayName": "Channel 8",
|
||||
"displayNameEvent": "Channel 8 input changed",
|
||||
"type": "bool",
|
||||
"ioType": "digitalInput",
|
||||
"defaultValue": false,
|
||||
"cached": true
|
||||
},
|
||||
{
|
||||
"id": "0dfda321-7da5-4e4c-880e-5b45098398e8",
|
||||
"name": "analogInputChannel1",
|
||||
"displayName": "Analog Channel 1",
|
||||
"displayNameEvent": "Analog channel 1 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "02882e2a-f0a6-4042-b91e-c7618c15399e",
|
||||
"name": "analogInputChannel2",
|
||||
"displayName": "Analog Channel 2",
|
||||
"displayNameEvent": "Analog channel 2 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "de9172e5-b1c1-4a7e-aebe-819ef22c2c5a",
|
||||
"name": "analogInputChannel3",
|
||||
"displayName": "Analog Channel 3",
|
||||
"displayNameEvent": "Analog channel 3 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "875ffbf1-3f11-4935-bd16-de983585baad",
|
||||
"name": "analogInputChannel4",
|
||||
"displayName": "Analog Channel 4",
|
||||
"displayNameEvent": "Analog channel 4 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "fa97d343-544d-4322-aacd-4f9f7c21224b",
|
||||
"name": "analogInputChannel5",
|
||||
"displayName": "Analog Channel 5",
|
||||
"displayNameEvent": "Analog channel 5 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "51d50bf9-6d40-4d63-a3a2-a0a7c0b5850d",
|
||||
"name": "analogInputChannel6",
|
||||
"displayName": "Analog Channel 6",
|
||||
"displayNameEvent": "Analog channel 6 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "25ecf6a9-2007-4768-93a4-28ec81bac179",
|
||||
"name": "analogInputChannel7",
|
||||
"displayName": "Analog Channel 7",
|
||||
"displayNameEvent": "Analog channel 7 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "93198a69-4ab6-4fd5-90ab-7f26a67b3b4a",
|
||||
"name": "analogInputChannel8",
|
||||
"displayName": "Analog Channel 8",
|
||||
"displayNameEvent": "Analog channel 8 value changed",
|
||||
"ioType": "analogInput",
|
||||
"type": "double",
|
||||
"minValue": 0,
|
||||
"maxValue": 1,
|
||||
"defaultValue": 0
|
||||
},
|
||||
{
|
||||
"id": "e06c88f4-38d9-42cd-bab9-2a07a9a83ce0",
|
||||
"name": "version",
|
||||
"displayName": "Version",
|
||||
"displayNameEvent": "Version changed",
|
||||
"type": "bool",
|
||||
"defaultValue": false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"title": "USB-RLY82",
|
||||
"tagline": "Control relays connected over USB and read digital and analog inputs.",
|
||||
"icon": "robotelectronics.png",
|
||||
"stability": "consumer",
|
||||
"offline": true,
|
||||
"technologies": [
|
||||
"usb"
|
||||
],
|
||||
"categories": [
|
||||
"diy"
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
|
|
@ -0,0 +1,190 @@
|
|||
#include "serialportmonitor.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
#include <QSerialPortInfo>
|
||||
|
||||
SerialPortMonitor::SerialPortMonitor(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_udev = udev_new();
|
||||
if (!m_udev) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not initialize udev";
|
||||
return;
|
||||
}
|
||||
|
||||
// Read initially all tty devices
|
||||
struct udev_enumerate *enumerate = udev_enumerate_new(m_udev);
|
||||
if (!enumerate) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not create udev enumerate for initial device reading.";
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// We are only interested in FTDI devices
|
||||
udev_enumerate_add_match_subsystem(enumerate, "tty");
|
||||
// udev_enumerate_add_match_property(enumerate, "ID_VENDOR_ID", "04d8");
|
||||
// udev_enumerate_add_match_property(enumerate, "ID_MODEL_ID", "ffee");
|
||||
|
||||
if (udev_enumerate_scan_devices(enumerate) < 0) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Failed to scan devices from udev enumerate.";
|
||||
udev_enumerate_unref(enumerate);
|
||||
enumerate = nullptr;
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcUsbRly82()) << "SerialPortMonitor: Load initial list of available serial ports...";
|
||||
struct udev_list_entry *devices = nullptr;
|
||||
devices = udev_enumerate_get_list_entry(enumerate);
|
||||
struct udev_list_entry *dev_list_entry = nullptr;
|
||||
udev_list_entry_foreach(dev_list_entry, devices) {
|
||||
struct udev_device *device = nullptr;
|
||||
const char *path;
|
||||
path = udev_list_entry_get_name(dev_list_entry);
|
||||
device = udev_device_new_from_syspath(m_udev, path);
|
||||
|
||||
// Print properties
|
||||
struct udev_list_entry *properties = udev_device_get_properties_list_entry(device);
|
||||
struct udev_list_entry *property_list_entry = nullptr;
|
||||
udev_list_entry_foreach(property_list_entry, properties) {
|
||||
qCDebug(dcUsbRly82()) << "SerialPortMonitor: - Property" << udev_list_entry_get_name(property_list_entry) << udev_list_entry_get_value(property_list_entry);
|
||||
}
|
||||
|
||||
QString vendorIdString = QString::fromLatin1(udev_device_get_property_value(device, "ID_VENDOR_ID"));
|
||||
QString productIdString = QString::fromLatin1(udev_device_get_property_value(device, "ID_MODEL_ID"));
|
||||
|
||||
SerialPortInfo info;
|
||||
info.systemLocation = QString::fromLatin1(udev_device_get_property_value(device,"DEVNAME"));
|
||||
info.manufacturer = QString::fromLatin1(udev_device_get_property_value(device,"ID_VENDOR_ENC"));
|
||||
info.product = QString::fromLatin1(udev_device_get_property_value(device,"ID_MODEL_ENC"));
|
||||
info.serialNumber = QString::fromLatin1(udev_device_get_property_value(device, "ID_SERIAL_SHORT"));
|
||||
info.vendorId = static_cast<quint16>(vendorIdString.toUInt(nullptr, 16));
|
||||
info.productId = static_cast<quint16>(productIdString.toUInt(nullptr, 16));
|
||||
|
||||
// Clean up this device since we have all information
|
||||
udev_device_unref(device);
|
||||
|
||||
qCDebug(dcUsbRly82()) << "SerialPortMonitor: [+]" << info;
|
||||
m_serialPortInfos.insert(info.systemLocation, info);
|
||||
emit serialPortAdded(info);
|
||||
}
|
||||
|
||||
udev_enumerate_unref(enumerate);
|
||||
enumerate = nullptr;
|
||||
|
||||
// Create udev monitor
|
||||
m_monitor = udev_monitor_new_from_netlink(m_udev, "udev");
|
||||
if (!m_monitor) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not initialize udev monitor.";
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Set monitor filter to tty subsystem
|
||||
if (udev_monitor_filter_add_match_subsystem_devtype(m_monitor, "tty", nullptr) < 0) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not set subsystem device type filter to tty.";
|
||||
udev_monitor_unref(m_monitor);
|
||||
m_monitor = nullptr;
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Enable the monitor
|
||||
if (udev_monitor_enable_receiving(m_monitor) < 0) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Could not enable udev monitor.";
|
||||
udev_monitor_unref(m_monitor);
|
||||
m_monitor = nullptr;
|
||||
udev_unref(m_udev);
|
||||
m_udev = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create socket notifier for read
|
||||
int socketDescriptor = udev_monitor_get_fd(m_monitor);
|
||||
m_notifier = new QSocketNotifier(socketDescriptor, QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this, [this, socketDescriptor](int socket){
|
||||
|
||||
if (socketDescriptor != socket) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: socket != socketdescriptor";
|
||||
return;
|
||||
}
|
||||
|
||||
// Create udev device
|
||||
udev_device *device = udev_monitor_receive_device(m_monitor);
|
||||
if (!device) {
|
||||
qCWarning(dcUsbRly82()) << "SerialPortMonitor: Got socket sotification but could not read device information.";
|
||||
return;
|
||||
}
|
||||
|
||||
QString actionString = QString::fromUtf8(udev_device_get_action(device));
|
||||
|
||||
QString vendorIdString = QString::fromUtf8(udev_device_get_property_value(device, "ID_VENDOR_ID"));
|
||||
QString productIdString = QString::fromUtf8(udev_device_get_property_value(device, "ID_MODEL_ID"));
|
||||
|
||||
SerialPortInfo info;
|
||||
info.systemLocation = QString::fromUtf8(udev_device_get_property_value(device,"DEVNAME"));
|
||||
info.manufacturer = QString::fromUtf8(udev_device_get_property_value(device,"ID_VENDOR_ENC"));
|
||||
info.product = QString::fromUtf8(udev_device_get_property_value(device,"ID_MODEL_ENC"));
|
||||
info.serialNumber = QString::fromUtf8(udev_device_get_property_value(device, "ID_SERIAL_SHORT"));
|
||||
info.vendorId = static_cast<quint16>(vendorIdString.toUInt(nullptr, 16));
|
||||
info.productId = static_cast<quint16>(productIdString.toUInt(nullptr, 16));
|
||||
|
||||
// Clean udev device
|
||||
udev_device_unref(device);
|
||||
|
||||
// Make sure we know the action
|
||||
if (actionString.isEmpty())
|
||||
return;
|
||||
|
||||
if (actionString == "add") {
|
||||
qCDebug(dcUsbRly82()) << "SerialPortMonitor: [+]" << info;
|
||||
if (!m_serialPortInfos.contains(info.systemLocation)) {
|
||||
m_serialPortInfos.insert(info.systemLocation, info);
|
||||
emit serialPortAdded(info);
|
||||
}
|
||||
}
|
||||
|
||||
if (actionString == "remove") {
|
||||
qCDebug(dcUsbRly82()) << "SerialPortMonitor: [-]" << info;
|
||||
|
||||
if (m_serialPortInfos.contains(info.systemLocation)) {
|
||||
m_serialPortInfos.remove(info.systemLocation);
|
||||
emit serialPortRemoved(info);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m_notifier->setEnabled(true);
|
||||
}
|
||||
|
||||
SerialPortMonitor::~SerialPortMonitor()
|
||||
{
|
||||
if (m_notifier)
|
||||
delete m_notifier;
|
||||
|
||||
if (m_monitor)
|
||||
udev_monitor_unref(m_monitor);
|
||||
|
||||
if (m_udev)
|
||||
udev_unref(m_udev);
|
||||
|
||||
}
|
||||
|
||||
QList<SerialPortMonitor::SerialPortInfo> SerialPortMonitor::serialPortInfos() const
|
||||
{
|
||||
return m_serialPortInfos.values();
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const SerialPortMonitor::SerialPortInfo &serialPortInfo)
|
||||
{
|
||||
dbg.nospace().noquote() << "SerialPort(" << QString("%1:%2")
|
||||
.arg(serialPortInfo.vendorId, 4, 16, QLatin1Char('0'))
|
||||
.arg(serialPortInfo.productId, 4, 16, QLatin1Char('0'))
|
||||
<< ", " << serialPortInfo.systemLocation
|
||||
<< ", " << serialPortInfo.manufacturer
|
||||
<< ", " << serialPortInfo.product << ") ";
|
||||
return dbg.maybeSpace().maybeQuote();
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
#ifndef SERIALPORTMONITOR_H
|
||||
#define SERIALPORTMONITOR_H
|
||||
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSerialPortInfo>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <libudev.h>
|
||||
|
||||
class SerialPortMonitor : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
typedef struct SerialPortInfo {
|
||||
QString manufacturer;
|
||||
QString product;
|
||||
QString serialNumber;
|
||||
QString systemLocation;
|
||||
quint16 vendorId;
|
||||
quint16 productId;
|
||||
} SerialPortInfo;
|
||||
|
||||
explicit SerialPortMonitor(QObject *parent = nullptr);
|
||||
~SerialPortMonitor();
|
||||
|
||||
QList<SerialPortInfo> serialPortInfos() const;
|
||||
|
||||
signals:
|
||||
void serialPortAdded(const SerialPortInfo &serialPortInfo);
|
||||
void serialPortRemoved(const SerialPortInfo &serialPortInfo);
|
||||
|
||||
private:
|
||||
struct udev *m_udev = nullptr;
|
||||
struct udev_monitor *m_monitor = nullptr;
|
||||
QSocketNotifier *m_notifier = nullptr;
|
||||
|
||||
QHash<QString, SerialPortInfo> m_serialPortInfos;
|
||||
|
||||
};
|
||||
|
||||
QDebug operator<< (QDebug dbg, const SerialPortMonitor::SerialPortInfo &serialPortInfo);
|
||||
|
||||
|
||||
#endif // SERIALPORTMONITOR_H
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
#include "usbrly82.h"
|
||||
#include "extern-plugininfo.h"
|
||||
|
||||
|
||||
UsbRly82Reply::Error UsbRly82Reply::error() const
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
QByteArray UsbRly82Reply::requestData() const
|
||||
{
|
||||
return m_requestData;
|
||||
}
|
||||
|
||||
QByteArray UsbRly82Reply::responseData() const
|
||||
{
|
||||
return m_responseData;
|
||||
}
|
||||
|
||||
UsbRly82Reply::UsbRly82Reply(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_timer.setSingleShot(true);
|
||||
m_timer.setInterval(1000);
|
||||
connect(&m_timer, &QTimer::timeout, this, [this](){
|
||||
m_error = ErrorTimeout;
|
||||
emit finished();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
UsbRly82::UsbRly82(QObject *parent) : QObject(parent)
|
||||
{
|
||||
qRegisterMetaType<QSerialPort::SerialPortError>();
|
||||
m_refreshTimer.setInterval(100);
|
||||
m_refreshTimer.setSingleShot(false);
|
||||
connect(&m_refreshTimer, &QTimer::timeout, this, &UsbRly82::poll);
|
||||
}
|
||||
|
||||
bool UsbRly82::available() const
|
||||
{
|
||||
return m_available;
|
||||
}
|
||||
|
||||
bool UsbRly82::powerRelay1() const
|
||||
{
|
||||
return m_powerRelay1;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::setRelay1Power(bool power)
|
||||
{
|
||||
UsbRly82Reply *reply;
|
||||
if (power) {
|
||||
reply = createReply(QByteArray::fromHex("65"), false);
|
||||
} else {
|
||||
reply = createReply(QByteArray::fromHex("6F"), false);
|
||||
}
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
bool UsbRly82::powerRelay2() const
|
||||
{
|
||||
return m_powerRelay2;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::setRelay2Power(bool power)
|
||||
{
|
||||
UsbRly82Reply *reply;
|
||||
if (power) {
|
||||
reply = createReply(QByteArray::fromHex("66"), false);
|
||||
} else {
|
||||
reply = createReply(QByteArray::fromHex("70"), false);
|
||||
}
|
||||
reply->m_expectsResponse = false;
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
quint8 UsbRly82::digitalInputs() const
|
||||
{
|
||||
return m_digitalInputs;
|
||||
}
|
||||
|
||||
bool UsbRly82::connectRelay(const QString &serialPort)
|
||||
{
|
||||
qCDebug(dcUsbRly82()) << "Connecting to" << serialPort;
|
||||
if (m_serialPort) {
|
||||
m_serialPort->close();
|
||||
delete m_serialPort;
|
||||
m_serialPort = nullptr;
|
||||
}
|
||||
|
||||
m_available = false;
|
||||
|
||||
m_serialPort = new QSerialPort(serialPort, this);
|
||||
m_serialPort->setBaudRate(19200);
|
||||
m_serialPort->setStopBits(QSerialPort::OneStop);
|
||||
m_serialPort->setParity(QSerialPort::NoParity);
|
||||
|
||||
if (!m_serialPort->open(QIODevice::ReadWrite)) {
|
||||
qCWarning(dcUsbRly82()) << "Could not open serial port" << serialPort << m_serialPort->errorString();
|
||||
m_serialPort->deleteLater();
|
||||
m_serialPort = nullptr;
|
||||
emit availableChanged(m_available);
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(m_serialPort, &QSerialPort::readyRead, this, &UsbRly82::onReadyRead);
|
||||
connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError)), Qt::QueuedConnection);
|
||||
|
||||
// Get serial number
|
||||
UsbRly82Reply *reply = getSerialNumber();
|
||||
connect(reply, &UsbRly82Reply::finished, this, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
qCWarning(dcUsbRly82()) << "Reading serial number finished with error" << reply->error();
|
||||
return;
|
||||
}
|
||||
|
||||
m_serialNumber = QString::fromUtf8(reply->responseData());
|
||||
qCDebug(dcUsbRly82()) << "Get serial number finished successfully." << m_serialNumber;
|
||||
|
||||
// Get software version
|
||||
UsbRly82Reply *reply = getSoftwareVersion();
|
||||
connect(reply, &UsbRly82Reply::finished, this, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
qCWarning(dcUsbRly82()) << "Reading software version finished with error" << reply->error();
|
||||
return;
|
||||
}
|
||||
|
||||
m_softwareVersion = QString::fromUtf8(reply->responseData().toHex());
|
||||
qCDebug(dcUsbRly82()) << "Get software version finished successfully." << m_softwareVersion;
|
||||
|
||||
UsbRly82Reply *reply = getRelayStates();
|
||||
connect(reply, &UsbRly82Reply::finished, this, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
qCWarning(dcUsbRly82()) << "Reading relay states finished with error" << reply->error();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcUsbRly82()) << "Reading relay states finished successfully." << reply->responseData().toHex();
|
||||
bool power = checkBit(reply->responseData().at(0), 0);
|
||||
if (m_powerRelay1 != power) {
|
||||
m_powerRelay1 = power;
|
||||
emit powerRelay1Changed(m_powerRelay1);
|
||||
}
|
||||
|
||||
power = checkBit(reply->responseData().at(0), 1);
|
||||
if (m_powerRelay2 != power) {
|
||||
m_powerRelay2 = power;
|
||||
emit powerRelay2Changed(m_powerRelay2);
|
||||
}
|
||||
|
||||
qCDebug(dcUsbRly82()) << "Relay 1:" << m_powerRelay1;
|
||||
qCDebug(dcUsbRly82()) << "Relay 2:" << m_powerRelay2;
|
||||
|
||||
m_available = true;
|
||||
emit availableChanged(m_available);
|
||||
|
||||
m_refreshTimer.start();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UsbRly82::disconnectRelay()
|
||||
{
|
||||
if (m_serialPort) {
|
||||
qCDebug(dcUsbRly82()) << "Disconnecting from" << m_serialPort->portName();
|
||||
m_serialPort->close();
|
||||
delete m_serialPort;
|
||||
m_serialPort = nullptr;
|
||||
}
|
||||
|
||||
m_refreshTimer.stop();
|
||||
|
||||
m_available = false;
|
||||
emit availableChanged(m_available);
|
||||
}
|
||||
|
||||
|
||||
UsbRly82Reply *UsbRly82::getSerialNumber()
|
||||
{
|
||||
UsbRly82Reply *reply = createReply(QByteArray::fromHex("38"));
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::getSoftwareVersion()
|
||||
{
|
||||
UsbRly82Reply *reply = createReply(QByteArray::fromHex("5A"));
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::getRelayStates()
|
||||
{
|
||||
UsbRly82Reply *reply = createReply(QByteArray::fromHex("5B"));
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::getDigitalInputs()
|
||||
{
|
||||
UsbRly82Reply *reply = createReply(QByteArray::fromHex("5E"));
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::getAdcValues()
|
||||
{
|
||||
UsbRly82Reply *reply = createReply(QByteArray::fromHex("80"));
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::getAdcReference()
|
||||
{
|
||||
UsbRly82Reply *reply = createReply(QByteArray::fromHex("82"));
|
||||
sendNextRequest();
|
||||
return reply;
|
||||
}
|
||||
|
||||
UsbRly82Reply *UsbRly82::createReply(const QByteArray &requestData, bool expectsResponse)
|
||||
{
|
||||
UsbRly82Reply *reply = new UsbRly82Reply(this);
|
||||
reply->m_expectsResponse = expectsResponse;
|
||||
reply->m_requestData = requestData;
|
||||
connect(reply, &UsbRly82Reply::finished, this, [=](){
|
||||
if (m_currentReply == reply) {
|
||||
m_currentReply = nullptr;
|
||||
sendNextRequest();
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
});
|
||||
|
||||
if (!expectsResponse) {
|
||||
m_replyQueue.enqueue(reply);
|
||||
} else {
|
||||
// Priorize requests without response (like switching the relay)
|
||||
m_replyQueue.prepend(reply);
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
void UsbRly82::sendNextRequest()
|
||||
{
|
||||
if (m_currentReply)
|
||||
return;
|
||||
|
||||
if (m_replyQueue.isEmpty())
|
||||
return;
|
||||
|
||||
m_currentReply = m_replyQueue.dequeue();
|
||||
qCDebug(dcUsbRly82()) << "-->" << m_currentReply->requestData().toHex();
|
||||
m_serialPort->write(m_currentReply->requestData());
|
||||
if (m_currentReply->m_expectsResponse) {
|
||||
m_currentReply->m_timer.start(1000);
|
||||
} else {
|
||||
// Finish the reply on next event loop
|
||||
QTimer::singleShot(0, m_currentReply, &UsbRly82Reply::finished);
|
||||
}
|
||||
}
|
||||
|
||||
bool UsbRly82::checkBit(quint8 byte, uint bitNumber)
|
||||
{
|
||||
return ((byte >> bitNumber) & 0x01) == 1;
|
||||
}
|
||||
|
||||
void UsbRly82::onReadyRead()
|
||||
{
|
||||
QByteArray data = m_serialPort->readAll();
|
||||
qCDebug(dcUsbRly82()) << "<--" << data.toHex();
|
||||
|
||||
if (m_currentReply) {
|
||||
m_currentReply->m_responseData = data;
|
||||
m_currentReply->m_timer.stop();
|
||||
emit m_currentReply->finished();
|
||||
} else {
|
||||
qCWarning(dcUsbRly82()) << "Unexpected data received" << data.toHex();
|
||||
}
|
||||
}
|
||||
|
||||
void UsbRly82::onError(QSerialPort::SerialPortError error)
|
||||
{
|
||||
if (error != QSerialPort::NoError && error != QSerialPort::OpenError && m_serialPort && m_serialPort->isOpen()) {
|
||||
qCWarning(dcUsbRly82()) << "Serial port error occurred:" << error << m_serialPort->errorString() << "(Is open:" << m_serialPort->isOpen() << ")";
|
||||
|
||||
m_available = false;
|
||||
emit availableChanged(available());
|
||||
|
||||
disconnectRelay();
|
||||
}
|
||||
}
|
||||
|
||||
void UsbRly82::poll()
|
||||
{
|
||||
if (m_replyQueue.count() > 10)
|
||||
return;
|
||||
|
||||
UsbRly82Reply *reply = getDigitalInputs();
|
||||
connect(reply, &UsbRly82Reply::finished, this, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
qCWarning(dcUsbRly82()) << "Reading digital inputs finished with error" << reply->error();
|
||||
return;
|
||||
}
|
||||
|
||||
if (reply->responseData().isEmpty())
|
||||
return;
|
||||
|
||||
quint8 digitalInputs = reply->responseData().at(0);
|
||||
if (m_digitalInputs != digitalInputs) {
|
||||
qCDebug(dcUsbRly82()) << "Digital inputs changed";
|
||||
m_digitalInputs = digitalInputs;
|
||||
emit digitalInputsChanged();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
reply = getAdcValues();
|
||||
connect(reply, &UsbRly82Reply::finished, this, [=](){
|
||||
if (reply->error() != UsbRly82Reply::ErrorNoError) {
|
||||
qCWarning(dcUsbRly82()) << "Reading analog inputs finished with error" << reply->error();
|
||||
return;
|
||||
}
|
||||
|
||||
qCDebug(dcUsbRly82()) << "Analog inputs";
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef USBRLY82_H
|
||||
#define USBRLY82_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QQueue>
|
||||
#include <QSerialPort>
|
||||
|
||||
class UsbRly82Reply : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
friend class UsbRly82;
|
||||
|
||||
public:
|
||||
enum Error {
|
||||
ErrorNoError,
|
||||
ErrorTimeout
|
||||
};
|
||||
Q_ENUM(Error)
|
||||
|
||||
Error error() const;
|
||||
QByteArray requestData() const;
|
||||
QByteArray responseData() const;
|
||||
|
||||
signals:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
explicit UsbRly82Reply(QObject *parent = nullptr);
|
||||
|
||||
Error m_error = ErrorNoError;
|
||||
QTimer m_timer;
|
||||
bool m_expectsResponse = true;
|
||||
|
||||
QByteArray m_requestData;
|
||||
QByteArray m_responseData;
|
||||
};
|
||||
|
||||
class UsbRly82 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit UsbRly82(QObject *parent = nullptr);
|
||||
|
||||
bool available() const;
|
||||
|
||||
bool powerRelay1() const;
|
||||
UsbRly82Reply *setRelay1Power(bool power);
|
||||
|
||||
bool powerRelay2() const;
|
||||
UsbRly82Reply *setRelay2Power(bool power);
|
||||
|
||||
quint8 digitalInputs() const;
|
||||
|
||||
bool connectRelay(const QString &serialPort);
|
||||
void disconnectRelay();
|
||||
|
||||
UsbRly82Reply *getSerialNumber();
|
||||
UsbRly82Reply *getSoftwareVersion();
|
||||
UsbRly82Reply *getRelayStates();
|
||||
|
||||
UsbRly82Reply *getDigitalInputs();
|
||||
UsbRly82Reply *getAdcValues();
|
||||
UsbRly82Reply *getAdcReference();
|
||||
|
||||
static bool checkBit(quint8 byte, uint bitNumber);
|
||||
|
||||
|
||||
signals:
|
||||
void availableChanged(bool available);
|
||||
|
||||
void powerRelay1Changed(bool powerRelay1);
|
||||
void powerRelay2Changed(bool powerRelay2);
|
||||
|
||||
void digitalInputsChanged();
|
||||
|
||||
private:
|
||||
QTimer m_refreshTimer;
|
||||
QSerialPort *m_serialPort = nullptr;
|
||||
bool m_available = false;
|
||||
|
||||
QString m_serialNumber;
|
||||
QString m_softwareVersion;
|
||||
|
||||
bool m_powerRelay1 = false;
|
||||
bool m_powerRelay2 = false;
|
||||
|
||||
UsbRly82Reply *m_currentReply = nullptr;
|
||||
QQueue<UsbRly82Reply *> m_replyQueue;
|
||||
|
||||
UsbRly82Reply *createReply(const QByteArray &requestData, bool expectsResponse = true);
|
||||
void sendNextRequest();
|
||||
|
||||
|
||||
quint8 m_digitalInputs = 0x00;
|
||||
|
||||
private slots:
|
||||
void onReadyRead();
|
||||
void onError(QSerialPort::SerialPortError error);
|
||||
|
||||
void poll();
|
||||
|
||||
};
|
||||
|
||||
#endif // USBRLY82_H
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
include(../plugins.pri)
|
||||
|
||||
QT *= serialport
|
||||
|
||||
PKGCONFIG += libudev
|
||||
|
||||
HEADERS += \
|
||||
integrationpluginusbrly82.h \
|
||||
serialportmonitor.h \
|
||||
usbrly82.h
|
||||
|
||||
SOURCES += \
|
||||
integrationpluginusbrly82.cpp \
|
||||
serialportmonitor.cpp \
|
||||
usbrly82.cpp
|
||||
|
||||
Loading…
Reference in New Issue