python - Mouse movement accuracy problem via Bluetooth HID - Stack Overflow

I'm developing a task automation bot in Python that remotely controls a mouse and keyboard via Blu

I'm developing a task automation bot in Python that remotely controls a mouse and keyboard via Bluetooth HID. The bot uses a C driver to send keyboard and mouse commands via Bluetooth, emulating an HID input device. Problem: I'm having trouble getting precise mouse movements. When I send commands to move the cursor to specific coordinates on the screen, the observed behavior doesn't match the values sent:

When sending a movement of -50px in X for testing, the cursor moves much more than apparently 50 pixels on the screen When trying to implement gradual movements to specific points, the cursor often overshoots the desired target

Technical details:

The C code receives commands via the Unix socket in the format: mouse_move x y The commands are converted into HID reports via Bluetooth The HID mouse protocol uses 8-bit values with sign (-128 to 127) for relative movements The Bluetooth driver runs in a loop (LE_TIMER), processing commands in a queue

The function in Python for calculating distance and send move movement commands

    def move_mouse_gradually(self, target_x, target_y, from_center=False):
        start_time = time.time()
        
        if from_center:
            current_x, current_y = 960, 540
        else:
            current_x, current_y = self.get_mouse_position()
        
        self.logger.log("MOUSE", f"Iniciando movimento: ({current_x}, {current_y}) → ({target_x}, {target_y})", level="INFO")
        
        max_attempts = 150  
        attempts = 0
        
        min_distance = 10  # pixels
        
        move_interval = 0.040  
        
        while attempts < max_attempts:
            current_x, current_y = self.get_mouse_position()
            dx = target_x - current_x
            dy = target_y - current_y
            distance = math.sqrt(dx*dx + dy*dy)
            
            if distance <= min_distance:
                break
            
            step_size = 1
            if distance > 100:
                step_size = 8
            elif distance > 50:
                step_size = 4
            elif distance > 20:
                step_size = 2
                
            if dx != 0 or dy != 0:
                
                magnitude = math.sqrt(dx*dx + dy*dy)
                normalized_dx = dx / magnitude
                normalized_dy = dy / magnitude
                
                
                move_dx = int(normalized_dx * step_size)
                move_dy = int(normalized_dy * step_size)
                
                if abs(dx) > 5 and move_dx == 0:
                    move_dx = 1 if dx > 0 else -1
                if abs(dy) > 5 and move_dy == 0:
                    move_dy = 1 if dy > 0 else -1
                    
                if abs(dx) <= 5 and random.random() > 0.7:
                    move_dx = 0
                if abs(dy) <= 5 and random.random() > 0.7:
                    move_dy = 0
            else:
                move_dx = move_dy = 0
            
            if move_dx == 0 and move_dy == 0:
                time.sleep(move_interval)
                attempts += 1
                continue
            
            
            self.hid.mouse_move(move_dx, move_dy)
            
            actual_interval = move_interval
            if distance < 30:
                actual_interval = move_interval * 1.5  # Mais lento perto do alvo
            time.sleep(actual_interval)
            
            attempts += 1
        

        final_x, final_y = self.get_mouse_position()
        final_distance = math.sqrt((final_x - target_x)**2 + (final_y - target_y)**2)
        
        

        self.update_mouse_position(final_x, final_y)

        execution_time = (time.time() - start_time) * 1000
        self.logger.log_performance("mouse", "move_gradually", execution_time, 0, 
                            f"attempts={attempts},final_distance={final_distance:.1f}")
        
        return final_distance <= min_distance

Keymouse.c code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include "btlib.h"
#include <unistd.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <stdlib.h>
#include <sys/time.h>
#include <pthread.h>
#include <sys/select.h>

#define SOCKET_PATH "/tmp/keymouse-a.sock"

#define KEY_DELAY_MIN 66000
#define KEY_DELAY_MAX 132000
#define CLICK_DELAY_MIN 74000
#define CLICK_DELAY_MAX 123000

