-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrootctl.py
More file actions
156 lines (118 loc) · 4.28 KB
/
rootctl.py
File metadata and controls
156 lines (118 loc) · 4.28 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
import math
from queue import Queue
from collections import deque
import rootprotocol as rp
from rootbluetoothdevice import RootDeviceManager
class Point:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __add__(self, pt):
return Point(self.x + pt.x, self.y + pt.y)
def __repr__(self):
return '<Point {} {}>'.format(self.x, self.y)
def towards(self, pt):
return math.atan2(pt.x - self.x, pt.y - self.y) % (2 * math.pi)
def distance(self, pt):
return math.sqrt((pt.x - self.x)**2 + (pt.y - self.y)**2)
class RootState:
def __init__(self):
self.pos = Point()
self.angle = math.pi / 2
self.pen = False
self.eraser = False
def forward(self, dist):
self.pos += Point(dist * math.sin(self.angle),
dist * math.cos(self.angle))
return rp.build_command('drive', dist)
def backward(self, dist):
return self.forward(-dist)
def right(self, angle):
"""
Turn right by \a angle degrees
"""
self.angle += math.pi / 180 * angle
self.angle %= 2 * math.pi
return rp.build_command('turn', angle * 10)
def left(self, angle):
return self.right(-angle)
def penup(self):
self.pen = False
self.eraser = False
return rp.build_command('setpen', 0)
def pendown(self):
self.pen = True
self.eraser = False
return rp.build_command('setpen', 1)
def say(self, phrase):
return rp.build_command('say', *rp.str_to_payload(phrase))
def goto(self, x, y):
bearing = self.pos.towards(Point(x, y))
distance = self.pos.distance(Point(x, y))
angle = (bearing - self.angle) % (2 * math.pi)
# turn left for forth quadrant, go backwards for second and third
if angle > 1.5 * math.pi:
angle -= 2 * math.pi
elif angle > 0.5 * math.pi:
angle -= math.pi
distance *= -1
print(self.pos, self.angle, bearing, distance, angle)
self.pos = Point(x, y)
return [ rp.build_command('turn', int(1800 / math.pi * angle)),
rp.build_command('drive', int(distance)),
rp.build_command('turn', int(-1800 / math.pi * angle)) ]
def reset(self):
angle = (self.angle - math.pi / 2)
if abs(angle) > math.pi:
angle = math.pi / 2 - self.angle
return (goto(0, 0) +
[rp.build_command('turn', 1800 / math.pi * angle)])
def direct_drive(self, left, right):
return rp.build_command('motors', left, right)
class RootDevice:
def wrap_command(self, state_command):
def wrapped(*args, **kwargs):
commands = state_command(*args, **kwargs)
if type(commands[0]) is not list:
commands = [commands]
for c in commands:
id = self.robot.send_command(c)
response = self.wait_for_response(id)
return wrapped
def __init__(self, bluetooth_adapter_name='hci0'):
self.state = RootState()
# Add command methods from RootState
command_list = ['forward', 'backward',
'left', 'right',
'penup', 'pendown',
'say',
'goto', 'reset',
]
for c in command_list:
setattr(self, c, self.wrap_command(getattr(self.state, c)))
self.robot = RootDeviceManager(adapter_name=bluetooth_adapter_name)
self.result_queue = Queue()
self.robot.connect(self.result_queue)
response = self.result_queue.get()
def __del__(self):
self.disconnect()
def disconnect(self):
self.robot.disconnect()
def wait_for_response(self, id):
while True:
response = self.result_queue.get()
print(id, response)
if response.id == id:
return response
def direct_drive(self, left, right):
self.robot.send_command(self.state.direct_drive(left, right))
def test():
root = RootDevice()
root.say('hello world')
root.pendown()
for i in range(4):
root.forward(100)
root.right(90)
root.disconnect()
if __name__ == '__main__':
test()