ftservo-python-sdk 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ftservo_python_sdk-2.0.0.dist-info/METADATA +70 -0
- ftservo_python_sdk-2.0.0.dist-info/RECORD +13 -0
- ftservo_python_sdk-2.0.0.dist-info/WHEEL +5 -0
- ftservo_python_sdk-2.0.0.dist-info/licenses/LICENSE +21 -0
- ftservo_python_sdk-2.0.0.dist-info/top_level.txt +1 -0
- scservo_sdk/__init__.py +8 -0
- scservo_sdk/group_sync_read.py +151 -0
- scservo_sdk/group_sync_write.py +73 -0
- scservo_sdk/port_handler.py +115 -0
- scservo_sdk/protocol_packet_handler.py +530 -0
- scservo_sdk/scscl.py +104 -0
- scservo_sdk/scservo_def.py +25 -0
- scservo_sdk/sms_sts.py +111 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ftservo-python-sdk
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: This is source code from official feetech repository
|
|
5
|
+
Home-page: https://github.com/ftservo/FTServo_Python
|
|
6
|
+
Author: ftservo
|
|
7
|
+
Author-email: 117692731@qq.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Topic :: System :: Hardware :: Universal Serial Bus (USB) :: Communications Device Class (CDC)
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pyserial
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: author-email
|
|
17
|
+
Dynamic: classifier
|
|
18
|
+
Dynamic: description
|
|
19
|
+
Dynamic: description-content-type
|
|
20
|
+
Dynamic: home-page
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
Dynamic: requires-dist
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
This is source code from [official feetech repository](https://github.com/ftservo/FTServo_Python).
|
|
26
|
+
|
|
27
|
+
## Structure
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
root dirrectory
|
|
31
|
+
|---scservo_sdk
|
|
32
|
+
|---sms_sts
|
|
33
|
+
|---scscl
|
|
34
|
+
```
|
|
35
|
+
The 'scscl' and 'sms_sts' directories contain examples of using the library.
|
|
36
|
+
|
|
37
|
+
The source code of the library is located in the `scservo_sdk` directory.
|
|
38
|
+
|
|
39
|
+
The 'scsservo_sdk' directory contains the original archive with the source code of the library from the developer.
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
Tested on Linux Raspbian GNU/Linux 9.13 (stretch).
|
|
44
|
+
Python version Python 3.5.3
|
|
45
|
+
|
|
46
|
+
### Method 1. Clone repositry
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
$ cd /usr/src/
|
|
50
|
+
$ sudo git clone https://github.com/ftservo/FTServo_Python.git
|
|
51
|
+
$ sudo chown -R pi FTServo_Python
|
|
52
|
+
$ cd FTServo_Python/sms_sts
|
|
53
|
+
$ python3 ping.py
|
|
54
|
+
Succeeded to open the port
|
|
55
|
+
Succeeded to change the baudrate
|
|
56
|
+
[ID:001] ping Succeeded. SCServo model number : 1540
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Method 2. Install pip package
|
|
60
|
+
|
|
61
|
+
Copy the sample file to any location convenient for you. In the example I use '/home/pi/FeetechTestFiles'
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
$ pip install ftservo-python-sdk
|
|
65
|
+
$ cd /home/pi/FeetechTestFiles/sms_sts
|
|
66
|
+
$ python3 ping.py
|
|
67
|
+
Succeeded to open the port
|
|
68
|
+
Succeeded to change the baudrate
|
|
69
|
+
[ID:001] ping Succeeded. SCServo model number : 1540
|
|
70
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ftservo_python_sdk-2.0.0.dist-info/licenses/LICENSE,sha256=7kzXw-DouYF2VTN5ykG5z9eOxoSn1I9ITJ0NFVsFvy8,1083
|
|
2
|
+
scservo_sdk/__init__.py,sha256=GP8TUZ1FsNWwr9fmn-UNr-yvAt_GlBjzwkWJAk2S2BY,205
|
|
3
|
+
scservo_sdk/group_sync_read.py,sha256=RaZs3Z__O1qsKaX-_kmwbInuF5wxOcznWdtiMEVetCI,5350
|
|
4
|
+
scservo_sdk/group_sync_write.py,sha256=Epps3ImNuvkRVTI-PePE0sTR8LmRtpJe_JiFLuOqB1E,1982
|
|
5
|
+
scservo_sdk/port_handler.py,sha256=sK1SZAYZWaAlxukAGfvC8tpSuWgpvkXJnuEEupVkGGU,3185
|
|
6
|
+
scservo_sdk/protocol_packet_handler.py,sha256=YKEQvAoZj6_NH9MAe-hcCtCUfyoCL4vMGFfxSQLYFHU,18287
|
|
7
|
+
scservo_sdk/scscl.py,sha256=N3vioVecwBZ3k4mI5KFKBxkUjFFIeylQxYa8sfzigGw,3887
|
|
8
|
+
scservo_sdk/scservo_def.py,sha256=PnKCh4I_12okAAHTA8pbmzpQZlogXetf5PfIDiGgSbE,734
|
|
9
|
+
scservo_sdk/sms_sts.py,sha256=YUq9QuMx9RDp0EhukL2vXs4FPiJ_YOGfYY5wK2W0gQE,4062
|
|
10
|
+
ftservo_python_sdk-2.0.0.dist-info/METADATA,sha256=SZAw-ea2RuN_Nf09_5nDBG3c3a517iFB8gHk4s-KVpA,2043
|
|
11
|
+
ftservo_python_sdk-2.0.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
12
|
+
ftservo_python_sdk-2.0.0.dist-info/top_level.txt,sha256=aH94_9kpy8x3zbQCNQNK8uAeJ_PtMM2r3S2tmEShADE,12
|
|
13
|
+
ftservo_python_sdk-2.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 ftservo
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
scservo_sdk
|
scservo_sdk/__init__.py
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from .scservo_def import *
|
|
4
|
+
|
|
5
|
+
class GroupSyncRead:
|
|
6
|
+
def __init__(self, ph, start_address, data_length):
|
|
7
|
+
self.ph = ph
|
|
8
|
+
self.start_address = start_address
|
|
9
|
+
self.data_length = data_length
|
|
10
|
+
|
|
11
|
+
self.last_result = False
|
|
12
|
+
self.is_param_changed = False
|
|
13
|
+
self.param = []
|
|
14
|
+
self.data_dict = {}
|
|
15
|
+
|
|
16
|
+
self.clearParam()
|
|
17
|
+
|
|
18
|
+
def makeParam(self):
|
|
19
|
+
if not self.data_dict: # len(self.data_dict.keys()) == 0:
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
self.param = []
|
|
23
|
+
|
|
24
|
+
for scs_id in self.data_dict:
|
|
25
|
+
self.param.append(scs_id)
|
|
26
|
+
|
|
27
|
+
def addParam(self, scs_id):
|
|
28
|
+
if scs_id in self.data_dict: # scs_id already exist
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
self.data_dict[scs_id] = [] # [0] * self.data_length
|
|
32
|
+
|
|
33
|
+
self.is_param_changed = True
|
|
34
|
+
return True
|
|
35
|
+
|
|
36
|
+
def removeParam(self, scs_id):
|
|
37
|
+
if scs_id not in self.data_dict: # NOT exist
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
del self.data_dict[scs_id]
|
|
41
|
+
|
|
42
|
+
self.is_param_changed = True
|
|
43
|
+
|
|
44
|
+
def clearParam(self):
|
|
45
|
+
self.data_dict.clear()
|
|
46
|
+
|
|
47
|
+
def txPacket(self):
|
|
48
|
+
if len(self.data_dict.keys()) == 0:
|
|
49
|
+
|
|
50
|
+
return COMM_NOT_AVAILABLE
|
|
51
|
+
|
|
52
|
+
if self.is_param_changed is True or not self.param:
|
|
53
|
+
self.makeParam()
|
|
54
|
+
|
|
55
|
+
return self.ph.syncReadTx(self.start_address, self.data_length, self.param, len(self.data_dict.keys()))
|
|
56
|
+
|
|
57
|
+
def rxPacket(self):
|
|
58
|
+
self.last_result = True
|
|
59
|
+
|
|
60
|
+
result = COMM_RX_FAIL
|
|
61
|
+
|
|
62
|
+
if len(self.data_dict.keys()) == 0:
|
|
63
|
+
return COMM_NOT_AVAILABLE
|
|
64
|
+
|
|
65
|
+
result, rxpacket = self.ph.syncReadRx(self.data_length, len(self.data_dict.keys()))
|
|
66
|
+
# print(rxpacket)
|
|
67
|
+
if len(rxpacket) >= (self.data_length+6):
|
|
68
|
+
for scs_id in self.data_dict:
|
|
69
|
+
self.data_dict[scs_id], result = self.readRx(rxpacket, scs_id, self.data_length)
|
|
70
|
+
if result != COMM_SUCCESS:
|
|
71
|
+
self.last_result = False
|
|
72
|
+
# print(scs_id)
|
|
73
|
+
else:
|
|
74
|
+
self.last_result = False
|
|
75
|
+
# print(self.last_result)
|
|
76
|
+
return result
|
|
77
|
+
|
|
78
|
+
def txRxPacket(self):
|
|
79
|
+
result = self.txPacket()
|
|
80
|
+
if result != COMM_SUCCESS:
|
|
81
|
+
return result
|
|
82
|
+
|
|
83
|
+
return self.rxPacket()
|
|
84
|
+
|
|
85
|
+
def readRx(self, rxpacket, scs_id, data_length):
|
|
86
|
+
# print(scs_id)
|
|
87
|
+
# print(rxpacket)
|
|
88
|
+
data = []
|
|
89
|
+
rx_length = len(rxpacket)
|
|
90
|
+
# print(rx_length)
|
|
91
|
+
rx_index = 0;
|
|
92
|
+
while (rx_index+6+data_length) <= rx_length:
|
|
93
|
+
headpacket = [0x00, 0x00, 0x00]
|
|
94
|
+
while rx_index < rx_length:
|
|
95
|
+
headpacket[2] = headpacket[1];
|
|
96
|
+
headpacket[1] = headpacket[0];
|
|
97
|
+
headpacket[0] = rxpacket[rx_index];
|
|
98
|
+
rx_index += 1
|
|
99
|
+
if (headpacket[2] == 0xFF) and (headpacket[1] == 0xFF) and headpacket[0] == scs_id:
|
|
100
|
+
# print(rx_index)
|
|
101
|
+
break
|
|
102
|
+
# print(rx_index+3+data_length)
|
|
103
|
+
if (rx_index+3+data_length) > rx_length:
|
|
104
|
+
break;
|
|
105
|
+
if rxpacket[rx_index] != (data_length+2):
|
|
106
|
+
rx_index += 1
|
|
107
|
+
# print(rx_index)
|
|
108
|
+
continue
|
|
109
|
+
rx_index += 1
|
|
110
|
+
Error = rxpacket[rx_index]
|
|
111
|
+
rx_index += 1
|
|
112
|
+
calSum = scs_id + (data_length+2) + Error
|
|
113
|
+
data = [Error]
|
|
114
|
+
data.extend(rxpacket[rx_index : rx_index+data_length])
|
|
115
|
+
for i in range(0, data_length):
|
|
116
|
+
calSum += rxpacket[rx_index]
|
|
117
|
+
rx_index += 1
|
|
118
|
+
calSum = ~calSum & 0xFF
|
|
119
|
+
# print(calSum)
|
|
120
|
+
if calSum != rxpacket[rx_index]:
|
|
121
|
+
return None, COMM_RX_CORRUPT
|
|
122
|
+
return data, COMM_SUCCESS
|
|
123
|
+
# print(rx_index)
|
|
124
|
+
return None, COMM_RX_CORRUPT
|
|
125
|
+
|
|
126
|
+
def isAvailable(self, scs_id, address, data_length):
|
|
127
|
+
#if self.last_result is False or scs_id not in self.data_dict:
|
|
128
|
+
if scs_id not in self.data_dict:
|
|
129
|
+
return False, 0
|
|
130
|
+
|
|
131
|
+
if (address < self.start_address) or (self.start_address + self.data_length - data_length < address):
|
|
132
|
+
return False, 0
|
|
133
|
+
if not self.data_dict[scs_id]:
|
|
134
|
+
return False, 0
|
|
135
|
+
if len(self.data_dict[scs_id])<(data_length+1):
|
|
136
|
+
return False, 0
|
|
137
|
+
return True, self.data_dict[scs_id][0]
|
|
138
|
+
|
|
139
|
+
def getData(self, scs_id, address, data_length):
|
|
140
|
+
if data_length == 1:
|
|
141
|
+
return self.data_dict[scs_id][address-self.start_address+1]
|
|
142
|
+
elif data_length == 2:
|
|
143
|
+
return self.ph.scs_makeword(self.data_dict[scs_id][address-self.start_address+1],
|
|
144
|
+
self.data_dict[scs_id][address-self.start_address+2])
|
|
145
|
+
elif data_length == 4:
|
|
146
|
+
return self.ph.scs_makedword(self.ph.scs_makeword(self.data_dict[scs_id][address-self.start_address+1],
|
|
147
|
+
self.data_dict[scs_id][address-self.start_address+2]),
|
|
148
|
+
self.ph.scs_makeword(self.data_dict[scs_id][address-self.start_address+3],
|
|
149
|
+
self.data_dict[scs_id][address-self.start_address+4]))
|
|
150
|
+
else:
|
|
151
|
+
return 0
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from .scservo_def import *
|
|
4
|
+
|
|
5
|
+
class GroupSyncWrite:
|
|
6
|
+
def __init__(self, ph, start_address, data_length):
|
|
7
|
+
self.ph = ph
|
|
8
|
+
self.start_address = start_address
|
|
9
|
+
self.data_length = data_length
|
|
10
|
+
|
|
11
|
+
self.is_param_changed = False
|
|
12
|
+
self.param = []
|
|
13
|
+
self.data_dict = {}
|
|
14
|
+
|
|
15
|
+
self.clearParam()
|
|
16
|
+
|
|
17
|
+
def makeParam(self):
|
|
18
|
+
if not self.data_dict:
|
|
19
|
+
return
|
|
20
|
+
|
|
21
|
+
self.param = []
|
|
22
|
+
|
|
23
|
+
for scs_id in self.data_dict:
|
|
24
|
+
if not self.data_dict[scs_id]:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
self.param.append(scs_id)
|
|
28
|
+
self.param.extend(self.data_dict[scs_id])
|
|
29
|
+
|
|
30
|
+
def addParam(self, scs_id, data):
|
|
31
|
+
if scs_id in self.data_dict: # scs_id already exist
|
|
32
|
+
return False
|
|
33
|
+
|
|
34
|
+
if len(data) > self.data_length: # input data is longer than set
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
self.data_dict[scs_id] = data
|
|
38
|
+
|
|
39
|
+
self.is_param_changed = True
|
|
40
|
+
return True
|
|
41
|
+
|
|
42
|
+
def removeParam(self, scs_id):
|
|
43
|
+
if scs_id not in self.data_dict: # NOT exist
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
del self.data_dict[scs_id]
|
|
47
|
+
|
|
48
|
+
self.is_param_changed = True
|
|
49
|
+
|
|
50
|
+
def changeParam(self, scs_id, data):
|
|
51
|
+
if scs_id not in self.data_dict: # NOT exist
|
|
52
|
+
return False
|
|
53
|
+
|
|
54
|
+
if len(data) > self.data_length: # input data is longer than set
|
|
55
|
+
return False
|
|
56
|
+
|
|
57
|
+
self.data_dict[scs_id] = data
|
|
58
|
+
|
|
59
|
+
self.is_param_changed = True
|
|
60
|
+
return True
|
|
61
|
+
|
|
62
|
+
def clearParam(self):
|
|
63
|
+
self.data_dict.clear()
|
|
64
|
+
|
|
65
|
+
def txPacket(self):
|
|
66
|
+
if len(self.data_dict.keys()) == 0:
|
|
67
|
+
return COMM_NOT_AVAILABLE
|
|
68
|
+
|
|
69
|
+
if self.is_param_changed is True or not self.param:
|
|
70
|
+
self.makeParam()
|
|
71
|
+
|
|
72
|
+
return self.ph.syncWriteTxOnly(self.start_address, self.data_length, self.param,
|
|
73
|
+
len(self.data_dict.keys()) * (1 + self.data_length))
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import serial
|
|
5
|
+
import sys
|
|
6
|
+
import platform
|
|
7
|
+
|
|
8
|
+
DEFAULT_BAUDRATE = 1000000
|
|
9
|
+
LATENCY_TIMER = 50
|
|
10
|
+
|
|
11
|
+
class PortHandler(object):
|
|
12
|
+
def __init__(self, port_name):
|
|
13
|
+
self.is_open = False
|
|
14
|
+
self.baudrate = DEFAULT_BAUDRATE
|
|
15
|
+
self.packet_start_time = 0.0
|
|
16
|
+
self.packet_timeout = 0.0
|
|
17
|
+
self.tx_time_per_byte = 0.0
|
|
18
|
+
|
|
19
|
+
self.is_using = False
|
|
20
|
+
self.port_name = port_name
|
|
21
|
+
self.ser = None
|
|
22
|
+
|
|
23
|
+
def openPort(self):
|
|
24
|
+
return self.setBaudRate(self.baudrate)
|
|
25
|
+
|
|
26
|
+
def closePort(self):
|
|
27
|
+
self.ser.close()
|
|
28
|
+
self.is_open = False
|
|
29
|
+
|
|
30
|
+
def clearPort(self):
|
|
31
|
+
self.ser.flush()
|
|
32
|
+
|
|
33
|
+
def setPortName(self, port_name):
|
|
34
|
+
self.port_name = port_name
|
|
35
|
+
|
|
36
|
+
def getPortName(self):
|
|
37
|
+
return self.port_name
|
|
38
|
+
|
|
39
|
+
def setBaudRate(self, baudrate):
|
|
40
|
+
baud = self.getCFlagBaud(baudrate)
|
|
41
|
+
|
|
42
|
+
if baud <= 0:
|
|
43
|
+
# self.setupPort(38400)
|
|
44
|
+
# self.baudrate = baudrate
|
|
45
|
+
return False # TODO: setCustomBaudrate(baudrate)
|
|
46
|
+
else:
|
|
47
|
+
self.baudrate = baudrate
|
|
48
|
+
return self.setupPort(baud)
|
|
49
|
+
|
|
50
|
+
def getBaudRate(self):
|
|
51
|
+
return self.baudrate
|
|
52
|
+
|
|
53
|
+
def getBytesAvailable(self):
|
|
54
|
+
return self.ser.in_waiting
|
|
55
|
+
|
|
56
|
+
def readPort(self, length):
|
|
57
|
+
if (sys.version_info > (3, 0)):
|
|
58
|
+
return self.ser.read(length)
|
|
59
|
+
else:
|
|
60
|
+
return [ord(ch) for ch in self.ser.read(length)]
|
|
61
|
+
|
|
62
|
+
def writePort(self, packet):
|
|
63
|
+
return self.ser.write(packet)
|
|
64
|
+
|
|
65
|
+
def setPacketTimeout(self, packet_length):
|
|
66
|
+
self.packet_start_time = self.getCurrentTime()
|
|
67
|
+
self.packet_timeout = (self.tx_time_per_byte * packet_length) + (self.tx_time_per_byte * 3.0) + LATENCY_TIMER
|
|
68
|
+
|
|
69
|
+
def setPacketTimeoutMillis(self, msec):
|
|
70
|
+
self.packet_start_time = self.getCurrentTime()
|
|
71
|
+
self.packet_timeout = msec
|
|
72
|
+
|
|
73
|
+
def isPacketTimeout(self):
|
|
74
|
+
if self.getTimeSinceStart() > self.packet_timeout:
|
|
75
|
+
self.packet_timeout = 0
|
|
76
|
+
return True
|
|
77
|
+
|
|
78
|
+
return False
|
|
79
|
+
|
|
80
|
+
def getCurrentTime(self):
|
|
81
|
+
return round(time.time() * 1000000000) / 1000000.0
|
|
82
|
+
|
|
83
|
+
def getTimeSinceStart(self):
|
|
84
|
+
time_since = self.getCurrentTime() - self.packet_start_time
|
|
85
|
+
if time_since < 0.0:
|
|
86
|
+
self.packet_start_time = self.getCurrentTime()
|
|
87
|
+
|
|
88
|
+
return time_since
|
|
89
|
+
|
|
90
|
+
def setupPort(self, cflag_baud):
|
|
91
|
+
if self.is_open:
|
|
92
|
+
self.closePort()
|
|
93
|
+
|
|
94
|
+
self.ser = serial.Serial(
|
|
95
|
+
port=self.port_name,
|
|
96
|
+
baudrate=self.baudrate,
|
|
97
|
+
# parity = serial.PARITY_ODD,
|
|
98
|
+
# stopbits = serial.STOPBITS_TWO,
|
|
99
|
+
bytesize=serial.EIGHTBITS,
|
|
100
|
+
timeout=0
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self.is_open = True
|
|
104
|
+
|
|
105
|
+
self.ser.reset_input_buffer()
|
|
106
|
+
|
|
107
|
+
self.tx_time_per_byte = (1000.0 / self.baudrate) * 10.0
|
|
108
|
+
|
|
109
|
+
return True
|
|
110
|
+
|
|
111
|
+
def getCFlagBaud(self, baudrate):
|
|
112
|
+
if baudrate in [4800, 9600, 14400, 19200, 38400, 57600, 115200, 128000, 250000, 500000, 1000000]:
|
|
113
|
+
return baudrate
|
|
114
|
+
else:
|
|
115
|
+
return -1
|
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from .scservo_def import *
|
|
4
|
+
|
|
5
|
+
TXPACKET_MAX_LEN = 250
|
|
6
|
+
RXPACKET_MAX_LEN = 250
|
|
7
|
+
|
|
8
|
+
# for Protocol Packet
|
|
9
|
+
PKT_HEADER0 = 0
|
|
10
|
+
PKT_HEADER1 = 1
|
|
11
|
+
PKT_ID = 2
|
|
12
|
+
PKT_LENGTH = 3
|
|
13
|
+
PKT_INSTRUCTION = 4
|
|
14
|
+
PKT_ERROR = 4
|
|
15
|
+
PKT_PARAMETER0 = 5
|
|
16
|
+
|
|
17
|
+
# Protocol Error bit
|
|
18
|
+
ERRBIT_VOLTAGE = 1
|
|
19
|
+
ERRBIT_ANGLE = 2
|
|
20
|
+
ERRBIT_OVERHEAT = 4
|
|
21
|
+
ERRBIT_OVERELE = 8
|
|
22
|
+
ERRBIT_OVERLOAD = 32
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class protocol_packet_handler(object):
|
|
26
|
+
def __init__(self, portHandler, protocol_end):
|
|
27
|
+
#self.scs_setend(protocol_end)# SCServo bit end(STS/SMS=0, SCS=1)
|
|
28
|
+
self.portHandler = portHandler
|
|
29
|
+
self.scs_end = protocol_end
|
|
30
|
+
|
|
31
|
+
def scs_getend(self):
|
|
32
|
+
return self.scs_end
|
|
33
|
+
|
|
34
|
+
def scs_setend(self, e):
|
|
35
|
+
self.scs_end = e
|
|
36
|
+
|
|
37
|
+
def scs_tohost(self, a, b):
|
|
38
|
+
if (a & (1<<b)):
|
|
39
|
+
return -(a & ~(1<<b))
|
|
40
|
+
else:
|
|
41
|
+
return a
|
|
42
|
+
|
|
43
|
+
def scs_toscs(self, a, b):
|
|
44
|
+
if (a<0):
|
|
45
|
+
return (-a | (1<<b))
|
|
46
|
+
else:
|
|
47
|
+
return a
|
|
48
|
+
|
|
49
|
+
def scs_makeword(self, a, b):
|
|
50
|
+
if self.scs_end==0:
|
|
51
|
+
return (a & 0xFF) | ((b & 0xFF) << 8)
|
|
52
|
+
else:
|
|
53
|
+
return (b & 0xFF) | ((a & 0xFF) << 8)
|
|
54
|
+
|
|
55
|
+
def scs_makedword(self, a, b):
|
|
56
|
+
return (a & 0xFFFF) | (b & 0xFFFF) << 16
|
|
57
|
+
|
|
58
|
+
def scs_loword(self, l):
|
|
59
|
+
return l & 0xFFFF
|
|
60
|
+
|
|
61
|
+
def scs_hiword(self, h):
|
|
62
|
+
return (h >> 16) & 0xFFFF
|
|
63
|
+
|
|
64
|
+
def scs_lobyte(self, w):
|
|
65
|
+
if self.scs_end==0:
|
|
66
|
+
return w & 0xFF
|
|
67
|
+
else:
|
|
68
|
+
return (w >> 8) & 0xFF
|
|
69
|
+
|
|
70
|
+
def scs_hibyte(self, w):
|
|
71
|
+
if self.scs_end==0:
|
|
72
|
+
return (w >> 8) & 0xFF
|
|
73
|
+
else:
|
|
74
|
+
return w & 0xFF
|
|
75
|
+
|
|
76
|
+
def getProtocolVersion(self):
|
|
77
|
+
return 1.0
|
|
78
|
+
|
|
79
|
+
def getTxRxResult(self, result):
|
|
80
|
+
if result == COMM_SUCCESS:
|
|
81
|
+
return "[TxRxResult] Communication success!"
|
|
82
|
+
elif result == COMM_PORT_BUSY:
|
|
83
|
+
return "[TxRxResult] Port is in use!"
|
|
84
|
+
elif result == COMM_TX_FAIL:
|
|
85
|
+
return "[TxRxResult] Failed transmit instruction packet!"
|
|
86
|
+
elif result == COMM_RX_FAIL:
|
|
87
|
+
return "[TxRxResult] Failed get status packet from device!"
|
|
88
|
+
elif result == COMM_TX_ERROR:
|
|
89
|
+
return "[TxRxResult] Incorrect instruction packet!"
|
|
90
|
+
elif result == COMM_RX_WAITING:
|
|
91
|
+
return "[TxRxResult] Now receiving status packet!"
|
|
92
|
+
elif result == COMM_RX_TIMEOUT:
|
|
93
|
+
return "[TxRxResult] There is no status packet!"
|
|
94
|
+
elif result == COMM_RX_CORRUPT:
|
|
95
|
+
return "[TxRxResult] Incorrect status packet!"
|
|
96
|
+
elif result == COMM_NOT_AVAILABLE:
|
|
97
|
+
return "[TxRxResult] Protocol does not support this function!"
|
|
98
|
+
else:
|
|
99
|
+
return ""
|
|
100
|
+
|
|
101
|
+
def getRxPacketError(self, error):
|
|
102
|
+
if error & ERRBIT_VOLTAGE:
|
|
103
|
+
return "[ServoStatus] Input voltage error!"
|
|
104
|
+
|
|
105
|
+
if error & ERRBIT_ANGLE:
|
|
106
|
+
return "[ServoStatus] Angle sen error!"
|
|
107
|
+
|
|
108
|
+
if error & ERRBIT_OVERHEAT:
|
|
109
|
+
return "[ServoStatus] Overheat error!"
|
|
110
|
+
|
|
111
|
+
if error & ERRBIT_OVERELE:
|
|
112
|
+
return "[ServoStatus] OverEle error!"
|
|
113
|
+
|
|
114
|
+
if error & ERRBIT_OVERLOAD:
|
|
115
|
+
return "[ServoStatus] Overload error!"
|
|
116
|
+
|
|
117
|
+
return ""
|
|
118
|
+
|
|
119
|
+
def txPacket(self, txpacket):
|
|
120
|
+
checksum = 0
|
|
121
|
+
total_packet_length = txpacket[PKT_LENGTH] + 4 # 4: HEADER0 HEADER1 ID LENGTH
|
|
122
|
+
|
|
123
|
+
if self.portHandler.is_using:
|
|
124
|
+
return COMM_PORT_BUSY
|
|
125
|
+
self.portHandler.is_using = True
|
|
126
|
+
|
|
127
|
+
# check max packet length
|
|
128
|
+
if total_packet_length > TXPACKET_MAX_LEN:
|
|
129
|
+
self.portHandler.is_using = False
|
|
130
|
+
return COMM_TX_ERROR
|
|
131
|
+
|
|
132
|
+
# make packet header
|
|
133
|
+
txpacket[PKT_HEADER0] = 0xFF
|
|
134
|
+
txpacket[PKT_HEADER1] = 0xFF
|
|
135
|
+
|
|
136
|
+
# add a checksum to the packet
|
|
137
|
+
for idx in range(2, total_packet_length - 1): # except header, checksum
|
|
138
|
+
checksum += txpacket[idx]
|
|
139
|
+
|
|
140
|
+
txpacket[total_packet_length - 1] = ~checksum & 0xFF
|
|
141
|
+
|
|
142
|
+
#print "[TxPacket] %r" % txpacket
|
|
143
|
+
|
|
144
|
+
# tx packet
|
|
145
|
+
self.portHandler.clearPort()
|
|
146
|
+
written_packet_length = self.portHandler.writePort(txpacket)
|
|
147
|
+
if total_packet_length != written_packet_length:
|
|
148
|
+
self.portHandler.is_using = False
|
|
149
|
+
return COMM_TX_FAIL
|
|
150
|
+
|
|
151
|
+
return COMM_SUCCESS
|
|
152
|
+
|
|
153
|
+
def rxPacket(self):
|
|
154
|
+
rxpacket = []
|
|
155
|
+
|
|
156
|
+
result = COMM_TX_FAIL
|
|
157
|
+
checksum = 0
|
|
158
|
+
rx_length = 0
|
|
159
|
+
wait_length = 6 # minimum length (HEADER0 HEADER1 ID LENGTH ERROR CHKSUM)
|
|
160
|
+
|
|
161
|
+
while True:
|
|
162
|
+
rxpacket.extend(self.portHandler.readPort(wait_length - rx_length))
|
|
163
|
+
rx_length = len(rxpacket)
|
|
164
|
+
if rx_length >= wait_length:
|
|
165
|
+
# find packet header
|
|
166
|
+
for idx in range(0, (rx_length - 1)):
|
|
167
|
+
if (rxpacket[idx] == 0xFF) and (rxpacket[idx + 1] == 0xFF):
|
|
168
|
+
break
|
|
169
|
+
|
|
170
|
+
if idx == 0: # found at the beginning of the packet
|
|
171
|
+
if (rxpacket[PKT_ID] > 0xFD) or (rxpacket[PKT_LENGTH] > RXPACKET_MAX_LEN) or (
|
|
172
|
+
rxpacket[PKT_ERROR] > 0x7F):
|
|
173
|
+
# unavailable ID or unavailable Length or unavailable Error
|
|
174
|
+
# remove the first byte in the packet
|
|
175
|
+
del rxpacket[0]
|
|
176
|
+
rx_length -= 1
|
|
177
|
+
continue
|
|
178
|
+
|
|
179
|
+
# re-calculate the exact length of the rx packet
|
|
180
|
+
if wait_length != (rxpacket[PKT_LENGTH] + PKT_LENGTH + 1):
|
|
181
|
+
wait_length = rxpacket[PKT_LENGTH] + PKT_LENGTH + 1
|
|
182
|
+
continue
|
|
183
|
+
|
|
184
|
+
if rx_length < wait_length:
|
|
185
|
+
# check timeout
|
|
186
|
+
if self.portHandler.isPacketTimeout():
|
|
187
|
+
if rx_length == 0:
|
|
188
|
+
result = COMM_RX_TIMEOUT
|
|
189
|
+
else:
|
|
190
|
+
result = COMM_RX_CORRUPT
|
|
191
|
+
break
|
|
192
|
+
else:
|
|
193
|
+
continue
|
|
194
|
+
|
|
195
|
+
# calculate checksum
|
|
196
|
+
for i in range(2, wait_length - 1): # except header, checksum
|
|
197
|
+
checksum += rxpacket[i]
|
|
198
|
+
checksum = ~checksum & 0xFF
|
|
199
|
+
|
|
200
|
+
# verify checksum
|
|
201
|
+
if rxpacket[wait_length - 1] == checksum:
|
|
202
|
+
result = COMM_SUCCESS
|
|
203
|
+
else:
|
|
204
|
+
result = COMM_RX_CORRUPT
|
|
205
|
+
break
|
|
206
|
+
|
|
207
|
+
else:
|
|
208
|
+
# remove unnecessary packets
|
|
209
|
+
del rxpacket[0: idx]
|
|
210
|
+
rx_length -= idx
|
|
211
|
+
|
|
212
|
+
else:
|
|
213
|
+
# check timeout
|
|
214
|
+
if self.portHandler.isPacketTimeout():
|
|
215
|
+
if rx_length == 0:
|
|
216
|
+
result = COMM_RX_TIMEOUT
|
|
217
|
+
else:
|
|
218
|
+
result = COMM_RX_CORRUPT
|
|
219
|
+
break
|
|
220
|
+
|
|
221
|
+
self.portHandler.is_using = False
|
|
222
|
+
return rxpacket, result
|
|
223
|
+
|
|
224
|
+
def txRxPacket(self, txpacket):
|
|
225
|
+
rxpacket = None
|
|
226
|
+
error = 0
|
|
227
|
+
|
|
228
|
+
# tx packet
|
|
229
|
+
result = self.txPacket(txpacket)
|
|
230
|
+
if result != COMM_SUCCESS:
|
|
231
|
+
return rxpacket, result, error
|
|
232
|
+
|
|
233
|
+
# (ID == Broadcast ID) == no need to wait for status packet or not available
|
|
234
|
+
if (txpacket[PKT_ID] == BROADCAST_ID):
|
|
235
|
+
self.portHandler.is_using = False
|
|
236
|
+
return rxpacket, result, error
|
|
237
|
+
|
|
238
|
+
# set packet timeout
|
|
239
|
+
if txpacket[PKT_INSTRUCTION] == INST_READ:
|
|
240
|
+
self.portHandler.setPacketTimeout(txpacket[PKT_PARAMETER0 + 1] + 6)
|
|
241
|
+
else:
|
|
242
|
+
self.portHandler.setPacketTimeout(6) # HEADER0 HEADER1 ID LENGTH ERROR CHECKSUM
|
|
243
|
+
|
|
244
|
+
# rx packet
|
|
245
|
+
while True:
|
|
246
|
+
rxpacket, result = self.rxPacket()
|
|
247
|
+
if result != COMM_SUCCESS or txpacket[PKT_ID] == rxpacket[PKT_ID]:
|
|
248
|
+
break
|
|
249
|
+
|
|
250
|
+
if result == COMM_SUCCESS and txpacket[PKT_ID] == rxpacket[PKT_ID]:
|
|
251
|
+
error = rxpacket[PKT_ERROR]
|
|
252
|
+
|
|
253
|
+
return rxpacket, result, error
|
|
254
|
+
|
|
255
|
+
def ping(self, scs_id):
|
|
256
|
+
model_number = 0
|
|
257
|
+
error = 0
|
|
258
|
+
|
|
259
|
+
txpacket = [0] * 6
|
|
260
|
+
|
|
261
|
+
if scs_id > BROADCAST_ID:
|
|
262
|
+
return model_number, COMM_NOT_AVAILABLE, error
|
|
263
|
+
|
|
264
|
+
txpacket[PKT_ID] = scs_id
|
|
265
|
+
txpacket[PKT_LENGTH] = 2
|
|
266
|
+
txpacket[PKT_INSTRUCTION] = INST_PING
|
|
267
|
+
|
|
268
|
+
rxpacket, result, error = self.txRxPacket(txpacket)
|
|
269
|
+
|
|
270
|
+
if result == COMM_SUCCESS:
|
|
271
|
+
data_read, result, error = self.readTxRx(scs_id, 3, 2) # Address 3 : Model Number
|
|
272
|
+
if result == COMM_SUCCESS:
|
|
273
|
+
model_number = self.scs_makeword(data_read[0], data_read[1])
|
|
274
|
+
|
|
275
|
+
return model_number, result, error
|
|
276
|
+
|
|
277
|
+
def action(self, scs_id):
|
|
278
|
+
txpacket = [0] * 6
|
|
279
|
+
|
|
280
|
+
txpacket[PKT_ID] = scs_id
|
|
281
|
+
txpacket[PKT_LENGTH] = 2
|
|
282
|
+
txpacket[PKT_INSTRUCTION] = INST_ACTION
|
|
283
|
+
|
|
284
|
+
_, result, _ = self.txRxPacket(txpacket)
|
|
285
|
+
|
|
286
|
+
return result
|
|
287
|
+
|
|
288
|
+
def readTx(self, scs_id, address, length):
|
|
289
|
+
|
|
290
|
+
txpacket = [0] * 8
|
|
291
|
+
|
|
292
|
+
if scs_id >= BROADCAST_ID:
|
|
293
|
+
return COMM_NOT_AVAILABLE
|
|
294
|
+
|
|
295
|
+
txpacket[PKT_ID] = scs_id
|
|
296
|
+
txpacket[PKT_LENGTH] = 4
|
|
297
|
+
txpacket[PKT_INSTRUCTION] = INST_READ
|
|
298
|
+
txpacket[PKT_PARAMETER0 + 0] = address
|
|
299
|
+
txpacket[PKT_PARAMETER0 + 1] = length
|
|
300
|
+
|
|
301
|
+
result = self.txPacket(txpacket)
|
|
302
|
+
|
|
303
|
+
# set packet timeout
|
|
304
|
+
if result == COMM_SUCCESS:
|
|
305
|
+
self.portHandler.setPacketTimeout(length + 6)
|
|
306
|
+
|
|
307
|
+
return result
|
|
308
|
+
|
|
309
|
+
def readRx(self, scs_id, length):
|
|
310
|
+
result = COMM_TX_FAIL
|
|
311
|
+
error = 0
|
|
312
|
+
|
|
313
|
+
rxpacket = None
|
|
314
|
+
data = []
|
|
315
|
+
|
|
316
|
+
while True:
|
|
317
|
+
rxpacket, result = self.rxPacket()
|
|
318
|
+
|
|
319
|
+
if result != COMM_SUCCESS or rxpacket[PKT_ID] == scs_id:
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
if result == COMM_SUCCESS and rxpacket[PKT_ID] == scs_id:
|
|
323
|
+
error = rxpacket[PKT_ERROR]
|
|
324
|
+
|
|
325
|
+
data.extend(rxpacket[PKT_PARAMETER0 : PKT_PARAMETER0+length])
|
|
326
|
+
|
|
327
|
+
return data, result, error
|
|
328
|
+
|
|
329
|
+
def readTxRx(self, scs_id, address, length):
|
|
330
|
+
txpacket = [0] * 8
|
|
331
|
+
data = []
|
|
332
|
+
|
|
333
|
+
if scs_id >= BROADCAST_ID:
|
|
334
|
+
return data, COMM_NOT_AVAILABLE, 0
|
|
335
|
+
|
|
336
|
+
txpacket[PKT_ID] = scs_id
|
|
337
|
+
txpacket[PKT_LENGTH] = 4
|
|
338
|
+
txpacket[PKT_INSTRUCTION] = INST_READ
|
|
339
|
+
txpacket[PKT_PARAMETER0 + 0] = address
|
|
340
|
+
txpacket[PKT_PARAMETER0 + 1] = length
|
|
341
|
+
|
|
342
|
+
rxpacket, result, error = self.txRxPacket(txpacket)
|
|
343
|
+
if result == COMM_SUCCESS:
|
|
344
|
+
error = rxpacket[PKT_ERROR]
|
|
345
|
+
|
|
346
|
+
data.extend(rxpacket[PKT_PARAMETER0 : PKT_PARAMETER0+length])
|
|
347
|
+
|
|
348
|
+
return data, result, error
|
|
349
|
+
|
|
350
|
+
def read1ByteTx(self, scs_id, address):
|
|
351
|
+
return self.readTx(scs_id, address, 1)
|
|
352
|
+
|
|
353
|
+
def read1ByteRx(self, scs_id):
|
|
354
|
+
data, result, error = self.readRx(scs_id, 1)
|
|
355
|
+
data_read = data[0] if (result == COMM_SUCCESS) else 0
|
|
356
|
+
return data_read, result, error
|
|
357
|
+
|
|
358
|
+
def read1ByteTxRx(self, scs_id, address):
|
|
359
|
+
data, result, error = self.readTxRx(scs_id, address, 1)
|
|
360
|
+
data_read = data[0] if (result == COMM_SUCCESS) else 0
|
|
361
|
+
return data_read, result, error
|
|
362
|
+
|
|
363
|
+
def read2ByteTx(self, scs_id, address):
|
|
364
|
+
return self.readTx(scs_id, address, 2)
|
|
365
|
+
|
|
366
|
+
def read2ByteRx(self, scs_id):
|
|
367
|
+
data, result, error = self.readRx(scs_id, 2)
|
|
368
|
+
data_read = self.scs_makeword(data[0], data[1]) if (result == COMM_SUCCESS) else 0
|
|
369
|
+
return data_read, result, error
|
|
370
|
+
|
|
371
|
+
def read2ByteTxRx(self, scs_id, address):
|
|
372
|
+
data, result, error = self.readTxRx(scs_id, address, 2)
|
|
373
|
+
data_read = self.scs_makeword(data[0], data[1]) if (result == COMM_SUCCESS) else 0
|
|
374
|
+
return data_read, result, error
|
|
375
|
+
|
|
376
|
+
def read4ByteTx(self, scs_id, address):
|
|
377
|
+
return self.readTx(scs_id, address, 4)
|
|
378
|
+
|
|
379
|
+
def read4ByteRx(self, scs_id):
|
|
380
|
+
data, result, error = self.readRx(scs_id, 4)
|
|
381
|
+
data_read = self.scs_makedword(self.scs_makeword(data[0], data[1]),
|
|
382
|
+
self.scs_makeword(data[2], data[3])) if (result == COMM_SUCCESS) else 0
|
|
383
|
+
return data_read, result, error
|
|
384
|
+
|
|
385
|
+
def read4ByteTxRx(self, scs_id, address):
|
|
386
|
+
data, result, error = self.readTxRx(scs_id, address, 4)
|
|
387
|
+
data_read = self.scs_makedword(self.scs_makeword(data[0], data[1]),
|
|
388
|
+
self.scs_makeword(data[2], data[3])) if (result == COMM_SUCCESS) else 0
|
|
389
|
+
return data_read, result, error
|
|
390
|
+
|
|
391
|
+
def writeTxOnly(self, scs_id, address, length, data):
|
|
392
|
+
txpacket = [0] * (length + 7)
|
|
393
|
+
|
|
394
|
+
txpacket[PKT_ID] = scs_id
|
|
395
|
+
txpacket[PKT_LENGTH] = length + 3
|
|
396
|
+
txpacket[PKT_INSTRUCTION] = INST_WRITE
|
|
397
|
+
txpacket[PKT_PARAMETER0] = address
|
|
398
|
+
|
|
399
|
+
txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length]
|
|
400
|
+
|
|
401
|
+
result = self.txPacket(txpacket)
|
|
402
|
+
self.portHandler.is_using = False
|
|
403
|
+
|
|
404
|
+
return result
|
|
405
|
+
|
|
406
|
+
def writeTxRx(self, scs_id, address, length, data):
|
|
407
|
+
txpacket = [0] * (length + 7)
|
|
408
|
+
|
|
409
|
+
txpacket[PKT_ID] = scs_id
|
|
410
|
+
txpacket[PKT_LENGTH] = length + 3
|
|
411
|
+
txpacket[PKT_INSTRUCTION] = INST_WRITE
|
|
412
|
+
txpacket[PKT_PARAMETER0] = address
|
|
413
|
+
|
|
414
|
+
txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length]
|
|
415
|
+
rxpacket, result, error = self.txRxPacket(txpacket)
|
|
416
|
+
|
|
417
|
+
return result, error
|
|
418
|
+
|
|
419
|
+
def write1ByteTxOnly(self, scs_id, address, data):
|
|
420
|
+
data_write = [data]
|
|
421
|
+
return self.writeTxOnly(scs_id, address, 1, data_write)
|
|
422
|
+
|
|
423
|
+
def write1ByteTxRx(self, scs_id, address, data):
|
|
424
|
+
data_write = [data]
|
|
425
|
+
return self.writeTxRx(scs_id, address, 1, data_write)
|
|
426
|
+
|
|
427
|
+
def write2ByteTxOnly(self, scs_id, address, data):
|
|
428
|
+
data_write = [self.scs_lobyte(data), self.scs_hibyte(data)]
|
|
429
|
+
return self.writeTxOnly(scs_id, address, 2, data_write)
|
|
430
|
+
|
|
431
|
+
def write2ByteTxRx(self, scs_id, address, data):
|
|
432
|
+
data_write = [self.scs_lobyte(data), self.scs_hibyte(data)]
|
|
433
|
+
return self.writeTxRx(scs_id, address, 2, data_write)
|
|
434
|
+
|
|
435
|
+
def write4ByteTxOnly(self, scs_id, address, data):
|
|
436
|
+
data_write = [self.scs_lobyte(self.scs_loword(data)),
|
|
437
|
+
self.scs_hibyte(self.scs_loword(data)),
|
|
438
|
+
self.scs_lobyte(self.scs_hiword(data)),
|
|
439
|
+
self.scs_hibyte(self.scs_hiword(data))]
|
|
440
|
+
return self.writeTxOnly(scs_id, address, 4, data_write)
|
|
441
|
+
|
|
442
|
+
def write4ByteTxRx(self, scs_id, address, data):
|
|
443
|
+
data_write = [self.scs_lobyte(self.scs_loword(data)),
|
|
444
|
+
self.scs_hibyte(self.scs_loword(data)),
|
|
445
|
+
self.scs_lobyte(self.scs_hiword(data)),
|
|
446
|
+
self.scs_hibyte(self.scs_hiword(data))]
|
|
447
|
+
return self.writeTxRx(scs_id, address, 4, data_write)
|
|
448
|
+
|
|
449
|
+
def regWriteTxOnly(self, scs_id, address, length, data):
|
|
450
|
+
txpacket = [0] * (length + 7)
|
|
451
|
+
|
|
452
|
+
txpacket[PKT_ID] = scs_id
|
|
453
|
+
txpacket[PKT_LENGTH] = length + 3
|
|
454
|
+
txpacket[PKT_INSTRUCTION] = INST_REG_WRITE
|
|
455
|
+
txpacket[PKT_PARAMETER0] = address
|
|
456
|
+
|
|
457
|
+
txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length]
|
|
458
|
+
|
|
459
|
+
result = self.txPacket(txpacket)
|
|
460
|
+
self.portHandler.is_using = False
|
|
461
|
+
|
|
462
|
+
return result
|
|
463
|
+
|
|
464
|
+
def regWriteTxRx(self, scs_id, address, length, data):
|
|
465
|
+
txpacket = [0] * (length + 7)
|
|
466
|
+
|
|
467
|
+
txpacket[PKT_ID] = scs_id
|
|
468
|
+
txpacket[PKT_LENGTH] = length + 3
|
|
469
|
+
txpacket[PKT_INSTRUCTION] = INST_REG_WRITE
|
|
470
|
+
txpacket[PKT_PARAMETER0] = address
|
|
471
|
+
|
|
472
|
+
txpacket[PKT_PARAMETER0 + 1: PKT_PARAMETER0 + 1 + length] = data[0: length]
|
|
473
|
+
|
|
474
|
+
_, result, error = self.txRxPacket(txpacket)
|
|
475
|
+
|
|
476
|
+
return result, error
|
|
477
|
+
|
|
478
|
+
def syncReadTx(self, start_address, data_length, param, param_length):
|
|
479
|
+
txpacket = [0] * (param_length + 8)
|
|
480
|
+
# 8: HEADER0 HEADER1 ID LEN INST START_ADDR DATA_LEN CHKSUM
|
|
481
|
+
|
|
482
|
+
txpacket[PKT_ID] = BROADCAST_ID
|
|
483
|
+
txpacket[PKT_LENGTH] = param_length + 4 # 7: INST START_ADDR DATA_LEN CHKSUM
|
|
484
|
+
txpacket[PKT_INSTRUCTION] = INST_SYNC_READ
|
|
485
|
+
txpacket[PKT_PARAMETER0 + 0] = start_address
|
|
486
|
+
txpacket[PKT_PARAMETER0 + 1] = data_length
|
|
487
|
+
|
|
488
|
+
txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + param_length] = param[0: param_length]
|
|
489
|
+
|
|
490
|
+
# print(txpacket)
|
|
491
|
+
result = self.txPacket(txpacket)
|
|
492
|
+
return result
|
|
493
|
+
|
|
494
|
+
def syncReadRx(self, data_length, param_length):
|
|
495
|
+
wait_length = (6 + data_length) * param_length
|
|
496
|
+
self.portHandler.setPacketTimeout(wait_length)
|
|
497
|
+
rxpacket = []
|
|
498
|
+
rx_length = 0
|
|
499
|
+
while True:
|
|
500
|
+
rxpacket.extend(self.portHandler.readPort(wait_length - rx_length))
|
|
501
|
+
rx_length = len(rxpacket)
|
|
502
|
+
if rx_length >= wait_length:
|
|
503
|
+
result = COMM_SUCCESS
|
|
504
|
+
break
|
|
505
|
+
else:
|
|
506
|
+
# check timeout
|
|
507
|
+
if self.portHandler.isPacketTimeout():
|
|
508
|
+
if rx_length == 0:
|
|
509
|
+
result = COMM_RX_TIMEOUT
|
|
510
|
+
else:
|
|
511
|
+
result = COMM_RX_CORRUPT
|
|
512
|
+
break
|
|
513
|
+
self.portHandler.is_using = False
|
|
514
|
+
return result, rxpacket
|
|
515
|
+
|
|
516
|
+
def syncWriteTxOnly(self, start_address, data_length, param, param_length):
|
|
517
|
+
txpacket = [0] * (param_length + 8)
|
|
518
|
+
# 8: HEADER0 HEADER1 ID LEN INST START_ADDR DATA_LEN ... CHKSUM
|
|
519
|
+
|
|
520
|
+
txpacket[PKT_ID] = BROADCAST_ID
|
|
521
|
+
txpacket[PKT_LENGTH] = param_length + 4 # 4: INST START_ADDR DATA_LEN ... CHKSUM
|
|
522
|
+
txpacket[PKT_INSTRUCTION] = INST_SYNC_WRITE
|
|
523
|
+
txpacket[PKT_PARAMETER0 + 0] = start_address
|
|
524
|
+
txpacket[PKT_PARAMETER0 + 1] = data_length
|
|
525
|
+
|
|
526
|
+
txpacket[PKT_PARAMETER0 + 2: PKT_PARAMETER0 + 2 + param_length] = param[0: param_length]
|
|
527
|
+
|
|
528
|
+
_, result, _ = self.txRxPacket(txpacket)
|
|
529
|
+
|
|
530
|
+
return result
|
scservo_sdk/scscl.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from .scservo_def import *
|
|
4
|
+
from .protocol_packet_handler import *
|
|
5
|
+
from .group_sync_write import *
|
|
6
|
+
|
|
7
|
+
#波特率定义
|
|
8
|
+
SCSCL_1M = 0
|
|
9
|
+
SCSCL_0_5M = 1
|
|
10
|
+
SCSCL_250K = 2
|
|
11
|
+
SCSCL_128K = 3
|
|
12
|
+
SCSCL_115200 = 4
|
|
13
|
+
SCSCL_76800 = 5
|
|
14
|
+
SCSCL_57600 = 6
|
|
15
|
+
SCSCL_38400 = 7
|
|
16
|
+
|
|
17
|
+
#内存表定义
|
|
18
|
+
#-------EPROM(只读)--------
|
|
19
|
+
SCSCL_MODEL_L = 3
|
|
20
|
+
SCSCL_MODEL_H = 4
|
|
21
|
+
|
|
22
|
+
#-------EPROM(读写)--------
|
|
23
|
+
scs_id = 5
|
|
24
|
+
SCSCL_BAUD_RATE = 6
|
|
25
|
+
SCSCL_MIN_ANGLE_LIMIT_L = 9
|
|
26
|
+
SCSCL_MIN_ANGLE_LIMIT_H = 10
|
|
27
|
+
SCSCL_MAX_ANGLE_LIMIT_L = 11
|
|
28
|
+
SCSCL_MAX_ANGLE_LIMIT_H = 12
|
|
29
|
+
SCSCL_CW_DEAD = 26
|
|
30
|
+
SCSCL_CCW_DEAD = 27
|
|
31
|
+
|
|
32
|
+
#-------SRAM(读写)--------
|
|
33
|
+
SCSCL_TORQUE_ENABLE = 40
|
|
34
|
+
SCSCL_GOAL_POSITION_L = 42
|
|
35
|
+
SCSCL_GOAL_POSITION_H = 43
|
|
36
|
+
SCSCL_GOAL_TIME_L = 44
|
|
37
|
+
SCSCL_GOAL_TIME_H = 45
|
|
38
|
+
SCSCL_GOAL_SPEED_L = 46
|
|
39
|
+
SCSCL_GOAL_SPEED_H = 47
|
|
40
|
+
SCSCL_LOCK = 48
|
|
41
|
+
|
|
42
|
+
#-------SRAM(只读)--------
|
|
43
|
+
SCSCL_PRESENT_POSITION_L = 56
|
|
44
|
+
SCSCL_PRESENT_POSITION_H = 57
|
|
45
|
+
SCSCL_PRESENT_SPEED_L = 58
|
|
46
|
+
SCSCL_PRESENT_SPEED_H = 59
|
|
47
|
+
SCSCL_PRESENT_LOAD_L = 60
|
|
48
|
+
SCSCL_PRESENT_LOAD_H = 61
|
|
49
|
+
SCSCL_PRESENT_VOLTAGE = 62
|
|
50
|
+
SCSCL_PRESENT_TEMPERATURE = 63
|
|
51
|
+
SCSCL_MOVING = 66
|
|
52
|
+
SCSCL_PRESENT_CURRENT_L = 69
|
|
53
|
+
SCSCL_PRESENT_CURRENT_H = 70
|
|
54
|
+
|
|
55
|
+
class scscl(protocol_packet_handler):
|
|
56
|
+
def __init__(self, portHandler):
|
|
57
|
+
protocol_packet_handler.__init__(self, portHandler, 1)
|
|
58
|
+
self.groupSyncWrite = GroupSyncWrite(self, SCSCL_GOAL_POSITION_L, 6)
|
|
59
|
+
|
|
60
|
+
def WritePos(self, scs_id, position, time, speed):
|
|
61
|
+
txpacket = [self.scs_lobyte(position), self.scs_hibyte(position), self.scs_lobyte(time), self.scs_hibyte(time), self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
62
|
+
return self.writeTxRx(scs_id, SCSCL_GOAL_POSITION_L, len(txpacket), txpacket)
|
|
63
|
+
|
|
64
|
+
def ReadPos(self, scs_id):
|
|
65
|
+
scs_present_position, scs_comm_result, scs_error = self.read2ByteTxRx(scs_id, SCSCL_PRESENT_POSITION_L)
|
|
66
|
+
return scs_present_position, scs_comm_result, scs_error
|
|
67
|
+
|
|
68
|
+
def ReadSpeed(self, scs_id):
|
|
69
|
+
scs_present_speed, scs_comm_result, scs_error = self.read2ByteTxRx(scs_id, SCSCL_PRESENT_SPEED_L)
|
|
70
|
+
return self.scs_tohost(scs_present_speed, 15), scs_comm_result, scs_error
|
|
71
|
+
|
|
72
|
+
def ReadPosSpeed(self, scs_id):
|
|
73
|
+
scs_present_position_speed, scs_comm_result, scs_error = self.read4ByteTxRx(scs_id, SCSCL_PRESENT_POSITION_L)
|
|
74
|
+
scs_present_position = self.scs_loword(scs_present_position_speed)
|
|
75
|
+
scs_present_speed = self.scs_hiword(scs_present_position_speed)
|
|
76
|
+
return scs_present_position, self.scs_tohost(scs_present_speed, 15), scs_comm_result, scs_error
|
|
77
|
+
|
|
78
|
+
def ReadMoving(self, scs_id):
|
|
79
|
+
moving, scs_comm_result, scs_error = self.read1ByteTxRx(scs_id, SCSCL_MOVING)
|
|
80
|
+
return moving, scs_comm_result, scs_error
|
|
81
|
+
|
|
82
|
+
def SyncWritePos(self, scs_id, position, time, speed):
|
|
83
|
+
txpacket = [self.scs_lobyte(position), self.scs_hibyte(position), self.scs_lobyte(time), self.scs_hibyte(time), self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
84
|
+
return self.groupSyncWrite.addParam(scs_id, txpacket)
|
|
85
|
+
|
|
86
|
+
def RegWritePos(self, scs_id, position, time, speed):
|
|
87
|
+
txpacket = [self.scs_lobyte(position), self.scs_hibyte(position), self.scs_lobyte(time), self.scs_hibyte(time), self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
88
|
+
return self.regWriteTxRx(scs_id, SCSCL_GOAL_POSITION_L, len(txpacket), txpacket)
|
|
89
|
+
|
|
90
|
+
def RegAction(self):
|
|
91
|
+
return self.action(BROADCAST_ID)
|
|
92
|
+
|
|
93
|
+
def PWMMode(self, scs_id):
|
|
94
|
+
txpacket = [0, 0, 0, 0]
|
|
95
|
+
return self.writeTxRx(scs_id, SCSCL_MIN_ANGLE_LIMIT_L, len(txpacket), txpacket)
|
|
96
|
+
|
|
97
|
+
def WritePWM(self, scs_id, time):
|
|
98
|
+
return self.write2ByteTxRx(scs_id, SCSCL_GOAL_TIME_L, self.scs_toscs(time, 10))
|
|
99
|
+
|
|
100
|
+
def LockEprom(self, scs_id):
|
|
101
|
+
return self.write1ByteTxRx(scs_id, SCSCL_LOCK, 1)
|
|
102
|
+
|
|
103
|
+
def unLockEprom(self, scs_id):
|
|
104
|
+
return self.write1ByteTxRx(scs_id, SCSCL_LOCK, 0)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
BROADCAST_ID = 0xFE # 254
|
|
4
|
+
MAX_ID = 0xFC # 252
|
|
5
|
+
SCS_END = 0
|
|
6
|
+
|
|
7
|
+
# Instruction for SCS Protocol
|
|
8
|
+
INST_PING = 1
|
|
9
|
+
INST_READ = 2
|
|
10
|
+
INST_WRITE = 3
|
|
11
|
+
INST_REG_WRITE = 4
|
|
12
|
+
INST_ACTION = 5
|
|
13
|
+
INST_SYNC_WRITE = 131 # 0x83
|
|
14
|
+
INST_SYNC_READ = 130 # 0x82
|
|
15
|
+
|
|
16
|
+
# Communication Result
|
|
17
|
+
COMM_SUCCESS = 0 # tx or rx packet communication success
|
|
18
|
+
COMM_PORT_BUSY = -1 # Port is busy (in use)
|
|
19
|
+
COMM_TX_FAIL = -2 # Failed transmit instruction packet
|
|
20
|
+
COMM_RX_FAIL = -3 # Failed get status packet
|
|
21
|
+
COMM_TX_ERROR = -4 # Incorrect instruction packet
|
|
22
|
+
COMM_RX_WAITING = -5 # Now recieving status packet
|
|
23
|
+
COMM_RX_TIMEOUT = -6 # There is no status packet
|
|
24
|
+
COMM_RX_CORRUPT = -7 # Incorrect status packet
|
|
25
|
+
COMM_NOT_AVAILABLE = -9 #
|
scservo_sdk/sms_sts.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
from .scservo_def import *
|
|
4
|
+
from .protocol_packet_handler import *
|
|
5
|
+
from .group_sync_read import *
|
|
6
|
+
from .group_sync_write import *
|
|
7
|
+
|
|
8
|
+
#波特率定义
|
|
9
|
+
SMS_STS_1M = 0
|
|
10
|
+
SMS_STS_0_5M = 1
|
|
11
|
+
SMS_STS_250K = 2
|
|
12
|
+
SMS_STS_128K = 3
|
|
13
|
+
SMS_STS_115200 = 4
|
|
14
|
+
SMS_STS_76800 = 5
|
|
15
|
+
SMS_STS_57600 = 6
|
|
16
|
+
SMS_STS_38400 = 7
|
|
17
|
+
|
|
18
|
+
#内存表定义
|
|
19
|
+
#-------EPROM(只读)--------
|
|
20
|
+
SMS_STS_MODEL_L = 3
|
|
21
|
+
SMS_STS_MODEL_H = 4
|
|
22
|
+
|
|
23
|
+
#-------EPROM(读写)--------
|
|
24
|
+
SMS_STS_ID = 5
|
|
25
|
+
SMS_STS_BAUD_RATE = 6
|
|
26
|
+
SMS_STS_MIN_ANGLE_LIMIT_L = 9
|
|
27
|
+
SMS_STS_MIN_ANGLE_LIMIT_H = 10
|
|
28
|
+
SMS_STS_MAX_ANGLE_LIMIT_L = 11
|
|
29
|
+
SMS_STS_MAX_ANGLE_LIMIT_H = 12
|
|
30
|
+
SMS_STS_CW_DEAD = 26
|
|
31
|
+
SMS_STS_CCW_DEAD = 27
|
|
32
|
+
SMS_STS_OFS_L = 31
|
|
33
|
+
SMS_STS_OFS_H = 32
|
|
34
|
+
SMS_STS_MODE = 33
|
|
35
|
+
|
|
36
|
+
#-------SRAM(读写)--------
|
|
37
|
+
SMS_STS_TORQUE_ENABLE = 40
|
|
38
|
+
SMS_STS_ACC = 41
|
|
39
|
+
SMS_STS_GOAL_POSITION_L = 42
|
|
40
|
+
SMS_STS_GOAL_POSITION_H = 43
|
|
41
|
+
SMS_STS_GOAL_TIME_L = 44
|
|
42
|
+
SMS_STS_GOAL_TIME_H = 45
|
|
43
|
+
SMS_STS_GOAL_SPEED_L = 46
|
|
44
|
+
SMS_STS_GOAL_SPEED_H = 47
|
|
45
|
+
SMS_STS_LOCK = 55
|
|
46
|
+
|
|
47
|
+
#-------SRAM(只读)--------
|
|
48
|
+
SMS_STS_PRESENT_POSITION_L = 56
|
|
49
|
+
SMS_STS_PRESENT_POSITION_H = 57
|
|
50
|
+
SMS_STS_PRESENT_SPEED_L = 58
|
|
51
|
+
SMS_STS_PRESENT_SPEED_H = 59
|
|
52
|
+
SMS_STS_PRESENT_LOAD_L = 60
|
|
53
|
+
SMS_STS_PRESENT_LOAD_H = 61
|
|
54
|
+
SMS_STS_PRESENT_VOLTAGE = 62
|
|
55
|
+
SMS_STS_PRESENT_TEMPERATURE = 63
|
|
56
|
+
SMS_STS_MOVING = 66
|
|
57
|
+
SMS_STS_PRESENT_CURRENT_L = 69
|
|
58
|
+
SMS_STS_PRESENT_CURRENT_H = 70
|
|
59
|
+
|
|
60
|
+
class sms_sts(protocol_packet_handler):
|
|
61
|
+
def __init__(self, portHandler):
|
|
62
|
+
protocol_packet_handler.__init__(self, portHandler, 0)
|
|
63
|
+
self.groupSyncWrite = GroupSyncWrite(self, SMS_STS_ACC, 7)
|
|
64
|
+
|
|
65
|
+
def WritePosEx(self, scs_id, position, speed, acc):
|
|
66
|
+
txpacket = [acc, self.scs_lobyte(position), self.scs_hibyte(position), 0, 0, self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
67
|
+
return self.writeTxRx(scs_id, SMS_STS_ACC, len(txpacket), txpacket)
|
|
68
|
+
|
|
69
|
+
def ReadPos(self, scs_id):
|
|
70
|
+
scs_present_position, scs_comm_result, scs_error = self.read2ByteTxRx(scs_id, SMS_STS_PRESENT_POSITION_L)
|
|
71
|
+
return self.scs_tohost(scs_present_position, 15), scs_comm_result, scs_error
|
|
72
|
+
|
|
73
|
+
def ReadSpeed(self, scs_id):
|
|
74
|
+
scs_present_speed, scs_comm_result, scs_error = self.read2ByteTxRx(scs_id, SMS_STS_PRESENT_SPEED_L)
|
|
75
|
+
return self.scs_tohost(scs_present_speed, 15), scs_comm_result, scs_error
|
|
76
|
+
|
|
77
|
+
def ReadPosSpeed(self, scs_id):
|
|
78
|
+
scs_present_position_speed, scs_comm_result, scs_error = self.read4ByteTxRx(scs_id, SMS_STS_PRESENT_POSITION_L)
|
|
79
|
+
scs_present_position = self.scs_loword(scs_present_position_speed)
|
|
80
|
+
scs_present_speed = self.scs_hiword(scs_present_position_speed)
|
|
81
|
+
return self.scs_tohost(scs_present_position, 15), self.scs_tohost(scs_present_speed, 15), scs_comm_result, scs_error
|
|
82
|
+
|
|
83
|
+
def ReadMoving(self, scs_id):
|
|
84
|
+
moving, scs_comm_result, scs_error = self.read1ByteTxRx(scs_id, SMS_STS_MOVING)
|
|
85
|
+
return moving, scs_comm_result, scs_error
|
|
86
|
+
|
|
87
|
+
def SyncWritePosEx(self, scs_id, position, speed, acc):
|
|
88
|
+
txpacket = [acc, self.scs_lobyte(position), self.scs_hibyte(position), 0, 0, self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
89
|
+
return self.groupSyncWrite.addParam(scs_id, txpacket)
|
|
90
|
+
|
|
91
|
+
def RegWritePosEx(self, scs_id, position, speed, acc):
|
|
92
|
+
txpacket = [acc, self.scs_lobyte(position), self.scs_hibyte(position), 0, 0, self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
93
|
+
return self.regWriteTxRx(scs_id, SMS_STS_ACC, len(txpacket), txpacket)
|
|
94
|
+
|
|
95
|
+
def RegAction(self):
|
|
96
|
+
return self.action(BROADCAST_ID)
|
|
97
|
+
|
|
98
|
+
def WheelMode(self, scs_id):
|
|
99
|
+
return self.write1ByteTxRx(scs_id, SMS_STS_MODE, 1)
|
|
100
|
+
|
|
101
|
+
def WriteSpec(self, scs_id, speed, acc):
|
|
102
|
+
speed = self.scs_toscs(speed, 15)
|
|
103
|
+
txpacket = [acc, 0, 0, 0, 0, self.scs_lobyte(speed), self.scs_hibyte(speed)]
|
|
104
|
+
return self.writeTxRx(scs_id, SMS_STS_ACC, len(txpacket), txpacket)
|
|
105
|
+
|
|
106
|
+
def LockEprom(self, scs_id):
|
|
107
|
+
return self.write1ByteTxRx(scs_id, SMS_STS_LOCK, 1)
|
|
108
|
+
|
|
109
|
+
def unLockEprom(self, scs_id):
|
|
110
|
+
return self.write1ByteTxRx(scs_id, SMS_STS_LOCK, 0)
|
|
111
|
+
|