Skip to content

Instantly share code, notes, and snippets.

@vampjaz
Last active December 20, 2015 10:49
Show Gist options
  • Save vampjaz/6118462 to your computer and use it in GitHub Desktop.
Save vampjaz/6118462 to your computer and use it in GitHub Desktop.
A simple python script for RASPBERRY PI to display prices and quantities of various SparkFun Products
#!/usr/bin/python
# Python library for Adafruit RGB-backlit LCD plate for Raspberry Pi.
# Written by Adafruit Industries. MIT license.
# This is essentially a complete rewrite, but the calling syntax
# and constants are based on code from lrvick and LiquidCrystal.
# lrvic - https://github.com/lrvick/raspi-hd44780/blob/master/hd44780.py
# LiquidCrystal - https://github.com/arduino/Arduino/blob/master/libraries/LiquidCrystal/LiquidCrystal.cpp
from Adafruit_I2C import Adafruit_I2C
from time import sleep
class Adafruit_CharLCDPlate(Adafruit_I2C):
# ----------------------------------------------------------------------
# Constants
# Port expander registers
MCP23017_IOCON_BANK0 = 0x0A # IOCON when Bank 0 active
MCP23017_IOCON_BANK1 = 0x15 # IOCON when Bank 1 active
# These are register addresses when in Bank 1 only:
MCP23017_GPIOA = 0x09
MCP23017_IODIRB = 0x10
MCP23017_GPIOB = 0x19
# Port expander input pin definitions
SELECT = 0
RIGHT = 1
DOWN = 2
UP = 3
LEFT = 4
# LED colors
OFF = 0x00
RED = 0x01
GREEN = 0x02
BLUE = 0x04
YELLOW = RED + GREEN
TEAL = GREEN + BLUE
VIOLET = RED + BLUE
WHITE = RED + GREEN + BLUE
ON = RED + GREEN + BLUE
# LCD Commands
LCD_CLEARDISPLAY = 0x01
LCD_RETURNHOME = 0x02
LCD_ENTRYMODESET = 0x04
LCD_DISPLAYCONTROL = 0x08
LCD_CURSORSHIFT = 0x10
LCD_FUNCTIONSET = 0x20
LCD_SETCGRAMADDR = 0x40
LCD_SETDDRAMADDR = 0x80
# Flags for display on/off control
LCD_DISPLAYON = 0x04
LCD_DISPLAYOFF = 0x00
LCD_CURSORON = 0x02
LCD_CURSOROFF = 0x00
LCD_BLINKON = 0x01
LCD_BLINKOFF = 0x00
# Flags for display entry mode
LCD_ENTRYRIGHT = 0x00
LCD_ENTRYLEFT = 0x02
LCD_ENTRYSHIFTINCREMENT = 0x01
LCD_ENTRYSHIFTDECREMENT = 0x00
# Flags for display/cursor shift
LCD_DISPLAYMOVE = 0x08
LCD_CURSORMOVE = 0x00
LCD_MOVERIGHT = 0x04
LCD_MOVELEFT = 0x00
# ----------------------------------------------------------------------
# Constructor
def __init__(self, busnum=-1, addr=0x20, debug=False):
self.i2c = Adafruit_I2C(addr, busnum, debug)
# I2C is relatively slow. MCP output port states are cached
# so we don't need to constantly poll-and-change bit states.
self.porta, self.portb, self.ddrb = 0, 0, 0b00010000
# Set MCP23017 IOCON register to Bank 0 with sequential operation.
# If chip is already set for Bank 0, this will just write to OLATB,
# which won't seriously bother anything on the plate right now
# (blue backlight LED will come on, but that's done in the next
# step anyway).
self.i2c.bus.write_byte_data(
self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
# Brute force reload ALL registers to known state. This also
# sets up all the input pins, pull-ups, etc. for the Pi Plate.
self.i2c.bus.write_i2c_block_data(
self.i2c.address, 0,
[ 0b00111111, # IODIRA R+G LEDs=outputs, buttons=inputs
self.ddrb , # IODIRB LCD D7=input, Blue LED=output
0b00111111, # IPOLA Invert polarity on button inputs
0b00000000, # IPOLB
0b00000000, # GPINTENA Disable interrupt-on-change
0b00000000, # GPINTENB
0b00000000, # DEFVALA
0b00000000, # DEFVALB
0b00000000, # INTCONA
0b00000000, # INTCONB
0b00000000, # IOCON
0b00000000, # IOCON
0b00111111, # GPPUA Enable pull-ups on buttons
0b00000000, # GPPUB
0b00000000, # INTFA
0b00000000, # INTFB
0b00000000, # INTCAPA
0b00000000, # INTCAPB
self.porta, # GPIOA
self.portb, # GPIOB
self.porta, # OLATA 0 on all outputs; side effect of
self.portb ]) # OLATB turning on R+G+B backlight LEDs.
# Switch to Bank 1 and disable sequential operation.
# From this point forward, the register addresses do NOT match
# the list immediately above. Instead, use the constants defined
# at the start of the class. Also, the address register will no
# longer increment automatically after this -- multi-byte
# operations must be broken down into single-byte calls.
self.i2c.bus.write_byte_data(
self.i2c.address, self.MCP23017_IOCON_BANK0, 0b10100000)
self.displayshift = (self.LCD_CURSORMOVE |
self.LCD_MOVERIGHT)
self.displaymode = (self.LCD_ENTRYLEFT |
self.LCD_ENTRYSHIFTDECREMENT)
self.displaycontrol = (self.LCD_DISPLAYON |
self.LCD_CURSOROFF |
self.LCD_BLINKOFF)
self.write(0x33) # Init
self.write(0x32) # Init
self.write(0x28) # 2 line 5x8 matrix
self.write(self.LCD_CLEARDISPLAY)
self.write(self.LCD_CURSORSHIFT | self.displayshift)
self.write(self.LCD_ENTRYMODESET | self.displaymode)
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
self.write(self.LCD_RETURNHOME)
# ----------------------------------------------------------------------
# Write operations
# The LCD data pins (D4-D7) connect to MCP pins 12-9 (PORTB4-1), in
# that order. Because this sequence is 'reversed,' a direct shift
# won't work. This table remaps 4-bit data values to MCP PORTB
# outputs, incorporating both the reverse and shift.
flip = ( 0b00000000, 0b00010000, 0b00001000, 0b00011000,
0b00000100, 0b00010100, 0b00001100, 0b00011100,
0b00000010, 0b00010010, 0b00001010, 0b00011010,
0b00000110, 0b00010110, 0b00001110, 0b00011110 )
# Low-level 4-bit interface for LCD output. This doesn't actually
# write data, just returns a byte array of the PORTB state over time.
# Can concatenate the output of multiple calls (up to 8) for more
# efficient batch write.
def out4(self, bitmask, value):
hi = bitmask | self.flip[value >> 4]
lo = bitmask | self.flip[value & 0x0F]
return [hi | 0b00100000, hi, lo | 0b00100000, lo]
# The speed of LCD accesses is inherently limited by I2C through the
# port expander. A 'well behaved program' is expected to poll the
# LCD to know that a prior instruction completed. But the timing of
# most instructions is a known uniform 37 mS. The enable strobe
# can't even be twiddled that fast through I2C, so it's a safe bet
# with these instructions to not waste time polling (which requires
# several I2C transfers for reconfiguring the port direction).
# The D7 pin is set as input when a potentially time-consuming
# instruction has been issued (e.g. screen clear), as well as on
# startup, and polling will then occur before more commands or data
# are issued.
pollables = ( LCD_CLEARDISPLAY, LCD_RETURNHOME )
# Write byte, list or string value to LCD
def write(self, value, char_mode=False):
""" Send command/data to LCD """
# If pin D7 is in input state, poll LCD busy flag until clear.
if self.ddrb & 0b00010000:
lo = (self.portb & 0b00000001) | 0b01000000
hi = lo | 0b00100000 # E=1 (strobe)
self.i2c.bus.write_byte_data(
self.i2c.address, self.MCP23017_GPIOB, lo)
while True:
# Strobe high (enable)
self.i2c.bus.write_byte(self.i2c.address, hi)
# First nybble contains busy state
bits = self.i2c.bus.read_byte(self.i2c.address)
# Strobe low, high, low. Second nybble (A3) is ignored.
self.i2c.bus.write_i2c_block_data(
self.i2c.address, self.MCP23017_GPIOB, [lo, hi, lo])
if (bits & 0b00000010) == 0: break # D7=0, not busy
self.portb = lo
# Polling complete, change D7 pin to output
self.ddrb &= 0b11101111
self.i2c.bus.write_byte_data(self.i2c.address,
self.MCP23017_IODIRB, self.ddrb)
bitmask = self.portb & 0b00000001 # Mask out PORTB LCD control bits
if char_mode: bitmask |= 0b10000000 # Set data bit if not a command
# If string or list, iterate through multiple write ops
if isinstance(value, str):
last = len(value) - 1 # Last character in string
data = [] # Start with blank list
for i, v in enumerate(value): # For each character...
# Append 4 bytes to list representing PORTB over time.
# First the high 4 data bits with strobe (enable) set
# and unset, then same with low 4 data bits (strobe 1/0).
data.extend(self.out4(bitmask, ord(v)))
# I2C block data write is limited to 32 bytes max.
# If limit reached, write data so far and clear.
# Also do this on last byte if not otherwise handled.
if (len(data) >= 32) or (i == last):
self.i2c.bus.write_i2c_block_data(
self.i2c.address, self.MCP23017_GPIOB, data)
self.portb = data[-1] # Save state of last byte out
data = [] # Clear list for next iteration
elif isinstance(value, list):
# Same as above, but for list instead of string
last = len(value) - 1
data = []
for i, v in enumerate(value):
data.extend(self.out4(bitmask, v))
if (len(data) >= 32) or (i == last):
self.i2c.bus.write_i2c_block_data(
self.i2c.address, self.MCP23017_GPIOB, data)
self.portb = data[-1]
data = []
else:
# Single byte
data = self.out4(bitmask, value)
self.i2c.bus.write_i2c_block_data(
self.i2c.address, self.MCP23017_GPIOB, data)
self.portb = data[-1]
# If a poll-worthy instruction was issued, reconfigure D7
# pin as input to indicate need for polling on next call.
if (not char_mode) and (value in self.pollables):
self.ddrb |= 0b00010000
self.i2c.bus.write_byte_data(self.i2c.address,
self.MCP23017_IODIRB, self.ddrb)
# ----------------------------------------------------------------------
# Utility methods
def begin(self, cols, lines):
self.currline = 0
self.numlines = lines
self.clear()
# Puts the MCP23017 back in Bank 0 + sequential write mode so
# that other code using the 'classic' library can still work.
# Any code using this newer version of the library should
# consider adding an atexit() handler that calls this.
def stop(self):
self.porta = 0b11000000 # Turn off LEDs on the way out
self.portb = 0b00000001
sleep(0.0015)
self.i2c.bus.write_byte_data(
self.i2c.address, self.MCP23017_IOCON_BANK1, 0)
self.i2c.bus.write_i2c_block_data(
self.i2c.address, 0,
[ 0b00111111, # IODIRA
self.ddrb , # IODIRB
0b00000000, # IPOLA
0b00000000, # IPOLB
0b00000000, # GPINTENA
0b00000000, # GPINTENB
0b00000000, # DEFVALA
0b00000000, # DEFVALB
0b00000000, # INTCONA
0b00000000, # INTCONB
0b00000000, # IOCON
0b00000000, # IOCON
0b00111111, # GPPUA
0b00000000, # GPPUB
0b00000000, # INTFA
0b00000000, # INTFB
0b00000000, # INTCAPA
0b00000000, # INTCAPB
self.porta, # GPIOA
self.portb, # GPIOB
self.porta, # OLATA
self.portb ]) # OLATB
def clear(self):
self.write(self.LCD_CLEARDISPLAY)
def home(self):
self.write(self.LCD_RETURNHOME)
row_offsets = ( 0x00, 0x40, 0x14, 0x54 )
def setCursor(self, col, row):
if row > self.numlines: row = self.numlines - 1
elif row < 0: row = 0
self.write(self.LCD_SETDDRAMADDR | (col + self.row_offsets[row]))
def display(self):
""" Turn the display on (quickly) """
self.displaycontrol |= self.LCD_DISPLAYON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def noDisplay(self):
""" Turn the display off (quickly) """
self.displaycontrol &= ~self.LCD_DISPLAYON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def cursor(self):
""" Underline cursor on """
self.displaycontrol |= self.LCD_CURSORON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def noCursor(self):
""" Underline cursor off """
self.displaycontrol &= ~self.LCD_CURSORON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def ToggleCursor(self):
""" Toggles the underline cursor On/Off """
self.displaycontrol ^= self.LCD_CURSORON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def blink(self):
""" Turn on the blinking cursor """
self.displaycontrol |= self.LCD_BLINKON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def noBlink(self):
""" Turn off the blinking cursor """
self.displaycontrol &= ~self.LCD_BLINKON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def ToggleBlink(self):
""" Toggles the blinking cursor """
self.displaycontrol ^= self.LCD_BLINKON
self.write(self.LCD_DISPLAYCONTROL | self.displaycontrol)
def scrollDisplayLeft(self):
""" These commands scroll the display without changing the RAM """
self.displayshift = self.LCD_DISPLAYMOVE | self.LCD_MOVELEFT
self.write(self.LCD_CURSORSHIFT | self.displayshift)
def scrollDisplayRight(self):
""" These commands scroll the display without changing the RAM """
self.displayshift = self.LCD_DISPLAYMOVE | self.LCD_MOVERIGHT
self.write(self.LCD_CURSORSHIFT | self.displayshift)
def leftToRight(self):
""" This is for text that flows left to right """
self.displaymode |= self.LCD_ENTRYLEFT
self.write(self.LCD_ENTRYMODESET | self.displaymode)
def rightToLeft(self):
""" This is for text that flows right to left """
self.displaymode &= ~self.LCD_ENTRYLEFT
self.write(self.LCD_ENTRYMODESET | self.displaymode)
def autoscroll(self):
""" This will 'right justify' text from the cursor """
self.displaymode |= self.LCD_ENTRYSHIFTINCREMENT
self.write(self.LCD_ENTRYMODESET | self.displaymode)
def noAutoscroll(self):
""" This will 'left justify' text from the cursor """
self.displaymode &= ~self.LCD_ENTRYSHIFTINCREMENT
self.write(self.LCD_ENTRYMODESET | self.displaymode)
def createChar(self, location, bitmap):
self.write(self.LCD_SETCGRAMADDR | ((location & 7) << 3))
self.write(bitmap, True)
self.write(self.LCD_SETDDRAMADDR)
def message(self, text):
""" Send string to LCD. Newline wraps to second line"""
lines = str(text).split('\n') # Split at newline(s)
for i, line in enumerate(lines): # For each substring...
if i > 0: # If newline(s),
self.write(0xC0) # set DDRAM address to 2nd line
self.write(line, True) # Issue substring
def backlight(self, color):
c = ~color
self.porta = (self.porta & 0b00111111) | ((c & 0b011) << 6)
self.portb = (self.portb & 0b11111110) | ((c & 0b100) >> 2)
# Has to be done as two writes because sequential operation is off.
self.i2c.bus.write_byte_data(
self.i2c.address, self.MCP23017_GPIOA, self.porta)
self.i2c.bus.write_byte_data(
self.i2c.address, self.MCP23017_GPIOB, self.portb)
# Read state of single button
def buttonPressed(self, b):
return (self.i2c.readU8(self.MCP23017_GPIOA) >> b) & 1
# Read and return bitmask of combined button state
def buttons(self):
return self.i2c.readU8(self.MCP23017_GPIOA) & 0b11111
# ----------------------------------------------------------------------
# Test code
if __name__ == '__main__':
lcd = Adafruit_CharLCDPlate()
lcd.begin(16, 2)
lcd.clear()
lcd.message("Adafruit RGB LCD\nPlate w/Keypad!")
sleep(1)
col = (('Red' , lcd.RED) , ('Yellow', lcd.YELLOW), ('Green' , lcd.GREEN),
('Teal', lcd.TEAL), ('Blue' , lcd.BLUE) , ('Violet', lcd.VIOLET),
('Off' , lcd.OFF) , ('On' , lcd.ON))
print "Cycle thru backlight colors"
for c in col:
print c[0]
lcd.clear()
lcd.message(c[0])
lcd.backlight(c[1])
sleep(0.5)
btn = ((lcd.SELECT, 'Select', lcd.ON),
(lcd.LEFT , 'Left' , lcd.RED),
(lcd.UP , 'Up' , lcd.BLUE),
(lcd.DOWN , 'Down' , lcd.GREEN),
(lcd.RIGHT , 'Right' , lcd.VIOLET))
print "Try buttons on plate"
lcd.clear()
lcd.message("Try buttons")
prev = -1
while True:
for b in btn:
if lcd.buttonPressed(b[0]):
if b is not prev:
print b[1]
lcd.clear()
lcd.message(b[1])
lcd.backlight(b[2])
prev = b
break
#!/usr/bin/python
import smbus
# ===========================================================================
# Adafruit_I2C Class
# ===========================================================================
class Adafruit_I2C :
@staticmethod
def getPiRevision():
"Gets the version number of the Raspberry Pi board"
# Courtesy quick2wire-python-api
# https://github.com/quick2wire/quick2wire-python-api
try:
with open('/proc/cpuinfo','r') as f:
for line in f:
if line.startswith('Revision'):
return 1 if line.rstrip()[-1] in ['1','2'] else 2
except:
return 0
@staticmethod
def getPiI2CBusNumber():
# Gets the I2C bus number /dev/i2c#
return 1 if Adafruit_I2C.getPiRevision() > 1 else 0
def __init__(self, address, busnum=-1, debug=False):
self.address = address
# By default, the correct I2C bus is auto-detected using /proc/cpuinfo
# Alternatively, you can hard-code the bus version below:
# self.bus = smbus.SMBus(0); # Force I2C0 (early 256MB Pi's)
# self.bus = smbus.SMBus(1); # Force I2C1 (512MB Pi's)
self.bus = smbus.SMBus(
busnum if busnum >= 0 else Adafruit_I2C.getPiI2CBusNumber())
self.debug = debug
def reverseByteOrder(self, data):
"Reverses the byte order of an int (16-bit) or long (32-bit) value"
# Courtesy Vishal Sapre
byteCount = len(hex(data)[2:].replace('L','')[::2])
val = 0
for i in range(byteCount):
val = (val << 8) | (data & 0xff)
data >>= 8
return val
def errMsg(self):
print "Error accessing 0x%02X: Check your I2C address" % self.address
return -1
def write8(self, reg, value):
"Writes an 8-bit value to the specified register/address"
try:
self.bus.write_byte_data(self.address, reg, value)
if self.debug:
print "I2C: Wrote 0x%02X to register 0x%02X" % (value, reg)
except IOError, err:
return self.errMsg()
def write16(self, reg, value):
"Writes a 16-bit value to the specified register/address pair"
try:
self.bus.write_word_data(self.address, reg, value)
if self.debug:
print ("I2C: Wrote 0x%02X to register pair 0x%02X,0x%02X" %
(value, reg, reg+1))
except IOError, err:
return self.errMsg()
def writeList(self, reg, list):
"Writes an array of bytes using I2C format"
try:
if self.debug:
print "I2C: Writing list to register 0x%02X:" % reg
print list
self.bus.write_i2c_block_data(self.address, reg, list)
except IOError, err:
return self.errMsg()
def readList(self, reg, length):
"Read a list of bytes from the I2C device"
try:
results = self.bus.read_i2c_block_data(self.address, reg, length)
if self.debug:
print ("I2C: Device 0x%02X returned the following from reg 0x%02X" %
(self.address, reg))
print results
return results
except IOError, err:
return self.errMsg()
def readU8(self, reg):
"Read an unsigned byte from the I2C device"
try:
result = self.bus.read_byte_data(self.address, reg)
if self.debug:
print ("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" %
(self.address, result & 0xFF, reg))
return result
except IOError, err:
return self.errMsg()
def readS8(self, reg):
"Reads a signed byte from the I2C device"
try:
result = self.bus.read_byte_data(self.address, reg)
if result > 127: result -= 256
if self.debug:
print ("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" %
(self.address, result & 0xFF, reg))
return result
except IOError, err:
return self.errMsg()
def readU16(self, reg):
"Reads an unsigned 16-bit value from the I2C device"
try:
hibyte = self.readU8(reg)
lobyte = self.readU8(reg+1)
result = (hibyte << 8) + lobyte
if (self.debug):
print "I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)
return result
except IOError, err:
return self.errMsg()
def readS16(self, reg):
"Reads a signed 16-bit value from the I2C device"
try:
hibyte = self.readS8(reg)
lobyte = self.readU8(reg+1)
result = (hibyte << 8) + lobyte
if (self.debug):
print "I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)
return result
except IOError, err:
return self.errMsg()
def readU16Rev(self, reg):
"Reads an unsigned 16-bit value from the I2C device with rev byte order"
try:
lobyte = self.readU8(reg)
hibyte = self.readU8(reg+1)
result = (hibyte << 8) + lobyte
if (self.debug):
print "I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)
return result
except IOError, err:
return self.errMsg()
def readS16Rev(self, reg):
"Reads a signed 16-bit value from the I2C device with rev byte order"
try:
lobyte = self.readS8(reg)
hibyte = self.readU8(reg+1)
result = (hibyte << 8) + lobyte
if (self.debug):
print "I2C: Device 0x%02X returned 0x%04X from reg 0x%02X" % (self.address, result & 0xFFFF, reg)
return result
except IOError, err:
return self.errMsg()
if __name__ == '__main__':
try:
bus = Adafruit_I2C(address=0)
print "Default I2C bus is accessible"
except:
print "Error accessing default I2C bus"
#!/usr/bin/python
# Copyright 2012 Daniel Berlin (with some changes by Limor Fried,
# Adafruit Industries)
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
from Adafruit_I2C import Adafruit_I2C
class Adafruit_MCP230XX(Adafruit_I2C):
INPUT = True
OUTPUT = False
MCP23017_IODIRA = 0x00
MCP23017_IODIRB = 0x01
MCP23017_GPPUA = 0x0C
MCP23017_GPPUB = 0x0D
MCP23017_GPIOA = 0x12
MCP23017_GPIOB = 0x13
MCP23017_OLATA = 0x14
MCP23017_OLATB = 0x15
MCP23008_IODIR = 0x00
MCP23008_GPIO = 0x09
MCP23008_GPPU = 0x06
MCP23008_OLAT = 0x0A
def __init__(self, address, num_gpios=8, busnum=-1, debug=False):
assert 0 < num_gpios < 17, "Number of GPIOs must be between 1 and 16"
self.i2c = Adafruit_I2C(address, busnum, debug)
self.num_gpios = num_gpios
self.pullups = 0
# Set default pin values -- all inputs with pull-ups disabled.
# Current OLAT (output) value is polled, not set.
if num_gpios <= 8:
self.direction = 0xFF
self.i2c.write8(self.MCP23008_IODIR, self.direction)
self.i2c.write8(self.MCP23008_GPPU , self.pullups)
self.outputvalue = self.i2c.readU8(self.MCP23008_OLAT)
else:
self.direction = 0xFFFF
self.i2c.write16(self.MCP23017_IODIRA, self.direction)
self.i2c.write16(self.MCP23017_GPPUA , self.pullups)
self.outputvalue = self.i2c.readU16(self.MCP23017_OLATA)
# Set single pin to either INPUT or OUTPUT mode
def config(self, pin, mode):
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
if mode is self.INPUT: self.direction |= (1 << pin)
else: self.direction &= ~(1 << pin)
if self.num_gpios <= 8:
self.i2c.write8(self.MCP23008_IODIR, self.direction)
elif pin < 8:
# Replace low bits (IODIRA)
self.i2c.write8(self.MCP23017_IODIRA, self.direction & 0xFF)
else:
# Replace high bits (IODIRB)
self.i2c.write8(self.MCP23017_IODIRB, self.direction >> 8)
return self.direction
# Enable pull-up resistor on single input pin
def pullup(self, pin, enable, check=False):
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
if check:
assert (self.direction & (1 << pin)) != 0, "Pin %s not set to input" % pin
if enable: self.pullups |= (1 << pin)
else: self.pullups &= ~(1 << pin)
if self.num_gpios <= 8:
self.i2c.write8(self.MCP23008_GPPU, self.pullups)
elif pin < 8:
# Replace low bits (GPPUA)
self.i2c.write8(self.MCP23017_GPPUA, self.pullups & 0xFF)
else:
# Replace high bits (GPPUB)
self.i2c.write8(self.MCP23017_GPPUB, self.pullups >> 8)
return self.pullups
# Read value from single input pin
def input(self, pin, check=True):
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
if check:
assert (self.direction & (1 << pin)) != 0, "Pin %s not set to input" % pin
if self.num_gpios <= 8:
value = self.i2c.readU8(self.MCP23008_GPIO)
return (value >> pin) & 1
elif pin < 8:
# Read from low bits (GPIOA)
value = self.i2c.readU8(self.MCP23017_GPIOA)
return (value >> pin) & 1
else:
# Read from high bits (GPIOB)
value = self.i2c.readU8(self.MCP23017_GPIOB)
return (value >> (pin - 8)) & 1
# Write value to single output pin
def output(self, pin, value):
assert 0 <= pin < self.num_gpios, "Pin number %s is invalid, must be between 0 and %s" % (pin, self.num_gpios-1)
# assert self.direction & (1 << pin) == 0, "Pin %s not set to output" % pin
if value: new = self.outputvalue | (1 << pin)
else: new = self.outputvalue & ~(1 << pin)
# Only write if pin value has changed:
if new is not self.outputvalue:
self.outputvalue = new
if self.num_gpios <= 8:
self.i2c.write8(self.MCP23008_OLAT, new)
elif pin < 8:
# Write to low bits (OLATA)
self.i2c.write8(self.MCP23017_OLATA, new & 0xFF)
else:
# Write to high bits (OLATB)
self.i2c.write8(self.MCP23017_OLATB, new >> 8)
return new
# The following two methods (inputAll and outputAll) neither assert
# inputs nor invoke the base class methods that handle I/O exceptions.
# The underlying smbus calls are invoked directly for expediency, the
# expectation being that any I2C access or address type errors have
# already been identified during initialization.
# Read contiguous value from all input pins
def inputAll(self):
if self.num_gpios <= 8:
return self.i2c.bus.read_byte_data(self.i2c.address,
self.MCP23008_GPIO)
else:
return self.i2c.bus.read_word_data(self.i2c.address,
self.MCP23017_GPIOA)
# Write contiguous value to all output pins
def outputAll(self, value):
self.outputvalue = value
if self.num_gpios <= 8:
self.i2c.bus.write_byte_data(self.i2c.address,
self.MCP23008_OLAT, value)
else:
self.i2c.bus.write_word_data(self.i2c.address,
self.MCP23017_OLATA, value)
# RPi.GPIO compatible interface for MCP23017 and MCP23008
class MCP230XX_GPIO(object):
OUT = 0
IN = 1
BCM = 0
BOARD = 0
def __init__(self, busnum, address, num_gpios):
self.chip = Adafruit_MCP230XX(busnum, address, num_gpios)
def setmode(self, mode):
pass # do nothing
def setup(self, pin, mode):
self.chip.config(pin, mode)
def input(self, pin):
return self.chip.input(pin)
def output(self, pin, value):
self.chip.output(pin, value)
def pullup(self, pin, value):
self.chip.pullup(pin, value)
if __name__ == '__main__':
# ****************************************************
# Set num_gpios to 8 for MCP23008 or 16 for MCP23017!
# If you have a new Pi you may also need to add: bus=1
# ****************************************************
mcp = Adafruit_MCP230XX(address=0x20, num_gpios=16)
# Set pins 0, 1, 2 as outputs
mcp.config(0, mcp.OUTPUT)
mcp.config(1, mcp.OUTPUT)
mcp.config(2, mcp.OUTPUT)
# Set pin 3 to input with the pullup resistor enabled
mcp.pullup(3, True)
# Read pin 3 and display the results
print "%d: %x" % (3, mcp.input(3))
# Python speed test on output 0 toggling at max speed
while True:
mcp.output(0, 1) # Pin 0 High
mcp.output(0, 0) # Pin 0 Low
#!/usr/bin/python
import time
from Adafruit_CharLCDPlate import Adafruit_CharLCDPlate
import urllib
import csv
# Initialize the LCD plate. Should auto-detect correct I2C bus. If not,
# pass '0' for early 256 MB Model B boards or '1' for all later versions
lcd = Adafruit_CharLCDPlate(1)
# Clear display
lcd.clear()
#sparkfun product IDs
productid = [11546,11868,11589,11021,11286,9716,11215,11712,8938,709,569,8653]
val = 1
while val == 1:
lcd.clear()
lcd.message('SparkFun @\nwww.sparkfun.com')
time.sleep(2)
for i in productid:
sdata = urllib.urlopen('https://204.144.132.37/products/'+str(i)+'.csv')
csvdata = [row for row in csv.reader(sdata)]
name = csvdata[1][2]
count = csvdata[1][7]
price = csvdata[1][5]
stock = csvdata[1][6]
lcd.clear()
if stock == '1':
lcd.message(name+'\n'+count+' $'+price)
else:
lcd.message(name+'\nout $'+price)
time.sleep(3)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment