Spaces:
Running
Running
/** | |
* Motor Communication Utilities | |
* STS3215 motor reading and writing operations | |
*/ | |
import { STS3215_PROTOCOL } from "./sts3215-protocol.js"; | |
/** | |
* Interface for motor communication port | |
* Compatible with both WebSerialPortWrapper and RobotConnectionManager | |
*/ | |
export interface MotorCommunicationPort { | |
write(data: Uint8Array): Promise<void>; | |
read(timeout?: number): Promise<Uint8Array>; | |
} | |
/** | |
* Read single motor position with retry logic | |
*/ | |
export async function readMotorPosition( | |
port: MotorCommunicationPort, | |
motorId: number | |
): Promise<number | null> { | |
try { | |
// Create Read Position packet | |
const packet = new Uint8Array([ | |
0xff, | |
0xff, // Header | |
motorId, // Servo ID | |
0x04, // Length | |
0x02, // Instruction: READ_DATA | |
STS3215_PROTOCOL.PRESENT_POSITION_ADDRESS, // Present_Position register address | |
0x02, // Data length (2 bytes) | |
0x00, // Checksum placeholder | |
]); | |
const checksum = | |
~( | |
motorId + | |
0x04 + | |
0x02 + | |
STS3215_PROTOCOL.PRESENT_POSITION_ADDRESS + | |
0x02 | |
) & 0xff; | |
packet[7] = checksum; | |
// Retry communication with timeouts | |
let attempts = 0; | |
while (attempts < STS3215_PROTOCOL.MAX_RETRIES) { | |
attempts++; | |
// Clear any remaining data in buffer first | |
try { | |
await port.read(0); // Non-blocking read to clear buffer | |
} catch (e) { | |
// Expected - buffer was empty | |
} | |
// Write command | |
await port.write(packet); | |
// Wait for motor response | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.WRITE_TO_READ_DELAY) | |
); | |
try { | |
const response = await port.read(150); | |
if (response.length >= 7) { | |
const id = response[2]; | |
const error = response[4]; | |
if (id === motorId && error === 0) { | |
const position = response[5] | (response[6] << 8); | |
return position; | |
} | |
} | |
} catch (readError) { | |
// Read timeout, retry | |
} | |
// Wait between retry attempts | |
if (attempts < STS3215_PROTOCOL.MAX_RETRIES) { | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.RETRY_DELAY) | |
); | |
} | |
} | |
// If all attempts failed, return null | |
return null; | |
} catch (error) { | |
return null; | |
} | |
} | |
/** | |
* Read all motor positions | |
*/ | |
export async function readAllMotorPositions( | |
port: MotorCommunicationPort, | |
motorIds: number[] | |
): Promise<number[]> { | |
const motorPositions: number[] = []; | |
for (let i = 0; i < motorIds.length; i++) { | |
const motorId = motorIds[i]; | |
const position = await readMotorPosition(port, motorId); | |
if (position !== null) { | |
motorPositions.push(position); | |
} else { | |
// Use fallback value for failed reads | |
const fallback = Math.floor((STS3215_PROTOCOL.RESOLUTION - 1) / 2); | |
motorPositions.push(fallback); | |
} | |
// Delay between motor reads | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.INTER_MOTOR_DELAY) | |
); | |
} | |
return motorPositions; | |
} | |
/** | |
* Write motor goal position | |
*/ | |
export async function writeMotorPosition( | |
port: MotorCommunicationPort, | |
motorId: number, | |
position: number | |
): Promise<void> { | |
// STS3215 Write Goal_Position packet | |
const packet = new Uint8Array([ | |
0xff, | |
0xff, // Header | |
motorId, // Servo ID | |
0x05, // Length | |
0x03, // Instruction: WRITE_DATA | |
STS3215_PROTOCOL.GOAL_POSITION_ADDRESS, // Goal_Position register address | |
position & 0xff, // Position low byte | |
(position >> 8) & 0xff, // Position high byte | |
0x00, // Checksum placeholder | |
]); | |
// Calculate checksum | |
const checksum = | |
~( | |
motorId + | |
0x05 + | |
0x03 + | |
STS3215_PROTOCOL.GOAL_POSITION_ADDRESS + | |
(position & 0xff) + | |
((position >> 8) & 0xff) | |
) & 0xff; | |
packet[8] = checksum; | |
await port.write(packet); | |
} | |
/** | |
* Generic function to write a 2-byte value to a motor register | |
*/ | |
export async function writeMotorRegister( | |
port: MotorCommunicationPort, | |
motorId: number, | |
registerAddress: number, | |
value: number | |
): Promise<void> { | |
// Create Write Register packet | |
const packet = new Uint8Array([ | |
0xff, | |
0xff, // Header | |
motorId, // Servo ID | |
0x05, // Length | |
0x03, // Instruction: WRITE_DATA | |
registerAddress, // Register address | |
value & 0xff, // Data_L (low byte) | |
(value >> 8) & 0xff, // Data_H (high byte) | |
0x00, // Checksum placeholder | |
]); | |
// Calculate checksum | |
const checksum = | |
~( | |
motorId + | |
0x05 + | |
0x03 + | |
registerAddress + | |
(value & 0xff) + | |
((value >> 8) & 0xff) | |
) & 0xff; | |
packet[8] = checksum; | |
// Write register value | |
await port.write(packet); | |
// Wait for response (silent unless error) | |
try { | |
await port.read(200); | |
} catch (error) { | |
// Silent - response not required for successful operation | |
} | |
} | |
/** | |
* Lock a motor (motor will hold its position and resist movement) | |
*/ | |
export async function lockMotor( | |
port: MotorCommunicationPort, | |
motorId: number | |
): Promise<void> { | |
await writeMotorRegister( | |
port, | |
motorId, | |
STS3215_PROTOCOL.TORQUE_ENABLE_ADDRESS, | |
1 | |
); | |
// Small delay for command processing | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.WRITE_TO_READ_DELAY) | |
); | |
} | |
/** | |
* Release a motor (motor can be moved freely by hand) | |
*/ | |
export async function releaseMotor( | |
port: MotorCommunicationPort, | |
motorId: number | |
): Promise<void> { | |
await writeMotorRegister( | |
port, | |
motorId, | |
STS3215_PROTOCOL.TORQUE_ENABLE_ADDRESS, | |
0 | |
); | |
// Small delay for command processing | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.WRITE_TO_READ_DELAY) | |
); | |
} | |
/** | |
* Lock motors (motors will hold their positions - perfect after calibration) | |
*/ | |
export async function lockMotors( | |
port: MotorCommunicationPort, | |
motorIds: number[] | |
): Promise<void> { | |
for (const motorId of motorIds) { | |
await lockMotor(port, motorId); | |
// Small delay between motors | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.INTER_MOTOR_DELAY) | |
); | |
} | |
} | |
/** | |
* Release motors (motors can be moved freely - perfect for calibration) | |
*/ | |
export async function releaseMotors( | |
port: MotorCommunicationPort, | |
motorIds: number[] | |
): Promise<void> { | |
for (const motorId of motorIds) { | |
await releaseMotor(port, motorId); | |
// Small delay between motors | |
await new Promise((resolve) => | |
setTimeout(resolve, STS3215_PROTOCOL.INTER_MOTOR_DELAY) | |
); | |
} | |
} | |