Skip to content

Commit

Permalink
Added simplemotion UI
Browse files Browse the repository at this point in the history
  • Loading branch information
Ultrawipf committed Jan 20, 2023
1 parent 8098a14 commit 2400da1
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 4 deletions.
3 changes: 1 addition & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
### Changes this version:
- Added an emergency stop indication to the bottom ffb rate status bar (Error/Stop symbol + Text)
- Fixed a possible crash when changing certain effect gains in newer Qt6 versions
- Added support for Simplemotion V2 (Ioni/Argon motor drivers)
12 changes: 10 additions & 2 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@
import effects_monitor
import effects_graph_ui
import updater
import simplemotion_ui

# This GUIs version
VERSION = "1.11.1"
VERSION = "1.12.0"

# Minimal supported firmware version.
# Major version of firmware must match firmware. Minor versions must be higher or equal
MIN_FW = "1.11.0"
MIN_FW = "1.12.0"

class MainUi(PyQt6.QtWidgets.QMainWindow, base_ui.WidgetUI, base_ui.CommunicationHandler):
"""Display and manage the main UI."""
Expand Down Expand Up @@ -415,6 +416,13 @@ def update_tabs_cb(active):
self.active_classes[name] = classe
self.add_tab(classe, name_axis)
self.profile_ui.set_save_btn(True)
elif classe_active["id"] == 0x89 or classe_active["id"] == 0x8A:
classe = simplemotion_ui.SimplemotionUI(main=self, unique=classe_active["unique"])
name_axis = classe_active["name"]
self.active_classes[name] = classe
self.add_tab(classe, name_axis)
self.profile_ui.set_save_btn(True)


self.tabsinitialized.emit(True)

Expand Down
222 changes: 222 additions & 0 deletions res/simplemotion.ui
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>689</width>
<height>536</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="4" column="0" colspan="2">
<widget class="QGroupBox" name="groupBox_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<verstretch>1</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Help</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTextBrowser" name="textBrowser">
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-size:10pt; font-weight:600; text-decoration: underline;&quot;&gt;Info:&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This mode supports Granite Ioni (and likely Argon) devices set to address 0 via RS485 (requires external transceiver) connected to the motor uart.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Due to the limited bandwidth it is recommended to not have this tab active when FFB is used.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;F407 pinout:&lt;br /&gt;* gp7 = TX (RO)&lt;br /&gt;* gp8 = RX (DI)&lt;br /&gt;* DRV_GP = transceiver read/!write (DE/RE)&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The driver must be set into torque mode and enabled and the hardware enable and STO pins have to be set accordingly.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;This support class is experimental and may not work perfectly but was tested successfully with an IONI + BLDC + ABN encoder.&lt;/p&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Check the &lt;a href=&quot;https://github.com/Ultrawipf/OpenFFBoard/wiki&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;OpenFFBoard wiki&lt;/span&gt;&lt;/a&gt; and &lt;a href=&quot;https://granitedevices.com/wiki/Main_Page&quot;&gt;&lt;span style=&quot; font-size:8pt; text-decoration: underline; color:#0000ff;&quot;&gt;Granite wiki&lt;/span&gt;&lt;/a&gt; pages for how to connect and set up the motor driver.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="1">
<widget class="QGroupBox" name="groupBox_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>2</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Info</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Torque:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>Voltage:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_voltage">
<property name="enabled">
<bool>true</bool>
</property>
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<property name="frame">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="suffix">
<string>V</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_torque">
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
<property name="keyboardTracking">
<bool>false</bool>
</property>
<property name="suffix">
<string>A</string>
</property>
<property name="minimum">
<double>-99.000000000000000</double>
</property>
</widget>
</item>
<item row="3" column="0" colspan="2">
<widget class="QLabel" name="label_comm_errors">
<property name="text">
<string>No communication errors</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Simplemotion</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>Driver status flags:</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QPushButton" name="pushButton_restart">
<property name="text">
<string>Restart driver</string>
</property>
</widget>
</item>
<item row="0" column="1" rowspan="2">
<widget class="QLabel" name="label_state">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="text">
<string>0</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="textInteractionFlags">
<set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
80 changes: 80 additions & 0 deletions simplemotion_ui.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
from PyQt6.QtCore import QTimer
import main
from base_ui import WidgetUI
from base_ui import CommunicationHandler

class SimplemotionUI(WidgetUI,CommunicationHandler):
SM_STATES = ["RESERVED","TARGET_REACHED","FERROR_RECOVERY","RUNNING","ENABLED","<font color='red'>FAULTSTOP</font>","<font color='orange'>FERROR_WARNING</font>","<font color='red'>STO_ACTIVE</font>","SERVO_READY","BRAKING","HOMING","AXIS_STATE_HOMING","INITIALIZED","VOLTAGES_OK","<font color='red'>PERMANENT_STOP</font>"]

def __init__(self, main=None, unique=None):
WidgetUI.__init__(self, main,'simplemotion.ui')
CommunicationHandler.__init__(self)
self.main = main #type: main.MainUi
self.prefix = unique
self.crcerr = 0
self.uarterr = 0
self.register_callback("sm2","state",self.stateCb,self.prefix,int)
self.register_callback("sm2","voltage",lambda v: self.doubleSpinBox_voltage.setValue(v/1000),self.prefix,int)
self.register_callback("sm2","torque",lambda v: self.doubleSpinBox_torque.setValue(v/1000),self.prefix,int)
self.register_callback("sm2","crcerr",self.crcErrCb,self.prefix,int)
self.register_callback("sm2","uarterr",self.uartErrCb,self.prefix,int)
self.pushButton_restart.clicked.connect(self.restart)

self.timer = QTimer(self)
self.timer.timeout.connect(self.updateTimer)
self.init_ui()

# Tab is currently shown
def showEvent(self,event):
self.init_ui()
self.timer.start(1000)

# Tab is hidden
def hideEvent(self,event):
self.timer.stop()

def init_ui(self):
commands = ["state","voltage","torque"]
self.send_commands("sm2",commands,self.prefix)

def stateCb(self,codes):
if not codes:
self.label_state.setText("Not connected")
return
states = []

for i,name in enumerate(self.SM_STATES):
if(codes & 1<<i != 0):
states.append(name)
if len(states) == 0:
states = [str(states)]
statstr = "<br>".join(states)

self.label_state.setText(statstr)

def crcErrCb(self,v):
self.crcerr = v

def uartErrCb(self,v):
self.uarterr = v
self.updateErrText()

def restart(self):
self.send_command("sm2","restart",self.prefix)


def updateErrText(self):
if self.crcerr == 0 and self.uarterr == 0:
self.label_comm_errors.setText("No communication errors")
return
text = "Errors: "
if self.crcerr:
text+=f"CRC: {self.crcerr} "
if self.uarterr:
text+=f"UART: {self.uarterr} "
self.label_comm_errors.setText(text)


def updateTimer(self):
commands = ["state","crcerr","voltage","uarterr","torque"]
self.send_commands("sm2",commands,self.prefix)

0 comments on commit 2400da1

Please sign in to comment.