Package detail

dynamixel

andyshinn203MIT0.0.5

Node.js library for controlling DYNAMIXEL servo motors via U2D2 interface with Protocol 2.0 support

dynamixel, servo, motor, robotics

readme

node-dynamixel

A Node.js library for controlling DYNAMIXEL servo motors using Protocol 2.0 via the U2D2 USB-to-TTL converter.

Features

  • DYNAMIXEL Protocol 2.0 implementation with full CRC validation
  • U2D2 USB Communication for reliable device connection
  • Device Discovery using Ping packets
  • Individual Device Control with convenient methods
  • Event-driven Architecture for real-time monitoring
  • TypeScript-like JSDoc annotations for better IDE support
  • Cross-platform support (Linux, macOS, Windows)
  • Electron & Web Serial API support for desktop applications
  • Multiple Connection Types (USB, Serial, Web Serial)
  • 🆕 Separated Device Discovery for better Electron app integration
  • 🆕 Advanced Alarm Management with intelligent thresholds and monitoring
  • 🆕 Motor Profiles System for optimal motor configurations
  • 🆕 Enhanced Logging with performance metrics and structured output
  • 🆕 Multi-Motor Synchronization optimization settings

Supported Devices

This library supports all DYNAMIXEL devices that use Protocol 2.0, including:

  • X Series: XL-320, XL330, XL430, XC330, XC430, XM430, XM540, XH430, XH540
  • P Series: PH42, PH54, PM42, PM54
  • Y Series: YM070, YM080
  • MX Series (2.0): MX-28(2.0), MX-64(2.0), MX-106(2.0)

Hardware Requirements

  • U2D2 USB-to-TTL converter (ROBOTIS U2D2)
  • Compatible DYNAMIXEL servo motors
  • Appropriate power supply for your servos

Installation

npm install dynamixel
# or
yarn add dynamixel

Additional Dependencies

  • For Node.js USB support: npm install usb (optional, may require sudo)
  • For Node.js Serial support: npm install serialport (included as dependency)
  • For Electron: No additional dependencies required (Web Serial API built-in)
  • For Browsers: Use Web Serial API (Chrome/Edge 89+, no installation needed)

Quick Start

Node.js / Server-side

import { DynamixelController } from 'dynamixel';

async function main() {
  // Create controller (auto-detects best connection method)
  const controller = new DynamixelController();

  // Connect to U2D2
  await controller.connect();

  // Discover devices
  const devices = await controller.quickDiscovery();
  console.log(`Found ${devices.length} DYNAMIXEL devices`);

  // Control first device
  if (devices.length > 0) {
    const device = controller.getDevice(devices[0].id);

    // Enable torque
    await device.setTorqueEnable(true);

    // Move to position (90 degrees)
    await device.setGoalPosition(device.degreesToPosition(90));

    // Wait for movement to complete
    while (await device.isMoving()) {
      await new Promise(resolve => setTimeout(resolve, 100));
    }

    console.log('Movement complete!');
  }

  // Cleanup
  await controller.disconnect();
}

main().catch(console.error);

Electron Renderer Process

import { DynamixelController } from 'dynamixel';

async function main() {
  // Create controller for Web Serial API (Electron renderer)
  const controller = new DynamixelController({
    connectionType: 'webserial'  // Use Web Serial API
  });

  // Connect (will show browser serial port selection dialog)
  await controller.connect();

  // Rest of the code is the same as Node.js example...
  const devices = await controller.quickDiscovery();
  console.log(`Found ${devices.length} DYNAMIXEL devices`);
}

main().catch(console.error);

📋 For complete Electron setup instructions, see Electron Setup Guide

Separated Device Discovery (for Electron Apps)

For Electron applications, you can separate device discovery from motor discovery to provide better user experience:

import { DynamixelController } from 'dynamixel';

// Step 1: Discover available devices (no connection)
const devices = await DynamixelController.discoverCommunicationDevices();
console.log(`Found ${devices.usb.length} USB and ${devices.serial.length} serial devices`);

// Step 2: Get U2D2-specific devices
const u2d2Devices = await DynamixelController.discoverU2D2Devices();

// Step 3: Create controller with deferred connection
const controller = new DynamixelController({
  deferConnection: true // Don't connect immediately
});

// Step 4: Connect to specific device (user selection)
const selectedDevice = u2d2Devices[0]; // From UI selection
await controller.connectToDevice(selectedDevice);

// Step 5: Now discover motors
const motors = await controller.quickDiscovery();
console.log(`Found ${motors.length} motors`);

📋 For complete separated discovery guide, see Enhanced Features