#define MAX_QUEUE_SIZE 256

#define KEY_RATE_LIMIT 20
#define CLICK_RATE_LIMIT 30
#define MOVE_RATE_LIMIT 10

typedef struct {
    int type;
    int arg1;
    int arg2;
} CommandItem;

typedef struct {
    int command_socket;
    int client_socket;
    pthread_t receiver_thread;
    volatile int thread_running;
    
    CommandItem key_queue[MAX_QUEUE_SIZE];
    int key_queue_head;
    int key_queue_tail;
    int key_queue_size;
    pthread_mutex_t key_mutex;
    
    CommandItem click_queue[MAX_QUEUE_SIZE];
    int click_queue_head;
    int click_queue_tail;
    int click_queue_size;
    pthread_mutex_t click_mutex;
    
    CommandItem move_queue[MAX_QUEUE_SIZE];
    int move_queue_head;
    int move_queue_tail;
    int move_queue_size;
    pthread_mutex_t move_mutex;
    
    long long last_key_time;
    long long last_click_time;
    long long last_move_time;
} KeyMouseContext;

int lecallback(int clientnode, int op, int cticn);
int send_key(int key);
int send_mouse(char x, char y, char but);
void handle_sigint(int sig);
long long get_timestamp();
int random_delay(int min, int max);
KeyMouseContext* create_context();
void destroy_context(KeyMouseContext* ctx);

volatile int running = 1;
KeyMouseContext* g_context = NULL;

unsigned char reportmap[99] = {0x05,0x01,0x09,0x06,0xA1,0x01,0x85,0x01,0x05,0x07,0x19,0xE0,0x29,0xE7,0x15,0x00,
                               0x25,0x01,0x75,0x01,0x95,0x08,0x81,0x02,0x95,0x01,0x75,0x08,0x81,0x01,0x95,0x06,
                              0x75,0x08,0x15,0x00,0x25,0x65,0x05,0x07,0x19,0x00,0x29,0x65,0x81,0x00,0xC0,
                              0x05,0x01,0x09,0x02,0xA1,0x01,0x85,0x02,0x09,0x01,0xA1,0x00,0x05,0x09,0x19,0x01,
                              0x29,0x03,0x15,0x00,0x25,0x01,0x95,0x03,0x75,0x01,0x81,0x02,0x95,0x01,0x75,0x05,
                              0x81,0x01,0x05,0x01,0x09,0x30,0x09,0x31,0x15,0x81,0x25,0x7F,0x75,0x08,0x95,0x02,
                              0x81,0x06,0xC0,0xC0};

unsigned char report[8] = {0,0,0,0,0,0,0,0};
unsigned char *name = "ConnectHub"; 
unsigned char appear[2] = {0xC1,0x03};
unsigned char pnpinfo[7] = {0x02,0x6B,0x1D,0x46,0x02,0x37,0x05};
unsigned char protocolmode[1] = {0x01};
unsigned char hidinfo[4] = {0x01,0x11,0x00,0x02};

KeyMouseContext* create_context() {
    KeyMouseContext* ctx = malloc(sizeof(KeyMouseContext));
    if (!ctx) {
        printf("[ERROR] Failed to allocate context memory\n");
        return NULL;
    }
    
    memset(ctx, 0, sizeof(KeyMouseContext));
    ctx->command_socket = -1;
    ctx->client_socket = -1;
    ctx->thread_running = 0;
    
    if (pthread_mutex_init(&ctx->key_mutex, NULL) != 0 ||
        pthread_mutex_init(&ctx->click_mutex, NULL) != 0 ||
        pthread_mutex_init(&ctx->move_mutex, NULL) != 0) {
        printf("[ERROR] Failed to initialize mutexes\n");
        free(ctx);
        return NULL;
    }
    
    return ctx;
}

