cmvr-es/script/test.py

168 lines
5.6 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
text_dgceshi.py 写死 5 组表情已按 XML 限位约束 5 秒切换循环发送到 ESP32
依赖pip install pyserial
用法
python text_dgceshi.py /dev/ttyUSB0 [baudrate]
帧格式与你现有固件一致
[0] 0xAA
[1] count = 指令条数每条是5字节addr, ch, ang, durL, durH
[2..] 指令序列 count*5 字节
[end] XOR 校验对前面所有字节异或
"""
import sys
import time
import serial
FRAME_HEADER = 0xAA
def calc_checksum(data: bytearray) -> int:
"""XOR 校验"""
cs = 0
for b in data:
cs ^= b
return cs & 0xFF
def pack_pose(raw_list):
"""
raw_list: [addr, ch, ang, durL, durH, addr, ch, ang, durL, durH, ...]
返回带帧头/数量/校验的完整数据包 bytes
"""
assert len(raw_list) % 5 == 0, "原始列表长度必须是 5 的倍数"
count = len(raw_list) // 5
pkt = bytearray([FRAME_HEADER, count & 0xFF])
pkt.extend(int(x) & 0xFF for x in raw_list)
pkt.append(calc_checksum(pkt))
return bytes(pkt)
def print_packet(pkt):
print("发送数据包:", ' '.join(f"0x{b:02X}" for b in pkt))
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.02)
ser.setDTR(True)
ser.setRTS(True)
ser.write(b"\r\n")
time.sleep(0.2)
except Exception as e:
print(f"[WARN] 唤醒失败:{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)
# =======================
# 五组“写死”的表情数据(严格遵守你的 XML 安全限位)
# 64:0~3 眉offset 90/90/90/90限位 45~120/…/50~120
# 64:4~9 眼offset 90..,限位 [90~170, 10~90, 10~90, 90~170, 10~170, 10~170]
# 65:0~9 嘴offset 90..,限位 [60~130, 60~130, 50~120, 50~130, 50~100, 50~130, 50~130, 90~120, 91~120, 60~89]
# duration 全部为 0立即执行如需时长可把毫秒拆成 durL/durH 小端写入
# =======================
POSES_RAW = [
# # Pose 1 — 中性位置(全部 offset
[
# 板 64 — 眼眉 0~3
64,0,90,0,0, 64,1,90,0,0, 64,2,90,0,0, 64,3,90,0,0,
# 板 64 — 眼睛 4~9选在各自安全范围内的中值/视觉舒适值)
64,4,160,0,0, 64,5,20,0,0, 64,6,20,0,0, 64,7,160,0,0, 64,8,150,0,0, 64,9,150,0,0,
# 板 65 — 嘴巴 0~9
65,0,90,0,0, 65,1,90,0,0, 65,2,85,0,0, 65,3,90,0,0, 65,4,90,0,0,
65,5,90,0,0, 65,6,90,0,0, 65,7,90,0,0, 65,8,90,0,0, 65,9,90,0,0
],
# Pose 5 — 大笑(接近上限但不越界)
[
64,0,115,0,0, 64,1,75,0,0, 64,2,75,0,0, 64,3,115,0,0,
64,4,160,0,0, 64,5,20,0,0, 64,6,20,0,0, 64,7,160,0,0, 64,8,50,0,0, 64,9,40,0,0,
65,0,50,0,0, 65,1,110,0,0, 65,2,70,0,0, 65,3,50,0,0, 65,4,100,0,0,
65,5,110,0,0, 65,6,130,0,0, 65,7,90,0,0, 65,8,110,0,0, 65,9,70,0,0
],
[
64,0,115,0,0, 64,1,75,0,0, 64,2,75,0,0, 64,3,115,0,0,
64,4,160,0,0, 64,5,20,0,0, 64,6,20,0,0, 64,7,160,0,0, 64,8,50,0,0, 64,9,130,0,0,
65,0,50,0,0, 65,1,110,0,0, 65,2,70,0,0, 65,3,50,0,0, 65,4,90,0,0,
65,5,110,0,0, 65,6,130,0,0, 65,7,90,0,0, 65,8,90,0,0, 65,9,90,0,0
],
[
64,0,115,0,0, 64,1,75,0,0, 64,2,75,0,0, 64,3,115,0,0,
64,4,160,0,0, 64,5,20,0,0, 64,6,20,0,0, 64,7,160,0,0, 64,8,50,0,0, 64,9,40,0,0,
65,0,50,0,0, 65,1,110,0,0, 65,2,70,0,0, 65,3,50,0,0, 65,4,90,0,0,
65,5,110,0,0, 65,6,130,0,0, 65,7,90,0,0, 65,8,110,0,0, 65,9,70,0,0
]
]
POSES_RAW1 = [[
# 板 64 — 眼眉 0~3
64,0,90,0,0, 64,1,90,0,0, 64,2,90,0,0, 64,3,90,0,0,
# 板 64 — 眼睛 4~9选在各自安全范围内的中值/视觉舒适值)
64,4,90,0,0, 64,5,90,0,0, 64,6,90,0,0, 64,7,90,0,0, 64,8,150,0,0, 64,9,150,0,0,
# 板 65 — 嘴巴 0~9
65,0,90,0,0, 65,1,90,0,0, 65,2,85,0,0, 65,3,90,0,0, 65,4,90,0,0,
65,5,90,0,0, 65,6,90,0,0, 65,7,90,0,0, 65,8,90,0,0, 65,9,90,0,0
],
# Pose 5 — 大笑(接近上限但不越界)
[
64,0,115,0,0, 64,1,75,0,0, 64,2,75,0,0, 64,3,115,0,0,
64,4,160,0,0, 64,5,20,0,0, 64,6,20,0,0, 64,7,160,0,0, 64,8,50,0,0, 64,9,40,0,0,
65,0,50,0,0, 65,1,110,0,0, 65,2,70,0,0, 65,3,50,0,0, 65,4,100,0,0,
65,5,110,0,0, 65,6,130,0,0, 65,7,90,0,0, 65,8,110,0,0, 65,9,70,0,0
],
]
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
# 可选:尝试唤醒 ESP32
wakeup_esp32(port, baud)
ser = open_serial(port, baud)
try:
pose_idx = 0
hold_seconds = 5
print(f"开始循环播放 5 组表情,每组间隔 {hold_seconds} 秒...")
while True:
pose = POSES_RAW[pose_idx]
pkt = pack_pose(pose)
print(f"\n→ 发送 Pose {pose_idx+1}/{len(POSES_RAW)}")
print_packet(pkt)
ser.write(pkt)
ser.flush()
time.sleep(hold_seconds)
pose_idx = (pose_idx + 1) % len(POSES_RAW)
except KeyboardInterrupt:
print("\n用户中断,退出")
finally:
ser.close()
print("串口已关闭")
if __name__ == "__main__":
main()