-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
272 lines (188 loc) · 6.82 KB
/
main.py
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
#!/usr/bin/env python
# The interesting part is at the bottom!
from __future__ import division, print_function
# Parse command line arguments
import argparse
parser = argparse.ArgumentParser(
description='NXT Ultrasonic Mouser',
formatter_class=argparse.RawTextHelpFormatter
)
parser.add_argument('-d', '--direction', action='store', default='up', choices=['left', 'right', 'up', 'down'], help='the direction the sensor is pointing across the screen')
parser.add_argument('-r', '--recalibrate', action='store_true', help='recalibrate the sensor to the screen')
parser.add_argument('-c', '--clicky', action='store_true', help='auto-click repeatedly while interacting')
parser.add_argument('-b', '--bounds', action='store_true', help='choose the on-screen bounds for auto-clicking')
parser.add_argument('-w', '--swirly', action='store_true', help=';)')
args = vars(parser.parse_args())
closer_edge = {
'left': 'right',
'right': 'left',
'up': 'bottom',
'down': 'top'
}[args['direction']]
further_edge = {
'left': 'left',
'right': 'right',
'up': 'top',
'down': 'bottom'
}[args['direction']]
# Set up mouse emulation
from pymouse import PyMouse
mouse = PyMouse()
screen_width, screen_height = mouse.screen_size()
# Set up additional mouse abstraction
mouse_is_pressed = False
mouse_last_x = screen_width / 2
mouse_last_y = screen_height / 2
def mouse_coords(x = None, y = None):
global mouse_last_x
global mouse_last_y
if x is None or y is None:
# Retrieving the mouse position for some reason causes "Communication bus error"s
# x, y = mouse.position()
x = mouse_last_x
y = mouse_last_y
else:
mouse_last_x = x
mouse_last_y = y
return int(x), int(y)
def mouse_move(x, y):
x, y = mouse_coords(x, y)
mouse.move(x, y)
def mouse_press(x = None, y = None):
global mouse_is_pressed
x, y = mouse_coords(x, y)
mouse.press(x, y)
mouse_is_pressed = True
def mouse_release(x = None, y = None):
global mouse_is_pressed
x, y = mouse_coords(x, y)
mouse.release(x, y)
mouse_is_pressed = False
# ;)
if args['swirly']:
import time
import math
tau = math.pi * 2
for i in range(500):
x = int(math.sin(tau*i/100)*screen_width/3 + screen_width/2)
y = int(math.cos(tau*i/100)*screen_height/3 + screen_height/2)
mouse_move(x, y)
time.sleep(.01)
parser.exit()
# Connect to the NXT
from nxt.locator import *
from nxt.sensor import *
brick = find_one_brick(method=Method(usb=True, bluetooth=True, fantomusb=True, fantombt=True))
# Exit handler
def exit_handler():
print('Disconnecting NXT socket')
brick.sock.close()
print('Releasing mouse')
mouse_release()
import atexit
atexit.register(exit_handler)
# Print brick info
name, host, signal_strength, user_flash = brick.get_device_info()
print('')
print('NXT brick name:', name)
print('Host address:', host)
print('Bluetooth signal strength:', signal_strength)
print('Free user flash:', user_flash)
print('')
# Ultrasonic Sensor
ultrasonic = get_sensor(brick, PORT_4)
# Calibration
config_file_name = 'config.ini'
from ConfigParser import *
config = SafeConfigParser()
config.read(config_file_name)
ceiling_sample = None
upper_sample = None
lower_sample = None
try:
ceiling_sample = config.getint('Calibration', 'ceiling')
upper_sample = config.getint('Calibration', 'upper')
lower_sample = config.getint('Calibration', 'lower')
except Exception:
pass
import sys
if args['recalibrate'] or not (ceiling_sample and upper_sample and lower_sample):
print('Begin calibration.')
def input_sample(prompt, display):
# Todo: interactive display until input
raw_input(prompt)
sample = ultrasonic.get_sample()
print(display.format(sample))
return sample
ceiling_sample = input_sample('Press Enter to record the distance when you aren\'t interacting with the sensor.', 'Ceiling sample: {0}')
if ceiling_sample is 255:
print('Warning: 255 means the distance is far away (which is good), but it can cause issues if it fluctuates between 255 and something much lower. If it moves your mouse to the edge of the screen when you aren\'t interacting with it, recalibrate and see if you can get a lower number.')
lower_sample = input_sample('Place your hand over the sensor level with the %s edge of your screen. Press Enter.' % closer_edge, 'Lower sample: {0}')
if lower_sample is 255:
print('Abort: 255 means the distance is out of range of your sensor (too far away!)')
sys.exit(1)
upper_sample = input_sample('Move your hand to the %s edge of your screen. Press Enter to finish calibration.' % further_edge, 'Upper sample: {0}')
if upper_sample is 255:
print('Abort: 255 means the distance is out of range of your sensor (too far away!)')
sys.exit(1)
# Write the configuration to a file
try:
config.add_section('Calibration')
except DuplicateSectionError:
pass
config.set('Calibration', 'ceiling', str(ceiling_sample))
config.set('Calibration', 'upper', str(upper_sample))
config.set('Calibration', 'lower', str(lower_sample))
with open(config_file_name, 'w') as config_file:
config.write(config_file)
# End Calibration
# The threshold below the recorded ceiling distance within which new samples are also considered ceiling
ceiling_threshold_sample = 30
# Here comes the interesting part
while True:
try:
current_sample = ultrasonic.get_sample()
except KeyboardInterrupt:
print('')
sys.exit(0)
x = screen_width / 2
y = screen_height / 2
# when current_sample = upper_sample, sample_ratio should = 1
# when current_sample = lower_sample, sample_ratio should = 0
sample_ratio = (current_sample - lower_sample) / (upper_sample - lower_sample)
if args['direction'] == 'left':
# Pointin' to da left
x = screen_width * (1 - sample_ratio)
elif args['direction'] == 'right':
# Pointin' to da right
x = screen_width * sample_ratio
elif args['direction'] == 'up':
# Pointing up
y = screen_height * (1 - sample_ratio)
else:
# Are you hanging this from the ceiling or something? Okay...
y = screen_height * sample_ratio
def print_status(format_string):
text = format_string.format(
sample=current_sample,
us=upper_sample,
ls=lower_sample,
percent=int(sample_ratio*100),
x=int(x), y=int(y)
)
sys.stdout.write('\r\x1b[K' + text)
sys.stdout.flush()
if current_sample < ceiling_sample - ceiling_threshold_sample:
print_status('sample: {us} > ({sample}) > {ls}, {percent}%, move mouse to ({x}, {y})')
if args['clicky']:
if 100 < y < (screen_height - 100):
mouse_press(x, y)
mouse_release(x, y)
else:
mouse_release(x, y)
else:
mouse_move(x, y)
else:
print_status('sample: {us} > ({sample}) > {ls}, {percent}%, (near ceiling)')
if mouse_is_pressed:
mouse_release()