void destroy_context(KeyMouseContext* ctx) {
    if (!ctx) return;
    
    if (ctx->thread_running) {
        ctx->thread_running = 0;
        pthread_join(ctx->receiver_thread, NULL);
    }
    
    if (ctx->client_socket >= 0) {
        close(ctx->client_socket);
    }
    if (ctx->command_socket >= 0) {
        close(ctx->command_socket);
        unlink(SOCKET_PATH);
    }
    
    pthread_mutex_destroy(&ctx->key_mutex);
    pthread_mutex_destroy(&ctx->click_mutex);
    pthread_mutex_destroy(&ctx->move_mutex);
    
    free(ctx);
}

int enqueue_key_command(KeyMouseContext* ctx, char key) {
    pthread_mutex_lock(&ctx->key_mutex);
    
    if (ctx->key_queue_size >= MAX_QUEUE_SIZE) {
        pthread_mutex_unlock(&ctx->key_mutex);
        printf("[WARNING] Key queue full, command dropped\n");
        return 0;
    }
    
    ctx->key_queue[ctx->key_queue_tail].type = 1;
    ctx->key_queue[ctx->key_queue_tail].arg1 = key;
    ctx->key_queue_tail = (ctx->key_queue_tail + 1) % MAX_QUEUE_SIZE;
    ctx->key_queue_size++;
    
    pthread_mutex_unlock(&ctx->key_mutex);
    return 1;
}

int enqueue_click_command(KeyMouseContext* ctx, int button) {
    pthread_mutex_lock(&ctx->click_mutex);
    
    if (ctx->click_queue_size >= MAX_QUEUE_SIZE) {
        pthread_mutex_unlock(&ctx->click_mutex);
        printf("[WARNING] Click queue full, command dropped\n");
        return 0;
    }
    
    ctx->click_queue[ctx->click_queue_tail].type = 2;
    ctx->click_queue[ctx->click_queue_tail].arg1 = button;
    ctx->click_queue_tail = (ctx->click_queue_tail + 1) % MAX_QUEUE_SIZE;
    ctx->click_queue_size++;
    
    pthread_mutex_unlock(&ctx->click_mutex);
    return 1;
}

int enqueue_move_command(KeyMouseContext* ctx, int x, int y) {
    pthread_mutex_lock(&ctx->move_mutex);
    
    if (ctx->move_queue_size >= MAX_QUEUE_SIZE) {
        pthread_mutex_unlock(&ctx->move_mutex);
        printf("[WARNING] Move queue full, command dropped\n");
        return 0;
    }
    
    ctx->move_queue[ctx->move_queue_tail].type = 3;
    ctx->move_queue[ctx->move_queue_tail].arg1 = x;
    ctx->move_queue[ctx->move_queue_tail].arg2 = y;
    ctx->move_queue_tail = (ctx->move_queue_tail + 1) % MAX_QUEUE_SIZE;
    ctx->move_queue_size++;
    
    pthread_mutex_unlock(&ctx->move_mutex);
    return 1;
}

