Skip to content

Commit

Permalink
Merge pull request #1715 from crocsaint/master
Browse files Browse the repository at this point in the history
basic phidget stepper motor support for slider and buttons
  • Loading branch information
MAKOMO authored Oct 18, 2024
2 parents 899462a + 254df7e commit c139b69
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 3 deletions.
2 changes: 2 additions & 0 deletions src/artisanlib/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -12548,6 +12548,8 @@ def closePhidgetOUTPUTs(self) -> None:
self.aw.ser.phidgetDCMotorClose()
# close Phidget RC Servos
self.aw.ser.phidgetRCclose()
# close Phidget Stepper Motors
self.aw.ser.phidgetStepperClose()
# close Yocto Voltage Outputs
self.aw.ser.yoctoVOUTclose()
# close Yocto Current Outputs
Expand Down
89 changes: 88 additions & 1 deletion src/artisanlib/comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
from Phidget22.Devices.DigitalOutput import DigitalOutput # type: ignore # @UnusedWildImport
from Phidget22.Devices.VoltageOutput import VoltageOutput, VoltageOutputRange # type: ignore # @UnusedWildImport
from Phidget22.Devices.RCServo import RCServo # type: ignore # @UnusedWildImport
from Phidget22.Devices.Stepper import Stepper # type: ignore # @UnusedWildImport
from Phidget22.Devices.CurrentInput import CurrentInput # type: ignore # @UnusedWildImport
from Phidget22.Devices.FrequencyCounter import FrequencyCounter # type: ignore # @UnusedWildImport
from Phidget22.Devices.DCMotor import DCMotor # type: ignore # @UnusedWildImport
Expand Down Expand Up @@ -243,7 +244,7 @@ class serialport:
'Phidget1045semaphore','PhidgetBridgeSensor','Phidget1046values','Phidget1046lastvalues','Phidget1046semaphores',\
'PhidgetIO','PhidgetIOvalues','PhidgetIOlastvalues','PhidgetIOsemaphores','PhidgetDigitalOut',\
'PhidgetDigitalOutLastPWM','PhidgetDigitalOutLastToggle','PhidgetDigitalOutHub','PhidgetDigitalOutLastPWMhub',\
'PhidgetDigitalOutLastToggleHub','PhidgetAnalogOut','PhidgetDCMotor','PhidgetRCServo',\
'PhidgetDigitalOutLastToggleHub','PhidgetAnalogOut','PhidgetDCMotor','PhidgetRCServo','PhidgetStepperMotor',\
'YOCTOlibImported','YOCTOsensor','YOCTOchan1','YOCTOchan2','YOCTOtempIRavg','YOCTOvalues','YOCTOlastvalues','YOCTOsemaphores',\
'YOCTOthread','YOCTOvoltageOutputs','YOCTOcurrentOutputs','YOCTOrelays','YOCTOservos','YOCTOpwmOutputs','HH506RAid','MS6514PrevTemp1','MS6514PrevTemp2','DT301PrevTemp','EXTECH755PrevTemp',\
'controlETpid','readBTpid','useModbusPort','showFujiLCDs','arduinoETChannel','arduinoBTChannel','arduinoATChannel',\
Expand Down Expand Up @@ -304,6 +305,8 @@ def __init__(self, aw:'ApplicationWindow') -> None:
self.PhidgetAnalogOut:Dict[Optional[str], List[Phidget]] = {} # type:ignore[no-any-unimported,unused-ignore] # a dict associating serials with lists of channels
#store the servo objects
self.PhidgetRCServo:Dict[Optional[str], List[Phidget]] = {} # type:ignore[no-any-unimported,unused-ignore] # a dict associating serials with lists of channels
#store the Phidget StepperMotor objects
self.PhidgetStepperMotor:Dict[Optional[str], List[Phidget]] = {} # type:ignore[no-any-unimported,unused-ignore] # a dict associating serials with lists of channels
#store the Phidget DCMotor objects
self.PhidgetDCMotor:Dict[Optional[str], List[Phidget]] = {} # type:ignore[no-any-unimported,unused-ignore] # a dict associating serials with lists of channels
# Phidget Ambient Sensor Channels
Expand Down Expand Up @@ -5229,6 +5232,90 @@ def yoctoSERVOclose(self) -> None:
except Exception as e: # pylint: disable=broad-except
_log.exception(e)

