|
| 1 | +# hicompass module - for interfacing with |
| 2 | +# the HiTechnic Lego Mindstorms NXT compass sensor |
| 3 | +# |
| 4 | +# This program is free software: you can redistribute it and/or modify |
| 5 | +# it under the terms of the GNU General Public License as published by |
| 6 | +# the Free Software Foundation, either version 3 of the License, or |
| 7 | +# (at your option) any later version. |
| 8 | +# |
| 9 | +# This program is distributed in the hope that it will be useful, |
| 10 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +# GNU General Public License for more details. |
| 13 | + |
| 14 | +#Get the sensor library independent of whether hicompass is in the nxt directory |
| 15 | +try: |
| 16 | + import sensor |
| 17 | +except: |
| 18 | + import nxt.sensor as sensor |
| 19 | +import sys |
| 20 | +from nxt.error import DirProtError |
| 21 | +from time import sleep |
| 22 | + |
| 23 | +# I2C addresses for a HiTechnic compass sensor |
| 24 | +I2C_ADDRESS_CMPS_NX = { |
| 25 | + 0x00: ('version', 8, True), |
| 26 | + 0x08: ('manufacturer', 8, True), |
| 27 | + 0x10: ('sensor_type', 1, True), |
| 28 | + 0x41: ('mode_control', 1, True), |
| 29 | + 0x42: ('heading_msb', 1, False), |
| 30 | + 0x43: ('heading_lsb', 1, False), |
| 31 | + 0x44: ('heading', 2, True), |
| 32 | +} |
| 33 | + |
| 34 | +class _MetaCMPS_Nx(sensor._Meta): |
| 35 | + 'Metaclass which adds accessor methods for CMPS-Nx I2C addresses' |
| 36 | + |
| 37 | + def __init__(cls, name, bases, dict): |
| 38 | + super(_MetaCMPS_Nx, cls).__init__(name, bases, dict) |
| 39 | + for address in I2C_ADDRESS_CMPS_NX: |
| 40 | + name, n_bytes, set_method = I2C_ADDRESS_CMPS_NX[address] |
| 41 | + q = sensor._make_query(address, n_bytes) |
| 42 | + setattr(cls, 'get_' + name, q) |
| 43 | + if set_method: |
| 44 | + c = sensor._make_command(address) |
| 45 | + setattr(cls, 'set_' + name, c) |
| 46 | + |
| 47 | +class CompassSensor(sensor.DigitalSensor): |
| 48 | + |
| 49 | + __metaclass__ = _MetaCMPS_Nx |
| 50 | + |
| 51 | + def __init__(self, brick, port): |
| 52 | + super(CompassSensor, self).__init__(brick, port) |
| 53 | + self.sensor_type = sensor.Type.LOW_SPEED_9V |
| 54 | + self.mode = sensor.Mode.RAW |
| 55 | + self.set_input_mode() |
| 56 | + sleep(0.1) # Give I2C time to initialize |
| 57 | + |
| 58 | + |
| 59 | + def get_manufacturer(self): |
| 60 | + return string(self.i2c_query(0x00,8)); |
| 61 | + |
| 62 | + def get_sample(self,numtries=5): |
| 63 | + tries = numtries |
| 64 | + heading = 10000 |
| 65 | + while heading > 360 or heading < 0: |
| 66 | + try: |
| 67 | + data = self.i2c_query(0x44,2) |
| 68 | + heading = ord(data[0]) + 255*ord(data[1]) |
| 69 | + except DirProtError: |
| 70 | + heading = 10000 |
| 71 | + tries -= 1 |
| 72 | + if tries < 0: |
| 73 | + errstr = "Error: " + str(numtries) + " consecutive bus failures. \nCheck your compass's connection" |
| 74 | + sys.exit(errstr) |
| 75 | + |
| 76 | + return heading |
| 77 | + |
| 78 | + def get_relative_heading(self,target=0): |
| 79 | + rheading = self.get_sample()-target |
| 80 | + if rheading > 180: |
| 81 | + rheading -= 360 |
| 82 | + elif rheading < -180: |
| 83 | + rheading += 360 |
| 84 | + return rheading |
| 85 | + |
| 86 | + #this deserves a little explanation: |
| 87 | + #if max > min, it's straightforward, but |
| 88 | + #if min < max, it switches the values of max and min |
| 89 | + #and returns true iff heading is NOT between the new max and min |
| 90 | + def is_in_range(self,min,max): |
| 91 | + reversed = False |
| 92 | + if min > max: |
| 93 | + (max,min) = (min,max) |
| 94 | + reversed = True |
| 95 | + heading = self.get_sample() |
| 96 | + in_range = (heading > min) and (heading < max) |
| 97 | + #an xor handles the reversal |
| 98 | + #a faster, more compact way of saying |
| 99 | + #if !reversed return in_range |
| 100 | + #if reversed return !in_range |
| 101 | + return bool(reversed) ^ bool(in_range) |
| 102 | + |
| 103 | + def calibrate_mode(self): |
| 104 | + self.i2c_command(0x41,0x43) |
| 105 | + |
| 106 | + def measure_mode(self): |
| 107 | + self.i2c_command(0x41,0x00) |
| 108 | + |
0 commit comments