Motoron Motor Controller library for Raspberry Pi
Loading...
Searching...
No Matches
i2c_set_addresses_example.py
1#!/usr/bin/env python3
2
3# This program is an interactive utility you can use to set the I2C
4# addresses for a single Motoron controller or multiple controllers.
5#
6# When you run this program in an terminal, it prompts you to enter a
7# command. Type the command you want to run and then press Enter.
8
9help_message = """
10To assign an I2C address to a Motoron, type "a" followed by the address
11(in decimal) while the JMP1 pin of the Motoron you wish to change is
12shorted to GND. For example, "a7" sets the address of the Motoron to
13decimal 17.
14
15Alternatively, you can type "a" by itself in order to automatically
16to have this program automatically pick an address for you.
17
18To make the Motoron start using its new address, you can reset it by
19power cycling it, driving its RST line low, or by typing "r".
20
21The "s" command does a simple scan of the bus to see which addresses
22have devices responding. This can help you make sure you have set up
23your I2C bus correctly.
24
25The "i" command goes further, sending extra commands to identify the
26Motoron devices on the bus and print information about them.
27
28Warning: The "i" command sends Motoron commands to every I2C device on
29the bus. If you have devices on your bus that are not Motorons, this
30could cause undesired behavior.
31
32Warning: The "a" and "r" commands use the I2C general call address (0), so
33they might cause undesired behavior on other devices that use that address.
34Also, they will not work if you disabled the general call address on a
35Motoron.
36"""
37
38start_message = """Motoron Set I2C Addresses Utility
39
40Type "h" for help, "a" followed by a number to assign an address, "r" to reset
41Motorons, "s" to scan, "i" to identify devices, or Ctrl+C to quit.
42
43Warning: If you have devices that are not Motorons, these commands could cause
44undesired behavior. Type "h" for more info.
45"""
46
47import sys
48import time
49import motoron
50
51# By default, we use the SMBus library to access the I2C bus at '/dev/i2c-1'.
52# That only works on Linux.
53from smbus2 import SMBus
54i2c = SMBus(1)
55
56# Alternatively, you can comment out the code above and then uncomment the
57# code below to use a Pololu USB-to-I2C Adapter. If needed, change
58# '/dev/ttyACM0' to the serial port of the adapter (e.g. 'COM11').
59#import pololu_usb_i2c_adapter
60#i2c = pololu_usb_i2c_adapter.Adapter('/dev/ttyACM0')
61
62# Make a MotoronI2C object configured to use the general call address (0).
63mc = motoron.MotoronI2C(bus=i2c, address=0)
64
65# The next address this program will try to use when you send an "a" command
66# to automatically assign an address.
67next_address = 17
68
69# This function defines which I2C addresses this program is
70# allowed to communicate with.
71def allow_address_communication(address):
72 # Addresses cannot be larger than 127.
73 if address >= 128: return False
74
75 # If you have devices on your bus and you want to prevent this
76 # code from talking to them, potentially causing unwanted
77 # operations, add their 7-bit addresses here with a line like this:
78 # if address == 0x6B: return False
79
80 return True
81
82# This function defines which I2C addresses this program is allowed
83# to communicate with.
84def allow_address_assignment(address):
85 return allow_address_communication(address) and address != 0
86
87type_is_smbus = None
88
89# Checks to see if a device is responding at the specified address.
90def scan_address(address):
91 if not allow_address_communication(address): return False
92 global type_is_smbus
93 bus = mc.bus
94 if type_is_smbus is None:
95 type_is_smbus = (getattr(bus, 'i2c_rdwr', None) != None)
96 if type_is_smbus:
97 try:
98 bus.i2c_rdwr(mc._msg.write(address, []))
99 except OSError as e:
100 if e.args[0] == 6 or e.args[0] == 121: return False
101 raise
102 else:
103 try:
104 bus.writeto(address, b'')
105 return True
106 except RuntimeError as e:
107 if e.__class__.__name__ == 'AdapterError' and e.error_code == 8:
108 return False
109 else:
110 raise
111
112def assign_address(line):
113 global next_address
114 desired_address_specified = False
115 desired_address = next_address
116 if len(line) > 1:
117 try:
118 desired_address = int(line[1:]) & 127
119 desired_address_specified = True
120 except ValueError:
121 pass
122
123 while True:
124 if allow_address_assignment(desired_address):
125 # Unless the address was explicitly specified by the user,
126 # make sure it is not already used by some other device.
127 if desired_address_specified or not scan_address(desired_address):
128 break
129 print("Found a device at address %d." % desired_address)
130
131 elif desired_address_specified:
132 print("Assignment to address %d not allowed." % desired_address)
133
134 # Try the next higher address.
135 while True:
136 desired_address = (desired_address + 1) & 127
137 if allow_address_assignment(desired_address): break
138
139 mc.enable_crc()
140
141 # Send a command to set the EEPROM device number to the desired
142 # address. This command will only be received by Motorons that
143 # are configured to listen to the general call address (which is
144 # the default) and will only be obeyed by Motorons that have JMP1
145 # shorted to GND.
146 mc.write_eeprom_device_number(desired_address)
147
148 print("Assigned address", desired_address)
149 next_address = desired_address + 1
150
151def scan_for_devices():
152 print("Scanning for I2C devices...")
153 for i in range(0, 128):
154 if scan_address(i):
155 print("Found device at address", i)
156
157# Note: This routine writes to many different addresses on your
158# I2C bus. If you have any non-Motoron devices on your bus,
159# this could cause unexpected changes to those devices.
160# You can modify allow_address_communication() to prevent this.
161def identify_devices():
162 print("Identifying Motoron controllers...")
163 for i in range(1, 128):
164 if not allow_address_communication(i): continue
165 test = motoron.MotoronI2C(bus=mc.bus, address=i)
166 try:
167 # Multiple Motorons on the same address sending different responses will
168 # cause CRC errors but we would like to ignore them.
169 test.disable_crc_for_responses()
170 v = test.get_firmware_version()
171 jumper_state = test.get_jumper_state() & 3
172 except OSError:
173 continue
174 except RuntimeError as e:
175 if e.__class__.__name__ == 'AdapterError' and e.error_code == 8:
176 continue
177 else:
178 raise
179 jumper_string = ['both', 'on', 'off', 'err'][jumper_state]
180 print("%3d: product=0x%04X version=%x.%02x JMP1=%s" % (i, v['product_id'],
181 v['firmware_version']['major'], v['firmware_version']['minor'], jumper_string))
182
183def process_input_line(line):
184 if not line: return
185 elif line[0] == 'a': assign_address(line)
186 elif line[0] == 'r':
187 print("Reset")
188 mc.reset()
189 elif line[0] == 's': scan_for_devices()
190 elif line[0] == 'i': identify_devices()
191 elif line[0] == 'h' or line[0] == 'H' or line[0] == '?': print(help_message)
192 elif line == 'q' or line == 'quit': sys.exit(0)
193 else: print("Error: Unrecognized command. Type h for help.")
194
195print(start_message)
196try:
197 while True:
198 process_input_line(input('Enter command: '))
199except (KeyboardInterrupt, EOFError):
200 print()
201
Represents an I2C connection to a Pololu Motoron Motor Controller.
Definition motoron.py:1669