00001
00002
00003 """
00004 A Python library for the cmRobot Element microcontroller.
00005 Created for The Pi Robot Project: http://www.pirobot.org
00006 Copyright (c) 2012 Patrick Goebel. All rights reserved.
00007
00008 This program is free software; you can redistribute it and/or modify
00009 it under the terms of the GNU General Public License as published by
00010 the Free Software Foundation; either version 2 of the License, or
00011 (at your option) any later version.
00012
00013 This program is distributed in the hope that it will be useful,
00014 but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016 GNU General Public License for more details at:
00017
00018 http://www.gnu.org/licenses/gpl.html
00019
00020 NOTE: See the offical Element documentation at:
00021
00022 http://www.cmrobot.com/#!element
00023
00024 See the example files for more details.
00025 """
00026
00027 import thread
00028 import math
00029 import os
00030 import time
00031 import sys
00032 from serial.serialutil import SerialException
00033 from serial import Serial
00034
00035 class Element:
00036 ''' Configuration Parameters
00037 '''
00038 N_ANALOG_PORTS = 6
00039 N_DIGITAL_PORTS = 12
00040
00041 MILLISECONDS_PER_PID_LOOP = 1.6
00042
00043 pi_robot = False
00044 if pi_robot:
00045 default_pid_params['units'] = 0
00046 default_pid_params['wheel_diameter'] = 0.132
00047 default_pid_params['wheel_track'] = 0.3365
00048 default_pid_params['encoder_resolution'] = 624
00049 default_pid_params['gear_reduction'] = 1.667
00050
00051 default_pid_params['encoder_type'] = 1
00052 default_pid_params['motors_reversed'] = True
00053
00054 default_pid_params['init_pid'] = False
00055
00056 default_pid_params['VPID_P'] = 2
00057 default_pid_params['VPID_I'] = 0
00058 default_pid_params['VPID_D'] = 5
00059 default_pid_params['VPID_L'] = 45
00060
00061 default_pid_params['DPID_P'] = 1
00062 default_pid_params['DPID_I'] = 0
00063 default_pid_params['DPID_D'] = 0
00064 default_pid_params['DPID_A'] = 5
00065 default_pid_params['DPID_B'] = 10
00066 else:
00067 default_pid_params = dict()
00068
00069
00070 BAD_VALUE = -999
00071
00072 def __init__(self, port="/dev/ttyUSB0", baudrate=19200, timeout=0.5, units=0):
00073 self.port = port
00074 self.baudrate = baudrate
00075 self.timeout = timeout
00076 self.units = units
00077 self.encoder_count = 0
00078 self.writeTimeout = timeout
00079 self.interCharTimeout = timeout / 30.
00080 self.count = 0
00081 self.shutdown = False
00082
00083
00084 self.mutex = thread.allocate_lock()
00085
00086
00087 self.analog_sensor_cache = [None] * self.N_ANALOG_PORTS
00088
00089
00090 self.digital_sensor_cache = [None] * self.N_DIGITAL_PORTS
00091
00092 def connect(self, use_base_controller=False, pid_params=default_pid_params):
00093 try:
00094 print "Connecting to Element on port", self.port, "..."
00095 self.port = Serial(port=self.port, baudrate=self.baudrate, timeout=self.timeout, writeTimeout=self.writeTimeout)
00096
00097 time.sleep(1)
00098 test = self.get_baud()
00099 if test != self.baudrate:
00100 time.sleep(1)
00101 test = self.get_baud()
00102 if test != self.baudrate:
00103 raise SerialException
00104 print "Connected at", self.baudrate, "baud. Main voltage:", self.voltage()
00105 print "Element is ready."
00106
00107 except SerialException:
00108 print "Cannot connect to Element!"
00109 print "Make sure you are plugged in and turned on."
00110 os._exit(1)
00111
00112
00113
00114 if self.get_units() != self.units:
00115 self.set_units(self.units)
00116
00117 def setup_base_controller(self, pid_params):
00118
00119 for param in pid_params:
00120 if pid_params[param] == "":
00121 print("*** PID Parameter " + param + " is missing. ***")
00122 os._exit(1)
00123
00124 self.wheel_diameter = pid_params['wheel_diameter']
00125 self.wheel_track = pid_params['wheel_track']
00126 self.encoder_resolution = pid_params['encoder_resolution']
00127 self.encoder_type = pid_params['encoder_type']
00128 self.gear_reduction = pid_params['gear_reduction']
00129 self.motors_reversed = pid_params['motors_reversed']
00130
00131 self.init_pid = pid_params['init_pid']
00132 self.VPID_P = pid_params['VPID_P']
00133 self.VPID_I = pid_params['VPID_I']
00134 self.VPID_D = pid_params['VPID_D']
00135 self.VPID_L = pid_params['VPID_L']
00136 self.DPID_P = pid_params['DPID_P']
00137 self.DPID_I = pid_params['DPID_I']
00138 self.DPID_D = pid_params['DPID_D']
00139 self.DPID_A = pid_params['DPID_A']
00140 self.DPID_B = pid_params['DPID_B']
00141
00142 self.loop_interval = None
00143 self.ticks_per_meter = None
00144
00145 if pid_params['init_pid']:
00146
00147 self.init_PID()
00148 else:
00149
00150 self.units = self.get_units()
00151 [self.VPID_P, self.VPID_I, self.VPID_D, self.VPID_L] = self.get_vpid()
00152 [self.DPID_P, self.DPID_I, self.DPID_D, self.DPID_A, self.DPID_B] = self.get_dpid()
00153
00154 if self.units == 0:
00155 self.ticks_per_meter = int(self.encoder_resolution * self.gear_reduction / (self.wheel_diameter * math.pi))
00156 elif self.units == 1:
00157 self.ticks_per_meter = int(self.encoder_resolution * self.gear_reduction / (self.wheel_diameter * math.pi * 2.54 / 100.0))
00158
00159 self.loop_interval = self.VPID_L * self.MILLISECONDS_PER_PID_LOOP / 1000
00160
00161
00162 def init_PID(self):
00163 print "Storing Units and PID parameters in EEPROM"
00164 self.set_encoder(self.encoder_type)
00165 self.set_units(self.units)
00166 self.set_vpid(self.VPID_P, self.VPID_I, self.VPID_D, self.VPID_L)
00167 self.set_dpid(self.DPID_P, self.DPID_I, self.DPID_D, self.DPID_A, self.DPID_B)
00168
00169 def open(self):
00170 ''' Open the serial port.
00171 '''
00172 self.port.open()
00173
00174 def close(self):
00175 ''' Close the serial port.
00176 '''
00177 self.port.close()
00178
00179 def send(self, cmd):
00180 ''' This command should not be used on its own: it is called by the execute commands
00181 below in a thread safe manner.
00182 '''
00183 self.port.write(cmd + '\r')
00184
00185 def recv(self, timeout=0.5):
00186 timeout = min(timeout, self.timeout)
00187 ''' This command should not be used on its own: it is called by the execute commands
00188 below in a thread safe manner. Note: we use read() instead of readline() since
00189 readline() tends to return garbage characters from the Element
00190 '''
00191 c = ''
00192 value = ''
00193 attempts = 0
00194 while c != '>':
00195 c = self.port.read(1)
00196 value += c
00197 attempts += 1
00198 if attempts * self.interCharTimeout > timeout:
00199 return None
00200
00201 value = value.strip('>')
00202
00203 return value
00204
00205 def recv_ack(self):
00206 ''' This command should not be used on its own: it is called by the execute commands
00207 below in a thread safe manner.
00208 '''
00209 ack = self.recv(self.timeout)
00210 return ack == 'ACK'
00211
00212 def recv_int(self):
00213 ''' This command should not be used on its own: it is called by the execute commands
00214 below in a thread safe manner.
00215 '''
00216 value = self.recv(self.timeout)
00217 try:
00218 return int(value)
00219 except:
00220 return None
00221
00222 def recv_array(self):
00223 ''' This command should not be used on its own: it is called by the execute commands
00224 below in a thread safe manner.
00225 '''
00226 try:
00227 values = self.recv(self.timeout * self.N_ANALOG_PORTS).split()
00228 return map(int, values)
00229 except:
00230 return []
00231
00232 def execute(self, cmd):
00233 ''' Thread safe execution of "cmd" on the Element returning a single value.
00234 '''
00235 self.mutex.acquire()
00236
00237 try:
00238 self.port.flushInput()
00239 except:
00240 pass
00241
00242 ntries = 1
00243 attempts = 0
00244
00245 try:
00246 self.port.write(cmd + '\r')
00247 value = self.recv(self.timeout)
00248 while attempts < ntries and (value == '' or value == 'NACK' or value == None):
00249 try:
00250 self.port.flushInput()
00251 self.port.write(cmd + '\r')
00252 value = self.recv(self.timeout)
00253 except:
00254 print "Exception executing command: " + cmd
00255 attempts += 1
00256 except:
00257 self.mutex.release()
00258 if not self.shutdown:
00259 print "Exception executing command: " + cmd
00260 value = None
00261
00262 self.mutex.release()
00263 return value
00264
00265 def execute_array(self, cmd):
00266 ''' Thread safe execution of "cmd" on the Element returning an array.
00267 '''
00268 self.mutex.acquire()
00269
00270 try:
00271 self.port.flushInput()
00272 except:
00273 pass
00274
00275 ntries = 1
00276 attempts = 0
00277
00278 try:
00279 self.port.write(cmd + '\r')
00280 values = self.recv_array()
00281 while attempts < ntries and (values == '' or values == ['NACK'] or values == [] or values == None):
00282 try:
00283 self.port.flushInput()
00284 self.port.write(cmd + '\r')
00285 values = self.recv_array()
00286 except:
00287 print("Exception executing command: " + cmd)
00288 attempts += 1
00289 except:
00290 self.mutex.release()
00291 print "Exception executing command: " + cmd
00292 if not self.shutdown:
00293 raise SerialException
00294 return []
00295
00296 try:
00297 values = map(int, values)
00298 except:
00299 values = []
00300
00301 self.mutex.release()
00302 return values
00303
00304 def execute_ack(self, cmd):
00305 ''' Thread safe execution of "cmd" on the Element returning True if response is ACK.
00306 '''
00307 self.mutex.acquire()
00308
00309 try:
00310 self.port.flushInput()
00311 except:
00312 pass
00313
00314 ntries = 1
00315 attempts = 0
00316
00317 try:
00318 self.port.write(cmd + '\r')
00319 ack = self.recv(self.timeout)
00320 while attempts < ntries and (ack == '' or ack == 'NACK' or ack == None):
00321 try:
00322 self.port.flushInput()
00323 self.port.write(cmd + '\r')
00324 ack = self.recv(self.timeout)
00325 except:
00326 print "Exception executing command: " + cmd
00327 attempts += 1
00328 except:
00329 self.mutex.release()
00330 if not self.shutdown:
00331 print "execute_ack exception when executing", cmd
00332 print sys.exc_info()
00333 return 0
00334
00335 self.mutex.release()
00336 return ack == 'ACK'
00337
00338 def execute_int(self, cmd):
00339 ''' Thread safe execution of "cmd" on the Element returning an int.
00340 '''
00341 self.mutex.acquire()
00342
00343 try:
00344 self.port.flushInput()
00345 except:
00346 print "Flush exception!"
00347
00348 ntries = 1
00349 attempts = 0
00350
00351 try:
00352 self.port.write(cmd + '\r')
00353 value = self.recv(self.timeout)
00354 while attempts < ntries and (value == '' or value == 'NACK' or value == None):
00355 try:
00356 self.port.flushInput()
00357 self.port.write(cmd + '\r')
00358 value = self.recv(self.timeout)
00359 except:
00360 print "Write/Flush exception!"
00361 attempts += 1
00362 except:
00363 self.mutex.release()
00364 if not self.shutdown:
00365 print "execute_int exception when executing", cmd
00366 print sys.exc_info()
00367 return None
00368
00369 try:
00370 value = int(value)
00371 except:
00372 value = None
00373
00374 self.mutex.release()
00375 return value
00376
00377 def update_digital_cache(self, id, value):
00378 if value != "NACK":
00379 self.digital_sensor_cache[id] = value
00380
00381 def update_analog_cache(self, id, value):
00382 if value != "NACK":
00383 self.analog_sensor_cache[id] = value
00384
00385 def get_analog_cache(self, ids):
00386 values = list()
00387 for id in ids:
00388 values.append(self.analog_sensor_cache[id])
00389 return values
00390
00391 def fw(self):
00392 ''' The fw command returns the current firmware version.
00393 '''
00394 return self.execute('fw')
00395
00396 def reset(self):
00397 ''' The reset command resets the Element board and reboots it. You
00398 will see the Element welcome screen appear after a short delay.
00399 Once the welcome string appears, the Element is ready to accept
00400 commands.
00401 '''
00402 return self.execute('reset')
00403
00404 def blink_led(self, id, rate):
00405 ''' Usage 1: blink_led(id, rate)
00406 Usage 2: blink_led([id1, id2], [rate1, rate2])
00407 The blink_led command can blink one of the two onboard green LEDs
00408 simultaneously, or individually. Each complex parameter is comprised
00409 of an <ledId:blinkRate> pair. The ledId specifies which of the two
00410 green LEDs to blink, and blinkRate specifies the delay between blinks.
00411 The minimum blink rate is 1, and the largest is 127. A value of 0 turns
00412 the led off.
00413 '''
00414 if type(id) == int: id = [id]
00415 if type(rate) == int: rate = [rate]
00416 return self.execute_ack('blink %s' %' '.join(map(lambda x: '%d:%d' %x, zip(id,rate))))
00417
00418 def get_compass(self, i2c_addr=None):
00419 ''' Usage 1: heading = get_compass()
00420 Usage 2: heading = get_compass(i2c_addr)
00421 The get_compass command queries a Devantech CMPS03 Electronic
00422 compass module attached to the Elements I2C port.
00423 The current heading is returned in Binary Radians, or BRADS.To
00424 convert BRADS to DEGREES, multiply BRADS by 360/255 (~1.41).
00425 The default I2C address is 0xC0, however another I2C address can be
00426 supplied as an optional parameter.
00427 '''
00428 return 360. / 255. * self.execute_int('cmps03 %X' %i2c_addr if i2c_addr else 'cmps03' )
00429
00430 def get_encoder(self):
00431 ''' The get_encoder command returns the encoder type for the Element,
00432 single (0) or quadrature (1).
00433 '''
00434 return self.execute_int('cfg enc');
00435
00436 def set_encoder(self, encoder_type):
00437 ''' The set_encoder command configures the internal encoder type to be either
00438 single (0) or quadrature (1) type. This information is saved in the
00439 EEPROM, so that the configuration will be retained after a reboot. If
00440 you are using a quadrature encoder (dual channels), and the Element
00441 is configured for single encoder operation, then the second quadrature
00442 channel will be ignored. Thus make sure the correct encoder type is
00443 configured according to your setup. The cfg enc command without a
00444 parameter returns the value currently stored in EEPROM.
00445 '''
00446 return self.execute_ack('cfg enc %d' %encoder_type);
00447
00448 def get_baud(self):
00449 ''' Get the current baud rate on the serial port.
00450 '''
00451 return self.execute_int('cfg baud');
00452
00453 def set_baud(self, baudrate):
00454 ''' The set_baud command configures the serial baud rate on the
00455 Element. Values can be 0=2400, 1=4800, 2=9600, 3=19200,
00456 4=57600, or 5=115200. You can also type in the actual baud rate
00457 string as well (e.g. 19200). The default baud rate used to
00458 communicate with the Element is 19200. The cfg baud command
00459 without a parameter returns the value currently stored in EEPROM.
00460 '''
00461 return self.execute_ack('cfg baud %d' %baudrate);
00462
00463 def get_units(self):
00464 ''' The get_units returns the current units used for sensor
00465 readings. Values are 0 for metric mode, 1 for English mode, and 2 for
00466 raw mode. In raw mode, srf04, srf05, pping, and maxez1 return
00467 reading in units of 0.4us. srf08 and srf10 return readings of 1us..
00468 '''
00469 return self.execute_int('cfg units')
00470
00471 def set_units(self, units):
00472 ''' The set_units command sets the internal units used for sensor
00473 readings. Values are 0 for metric mode, 1 for English mode, and 2 for
00474 raw mode. In raw mode, srf04, srf05, pping, and maxez1 return
00475 reading in units of 0.4us. srf08 and srf10 return readings of 1us.
00476 The cfg units command without a parameter returns the value
00477 '''
00478 self.units = units
00479 return self.execute_ack('cfg units %d' %units);
00480
00481 def get_encoder_count(self, id):
00482 ''' Usage 1: get_encoder_count(id)
00483 Usage 2: get_encoder_count([id1, id2])
00484 The get_encoder_count command returns the values of the encoder
00485 count (channel B) for the specified encoder Id(s). NOTE: The encoder
00486 counts for channel A are used for internal VPID and DPID algorithms.
00487 '''
00488 if type(id) == int: id=[id]
00489 values = self.execute_array('getenc %s' %' '.join(map(str, id)))
00490 if len(values) != len(id):
00491 print "Encoder count did not match ID count for ids", id
00492 raise SerialException
00493 else:
00494 if self.motors_reversed:
00495 for i in range(len(id)):
00496 values[i] = -1 * values[i]
00497 return values
00498
00499 def clear_encoder(self, id):
00500 ''' Usage 1: clear_encoder(id)
00501 Usage 2: clear_encoder([id1, id2])
00502 The clear_encoder command clears the values of the encoder
00503 count (channel B) for the specified encoder Id.
00504 '''
00505 if type(id) == int: id=[id]
00506 return self.execute_ack('clrenc %s' %' '.join(map(str, id)))
00507
00508 def get_io(self, id):
00509 ''' Usage 1: get_io(id)
00510 Usage 2: get_io([id1, id2, id3, ... , idN])
00511 The get_io command changes the pin, pinId (range 0-12), to an input
00512 (if it was an output), and gets the value of the specified General
00513 Purpose I/O lines on the Element. The valid range of I/O pin Ids is
00514 0 thru 12. More than one pid can be specified by enter a list as argument.
00515 '''
00516 if type(id) == int: id=[id]
00517 values = self.execute_array('getio %s' %' '.join(map(str, id)))
00518 n = len(id)
00519 for i in range(n):
00520 self.update_digital_cache(id[i], values[i])
00521 if n == 1:
00522 return values[0]
00523 else:
00524 return values
00525
00526 def set_io(self, id, val):
00527 ''' Usage 1: set_io(id, val)
00528 Usage 2: set_io([id1, id2, ... , idN], [val1, val2, ..., valN)
00529 The set_io command sets the specified General Purpose I/O line pinId
00530 (range 0-12) on the Element to the specified value. Each complex
00531 parameter is a <pinId:value> pair, where the valid range of pinId is 0
00532 thru 12, and value can be 0 or 1 which corresponds to 0v or +5V
00533 respectively. Also I/O lines 4,5,6,7, 8 and 9 cannot be used if you
00534 have servos connected to them. Pin 10, 11, and 12 correspond to the
00535 internal h-bridge enable, SCL, and SDA respectively.
00536 '''
00537 if type(id) == int: id = [id]
00538 if type(val) == int: val = [val]
00539 return self.execute_ack('setio %s' %' '.join(map(lambda x: '%d:%d' %x, zip(id, val))))
00540
00541 def get_maxez1(self, triggerPin, outputPin):
00542 ''' The maxez1 command queries a Maxbotix MaxSonar-EZ1 sonar
00543 sensor connected to the General Purpose I/O lines, triggerPin, and
00544 outputPin, for a distance, and returns it in Centimeters. NOTE: MAKE
00545 SURE there's nothing directly in front of the MaxSonar-EZ1 upon
00546 power up, otherwise it wont range correctly for object less than 6
00547 inches away! The sensor reading defaults to use English units
00548 (inches). The sensor distance resolution is integer based. Also, the
00549 maxsonar trigger pin is RX, and the echo pin is PW.
00550 '''
00551 return self.execute_int('maxez1 %d %d' %(triggerPin, outputPin))
00552
00553 def stop(self):
00554 ''' Stop both motors.
00555 '''
00556 return self.execute_ack('stop')
00557
00558 def get_vpid(self):
00559 ''' Get the PIDL parameter values.
00560 '''
00561 pidl = self.execute('vpid')
00562 return map(lambda x: int(x.split(':')[1]), pidl.split())
00563
00564 def set_vpid(self, prop, integ, deriv, loop):
00565 ''' The set_vpid command sets the PIDL (Proportional, Integral,
00566 Derivative, and Loop) parameters for the Velocity PID control on the
00567 Element. The PIDL parameters are parsed, and saved (in
00568 eeprom). For more information on PIDL control, see the PIDL
00569 configuration section below. By default the Element VPID
00570 parameters are configured to work with our Traxster Robot Kit
00571 '''
00572 [self.VPID_P, self.VPID_I, self.VPID_D, self.VPID_L] = [prop, integ, deriv, loop]
00573 return self.execute_ack('vpid %d:%d:%d:%d' %(prop, integ, deriv, loop))
00574
00575 def digo(self, id, dist, vel):
00576 ''' Usage 1: m(id, dist, vel)
00577 Usage 2: digo([id1, id2], [dist1, dist2], [vel1, vel2])
00578 Simply put, the digo command allows you to command your robot to
00579 travel a specified distance, at a specified speed. This command uses
00580 the internal VPID and DPID algorithms to control velocity and distance.
00581 Therefore, you must have dual motors, and dual wheel encoders
00582 connected to the Element motor ports and encoder inputs.
00583 '''
00584 if type(id) == int: id = [id]
00585 if type(dist) == int: id = [dist]
00586 if type(vel) == int: vel = [vel]
00587
00588 if self.motors_reversed:
00589 for i in range(len(dist)):
00590 dist[i] = -1 * dist[i]
00591
00592 return self.execute('digo %s' %' '.join(map(lambda x: '%d:%d:%d' %x, zip(id, dist, vel))))
00593
00594 def digo_m_per_s(self, id, dist, vel):
00595 ''' Identical to digo but specifies velocities in meters per second.
00596 '''
00597 if type(id) == int: id = [id]
00598 if type(dist) == int: id = [dist]
00599 if type(vel) == int: vel = [vel]
00600
00601 spd = list()
00602 for v in vel:
00603 if self.units == 0:
00604 revs_per_second = float(v) / (self.wheel_diameter * math.pi)
00605 elif self.units == 1:
00606 revs_per_second = float(v) / (self.wheel_diameter * math.pi * 2.54 / 100)
00607 ticks_per_loop = revs_per_second * self.encoder_resolution * self.loop_interval * self.gear_reduction
00608 spd.append(int(ticks_per_loop))
00609
00610 return self.execute('digo %s' %' '.join(map(lambda x: '%d:%d:%d' %x, zip(id, dist, spd))))
00611
00612 def get_dpid(self):
00613 ''' Get the PIDA parameter values.
00614 '''
00615 dpid = self.execute('dpid')
00616 return map(lambda x: int(x.split(':')[1]), dpid.split())
00617
00618 def set_dpid(self, prop, integ, deriv, accel, dead_band):
00619 ''' The set_dpid command gets/sets the PIDA (Proportional, Integral,
00620 Derivative, and Acceleration) parameters for the distance PID control
00621 on the Element. If the PIDA parameters are absent, the PIDA
00622 values are returned. Otherwise the PIDA parameters are parsed, and
00623 saved (in eeprom).
00624 '''
00625 [self.DPID_P, self.DPID_I, self.DPID_D, self.DPID_A, self.DPID_B] = [prop, integ, deriv, accel, dead_band]
00626 return self.execute_ack('dpid %d:%d:%d:%d:%d' %(prop, integ, deriv, accel, dead_band))
00627
00628 def set_rpid(self, r):
00629 ''' The rpid command sets the default PID params known to work with
00630 either the Stinger or Traxster Robotic Kits in the firmware. This makes
00631 it quick and easy to set up the PID params for both robots.
00632 '''
00633 return self.execute_ack('rpid %c' %r)
00634
00635 def get_pids(self):
00636 ''' Once a digo command is issued, an internal state variable within the
00637 firmware is set to 1, and it stays in that state until the algorithm has
00638 completed. Upon completion, the state is set to 0. The pids
00639 command simply returns the value of the internal variable to
00640 determine if the algorithms is currently busy, or if it has finished, thus
00641 allowing subsequent digo commands to be issued w/o clobbering
00642 previous ones.
00643 '''
00644 return self.execute_int('pids');
00645
00646 def pwm(self, id, vel, rate=None):
00647 ''' Usage 1: pwm(id, vel)
00648 Usage 2: pwm(id, vel, rate=r)
00649 Usage 3: pwm([id1, id2], [vel1, vel2])
00650 Usage 4: pwm([id1, id2], [vel1, vel2], rate=r)
00651 The pwm command sets the Pulse Width Modulation value for
00652 Motor 1 & Motor 2. Each complex parameter is a motor
00653 <motorId:pwm value> pair, where the motor id can be 1 or 2, and the
00654 pwm value can be -100 to 100. Each complex parameter pair is
00655 separated by one or more spaces.
00656 The optional rate parameter allows the
00657 motor(s) speed(s)s to be ramped up or down to the specified speed
00658 from the current motor speed.
00659 '''
00660 if type(id) == int: id = [id]
00661 if type(vel) == int: vel = [vel]
00662 if rate != None:
00663 cmd = 'pwm r:%d ' %rate + ' '.join(map(lambda x: '%d:%d' %x, zip(id, vel)))
00664 else:
00665 cmd = 'pwm %s' % ' '.join(map(lambda x: '%d:%d' %x, zip(id, vel)))
00666 return self.execute(cmd)
00667
00668 def step(self, dir, speed ,steps):
00669 ''' The step command is used to step a bipolar stepper motor in direction
00670 dir, at the specified speed, for the specified number of steps.
00671 The dir parameter specifies a CW or CCW rotational direction, and its
00672 value can be either 0 (CCW) or 1(CW). Your specific direction is based
00673 on the way that you have your bipolar motor connected to the
00674 Element. The speed parameter can be a value from 0 to 100.
00675 The steps parameter specifies the maximum number of steps to take.
00676 A value of 0 means step infinitely. Internally, this number is stored in
00677 an unsigned 32 bit variable, so the user can specify a larger number of
00678 steps.
00679 '''
00680 return self.execute_ack('step %d %d %d' %(dir, speed, steps))
00681
00682 def sweep(self, id, speed, steps):
00683 ''' The sweep command is used to sweep a bipolar motor, for step
00684 number of steps, at speed (0-100), thus providing a sweeping motion.
00685 The initial rotational direction of sweep is in the CW direction.
00686 Upon initial receipt of the command, the firmware will sweep the
00687 motor for 1/2 of the number of steps specified, starting in a CW
00688 direction. Once that number of steps has occurred, the sweep
00689 direction will change, and subsequent sweeps will rotate for the full
00690 amount of steps. Thus, the starting point for the motor is in the
00691 middle of each sweep. You may stop the sweep by either issuing a sweep
00692 command w a 0 speed, or simply sending a stop command.
00693 '''
00694 return self.execute_ack('sweep %d %d %d' %(id, speed, steps))
00695
00696 def sensor(self, id):
00697 ''' Usage 1: reading = sensor(id)
00698 Usage 2: readings = sensor([id1, id2, ..., idN])
00699 The sensor command returns the raw A/D (8 bit) reading from the
00700 analog sensor ports 0-5. Multiple values can be read at a time by
00701 specifying multiple pins as a list. Pin 5 is 1/3 of
00702 the voltage of the power supply for the Element. To calculate the
00703 battery voltage, simply multiply the value returned by Sensor 5 by
00704 15/1024.
00705 '''
00706
00707 if type(id) == int: id = [id]
00708 cmd = 'sensor %s' %' '.join(map(str, id))
00709 values = self.execute_array(cmd)
00710 n = len(values)
00711 if n != len(id):
00712 print "Array size incorrect: returning cached values for sensors", id
00713 values = self.get_analog_cache(id)
00714 if len(values) == 1:
00715 return values[0]
00716 else:
00717 return values
00718 try:
00719 for i in range(n):
00720 if values[i] == None:
00721 values[i] = self.BAD_VALUE
00722 for i in range(n):
00723 self.update_analog_cache(id[i], values[i])
00724 if n == 1:
00725 return values[0]
00726 else:
00727 return values
00728 except:
00729 print "Exception reading analog sensors: returning cached values for sensors", id
00730 return self.analog_sensor_cache
00731
00732
00733 def get_analog(self, id):
00734 return self.sensor(id)
00735
00736 def get_all_analog(self):
00737 ''' Return the readings from all analog ports.
00738 '''
00739 values = self.sensor(range(self.N_ANALOG_PORTS))
00740 return values
00741
00742 def servo(self, id, pos):
00743 ''' Usage 1: servo(id, pos)
00744 Usage 2: servo([id1, id2, ... , idN], [pos1, pos2, ..., posN)
00745 The servo command sets a servo connected to General Purpose I/O
00746 port the specified position. The value of the position can range from
00747 -99 to 100, where 0 is the center position. Setting the position to -100
00748 will disable the servo, allowing it to turn freely by hand.
00749 Each parameter is a <servo id:position> pair, where the servo
00750 id can be 1,2,3,4,5, or 6.
00751 '''
00752 if type(id) == int: id = [id]
00753 if type(pos) == int: pos = [pos]
00754 return self.execute_ack('servo %s' %' '.join(map(lambda x: '%d:%d' %x, zip(id, pos))))
00755
00756 def sp03(self, msg, i2c_addr):
00757 ''' Usage1: sp03(msg)
00758 Usage2: sp03(msg, ic2_addr)
00759 The sp03 command instructs a Devantech SP03 Speech Synthesizer to
00760 speak the appropriate phrase. If a character representing a number in
00761 the range of 0 to 30, then the SP03 will speak previously programmed
00762 canned phrases. If a phrase is sent, then it will speak the phrase. An
00763 optional I2C address can also be specified. Otherwise, the default I2C
00764 address of 0xC4.
00765 '''
00766 return not self.execute_ack('sp03 0x%X %s' %(i2c_addr, msg) if i2c_addr else 'sp03 %s' %msg)
00767
00768 def srf04(self, triggerPin, outputPin):
00769 ''' The srf04 command queries an SRF04 sonar sensor
00770 connected to the General Purpose I/O lines triggerPin and outputPin,
00771 for a distance and returns it in the units configured (default is English
00772 inches). If the Element units are configured (using cfg units) for raw mode,
00773 srf04 returns readings in units of 0.4us, and the max distance returned
00774 is 65000 (out of range). When configured for English units, max
00775 distance returned is 100 inches (out of range), and when configured
00776 for Metric units, max distance returned is 255 (out of range). NOTE:
00777 Sonar distance resolution is integer based.
00778 '''
00779 return self.execute_int('srf04 %d %d' %(triggerPin, outputPin))
00780
00781 def srf05(self, pinId):
00782 ''' The srf05/Ping command queries an SRF05/Ping sonar sensor
00783 connected to the General Purpose I/O line pinId for a distance,
00784 and returns it in the units configured (default is English - inches).
00785 If the Element units are configured (using cfg units) for raw mode,
00786 pping and srf05 return readings in units of 0.4us, and the max
00787 distance returned is 65000 (out of range). When configured for
00788 English units, max distance returned is 100 inches (out of range), and
00789 when configured for Metric units, max distance returned is 255 (out of
00790 range). Sonar distance resolution is integer based.
00791 '''
00792 return self.execute_int('srf05 %d' %pinId);
00793
00794 def pping(self, pinId):
00795 ''' The srf05/Ping command queries an SRF05/Ping sonar sensor
00796 connected to the General Purpose I/O line pinId for a distance,
00797 and returns it in the units configured (default is English - inches).
00798 If the Element units are configured (using cfg units) for raw mode,
00799 pping and srf05 return readings in units of 0.4us, and the max
00800 distance returned is 65000 (out of range). When configured for
00801 English units, max distance returned is 100 inches (out of range), and
00802 when configured for Metric units, max distance returned is 255 (out of
00803 range). Sonar distance resolution is integer based.
00804 '''
00805 value = self.execute_int('pping %d' %pinId);
00806 self.update_digital_cache(pinId, value)
00807 return value
00808
00809 def srf08(self, i2c_addr=None):
00810 ''' Usage1: srf08()
00811 Usage2: srf08(ic2_addr)
00812 The srf08/srf10 command queries a Devantech SRF08/SRF10 sonar
00813 sensor at address i2c_addr for a distance reading in the units
00814 configured (default is English - inches). The i2cAddr parameter is
00815 optional, and defaults to 0xE0 for both sensors. The i2c address can
00816 be changed for any i2c module using the i2cp command. Sonar
00817 distance resolution is integer based. If the Element units are
00818 configured (using cfg units) for raw mode, srf08 and srf10 return
00819 readings in units of 1us.
00820 '''
00821 return self.execute_int('srf08 0x%X' %i2c_addr if i2c_addr else 'srf08')
00822
00823 def srf10(self, i2caddr=None):
00824 ''' Usage1: srf10()
00825 Usage2: srf10(ic2_addr)
00826 The srf08/srf10 command queries a Devantech SRF08/SRF10 sonar
00827 sensor at address ic2_addr for a distance reading in the units
00828 configured (default is English - inches). The ic2_addr parameter is
00829 optional, and defaults to 0xE0 for both sensors. The i2c address can
00830 be changed for any i2c module using the i2cp command. Sonar
00831 distance resolution is integer based. If the Element units are
00832 configured (using cfg units) for raw mode, srf08 and srf10 return
00833 readings in units of 1us.
00834 '''
00835 return self.execute_int('srf10 0x%X' %i2caddr if i2caddr else 'srf10')
00836
00837 def tpa81(self, i2caddr=None):
00838 ''' Usage1: tpa81()
00839 Usage2: tpa81(ic2_addr)
00840 The tpa81 command queries a Devantech TPA81 thermopile sensor for
00841 temperature values. It returns 8 temperature values.
00842 '''
00843 return self.execute_array('tpa81 0x%X' %i2caddr if i2caddr else 'tpa81')
00844
00845 def vel(self):
00846 ''' The vel command returns the left and right wheel velocities. The
00847 velocity returned is based on the PIDL parameter configuration. The
00848 units are encoder ticks per PID loop interval.
00849 '''
00850 return self.execute_array('vel')
00851
00852 def vel_m_per_s(self):
00853 ''' Return the left and right wheel velocities in meters per second.
00854 '''
00855 [left_ticks_per_loop, right_ticks_per_loop] = self.vel()
00856 left_revs_per_second = float(left_ticks_per_loop) / self.encoder_resolution / self.loop_interval
00857 right_revs_per_second = float(right_ticks_per_loop) / self.encoder_resolution / self.loop_interval
00858 if self.units == 0:
00859 left_m_s = left_revs_per_second * self.wheel_diameter * math.pi
00860 right_m_s = right_revs_per_second * self.wheel_diameter * math.pi
00861 elif self.units == 1:
00862 left_m_s = left_revs_per_second * self.wheel_diameter * 2.54 * math.pi / 100
00863 right_m_s = right_revs_per_second * self.wheel_diameter * 2.54 * math.pi / 100
00864 return list([left_m_s, right_m_s])
00865
00866 def vel_mph(self):
00867 ''' Return the left and right wheel velocities in miles per hour.
00868 '''
00869 [left_m_s, right_m_s] = self.vel_m_per_s()
00870 m_s_2_mph = 2.25
00871 return list([left_m_s * m_s_2_mph, right_m_s * m_s_2_mph])
00872
00873 def vel_fps(self):
00874 ''' Return the left and right wheel velocities in feet per second.
00875 '''
00876 [left_m_s, right_m_s] = self.vel_m_per_s()
00877 m_s_2_fps = 3.2808
00878 return list([left_m_s * m_s_2_fps, right_m_s * m_s_2_fps])
00879
00880 def restore(self):
00881 ''' Restores the factory default settings, and resets the board. NOTE:
00882 This will erase any configurations you have saved to EEPROM,
00883 including VPID, DPID, and baud rate settings.
00884 '''
00885 print self.execute('restore')
00886
00887 def line(self, addr, newaddr=None, seven=False):
00888 ''' Queries a RoboticsConnection Line Following Sensor at address addr.
00889 If the -a option is specified, then the address of the module will be
00890 changed to the new address associated w the -a switch.
00891 If the optional 7 is appended to the end of the line command, e.g.
00892 line7, then two additional values will be returned from those Line
00893 Following Sensors (manufactured after 11/1/07) which have additional
00894 sensor inputs on the sides of the board. This can be used to read
00895 additional Single Line Following sensors, or read any type of on/off
00896 momentary switch, such those used for bumpers.
00897 '''
00898 cmd = ('line7 %d' if seven else 'line %d') %addr
00899 if newaddr: cmd += ' -a %d' %newaddr
00900 return self.execute_array(cmd)
00901
00902 def i2c(self, op, addr, data=None):
00903 ''' Usage1: i2c(op, addr)
00904 Usage2: i2c(op, addr, data)
00905 The flexible i2c command allows you to execute a generic i2c read, or
00906 write command to the specified device at address addr. Depending on
00907 whether you issue a read or write, additional parameters vary.
00908 '''
00909 cmd = 'i2c %c 0x%X ' %(op, addr)
00910 if data:
00911 cmd += ''.join(data)
00912 if op == 'w':
00913 return self.execute_ack(cmd)
00914 else:
00915 return self.execute_array(cmd)
00916
00917 def voltage(self, cached=False):
00918 if cached:
00919 try:
00920 value = self.analog_sensor_cache[5]
00921 except:
00922 try:
00923 value = self.sensor(5)
00924 self.update_analog_cache(5, value)
00925 except:
00926 return None
00927 else:
00928 try:
00929 value = self.sensor(5)
00930 self.update_analog_cache(5, value)
00931 except:
00932 return None
00933
00934 return value * 15. / 1024.
00935
00936 def set_wheel_diameter(self, diameter):
00937 self.wheel_diameter = diameter
00938
00939 def get_wheel_diameter(self):
00940 return self.wheel_diameter
00941
00942 def set_wheel_track(self, track):
00943 self.wheel_track = track
00944
00945 def get_wheel_track(self):
00946 return self.wheel_track
00947
00948 def set_gear_reduction(self, ratio):
00949 self.gear_reduction = ratio
00950
00951 def get_gear_reduction(self):
00952 return self.gear_reduction
00953
00954 def set_encoder_resolution(self, ticks):
00955 self.encoder_resolution = ticks
00956
00957 def get_encoder_resolution(self):
00958 return self.encoder_resolution
00959
00960 def get_Ping(self, pin, cached=False):
00961 ''' Get the distance reading from a Ping sonarl sensor on the given GPIO pin.
00962 '''
00963 if cached:
00964 value = self.digital_sensor_cache[pin]
00965 else:
00966 value = self.pping(pin)
00967 return value
00968
00969 def get_MaxEZ1(self, trigger_pin, output_pin, cached=False):
00970 ''' Get the distance reading from a MaxEZ1 sonarl sensor on the given trigger and output pins.
00971 '''
00972 if cached:
00973 value = self.digital_sensor_cache[trigger_pin]
00974 else:
00975 value = self.get_maxez1(trigger_pin, output_pin)
00976 return value
00977
00978 def get_GP2D12(self, pin, cached=False):
00979 ''' Get the distance reading from a GP2D12 IR sensor on the given analog pin.
00980 '''
00981 if cached:
00982 value = self.analog_sensor_cache[pin]
00983 else:
00984 value = self.sensor(pin)
00985 try:
00986 distance = (6787 / (value - 3)) - 4
00987 except:
00988 distance = 80
00989 if distance > 80: distance = 80
00990 if distance < 10: distance = 10
00991 if self.units == 0:
00992 return distance
00993 elif self.units == 1:
00994 return distance / 2.54
00995 else:
00996 return value
00997
00998 def get_PhidgetsTemperature(self, pin, cached=False, units="F"):
00999 ''' Get the temperature from a Phidgets Temperature sensor on an analog sensor port and return
01000 the reading in either Farhenheit or Celcius depending on the units argument.
01001 '''
01002 self.temp_units = units
01003 if cached:
01004 value = self.analog_sensor_cache[pin]
01005 else:
01006 value = self.sensor(pin)
01007 try:
01008
01009 tempC = (value / 4.0 * 0.2222) - 61.111
01010
01011 if self.temp_units == "C":
01012 return tempC
01013 else:
01014 return 9. * tempC / 5. + 32.
01015 except:
01016 return self.BAD_VALUE
01017
01018 def get_PhidgetsVoltage(self, pin, cached=False):
01019 ''' Get the voltage from a Phidgets Voltage sensor on an analog sensor port.
01020 '''
01021 if cached:
01022 value = self.analog_sensor_cache[pin]
01023 else:
01024 value = self.sensor(pin)
01025 try:
01026 return 0.06 * (value - 500.)
01027 except:
01028 return self.BAD_VALUE
01029
01030 def get_PhidgetsCurrent(self, pin, cached=False, model=20, ac_dc="dc"):
01031 if cached:
01032 value = self.analog_sensor_cache[pin]
01033 else:
01034 value = self.sensor(pin)
01035 try:
01036 if model == 20:
01037 if ac_dc == "dc":
01038 return 0.05 * (value - 500.)
01039 else:
01040 return 0.025 * value
01041 else:
01042 if ac_dc == "dc":
01043 return 0.125 * (value - 500.)
01044 else:
01045 return 0.625 * value
01046 except:
01047 return self.BAD_VALUE
01048
01049 def travel_distance(self, dist, vel):
01050 ''' Move forward or backward 'dist' (inches or meters depending on units) at speed 'vel'. Use negative distances
01051 to move backward.
01052 '''
01053
01054 revs_per_second = float(vel) / (self.wheel_diameter * math.pi)
01055
01056 ticks_per_loop = revs_per_second * self.encoder_resolution * self.loop_interval * self.gear_reduction
01057 vel = int(ticks_per_loop)
01058
01059 revs = dist / (self.wheel_diameter * math.pi)
01060 ticks = revs * self.encoder_resolution * self.gear_reduction
01061 self.digo([1, 2], [ticks, ticks], [vel, vel])
01062
01063 def mogo(self, id, vel):
01064 ''' Usage 1: mogo(id, vel)
01065 Usage 2: mogo([id1, id2], [vel1, vel2])
01066 The mogo command sets motor speed using one or more complex
01067 parameters containing a <motorId:spd> value pair.
01068 The motorId can be either 1 or 2, which corresponds to the Motor
01069 Terminal port.
01070 The vel value specifies the motor velocity, and it's range depends on
01071 your VPID settings. See the VPID parameters section below to
01072 determine your MAX velocity. A positive value rotates the motors in
01073 one direction, which a negative value rotates the motors in the
01074 opposite direction.
01075 You will have to determine which direction is positive for your motors,
01076 and connect the motors wires to the terminals on the Element board
01077 in the appropriate configuration.
01078 '''
01079 if type(id) == int: id = [id]
01080 if type(vel) == int: vel = [vel]
01081
01082 if self.motors_reversed:
01083 for i in range(len(vel)):
01084 vel[i] = -1 * vel[i]
01085
01086 return self.execute_ack('mogo %s' %' '.join(map(lambda x: '%d:%d' %x, zip(id, vel))))
01087
01088 def mogo_m_per_s(self, id, vel):
01089 ''' Set the motor speeds in meters per second.
01090 '''
01091 if type(id) != list: id = [id]
01092 if type(vel) != list: vel = [vel]
01093 spd = list()
01094 for v in vel:
01095 if self.units == 0:
01096 revs_per_second = float(v) / (self.wheel_diameter * math.pi)
01097 elif self.units == 1:
01098 revs_per_second = float(v) / (self.wheel_diameter * math.pi * 2.54 / 100)
01099 ticks_per_loop = revs_per_second * self.encoder_resolution * self.loop_interval * self.gear_reduction
01100 spd.append(int(ticks_per_loop))
01101
01102 if self.motors_reversed:
01103 for i in range(len(spd)):
01104 spd[i] = -1 * spd[i]
01105
01106 cmd = 'mogo %s' %' '.join(map(lambda x: '%d:%d' %x, zip(id, spd)))
01107
01108 return self.execute_ack(cmd)
01109
01110 def travel_at_speed(self, id, vel):
01111 ''' Move forward or backward at speed 'vel' in meters per second. Use negative speeds
01112 to move backward. This is just an alias for mogo_m_per_s so see that function for
01113 more details.
01114 '''
01115 self.mogo_m_per_s(id, vel)
01116
01117 def rotate(self, angle, vel):
01118 ''' Rotate the robot through 'angle' degrees or radians at speed 'vel'. Use the ROS convention for right handed
01119 coordinate systems with the z-axis pointing up so that a positive angle is counterclockwise.
01120 Use negative angles to rotate clockwise.
01121 '''
01122 if self.units == 0:
01123 revs_per_second = float(vel) / (2.0 * math.pi)
01124 rotation_fraction = angle / (2.0 * math.pi)
01125 full_rotation_dist = self.wheel_track * math.pi
01126 elif self.units == 1:
01127 revs_per_second = float(vel) / 360.
01128 rotation_fraction = angle / 360.
01129 full_rotation_dist = self.wheel_track * 2.54 / 100 * math.pi
01130
01131 vel = revs_per_second * full_rotation_dist
01132
01133 rotation_dist = rotation_fraction * full_rotation_dist
01134 revs = rotation_dist / (self.wheel_diameter * math.pi)
01135
01136 ticks = revs * self.encoder_resolution * self.gear_reduction
01137 self.digo_m_per_s([1, 2], [ticks, -ticks], [vel, vel])
01138
01139 def rotate_at_speed(self, vel):
01140 ''' Rotate the robot continously at speed 'vel' radians or degrees per second. Use the ROS convention for right handed
01141 coordinate systems with the z-axis pointing up so that a positive speed is counterclockwise.
01142 Use negative speeds to rotate clockwise.
01143 '''
01144
01145 if self.units == 1:
01146 vel = vel / 180. * math.pi
01147
01148
01149 if vel > 5.0:
01150 print "That is a rather high rotation rate. Are you sure you specified rotation velocity in the correct units?"
01151 print "Degrees per second for English units and radians per second for metric."
01152 print "Keep in mind that 1 radian per second is about 60 degrees per second."
01153 os._exit(1)
01154
01155 if self.units == 1:
01156 full_rotation_dist = self.wheel_track * 2.54 / 100 * math.pi
01157 revs_per_second = float(vel) / 360.
01158
01159 else:
01160 full_rotation_dist = self.wheel_track * math.pi
01161 revs_per_second = float(vel) / (2.0 * math.pi)
01162
01163 vel = revs_per_second * full_rotation_dist
01164
01165 self.mogo_m_per_s([1, 2], [-vel, vel])
01166
01167 def twist(self, twist):
01168 angle = math.atan2(twist.linear.y, twist.linear.x)
01169 self.rotate(angle, twist.angular.z)
01170 while self.get_pids():
01171 time.sleep(0.05)
01172 speed = math.sqrt(twist.linear.x * twist.linear.x + twist.linear.y * twist.linear.y)
01173 self.mogo_m_per_s([1, 2], [speed, speed])
01174
01175 class Linear:
01176 def __init__(self):
01177 self.x = 0
01178 self.y = 0
01179 self.z = 0
01180
01181 class Angular:
01182 def __init__(self):
01183 self.x = 0
01184 self.y = 0
01185 self.z = 0
01186
01187 class Twist:
01188 def __init__(self):
01189 self.linear = Linear()
01190 self.angular = Angular()
01191
01192 class PhidgetsTemperature():
01193 def __init__(self, Element, pin, units="F"):
01194 ''' Usage: myTemp = PhidgetsTemperature(Element, pin)
01195 reading = myTemp.value()
01196 reading = myTemp.value(cached=True) # gets value from the cache
01197 The Phidgets Temperature Sensor class wraps an analog sensor port and converts the raw
01198 sensor reading to either Farhenheit or Celcius depending on the units argument.
01199 '''
01200 self.Element = Element
01201 self.pin = pin
01202 self.temp_units = units
01203
01204 def value(self, cached=False, units="F"):
01205 return self.Element.get_PhidgetsTemperature(self.pin, cached, units)
01206
01207
01208 class PhidgetsVoltage():
01209 def __init__(self, Element, pin):
01210 ''' Usage: myVolts = PhidgetsVoltage(Element, pin)
01211 reading = myVolts.value()
01212 reading = myVolts.value(cached=True) # gets value from the cache
01213 The Phidgets Voltage Sensor class wraps an analog sensor port and converts the raw
01214 sensor reading to volts.
01215 '''
01216 self.Element = Element
01217 self.pin = pin
01218
01219 def value(self, cached=False):
01220 return self.Element.get_PhidgetsVoltage(self.pin, cached)
01221
01222
01223 class PhidgetsCurrent():
01224 def __init__(self, Element, pin, model=20, ac_dc="dc"):
01225 ''' Usage: myAmps = PhidgetsCurrent(Element, pin)
01226 reading = myAmps.value()
01227 reading = myAmps.value(cached=True) # gets value from the cache
01228 The Phidgets Current Sensor class wraps an analog sensor port and converts the raw
01229 sensor reading to amps. Note there are two models of the Phidgets current sensor,
01230 one with a 20 amp max and the other with a 50 amp max. Also, either model can
01231 measure either DC or AC current.
01232 '''
01233 self.Element = Element
01234 self.pin = pin
01235 self.model = model
01236 self.ac_dc = ac_dc
01237
01238 def value(self, cached=False):
01239 return self.Element.get_PhidgetsCurrent(self.pin, cached, self.model, self.ac_dc)
01240
01241
01242 class Ping():
01243 def __init__(self, Element, pin):
01244 ''' Usage: myPing = Ping(Element, pin)
01245 reading = myPing.value()
01246 reading = myPing.value(cached=True) # gets value from the cache
01247 The Parallax Ping Sonar Sensor class wraps a digital sensor port returns the value
01248 from the pping() command which in turn is in inches or cm depending on the units settings.
01249 '''
01250 self.Element = Element
01251 self.pin = pin
01252
01253 def value(self, cached=False):
01254 return self.Element.get_Ping(self.pin, cached)
01255
01256 class GP2D12():
01257 def __init__(self, Element, pin):
01258 ''' Usage: myIR = GP2D12(Element, pin)
01259 reading = myIR.value()
01260 reading = myIR.value(cached=True) # gets value from the cache
01261 The Sharp GPD12 IR Sensor class wraps an analog sensor port and converts the raw
01262 sensor reading to either inches or cm depending on the units settings.
01263 '''
01264 self.Element = Element
01265 self.pin = pin
01266
01267 def value(self, cached=False):
01268 return self.Element.get_GP2D12(self.pin, cached)
01269
01270
01271 """ Basic test for connectivity """
01272 if __name__ == "__main__":
01273 if os.name == "posix":
01274 portName = "/dev/ttyUSB0"
01275
01276
01277
01278 else:
01279 portName = "COM43"
01280
01281 baudRate = 57600
01282
01283 myElement = Element(port=portName, baudrate=baudRate, timeout=0.5, units=0)
01284 myElement.connect()
01285
01286 print "Sleeping for 2 seconds..."
01287 time.sleep(2)
01288
01289 print "Firmware Version", myElement.fw()
01290 print "Units", myElement.get_units()
01291 print "Baudrate", myElement.get_baud()
01292 print "VPID", myElement.get_vpid()
01293 print "DPID", myElement.get_dpid()
01294 print "Voltage", myElement.voltage()
01295
01296 print "Connection test successful.",
01297
01298 myElement.stop()
01299 myElement.close()
01300
01301 print "Shutting down Element."
01302