diff --git a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.cpp b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.cpp
index 1dfae99..fbb43dd 100644
--- a/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.cpp
+++ b/libnymea-zigbee/backends/deconz/interface/zigbeeinterfacedeconz.cpp
@@ -48,6 +48,16 @@ ZigbeeInterfaceDeconz::~ZigbeeInterfaceDeconz()
}
+bool ZigbeeInterfaceDeconz::available() const
+{
+ return m_available;
+}
+
+QString ZigbeeInterfaceDeconz::serialPort() const
+{
+ return m_serialPort->portName();
+}
+
quint16 ZigbeeInterfaceDeconz::calculateCrc(const QByteArray &data)
{
quint16 crc = 0;
@@ -251,7 +261,7 @@ bool ZigbeeInterfaceDeconz::enable(const QString &serialPort, qint32 baudrate)
m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
connect(m_serialPort, &QSerialPort::readyRead, this, &ZigbeeInterfaceDeconz::onReadyRead);
- connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError)));
+ connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError)), Qt::QueuedConnection);
if (!m_serialPort->open(QSerialPort::ReadWrite)) {
qCWarning(dcZigbeeInterface()) << "Could not open serial port" << serialPort << baudrate << m_serialPort->errorString();
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterface.cpp b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterface.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterface.cpp
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterface.cpp
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterface.h b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterface.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterface.h
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterface.h
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacemessage.cpp b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacemessage.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterfacemessage.cpp
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacemessage.cpp
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacemessage.h b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacemessage.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterfacemessage.h
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacemessage.h
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacereply.cpp b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacereply.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterfacereply.cpp
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacereply.cpp
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacereply.h b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacereply.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterfacereply.h
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacereply.h
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacerequest.cpp b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacerequest.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterfacerequest.cpp
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacerequest.cpp
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacerequest.h b/libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacerequest.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/interface/zigbeeinterfacerequest.h
rename to libnymea-zigbee/backends/nxp-old/interface/zigbeeinterfacerequest.h
diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp b/libnymea-zigbee/backends/nxp-old/zigbeebridgecontrollernxp.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp
rename to libnymea-zigbee/backends/nxp-old/zigbeebridgecontrollernxp.cpp
diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h b/libnymea-zigbee/backends/nxp-old/zigbeebridgecontrollernxp.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h
rename to libnymea-zigbee/backends/nxp-old/zigbeebridgecontrollernxp.h
diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/backends/nxp-old/zigbeenetworknxp.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp
rename to libnymea-zigbee/backends/nxp-old/zigbeenetworknxp.cpp
diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.h b/libnymea-zigbee/backends/nxp-old/zigbeenetworknxp.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeenetworknxp.h
rename to libnymea-zigbee/backends/nxp-old/zigbeenetworknxp.h
diff --git a/libnymea-zigbee/backends/nxp/zigbeenodeendpointnxp.cpp b/libnymea-zigbee/backends/nxp-old/zigbeenodeendpointnxp.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeenodeendpointnxp.cpp
rename to libnymea-zigbee/backends/nxp-old/zigbeenodeendpointnxp.cpp
diff --git a/libnymea-zigbee/backends/nxp/zigbeenodeendpointnxp.h b/libnymea-zigbee/backends/nxp-old/zigbeenodeendpointnxp.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeenodeendpointnxp.h
rename to libnymea-zigbee/backends/nxp-old/zigbeenodeendpointnxp.h
diff --git a/libnymea-zigbee/backends/nxp/zigbeenodenxp.cpp b/libnymea-zigbee/backends/nxp-old/zigbeenodenxp.cpp
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeenodenxp.cpp
rename to libnymea-zigbee/backends/nxp-old/zigbeenodenxp.cpp
diff --git a/libnymea-zigbee/backends/nxp/zigbeenodenxp.h b/libnymea-zigbee/backends/nxp-old/zigbeenodenxp.h
similarity index 100%
rename from libnymea-zigbee/backends/nxp/zigbeenodenxp.h
rename to libnymea-zigbee/backends/nxp-old/zigbeenodenxp.h
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp
new file mode 100644
index 0000000..574acb0
--- /dev/null
+++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp
@@ -0,0 +1,302 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* 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 .
+*
+* 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 "zigbeeinterfacenxp.h"
+#include "zigbee.h"
+#include "zigbeeutils.h"
+#include "loggingcategory.h"
+
+#include
+
+// SLIP: https://tools.ietf.org/html/rfc1055
+
+ZigbeeInterfaceNxp::ZigbeeInterfaceNxp(QObject *parent) : QObject(parent)
+{
+ m_reconnectTimer = new QTimer(this);
+ m_reconnectTimer->setSingleShot(true);
+ m_reconnectTimer->setInterval(5000);
+
+ connect(m_reconnectTimer, &QTimer::timeout, this, &ZigbeeInterfaceNxp::onReconnectTimeout);
+}
+
+ZigbeeInterfaceNxp::~ZigbeeInterfaceNxp()
+{
+
+}
+
+bool ZigbeeInterfaceNxp::available() const
+{
+ return m_available;
+}
+
+QString ZigbeeInterfaceNxp::serialPort() const
+{
+ return m_serialPort->portName();
+}
+
+quint8 ZigbeeInterfaceNxp::calculateCrc(const QByteArray &data)
+{
+ quint8 crc = 0;
+ for(int i = 0; i < data.length(); i++) {
+ crc ^= data[i];
+ }
+
+ return crc;
+}
+
+QByteArray ZigbeeInterfaceNxp::unescapeData(const QByteArray &data)
+{
+ QByteArray deserializedData;
+ // Parse serial data and built InterfaceMessage
+ bool escaped = false;
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+
+ if (escaped) {
+ if (byte == ProtocolByteTransposedEnd) {
+ deserializedData.append(static_cast(ProtocolByteEnd));
+ } else if (byte == ProtocolByteTransposedEsc) {
+ deserializedData.append(static_cast(ProtocolByteEsc));
+ } else {
+ qCWarning(dcZigbeeInterfaceTraffic()) << "Error while deserialing data. Escape character received but the escaped character was not recognized.";
+ return QByteArray();
+ }
+
+ escaped = false;
+ continue;
+ }
+
+ // If escape byte, the next byte has to be a modified byte
+ if (byte == ProtocolByteEsc) {
+ escaped = true;
+ } else {
+ deserializedData.append(static_cast(byte));
+ }
+ }
+
+ return deserializedData;
+}
+
+QByteArray ZigbeeInterfaceNxp::escapeData(const QByteArray &data)
+{
+ QByteArray serializedData;
+ QDataStream stream(&serializedData, QIODevice::WriteOnly);
+
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+ switch (byte) {
+ case ProtocolByteEnd:
+ stream << static_cast(ProtocolByteEsc);
+ stream << static_cast(ProtocolByteTransposedEnd);
+ break;
+ case ProtocolByteEsc:
+ stream << static_cast(ProtocolByteEsc);
+ stream << static_cast(ProtocolByteTransposedEsc);
+ break;
+ default:
+ stream << byte;
+ break;
+ }
+ }
+
+ return serializedData;
+}
+
+void ZigbeeInterfaceNxp::setAvailable(bool available)
+{
+ if (m_available == available)
+ return;
+
+ // Clear the data buffer in any case
+ if (m_available) {
+ m_dataBuffer.clear();
+ }
+
+ m_available = available;
+ emit availableChanged(m_available);
+}
+
+void ZigbeeInterfaceNxp::onReconnectTimeout()
+{
+ if (m_serialPort && !m_serialPort->isOpen()) {
+ if (!m_serialPort->open(QSerialPort::ReadWrite)) {
+ setAvailable(false);
+ m_reconnectTimer->start();
+ } else {
+ qCDebug(dcZigbeeInterface()) << "Interface reconnected successfully on" << m_serialPort->portName() << m_serialPort->baudRate();
+ m_serialPort->clear();
+ setAvailable(true);
+ }
+ }
+}
+
+void ZigbeeInterfaceNxp::onReadyRead()
+{
+ QByteArray data = m_serialPort->readAll();
+
+ // Read each byte until we get END byte, then unescape the package
+ for (int i = 0; i < data.length(); i++) {
+ quint8 byte = static_cast(data.at(i));
+ if (byte == ProtocolByteEnd) {
+ // If there is no data...continue since it might be a starting END byte
+ if (m_dataBuffer.isEmpty())
+ continue;
+
+ qCDebug(dcZigbeeInterfaceTraffic()) << "<--" << ZigbeeUtils::convertByteArrayToHexString(m_dataBuffer);
+ QByteArray frame = unescapeData(m_dataBuffer);
+ if (frame.isNull()) {
+ qCWarning(dcZigbeeInterface()) << "Received inconsistant message. Ignoring data" << ZigbeeUtils::convertByteArrayToHexString(m_dataBuffer);
+ } else {
+ QByteArray package = frame.left(frame.length() - 2);
+ QByteArray checksumBytes = frame.right(2);
+ QDataStream stream(&checksumBytes, QIODevice::ReadOnly);
+ stream.setByteOrder(QDataStream::LittleEndian);
+ quint16 receivedChecksum = 0;
+ stream >> receivedChecksum;
+ quint16 calculatedChecksum = calculateCrc(package);
+ if (receivedChecksum != calculatedChecksum) {
+ qCWarning(dcZigbeeInterfaceTraffic()) << "Checksum verification failed for frame" << ZigbeeUtils::convertByteArrayToHexString(m_dataBuffer) << receivedChecksum << "!=" << calculatedChecksum;
+ m_dataBuffer.clear();
+ continue;
+ }
+
+ // Checksum verified, we got valid data
+ qCDebug(dcZigbeeInterface()) << "Received frame" << ZigbeeUtils::convertByteArrayToHexString(frame);
+ emit packageReceived(package);
+ }
+ m_dataBuffer.clear();
+ } else {
+ m_dataBuffer.append(data.at(i));
+ }
+ }
+}
+
+void ZigbeeInterfaceNxp::onError(const QSerialPort::SerialPortError &error)
+{
+ if (error != QSerialPort::NoError && m_serialPort->isOpen()) {
+ qCWarning(dcZigbeeInterface()) << "Serial port error:" << error << m_serialPort->errorString();
+ m_reconnectTimer->start();
+ m_serialPort->close();
+ setAvailable(false);
+ }
+}
+
+void ZigbeeInterfaceNxp::sendPackage(const QByteArray &package)
+{
+ if (!m_available) {
+ qCWarning(dcZigbeeInterface()) << "Can not send data. The interface is not available";
+ return;
+ }
+
+ // Build the frame and escape the package data and crc
+ quint8 checksum = calculateCrc(package);
+
+ QByteArray frame = package;
+ QDataStream frameStream(&frame, QIODevice::WriteOnly | QIODevice::Append);
+ frameStream.setByteOrder(QDataStream::LittleEndian);
+ frameStream << checksum;
+
+ qCDebug(dcZigbeeInterface()) << "Send frame" << ZigbeeUtils::convertByteArrayToHexString(frame);
+
+ // Escape data according to SLIP for transfere
+ QByteArray serializedData = escapeData(frame);
+
+ // Build transport data
+ QByteArray data;
+ QDataStream stream(&data, QIODevice::WriteOnly);
+ stream << static_cast(ProtocolByteEnd); // Start with SLIP END character
+ for (int i = 0; i < serializedData.length(); i++)
+ stream << static_cast(serializedData.at(i));
+
+ stream << static_cast(ProtocolByteEnd); // End with SLIP END character
+
+ // Send the data
+ qCDebug(dcZigbeeInterfaceTraffic()) << "-->" << ZigbeeUtils::convertByteArrayToHexString(data);
+ if (m_serialPort->write(data) < 0) {
+ qCWarning(dcZigbeeInterface()) << "Could not stream byte" << ZigbeeUtils::convertByteArrayToHexString(data);
+ }
+}
+
+bool ZigbeeInterfaceNxp::enable(const QString &serialPort, qint32 baudrate)
+{
+ qCDebug(dcZigbeeInterface()) << "Start UART interface " << serialPort << baudrate;
+
+ if (m_serialPort) {
+ delete m_serialPort;
+ m_serialPort = nullptr;
+ }
+
+ m_serialPort = new QSerialPort(serialPort, this);
+ m_serialPort->setBaudRate(baudrate);
+ m_serialPort->setDataBits(QSerialPort::Data8);
+ m_serialPort->setStopBits(QSerialPort::OneStop);
+ m_serialPort->setParity(QSerialPort::NoParity);
+ m_serialPort->setFlowControl(QSerialPort::NoFlowControl);
+
+ connect(m_serialPort, &QSerialPort::readyRead, this, &ZigbeeInterfaceNxp::onReadyRead);
+ connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), this, SLOT(onError(QSerialPort::SerialPortError)), Qt::QueuedConnection);
+
+ if (!m_serialPort->open(QSerialPort::ReadWrite)) {
+ qCWarning(dcZigbeeInterface()) << "Could not open serial port" << serialPort << baudrate << m_serialPort->errorString();
+ m_reconnectTimer->start();
+ return false;
+ }
+
+ qCDebug(dcZigbeeInterface()) << "Interface enabled successfully on" << serialPort << baudrate;
+ m_serialPort->clear();
+
+ setAvailable(true);
+ return true;
+}
+
+void ZigbeeInterfaceNxp::reconnectController()
+{
+ if (!m_serialPort)
+ return;
+
+ if (m_serialPort->isOpen())
+ m_serialPort->close();
+
+ delete m_serialPort;
+ m_serialPort = nullptr;
+ setAvailable(false);
+ m_reconnectTimer->start();
+}
+
+void ZigbeeInterfaceNxp::disable()
+{
+ if (!m_serialPort)
+ return;
+
+ if (m_serialPort->isOpen())
+ m_serialPort->close();
+
+ delete m_serialPort;
+ m_serialPort = nullptr;
+ setAvailable(false);
+ qCDebug(dcZigbeeInterface()) << "Interface disabled";
+}
diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.h b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.h
new file mode 100644
index 0000000..0d36ddf
--- /dev/null
+++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.h
@@ -0,0 +1,83 @@
+/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+*
+* Copyright 2013 - 2020, nymea GmbH
+* Contact: contact@nymea.io
+*
+* This file is part of nymea-zigbee.
+* 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 .
+*
+* 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 ZIGBEEINTERFACENXP_H
+#define ZIGBEEINTERFACENXP_H
+
+#include
+#include
+#include
+
+class ZigbeeInterfaceNxp : public QObject
+{
+ Q_OBJECT
+
+public:
+ enum ProtocolByte {
+ ProtocolByteEnd = 0xC0,
+ ProtocolByteEsc = 0xDB,
+ ProtocolByteTransposedEnd = 0xDC,
+ ProtocolByteTransposedEsc = 0xDD
+ };
+ Q_ENUM(ProtocolByte)
+
+ explicit ZigbeeInterfaceNxp(QObject *parent = nullptr);
+ ~ZigbeeInterfaceNxp();
+
+ bool available() const;
+ QString serialPort() const;
+
+private:
+ QTimer *m_reconnectTimer = nullptr;
+ QSerialPort *m_serialPort = nullptr;
+ bool m_available = false;
+ QByteArray m_dataBuffer;
+
+ quint8 calculateCrc(const QByteArray &data);
+ QByteArray unescapeData(const QByteArray &data);
+ QByteArray escapeData(const QByteArray &data);
+
+ void setAvailable(bool available);
+
+signals:
+ void availableChanged(bool available);
+ void packageReceived(const QByteArray &package);
+
+private slots:
+ void onReconnectTimeout();
+ void onReadyRead();
+ void onError(const QSerialPort::SerialPortError &error);
+
+public slots:
+ void sendPackage(const QByteArray &package);
+ bool enable(const QString &serialPort = "/dev/ttyS0", qint32 baudrate = 115200);
+ void reconnectController();
+ void disable();
+
+};
+
+#endif // ZIGBEEINTERFACENXP_H
diff --git a/libnymea-zigbee/libnymea-zigbee.pro b/libnymea-zigbee/libnymea-zigbee.pro
index ef0b73a..0919e6a 100644
--- a/libnymea-zigbee/libnymea-zigbee.pro
+++ b/libnymea-zigbee/libnymea-zigbee.pro
@@ -8,6 +8,7 @@ SOURCES += \
backends/deconz/interface/zigbeeinterfacedeconzreply.cpp \
backends/deconz/zigbeebridgecontrollerdeconz.cpp \
backends/deconz/zigbeenetworkdeconz.cpp \
+ backends/nxp/interface/zigbeeinterfacenxp.cpp \
zcl/general/zigbeeclusteridentify.cpp \
zcl/general/zigbeeclusterlevelcontrol.cpp \
zcl/general/zigbeeclusteronoff.cpp \
@@ -59,6 +60,7 @@ HEADERS += \
backends/deconz/interface/zigbeeinterfacedeconzreply.h \
backends/deconz/zigbeebridgecontrollerdeconz.h \
backends/deconz/zigbeenetworkdeconz.h \
+ backends/nxp/interface/zigbeeinterfacenxp.h \
zcl/general/zigbeeclusteridentify.h \
zcl/general/zigbeeclusterlevelcontrol.h \
zcl/general/zigbeeclusteronoff.h \