You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python+Arduino模拟鼠标左键异常:按住触发自动连点而非持续按住

问题描述

需要实现鼠标左键持续按压功能:按住摇杆按钮时保持左键按下状态,但当前程序按住按钮时每秒触发约35次点击,而非持续按住。

Python通过串口读取Arduino发送的三个数值,其中Z代表左键按下/释放状态,先后使用pyautogui.click()和mouseDown/mouseUp逻辑均未解决问题。

Python代码

import serial
import pydirectinput
import time
import pyautogui
try:
    arduino = serial.Serial('COM4', 115200, timeout=.1)
except serial.SerialException as e:
    print(f"Error opening serial port: {e}")
    exit()

pydirectinput.PAUSE=0
pyautogui.PAUSE=0
pyautogui.FAILSAFE=False
keys_down = {}  # list of currently pressed keys


def key_down(key):
    keys_down[key] = True
    pydirectinput.keyDown(key)


def key_up(key):
    if key in keys_down:
        del keys_down[key]
        pydirectinput.keyUp(key)


def handle_joystick_as_arrow_keys(x, y, z):
    if x == 0:
        pyautogui.moveRel(0, -10)#drag 10 pixels up
    elif x == 2:
        pyautogui.moveRel(0, 10)#drag 10 pixels down

    if y == 2:
        pyautogui.moveRel(-10, 0)
    elif y == 0:
        pyautogui.moveRel(10, 0)
    if z == 1:
        pyautogui.mouseDown(button='left')
    else:
        pyautogui.mouseUp(button='left')

        
try:
    while True:
        raw_data = arduino.readline()
        data = str(raw_data.decode('utf-8'))
        if data.startswith("S"):
            x_direction = int(data[1])
            y_direction = int(data[3])
            joystick_button = int(data[5])

            handle_joystick_as_arrow_keys(x_direction, y_direction, joystick_button)

except KeyboardInterrupt:
    print("Script terminated by user.")

finally:
    arduino.close()

Arduino代码

const int JoyStick_pin = 8; //plug Joystick 'Button' into pin 8
const int X_pin = A0;       //plug joystick X direction into pin A0
const int Y_pin = A1;       //plug joystick Y direction into pin A1
int xc;
int yc;
int JSButton;
int prevButtonState = HIGH; //

void setup() {
    pinMode(JoyStick_pin, INPUT_PULLUP);
  
  Serial.begin(115200);
}

void loop() {
  int x = analogRead(X_pin) - 517;  //read x direction value and -517 to bring back to around 0
  int y = analogRead(Y_pin) - 512;  //read y direction value and -512 to bring back to around 0

  if (x < -10) {         //joystick has off set of +/-8 so this negates that
    xc = 0;             //turn analogue value into integer. 0, 1 or 2 depending on state
  } else if (x > 10) {   
    xc = 2;
  } else {
    xc = 1;
  }

  if (y < -10) {
    yc = 0;
  } else if (y > 10) {
    yc = 2;
  } else {
    yc = 1;
  }

  int buttonStates = 0;   //set starting value of Joystick button

  buttonStates |= ((digitalRead(JoyStick_pin) == LOW) ? 1 : 0) << 0;
 
  Serial.print("S");  //start printing the data, format is Sxc,yc,buttonStates > S1,1,0
  Serial.print(xc);
  Serial.print(",");
  Serial.print(yc);
  Serial.print(",");
  Serial.println((buttonStates));

  delay(40);

}
解决方案

问题核心是Arduino持续发送相同的按钮按下状态,Python每次收到都会重复调用pyautogui.mouseDown(),导致系统误判为多次点击。只需在Python中维护左键状态,仅当状态变化时执行操作即可解决。

修改Python代码

添加状态变量并更新处理函数:

import serial
import pydirectinput
import time
import pyautogui
try:
    arduino = serial.Serial('COM4', 115200, timeout=.1)
except serial.SerialException as e:
    print(f"Error opening serial port: {e}")
    exit()

pydirectinput.PAUSE=0
pyautogui.PAUSE=0
pyautogui.FAILSAFE=False
keys_down = {}  # list of currently pressed keys
left_mouse_down = False  # 记录当前左键状态


def key_down(key):
    keys_down[key] = True
    pydirectinput.keyDown(key)


def key_up(key):
    if key in keys_down:
        del keys_down[key]
        pydirectinput.keyUp(key)


def handle_joystick_as_arrow_keys(x, y, z):
    global left_mouse_down
    if x == 0:
        pyautogui.moveRel(0, -10)#drag 10 pixels up
    elif x == 2:
        pyautogui.moveRel(0, 10)#drag 10 pixels down

    if y == 2:
        pyautogui.moveRel(-10, 0)
    elif y == 0:
        pyautogui.moveRel(10, 0)
    
    # 仅当状态变化时执行按压/释放
    if z == 1 and not left_mouse_down:
        pyautogui.mouseDown(button='left')
        left_mouse_down = True
    elif z == 0 and left_mouse_down:
        pyautogui.mouseUp(button='left')
        left_mouse_down = False

        
try:
    while True:
        raw_data = arduino.readline()
        data = str(raw_data.decode('utf-8'))
        if data.startswith("S"):
            x_direction = int(data[1])
            y_direction = int(data[3])
            joystick_button = int(data[5])

            handle_joystick_as_arrow_keys(x_direction, y_direction, joystick_button)

except KeyboardInterrupt:
    print("Script terminated by user.")

finally:
    arduino.close()

可选优化:减少Arduino串口发送量

让Arduino仅在状态变化时发送数据,降低串口负载:

const int JoyStick_pin = 8; //plug Joystick 'Button' into pin 8
const int X_pin = A0;       //plug joystick X direction into pin A0
const int Y_pin = A1;       //plug joystick Y direction into pin A1
int xc;
int yc;
int prevXc = -1;
int prevYc = -1;
int prevButtonState = HIGH;

void setup() {
    pinMode(JoyStick_pin, INPUT_PULLUP);
    Serial.begin(115200);
}

void loop() {
  int x = analogRead(X_pin) - 517;  //read x direction value and -517 to bring back to around 0
  int y = analogRead(Y_pin) - 512;  //read y direction value and -512 to bring back to around 0

  if (x < -10) {         //joystick has off set of +/-8 so this negates that
    xc = 0;             //turn analogue value into integer. 0, 1 or 2 depending on state
  } else if (x > 10) {   
    xc = 2;
  } else {
    xc = 1;
  }

  if (y < -10) {
    yc = 0;
  } else if (y > 10) {
    yc = 2;
  } else {
    yc = 1;
  }

  int buttonState = (digitalRead(JoyStick_pin) == LOW) ? 1 : 0;
 
  // 仅当状态变化时发送数据
  if (xc != prevXc || yc != prevYc || buttonState != prevButtonState) {
    Serial.print("S");
    Serial.print(xc);
    Serial.print(",");
    Serial.print(yc);
    Serial.print(",");
    Serial.println(buttonState);
    
    prevXc = xc;
    prevYc = yc;
    prevButtonState = buttonState;
  }

  delay(40);
}

内容的提问来源于stack exchange,提问作者Justin Li

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 07:07:04