diff --git a/commands.cpp b/commands.cpp index 45d79c68b..f176f9f78 100755 --- a/commands.cpp +++ b/commands.cpp @@ -284,6 +284,7 @@ void Commands::processPacket(QByteArray data) quint8 status = vb.vbPopFrontUint8(); values.has_timeout = status & 1; values.kill_sw_active = (status >> 1) & 1; + values.motor_disabled = (status >> 2) & 1; } } @@ -1509,6 +1510,14 @@ void Commands::shutdown() emitData(vb); } +void Commands::disableMotor(bool disable) +{ + VByteArray vb; + vb.vbAppendInt8(COMM_DISABLE); + vb.vbAppendInt8(disable ? 1 : 0); + emitData(vb); +} + void Commands::sendAlive() { VByteArray vb; diff --git a/commands.h b/commands.h index ff21cb23a..6af9ec71c 100644 --- a/commands.h +++ b/commands.h @@ -204,6 +204,7 @@ public slots: void detectMotorParam(double current, double min_rpm, double low_duty); void reboot(); void shutdown(); + void disableMotor(bool disable); void sendAlive(); void getDecodedPpm(); void getDecodedAdc(); diff --git a/datatypes.h b/datatypes.h index 97064fcb0..62b4e1221 100644 --- a/datatypes.h +++ b/datatypes.h @@ -188,6 +188,7 @@ struct MC_VALUES { Q_PROPERTY(double vq MEMBER vq) Q_PROPERTY(bool has_timeout MEMBER has_timeout) Q_PROPERTY(bool kill_sw_active MEMBER kill_sw_active) + Q_PROPERTY(bool motor_disabled MEMBER motor_disabled) public: MC_VALUES() { @@ -216,6 +217,7 @@ struct MC_VALUES { vq = 0.0; has_timeout = false; kill_sw_active = false; + motor_disabled = false; } bool operator==(const MC_VALUES &other) const { @@ -254,6 +256,7 @@ struct MC_VALUES { double vq; bool has_timeout; bool kill_sw_active; + bool motor_disabled; }; Q_DECLARE_METATYPE(MC_VALUES) @@ -1038,6 +1041,8 @@ typedef enum { COMM_CAN_UPDATE_BAUD_ALL = 158, COMM_MOTOR_ESTOP = 159, + + COMM_DISABLE = 160, } COMM_PACKET_ID; // CAN commands diff --git a/mainwindow.cpp b/mainwindow.cpp index 6a9f762e7..1b00d0f25 100755 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -220,6 +220,7 @@ MainWindow::MainWindow(QWidget *parent) : mPreferences->setVesc(mVesc); mStatusInfoTime = 0; + mMotorDisabledNow = false; mStatusLabel = new QLabel(this); ui->statusBar->addPermanentWidget(mStatusLabel); mDebugTimer = new QTimer(this); @@ -796,8 +797,11 @@ void MainWindow::timerSlot() } } else { QString str = mVesc->getConnectedPortName(); + if (mMotorDisabledNow && mVesc->isPortConnected()) { + str += tr(" | MOTOR DISABLED"); + } if (str != mStatusLabel->text()) { - mStatusLabel->setText(mVesc->getConnectedPortName()); + mStatusLabel->setText(str); static QString statusLast = ""; if (str != statusLast) { mPageDebugPrint->printConsole("Status: " + str + "
"); @@ -1075,6 +1079,7 @@ void MainWindow::valuesReceived(MC_VALUES values, unsigned int mask) (void)mask; ui->dispCurrent->setVal(values.current_motor); ui->dispDuty->setVal(values.duty_now * 100.0); + mMotorDisabledNow = values.motor_disabled; } void MainWindow::paramChangedDouble(QObject *src, QString name, double newParam) @@ -1118,6 +1123,25 @@ void MainWindow::on_actionShutdown_triggered() mVesc->commands()->shutdown(); } +void MainWindow::on_actionMotorDisable_triggered() +{ + QMessageBox::StandardButton answer = QMessageBox::question( + this, + tr("Disable Motor"), + tr("This will disable the motor until it is enabled again. Continue?"), + QMessageBox::Yes | QMessageBox::Cancel + ); + + if (answer == QMessageBox::Yes) { + mVesc->commands()->disableMotor(true); + } +} + +void MainWindow::on_actionMotorEnable_triggered() +{ + mVesc->commands()->disableMotor(false); +} + void MainWindow::on_stopButton_clicked() { mVesc->commands()->setCurrent(0); diff --git a/mainwindow.h b/mainwindow.h index efd3c8e15..64f0f60a6 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -104,6 +104,8 @@ private slots: void on_actionDisconnect_triggered(); void on_actionReboot_triggered(); void on_actionShutdown_triggered(); + void on_actionMotorDisable_triggered(); + void on_actionMotorEnable_triggered(); void on_stopButton_clicked(); void on_fullBrakeButton_clicked(); void on_actionReadMcconf_triggered(); @@ -177,6 +179,7 @@ private slots: QTimer *mTimer; QLabel *mStatusLabel; int mStatusInfoTime; + bool mMotorDisabledNow; bool mKeyLeft; bool mKeyRight; bool mMcConfRead; diff --git a/mainwindow.ui b/mainwindow.ui index 3478322d9..dccc71b60 100644 --- a/mainwindow.ui +++ b/mainwindow.ui @@ -673,6 +673,10 @@ + + + + @@ -771,6 +775,22 @@ Shutdown (for controllers with auto-shutdown support only) + + + Disable Motor + + + Disable the motor persistently (survives reboots). Only works while the motor is stationary. + + + + + Enable Motor + + + Re-enable the motor after Disable Motor + + true diff --git a/mobile/RtData.qml b/mobile/RtData.qml index ae0ea93c3..3cf250cd1 100644 --- a/mobile/RtData.qml +++ b/mobile/RtData.qml @@ -226,7 +226,8 @@ Item { "Wh Draw : " + parseFloat(values.watt_hours).toFixed(2) + " Wh\n" + "Wh Charge : " + parseFloat(values.watt_hours_charged).toFixed(2) + " Wh\n" + "ABS Tacho : " + values.tachometer_abs + " Counts\n" + - "Fault : " + values.fault_str + "Fault : " + values.fault_str + + (values.motor_disabled ? "\nDisabled : TRUE" : "") } } } diff --git a/mobile/Terminal.qml b/mobile/Terminal.qml index fd8d1a665..342700d5f 100644 --- a/mobile/Terminal.qml +++ b/mobile/Terminal.qml @@ -118,6 +118,18 @@ Item { mCommands.shutdown() } } + MenuItem { + text: "Disable Motor" + onTriggered: { + motorDisableDialog.open() + } + } + MenuItem { + text: "Enable Motor" + onTriggered: { + mCommands.disableMotor(false) + } + } MenuItem { text: "Restart LispBM" onTriggered: { @@ -125,6 +137,38 @@ Item { } } } + + Dialog { + id: motorDisableDialog + standardButtons: Dialog.Yes | Dialog.Cancel + modal: true + focus: true + width: parent.width - 20 + height: 200 + closePolicy: Popup.CloseOnEscape + title: "Disable Motor" + x: 10 + y: 10 + parent.height / 2 - height / 2 + parent: terminalPageItem + + Overlay.modal: Rectangle { + color: "#AA000000" + } + + Text { + color: Utility.getAppHexColor("lightText") + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + wrapMode: Text.WordWrap + text: "This is going to disable the motor until it is enabled again, " + + "even after reboot. The command is ignored while the motor is " + + "spinning. Are you sure?" + } + + onAccepted: { + mCommands.disableMotor(true) + } + } } } diff --git a/mobile/main.qml b/mobile/main.qml index a9e7afc62..244a6701a 100644 --- a/mobile/main.qml +++ b/mobile/main.qml @@ -32,6 +32,7 @@ import Vedder.vesc.vesc3ditem 1.0 ApplicationWindow { id: appWindow property Commands mCommands: VescIf.commands() + property bool motorDisabledNow: false property ConfigParams mMcConf: VescIf.mcConfig() property ConfigParams mAppConf: VescIf.appConfig() property ConfigParams mInfoConf: VescIf.infoConfig() @@ -820,8 +821,12 @@ ApplicationWindow { running: true repeat: true onTriggered: { - if (!statusTimer.running && connectedText.text !== VescIf.getConnectedPortName()) { - connectedText.text = VescIf.getConnectedPortName() + var statusStr = VescIf.getConnectedPortName() + if (appWindow.motorDisabledNow && VescIf.isPortConnected()) { + statusStr += " | MOTOR DISABLED" + } + if (!statusTimer.running && connectedText.text !== statusStr) { + connectedText.text = statusStr } } } @@ -1183,6 +1188,10 @@ ApplicationWindow { Connections { target: mCommands + function onValuesReceived(values, mask) { + appWindow.motorDisabledNow = values.motor_disabled + } + function onValuesImuReceived(values, mask) { if (tabBar.currentIndex == (1 + indexOffset()) && rtSwipeView.currentIndex == 2) { vesc3dLoader.item.setRotation(values.roll, values.pitch, diff --git a/widgets/rtdatatext.cpp b/widgets/rtdatatext.cpp index 3dbb69bfb..338ef3aeb 100644 --- a/widgets/rtdatatext.cpp +++ b/widgets/rtdatatext.cpp @@ -82,6 +82,7 @@ void RtDataText::paintEvent(QPaintEvent *event) "T\n" "T\n" "T\n" + "T\n" "T\n"); int boxh_new = br.height(); @@ -125,14 +126,20 @@ void RtDataText::paintEvent(QPaintEvent *event) Qt::AlignLeft, str); // Middle info box + bool fault_active = mValues.fault_code != FAULT_CODE_NONE; + + // The fault value and the TRUE of the disabled row are left out here and + // drawn separately in red below. str = QString::asprintf("T FET : %.2f \u00B0C\n" "T Motor : %.2f \u00B0C\n" "Fault : %s\n" + "%s" "Tac : %i\n" "Tac ABS : %i\n", mValues.temp_mos, mValues.temp_motor, - mValues.fault_str.toLocal8Bit().data(), + fault_active ? "" : mValues.fault_str.toLocal8Bit().data(), + mValues.motor_disabled ? "Disabled: \n" : "", mValues.tachometer, mValues.tachometer_abs); @@ -140,10 +147,31 @@ void RtDataText::paintEvent(QPaintEvent *event) painter.fillRect(vidw / 2.0 - bbox_w / 2.0, 0, bbox_w, bbow_h, Utility::getAppQColor("normalBackground")); painter.setOpacity(1.0); + const double xmid = vidw / 2.0 - bbox_w / 2.0 + mTxtOfs; + painter.setPen(Utility::getAppQColor("normalText")); - painter.drawText(QRectF(vidw / 2.0 - bbox_w / 2.0 + mTxtOfs, mTxtOfs, mBoxW, mBoxH), + painter.drawText(QRectF(xmid, mTxtOfs, mBoxW, mBoxH), Qt::AlignLeft, str); + if (fault_active || mValues.motor_disabled) { + QFontMetrics fm = painter.fontMetrics(); + painter.setPen(Utility::getAppQColor("red")); + + if (fault_active) { + painter.drawText(QRectF(xmid + fm.horizontalAdvance("Fault : "), + mTxtOfs + 2 * fm.lineSpacing(), mBoxW, mBoxH), + Qt::AlignLeft, mValues.fault_str); + } + + if (mValues.motor_disabled) { + painter.drawText(QRectF(xmid + fm.horizontalAdvance("Disabled: "), + mTxtOfs + 3 * fm.lineSpacing(), mBoxW, mBoxH), + Qt::AlignLeft, "TRUE"); + } + + painter.setPen(Utility::getAppQColor("normalText")); + } + // Right info box str = QString::asprintf("Ah Draw : %.1f mAh\n" "Ah Charge : %.1f mAh\n"