Repository navigation
Expand file tree
/
Copy pathcode.py
More file actions
329 lines (264 loc) · 13.5 KB
/
Copy pathcode.py
File metadata and controls
329 lines (264 loc) · 13.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
"""
=============================================================================
Mouse Jiggler — Bézier Curve Edition
=============================================================================
Hardware: Waveshare RP2350-One (CircuitPython)
Purpose: Sending mouse movement
via USB HID, spoofing an Optical Mouse (VID/PID
0x093A:0x2510) so the device is seen as a real mouse.
Algorithm: Cubic Bézier curves generate smooth, natural cursor paths
that mimic human mouse usage. Unlike simple pixel twitches,
curved paths with variable speed reduce detectable patterns flagged by endpoint detection software (EDR) that flags repetitive linear patterns.
A cubic Bézier curve is defined by four points:
P0 (start) → P1 (control) → P2 (control) → P3 (end)
The parameter t moves from 0.0 (at P0) to 1.0 (at P3).
The curve bends toward P1 and P2 without passing through them.
LED Status:
Green — Idle (USB bus silent, mimicking a human sitting still)
Yellow — Moving (HID reports being sent along the Bézier curve)
Red — Error (HID write failed)
"""
# =============================================================================
# Imports
# =============================================================================
import time # Sleep and timing functions
import random # Random integers, floats, and angles for natural variation
import math # Trigonometry (cos, sin) for Bézier path generation
import board # CircuitPython board pin definitions (GP16 for NeoPixel)
import usb_hid # CircuitPython USB HID interface
import neopixel # CircuitPython driver for WS2812B / NeoPixel LEDs
from adafruit_hid.mouse import Mouse # High-level mouse HID wrapper
# =============================================================================
# Hardware Setup
# =============================================================================
# Initialize the built-in RGB LED on GP16.
# The RP2350-One has a single WS2812B (NeoPixel) LED.
# brightness=0.1
# pixel_order=RGB matches the physical wire order of the RP2350-One's LED.
pixel = neopixel.NeoPixel(
board.GP16, 1,
brightness=0.1,
auto_write=True,
pixel_order=neopixel.RGB
)
# Initialize the mouse HID device.
# usb_hid.devices is a tuple of all active HID interfaces (set in boot.py).
# The Mouse class wraps raw HID report construction into simple .move() calls.
# HID mice use RELATIVE movement: each report says "move N pixels from here",
# not "go to absolute coordinates X,Y". This is why we track position ourselves.
mouse = Mouse(usb_hid.devices)
# =============================================================================
# LED Color Constants (RGB tuples)
# =============================================================================
Green = (0, 255, 0) # Idle — device is active but silent
Yellow = (255, 255, 0) # Moving — HID reports are being sent
# Red is inline as (255, 0, 0) to keep the error handler compact
# =============================================================================
# Cursor Position State
# =============================================================================
# HID mice report relative deltas (dx, dy), so we must track the cursor's
# absolute position client-side to compute each step's delta.
# Starting at screen center (960, 540) assumes a 1920×1080 display.
# The path generator clamps coordinates to stay within safe bounds.
cur_x, cur_y = 960.0, 540.0
# =============================================================================
# Bézier Curve Math
# =============================================================================
def bezier(t, p0, p1, p2, p3):
"""
Evaluate a cubic Bézier curve at parameter t.
Formula:
B(t) = (1-t)³·P0 + 3(1-t)²t·P1 + 3(1-t)t²·P2 + t³·P3
Where:
t — Progress along the curve (0.0 = start, 1.0 = end)
p0 — Start point (x, y)
p1 — First control point (pulls curve toward it)
p2 — Second control point (pulls curve toward it)
p3 — End point (x, y)
Returns:
(x, y) tuple — the interpolated point on the curve at t.
The control points p1 and p2 determine the curvature. They are NOT
on the curve itself — the curve "bends toward" them. Moving p1/p2
further from the start→end line creates more pronounced arcs.
"""
u = 1.0 - t # Complement of t
u2 = u * u # u squared — used twice, pre-computed for speed
t2 = t * t # t squared — used twice, pre-computed for speed
return (
u2 * u * p0[0] + 3.0 * u2 * t * p1[0] + 3.0 * u * t2 * p2[0] + t2 * t * p3[0],
u2 * u * p0[1] + 3.0 * u2 * t * p1[1] + 3.0 * u * t2 * p2[1] + t2 * t * p3[1],
)
def ease(t):
"""
Apply smoothstep easing to a linear parameter t.
Formula: ease(t) = t² × (3 - 2t)
This transforms a linear 0→1 ramp into an S-curve:
- Slow at the start (cursor accelerates gradually)
- Fast in the middle (cursor reaches peak speed)
- Slow at the end (cursor decelerates to a stop)
Without easing, the cursor moves at constant speed — which looks
robotic. Humans naturally accelerate out of a resting position and
decelerate when approaching a target. Smoothstep replicates this.
Visual comparison (t vs ease(t)):
t=0.0 → 0.000 (slow start)
t=0.25 → 0.156
t=0.5 → 0.500 (midpoint, linear)
t=0.75 → 0.844
t=1.0 → 1.000 (slow end)
"""
return t * t * (3.0 - 2.0 * t)
# =============================================================================
# Path Generation
# =============================================================================
def generate_path(cx, cy):
"""
Generate a random cubic Bézier path from the current cursor position.
Algorithm:
1. Pick a random direction (angle) and distance (30–200 px).
2. Compute the end point, clamped to screen bounds.
3. Find the midpoint between start and end.
4. Place two control points perpendicular to the start→end line,
offset by random amounts (20–80 px each side).
This creates natural S-curves and arcs instead of straight lines.
5. Return the four control points as ((x,y), (x,y), (x,y), (x,y)).
Parameters:
cx, cy — Current cursor position (floats).
Returns:
Tuple of 4 points: (p0, p1, p2, p3) for the Bézier curve.
"""
# Step 1: Random direction (0 to 2π radians = 0° to 360°)
angle = random.uniform(0.0, 6.283185) # 2π
# Step 1b: Random distance — 30px is a small nudge, 200px crosses half the screen
dist = random.randint(30, 200)
# Step 2: Compute end point using polar to Cartesian conversion
ex = cx + dist * math.cos(angle)
ey = cy + dist * math.sin(angle)
# Step 2b: Clamp to screen bounds (conservative 1920×1080 safe zone)
# The 10px margin prevents the cursor from hitting screen edges where
# it might trigger corner hot zones (e.g., Windows taskbar, macOS Dock).
ex = max(10.0, min(1910.0, ex))
ey = max(10.0, min(1070.0, ey))
# Step 3: Midpoint between start and end — control points are placed here
mx = (cx + ex) * 0.5
my = (cy + ey) * 0.5
# Step 4: Perpendicular angle (90° offset from the travel direction)
# Adding π/2 radians (≈1.570796) rotates the direction vector 90°.
# This is the key to creating curves: control points sit off to the
# side of the straight line, pulling the curve into an arc.
pa = angle + 1.570796 # π/2
# Random perpendicular offsets for each control point.
# Different offsets on each side create asymmetric, natural-looking curves.
# Matching offsets would produce symmetric arcs (less human-like).
o1 = random.randint(20, 80) # Control point 1 offset
o2 = random.randint(20, 80) # Control point 2 offset (independent)
# Place control points on opposite sides of the midpoint.
# c1 is offset in the +perpendicular direction.
# c2 is offset in the -perpendicular direction.
c1 = (mx + o1 * math.cos(pa), my + o1 * math.sin(pa))
c2 = (mx - o2 * math.cos(pa), my - o2 * math.sin(pa))
return ((cx, cy), c1, c2, (ex, ey))
# =============================================================================
# HID Movement Helper
# =============================================================================
def send_move(dx, dy):
"""
Send a single relative mouse movement via USB HID.
Parameters:
dx — Horizontal delta in pixels (positive = right, negative = left).
dy — Vertical delta in pixels (positive = down, negative = up).
Returns:
True if the HID report was sent successfully.
False if the write failed (USB bus likely asleep from autosuspend).
On failure, the LED flashes red as a visual diagnostic.
"""
try:
mouse.move(x=dx, y=dy)
return True
except Exception:
# HID write failed — flash red
pixel[0] = (255, 0, 0)
time.sleep(0.15)
pixel[0] = Yellow
return False
# =============================================================================
# Startup
# =============================================================================
# Green LED indicates the device is powered on and idle
pixel[0] = Green
# =============================================================================
# Main Loop
# =============================================================================
while True:
# -------------------------------------------------------------------------
# Phase 1: Silent Idle (mimics a human sitting still)
# -------------------------------------------------------------------------
# Random delay between 5–15 seconds. During this time the device sends
# ZERO bytes on the USB bus — completely radio silent. This matches
# human behavior: people don't move the mouse constantly.
time.sleep(random.randint(5, 15))
# -------------------------------------------------------------------------
# Phase 2: Generate a Random Bézier Path
# -------------------------------------------------------------------------
# Creates a curved trajectory from the current cursor position to a
# random destination 30–200px away. The control points create natural
# arcs that look like a human casually moving the mouse.
path = generate_path(cur_x, cur_y)
# Randomize the curve resolution and duration.
# More steps = smoother curve but more HID reports sent.
# Duration controls how fast the cursor travels (100–300ms).
steps = random.randint(20, 40)
duration = random.uniform(0.1, 0.3) # seconds
step_delay = duration / steps # time between each HID report
# -------------------------------------------------------------------------
# Phase 3: Wake the USB Bus
# -------------------------------------------------------------------------
# USB autosuspend puts the port to sleep after ~1–2 seconds of inactivity.
# The first HID write after sleep may fail (red LED). We send 3 small
# "nudge" moves 100ms apart to wake the bus, then wait 100ms for it to
# stabilize. This prevents the main curve from losing its first steps.
pixel[0] = Yellow
# Determine nudge direction based on the path's horizontal travel
wake_x = 1 if (path[3][0] - cur_x) > 0 else -1
for _ in range(3):
send_move(wake_x, 0)
time.sleep(0.1)
time.sleep(0.1) # Settle pause for bus to fully stabilize
# -------------------------------------------------------------------------
# Phase 4: Interpolate Along the Bézier Curve
# -------------------------------------------------------------------------
# Walk t from 0.0 to 1.0 in `steps` increments.
# At each step:
# 1. Apply easing to t (slow→fast→slow)
# 2. Compute the Bézier point at that t
# 3. Calculate the relative delta from the previous position
# 4. Send the delta as an HID mouse report
# This produces smooth, curved movement with natural acceleration.
prev_x, prev_y = cur_x, cur_y
for i in range(1, steps + 1):
# Linear progress: 0.0 → 1.0
linear_t = i / steps
# Eased progress: S-curve (slow start, fast middle, slow end)
t = ease(linear_t)
# Compute the point on the Bézier curve at this t
nx, ny = bezier(t, *path) # *path unpacks into p0, p1, p2, p3
# Convert absolute position to relative HID delta
dx = int(nx - prev_x)
dy = int(ny - prev_y)
# Only send the report if there's actual movement.
# HID reports with (0, 0) are wasteful and could look suspicious
# to monitoring software that counts total reports sent.
if dx != 0 or dy != 0:
send_move(dx, dy)
prev_x, prev_y = nx, ny
# Uniform delay between steps. The easing function handles speed
# variation — steps near the start/end cover less distance (slow
# visually) while middle steps cover more (fast visually), even
# though the time between reports is constant.
time.sleep(step_delay)
# -------------------------------------------------------------------------
# Phase 5: Update State and Return to Idle
# -------------------------------------------------------------------------
# Record the final cursor position so the next path starts from here.
# Switch back to green LED to indicate the movement phase is complete.
cur_x, cur_y = prev_x, prev_y
pixel[0] = Green