Architecture

The library is organized into logical layers for maintainability and extensibility:

  • src/transport/ - Communication layer (USB, Serial, Web Serial)
  • src/dynamixel/ - Protocol and device logic (Protocol 2.0, device control)
  • src/DynamixelController.js - Main orchestration layer

📋 For detailed architecture documentation, see Architecture Guide

API Reference

DynamixelController

Main controller class for managing DYNAMIXEL devices.

Constructor

const controller = new DynamixelController(options);

Options:

  • connectionType (string): Connection type - 'auto', 'usb', 'serial', 'webserial' (default: 'auto')
  • timeout (number): Default timeout in milliseconds (default: 1000)
  • debug (boolean): Enable debugging output (default: false)
  • baudRate (number): Serial baud rate (default: 57600)
  • portPath (string): Specific serial port path (for serial connections)

Methods

  • connect()Promise<boolean> - Connect to U2D2 device
  • disconnect()Promise<void> - Disconnect from U2D2 device
  • ping(id, timeout?)Promise<Object> - Ping specific device
  • discoverDevices(options?)Promise<Array> - Discover all devices
  • quickDiscovery(onProgress?)Promise<Array> - Quick scan (IDs 1-20)
  • fullDiscovery(onProgress?)Promise<Array> - Full scan (IDs 1-252)
  • getDevice(id)DynamixelDevice|null - Get device by ID
  • getAllDevices()Array<DynamixelDevice> - Get all discovered devices

Events

  • 'connected' - U2D2 connection established
  • 'disconnected' - U2D2 connection lost
  • 'deviceFound' - New device discovered
  • 'discoveryComplete' - Device discovery finished
  • 'error' - Error occurred

DynamixelDevice

Individual device control class.

Methods

Basic Control:

  • ping(timeout?)Promise<Object> - Ping device
  • read(address, length)Promise<Buffer> - Read from control table
  • write(address, data)Promise<boolean> - Write to control table

Convenience Methods:

  • setTorqueEnable(enable)Promise<boolean> - Enable/disable torque
  • getTorqueEnable()Promise<boolean> - Get torque status
  • setGoalPosition(position)Promise<boolean> - Set goal position
  • getGoalPosition()Promise<number> - Get goal position
  • getPresentPosition()Promise<number> - Get current position
  • setGoalVelocity(velocity)Promise<boolean> - Set goal velocity
  • getPresentVelocity()Promise<number> - Get current velocity
  • getPresentTemperature()Promise<number> - Get temperature (°C)
  • getPresentVoltage()Promise<number> - Get voltage (0.1V units)
  • setLED(on)Promise<boolean> - Control LED
  • isMoving()Promise<boolean> - Check if device is moving

Utility Methods:

  • positionToDegrees(position)number - Convert position to degrees
  • degreesToPosition(degrees)number - Convert degrees to position
  • velocityToRPM(velocity)number - Convert velocity to RPM
  • rpmToVelocity(rpm)number - Convert RPM to velocity
  • voltageToVolts(reading)number - Convert voltage reading to volts

Connection Types

The library supports multiple connection methods and automatically detects the best one for your environment:

Auto-Detection (Recommended)

// Auto-detects best connection: Web Serial API in browsers/Electron, SerialPort in Node.js
const controller = new DynamixelController({ connectionType: 'auto' });

Web Serial API (Browsers & Electron)

// Force Web Serial API (for Electron renderer or modern browsers)
const controller = new DynamixelController({ connectionType: 'webserial' });

Node.js Serial Port

// Use Node.js SerialPort (no sudo required)
const controller = new DynamixelController({
  connectionType: 'serial',
  portPath: '/dev/ttyUSB0'  // Optional: specify port
});

USB Direct (Node.js)

// Direct USB communication (may require sudo on some systems)
const controller = new DynamixelController({ connectionType: 'usb' });

Examples

Device Discovery

import { DynamixelController } from 'dynamixel';

const controller = new DynamixelController();

// Event-driven discovery
controller.on('deviceFound', (device) => {
  console.log(`Found: ID ${device.id}, Model: ${device.modelNumber}`);
});

await controller.connect();
const devices = await controller.quickDiscovery();
console.log(`Discovery complete: ${devices.length} devices found`);

Position Control

const device = controller.getDevice(1);

// Enable torque
await device.setTorqueEnable(true);

// Move to 180 degrees
const goalPosition = device.degreesToPosition(180);
await device.setGoalPosition(goalPosition);