void *socket_receiver_thread(void *arg) {
    KeyMouseContext* ctx = (KeyMouseContext*)arg;
    char buffer[8192];
    fd_set readfds;
    struct timeval tv;
    
    printf("[INFO] Socket receiver thread started\n");
    
    while (ctx->thread_running) {
        FD_ZERO(&readfds);
        FD_SET(ctx->client_socket, &readfds);
        
        tv.tv_sec = 0;
        tv.tv_usec = 10000;
        
        int result = select(ctx->client_socket + 1, &readfds, NULL, NULL, &tv);
        
        if (result > 0 && FD_ISSET(ctx->client_socket, &readfds)) {
            memset(buffer, 0, sizeof(buffer));
            int n = read(ctx->client_socket, buffer, sizeof(buffer)-1);
            
            if (n > 0) {
                buffer[n] = '\0';
                char *command = strtok(buffer, "\n");
                while (command != NULL) {
                    printf("[INFO] Received command: %s\n", command);
                    
                    if (strncmp(command, "key ", 4) == 0) {
                        enqueue_key_command(ctx, command[4]);
                        write(ctx->client_socket, "[INFO] Key command queued\n", 25);
                    }
                    else if (strncmp(command, "mouse_click ", 11) == 0) {
                        int button = atoi(command + 11);
                        enqueue_click_command(ctx, button);
                        write(ctx->client_socket, "[INFO] Click command queued\n", 27);
                    }
                    else if (strncmp(command, "mouse_move ", 11) == 0) {
                        float x, y;
                        sscanf(command + 11, "%f %f", &x, &y);
                        enqueue_move_command(ctx, x, y);
                        write(ctx->client_socket, "[INFO] Move command queued\n", 26);
                    }
                    else {
                        write(ctx->client_socket, "[WARNING] Invalid command\n", 25);
                    }
                    
                    command = strtok(NULL, "\n");
                }
            }
            else if (n == 0 || (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
                printf("[INFO] Client disconnected or error: %s\n", 
                       n == 0 ? "closed by peer" : strerror(errno));
                close(ctx->client_socket);
                ctx->client_socket = -1;
                ctx->thread_running = 0;
                break;
            }
        }
        else if (result < 0 && errno != EINTR) {
            printf("[ERROR] Select failed: %s\n", strerror(errno));
            ctx->thread_running = 0;
            break;
        }
        
        if (!running) {
            ctx->thread_running = 0;
            break;
        }
    }
    
    printf("[INFO] Receiver thread exiting\n");
    return NULL;
}

int init_command_socket(KeyMouseContext* ctx) {
    struct sockaddr_un addr;
    
    unlink(SOCKET_PATH);
    
    ctx->command_socket = socket(AF_UNIX, SOCK_STREAM, 0);
    if (ctx->command_socket == -1) {
        printf("[ERROR] Failed to create socket: %s\n", strerror(errno));
        return 0;
    }
    
    memset(&addr, 0, sizeof(addr));
    addr.sun_family = AF_UNIX;
    strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path)-1);
    
    if (bind(ctx->command_socket, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
        printf("[ERROR] Failed at binding: %s\n", strerror(errno));
        close(ctx->command_socket);
        ctx->command_socket = -1;
        return 0;
    }
    
    if (listen(ctx->command_socket, 5) == -1) {
        printf("[ERROR] Failed at listening: %s\n", strerror(errno));
        close(ctx->command_socket);
        ctx->command_socket = -1;
        return 0;
    }
    
    int flags = fcntl(ctx->command_socket, F_GETFL, 0);
    fcntl(ctx->command_socket, F_SETFL, flags | O_NONBLOCK);
    
    printf("[INFO] Socket successfully initialized in %s\n", SOCKET_PATH);
    
    return 1;
}

int random_delay(int min, int max) {
    return min + (rand() % (max - min + 1));
}

long long get_timestamp() {
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
}

