bzr branch
http://bzr.ed.am/elec/quadcopter
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
1 |
#!/usr/bin/python |
2 |
# |
|
3 |
# rc-interface-test |
|
4 |
||
5 |
||
6 |
import pygtk |
|
7 |
pygtk.require( '2.0' ); |
|
8 |
import gtk, serial, struct |
|
9 |
from io import BytesIO |
|
10 |
||
11 |
||
12 |
class MainDialogue: |
|
13 |
||
14 |
||
15 |
# number of channels to read |
|
16 |
NUM_CHANNELS = 8 |
|
17 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
18 |
# number of motor channels |
19 |
NUM_MOTORS = 4 |
|
20 |
||
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
21 |
# maximum channel value |
22 |
MAX_CHANNEL_VALUE = 1000 |
|
23 |
||
24 |
# serial device to use |
|
20
by Tim Marston
switch rc-interface (and test app) to Arduino Pro Mini |
25 |
SERIAL_DEVICE = '/dev/ttyUSB0' |
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
26 |
|
27 |
||
28 |
def __init__( self ): |
|
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
29 |
try: |
30 |
self.link = serial.Serial( self.SERIAL_DEVICE, 9600 ) |
|
31 |
self.link_error = None |
|
32 |
except serial.serialutil.SerialException as e: |
|
33 |
self.link = None |
|
34 |
self.link_error = e |
|
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
35 |
self.got_one = False |
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
36 |
self.motor_values = [ 0 ] * self.NUM_MOTORS |
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
37 |
self.quit = False |
38 |
self.buffer = bytearray() |
|
39 |
||
40 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
41 |
def add_info( self, vbox, heading = False, text = '' ): |
42 |
label = gtk.Label() |
|
43 |
if( heading ): |
|
44 |
label.set_markup( "<b>%s</b>" % text ) |
|
45 |
else: |
|
46 |
label.set_label( text ) |
|
47 |
# label.set_justify( gtk.JUSTIFY_LEFT ) |
|
48 |
label.set_alignment( 0, 0 ) |
|
49 |
vbox.pack_start( label, False, False ) |
|
50 |
||
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
51 |
def setup_window( self ): |
52 |
||
53 |
# create and set up window |
|
54 |
self.window = gtk.Window( gtk.WINDOW_TOPLEVEL ) |
|
55 |
self.window.connect( "delete_event", self.destroy ) |
|
56 |
self.window.connect( "destroy", self.destroy ) |
|
57 |
self.window.set_title( "Testing rc-interface serial comms" ); |
|
58 |
self.window.set_border_width( 30 ) |
|
59 |
self.window.set_modal( True ) |
|
60 |
self.window.connect( "key-press-event", self.key_press_event ) |
|
61 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
62 |
hbox = gtk.HBox( False, 10 ) |
63 |
self.window.add( hbox ) |
|
64 |
||
65 |
# controls frame |
|
66 |
frame = gtk.Frame( "Channels" ) |
|
67 |
frame.set_shadow_type( gtk.SHADOW_IN ) |
|
68 |
hbox.pack_start( frame ) |
|
69 |
||
70 |
table = gtk.Table( 2, self.NUM_CHANNELS ) |
|
71 |
table.set_border_width( 25 ) |
|
72 |
table.set_row_spacings( 10 ) |
|
73 |
table.set_col_spacings( 15 ) |
|
74 |
frame.add( table ) |
|
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
75 |
|
76 |
# add channels |
|
77 |
self.controls = [] |
|
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
78 |
for a in range( self.NUM_CHANNELS ): |
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
79 |
|
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
80 |
label = gtk.Label( a + 1 ) |
81 |
table.attach( label, a, a + 1, 1, 2 ) |
|
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
82 |
|
83 |
adj = gtk.Adjustment( 0, 0, self.MAX_CHANNEL_VALUE, 1, 0, 0 ) |
|
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
84 |
control = gtk.VScale( adj ) |
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
85 |
control.set_draw_value( False ) |
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
86 |
control.set_inverted( True ) |
87 |
control.set_size_request( -1, 200 ) |
|
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
88 |
control.set_sensitive( False ) |
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
89 |
table.attach( control, a, a + 1, 0, 1 ) |
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
90 |
|
91 |
self.controls.append( control ) |
|
92 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
93 |
# motors frame |
94 |
frame = gtk.Frame( "Motors" ) |
|
95 |
frame.set_shadow_type( gtk.SHADOW_IN ) |
|
96 |
hbox.pack_start( frame ) |
|
97 |
||
98 |
table = gtk.Table( 2, self.NUM_MOTORS ) |
|
99 |
table.set_border_width( 25 ) |
|
100 |
table.set_row_spacings( 10 ) |
|
101 |
table.set_col_spacings( 15 ) |
|
102 |
frame.add( table ) |
|
103 |
||
104 |
# add motor controls |
|
105 |
self.motors = [] |
|
106 |
for a in range( self.NUM_MOTORS ): |
|
107 |
||
108 |
label = gtk.Label( a + 1 ) |
|
109 |
table.attach( label, a, a + 1, 1, 2 ) |
|
110 |
||
111 |
adj = gtk.Adjustment( 0, 0, self.MAX_CHANNEL_VALUE, 1, 0, 0 ) |
|
112 |
control = gtk.VScale( adj ) |
|
113 |
control.set_draw_value( False ) |
|
114 |
control.set_inverted( True ) |
|
115 |
control.set_size_request( -1, 200 ) |
|
116 |
table.attach( control, a, a + 1, 0, 1 ) |
|
117 |
||
118 |
control.connect( "value-changed", self.on_motor_change, a ) |
|
119 |
||
120 |
self.motors.append( control ) |
|
121 |
||
122 |
# info frame |
|
123 |
frame = gtk.Frame( "Info" ) |
|
124 |
hbox.pack_start( frame ) |
|
125 |
||
126 |
vbox = gtk.VBox() |
|
127 |
vbox.set_border_width( 10 ) |
|
128 |
frame.add( vbox ) |
|
129 |
||
130 |
self.add_info( vbox, True, "USB Device:" ) |
|
131 |
self.add_info( vbox, False, self.SERIAL_DEVICE ) |
|
132 |
self.add_info( vbox ) |
|
133 |
||
134 |
if( self.link_error is not None ): |
|
135 |
self.add_info( vbox, True, "USB Error:" ) |
|
136 |
label = gtk.Label( self.link_error ) |
|
137 |
label.set_line_wrap( True ) |
|
138 |
label.set_size_request( 150, -1 ) |
|
139 |
vbox.pack_start( label, False ) |
|
140 |
else: |
|
141 |
pass |
|
142 |
||
143 |
vbox.pack_start( gtk.Label( "" ) ) |
|
144 |
||
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
145 |
# show it |
146 |
self.window.show_all() |
|
147 |
||
148 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
149 |
def on_motor_change( self, widget, motor ): |
150 |
self.motor_values[ motor ] = int( widget.get_value() ) |
|
151 |
self.write_serial() |
|
152 |
||
153 |
||
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
154 |
def key_press_event( self, widget, event ): |
155 |
if event.keyval == gtk.keysyms.Escape: |
|
156 |
self.window.destroy() |
|
157 |
else: |
|
158 |
return False |
|
159 |
||
160 |
||
161 |
def destroy( self, widget, data = None ): |
|
162 |
self.quit = True |
|
163 |
||
164 |
||
165 |
def process_serial_data_at( self, pos ): |
|
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
166 |
for a in range( self.NUM_CHANNELS ): |
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
167 |
at = 2 * a + pos |
168 |
value = struct.unpack( "h", self.buffer[ at : at + 2 ] )[ 0 ] |
|
169 |
||
170 |
# update control |
|
171 |
self.controls[ a ].set_value( value ) |
|
172 |
||
173 |
||
174 |
def read_serial( self ): |
|
175 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
176 |
# serial problem? |
177 |
if self.link is None: return |
|
178 |
||
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
179 |
# try and read |
180 |
in_waiting = self.link.inWaiting() |
|
181 |
if( in_waiting ): |
|
182 |
self.buffer.extend( self.link.read( in_waiting ) ) |
|
183 |
||
184 |
# check buffer for double -1 |
|
185 |
got = bytearray() |
|
186 |
i = 0 |
|
187 |
while i < len( self.buffer ): |
|
188 |
||
189 |
# check foir double -1 |
|
190 |
if i > 0 and self.buffer[ i ] == 255 and \ |
|
191 |
self.buffer[ i - 1 ] == 255: |
|
192 |
||
193 |
# if we've got enough data, process it |
|
194 |
start_index = i - ( self.NUM_CHANNELS * 2 + 1 ) |
|
195 |
if start_index >= 0: |
|
196 |
self.process_serial_data_at( start_index ) |
|
197 |
||
198 |
# remove everything up to and including this double -1 from |
|
199 |
# the serial buffer |
|
200 |
self.buffer = self.buffer[ i + 1 : ] |
|
201 |
i = 0 |
|
202 |
||
203 |
else: |
|
204 |
i += 1 |
|
205 |
||
206 |
||
31
by Tim Marston
rc-interface-test: don't die when no serial; layout change, and added motor |
207 |
def write_serial( self ): |
208 |
||
209 |
# serial problem? |
|
210 |
if self.link is None: return |
|
211 |
||
212 |
# construct message |
|
213 |
bytes = bytearray() |
|
214 |
bytes.extend( struct.pack( "h", -1 ) ) |
|
215 |
for i in range( self.NUM_MOTORS ): |
|
216 |
bytes.extend( struct.pack( "h", self.motor_values[ i ] ) ) |
|
217 |
||
218 |
# send |
|
219 |
self.link.write( bytes ) |
|
220 |
||
221 |
||
19
by Tim Marston
added python GTK test program that reads and displays the serial data from the |
222 |
def main( self ): |
223 |
self.setup_window() |
|
224 |
while not self.quit: |
|
225 |
||
226 |
# update gtk |
|
227 |
while gtk.events_pending(): |
|
228 |
gtk.main_iteration( False ) |
|
229 |
||
230 |
# check serial interface |
|
231 |
self.read_serial() |
|
232 |
||
233 |
||
234 |
MainDialogue().main() |