odrive 0.6.9.dev0__py37-none-win_amd64.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.
- odrive/__init__.py +142 -0
- odrive/api_client.py +54 -0
- odrive/config.py +1296 -0
- odrive/crypto.py +369 -0
- odrive/data/brakeRs.json +18 -0
- odrive/data/drvs.json +82 -0
- odrive/data/encoders.json +86 -0
- odrive/data/motors.json +128 -0
- odrive/data/odrive-micro-x1.json +48 -0
- odrive/data/odrive-micro-x3.json +40 -0
- odrive/data/odrive-micro-x4.json +40 -0
- odrive/data/odrive-s1-x4.json +52 -0
- odrive/data/odrive-v4.4.json +58 -0
- odrive/data/schema-brakeR.json +6 -0
- odrive/data/schema-drv.json +6 -0
- odrive/data/schema-encoders.json +6 -0
- odrive/data/schema-motors.json +6 -0
- odrive/data/schema-odrive.json +5 -0
- odrive/data/schema.json +244 -0
- odrive/database.py +184 -0
- odrive/dfu.py +195 -0
- odrive/dfuse/DfuDevice.py +297 -0
- odrive/dfuse/__init__.py +1 -0
- odrive/enums.py +415 -0
- odrive/firmware.py +102 -0
- odrive/hw_version.py +67 -0
- odrive/legacy.py +73 -0
- odrive/legacy_config.py +123 -0
- odrive/legacy_dfu.py +442 -0
- odrive/lib/libodrive-windows-x64.dll +0 -0
- odrive/libodrive.py +325 -0
- odrive/pyfibre/fibre/__init__.py +4 -0
- odrive/pyfibre/fibre/libfibre-windows-amd64.dll +0 -0
- odrive/pyfibre/fibre/libfibre.py +1078 -0
- odrive/pyfibre/fibre/libwinpthread-1.dll +0 -0
- odrive/pyfibre/fibre/shell.py +162 -0
- odrive/pyfibre/fibre/utils.py +133 -0
- odrive/release_api.py +184 -0
- odrive/rich_text.py +134 -0
- odrive/shell.py +144 -0
- odrive/utils.py +881 -0
- odrive/version.py +1 -0
- odrive-0.6.9.dev0.data/scripts/odrive_demo.py +50 -0
- odrive-0.6.9.dev0.data/scripts/odrivetool +263 -0
- odrive-0.6.9.dev0.data/scripts/odrivetool.bat +2 -0
- odrive-0.6.9.dev0.dist-info/METADATA +22 -0
- odrive-0.6.9.dev0.dist-info/RECORD +49 -0
- odrive-0.6.9.dev0.dist-info/WHEEL +5 -0
- odrive-0.6.9.dev0.dist-info/top_level.txt +1 -0
odrive/crypto.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Provides cryptographic methods for client/server authentication and device
|
|
3
|
+
provisioning and authentication.
|
|
4
|
+
|
|
5
|
+
The following algorithms and encodings are used:
|
|
6
|
+
|
|
7
|
+
Signature algorithm: NIST P-256 (aka SECP256R1)
|
|
8
|
+
Hashing algorithm: SHA-256
|
|
9
|
+
|
|
10
|
+
Private key: 32 bytes private value (big endian)
|
|
11
|
+
Public key: 32 bytes X coordinate (big endian) + 32 bytes Y coordinate (big endian)
|
|
12
|
+
Signature: 32 bytes R (big endian) + 32 bytes S (big endian)
|
|
13
|
+
|
|
14
|
+
These match the algorithms/encodings in ODrive hardware and must therefore not
|
|
15
|
+
be changed.
|
|
16
|
+
|
|
17
|
+
The following copyright applies to all core cryptographic code in this file:
|
|
18
|
+
################################################################################
|
|
19
|
+
# Copyright (c) 2022 Anirudha Bose #
|
|
20
|
+
# Adapted from https://onyb.gitbook.io/secp256k1-python under the MIT license. #
|
|
21
|
+
################################################################################
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import base64
|
|
25
|
+
import hashlib
|
|
26
|
+
from random import randint
|
|
27
|
+
from typing import Optional, NamedTuple
|
|
28
|
+
|
|
29
|
+
class InvalidSignature(Exception):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
def _gcd_extended(a, b):
|
|
33
|
+
"""Extended Euclidean algorithm"""
|
|
34
|
+
if a == 0:
|
|
35
|
+
return b, 0, 1
|
|
36
|
+
gcd, x, y = _gcd_extended(b % a, a)
|
|
37
|
+
return (gcd, y - (b//a) * x, x)
|
|
38
|
+
|
|
39
|
+
def _mod_pow(a, b, z):
|
|
40
|
+
# This function is only needed for Python <3.8. For 3.8+ the built-in
|
|
41
|
+
# function does all we need.
|
|
42
|
+
if b > 0:
|
|
43
|
+
return pow(a, b, z)
|
|
44
|
+
elif b == -1:
|
|
45
|
+
return _gcd_extended(a, z)[1]
|
|
46
|
+
else:
|
|
47
|
+
raise Exception("not implemented")
|
|
48
|
+
|
|
49
|
+
class PrimeGaloisField(NamedTuple):
|
|
50
|
+
prime: int
|
|
51
|
+
|
|
52
|
+
def __contains__(self, field_value: "FieldElement") -> bool:
|
|
53
|
+
# called whenever you do: <FieldElement> in <PrimeGaloisField>
|
|
54
|
+
return 0 <= field_value.value < self.prime
|
|
55
|
+
|
|
56
|
+
class FieldElement(NamedTuple):
|
|
57
|
+
value: int
|
|
58
|
+
field: PrimeGaloisField
|
|
59
|
+
|
|
60
|
+
def __repr__(self):
|
|
61
|
+
return "0x" + f"{self.value:x}".zfill(64)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def P(self) -> int:
|
|
65
|
+
return self.field.prime
|
|
66
|
+
|
|
67
|
+
def __add__(self, other: "FieldElement") -> "FieldElement":
|
|
68
|
+
return FieldElement(
|
|
69
|
+
value=(self.value + other.value) % self.P,
|
|
70
|
+
field=self.field
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
def __sub__(self, other: "FieldElement") -> "FieldElement":
|
|
74
|
+
return FieldElement(
|
|
75
|
+
value=(self.value - other.value) % self.P,
|
|
76
|
+
field=self.field
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def __rmul__(self, scalar: int) -> "FieldElement":
|
|
80
|
+
return FieldElement(
|
|
81
|
+
value=(self.value * scalar) % self.P,
|
|
82
|
+
field=self.field
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def __mul__(self, other: "FieldElement") -> "FieldElement":
|
|
86
|
+
return FieldElement(
|
|
87
|
+
value=(self.value * other.value) % self.P,
|
|
88
|
+
field=self.field
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def __pow__(self, exponent: int) -> "FieldElement":
|
|
92
|
+
return FieldElement(
|
|
93
|
+
value=_mod_pow(self.value, exponent, self.P),
|
|
94
|
+
field=self.field
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
def __truediv__(self, other: "FieldElement") -> "FieldElement":
|
|
98
|
+
other_inv = other ** -1
|
|
99
|
+
return self * other_inv
|
|
100
|
+
|
|
101
|
+
class EllipticCurve(NamedTuple):
|
|
102
|
+
a: FieldElement
|
|
103
|
+
b: FieldElement
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def field(self):
|
|
107
|
+
assert self.a.field == self.b.field
|
|
108
|
+
return self.a.field
|
|
109
|
+
|
|
110
|
+
def __contains__(self, point: "Point") -> bool:
|
|
111
|
+
x, y = point.x, point.y
|
|
112
|
+
return y ** 2 == x ** 3 + self.a * x + self.b
|
|
113
|
+
|
|
114
|
+
class Point():
|
|
115
|
+
def __init__(self, x: int, y: int, curve: EllipticCurve):
|
|
116
|
+
# Encapsulate int coordinates in FieldElement
|
|
117
|
+
self.x = None if x is None else FieldElement(x, curve.field)
|
|
118
|
+
self.y = None if y is None else FieldElement(y, curve.field)
|
|
119
|
+
self.curve = curve
|
|
120
|
+
|
|
121
|
+
# Ignore validation for I
|
|
122
|
+
if self.x is None and self.y is None:
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
# Verify if the point satisfies the curve equation
|
|
126
|
+
if self not in self.curve:
|
|
127
|
+
raise ValueError
|
|
128
|
+
|
|
129
|
+
def __eq__(self, other):
|
|
130
|
+
return isinstance(other, Point) and (self.x, self.y, self.curve) == (other.x, other.y, other.curve)
|
|
131
|
+
|
|
132
|
+
def __hash__(self):
|
|
133
|
+
return hash((self.x, self.y, self.curve))
|
|
134
|
+
|
|
135
|
+
def __add__(self, other):
|
|
136
|
+
#################################################################
|
|
137
|
+
# Point Addition for P₁ or P₂ = I (identity) #
|
|
138
|
+
# #
|
|
139
|
+
# Formula: #
|
|
140
|
+
# P + I = P #
|
|
141
|
+
# I + P = P #
|
|
142
|
+
#################################################################
|
|
143
|
+
if self == secp256r1_I:
|
|
144
|
+
return other
|
|
145
|
+
|
|
146
|
+
if other == secp256r1_I:
|
|
147
|
+
return self
|
|
148
|
+
|
|
149
|
+
#################################################################
|
|
150
|
+
# Point Addition for X₁ = X₂ (additive inverse) #
|
|
151
|
+
# #
|
|
152
|
+
# Formula: #
|
|
153
|
+
# P + (-P) = I #
|
|
154
|
+
# (-P) + P = I #
|
|
155
|
+
#################################################################
|
|
156
|
+
if self.x == other.x and self.y == (-1 * other.y):
|
|
157
|
+
return secp256r1_I
|
|
158
|
+
|
|
159
|
+
#################################################################
|
|
160
|
+
# Point Addition for X₁ ≠ X₂ (line with slope) #
|
|
161
|
+
# #
|
|
162
|
+
# Formula: #
|
|
163
|
+
# S = (Y₂ - Y₁) / (X₂ - X₁) #
|
|
164
|
+
# X₃ = S² - X₁ - X₂ #
|
|
165
|
+
# Y₃ = S(X₁ - X₃) - Y₁ #
|
|
166
|
+
#################################################################
|
|
167
|
+
if self.x != other.x:
|
|
168
|
+
x1, x2 = self.x, other.x
|
|
169
|
+
y1, y2 = self.y, other.y
|
|
170
|
+
|
|
171
|
+
s = (y2 - y1) / (x2 - x1)
|
|
172
|
+
x3 = s ** 2 - x1 - x2
|
|
173
|
+
y3 = s * (x1 - x3) - y1
|
|
174
|
+
|
|
175
|
+
return self.__class__(
|
|
176
|
+
x=x3.value,
|
|
177
|
+
y=y3.value,
|
|
178
|
+
curve=secp256r1
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
#################################################################
|
|
182
|
+
# Point Addition for P₁ = P₂ (vertical tangent) #
|
|
183
|
+
# #
|
|
184
|
+
# Formula: #
|
|
185
|
+
# S = ∞ #
|
|
186
|
+
# (X₃, Y₃) = I #
|
|
187
|
+
#################################################################
|
|
188
|
+
if self == other and self.y == float("inf"):
|
|
189
|
+
return secp256r1_I
|
|
190
|
+
|
|
191
|
+
#################################################################
|
|
192
|
+
# Point Addition for P₁ = P₂ (tangent with slope) #
|
|
193
|
+
# #
|
|
194
|
+
# Formula: #
|
|
195
|
+
# S = (3X₁² + a) / 2Y₁ .. ∂(Y²) = ∂(X² + aX + b) #
|
|
196
|
+
# X₃ = S² - 2X₁ #
|
|
197
|
+
# Y₃ = S(X₁ - X₃) - Y₁ #
|
|
198
|
+
#################################################################
|
|
199
|
+
if self == other:
|
|
200
|
+
x1, y1, a = self.x, self.y, self.curve.a
|
|
201
|
+
|
|
202
|
+
s = (3 * x1 ** 2 + a) / (2 * y1)
|
|
203
|
+
x3 = s ** 2 - 2 * x1
|
|
204
|
+
y3 = s * (x1 - x3) - y1
|
|
205
|
+
|
|
206
|
+
return self.__class__(
|
|
207
|
+
x=x3.value,
|
|
208
|
+
y=y3.value,
|
|
209
|
+
curve=secp256r1
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
assert False
|
|
213
|
+
|
|
214
|
+
def __rmul__(self, scalar: int) -> "Point":
|
|
215
|
+
# Naive approach:
|
|
216
|
+
#
|
|
217
|
+
# result = I
|
|
218
|
+
# for _ in range(scalar): # or range(scalar % N)
|
|
219
|
+
# result = result + self
|
|
220
|
+
# return result
|
|
221
|
+
|
|
222
|
+
# Optimized approach using binary expansion
|
|
223
|
+
current = self
|
|
224
|
+
result = secp256r1_I
|
|
225
|
+
while scalar:
|
|
226
|
+
if scalar & 1: # same as scalar % 2
|
|
227
|
+
result = result + current
|
|
228
|
+
current = current + current # point doubling
|
|
229
|
+
scalar >>= 1 # same as scalar / 2
|
|
230
|
+
return result
|
|
231
|
+
|
|
232
|
+
class PrivateKey(NamedTuple):
|
|
233
|
+
secret: int
|
|
234
|
+
|
|
235
|
+
def public_key(self):
|
|
236
|
+
return self.secret * secp256r1_G
|
|
237
|
+
|
|
238
|
+
PublicKey = Point
|
|
239
|
+
|
|
240
|
+
secp256r1_field = PrimeGaloisField(prime=0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff)
|
|
241
|
+
secp256r1 = EllipticCurve(
|
|
242
|
+
a=FieldElement(0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc, secp256r1_field),
|
|
243
|
+
b=FieldElement(0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b, secp256r1_field)
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
secp256r1_G = Point(
|
|
247
|
+
x=0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296,
|
|
248
|
+
y=0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5,
|
|
249
|
+
curve=secp256r1
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
secp256r1_I = Point(x=None, y=None, curve=secp256r1)
|
|
253
|
+
|
|
254
|
+
# Order of the group generated by G, such that nG = I
|
|
255
|
+
secp256r1_N = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
|
|
256
|
+
|
|
257
|
+
def gen_key() -> PrivateKey:
|
|
258
|
+
"""
|
|
259
|
+
Creates a new private key. The public key can be derived from this.
|
|
260
|
+
"""
|
|
261
|
+
return PrivateKey(randint(0, secp256r1_N))
|
|
262
|
+
|
|
263
|
+
def get_private_bytes(private_key: PrivateKey) -> bytes:
|
|
264
|
+
"""
|
|
265
|
+
Returns the raw byte representation of `private_key`.
|
|
266
|
+
See file header for encoding details.
|
|
267
|
+
"""
|
|
268
|
+
return private_key.secret.to_bytes(32, 'big')
|
|
269
|
+
|
|
270
|
+
def get_public_bytes(public_key: Point) -> bytes:
|
|
271
|
+
"""
|
|
272
|
+
Returns the raw byte representation of `public_key`.
|
|
273
|
+
See file header for encoding details.
|
|
274
|
+
"""
|
|
275
|
+
return public_key.x.value.to_bytes(32, 'big') + public_key.y.value.to_bytes(32, 'big')
|
|
276
|
+
|
|
277
|
+
def load_private_key(private_bytes: bytes) -> PrivateKey:
|
|
278
|
+
"""
|
|
279
|
+
Loads a private key object from the raw representation in `private_bytes`.
|
|
280
|
+
See file header for encoding details.
|
|
281
|
+
"""
|
|
282
|
+
assert len(private_bytes) == 32
|
|
283
|
+
private_value = int.from_bytes(private_bytes, 'big')
|
|
284
|
+
return PrivateKey(private_value)
|
|
285
|
+
|
|
286
|
+
def load_public_key(public_bytes: bytes) -> PublicKey:
|
|
287
|
+
"""
|
|
288
|
+
Loads a public key object from the raw representation in `public_bytes`.
|
|
289
|
+
See file header for encoding details.
|
|
290
|
+
"""
|
|
291
|
+
assert len(public_bytes) == 64
|
|
292
|
+
x_int = int.from_bytes(public_bytes[:32], 'big')
|
|
293
|
+
y_int = int.from_bytes(public_bytes[32:], 'big')
|
|
294
|
+
return Point(x_int, y_int, secp256r1)
|
|
295
|
+
|
|
296
|
+
def _sha256(data: bytes):
|
|
297
|
+
alg = hashlib.sha256()
|
|
298
|
+
alg.update(data)
|
|
299
|
+
return int.from_bytes(alg.digest(), 'big')
|
|
300
|
+
|
|
301
|
+
def sign(private_key: PrivateKey, data: bytes) -> bytes:
|
|
302
|
+
z = _sha256(data)
|
|
303
|
+
|
|
304
|
+
e = private_key.secret
|
|
305
|
+
k = randint(0, secp256r1_N)
|
|
306
|
+
R = k * secp256r1_G
|
|
307
|
+
r = R.x.value
|
|
308
|
+
k_inv = _mod_pow(k, -1, secp256r1_N)
|
|
309
|
+
s = ((z + r*e) * k_inv) % secp256r1_N
|
|
310
|
+
|
|
311
|
+
return r.to_bytes(32, 'big') + s.to_bytes(32, 'big')
|
|
312
|
+
|
|
313
|
+
def verify(public_key: Point, signature: bytes, data: bytes) -> bool:
|
|
314
|
+
z = _sha256(data)
|
|
315
|
+
r = int.from_bytes(signature[:32], 'big')
|
|
316
|
+
s = int.from_bytes(signature[32:], 'big')
|
|
317
|
+
|
|
318
|
+
s_inv = _mod_pow(s, -1, secp256r1_N)
|
|
319
|
+
u = (z * s_inv) % secp256r1_N
|
|
320
|
+
v = (r * s_inv) % secp256r1_N
|
|
321
|
+
|
|
322
|
+
if (u*secp256r1_G + v*public_key).x.value != r:
|
|
323
|
+
raise InvalidSignature()
|
|
324
|
+
|
|
325
|
+
def verify_cert(certificate: bytes, device_public_key: Point, test_mode: bool = False):
|
|
326
|
+
"""
|
|
327
|
+
Verifies the device certificate cryptographically against the ODrive master
|
|
328
|
+
key.
|
|
329
|
+
Format of `certificate` documented at ../README.md#device-certificate
|
|
330
|
+
"""
|
|
331
|
+
assert len(certificate) == 272, len(certificate)
|
|
332
|
+
|
|
333
|
+
message = certificate[:80] + get_public_bytes(device_public_key)
|
|
334
|
+
verify(load_public_key(certificate[144:208]), certificate[80:144], message)
|
|
335
|
+
|
|
336
|
+
message = b'ODrive batch key'.ljust(32, b'\0') + certificate[144:208]
|
|
337
|
+
verify(test_master_key if test_mode else master_key, certificate[208:272], message)
|
|
338
|
+
|
|
339
|
+
def b64encode(buf: bytes) -> str:
|
|
340
|
+
return base64.b64encode(buf).decode('utf-8')
|
|
341
|
+
|
|
342
|
+
def b64decode(buf: str) -> bytes:
|
|
343
|
+
return base64.b64decode(buf.encode('utf-8'))
|
|
344
|
+
|
|
345
|
+
def safe_b64encode(buf: bytes) -> str:
|
|
346
|
+
return base64.urlsafe_b64encode(buf).decode('utf-8').rstrip('=')
|
|
347
|
+
|
|
348
|
+
def safe_b64decode(buf: str) -> bytes:
|
|
349
|
+
return base64.urlsafe_b64decode((buf + "=" * (-len(buf) % 4)).encode('utf-8'))
|
|
350
|
+
|
|
351
|
+
master_key = load_public_key(bytes([
|
|
352
|
+
0x60, 0xea, 0x3e, 0x6d, 0xc1, 0x18, 0xa2, 0x1f,
|
|
353
|
+
0x3a, 0x61, 0x99, 0x0c, 0x61, 0x6e, 0xe4, 0x4a,
|
|
354
|
+
0x02, 0x68, 0x80, 0xa2, 0x5c, 0x70, 0x21, 0xac,
|
|
355
|
+
0x6c, 0x63, 0x0b, 0x75, 0x39, 0x9b, 0x1b, 0xe2,
|
|
356
|
+
0x7c, 0xd1, 0x34, 0xc5, 0xd4, 0xf2, 0xa9, 0x1e,
|
|
357
|
+
0x0b, 0x23, 0x3a, 0x18, 0xb6, 0x43, 0xd5, 0x49,
|
|
358
|
+
0x7a, 0xd9, 0xe9, 0x3b, 0x8a, 0x52, 0xfe, 0x92,
|
|
359
|
+
0x95, 0x06, 0xcd, 0x46, 0x18, 0xcf, 0x4c, 0x59
|
|
360
|
+
]))
|
|
361
|
+
|
|
362
|
+
test_master_key_private = load_private_key(bytes([
|
|
363
|
+
0xa9, 0x6a, 0x76, 0xd7, 0x54, 0x53, 0x2e, 0x2a,
|
|
364
|
+
0x38, 0x7a, 0xc5, 0x54, 0x16, 0x53, 0x70, 0xf3,
|
|
365
|
+
0x48, 0x84, 0x9a, 0xf1, 0x82, 0x11, 0xbf, 0xd2,
|
|
366
|
+
0x1f, 0x3c, 0x05, 0xf7, 0xf3, 0xeb, 0xef, 0x08
|
|
367
|
+
]))
|
|
368
|
+
|
|
369
|
+
test_master_key = test_master_key_private.public_key()
|
odrive/data/brakeRs.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"50w2rj": {
|
|
3
|
+
"resistance": 2,
|
|
4
|
+
"power_rating": 50
|
|
5
|
+
},
|
|
6
|
+
"500w2rj": {
|
|
7
|
+
"resistance": 2,
|
|
8
|
+
"power_rating": 500
|
|
9
|
+
},
|
|
10
|
+
"300w2rj": {
|
|
11
|
+
"resistance": 2,
|
|
12
|
+
"power_rating": 300
|
|
13
|
+
},
|
|
14
|
+
"625w1r": {
|
|
15
|
+
"resistance": 1,
|
|
16
|
+
"power_rating": 625
|
|
17
|
+
}
|
|
18
|
+
}
|
odrive/data/drvs.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"drv8301": {
|
|
3
|
+
"faults": [
|
|
4
|
+
[0, "FETLC_OC"],
|
|
5
|
+
[1, "FETHC_OC"],
|
|
6
|
+
[2, "FETLB_OC"],
|
|
7
|
+
[3, "FETHB_OC"],
|
|
8
|
+
[4, "FETLA_OC"],
|
|
9
|
+
[5, "FETHA_OC"],
|
|
10
|
+
[6, "OTW"],
|
|
11
|
+
[7, "OTSD"],
|
|
12
|
+
[8, "PVDD_UV"],
|
|
13
|
+
[9, "GVDD_UV"],
|
|
14
|
+
[10, "FAULT"],
|
|
15
|
+
[23, "GVDD_OV"]
|
|
16
|
+
],
|
|
17
|
+
"config": []
|
|
18
|
+
},
|
|
19
|
+
"drv8353": {
|
|
20
|
+
"faults": [
|
|
21
|
+
[10, "FAULT"],
|
|
22
|
+
[9, "VDS_OCP"],
|
|
23
|
+
[8, "GDF"],
|
|
24
|
+
[7, "UVLO"],
|
|
25
|
+
[6, "OTSD"],
|
|
26
|
+
[5, "VDS_HA"],
|
|
27
|
+
[4, "VDS_LA"],
|
|
28
|
+
[3, "VDS_HB"],
|
|
29
|
+
[2, "VDS_LB"],
|
|
30
|
+
[1, "VDS_HC"],
|
|
31
|
+
[0, "VDS_LC"],
|
|
32
|
+
[26, "SA_OC"],
|
|
33
|
+
[25, "SB_OC"],
|
|
34
|
+
[24, "SC_OC"],
|
|
35
|
+
[23, "OTW"],
|
|
36
|
+
[22, "GDUV"],
|
|
37
|
+
[21, "VGS_HA"],
|
|
38
|
+
[20, "VGS_LA"],
|
|
39
|
+
[19, "VGS_HB"],
|
|
40
|
+
[18, "VGS_LB"],
|
|
41
|
+
[17, "VGS_HC"],
|
|
42
|
+
[16, "VGS_LC"]
|
|
43
|
+
],
|
|
44
|
+
"config": [
|
|
45
|
+
{"name": "IDRIVEN_HS", "pos": 11, "mask": 15, "codes": ["100mA", "100mA", "200mA", "300mA", "600mA", "700mA", "800mA", "900mA", "1100mA", "1200mA", "1300mA", "1400mA", "1700mA", "1800mA", "1900mA", "2000mA"]},
|
|
46
|
+
{"name": "IDRIVEP_HS", "pos": 15, "mask": 15, "codes": ["50mA", "50mA", "100mA", "150mA", "300mA", "350mA", "400mA", "450mA", "550mA", "600mA", "650mA", "700mA", "850mA", "900mA", "950mA", "1000mA"]},
|
|
47
|
+
{"name": "IDRIVEN_LS", "pos": 22, "mask": 15, "codes": ["100mA", "100mA", "200mA", "300mA", "600mA", "700mA", "800mA", "900mA", "1100mA", "1200mA", "1300mA", "1400mA", "1700mA", "1800mA", "1900mA", "2000mA"]},
|
|
48
|
+
{"name": "IDRIVEP_LS", "pos": 26, "mask": 15, "codes": ["50mA", "50mA", "100mA", "150mA", "300mA", "350mA", "400mA", "450mA", "550mA", "600mA", "650mA", "700mA", "850mA", "900mA", "950mA", "1000mA"]},
|
|
49
|
+
{"name": "VDS_LVL", "pos": 33, "mask": 15, "codes": ["0.06V", "0.07V", "0.08V", "0.09V", "0.1V", "0.2V", "0.3V", "0.4V", "0.5V", "0.6V", "0.7V", "0.8V", "0.9V", "1.0V", "1.5V", "2.0V"]},
|
|
50
|
+
{"name": "DEAD_TIME", "pos": 41, "mask": 3, "codes": ["50ns", "100ns", "200ns", "400ns"]},
|
|
51
|
+
{"name": "SEN_LVL", "pos": 44, "mask": 3, "codes": ["0.25V", "0.5V", "0.75V", "1.0V"]}
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
"drv8316": {
|
|
55
|
+
"faults": [
|
|
56
|
+
[6, "BK_FLT"],
|
|
57
|
+
[5, "SPI_FLT"],
|
|
58
|
+
[4, "OCP"],
|
|
59
|
+
[3, "NPOR"],
|
|
60
|
+
[2, "OVP"],
|
|
61
|
+
[1, "OT"],
|
|
62
|
+
[0, "FAULT"],
|
|
63
|
+
[15, "OTW"],
|
|
64
|
+
[14, "OTS"],
|
|
65
|
+
[13, "OCP_HC"],
|
|
66
|
+
[12, "OCL_LC"],
|
|
67
|
+
[11, "OCP_HB"],
|
|
68
|
+
[10, "OCP_LB"],
|
|
69
|
+
[9, "OCP_HA"],
|
|
70
|
+
[8, "OCP_LA"],
|
|
71
|
+
[22, "OTP_ERR"],
|
|
72
|
+
[21, "BUCK_OCP"],
|
|
73
|
+
[20, "BUCK_UV"],
|
|
74
|
+
[19, "VCP_UV"],
|
|
75
|
+
[18, "SPI_PARITY"],
|
|
76
|
+
[17, "SPI_SCLK_FLT"],
|
|
77
|
+
[16, "SPI_ADDR_FLT"]
|
|
78
|
+
],
|
|
79
|
+
"config": [
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"AMT10x": {
|
|
3
|
+
"type": "incremental",
|
|
4
|
+
"cpr": 8192,
|
|
5
|
+
"max_rpm": 7500,
|
|
6
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt10.pdf",
|
|
7
|
+
"show_in_gui": true,
|
|
8
|
+
"image": "AMT10.jpeg",
|
|
9
|
+
"shop_url": "https://odriverobotics.com/shop/cui-amt-102"
|
|
10
|
+
},
|
|
11
|
+
"AMT10Ex": {
|
|
12
|
+
"type": "incremental",
|
|
13
|
+
"cpr": 20480,
|
|
14
|
+
"max_rpm": 3000,
|
|
15
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt10e.pdf",
|
|
16
|
+
"show_in_gui": true,
|
|
17
|
+
"image": "AMT10.jpeg",
|
|
18
|
+
"shop_url": "https://odriverobotics.com/shop/20480-cpr-encoder-with-odrive-cable"
|
|
19
|
+
},
|
|
20
|
+
"AMT21xA": {
|
|
21
|
+
"type": "rs485",
|
|
22
|
+
"protocol": "AMT21_POLLING",
|
|
23
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt21.pdf",
|
|
24
|
+
"cpr": 4096,
|
|
25
|
+
"max_rpm": 8000,
|
|
26
|
+
"show_in_gui": false
|
|
27
|
+
},
|
|
28
|
+
"AMT21xB": {
|
|
29
|
+
"type": "rs485",
|
|
30
|
+
"protocol": "AMT21_POLLING",
|
|
31
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt21.pdf",
|
|
32
|
+
"cpr": 16384,
|
|
33
|
+
"max_rpm": 4000,
|
|
34
|
+
"show_in_gui": true,
|
|
35
|
+
"image": "AMT212x-V.jpeg"
|
|
36
|
+
},
|
|
37
|
+
"AMT21xB-V-OD": {
|
|
38
|
+
"type": "rs485",
|
|
39
|
+
"protocol": "AMT21_EVENT_DRIVEN",
|
|
40
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt21.pdf",
|
|
41
|
+
"cpr": 16384,
|
|
42
|
+
"max_rpm": 6000,
|
|
43
|
+
"show_in_gui": true,
|
|
44
|
+
"image": "AMT212x-V.jpeg",
|
|
45
|
+
"shop_url": "https://odriverobotics.com/shop/cui-amt212b-v-od"
|
|
46
|
+
},
|
|
47
|
+
"AMT21xC": {
|
|
48
|
+
"type": "rs485",
|
|
49
|
+
"protocol": "AMT21_POLLING",
|
|
50
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt21.pdf",
|
|
51
|
+
"cpr": 4096,
|
|
52
|
+
"max_rpm": 8000,
|
|
53
|
+
"show_in_gui": false
|
|
54
|
+
},
|
|
55
|
+
"AMT21xD": {
|
|
56
|
+
"type": "rs485",
|
|
57
|
+
"protocol": "AMT21_POLLING",
|
|
58
|
+
"datasheet_url": "https://www.cuidevices.com/product/resource/amt21.pdf",
|
|
59
|
+
"cpr": 16384,
|
|
60
|
+
"max_rpm": 4000,
|
|
61
|
+
"show_in_gui": false
|
|
62
|
+
},
|
|
63
|
+
"JKongMotor-encoder": {
|
|
64
|
+
"type": "incremental",
|
|
65
|
+
"datasheet_url": "https://jkongmotor.en.alibaba.com/product/60286588137-800888379/JK86BLS125_48V_3000RPM_660W_BLDC_MOTOR.html",
|
|
66
|
+
"cpr": 10000,
|
|
67
|
+
"max_rpm": 3000,
|
|
68
|
+
"show_in_gui": false
|
|
69
|
+
},
|
|
70
|
+
"KingKong-MBS": {
|
|
71
|
+
"type": "rs485",
|
|
72
|
+
"protocol": "MBS",
|
|
73
|
+
"cpr": 16384,
|
|
74
|
+
"max_rpm": 20000,
|
|
75
|
+
"show_in_gui": false
|
|
76
|
+
},
|
|
77
|
+
"ODrive-OA1": {
|
|
78
|
+
"type": "rs485",
|
|
79
|
+
"protocol": "ODRIVE_OA1",
|
|
80
|
+
"cpr": 4096,
|
|
81
|
+
"max_rpm": 60000,
|
|
82
|
+
"show_in_gui": true,
|
|
83
|
+
"image": "odrive-mag-encoder.jpeg",
|
|
84
|
+
"shop_url": "https://odriverobotics.com/shop/odrive-rs485-magnetic-encoder"
|
|
85
|
+
}
|
|
86
|
+
}
|
odrive/data/motors.json
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
{
|
|
2
|
+
"D5065-270KV": {
|
|
3
|
+
"phase_resistance": 0.039,
|
|
4
|
+
"phase_inductance": 1.57e-05,
|
|
5
|
+
"pole_pairs": 7,
|
|
6
|
+
"max_current": 65,
|
|
7
|
+
"kv": 270,
|
|
8
|
+
"moment_of_inertia": 1e-4,
|
|
9
|
+
"thermistor_r25": 10000,
|
|
10
|
+
"thermistor_beta": 3435,
|
|
11
|
+
"max_temp": 130,
|
|
12
|
+
"shop_url": "https://odriverobotics.com/shop/odrive-custom-motor-d5065",
|
|
13
|
+
"show_in_gui": true
|
|
14
|
+
},
|
|
15
|
+
"D6374-150KV": {
|
|
16
|
+
"phase_resistance": 0.039,
|
|
17
|
+
"phase_inductance": 2.40e-05,
|
|
18
|
+
"pole_pairs": 7,
|
|
19
|
+
"max_current": 70,
|
|
20
|
+
"kv": 150,
|
|
21
|
+
"moment_of_inertia": 1e-4,
|
|
22
|
+
"thermistor_r25": 10000,
|
|
23
|
+
"thermistor_beta": 3435,
|
|
24
|
+
"max_temp": 130,
|
|
25
|
+
"shop_url": "https://odriverobotics.com/shop/odrive-custom-motor-d6374-150kv",
|
|
26
|
+
"show_in_gui": true
|
|
27
|
+
},
|
|
28
|
+
"D5312s-330KV": {
|
|
29
|
+
"phase_resistance": 0.037,
|
|
30
|
+
"phase_inductance": 2.274e-05,
|
|
31
|
+
"pole_pairs": 7,
|
|
32
|
+
"max_current": 40,
|
|
33
|
+
"kv": 330,
|
|
34
|
+
"thermistor_r25": 10000,
|
|
35
|
+
"thermistor_beta": 3435,
|
|
36
|
+
"max_temp": 130,
|
|
37
|
+
"shop_url": "https://odriverobotics.com/shop/dual-shaft-motor-d5212s-300kv",
|
|
38
|
+
"show_in_gui": true
|
|
39
|
+
},
|
|
40
|
+
"M8325s-100KV": {
|
|
41
|
+
"phase_resistance": 0.024,
|
|
42
|
+
"phase_inductance": 9.97e-06,
|
|
43
|
+
"pole_pairs": 20,
|
|
44
|
+
"max_current": 50,
|
|
45
|
+
"kv": 100,
|
|
46
|
+
"thermistor_r25": 10000,
|
|
47
|
+
"thermistor_beta": 3435,
|
|
48
|
+
"max_temp": 130,
|
|
49
|
+
"shop_url": "https://odriverobotics.com/shop/m8325s",
|
|
50
|
+
"show_in_gui": true
|
|
51
|
+
},
|
|
52
|
+
"BotWheel": {
|
|
53
|
+
"phase_resistance": 0.8,
|
|
54
|
+
"phase_inductance": 1.7e-03,
|
|
55
|
+
"pole_pairs": 15,
|
|
56
|
+
"max_current": 16,
|
|
57
|
+
"kv": 8.7,
|
|
58
|
+
"thermistor_r25": 20000,
|
|
59
|
+
"thermistor_beta": 3950,
|
|
60
|
+
"max_temp": 130,
|
|
61
|
+
"shop_url": "https://odriverobotics.com/shop/botwheels",
|
|
62
|
+
"show_in_gui": true,
|
|
63
|
+
"builtin_inc_cpr": 3200,
|
|
64
|
+
"builtin_hall": true
|
|
65
|
+
},
|
|
66
|
+
"Herlea Pancake": {
|
|
67
|
+
"phase_resistance": 0.0463,
|
|
68
|
+
"phase_inductance": 3.22e-5,
|
|
69
|
+
"pole_pairs": 20,
|
|
70
|
+
"max_current": 50,
|
|
71
|
+
"kv": 120,
|
|
72
|
+
"show_in_gui": false
|
|
73
|
+
},
|
|
74
|
+
"ME1114": {
|
|
75
|
+
"phase_resistance": 0.033,
|
|
76
|
+
"phase_inductance": 1.80e-5,
|
|
77
|
+
"pole_pairs": 4,
|
|
78
|
+
"max_current": 200,
|
|
79
|
+
"calibration_current": 16,
|
|
80
|
+
"torque_constant": 0.12,
|
|
81
|
+
"max_vel": 83.33333333333333,
|
|
82
|
+
"show_in_gui": false
|
|
83
|
+
},
|
|
84
|
+
"NEMA34-JKongMotor": {
|
|
85
|
+
"phase_resistance": 0.0623,
|
|
86
|
+
"phase_inductance": 9.068e-5,
|
|
87
|
+
"pole_pairs": 4,
|
|
88
|
+
"max_current": 20,
|
|
89
|
+
"torque_constant": 0.13,
|
|
90
|
+
"thermistor_r25": 10000,
|
|
91
|
+
"thermistor_beta": 3435,
|
|
92
|
+
"max_temp": 140,
|
|
93
|
+
"show_in_gui": false
|
|
94
|
+
},
|
|
95
|
+
"NEMA34-Leison": {
|
|
96
|
+
"phase_resistance": 0.0623,
|
|
97
|
+
"phase_inductance": 9.068e-5,
|
|
98
|
+
"pole_pairs": 4,
|
|
99
|
+
"kv": 87,
|
|
100
|
+
"max_current": 20,
|
|
101
|
+
"thermistor_r25": 10000,
|
|
102
|
+
"thermistor_beta": 3435,
|
|
103
|
+
"max_temp": 140,
|
|
104
|
+
"show_in_gui": false
|
|
105
|
+
},
|
|
106
|
+
"NEMA32-Leison": {
|
|
107
|
+
"phase_resistance": 0.1505899578332901,
|
|
108
|
+
"phase_inductance": 0.00010684521112125367,
|
|
109
|
+
"pole_pairs": 4,
|
|
110
|
+
"kv": 145,
|
|
111
|
+
"max_current": 8,
|
|
112
|
+
"thermistor_r25": 10000,
|
|
113
|
+
"thermistor_beta": 3435,
|
|
114
|
+
"max_temp": 140,
|
|
115
|
+
"show_in_gui": false
|
|
116
|
+
},
|
|
117
|
+
"NEMA23-Leison": {
|
|
118
|
+
"phase_resistance": 0.147,
|
|
119
|
+
"phase_inductance": 107.1e-6,
|
|
120
|
+
"pole_pairs": 4,
|
|
121
|
+
"kv": 180.5,
|
|
122
|
+
"max_current": 10,
|
|
123
|
+
"thermistor_r25": 10000,
|
|
124
|
+
"thermistor_beta": 3435,
|
|
125
|
+
"max_temp": 140,
|
|
126
|
+
"show_in_gui": false
|
|
127
|
+
}
|
|
128
|
+
}
|