int main() {
    unsigned char uuid[2];
    unsigned char randadd[6];
    
    srand(time(NULL));
    
    signal(SIGINT, handle_sigint);
    
    g_context = create_context();
    if (!g_context) {
        printf("[ERROR] Failed to create context\n");
        return 1;
    }
    
    if(init_blue_ex("keymouse-a.txt", 1) == 0) {
        destroy_context(g_context);
        return 0;
    }
    
    if(localnode() != 1) {
        printf("[ERROR] Device Address not set.");
        printf("[WARNING] Edit keymouse.txt to set ADDRESS = %s\n", device_address(localnode()));
        destroy_context(g_context);
        return 0;
    }

    uuid[0] = 0x2A;
    uuid[1] = 0x00;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), name, 3); 

    uuid[0] = 0x2A;
    uuid[1] = 0x01;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), appear, 0);

    uuid[0] = 0x2A;
    uuid[1] = 0x4E;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), protocolmode, 0);

    uuid[0] = 0x2A;
    uuid[1] = 0x4A;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), hidinfo, 0);

    uuid[0] = 0x2A;
    uuid[1] = 0x4B;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), reportmap, 0);

    uuid[0] = 0x2A;
    uuid[1] = 0x4D;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), report, 0);

    uuid[0] = 0x2A;
    uuid[1] = 0x50;
    write_ctic(localnode(), find_ctic_index(localnode(), UUID_2, uuid), pnpinfo, 0);
    
    randadd[0] = 0xD3;
    randadd[1] = 0x56;
    randadd[2] = 0xDB;
    randadd[3] = 0x77;
    randadd[4] = 0x32;
    randadd[5] = 0xA0;
    set_le_random_address(randadd);

    set_le_interval(6, 6);
        
    if(!init_command_socket(g_context)) {
        printf("[ERROR] Failed to initialize command socket\n");
        destroy_context(g_context);
        return 1;
    } 

    set_le_wait(20000);                     
    le_pair(localnode(), JUST_WORKS, 0);
    le_server(lecallback, 5);
    
    destroy_context(g_context);
    close_all();
    return 1;
}

void handle_sigint(int sig) {
    running = 0;
    printf("\n[INFO] Turning off HID server...\n");
}

void process_key_command(int key) {
    int n, hidcode;
    unsigned char buf[8];
    static int reportindex = -1;
    int delay;
    if (key >= '1' && key <= '3') {
        delay = random_delay(KEY_DELAY_MIN/2, KEY_DELAY_MAX/2);
    } else {
         delay = random_delay(KEY_DELAY_MIN, KEY_DELAY_MAX);
    }
        
    hidcode = hid_key_code(key);
    if(hidcode == 0)
        return;
        
    printf("[DEBUG] Sending HID code: %02X\n", hidcode);

    if(reportindex < 0) {  
        buf[0] = 0x2A;
        buf[1] = 0x4D;
        reportindex = find_ctic_index(localnode(), UUID_2, buf);
        if(reportindex < 0) {
            printf("[ERROR] Failed to find Report characteristic\n");
            return;
        }
    }     

    for(n = 0; n < 8; ++n)
        buf[n] = 0;
       
    buf[0] = (hidcode >> 8) & 0xFF;
    buf[2] = hidcode & 0xFF;
    write_ctic(localnode(), reportindex, buf, 0);
   
    usleep(delay);
    
    buf[0] = 0;
    buf[2] = 0;
    write_ctic(localnode(), reportindex, buf, 0); 
}

void process_mouse_command(char x, char y, char but) {
    unsigned char buf[3];
    static int reportindex = -1;
    int delay = random_delay(CLICK_DELAY_MIN, CLICK_DELAY_MAX);
   
    if(reportindex < 0) {  
        buf[0] = 0x2A;
        buf[1] = 0x4D;
        reportindex = find_ctic_index(localnode(), UUID_2, buf);
        if(reportindex < 0) {
            printf("[ERROR] Failed to find Report characteristic\n");
            return;
        }
        ++reportindex;
    }     

    buf[0] = but;
    buf[1] = x;
    buf[2] = y;
       
    write_ctic(localnode(), reportindex, buf, 0);

    if(buf[0] != 0) {
        usleep(delay);
        
        buf[0] = 0;
        write_ctic(localnode(), reportindex, buf, 0);
    }
}

