cmvr-biohead/text_dgceshi.py
2025-08-21 13:49:41 +08:00

199 lines
5.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
interactive_servo_test.py — 基于 Python 的 ESP32 舵机控制交互脚本
依赖pip install pyserial
用法:
python3 interactive_servo_test.py /dev/ttyUSB0 [baudrate]
功能:
1. 控制单个舵机
2. 控制多个舵机
3. 退出
4. 自动增减模式:从 0 到 180° 每步 +6° → -6° 循环50Hz 频率发送
5. 设置自动模式舵机数量(通道 1~N
"""
import sys
import time
import serial
FRAME_HEADER = 0xAA # 帧头
def wakeup_esp32(port, baud=115200):
"""通过 RTS/DTR 线复位并唤醒 ESP32"""
try:
ser = serial.Serial(port, baud, timeout=1)
ser.setDTR(False)
ser.setRTS(False)
time.sleep(0.01)
ser.setDTR(True)
ser.setRTS(True)
ser.write(b"\r\n")
time.sleep(1)
except Exception as e:
print(f"ESP32 唤醒失败: {e}")
finally:
try:
ser.close()
except:
pass
def open_serial(port, baud):
"""打开并配置串口"""
try:
ser = serial.Serial(port, baud, timeout=1)
print(f"已打开串口: {port} @ {baud}bps")
return ser
except Exception as e:
print(f"打开串口失败: {e}")
sys.exit(1)
def calc_checksum(data: bytearray) -> int:
"""计算 XOR 校验和"""
cs = 0
for b in data:
cs ^= b
return cs
def pack_commands(cmds):
"""
cmds: list of (addr, channel, angle, duration_ms)
返回:完整帧字节数组
格式: [FRAME_HEADER][count] [addr, ch, ang, durL, durH]... [checksum]
"""
pkt = bytearray([FRAME_HEADER, len(cmds)])
for addr, ch, ang, dur in cmds:
pkt.extend([addr, ch, ang, dur & 0xFF, (dur >> 8) & 0xFF])
pkt.append(calc_checksum(pkt))
return pkt
def print_packet(pkt):
"""打印十六进制数据包"""
print("发送数据包:", ' '.join(f"0x{b:02X}" for b in pkt))
def input_hex(prompt):
"""输入 0x 开头或十进制整型"""
val = input(prompt).strip()
try:
return int(val, 0)
except ValueError:
print(f"无效数字: {val}")
return input_hex(prompt)
def input_cmd_single():
"""交互输入单条命令"""
addr = input_hex("输入 addr (hex or dec): ")
ch = input_hex("输入 channel (hex or dec): ")
ang = input_hex("输入 angle (0-180): ")
dur = input_hex("输入 duration(ms): ")
return [(addr, ch, ang, dur)]
def input_cmd_multiple():
"""交互输入多条命令"""
cnt = input_hex("输入命令数量: ")
cmds = []
for i in range(cnt):
print(f"{i+1} 条:")
cmds.extend(input_cmd_single())
return cmds
def auto_mode(ser, channels):
"""自动增减模式0→180→0步长650Hz多舵机"""
addr = input_hex("自动模式: 输入 addr (hex or dec): ")
interval = 1.0 / 50.0
dur_ms = int(interval * 1000)
angle = 0
direction = 1
step = 6
print(f"启动自动模式 @50Hz, addr=0x{addr:02X}, channels={channels}, step={step}°")
try:
while True:
cmds = [(addr, ch, angle, dur_ms) for ch in channels]
pkt = pack_commands(cmds)
print_packet(pkt)
ser.write(pkt)
# 更新角度
angle += direction * step
if angle >= 180:
angle = 180
direction = -1
elif angle <= 0:
angle = 0
direction = 1
time.sleep(interval)
except KeyboardInterrupt:
print("\n自动模式已停止,返回菜单")
def set_channels():
"""自定义舵机数量,返回通道列表 1~N"""
cnt = input_hex("输入自动模式舵机数量 N: ")
if cnt < 1:
print("数量必须 >= 1")
return None
channels = list(range(1, cnt+1))
print(f"已设置通道列表: {channels}")
return channels
def main():
if len(sys.argv) < 2:
print(f"用法: {sys.argv[0]} /dev/ttyUSB0 [baudrate]")
sys.exit(1)
port = sys.argv[1]
baud = int(sys.argv[2]) if len(sys.argv) > 2 else 115200
print("唤醒 ESP32...")
wakeup_esp32(port, baud)
ser = open_serial(port, baud)
channels = list(range(1, 10)) # 默认通道 1-9
try:
while True:
print("\n=== 操作菜单 ===")
print("1. 控制单个舵机")
print("2. 控制多个舵机")
print("3. 退出")
print("4. 自动增减模式(50Hz)")
print("5. 设置自动模式舵机数量")
opt = input("选择: ").strip()
if opt == '1':
cmds = input_cmd_single()
elif opt == '2':
cmds = input_cmd_multiple()
elif opt == '4':
auto_mode(ser, channels)
continue
elif opt == '5':
new_ch = set_channels()
if new_ch:
channels = new_ch
continue
elif opt == '3':
break
else:
print("无效选项,重试")
continue
pkt = pack_commands(cmds)
print_packet(pkt)
ser.write(pkt)
print("✅ 指令已发送")
except KeyboardInterrupt:
print("\n用户中断,退出")
finally:
ser.close()
print("串口已关闭")
if __name__ == '__main__':
main()