/elec/quadcopter

To get this branch, use:
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()