int lecallback(int clientnode, int op, int cticn) {
    static int connection_established = 0;
    
    if (op == LE_CONNECT) {
        printf("[INFO] Connected OK.\n");
        connection_established = 1;
    }

    if (!running || op == LE_DISCONNECT) {
        printf("[INFO] Turning off HID server...\n");
        return SERVER_EXIT;
    }

    if (connection_established && op == LE_TIMER) {
        if (g_context->client_socket == -1) {
            g_context->client_socket = accept(g_context->command_socket, NULL, NULL);
            if (g_context->client_socket > 0) {
                printf("[INFO] Client connected\n");
                
                int flags = fcntl(g_context->client_socket, F_GETFL, 0);
                fcntl(g_context->client_socket, F_SETFL, flags | O_NONBLOCK);
                
                g_context->thread_running = 1;
                if (pthread_create(&g_context->receiver_thread, NULL, socket_receiver_thread, g_context) != 0) {
                    printf("[ERROR] Failed to create receiver thread: %s\n", strerror(errno));
                    close(g_context->client_socket);
                    g_context->client_socket = -1;
                }
            }
        }
        
        long long current_time = get_timestamp();

int keys_processed = 0;
const int max_keys_per_cycle = 3;

while (keys_processed < max_keys_per_cycle) {
    pthread_mutex_lock(&g_context->key_mutex);
    if (g_context->key_queue_size > 0) {
        char key = g_context->key_queue[g_context->key_queue_head].arg1;
        g_context->key_queue_head = (g_context->key_queue_head + 1) % MAX_QUEUE_SIZE;
        g_context->key_queue_size--;
        pthread_mutex_unlock(&g_context->key_mutex);
        
        process_key_command(key);
        keys_processed++;
        
        if (keys_processed == 1) {
            g_context->last_key_time = current_time;
        }
    } else {
        pthread_mutex_unlock(&g_context->key_mutex);
        break;
    }
}

int clicks_processed = 0;
const int max_clicks_per_cycle = 2;

while (clicks_processed < max_clicks_per_cycle) {
    pthread_mutex_lock(&g_context->click_mutex);
    if (g_context->click_queue_size > 0) {
        int button = g_context->click_queue[g_context->click_queue_head].arg1;
        g_context->click_queue_head = (g_context->click_queue_head + 1) % MAX_QUEUE_SIZE;
        g_context->click_queue_size--;
        pthread_mutex_unlock(&g_context->click_mutex);
        
        process_mouse_command(0, 0, button);
        clicks_processed++;
        
        if (clicks_processed == 1) {
            g_context->last_click_time = current_time;
        }
    } else {
        pthread_mutex_unlock(&g_context->click_mutex);
        break;
    }
}

const int max_moves_per_cycle = 8;
int moves_processed = 0;

while (moves_processed < max_moves_per_cycle) {
    pthread_mutex_lock(&g_context->move_mutex);
    if (g_context->move_queue_size > 0) {
        int x = g_context->move_queue[g_context->move_queue_head].arg1;
        int y = g_context->move_queue[g_context->move_queue_head].arg2;
        g_context->move_queue_head = (g_context->move_queue_head + 1) % MAX_QUEUE_SIZE;
        g_context->move_queue_size--;
        pthread_mutex_unlock(&g_context->move_mutex);
        
        process_mouse_command(x, y, 0);
        moves_processed++;
        
        if (moves_processed == 1) {
            g_context->last_move_time = current_time;
        }
    } else {
        pthread_mutex_unlock(&g_context->move_mutex);
        break;
    }
}
        
        if (g_context->thread_running && g_context->client_socket != -1) {
            if (pthread_kill(g_context->receiver_thread, 0) != 0) {
                printf("[WARNING] Receiver thread appears to have died, resetting\n");
                g_context->thread_running = 0;
                close(g_context->client_socket);
                g_context->client_socket = -1;
            }
        }
    }

    return SERVER_CONTINUE;
}

Attempts:

I implemented gradual movements by dividing the distance into small steps I experimented with different step sizes and waiting times Added feedback check (current position vs. desired position)

The latest version “almost” works, but still tends to overshoot the target by a few pixels. I suspect the problem is related to:

Mouse acceleration in the Windows operating system How values are interpreted by the HID protocol Possible non-linear scaling between the values sent and the distance traveled by the cursor

Any suggestions for more precise movements via Bluetooth HID, especially for fine positioning of the cursor?

PS: The C code is a adapted version of btferret library available in github.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744230023a4564200.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信