├── README ├── mcp23017.py └── mcp23017.wsgi /README: -------------------------------------------------------------------------------- 1 | Python MCP230XX I2C IO Expander Tools 2 | ------------------------------------- 3 | 4 | A couple of basic tools I knocked up while playing with the MCP23017 on my Raspberry Pi. More info on my blog here: http://nathan.chantrell.net 5 | 6 | 7 | mcp23017.py 8 | ----------- 9 | A simple command line tool for setting outputs as high or low. 10 | 11 | Usage: mcp23017.py -b -o -s 12 | 13 | eg. to set GPA1 high: mcp23017.py -b a -o 1 -s high 14 | 15 | Response: Output GPA1 changed to high 16 | 17 | Requires python-smbus 18 | 19 | 20 | mcp23017.wsgi 21 | ------------- 22 | 23 | A web interface using Python and modwsgi. Can be controlled through the built in web form or via GET requests with optional JSON like responses. 24 | 25 | eg. to set output GPA1 high: 26 | http://rpi/mcp23017.wsgi?bank=a&output=1&state=high&mode=json 27 | 28 | Response: {"GPA1":"HIGH"} 29 | 30 | Requires python-smbus and apache with mod-wsgi. Note that you will need to make sure your web server has permissions for the i2c bus, eg. /dev/i2c-0 31 | 32 | To do: Extend to cater for input as well as output and report on the current state of the outputs. 33 | -------------------------------------------------------------------------------- /mcp23017.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | 3 | # A simple Python command line tool to control an MCP23017 I2C IO Expander 4 | # By Nathan Chantrell http://nathan.chantrell.net 5 | # GNU GPL V3 6 | 7 | import smbus 8 | import sys 9 | import getopt 10 | 11 | bus = smbus.SMBus(0) # For revision 1 Raspberry Pi, change to bus = smbus.SMBus(1) for revision 2. 12 | 13 | address = 0x20 # I2C address of MCP23017 14 | bus.write_byte_data(0x20,0x00,0x00) # Set all of bank A to outputs 15 | bus.write_byte_data(0x20,0x01,0x00) # Set all of bank B to outputs 16 | 17 | 18 | # Let them know how it works 19 | def usage(): 20 | print 'Usage: mcp23017.py -b -o -s ' 21 | 22 | # Handle the command line arguments 23 | def main(): 24 | try: 25 | opts, args = getopt.getopt(sys.argv[1:],"hb:o:s:",["bank=","output=","state="]) 26 | 27 | if not opts: 28 | usage() 29 | sys.exit(2) 30 | 31 | except getopt.GetoptError: 32 | usage() 33 | sys.exit(2) 34 | 35 | for opt, arg in opts: 36 | if opt == '-h': 37 | usage() 38 | sys.exit() 39 | elif opt in ("-b", "--bank"): 40 | bank = arg 41 | elif opt in ("-o", "--output"): 42 | output = int(arg) 43 | elif opt in ("-s", "--state"): 44 | state = arg 45 | 46 | # Set the correct register for the banks 47 | if bank == "a" : 48 | register = 0x12 49 | elif bank == "b" : 50 | register = 0x13 51 | else: 52 | print "Error! Bank must be a or b" 53 | sys.exit() 54 | 55 | # Read current values from the IO Expander 56 | value = bus.read_byte_data(address,register) 57 | 58 | # Shift the bits for the register value, checking if they are already set first 59 | if state == "high": 60 | if (value >> output) & 1 : 61 | print "Output GP"+bank.upper()+str(output), "is already high." 62 | sys.exit() 63 | else: 64 | value += (1 << output) 65 | elif state == "low": 66 | if (value >> output) & 1 : 67 | value -= (1 << output) 68 | else: 69 | print "Output GP"+bank.upper()+str(output), "is already low." 70 | sys.exit() 71 | elif state == "read": 72 | if (value >> output) & 1 : 73 | print "high" 74 | else: 75 | print "low" 76 | sys.exit() 77 | else: 78 | print "Error! state must be high or low" 79 | sys.exit() 80 | 81 | # Now write to the IO expander 82 | bus.write_byte_data(address,register,value) 83 | 84 | # Tell them what we did 85 | print "Output GP"+bank.upper()+str(output), "changed to", state 86 | 87 | if __name__ == "__main__": 88 | main() 89 | -------------------------------------------------------------------------------- /mcp23017.wsgi: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # A basic Python and modwsgi interface to the MCP23017 I2C IO Expander 4 | # Built in web form or use GET requests, add &mode=json for JSON like responses 5 | # By Nathan Chantrell http://nathan.chantrell.net 6 | # GNU GPL V3 7 | 8 | from wsgiref.simple_server import make_server 9 | from cgi import parse_qs, escape 10 | import smbus 11 | import sys 12 | import getopt 13 | 14 | bus = smbus.SMBus(0) # For revision 1 Raspberry Pi, change to bus = smbus.SMBus(1) for revision 2. 15 | 16 | address = 0x20 # I2C address of IO Expander 17 | bus.write_byte_data(0x20,0x00,0x00) # Set all of bank A to outputs 18 | bus.write_byte_data(0x20,0x01,0x00) # Set all of bank B to outputs 19 | 20 | # The HTML used to build the full web page option 21 | html = """ 22 | 23 | 24 |
25 |