#-- Phidget Stepper Motor (only one supported for now)
# - Phidget STC1005: 1 channels (control of one bipolar stepper motor)
# commands:
# engaged(ch,state[,sn]) # engage channel
# set(ch,pos[,sn]) # sets position
# rescale(ch,rf,[,sn]) # sets rescalefactor

def phidgetStepperAttach(self, channel:int, serial:Optional[str]=None) -> None:
if serial not in self.aw.ser.PhidgetStepperMotor:
if self.aw.qmc.phidgetManager is None:
self.aw.qmc.startPhidgetManager()
if self.aw.qmc.phidgetManager is not None:
s,p = self.serialString2serialPort(serial)
# try to attach a Phidget STC1005 module
ser,port = self.aw.qmc.phidgetManager.getFirstMatchingPhidget('PhidgetStepper',DeviceID.PHIDID_STC1005,
remote=self.aw.qmc.phidgetRemoteFlag,remoteOnly=self.aw.qmc.phidgetRemoteOnlyFlag,serial=s,hubport=p)
ports = 1

if ser is not None:
self.aw.ser.PhidgetStepperMotor[serial] = []
for i in range(ports):
stepper = Stepper()
if port is not None:
stepper.setHubPort(port)
stepper.setDeviceSerialNumber(ser)
stepper.setChannel(i)
if self.aw.qmc.phidgetRemoteOnlyFlag and self.aw.qmc.phidgetRemoteFlag:
stepper.setIsRemote(True)
stepper.setIsLocal(False)
self.aw.ser.PhidgetStepperMotor[serial].append(stepper)
if serial is None:
# we make this also accessible via its serial number
self.aw.ser.PhidgetStepperMotor[str(ser)] = self.aw.ser.PhidgetStepperMotor[None]

try:
ch = self.aw.ser.PhidgetStepperMotor[serial][channel]
ch.setOnAttachHandler(self.phidgetOUTattached)
ch.setOnDetachHandler(self.phidgetOUTdetached)
if not ch.getAttached():
if self.aw.qmc.phidgetRemoteFlag:
ch.openWaitForAttachment(3000)
else:
ch.openWaitForAttachment(1500)
if serial is None and ch.getAttached():
# we make this also accessible via its serial number + port
si = self.serialPort2serialString(ch.getDeviceSerialNumber(),ch.getHubPort())
self.aw.ser.PhidgetStepperMotor[str(si)] = self.aw.ser.PhidgetStepperMotor[None]
except Exception: # pylint: disable=broad-except
pass

# sets rescalefactor
def phidgetStepperRescale(self, channel:int, value:int, serial:Optional[str]=None) -> None:
_log.debug('phidgetStepperRescale(%s,%s,%s,%s)',channel,value,serial)
self.phidgetStepperAttach(channel,serial)
if serial in self.aw.ser.PhidgetStepperMotor and len(self.aw.ser.PhidgetStepperMotor[serial])>channel:
self.aw.ser.PhidgetStepperMotor[serial][channel].setRescaleFactor(value)

# sets position
def phidgetStepperSet(self, channel:int, value:int, serial:Optional[str]=None) -> None:
_log.debug('phidgetStepperSet(%s,%s,%s,%s)',channel,value,serial)
self.phidgetStepperAttach(channel,serial)
if serial in self.aw.ser.PhidgetStepperMotor and len(self.aw.ser.PhidgetStepperMotor[serial])>channel:
self.aw.ser.PhidgetStepperMotor[serial][channel].setTargetPosition(value)

# engages channel
def phidgetStepperEngaged(self, channel:int, state:bool, serial:Optional[str]=None) -> None:
_log.debug('phidgetStepperEngaged(%s,%s,%s,%s)',channel,state,serial)
self.phidgetStepperAttach(channel,serial)
if serial in self.aw.ser.PhidgetStepperMotor and len(self.aw.ser.PhidgetStepperMotor[serial])>channel:
self.aw.ser.PhidgetStepperMotor[serial][channel].setEngaged(state)

def phidgetStepperClose(self) -> None:
_log.debug('phidgetStepperClose')
for c in self.aw.ser.PhidgetStepperMotor:
st = self.aw.ser.PhidgetStepperMotor[c]
for i, _ in enumerate(st):
try:
if st[i].getAttached():
st[i].setEngaged(False)
self.phidgetOUTdetached(st[i])
st[i].close()
except Exception: # pylint: disable=broad-except
pass
self.aw.ser.PhidgetStepperMotor = {}

