import serial
import time
import os
import sys
import signal
# -------------------------- 核心配置 --------------------------
SERIAL_PORT = "/dev/ttyUSB0"
BAUDRATE = 9600
FRAME_HEAD = [0x57, 0xAB]
ADDR = 0x00
CMD_KB_GENERAL = 0x02
# HID码表(补充ESC/CapsLock/Super键,适配输入法复位)
HID_CODE = {
# 基础键
"U": 0x18, "0": 0x27, "1": 0x1E, "2": 0x1F, "3": 0x20,
"4": 0x21, "5": 0x22, "6": 0x23, "7": 0x24, "8": 0x25, "9": 0x26,
"A": 0x04, "B": 0x05, "C": 0x06, "D": 0x07, "E": 0x08, "F": 0x09,
# 功能键
"SPACE": 0x2C, "ENTER": 0x28, "ESC": 0x29, "CAPSLOCK": 0x39,
"LSHIFT": 0x2A, "LSUPER": 0x38 # LSUPER=Win/Super键
}
# 修饰键掩码
MOD_KEY = {
"LCTRL": 0x01,
"LSHIFT": 0x02,
"LSUPER": 0x08 # Super键掩码(原厂P6定义)
}
# 全局变量
ser = None
is_key_held = False
# -------------------------- CH9329协议核心 --------------------------
def calculate_checksum(data):
return sum(data) & 0xFF
def build_packet(addr, cmd, data):
len_data = len(data)
packet = FRAME_HEAD + [addr, cmd, len_data] + data
packet.append(calculate_checksum(packet))
return bytes(packet)
def set_serial_permission(port):
try:
os.system(f"sudo chmod 666 {port} > /dev/null 2>&1")
return True
except:
print("❌ 串口赋权失败,请手动执行:sudo chmod 666 /dev/ttyUSB0")
return False
def init_serial(port, baudrate):
global ser
try:
ser = serial.Serial(
port=port, baudrate=baudrate,
parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS,
timeout=1, write_timeout=1
)
if not ser.is_open:
ser.open()
return True
except Exception as e:
print(f"❌ 串口初始化失败:{str(e)[:50]}")
return False
# -------------------------- 输入法干扰处理核心方法 --------------------------
def reset_input_method():
"""
硬件级复位输入法状态(三重保障):
1. ESC清除输入法缓冲区 2. Win+Space切换输入法 3. CapsLock开启大写锁定
"""
global ser
if not ser or not ser.is_open:
return
print("🔧 开始复位输入法状态...")
# 1. 敲ESC,清除输入法残余缓冲区
esc_hid = HID_CODE["ESC"]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00, 0x00, esc_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00]*8))
ser.flush()
time.sleep(0.1)
# 2. 发送Win+Space(Ubuntu切换输入法快捷键)
super_mask = MOD_KEY["LSUPER"]
# 按住Win键
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [super_mask, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
# 敲Space
space_hid = HID_CODE["SPACE"]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [super_mask, 0x00, space_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [super_mask, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
# 松开Win键
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00]*8))
ser.flush()
time.sleep(0.1)
# 3. 开启CapsLock(大写锁定穿透输入法)
caps_hid = HID_CODE["CAPSLOCK"]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00, 0x00, caps_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00]*8))
ser.flush()
time.sleep(0.1)
print("✅ 输入法状态复位完成(大写锁定已开启)")
def restore_capslock():
"""输入完成后还原CapsLock状态"""
global ser
if not ser or not ser.is_open:
return
print("🔧 还原大写锁定状态...")
caps_hid = HID_CODE["CAPSLOCK"]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00, 0x00, caps_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [0x00]*8))
ser.flush()
time.sleep(0.1)
print("✅ 大写锁定已还原")
# -------------------------- 核心释放方法 --------------------------
def force_release_all():
global ser, is_key_held
if ser and ser.is_open:
try:
release_pkt = build_packet(ADDR, CMD_KB_GENERAL, [0x00]*8)
ser.write(release_pkt)
ser.flush()
time.sleep(0.05)
is_key_held = False
print("✅ 所有按键已强制释放")
except:
pass
# -------------------------- Gemini优化版Unicode输入 --------------------------
def unicode2ubuntu(unicode_hex):
global ser
if not ser or not ser.is_open:
print("❌ 串口未就绪")
return False
try:
# --- 步骤 1: 全程按住Ctrl+Shift(优先级高于输入法) ---
mod_mask = MOD_KEY["LCTRL"] | MOD_KEY["LSHIFT"]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.1) # 等待系统识别组合键(优先级高于输入法)
# --- 步骤 2: 按住Ctrl+Shift敲U键(大写U穿透输入法) ---
u_hid = HID_CODE["U"]
# 按下U(仍带修饰键掩码)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, u_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
# 松开U(保留修饰键掩码)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.1) # 此时屏幕出现带下划线的u,输入法已被绕过
# --- 步骤 3: 输入码点(大写锁定+修饰键双重保障) ---
for c in unicode_hex.upper():
if c in HID_CODE:
# 每个字符都带修饰键掩码,彻底绕过输入法
char_hid = HID_CODE[c]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, char_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.02)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.02)
# --- 步骤 4: 按Enter确认(仍带修饰键掩码) ---
enter_hid = HID_CODE["ENTER"]
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, enter_hid, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.02)
ser.write(build_packet(ADDR, CMD_KB_GENERAL, [mod_mask, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))
ser.flush()
time.sleep(0.05)
# --- 步骤 5: 释放所有修饰键 ---
force_release_all()
print(f"✅ Unicode码点【{unicode_hex}】发送成功(已绕过输入法)")
return True
except Exception as e:
print(f"❌ 执行失败:{str(e)}")
force_release_all()
return False
# -------------------------- 信号捕获+主程序 --------------------------
def signal_handler(signum, frame):
print(f"\n⚠️ 捕获中断信号,释放所有键...")
force_release_all()
restore_capslock() # 还原大写锁定
if ser and ser.is_open:
ser.close()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
def main():
global ser
print("=====================================")
print("✅ 防输入法干扰版 CH9329 Unicode Demo")
print("📌 核心:自动复位输入法+大写锁定+全程修饰键")
print("=====================================\n")
# 串口初始化
if not set_serial_permission(SERIAL_PORT):
return
if not init_serial(SERIAL_PORT, BAUDRATE):
return
# 5秒延时切焦点
print("📢 5秒后自动执行,请切焦点到输入框!")
for i in range(5, 0, -1):
print(f"⏳ {i}秒")
time.sleep(1)
# 核心步骤:1.复位输入法 2.执行Unicode输入 3.还原状态
reset_input_method()
unicode2ubuntu("4E2D") # 中
unicode2ubuntu("56FD") # 国
restore_capslock()
# 最终兜底
force_release_all()
if ser and ser.is_open:
ser.close()
print("\n🎉 执行完成!无需切换英文,直接输出中文!")
if __name__ == "__main__":
main()