// Monitor movement
while (await device.isMoving()) {
  const currentPos = await device.getPresentPosition();
  const degrees = device.positionToDegrees(currentPos);
  console.log(`Current position: ${degrees.toFixed(1)}°`);

  await new Promise(resolve => setTimeout(resolve, 100));
}

Velocity Control

const device = controller.getDevice(1);

// Set to velocity control mode (if supported by model)
await device.setTorqueEnable(false); // Disable torque first
// ... set operating mode to velocity control ...
await device.setTorqueEnable(true);

// Set goal velocity (50 RPM)
const goalVelocity = device.rpmToVelocity(50);
await device.setGoalVelocity(goalVelocity);

Multiple Device Control

const devices = controller.getAllDevices();

// Control multiple devices simultaneously
const promises = devices.map(async (device) => {
  await device.setTorqueEnable(true);
  const randomPosition = Math.random() * 4095;
  await device.setGoalPosition(randomPosition);
});

await Promise.all(promises);
console.log('All devices moving!');

Enhanced Features (Inspired by DynaNode)

The library includes several advanced features for professional robotics applications:

Alarm Management

Advanced alarm system with intelligent thresholds and monitoring:

import { AlarmManager } from 'dynamixel';

const alarmManager = new AlarmManager();

// Set up alarm listeners
alarmManager.on('alarm', (alarm) => {
  console.log(`⚠️ ${alarm.severity}: ${alarm.message}`);
});

alarmManager.on('emergency_stop', (event) => {
  console.log(`🛑 Emergency stop for device ${event.deviceId}`);
  // Implement emergency stop logic
});

// Monitor device sensors
alarmManager.checkSensorAlarms(deviceId, {
  temperature: 75,  // °C
  voltage: 12.0,    // V
  load: 85          // %
});

// Process hardware error flags
alarmManager.processHardwareError(deviceId, errorFlags);

Motor Profiles

Predefined configurations for different motor models and applications:

import { MotorProfiles } from 'dynamixel';

const motorProfiles = new MotorProfiles();

// Get profile for specific motor
const profile = motorProfiles.getProfile('XM430-W350');
console.log('Max torque:', profile.specs.stallTorque, 'kg·cm');

// Get recommended settings for precision mode
const settings = motorProfiles.getRecommendedSettings('XM430-W350', 'precision');
await device.applySettings(settings);

// Get application profiles
const armProfile = motorProfiles.getProfile('ROBOT_ARM_6DOF');
console.log('Application:', armProfile.description);

// Multi-motor synchronization settings
const syncSettings = motorProfiles.getSynchronizationSettings(['XM430-W350', 'MX-28']);
console.log('Recommended sync velocity:', syncSettings.recommendedVelocity);

Enhanced Logging

Structured logging with performance metrics and filtering:

import { Logger } from 'dynamixel';

const logger = new Logger({
  level: 'debug',
  enablePerformanceMetrics: true
});

// Create device-specific logger
const deviceLogger = logger.forDevice(1);

// Performance measurement
const result = await logger.measureAsync('device_discovery', async () => {
  return await controller.discoverDevices();
});

// Protocol logging
logger.logPacketSent(deviceId, 'PING', [], { duration: 2.5 });
logger.logPacketReceived(deviceId, packet, { duration: 1.8 });

// Get filtered logs
const errorLogs = logger.getLogs({ level: 'error', deviceId: 1 });
const exportedLogs = logger.exportLogs('csv', { since: Date.now() - 3600000 });

Complete Enhanced Example

import {
  DynamixelController,
  AlarmManager,
  MotorProfiles,
  Logger
} from 'dynamixel';

// Setup enhanced features
const logger = new Logger({ level: 'debug', enablePerformanceMetrics: true });
const alarmManager = new AlarmManager();
const motorProfiles = new MotorProfiles();

const controller = new DynamixelController({
  connectionType: 'auto',
  logger: logger.forCategory('controller')
});

// Enhanced event handling
controller.on('device_discovered', (device) => {
  logger.info(`Device discovered: ${device.modelName} (ID: ${device.id})`);

  // Apply motor profile
  const profile = motorProfiles.getProfile(device.modelName);
  if (profile) {
    const settings = motorProfiles.getRecommendedSettings(device.modelName, 'balanced');
    // Apply settings to device...
  }
});

// Monitor devices with alarms
async function monitorDevice(device) {
  const status = await device.getStatus();

  // Check for alarms
  alarmManager.checkSensorAlarms(device.id, {
    temperature: status.temperature,
    voltage: status.voltage,
    load: status.load
  });

  // Process hardware errors
  if (status.hardwareError > 0) {
    alarmManager.processHardwareError(device.id, status.hardwareError);
  }
}

