diff --git a/libnymea-zigbee/backends/nxp/interface/nxp.h b/libnymea-zigbee/backends/nxp/interface/nxp.h index 6fededa..213f692 100644 --- a/libnymea-zigbee/backends/nxp/interface/nxp.h +++ b/libnymea-zigbee/backends/nxp/interface/nxp.h @@ -10,7 +10,12 @@ public: enum Command { CommandGetVersion = 0x00, CommandGetControllerState = 0x01, - CommandSoftReset = 0x02 + CommandSoftReset = 0x02, + CommandFactoryReset = 0x03, + CommandSetPanId = 0x04, + CommandSetChannelMask = 0x05, + CommandSetSecurityKey = 0x06, + CommandStartNetwork = 0x07 }; Q_ENUM(Command) @@ -24,7 +29,8 @@ public: StatusSuccess = 0x00, StatusProtocolError = 0x01, StatusUnknownCommand = 0x02, - StatusInvalidCrc = 0x03 + StatusInvalidCrc = 0x03, + StatusStackError = 0x04 }; Q_ENUM(Status) diff --git a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp index a19d83c..b2c58b1 100644 --- a/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp +++ b/libnymea-zigbee/backends/nxp/interface/zigbeeinterfacenxp.cpp @@ -163,7 +163,7 @@ void ZigbeeInterfaceNxp::onReadyRead() // 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)); - qCDebug(dcZigbeeInterfaceTraffic()) << "[in] " << ZigbeeUtils::convertByteToHexString(byte); + //qCDebug(dcZigbeeInterfaceTraffic()) << "[in] " << ZigbeeUtils::convertByteToHexString(byte); if (byte == ProtocolByteEnd) { // If there is no data...continue since it might be a starting END byte if (m_dataBuffer.isEmpty()) @@ -235,9 +235,9 @@ void ZigbeeInterfaceNxp::sendPackage(const QByteArray &package) // Send the data qCDebug(dcZigbeeInterfaceTraffic()) << "-->" << ZigbeeUtils::convertByteArrayToHexString(data); - for (int i = 0; i < data.length(); i++) { - qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << ZigbeeUtils::convertByteToHexString(data.at(i)); - } +// for (int i = 0; i < data.length(); i++) { +// qCDebug(dcZigbeeInterfaceTraffic()) << "[out]" << ZigbeeUtils::convertByteToHexString(data.at(i)); +// } if (m_serialPort->write(data) < 0) { qCWarning(dcZigbeeInterface()) << "Could not stream byte" << ZigbeeUtils::convertByteArrayToHexString(data); diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp index 13dd541..b09c89d 100644 --- a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.cpp @@ -25,10 +25,11 @@ ZigbeeBridgeControllerNxp::ControllerState ZigbeeBridgeControllerNxp::controller ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestVersion() { QByteArray message; + bumpSequenceNumber(); QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Nxp::CommandGetVersion); - stream << static_cast(m_sequenceNumber++); + stream << static_cast(m_sequenceNumber); stream << static_cast(0); // Frame length return createReply(Nxp::CommandGetVersion, m_sequenceNumber, "Request controller version", message, this); @@ -37,10 +38,11 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestVersion() ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestControllerState() { QByteArray message; + bumpSequenceNumber(); QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Nxp::CommandGetControllerState); - stream << static_cast(m_sequenceNumber++); + stream << static_cast(m_sequenceNumber); stream << static_cast(0); // Frame length return createReply(Nxp::CommandGetControllerState, m_sequenceNumber, "Request controller state", message, this); @@ -49,15 +51,57 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestControllerState() ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSoftResetController() { QByteArray message; + bumpSequenceNumber(); QDataStream stream(&message, QIODevice::WriteOnly); stream.setByteOrder(QDataStream::LittleEndian); stream << static_cast(Nxp::CommandSoftReset); - stream << static_cast(m_sequenceNumber++); + stream << static_cast(m_sequenceNumber); stream << static_cast(0); // Frame length return createReply(Nxp::CommandSoftReset, m_sequenceNumber, "Request soft reset controller", message, this); } +ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestFactoryResetController() +{ + QByteArray message; + bumpSequenceNumber(); + QDataStream stream(&message, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(Nxp::CommandFactoryReset); + stream << static_cast(m_sequenceNumber); + stream << static_cast(0); // Frame length + + return createReply(Nxp::CommandFactoryReset, m_sequenceNumber, "Request factory reset controller", message, this); +} + +ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSetPanId(quint64 panId) +{ + QByteArray message; + bumpSequenceNumber(); + QDataStream stream(&message, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(Nxp::CommandSetPanId); + stream << static_cast(m_sequenceNumber); + stream << static_cast(8); // Frame length + stream << panId; + + return createReply(Nxp::CommandSetPanId, m_sequenceNumber, "Request set PAN ID", message, this); +} + +ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::requestSetChannelMask(quint32 channelMask) +{ + QByteArray message; + bumpSequenceNumber(); + QDataStream stream(&message, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(Nxp::CommandSetChannelMask); + stream << static_cast(m_sequenceNumber); + stream << static_cast(4); // Frame length + stream << channelMask; + + return createReply(Nxp::CommandSetChannelMask, m_sequenceNumber, "Request set channel mask", message, this); +} + ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent) { // Create the reply @@ -65,23 +109,32 @@ ZigbeeInterfaceNxpReply *ZigbeeBridgeControllerNxp::createReply(Nxp::Command com reply->m_requestName = requestName; reply->m_requestData = requestData; reply->m_sequenceNumber = sequenceNumber; - // Make sure we clean up on timeout connect(reply, &ZigbeeInterfaceNxpReply::timeout, this, [reply](){ qCWarning(dcZigbeeController()) << "Reply timeout" << reply; }); // Auto delete the object on finished - connect(reply, &ZigbeeInterfaceNxpReply::finished, reply, [reply](){ - qCDebug(dcZigbeeController()) << "Interface reply finished" << reply->command() << reply->sequenceNumber() << reply->status(); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ reply->deleteLater(); + if (m_currentReply == reply) { + m_currentReply = nullptr; + QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection); + } }); - m_pendingReplies.insert(sequenceNumber, reply); - m_interface->sendPackage(requestData); + qCDebug(dcZigbeeController()) << "Enqueue request" << reply->command() << "SQN:" << reply->sequenceNumber(); + m_replyQueue.enqueue(reply); + + QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection); return reply; } +void ZigbeeBridgeControllerNxp::bumpSequenceNumber() +{ + m_sequenceNumber += 1; +} + void ZigbeeBridgeControllerNxp::onInterfaceAvailableChanged(bool available) { qCDebug(dcZigbeeController()) << "Interface available changed" << available; @@ -106,14 +159,14 @@ void ZigbeeBridgeControllerNxp::onInterfacePackageReceived(const QByteArray &pac } Nxp::Notification notification = static_cast(commandInt); - //qCDebug(dcZigbeeController()) << "Interface notification received" << notification << "SQN:" << sequenceNumber << ZigbeeUtils::convertByteArrayToHexString(data); + qCDebug(dcZigbeeController()) << "Interface notification received" << notification << "SQN:" << sequenceNumber << ZigbeeUtils::convertByteArrayToHexString(data); switch (notification) { case Nxp::NotificationDebugMessage: if (data.isEmpty()) { qCWarning(dcZigbeeController()) << "Received empty debug log notification"; return; } - qCDebug(dcZigbeeController()) << "DEBUG" << static_cast(data.at(0)) << qUtf8Printable(data.right(data.length() - 1)); + qCDebug(dcZigbeeController()) << "*****DEBUG*****" << static_cast(data.at(0)) << Qt::endl << qUtf8Printable(data.right(data.length() - 1)); break; case Nxp::NotificationDeviceStatusChanged: m_controllerState = static_cast(data.at(0)); @@ -135,21 +188,37 @@ void ZigbeeBridgeControllerNxp::onInterfacePackageReceived(const QByteArray &pac Nxp::Command command = static_cast(commandInt); Nxp::Status status = static_cast(statusInt); qCDebug(dcZigbeeController()) << "Interface response received" << command << "SQN:" << sequenceNumber << status << ZigbeeUtils::convertByteArrayToHexString(data); - if (m_pendingReplies.keys().contains(sequenceNumber)) { - ZigbeeInterfaceNxpReply * reply = m_pendingReplies.take(sequenceNumber); - if (reply->command() == command) { - reply->m_status = status; - reply->m_responseData = data; + if (m_currentReply->sequenceNumber() == sequenceNumber) { + if (m_currentReply->command() == command) { + m_currentReply->m_status = status; + m_currentReply->m_responseData = data; } else { - qCWarning(dcZigbeeController()) << "Received interface response for a pending sequence number but the command does not match the request." << command << reply->command(); + qCWarning(dcZigbeeController()) << "Received interface response for a pending sequence number but the command does not match the request." << command << m_currentReply->command(); } - reply->setFinished(); + m_currentReply->setFinished(); } else { qCWarning(dcZigbeeController()) << "Received a response for a non pending reply. There is no pending reply for command" << command << "SQN:" << sequenceNumber; } } } +void ZigbeeBridgeControllerNxp::sendNextRequest() +{ + // Check if there is a reply request to send + if (m_replyQueue.isEmpty()) + return; + + // Check if there is currently a running reply + if (m_currentReply) + return; + + // Send next message + m_currentReply = m_replyQueue.dequeue(); + qCDebug(dcZigbeeController()) << "Send request" << m_currentReply; + m_interface->sendPackage(m_currentReply->requestData()); + m_currentReply->m_timer->start(); +} + bool ZigbeeBridgeControllerNxp::enable(const QString &serialPort, qint32 baudrate) { return m_interface->enable(serialPort, baudrate); diff --git a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h index d5ab5de..e3baa7e 100644 --- a/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h +++ b/libnymea-zigbee/backends/nxp/zigbeebridgecontrollernxp.h @@ -17,6 +17,9 @@ class ZigbeeBridgeControllerNxp : public ZigbeeBridgeController { Q_OBJECT + + friend class ZigbeeNetworkNxp; + public: explicit ZigbeeBridgeControllerNxp(QObject *parent = nullptr); ~ZigbeeBridgeControllerNxp() override; @@ -36,6 +39,9 @@ public: ZigbeeInterfaceNxpReply *requestVersion(); ZigbeeInterfaceNxpReply *requestControllerState(); ZigbeeInterfaceNxpReply *requestSoftResetController(); + ZigbeeInterfaceNxpReply *requestFactoryResetController(); + ZigbeeInterfaceNxpReply *requestSetPanId(quint64 panId); + ZigbeeInterfaceNxpReply *requestSetChannelMask(quint32 channelMask); signals: void controllerStateChanged(ControllerState controllerState); @@ -46,13 +52,18 @@ private: ControllerState m_controllerState = ControllerStateNotRunning; quint8 m_sequenceNumber = 0; - QHash m_pendingReplies; + ZigbeeInterfaceNxpReply *m_currentReply = nullptr; + QQueue m_replyQueue; ZigbeeInterfaceNxpReply *createReply(Nxp::Command command, quint8 sequenceNumber, const QString &requestName, const QByteArray &requestData, QObject *parent); + void bumpSequenceNumber(); + private slots: void onInterfaceAvailableChanged(bool available); void onInterfacePackageReceived(const QByteArray &package); + void sendNextRequest(); + public slots: bool enable(const QString &serialPort, qint32 baudrate); void disable(); diff --git a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp index 476c10f..5b217ca 100644 --- a/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp +++ b/libnymea-zigbee/backends/nxp/zigbeenetworknxp.cpp @@ -2,6 +2,8 @@ #include "loggingcategory.h" #include "zigbeeutils.h" +#include + ZigbeeNetworkNxp::ZigbeeNetworkNxp(QObject *parent) : ZigbeeNetwork(parent) { @@ -39,7 +41,7 @@ void ZigbeeNetworkNxp::onControllerAvailableChanged(bool available) qCDebug(dcZigbeeNetwork()) << "Controller is" << (available ? "now available" : "not available any more"); if (available) { - reset(); + reset(); } } @@ -49,8 +51,19 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr case ZigbeeBridgeControllerNxp::ControllerStateRunning: { qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); - connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); + QByteArray payload = reply->responseData(); + QDataStream stream(&payload, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 major = 0; quint8 minor = 0; quint8 patch = 0; quint16 sdkVersion = 0; + stream >> major >> minor >> patch >> sdkVersion; + + QString versionString = QString ("%1.%2.%3 - %4").arg(major).arg(minor).arg(patch).arg(sdkVersion); + qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; + m_controller->setFirmwareVersion(versionString); + + // We are done here... }); break; @@ -62,8 +75,30 @@ void ZigbeeNetworkNxp::onControllerStateChanged(ZigbeeBridgeControllerNxp::Contr case ZigbeeBridgeControllerNxp::ControllerStateRunningUninitialized: { qCDebug(dcZigbeeNetwork()) << "Request controller version"; ZigbeeInterfaceNxpReply *reply = m_controller->requestVersion(); - connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ qCDebug(dcZigbeeNetwork()) << "Version reply finished" << reply->status(); + QByteArray payload = reply->responseData(); + QDataStream stream(&payload, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + quint8 major = 0; quint8 minor = 0; quint8 patch = 0; quint16 sdkVersion = 0; + stream >> major >> minor >> patch >> sdkVersion; + + QString versionString = QString ("%1.%2.%3 - %4").arg(major).arg(minor).arg(patch).arg(sdkVersion); + qCDebug(dcZigbeeNetwork()) << "Controller version" << versionString; + m_controller->setFirmwareVersion(versionString); + + qCDebug(dcZigbeeNetwork()) << "Set pan id" << ZigbeeUtils::convertUint64ToHexString(extendedPanId()) << extendedPanId(); + ZigbeeInterfaceNxpReply *reply = m_controller->requestSetPanId(extendedPanId()); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [this, reply](){ + qCDebug(dcZigbeeNetwork()) << "Set PAN ID reply finished" << reply->status(); + + qCDebug(dcZigbeeNetwork()) << "Set channel mask" << channelMask() << ZigbeeUtils::convertUint32ToHexString(channelMask().toUInt32()) << channelMask().toUInt32(); + ZigbeeInterfaceNxpReply *reply = m_controller->requestSetChannelMask(channelMask().toUInt32()); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [reply](){ + qCDebug(dcZigbeeNetwork()) << "Set channel mask reply finished" << reply->status(); + + }); + }); }); break; } @@ -110,5 +145,8 @@ void ZigbeeNetworkNxp::reset() void ZigbeeNetworkNxp::factoryResetNetwork() { - + ZigbeeInterfaceNxpReply *reply = m_controller->requestFactoryResetController(); + connect(reply, &ZigbeeInterfaceNxpReply::finished, this, [](){ + qCDebug(dcZigbeeNetwork()) << "Factory reset reply finished"; + }); } diff --git a/zigbee-cli/main.cpp b/zigbee-cli/main.cpp index 0f889f1..5fc2794 100644 --- a/zigbee-cli/main.cpp +++ b/zigbee-cli/main.cpp @@ -168,6 +168,10 @@ int main(int argc, char *argv[]) network->setSerialPortName(parser.value(serialOption)); network->setSerialBaudrate(baudrate); network->setSettingsFileName("/tmp/zigbee.conf"); + network->setExtendedPanId(5); + ZigbeeChannelMask mask; + mask.setChannel(Zigbee::ZigbeeChannel13); + network->setChannelMask(mask); network->startNetwork(); //Core core(parser.value(serialOption), baudrate, channel);