26 | Bank
27 | A 28 | B
29 |

30 |

31 | Output
32 | 0
33 | 1
34 | 2
35 | 3
36 | 4
37 | 5
38 | 6
39 | 7
40 |

41 |

42 | state: 43 | High 44 | Low 45 |

46 |

47 | 48 |

49 |
50 |

51 | Bank: %s
52 | Output: %s
53 | State: %s
54 | Debug: %s 55 |

56 | 57 | 58 | """ 59 | 60 | # HTML for the minimal JSON response (activated with &mode=json in the query string) 61 | htmlshort = """{"%s%s":"%s"}""" 62 | 63 | def application(environ, start_response): 64 | 65 | # Get the query string variables 66 | d = parse_qs(environ['QUERY_STRING']) 67 | bank = d.get('bank', [''])[0] 68 | output = d.get('output', ['8'])[0] 69 | state = d.get('state', [''])[0] 70 | mode = d.get('mode', [''])[0] 71 | 72 | # Escape user input 73 | bank = escape(bank) 74 | output = int(escape(output)) 75 | state = escape(state) 76 | mode = escape(mode) 77 | 78 | # Set the correct register for the banks 79 | if bank == "a" : 80 | register = 0x12 81 | elif bank == "b" : 82 | register = 0x13 83 | else: 84 | register = 0 85 | 86 | # Read current values from the IO Expander 87 | value = bus.read_byte_data(address,register) 88 | 89 | # Shift the bits for the register value, checking if they are already set first 90 | if state == "high": 91 | if not (value >> output) & 1 : 92 | value += (1 << output) 93 | elif state == "low": 94 | if (value >> output) & 1 : 95 | value -= (1 << output) 96 | else: 97 | state = "none" 98 | 99 | # Quick sanity check 100 | if (0 <= output < 8) and register != 0 and state != "none": 101 | # Looks OK, now write to the IO expander 102 | bus.write_byte_data(address,register,value) 103 | if mode == "json" : # minimal mode 104 | response_body = htmlshort % (bank.upper(), output, state.upper()) 105 | else: # otherwise show full web page 106 | response_body = html % (bank.upper(), output, state.upper(), value) 107 | elif mode == "json" : 108 | response_body = "SYNTAX ERROR" 109 | else: 110 | response_body = html % ("n/a", "n/a", "n/a", value) 111 | 112 | # Show the HTML page 113 | status = '200 OK' 114 | response_headers = [('Content-Type', 'text/html'), 115 | ('Content-Length', str(len(response_body)))] 116 | start_response(status, response_headers) 117 | return [response_body] 118 | 119 | --------------------------------------------------------------------------------