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条)