// Use enhanced discovery with monitoring
const devices = await logger.measureAsync('enhanced_discovery', async () => {
  return await controller.discoverDevices();
});

// Monitor all devices
for (const device of devices) {
  await monitorDevice(device);
}

Error Handling

The library includes comprehensive error handling:

try {
  await device.setGoalPosition(2048);
} catch (error) {
  if (error.message.includes('CRC mismatch')) {
    console.log('Communication error - check connections');
  } else if (error.message.includes('Timeout')) {
    console.log('Device not responding - check power and ID');
  } else {
    console.log('Device error:', error.message);
  }
}

Troubleshooting

U2D2 Not Found

  • Ensure U2D2 is connected via USB
  • Install FTDI drivers if needed
  • Check that device permissions allow access
  • Verify no other software is using the device

No Devices Found

  • Check DYNAMIXEL power supply
  • Verify baud rate (default: 57600 for Protocol 2.0)
  • Ensure devices are properly wired
  • Try different ID ranges in discovery

Communication Errors

  • Check cable connections
  • Verify proper termination resistors
  • Reduce baud rate if experiencing errors
  • Ensure adequate power supply

Protocol 2.0 Reference

This library implements DYNAMIXEL Protocol 2.0 as specified by ROBOTIS.

Key Features:

  • 16-bit CRC error detection
  • Extended ID range (0-252)
  • Improved packet structure
  • Enhanced error reporting

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

License

MIT License - see LICENSE file for details.

Links

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[Unreleased]

[0.0.5] - 2025-01-20

Added

  • Separated Discovery Pattern for Electron applications
    • discoverCommunicationDevices() - Discover USB/Serial devices without connecting
    • discoverU2D2Devices() - Find U2D2-compatible devices specifically
    • connectToDevice(device) - Connect to user-selected device
    • deferConnection option in DynamixelController constructor
    • Example: separated-discovery.js demonstrating the complete workflow
  • TypeScript Support
    • Complete TypeScript declaration files generation
    • Proper type annotations for all public APIs
    • deferConnection correctly typed as boolean (was any)
    • JSDoc type definitions for better IDE support
  • Build System Enhancements
    • Rollup configuration for ESM and CommonJS builds
    • Automatic TypeScript declaration generation
    • Support for both import and require usage patterns
    • Proper package.json exports configuration
  • Enhanced Examples
    • electron-main-process.js - Electron main process integration
    • electron-renderer.js - Electron renderer process integration
    • electron-dynamixel-service.js - Complete Electron service example
    • typescript-example.ts - TypeScript usage demonstration
    • prioritize-serial.js - Serial device prioritization example

Changed

  • Device Discovery Priority: Serial devices now properly prioritized over USB devices
  • Improved Error Handling: Better error messages and timeout handling
  • Enhanced Motor Property Reading: Motor model numbers and firmware versions now display correctly
  • Test Reliability: All 284 tests now pass consistently without hanging
  • Protocol Buffer Processing: Fixed infinite loop issues in packet processing
  • Connection Stability: Improved timeout handling and connection cleanup

Fixed

  • Critical Motor Discovery Bug: Fixed infinite loop in processReceiveBuffer() methods
    • Changed condition from packetLength === -1 to packetLength === 0
    • Added buffer size limits (1KB max) to prevent memory issues
    • Added maximum packets per processing call (10) to prevent infinite loops
    • Applied to all transport classes: SerialConnection, U2D2Connection
  • Motor Properties Null Issue: Fixed motor properties showing as null values
    • Root cause: ping() methods incorrectly parsing response buffers
    • Fixed: Proper parsing of raw buffers before extracting ping information
    • Now correctly displays: "Motor ID 1: XC430-W150 (Model: 1200), FW: 52"
  • Test Hanging Issue: Resolved Jest hanging on test completion
    • Fixed USB device handle leaks by using mocked connections
    • Fixed timeout handle leaks by clearing setTimeout properly
    • All tests now exit cleanly with --detectOpenHandles
  • DynamixelDevice Buffer Parsing: Fixed raw response buffer handling
    • read() and write() methods now properly parse status packets
    • Added Protocol2.parseStatusPacket() calls before error checking
    • Updated test mocks to return proper status packet buffers
  • Missing CommonJS Methods: Added missing discovery methods to CommonJS exports
    • discoverCommunicationDevices(), discoverU2D2Devices(), connectToDevice()
    • Fixed inconsistency between ESM and CommonJS module exports

