''' PicoBoardReader module for reading values from the PicoBoard. PicoBoard Web site: http://www.picocricket.com/picoboard.html >>> sbr = PicoBoardReader() >>> vals = sbr.getValues() >>> sbr.displayValues(vals, skipChannels=(CH_SOUND,CH_LIGHT)) Button: 1023 Firmware: 4 Resistance-A: 1023 Resistance-B: 1023 Resistance-C: 1023 Resistance-D: 1023 Slider: 1023 >>> sbr.close() ''' __author__ = 'daveb@davebsoft.com (Dave Briccetti)' import time import serial # pySerial: http://pyserial.sourceforge.net MAC_DEVICE = '/dev/cu.usbserial' DATA_RATE = 38400 NUM_CHANNELS = 9 BYTES_PER_CHANNEL = 2 SEND_VALUES_CODE = chr(1) PRESSED = 0 # Value for button pressed CH_RESIST_A, CH_RESIST_B, CH_RESIST_C, CH_RESIST_D = 4, 2, 1, 0 CH_BUTTON = 3 CH_LIGHT = 5 CH_SOUND = 6 CH_SLIDER = 7 CH_FIRMWARE = 15 CH_DESC = ('Resistance-D', 'Resistance-C', 'Resistance-B', 'Button', 'Resistance-A', 'Light', 'Sound', 'Slider', '', '', '', '', '', '', '', 'Firmware') class PicoBoardReader(object): '''Provide access to a PicoBoard.''' def __init__(self, device=MAC_DEVICE): self.ser = serial.Serial(device, DATA_RATE) self.oldVals = {} def close(self): '''Close the serial object.''' self.ser.close() def getValues(self, changesOnly=False): '''Return a dictionary of channel -> data mappings. If changesOnly is True, only include the channels with changes since the last call.''' vals = {} self.ser.write(SEND_VALUES_CODE) # Ask the Pico Board for its values resp = self.ser.read(NUM_CHANNELS * BYTES_PER_CHANNEL) # Read the values for n in range(0, len(resp), BYTES_PER_CHANNEL): # Process data for each channel chan, data = self._extractFields(ord(resp[n]), ord(resp[n+1])) vals[chan] = data result = PicoBoardReader.getChangedValues(self.oldVals, vals) \ if changesOnly else vals self.oldVals = vals return result def _extractFields(self, hi, lo): '''Given the high- and low-order bytes of a channel's data, return the channel and data.''' chan = (hi & 0x78) >> 3 # Bits 6-3 in high-order byte datahigh = hi & 7 # Bits 2-0 in high-order byte datalow = lo & 0x7f # Mask out the high-order bit (even though it's always 0) data = datahigh << 7 | datalow # Join the 3 data bits from the high-order byte with the # 7 bits from the low-order byte return chan, data @staticmethod def displayValues(vals, skipChannels=()): '''Display values for all the channels, sorted by channel description. skipChannels is a tuple of channels IDs to skip.''' for chanDesc, data in sorted([(CH_DESC[chan], data) for chan, data in vals.iteritems() if chan not in skipChannels]): print '%15s: %d' % (chanDesc, data) @staticmethod def getChangedValues(oldVals, newVals): '''Return a dictionary of the values in newVals not in oldVals.''' setOld = set(oldVals.iteritems()) setNew = set(newVals.iteritems()) return dict(setNew.difference(setOld)) def monitor(): '''Poll periodically and show changes.''' sbr = PicoBoardReader() for n in range(10): vals = sbr.getValues(changesOnly=True) sbr.displayValues(vals, skipChannels=(CH_SOUND,CH_LIGHT)) time.sleep(1) sbr.close() if __name__ == '__main__': import doctest doctest.testmod()