Fixes reconnecting the ZigBee controller after a reset during runtime

Resets may occur by unplugging the stick or calling a softReset() from
the software side.

Additionally resets the stick when 5 subsequent timeouts happen
as there are firmwares of the CC2652 available which tend to
crash occationally.
pull/79/head
Michael Zanetti 2022-12-08 23:45:26 +01:00
parent 0f18424d6d
commit 22837865f1
6 changed files with 44 additions and 18 deletions

View File

@ -99,6 +99,7 @@ void ZigbeeInterfaceTi::setAvailable(bool available)
void ZigbeeInterfaceTi::onReconnectTimeout()
{
qCDebug(dcZigbeeInterface()) << "Reconnecting to serial port...";
if (m_serialPort && !m_serialPort->isOpen()) {
if (!m_serialPort->open(QSerialPort::ReadWrite)) {
setAvailable(false);
@ -169,9 +170,7 @@ void ZigbeeInterfaceTi::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);
reconnectController();
}
}
@ -207,7 +206,7 @@ void ZigbeeInterfaceTi::sendPacket(Ti::CommandType type, Ti::SubSystem subSystem
bool ZigbeeInterfaceTi::enable(const QString &serialPort, qint32 baudrate)
{
qCDebug(dcZigbeeInterface()) << "Start UART interface " << serialPort << baudrate;
qCDebug(dcZigbeeInterface()) << "Starting UART interface " << serialPort << baudrate;
if (m_serialPort) {
delete m_serialPort;
@ -243,13 +242,18 @@ void ZigbeeInterfaceTi::reconnectController()
if (!m_serialPort)
return;
if (m_serialPort->isOpen())
if (m_serialPort->isOpen()) {
m_serialPort->close();
}
QString portName = m_serialPort->portName();
int baudrate = m_serialPort->baudRate();
setAvailable(false);
delete m_serialPort;
m_serialPort = nullptr;
setAvailable(false);
m_reconnectTimer->start();
enable(portName, baudrate);
}
void ZigbeeInterfaceTi::disable()

View File

@ -52,7 +52,7 @@ Ti::StatusCode ZigbeeInterfaceTiReply::statusCode() const
return m_statusCode;
}
bool ZigbeeInterfaceTiReply::timendOut() const
bool ZigbeeInterfaceTiReply::timedOut() const
{
return m_timeout;
}

View File

@ -54,7 +54,7 @@ public:
// Response content
Ti::StatusCode statusCode() const;
bool timendOut() const;
bool timedOut() const;
bool aborted() const;
void abort();

View File

@ -72,14 +72,22 @@ ZigbeeInterfaceTiReply* ZigbeeBridgeControllerTi::reset()
stream << static_cast<quint8>(Ti::ResetTypeSoft);
ZigbeeInterfaceTiReply *resetReply = sendCommand(Ti::SubSystemSys, Ti::SYSCommandResetReq, payload);
waitFor(resetReply, Ti::SubSystemSys, Ti::SYSCommandResetInd);
connect(resetReply, &ZigbeeInterfaceTiReply::finished, this, [=](){
m_interface->reconnectController();
});
return resetReply;
}
ZigbeeInterfaceTiReply *ZigbeeBridgeControllerTi::init()
{
m_registeredEndpointIds.clear();
ZigbeeInterfaceTiReply *initReply = new ZigbeeInterfaceTiReply(this, 15000);
ZigbeeInterfaceTiReply *resetReply = reset();
// Not using public reset() as that will start the init from scratch by reconnecting the controller
NEW_PAYLOAD
stream << static_cast<quint8>(Ti::ResetTypeSoft);
ZigbeeInterfaceTiReply *resetReply = sendCommand(Ti::SubSystemSys, Ti::SYSCommandResetReq, payload);
connect(resetReply, &ZigbeeInterfaceTiReply::finished, initReply, [=]() {
qCDebug(dcZigbeeController()) << "Skipping CC2530/CC2531 bootloader.";
@ -486,17 +494,25 @@ void ZigbeeBridgeControllerTi::sendNextRequest()
ZigbeeInterfaceTiReply *ZigbeeBridgeControllerTi::sendCommand(Ti::SubSystem subSystem, quint8 command, const QByteArray &payload, int timeout)
{
// Create the reply
ZigbeeInterfaceTiReply *reply = new ZigbeeInterfaceTiReply(subSystem, command, this, payload, timeout);
// Make sure we clean up on timeout
connect(reply, &ZigbeeInterfaceTiReply::timeout, this, [reply](){
qCWarning(dcZigbeeController()) << "Reply timeout" << reply;
// Note: send next reply with the finished signal
});
connect(reply, &ZigbeeInterfaceTiReply::finished, reply, [=](){
if (reply->timedOut()) {
if (m_controllerState == ControllerStateRunning) {
qCWarning(dcZigbeeController()) << "Interface command timed out.";
if (++m_timeouts < 5) {
qCInfo(dcZigbeeController()) << "Retrying..." << m_timeouts << "/" << 5;
sendCommand(subSystem, command, payload, timeout);
} else {
qCInfo(dcZigbeeController()) << "Resetting ZigBee interface";
m_interface->reconnectController();
}
}
} else {
m_timeouts = 0;
}
// Auto delete the object on finished
connect(reply, &ZigbeeInterfaceTiReply::finished, reply, [this, reply](){
if (m_currentReply == reply) {
m_currentReply = nullptr;
QMetaObject::invokeMethod(this, "sendNextRequest", Qt::QueuedConnection);
@ -731,6 +747,9 @@ void ZigbeeBridgeControllerTi::onInterfaceAvailableChanged(bool available)
ZigbeeInterfaceTiReply *reply = m_replyQueue.dequeue();
reply->abort();
}
m_controllerState = ControllerStateDown;
emit controllerStateChanged(m_controllerState);
}
setAvailable(available);

View File

@ -144,6 +144,7 @@ private:
QTimer m_permitJoinTimer;
QList<int> m_registeredEndpointIds;
int m_timeouts = 0;
void finishRequest(Ti::StatusCode statusCode = Ti::StatusCodeSuccess);
};

View File

@ -206,6 +206,7 @@ ZigbeeClusterReply *ZigbeeCluster::createClusterReply(const ZigbeeNetworkRequest
zclReply->m_transactionSequenceNumber = frame.header.transactionSequenceNumber;
m_pendingReplies.insert(zclReply->transactionSequenceNumber(), zclReply);
connect(zclReply, &ZigbeeClusterReply::finished, this, [this, zclReply](){
qCDebug(dcZigbeeCluster()) << "ZCL request to" << zclReply->request().destinationShortAddress() << "finished with status:" << zclReply->error();
zclReply->deleteLater();
m_pendingReplies.remove(zclReply->transactionSequenceNumber());
});
@ -367,6 +368,7 @@ bool ZigbeeCluster::verifyNetworkError(ZigbeeClusterReply *zclReply, ZigbeeNetwo
case ZigbeeNetworkReply::ErrorNoError:
// The request has been transported successfully to he destination, now
// wait for the expected indication or check if we already recieved it
qCDebug(dcZigbeeCluster()) << "ZCL request sent. Waiting for response data indication...";
zclReply->m_apsConfirmReceived = true;
if (!zclReply->m_zclIndicationReceived) {
zclReply->m_timeoutTimer.start();