cmvr-es/script/test.py

168 lines
5.6 KiB
Python
Raw 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 -*-
"""
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()