/** * 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; read(timeout?: number): Promise; } /** * Read single motor position with retry logic */ export async function readMotorPosition( port: MotorCommunicationPort, motorId: number ): Promise { 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 { 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 { // 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 { // 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 { 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 { 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 { 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 { for (const motorId of motorIds) { await releaseMotor(port, motorId); // Small delay between motors await new Promise((resolve) => setTimeout(resolve, STS3215_PROTOCOL.INTER_MOTOR_DELAY) ); } }