#--- Phidget RC (only one supported for now)
# supporting up to 16 channels for the following RC Phidgets
Expand Down
6 changes: 4 additions & 2 deletions src/artisanlib/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,8 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', activeTab:int = 0) ->
QApplication.translate('ComboBox','Aillio R1 Command'),
QApplication.translate('ComboBox','Artisan Command'),
QApplication.translate('ComboBox','RC Command'),
QApplication.translate('ComboBox','WebSocket Command')]
QApplication.translate('ComboBox','WebSocket Command'),
QApplication.translate('ComboBox','Stepper Command')]
self.custom_button_actions_sorted:List[str] = sorted(self.custom_button_actions)

titlefont = QFont()
Expand Down Expand Up @@ -774,7 +775,8 @@ def __init__(self, parent:QWidget, aw:'ApplicationWindow', activeTab:int = 0) ->
QApplication.translate('ComboBox', 'Aillio R1 Drum'),
QApplication.translate('ComboBox', 'Artisan Command'),
QApplication.translate('ComboBox', 'RC Command'),
QApplication.translate('ComboBox', 'WebSocket Command')]
QApplication.translate('ComboBox', 'WebSocket Command'),
QApplication.translate('ComboBox', 'Stepper Command')]
self.sliderActionTypesSorted = sorted(self.sliderActionTypes)
self.E1action = QComboBox()
self.E1action.setToolTip(QApplication.translate('Tooltip', 'Action Type'))
Expand Down
51 changes: 51 additions & 0 deletions src/artisanlib/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10249,6 +10249,57 @@ def eventaction_internal(self, action:int, cmd:str, eventtype:Optional[int]) ->
else:
# command not recognized
_log.info('WebSocket Command <%s> not recognized', cs)
elif action == 23:
# PHIDGETS sn : has the form <hub_serial>[:<hub_port>], an optional serial number of the hub, optionally specifying the port number the module is connected to
## rescale(ch,rs,[,sn]) : sets the rescaleFactor
## engaged(ch,b[,sn]) : engage (b=1) or disengage (b = 0)
## set(ch,pos[,sn]) : set the target position
if cmd_str:
cmds = filter(None, cmd_str.split(';')) # allows for sequences of commands like in "<cmd>;<cmd>;...;<cmd>"
for c in cmds:
cs = c.strip()
# rescale(ch,val[,sn]) # sets rescaleFactor
if cs.startswith('rescale(') and len(cs) > 11:
try:
n = 2
cs_split = cs[len('rescale('):-1].split(',')
channel_str,value = cs_split[0:n]
if len(cs_split)>n:
sn = cs_split[n]
else:
sn = None
self.ser.phidgetStepperRescale(int(channel_str),toFloat(eval(value)),sn) # pylint: disable=eval-used
except Exception as e: # pylint: disable=broad-except
_log.exception(e)
# engaged(ch,state[,sn]) # engage channel
elif cs.startswith('engaged(') and len(cs) > 11:
try:
n = 2
cs_split = cs[len('engaged('):-1].split(',')
channel_str, state_str = cs_split[0:n]
if len(cs_split)>n:
sn = cs_split[n]
else:
sn = None
state_engaged:bool = bool(state_str.lower() in {'yes', 'true', 't', '1'})
self.ser.phidgetStepperEngaged(int(channel_str), state_engaged, sn)
except Exception as e: # pylint: disable=broad-except
_log.exception(e)
# set(ch,pos[,sn]) # set position
elif cs.startswith('set(') and len(cs) > 7:
try:
n = 2
cs_split = cs[len('set('):-1].split(',')
channel_str,pos = cs_split[0:n]
if len(cs_split)>n:
sn = cs_split[n]
else:
sn = None
self.ser.phidgetStepperSet(int(channel_str),toFloat(eval(pos)),sn) # pylint: disable=eval-used
except Exception as e: # pylint: disable=broad-except
_log.exception(e)
else:
_log.info('Stepper Command <%s> not recognized', cs)
except Exception as e: # pylint: disable=broad-except
_log.exception(e)

Expand Down

0 comments on commit c139b69

Please sign in to comment.