Closed jehun0908 closed 7 months ago
I think OP state is necessary to change the states of outputs. You probably also need to toggle the output via PDO communication like in the basic_example.py. PreOP, SafeOP and then OP state is the right sequence, yes.
I turned on the LED referring to basic_example.py. This is a reference code for other people.
import sys
import struct
import time
import threading
from collections import namedtuple
import pysoem
class BasicExample:
BECKHOFF_VENDOR_ID = 0x0002
EK1100_PRODUCT_CODE = 0x044c2c52
EL2008_PRODUCT_CODE = 0x7d83052
def __init__(self, ifname):
self._ifname = ifname
self._pd_thread_stop_event = threading.Event()
self._ch_thread_stop_event = threading.Event()
self._actual_wkc = 0
self._master = pysoem.Master()
self._master.in_op = False
self._master.do_check_state = False
SlaveSet = namedtuple('SlaveSet', 'name product_code config_func')
self._expected_slave_layout = {0: SlaveSet('EK1100', self.EK1100_PRODUCT_CODE, None),
1: SlaveSet('EL2008', self.EL2008_PRODUCT_CODE, None)}
def _processdata_thread(self):
while not self._pd_thread_stop_event.is_set():
self._master.send_processdata()
self._actual_wkc = self._master.receive_processdata(10000)
if not self._actual_wkc == self._master.expected_wkc:
print('incorrect wkc')
# time.sleep(0.01)
def _pdo_update_loop(self):
self._master.in_op = True
output_len = len(self._master.slaves[1].output)
tmp = bytearray([0 for i in range(output_len)])
toggle = True
try:
while 1:
if toggle:
tmp[0] = 0x00
else:
tmp[0] = 0xFF
self._master.slaves[1].output = bytes(tmp)
toggle ^= True
# time.sleep(1)
except KeyboardInterrupt:
# ctrl-C abort handling
print('stopped')
def run(self):
self._master.open(self._ifname)
if not self._master.config_init() > 0:
self._master.close()
raise BasicExampleError('no slave found')
for i, slave in enumerate(self._master.slaves):
if not ((slave.man == self.BECKHOFF_VENDOR_ID) and
(slave.id == self._expected_slave_layout[i].product_code)):
self._master.close()
raise BasicExampleError('unexpected slave layout')
slave.config_func = self._expected_slave_layout[i].config_func
slave.is_lost = False
self._master.config_map()
if self._master.state_check(pysoem.SAFEOP_STATE, 50000) != pysoem.SAFEOP_STATE:
self._master.close()
raise BasicExampleError('not all slaves reached SAFEOP state')
self._master.state = pysoem.OP_STATE
check_thread = threading.Thread(target=self._check_thread)
check_thread.start()
proc_thread = threading.Thread(target=self._processdata_thread)
proc_thread.start()
# send one valid process data to make outputs in slaves happy
self._master.send_processdata()
self._master.receive_processdata(2000)
# request OP state for all slaves
self._master.write_state()
all_slaves_reached_op_state = False
for i in range(40):
self._master.state_check(pysoem.OP_STATE, 50000)
if self._master.state == pysoem.OP_STATE:
all_slaves_reached_op_state = True
break
if all_slaves_reached_op_state:
self._pdo_update_loop()
self._pd_thread_stop_event.set()
self._ch_thread_stop_event.set()
proc_thread.join()
check_thread.join()
self._master.state = pysoem.INIT_STATE
# request INIT state for all slaves
self._master.write_state()
self._master.close()
if not all_slaves_reached_op_state:
raise BasicExampleError('not all slaves reached OP state')
@staticmethod
def _check_slave(slave, pos):
if slave.state == (pysoem.SAFEOP_STATE + pysoem.STATE_ERROR):
print(
'ERROR : slave {} is in SAFE_OP + ERROR, attempting ack.'.format(pos))
slave.state = pysoem.SAFEOP_STATE + pysoem.STATE_ACK
slave.write_state()
elif slave.state == pysoem.SAFEOP_STATE:
print(
'WARNING : slave {} is in SAFE_OP, try change to OPERATIONAL.'.format(pos))
slave.state = pysoem.OP_STATE
slave.write_state()
elif slave.state > pysoem.NONE_STATE:
if slave.reconfig():
slave.is_lost = False
print('MESSAGE : slave {} reconfigured'.format(pos))
elif not slave.is_lost:
slave.state_check(pysoem.OP_STATE)
if slave.state == pysoem.NONE_STATE:
slave.is_lost = True
print('ERROR : slave {} lost'.format(pos))
if slave.is_lost:
if slave.state == pysoem.NONE_STATE:
if slave.recover():
slave.is_lost = False
print(
'MESSAGE : slave {} recovered'.format(pos))
else:
slave.is_lost = False
print('MESSAGE : slave {} found'.format(pos))
def _check_thread(self):
while not self._ch_thread_stop_event.is_set():
if self._master.in_op and ((self._actual_wkc < self._master.expected_wkc) or self._master.do_check_state):
self._master.do_check_state = False
self._master.read_state()
for i, slave in enumerate(self._master.slaves):
if slave.state != pysoem.OP_STATE:
self._master.do_check_state = True
BasicExample._check_slave(slave, i)
if not self._master.do_check_state:
print('OK : all slaves resumed OPERATIONAL.')
time.sleep(0.01)
class BasicExampleError(Exception):
def __init__(self, message):
super(BasicExampleError, self).__init__(message)
self.message = message
if __name__ == '__main__':
print('basic_example started')
ifname = '[Your Device Name]'
try:
BasicExample(ifname).run()
except BasicExampleError as expt:
print('basic_example failed: ' + expt.message)
sys.exit(1)
I'd like to make a motor move separately from the above code. I faced one difficulty.
if master.state_check(pysoem.SAFEOP_STATE, 50_000) == pysoem.SAFEOP_STATE:
If you print from the code inside "",
Master.state_check(pysoem.SAFEOP_STATE) outputs 2 values, and pysoem.SAFEOP_STATE outputs 4 values. So, we are not able to enter the SAFEOP state. Can you help me?
BTW thiw is the way to generate code blocks on GitHub:
```py
def foo():
pass
```
This will result in syntax highlighted code blocks:
def foo():
pass
In #30 there is an example that brings a motor of a servo drive to move. If you cannot reach SafeOP state, there is probably an configuration issue, it is good to read out the AL status code like in the minimal_example.py
I looked at #30 and referred to it. Al status code indicates 0x0 (No error) I cannot reach SafeOP state.....
import pysoem
import time
import ctypes
import struct
KINBT11 = None
class InputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('Statusword', ctypes.c_uint16),
('Actual_Postion', ctypes.c_int32),
('Actual_Velocity', ctypes.c_int32),
('Digital_Input', ctypes.c_uint16),
]
class OutputPdo(ctypes.Structure):
_pack_ = 1
_fields_ = [
('Target_Velocity', ctypes.c_int32),
('Controlword', ctypes.c_uint16),
]
def config_func(slave_pos):
global KINBT11
# encoder increments
KINBT11.sdo_write(0x608F, 1, bytes(ctypes.c_uint32(500)))
# Motor revolutions
KINBT11.sdo_write(0x608F, 2, bytes(ctypes.c_uint32(1)))
# Max profile velocity
KINBT11.sdo_write(0x607F, 0, bytes(ctypes.c_uint32(8000)))
# Max motor speed
KINBT11.sdo_write(0x6080, 0, bytes(ctypes.c_uint32(8000)))
# # gear ratio
KINBT11.sdo_write(0x6091, 1, bytes(ctypes.c_uint32(1)))
KINBT11.sdo_write(0x6091, 2, bytes(ctypes.c_uint32(91)))
def convert_input_data(data):
return InputPdo.from_buffer_copy(data)
def main():
global KINBT11
master = pysoem.Master()
master.open('[My device name]')
if master.config_init() > 0:
KINBT11 = master.slaves[0]
KINBT11.config_func = config_func
master.config_map()
print(master.state_check(pysoem.SAFEOP_STATE,50000)) #### The value appears as 2.
print(pysoem.SAFEOP_STATE) #### The value appears as 4.
if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE: # From here, we keep going to the else
print("True")
master.state = pysoem.OP_STATE
master.write_state()
master.state_check(pysoem.OP_STATE, 5000000)
if master.state == pysoem.OP_STATE:
output_data = OutputPdo()
output_data.Target_Velocity = 500
output_data.Controlword = 15
KINBT11.output = bytes(output_data)
master.send_processdata()
master.receive_processdata(1000)
time.sleep(0.01)
try:
while 1:
master.send_processdata()
master.receive_processdata(1000)
print(convert_input_data(KINBT11.input).Target_Velocity)
print(convert_input_data(KINBT11.input).Statusword)
time.sleep(0.01)
except KeyboardInterrupt:
print('stopped')
# zero everything
KINBT11.output = bytes(len(KINBT11.output))
master.send_processdata()
master.receive_processdata(1000)
else:
print('failed to got to op state')
print('al status code {} ({})'.format(hex(KINBT11.al_status), pysoem.al_status_code_to_string(KINBT11.al_status)))
else:
print('failed to got to safeop state')
print('al status code {} ({})'.format(hex(KINBT11.al_status), pysoem.al_status_code_to_string(KINBT11.al_status)))
master.state = pysoem.PREOP_STATE
master.write_state()
else:
print('no device found')
master.close()
if __name__ == '__main__':
main()
You probably tried to increase the timeout for the state_check
?
And you are sure the KINBT11 is the only device in the EtherCAT network?
if master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE:
When outputting the value "if master.state_check(pysoem.SAFEOP_STATE, 50000" is 2 and go to the else statement.
Accept 0x1A02 value from inputPdo instead of default 0x1A00. Where should I add this part? Can there be an error because of this part?
Can you please also take a screenshot of the Startup tab? There you will see what sdo_writes are needed in the config function.
I'm not sure if the picture of start_tab you want is correct.
The one next to this one, on the left.
Oh! Startup_Tab is here.
Right, this is what TwinCAT is doing at the transition from PreOP to SafeOP state.
The equivalent place to put that is in the "config function", because the code there is also called in the transition from PreOP to SafeOP (during the config_map()
)
You could do that ether this way:
def config_func(slave_pos):
global KINBT11
rx_map_obj_bytes = struct.pack('BxH', 1, 0x1602)
KINBT11.sdo_write(0x1c12, 0, rx_map_obj_bytes, True)
tx_map_obj_bytes = struct.pack('BxH', 1, 0x1A02)
KINBT11.sdo_write(0x1c13, 0, tx_map_obj_bytes, True)
or that way:
def config_func(slave_pos):
global KINBT11
KINBT11.sdo_write(0x1c12, 0, bytes([0x01, 0x00, 0x02, 0x16]), True)
KINBT11.sdo_write(0x1c13, 0, bytes([0x01, 0x00, 0x02, 0x1A]), True)
Not sure if this is 100% correct. I'm also not sure it this will solve the problem, but it is worth to try it.
Thank you for your time on my issue. I changed it according to your advice. But there is still a problem. The result value of the code below is still 2. I am interpreting "master.state_check(pysoem.SAFEOP_STATE, 50000)" as waiting for the master to reach the state of "SAFEOP_STATE"
Is there a way to do it without the code below?
master.state_check(pysoem.SAFEOP_STATE, 50000) == pysoem.SAFEOP_STATE
Accessing COE objects with sdo_write and sdo_read works fine. But I still can't change to SAFEOP_STATE.
oh.. I thought the computer read the COE without any problems. However, if you try to read coe after config_map(), an error is printed. Is this because of the error of misreading eeprom?
Could you post the error message? Maybe the drive also needs DC (Distributed Clocks) configured. You can check the DC tab in TwinCAT or take a look at the ESI file.
This is the error message and DC tab you mentioned. max_motor_speed1 is output. However, an error occurs when outputting again after config_map().
Okay DC is not needed, this is fine.
For some reaseon the drive issues an Emergency Error. The 40960 is hex 0xA000. You need to check the manual what might cause this Emergency Error or ask the vendor of you device.
Thank you for your advice. I will look at the manual and ask the supplier according to your advice. There are still two questions left.
It has been a while. Sorry for not answering to your last post, somehow I lost track of this conversation. Do you still have issues with this? Did you had any success in the end?
Closed due to inactivity of the OP. Please reopen the ticket if you need further help on this.
I am using EK1100, EL2008 module. I want to light the EL2008 with an LED. 1) In this case, do I need a safe_op or an open state? 2) Also, if necessary, is it operated in the order of PreOP, SafeOP, and OPstate? 3) If you print the values of print(master.state_check(pysoem.SAFEOP_STATE) and print(pysoem.SAFEOP_STATE), they are different by 2 and 4, what is the solution? 4) Please tell me what to do in the same situation as Question 3.
my Code
`import sys import time import pysoem import chardet import ctypes
master = pysoem.Master() master.open('NPF_{F0A11C28-9838-4230-8341-3FA8D32CD718}') print("EtherCAT master created and started...")
if master.config_init() > 0:
else: print("No device found")
master.close()
print("------------------------------------End--------------------------------")`