262 lines
7.6 KiB
Python
Executable File
262 lines
7.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright 2025 Enactic, Inc.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""
|
|
DM Motor ID and Baudrate Writer script for writing baudrate and ID parameters to DM motors
|
|
Work with python-can library
|
|
Original author: @necobit (Co-Authored-By: Claude)
|
|
"""
|
|
|
|
import argparse
|
|
import sys
|
|
import can
|
|
import time
|
|
from typing import Optional
|
|
|
|
|
|
class DMMotorWriter:
|
|
"""DM Motor parameter writer"""
|
|
|
|
# Supported baudrates and their codes
|
|
BAUDRATE_MAP = {
|
|
125000: 0, # 125K
|
|
200000: 1, # 200K
|
|
250000: 2, # 250K
|
|
500000: 3, # 500K
|
|
1000000: 4, # 1M
|
|
2000000: 5, # 2M
|
|
2500000: 6, # 2.5M
|
|
3200000: 7, # 3.2M
|
|
4000000: 8, # 4M
|
|
5000000: 9 # 5M
|
|
}
|
|
|
|
def __init__(self, socketcan_port: str = "can0"):
|
|
self.socketcan_port = socketcan_port
|
|
self.can_bus: Optional[can.BusABC] = None
|
|
|
|
def connect(self) -> bool:
|
|
"""Connect to CAN bus"""
|
|
try:
|
|
print(f"Connecting to CAN interface: {self.socketcan_port}")
|
|
self.can_bus = can.interface.Bus(
|
|
channel=self.socketcan_port,
|
|
interface='socketcan'
|
|
)
|
|
print(f"✓ Connected to {self.socketcan_port}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ Connection failed: {e}")
|
|
return False
|
|
|
|
def disconnect(self):
|
|
"""Disconnect from CAN bus"""
|
|
if self.can_bus:
|
|
self.can_bus.shutdown()
|
|
self.can_bus = None
|
|
print("Disconnected from CAN bus")
|
|
|
|
def validate_baudrate(self, baudrate: int) -> bool:
|
|
"""Validate if baudrate is supported"""
|
|
return baudrate in self.BAUDRATE_MAP
|
|
|
|
def write_baudrate(self, motor_id: int, baudrate: int) -> bool:
|
|
"""Write baudrate to motor"""
|
|
if not self.can_bus:
|
|
print("✗ Not connected to CAN bus")
|
|
return False
|
|
|
|
if not self.validate_baudrate(baudrate):
|
|
print(f"✗ Unsupported baudrate: {baudrate}")
|
|
print(f"Supported baudrates: {list(self.BAUDRATE_MAP.keys())}")
|
|
return False
|
|
|
|
try:
|
|
baudrate_code = self.BAUDRATE_MAP[baudrate]
|
|
|
|
print(
|
|
f"Writing baudrate {baudrate} (code: {baudrate_code}) to motor ID {motor_id}")
|
|
|
|
# Set Baudrate (can_br) - RID 35 (0x23)
|
|
baudrate_data = [
|
|
motor_id & 0xFF,
|
|
(motor_id >> 8) & 0xFF,
|
|
0x55,
|
|
0x23,
|
|
baudrate_code,
|
|
0x00,
|
|
0x00,
|
|
0x00
|
|
]
|
|
|
|
baudrate_msg = can.Message(
|
|
arbitration_id=0x7FF,
|
|
data=baudrate_data,
|
|
is_extended_id=False
|
|
)
|
|
|
|
self.can_bus.send(baudrate_msg)
|
|
time.sleep(0.1) # Wait for processing
|
|
|
|
print(f"✓ Baudrate {baudrate} written to motor ID {motor_id}")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"✗ Failed to write baudrate: {e}")
|
|
return False
|
|
|
|
def save_to_flash(self, motor_id: int) -> bool:
|
|
"""Save parameters to flash memory"""
|
|
if not self.can_bus:
|
|
print("✗ Not connected to CAN bus")
|
|
return False
|
|
|
|
try:
|
|
print(f"Saving parameters to flash for motor ID {motor_id}")
|
|
print("⚠️ Motor has a hard limit of writing to flash of 10000 times")
|
|
print("⚠️ Motor will be disabled during save operation")
|
|
|
|
# Step 1: Disable motor first (required for save operation)
|
|
disable_data = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD]
|
|
disable_msg = can.Message(
|
|
arbitration_id=motor_id,
|
|
data=disable_data,
|
|
is_extended_id=False
|
|
)
|
|
|
|
self.can_bus.send(disable_msg)
|
|
time.sleep(0.5) # Wait for disable to take effect
|
|
|
|
# Step 2: Send save command
|
|
save_data = [
|
|
motor_id & 0xFF,
|
|
(motor_id >> 8) & 0xFF,
|
|
0xAA,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00,
|
|
0x00
|
|
]
|
|
|
|
save_msg = can.Message(
|
|
arbitration_id=0x7FF,
|
|
data=save_data,
|
|
is_extended_id=False
|
|
)
|
|
|
|
self.can_bus.send(save_msg)
|
|
time.sleep(0.5) # Wait for save operation (up to 30ms)
|
|
|
|
print(f"✓ Parameters saved to flash for motor ID {motor_id}")
|
|
print("⚠️ Motor is now in DISABLE mode")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"✗ Failed to save to flash: {e}")
|
|
return False
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(
|
|
description="DM Motor ID and Baudrate Writer",
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
epilog="""
|
|
Examples:
|
|
# Write baudrate 1000000 to motor ID 2 on can0
|
|
python change_baudrate.py --baudrate 1000000 --canid 2 --socketcan can0
|
|
|
|
# Write baudrate and save to flash
|
|
python change_baudrate.py --baudrate 500000 --canid 1 --socketcan can0 --flash
|
|
|
|
# Write to motor on different CAN port
|
|
python change_baudrate.py --baudrate 2000000 --canid 3 --socketcan can1
|
|
|
|
Supported baudrates:
|
|
125000, 200000, 250000, 500000, 1000000, 2000000, 2500000, 3200000, 4000000, 5000000
|
|
"""
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-b', '--baudrate',
|
|
type=int,
|
|
required=True,
|
|
help='Baudrate to write (125000, 200000, 250000, 500000, 1000000, 2000000, 2500000, 3200000, 4000000, 5000000)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-c', '--canid',
|
|
type=int,
|
|
required=True,
|
|
help='Motor CAN ID (slave ID) to write to (0-255)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-s', '--socketcan',
|
|
type=str,
|
|
default='can0',
|
|
help='SocketCAN port (default: can0)'
|
|
)
|
|
|
|
parser.add_argument(
|
|
'-f', '--flash',
|
|
action='store_true',
|
|
help='Save parameters to flash memory after writing'
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Validate inputs
|
|
if args.canid < 0 or args.canid > 255:
|
|
print("✗ Error: CAN ID must be between 0 and 255")
|
|
sys.exit(1)
|
|
|
|
# Create writer instance
|
|
writer = DMMotorWriter(args.socketcan)
|
|
|
|
try:
|
|
# Connect to CAN bus
|
|
if not writer.connect():
|
|
print("✗ Failed to connect to CAN bus")
|
|
sys.exit(1)
|
|
|
|
# Write baudrate
|
|
if not writer.write_baudrate(args.canid, args.baudrate):
|
|
print("✗ Failed to write baudrate")
|
|
sys.exit(1)
|
|
|
|
# Save to flash if requested
|
|
if args.flash:
|
|
if not writer.save_to_flash(args.canid):
|
|
print("✗ Failed to save to flash")
|
|
sys.exit(1)
|
|
|
|
print("✓ Operation completed successfully")
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n⚠️ Operation cancelled by user")
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"✗ Unexpected error: {e}")
|
|
sys.exit(1)
|
|
finally:
|
|
writer.disconnect()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|