Technical Improvements

  • Robust Packet Processing: Enhanced buffer processing with safety limits
  • Memory Management: Better cleanup of USB handles and timeouts
  • Test Infrastructure: Proper mock data generation for DYNAMIXEL Protocol 2.0
  • Error Propagation: Consistent error handling across transport layers
  • Documentation: Comprehensive examples for Electron integration patterns

Breaking Changes

  • None - All changes are backward compatible

Migration Guide

  • For Electron apps: Consider using the new separated discovery pattern for better UX
  • For TypeScript users: Update imports to get better type safety
  • For CommonJS users: No changes required, all new methods available

[0.0.4] - 2025-06-15

Added

  • Enhanced Alarm Management System (inspired by DynaNode architecture)
    • Intelligent alarm thresholds for temperature, voltage, and load monitoring
    • Hardware error flag processing with severity classification
    • Alarm history tracking and statistics
    • Emergency stop events for fatal conditions
  • Motor Profiles System for optimal motor configurations
    • Predefined profiles for AX, MX, and X series motors
    • Application-specific profiles (robot arms, mobile robots, grippers)
    • Recommended settings for different use cases (precision, balanced, dynamic)
    • Multi-motor synchronization optimization settings
  • Enhanced Logging System with structured output
    • Performance metrics tracking and measurement tools
    • Protocol-level logging for debugging
    • Device-specific and category-specific loggers
    • Log filtering, export (JSON/CSV/text), and statistics
    • Configurable log levels with environment variable support
  • Comprehensive Test Suite for enhanced features
    • 108 unit tests covering AlarmManager, MotorProfiles, and Logger
    • 17 integration tests demonstrating real-world usage scenarios
    • Enhanced features documentation and examples

Changed

  • Reorganized src directory structure for better separation of concerns
  • Moved connection classes to src/transport/ directory
  • Moved DYNAMIXEL-specific classes to src/dynamixel/ directory
  • Updated main entry point exports to include enhanced features
  • Enhanced documentation with comprehensive enhanced features section

Features Inspired by DynaNode

  • Advanced alarm categorization and threshold management
  • Comprehensive motor model specifications and configurations
  • Application profile templates for common robotics use cases
  • Performance monitoring and optimization tools

[0.0.3] - 2025-06-12

Changed

  • Updated GitHub Actions workflows for improved CI/CD
  • Enhanced npm publishing process with provenance
  • Improved package configuration for npm registry
  • Adjusted test coverage thresholds for realistic targets

Fixed

  • GitHub Actions workflow issues with Node.js 22 compatibility
  • npm publishing configuration and authentication
  • Package files inclusion for distribution

[0.0.2] - 2025-06-12

Added

  • Comprehensive test suite with Jest
  • GitHub Actions CI/CD workflows
  • Automated npm publishing on release
  • Security and dependency scanning
  • Coverage reporting with Codecov and Coveralls
  • Contributing guidelines and documentation

Changed

  • Improved package.json metadata for npm publishing
  • Enhanced error handling and validation
  • Updated dependencies to latest stable versions

Fixed

  • CRC-16 calculation implementation for Protocol 2.0
  • Device discovery reliability issues
  • ES modules compatibility with testing framework

[0.0.1] - 2025-06-12

Added

  • Initial release of node-dynamixel library
  • DYNAMIXEL Protocol 2.0 support
  • U2D2 USB interface connection
  • Serial port connection support
  • Device discovery and control
  • Basic motor operations (position, LED, etc.)
  • Example scripts for device discovery and diagnostics

Features

  • DynamixelController: Main controller class for managing connections and devices
  • DynamixelDevice: Individual device control and monitoring
  • Protocol2: Complete DYNAMIXEL Protocol 2.0 implementation
  • U2D2Connection: USB connection via U2D2 interface
  • SerialConnection: Direct serial port communication
  • Device Discovery: Automatic detection of connected DYNAMIXEL motors
  • Register Operations: Read/write device registers and parameters
  • Error Handling: Comprehensive error detection and reporting

Supported Hardware

  • U2D2 USB interface
  • DYNAMIXEL X-series motors (XL430, XC430, XM430, XH430, etc.)
  • Serial communication interfaces

Requirements

  • Node.js 18.0.0 or higher
  • USB support (optional, for U2D2 interface)
  • Serial port access

Release Notes Template

When creating a new release, use this template:

## [X.Y.Z] - YYYY-MM-DD

### Added
- New features and capabilities

### Changed
- Changes to existing functionality

### Deprecated
- Features that will be removed in future versions

### Removed
- Features that have been removed

### Fixed
- Bug fixes and corrections

### Security
- Security-related changes and fixes

Contributing

See CONTRIBUTING.md for information about contributing to this project and the release process.