feetech-servo-ts
Version:
TypeScript SDK for Feetech servo motors using Web Serial API
901 lines (900 loc) • 28.2 kB
JavaScript
const R = {
// EEPROM Area
Model: { address: 3, size: 2 },
ID: { address: 5, size: 1 },
Baud_Rate: { address: 6, size: 1 },
Return_Delay: { address: 7, size: 1 },
Response_Status_Level: { address: 8, size: 1 },
Min_Angle_Limit: { address: 9, size: 2 },
Max_Angle_Limit: { address: 11, size: 2 },
Max_Temperature_Limit: { address: 13, size: 1 },
Max_Voltage_Limit: { address: 14, size: 1 },
Min_Voltage_Limit: { address: 15, size: 1 },
Max_Torque_Limit: { address: 16, size: 2 },
Phase: { address: 18, size: 1 },
Unloading_Condition: { address: 19, size: 1 },
LED_Alarm_Condition: { address: 20, size: 1 },
P_Coefficient: { address: 21, size: 1 },
D_Coefficient: { address: 22, size: 1 },
I_Coefficient: { address: 23, size: 1 },
Minimum_Startup_Force: { address: 24, size: 2 },
CW_Dead_Zone: { address: 26, size: 1 },
CCW_Dead_Zone: { address: 27, size: 1 },
Protection_Current: { address: 28, size: 2 },
Angular_Resolution: { address: 30, size: 1 },
Offset: { address: 31, size: 2 },
Mode: { address: 33, size: 1 },
Protective_Torque: { address: 34, size: 1 },
Protection_Time: { address: 35, size: 1 },
Overload_Torque: { address: 36, size: 1 },
Speed_closed_loop_P_proportional_coefficient: { address: 37, size: 1 },
Over_Current_Protection_Time: { address: 38, size: 1 },
Velocity_closed_loop_I_integral_coefficient: { address: 39, size: 1 },
// RAM Area
Torque_Enable: { address: 40, size: 1 },
Acceleration: { address: 41, size: 1 },
Goal_Position: { address: 42, size: 2 },
Goal_Time: { address: 44, size: 2 },
Goal_Speed: { address: 46, size: 2 },
Torque_Limit: { address: 48, size: 2 },
Lock: { address: 55, size: 1 },
Present_Position: { address: 56, size: 2 },
Present_Speed: { address: 58, size: 2 },
Present_Load: { address: 60, size: 2 },
Present_Voltage: { address: 62, size: 1 },
Present_Temperature: { address: 63, size: 1 },
Status: { address: 65, size: 1 },
Moving: { address: 66, size: 1 },
Present_Current: { address: 69, size: 2 },
Maximum_Acceleration: { address: 85, size: 2 }
}, F = {
scs_series: R,
sts3215: R,
sms_series: R
}, N = {
scs_series: 4096,
sts3215: 4096,
sms_series: 4096
}, A = {
0: 1e6,
1: 5e5,
2: 25e4,
3: 128e3,
4: 115200,
5: 57600,
6: 38400,
7: 19200
}, we = {
scs_series: A,
sts3215: A,
sms_series: A
}, Ee = ["Goal_Position", "Present_Position"], fe = ["Goal_Position", "Present_Position"];
function h(r, e) {
const t = F[r];
if (!t)
throw new Error(`Unknown model: ${r}`);
return t[e];
}
function _e(r, e) {
const t = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set();
for (const n of r) {
const a = h(n, e);
if (!a)
throw new Error(`Data name '${e}' not found for model '${n}'`);
t.add(a.address), s.add(a.size);
}
if (t.size > 1)
throw new Error(
`Different models use different addresses for '${e}'. This is not supported.`
);
if (s.size > 1)
throw new Error(
`Different models use different byte sizes for '${e}'. This is not supported.`
);
}
var q = /* @__PURE__ */ ((r) => (r.DEGREE = "DEGREE", r.LINEAR = "LINEAR", r))(q || {}), Y = /* @__PURE__ */ ((r) => (r[r.NORMAL = 0] = "NORMAL", r[r.INVERTED = 1] = "INVERTED", r))(Y || {}), L = /* @__PURE__ */ ((r) => (r.SCS_SERIES = "scs_series", r.STS3215 = "sts3215", r.SMS_SERIES = "sms_series", r))(L || {});
const C = {
scs_series: {
resolution: 4096,
maxPosition: 4095,
minPosition: 0,
defaultBaudRate: 1e6
},
sts3215: {
resolution: 4096,
maxPosition: 4095,
minPosition: 0,
defaultBaudRate: 1e6
},
sms_series: {
resolution: 4096,
maxPosition: 4095,
minPosition: 0,
defaultBaudRate: 1e6
}
};
var w = /* @__PURE__ */ ((r) => (r[r.PING = 1] = "PING", r[r.READ = 2] = "READ", r[r.WRITE = 3] = "WRITE", r[r.REG_WRITE = 4] = "REG_WRITE", r[r.ACTION = 5] = "ACTION", r[r.FACTORY_RESET = 6] = "FACTORY_RESET", r[r.REBOOT = 8] = "REBOOT", r[r.SYNC_WRITE = 131] = "SYNC_WRITE", r[r.SYNC_READ = 130] = "SYNC_READ", r))(w || {}), u = /* @__PURE__ */ ((r) => (r[r.VOLTAGE = 1] = "VOLTAGE", r[r.ANGLE = 2] = "ANGLE", r[r.OVERHEAT = 4] = "OVERHEAT", r[r.RANGE = 8] = "RANGE", r[r.CHECKSUM = 16] = "CHECKSUM", r[r.OVERLOAD = 32] = "OVERLOAD", r[r.INSTRUCTION = 64] = "INSTRUCTION", r))(u || {});
class o extends Error {
constructor(e, t, s) {
super(e), this.code = t, this.deviceId = s, this.name = "ServoError";
}
}
var c = /* @__PURE__ */ ((r) => (r.CONNECTION_FAILED = "CONNECTION_FAILED", r.CONNECTION_LOST = "CONNECTION_LOST", r.PORT_NOT_FOUND = "PORT_NOT_FOUND", r.PERMISSION_DENIED = "PERMISSION_DENIED", r.TIMEOUT = "TIMEOUT", r.CHECKSUM_ERROR = "CHECKSUM_ERROR", r.INVALID_RESPONSE = "INVALID_RESPONSE", r.DEVICE_NOT_FOUND = "DEVICE_NOT_FOUND", r.WRITE_ERROR = "WRITE_ERROR", r.READ_ERROR = "READ_ERROR", r.INVALID_PARAMETER = "INVALID_PARAMETER", r.HARDWARE_ERROR = "HARDWARE_ERROR", r))(c || {});
const g = 254, pe = 253, E = [255, 255], T = 2, P = 1, m = 1, K = 1, Re = 1, j = 1, D = T + P + m + K + j, M = 1e6, p = 1e3, Ae = 3, I = 8, Q = 1, X = 0, Z = 0, J = 4095, Te = 2048, ee = 0, S = 1023, Pe = 0.1, me = 1;
function f(r, e) {
return e << 8 | r;
}
function H(r) {
return r & 255;
}
function k(r) {
return r >> 8 & 255;
}
class te {
constructor(e = { baudRate: M }, t = p) {
this.options = e, this.timeout = t, this.port = null, this.reader = null, this.writer = null, this.readBuffer = new Uint8Array(0), this.isOpen = !1;
}
async connect() {
try {
if (!("serial" in navigator))
throw new o(
"Web Serial API is not supported in this browser",
c.CONNECTION_FAILED
);
if (this.port = await navigator.serial.requestPort(), await this.port.open(this.options), !this.port.readable || !this.port.writable)
throw new o(
"Port is not readable or writable",
c.CONNECTION_FAILED
);
this.reader = this.port.readable.getReader(), this.writer = this.port.writable.getWriter(), this.isOpen = !0, this.startReading();
} catch (e) {
throw e instanceof o ? e : new o(
`Failed to connect: ${e instanceof Error ? e.message : "Unknown error"}`,
c.CONNECTION_FAILED
);
}
}
async disconnect() {
try {
this.isOpen = !1, this.reader && (await this.reader.cancel(), this.reader = null), this.writer && (await this.writer.close(), this.writer = null), this.port && (await this.port.close(), this.port = null), this.readBuffer = new Uint8Array(0);
} catch (e) {
throw new o(
`Failed to disconnect: ${e instanceof Error ? e.message : "Unknown error"}`,
c.CONNECTION_LOST
);
}
}
async write(e) {
if (!this.writer || !this.isOpen)
throw new o("Port is not connected", c.CONNECTION_LOST);
try {
await this.writer.write(e);
} catch (t) {
throw new o(
`Write error: ${t instanceof Error ? t.message : "Unknown error"}`,
c.WRITE_ERROR
);
}
}
async read(e, t) {
if (!this.reader || !this.isOpen)
throw new o("Port is not connected", c.CONNECTION_LOST);
const s = t ?? this.timeout, n = Date.now();
for (; this.readBuffer.length < e; ) {
if (Date.now() - n > s)
throw new o(
`Read timeout: expected ${e} bytes, got ${this.readBuffer.length}`,
c.TIMEOUT
);
await new Promise((i) => setTimeout(i, 1));
}
const a = this.readBuffer.slice(0, e);
return this.readBuffer = this.readBuffer.slice(e), a;
}
async flush() {
this.readBuffer = new Uint8Array(0);
}
isConnected() {
return this.isOpen && this.port !== null;
}
async startReading() {
if (this.reader)
try {
for (; this.isOpen; ) {
const { value: e, done: t } = await this.reader.read();
if (t) break;
e && this.appendToBuffer(e);
}
} catch (e) {
this.isOpen && console.error("Read error:", e);
}
}
appendToBuffer(e) {
const t = new Uint8Array(this.readBuffer.length + e.length);
t.set(this.readBuffer), t.set(e, this.readBuffer.length), this.readBuffer = t;
}
async waitForData(e, t) {
const s = t ?? this.timeout, n = Date.now();
for (; !e(this.readBuffer); ) {
if (Date.now() - n > s)
throw new o("Wait for data timeout", c.TIMEOUT);
await new Promise((a) => setTimeout(a, 1));
}
}
getBufferLength() {
return this.readBuffer.length;
}
peekBuffer(e) {
return this.readBuffer.slice(0, Math.min(e, this.readBuffer.length));
}
}
class O {
createPacket(e, t, s) {
const n = (s == null ? void 0 : s.length) ?? 0, a = D + n, i = new Uint8Array(a);
i[0] = E[0], i[1] = E[1], i[2] = e, i[3] = n + 2, i[4] = t, s && n > 0 && i.set(s, 5);
const d = this.calculateChecksum(i.slice(2, a - 1));
return i[a - 1] = d, i;
}
parsePacket(e) {
if (e.length < D)
throw new o(
`Packet too short: ${e.length} bytes`,
c.INVALID_RESPONSE
);
if (e[0] !== E[0] || e[1] !== E[1])
throw new o(
"Invalid packet header",
c.INVALID_RESPONSE
);
const t = e[2], s = e[3], n = e[4], a = T + P + m + s;
if (e.length < a)
throw new o(
`Incomplete packet: expected ${a} bytes, got ${e.length}`,
c.INVALID_RESPONSE
);
const i = s - 2, d = i > 0 ? e.slice(5, 5 + i) : new Uint8Array(0), l = e[a - 1], _ = this.calculateChecksum(
e.slice(2, a - 1)
);
if (l !== _)
throw new o(
`Checksum mismatch: expected ${_}, got ${l}`,
c.CHECKSUM_ERROR,
t
);
return {
id: t,
instruction: 0,
// Response packets don't have instruction
parameters: d,
length: s,
error: n,
checksum: l
};
}
calculateChecksum(e) {
let t = 0;
for (const s of e)
t += s;
return ~t & 255;
}
createPingPacket(e) {
return this.createPacket(e, w.PING);
}
createReadPacket(e, t, s) {
const n = new Uint8Array([t, s]);
return this.createPacket(e, w.READ, n);
}
createWritePacket(e, t, s) {
const n = new Uint8Array(1 + s.length);
return n[0] = t, n.set(s, 1), this.createPacket(e, w.WRITE, n);
}
createSyncWritePacket(e, t, s) {
let n = 0;
for (const d of s)
n += 1 + d.values.length;
const a = new Uint8Array(2 + n);
a[0] = e, a[1] = t;
let i = 2;
for (const d of s)
a[i] = d.id, a.set(d.values, i + 1), i += 1 + d.values.length;
return this.createPacket(g, w.SYNC_WRITE, a);
}
createSyncReadPacket(e, t, s) {
const n = new Uint8Array(2 + s.length);
return n[0] = e, n[1] = t, n.set(s, 2), this.createPacket(g, w.SYNC_READ, n);
}
checkError(e) {
if (e.error === 0) return;
const t = [];
if (e.error & u.VOLTAGE && t.push("Input voltage error"), e.error & u.ANGLE && t.push("Angle limit error"), e.error & u.OVERHEAT && t.push("Overheat error"), e.error & u.RANGE && t.push("Out of range error"), e.error & u.CHECKSUM && t.push("Checksum error"), e.error & u.OVERLOAD && t.push("Overload error"), e.error & u.INSTRUCTION && t.push("Invalid instruction error"), t.length > 0)
throw new o(
`Servo error (0x${e.error.toString(16)}): ${t.join(", ")}`,
c.HARDWARE_ERROR,
e.id
);
}
isValidPacketStart(e) {
return e.length >= 2 && e[0] === E[0] && e[1] === E[1];
}
getPacketLength(e) {
return e.length < 4 ? null : T + P + m + e[3];
}
}
function U(r, e = "scs_series") {
const t = N[e] || 4096;
return Math.round(r / 360 * t);
}
function v(r, e = "scs_series") {
const t = N[e] || 4096;
return r / t * 360;
}
function se(r) {
return Math.round(r / 114 * S);
}
function re(r) {
return r / S * 114;
}
function z(r = "scs_series") {
var e;
return ((e = C[r]) == null ? void 0 : e.maxPosition) ?? J;
}
function b(r = "scs_series") {
var e;
return ((e = C[r]) == null ? void 0 : e.minPosition) ?? Z;
}
function y(r, e = "scs_series") {
const t = b(e), s = z(e);
return Math.max(t, Math.min(s, r));
}
function B(r) {
return Math.max(ee, Math.min(S, r));
}
function V(r) {
if (r < 0 || r > 253)
throw new Error(`Invalid servo ID: ${r}. Must be between 0 and 253.`);
}
function ne(r) {
return new Promise((e) => setTimeout(e, r));
}
function ae(r) {
let e = 0;
for (const t of r)
e += t;
return ~e & 255;
}
function ie(r, e) {
const t = Array.isArray(r) ? r : [r], s = Array.isArray(e) ? e : [e];
return t.map((a, i) => {
const d = s[i] || s[0], l = N[d] || 4096;
return Math.round(a / 180 * (l / 2));
});
}
function W(r, e = 2) {
return `0x${r.toString(16).padStart(e, "0").toUpperCase()}`;
}
function oe(r) {
return Array.from(r).map((e) => W(e)).join(" ");
}
function G() {
return "serial" in navigator;
}
async function ce() {
if (!G())
throw new Error("Web Serial API is not supported in this browser");
try {
return await navigator.serial.requestPort();
} catch (r) {
if (r instanceof Error && r.name === "NotFoundError")
return null;
throw r;
}
}
const x = {
0: 1e6,
1: 5e5,
2: 25e4,
3: 128e3,
4: 115200,
5: 57600,
6: 38400,
7: 19200
};
function $(r) {
for (const [e, t] of Object.entries(x))
if (t === r) return Number(e);
throw new Error(`Unsupported baud rate ${r}`);
}
function de(r) {
return x[r] ?? 0;
}
const Ie = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
__proto__: null,
bytesToHexString: oe,
calculateChecksum: ae,
clampPosition: y,
clampSpeed: B,
convertDegreesToSteps: ie,
degreesToPosition: U,
formatHex: W,
getActualBaudRate: de,
getBaudRateValue: $,
getModelMaxPosition: z,
getModelMinPosition: b,
isWebSerialSupported: G,
positionToDegrees: v,
requestPort: ce,
rpmToSpeed: se,
sleep: ne,
speedToRpm: re,
validateServoId: V
}, Symbol.toStringTag, { value: "Module" }));
class he {
// Default model
constructor(e, t = p) {
this.portHandler = e, this.timeout = t, this.model = "scs_series", this.packetHandler = new O();
}
setModel(e) {
this.model = e;
}
async sendAndReceive(e, t) {
for (await this.portHandler.flush(), await this.portHandler.write(e); ; ) {
await this.portHandler.waitForData((_) => _.length >= 2, this.timeout);
const l = this.portHandler.peekBuffer(2);
if (l[0] === 255 && l[1] === 255) break;
await this.portHandler.read(1);
}
const s = await this.portHandler.read(4), n = this.packetHandler.getPacketLength(s);
if (n === null)
throw new o("Invalid packet length", c.INVALID_RESPONSE);
const a = await this.portHandler.read(n - 4), i = new Uint8Array(n);
i.set(s), i.set(a, 4);
const d = this.packetHandler.parsePacket(i);
if (d.id !== t)
throw new o(
`ID mismatch: expected ${t}, got ${d.id}`,
c.INVALID_RESPONSE
);
return this.packetHandler.checkError(d), d;
}
async ping(e) {
const t = this.packetHandler.createPingPacket(e);
await this.sendAndReceive(t, e);
let s = 0, n = 0;
try {
const a = await this.readData(e, 3, 2);
s = f(a[0], a[1]), n = (await this.readData(e, 2, 1))[0];
} catch {
}
return { id: e, modelNumber: s, firmwareVersion: n };
}
async readData(e, t, s) {
const n = this.packetHandler.createReadPacket(e, t, s), a = await this.sendAndReceive(n, e);
if (a.parameters.length < s)
throw new o(
`Invalid read response: expected ${s} bytes, got ${a.parameters.length}`,
c.INVALID_RESPONSE,
e
);
return a.parameters.slice(0, s);
}
async writeData(e, t, s) {
const n = this.packetHandler.createWritePacket(e, t, s);
await this.sendAndReceive(n, e), await new Promise((a) => setTimeout(a, I));
}
async readByte(e, t) {
return (await this.readData(e, t, 1))[0];
}
async writeByte(e, t, s) {
const n = new Uint8Array([s & 255]);
await this.writeData(e, t, n);
}
async readWord(e, t) {
const s = await this.readData(e, t, 2);
return f(s[0], s[1]);
}
async writeWord(e, t, s) {
const n = new Uint8Array([H(s), k(s)]);
await this.writeData(e, t, n);
}
async readPosition(e) {
const t = h(this.model, "Present_Position");
if (!t) throw new o("Present_Position not found in control table", c.INVALID_PARAMETER);
return await this.readWord(e, t.address);
}
async writePosition(e, t, s) {
if (s !== void 0) {
const a = h(this.model, "Goal_Speed");
if (!a) throw new o("Goal_Speed not found in control table", c.INVALID_PARAMETER);
await this.writeWord(e, a.address, s);
}
const n = h(this.model, "Goal_Position");
if (!n) throw new o("Goal_Position not found in control table", c.INVALID_PARAMETER);
await this.writeWord(e, n.address, t);
}
async setTorque(e, t) {
const s = h(this.model, "Torque_Enable");
if (!s) throw new o("Torque_Enable not found in control table", c.INVALID_PARAMETER);
await this.writeByte(
e,
s.address,
t ? Q : X
);
}
async readStatus(e) {
const t = h(this.model, "Present_Position"), s = h(this.model, "Present_Voltage"), n = h(this.model, "Moving");
if (!t || !s || !n)
throw new o("Required status entries not found in control table", c.INVALID_PARAMETER);
const a = await this.readData(
e,
t.address,
8
// Position(2) + Speed(2) + Load(2) + Voltage(1) + Temperature(1)
);
return {
position: f(a[0], a[1]),
speed: f(a[2], a[3]),
load: f(a[4], a[5]),
voltage: a[6] / 10,
// Convert to volts
temperature: a[7],
moving: await this.readByte(e, n.address) === 1,
errorStatus: 0
// Will be set from last response error
};
}
async setID(e, t) {
if (t < 0 || t > 253)
throw new o(
"Invalid ID: must be between 0 and 253",
c.INVALID_PARAMETER
);
const s = h(this.model, "ID");
if (!s) throw new o("ID not found in control table", c.INVALID_PARAMETER);
await this.writeByte(e, s.address, t);
}
async setBaudRate(e, t) {
let s;
try {
s = $(t);
} catch {
throw new o(
`Unsupported baud rate: ${t}`,
c.INVALID_PARAMETER
);
}
const n = h(this.model, "Baud_Rate");
if (!n) throw new o("Baud_Rate not found in control table", c.INVALID_PARAMETER);
await this.writeByte(e, n.address, s), await new Promise((a) => setTimeout(a, I));
}
async factoryReset(e) {
const t = this.packetHandler.createPacket(e, w.FACTORY_RESET);
await this.sendAndReceive(t, e), await new Promise((s) => setTimeout(s, 500));
}
async reboot(e) {
const t = this.packetHandler.createPacket(e, w.REBOOT);
await this.sendAndReceive(t, e), await new Promise((s) => setTimeout(s, 500));
}
}
class le {
constructor(e, t, s) {
this.portHandler = e, this.address = t, this.dataLength = s, this.dataMap = /* @__PURE__ */ new Map(), this.packetHandler = new O();
}
addParam(e, t) {
if (t.length !== this.dataLength)
throw new o(
`Data length mismatch: expected ${this.dataLength}, got ${t.length}`,
c.INVALID_PARAMETER,
e
);
this.dataMap.set(e, new Uint8Array(t));
}
removeParam(e) {
this.dataMap.delete(e);
}
clearParam() {
this.dataMap.clear();
}
async txPacket() {
if (this.dataMap.size === 0)
throw new o(
"No parameters to send",
c.INVALID_PARAMETER
);
const e = [];
for (const [s, n] of this.dataMap)
e.push({ id: s, values: n });
const t = this.packetHandler.createSyncWritePacket(
this.address,
this.dataLength,
e
);
await this.portHandler.flush(), await this.portHandler.write(t), await new Promise((s) => setTimeout(s, I));
}
getAddress() {
return this.address;
}
getDataLength() {
return this.dataLength;
}
getParamCount() {
return this.dataMap.size;
}
hasParam(e) {
return this.dataMap.has(e);
}
getParam(e) {
const t = this.dataMap.get(e);
return t ? new Uint8Array(t) : void 0;
}
}
class ue {
constructor(e, t, s, n = p) {
this.portHandler = e, this.address = t, this.dataLength = s, this.timeout = n, this.idList = /* @__PURE__ */ new Set(), this.dataMap = /* @__PURE__ */ new Map(), this.packetHandler = new O();
}
addParam(e) {
this.idList.add(e);
}
removeParam(e) {
this.idList.delete(e), this.dataMap.delete(e);
}
clearParam() {
this.idList.clear(), this.dataMap.clear();
}
async txRxPacket() {
if (this.idList.size === 0)
throw new o(
"No parameters to read",
c.INVALID_PARAMETER
);
this.dataMap.clear();
const e = Array.from(this.idList), t = this.packetHandler.createSyncReadPacket(
this.address,
this.dataLength,
e
);
await this.portHandler.flush(), await this.portHandler.write(t);
for (const s of e)
try {
const n = await this.readResponse(s);
n.parameters.length >= this.dataLength && this.dataMap.set(s, n.parameters.slice(0, this.dataLength));
} catch (n) {
console.error(`Failed to read from servo ${s}:`, n);
}
}
async readResponse(e) {
for (; ; ) {
await this.portHandler.waitForData((l) => l.length >= 2, this.timeout);
const d = this.portHandler.peekBuffer(2);
if (d[0] === 255 && d[1] === 255) break;
await this.portHandler.read(1);
}
const t = await this.portHandler.read(4), s = this.packetHandler.getPacketLength(t);
if (s === null)
throw new o("Invalid packet length", c.INVALID_RESPONSE);
const n = await this.portHandler.read(s - 4), a = new Uint8Array(s);
a.set(t), a.set(n, 4);
const i = this.packetHandler.parsePacket(a);
if (i.id !== e)
throw new o(
`ID mismatch: expected ${e}, got ${i.id}`,
c.INVALID_RESPONSE
);
return this.packetHandler.checkError(i), i;
}
isAvailable(e, t, s) {
const n = this.dataMap.get(e);
return !!n && n.length >= this.dataLength;
}
getData(e, t, s) {
const n = this.dataMap.get(e);
return n ? new Uint8Array(n) : void 0;
}
getAddress() {
return this.address;
}
getDataLength() {
return this.dataLength;
}
getParamCount() {
return this.idList.size;
}
hasParam(e) {
return this.idList.has(e);
}
}
class ye {
constructor(e = {}) {
this.isConnected = !1, this.motors = {};
const {
port: t = { baudRate: M },
motors: s = {},
defaultModel: n = L.SCS_SERIES,
timeout: a = p
} = e;
this.portHandler = new te(t, a), this.protocolHandler = new he(this.portHandler, a), this.motors = s, this.defaultModel = n, this.protocolHandler.setModel(n);
}
getModelByServoId(e) {
for (const t in this.motors) {
const s = this.motors[t];
if (s.id === e) return s.model;
}
return this.defaultModel;
}
setMotors(e) {
this.motors = e;
}
getMotorModel(e) {
return this.motors[e] ? this.motors[e].model : this.defaultModel;
}
setDefaultModel(e) {
this.defaultModel = e, this.protocolHandler.setModel(e);
}
async connect() {
await this.portHandler.connect(), this.isConnected = !0;
}
async disconnect() {
await this.portHandler.disconnect(), this.isConnected = !1;
}
getConnectionStatus() {
return this.isConnected && this.portHandler.isConnected();
}
async ping(e) {
return this.ensureConnected(), await this.protocolHandler.ping(e);
}
async scan(e = 0, t = 253) {
this.ensureConnected();
const s = [];
for (let n = e; n <= t; n++)
try {
const a = await this.ping(n);
s.push(a);
} catch {
}
return s;
}
async readPosition(e) {
return this.ensureConnected(), await this.protocolHandler.readPosition(e);
}
async writePosition(e, t, s) {
this.ensureConnected();
const n = this.getModelByServoId(e), a = y(t, n), i = s !== void 0 ? B(s) : void 0;
await this.protocolHandler.writePosition(e, a, i);
}
async setPositionInDegrees(e, t, s) {
const n = this.getModelByServoId(e), a = U(t, n);
await this.writePosition(e, a, s);
}
async getPositionInDegrees(e) {
const t = await this.readPosition(e), s = this.getModelByServoId(e);
return v(t, s);
}
async enableTorque(e) {
this.ensureConnected(), await this.protocolHandler.setTorque(e, !0);
}
async disableTorque(e) {
this.ensureConnected(), await this.protocolHandler.setTorque(e, !1);
}
async readStatus(e) {
return this.ensureConnected(), await this.protocolHandler.readStatus(e);
}
async setID(e, t) {
this.ensureConnected(), V(t), await this.protocolHandler.setID(e, t);
}
async setBaudRate(e, t) {
this.ensureConnected(), await this.protocolHandler.setBaudRate(e, t);
}
async factoryReset(e) {
this.ensureConnected(), await this.protocolHandler.factoryReset(e);
}
async reboot(e) {
this.ensureConnected(), await this.protocolHandler.reboot(e);
}
createSyncWrite(e, t) {
return this.ensureConnected(), new le(this.portHandler, e, t);
}
createSyncRead(e, t) {
return this.ensureConnected(), new ue(this.portHandler, e, t);
}
async syncWritePosition(e) {
this.ensureConnected();
const t = h(this.defaultModel, "Goal_Position");
if (!t) throw new o("Goal_Position not found in control table", c.INVALID_PARAMETER);
const s = this.createSyncWrite(t.address, t.size);
for (const n of e) {
const a = this.getModelByServoId(n.id), i = y(n.position, a), d = new Uint8Array([
H(i),
k(i)
]);
s.addParam(n.id, d);
}
await s.txPacket();
}
async syncReadPosition(e) {
this.ensureConnected();
const t = h(this.defaultModel, "Present_Position");
if (!t) throw new o("Present_Position not found in control table", c.INVALID_PARAMETER);
const s = this.createSyncRead(t.address, t.size);
for (const a of e)
s.addParam(a);
await s.txRxPacket();
const n = /* @__PURE__ */ new Map();
for (const a of e) {
const i = s.getData(a);
i && i.length >= 2 && n.set(a, f(i[0], i[1]));
}
return n;
}
async readByte(e, t) {
return this.ensureConnected(), await this.protocolHandler.readByte(e, t);
}
async writeByte(e, t, s) {
this.ensureConnected(), await this.protocolHandler.writeByte(e, t, s);
}
async readWord(e, t) {
return this.ensureConnected(), await this.protocolHandler.readWord(e, t);
}
async writeWord(e, t, s) {
this.ensureConnected(), await this.protocolHandler.writeWord(e, t, s);
}
async readData(e, t, s) {
return this.ensureConnected(), await this.protocolHandler.readData(e, t, s);
}
async writeData(e, t, s) {
this.ensureConnected(), await this.protocolHandler.writeData(e, t, s);
}
ensureConnected() {
if (!this.getConnectionStatus())
throw new o(
"Not connected to serial port",
c.CONNECTION_LOST
);
}
}
export {
g as BROADCAST_ID,
Ee as CALIBRATION_REQUIRED,
Te as CENTER_POSITION_VALUE,
j as CHECKSUM_LENGTH,
fe as CONVERT_UINT32_TO_INT32_REQUIRED,
q as CalibrationMode,
M as DEFAULT_BAUDRATE,
Ae as DEFAULT_RETRY_COUNT,
p as DEFAULT_TIMEOUT,
Y as DriveMode,
Re as ERROR_LENGTH,
u as ErrorBit,
c as ErrorCode,
ye as FeetechServo,
E as HEADER,
T as HEADER_LENGTH,
P as ID_LENGTH,
K as INSTRUCTION_LENGTH,
w as Instruction,
I as LATENCY_TIMER,
m as LENGTH_LENGTH,
pe as MAX_ID,
J as MAX_POSITION_VALUE,
S as MAX_SPEED_VALUE,
D as MIN_PACKET_LENGTH,
Z as MIN_POSITION_VALUE,
ee as MIN_SPEED_VALUE,
we as MODEL_BAUDRATE_TABLE,
C as MODEL_CONFIGS,
F as MODEL_CONTROL_TABLE,
N as MODEL_RESOLUTION,
L as MotorModel,
A as SCS_SERIES_BAUDRATE_TABLE,
R as SCS_SERIES_CONTROL_TABLE,
o as ServoError,
me as TEMPERATURE_UNIT,
X as TORQUE_DISABLE,
Q as TORQUE_ENABLE,
Pe as VOLTAGE_UNIT,
_e as assertSameAddress,
ye as default,
h as getControlTableEntry,
k as getHighByte,
H as getLowByte,
Ie as helpers,
f as makeWord
};